enex_analysis.dynamic_context¶
Shared dynamic simulation context and control helpers.
Provides reusable dataclasses and pure functions that form the
backbone of time-stepping heat-pump simulations. Extracted from
AirSourceHeatPumpBoiler so that GroundSourceHeatPumpBoiler
and future models can share the same infrastructure.
Functions
|
Check whether current hour falls within HP operating schedule. |
|
Hysteresis-based heat-source on/off decision. |
|
Determine refill flow rate from current level and operational mode. |
|
Energy and mass balance residuals at T^{n+1}. |
Classes
|
Heat-source control decisions for one timestep. |
|
Per-timestep immutable context (time, environment, demand). |
|
Pluggable subsystem interface. |
|
Subsystem-specific exergy calculation results. |
- 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]¶
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)
- class enex_analysis.dynamic_context.ControlState(is_on, Q_heat_source, dV_tank_w_in_ctrl, result=<factory>)[source]¶
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)
- class enex_analysis.dynamic_context.SubsystemExergy(columns, X_tot_add=0.0, X_in_tank_add=0.0, X_out_tank_add=0.0)[source]¶
Subsystem-specific exergy calculation results.
Each subsystem’s
calc_exergy()returns this object so that the host boiler can merge subsystem columns into the result DataFrame and adjust system-level totals.- columns¶
Exergy columns to append (key = column name).
- Type:
dict[str, pd.Series]
- X_tot_add¶
Additive contribution to system total exergy input
X_tot [W](e.g. pump electricity).- Type:
pd.Series | float
- X_in_tank_add¶
Additive exergy entering the tank boundary (e.g. heated return water in
tank_circuit).- Type:
pd.Series | float
- X_out_tank_add¶
Additive exergy leaving the tank boundary (e.g. water drawn to STC in
tank_circuit).- Type:
pd.Series | float
- Parameters:
columns (
dict)X_tot_add (
object)X_in_tank_add (
object)X_out_tank_add (
object)
-
columns:
dict¶
-
X_tot_add:
object= 0.0¶
-
X_in_tank_add:
object= 0.0¶
-
X_out_tank_add:
object= 0.0¶
- __init__(columns, X_tot_add=0.0, X_in_tank_add=0.0, X_out_tank_add=0.0)¶
- Parameters:
columns (
dict)X_tot_add (
object)X_in_tank_add (
object)X_out_tank_add (
object)
- class enex_analysis.dynamic_context.Subsystem(*args, **kwargs)[source]¶
Pluggable subsystem interface.
Each subsystem computes its contribution for a single timestep and assembles result columns for the output DataFrame. New subsystems (PV, battery, …) implement this protocol and register with the boiler model.
- step(ctx, ctrl, dt, T_tank_w_in_K)[source]¶
Compute subsystem state for this timestep.
- Parameters:
ctx (StepContext) – Current-step immutable context.
ctrl (ControlState) – Heat-source control decisions.
dt (float) – Time-step size [s].
T_tank_w_in_K (float) – Mains water inlet temperature [K].
- Returns:
Must include at least: -
'Q_contribution'(float):Net energy contribution to tank [W].
'E_subsystem'(float):Electrical power consumed [W].
'T_tank_w_in_override_K'(float | None):If the subsystem modifies the tank inlet temperature (e.g. mains preheat), provide the heated temperature [K].
Nonemeans no modification.
- Return type:
dict
- assemble_results(ctx, ctrl, step_state, T_solved_K)[source]¶
Build result columns for DataFrame output.
- Parameters:
ctx (StepContext) – Current-step immutable context.
ctrl (ControlState) – HP control decisions.
step_state (dict) – Dict returned by
step().T_solved_K (float) – Solved tank temperature [K].
- Returns:
Keyed result entries for the output DataFrame.
- Return type:
dict
- calc_exergy(df, T0_K)[source]¶
Compute subsystem-level exergy items.
- Parameters:
df (
DataFrame)T0_K (
Series)
- Return type:
SubsystemExergy|None
- calc_performance(**kwargs)[source]¶
Calculate performance at a specific condition.
- Return type:
dict
- __init__(*args, **kwargs)¶
- enex_analysis.dynamic_context.check_hp_schedule_active(hour, hp_on_schedule)[source]¶
Check whether current hour falls within HP operating schedule.
- Parameters:
hour (float) – Current time of day [h] (0.0–24.0).
hp_on_schedule (list of tuple) – List of
(start_hour, end_hour)operating windows.
- Return type:
bool
- enex_analysis.dynamic_context.determine_heat_source_on_off(T_tank_w_C, T_lower, T_upper, is_on_prev, hour_of_day, on_schedule)[source]¶
Hysteresis-based heat-source on/off decision.
- Parameters:
T_tank_w_C (float) – Current tank water temperature [°C].
T_lower (float) – Lower hysteresis bound [°C].
T_upper (float) – Upper hysteresis bound [°C].
is_on_prev (bool) – Heat-source state at the previous timestep.
hour_of_day (float) – Hour within the day (0–24).
on_schedule (list[tuple[float, float]]) – Active operating windows
(start_h, end_h).
- Returns:
Whether the heat source should run this timestep.
- Return type:
bool
- enex_analysis.dynamic_context.determine_tank_refill_flow(dt, tank_level, dV_tank_w_out, V_tank_full, tank_always_full, prevent_simultaneous_flow, tank_level_lower_bound, tank_level_upper_bound, dV_tank_w_in_refill, is_refilling)[source]¶
Determine refill flow rate from current level and operational mode.
Pure tank-level management: all subsystem-specific flow overrides (e.g. STC mains-preheat forced refill) are the responsibility of the scenario class via
_run_subsystems/ctrl.dV_tank_w_in_ctrl.- Parameters:
dt (float) – Time-step size [s].
tank_level (float) – Current fractional tank level (0–1).
dV_tank_w_out (float) – Current outflow rate [m³/s].
V_tank_full (float) – Tank full volume [m³].
tank_always_full (bool) – Whether the tank is forced to stay full.
prevent_simultaneous_flow (bool) – Exclusive-flow mode flag.
tank_level_lower_bound (float) – Level lower bound for refill trigger.
tank_level_upper_bound (float) – Level upper bound for refill cut-off.
dV_tank_w_in_refill (float) – Refill flow rate [m³/s].
is_refilling (bool) – Whether we are currently in a refill cycle.
- Returns:
(dV_tank_w_in, is_refilling).Nonemeans always-full sentinel (no PSF).- Return type:
tuple[float | None, bool]
- enex_analysis.dynamic_context.tank_mass_energy_residual(x, ctx, ctrl, dt, T_tank_w_in_K, T_sup_w_K, T_mix_w_out_K, C_tank, UA_tank, V_tank_full, subsystems, sub_states)[source]¶
Energy and mass balance residuals at T^{n+1}.
The 3-way mixing valve ratio α(T) makes the outflow a nonlinear function of T^{n+1}, requiring
fsolve.Subsystem energy contributions and tank-inlet temperature overrides are read from
sub_states.- Parameters:
x (list[float]) –
[T_next_K, level_next].ctx (StepContext) – Current-step immutable context.
ctrl (ControlState) – Current-step HP control decisions.
dt (float) – Time-step size [s].
T_tank_w_in_K (float) – Mains water inlet temperature [K].
T_sup_w_K (float) – Mains water supply temperature [K] (for mixing valve).
T_mix_w_out_K (float) – Target mixing-valve outlet temperature [K].
C_tank (float) – Tank thermal capacitance [J/K].
UA_tank (float) – Tank overall heat-loss coefficient [W/K].
V_tank_full (float) – Tank full volume [m³].
subsystems (dict[str, Subsystem]) – Registered subsystem instances.
sub_states (dict[str, dict]) – Per-subsystem state dicts from
step().
- Returns:
[r_energy, r_mass].- Return type:
list[float]