Skip to content

Commit 293fb6c

Browse files
committed
FEAT: Electron - neutral inelastic collsisions
And change the name of the elastic collisions so they're separate...
1 parent b38bde0 commit 293fb6c

File tree

4 files changed

+212
-14
lines changed

4 files changed

+212
-14
lines changed

include/inputs.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -454,10 +454,17 @@ class Inputs {
454454
bool get_do_electron_ion_collisional_heating();
455455

456456
/**********************************************************************
457-
\brief returns settings["Sources"]["Ions"]["IncludeElectronNeutralCollisionalHeating"]
457+
\brief returns settings["Sources"]["Ions"]["IncludeElectronNeutralElasticCollisionalHeating"]
458458
\param none
459459
**/
460-
bool get_do_electron_neutral_collisional_heating();
460+
bool get_do_electron_neutral_elastic_collisional_heating();
461+
462+
/**********************************************************************
463+
\brief returns settings["Sources"]["Ions"]["IncludeElectronNeutralInelasticCollisionalHeating"]
464+
\param none
465+
**/
466+
bool get_do_electron_neutral_inelastic_collisional_heating();
467+
461468

462469
/**********************************************************************
463470
\brief returns settings["

src/calc_electron_temperature.cpp

Lines changed: 192 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,17 @@ std::vector<arma_cube> calc_electron_ion_collisions(Ions &ions);
4949
/// @param ions
5050
/// @param neutrals
5151
/// @return vector<Qencp, Qencm, Qenc_v>
52-
std::vector<arma_cube> calc_electron_neutral_collisions(Ions &ions, Neutrals &neutrals);
52+
std::vector<arma_cube> calc_electron_neutral_elastic_collisions(Ions &ions, Neutrals &neutrals);
53+
54+
/// @brief Calculates electron-neutral inelastic collisional heating
55+
/// @details From Schunk and Nagy 2009 pages 277, 282.
56+
/// This includes N2, O2 rotation, fine structure, O(1D) exitation & vibration, N2 vibration.
57+
/// See equation 15 from (Zhu, Ridley, Deng, 2016) https://doi.org/10.1016/j.jastp.2016.01.005
58+
/// @param ions
59+
/// @param neutrals
60+
/// @return vector<Qencp, Qencm, Qenc_v>
61+
std::vector<arma_cube> calc_electron_neutral_inelastic_collisions(Ions &ions, Neutrals &neutrals);
62+
5363

5464

5565
// --------------------------------------------------------------------------
@@ -132,14 +142,21 @@ void Ions::calc_electron_temperature(Neutrals neutrals, Grid grid) {
132142

133143
report.print(4, "Calculating electron-neutral collisions");
134144

135-
// electron-neutral collisions
136-
if (input.get_do_electron_neutral_collisional_heating()) {
137-
Qenc = calc_electron_neutral_collisions(*this, neutrals);
145+
// electron-neutral Elastic collisions
146+
if (input.get_do_electron_neutral_elastic_collisional_heating()) {
147+
Qenc = calc_electron_neutral_elastic_collisions(*this, neutrals);
138148
Qencp = Qenc[0];
139149
Qencm = Qenc[1];
140150
Qenc_v = Qenc[2]; // Friction
141151
}
142152

153+
// electron-neutral inelastic collisions
154+
if (input.get_do_electron_neutral_inelastic_collisional_heating()) {
155+
Qenc = calc_electron_neutral_inelastic_collisions(*this, neutrals);
156+
Qencp = Qenc[0];
157+
Qencm = Qenc[1];
158+
Qenc_v = Qenc[2]; // Friction
159+
}
143160

144161
electron_temperature_scgc = neutrals.temperature_scgc;
145162

@@ -309,11 +326,11 @@ std::vector<arma_cube> calc_electron_ion_collisions(Ions &ions){
309326
}
310327

311328
// --------------------------------------------------------------------------
312-
// Calculate electron-neutral collisions
329+
// Calculate electron-neutral elastic collisions
313330
// --------------------------------------------------------------------------
314-
std::vector<arma_cube> calc_electron_neutral_collisions(Ions &ions, Neutrals &neutrals){
331+
std::vector<arma_cube> calc_electron_neutral_elastic_collisions(Ions &ions, Neutrals &neutrals){
315332

316-
std::string function = "calc_electron_neutral_collisions";
333+
std::string function = "calc_electron_neutral_elastic_collisions";
317334
static int iFunction = -1;
318335
report.enter(function, iFunction);
319336

@@ -382,4 +399,171 @@ std::vector<arma_cube> calc_electron_neutral_collisions(Ions &ions, Neutrals &ne
382399
report.exit(function);
383400

384401
return std::vector<arma_cube> {Qencp, Qencm, Qenc_v};
385-
}
402+
}
403+
404+
// --------------------------------------------------------------------------
405+
// Calculate electron-neutral inelasticcollisions
406+
// --------------------------------------------------------------------------
407+
std::vector<arma_cube> calc_electron_neutral_inelastic_collisions(Ions &ions, Neutrals &neutrals){
408+
409+
std::string function = "calc_electron_neutral_inelastic_collisions";
410+
static int iFunction = -1;
411+
report.enter(function, iFunction);
412+
413+
// initialize & zero the quantities we need:
414+
arma_cube Qrot;
415+
Qrot.set_size(ions.density_scgc.n_rows, ions.density_scgc.n_cols, ions.density_scgc.n_slices);
416+
Qrot.zeros(); // N2, O2 roration (Shunk & Nagy pp. 277)
417+
arma_cube Qrotp = Qrot;
418+
arma_cube Qrotm = Qrot;
419+
arma_cube Qf = Qrot; //fine structure heating rate (Shunk & Nagy pp. 282)
420+
arma_cube Qfp = Qrot, Qfm = Qrot;
421+
arma_cube Qexc = Qrot; // O(1D) exitation
422+
arma_cube Qexcp = Qrot, Qexcm = Qrot;
423+
arma_cube Qvib_O2 = Qrot; // O2 vibration
424+
arma_cube logQ = Qrot, Qvib_O2p = Qrot, Qvib_O2m = Qrot;
425+
arma_cube Qvib_N2 = Qrot; // N2 vibration (Pavlov, 1998a)
426+
427+
int64_t inO2 = neutrals.get_species_id("O2");
428+
int64_t inN2 = neutrals.get_species_id("N2");
429+
int64_t inO = neutrals.get_species_id("O");
430+
431+
if ((inO == -1) || (inN2 == -1) || (inO2 == -1)) {
432+
report.error("Could not find O, N2, or O2 in neutrals species list");
433+
}
434+
435+
// some aliases for common quantities
436+
arma_cube ne = ions.density_scgc;
437+
arma_cube Te = ions.electron_temperature_scgc;
438+
arma_cube Ti = ions.temperature_scgc;
439+
arma_cube Tn = neutrals.temperature_scgc;
440+
arma_cube no2 = neutrals.species[inO2].density_scgc;
441+
arma_cube nn2 = neutrals.species[inN2].density_scgc;
442+
arma_cube no = neutrals.species[inO].density_scgc;
443+
444+
// Make sure (Ti && Te) > Tn everywhere (from GITM):
445+
arma::uvec Ti_mask = find(Ti <= Tn);
446+
arma::uvec Te_mask = find(Te <= Tn);
447+
Ti.elem(Ti_mask) = Tn.elem(Ti_mask) * 1.0001;
448+
Te.elem(Ti_mask) = Tn.elem(Te_mask) * 1.0001;
449+
450+
// N2, O2 rotation (Shunk & Nagy pp. 277)
451+
Qrot = 3.5e-14*ne*1.e-6%nn2*1.e-6%(Tn - Te)/(pow(Te,0.5))
452+
+ 5.2e-15*ne*1.e-6%no2*1.e-6%(Tn - Te)/(pow(Te, 0.5));
453+
Qrot = Qrot*1.6e-13; // eV cm-3 s -> J m-3
454+
455+
Qrotp = 3.5e-14*ne*1.e-6%nn2*1.e-6/(pow(Te,0.5)) // N2
456+
+ 5.2e-15*ne*1.e-6%no2*1.e-6/(pow(Te, 0.5)); // O2
457+
Qrotp = Qrotp*1.6e-13;
458+
Qrotm = Qrotp%Tn;
459+
460+
// fine strcture heating rate by Shunk and Nagy Page 282
461+
Qf = 0.0;
462+
arma_cube Dfine = 5.0 + exp(-326.6/Tn) + 3.0 *exp(-227.7/Tn);
463+
arma_cube s10 = 8.249e-16*pow(Te, 0.6) % exp(-227.7/Tn);
464+
Qf = ne%no * 1.e-12/Dfine%(s10%(1.-exp(98.9*(1/Te - 1/Tn)))
465+
+ 1.191e-11*(1.-exp(326.6*(1/Te - 1/Tn)))
466+
+ 1.863e-11*(1.-exp(227.7*(1/Te - 1/Tn))));
467+
Qf = -Qf*1.6e-13; // eV cm-3 s-1 -> J m-3 s-1
468+
469+
Qfp = 0.0;
470+
Qfm = Qf;
471+
472+
// O(1D) excitation
473+
arma_cube te_exc = Te.clamp(0.0, 18000.0);
474+
arma_cube dexc = 2.4e4 + 0.3*(te_exc - 1500.) - 1.947e-5*(te_exc - 1500.)%(te_exc - 4000.);
475+
Qexc = 1.57e-12*ne%no*1.e-12%exp(dexc%(te_exc - 3000.)/3000./te_exc)
476+
%(exp(-22713*(te_exc - Tn)/te_exc/Tn) - 1);
477+
Qexc = Qexc*1.6e-13; // eV cm-3 s-1 -> J m-3 s-1
478+
479+
Qexcp = 0.0;
480+
Qexcm = Qexc;
481+
482+
// O2 vibration:
483+
484+
// Calculated differently from GITM, but the result is the same...
485+
// GITM clamped e- temp before calculating, but that makles things really hard here.
486+
// Instead we will use all Te's, and then limit the outputs after.
487+
488+
arma_cube logQ = (5.0148e-31*pow(Te,9) - 1.5346e-26*pow(Te,8)
489+
+ 2.0127e-22*pow(Te,7) - 1.4791e-18*pow(Te,6)
490+
+ 6.6865e-15*pow(Te,5) - 1.9228e-11*pow(Te,4)
491+
+ 3.5187e-8*pow(Te,3) - 3.996e-5*pow(Te,2)
492+
+ 0.0267*Te - 19.9171);
493+
// GITM's Te_6000 was from 300 - 6000, which corresponds to ~-15.9 & 198.3 for logQ
494+
// Mask the values outside of this range...
495+
// TODO: Should we do it this way or just use the clamp?
496+
logQ.clamp(-15.9, 198.273);
497+
logQ.elem( find(logQ < -15.9) ).fill(-20.0);
498+
499+
500+
Qvib_O2 = ne % no2 * 1.e-12 % exp10(logQ) % (1 - exp(2239. * (1. / Te - 1. / Tn)));
501+
Qvib_O2 = -Qvib_O2*1.6e-13;
502+
Qvib_O2p = 0.;
503+
Qvib_O2m = Qvib_O2;
504+
505+
506+
// N2 vibration from Pavlov 1998a
507+
508+
// we'll use the same loop GITM uses:
509+
arma_vec Av0 = {2.025, -7.066, -8.211, -9.713, -10.353, -10.819, -10.183, -12.698, -14.710, -17.538};
510+
arma_vec Bv0 = {8.782e-4, 1.001e-2, 1.092e-2, 1.204e-2, 1.243e-2, 1.244e-2, 1.185e-2, 1.309e-2, 1.409e-2, 1.6e-2};
511+
arma_vec Cv0 = {2.954e-7, -3.066e-6, -3.369e-6, -3.732e-6, -3.850e-6, -3.771e-6, -3.570e-6, -3.952e-6, -4.249e-6, -4.916e-6};
512+
arma_vec Dv0 = {-9.562e-11, 4.436e-10, 4.891e-10, 5.431e-10, 5.6e-10, 5.385e-10, 5.086e-10, 5.636e-10, 6.058e-10, 7.128e-10};
513+
arma_vec Fv0 = {7.252e-15, -2.449e-14, -2.706e-14, -3.008e-14, -3.1e-14, -2.936e-14, -2.769e-14, -3.071e-14, -3.3e-14, -3.941e-14};
514+
515+
precision_t Av0L = -6.462;
516+
precision_t Bv0L = 3.151e-2;
517+
precision_t Cv0L = -4.075e-5;
518+
precision_t Dv0L = 2.439e-8;
519+
precision_t Fv0L = -5.479e-12;
520+
521+
arma_vec Av1 = {-3.413, -4.16, -5.193, -5.939, -8.261, -8.185, -10.823, -11.273};
522+
arma_vec Bv1 = {7.326e-3, 7.803e-3, 8.36e-3, 8.807e-3, 1.01e-2, 1.01e-2, 1.199e-2, 1.283e-2};
523+
arma_vec Cv1 = {-2.2e-6, -2.352e-6, -2.526e-6, -2.669e-6, -3.039e-6, -3.039e-6, -3.62e-6, -3.879e-6};
524+
arma_vec Dv1 = {3.128e-10, 3.352e-10, 3.606e-10, 3.806e-10, 4.318e-10, 4.318e-10, 5.159e-10, 5.534e-10};
525+
arma_vec Fv1 = {-1.702e-14, -1.828e-14, -1.968e-14, -2.073e-14, -2.347e-14, -2.347e-14, -2.81e-14, -3.016e-14};
526+
527+
arma_vec logQv0, logQv1;
528+
logQv0.set_size(10);
529+
logQv1.set_size(8);
530+
531+
double tte; // since we need the min of this & 6000 & std::min can't accept precision_t
532+
precision_t ttn, tte_6000;
533+
534+
for (int64_t iLon = 0; iLon < ions.density_scgc.n_rows; iLon++) { //nLons
535+
for (int64_t iLat = 0; iLat < ions.density_scgc.n_cols; iLat++) { // nLats
536+
for (int64_t iAlt = 0; iAlt < ions.density_scgc.n_slices; iAlt++) { // nAlts
537+
tte = Te(iLon, iLat, iAlt);
538+
ttn = Tn(iLon, iLat, iAlt);
539+
tte_6000 = std::min(tte, 6000.0);
540+
logQv0 = -20.0;
541+
logQv1 = -20.0;
542+
543+
if (tte <= 1500 && tte > 300) {
544+
logQv0(0) = Av0L + Bv0L * tte + Cv0L * pow(tte, 2) + Dv0L * pow(tte, 3) + Fv0L * pow(tte, 4) - 16.0;
545+
Qvib_N2(iLon, iLat, iAlt) = (1.0 - exp(-3353.0 / ttn)) * exp10(logQv0(0))
546+
* (1.0 - exp(3353.0 * (1.0 / tte - 1.0 / ttn)));
547+
} else if (tte > 1500) {
548+
for (int iLevel = 0; iLevel < 10; iLevel++) {
549+
logQv0(iLevel) = Av0(iLevel) + Bv0(iLevel) * tte_6000 + Cv0(iLevel) * pow(tte, 2)
550+
+ Dv0(iLevel) * pow(tte, 3) + Fv0(iLevel) * pow(tte, 4) - 16.0;
551+
Qvib_N2(iLon, iLat, iAlt) += (1.0 - exp(-3353.0 / ttn)) * exp10(logQv0(0))
552+
* (1.0 - exp((iLevel + 1) * 3353.0 * (1.0 / tte - 1.0 / ttn)));
553+
}
554+
555+
for (int iLevel = 0; iLevel < 8; iLevel++) {
556+
logQv1(iLevel) = Av1(iLevel) + Bv1(iLevel) * tte_6000 + Cv1(iLevel) * pow(tte_6000, 2)
557+
+ Dv1(iLevel) * pow(tte_6000, 3) + Fv1(iLevel) * pow(tte_6000, 4) - 16.0;
558+
Qvib_N2(iLon, iLat, iAlt) += (1.0 - exp(-3353.0 / ttn)) * exp(-3353.0 / ttn)
559+
* exp10(logQv1(iLevel))
560+
* (1.0 - exp(iLevel * 3353.0 * (1.0 / tte - 1.0 / ttn)));
561+
}
562+
}
563+
}
564+
}
565+
}
566+
567+
Qvib_N2 = -ne % nn2 * 1.e-12 % Qvib_N2 * 1.6e-13;
568+
569+
}

src/calc_ion_temperature.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ void Ions::init_ion_temperature(Neutrals neutrals, Grid grid) {
3434
||(neutrals.get_species_id("N2") == -1)){
3535
if (input.get_do_photoelectron_heating()
3636
|| input.get_do_ionization_heating()
37-
|| input.get_do_electron_neutral_collisional_heating()) {
37+
|| input.get_do_electron_neutral_elastic_collisional_heating()) {
3838
report.error("Your electron temperature sources require neutral O, O2, and N2 to be present.");
3939
}
4040
}

src/inputs.cpp

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -780,13 +780,20 @@ bool Inputs::get_do_electron_ion_collisional_heating() {
780780
}
781781

782782
// -----------------------------------------------------------------------
783-
// Return whether to include electron-neutral collisional heating
783+
// Return whether to include electron-neutral elastic collisional heating
784784
// -----------------------------------------------------------------------
785785

786-
bool Inputs::get_do_electron_neutral_collisional_heating() {
787-
return get_setting_bool("Sources", "Ions", "IncludeElectronNeutralCollisionalHeating");
786+
bool Inputs::get_do_electron_neutral_elastic_collisional_heating() {
787+
return get_setting_bool("Sources", "Ions", "IncludeElectronNeutralElasticCollisionalHeating");
788788
}
789789

790+
// -----------------------------------------------------------------------
791+
// Return whether to include electron-neutral inelastic collisional heating
792+
// -----------------------------------------------------------------------
793+
794+
bool Inputs::get_do_electron_neutral_inelastic_collisional_heating() {
795+
return get_setting_bool("Sources", "Ions", "IncludeElectronNeutralInelasticCollisionalHeating");
796+
}
790797

791798
// -----------------------------------------------------------------------
792799
// Return centripetal acceleration

0 commit comments

Comments
 (0)