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:
objectGround 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¶
Unit conversions & dead-state refrigerant properties
Temporal superposition – pre-compute history term
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
Store history, advance timer
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:
objectGround 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¶
Unit conversions & dead-state refrigerant properties
Temporal superposition – pre-compute history term
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
Store history, advance timer
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:
objectTDMA-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.
- 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
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:
objectPer-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:
objectHeat-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)