GithubHelp home page GithubHelp logo

Comments (11)

fwitte avatar fwitte commented on June 15, 2024 1

Hi Yoel,
thanks for your detailed reply. I was talking about LHV/HHV :), I should have written that more clearly.

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!
Francesco

from thermosteam.

fwitte avatar fwitte commented on June 15, 2024 1

Hi Yoel,
great, thank you for taking your time with this. Honestly, I was questioning, if I did implement the combustion chamber balance over at https://github.com/oemof/tespy incorrectly. I am somewhat relieved now :D.
Thanks again, have a nice week!
Francesco

from thermosteam.

yoelcortes avatar yoelcortes commented on June 15, 2024

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:

BEFORE ADIABATIC REACTION
Stream: s21
 phase: 'g', T: 373.15 K, P: 101325 Pa
 flow (kmol/hr): Methane  1
                 O2       2
                 N2       100
AFTER ADIABATIC REACTION
Stream: s21
 phase: 'g', T: 631.91 K, P: 101325 Pa
 flow (kmol/hr): Water  2
                 CO2    1
                 N2     100
Error in energy conservation: 0.00
Heat of combution -890590.00 is equal to the heat gained by the system 890590.00

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!

from thermosteam.

yoelcortes avatar yoelcortes commented on June 15, 2024

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:

Lower heating value: -802567.008 [kJ/hr]
Higher heating value: -890590.0 [kJ/hr]
Lower heating value of methane: -802567.008 [J/mol]
Higher heating value of methane: -890590.0 [J/mol]

Hope this helps,
Thanks!

from thermosteam.

yoelcortes avatar yoelcortes commented on June 15, 2024

Sounds good!

from thermosteam.

yoelcortes avatar yoelcortes commented on June 15, 2024

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 dH does not account for latent heats, and a warning in the documentation as well:

I'll go ahead and close the issue, but feel free to reopen if anything comes up.

Thanks!

from thermosteam.

yoelcortes avatar yoelcortes commented on June 15, 2024

@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!

from thermosteam.

fwitte avatar fwitte commented on June 15, 2024

Hi Yoel,
that is a very good addition to thermosteam, I think!

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.
Thank you and best regards
Francesco

from thermosteam.

yoelcortes avatar yoelcortes commented on June 15, 2024

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]:

T       H_vap
298.15, 43927.16
303.15, 43718.68
308.15, 43510.46
313.15, 43302.43
318.15, 43094.50
323.15, 42886.59
328.15, 42678.65
333.15, 42470.61
338.15, 42262.42
343.15, 42054.05
348.15, 41845.45
353.15, 41636.60
358.15, 41427.48
363.15, 41218.08
368.15, 41008.39
373.15, 40798.42
378.15, 40588.18
383.15, 40377.68
388.15, 40166.96
393.15, 39956.01

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 chemicals library to compute HHV and LHV:

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:

CombustionData(stoichiometry={'CO2': 1, 'O2': -2.0, 'H2O': 2.0}, HHV=-890590.0, Hf=-74534.0, MW=16.04246)
-802567.008

I hope this helps,
Let me know if you have any more questions!

from thermosteam.

yoelcortes avatar yoelcortes commented on June 15, 2024

@fwitte, I just made a correction to the combustion_data code block. I forgot to pass Hf (heat of formation of the chemical).

Thanks,
Sorry for the confusion!

from thermosteam.

yoelcortes avatar yoelcortes commented on June 15, 2024

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')
Heat of reaction with latent heats minus sensible heats: -802567.01 kJ/hr
Lower heating value of methane: -802567.01 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')
Heat of reaction: 285825.00 kJ/hr
HHV: -285825.00 kJ/hr
Heat of reaction: 241813.50 kJ/hr
LHV: -241813.50 kJ/hr

Thanks again,
Have a nice day!

from thermosteam.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.