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_hp_schedule_active(hour, hp_on_schedule)

Check whether current hour falls within HP operating schedule.

determine_heat_source_on_off(T_tank_w_C, ...)

Hysteresis-based heat-source on/off decision.

determine_tank_refill_flow(dt, tank_level, ...)

Determine refill flow rate from current level and operational mode.

tank_mass_energy_residual(x, ctx, ctrl, dt, ...)

Energy and mass balance residuals at T^{n+1}.

Classes

ControlState(is_on, Q_heat_source, ...[, result])

Heat-source control decisions for one timestep.

StepContext(n, current_time_s, current_hour, ...)

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

Subsystem(*args, **kwargs)

Pluggable subsystem interface.

SubsystemExergy(columns[, X_tot_add, ...])

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]. None means 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). None means 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]