<jupyter_start><jupyter_text>Stoichiometric reactions Thermosteam provides array based objects that can model stoichiometric reactions given a conversion.  Single reaction Create a Reaction object based on the transesterification reaction:|Reaction|Reactant|% Converted||---|---|---||Lipid + 3Methanol -> 3Biodiesel + Glycerol|Lipid|90|<jupyter_code>import thermosteam as tmo
from biorefineries import lipidcane as lc

tmo.settings.set_thermo(lc.chemicals)
transesterification = tmo.Reaction(
    'TAG + Methanol -> Biodiesel + Glycerol', # Reaction
    correct_atomic_balance=True, # Corrects stoichiometric coefficients by atomic balance
    reactant='TAG', # Limiting reactant
    X=0.9, # Conversion
)
transesterification.show()
transesterification.chemicals
transesterification.stoichiometry
transesterification.reactant
transesterification.X
transesterification.dH # Heat of reaction J / mol-reactant (accounts for conversion); latent heats are not included<jupyter_output><empty_output><jupyter_text>When a Reaction object is called with a stream, it updates the material data to reflect the reaction:<jupyter_code>feed = tmo.Stream(TAG=100, Methanol=600)
print('BEFORE REACTION')
feed.show(N=100)

# React feed molar flow rate
transesterification(feed)

print('AFTER REACTION')
feed.show(N=100)<jupyter_output>BEFORE REACTION
Stream: s1
 phase: 'l', T: 298.15 K, P: 101325 Pa
 flow (kmol/hr): Methanol  600
                 TriOlein  100
AFTER REACTION
Stream: s1
 phase: 'l', T: 298.15 K, P: 101325 Pa
 flow (kmol/hr): Biodiesel  270
                 Methanol   330
                 Glycerol   90
                 TriOlein   10<jupyter_text>Let's change the basis of the reaction:<jupyter_code>transesterification.basis = 'wt'
transesterification.show()<jupyter_output>Reaction (by wt):
stoichiometry                                            reactant    X[%]
0.109 Methanol + TriOlein -> Biodiesel + 0.104 Glycerol  TriOlein   90.00<jupyter_text>Notice that the stoichiometry also adjusted. If we react a stream, we should see the same result, regardless of basis:<jupyter_code>feed = tmo.Stream(TAG=100, Methanol=600)
print('BEFORE REACTION')
feed.show(N=100)

# React feed molar flow rate
transesterification(feed)

print('AFTER REACTION')
feed.show(N=100)<jupyter_output>BEFORE REACTION
Stream: s2
 phase: 'l', T: 298.15 K, P: 101325 Pa
 flow (kmol/hr): Methanol  600
                 TriOlein  100
AFTER REACTION
Stream: s2
 phase: 'l', T: 298.15 K, P: 101325 Pa
 flow (kmol/hr): Biodiesel  270
                 Methanol   330
                 Glycerol   90
                 TriOlein   10<jupyter_text>The heat of reaction is now in units of J/kg-reactant:<jupyter_code>transesterification.dH # Accounts for conversion too<jupyter_output><empty_output><jupyter_text>Note that these reactions are carried out isothermally, but it is also possible to do so adiabatically:<jupyter_code>feed = tmo.Stream(Glucose=10, H2O=100, units='kg/hr')
print('BEFORE REACTION')
feed.show(N=100)

# React feed adiabatically (temperature should increase)
fermentation = tmo.Reaction('Glucose + O2 -> Ethanol + CO2',
                            reactant='Glucose',  X=0.9,
                            correct_atomic_balance=True)
fermentation.adiabatic_reaction(feed)

print('AFTER REACTION')
feed.show(N=100)<jupyter_output>BEFORE REACTION
Stream: s3
 phase: 'l', T: 298.15 K, P: 101325 Pa
 flow (kmol/hr): Water    5.55
                 Glucose  0.0555
AFTER REACTION
Stream: s3
 phase: 'l', T: 306.19 K, P: 101325 Pa
 flow (kmol/hr): Water    5.55
                 Ethanol  0.0999
                 Glucose  0.00555
                 CO2      0.0999<jupyter_text>Phases changes can be included in the reaction:<jupyter_code>fermentation = tmo.Reaction('Glucose,l -> Ethanol,l + CO2,g',
                            reactant='Glucose',  X=0.9,
                            correct_atomic_balance=True)
fermentation.show()
feed = tmo.Stream(Glucose=10, H2O=100, units='kg/hr')
feed.phases = 'gl'
print('BEFORE ADIABATIC REACTION')
feed.show(N=100)

# React feed adiabatically (temperature should increase)
fermentation.adiabatic_reaction(feed)

print('AFTER ADIABATIC REACTION')
feed.show(N=100)<jupyter_output>BEFORE ADIABATIC REACTION
MultiStream: s4
 phases: ('g', 'l'), T: 298.15 K, P: 101325 Pa
 flow (kmol/hr): (l) Water    5.55
                     Glucose  0.0555
AFTER ADIABATIC REACTION
MultiStream: s4
 phases: ('g', 'l'), T: 306.19 K, P: 101325 Pa
 flow (kmol/hr): (g) CO2      0.0999
                 (l) Water    5.55
                     Ethanol  0.0999
                     Glucose  0.00555<jupyter_text>Lastly, when working with positive ions, simply pass a dictionary of stoichiometric coefficients instead of the equation:<jupyter_code># First let's define a new set of chemicals
chemicals = NaCl, SodiumIon, ChlorideIon = tmo.Chemicals(['NaCl', 'Na+', 'Cl-'])

# We set the state to completely ignore other possible phases
NaCl.at_state('s')
SodiumIon.at_state('l')
ChlorideIon.at_state('l')

# Molar volume doesn't matter in this scenario, but its
# required to compile the chemicals. We can assume 
# a very low volume since its in solution.
NaCl.V.add_model(1e-6)
SodiumIon.V.add_model(1e-6)
ChlorideIon.V.add_model(1e-6)

# We can pass a Chemicals object to not have to override 
# the lipidcane chemicals we set earlier.
dissociation = tmo.Reaction({'NaCl':-1, 'Na+':1, 'Cl-': 1},
                            reactant='NaCl',  X=1.,
                            chemicals=chemicals)
dissociation.show()<jupyter_output>Reaction (by mol):
stoichiometry      reactant    X[%]
NaCl -> Na+ + Cl-  NaCl      100.00<jupyter_text>Parallel reactions Model the pretreatment hydrolysis reactions and assumed conversions from Humbird et. al. as shown in the follwing table [[1]](References):|Reaction|Reactant|% Converted||---|---|---||(Glucan)n + n H2O→ n Glucose|Glucan|9.9||(Glucan)n + n H2O → n Glucose Oligomer|Glucan|0.3||(Glucan)n → n HMF + 2n H2O|Glucan|0.3||Sucrose → HMF + Glucose + 2 H2O|Sucrose|100.0||(Xylan)n + n H2O→ n Xylose|Xylan|90.0||(Xylan)n + m H2O → m Xylose Oligomer|Xylan|2.4||(Xylan)n → n Furfural + 2n H2O|Xylan|5.0||Acetate → Acetic Acid|Acetate|100.0||(Lignin)n → n Soluble Lignin|Lignin|5.0| Create a ParallelReaction from Reaction objects:<jupyter_code>from biorefineries import cornstover as cs

# Set chemicals as defined in [1-4]
tmo.settings.set_thermo(cs.chemicals)

# Create reactions
pretreatment_parallel_rxn = tmo.ParallelReaction([
    #            Reaction definition                 Reactant    Conversion
    tmo.Reaction('Glucan + H2O -> Glucose',          'Glucan',   0.0990),
    tmo.Reaction('Glucan + H2O -> GlucoseOligomer',  'Glucan',   0.0030),
    tmo.Reaction('Glucan -> HMF + 2 H2O',            'Glucan',   0.0030),
    tmo.Reaction('Sucrose -> HMF + Glucose + 2H2O',  'Sucrose',  0.0030),
    tmo.Reaction('Xylan + H2O -> Xylose',            'Xylan',    0.9000),
    tmo.Reaction('Xylan + H2O -> XyloseOligomer',    'Xylan',    0.0024),
    tmo.Reaction('Xylan -> Furfural + 2 H2O',        'Xylan',    0.0050),
    tmo.Reaction('Acetate -> AceticAcid',            'Acetate',  1.0000),
    tmo.Reaction('Lignin -> SolubleLignin',          'Lignin',   0.0050)])

pretreatment_parallel_rxn.show()<jupyter_output>ParallelReaction (by mol):
index  stoichiometry                       reactant    X[%]
[0]    Water + Glucan -> Glucose           Glucan      9.90
[1]    Water + Glucan -> GlucoseOligomer   Glucan      0.30
[2]    Glucan -> 2 Water + HMF             Glucan      0.30
[3]    Sucrose -> 2 Water + HMF + Glucose  Sucrose     0.30
[4]    Water + Xylan -> Xylose             Xylan      90.00
[5]    Water + Xylan -> XyloseOligomer     Xylan       0.24
[6]    Xylan -> 2 Water + Furfural         Xylan       0.50
[7]    Acetate -> AceticAcid               Acetate   100.00
[8]    Lignin -> SolubleLignin             Lignin      0.50<jupyter_text>Model the reaction:<jupyter_code>feed = tmo.Stream(H2O=2.07e+05,
                  Ethanol=18,
                  H2SO4=1.84e+03,
                  Sucrose=1.87,
                  Extract=67.8,
                  Acetate=25.1,
                  Ash=4.11e+03,
                  Lignin=1.31e+04,
                  Protein=108,
                  Glucan=180,
                  Xylan=123,
                  Arabinan=9.02,
                  Mannan=3.08,
                  Furfural=172)
print('BEFORE REACTION')
feed.show(N=100)

# React feed molar flow rate
pretreatment_parallel_rxn(feed)

print('AFTER REACTION')
feed.show(N=100)<jupyter_output>BEFORE REACTION
Stream: s5
 phase: 'l', T: 298.15 K, P: 101325 Pa
 flow (kmol/hr): Water     2.07e+05
                 Ethanol   18
                 Furfural  172
                 H2SO4     1.84e+03
                 Sucrose   1.87
                 Extract   67.8
                 Acetate   25.1
                 Ash       4.11e+03
                 Lignin    1.31e+04
                 Protein   108
                 Glucan    180
                 Xylan     123
                 Arabinan  9.02
                 Mannan    3.08
AFTER REACTION
Stream: s5
 phase: 'l', T: 298.15 K, P: 101325 Pa
 flow (kmol/hr): Water            2.07e+05
                 Ethanol          18
                 AceticAcid       25.1
                 Furfural         173
                 H2SO4            1.84e+03
                 HMF              0.546
                 Glucose          17.8
                 Xylose           111
                 Sucrose          1.86
                 Extract          67.8
                [...]<jupyter_text>Reactions in series SeriesReaction objects work the same way, but in series:<jupyter_code>pretreatment_series_rxn = tmo.SeriesReaction(pretreatment_parallel_rxn)
pretreatment_series_rxn.show()<jupyter_output>SeriesReaction (by mol):
index  stoichiometry                       reactant    X[%]
[0]    Water + Glucan -> Glucose           Glucan      9.90
[1]    Water + Glucan -> GlucoseOligomer   Glucan      0.30
[2]    Glucan -> 2 Water + HMF             Glucan      0.30
[3]    Sucrose -> 2 Water + HMF + Glucose  Sucrose     0.30
[4]    Water + Xylan -> Xylose             Xylan      90.00
[5]    Water + Xylan -> XyloseOligomer     Xylan       0.24
[6]    Xylan -> 2 Water + Furfural         Xylan       0.50
[7]    Acetate -> AceticAcid               Acetate   100.00
[8]    Lignin -> SolubleLignin             Lignin      0.50<jupyter_text>Net conversion in parallel:<jupyter_code>pretreatment_parallel_rxn.X_net()<jupyter_output><empty_output><jupyter_text>Net conversion in series:<jupyter_code># Notice how the conversion is
# slightly lower for some reactants
pretreatment_series_rxn.X_net()
feed = tmo.Stream(H2O=2.07e+05,
                  Ethanol=18,
                  H2SO4=1.84e+03,
                  Sucrose=1.87,
                  Extract=67.8,
                  Acetate=25.1,
                  Ash=4.11e+03,
                  Lignin=1.31e+04,
                  Protein=108,
                  Glucan=180,
                  Xylan=123,
                  Arabinan=9.02,
                  Mannan=3.08,
                  Furfural=172)
print('BEFORE REACTION')
feed.show(N=100)

# React feed molar flow rate
pretreatment_series_rxn(feed)

print('AFTER REACTION')
feed.show(N=100)<jupyter_output>BEFORE REACTION
Stream: s6
 phase: 'l', T: 298.15 K, P: 101325 Pa
 flow (kmol/hr): Water     2.07e+05
                 Ethanol   18
                 Furfural  172
                 H2SO4     1.84e+03
                 Sucrose   1.87
                 Extract   67.8
                 Acetate   25.1
                 Ash       4.11e+03
                 Lignin    1.31e+04
                 Protein   108
                 Glucan    180
                 Xylan     123
                 Arabinan  9.02
                 Mannan    3.08
AFTER REACTION
Stream: s6
 phase: 'l', T: 298.15 K, P: 101325 Pa
 flow (kmol/hr): Water            2.07e+05
                 Ethanol          18
                 AceticAcid       25.1
                 Furfural         172
                 H2SO4            1.84e+03
                 HMF              0.491
                 Glucose          17.8
                 Xylose           111
                 Sucrose          1.86
                 Extract          67.8
                [...]<jupyter_text>Indexing reactions Both SeriesReaction, and ParallelReaction objects are indexable:<jupyter_code># Index a slice
pretreatment_parallel_rxn[0:2].show()
# Index an item
pretreatment_parallel_rxn[0].show()
# Change conversion through the item
pretreatment_parallel_rxn[0].X = 0.10
pretreatment_parallel_rxn.show()<jupyter_output>ParallelReaction (by mol):
index  stoichiometry                       reactant    X[%]
[0]    Water + Glucan -> Glucose           Glucan     10.00
[1]    Water + Glucan -> GlucoseOligomer   Glucan      0.30
[2]    Glucan -> 2 Water + HMF             Glucan      0.30
[3]    Sucrose -> 2 Water + HMF + Glucose  Sucrose     0.30
[4]    Water + Xylan -> Xylose             Xylan      90.00
[5]    Water + Xylan -> XyloseOligomer     Xylan       0.24
[6]    Xylan -> 2 Water + Furfural         Xylan       0.50
[7]    Acetate -> AceticAcid               Acetate   100.00
[8]    Lignin -> SolubleLignin             Lignin      0.50
{'fraction_non_alphanumeric': 0.0987617034128662, 'fraction_numerical': 0.0630474176985805, 'mean_word_length': 2.0490331491712706, 'pattern_counts': {'":': 0, '<': 46, '<?xml version=': 0, '>': 88, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '82309', 'license': 'MIT', 'n_tokens_mistral': 5010, 'n_tokens_neox': 4434, 'n_words': 1105, 'path': 'docs/tutorial/Stoichiometric_reactions.ipynb', 'repo_name': 'yoelcortes/thermotree', 'size': '13244'}