bloc.io.mechanisms#
Loading Cantera mechanisms: caches, and cheap phases built from them.
Everything here answers “give me a phase for this mechanism file” – which is
file IO plus Cantera’s parser, not thermodynamics, hence bloc.io rather
than bloc.chem. What callers do with the resulting phase (state
lookups, property queries) stays in bloc.chem.state.
The cost being managed here is a large mechanism’s parse. On
CRECK_2003_TOT_HT_SOOT_kinetics_transport.yaml (452 species, 24041
reactions) a full ct.Solution is 6.5 s, reading its species alone is 3.6 s,
and reading only the species: section is 0.46 s – see
tests.io.test_mechanism_read_paths, which compares every route on that
file. Three layers of reuse sit on top: this module’s two caches, and the
species_from argument of build_lite_solution() for callers that
already hold a phase.
Attributes#
Classes#
A |
Functions#
|
Get or create a cached Solution object for a mechanism. |
|
Return the mechanism gas was built from, or None when it cannot be told. |
|
|
|
Return a fresh, independent |
|
Construct a fresh thermo(+transport) |
Module Contents#
- bloc.io.mechanisms.MECHANISM_SOLUTION_CACHE: dict[str, cantera.Solution]#
- bloc.io.mechanisms.get_cached_solution(mechanism_source)#
Get or create a cached Solution object for a mechanism.
This function provides centralized access to the MECHANISM_SOLUTION_CACHE, creating new Solution objects only when needed and reusing existing ones for performance optimization.
- Parameters:
mechanism_source (
str) – The mechanism source (e.g., ‘gri30.yaml’ or path to mechanism file)- Returns:
Cached Solution object for the mechanism
- Return type:
ct.Solution
Examples
>>> gas = get_cached_solution("gri30.yaml") >>> gas.TPX = 1000, ct.one_atm, {"H2": 1.0} >>> print(gas.T) 1000.0
- class bloc.io.mechanisms.BlocSolution#
Bases:
cantera.SolutionA
ct.Solutionderived from a parsed mechanism that remembers which one.Cantera’s
sourceis the file a phase was read from – and only that: a phase assembled from species/reaction objects reports"custom parts", a reactor-cloned one"<unknown>". Bloc keys a lot of behaviour on the mechanism (cached lookups, species-basis mixing, KPI helpers), so phases it derives carry the path here; read it throughmechanism_path_of().- mechanism_path: str#
- bloc.io.mechanisms.mechanism_path_of(gas)#
Return the mechanism gas was built from, or None when it cannot be told.
The stamped
BlocSolution.mechanism_pathfirst (any phase built bynew_solution()/build_lite_solution(), or by Boulder’s factory), else asourcethat names a file or a bare Cantera data name. None for a cloned or otherwise anonymous phase – callers that need the mechanism userequire_mechanism_path()for a clear error instead ofct.Solution("<unknown>")failing deep inside Cantera.
- bloc.io.mechanisms.require_mechanism_path(gas)#
mechanism_path_of(), raisingValueErrorwhen it is unknown.
- bloc.io.mechanisms.new_solution(mechanism_source, *, kinetics=True, transport=False)#
Return a fresh, independent
ct.Solutionfor mechanism_source, cheaply.The file is parsed once per process (
get_cached_solution()); this installs its parsed species and reactions into a new phase. OnCRECK_2003_TOT_HT_SOOT_kinetics_transport.yaml(452 species, 24041 reactions) that is 0.36 s against 5-8 s forct.Solution(file)and 2.6 s forReactor(gas, clone=True), with identical thermo andnet_production_rates(to the last digit) and an independent state.kinetics=Falseleaves the reactions out (a state holder, ~1 ms) – never integrate chemistry on such a phase.transport=False(default) skips the transport fit, which is the dominant per-object cost (~3 s on that mechanism) and only matters to callers reading viscosity or thermal conductivity. The phase name is copied socantera.Quantitymixes the result with phases loaded from the file, andmechanism_pathis stamped somechanism_path_of()still answers.
- bloc.io.mechanisms.build_lite_solution(mechanism_source, transport=True, species_from=None)#
Construct a fresh thermo(+transport)
ct.Solutionwith no reactions.For a large mechanism, most of the memory (and load time) a
ct.Solutioncosts is reaction-kinetics data: onCRECK_2003_TOT_HT_SOOT_kinetics_transport.yaml(452 species, 24041 reactions), a fullct.Solutionis ~199 MB versus ~21 MB here – about a 90% reduction (see issue #331). Use this instead ofct.Solution(mechanism_source)wherever the object is only used to hold a thermodynamic/transport state (settingTPX/TPYand reading things like density, cp_mass, viscosity, thermal_conductivity, elemental fractions, heating values, or forct.Quantitymixing) rather than to integrate chemistry.The returned object has
n_reactions == 0. Calling anything that requires active kinetics (net_production_rates, advancing aReactorNetbuilt on it, etc.) will not raise, but will silently give a physically meaningless answer (no reactions to produce a rate) – never use it for that.Pass
transport=Falsewhen the phase will only be asked for thermodynamic properties. Fitting a mixture-averaged transport model is the cost of building a phase for a large mechanism – 2149 ms against 0.7 ms onCRECK_2003_TOT_HT_SOOT_kinetics_transport.yaml(452 species), a factor of 3000 – and it buys nothing for a caller that reads density, cp, elemental fractions or heating values. The result then reportstransport_model == "none", so a caller that does ask forviscosity/thermal_conductivitygets a loudNotImplementedErrorrather than a wrong number; the two places in Bloc that read them (bloc.chem.heat_transfer()and the tube-furnace recorder) check for"none"first anyway.Pass
species_fromwhen the caller already holds a phase for the same mechanism – a solved reactor’s phase, say. Only its species definitions are borrowed, never its state, so the result is still a fresh, independently mutable phase, and several can be built from one such phase and given different states. They are already parsed, so the new phase costs ~0.04 ms against seconds for a file, and the mechanism file is never opened.Borrowed only when that phase’s mechanism (
mechanism_path_of()) matches mechanism_source – stages either side of amechanism_switchlegitimately carry different species, and taking the wrong set would not raise, it would answer wrongly – and, whentransportis requested, only when it carries a transport model of its own: a cloned reservoir phase reports"none"and would silently strip viscosity and thermal conductivity from the result. Either check failing falls back to reading the file, sospecies_fromis an optimisation, never a requirement.A phase built this way mixes with one built from the file:
Quantityaddition accepts them and returns the same state to the last digit. It is building a phase from a file’s phase definition that is incompatible with both.Always builds a new, independent object (
new_solution()is the kinetics-carrying counterpart). The result is aBlocSolution, somechanism_path_of()still names the mechanism although Cantera’ssourcedoes not.Two phases from this function (same mechanism) can be added as
cantera.Quantityobjects and give the same mixed state as two fullct.Solution``s would. Phases built by *different* routes cannot: ``Quantity.__add__compares phase definitions, not species lists, so a mixing chain must build every side the same way.