Thermal Analysis

class missiontools.ThermalCircuit[source]

Bases: object

Lumped-parameter thermal network.

Example

>>> circuit = ThermalCircuit()
>>> circuit.add_capacitance('bench', 50.0, initial_temp=300.0)
>>> circuit.add_capacitance('detector', 5.0, initial_temp=300.0)
>>> circuit.connect('bench', 'detector', 0.5)
>>> circuit.add_heat_source('electronics', 10.0, target='bench')
>>> result = circuit.solve(3600.0)
add_capacitance(name, capacity, initial_temp=293.15)[source]

Add a thermal mass node.

Parameters:
  • name (str) – Unique identifier for this element.

  • capacity (float) – Thermal capacitance (J/K). Must be positive.

  • initial_temp (float) – Initial temperature (K). Defaults to 293.15 K (20 °C).

Parameters:
Return type:

None

add_heat_source(name, power, target)[source]

Add a constant heat source injecting into a capacitance node.

Parameters:
  • name (str) – Unique identifier for this element.

  • power (float) – Heat dissipation (W). Must be non-negative.

  • target (str) – Name of the capacitance node receiving the heat.

Parameters:
Return type:

None

add_cooler(name, cold_node, hot_node, power, efficiency, cop_max=20.0)[source]

Add an active cooler (heat pump) between two capacitance nodes.

The cooler extracts heat from cold_node and rejects it to hot_node. Its coefficient of performance (COP) is computed as:

COP = min(efficiency * T_cold / (T_hot - T_cold), cop_max)

where efficiency is the fraction of the Carnot COP (not the COP itself). An efficiency of 1.0 means the cooler operates at its Carnot limit; 0.5 means half the Carnot COP.

Parameters:
  • name (str) – Unique identifier for this element.

  • cold_node (str) – Capacitance node from which heat is extracted.

  • hot_node (str) – Capacitance node to which heat is rejected.

  • power (float) – Electrical input power to the cooler (W). Must be positive.

  • efficiency (float) – Fraction of Carnot COP, in (0, 1].

  • cop_max (float) – Maximum COP to prevent divergence when T_hot ≈ T_cold. Defaults to 20.0.

Parameters:
Return type:

None

add_load(name, node, load_fn)[source]

Add a time- and temperature-dependent heat load to a node.

This is the general mechanism for coupling external physics (e.g. orbital heat fluxes, radiative emission) into the thermal network.

Parameters:
  • name (str) – Unique identifier for this element.

  • node (str) – Target capacitance node.

  • load_fn (callable) – Signature (t, T) -> Q where t is time (s), T is the node temperature (K), and Q is the heat load (W). Positive values heat the node; negative values cool it.

Parameters:
Return type:

None

connect(node_a, node_b, resistance)[source]

Connect two capacitance nodes with a thermal resistance.

Parameters:
  • node_a, node_b (str) – Capacitance node names.

  • resistance (float) – Thermal resistance (K/W). Must be positive.

Parameters:
Return type:

None

set_initial_temp(name, temp)[source]

Override the initial temperature of a capacitance node.

Parameters:
  • name (str) – Capacitance node name.

  • temp (float) – Temperature (K). Must be positive.

Parameters:
Return type:

None

solve(duration, *, method='Radau', max_step=None, rtol=1e-06, atol=1e-09, t_eval=None)[source]

Solve the transient thermal response.

Parameters:
  • duration (float) – Simulation duration (s). Must be positive.

  • method (str) – scipy.integrate.solve_ivp method. Defaults to 'Radau' (implicit, suitable for stiff thermal systems).

  • max_step (float, optional) – Maximum integration step (s).

  • rtol, atol (float) – Relative and absolute tolerances for the ODE solver.

  • t_eval (array_like, optional) – Times (s) at which to store the solution. If None, the solver chooses its own output times.

Returns:

ThermalResult

Parameters:
Return type:

ThermalResult

steady_state()[source]

Compute steady-state temperatures (linear systems only).

For circuits with only capacitances, resistances, and heat sources (no coolers), solves the linear system directly.

Returns:

dict[str, float] – Node name to steady-state temperature (K).

Raises:

RuntimeError – If the circuit contains coolers.

Return type:

dict[str, float]

property nodes: list[str]

Names of all capacitance nodes.

property num_nodes: int

Number of capacitance nodes.

class missiontools.ThermalResult(t, temperatures, success, message)[source]

Bases: object

Result of a thermal circuit simulation.

Parameters:
  • t (np.ndarray)

  • temperatures (dict[str, np.ndarray])

  • success (bool)

  • message (str)

t

Time points (s).

Type:

ndarray, shape (M,)

temperatures

Mapping from node name to temperature history array, shape (M,).

Type:

dict[str, ndarray]

success

Whether the ODE solver converged.

Type:

bool

message

Solver status message.

Type:

str

class missiontools.AbstractThermalConfig(areas, emissivities, absorptivities, irradiance=1366.0, earth_ir=240.0, albedo=0.3)[source]

Bases: ABC

Base class for thermal surface configurations.

Stores per-face areas, emissivities, and absorptivities. Subclasses implement _compute_absorbed_solar() and _compute_earth_loads() to define how incident solar and Earth fluxes are projected onto each face. The concrete attach() method pre-computes all environmental heat loads over a time span and creates load functions (combining them with T⁴ emission) that are added to a ThermalCircuit.

Parameters:
  • areas (array_like, shape (M,)) – Face areas (m²). Must be positive.

  • emissivities (array_like, shape (M,)) – IR emissivity of each face, in [0, 1].

  • absorptivities (array_like, shape (M,)) – Solar absorptivity of each face, in [0, 1].

  • irradiance (float) – Solar irradiance (W m⁻²). Defaults to AM0 constant, 1366 W m⁻².

  • earth_ir (float) – Average Earth IR flux (W m⁻²). Defaults to 240 W m⁻².

  • albedo (float) – Earth albedo coefficient, in [0, 1]. Defaults to 0.3.

Parameters:
  • areas (npt.ArrayLike)

  • emissivities (npt.ArrayLike)

  • absorptivities (npt.ArrayLike)

  • irradiance (float)

  • earth_ir (float)

  • albedo (float)

property areas: ndarray[tuple[Any, ...], dtype[floating]]

Face areas (m²), shape (M,).

property emissivities: ndarray[tuple[Any, ...], dtype[floating]]

IR emissivity per face, shape (M,).

property absorptivities: ndarray[tuple[Any, ...], dtype[floating]]

Solar absorptivity per face, shape (M,).

property irradiance: float

Solar irradiance (W m⁻²).

property earth_ir: float

Average Earth IR flux (W m⁻²).

property albedo: float

Earth albedo coefficient.

property num_faces: int

Number of faces.

property spacecraft

Spacecraft this config is attached to, or None.

attach(circuit, face_nodes, t_start, t_end, step, *, prefix='thermal')[source]

Couple surface faces to a ThermalCircuit.

Pre-computes environmental heat loads (direct solar, Earth albedo, Earth IR) over the time span, then creates load functions for each face that combine interpolated absorption with Stefan-Boltzmann emission ( σ A T⁴). Each load is registered on the circuit via add_load().

Parameters:
  • circuit (ThermalCircuit) – The thermal circuit to attach loads to.

  • face_nodes (list of str) – Capacitance node name for each face. Length must equal num_faces.

  • t_start (datetime64) – Start of the simulation window.

  • t_end (datetime64) – End of the simulation window.

  • step (timedelta64) – Time step for orbital propagation (used to pre-compute solar absorption).

  • prefix (str) – Name prefix for the load elements added to the circuit. Defaults to 'thermal'.

Returns:

float – Simulation duration in seconds, for passing to circuit.solve().

Parameters:
Return type:

float

class missiontools.NormalVectorThermalConfig(normal_vecs, areas, emissivities, absorptivities, irradiance=1366.0, earth_ir=240.0, albedo=0.3)[source]

Bases: AbstractThermalConfig

Thermal config defined by face normal vectors.

Each face is characterised by an outward-facing normal vector in the spacecraft body frame, an area, an IR emissivity, and a solar absorptivity.

Direct solar absorption on face m:

\[Q_{\mathrm{solar},m} = \alpha_m \, A_m \, \max(\hat{n}_m \cdot \hat{s},\; 0) \, S\]

Power is zero in eclipse.

Earth IR absorbed on face m:

\[Q_{\mathrm{EIR},m} = \varepsilon_m \, A_m \, F_m \, q_{\mathrm{EIR}}\]

where \(F_m = \max(\hat{n}_m \cdot \hat{r}_{\mathrm{nadir}}, 0) \, (R_E / r)^2\) is the flat-plate view factor to Earth, and \(q_{\mathrm{EIR}}\) is the average Earth IR flux (240 W m⁻²). IR absorptivity equals emissivity (Kirchhoff’s law).

Earth albedo absorbed on face m:

\[Q_{\mathrm{alb},m} = \alpha_m \, A_m \, F_m \, a \, S \, \max(\hat{s} \cdot \hat{r}_{\mathrm{zenith}}, 0)\]

where a is the albedo coefficient (default 0.3) and the final cosine term accounts for the solar illumination of the sub-satellite point.

Parameters:
  • normal_vecs (array_like, shape (M, 3)) – Outward-facing normal vectors in the spacecraft body frame. Normalised internally.

  • areas (array_like, shape (M,)) – Face areas (m²).

  • emissivities (array_like, shape (M,)) – IR emissivity of each face, in [0, 1].

  • absorptivities (array_like, shape (M,)) – Solar absorptivity of each face, in [0, 1].

  • irradiance (float) – Solar irradiance (W m⁻²). Defaults to 1366 W m⁻².

  • earth_ir (float) – Average Earth IR flux (W m⁻²). Defaults to 240 W m⁻².

  • albedo (float) – Earth albedo coefficient, in [0, 1]. Defaults to 0.3.

Parameters:
  • normal_vecs (npt.ArrayLike)

  • areas (npt.ArrayLike)

  • emissivities (npt.ArrayLike)

  • absorptivities (npt.ArrayLike)

  • irradiance (float)

  • earth_ir (float)

  • albedo (float)

property normals: ndarray[tuple[Any, ...], dtype[floating]]

Face unit normals in the body frame, shape (M, 3).