@@ -49,7 +49,17 @@ std::vector<arma_cube> calc_electron_ion_collisions(Ions &ions);
49
49
// / @param ions
50
50
// / @param neutrals
51
51
// / @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
+
53
63
54
64
55
65
// --------------------------------------------------------------------------
@@ -132,14 +142,21 @@ void Ions::calc_electron_temperature(Neutrals neutrals, Grid grid) {
132
142
133
143
report.print (4 , " Calculating electron-neutral collisions" );
134
144
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);
138
148
Qencp = Qenc[0 ];
139
149
Qencm = Qenc[1 ];
140
150
Qenc_v = Qenc[2 ]; // Friction
141
151
}
142
152
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
+ }
143
160
144
161
electron_temperature_scgc = neutrals.temperature_scgc ;
145
162
@@ -309,11 +326,11 @@ std::vector<arma_cube> calc_electron_ion_collisions(Ions &ions){
309
326
}
310
327
311
328
// --------------------------------------------------------------------------
312
- // Calculate electron-neutral collisions
329
+ // Calculate electron-neutral elastic collisions
313
330
// --------------------------------------------------------------------------
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){
315
332
316
- std::string function = " calc_electron_neutral_collisions " ;
333
+ std::string function = " calc_electron_neutral_elastic_collisions " ;
317
334
static int iFunction = -1 ;
318
335
report.enter (function, iFunction);
319
336
@@ -382,4 +399,171 @@ std::vector<arma_cube> calc_electron_neutral_collisions(Ions &ions, Neutrals &ne
382
399
report.exit (function);
383
400
384
401
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
+ }
0 commit comments