bloc.chem.conservation#

Post-solve mass and energy conservation checks, per physical node.

These checks assume the solved network represents a steady operating point: for a node with no accumulation, mass and energy flowing in must equal mass and energy flowing out. A node that fails to close is a signal worth surfacing – it may be a real accounting bug, a mis-specified connection (e.g. mismatched MassFlowController rates), or – for residence-time-sized reactors such as ContinuousMixingReactor – a sign that the stage has not actually reached steady state yet (advanced for only ~1 nominal residence time, still accumulating mass/energy).

Pure boundary nodes (a source Reservoir with no inlet, or a terminal sink with no outlet) are excluded from the pass/fail verdict: conservation is only meaningful for nodes with mass flow on both sides.

Attributes#

Exceptions#

ConservationCheckFailed

Raised when one or more nodes fail mass/energy conservation.

Classes#

NodeConservationResult

Mass and energy balance for one physical node.

Functions#

check_mass_conservation(sim[, ...])

Per-node mass balance: sum(mdot_in) vs sum(mdot_out), in kg/s.

run_conservation_checks(sim[, ...])

Run mass + energy conservation checks at every physical node.

log_conservation_report(results[, rtol, log])

Log a per-node conservation summary; return True iff every node closes.

failing_nodes(report[, rtol])

Non-boundary nodes that fail either the mass or the energy check.

require_conservation_ok(report[, rtol, interactive])

Raise ConservationCheckFailed if any node fails to close.

conservation_report_to_dicts(report)

JSON-safe serialization of a report, for caching/bundle storage.

Module Contents#

bloc.chem.conservation.logger#
bloc.chem.conservation.DEFAULT_RTOL = 0.005#
class bloc.chem.conservation.NodeConservationResult#

Mass and energy balance for one physical node.

node: str#
mass_in_kg_s: float#
mass_out_kg_s: float#
energy_in_kw: float#
energy_out_kw: float#
property is_boundary: bool#

True for a pure source/sink (mass flow on only one side).

property mass_imbalance_rel: float#
property energy_imbalance_rel: float#
mass_closes(rtol=DEFAULT_RTOL)#
energy_closes(rtol=DEFAULT_RTOL)#
bloc.chem.conservation.check_mass_conservation(sim, connection_mass_flows_kg_s=None, connection_endpoints=None)#

Per-node mass balance: sum(mdot_in) vs sum(mdot_out), in kg/s.

Mirrors collect_energy_flows()’s two data sources: connection-based (from a solved SimulationResult) when available, else the live network’s .outlets (mass_flow_rate / downstream).

Parameters:

sim (cantera.ReactorNet) – The solved reactor network.

Returns:

{node_name: {"mdot_in_kg_s": ..., "mdot_out_kg_s": ...}}.

Return type:

dict

bloc.chem.conservation.run_conservation_checks(sim, connection_mass_flows_kg_s=None, connection_endpoints=None)#

Run mass + energy conservation checks at every physical node.

Does not raise or filter by tolerance itself – see NodeConservationResult.mass_closes() / energy_closes (or log_conservation_report()) to turn this into a pass/fail verdict.

bloc.chem.conservation.log_conservation_report(results, rtol=DEFAULT_RTOL, log=None)#

Log a per-node conservation summary; return True iff every node closes.

Passing (and boundary) nodes log at debug level; a failing node logs a warning with its computed imbalance, so it shows up in normal operation without configuring debug logging.

bloc.chem.conservation.failing_nodes(report, rtol=DEFAULT_RTOL)#

Non-boundary nodes that fail either the mass or the energy check.

exception bloc.chem.conservation.ConservationCheckFailed(failing)#

Bases: RuntimeError

Raised when one or more nodes fail mass/energy conservation.

Used to hard-block Calculation Note export when running headless (a CLI script, CI): shipping an engineering deliverable built from a non-physical solve with no one watching is worse than refusing to export it. In an interactive GUI session, require_conservation_ok() is called with interactive=True instead and never raises this – the failure is already visible via log_conservation_report() and the per-node "conservation" entry (see BlocConverter in bloc/core/converter.py), and a human can judge whether to proceed. Carries the failing NodeConservationResult entries (.failing) for callers that want to format their own message instead of this one.

failing#
bloc.chem.conservation.require_conservation_ok(report, rtol=DEFAULT_RTOL, *, interactive=False)#

Raise ConservationCheckFailed if any node fails to close.

interactive=True (a human is watching a live GUI session, as opposed to a headless CLI/CI run – see BLOC_NO_GUI in bloc/reporting/orchestrator_stone.py) downgrades this to a no-op: the failure was already surfaced via log_conservation_report(), and hard-blocking the export mid-session would be more disruptive than informative when someone can just look at the warning and decide. The default (False) keeps the hard-block for unattended callers.

bloc.chem.conservation.conservation_report_to_dicts(report)#

JSON-safe serialization of a report, for caching/bundle storage.