Simulation helpers

Per-step state, energy / exergy helpers, and the stdout summary tables emitted at the end of a dynamic run.

Per-step 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.

class tmhp.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)

class tmhp.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)

class tmhp.dynamic_context.Subsystem(*args, **kwargs)[source]

Bases: Protocol

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 the following keys:

  • 'Q_contribution' (float) — net energy contribution to the tank [W].

  • 'E_subsystem' (float) — electrical power consumed [W].

  • 'T_tank_w_in_override_K' (float | None) — heated tank-inlet temperature [K] if the subsystem modifies the inlet (e.g. mains preheat); None if there is 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)
class tmhp.dynamic_context.SubsystemExergy(columns, X_tot_add=0.0, X_in_tank_add=0.0, X_out_tank_add=0.0)[source]

Bases: object

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)

tmhp.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

tmhp.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

tmhp.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]

tmhp.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]

Energy & exergy helpers

Utility functions for energy, entropy, and exergy analysis.

This module contains helper functions organized into the following categories:

  1. Friction and Flow Functions - darcy_friction_factor: Calculate Darcy friction factor - calc_Orifice_flow_coefficient: Calculate orifice flow coefficient - calc_boussinessq_mixing_flow: Calculate mixing flow based on Boussinesq approximation

  2. Heat Transfer Functions - calc_h_vertical_plate: Natural convection heat transfer coefficient - calc_UA_tank_arr: Tank heat loss UA calculation - calc_lmtd_*: Log mean temperature difference calculations - calc_UA_from_dV_fan: Heat transfer coefficient from fan flow rate

  3. Curve Fitting Functions - linear_function, quadratic_function, cubic_function, quartic_function

  4. Exergy and Entropy Functions - generate_entropy_exergy_term: Calculate entropy and exergy terms - calc_exergy_flow: Calculate exergy flow rate due to material flow

  5. G-function Calculations (Ground Source Heat Pumps) - f, chi, G_FLS: Helper functions for g-function calculation

  6. TDMA Solver Functions - TDMA: Solve tri-diagonal matrix system - _add_loop_advection_terms: Add forced convection terms to TDMA coefficients

  7. Heat Pump Cycle Functions - calculate_ASHP_*_COP: Air source heat pump COP calculations - calculate_GSHP_COP: Ground source heat pump COP calculation - calc_ref_state: Calculate refrigerant cycle states (with superheating/subcooling support) - find_ref_loop_optimal_operation: Find optimal operation point

  8. Tank Functions - update_tank_temperature: Update tank temperature based on energy balance

  9. Schedule Functions - _build_dhw_usage_ratio: Build schedule ratio array

  10. Balance Printing Utilities - print_balance: Print energy/entropy/exergy balance

tmhp.enex_functions.calc_HX_perf_for_target_heat(Q_ref_target, T_a_in_C=None, T_ref_sat_K=None, A_cross=None, UA_design=None, dV_fan_design=None, is_active=True, exponent=0.71, T_ou_a_in_C=None, T_ref_evap_sat_K=None, T_ref_cond_sat_l_K=None)[source]

Numerically solve for the air-side flow rate of an ε-NTU heat exchanger.

Given a target heat transfer duty, find the airflow that delivers it, accounting for the velocity dependence of UA via the Wang et al. (2000) fin-and-tube correlation (UA ∝ velocity^0.71).

Parameters:
  • Q_ref_target (float) – Target heat transfer rate [W] (always positive).

  • T_a_in_C (float, optional) – Air-side inlet temperature [°C].

  • T_ref_sat_K (float, optional) – Refrigerant saturation temperature [K] on the constant-temperature side.

  • A_cross (float) – Heat-exchanger cross-sectional area [m²].

  • UA_design (float) – Design UA [W/K].

  • dV_fan_design (float) – Design fan volumetric flow rate [m³/s].

  • is_active (bool) – Active flag.

  • exponent (float) – UA scaling exponent (default: 0.71).

  • (optional) (# Legacy aliases)

  • T_ou_a_in_C (float, optional) – Backward-compat alias for T_a_in_C.

  • T_ref_evap_sat_K (float, optional) – Backward-compat alias for T_ref_sat_K.

  • T_ref_cond_sat_l_K (float, optional) – Unused; kept only to preserve the older function signature.

Returns:

Dictionary with the following keys:

  • dV_fan — required air-side flow rate [m³/s]

  • UA — overall heat-transfer coefficient at the solution point [W/K]

  • T_a_mid_C — air temperature between the heat exchanger and the fan [°C]

  • Q_air — heat-transfer rate at the operating point [W]

  • epsilon — effectiveness at the operating point [–]

  • converged — whether the solver converged

All numeric values are np.nan when is_active=False.

Return type:

dict

tmhp.enex_functions.calc_Orifice_flow_coefficient(D0, D1)[source]

Calculate the orifice flow coefficient based on diameters.

Flow configuration:

---------------
 ->      |
 D0     D1 ->
 ->      |
---------------
Parameters:
  • D0 (float) – Pipe diameter [m]

  • D1 (float) – Hole diameter [m]

Returns:

C_d – Orifice flow coefficient (dimensionless)

Return type:

float

Notes

This is a simplified calculation. A more complete implementation should be based on physical equations.

tmhp.enex_functions.calc_boussinessq_mixing_flow(T_upper, T_lower, A, dz, C_d=0.1)[source]

Calculate mixing flow rate between two adjacent nodes based on Boussinesq approximation.

Mixing occurs only when the lower node temperature is higher than the upper node, creating a gravitationally unstable condition.

Parameters:
  • T_upper (float) – Upper node temperature [K]

  • T_lower (float) – Lower node temperature [K]

  • A (float) – Tank cross-sectional area [m²]

  • dz (float) – Node height [m]

  • C_d (float, optional) – Flow coefficient (empirical constant), default 0.1

Returns:

dV_mix – Volumetric flow rate exchanged between nodes [m3/s]

Return type:

float

Notes

TODO: C_d value should be calculated based on physical equations.

tmhp.enex_functions.calc_mixing_valve_flows(dV_mix_out, alp)[source]

Calculate volumetric flow rates at a 3-way mixing valve given a mixing ratio.

Parameters:
  • dV_mix_out (float) – Total requested service/mixed flow rate [m³/s].

  • alp (float) – Hot water mixing ratio [0-1].

Returns:

Dictionary containing generic mixing valve mass balances: - dV_hot_in: Flow rate drawn from the hot source [m³/s] - dV_cold_in: Flow rate drawn from the cold source [m³/s] - dV_mix_out: Total mixed flow rate [m³/s]

Return type:

dict

tmhp.enex_functions.calc_mixing_valve_temp(T_tank_w_K, T_tank_w_in_K, T_mix_w_out_K)[source]

Calculate 3-way mixing valve output temperature and mixing ratio.

Mixes hot tank water with cold mains water to achieve the target service temperature T_mix_w_out_K.

Parameters:
  • T_tank_w_K (float) – Current tank water temperature [K].

  • T_tank_w_in_K (float) – Mains (cold) water supply temperature [K].

  • T_mix_w_out_K (float) – Target delivery temperature [K].

Returns:

{'alp': float, 'T_mix_w_out': float, 'T_mix_w_out_K': float} - alp: hot-water fraction [0–1] - T_mix_w_out: actual service temperature [°C] - T_mix_w_out_K: actual service temperature [K]

Return type:

dict

tmhp.enex_functions.calc_stc_performance(I_DN_stc, I_dH_stc, T_stc_w_in_K, T0_K, A_stc_pipe, alpha_stc, h_o_stc, h_r_stc, k_ins_stc, x_air_stc, x_ins_stc, dV_stc, E_pump, is_active=True)[source]

Compute the performance of a solar thermal collector (STC).

Adapted from the SolarAssistedGasBoiler.system_update logic in the legacy enex_engine.py.

Parameters:
  • I_DN_stc (float) – Direct normal irradiance [W/m²].

  • I_dH_stc (float) – Diffuse horizontal irradiance [W/m²].

  • T_stc_w_in_K (float) – Inlet water temperature to the STC (equal to the tank temperature) [K].

  • T0_K (float) – Reference / ambient temperature [K].

  • A_stc_pipe (float) – STC pipe area [m²].

  • alpha_stc (float) – Absorptance [-].

  • h_o_stc (float) – Outer convective heat-transfer coefficient [W/m²K].

  • h_r_stc (float) – Air-gap radiative heat-transfer coefficient [W/m²K].

  • k_ins_stc (float) – Insulation thermal conductivity [W/mK].

  • x_air_stc (float) – Air-gap thickness [m].

  • x_ins_stc (float) – Insulation thickness [m].

  • dV_stc (float) – STC volumetric flow rate [m³/s].

  • E_pump (float) – Pump electrical input [W].

  • is_active (bool, optional) – Active flag (default: True). When False, returns a dict filled with np.nan (except T_stc_w_out_K and T_stc_w_in_K, which are set to the inlet temperature).

Returns:

  • I_sol_stc — total incident irradiance [W/m²]

  • Q_sol_stc — absorbed solar heat [W]

  • Q_stc_w_in — inlet enthalpy flow relative to T0_K [W]

  • Q_stc_w_out — outlet enthalpy flow relative to T0_K [W]

  • ksi_stc — dimensionless efficiency parameter [-]

  • T_stc_w_out_K — outlet water temperature [K]

  • T_stc_w_final_K— outlet temperature including pump heat gain [K]

  • T_stc_w_in_K — inlet water temperature (echoed back) [K]

  • T_stc_K — mean absorber-plate temperature [K]

  • Q_l_stc — absorber-plate heat loss [W]

Return type:

dict

Notes

  • All variable names are suffixed with _stc to mark them as STC-specific.

  • Heat losses are reported as Q_l_stc.

  • Entropy and exergy quantities are intentionally not computed here; do them as a post-processing step on the result CSV if needed.

tmhp.enex_functions.cubic_function(x, a, b, c, d)[source]

Cubic function: y = a*x³ + b*x² + c*x + d

tmhp.enex_functions.linear_function(x, a, b)[source]

Linear function: y = a*x + b

tmhp.enex_functions.print_balance(balance, decimal=2)[source]

Print energy, entropy, or exergy balance dictionary in a formatted way.

This function prints balance information for subsystems, categorizing entries into in, out, consumed, and generated categories.

Parameters:
  • balance (dict) – Dictionary containing balance information for subsystems. Structure: {subsystem_name: {category: {symbol: value}}} Categories: ‘in’, ‘out’, ‘con’ (consumed), ‘gen’ (generated)

  • decimal (int, optional) – Number of decimal places for output (default: 2)

Returns:

Only prints output

Return type:

None

Example

>>> balance = {
...     "hot water tank": {
...         "in": {"E_heater": 5000.0},
...         "out": {"Q_w_tank": 4500.0, "Q_l_tank": 400.0},
...         "con": {"X_c_tank": 100.0}
...     }
... }
>>> print_balance(balance)
tmhp.enex_functions.quadratic_function(x, a, b, c)[source]

Quadratic function: y = a*x² + b*x + c

tmhp.enex_functions.quartic_function(x, a, b, c, d, e)[source]

Quartic function: y = a*x⁴ + b*x³ + c*x² + d*x + e

tmhp.enex_functions.update_tank_temperature(T_tank_w_K, Q_gain, UA_tank, T0_K, C_tank, dt)[source]

Update tank temperature using the Crank-Nicolson implicit scheme.

The governing ODE for a lumped-capacitance tank is:

C dT/dt = Q_gain - UA (T - T0)

Crank-Nicolson averages the loss term across both time levels:

T^{n+1} = [(C/dt - UA/2) T^n + Q_gain + UA T0] / (C/dt + UA/2)

This scheme is second-order accurate in time and unconditionally stable, eliminating the overshoot that Forward Euler can exhibit when dt is large relative to the thermal time constant C/UA.

Parameters:
  • T_tank_w_K (float) – Current tank temperature [K].

  • Q_gain (float) – Total heat gain rate [W] (condenser, UV, STC, refill, etc.).

  • UA_tank (float) – Overall tank heat-loss coefficient [W/K].

  • T0_K (float) – Dead-state / ambient temperature [K].

  • C_tank (float) – Tank thermal capacitance [J/K] (= c_w * rho_w * V_tank * level).

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

Returns:

Updated tank temperature [K].

Return type:

float

tmhp.enex_functions.G_FLS(t, ks, as_, rb, H)[source]

Calculate the g-function for finite line source (FLS) model.

This function calculates the g-function used in ground source heat pump analysis. Results are cached for performance.

Parameters:
  • t (float) – Time [s]

  • ks (float) – Ground thermal conductivity [W/mK]

  • as (float) – Ground thermal diffusivity [m²/s]

  • rb (float) – Borehole radius [m]

  • H (float) – Borehole height [m]

Returns:

g-function value [mK/W]. Returns scalar for single time value, array for multiple time values.

Return type:

float or array

tmhp.enex_functions.air_dynamic_viscosity(T_K)[source]

Calculate air dynamic viscosity using Sutherland’s formula.

Parameters:

T_K (float) – Temperature [K]

Returns:

  • float – Dynamic viscosity [Pa·s]

  • Reference (Sutherland’s formula for air)

  • mu = mu0 * (T/T0)^1.5 * (T0 + S) / (T + S)

  • where mu0 = 1.716e-5 Pa·s at T0 = 273.15 K, S = 110.4 K

tmhp.enex_functions.air_prandtl_number(T_K)[source]

Calculate air Prandtl number.

Parameters:

T_K (float) – Temperature [K]

Returns:

  • float – Prandtl number [-]

  • Note (Pr ≈ 0.71 for air at typical temperatures (20-50°C))

  • Temperature dependence is weak, so using constant value.

tmhp.enex_functions.calc_ASHP_cooling_COP(T_a_int_out, T_a_ext_in, Q_r_int, Q_r_max, COP_ref)[source]

Calculate the Coefficient of Performance (COP) for an Air Source Heat Pump (ASHP) in cooling mode.

Reference: https://publications.ibpsa.org/proceedings/bs/2023/papers/bs2023_1118.pdf

Parameters:
  • T_a_int_out (float) – Indoor air temperature [K]

  • T_a_ext_in (float) – Outdoor air temperature [K]

  • Q_r_int (float) – Indoor heat load [W]

  • Q_r_max (float) – Maximum cooling capacity [W]

  • COP_ref (float) – Reference COP at standard conditions

Returns:

COP value

Return type:

float

Note

COP is calculated based on: - PLR: Part Load Ratio - EIR: Energy input to cooling output ratio

tmhp.enex_functions.calc_ASHP_heating_COP(T0, Q_r_int, Q_r_max)[source]

Calculate the Coefficient of Performance (COP) for an Air Source Heat Pump (ASHP) in heating mode.

Reference: https://www.mdpi.com/2071-1050/15/3/1880

Parameters:
  • T0 (float) – Environmental temperature [K]

  • Q_r_int (float) – Indoor heat load [W]

  • Q_r_max (float) – Maximum heating capacity [W]

Returns:

COP value

Return type:

float

Note

COP is calculated based on PLR (Part Load Ratio).

tmhp.enex_functions.calc_energy_flow(G, T, T0)[source]

Calculate energy flow rate.

Parameters:
  • G (float or pd.Series) – Heat capacity flow rate (mass_flow * Cp) [W/K].

  • T (float or pd.Series) – Current temperature [K].

  • T0 (float or pd.Series) – Reference/dead state temperature [K].

Returns:

Energy flow rate [W].

Return type:

float or pd.Series

tmhp.enex_functions.calc_exergy_flow(G, T, T0)[source]

Calculate exergy flow rate.

Parameters:
  • G (float or pd.Series) – Heat capacity flow rate (mass_flow * Cp) [W/K].

  • T (float or pd.Series) – Current temperature [K].

  • T0 (float or pd.Series) – Reference/dead state temperature [K].

Returns:

Exergy flow rate [W].

Return type:

float or pd.Series

tmhp.enex_functions.calc_fan_power_from_dV_fan(dV_fan, fan_params, vsd_coeffs, is_active=True)[source]

Calculate fan power using ASHRAE 90.1 VSD Curve.

Parameters:
  • dV_fan (float) – Current flow rate [m³/s].

  • fan_params (dict) – Must contain fan_design_flow_rate and fan_design_power.

  • vsd_coeffs (dict) – VSD Curve coefficients (c1 through c5).

  • is_active (bool) – If False, returns np.nan.

Returns:

Fan power [W].

Return type:

float

tmhp.enex_functions.calc_GSHP_COP(T_a_iu_in_K, T_f_out_K, dV_a_ratio, mode)[source]

Calculate COP for a Ground Source Heat Pump.

Uses the EnergyPlus Coil:*:WaterToAir:EquationFit model (Equation-Fit Method).

References

EnergyPlus Engineering Reference, Ch. 16.5.10.2 (Eq. 16.412–16.418, Tang 2005). Dataset: TCH072_GLHP (ClimateMaster 6-ton, Ground Loop HP), source: EnergyPlus/datasets/WaterToAirHeatPumps.idf.

Rated conditions (from IDF comment, 15% methanol antifreeze):

  • Cooling: 70.29 kBtu/h @ 77 °F (25 °C) entering water, EER = 14.35

  • Heating: 56.14 kBtu/h @ 32 °F (0 °C) entering water, COP = 3.42

Parameters:
  • T_a_iu_in_K (float) – Indoor air inlet dry-bulb temperature [K] (= T_a_room). For cooling, internally converted to wet-bulb (T_wb) via CoolProp assuming RH = 50%.

  • T_f_out_K (float) – Source-side inlet water temperature [K] — water returning from the borehole to the heat pump (T_w,in in EnergyPlus notation).

  • dV_a_ratio (float) – Load-side air volumetric flow ratio: V_a / V_a_ref [-] V_a_ref = Q_rated / (rho_a * c_a * 10K) [m³/s]

  • mode (str) – “cooling” or “heating”

  • Units

  • -----

  • Temperatures (K)

  • rates (Flow)

  • Heat/Power (W)

Returns:

COP (dimensionless). Polynomial model is unconditionally stable— no ValueError on temperature inversion.

Return type:

float

tmhp.enex_functions.calc_refrigerant_exergy(df, ref, T0_K, P0=101325)[source]

Calculate refrigerant state-point exergy using pre-computed properties.

Uses the entropy (s_ref_*) and enthalpy (h_ref_*) columns already present in df (produced by calc_ref_state) to compute specific exergy and exergy flow rate for each refrigerant state point.

Dead-state properties (h0, s0) are evaluated at (T0, P0) for the given refrigerant using CoolProp (vectorized via unique T0 values).

Parameters:
  • df (pd.DataFrame) – DataFrame containing pre-computed enthalpy h_ref_* [J/kg], entropy s_ref_* [J/(kg·K)], and mass-flow m_dot_ref [kg/s] columns.

  • ref (str) – CoolProp refrigerant identifier (e.g. 'R410A').

  • T0_K (pd.Series) – Dead-state (environment) temperature per row [K].

  • P0 (float) – Dead-state pressure [Pa] (default 101325).

Returns:

df with columns added per state point: x_ref_{name} [J/kg], X_ref_{name} [W].

Return type:

pd.DataFrame

Notes

  • Exergy equation: x = (h − h0) − T0·(s − s0) [J/kg]

  • Exergy flow: X = ṁ · x [W]

  • Rows with NaN enthalpy/entropy propagate NaN naturally.

tmhp.enex_functions.calc_UA_from_dV_fan(dV_fan, dV_fan_design, A_cross, UA, exponent=0.71)[source]

Calculate velocity-dependent UA via lumped scaling (Wang et al., 2000).

Parameters:
  • dV_fan (float) – Current fan flow rate [m³/s].

  • dV_fan_design (float) – Design fan flow rate [m³/s].

  • A_cross (float) – Heat exchanger cross-sectional area [m²].

  • UA (float) – Design UA value [W/K].

  • exponent (float) – Exponent for velocity scaling. Default is 0.71 for a 1-row configuration.

Returns:

Scaled UA value [W/K].

Return type:

float

Notes

Instead of the Dittus-Boelter tube-side exponent (0.8), this uses a simplified lumped exponent (default 0.71). This derivation assumes a 1-row plain fin-and-tube configuration (N=1) where the Colburn j-factor is proportional to Re^-0.29, leading to h ∝ V^0.71. Multi-row coils may use exponents between 0.5 and 0.8 depending on configuration. Reference: Wang et al. (2000), DOI: 10.1016/S0017-9310(99)00333-6

tmhp.enex_functions.calc_uv_exposure_time(radius_cm, uvc_output_W, lamp_arc_length_cm, target_dose_mj_cm2=186, turbidity_ntu=0.25)[source]

Calculate required UV lamp exposure time via Radial Model.

Reference: ADA453967.pdf — Radial Model for UV disinfection.

Parameters:
  • radius_cm (float) – Tank radius (lamp-to-wall distance) [cm].

  • uvc_output_W (float) – UV-C output power of the lamp [W].

  • lamp_arc_length_cm (float) – Arc length of the lamp [cm].

  • target_dose_mj_cm2 (float) – Target germicidal dose [mJ/cm²]. Default 186 (EPA 4-log virus).

  • turbidity_ntu (float) – Water turbidity [NTU].

Returns:

Required single-exposure time [min].

Return type:

float

tmhp.enex_functions.calc_uv_lamp_power(current_time_s, period_sec, num_switching, exposure_sec, lamp_watts)[source]

Calculate UV lamp power at a given time instant.

The lamp switches on num_switching times per period_sec, each activation lasting exposure_sec.

Parameters:
  • current_time_s (float) – Current simulation time [s].

  • period_sec (float) – Switching period (e.g. 3 h → 10800 s).

  • num_switching (int) – Number of on-cycles per period.

  • exposure_sec (float) – Duration of each on-cycle [s].

  • lamp_watts (float) – Rated lamp power [W].

Returns:

Instantaneous lamp power [W] (0 or lamp_watts).

Return type:

float

tmhp.enex_functions.chi(s, rb, H, z0=0)[source]

Helper function for G-function calculation.

Parameters:
  • s (float) – Integration variable

  • rb (float) – Borehole radius [m]

  • H (float) – Borehole height [m]

  • z0 (float, optional) – Reference depth [m] (default: 0)

Returns:

chi function value

Return type:

float

tmhp.enex_functions.convert_electricity_to_exergy(df)[source]

Copy all electricity columns (E_*) to exergy columns (X_*).

Electrical energy is 100 %% pure exergy, so X = E for all electricity-consumption columns.

The function searches for columns matching the pattern E_xxx [W] and creates corresponding X_xxx [W] columns.

Parameters:

df (pd.DataFrame) – DataFrame with E_xxx [W] columns.

Returns:

df with X_xxx [W] columns added.

Return type:

pd.DataFrame

tmhp.enex_functions.f(x)[source]

Helper function for G-function calculation.

Parameters:

x (float) – Input value

Returns:

f(x) = x*erf(x) - (1-exp(-x²))/√π

Return type:

float

tmhp.enex_functions.generate_entropy_exergy_term(fluid, T, P, Q, T0, P0, phase='gas')[source]

Calculate entropy, enthalpy, and exergy.

Parameters:
  • fluid (str) – Fluid name.

  • T (float) – Temperature [K].

  • P (float) – Pressure [Pa].

  • Q (float) – Quality (0 to 1).

  • T0 (float) – Dead state temperature [K].

  • P0 (float) – Dead state pressure [Pa].

  • phase (Literal['gas', 'liquid', 'twophase'], optional) – Fluid phase. Default is ‘gas’.

Returns:

Entropy [J/kg-K], Enthalpy [J/kg], Exergy [J/kg].

Return type:

tuple[float, float, float]

tmhp.enex_functions.get_uv_params_from_turbidity(turbidity_ntu)[source]

Return UV parameters from a turbidity lookup table.

Table data based on Table 1. Effect of Turbidity on UVT, UV Absorbance, UV Intensity, and Exposure Time.

Parameters:

turbidity_ntu (float) – Turbidity value [NTU].

Returns:

Keys: uv_absorbance, uv_transmittance_percent, reference_intensity_mw_cm2, reference_exposure_time_sec.

Return type:

dict

Simulation summary tables

Simulation summary output functions.

tmhp.simulation_summary.print_simulation_summary(df, simulation_time_step, dV_ou_a_design)[source]

Print a comprehensive summary of simulation results.

Parameters:
  • df (pd.DataFrame) – Simulation result DataFrame.

  • simulation_time_step (int) – Time step [s].

  • dV_ou_a_design (float) – Design airflow rate of outdoor unit [m3/s].

Return type:

None