-
-
Notifications
You must be signed in to change notification settings - Fork 346
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
Enhance the PlasmaPhase to use the cross section data with an example of calculating the elastic collision energy loss rate #1731
base: main
Are you sure you want to change the base?
Conversation
…a phase and elasticElectronEnergyLossRate
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #1731 +/- ##
==========================================
+ Coverage 72.76% 75.71% +2.94%
==========================================
Files 378 445 +67
Lines 56986 61622 +4636
Branches 20691 9675 -11016
==========================================
+ Hits 41468 46658 +5190
+ Misses 12463 11894 -569
- Partials 3055 3070 +15 ☔ View full report in Codecov by Sentry. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for working on this, @BangShiuh. I had some suggestions that I think will lead to a simpler implementation. Please let me know if you have any questions.
@@ -1905,6 +1905,24 @@ cdef class ThermoPhase(_SolutionBase): | |||
raise ThermoModelMethodError(self.thermo_model) | |||
return pystr(self.plasma.electronSpeciesName()) | |||
|
|||
property elastic_electron_energy_loss_rate: | |||
""" Elastic electron energy loss rate """ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What are the units of this quantity?
double elasticElectronEnergyLossRate() | ||
double normalizedElasticElectronEnergyLossRate() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These methods should probably have the except +translate_exception
specifier -- generally recommended except for trivial getters that can't throw.
Normalized elastic electron energy loss rate | ||
The elastic electron energy loss rate is normalized | ||
by dividing the concentration of electron. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Careful with the formatting here -- the second line will be seen as a continuation of the same sentence by Sphinx.
//! Get elastic electron energy loss rate (eV/s) | ||
double elasticElectronEnergyLossRate() { | ||
return concentration(m_electronSpeciesIndex) * | ||
normalizedElasticElectronEnergyLossRate(); | ||
} | ||
|
||
//! Get normalized elastic electron energy loss rate (eV-m3/kmol/s) | ||
double normalizedElasticElectronEnergyLossRate(); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need both of these methods, given the relatively simple conversion? Which one is more likely to be used? My guess would be the non-normalized version, but I could easily be convinced otherwise. Also, are you sure about the units for the first method? I think this should be an intrinsic property, so the dimensions should be power per unit mass (or possibly power per unit volume).
Also, can you write out the equation being evaluated, and say something about how that's being done? (since as you note in the PR, the finite difference scheme chosen matters)
self.phase.TPX = 1000, ct.one_atm, "O2:1, E:1e-10" | ||
r1 = self.phase.elastic_electron_energy_loss_rate | ||
c_electron = self.phase.concentrations[self.phase.species_index("E")] | ||
self.assertNear(self.phase.elastic_electron_energy_loss_rate, 4.233683742587e-05) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We've been shifting away from the unittest
style assert methods to use plain assert
statements that are handled by pytest
. Here, you can write:
self.assertNear(self.phase.elastic_electron_energy_loss_rate, 4.233683742587e-05) | |
assert self.phase.elastic_electron_energy_loss_rate == approx(4.233683742587e-05) |
vector<shared_ptr<Reaction>> reactions; | ||
for (AnyMap R : reactionsAnyMapList(*m_kinetics, m_input, m_root)) { | ||
reactions.push_back(newReaction(R, *m_kinetics)); | ||
} | ||
|
||
// add reactions to kinetics object | ||
addReactions(*m_kinetics, reactions); | ||
|
||
// init m_collisions | ||
m_collisions.resize(0); | ||
size_t i = 0; | ||
for (shared_ptr<Reaction> reaction : reactions) { | ||
if (reaction->type() == "electron-collision-plasma") { | ||
m_collisions.push_back(reaction); | ||
if (reaction->reactants == reaction->products) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of building a standalone list of Reaction
s, you could just call the existing method addReactions(*m_kinetics, m_input, m_root)
, and then use Kinetics.reaction(i)
to loop over the reaction objects and build up m_collisions
. Then, you wouldn't need the two new functions reactionsAnyMapList
and addReactions(Kinetics&, vector<shared_ptr<Reaction>>)
.
//! The kinetics object associates with ThermoPhase | ||
//! Some phase requires Kinetics to perform calculation | ||
//! such as PlasmaPhase | ||
shared_ptr<Kinetics> m_kinetics; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the Kinetics
object also holds a shared_ptr<ThermoPhase>
, this will create a reference cycle and introduce a memory leak. The solution to this problem is to only hold a weak_ptr
in one direction. I'd recommend doing this by having the ThermoPhase
object hold a weak_ptr
to the Solution
(say, m_soln
), which will also eliminate the need to inherit from enable_shared_from_this
, as you can access the shared_ptr<ThermoPhase>
from the Solution
.
shared_ptr<Kinetics> kinetics() { | ||
return m_kinetics; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Method needs a docstring.
if (!sol->thermo()->kinetics()) { | ||
// only add phases that doesn't have kinetics | ||
// The phase that has kinetics already | ||
phases.push_back(sol->thermo()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This comment could be worded more clearly. Also, I'm not sure I follow the behavior here. Shouldn't the behavior be to skip calling newKinetics
if the phase already has a Kinetics
object? The logic about adjacent phases only applies to surfaces/edges in any case.
m_kinetics = newKinetics("bulk"); | ||
m_kinetics->addThermo(shared_from_this()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a lot of this logic might be simpler if you leave initialization of the Kinetics
object to the newSolution
factory function as it originally stood. The way to connect the Kinetics
object to the PlasmaPhase
would then be to add here in initThermo
a callback to the Solution
object (see Solution.registerChangedCallback
) that would then update m_collisions
when it sees a change in the Kinetics
object. You'll also need to call removeChangedCallback
from the PlasmaPhase
destructor.
Changes proposed in this pull request
This PR change the initiation of the PlasmaPhase. The PlasmaPhase after this change takes the ElectronCollisionPlasmaRate cross section data to calculate the plasma thermal properties (specifically the elastic collision energy loss rate). The elastic collision energy loss is the energy transfer from electron to molecule when a fast electron (higher electron temperature) collides to a slower molecule (lower gas temperature). This is useful for modelling a non-equilibrium plasma which electron temperature is much higher than the gas temperature. The elastic collision reaction does not produce a new species, but can heat up the gas: e + O2 => e + O2 + heat. We check all the ElectronCollisionPlasmaReaction to find every reaction which the reactants is equal to the products in the initTherm method in PlasmaPhase. We then calculate the elastic collision energy loss using the cross section data from those reactions we found. The result is compared to BOLOS https://github.com/aluque/bolos. The grid definition and finite difference is different. The result is close when the number of grid points is high. The definition of the electron energy distribution function is different in Cantera and BOLOS. To compare the result, I made a modification to BOLOS, https://github.com/BangShiuh/bolos/tree/mod. You can reproduce the result using the scripts and data in
scripts.zip.
Future work to utilize "effective" cross section:
BOLOS calculates the elastic cross section subtracting the inelastic cross section from the effective cross section. This can be included in the parsing script, lxcat2yaml.py to obtain the elastic cross section. I think it is much simpler to use an elastic cross section in a yaml input file as it does not need further transformation like an effective cross section.
Design philosophy: the original cross section data has the collision type such as attachment, ionization, elastic,... etc. However, the collision type can be determined from the collision equation. For example, elastic collisions have the same reactants and products. Ionization collisions have an extra electron in the products. Cantera manages the reaction equations well and we should use it to our advantage to simplify the input data format.
If applicable, fill in the issue number this pull request is fixing
Closes #
If applicable, provide an example illustrating new features this pull request is introducing
Checklist
scons build
&scons test
) and unit tests address code coverage