-
Notifications
You must be signed in to change notification settings - Fork 12
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Heat of reaction does not account for state of water (liquid/gaseous) after reaction #29
Comments
Hi Francesco, I am not too sure if I understand your question correctly, but I think I can help clarify some details (that are not too apparent) and hopefully answer it in the process. The dH property is the heat of reaction in J/mol-reactant. It takes into account the conversion of the reactant too, so dH at X=0.5 is half dH at X=1. Calling the Reaction object with a Stream object assumes an isothermal reaction (as you already know). For an adiabatic reaction, you can use the adiabatic_reaction method: The following example shows that the flow rate of reactant multiplied by dH is equal to the total heat produced from the reaction: import thermosteam as tmo
from thermosteam import reaction as rxn
chemicals = tmo.Chemicals(['Water', 'Methane', 'CO2', 'O2', 'N2'])
tmo.settings.set_thermo(chemicals)
combustion = rxn.Reaction('Methane + 2O2 -> 2Water + CO2', reactant='Methane', X=1)
heat_of_combustion = combustion.dH
feed = tmo.Stream(Methane=1, O2=2, N2=100, T=373.15, phase='g') # Dilute with nitrogen to keep temperature change low
reactant_mol = feed.imol[combustion.reactant]
H0 = feed.H # Enthalpy not including formation enthalpies
Hnet0 = feed.Hnet # Enthalpy including formation enthalpies
print('BEFORE ADIABATIC REACTION')
feed.show(N=100)
# React feed molar flow rate
combustion.adiabatic_reaction(feed)
print('AFTER ADIABATIC REACTION')
feed.show(N=100)
Hf = feed.H
Hnetf = feed.Hnet
heat_gained = Hf - H0
dHnet = Hnetf - Hnet0
print(f"Error in energy conservation: {dHnet:.2f}")
print(f'Heat of combution {reactant_mol * heat_of_combustion:.2f} is equal to the heat gained by the system {heat_gained:.2f}') Output:
I hope I didn't complicate things more, please let me know if I haven't answered your question (or if you have anymore). Thanks! |
Hi Francesco, I just realized what you meant after reading the title of the issue! Sorry, there is no direct way of taking the state of chemicals. You could use a multistream to change the state of water after an adiabatic reaction and compute the enthalpy difference. If you are just working with combustion reactions, I would suggest you use the "LHV" and "HHV" properties of streams and chemicals: import thermosteam as tmo
chemicals = tmo.Chemicals(['Water', 'Methane', 'CO2', 'O2', 'N2'])
tmo.settings.set_thermo(chemicals)
feed = tmo.Stream(Methane=1, O2=2, N2=100, T=373.15, phase='g')
print(f'Lower heating value: {feed.LHV} [kJ/hr]')
print(f'Higher heating value: {feed.HHV} [kJ/hr]')
print(f'Lower heating value of methane: {chemicals.Methane.LHV} [J/mol]')
print(f'Higher heating value of methane: {chemicals.Methane.HHV} [J/mol]') Output:
Hope this helps, |
Hi Yoel, I think adding a sentence or two the corresponding parts of your documentation, where you state, that the heat of reaction is not considering the state of the reaction educts (the same "issue" happens when modeling water electrolysis with evaporated water as input) and reaction products. Thank you, have a nice day! |
Sounds good! |
Hi Francesco, I added some cool new features to the Reaction, ParallelReaction, and SeriesReaction objects to be able to handle phase changes: import thermosteam as tmo
from thermosteam import reaction as rxn
chemicals = tmo.Chemicals(['Water', 'Methane', 'CO2', 'O2', 'N2'])
tmo.settings.set_thermo(chemicals)
combustion = rxn.Reaction('Methane,g + O2,g -> Water,l + CO2,g',
reactant='Methane', X=1,
correct_atomic_balance=True)
feed = tmo.Stream(Methane=1, O2=2, N2=100, T=373.15, phase='g') # Dilute with nitrogen to keep temperature change low
feed.phases = 'lg'
print('BEFORE ADIABATIC REACTION')
feed.show(N=100)
# React feed molar flow rate
combustion.adiabatic_reaction(feed)
print('AFTER ADIABATIC REACTION')
feed.show(N=100) Output: BEFORE ADIABATIC REACTION
MultiStream: s1
phases: ('g', 'l'), T: 373.15 K, P: 101325 Pa
flow (kmol/hr): (g) Methane 1
O2 2
N2 100
AFTER ADIABATIC REACTION
MultiStream: s1
phases: ('g', 'l'), T: 648.24 K, P: 101325 Pa
flow (kmol/hr): (g) CO2 1
N2 100
(l) Water 2 Note that latent heats will change depending on the temperature, so Reaction objects can only take into account heats of formation (not latent heats). In any case, by making the reaction adiabatic, these latent heats are accounted for in the energy balance. I added a comment in the tutorial that
I'll go ahead and close the issue, but feel free to reopen if anything comes up. Thanks! |
@fwitte Almost forgot to mention. I'm not too sure if this interests you, but it is possible to calculate heats of reaction with latent heats as follows: import thermosteam as tmo
chemicals = tmo.Chemicals(['Water', 'Methane', 'CO2', 'O2', 'N2'])
tmo.settings.set_thermo(chemicals)
combustion = tmo.Reaction('Methane,g + O2,g -> Water,l + CO2,g',
reactant='Methane', X=1,
correct_atomic_balance=True)
feed = tmo.Stream(Methane=1, O2=2, N2=100, T=298.15, phase='g')
feed.phases = 'lg'
H0 = feed.Hnet
combustion(feed)
Hf = feed.Hnet
print(f'Heat of reaction with latent heats: {Hf - H0:.2f} kJ/hr')
print(f'Higher heating value of methane: {chemicals.Methane.HHV:.2f} J/mol') Output: Heat of reaction with latent heats: -890590.00 kJ/hr
Higher heating value of methane: -890590.00 J/mol Thanks! |
Hi Yoel, But, I actually have one more question: There seems to be a little mismatch between the lower heating value and the heat of reaction when water is in gaseous state. I cannot really spot the origin of that. Maybe an issue in the reference point definition? chemicals = tmo.Chemicals(['Water', 'Methane', 'CO2', 'O2', 'N2'])
tmo.settings.set_thermo(chemicals)
combustion = tmo.Reaction('Methane + O2 -> Water + CO2',
reactant='Methane', X=1,
correct_atomic_balance=True)
feed = tmo.Stream(Methane=1, O2=2, N2=100, T=350.15, phase='g')
H0 = feed.Hnet
combustion(feed)
Hf = feed.Hnet
print(f'Heat of reaction with latent heats: {Hf - H0:.2f} kJ/hr')
print(f'Lower heating value of methane: {chemicals.Methane.LHV:.2f} J/mol') Heat of reaction with latent heats: -797156.83 kJ/hr
Lower heating value of methane: -802567.01 J/mol It is more obvious with the electrolysis of water: The heat of reaction with liquid water is correct, the heat of reaction with gaseous water is slightly below the expected value (which should be the enthalpy of formation of gaseous water, so about -241.8 kJ/mol, or?). chemicals = tmo.Chemicals(['H2O', 'H2', 'O2'], cache=True)
tmo.settings.set_thermo(chemicals)
reaction = rxn.Reaction('2H2O,l -> 2H2,g + O2,g', reactant='H2O', X=1)
reaction.show() # Note that the default basis is by 'mol'
reaction.reactant # The reactant is a tuple of phase and chemical ID
feed = tmo.Stream('feed', H2O=1)
H0 = feed.Hnet
feed.phases = ('g', 'l') # Gas and liquid phases must be available
reaction(feed) # Call to run reaction on molar flow
Hf = feed.Hnet
print(f'Heat of reaction with latent heats: {Hf - H0:.2f} kJ/hr')
reaction = rxn.Reaction('2H2O -> 2H2 + O2', reactant='H2O', X=1)
reaction.show() # Note that the default basis is by 'mol'
reaction.reactant # The reactant is a tuple of phase and chemical ID
feed = tmo.Stream('feed', H2O=1, T=298.15, phase='g')
H0 = feed.Hnet
reaction(feed) # Call to run reaction on molar flow
Hf = feed.Hnet
print(f'Heat of reaction with latent heats: {Hf - H0:.2f} kJ/hr') Reaction (by mol):
stoichiometry reactant X[%]
H2O,l -> H2,g + 0.5 O2,g H2O,l 100.00
Heat of reaction with latent heats: 285825.00 kJ/hr
Reaction (by mol):
stoichiometry reactant X[%]
H2O -> H2 + 0.5 O2 H2O 100.00
Heat of reaction with latent heats: 239365.21 kJ/hr The difference between heat of reaction and lower heating value in the combustion reaction of methane is actually very close to the offset in the gaseous water. Maybe you can help me spot the issue. |
Hi Fracesco, This one is easy to spot for me, but I know its hard to find at first sight. The heat capacities of reactants and products are different. Thus, this adds changes in sensible heats (in addition to the latent heats and heats of formation). Note that in my example, I used a temperature of 298.15 K. At this temperature, the enthalpy of chemicals at their reference state is 0 (since this is the reference point). That is why it worked before. Perhaps this threw you off to generalize, sorry! You might ask yourself why is the electrolysis of water in the gas phase appear to be off as well? This is because the enthalpy of gaseous water at 298.15 K is not at the reference state (the reference state is a liquid). Note that heats of vaporization change depending on the temperature (another way to view it is to go from 25C to 100C as liquid water, boil, then go from 100C to 25 as steam). Perhaps this quick example may help: import thermosteam as tmo
Water = tmo.Chemical('Water')
print(f"T H_vap")
for i in range(20):
T = 298.15 + 5*i
H_liq = Water.H('l', T)
H_gas = Water.H('g', T)
H_vap = H_gas - H_liq
print(f"{T:.2f}, {H_vap:.2f}") # Values may differ depending what models you use. Eventually I hope to implement IAPWS models... Output [EDITED after bug in gas enthalpy was fixed]:
If you are looking to get the exact value for HHVs and LHV (without taking into account sensible heats), it is best to perform these calculations separately. Thermosteam uses this function from the from chemicals import combustion_data, Hfg, CAS_from_any
CAS = CAS_from_any('Methane')
cd = combustion_data('CH4', Hf=Hfg(CAS)) # If Hf is not passed, combustion is estimated using Dulong's formula (not accurate here)
print(cd)
print(cd.LHV) # A property Output:
I hope this helps, |
This comment was marked as resolved.
This comment was marked as resolved.
Hi Francesco, I went through the calculations to be able to show that everything balances out, and I found a bug in the calculation of the gas phase enthalpy. Thanks for looking at everything in detail! Here is the demonstration to show that everything balances out by removing the sensible heats. Please pull my changes before you try the example. Methane example import thermosteam as tmo
chemicals = Water, Methane, CO2, O2, N2 = tmo.Chemicals(['Water', 'Methane', 'CO2', 'O2', 'N2'])
Water.H.g.Hvap_Tb = 44011.496 # Depending on the model, this value may be different.
tmo.settings.set_thermo(chemicals)
combustion = tmo.Reaction('Methane + O2 -> Water + CO2',
reactant='Methane', X=1,
correct_atomic_balance=True)
Tref = 298.15
Tb = Water.Tb
feed = tmo.Stream(Methane=1, O2=2, T=Tb, phase='g')
H0 = feed.Hnet - Methane.Cn.g.integrate_by_T(Tref, Tb) - 2 * O2.Cn.g.integrate_by_T(Tref, Tb)
combustion(feed)
Hf = feed.Hnet - 2 * Water.Cn.l.integrate_by_T(Tref, Tb) - CO2.Cn.g.integrate_by_T(Tref, Tb)
print(f'Heat of reaction with latent heats minus sensible heats: {Hf - H0:.2f} kJ/hr')
print(f'Lower heating value of methane: {chemicals.Methane.LHV:.2f} J/mol')
Electrolysis example: import thermosteam as tmo
H2O, H2, O2 = chemicals = tmo.Chemicals(['H2O', 'H2', 'O2'], cache=True)
H2O.H.g.Hvap_Tb = 44011.496
tmo.settings.set_thermo(chemicals)
reaction = tmo.Reaction('2H2O,l -> 2H2,g + O2,g', reactant='H2O', X=1)
Tref = 298.15
feed = tmo.Stream('feed', H2O=1)
H0 = feed.Hnet
feed.phases = ('g', 'l') # Gas and liquid phases must be available
reaction(feed) # Call to run reaction on molar flow
Hf = feed.Hnet
print(f'Heat of reaction: {Hf - H0:.2f} kJ/hr')
print(f'HHV: {H2.HHV:.2f} kJ/hr')
reaction = tmo.Reaction('2H2O -> 2H2 + O2', reactant='H2O', X=1)
feed = tmo.Stream('feed', H2O=1, T=Tref, phase='g')
H0 = feed.Hnet - H2O.Cn.l.integrate_by_T(Tref, H2O.Tb) - H2O.Cn.g.integrate_by_T(H2O.Tb, Tref)
reaction(feed) # Call to run reaction on molar flow
Hf = feed.Hnet
print(f'Heat of reaction: {Hf - H0:.2f} kJ/hr')
print(f'LHV: {H2.LHV:.2f} kJ/hr')
Thanks again, |
Hi Yoel, |
Hi Yoel,
again looking at methane combustion. Feeding a stream of Methane and O2 into a reaction at a temperature of T = 100 °C (could be less, I wanted to make sure after reaction temperature is larger than saturation temperature at partial pressure of the water) the outlet temperature is the same and therefore water should be gaseous.
Is there a way to calculate the actual heat of reaction in respect to the before/after reaction state? Or is the heat of reaction not supposed to be calculated on this basis?
Thank you very much and best regards
Francesco
The text was updated successfully, but these errors were encountered: