Equivalence Ratio - Combustion - calculation understanding

1,401 views
Skip to first unread message

Guillaume Thiriet

unread,
Feb 1, 2022, 12:49:09 PM2/1/22
to Cantera Users' Group
Good morning mates,

I am a new user of the Cantera tool and I have difficulties to understand the way "equivalence_ratio" is running. Thereafter, the code parts are in red.

There is the definition of my mixture,
*****
gas1 = ct.Solution('gri30.yaml')
X_H2 = 2.0
X_O2 = 1.0
X_N2 = 3.762
#I want to add a proportion of initial H2O to my combustion (here - 30 %)
Ratio_H2O = 0.3              
#This line allows to add the accurate proportion of H2O to the mixture
X_H2O = (X_H2+X_O2+X_N2)*(Ratio_H2O/(1-Ratio_H2O))   
gas1.X = {'H2':X_H2, 'O2':X_O2, 'N2':X_N2, 'H2O':X_H2O}
gas1.TP = 298, 101325    
*****
The "equivalence_ratio" command give me:
gas1.equivalence_ratio()
--> 1 (Ok , with that)

gas1.equivalence_ratio('H2','O2')
--> 1 (Ok , with that)

Now, if I change  X_H2 from 2 to 4 (X_H2 = 4.0). There is the definition of my new mixture,
*****
gas1 = ct.Solution('gri30.yaml')
X_H2 = 4.0
X_O2 = 1.0
X_N2 = 3.762
#I want to add a proportion of initial H2O to my combustion (here - 30 %)
Ratio_H2O = 0.3              
#This line allows to add the accurate proportion of H2O to the mixture
X_H2O = (X_H2+X_O2+X_N2)*(Ratio_H2O/(1-Ratio_H2O))       
*****
The "equivalence_ratio" command give me:
gas1.equivalence_ratio()
--> 1.3475 (/!\/!\/!\ - Why this value is not equal to 2)

gas1.equivalence_ratio('H2','O2')
--> 1.17233 ( /!\/!\/!\ - Why this value is not equal to 2  /  /!\/!\/!\ - Why this value is different from the previously calculated one (1.3475) )

Additionnal information, I noticed changes when I put the water fraction to 0. There is the definition of my new mixture,
*****
gas1 = ct.Solution('gri30.yaml')
X_H2 = 4.0
X_O2 = 1.0
X_N2 = 3.762
#I want to add a proportion of initial H2O to my combustion (here - 30 %)
Ratio_H2O = 0.0              
#This line allows to add the accurate proportion of H2O to the mixture
X_H2O = (X_H2+X_O2+X_N2)*(Ratio_H2O/(1-Ratio_H2O))       
*****
The "equivalence_ratio" command give me:
gas1.equivalence_ratio()
--> 2.0 (I agree with that value)

gas1.equivalence_ratio('H2','O2')
--> 1.25478 ( /!\/!\/!\ - Why this value is not equal to 2 )

In this way, I would like to understand how is calculate the equivalence_ratio of the mixture. According to my definition (what I learn at school and Internet), the equivalence ratio is the following MASS ratio (fuel/oxidizer)/(fuel/oxidizer)stoi. But, Cantera seems to proceed differently as for instance H2O seems to have a role in the calculation....

Thank you very much for your support on that topic

Yours faithfully

Guillaume THIRIET

Guillaume Thiriet

unread,
Feb 4, 2022, 9:58:54 AM2/4/22
to Cantera Users' Group
Good evening everyone,
 A small up of that topic with an additionnal test I just run:

I created a mixture using the following code:

*****
gas1 = ct.Solution('gri30.yaml')

X_H2 = 4.0
X_O2 = 1.0
X_N2 = 3.762
Ratio_H2O = 0.3
X_H2O = (X_H2+X_O2+X_N2)*(Ratio_H2O/(1-Ratio_H2O)) # Compute the molar quantity of H2O required to have "Ratio_H2O" % of H2O

gas1.X = {'H2':X_H2, 'O2':X_O2, 'N2':X_N2, 'H2O':X_H2O}
gas1.TP = 298, 101325

*****

I am expecting that the equivalence ratio would be equal to 2 as my definition of the equivalence ratio is
PHI = (Fuel Mass/Oxydizer Mass)/(Fuel Mass / Oxydizer Mass)stoe

the oxydiser could be only O2 or whole air according to definitions...

But, thereafter, a few test of the "equivalence_ratio" command of Cantera
****
gas1.equivalence_ratio('H2','O2:1,N2:3.762')
Out[28]: 1.676425666205303

gas1.equivalence_ratio('H2','O2:1,N2:3.762,H2O:3.755')
Out[29]: 1.9999876902406686


gas1.equivalence_ratio('H2','O2:1')
Out[30]: 1.172336630644669


gas1.equivalence_ratio('H2','O2')
Out[31]: 1.172336630644669

gas1.equivalence_ratio()
Out[32]: 1.3475152658491785
*****

Does someone know why Cantera is involving water within the calculation of the equivalence ratio ?

Yours faithfully

Guillaume THIRIET

jeffrey...@gmail.com

unread,
Feb 4, 2022, 12:56:33 PM2/4/22
to Cantera Users' Group
This is very strange! I tested it out, and the equivalence ratio is correct when using "gas1.get_equivalence_ratio()". But, "get_equivalence_ratio()" gives a deprecation warning. 
So, what is the difference between get_equivalence_ratio() and equivalence_ratio()?

jeffrey...@gmail.com

unread,
Feb 4, 2022, 1:52:05 PM2/4/22
to Cantera Users' Group
Sorry for the double message, but I've found a related issue that may help someone solve the problem. It seems that this is not a problem when the mixture is stoichiometric. For off-stoichiometric mixtures, addition of water moves the result from equivalence_ratio() to be closer to stoichiometric. See examples below:
---------------------------------------------------------------------------------------------------------
# Stoichiometric mixture works as expected
gas1.X = {'H2': 1, 'O2': 0.5}

gas1.equivalence_ratio()
Out[40]: 1.0

gas1.X = {'H2': 1, 'O2': 0.5, 'H2O': 1}

gas1.equivalence_ratio()
Out[42]: 1.0  # Adding H2O didn't make a difference
-------------------------------------------------------------------------------------------------------------
# Rich or lean mixture does not work correctly
gas1.X = {'H2': 2, 'O2': 0.5}

gas1.equivalence_ratio()
Out[44]: 2.0

gas1.X = {'H2': 2, 'O2': 0.5, 'H2O': 1}

gas1.equivalence_ratio()
Out[46]: 1.5  # Adding H2O decreased the equivalence ratio.

gas1.X = {'H2': 2, 'H2O': 1}  # No O2, equivalence ratio should be infinite or undefined

gas1.equivalence_ratio()
Out[48]: 3.0  # Again, H2O is treated as if it has extra O-atoms

gas1.X = {'H2':2, 'O2': 2}

gas1.equivalence_ratio()
Out[62]: 0.5

gas1.X = {'H2':2, 'O2': 2, 'H2O': 1}

gas1.equivalence_ratio()
Out[64]: 0.6000000000000001  # With a lean mixture, H2O makes the mixture richer.
------------------------------------------------------------------------------------------------------------------------------------------------------
I'm guessing the problem is somewhere in the definitions of "o2_required" and "o2_present" in cantera/src/thermo/ThermoPhase.cpp, but I can't figure it out.

Attarde Lalit Yadnyeshwar me21s034

unread,
Feb 5, 2022, 8:15:23 AM2/5/22
to canter...@googlegroups.com
Hi, 
While performing tutorial, I had input mole fraction like 0.01fuel+0.825Ar+0.075O2.
This command gas.set_equivalence_ratio() don't consider mole fraction of fuel. 
So i defined 'composition' in gas.TPX and gave value of mole fraction to 'composition' . 
Equivalence ratio is automatically considered correctly. 

--
You received this message because you are subscribed to the Google Groups "Cantera Users' Group" group.
To unsubscribe from this group and stop receiving emails from it, send an email to cantera-user...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/cantera-users/eab5f4e0-8b36-4368-a99a-50040c36ed8en%40googlegroups.com.

Guillaume Thiriet

unread,
Feb 7, 2022, 2:26:21 AM2/7/22
to Cantera Users' Group
Good morning everyone,
First of all thank you very much for your replies. As far as I understand, it seems that the equivalence_ratio() command does not run properly when water is added. 

@Jeffrey, thank you for your analysis, following the tests I did last Friday, I also came to this conclusion and I am kind of please that your own analysis can corroborate this conclusion (although, the problem remains :( ).

I think I will consider defining my own function for the calculation of the equivalence ratio as I am rather not confident in the one implemented in Cantera (especially when water is included).

@me21, Thank you very much for your contribution as well. For my part, it seems that set_equivalence_ratio()  is working properly as I succeeded to run a H2/Air combustion while increasing the equivalence ratio of the mixture last week. Nevertheless, I did not include Ar so maybe, the addition of that rare gas may influence the result. 
I will check this afternoon.

Once again, thank you very much for your support and advices.

I wish you a very good day.

Regards

Ray Speth

unread,
Feb 8, 2022, 6:20:15 PM2/8/22
to Cantera Users' Group
Hi all,

The definition of equivalence ratio as fuel-oxidizer ratio divided by stoichiometric fuel-oxidizer ratio depends on what your definition of the fuel and oxidizer are, and how you treat the presence of species that aren't included in either of those.

Cantera uses a definition of the equivalence ratio that's determined from the elemental mass fractions so that it is independent of the reaction progress. By default, this uses an assumption that the fuel contains no oxygen and that oxidizer contains no carbon or hydrogen. If that's not the case for your situation, then you need to provide a definition for the fuel and for the oxidizer composition, including any inert species. The method for calculating it is described in the documentation for the methods implementing it in C++, starting with ThermoPhase.equivalenceRatio().

In this particular situation, you can think of the effect that adding water to the mixture has as being equivalent to adding the same mass of stoichiometric H2 and O2, which would obviously tend to pull the equivalence ratio closer to 1.0.

Regards,
Ray

jeffrey...@gmail.com

unread,
Feb 8, 2022, 7:51:18 PM2/8/22
to Cantera Users' Group
Ray,

Thanks for the explanation. I remember we talked about this when I made this pull request, and decided to define equivalence ratio as a quantity that isn't conserved during oxidation, and is only defined for an unburned mixture. I really think we should stick with that definition. 

Most people use equivalence ratio to refer to [(F/Ox) / (F/Ox)_stoich] for an unburned mixture, where "F" is a fuel that can be oxidized, and "Ox" is an oxidizer. H2O and CO2 are (relatively) inert - they have minor impacts on chemistry, but not nearly as much of an impact as the F/ox ratio. To first approximation, they can be treated as diluents like N2 or Ar, whose major impact is to absorb heat during oxidation. So, they shouldn't factor into the equivalence ratio. In other words, adding H2O or CO2 to a lean mixture doesn't cause it to behave like a richer mixture.

Here's some evidence for my statement above about what "most people" use. On page 55 of this pdf from Heinz Pitsch (61 on the page numbers), equivalence ratio is defined based on F and O2 of the unburned mixture. Addition of H2O or CO2 doesn't affect the stoichiometric ratio (nu) or the relative fuel and oxidizer fractions, so it shouldn't affect the equivalence ratio. As another example, if you search through google scholar for publications on H2O or CO2 addition, you will find many where the equivalence ratio is defined in a way that treats H2O as inert. For example, see table 2 in this paper (by the former Combustion Institute President!) where the water mole fraction does not affect the equivalence ratio.

I understand that a large portion of the modeling community uses the mixture fraction, which is conserved during reactions. But, the equivalence ratio is a separate quantity, that is only defined for an unburned mixture, and ignores contributions of inerts. I just don't see the need to make equivalence ratio into a conserved quantity like mixture fraction. I think the default behavior should be the one that is most commonly used in the community - equivalence ratio is defined for an unburned mixture, isn't affected by addition of complete combustion products (CO2, H2O, SO2), and is not conserved during reaction. If users want a conserved quantity, they can use the mixture fraction. At the very least, the documentation should explicitly state that the equation that determines this equivalence ratio, and warn users that it depends on H2O, CO2, and SO2 content.

I hope this wasn't too much of a rant, and thanks for reading!
Jeff

Ray Speth

unread,
Feb 8, 2022, 9:44:23 PM2/8/22
to Cantera Users' Group

Hi Jeff,

I agree that adding H2O or CO2 to an unburned mixture does not change its equivalence ratio. However, Cantera can’t tell that the CO2 or H2O present in such a case is just a diluent and not a result of partial combustion. If you tell Cantera that these species are part of the unburned fuel or oxidizer mixture with the appropriate mole fraction, you will get the expected value of the equivalence ratio:

import cantera as ct
gas = ct.Solution('gri30.yaml')
gas.X = 'H2: 4.0, O2: 1.0, H2O: 1.0'
print(gas.equivalence_ratio(fuel='H2:1.0', oxidizer='O2:1.0, H2O:1.0'))  # == 2.0
print(gas.equivalence_ratio(fuel='H2:4.0, H2O:1.0', oxidizer='O2:1.0'))  # == 2.0

I read the Pitsch slide as being consistent with the treatment of the equivalence ratio as being a conserved quantity — it is defined based on the properties of the unburned mixture, and should therefore not take on other values as the mixture burns. While it’s perhaps possible to theoretically say that the equivalence ratio is undefined for a mixture that is partially or fully burned, there isn’t really a way for Cantera to make that determination and refuse to calculate an equivalence ratio. So I think it’s better to use a definition that effectively corresponds to that hypothetical initial state, even if that requires some additional inputs depending on what that initial state was.

All that said, I would certainly agree that some additional clarity could be used in the documentation.

Regards,
Ray

jeffrey...@gmail.com

unread,
Feb 8, 2022, 11:48:13 PM2/8/22
to Cantera Users' Group
Ray,
I didn't really understand those fuel and oxidizer options, that makes it clearer. Since Pitsch directly connects equivalence ratio to mixture fraction, I can see how it can be a conserved quantity, defined based on the unburned mixture as you say.

Equivalence ratio is defined based on the unburned mixture, so gas.equivalence_ratio must know what the unburned mixture was. The current default behavior assumes that the initial state is "maximally unburned," meaning that there were zero combustion products. Instead, I think the default behavior should assume that the current "gas" object is the unburned mixture. Both of these are valid assumptions about the initial state, but I think that in normal simulations, it's more common to query the equivalence ratio of an unburned mixture than of a partially or fully burned mixture. In a premixed simulation, one always has access to the initial mixture, so one would likely call equivalence_ratio() on that initial mixture. The only users who would ask the question "What was the unburned equivalence ratio of this partially burned gas?" are probably simulating non-premixed or partially-premixed combustion, where they would use the mixture fraction anyway.

At the very least, I think there should be a simple option to tell cantera that the "gas" object is the initial state, instead of requiring the user to split the current mixture into the optional fuel and oxidizer parameters. Something like this:

    gas.equivalence_ratio(initial_state=True)  # gas represents the initial, unburned mixture. I think this should be the default behavior.
    gas.equivalence_ratio(initial_state=False)  # gas is not the initial state. The initial mixture contained no CO2, H2O, or SO2. This is the current default, which I think should be an option.
    gas.equivalence_ratio(fuel=f, oxidizer=o)  # gas may or may not be the initial state. The initial mixture contained a fuel stream with composition f and an oxidizer stream with composition o. This is a current, useful option.

Alternatively, there could be a warning (that can be suppressed in options) that explains the calculation whenever a user calls equivalence_ratio() on a mixture that includes CO2, H2O, or SO2.

If we settle on any changes, I'm happy to attempt to implement them, although I'd have to learn some cython....

Thanks,
Jeff

Guillaume Thiriet

unread,
Feb 9, 2022, 4:07:17 AM2/9/22
to Cantera Users' Group
Good morning everyone,

First of all, I would like to thank you very much for your very detailed replies and explanations. 

@Ray Speth
Thank you very much for the URL link toward the code documentation. I haven't search that much in it and I should have done that before to ask my question here as it is pretty clear..

In the same time, I agree with @Jeffrey in the way that from my own experience, It seems that most of people / engineers / researchers in the combustion field use the equivalence ratio as the  fuel-oxidizer ratio divided by stoichiometric fuel-oxidizer ratio based on the unburned mixture (as most of the time, the equivalence ratio is described as an initial condition). The neutral species (or considered as neutral) do not play any role in the calculation of the equivalence ratio.

In the example quoted before:
***
import cantera as ct gas = ct.Solution('gri30.yaml') gas.X = 'H2: 4.0, O2: 1.0, H2O: 1.0'
***
The calculation with the equivalence_ratio() command (no argument) will consider that H2O is kind of 1 quantity (mole) of H2 (which is added to the fuel) and 1 quantity of O (which is added to the oxidizer side) and that is why we get 1.6666666666666667 with the command gas.equivalence_ratio().

From my own comprehension so far, in the absence of such species (H2O / CO2 / SO2), in the case where we have for instance, let's say a simple combustion of CH4 with Air (0.21 O2 + 0.79 N2), things are different as:
***
gas1 = ct.Solution('gri30.yaml')

X_CH4 = 4.0
X_O2 = 2.0
X_N2 = 7.5208

gas1.X = {'CH4':X_CH4, 'O2':X_O2, 'N2':X_N2}
gas1.TP = 298, 101325

gas1.equivalence_ratio() --> 4.0 - OK
gas1.equivalence_ratio(fuel='CH4',oxidizer='O2:2.0,N2:7.5208') --> 4.0 - OK
gas1.equivalence_ratio(fuel='CH4:4,N2:7.5208',oxidizer='O2:2.0') --> 4.00000000002 - OK
gas1.equivalence_ratio(fuel='CH4',oxidizer='O2') -->  1.8259200385382435 - NOK
***
The deviation observed here is, if I understand well, due to the way the equivalence_ratio is calculated when using arguments (i.e. using the Bilger definition). In that case, the non specification of the N2 which is inert (as it does not incorporate C, S, H nor O) modify the global mixture fraction and therefore the result (I did not check the calculation manually).

Finally, I think that some additional clarity should be added in the documentation as I was really convinced of my definition of the equivalence_ratio was the only one existing.

Be that as it may and as I told before, I am quite a young combustion engineer as I only have three years of experience in the field. It is therefore highly possible that I do not have the require distance to evaluate all the impacts and stakes beside the way of defining the equivalence_ratio in a way or another.

I would like to thank both of you again for your explanations.
I wish you a very good day (afternoon / end of day in the case you're living on the other side of the Atlantic)

Regards

Guillaume THIRIET

Ingmar Schoegl

unread,
Feb 9, 2022, 8:42:17 AM2/9/22
to Cantera Users' Group
I have used both definitions in the past, so I guess there isn’t one correct interpretation. I am, however, wondering whether this enhancement issue could introduce some improvements: https://github.com/Cantera/enhancements/issues/108
-Ingmar-

g3

unread,
Feb 18, 2022, 7:09:46 PM2/18/22
to Cantera Users' Group

Just to give a few more details and maybe to clarify things:

The two ways of computing the equivalence ratio currently supported are:

1) Calling equivalence_ratio without any arguments: this assumes there is no knowledge about the original oxidizer and fuel composition. So the best guess is to assume that all O atoms come from the oxidizer and all C,H,S atoms stem from the fuel. Having this definition is convenient if you are given an arbitrary gas composition without further information.

2) Calling equivalence_ratio with arguments specifying the original fuel and oxidizer composition will give you the equivalence ratio based on that knowledge. For example, if there is some exhaust gas recirculation as part of the oxidizer; or you have exhaust gas measurements and you want to double check that the equivalence ratio fits to the specified fueld and oxidizer streams before combustion.


The two definitions are the same if all C,H,S atoms come from the fuel and all O atoms from the oxidizer. Both are invariant to the reaction progress and consistent with element-based mixture fractions (e.g. single element or Bilger mixture fraction). As Ray stated, all information about the computations are written here: https://cantera.org/documentation/docs-2.5/doxygen/html/dc/d38/classCantera_1_1ThermoPhase.html#a140acffb0b0346fd4a4581919335b8d9

Just a few words about the arguments to that function: Giving a species composition without a numerical value will be interpreted as “1”, so for example

equivalence_ratio('H2','O2') is equivalent to equivalence_ratio(fuel='H2:1',oxidizer='O2:1')

This means, that the composition of the fuel is assumed to be 100 mol-% H2 and the composition of the oxidizer is 100 mol-% O2.

These ratios are given in mole fractions, but you can change them to be interpreted as mass fraction as well:

equivalence_ratio(fuel='H2:1',oxidizer='O2:1',basis=mass '  )

If you want to take only certain species into account for the equivalence ratio calculation, you could write a little helper function:

# compute the equivalence ratio of the current mixture, taking only a subset of species into account:
def equivlance_ratio_unburnt(gas, species):
    X = gas.X
    gas.X = ",".join([s+':'+str(gas[s].X[0]) for s in species])
    equiv = gas.equivalence_ratio()
    gas.X = X # reset the gas back to the original composition
    return equiv


So for the example given in the second post, you would call it like this:

gas = ct.Solution("gri30.yaml")

X_H2 = 4.0
X_O2 = 1.0
X_N2 = 3.762
Ratio_H2O = 0.3
X_H2O = (X_H2+X_O2+X_N2)*(Ratio_H2O/(1-Ratio_H2O))

gas.X = {'H2':X_H2, 'O2':X_O2, 'N2':X_N2, 'H2O':X_H2O}
print(equivlance_ratio_unburnt(gas, ['O2','H2'])) # now outputs 2.0


As Ingmar mentioned, for setting a mixture composition given a certain equivalence ratio considering only a subset of species, the code at https://github.com/Cantera/enhancements/issues/108 could be used. First, the mixture is set according to the pure fuel/oxidizer species, and then it is diluted with the rest of the species, e.g. CO2 or H2O.

There are also some additional examples here: https://github.com/Cantera/cantera/blob/main/interfaces/cython/cantera/examples/thermo/equivalenceRatio.py

And more test cases showing the use of equivalence_ratio with arbitrary composition of fuel and oxidizer mixtures:
https://github.com/Cantera/cantera/blob/main/interfaces/cython/cantera/test/test_thermo.py#L303-L423

In my opinion, having an initial_state=True/False argument is not useful. Either, the equivalence ratio is independent of the reaction progress which makes it redundant; or the equivalence ratio is based only on a subset of species, then the user would have to specify that list of species anyways (for example for higher hydrocarbons: what was the initial fuel? Maybe heptane? Should CH4 be included in the computation or is it an intermediate species in this case?) But I might be biased, since I wrote the current implementation in Cantera :)

I could open a PR to include an optional list of species to consider for equivlance_ratio as shown above as well as for the code posted in the enchantsment issue for set_equivalence_ratio to include an additonal dilution parameter. What do you think?

Ingmar Schoegl

unread,
Feb 20, 2022, 12:13:44 PM2/20/22
to Cantera Users' Group
Hi Thorsten,

Thank you for the detailed post. Regarding a potential PR, I believe this would be useful and welcome. We are currently preparing to get things in line for the 2.6 beta release, so the timing should be good. In case you should have questions, please post in the enhancement  issue (or create a draft PR with what you already posted there). Best,

-ingmar-
Reply all
Reply to author
Forward
0 new messages