bloc.core#

Bloc runtime — CLI, converter, and runner.

Submodules#

Classes#

BlocConverter

Cantera converter pre-loaded with Bloc Design* reactors and hooks.

BlocRunner

Orchestrator for the full YAML-to-SimulationResult pipeline, Bloc flavour.

Functions#

main([argv])

Entry point for the bloc console script.

Package Contents#

bloc.core.main(argv=None)#

Entry point for the bloc console script.

class bloc.core.BlocConverter(mechanism=None, plugins=None)#

Bases: boulder.cantera_converter.DualCanteraConverter

Cantera converter pre-loaded with Bloc Design* reactors and hooks.

Parameters:
  • mechanism – Raw mechanism name/path (not yet path-resolved). Resolution is performed by resolve_mechanism during construction.

  • plugins – Optional pre-built plugin container. Bloc solver plugins are registered into this container (idempotent) regardless of whether it is provided or freshly created.

SCRIPT_EMITTER_CLASS#
post_build(config)#

Run post-build hooks, but fail-fast on equipment-envelope violations.

Boulder’s default post_build() downgrades every hook exception to a warning (tolerant to optional hooks). Bloc keeps that tolerance but re-raises PBR100EnvelopeError so a supplier model configured outside its nameplate envelope hard-fails the build instead of silently degrading.

resolve_mechanism(name)#

Resolve name using Bloc’s mechanism search hierarchy.

Called from every site that turns a mechanism string into a ct.Solution — both the top-level default mechanism (in __init__) and per-node mechanism overrides (in _get_gas_for_mech). If get_mechanism_path cannot locate the file, returns name unchanged so Cantera can handle bare built-in names (e.g. "gri30.yaml").

script_load_lines(config_path, plan=None)#

Emit a Bloc-aware Cantera-native staged-solve script.

The generated file uses module-level reactors / connections / walls registries, direct Design* construction, and named network_<stage>.advance(...) calls — no BlocConverter shell.

mechanism = 'gri30.yaml'#
plugins#
reactors: Dict[str, cantera.ReactorBase]#
reactor_meta: Dict[str, Dict[str, Any]]#
connections: Dict[str, cantera.FlowDevice]#
walls: Dict[str, Any]#
network: cantera.ReactorNet | None = None#
code_lines: List[str] = []#
last_network: cantera.ReactorNet | None = None#
parse_composition(comp_str)#

Parse a "species:value,species:value,..." composition string.

A handful of real mechanisms (e.g. n-heptane-NUIG-2016.yaml) have species names that themselves contain a literal comma (C6H101-3,3), so naively splitting on every , before every : misparses them. Values are always plain floats, so a fragment is only a complete entry once the text after its last : parses as one; until then the next comma-separated fragment is folded into the same (comma-bearing) name.

set_reactor_volume(reactor, props, reactor_id)#

Set reactor volume if specified in properties.

create_reactor_from_node(node, gas_for_node)#

Instantiate a Cantera reactor from a normalized node dict.

Uses the plugin reactor builder registry when the type matches a registered custom builder; otherwise falls back to the standard Cantera reactor types.

Parameters:
  • node – Normalized node dict with id, type, and properties.

  • gas_for_node – The Solution carrying the initial state.

Returns:

The newly created reactor (not yet added to any network).

Return type:

ct.Reactor

build_connection(conn)#

Create and register one Cantera flow device or wall from a connection dict.

The connection is added to self.connections (for FlowDevice subtypes) or self.walls (for Wall).

Parameters:

conn – Normalized connection dict with id, type, source, target, and properties.

apply_flow_conservation()#

Resolve unset MFC flow rates via mass conservation, then reset tracking state.

Called after all connections for a network (or sub-network stage) have been built. MFCs without an explicit mass_flow_rate in the YAML config are resolved by enforcing steady-state mass conservation at each non-Reservoir reactor node. Resolved values are also appended to code_lines so the --download script reflects the actual flow rates.

Raises:

ValueError – Propagated from resolve_unset_flow_rates() if any flow rate cannot be uniquely determined.

build_isolated_reactor(node)#

Build a single reactor from one node, with no surrounding network.

Resolves the node’s mechanism, sets the gas state from its initial properties (temperature / pressure / composition / mass_composition, either top-level or under initial:), and delegates to create_reactor_from_node() so that energy, clone, volume and any other recognised properties are honoured exactly as in a full network build. This is the single source of truth for reactor construction for callers (e.g. parametric τ-sweeps) that drive their own ReactorNet instead of the staged solver.

Property values may be unit strings ("1273.15 K", "1 bar") or plain numbers; temperature/pressure are coerced to SI.

Parameters:

node – A node dict with id, type, and properties (the normalised form; STONE Kind: {...} blocks must be converted to type / properties by the caller first).

Returns:

The constructed reactor and the Solution carrying its initial state.

Return type:

(reactor, gas)

build_sub_network(stage_nodes, stage_connections, stage_mechanism, inlet_states, stage_id='', stage=None, pre_solve_hook=None)#

Build (and solve) a ReactorNet for one stage.

Reactors whose IDs appear in inlet_states are initialised from the provided Solution (upstream outlet, already mechanism-switched) instead of from the YAML properties.

Parameters:
  • stage_nodes – Normalized node dicts for this stage only.

  • stage_connections – Intra-stage normalized connection dicts.

  • stage_mechanism – Default kinetic mechanism for the stage.

  • inlet_states{node_id: ct.Solution} mapping inlet conditions for reactors that receive inter-stage flow.

  • stage_id – For logging/error messages.

  • stageStage dataclass; used to set the solve directive (advance_to_steady_state vs advance).

  • pre_solve_hook – Optional callback invoked with self after self.reactors/ self.connections are populated for this stage but before the solve dispatch runs. This is the only correct place to wire causal-layer bindings (e.g. apply_bindings_block) — calling them after this method returns means the solve already ran to completion with no schedule callbacks registered.

Returns:

network is the solved ReactorNet. stage_reactors is a {node_id: ct.ReactorBase} dict for this stage (a subset of self.reactors).

Return type:

(network, stage_reactors)

build_viz_network(all_connections, built_conn_ids=None)#

Build a visualization-only ReactorNet.

Uses all reactor objects already in self.reactors (which carry converged states after a staged solve) and adds any inter-stage connections that were not built during the per-stage solve.

The returned network is initialized with advance(0.0) so flow-device properties (mass_flow_rate) are ready for downstream reporting.

Parameters:
  • all_connections – The full list of normalized connection dicts (including inter-stage ones).

  • built_conn_ids – Set of connection IDs already built (intra-stage). Inter-stage connections not in this set will be created now.

Return type:

ct.ReactorNet

build_network(config, progress_callback=None)#

Build and solve the Cantera network through the staged solver.

normalize_config guarantees that the config has a top-level groups section (synthesising a single default group when the YAML does not declare one), so this method always delegates to solve_staged(). It builds one sub- ReactorNet per stage, solves each according to its solve_directive and returns a visualization ReactorNet with all reactors in their converged state. The Lagrangian trajectory is stored on self._staged_trajectory.

Parameters:
  • config – Normalised and validated configuration dict.

  • progress_callback – Optional (stage_id: str, n_done: int, n_total: int) -> None called after each stage completes. Forwarded to solve_staged().

run_streaming_simulation(simulation_time=10.0, time_step=1.0, progress_callback=None, config=None)#

Run simulation with streaming progress updates.

finalize_results(times, reactors_series)#

Finalize simulation results with post-processing.

build_network_and_code(config)#

Build+solve the network and return (network, results, code_str).

build_network now solves the whole network through the staged solver, so results only reports what the staged path already produced: the converged per-reactor states (as a single-time-point SolutionArray) and any scalars stored by post-build hooks.

class bloc.core.BlocRunner(config, *, plugins=None, config_path=None)#

Bases: boulder.runner.BoulderRunner

Orchestrator for the full YAML-to-SimulationResult pipeline, Bloc flavour.

Differences from BoulderRunner:

  • Uses BlocConverter as the converter.

  • normalize rejects deprecated mechanism_reac / mechanism_torch keys.

  • report_metadata and schema_entry ensure Bloc reactor schemas are registered in Boulder’s global _SCHEMA_REGISTRY before lookup.

converter_class#
classmethod load(path)#

Load raw config with Bloc from: inheritance resolved.

Bloc YAML files may use a from: <parent>.yaml key for STONE overlay inheritance. Boulder’s load_config_file is a plain yaml.safe_load and passes the raw dict (including from:) straight to normalize_config, which then rejects from: as an unknown top-level key.

This override resolves the full inheritance chain via load_yaml_with_inheritance() before handing the merged dict to Boulder’s normaliser. Files without a from: key are loaded identically to the base implementation.

classmethod normalize(cfg)#

Normalise config and inject Bloc-specific defaults.

Extends Boulder’s normalize_config by:

  1. Rejecting deprecated mechanism_reac / mechanism_torch fields in phases.gas (removed in the STONE architecture).

  2. Defaulting metadata.gui_app_title to "Bloc" and metadata.gui_app_version to the installed version so the GUI header shows the version once (title + version chip) rather than twice. Explicitly set values are never overwritten.

classmethod report_metadata(config, *, explicit_kind=None)#

Return Calculation-Note reporting metadata for config.

Ensures Bloc’s reactor schemas are registered in Boulder’s global _SCHEMA_REGISTRY before delegating to boulder.schema_registry.get_report_metadata_for_config().

Parameters:
  • config – Normalised config dict.

  • explicit_kind – Override the reactor kind used for schema lookup (forwarded as explicit_kind= to get_report_metadata_for_config).

classmethod schema_entry(kind)#

Return the ReactorSchemaEntry for kind.

Ensures Bloc’s reactor schemas are registered before lookup.

config#
config_path = None#
plugins = None#
converter: boulder.cantera_converter.DualCanteraConverter | None = None#
network: boulder.staged_network.StagedReactorNet | cantera.ReactorNet | None = None#
results: Dict[str, Any] | None = None#
code: str | None = None#
result: boulder.simulation_result.SimulationResult | None = None#
property scopes: Dict[str, Any]#

Return scope data as a dict[str, pandas.DataFrame].

Each key is a scope variable path (e.g. nodes.r1.T); each value is a DataFrame with columns t (seconds) and value.

Returns an empty dict if no scopes were declared or if build() has not been called yet.

property exposed_inputs: Dict[str, Any]#

Return signals that are not bound to any internal network target.

These are the FMI-friendly input variables — signals that a co- simulation master (FMU) could override at each doStep.

A signal is considered exposed (unbound) if its id does not appear as a source in any entry of the bindings: block.

Returns a dict[signal_id, signal_spec] where each value is the raw spec dict from the signals: block. Returns an empty dict if no signals are declared or if build() has not been called yet.

Note

This is the Phase E FMU data-shape contract. No FMU code is generated here; this property just exposes the data structure that boulder.fmi would use. See FMI_FMU_EXPORT.md.

classmethod from_yaml(path)#

Load, normalise, and validate a YAML file, returning a runner instance.

classmethod validate(cfg)#

Validate a normalised config, raising on schema errors.

build_stage_graph()#

Parse the config into a topologically-sorted StageExecutionPlan.

Pure config parsing — no Cantera objects are created. Stage IDs and node lists are available immediately after this call.

new_trajectory()#

Return a fresh, empty LagrangianTrajectory.

solve_stage(plan, stage, inlet_states, trajectory, stream_reservoirs=True, _stream_node_dicts=None, _stream_conns_by_stage=None, interface_reservoirs=None, _iface_node_dicts=None, _iface_conns_by_stage=None)#

Build and solve one stage, updating inlet_states and trajectory in place.

This is exactly one iteration of the solve_staged() loop body. Calling it once per stage in topological order is equivalent to calling solve_staged() on the full plan.

Parameters:
  • plan – Full execution plan (needed for mechanism-switch target lookup).

  • stage – The Stage to solve.

  • inlet_states – Mutable {node_id: ct.Solution} dict. Populated by upstream stages; consumed here to initialise inter-stage-inlet reactors. Updated in place with this stage’s outlet states.

  • trajectory – Accumulates per-stage SolutionArray segments.

  • stream_reservoirs – When True (default), use synthesised stream-point reservoirs and real MFCs on the downstream side of each stage boundary.

  • _stream_node_dicts – Pre-built mapping {stream_point_id: node_dict}. Populated by build().

  • _stream_conns_by_stage – Pre-built mapping {stage_id: [conn_dict, ...]}. Populated by build().

  • interface_reservoirs – Deprecated alias for stream_reservoirs.

  • _iface_node_dicts – Deprecated alias for _stream_node_dicts.

  • _iface_conns_by_stage – Deprecated alias for _stream_conns_by_stage.

Examples

build_viz_network(plan, trajectory, stream_reservoirs=True, interface_reservoirs=None)#

Assemble the full visualization ReactorNet from all converged states.

Connects all inter-stage connections into one ReactorNet (not solved again — just for visualization and Sankey generation). Sets self.network and trajectory.viz_network.

Parameters:
  • plan – Execution plan (provides inter-stage connection list).

  • trajectory – Updated in place: trajectory.viz_network is set.

  • stream_reservoirs – When True (default), the original inter-stage connection IDs are added to built_conn_ids so that build_viz_network does not create a direct source→target MFC that bypasses the stream-point reservoir.

  • interface_reservoirs – Deprecated alias for stream_reservoirs.

Examples

run_continuation(continuation=None)#

Execute a parameter continuation sweep as defined in continuation: STONE block.

Wraps solve_stage() in an outer loop, mutating the target parameter between solves and collecting the trajectory. Equivalent to the manual loop in combustor.py:

while combustor.T > 500:
    sim.solve_steady()
    inlet.mass_flow_rate *= 0.9
Parameters:

continuation

Parsed continuation: dict from the STONE YAML. When None the method reads self.config.get("continuation"). Structure:

parameter: connections.<id>.mass_flow_rate
update:
  multiply: 0.9          # or set: <val> or list: [...]
until:
  reactor_T_below: 500   # or reactor_T_above: <K>
  max_iters: 200

Returns:

For chaining. After this call self.network is the visualization network built from the last converged iteration.

Return type:

self

Raises:

ValueError – If continuation is missing required keys or parameter path cannot be resolved.

build()#

Instantiate the converter, build and solve the staged network.

Internally calls build_stage_graph(), solve_stage() for each stage, and build_viz_network() — the same sequence emitted in the downloadable Python script.

Returns self for chaining. After this call:

solve()#

Build (if not done) and produce a typed SimulationResult.

After this call self.result is a SimulationResult and self.network is the same StagedReactorNet facade as self.result.network — i.e. self.network is self.result.network.

Returns self for chaining.

run_headless(*, download_path=None, simulate=True, end_time=None, dt=None)#

Solve the network and optionally write a downloadable Python script.

This is the single source of truth for the --headless --download CLI flow. Custom BoulderRunner subclasses use the same method so the generated scripts stay on one code path (same converter wiring).

Parameters:
  • download_path – Path to write the standalone Python script. Skipped when None.

  • simulate – When True and end_time is set, run run_streaming_simulation to append the time-advance section to the generated code.

  • end_time – Simulation end time in seconds (from settings.end_time in the YAML).

  • dt – Simulation time step in seconds (from settings.dt in the YAML).