Advanced Dynamics & RefCycles

Detailed mathematical models for tracking internal states such as tank stratification, thermal contexts, and detailed refrigerant cycles.

Refrigerant Cycles

GroundSourceHeatPump_heating_RefCycle

class enex_analysis.ground_source_heat_pump_ref_cycle.GroundSourceHeatPump_heating_RefCycle[source]

Bases: object

Ground source heat pump – heating mode with detailed refrigerant cycle.

In heating mode:

Indoor unit → condenser (State 2→3, refrigerant heats room air) RWHX → evaporator (State 4→1, refrigerant absorbs from ground)

T_evap is derived from the outer-loop variable T_f_out (borehole outlet):

T_evap = T_f_out - 5 K

T_cond is optimised in an inner loop to minimise E_cmp + E_fan for the given heating load.

Call system_update() each time step.

system_update()[source]

Advance simulation by one time step.

Algorithm overview

  1. Unit conversions & dead-state refrigerant properties

  2. Temporal superposition – pre-compute history term

  3. Outer loop: converge T_f_out (RWHX ↔ GHX coupling) C1. T_evap = T_f_out - 5 K (RWHX approach-temperature) C2. Inner optimisation: T_cond* = argmin (E_cmp + E_fan)

    C2a. For each T_cond candidate:
    • ε-NTU → solve for required airflow dV_iu (heating: condenser)

    • Fan curve → E_fan

    • CoolProp cycle → State 1–4, m_r, E_cmp

    C2b. minimize_scalar over T_cond ∈ [T_a_room+1, T_cond_max]

    C3. Borehole superposition → T_b → T_f → T_f_out (new) C4. Convergence check

  4. Store history, advance timer

  5. Exergy calculations for all 6 sub-systems

__init__()

GroundSourceHeatPump_cooling_RefCycle

class enex_analysis.ground_source_heat_pump_ref_cycle.GroundSourceHeatPump_cooling_RefCycle[source]

Bases: object

Ground source heat pump – cooling mode with detailed refrigerant cycle.

The evaporating temperature T_evap is NOT a fixed input. Instead it is determined each time step by minimising E_cmp + E_fan subject to the required cooling load, given the condensing temperature derived from the ground-loop coupling.

Call system_update() each time step.

system_update()[source]

Advance simulation by one time step.

Algorithm overview

  1. Unit conversions & dead-state refrigerant properties

  2. Temporal superposition – pre-compute history term

  3. Outer loop: converge T_f_in (RWHX ↔ GHX coupling) C1. T_cond = T_f_in + 5 K (RWHX approach-temperature) C2. Inner optimisation: T_evap* = argmin (E_cmp + E_fan)

    C2a. For each T_evap candidate:
    • ε-NTU → solve for required airflow dV_iu

    • Fan curve → E_fan

    • CoolProp cycle → State 1–4, m_r, E_cmp

    C2b. minimize_scalar over T_evap ∈ [T_evap_min, T_a_room-1]

    C3. Borehole superposition → T_b → T_f → T_f_in (new) C4. Convergence check

  4. Store history, advance timer

  5. Exergy calculations for all 6 sub-systems

__init__()

Dynamic Storage Models

StratifiedTankTDMA

class enex_analysis.tank_stratification_model.StratifiedTankTDMA(H, N, r0, x_shell, x_ins, k_shell, k_ins, h_w, h_o, C_d_mix)[source]

Bases: object

TDMA-based 1D stratified hot-water tank model (vertical discretization).

This class models a cylindrical storage tank split into N vertical layers (nodes). Each node enforces an energy balance that includes: - Storage term via node thermal capacitance (C). - Effective thermal conduction between adjacent nodes using effective

conductivity (k_eff) that accounts for both molecular conduction and natural convection driven by buoyancy forces.

  • Advection due to draw/inlet flow (G = rho_w * c_w * dV).

  • Heat loss to ambient through a per-node UA array (side for all nodes; bottom/top discs additionally for the end nodes).

  • Optional point heater applied at a single node.

  • Optional external loop advection across a node range in either direction, with loop heat input Q_loop.

Effective Thermal Conductivity Approach:

The model uses an effective thermal conductivity (k_eff) approach to integrate molecular conduction and natural convection effects. For each node pair (i, i+1), the effective conductivity is calculated based on: - Temperature difference (dT = T[i+1] - T[i]) - Rayleigh number (Ra), which characterizes the buoyancy-driven flow - Nusselt number (Nu), which relates effective to molecular conductivity

Stable stratification (dT < 0, upper warmer than lower): - Convection is suppressed, primarily molecular conduction - Nu ≈ 1.0 with small correction terms

Unstable stratification (dT > 0, lower warmer than upper): - Natural convection enhances heat transfer - Nu > 1.0, increasing with Rayleigh number - Laminar (Ra < 1e7): Nu ∝ Ra^0.25 - Turbulent (Ra ≥ 1e7): Nu ∝ Ra^0.33

The effective conduction coefficient between nodes is: K_eff = k_eff * A / dh, where k_eff = k_molecular * Nu

The semi-implicit time advance assembles a tri-diagonal linear system a, b, c, d and solves it with a TDMA routine (see TDMA()) to obtain next-step temperatures.

Units - Temperatures: K - Geometry: m - Volumetric flow: m³/s - UA, K: W/K - Heater power, heat flows: W

type H:

float

param H:

Tank height [m].

type H:

float

type N:

int

param N:

Number of vertical layers (nodes).

type N:

int

type r0:

float

param r0:

Inner radius of tank [m] (D = 2*r0).

type r0:

float

type x_shell:

float

param x_shell:

Shell thickness [m].

type x_shell:

float

type x_ins:

float

param x_ins:

Insulation thickness [m].

type x_ins:

float

type k_shell:

float

param k_shell:

Shell thermal conductivity [W/mK].

type k_shell:

float

type k_ins:

float

param k_ins:

Insulation thermal conductivity [W/mK].

type k_ins:

float

type h_w:

float

param h_w:

Internal convective heat transfer coefficient (water side) [W/m²K].

type h_w:

float

type h_o:

float

param h_o:

External convective heat transfer coefficient (ambient side) [W/m²K].

type h_o:

float

type C_d_mix:

float

param C_d_mix:

Empirical discharge coefficient for buoyancy-driven mixing [-]. (Note: This parameter is retained for compatibility but is not used in the effective conductivity approach.)

type C_d_mix:

float

H, D, N

Geometry and discretization (D = 2*r0).

Type:

float, float, int

A

Cross-sectional area [m²].

Type:

float

dh

Layer height [m].

Type:

float

V

Per-node volume [m³].

Type:

float

UA

Node-to-ambient UA per node [W/K], shape (N,).

Type:

np.ndarray

K

Reference axial conduction equivalent between nodes [W/K] (based on molecular conductivity only, for reference).

Type:

float

C

Per-node thermal capacitance [J/K].

Type:

float

C_d_mix

Mixing discharge coefficient [-] (retained for compatibility).

Type:

float

g

Gravitational acceleration [m/s²].

Type:

float

beta

Volumetric expansion coefficient of water [1/K].

Type:

float

nu

Kinematic viscosity of water [m²/s].

Type:

float

alpha

Thermal diffusivity of water [m²/s].

Type:

float

Pr

Prandtl number [-].

Type:

float

k_molecular

Molecular thermal conductivity of water [W/m·K].

Type:

float

Ra_critical

Critical Rayleigh number for stable stratification (≈1708).

Type:

float

k_eff

Effective thermal conductivity between node pairs [W/m·K], shape (N-1,), updated during each time step.

Type:

np.ndarray

K_eff

Effective conduction coefficient between node pairs [W/K], shape (N-1,), updated during each time step.

Type:

np.ndarray

G_use, G_loop

Flow-related terms cached from the last update step.

Type:

float

effective_conductivity(T_upper, T_lower)[source]

Calculate effective thermal conductivity between two nodes based on temperature difference and Rayleigh number.

update_tank_temp(...)[source]

Advance temperatures by one time step using the TDMA scheme. Heater and optional external loop can be applied. Heater and loop node indices are 1-based in the public API.

info(as_dict=False, precision=3)[source]

Print or return a concise summary of model geometry and thermal properties.

Notes

  • Heater/loop node indices are 1-based (converted to 0-based internally).

  • External loop terms are added to the TDMA coefficients to represent directed advection across a node range.

  • The effective conductivity approach replaces the previous Boussinesq mixing flow model, providing a more physically consistent representation of heat transfer in stratified tanks.

__init__(H, N, r0, x_shell, x_ins, k_shell, k_ins, h_w, h_o, C_d_mix)[source]
effective_conductivity(T_upper, T_lower)[source]

Calculate effective thermal conductivity between two adjacent nodes.

Integrates molecular conduction and buoyancy-driven natural convection into a single effective conductivity using the Rayleigh–Nusselt approach.

Stable stratification (dT < 0, upper warmer):

Convection is suppressed; heat transfer is primarily by conduction. Nu ≈ 1.0 with a small correction term.

Unstable stratification (dT > 0, lower warmer):

Buoyancy drives natural convection, enhancing heat transfer. - Ra < 1e3: Nu = 1.0 (conduction-dominated) - Ra < 1e7: Nu = 0.2 · Ra^0.25 (laminar convection) - Ra ≥ 1e7: Nu = 0.1 · Ra^0.33 (turbulent convection)

Parameters:
  • T_upper (float) – Temperature of the upper node [K].

  • T_lower (float) – Temperature of the lower node [K].

Returns:

k_eff – Effective thermal conductivity [W/m·K].

Return type:

float

References

Incropera & DeWitt, Fundamentals of Heat and Mass Transfer, 7th ed. Bejan, Convection Heat Transfer, 4th ed.

update_tank_temp(T, dt, T_in, dV_use, T_amb, T0, heater_node=None, heater_capacity=None, loop_outlet_node=None, loop_inlet_node=None, dV_loop=0.0, Q_loop=0.0)[source]

Advance node temperatures by one time step using the TDMA scheme.

Parameters:
  • T (np.ndarray) – Current node temperature array [K], shape (N,).

  • dt (float) – Time step size [s].

  • T_in (float) – Inlet (mains) water temperature [K].

  • dV_use (float) – Draw-off volumetric flow rate [m³/s].

  • T_amb (float) – Ambient temperature [K].

  • T0 (float) – Dead-state (reference) temperature for exergy [K].

  • heater_node (int, optional) – 1-based node index where the heater is located.

  • heater_capacity (float, optional) – Heater thermal output [W].

  • loop_outlet_node (int, optional) – 1-based node index where the external loop exits the tank.

  • loop_inlet_node (int, optional) – 1-based node index where the external loop enters the tank.

  • dV_loop (float, optional) – External loop volumetric flow rate [m³/s].

  • Q_loop (float, optional) – Heat input from the external loop [W].

Returns:

Updated node temperatures for the next time step [K].

Return type:

np.ndarray

info(as_dict=False, precision=3)[source]

Print or return a summary of tank geometry and thermal properties.

Parameters:
  • as_dict (bool) – If True, return as dict; otherwise print a formatted summary.

  • precision (int) – Number of significant digits for display.

Context & Controllers

StepContext

class enex_analysis.dynamic_context.StepContext(n, current_time_s, current_hour, hour_of_day, T0, T0_K, activation_flags, T_tank_w_K, tank_level, dV_mix_w_out, I_DN=0.0, I_dH=0.0, T_sup_w_K=288.15)[source]

Bases: object

Per-timestep immutable context (time, environment, demand).

n

Current step index.

Type:

int

current_time_s

Elapsed simulation time [s].

Type:

float

current_hour

Elapsed simulation time [h].

Type:

float

hour_of_day

Hour within the current day (0–24, repeating).

Type:

float

T0

Dead-state / outdoor-air temperature [°C].

Type:

float

T0_K

Dead-state temperature [K].

Type:

float

activation_flags

Per-subsystem schedule activation flags for this step. e.g. {"stc": True} when the STC preheat window is active. An empty dict means no subsystem has a schedule constraint.

Type:

dict[str, bool]

T_tank_w_K

Current tank water temperature [K].

Type:

float

tank_level

Fractional tank fill level (0–1).

Type:

float

dV_mix_w_out

Service water draw-off flow rate [m³/s].

Type:

float

I_DN

Direct-normal irradiance on collector plane [W/m²].

Type:

float

I_dH

Diffuse-horizontal irradiance [W/m²].

Type:

float

T_sup_w_K

Mains water supply temperature [K].

Type:

float

Parameters:
  • n (int)

  • current_time_s (float)

  • current_hour (float)

  • hour_of_day (float)

  • T0 (float)

  • T0_K (float)

  • activation_flags (dict)

  • T_tank_w_K (float)

  • tank_level (float)

  • dV_mix_w_out (float)

  • I_DN (float)

  • I_dH (float)

  • T_sup_w_K (float)

n: int
current_time_s: float
current_hour: float
hour_of_day: float
T0: float
T0_K: float
activation_flags: dict
T_tank_w_K: float
tank_level: float
dV_mix_w_out: float
I_DN: float = 0.0
I_dH: float = 0.0
T_sup_w_K: float = 288.15
__init__(n, current_time_s, current_hour, hour_of_day, T0, T0_K, activation_flags, T_tank_w_K, tank_level, dV_mix_w_out, I_DN=0.0, I_dH=0.0, T_sup_w_K=288.15)
Parameters:
  • n (int)

  • current_time_s (float)

  • current_hour (float)

  • hour_of_day (float)

  • T0 (float)

  • T0_K (float)

  • activation_flags (dict)

  • T_tank_w_K (float)

  • tank_level (float)

  • dV_mix_w_out (float)

  • I_DN (float)

  • I_dH (float)

  • T_sup_w_K (float)

ControlState

class enex_analysis.dynamic_context.ControlState(is_on, Q_heat_source, dV_tank_w_in_ctrl, result=<factory>)[source]

Bases: object

Heat-source control decisions for one timestep.

Model-agnostic container: any boiler model populates these fields in its Phase-A helper. Subsystem states are managed separately via sub_states: dict[str, dict].

is_on

Whether the heat source is running.

Type:

bool

Q_heat_source

Net heat delivered to the tank from the heat source [W].

Type:

float

dV_tank_w_in_ctrl

Refill flow rate [m³/s]. None = always-full sentinel (inflow resolved inside residual).

Type:

float | None

result

Full result dictionary from the model’s _calc_state. Contents are model-specific.

Type:

dict

Parameters:
  • is_on (bool)

  • Q_heat_source (float)

  • dV_tank_w_in_ctrl (float | None)

  • result (dict)

is_on: bool
Q_heat_source: float
dV_tank_w_in_ctrl: float | None
result: dict
__init__(is_on, Q_heat_source, dV_tank_w_in_ctrl, result=<factory>)
Parameters:
  • is_on (bool)

  • Q_heat_source (float)

  • dV_tank_w_in_ctrl (float | None)

  • result (dict)