4  Reactor models

aka majordome_engineering.reactor

4.1 Warnings

Module warnings can be controlled throught the following attributes:

Notetoggle_reactor_warnings
toggle_reactor_warnings(**kwargs : Any): -> None:

Reverse truth value of warning flags.


Parameters
toggle_non_key_value : bool | None = None
    
If true, reverse truth value of warning about non key-value composition.
toggle_missing_species_name : bool | None = None
    
If true, reverse truth value of warning about missing species name.
toggle_unknown_species : bool | None = None
    
If true, reverse truth value of warning about unknown species.

4.2 Utilities

Let’s start by creating a standard Cantera solution:

solution = ct.Solution("airish.yaml")
solution.TPY = 273.15, ct.one_atm, "N2: 0.78 O2: 0.21, AR: 0.01"

Conversion of composition strings with name filtering is available:

Notecomposition_to_dict
composition_to_dict(
    Y : str,
    species_names : list[str] = []
    ) -> dict[str, float]:

Convert a Cantera composition string to dictionary.


Parameters
Y : str
    
Composition specification string, e.g. “O2: 1, N2: 3”.
species_names : list[str] = []
    
List of valid species names for validation. If provided, only species in this list will be included in the output dictionary. If not provided, all species will be included.
composition_to_dict("O2: 1, teste: 1", solution.species_names)
{'O2': 1.0}

It is also possible to set unit composition with species names; if you do not provide a validation list, all are are kept:

composition_to_dict("O2, N2, hello")
{'O2': 1.0, 'N2': 1.0, 'hello': 1.0}

When working with arrays, take care not to end up in the following situation:

composition_to_array(", teste: 1", solution.species_names)
array([0., 0., 0.])

There is also a helper for generating inputs for use with tabulate.tabulate:

Notesolution_report
solution_report(
    sol : cantera.composite.Solution | cantera.composite.Quantity,
    specific_props : bool = True,
    composition_spec : str = 'mass',
    selected_species : list[str] = [],
    **kwargs : Any
    ) -> list[tuple[str, str, typing.Any]]:

Generate a solution report for tabulation.


Parameters
sol : cantera.composite.Solution | cantera.composite.Quantity
    
Cantera solution object for report generation.
specific_props : bool = True
    
If true, add specific heat capacity and enthalpy.
composition_spec : str = 'mass'
    
Composition units specification, mass or mole.
selected_species : list[str] = []
    
Selected species to display; return all if a composition specification was provided.
show_mass : bool  = 'False'
    
If true, add mass of quantity to report if sol is a ct.composite.Quantity.
data = solution_report(solution, specific_props=True,
                       composition_spec="mass", selected_species=[])
print(tabulate(data))
----------------------  --------  ------------
Temperature             K            273.15
Pressure                Pa        101325
Density                 kg/m³          1.28735
Specific enthalpy       J/(kg.K)  -25114.9
Specific heat capacity  J/(kg.K)    1004.95
mass: AR                -              0.01
mass: N2                -              0.78
mass: O2                -              0.21
----------------------  --------  ------------

Because sometimes Cantera lacks hard-copy utilities for certain classes, we provide simple wrappers that create new instances and set the state to the same of the source object. Nothing checked, nothing tested. A first of this kind is copy_solution, illustrated below:

Notecopy_solution
copy_solution(sol : Solution): -> Solution:

Makes a hard copy of a Solution object.


Parameters
sol : Solution
    
Solution to be copied.
newairs = copy_solution(solution)
newairs.TPX = 373.15, None, newairs.X
print(tabulate(solution_report(newairs)))
----------------------  --------  -------------
Temperature             K            373.15
Pressure                Pa        101325
Density                 kg/m³          0.942356
Specific enthalpy       J/(kg.K)   75902.1
Specific heat capacity  J/(kg.K)    1015.82
mass: AR                -              0.01
mass: N2                -              0.78
mass: O2                -              0.21
----------------------  --------  -------------
print(tabulate(solution_report(solution)))
----------------------  --------  ------------
Temperature             K            273.15
Pressure                Pa        101325
Density                 kg/m³          1.28735
Specific enthalpy       J/(kg.K)  -25114.9
Specific heat capacity  J/(kg.K)    1004.95
mass: AR                -              0.01
mass: N2                -              0.78
mass: O2                -              0.21
----------------------  --------  ------------

In addition to this, there is copy_quantity, which proves quite useful in establishing an algebra of mixtures.

Notecopy_quantity
copy_quantity(qty : Quantity): -> Quantity:

Makes a hard copy of a ct.composite.Quantity object.


Parameters
qty : Quantity
    
Quantity to be copied.
air = copy_solution(solution)

air1 = ct.Quantity(air, mass=1.0)
air1.TPX = 373.15, None, newairs.X

air2 = copy_quantity(air1)
air2.TPX = 273.15, None, air2.X

mixair = air1 + air2
air1.T, air2.T, mixair.T
(373.15, 273.15, 323.337485365699)

4.3 NormalFlowRate

Common daily work activity for the process engineer is to perform mass balances, but wait, …, gas flow rates are generally provided under normal conditions, and compositions may vary, so you need to compute normal densities first… whatever. This class provides a calculator wrapping a Cantera solution object so that your life gets easier.

NoteNormalFlowRate
NormalFlowRate(
    mech : str | pathlib.Path,
    *,
    X : str | dict[str, float] | None = None,
    Y : str | dict[str, float] | None = None,
    T_ref : float = 273.15,
    P_ref : float = 101325.0,
    name : str | None = None
    ) -> None:

Compute normal flow rate for a given composition.

This class makes use of the user defined state to create a function object that converts industrial scale flow rates in normal cubic meters per hour to kilograms per second. Nothing more, nothing less, it aims at helping the process engineer in daily life for this quite repetitive need when performing mass balances.


Parameters
mech : str | pathlib.Path
    
Path to Cantera mechanism used to compute mixture properties.
X : str | dict[str, float] | None = None
    
Composition specification in mole fractions. Notice that both X and Y are mutally exclusive keyword arguments.
Y : str | dict[str, float] | None = None
    
Composition specification in mass fractions. Notice that both X and Y are mutally exclusive keyword arguments.
T_ref : float = 273.15
    
Reference temperature of the system. If your industry does not use the same standard as the default values, and only in that case, please consider updating this keyword.
P_ref : float = 101325.0
    
Reference pressure of the system. If your industry does not use the same standard as the default values, and only in that case, please consider updating this keyword.
name : str | None = None
    
Name of phase in mechanism, if more than one are specified within the same Cantera YAML database file.

Its simples use case is as follows:

nfr = NormalFlowRate("airish.yaml")
print(f"Convert 1000 Nm³/h to {nfr(1000.0):.5f} kg/s")
Convert 1000 Nm³/h to 0.35903 kg/s

If the database file default composition does not suit you, no problems:

nfr = NormalFlowRate("airish.yaml", X="N2: 1")
print(f"Convert 1000 Nm³/h to {nfr(1000.0):.5f} kg/s")
Convert 1000 Nm³/h to 0.34718 kg/s

You can also print a nice report to inspect the details of internal state. For more, please check its API documentation.

print(nfr.report())
|------------------------|----------|--------------|
| Temperature            | K        |    273.15    |
| Pressure               | Pa       | 101325       |
| Density                | kg/m³    |      1.24985 |
| Specific enthalpy      | J/(kg.K) | -25864.9     |
| Specific heat capacity | J/(kg.K) |   1035.52    |
| mass: N2               | -        |      1       |

4.4 Plug-flow reactors

4.4.1 PlugFlowAxialSources

NotePlugFlowAxialSources
PlugFlowAxialSources(
    n_reactors : int,
    n_species : int
    ) -> None:

Provides a data structure for use with PlugFlowChainCantera.

Helper data class for use with the solution method loop of the plug-flow reactor implementation. It provides the required memory for storage of source terms distributed along the reactor.


Parameters
n_reactors : int
    
Number of reactors in chain.
n_species : int
    
Number of species in mechanism.
Q : NDArray[np.float64] = None
    
Array of external heat source [W].
m : NDArray[np.float64] = None
    
Array of axial mass source terms [kg/s].
h : NDArray[np.float64] = None
    
Array of enthalpy of axial mass source terms [J/kg].
Y : NDArray[np.float64, np.float64] = None
    
Array of mass fractions of axial mass source terms [-].

4.4.2 PlugFlowChainCantera

NotePlugFlowChainCantera
PlugFlowChainCantera(
    mechanism : str,
    phase : str,
    z : numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float64]],
    V : numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float64]],
    P : float = 101325.0,
    K : float = 1.0,
    smoot_flux : bool = False,
    cantera_steady : bool = True
    ) -> None:

Plug-flow reactor as a chain of 0-D reactors with Cantera.


Parameters
mechanism : str
    
Name or path to Cantera mechanism to be used.
phase : str
    
Name of phase to simulate (not inferred, even if a single is present!).
z : numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float64]]
    
Spatial coordinates of reactor cells [m].
V : numpy.ndarray[tuple[typing.Any, ...], numpy.dtype[numpy.float64]]
    
Volumes of reactor cells [m³].
P : float = 101325.0
    
Reactor operating pressure [Pa].
K : float = 1.0
    
Valve response constant (do not use unless simulation fails).
smoot_flux : bool = False
    
Apply a smoot transition function when internal stepping is performed; this is intended to avoid unphysical steady state approximations.
cantera_steady : bool = True
    
If true, use Cantera’ s advance_to_steady_state to solve problem; otherwise advance over meaninful time-scale of the problem.
Noteget_reactor_data
get_reactor_data(pfr : PlugFlowChainCantera): -> PlugFlowAxialSources:

Wrapper to allocate properly dimensioned solver data.


Parameters
pfr : PlugFlowChainCantera
    
Reactor for which data is to be allocated.