Skip to content

Add nox generation in combustion#904

Open
HaSchneider wants to merge 27 commits intooemof:devfrom
HaSchneider:add-Nox-generation-in-combustion
Open

Add nox generation in combustion#904
HaSchneider wants to merge 27 commits intooemof:devfrom
HaSchneider:add-Nox-generation-in-combustion

Conversation

@HaSchneider
Copy link
Copy Markdown
Contributor

This pull request implements optional NO creation in combustion component. When the f_nox parameter is set, NO is created in the combustion, as long as enough oxygen and nitrogen is available. f_nox is the ratio of mass flow of created NO in relation to the fuel mass flow.

But there are still some issues...

Issues:

  1. coolprop does not have NO fluid implemented

  2. combustion component does not allow other fluid property engines than coolprop. I solved that as described in Not possible to use other fluid property engines than coolprop in combustion component #887.

  3. Pyromat wrapper can be used instead, but:

    • Pyromat NO fluid does not include T_crit and p_crit values.
    • it must be defined manually

    I solved that in the example by creating my own pyromat wrapper with the needed values and by adding a dummy NO flow in the inputs with the corresponding pyromat wrapper as fluid property engine. I think it would be better if this can be done automaticaly in the combustion component, but I have no idea how.

  4. following components might fail, due to missing fluid properties in the pyromat wrapper engine. This is demonstrated in the test_CombustionChamber_NO. I think this cant be solved proper as long as pyromat does not contain all needed properties and coolprop does not contain NO. So missing properties must be added manualy to the pyromat wrapper.

Example

from tespy.components import (
    Source, Sink, 
    SimpleHeatExchanger, 
    DiabaticCombustionChamber,
)
from tespy.tools.fluid_properties.wrappers import PyromatWrapper

from tespy.connections import Connection

from rva_classes import ModelTemplate

class my_PyromatWrapper(PyromatWrapper):
    def _set_constants(self):
        self._p_min, self._p_max = 100, 1000e5
        self._T_crit, self._p_crit = 180.15, 6400000 #self.AS.critical() not supported by pyromat
        self._T_min, self._T_max = self.AS.Tlim()
        self._molar_mass = self.AS.mw()



class create_solvent_combustion(ModelTemplate):
        
    def _create_network(self) -> None:
        super()._create_network()
        amb = Source('ambient air')
        sf = Source('fuel')
        fg = Sink('flue gas outlet')
        self.comb = DiabaticCombustionChamber('combustion chamber')
        self.hex = SimpleHeatExchanger('heat exchanger')
        
        
        self.amb_comb = Connection(amb, 'out1', self.comb, 'in1')
        self.sf_comb = Connection(sf, 'out1', self.comb, 'in2')
        self.comb_hex = Connection(self.comb, 'out1', self.hex, 'in1')
        self.hex_fg = Connection(self.hex, 'out1', fg, 'in1')
        self.nw.add_conns(self.sf_comb, self.amb_comb, self.comb_hex, self.hex_fg, 
        )
        self.comb.set_attr(ti=500000, pr=0.95, eta=1, lamb=4, f_nox= 0.00025)
        self.amb_comb.set_attr(
            p=1.2, T=20,
            fluid={'Ar': 0.0129, 'N2': 0.7553, 'CO2': 0.0004, 'O2': 0.2314, 'ig::NO':0},
            fluid_engines={"NO": my_PyromatWrapper}, 
        )
        self.sf_comb.set_attr(
            p=1.3, T=25, fluid={'CO2': 0.03, 'H2': 0.01, 'CH4': 0.96, 'ig::NO':0},
            fluid_engines={"NO": my_PyromatWrapper}, 
        )
        
        
        self.hex.set_attr(pr=0.95)

        #self.comb_hex.set_attr(T=1200)
        self.hex_fg.set_attr(T=200)

rva= create_solvent_combustion()

rva.nw.solve('design')
print(rva.comb_hex.fluid.val)
{'CO2': 0.03923764451245059, 
'Ar': 0.012711828022584415, 
'H2': 0.0, 
'N2': 0.7442833087203439, 
'NO': 1.8229232332386296e-06, 
'O2': 0.17101940248021757, 
'H2O': 0.03274599334117038, 
'CH4': 0.0}
  • Documentation
  • Implement an example and a system test

@fwitte
Copy link
Copy Markdown
Member

fwitte commented Feb 7, 2026

Hi @HaSchneider, thank you very much for bringing this up, very cool addition! I will review it in the next weeks :)

@fwitte fwitte mentioned this pull request Apr 19, 2026
3 tasks
@fwitte
Copy link
Copy Markdown
Member

fwitte commented Apr 24, 2026

The open todos are now:

  • somehow implant the NO stream in the outlet that uses a PyromatWrapper (this can be hardcoded for now, but should be done flexibly)
  • correct the energy balance equation by considering endothermal reaction of N2 and O2 to NO

Comment on lines +207 to +211
"f_nox": dc_cp(
quantity="ratio",
min_val=0,
max_val=1,
description="generation rate of NO in flue gas, only active if value is explicitly set"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HaSchneider: is this the correct descriptions, could it be more specific maybe?

+ inl[0].fluid.wrapper[self.o2]._molar_mass
)
if self.f_nox.is_set:
n_nox_param = inl[1].m.val_SI * self.f_nox.val_SI / M_no
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@HaSchneider: Is this intended, that n_nox_param is only derived from one of the mass flows? Generally, the combustion type components do not care whether the air connects to one or the other inlet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants