cloudy  trunk
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
mole_h2.cpp
Go to the documentation of this file.
1 /* This file is part of Cloudy and is copyright (C)1978-2013 by Gary J. Ferland and
2  * others. For conditions of distribution and use see copyright notice in license.txt */
3 /*H2_ContPoint set the ipCont struc element for the H2 molecule, called by ContCreatePointers */
4 /*H2_Accel radiative acceleration due to H2 */
5 /*H2_RadPress rad pressure due to h2 lines called in PresTotCurrent */
6 /*H2_InterEnergy internal energy of H2 called in PresTotCurrent */
7 /*H2_RT_diffuse do emission from H2 - called from RT_diffuse */
8 /*H2_itrzn - average number of H2 pop evaluations per zone */
9 /*H2_RTMake do RT for H2 - called from RT_line_all */
10 /*H2_RT_tau_inc increment optical depth for the H2 molecule, called from RT_tau_inc */
11 /*H2_LineZero initialize optical depths in H2, called from RT_tau_init */
12 /*H2_RT_tau_reset the large H2 molecule, called from RT_tau_reset */
13 /*H2_Colden maintain H2 column densities within X */
14 /*H2_LevelPops do level populations for H2, called by Hydrogenic */
15 /*H2_Level_low_matrix evaluate CO rotation cooling */
16 /*H2_cooling evaluate cooling and heating due to H2 molecule */
17 /*H2_X_coll_rate_evaluate find collisional rates within X */
18 /*cdH2_colden return column density in H2, negative -1 if cannot find state,
19  * header is cddrive */
20 /*H2_DR choose next zone thickness based on H2 big molecule */
21 /* turn this flag on to do minimal debug print of pops */
22 #define PRT_POPS false
23 /* this is limit to number of loops over H2 pops before giving up */
24 #define LIM_H2_POP_LOOP 10
25 /* this is a typical dissociation cross section (cm2) for H2 + Hnu -> 2H + ke */
26 /* >>chng 05 may 11, had been 2.5e-19 */
27 #define H2_DISS_ALLISON_DALGARNO 6e-19f
28 #include "cddefines.h"
29 #include "cddrive.h"
30 #include "physconst.h"
31 #include "taulines.h"
32 #include "atoms.h"
33 #include "conv.h"
34 #include "secondaries.h"
35 #include "pressure.h"
36 #include "trace.h"
37 #include "hmi.h"
38 #include "hextra.h"
39 #include "mole.h"
40 #include "rt.h"
41 #include "radius.h"
42 #include "ipoint.h"
43 #include "phycon.h"
44 #include "thermal.h"
45 #include "dense.h"
46 #include "rfield.h"
47 #include "lines_service.h"
48 #include "h2.h"
49 #include "h2_priv.h"
50 
53 
55 {
56  DEBUG_ENTRY( "diatomics::H2_X_sink_and_source()" );
57 
58  /* this is total density of all colliders, is only used for collisional dissociation
59  * rates for H2 are not included here, will be added separately*/
62  dense.eden;
63 
64  for( long ipHi=0; ipHi<nLevels_per_elec[0]; ++ipHi )
65  {
66  H2_X_source[ipHi] = 0.;
67  H2_X_sink[ipHi] = 0.;
68  }
69 
70  double source_so_far = 0.;
71  double sink_so_far = spon_diss_tot * H2_den_s;
72  double pop_tot = 0.;
73 
74  for( long ipHi=0; ipHi < nLevels_per_elec[0]; ++ipHi )
75  {
76  /* array of energy sorted indices within X */
77  long iVibHi = ipVib_H2_energy_sort[ipHi];
78  long iRotHi = ipRot_H2_energy_sort[ipHi];
79 
80  /* count formation from grains and H- as a collisional formation process */
81  /* cm-3 s-1, evaluated in mole_H2_form */
82  H2_X_source[ipHi] += H2_X_formation[iVibHi][iRotHi];
83 
84  /*>>chng 05 sep 18, GS, H2 + e = H- + H*, H2_X_Hmin_back has units s-1 */
85  H2_X_sink[ipHi] += H2_X_Hmin_back[iVibHi][iRotHi];
86 
87  /* this represents collisional dissociation into continuum of X,
88  * rates are just guesses */
91 
92  /*>>chng 05 jul 20, GS, collisional dissociation with H2g and H2s are added here*/
93  H2_X_sink[ipHi] += hmi.H2_total*
95 
96  /* rate (s-1) out of this level */
98  {
99  H2_X_sink[ipHi] += Cont_Dissoc_Rate[0][iVibHi][iRotHi];
100  }
101  else
102  H2_X_sink[ipHi] += rfield.flux_accum[H2_ipPhoto[iVibHi][iRotHi]-1]*H2_DISS_ALLISON_DALGARNO;
103 
104  source_so_far += H2_X_source[ipHi];
105  sink_so_far += H2_X_sink[ipHi] * states[ipHi].Pop();
106  pop_tot += states[ipHi].Pop();
107  }
108 
109  // cm-3 s-1
110  double sink_tot = mole.sink_rate_tot(sp) * pop_tot;
111  // cm-3 s-1
112  double sink_left = sink_tot - sink_so_far;
113  // divide by population in X to get units s-1
114  ASSERT( pop_tot > 1e-10 * (*dense_total) );
115  sink_left /= pop_tot;
116  if( sink_left >= 0. )
117  {
118  for( long ipHi=0; ipHi < nLevels_per_elec[0]; ++ipHi )
119  H2_X_sink[ipHi] += sink_left;
120  }
121  else
122  {
123  for( long ipHi=0; ipHi < nLevels_per_elec[0]; ++ipHi )
124  H2_X_sink[ipHi] *= sink_tot / sink_so_far;
125  }
126 
127  double sink_so_far_s = 0.;
128  double pop_tot_s = 0.;
129  for( long ipHi=0; ipHi < nLevels_per_elec[0]; ++ipHi )
130  {
131  if( states[ipHi].energy().WN() > ENERGY_H2_STAR && hmi.lgLeiden_Keep_ipMH2s )
132  {
133  sink_so_far_s += H2_X_sink[ipHi] * states[ipHi].Pop();
134  pop_tot_s += states[ipHi].Pop();
135  }
136  }
137  // cm-3 s-1
138  fixit(); // kill the second term (sp_star) when H2* is killed in chemistry
139  double sink_tot_s = mole.sink_rate_tot(sp) * pop_tot_s + mole.sink_rate_tot(sp_star) * pop_tot_s;
140  // cm-3 s-1
141  double sink_left_s = sink_tot_s - sink_so_far_s;
142  // divide by population in X to get units s-1
143  if( pop_tot_s > 1e-30 * (*dense_total) )
144  sink_left_s /= pop_tot_s;
145  else
146  sink_left_s = 0.;
147  if( sink_left_s >= 0. )
148  {
149  for( long ipHi=0; ipHi < nLevels_per_elec[0]; ++ipHi )
150  {
151  if( states[ipHi].energy().WN() > ENERGY_H2_STAR && hmi.lgLeiden_Keep_ipMH2s )
152  H2_X_sink[ipHi] += sink_left_s;
153  }
154  }
155 
156  fixit(); // kill the second term (sp_star) when H2* is killed in chemistry
157  double source_tot = mole.source_rate_tot(sp) + mole.source_rate_tot(sp_star);
158  double source_left = source_tot - source_so_far;
159  if( source_left >= 0. )
160  {
161  for( long ipHi=0; ipHi < nLevels_per_elec[0]; ++ipHi )
162  {
163  long iElec = states[ipHi].n();
164  long iVib = states[ipHi].v();
165  long iRot = states[ipHi].J();
166  H2_X_source[ipHi] += source_left * H2_populations_LTE[iElec][iVib][iRot];
167  }
168  }
169 
170  return;
171 }
172 
173 /*H2_X_coll_rate_evaluate find collisional rates within X -
174  * this is one time upon entry into H2_LevelPops */
176 {
177  DEBUG_ENTRY( "diatomics::H2_X_coll_rate_evaluate()" );
178 
179  /* set collider density
180  * the colliders are:
181  * [0] = H
182  * [1], [5] = He (old and new cs data)
183  * [2] = H2 ortho
184  * [3] = H2 para
185  * [4] = H+ + H3+ */
186  /* atomic hydrogen */
188  /* all ortho h2 */
189  /* He - H2 */
191  /* H2 - H2(ortho) */
193  /* all para H2 */
195  /* protons - ionized hydrogen */
197  /* H3+ - assume that H3+ has same rates as proton */
199 
201 
202  if( nTRACE >= n_trace_full )
203  {
204  fprintf(ioQQQ," Collider densities are:");
205  for( long nColl=0; nColl<N_X_COLLIDER; ++nColl )
206  {
207  fprintf(ioQQQ,"\t%.3e", collider_density[nColl]);
208  }
209  fprintf(ioQQQ,"\n");
210  }
211 
213 
214  for( long ipHi=0; ipHi < nLevels_per_elec[0]; ++ipHi )
215  {
216  if( lgColl_deexec_Calc )
217  {
218  /* excitation within X due to thermal particles */
219  for( long ipLo=0; ipLo<ipHi; ++ipLo )
220  {
221  /* collisional interactions with upper levels within X */
222  double colldown = 0.;
223  mr3ci CollRate = CollRateCoeff.begin(ipHi, ipLo);
224  for( long nColl=0; nColl<N_X_COLLIDER; ++nColl )
225  {
226  /* downward collision rate, units s-1 */
227  colldown += CollRate[nColl]*collider_density[nColl];
228  ASSERT( CollRate[nColl]*collider_density[nColl] >= 0. );
229  }
230  /* rate in from upper level, units cm-3 s-1 */
231  H2_X_coll_rate[ipHi][ipLo] += colldown;
232  }/* end loop over ipLo */
233  }
234  }
235 
236  return;
237 }
238 
239 /*H2_itrzn - average number of H2 pop evaluations per zone */
240 double diatomics::H2_itrzn( void )
241 {
242  if( lgEnabled && nH2_zone>0 )
243  {
244  return( (double)nH2_pops / (double)nH2_zone );
245  }
246  else
247  {
248  return 0.;
249  }
250 }
251 
252 /* set the ipCont struc element for the H2 molecule, called by ContCreatePointers */
254 {
255  if( !lgEnabled )
256  return;
257 
258  DEBUG_ENTRY( "H2_ContPoint()" );
259 
260  for( TransitionList::iterator tr = trans.begin(); tr != rad_end; ++tr )
261  {
262  ASSERT( (*tr).Emis().Aul() > 0. );
263  (*tr).ipCont() = ipLineEnergy( (*tr).EnergyRyd(), label.c_str(), 0 );
264  (*tr).Emis().ipFine() = ipFineCont( (*tr).EnergyRyd());
265  }
266  return;
267 }
268 
269 /* ===================================================================== */
270 /* radiative acceleration due to H2 called in rt_line_driving */
272 {
273  /* >>chng 05 jan 26, pops now set to LTE for small abundance case, so do this */
274  if( !lgEnabled /*|| !nCall_this_zone*/ )
275  return 0.;
276 
277  DEBUG_ENTRY( "H2_Accel()" );
278 
279  /* this routine computes the line driven radiative acceleration */
280 
281  double drive = 0.;
282 
283  for( TransitionList::iterator tr = trans.begin(); tr != rad_end; ++tr )
284  {
285  ASSERT( (*tr).ipCont() > 0 );
286  drive += (*tr).Emis().pump() * (*tr).Emis().PopOpc() * (*tr).EnergyErg();
287  }
288 
289  return drive;
290 }
291 
292 /* ===================================================================== */
293 /* rad pressure due to H2 lines called in PresTotCurrent */
295 {
296  /* will be used to check on size of opacity, was capped at this value */
297  realnum smallfloat=SMALLFLOAT*10.f;
298 
299  /* radiation pressure sum is expensive - do not evaluate if we did not
300  * bother evaluating large molecule */
301  if( !lgEnabled || !nCall_this_zone )
302  return 0.;
303 
304  DEBUG_ENTRY( "H2_RadPress()" );
305 
306  realnum doppler_width = GetDopplerWidth( mass_amu );
307  double press = 0.;
308 
309  for( TransitionList::iterator tr = trans.begin(); tr != rad_end; ++tr )
310  {
311  ASSERT( (*tr).ipCont() > 0 );
312  if( (*(*tr).Hi()).Pop() > smallfloat && (*tr).Emis().PopOpc() > smallfloat )
313  {
314  press += PressureRadiationLine( *tr, doppler_width );
315  }
316  }
317 
318  if(nTRACE >= n_trace_full)
319  fprintf(ioQQQ,
320  " H2_RadPress returns, radiation pressure is %.2e\n",
321  press );
322  return press;
323 }
324 
325 #if 0
326 /* ===================== */
327 /* internal energy of H2 */
328 double diatomics::H2_InterEnergy(void)
329 {
330  /* >>chng 05 jan 26, pops now set to LTE for small abundance case, so do this */
331  if( !lgEnabled /*|| !nCall_this_zone*/ )
332  return 0.;
333 
334  DEBUG_ENTRY( "H2_InterEnergy()" );
335 
336  double energy = 0.;
337  for( qList::iterator st = trans.states.begin(); st != trans.states.end(); ++st )
338  energy += st->Pop() * st->energy();
339 
340  return energy;
341 }
342 #endif
343 
344 /*H2_RT_diffuse do emission from H2 - called from RT_diffuse */
346 {
347  if( !lgEnabled || !nCall_this_zone )
348  return;
349 
350  DEBUG_ENTRY( "H2_RT_diffuse()" );
351 
352  for( TransitionList::iterator tr = trans.begin(); tr != rad_end; ++tr )
353  {
354  qList::iterator Hi = (*tr).Hi();
355  if( (*Hi).n() > 0 )
356  continue;
357  (*tr).outline_resonance();
358  }
359 
360  return;
361 }
362 
363 /* RT for H2 lines */
365 {
366  if( !lgEnabled )
367  return;
368 
369  DEBUG_ENTRY( "H2_RTMake()" );
370 
371  realnum doppler_width = GetDopplerWidth( mass_amu );
372  for( TransitionList::iterator tr = trans.begin(); tr != rad_end; ++tr )
373  {
374  /* >>chng 03 jun 18, added 4th parameter in call to this routine - says to not
375  * include self-shielding of line across this zone. This introduces a dr dependent
376  * variation in the line pumping rate, which made H2 abundance fluctuate due to
377  * Solomon process having slight dr-caused mole. */
378  RT_line_one( *tr, false, 0.f, doppler_width );
379  }
380 
381  return;
382 }
383 
384 /* increment optical depth for the H2 molecule, called from RT_tau_inc which is called by cloudy,
385  * one time per zone */
387 {
388  /* >>chng 05 jan 26, now use LTE populations for small H2 abundance case, since electronic
389  * lines become self-shielding surprisingly quickly */
390  if( !lgEnabled /*|| !nCall_this_zone*/ )
391  return;
392 
393  DEBUG_ENTRY( "H2_RT_tau_inc()" );
394 
395  /* remember largest and smallest chemistry renormalization factor -
396  * if both networks are parallel will be unity,
397  * but only do this after we have stable solution */
398  if( nzone > 0 && nCall_this_iteration>2 )
399  {
402  }
403 
404  realnum doppler_width = GetDopplerWidth( mass_amu );
405  for( TransitionList::iterator tr = trans.begin(); tr != rad_end; ++tr )
406  {
407  ASSERT( (*tr).ipCont() > 0 );
408  RT_line_one_tauinc( *tr,-9, -9, -9, -9, doppler_width );
409  }
410 
411  return;
412 }
413 
414 
415 /* initialize optical depths in H2, called from RT_tau_init */
417 {
418  if( !lgEnabled )
419  return;
420 
421  DEBUG_ENTRY( "H2_LineZero()" );
422 
423  for( TransitionList::iterator tr = trans.begin(); tr != rad_end; ++tr )
424  {
425  (*tr).Zero();
426  }
427 
428  return;
429 }
430 
431 /* the large H2 molecule, called from RT_tau_reset */
433 {
434  if( !lgEnabled )
435  return;
436 
437  DEBUG_ENTRY( "H2_RT_tau_reset()" );
438 
439  for( TransitionList::iterator tr = trans.begin(); tr != rad_end; ++tr )
440  {
441  RT_line_one_tau_reset( *tr );
442  }
443 
444  return;
445 }
446 
447 /*H2_Level_low_matrix evaluate lower populations within X */
449  /* total abundance within matrix */
450  realnum abundance )
451 {
452  /* will need to MALLOC space for these but only on first call */
453  bool lgDoAs;
454  int nNegPop;
455  bool lgDeBug,
456  lgZeroPop;
457  double rot_cooling , dCoolDT;
458 
459  DEBUG_ENTRY( "H2_Level_low_matrix()" );
460 
461  /* option to not use the matrix */
462  if( nXLevelsMatrix <= 1 )
463  {
464  return;
465  }
466 
467  if( lgFirst )
468  {
469  /* check that not more levels than there are in X */
471  {
472  /* number is greater than number of levels within X */
473  fprintf( ioQQQ,
474  " The total number of levels used in the matrix solver must be <= %li, the number of levels within X.\n Sorry.\n",
475  nLevels_per_elec[0]);
477  }
478  /* will never do this again */
479  lgFirst = false;
480  /* remember how much space we malloced in case ever called with more needed */
481  /* >>chng 05 jan 19, allocate max number of levels
482  ndimMalloced = nXLevelsMatrix;*/
484  /* allocate the 1D arrays*/
485  excit.resize( ndimMalloced );
486  stat_levn.resize( ndimMalloced );
487  pops.resize( ndimMalloced );
488  create.resize( ndimMalloced );
489  destroy.resize( ndimMalloced );
490  depart.resize( ndimMalloced );
491  /* create space for the 2D arrays */
492  AulPump = ((double **)MALLOC((size_t)(ndimMalloced)*sizeof(double *)));
493  CollRate_levn = ((double **)MALLOC((size_t)(ndimMalloced)*sizeof(double *)));
494  AulDest = ((double **)MALLOC((size_t)(ndimMalloced)*sizeof(double *)));
495  AulEscp = ((double **)MALLOC((size_t)(ndimMalloced)*sizeof(double *)));
496  col_str = ((double **)MALLOC((size_t)(ndimMalloced)*sizeof(double *)));
497  for( long i=0; i< ndimMalloced; ++i )
498  {
499  AulPump[i] = ((double *)MALLOC((size_t)(ndimMalloced)*sizeof(double )));
500  CollRate_levn[i] = ((double *)MALLOC((size_t)(ndimMalloced)*sizeof(double )));
501  AulDest[i] = ((double *)MALLOC((size_t)(ndimMalloced)*sizeof(double )));
502  AulEscp[i] = ((double *)MALLOC((size_t)(ndimMalloced)*sizeof(double )));
503  col_str[i] = ((double *)MALLOC((size_t)(ndimMalloced)*sizeof(double )));
504  }
505 
506  /* the statistical weights of the levels
507  * and excitation potentials of each level relative to ground */
508  for( long j=0; j < ndimMalloced; j++ )
509  {
510  /* statistical weights for each level */
511  stat_levn[j] = states[j].g();
512  /* excitation energy of each level relative to ground, in K */
513  excit[j] = states[j].energy().K();
514  }
515  }
516  /* end malloc space and creating constant terms */
517 
518  /* this is test for call with too many rotation levels to handle -
519  * logic needs for largest model atom to be called first */
521  {
522  fprintf(ioQQQ," H2_Level_low_matrix has been called with the number of rotor levels greater than space allocated.\n");
524  }
525 
526  /* all elements are used, and must be set to zero */
527  for( long i=0; i < nXLevelsMatrix; i++ )
528  {
529  pops[i] = 0.;
530  depart[i] = 0;
531  for( long j=0; j < nXLevelsMatrix; j++ )
532  {
533  col_str[j][i] = 0.;
534  }
535  }
536 
537  /* do we need to reevaluate radiative quantities? only do this one time per zone */
538  if( nzone!=nzoneAsEval || iteration!=iterationAsEval || nXLevelsMatrix!=levelAsEval)
539  {
540  lgDoAs = true;
541  nzoneAsEval = nzone;
545  }
546  else
547  {
548  lgDoAs = false;
549  }
550 
551  /* all elements are used, and must be set to zero */
552  if( lgDoAs )
553  {
554  for( long i=0; i < nXLevelsMatrix; i++ )
555  {
556  pops[i] = 0.;
557  depart[i] = 0;
558  for( long j=0; j < nXLevelsMatrix; j++ )
559  {
560  AulEscp[j][i] = 0.;
561  AulDest[j][i] = 0.;
562  AulPump[j][i] = 0.;
563  CollRate_levn[j][i] = 0.;
564  }
565  }
566  }
567 
568  /* find all radiative interactions within matrix, and between
569  * matrix and upper X and excited electronic states */
570  for( long ilo=0; ilo < nXLevelsMatrix; ilo++ )
571  {
572  long iRot = ipRot_H2_energy_sort[ilo];
573  long iVib = ipVib_H2_energy_sort[ilo];
574 
575  /* H2_X_sink[ilo] includes all processes that destroy H2 in one step,
576  * these include cosmic ray ionization and dissociation, photodissociation,
577  * BUT NOT THE SOLOMON process, which, directly, only goes to excited
578  * electronic states */
579  destroy[ilo] = H2_X_sink[ilo];
580 
581  /* rates H2 is created from grains and H- units cm-3 s-1, evaluated in mole_H2_form */
582  create[ilo] = H2_X_source[ilo];
583 
584  /* this loop does radiative decays from upper states inside matrix,
585  * and upward pumps within matrix region into this lower level */
586  if( lgDoAs )
587  {
588  for( long ihi=ilo+1; ihi<nXLevelsMatrix; ++ihi )
589  {
590  long iRotHi = ipRot_H2_energy_sort[ihi];
591  long iVibHi = ipVib_H2_energy_sort[ihi];
592  ASSERT( states[ihi].energy().WN() <= states[nXLevelsMatrix-1].energy().WN() );
593  /* general case - but line may not actually exist */
594  if( (abs(iRotHi-iRot)==2 || (iRotHi-iRot)==0 ) && (iVib<=iVibHi) )
595  {
596  if( lgH2_radiative[ihi][ilo] )
597  {
598  const TransitionList::iterator&tr = trans.begin()+ ipTransitionSort[ihi][ilo] ;
599  ASSERT( (*tr).ipCont() > 0 );
600 
601  /* NB - the destruction probability is included in
602  * the total and the destruction is set to zero
603  * since we want to only count one ots rate, in
604  * main calling routine, and do not want matrix
605  * solver below to include it */
606  AulEscp[ihi][ilo] = (*tr).Emis().Aul()*(
607  (*tr).Emis().Pesc() +
608  (*tr).Emis().Pelec_esc());
609  AulDest[ihi][ilo] = (*tr).Emis().Aul()*(*tr).Emis().Pdest();
610  AulPump[ilo][ihi] = (*tr).Emis().pump();
611  }
612  }
613  }
614  }
615 
616  double rateout = 0.;
617  double ratein = 0.;
618  /* now do all levels within X, which are above nXLevelsMatrix,
619  * the highest level inside the matrix */
620  for( long ihi=nXLevelsMatrix; ihi<nLevels_per_elec[0]; ++ihi )
621  {
622  long iRotHi = ipRot_H2_energy_sort[ihi];
623  long iVibHi = ipVib_H2_energy_sort[ihi];
624  if( (abs(iRotHi-iRot)==2 || (iRotHi-iRot)==0 ) && (iVib<=iVibHi) )
625  {
626  if( lgH2_radiative[ihi][ilo] )
627  {
628  const TransitionList::iterator&tr = trans.begin()+ ipTransitionSort[ihi][ilo] ;
629  ASSERT( (*tr).ipCont() > 0 );
630 
631  /* these will enter as net creation terms in creation vector, with
632  * units cm-3 s-1
633  * radiative transitions from above the matrix within X */
634  ratein +=
635  (*(*tr).Hi()).Pop() * (
636  (*tr).Emis().Aul()*( (*tr).Emis().Pesc() + (*tr).Emis().Pelec_esc() + (*tr).Emis().Pdest() ) +
637  (*tr).Emis().pump() * (*(*tr).Lo()).g() / (*(*tr).Hi()).g() );
638  /* rate out has units s-1 - destroys current lower level */
639  rateout +=
640  (*tr).Emis().pump();
641  }
642  }
643  }
644 
645  /* all states above the matrix but within X */
646  create[ilo] += ratein;
647 
648  /* rates out of matrix into levels in X but above matrix */
649  destroy[ilo] += rateout;
650 
651  /* Solomon process, this sum dos all pump and decays from all electronic excited states */
652  /* radiative rates [cm-3 s-1] from electronic excited states into X only vibration and rot */
653  create[ilo] += H2_X_rate_from_elec_excited[iVib][iRot];
654 
655  /* radiative & cosmic ray rates [s-1] to electronic excited states from X only vibration and rot */
656  destroy[ilo] += H2_X_rate_to_elec_excited[iVib][iRot];
657  }
658 
659  /* this flag set with atom H2 trace matrix */
660  if( nTRACE >= n_trace_matrix )
661  lgDeBug = true;
662  else
663  lgDeBug = false;
664 
665  /* now evaluate the rates for all transitions within matrix */
666  for( long ilo=0; ilo < nXLevelsMatrix; ilo++ )
667  {
668  long iRot = ipRot_H2_energy_sort[ilo];
669  long iVib = ipVib_H2_energy_sort[ilo];
670  if(lgDeBug)fprintf(ioQQQ,"DEBUG H2_Level_low_matrix, ilo=%li",ilo);
671  for( long ihi=ilo+1; ihi < nXLevelsMatrix; ihi++ )
672  {
673  long iRotHi = ipRot_H2_energy_sort[ihi];
674  long iVibHi = ipVib_H2_energy_sort[ihi];
675  /* >>chng 05 may 31, replace with simple expresion */
676  CollRate_levn[ihi][ilo] = H2_X_coll_rate[ihi][ilo];
677 
678  if(lgDeBug)fprintf(ioQQQ,"\t%.1e",CollRate_levn[ihi][ilo]);
679 
680  /* now get upward excitation rate - units s-1 */
681  CollRate_levn[ilo][ihi] = CollRate_levn[ihi][ilo]*
682  H2_Boltzmann[0][iVibHi][iRotHi]/SDIV(H2_Boltzmann[0][iVib][iRot])*
683  H2_stat[0][iVibHi][iRotHi] /
684  H2_stat[0][iVib][iRot];
685  }
686  if(lgDeBug)fprintf(ioQQQ,"\n");
687 
688  /* now do all collisions for levels within X, which are above nXLevelsMatrix,
689  * the highest level inside the matrix */
690  for( long ihi=nXLevelsMatrix; ihi<nLevels_per_elec[0]; ++ihi )
691  {
692  long iRotHi = ipRot_H2_energy_sort[ihi];
693  long iVibHi = ipVib_H2_energy_sort[ihi];
694  /* first do downward deexcitation rate */
695  /* >>chng 04 sep 14, do all levels */
696  /* >>chng 05 may 31, use summed rate */
697  double ratein = H2_X_coll_rate[ihi][ilo];
698  if(lgDeBug)fprintf(ioQQQ,"\t%.1e",ratein);
699 
700  /* now get upward excitation rate */
701  double rateout = ratein *
702  H2_Boltzmann[0][iVibHi][iRotHi]/SDIV(H2_Boltzmann[0][iVib][iRot])*
703  H2_stat[0][iVibHi][iRotHi]/H2_stat[0][iVib][iRot];
704 
705  /* these are general entries and exits going into vector */
706  create[ilo] += ratein * states[ihi].Pop();
707  destroy[ilo] += rateout;
708  }
709  if(lgDeBug)fprintf(ioQQQ,"\n");
710  }
711 
712  /* H2 grain interactions */
713  {
714  for( long ihi=2; ihi < nXLevelsMatrix; ihi++ )
715  {
716  long iVibHi = ipVib_H2_energy_sort[ihi];
717  long iRotHi = ipRot_H2_energy_sort[ihi];
718 
719  /* collisions with grains goes to either J=1 or J=0 depending on
720  * spin of upper level - this conserves op ratio - following
721  * var is 1 if ortho, 0 if para, so this conserves op ratio
722  * units are s-1 */
723  CollRate_levn[ihi][H2_lgOrtho[0][iVibHi][iRotHi]] += rate_grain_op_conserve;
724  }
725 
726  /* H2 ortho - para conversion on grain surface,
727  * rate (s-1) all v,J levels go to 0 or 1 */
728  CollRate_levn[1][0] +=
730  }
731 
732  /* now all levels in X above the matrix */
733  for( long ihi=nXLevelsMatrix; ihi<nLevels_per_elec[0]; ++ihi )
734  {
735  long iVibHi = ipVib_H2_energy_sort[ihi];
736  long iRotHi = ipRot_H2_energy_sort[ihi];
737 
738  /* these collisions all go into 0 or 1 depending on whether upper level was ortho or para
739  * units are cm-3 s-1 - rate new molecules appear in matrix */
740  create[H2_lgOrtho[0][iVibHi][iRotHi]] += states[ihi].Pop() * rate_grain_op_conserve;
741  }
742 
743  /* debug print individual contributors to matrix elements */
744  {
745  enum {DEBUG_LOC=false};
746  if( DEBUG_LOC || lgDeBug)
747  {
748  fprintf(ioQQQ,"DEBUG H2 matexcit");
749  for(long ilo=0; ilo<nXLevelsMatrix; ++ilo )
750  {
751  fprintf(ioQQQ,"\t%li",ilo );
752  }
753  fprintf(ioQQQ,"\n");
754  for(long ihi=0; ihi<nXLevelsMatrix;++ihi)
755  {
756  fprintf(ioQQQ,"\t%.2e",excit[ihi] );
757  }
758  fprintf(ioQQQ,"\n");
759  for(long ihi=0; ihi<nXLevelsMatrix;++ihi)
760  {
761  fprintf(ioQQQ,"\t%.2e",stat_levn[ihi] );
762  }
763  fprintf(ioQQQ,"\n");
764 
765  fprintf(ioQQQ,"AulEscp[n][]\\[][n] = Aul*Pesc\n");
766  for(long ilo=0; ilo<nXLevelsMatrix; ++ilo )
767  {
768  fprintf(ioQQQ,"\t%li",ilo );
769  }
770  fprintf(ioQQQ,"\n");
771  for(long ihi=0; ihi<nXLevelsMatrix;++ihi)
772  {
773  fprintf(ioQQQ,"%li", ihi);
774  for(long ilo=0; ilo<nXLevelsMatrix; ++ilo )
775  {
776  fprintf(ioQQQ,"\t%.2e",AulEscp[ilo][ihi] );
777  }
778  fprintf(ioQQQ,"\n");
779  }
780 
781  fprintf(ioQQQ,"AulPump [n][]\\[][n]\n");
782  for(long ilo=0; ilo<nXLevelsMatrix; ++ilo )
783  {
784  fprintf(ioQQQ,"\t%li",ilo );
785  }
786  fprintf(ioQQQ,"\n");
787  for(long ihi=0; ihi<nXLevelsMatrix;++ihi)
788  {
789  fprintf(ioQQQ,"%li", ihi);
790  for(long ilo=0; ilo<nXLevelsMatrix; ++ilo )
791  {
792  fprintf(ioQQQ,"\t%.2e",AulPump[ihi][ilo] );
793  }
794  fprintf(ioQQQ,"\n");
795  }
796 
797  fprintf(ioQQQ,"CollRate_levn [n][]\\[][n]\n");
798  for( long ilo=0; ilo<nXLevelsMatrix; ++ilo )
799  {
800  fprintf(ioQQQ,"\t%li",ilo );
801  }
802  fprintf(ioQQQ,"\n");
803  for( long ihi=0; ihi<nXLevelsMatrix;++ihi)
804  {
805  fprintf(ioQQQ,"%li", ihi);
806  for( long ilo=0; ilo<nXLevelsMatrix; ++ilo )
807  {
808  fprintf(ioQQQ,"\t%.2e",CollRate_levn[ihi][ilo] );
809  }
810  fprintf(ioQQQ,"\n");
811  }
812  fprintf(ioQQQ,"SOURCE");
813  for( long ihi=0; ihi<nXLevelsMatrix;++ihi)
814  {
815  fprintf(ioQQQ,"\t%.2e",create[ihi]);
816  }
817  fprintf(ioQQQ,"\nSINK");
818  for( long ihi=0; ihi<nXLevelsMatrix;++ihi)
819  {
820  fprintf(ioQQQ,"\t%.2e",destroy[ihi]);
821  }
822  fprintf(ioQQQ,"\n");
823  }
824  }
825 
826  atom_levelN(
827  /* number of levels */
828  nXLevelsMatrix,
829  abundance,
830  &stat_levn[0],
831  &excit[0],
832  'K',
833  &pops[0],
834  &depart[0],
835  /* net transition rate, A * escape prob, s-1, indices are [upper][lower] */
836  &AulEscp,
837  /* col str from high to low */
838  &col_str,
839  &AulDest,
840  &AulPump,
841  &CollRate_levn,
842  &create[0],
843  &destroy[0],
844  /* say that we have evaluated the collision rates already */
845  true,
846  &rot_cooling,
847  &dCoolDT,
848  " H2 ",
849  /* nNegPop positive if negative pops occurred, negative if too cold */
850  &nNegPop,
851  &lgZeroPop,
852  lgDeBug );/* option to print stuff - set to true for debug printout */
853 
854  for( long i=0; i< nXLevelsMatrix; ++i )
855  {
856  states[i].Pop() = pops[i];
857  }
858 
859  if( 0 && nTRACE >= n_trace_full)
860  {
861  /* print pops that came out of matrix */
862  fprintf(ioQQQ,"\n DEBUG H2_Level_lowJ dense_total: %.3e matrix rel pops\n", *dense_total);
863  fprintf(ioQQQ,"v\tJ\tpop\n");
864  for( long i=0; i<nXLevelsMatrix; ++i )
865  {
866  long iRot = ipRot_H2_energy_sort[i];
867  long iVib = ipVib_H2_energy_sort[i];
868  fprintf(ioQQQ,"%3li\t%3li\t%.3e\t%.3e\t%.3e\n",
869  iVib , iRot , states[i].Pop(), create[i] , destroy[i]);
870  }
871  }
872 
873  /* nNegPop positive if negative pops occurred, negative if too cold */
874  if( nNegPop > 0 )
875  {
876  fprintf(ioQQQ," H2_Level_low_matrix called atom_levelN which returned negative populations.\n");
877  ConvFail( "pops" , "H2" );
878  }
879  return;
880 }
881 /* do level populations for H2, called by Hydrogenic after ionization and H chemistry
882  * has been recomputed */
883 void diatomics::H2_LevelPops( bool &lgPopsConverged, double &old_val, double &new_val )
884 {
885  DEBUG_ENTRY( "H2_LevelPops()" );
886 
887  /* H2 not on, so space not allocated and return,
888  * also return if calculation has been declared a failure */
889  if( !lgEnabled || lgAbort )
890  {
891  // need to do this even if not doing big model
893  return;
894  }
895 
896  double old_solomon_rate=-1.;
897  long int n_pop_oscil = 0;
898  int kase=0;
899  bool lgConv_h2_soln,
900  lgPopsConv_total,
901  lgPopsConv_relative,
902  lgHeatConv,
903  lgSolomonConv,
904  lgOrthoParaRatioConv;
905  double quant_old=-1.,
906  quant_new=-1.;
907 
908  bool lgH2_pops_oscil=false,
909  lgH2_pops_ever_oscil=false;
910 
911  /* keep track of changes in population */
912  double PopChgMax_relative=0. , PopChgMaxOld_relative=0., PopChgMax_total=0., PopChgMaxOld_total=0.;
913  long int iRotMaxChng_relative , iVibMaxChng_relative,
914  iRotMaxChng_total , iVibMaxChng_total,
915  nXLevelsMatrix_save;
916  double popold_relative , popnew_relative , popold_total , popnew_total;
917  /* reason not converged */
918  char chReason[100];
919 
920  /* these are convergence criteria - will be increased during search phase */
921  double converge_pops_relative=1e-2,
922  converge_pops_total=1e-3,
923  converge_ortho_para=1e-2;
924 
925  double dens_rel_to_lim_react = mole.species[sp->index].xFracLim;
926 
927  if(nTRACE >= n_trace_full )
928  {
929  fprintf(ioQQQ,
930  "\n***************H2_LevelPops %s call %li this iteration, zone is %.2f, H2/H:%.e Te:%e ne:%e\n",
931  label.c_str(),
933  fnzone,
934  dens_rel_to_lim_react,
935  phycon.te,
936  dense.eden
937  );
938  }
939  else if( nTRACE >= n_trace_final )
940  {
941  static long int nzone_prt=-1;
942  if( nzone!=nzone_prt )
943  {
944  nzone_prt = nzone;
945  fprintf(ioQQQ,"DEBUG zone %li species %s rel_to_lim:%.3e Te:%.3e *ne:%.3e n(%s):%.3e\n",
946  nzone,
947  label.c_str(),
948  dens_rel_to_lim_react,
949  phycon.te,
950  dense.eden,
951  label.c_str(),
952  *dense_total );
953  }
954  }
955 
957 
958  /* evaluate Boltzmann factors and LTE unit population - for trivial abundances
959  * LTE populations are used in place of full solution */
960  mole_H2_LTE();
961 
962  /* zero out populations and cooling, and return, if H2 fraction is small
963  * but, if H2 has ever been done, redo irregardless of abundance -
964  * if large H2 is ever evaluated then mole.H2_to_H_limit is ignored */
965  if( (!lgEvaluated && dens_rel_to_lim_react < H2_to_H_limit )
966  || *dense_total < 1e-20 )
967  {
968  /* will not compute the molecule */
969  if( nTRACE >= n_trace_full )
970  fprintf(ioQQQ,
971  " H2_LevelPops %s pops too small, not computing, set to LTE and return, H2/H is %.2e and H2_to_H_limit is %.2e.",
972  label.c_str(),
973  dens_rel_to_lim_react,
974  H2_to_H_limit);
976  fixit(); // set lgEvaluated = false here?
977  /* end of zero abundance branch */
978  return;
979  }
980 
981  /* check whether we need to update the H2_Boltzmann factors, LTE level populations,
982  * and partition function. LTE level pops normalized by partition function,
983  * so sum of pops is unity */
984 
985  /* say that H2 has been computed, ignore previous limit to abund
986  * in future - this is to prevent oscillations as model is engaged */
987  lgEvaluated = true;
988  /* end loop setting H2_Boltzmann factors, partition function, and LTE populations */
989 
990  /* >>chng 05 jun 21,
991  * during search phase we want to use full matrix - save number of levels so that
992  * we can restore it */
993  nXLevelsMatrix_save = nXLevelsMatrix;
994  fixit(); // this does not appear to be necessary and may be counterproductive
995  if( conv.lgSearch )
996  {
998  }
999 
1000  /* 05 oct 27, had only reevaluated collision rates when 5% change in temperature
1001  * caused temp failures in large G0 sims -
1002  * do not check whether we need to update the collision rates but
1003  * reevaluate them always
1004  * >>chng 05 nov 04, above caused a 25% increase in the exec time for constant-T sims
1005  * in test suite- original code had reevaluated if > 0.05 change in T - was too much
1006  * change to 10x smaller, change > 0.005 */
1007  if( !fp_equal(phycon.te,TeUsedColl) )
1008  {
1010  TeUsedColl = phycon.te;
1011  }
1012 
1013  /* set the populations when this is the first call to this routine on
1014  * current iteration- will use LTE populations - populations were set by
1015  * call to mole_H2_LTE before above block */
1016  if( nCall_this_iteration==0 || lgLTE )
1017  {
1018  /* very first call so use LTE populations */
1019  if(nTRACE >= n_trace_full )
1020  fprintf(ioQQQ,"%s 1st call - using LTE level pops\n", label.c_str() );
1021 
1022  H2_den_s = 0.;
1023  H2_den_g = 0.;
1024  for( qList::iterator st = states.begin(); st != states.end(); ++st )
1025  {
1026  long iElec = (*st).n();
1027  long iVib = (*st).v();
1028  long iRot = (*st).J();
1029  /* LTE populations are for unit H2 density, so need to multiply
1030  * by total H2 density */
1031  double pop = H2_populations_LTE[iElec][iVib][iRot] * (*dense_total);
1032  H2_old_populations[iElec][iVib][iRot] = pop;
1033  (*st).Pop() = pop;
1034  /* find current population in H2s and H2g */
1035  if( (*st).energy().WN() > ENERGY_H2_STAR && hmi.lgLeiden_Keep_ipMH2s )
1036  {
1037  H2_den_s += pop;
1038  }
1039  else
1040  {
1041  H2_den_g += pop;
1042  }
1043  }
1044 
1045  /* first guess at ortho and para densities */
1046  ortho_density = 0.75* (*dense_total);
1047  para_density = 0.25* (*dense_total);
1048  {
1051  }
1055  /* this is the fraction of the H2 pops that are within the levels done with a matrix */
1056  frac_matrix = 1.;
1057  }
1058 
1059  // make some population sums, normalize total to value handed from chemistry
1060  {
1061  pops_per_vib.zero();
1062  fill_n( pops_per_elec, N_ELEC, 0. );
1063  double pop_total = 0.;
1064  for( qList::const_iterator st = states.begin(); st != states.end(); ++st )
1065  {
1066  long iElec = (*st).n();
1067  long iVib = (*st).v();
1068 
1069  pop_total += (*st).Pop();
1070  pops_per_elec[iElec] += (*st).Pop();
1071  pops_per_vib[iElec][iVib] += (*st).Pop();
1072  }
1074  // Now renorm the old populations to the correct current H2 density.
1075  H2_renorm_chemistry = *dense_total/ SDIV(pop_total);
1076  }
1077 
1078  if(nTRACE >= n_trace_full)
1079  fprintf(ioQQQ,
1080  "%s H2_renorm_chemistry is %.4e, *dense_total is %.4e pops_per_elec[0] is %.4e\n",
1081  label.c_str(),
1083  *dense_total,
1084  pops_per_elec[0]);
1085 
1086  /* renormalize all level populations for the current chemical solution */
1087  for( qList::iterator st = states.begin(); st != states.end(); ++st )
1088  {
1089  long iElec = (*st).n();
1090  long iVib = (*st).v();
1091  long iRot = (*st).J();
1092 
1093  (*st).Pop() *= H2_renorm_chemistry;
1094  H2_old_populations[iElec][iVib][iRot] = (*st).Pop();
1095  }
1096 
1097  if(nTRACE >= n_trace_full )
1098  fprintf(ioQQQ,
1099  " H2 entry, old pops sumed to %.3e, renorm to htwo den of %.3e\n",
1100  pops_per_elec[0],
1101  *dense_total);
1102 
1103  /* >>chng 05 feb 10, reset convergence criteria if we are in search phase */
1104  fixit(); // I suspect this is counterproductive. Test without it -- Ryan
1105  if( conv.lgSearch )
1106  {
1107  converge_pops_relative *= 2.; /*def is 0.1 */
1108  converge_pops_total *= 3.; /*def is 1e-3*/
1109  converge_ortho_para *= 3.; /*def is 1e-2*/
1110  }
1111 
1112  if( !conv.nTotalIoniz )
1114 
1115  /* update state specific rates in H2_X_formation (cm-3 s-1) that H2 forms from grains and H- */
1116  mole_H2_form();
1117 
1118  /* evaluate total collision rates */
1120 
1121  /* this flag will say whether H2 populations have converged,
1122  * by comparing old and new values */
1123  lgConv_h2_soln = false;
1124  /* this will count number of passes around following loop */
1125  long loop_h2_pops = 0;
1126  {
1127  if( nzone != nzoneEval )
1128  {
1129  nzoneEval = nzone;
1130  /* this is number of zones with H2 solution in this iteration */
1131  ++nH2_zone;
1132  }
1133  }
1134 
1135  if( lgLTE )
1136  lgConv_h2_soln = true;
1137 
1138  /* begin - start level population solution
1139  * first do electronic excited states, Lyman, Werner, etc
1140  * using old solution for X
1141  * then do matrix if used, then solve for pops of rest of X
1142  * >>chng 04 apr 06, subtract number of oscillations from limit - don't waste loops
1143  * if solution is unstable */
1144  while( loop_h2_pops < LIM_H2_POP_LOOP-n_pop_oscil && !lgConv_h2_soln && !lgLTE )
1145  {
1146  /* this is number of trips around loop this time */
1147  ++loop_h2_pops;
1148  /* this is number of times through this loop in entire iteration */
1149  ++nH2_pops;
1150 
1151  /* radiative rates [cm-3 s-1] from electronic excited states into X vibration and rot */
1153  /* radiative & cosmic ray rates [s-1] to electronic excited states from X */
1156  H2_rad_rate_in.zero();
1157  pops_per_vib.zero();
1158  fill_n( pops_per_elec, N_ELEC, 0. );
1159 
1161 
1162  /* evaluate rates that destroy or create ground electronic state */
1164 
1165  /* above set pops of excited electronic levels and found rates between them and X -
1166  * now solve highly excited levels within the X state by back-substitution */
1168 
1169  /* now do lowest levels populations with matrix,
1170  * these should be collisionally dominated */
1171  if( nXLevelsMatrix )
1172  {
1174  /* the total abundance - frac_matrix is fraction of pop that was in these
1175  * levels the last time this was done */
1176  (*dense_total) * (realnum)frac_matrix );
1177  }
1178  if(nTRACE >= n_trace_full)
1179  {
1180  long iElecHi = 0;
1181  fprintf(ioQQQ," Rel pop(e=%li)" ,iElecHi);
1182  }
1183 
1184  /* find ortho and para densites, sum of pops in each vibration */
1185  /* this will become total pop is X, which will be renormed to equal *dense_total */
1186  {
1187  pops_per_elec[0] = 0.;
1188  for( md2i it = pops_per_vib.begin(0); it != pops_per_vib.end(0); ++it )
1189  *it = 0.;
1190 
1191  for( qList::const_iterator st = states.begin(); st != states.end(); ++st )
1192  {
1193  long iElec = (*st).n();
1194  if( iElec > 0 ) continue;
1195  long iVib = (*st).v();
1196  pops_per_elec[iElec] += (*st).Pop();
1197  pops_per_vib[iElec][iVib] += (*st).Pop();
1198  }
1199 
1200  /* print sum of populations in each vibration if trace on */
1201  if(nTRACE >= n_trace_full)
1202  for( md2ci it = pops_per_vib.begin(0); it != pops_per_vib.end(0); ++it )
1203  fprintf(ioQQQ,"\t%.2e", *it/(*dense_total));
1204 
1206  }
1207  /* =======================INSIDE POPULATIONS CONVERGE LOOP =====================*/
1208  if(nTRACE >= n_trace_full)
1209  {
1210  fprintf(ioQQQ,"\n");
1211  /* print the ground vibration state */
1212  fprintf(ioQQQ," Rel pop(0,J)");
1213  for( qList::const_iterator st = states.begin(); st != states.end(); ++st )
1214  {
1215  long iElec = (*st).n();
1216  if( iElec > 0 ) continue;
1217  long iVib = (*st).v();
1218  if( iVib > 0 ) continue;
1219  fprintf(ioQQQ,"\t%.2e", (*st).Pop()/(*dense_total) );
1220  }
1221  fprintf(ioQQQ,"\n");
1222  }
1223 
1224  /* now find population in states done with matrix - this is only used to pass
1225  * to matrix solver */
1226  {
1227  double sum_pops_matrix = 0.;
1228  for( long i=0; i<nXLevelsMatrix; ++i )
1229  {
1230  sum_pops_matrix += states[i].Pop();
1231  }
1232  /* this is self consistent since pops_per_elec[0] came from current soln,
1233  * as did the matrix. pops will be renormalized by results from the chemistry
1234  * a few lines down */
1235  frac_matrix = sum_pops_matrix / SDIV(pops_per_elec[0]);
1236  }
1237 
1238  {
1239  double pop_total = 0.;
1240  for( qList::const_iterator st = states.begin(); st != states.end(); ++st )
1241  pop_total += (*st).Pop();
1242 
1243  // The ratio of H2 that came out of the chemistry network to what we just obtained.
1244  double H2_renorm_conserve = *dense_total/ SDIV(pop_total);
1245 
1246  /* renormalize populations - were updated by renorm when routine entered,
1247  * before pops determined - but population determinations above do not have a sum rule on total
1248  * population - this renorm is to preserve total population */
1249  pops_per_vib.zero();
1250  fill_n( pops_per_elec, N_ELEC, 0. );
1251  for( qList::iterator st = states.begin(); st != states.end(); ++st )
1252  {
1253  (*st).Pop() *= H2_renorm_conserve;
1254  long iElec = (*st).n();
1255  long iVib = (*st).v();
1256  pops_per_elec[iElec] += (*st).Pop();
1257  pops_per_vib[iElec][iVib] += (*st).Pop();
1258  }
1259  }
1260 
1261  /* these will do convergence check */
1262  PopChgMaxOld_relative = PopChgMax_relative;
1263  PopChgMaxOld_total = PopChgMax_total;
1264  PopChgMax_relative = 0.;
1265  PopChgMax_total = 0.;
1266  iRotMaxChng_relative =-1;
1267  iVibMaxChng_relative = -1;
1268  iRotMaxChng_total =-1;
1269  iVibMaxChng_total = -1;
1270  popold_relative = 0.;
1271  popnew_relative = 0.;
1272  popold_total = 0.;
1273  popnew_total = 0.;
1274 
1275  // *****************************************
1276  // *****************************************
1277  // *****************************************
1278  // *****************************************
1279  // Should be able to extract this loop!!!
1280  // *****************************************
1281  // *****************************************
1282  // *****************************************
1283  // *****************************************
1284  {
1285  /* this loop first checks for largest changes in populations, to determine whether
1286  * we have converged, then updates the population array with a new value,
1287  * which may be a mean of old and new
1288  * update populations check convergence converged */
1289  double sumold = 0.;
1290 
1291  for( qList::const_iterator st = states.begin(); st != states.end(); ++st )
1292  {
1293  long iElec = (*st).n();
1294  long iVib = (*st).v();
1295  long iRot = (*st).J();
1296  double pop = states[ ipEnergySort[iElec][iVib][iRot] ].Pop();
1297  double rel_change;
1298  /* keep track of largest relative change in populations to
1299  * determines convergence */
1300  if( fabs( pop - H2_old_populations[iElec][iVib][iRot])/
1301  /* on first call some very high J states can have zero pop ,
1302  * hence the SDIV, will retain sign for checks on oscilations,
1303  * hence the fabs */
1304  SDIV(pop) > fabs(PopChgMax_relative) &&
1305  /* >>chng 03 jul 19, this had simply been pop > SMALLFLOAT,
1306  * change to relative pops > 1e-15, spent too much time converging
1307  * levels at pops = 1e-37 */
1308  /* >>chng 03 dec 27, from rel pop 1e-15 to 1e-6 since converging heating will
1309  * be main convergence criteria check convergence */
1310  pop/SDIV(*dense_total)>1e-6 )
1311  {
1312  PopChgMax_relative =
1313  (pop - H2_old_populations[iElec][iVib][iRot])/SDIV(pop);
1314  iRotMaxChng_relative = iRot;
1315  iVibMaxChng_relative = iVib;
1316  popold_relative = H2_old_populations[iElec][iVib][iRot];
1317  popnew_relative = pop;
1318  }
1319  /* >>chng 05 feb 08, add largest rel change in total, this will be converged
1320  * down to higher accuracy than above
1321  * keep track of largest change in populations relative to total H2 to
1322  * determine convergence check convergence */
1323  rel_change = (pop - H2_old_populations[iElec][iVib][iRot])/SDIV(*dense_total);
1324  /* retain sign for checks on oscillations hence the fabs */
1325  if( fabs(rel_change) > fabs(PopChgMax_total) )
1326  {
1327  PopChgMax_total = rel_change;
1328  iRotMaxChng_total = iRot;
1329  iVibMaxChng_total = iVib;
1330  popold_total = H2_old_populations[iElec][iVib][iRot];
1331  popnew_total = pop;
1332  }
1333 
1334  kase = -1;
1335  /* update populations - we used the old populations to update the
1336  * current new populations - will do another iteration if they changed
1337  * by much. here old populations are updated for next sweep through molecule */
1338  /* pop oscillations have occurred - use small changes */
1339  /* >>chng 04 may 10, turn this back on - now with min on how small frac new
1340  * can become */
1341  rel_change = fabs( H2_old_populations[iElec][iVib][iRot] - pop ) / SDIV( pop );
1342 
1343  /* this branch very large changes, use mean of logs but onlly if both are positive*/
1344  if( rel_change > 3. && H2_old_populations[iElec][iVib][iRot] * pop > 0 )
1345  {
1346  /* large changes or oscillations - take average in the log */
1347  H2_old_populations[iElec][iVib][iRot] = pow( 10. ,
1348  log10(H2_old_populations[iElec][iVib][iRot])/2. +
1349  log10(pop)/2. );
1350  kase = 2;
1351  }
1352 
1353  /* modest change, use means of old and new */
1354  else if( rel_change> 0.1 )
1355  {
1356  realnum frac_old=0.25f;
1357  /* large changes or oscillations - take average */
1358  H2_old_populations[iElec][iVib][iRot] =
1359  frac_old*H2_old_populations[iElec][iVib][iRot] +
1360  (1.f-frac_old)*pop;
1361  kase = 3;
1362  }
1363  else
1364  {
1365  /* small changes, use new value */
1366  H2_old_populations[iElec][iVib][iRot] = pop;
1367  kase = 4;
1368  }
1369  sumold += H2_old_populations[iElec][iVib][iRot];
1370  }
1371 
1372  /* will renormalize so that total population is correct */
1373  double H2_renorm_conserve_init = *dense_total/sumold;
1374 
1375  /* renormalize populations - were updated by renorm when routine entered,
1376  * before pops determined - but population determinations above do not have a sum rule on total
1377  * population - this renorm is to preserve total population */
1378  for( qList::const_iterator st = states.begin(); st != states.end(); ++st )
1379  {
1380  long iElec = (*st).n();
1381  long iVib = (*st).v();
1382  long iRot = (*st).J();
1383  H2_old_populations[iElec][iVib][iRot] *= H2_renorm_conserve_init;
1384  }
1385  }
1386 
1387  /* get current ortho-para ratio, will be used as test on convergence */
1388  {
1389  ortho_density = 0.;
1390  para_density = 0.;
1391  H2_den_s = 0.;
1392  H2_den_g = 0.;
1393 
1394  for( qList::const_iterator st = states.begin(); st != states.end(); ++st )
1395  {
1396  long iElec = (*st).n();
1397  long iVib = (*st).v();
1398  long iRot = (*st).J();
1399  const double& pop = (*st).Pop();
1400  /* find current population in H2s and H2g */
1401  if( (*st).energy().WN() > ENERGY_H2_STAR && hmi.lgLeiden_Keep_ipMH2s )
1402  {
1403  H2_den_s += pop;
1404  }
1405  else
1406  {
1407  H2_den_g += pop;
1408  }
1409  if( H2_lgOrtho[iElec][iVib][iRot] )
1410  {
1411  ortho_density += pop;
1412  }
1413  else
1414  {
1415  para_density += pop;
1416  }
1417  }
1418  ASSERT( fp_equal_tol( H2_den_s + H2_den_g, *dense_total, 1e-5 * (*dense_total) ) );
1419  }
1420 
1421  /* these will be used to determine whether solution has converged */
1425 
1426  /* this will be evaluated in call to routine that follows - will check
1427  * whether this has converged */
1428  old_solomon_rate = Solomon_dissoc_rate_g;
1429 
1430  /* >>chng 05 jul 24, break code out into separate routine for clarify
1431  * located in mole_h2_etc.c - true says to only do Solomon rate */
1432  H2_Solomon_rate();
1433 
1434  /* are changes too large? must decide whether population shave converged,
1435  * will check whether populations themselves have changed by much,
1436  * but also change in heating by collisional deexcitation is stable */
1439  {
1440  /* check whether pops are oscillating, as evidenced by change in
1441  * heating changing sign */
1442  if( loop_h2_pops>2 && (
1443  (HeatChangeOld*HeatChange<0. ) ||
1444  (PopChgMax_relative*PopChgMaxOld_relative<0. ) ) )
1445  {
1446  lgH2_pops_oscil = true;
1447  if( loop_h2_pops > 6 )
1448  {
1449  loop_h2_oscil = loop_h2_pops;
1450  lgH2_pops_ever_oscil = true;
1451  ++n_pop_oscil;
1452  }
1453  }
1454  else
1455  {
1456  lgH2_pops_oscil = false;
1457  /* turn off flag if no oscillations for a while */
1458  if( loop_h2_pops - loop_h2_oscil > 4 )
1459  {
1460  lgH2_pops_ever_oscil = false;
1461  }
1462  }
1463  }
1464 
1465  /* reevaluate heating - cooling if H2 molecule is significant source or either,
1466  * since must have stable heating cooling rate */
1468  if(fabs(HeatDexc)/thermal.ctot > conv.HeatCoolRelErrorAllowed/10. ||
1469  HeatDexc==0. )
1470  H2_Cooling("H2lup");
1471 
1472  /* begin check on whether solution is converged */
1473  lgConv_h2_soln = true;
1474  lgPopsConv_total = true;
1475  lgPopsConv_relative = true;
1476  lgHeatConv = true;
1477  lgSolomonConv = true;
1478  lgOrthoParaRatioConv = true;
1479 
1480  /* these are all the convergence tests
1481  * check convergence converged */
1482  if( fabs(PopChgMax_relative)>converge_pops_relative )
1483  {
1484  /*lgPopsConv = (fabs(PopChgMax_relative)<=0.1);*/
1485  lgConv_h2_soln = false;
1486  lgPopsConv_relative = false;
1487  /* >>chng 04 sep 08, set quant_new to new chng max gs */
1488  /*quant_old = PopChgMax_relative;*/
1489  quant_old = PopChgMaxOld_relative;
1490  /*quant_new = 0.;*/
1491  quant_new = PopChgMax_relative;
1492 
1493  strcpy( chReason , "rel pops changed" );
1494  }
1495 
1496  /* check largest change in a level population relative to total h2
1497  * population convergence converged check */
1498  else if( fabs(PopChgMax_total)>converge_pops_total)
1499  {
1500  lgConv_h2_soln = false;
1501  lgPopsConv_total = false;
1502  /* >>chng 04 sep 08, set quant_new to new chng max gs */
1503  /*quant_old = PopChgMax_relative;*/
1504  quant_old = PopChgMaxOld_total;
1505  /*quant_new = 0.;*/
1506  quant_new = PopChgMax_total;
1507 
1508  strcpy( chReason , "tot pops changed" );
1509  }
1510 
1511  /* >>chng 04 apr 30, look at change in ortho-para ratio, also that is not
1512  * oscillating */
1513  /* >>chng 04 dec 15, only look at change, and don't make allowed change so tiny -
1514  * these were attempts at fixing problems that were due to shielding not thin*/
1515  else if( fabs(ortho_para_current-ortho_para_old) / SDIV(ortho_para_current)> converge_ortho_para )
1516  /* else if( fabs(ortho_para_current-ortho_para_old) / SDIV(ortho_para_current)> 1e-3
1517  && (ortho_para_current-ortho_para_old)*(ortho_para_old-ortho_para_older)>0. )*/
1518  {
1519  lgConv_h2_soln = false;
1520  lgOrthoParaRatioConv = false;
1521  quant_old = ortho_para_old;
1522  quant_new = ortho_para_current;
1523  strcpy( chReason , "ortho/para ratio changed" );
1524  }
1525  /* >>chng 04 dec 16, reduce error allowed fm /5 to /2, to be similar to
1526  * logic in conv_base */
1527  else if( !thermal.lgTemperatureConstant &&
1528  fabs(HeatDexc-HeatDexc_old)/MAX2(thermal.ctot,thermal.htot) >
1530  /* >>chng 04 may 09, do not check on error in heating if constant temperature */
1531  /*&& !(thermal.lgTemperatureConstant || phycon.te <= phycon.TEMP_LIMIT_LOW )*/ )
1532  {
1533  /* default on HeatCoolRelErrorAllowed is 0.02 */
1534  /*lgHeatConv = (fabs(HeatDexc-HeatDexc_old)/thermal.ctot <=
1535  * conv.HeatCoolRelErrorAllowed/5.);*/
1536  lgConv_h2_soln = false;
1537  lgHeatConv = false;
1538  quant_old = HeatDexc_old/MAX2(thermal.ctot,thermal.htot);
1539  quant_new = HeatDexc/MAX2(thermal.ctot,thermal.htot);
1540  strcpy( chReason , "heating changed" );
1541  /*fprintf(ioQQQ,"DEBUG old new trip \t%.4e \t %.4e\n",
1542  HeatDexc_old,
1543  HeatDexc);*/
1544  }
1545 
1546  /* check on Solomon rate,
1547  * >>chng 04 aug 28, do not do this check if induced processes are disabled,
1548  * since Solomon process is then irrelevant */
1549  /* >>chng 04 sep 21, GS*/
1550  else if( rfield.lgInducProcess &&
1551  /* this is check that H2 abundance has not been set - if it has been
1552  * then we don't care what the Solomon rate is doing */
1553  hmi.H2_frac_abund_set==0 &&
1554  /*>>chng 05 feb 10, rather than checking change in Solomon relative to Solomon,
1555  * check it relative to total h2 destruction rate */
1556  fabs( Solomon_dissoc_rate_g - old_solomon_rate)/SDIV(hmi.H2_rate_destroy) >
1558  {
1559  lgConv_h2_soln = false;
1560  lgSolomonConv = false;
1561  quant_old = old_solomon_rate;
1562  quant_new = Solomon_dissoc_rate_g;
1563  strcpy( chReason , "Solomon rate changed" );
1564  }
1565 
1566  /* did we pass all the convergence test */
1567  if( !lgConv_h2_soln )
1568  {
1569  /* this branch H2 populations within X are not converged,
1570  * print diagnostic */
1571 
1573  {
1574  /*fprintf(ioQQQ,"temppp\tnew\t%.4e\tnew\t%.4e\t%.4e\n",
1575  HeatDexc,
1576  HeatDexc_old,
1577  fabs(HeatDexc-HeatDexc_old)/thermal.ctot );*/
1578  fprintf(ioQQQ," %s loop %3li no conv oscl?%c why:%s ",
1579  label.c_str(),
1580  loop_h2_pops,
1581  TorF(lgH2_pops_ever_oscil),
1582  chReason );
1583  if( !lgPopsConv_relative )
1584  fprintf(ioQQQ," PopChgMax_relative:%.4e v:%li J:%li old:%.4e new:%.4e",
1585  PopChgMax_relative,
1586  iVibMaxChng_relative,
1587  iRotMaxChng_relative ,
1588  popold_relative ,
1589  popnew_relative );
1590  else if( !lgPopsConv_total )
1591  fprintf(ioQQQ," PopChgMax_total:%.4e v:%li J:%li old:%.4e new:%.4e",
1592  PopChgMax_total,
1593  iVibMaxChng_total,
1594  iRotMaxChng_total ,
1595  popold_total ,
1596  popnew_total );
1597  else if( !lgHeatConv )
1598  fprintf(ioQQQ," heat:%.4e old:%.4e new:%.4e",
1599  (HeatDexc-HeatDexc_old)/MAX2(thermal.ctot,thermal.htot),
1600  quant_old ,
1601  quant_new);
1602  /* Solomon rate changed */
1603  else if( !lgSolomonConv )
1604  fprintf(ioQQQ," d(sol rate)/tot dest\t%2e",(old_solomon_rate - Solomon_dissoc_rate_g)/SDIV(hmi.H2_rate_destroy));
1605  else if( !lgOrthoParaRatioConv )
1606  fprintf(ioQQQ," current, old, older ratios are %.4e %.4e %.4e",
1608  else
1609  TotalInsanity();
1610  fprintf(ioQQQ,"\n");
1611  }
1612  }
1613  /* end convergence criteria */
1614 
1615  if( trace.nTrConvg >= 5 )
1616  {
1617  fprintf( ioQQQ,
1618  " H2 5lev %li Conv?%c",
1619  loop_h2_pops ,
1620  TorF(lgConv_h2_soln) );
1621 
1622  if( fabs(PopChgMax_relative)>0.1 )
1623  fprintf(ioQQQ," pops, rel chng %.3e",PopChgMax_relative);
1624  else
1625  fprintf(ioQQQ," rel heat %.3e rel chng %.3e H2 heat/cool %.2e",
1626  HeatDexc/thermal.ctot ,
1627  fabs(HeatDexc-HeatDexc_old)/thermal.ctot ,
1628  HeatDexc/thermal.ctot);
1629 
1630  fprintf( ioQQQ,
1631  " Oscil?%c Ever Oscil?%c",
1632  TorF(lgH2_pops_oscil) ,
1633  TorF(lgH2_pops_ever_oscil) );
1634  fprintf(ioQQQ,"\n");
1635  }
1636 
1637  if( nTRACE >= n_trace_full )
1638  {
1639  fprintf(ioQQQ,
1640  "H2 loop\t%li\tkase pop chng\t%i\tchem renorm fac\t%.4e\tortho/para ratio:\t%.3e\tfrac of pop in matrix: %.3f\n",
1641  loop_h2_pops,
1642  kase,
1645  frac_matrix);
1646 
1647  /* =======================INSIDE POPULATIONS CONVERGE LOOP =====================*/
1648  if( iVibMaxChng_relative>=0 && iRotMaxChng_relative>=0 && PopChgMax_relative>1e-10 )
1649  fprintf(ioQQQ,
1650  "end loop %li H2 max rel chng=%.3e from %.3e to %.3e at v=%li J=%li\n\n",
1651  loop_h2_pops,
1652  PopChgMax_relative ,
1653  H2_old_populations[0][iVibMaxChng_relative][iRotMaxChng_relative],
1654  states[ ipEnergySort[0][iVibMaxChng_relative][iRotMaxChng_relative] ].Pop(),
1655  iVibMaxChng_relative , iRotMaxChng_relative
1656  );
1657  }
1658  }
1659  /* =======================END POPULATIONS CONVERGE LOOP =====================*/
1660 
1661  /* >>chng 05 feb 08, do not print if we are in search phase */
1662  if( !lgConv_h2_soln && !conv.lgSearch )
1663  {
1664  conv.lgConvPops = false;
1665  lgPopsConverged = false;
1666  old_val = quant_old;
1667  new_val = quant_new;
1668  }
1669 
1670  for( qList::iterator st = states.begin(); st != states.end(); ++st )
1671  {
1672  ASSERT( (*st).Pop() >= 0. );
1673  }
1674 
1675  for( TransitionList::iterator tr = trans.begin(); tr != rad_end; ++tr )
1676  {
1677  /* following two heat exchange excitation, deexcitation */
1678  (*tr).Coll().cool() = 0.;
1679  (*tr).Coll().heat() = 0.;
1680 
1681  (*tr).Emis().PopOpc() = (*(*tr).Lo()).Pop() - (*(*tr).Hi()).Pop() * (*(*tr).Lo()).g() / (*(*tr).Hi()).g();
1682 
1683  /* number of photons in the line */
1684  (*tr).Emis().phots() = (*tr).Emis().Aul() * ((*tr).Emis().Pesc() + (*tr).Emis().Pelec_esc()) * (*(*tr).Hi()).Pop();
1685 
1686  /* intensity of line */
1687  (*tr).Emis().xIntensity() = (*tr).Emis().phots() * (*tr).EnergyErg();
1688 
1689  }
1690 
1691  average_energy_g = 0.;
1692  average_energy_s = 0.;
1693  /* determine average energy in ground and star */
1694  for( qList::const_iterator st = states.begin(); st != states.end(); ++st )
1695  {
1696  double popTimesE = (*st).Pop() * (*st).energy().WN();
1697  if( (*st).energy().WN() > ENERGY_H2_STAR && hmi.lgLeiden_Keep_ipMH2s )
1698  average_energy_s += popTimesE;
1699  else
1700  average_energy_g += popTimesE;
1701  }
1702  /* average energy in ground and star */
1704  if( H2_den_s > 1e-30 * (*dense_total) )
1706  else
1707  average_energy_s = 0.;
1708 
1709  /* add up H2 + hnu => 2H, continuum photodissociation,
1710  * this is not the Solomon process, true continuum */
1711  /* >>chng 05 jun 16, GS, add dissociation to triplet states*/
1712  photodissoc_BigH2_H2s = 0.;
1713  photodissoc_BigH2_H2g = 0.;
1714  /* >>chng 05 jul 20, GS, add dissociation by H2 g and H2s*/
1719 
1720  rel_pop_LTE_g =0.;
1721  rel_pop_LTE_s = 0.;
1722 
1723  double exp_disoc = sexp(H2_DissocEnergies[0]/phycon.te_wn);
1724 
1725  /* >>chng 05 sep 12, TE, define a cutoff wavelength of 800 Angstrom
1726  * this is chosen as the cross sections given by
1727  *>>refer H2 photo cs Allison, A.C. & Dalgarno, A. 1969, Atomic Data, 1, 91
1728  * show a sharp decline in the cross section*/
1729  {
1730  static long ip_cut_off = -1;
1731  if( ip_cut_off < 0 )
1732  {
1733  /* one-time initialization of this pointer */
1734  ip_cut_off = ipoint( 1.14 );
1735  }
1736 
1737  /* >>chng 05 sep 12, TE, assume all H2s is at 2.5 eV
1738  * the dissociation threshold is at 1.07896 Rydberg*/
1739  double flux_accum_photodissoc_BigH2_H2s = 0;
1740  fixit(); // this 2.5 seems like a pretty bad (and unnecessary) approximation. Needs to be generalized at any rate.
1741  long ip_H2_level = ipoint( 1.07896 - 2.5 / EVRYD);
1742  for( long i= ip_H2_level; i < ip_cut_off; ++i )
1743  {
1744  flux_accum_photodissoc_BigH2_H2s += ( rfield.flux[0][i-1] + rfield.ConInterOut[i-1]+
1745  rfield.outlin[0][i-1]+ rfield.outlin_noplot[i-1] );
1746  }
1747 
1748  /* sum over all levels to obtain s and g populations and dissociation rates */
1749  for( qList::const_iterator st = states.begin(); st != states.end(); ++st )
1750  {
1751  long iElec = (*st).n();
1752  if( iElec > 0 ) continue;
1753  long iVib = (*st).v();
1754  long iRot = (*st).J();
1755  const double &pop = (*st).Pop();
1756  fixit(); // generalize this factor (present value is (2m_e/m_H)^1.5/(2*2). See Robin's Feb 7, 2009 email.
1757  const double mass_stat_factor = 3.634e-5/(2*2);
1758 
1759  /* >>chng 05 mar 22, TE, this should be for H2* rather than total */
1760  /* this is the total rate of direct photo-dissociation of excited electronic states into
1761  * the X continuum - this is continuum photodissociation, not the Solomon process */
1762  /* >>chng 03 sep 03, make sum of pops of excited states */
1763  if( (*st).energy().WN() > ENERGY_H2_STAR && hmi.lgLeiden_Keep_ipMH2s )
1764  {
1765  double arg_ratio;
1766  photodissoc_BigH2_H2s += pop * flux_accum_photodissoc_BigH2_H2s;
1767 
1768  /* >>chng 05 july 20, GS, collisional dissociation, unit s-1*/
1769  Average_collH_dissoc_s += pop * H2_coll_dissoc_rate_coef[iVib][iRot];
1771 
1772  /* >>chng 05 oct 17, GS, LTE populations of H2s*/
1773  arg_ratio = exp_disoc/SDIV(H2_Boltzmann[0][iVib][iRot]);
1774  if( arg_ratio > 0. )
1775  {
1776  /* >>chng 05 oct 21, GS, only add ratio if Boltzmann factor > 0 */
1777  rel_pop_LTE_s += SAHA/SDIV(phycon.te32*arg_ratio)*
1778  H2_stat[0][iVib][iRot] * mass_stat_factor;
1779  }
1780  }
1781  else
1782  {
1783  double arg_ratio;
1784  /* >>chng 05 sep 12, TE, for H2g do the sum explicitly for every level*/
1785  double flux_accum_photodissoc_BigH2_H2g = 0;
1786  /* this is the dissociation energy needed for the level*/
1787  ip_H2_level = ipoint( 1.07896 - (*st).energy().Ryd() );
1788 
1789  for( long i= ip_H2_level; i < ip_cut_off; ++i )
1790  {
1791  flux_accum_photodissoc_BigH2_H2g += ( rfield.flux[0][i-1] + rfield.ConInterOut[i-1]+
1792  rfield.outlin[0][i-1]+ rfield.outlin_noplot[i-1] );
1793  }
1794 
1795  photodissoc_BigH2_H2g += pop * flux_accum_photodissoc_BigH2_H2g;
1796 
1797  /* >>chng 05 jun 28, TE, determine average energy level in H2g */
1798  average_energy_g += (pop * (*st).energy().WN() );
1799 
1800  /* >>chng 05 july 20, GS, collisional dissociation, unit s-1*/
1801  Average_collH_dissoc_g += pop * H2_coll_dissoc_rate_coef[iVib][iRot];
1803 
1804  /* >>chng 05 oct 17, GS, LTE populations of H2g*/
1805  arg_ratio = exp_disoc/SDIV(H2_Boltzmann[0][iVib][iRot]);
1806  if( arg_ratio > 0. )
1807  {
1808  rel_pop_LTE_g += SAHA/SDIV(phycon.te32*arg_ratio)*
1809  H2_stat[0][iVib][iRot] * mass_stat_factor;
1810  }
1811  }
1812  }
1813  }
1814 
1815  /* above sum was rate per unit vol since mult by H2 density, now div by H2* density to get rate s-1 */
1816  /* 0.25e-18 is wild guess of typical photodissociation cross section, from
1817  * >>refer H2 dissoc Allison, A.C. & Dalgarno, A. 1969, Atomic Data, 1, 91
1818  * this is based on an average of the highest v values they gave. unfortunately, we want
1819  * the highest J values -
1820  * final units are s-1*/
1821 
1822  if( H2_den_g > SMALLFLOAT )
1823  {
1824  Average_collH_dissoc_g /= SDIV(H2_den_g);/* unit cm3s-1*/
1825  Average_collH2_dissoc_g /= SDIV(H2_den_g);/* unit cm3s-1*/
1827  }
1828  else
1829  {
1832  photodissoc_BigH2_H2g = 0.;
1833  }
1834  if( H2_den_s > SMALLFLOAT )
1835  {
1836  Average_collH_dissoc_s /= SDIV(H2_den_s);/* unit cm3s-1*/
1837  Average_collH2_dissoc_s /= SDIV(H2_den_s);/* unit cm3s-1*/
1839  }
1840  else
1841  {
1844  photodissoc_BigH2_H2s = 0.;
1845  }
1846 
1847 
1848  // calculate some average rates from H2* to H2g
1850 
1851  if( nTRACE )
1852  {
1853  fprintf(ioQQQ," H2_LevelPops exit1 %8.2f loops:%3li H2/H:%.3e Sol dis old %.3e new %.3e Sol dis star %.3e g-to-s %.3e photodiss star %.3e\n",
1854  fnzone ,
1855  loop_h2_pops ,
1856  dens_rel_to_lim_react,
1857  old_solomon_rate,
1860  gs_rate(),
1862  }
1863 
1864  /* >>chng 03 sep 01, add this population - before had just used H2star from chem network */
1865  /* if big H2 molecule is turned on and used for this zone, use its
1866  * value of H2* (pops of all states with v > 0 ) rather than simple network */
1867 
1868  /* update number of times we have been called */
1870 
1871  /* this will say how many times the large H2 molecule has been called in this zone -
1872  * if not called (due to low H2 abundance) then not need to update its line arrays */
1873  ++nCall_this_zone;
1874 
1875  /* >>chng 05 jun 21,
1876  * during search phase we want to use full matrix - save number of levels so that
1877  * we can restore it */
1878  nXLevelsMatrix = nXLevelsMatrix_save;
1879 
1880  /* >>chng 05 jan 19, check how many levels should be in the matrix if first call on
1881  * new zone, and we have a solution */
1882  /* end loop setting very first LTE populations */
1884  {
1885  /* this is fraction of populations to include in matrix */
1886  const double FRAC = 0.99999;
1887  /* this loop is over increasing energy */
1888  double sum_pop = 0.;
1889  long nEner = 0;
1890  long iElec = 0;
1891  const bool PRT = false;
1892  if( PRT ) fprintf(ioQQQ,"DEBUG pops ");
1893  while( nEner < nLevels_per_elec[0] && sum_pop/(*dense_total) < FRAC )
1894  {
1895  /* array of energy sorted indices within X */
1896  ASSERT( iElec == ipElec_H2_energy_sort[nEner] );
1897  long iVib = ipVib_H2_energy_sort[nEner];
1898  long iRot = ipRot_H2_energy_sort[nEner];
1899  sum_pop += H2_old_populations[iElec][iVib][iRot];
1900  if( PRT ) fprintf(ioQQQ,"\t%.3e ", H2_old_populations[iElec][iVib][iRot]);
1901  ++nEner;
1902  }
1903  if( PRT ) fprintf(ioQQQ,"\n");
1905  /*fprintf(ioQQQ,"DEBUG zone %.2f old nmatrix %li proposed nmatrix %li sum_pop %.4e H2_total %.4e\n",
1906  fnzone , nXLevelsMatrix ,nEner , sum_pop, *dense_total);
1907  nXLevelsMatrix = nEner;*/
1908  }
1909 
1910  return;
1911 }
1912 /*lint -e802 possible bad pointer */
1913 
1915 {
1916  DEBUG_ENTRY( "diatomics::SolveExcitedElectronicLevels()" );
1917 
1918  multi_arr<double,3> rate_in;
1919  rate_in.alloc( H2_rad_rate_out.clone() );
1920  rate_in.zero();
1921  spon_diss_tot = 0.;
1922  double CosmicRayHILyaExcitationRate = ( hmi.lgLeidenCRHack ) ? secondaries.x12tot : 0.;
1923 
1924  for( TransitionList::iterator tr = trans.begin(); tr != rad_end; ++tr )
1925  {
1926  qList::iterator Lo = (*tr).Lo();
1927  long iElecLo = (*Lo).n();
1928  long iVibLo = (*Lo).v();
1929  long iRotLo = (*Lo).J();
1930  qList::iterator Hi = (*tr).Hi();
1931  long iElecHi = (*Hi).n();
1932  if( iElecHi < 1 ) continue;
1933  long iVibHi = (*Hi).v();
1934  long iRotHi = (*Hi).J();
1935  /* solve electronic excited state,
1936  * rate lower level in X goes to electronic excited state, s-1
1937  * first term is direct pump, second is cosmic ray excitation */
1938  /* collisional excitation of singlets by non-thermal electrons
1939  * this is stored ratio of electronic transition relative
1940  * cross section relative to the HI Lya cross section */
1941  double rate_up = (*tr).Emis().pump() + CosmicRayHILyaExcitationRate * (*tr).Coll().col_str();
1942  double rate_down =
1943  (*tr).Emis().Aul() * ( (*tr).Emis().Pesc() + (*tr).Emis().Pelec_esc() + (*tr).Emis().Pdest() ) +
1944  rate_up * (*(*tr).Lo()).g() / (*(*tr).Hi()).g();
1945 
1946  /* this is a permitted electronic transition, must preserve nuclear spin */
1947  ASSERT( H2_lgOrtho[iElecHi][iVibHi][iRotHi] == H2_lgOrtho[iElecLo][iVibLo][iRotLo] );
1948 
1949  /* this is the rate [cm-3 s-1] electrons move into the upper level from X */
1950  rate_in[iElecHi][iVibHi][iRotHi] += H2_old_populations[iElecLo][iVibLo][iRotLo]*rate_up;
1951 
1952  /* rate [s-1] from levels within X to electronic excited states,
1953  * includes photoexcitation and cosmic ray excitation */
1954  if( iElecLo==0 )
1955  H2_X_rate_to_elec_excited[iVibLo][iRotLo] += rate_up;
1956  H2_rad_rate_out[iElecLo][iVibLo][iRotLo] += rate_up;
1957 
1958  /* this is the rate [s-1] electrons leave the excited electronic upper level
1959  * and decay into X - will be used to get pops of electronic excited states */
1960  H2_rad_rate_out[iElecHi][iVibHi][iRotHi] += rate_down;
1961  ASSERT( rate_up >= 0. && rate_down >= 0. );
1962  }
1963 
1964  for( qList::iterator st = states.begin(); st != states.end(); ++st )
1965  {
1966  if( (*st).n() < 1 )
1967  continue;
1968 
1969  long iElec = (*st).n();
1970  long iVib = (*st).v();
1971  long iRot = (*st).J();
1972 
1973  H2_rad_rate_out[iElec][iVib][iRot] += H2_dissprob[iElec][iVib][iRot];
1974 
1975  /* update population [cm-3] of the electronic excited state this only includes
1976  * radiative processes between X and excited electronic states, and cosmic rays -
1977  * thermal collisions are neglected
1978  * X is done below and includes all processes */
1979  double pop = rate_in[iElec][iVib][iRot] / SDIV( H2_rad_rate_out[iElec][iVib][iRot] );
1980  (*st).Pop() = pop;
1981  spon_diss_tot += pop * H2_dissprob[iElec][iVib][iRot];
1982  if( H2_old_populations[iElec][iVib][iRot]==0. )
1983  H2_old_populations[iElec][iVib][iRot] = pop;
1984  /* this is total pop in this vibration state */
1985  pops_per_vib[iElec][iVib] += pop;
1986  /* total pop in each electronic state */
1987  pops_per_elec[iElec] += pop;
1988  }
1989 
1990  fixit(); // uncomment and test
1991  if( H2_den_s > 1e-30 * (*dense_total) )
1993  else
1994  spon_diss_tot = 0.;
1995 
1996  if(nTRACE >= n_trace_full)
1997  {
1998  for( long iElec=1; iElec<n_elec_states; ++iElec )
1999  {
2000  fprintf(ioQQQ," Pop(e=%li):",iElec);
2001  for( md2i it = pops_per_vib.begin(iElec); it != pops_per_vib.end(iElec); ++it )
2002  fprintf( ioQQQ,"\t%.2e", *it/(*dense_total) );
2003  fprintf(ioQQQ,"\n");
2004  }
2005  }
2006 
2007  for( TransitionList::iterator tr = trans.begin(); tr != rad_end; ++tr )
2008  {
2009  qList::iterator Lo = (*tr).Lo();
2010  if( (*Lo).n() != 0 ) continue;
2011  qList::iterator Hi = (*tr).Hi();
2012  if( (*Hi).n() < 1 ) continue;
2013 
2014  /* radiative rates [cm-3 s-1] from electronic excited states to X */
2015  double rate = (*Hi).Pop() *
2016  ((*tr).Emis().Aul() * ( (*tr).Emis().Pesc() + (*tr).Emis().Pelec_esc() + (*tr).Emis().Pdest() ) +
2017  (*tr).Emis().pump() * (*Lo).g() / (*Hi).g());
2018  H2_X_rate_from_elec_excited[(*Lo).v()][(*Lo).J()] += rate;
2019  H2_rad_rate_in[(*Lo).v()][(*Lo).J()] += rate;
2020  }
2021 
2022  return;
2023 }
2024 
2026 {
2027  DEBUG_ENTRY("diatomics::SolveSomeGroundElectronicLevels()");
2028 
2029  /* these will be total rates into and out of the level */
2031  H2_col_rate_in.zero();
2032 
2033  /* now evaluate total rates for all levels within X */
2034  for( long ipHi=0; ipHi<nLevels_per_elec[0]; ++ipHi)
2035  {
2036  /* array of energy sorted indices within X */
2037  long iElecHi = ipElec_H2_energy_sort[ipHi];
2038  ASSERT( iElecHi == 0 );
2039  long iVibHi = ipVib_H2_energy_sort[ipHi];
2040  long iRotHi = ipRot_H2_energy_sort[ipHi];
2041 
2042  realnum H2stat = H2_stat[iElecHi][iVibHi][iRotHi];
2043  double H2boltz = H2_Boltzmann[iElecHi][iVibHi][iRotHi];
2044 
2045  for( long ipLo=0; ipLo<ipHi; ++ipLo )
2046  {
2047  long iVibLo = ipVib_H2_energy_sort[ipLo];
2048  long iRotLo = ipRot_H2_energy_sort[ipLo];
2049 
2050  /* collision de-excitation [s-1] */
2051  realnum colldn = H2_X_coll_rate[ipHi][ipLo];
2052  /* inverse, rate up, [cm-3 s-1] */
2053  realnum collup = colldn *
2054  H2stat / H2_stat[0][iVibLo][iRotLo] *
2055  H2boltz / SDIV( H2_Boltzmann[0][iVibLo][iRotLo] );
2056 
2057  H2_col_rate_out[iVibHi][iRotHi] += colldn;
2058  H2_col_rate_in[iVibLo][iRotLo] += colldn * H2_old_populations[0][iVibHi][iRotHi];
2059 
2060  H2_col_rate_out[iVibLo][iRotLo] += collup;
2061  H2_col_rate_in[iVibHi][iRotHi] += collup * H2_old_populations[0][iVibLo][iRotLo];
2062  }
2063  }
2064 
2065  /* begin solving for X by back-substitution
2066  * this is the main loop that determines populations within X
2067  * units of all rates in are cm-3 s-1, all rates out are s-1
2068  * nLevels_per_elec is number of levels within electronic 0 - so nEner is one
2069  * beyond end of array here - but will be decremented at start of loop
2070  * this starts at the highest energy wihtin X and moves down to lower energies */
2071  long nEner = nLevels_per_elec[0];
2072  while( (--nEner) >= nXLevelsMatrix )
2073  {
2074  /* array of energy sorted indices within X - we are moving down
2075  * starting from highest level within X */
2076  long iElec = ipElec_H2_energy_sort[nEner];
2077  ASSERT( iElec == 0 );
2078  long iVib = ipVib_H2_energy_sort[nEner];
2079  long iRot = ipRot_H2_energy_sort[nEner];
2080 
2081  if( nEner+1 < nLevels_per_elec[0] )
2082  ASSERT( states[nEner].energy().WN() < states[nEner+1].energy().WN() ||
2083  fp_equal( states[nEner].energy().WN(), states[nEner+1].energy().WN() ) );
2084 
2085  /* >>chng 05 apr 30,GS, Instead of *dense_total, the specific populations are used because high levels have much less
2086  * populations than ground levels which consists most of the H2 population.
2087  * only do this if working level is not v=0, J=0, 1 */
2088  if( nEner >1 )
2089  {
2090  H2_col_rate_out[iVib][iRot] +=
2091  /* H2 grain interactions
2092  * rate (s-1) all v,J levels go to 0 or 1 preserving spin */
2094 
2095  /* this goes into v=0, and J=0 or 1 depending on whether initial
2096  * state is ortho or para */
2097  H2_col_rate_in[0][H2_lgOrtho[0][iVib][iRot]] +=
2098  /* H2 grain interactions
2099  * rate (cm-3 s-1) all v,J levels go to 0 or 1 preserving spin,
2100  * in above lgOrtho says whether should go to 0 or 1 */
2102  }
2103  else if( nEner == 1 )
2104  {
2105  /* this is special J=1 to J=0 collision, which is only fast at
2106  * very low grain temperatures */
2107  H2_col_rate_out[0][1] +=
2108  /* H2 grain interactions
2109  * H2 ortho - para conversion on grain surface,
2110  * rate (s-1) all v,J levels go to 0 or 1, preserving nuclear spin */
2112 
2113  H2_col_rate_in[0][0] +=
2114  /* H2 grain interactions
2115  * H2 ortho - para conversion on grain surface,
2116  * rate (s-1) all v,J levels go to 0 or 1, preserving nuclear spin */
2118  }
2119 
2120  double pump_from_below = 0.;
2121  for( long ipLo = 0; ipLo<nEner; ++ipLo )
2122  {
2123  long iElecLo = ipElec_H2_energy_sort[ipLo];
2124  ASSERT( iElecLo == 0 );
2125  long iVibLo = ipVib_H2_energy_sort[ipLo];
2126  long iRotLo = ipRot_H2_energy_sort[ipLo];
2127  const TransitionList::iterator&tr = trans.begin() +ipTransitionSort[nEner][ipLo] ;
2128 
2129  /* the test on vibration is needed - the energies are ok but the space does not exist */
2130  if( ( abs(iRotLo-iRot) == 2 || iRotLo == iRot ) && (iVibLo <= iVib) && (*tr).ipCont() > 0 )
2131  {
2132  double rateone = (*tr).Emis().Aul() * ( (*tr).Emis().Pesc() + (*tr).Emis().Pelec_esc() + (*tr).Emis().Pdest() );
2133  // Pumping and cosmic-ray excitation from these levels up to higher (than X) levels is already included in H2_rad_rate_out before reaching here.
2134  // The following lines take care of that process within X
2135  double CosmicRayHILyaExcitationRate = ( hmi.lgLeidenCRHack ) ? secondaries.x12tot : 0.;
2136  double pump_up = (*tr).Emis().pump() + CosmicRayHILyaExcitationRate * (*tr).Coll().col_str();
2137  pump_from_below += pump_up * H2_old_populations[iElecLo][iVibLo][iRotLo];
2138  rateone += pump_up * (*(*tr).Lo()).g() / (*(*tr).Hi()).g();
2139  ASSERT( rateone >=0 );
2140  H2_rad_rate_out[0][iVib][iRot] += rateone;
2141  H2_rad_rate_in[iVibLo][iRotLo] += rateone * H2_old_populations[iElec][iVib][iRot];
2142  }
2143  }
2144 
2145  /* we now have the total rates into and out of this level, get its population
2146  * units cm-3 */
2147  states[nEner].Pop() =
2148  (H2_col_rate_in[iVib][iRot]+ H2_rad_rate_in[iVib][iRot]+H2_X_source[nEner]+pump_from_below) /
2149  SDIV(H2_col_rate_out[iVib][iRot]+H2_rad_rate_out[0][iVib][iRot]+H2_X_sink[nEner]);
2150 
2151  ASSERT( states[nEner].Pop() >= 0. );
2152  }
2153 
2154  return;
2155 }
2156 
2157 /*H2_cooling evaluate cooling and heating due to H2 molecule, called by
2158  * H2_LevelPops in convergence loop when h2 heating is important, also
2159  * called by CoolEvaluate to get final heating - argument is name of
2160  * routine that called it */
2161 #if defined(__ICC) && defined(__i386)
2162 #pragma optimization_level 1
2163 #endif
2165  /* string saying who called this routine,
2166  * "H2lup" call within H2 level populations solver
2167  * "CoolEvaluate" call from main cooling routine */
2168  const char *chRoutine)
2169 {
2170  DEBUG_ENTRY( "H2_Cooling()" );
2171 
2172  /* nCall_this_iteration is not incremented until after the level
2173  * populations have converged the first time. so for the first n calls
2174  * this will return zero, a good idea since populations will be wildly
2175  * incorrect during search for first valid pops */
2176  if( !lgEnabled || !nCall_this_iteration )
2177  {
2178  HeatDexc = 0.;
2179  HeatDiss = 0.;
2180  HeatDexc_deriv = 0.;
2181  return;
2182  }
2183 
2184  if( 0 )
2185  fprintf( ioQQQ, "DEBUG H2_Cooling called by %s.\n", chRoutine );
2186 
2187  HeatDiss = 0.;
2188  /* heating due to dissociation of electronic excited states */
2189  for( qList::iterator st = states.begin(); st != states.end(); ++st )
2190  {
2191  long iElec = (*st).n();
2192  long iVib = (*st).v();
2193  long iRot = (*st).J();
2194  HeatDiss += (*st).Pop() * H2_dissprob[iElec][iVib][iRot] * H2_disske[iElec][iVib][iRot];
2195  }
2196 
2197  /* dissociation heating was in eV - convert to ergs */
2198  HeatDiss *= EN1EV;
2199 
2200  /* now work on collisional heating due to bound-bound
2201  * collisional transitions within X */
2202  HeatDexc = 0.;
2203  /* these are the colliders that will be considered as depopulating agents */
2204  /* the colliders are H, He, H2 ortho, H2 para, H+ */
2205  /* atomic hydrogen */
2206 
2207  /* this will be derivative */
2208  HeatDexc_deriv = 0.;
2209  long iElecHi = 0;
2210  long iElecLo = 0;
2211  for( long ipHi=1; ipHi<nLevels_per_elec[iElecHi]; ++ipHi )
2212  {
2213  long iVibHi = ipVib_H2_energy_sort[ipHi];
2214  long iRotHi = ipRot_H2_energy_sort[ipHi];
2215  realnum H2statHi = states[ipHi].g();
2216  double H2boltzHi = H2_Boltzmann[iElecHi][iVibHi][iRotHi];
2217  double H2popHi = states[ipHi].Pop();
2218  double ewnHi = states[ipHi].energy().WN();
2219 
2220  for( long ipLo=0; ipLo<ipHi; ++ipLo )
2221  {
2222  long iVibLo = ipVib_H2_energy_sort[ipLo];
2223  long iRotLo = ipRot_H2_energy_sort[ipLo];
2224  double rate_dn_heat = 0.;
2225 
2226  /* this sum is total downward heating summed over all colliders */
2227  mr3ci H2cr = CollRateCoeff.begin(ipHi, ipLo);
2228  for( long nColl=0; nColl<N_X_COLLIDER; ++nColl )
2229  /* downward collision rate */
2230  rate_dn_heat += H2cr[nColl]*collider_density[nColl];
2231 
2232  /* now get upward collisional cooling by detailed balance */
2233  double rate_up_cool = rate_dn_heat * states[ipLo].Pop() *
2234  /* rest converts into upward collision rate */
2235  H2statHi / H2_stat[iElecLo][iVibLo][iRotLo] *
2236  H2boltzHi / SDIV( H2_Boltzmann[iElecLo][iVibLo][iRotLo] );
2237 
2238  rate_dn_heat *= H2popHi;
2239 
2240  /* net heating due to collisions within X -
2241  * positive if heating, negative is cooling
2242  * this will usually be heating if X is photo pumped
2243  * in printout and in save heating this is called "H2cX" */
2244  double conversion = (ewnHi - states[ipLo].energy().WN() ) * ERG1CM;
2245  double heatone = rate_dn_heat * conversion;
2246  double coolone = rate_up_cool * conversion;
2247  /* this is net heating, negative if cooling */
2248  double oneline = heatone - coolone;
2249  HeatDexc += oneline;
2250 
2251  /* derivative wrt temperature - assume exp wrt ground -
2252  * this needs to be divided by square of temperature in wn -
2253  * done at end of loop */
2254  HeatDexc_deriv += (realnum)(oneline * ewnHi);
2255 
2256  /* this would be a major logical error */
2257  ASSERT(
2258  (rate_up_cool==0 && rate_dn_heat==0) ||
2259  (states[ipHi].energy().WN() > states[ipLo].energy().WN()) );
2260  }/* end loop over lower levels, all collisions within X */
2261  }/* end loop over upper levels, all collisions within X */
2262 
2263  /* this is inside h2 cooling, and is called extra times when H2 heating is important */
2264  if( PRT_POPS )
2265  fprintf(ioQQQ,
2266  " DEBUG H2 heat fnzone\t%.2f\trenorm\t%.3e\tte\t%.4e\tdexc\t%.3e\theat/tot\t%.3e\n",
2267  fnzone ,
2269  phycon.te ,
2270  HeatDexc,
2272 
2273  /* this is derivative of collisional heating wrt temperature - needs
2274  * to be divided by square of temperature in wn */
2276 
2277  if( nTRACE >= n_trace_full )
2278  fprintf(ioQQQ,
2279  " H2_Cooling Ctot\t%.4e\t HeatDiss \t%.4e\t HeatDexc \t%.4e\n" ,
2280  thermal.ctot ,
2281  HeatDiss ,
2282  HeatDexc );
2283 
2284  /* when we are very far from solution, during search phase, collisions within
2285  * X can be overwhelmingly large heating and cooling terms, which nearly
2286  * cancel out. Some dense cosmic ray heated clouds could not find correct
2287  * initial solution due to noise introduced by large net heating which was
2288  * the very noisy tiny difference between very large heating and cooling
2289  * terms. Do not include collisions with x as heat/cool during the
2290  * initial search phase */
2291  if( conv.lgSearch )
2292  {
2293  HeatDexc = 0.;
2294  HeatDexc_deriv = 0.;
2295  }
2296  return;
2297 }
2298 
2299 /*cdH2_colden return column density in H2, negative -1 if cannot find state,
2300  * header is cdDrive */
2301 double cdH2_colden( long iVib , long iRot )
2302 {
2303  diatomics& diatom = h2;
2304 
2305  /*if iVib is negative, return
2306  * total column density - iRot=0
2307  * ortho column density - iRot 1
2308  * para column density - iRot 2
2309  * else return column density in iVib, iRot */
2310  if( iVib < 0 )
2311  {
2312  if( iRot==0 )
2313  {
2314  /* return total column density */
2315  return( diatom.ortho_colden + diatom.para_colden );
2316  }
2317  else if( iRot==1 )
2318  {
2319  /* return ortho H2 column density */
2320  return diatom.ortho_colden;
2321  }
2322  else if( iRot==2 )
2323  {
2324  /* return para H2 column density */
2325  return diatom.para_colden;
2326  }
2327  else
2328  {
2329  fprintf(ioQQQ," iRot must be 0 (total), 1 (ortho), or 2 (para), returning -1.\n");
2330  return -1.;
2331  }
2332  }
2333  else if( diatom.lgEnabled )
2334  {
2335  return diatom.GetXColden( iVib, iRot );
2336  }
2337  /* error condition - no valid parameter */
2338  else
2339  return -1;
2340 }
2341 
2342 realnum diatomics::GetXColden( long iVib, long iRot )
2343 {
2344  DEBUG_ENTRY( "diatomics::GetXColden()" );
2345 
2346  /* this branch want state specific column density, which can only result from
2347  * evaluation of big molecule */
2348  int iElec = 0;
2349  if( iRot <0 || iVib >nVib_hi[iElec] || iRot > nRot_hi[iElec][iVib])
2350  {
2351  fprintf(ioQQQ," iVib and iRot must lie within X, returning -2.\n");
2352  fprintf(ioQQQ," iVib must be <= %li and iRot must be <= %li.\n",
2353  nVib_hi[iElec],nRot_hi[iElec][iVib]);
2354  return -2.;
2355  }
2356  else
2357  return H2_X_colden[iVib][iRot];
2358 }
2359 
2360 /*H2_Colden maintain H2 column densities within X */
2361 void diatomics::H2_Colden( const char *chLabel )
2362 {
2363  /* >>chng 05 jan 26, pops now set to LTE for small abundance case, so do this */
2364  if( !lgEnabled /*|| !nCall_this_zone*/ )
2365  return;
2366 
2367  DEBUG_ENTRY( "H2_Colden()" );
2368 
2369  if( strcmp(chLabel,"ZERO") == 0 )
2370  {
2371  /* zero out formation rates and column densites */
2372  H2_X_colden.zero();
2374  }
2375 
2376  else if( strcmp(chLabel,"ADD ") == 0 )
2377  {
2378  /* add together column densities */
2379  for( qList::iterator st = states.begin(); st != states.end(); ++st )
2380  {
2381  long iElec = (*st).n();
2382  if( iElec > 0 ) continue;
2383  long iVib = (*st).v();
2384  long iRot = (*st).J();
2385  /* state specific H2 column density */
2386  H2_X_colden[iVib][iRot] += (realnum)( (*st).Pop() * radius.drad_x_fillfac);
2387  /* LTE state specific H2 column density - H2_populations_LTE is normed to unity
2388  * so must be multiplied by total H2 density */
2389  H2_X_colden_LTE[iVib][iRot] += (realnum)(H2_populations_LTE[0][iVib][iRot]*
2391  }
2392  }
2393 
2394  /* we will not print column densities so skip that - if not print then we have a problem */
2395  else if( strcmp(chLabel,"PRIN") != 0 )
2396  {
2397  fprintf( ioQQQ, " H2_Colden does not understand the label %s\n",
2398  chLabel );
2400  }
2401 
2402  return;
2403 }
2404 
2405 /*H2_DR choose next zone thickness based on H2 big molecule */
2406 double diatomics::H2_DR(void)
2407 {
2408  return BIGFLOAT;
2409 }
2410 
2411 /*H2_RT_OTS - add H2 ots fields */
2413 {
2414  /* do not compute if H2 not turned on, or not computed for these conditions */
2415  if( !lgEnabled || !nCall_this_zone )
2416  return;
2417 
2418  DEBUG_ENTRY( "H2_RT_OTS()" );
2419 
2420  for( TransitionList::iterator tr = trans.begin(); tr != rad_end; ++tr )
2421  {
2422  qList::iterator Hi = (*tr).Hi();
2423  if( (*Hi).n() > 0 )
2424  continue;
2425 
2426  /* ots destruction rate */
2427  (*tr).Emis().ots() = (*(*tr).Hi()).Pop() * (*tr).Emis().Aul() * (*tr).Emis().Pdest();
2428 
2429  /* dump the ots rate into the stack - but only for ground electronic state*/
2430  RT_OTS_AddLine( (*tr).Emis().ots(), (*tr).ipCont() );
2431  }
2432 
2433  return;
2434 }
2435 
2437 {
2438  /* >>chng 05 jul 09, GS*/
2439  /* average Einstein value for H2* to H2g, GS*/
2440  double sumpop1 = 0.;
2441  double sumpopA1 = 0.;
2442  double sumpopcollH2O_deexcit = 0.;
2443  double sumpopcollH2p_deexcit = 0.;
2444  double sumpopcollH_deexcit = 0.;
2445  double popH2s = 0.;
2446  double sumpopcollH2O_excit = 0.;
2447  double sumpopcollH2p_excit = 0.;
2448  double sumpopcollH_excit = 0.;
2449  double popH2g = 0.;
2450 
2451  for( qList::const_iterator stHi = states.begin(); stHi != states.end(); ++stHi )
2452  {
2453  long iElecHi = (*stHi).n();
2454  if( iElecHi > 0 ) continue;
2455  long iVibHi = (*stHi).v();
2456  long iRotHi = (*stHi).J();
2457  double ewnHi = (*stHi).energy().WN();
2458  for( qList::const_iterator stLo = states.begin(); stLo != stHi; ++stLo )
2459  {
2460  long iVibLo = (*stLo).v();
2461  long iRotLo = (*stLo).J();
2462  double ewnLo2 = (*stLo).energy().WN();
2463  if( ewnHi > ENERGY_H2_STAR && ewnLo2 < ENERGY_H2_STAR && hmi.lgLeiden_Keep_ipMH2s )
2464  {
2465  /* >>chng 05 jul 10, GS*/
2466  /* average collisional rate for H2* to H2g, GS*/
2467  if( H2_lgOrtho[0][iVibHi][iRotHi] == H2_lgOrtho[0][iVibLo][iRotLo] )
2468  {
2469  long ihi = ipEnergySort[0][iVibHi][iRotHi];
2470  long ilo = ipEnergySort[0][iVibLo][iRotLo];
2471  const TransitionList::iterator&tr =
2472  trans.begin()+ ipTransitionSort[ihi][ilo];
2473  double popHi = (*(*tr).Hi()).Pop();
2474  double popLo = (*(*tr).Lo()).Pop();
2475 
2476  /* sums of populations */
2477  popH2s += popHi;
2478  popH2g += popLo;
2479 
2480  /* sums of deexcitation rates - H2* to H2g */
2481  sumpopcollH_deexcit += popHi * CollRateCoeff[ihi][ilo][0];
2482  sumpopcollH2O_deexcit += popHi * CollRateCoeff[ihi][ilo][2];
2483  sumpopcollH2p_deexcit += popHi * CollRateCoeff[ihi][ilo][3];
2484 
2485  double temp = popLo *
2486  H2_stat[0][iVibHi][iRotHi] / H2_stat[0][iVibLo][iRotLo] *
2487  H2_Boltzmann[0][iVibHi][iRotHi] /SDIV( H2_Boltzmann[0][iVibLo][iRotLo] );
2488 
2489  /* sums of excitation rates - H2g to H2* */
2490  sumpopcollH_excit += temp * CollRateCoeff[ihi][ilo][0];
2491  sumpopcollH2O_excit += temp * CollRateCoeff[ihi][ilo][2];
2492  sumpopcollH2p_excit += temp * CollRateCoeff[ihi][ilo][3];
2493 
2494  if( lgH2_radiative[ihi][ilo] )
2495  {
2496  sumpop1 += popHi;
2497  sumpopA1 += popHi * (*tr).Emis().Aul();
2498  }
2499  }
2500  }
2501  }
2502  }
2503  Average_A = sumpopA1/SDIV(sumpop1);
2504 
2505  /* collisional excitation and deexcitation of H2g and H2s */
2506  Average_collH2_deexcit = (sumpopcollH2O_deexcit+sumpopcollH2p_deexcit)/SDIV(popH2s);
2507  Average_collH2_excit = (sumpopcollH2O_excit+sumpopcollH2p_excit)/SDIV(popH2g);
2508  Average_collH_excit = sumpopcollH_excit/SDIV(popH2g);
2509  Average_collH_deexcit = sumpopcollH_deexcit/SDIV(popH2s);
2510 
2511  /*fprintf(ioQQQ,
2512  "DEBUG Average_collH_excit sumpop = %.2e %.2e %.2e %.2e %.2e %.2e \n",
2513  popH2g,popH2s,sumpopcollH_deexcit ,sumpopcollH_excit ,
2514  sumpopcollH_deexcit/SDIV(popH2s) ,sumpopcollH_excit/SDIV(popH2g));*/
2515  /*fprintf(ioQQQ,"sumpop = %le sumpopA = %le Av= %le\n",
2516  sumpop1,sumpopA1 , Average_A );*/
2517 
2518  return;
2519 }
2520 
2522 {
2523  double H2_sum_excit_elec_den = 0.;
2524  for( long iElecHi=0; iElecHi<n_elec_states; ++iElecHi )
2525  {
2526  if( iElecHi > 0 )
2527  H2_sum_excit_elec_den += pops_per_elec[iElecHi];
2528  }
2529 
2530  return H2_sum_excit_elec_den;
2531 }
2532 /*lint +e802 possible bad pointer */
2533 
multi_arr< double, 2 > H2_rad_rate_in
Definition: h2_priv.h:650
#define MIN2
Definition: cddefines.h:765
int nTRACE
Definition: h2_priv.h:396
realnum x12tot
Definition: secondaries.h:53
iterator begin(size_type i1)
multi_arr< double, 2 > H2_col_rate_out
Definition: h2_priv.h:649
multi_arr< double, 2 >::const_iterator md2ci
const int N_ELEC
Definition: h2_priv.h:21
double sink_rate_tot(const char chSpecies[]) const
t_mole_global mole_global
Definition: mole.cpp:6
multi_arr< realnum, 3 > H2_dissprob
Definition: h2_priv.h:629
bool lgStancil
Definition: mole.h:289
const double ENERGY_H2_STAR
Definition: h2_priv.h:582
double Average_A
Definition: h2_priv.h:293
t_thermal thermal
Definition: thermal.cpp:5
double gs_rate(void)
realnum GetXColden(long iVib, long iRot)
Definition: mole_h2.cpp:2342
double renorm_min
Definition: h2_priv.h:333
double rel_pop_LTE_s
Definition: h2_priv.h:280
double rel_pop_LTE_g
Definition: h2_priv.h:279
void H2_RTMake(void)
Definition: mole_h2.cpp:364
double H2_DissocEnergies[N_ELEC]
Definition: h2_priv.h:606
void SolveExcitedElectronicLevels(void)
Definition: mole_h2.cpp:1914
void H2_LineZero(void)
Definition: mole_h2.cpp:416
double EdenErrorAllowed
Definition: conv.h:267
NORETURN void TotalInsanity(void)
Definition: service.cpp:886
double Average_collH2_excit
Definition: h2_priv.h:297
long int n_elec_states
Definition: h2_priv.h:406
realnum ** flux
Definition: rfield.h:86
double HeatChangeOld
Definition: h2_priv.h:290
realnum mass_amu
Definition: h2_priv.h:393
double spon_diss_tot
Definition: h2_priv.h:259
const realnum SMALLFLOAT
Definition: cpu.h:178
void H2_LevelPops(bool &lgPopsConverged, double &old_value, double &new_value)
Definition: mole_h2.cpp:883
bool lgLeiden_Keep_ipMH2s
Definition: hmi.h:208
int n_trace_full
Definition: h2_priv.h:399
double ortho_para_older
Definition: h2_priv.h:329
double average_energy_s
Definition: h2_priv.h:284
realnum * outlin_noplot
Definition: rfield.h:199
multi_arr< double, 2 > pops_per_vib
Definition: h2_priv.h:597
double H2_rate_destroy
Definition: hmi.h:21
double HeatDexc_deriv
Definition: h2_priv.h:289
multi_arr< realnum, 2 > H2_coll_dissoc_rate_coef_H2
Definition: h2_priv.h:668
char TorF(bool l)
Definition: cddefines.h:714
#define MAX2
Definition: cddefines.h:786
double ctot
Definition: thermal.h:110
molecule * sp_star
Definition: h2_priv.h:561
valarray< long > ipVib_H2_energy_sort
Definition: h2_priv.h:684
double ortho_colden
Definition: h2_priv.h:325
void RT_line_one(const TransitionProxy &t, bool lgShield_this_zone, realnum pestrk, realnum DopplerWidth)
t_conv conv
Definition: conv.cpp:5
double H2_to_H_limit
Definition: h2_priv.h:391
double ** AulEscp
Definition: h2_priv.h:694
bool lgEvaluated
Definition: h2_priv.h:307
t_phycon phycon
Definition: phycon.cpp:6
bool fp_equal_tol(sys_float x, sys_float y, sys_float tol)
Definition: cddefines.h:858
t_dense dense
Definition: dense.cpp:24
double rate_grain_op_conserve
Definition: h2_priv.h:270
double ** col_str
Definition: h2_priv.h:694
multi_arr< realnum, 3 >::const_iterator mr3ci
bool lgConvPops
Definition: conv.h:143
iterator begin(void)
Definition: transition.h:305
sys_float sexp(sys_float x)
Definition: service.cpp:914
#define PRT_POPS
Definition: mole_h2.cpp:22
static double * g
Definition: species2.cpp:28
long ipFineCont(double energy_ryd)
realnum ** outlin
Definition: rfield.h:199
FILE * ioQQQ
Definition: cddefines.cpp:7
double H2_den_g
Definition: h2_priv.h:679
molezone * findspecieslocal(const char buf[])
multi_arr< realnum, 2 > H2_coll_dissoc_rate_coef
Definition: h2_priv.h:665
double HeatChange
Definition: h2_priv.h:290
#define H2_DISS_ALLISON_DALGARNO
Definition: mole_h2.cpp:27
long int nzone
Definition: cddefines.cpp:14
double source_rate_tot(const char chSpecies[]) const
TransitionList trans
Definition: h2_priv.h:563
static realnum collider_density[N_X_COLLIDER]
Definition: mole_h2.cpp:51
double Average_collH2_dissoc_g
Definition: h2_priv.h:302
realnum * flux_accum
Definition: rfield.h:95
void H2_X_sink_and_source(void)
Definition: mole_h2.cpp:54
double xIonDense[LIMELM][LIMELM+1]
Definition: dense.h:125
double Average_collH2_deexcit
Definition: h2_priv.h:295
int n_trace_matrix
Definition: h2_priv.h:399
multi_arr< realnum, 2 > H2_X_formation
Definition: h2_priv.h:653
double Solomon_dissoc_rate_g
Definition: h2_priv.h:261
double TeUsedColl
Definition: h2_priv.h:413
multi_arr< realnum, 3 > CollRateCoeff
Definition: h2_priv.h:618
const double *const dense_total
Definition: h2_priv.h:586
multi_arr< realnum, 3 > H2_stat
Definition: h2_priv.h:638
static realnum collider_density_total_not_H2
Definition: mole_h2.cpp:52
valarray< realnum > H2_X_sink
Definition: h2_priv.h:672
long int levelAsEval
Definition: h2_priv.h:701
double ** CollRate_levn
Definition: h2_priv.h:694
multi_arr< double, 3 > Cont_Dissoc_Rate
Definition: h2_priv.h:276
multi_arr< long int, 3 > ipEnergySort
Definition: h2_priv.h:687
multi_arr< double, 3 > H2_old_populations
Definition: h2_priv.h:634
bool lgTemperatureConstant
Definition: thermal.h:32
double GetExcitedElecDensity(void)
Definition: mole_h2.cpp:2521
molecule * sp
Definition: h2_priv.h:558
double Average_collH2_dissoc_s
Definition: h2_priv.h:303
qList *& states()
Definition: transition.h:325
long int iteration
Definition: cddefines.cpp:16
void H2_CollidRateEvalAll(void)
long int iterationAsEval
Definition: h2_priv.h:643
bool lgSearch
Definition: conv.h:175
t_trace trace
Definition: trace.cpp:5
const multi_geom< d, ALLOC > & clone() const
bool fp_equal(sys_float x, sys_float y, int n=3)
Definition: cddefines.h:816
#define MALLOC(exp)
Definition: cddefines.h:505
multi_arr< realnum, 3 > H2_disske
Definition: h2_priv.h:630
double ortho_density
Definition: h2_priv.h:316
double HeatDexc_old
Definition: h2_priv.h:288
int n_trace_iterations
Definition: h2_priv.h:399
const double EVRYD
Definition: physconst.h:189
realnum para_density_f
Definition: h2_priv.h:321
void mole_H2_LTE(void)
long ipoint(double energy_ryd)
Definition: cont_ipoint.cpp:16
double cdH2_colden(long iVib, long iRot)
Definition: mole_h2.cpp:2301
void H2_RT_diffuse(void)
Definition: mole_h2.cpp:345
void mole_H2_form(void)
double para_density
Definition: h2_priv.h:316
const double ERG1CM
Definition: physconst.h:164
void H2_RT_tau_reset(void)
Definition: mole_h2.cpp:432
double H2_frac_abund_set
Definition: hmi.h:185
double ortho_para_old
Definition: h2_priv.h:329
bool lgLTE
Definition: h2_priv.h:366
double photodissoc_BigH2_H2s
Definition: h2_priv.h:254
#define POW2
Definition: cddefines.h:933
long int nzoneAsEval
Definition: h2_priv.h:643
long int nLevels_per_elec[N_ELEC]
Definition: h2_priv.h:615
vector< double > destroy
Definition: h2_priv.h:699
bool lgEnabled
Definition: h2_priv.h:342
double H2_den_s
Definition: h2_priv.h:679
long ipLineEnergy(double energy, const char *chLabel, long ipIonEnergy)
long int nzoneEval
Definition: h2_priv.h:385
multi_arr< double, 3 > H2_populations_LTE
Definition: h2_priv.h:636
string label
Definition: h2_priv.h:568
long int nCall_this_zone
Definition: h2_priv.h:338
void H2_RT_tau_inc(void)
Definition: mole_h2.cpp:386
t_mole_local mole
Definition: mole.cpp:7
t_rfield rfield
Definition: rfield.cpp:8
double Average_collH_deexcit
Definition: h2_priv.h:296
const int N_X_COLLIDER
Definition: h2_priv.h:13
bool lgFirst
Definition: h2_priv.h:702
long int nCall_this_iteration
Definition: h2_priv.h:723
double H2_InterEnergy(void)
long int ndimMalloced
Definition: h2_priv.h:693
void H2_RT_OTS(void)
Definition: mole_h2.cpp:2412
realnum * ConInterOut
Definition: rfield.h:164
float realnum
Definition: cddefines.h:107
valarray< class molezone > species
Definition: mole.h:355
#define EXIT_FAILURE
Definition: cddefines.h:144
multi_arr< realnum, 2 > H2_X_colden_LTE
Definition: h2_priv.h:659
double photodissoc_BigH2_H2g
Definition: h2_priv.h:255
multi_arr< bool, 2 > lgH2_radiative
Definition: h2_priv.h:711
long int nH2_pops
Definition: h2_priv.h:714
const realnum BIGFLOAT
Definition: cpu.h:176
long int nXLevelsMatrix
Definition: h2_priv.h:692
multi_arr< realnum, 2 > H2_X_Hmin_back
Definition: h2_priv.h:655
double ** AulDest
Definition: h2_priv.h:694
#define cdEXIT(FAIL)
Definition: cddefines.h:438
int index
Definition: mole.h:169
double PressureRadiationLine(const TransitionProxy &t, realnum DopplerWidth)
Definition: pressure.h:18
multi_arr< double, 3 > H2_rad_rate_out
Definition: h2_priv.h:631
void RT_OTS_AddLine(double ots, long int ip)
Definition: rt_ots.cpp:402
diatomics h2("h2", 4100.,&hmi.H2_total, Yan_H2_CS)
bool lgColl_deexec_Calc
Definition: h2_priv.h:356
realnum GetDopplerWidth(realnum massAMU)
double average_energy_g
Definition: h2_priv.h:283
double H2_DR(void)
Definition: mole_h2.cpp:2406
valarray< long > ipElec_H2_energy_sort
Definition: h2_priv.h:685
int nTrConvg
Definition: trace.h:27
realnum HeatCoolRelErrorAllowed
Definition: conv.h:278
void H2_ContPoint(void)
Definition: mole_h2.cpp:253
void RT_line_one_tauinc(const TransitionProxy &t, long int mas_species, long int mas_ion, long int mas_hi, long int mas_lo, realnum DopplerWidth)
void ConvFail(const char chMode[], const char chDetail[])
Definition: conv_fail.cpp:18
t_radius radius
Definition: radius.cpp:5
long int loop_h2_oscil
Definition: h2_priv.h:384
long int nTotalIoniz
Definition: conv.h:166
const double EN1EV
Definition: physconst.h:192
realnum ortho_density_f
Definition: h2_priv.h:321
double rate_grain_J1_to_J0
Definition: h2_priv.h:271
#define FRAC
double HeatDexc
Definition: h2_priv.h:287
void H2_Calc_Average_Rates(void)
Definition: mole_h2.cpp:2436
bool lgLeidenCRHack
Definition: hmi.h:209
double ortho_para_current
Definition: h2_priv.h:329
iterator end()
Definition: quantumstate.h:345
void atom_levelN(long int nLevelCalled, realnum abund, const double g[], const double ex[], char chExUnits, double pops[], double depart[], double ***AulEscp, double ***col_str, double ***AulDest, double ***AulPump, double ***CollRate, const double source[], const double sink[], bool lgCollRateDone, double *cooltl, double *coolder, const char *chLabel, int *nNegPop, bool *lgZeroPop, bool lgDeBug, bool lgLTE, multi_arr< double, 2 > *Cool, multi_arr< double, 2 > *dCooldT)
Definition: atom_leveln.cpp:15
double frac_matrix
Definition: h2_priv.h:409
#define ASSERT(exp)
Definition: cddefines.h:582
void H2_Solomon_rate(void)
Definition: mole_h2_etc.cpp:24
double HeatDiss
Definition: h2_priv.h:286
vector< double > stat_levn
Definition: h2_priv.h:699
multi_arr< double, 2 > H2_X_rate_to_elec_excited
Definition: h2_priv.h:663
double htot
Definition: thermal.h:149
void H2_zero_pops_too_low(void)
multi_arr< double, 2 > H2_X_rate_from_elec_excited
Definition: h2_priv.h:661
double renorm_max
Definition: h2_priv.h:333
double drad_x_fillfac
Definition: radius.h:71
qList states
Definition: h2_priv.h:562
void H2_Colden(const char *chLabel)
Definition: mole_h2.cpp:2361
double den
Definition: mole.h:396
static double ** CollRate
Definition: species2.cpp:29
double Average_collH_excit
Definition: h2_priv.h:298
double H2_RadPress(void)
Definition: mole_h2.cpp:294
void H2_X_coll_rate_evaluate(void)
Definition: mole_h2.cpp:175
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:688
double Solomon_dissoc_rate_s
Definition: h2_priv.h:262
iterator begin()
Definition: quantumstate.h:337
void H2_Level_low_matrix(realnum abundance)
Definition: mole_h2.cpp:448
const int ipHELIUM
Definition: cddefines.h:310
iterator end(size_type i1)
multi_arr< double, 2 >::iterator md2i
multi_arr< long int, 2 > ipTransitionSort
Definition: h2_priv.h:688
void mole_update_species_cache(void)
double H2_total
Definition: hmi.h:16
double eden
Definition: dense.h:190
vector< double > pops
Definition: h2_priv.h:699
void H2_Cooling(const char *chString)
Definition: mole_h2.cpp:2164
realnum H2_total_f
Definition: hmi.h:17
TransitionList::iterator rad_end
Definition: h2_priv.h:564
bool lgInducProcess
Definition: rfield.h:252
valarray< long > nRot_hi[N_ELEC]
Definition: h2_priv.h:610
double pops_per_elec[N_ELEC]
Definition: h2_priv.h:617
sys_float SDIV(sys_float x)
Definition: cddefines.h:956
double para_colden
Definition: h2_priv.h:325
double H2_renorm_chemistry
Definition: h2_priv.h:600
vector< double > create
Definition: h2_priv.h:699
double Average_collH_dissoc_g
Definition: h2_priv.h:300
valarray< long > ipRot_H2_energy_sort
Definition: h2_priv.h:686
double ** AulPump
Definition: h2_priv.h:694
valarray< realnum > H2_X_source
Definition: h2_priv.h:671
double H2_Accel(void)
Definition: mole_h2.cpp:271
long int nzone_nlevel_set
Definition: h2_priv.h:718
double te_wn
Definition: phycon.h:20
double Average_collH_dissoc_s
Definition: h2_priv.h:301
void CalcPhotoionizationRate(void)
t_secondaries secondaries
Definition: secondaries.cpp:5
t_hmi hmi
Definition: hmi.cpp:5
double fnzone
Definition: cddefines.cpp:15
#define LIM_H2_POP_LOOP
Definition: mole_h2.cpp:24
multi_arr< double, 3 > H2_Boltzmann
Definition: h2_priv.h:635
double te
Definition: phycon.h:11
const int ipHYDROGEN
Definition: cddefines.h:309
void fixit(void)
Definition: service.cpp:991
void RT_line_one_tau_reset(const TransitionProxy &t)
vector< double > depart
Definition: h2_priv.h:699
vector< double > excit
Definition: h2_priv.h:699
multi_arr< realnum, 2 > H2_X_coll_rate
Definition: h2_priv.h:603
double H2_itrzn(void)
Definition: mole_h2.cpp:240
long int nVib_hi[N_ELEC]
Definition: h2_priv.h:608
void SolveSomeGroundElectronicLevels(void)
Definition: mole_h2.cpp:2025
double te32
Definition: phycon.h:48
multi_arr< double, 2 > H2_col_rate_in
Definition: h2_priv.h:648
bool lgAbort
Definition: cddefines.cpp:10
int n_trace_final
Definition: h2_priv.h:399
multi_arr< realnum, 2 > H2_X_colden
Definition: h2_priv.h:657
const double SAHA
Definition: physconst.h:161
long int nH2_zone
Definition: h2_priv.h:715
multi_arr< int, 2 > H2_ipPhoto
Definition: h2_priv.h:647
multi_arr< bool, 3 > H2_lgOrtho
Definition: h2_priv.h:640