Reaction
- class thermosteam.reaction.Reaction(reaction, reactant, X, chemicals=None, basis='mol', *, phases=None, check_mass_balance=False, check_atomic_balance=False, correct_atomic_balance=False, correct_mass_balance=False)[source]
Create a Reaction object which defines a stoichiometric reaction and conversion. A Reaction object is capable of reacting the material flow rates of a
thermosteam.Stream
object.- Parameters
reaction (dict or str) – A dictionary of stoichiometric coefficients or a stoichiometric equation written as: i1 R1 + … + in Rn -> j1 P1 + … + jm Pm
reactant (str) – ID of reactant compound.
X (float) – Reactant conversion (fraction).
chemicals=None (Chemicals, defaults to settings.chemicals.) – Chemicals corresponing to each entry in the stoichiometry array.
basis='mol' ({'mol', 'wt'}) – Basis of reaction.
check_mass_balance=False (bool) – Whether to check if mass is not created or destroyed.
correct_mass_balance=False (bool) – Whether to make sure mass is not created or destroyed by varying the reactant stoichiometric coefficient.
check_atomic_balance=False (bool) – Whether to check if stoichiometric balance by atoms cancel out.
correct_atomic_balance=False (bool) – Whether to correct the stoichiometry according to the atomic balance.
Notes
A reaction object can react either a stream or an array. When a stream is passed, it reacts either the mol or mass flow rate according to the basis of the reaction object. When an array is passed, the array elements are reacted regardless of what basis they are associated with.
Warning
Negative conversions and conversions above 1.0 are fair game (allowed), but may lead to odd/infeasible values when reacting a stream.
Examples
Electrolysis of water to molecular hydrogen and oxygen:
>>> import thermosteam as tmo >>> chemicals = tmo.Chemicals(['H2O', 'H2', 'O2'], cache=True) >>> tmo.settings.set_thermo(chemicals) >>> reaction = tmo.Reaction('2H2O,l -> 2H2,g + O2,g', reactant='H2O', X=0.7) >>> reaction.show() # Note that the default basis is by 'mol' Reaction (by mol): stoichiometry reactant X[%] H2O,l -> H2,g + 0.5 O2,g H2O,l 70.00 >>> reaction.reactant # The reactant is a tuple of phase and chemical ID ('l', 'H2O') >>> feed = tmo.Stream('feed', H2O=100) >>> feed.phases = ('g', 'l') # Gas and liquid phases must be available >>> reaction(feed) # Call to run reaction on molar flow >>> feed.show() # Notice how 70% of water was converted to product MultiStream: feed phases: ('g', 'l'), T: 298.15 K, P: 101325 Pa flow (kmol/hr): (g) H2 70 O2 35 (l) H2O 30
Let’s change to a per ‘wt’ basis:
>>> reaction.basis = 'wt' >>> reaction.show() Reaction (by wt): stoichiometry reactant X[%] H2O,l -> 0.112 H2,g + 0.888 O2,g H2O,l 70.00
Although we changed the basis, the end result is the same if we pass a stream:
>>> feed = tmo.Stream('feed', H2O=100) >>> feed.phases = ('g', 'l') >>> reaction(feed) # Call to run reaction on mass flow >>> feed.show() # Notice how 70% of water was converted to product MultiStream: feed phases: ('g', 'l'), T: 298.15 K, P: 101325 Pa flow (kmol/hr): (g) H2 70 O2 35 (l) H2O 30
If chemicals phases are not specified, Reaction objects can react a any single phase Stream object (regardless of phase):
>>> reaction = tmo.Reaction('2H2O -> 2H2 + O2', reactant='H2O', X=0.7) >>> feed = tmo.Stream('feed', H2O=100, phase='g') >>> reaction(feed) >>> feed.show() Stream: feed phase: 'g', T: 298.15 K, P: 101325 Pa flow (kmol/hr): H2O 30 H2 70 O2 35
Alternatively, it’s also possible to react an array (instead of a stream):
>>> import numpy as np >>> array = np.array([100., 0. , 0.]) >>> reaction(array) >>> array array([30., 70., 35.])
Reaction objects with the same reactant can be added together:
>>> tmo.settings.set_thermo(['Glucose', 'Ethanol', 'H2O', 'O2', 'CO2']) >>> fermentation = tmo.Reaction('Glucose + O2 -> Ethanol + CO2', reactant='Glucose', X=0.7) >>> combustion = tmo.Reaction('Glucose + O2 -> H2O + CO2', reactant='Glucose', X=0.2) >>> mixed_reaction = fermentation + combustion >>> mixed_reaction.show() Reaction (by mol): stoichiometry reactant X[%] Glucose + O2 -> 0.778 Ethanol + 0.222 H2O + CO2 Glucose 90.00
Note how conversions are added and the stoichiometry rescales to a per reactant basis. Conversly, reaction objects may be substracted as well:
>>> combustion = mixed_reaction - fermentation >>> combustion.show() Reaction (by mol): stoichiometry reactant X[%] Glucose + O2 -> H2O + CO2 Glucose 20.00
When a Reaction object is multiplied (or divided), a new Reaction object with the conversion multiplied (or divided) is returned:
>>> combustion_multiplied = 2 * combustion >>> combustion_multiplied.show() Reaction (by mol): stoichiometry reactant X[%] Glucose + O2 -> H2O + CO2 Glucose 40.00 >>> fermentation_divided = fermentation / 2 >>> fermentation_divided.show() Reaction (by mol): stoichiometry reactant X[%] Glucose + O2 -> Ethanol + CO2 Glucose 35.00
- adiabatic_reaction(stream)[source]
React stream material adiabatically, accounting for the change in enthalpy due to the heat of reaction.
Examples
Note how the stream temperature changed after the reaction due to the heat of reaction:
>>> import thermosteam as tmo >>> chemicals = tmo.Chemicals(['H2', 'O2', 'H2O'], cache=True) >>> tmo.settings.set_thermo(chemicals) >>> reaction = tmo.Reaction('2H2 + O2 -> 2H2O', reactant='H2', X=0.7) >>> s1 = tmo.Stream('s1', H2=10, O2=20, H2O=1000, T=373.15, phase='g') >>> s2 = tmo.Stream('s2') >>> s2.copy_like(s1) # s1 and s2 are the same >>> s1.show() # Before reaction Stream: s1 phase: 'g', T: 373.15 K, P: 101325 Pa flow (kmol/hr): H2 10 O2 20 H2O 1e+03
>>> reaction.show() Reaction (by mol): stoichiometry reactant X[%] H2 + 0.5 O2 -> H2O H2 70.00
>>> reaction(s1) >>> s1.show() # After isothermal reaction Stream: s1 phase: 'g', T: 373.15 K, P: 101325 Pa flow (kmol/hr): H2 3 O2 16.5 H2O 1.01e+03
>>> reaction.adiabatic_reaction(s2) >>> s2.show() # After adiabatic reaction Stream: s2 phase: 'g', T: 421.6 K, P: 101325 Pa flow (kmol/hr): H2 3 O2 16.5 H2O 1.01e+03
- property dH
Heat of reaction at given conversion. Units are in either J/mol-reactant or J/g-reactant; depending on basis.
Warning
Latents heats of vaporization are not accounted for; only heats of formation are included in this term. Note that heats of vaporization are temperature dependent and cannot be calculated using a Reaction object.
- property X
[float] Reaction converion as a fraction.
- property stoichiometry
[array] Stoichiometry coefficients.
- property istoichiometry
[ChemicalIndexer] Stoichiometry coefficients.
- property reactant
[str] Reactant associated to conversion.
- property MWs
[1d array] Molecular weights of all chemicals [g/mol].
- property basis
{‘mol’, ‘wt’} Basis of reaction
- correct_mass_balance(variable=None)[source]
Make sure mass is not created or destroyed by varying the reactant stoichiometric coefficient.
- correct_atomic_balance(constants=None)[source]
Correct stoichiometry coffecients to satisfy atomic balance.
- Parameters
constants (str, optional) – IDs of chemicals for which stoichiometric coefficients are held constant.
Examples
Balance glucose fermentation to ethanol:
>>> import thermosteam as tmo >>> from biorefineries import lipidcane as lc >>> tmo.settings.set_thermo(lc.chemicals) >>> fermentation = tmo.Reaction('Glucose + O2 -> Ethanol + CO2', ... reactant='Glucose', X=0.9) >>> fermentation.correct_atomic_balance() >>> fermentation.show() Reaction (by mol): stoichiometry reactant X[%] Glucose -> 2 Ethanol + 2 CO2 Glucose 90.00
Balance methane combustion:
>>> combustion = tmo.Reaction('CH4 + O2 -> Water + CO2', ... reactant='CH4', X=1) >>> combustion.correct_atomic_balance() >>> combustion.show() Reaction (by mol): stoichiometry reactant X[%] 2 O2 + CH4 -> 2 Water + CO2 CH4 100.00
Balance electrolysis of water (with chemical phases specified):
>>> electrolysis = tmo.Reaction('H2O,l -> H2,g + O2,g', ... chemicals=tmo.Chemicals(['H2O', 'H2', 'O2']), ... reactant='H2O', X=1) >>> electrolysis.correct_atomic_balance() >>> electrolysis.show() Reaction (by mol): stoichiometry reactant X[%] H2O,l -> H2,g + 0.5 O2,g H2O,l 100.00
Note that if the reaction is underspecified, there are infinite ways to balance the reaction and a runtime error is raised:
>>> rxn_underspecified = tmo.Reaction('CH4 + Glucose + O2 -> Water + CO2', ... reactant='CH4', X=1) >>> rxn_underspecified.correct_atomic_balance() Traceback (most recent call last): RuntimeError: reaction stoichiometry is underspecified; pass the `constants` argument to the `<Reaction>.correct_atomic_balance` method to specify which stoichiometric coefficients to hold constant
Chemical coefficients can be held constant to prevent this error:
>>> rxn_underspecified = tmo.Reaction('CH4 + Glucose + O2 -> Water + CO2', ... reactant='CH4', X=1) >>> rxn_underspecified.correct_atomic_balance(['Glucose', 'CH4']) >>> rxn_underspecified.show() Reaction (by mol): stoichiometry reactant X[%] Glucose + 8 O2 + CH4 -> 8 Water + 7 CO2 CH4 100.00