cloudy  trunk
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
species2.cpp
Go to the documentation of this file.
1 /* This file is part of Cloudy and is copyright (C)1978-2022 by Gary J. Ferland and
2  * others. For conditions of distribution and use see copyright notice in license.txt */
3 #include "cddefines.h"
4 #include "atmdat.h"
5 #include "phycon.h"
6 #include "taulines.h"
7 #include "atoms.h"
8 #include "rfield.h"
9 #include "conv.h"
10 #include "secondaries.h"
11 #include "thermal.h"
12 #include "cooling.h"
13 #include "ionbal.h"
14 #include "iso.h"
15 #include "mole.h"
16 #include "dense.h"
17 #include "lines_service.h"
18 #include "trace.h"
19 #include "doppvel.h"
20 #include "oxy.h"
21 #include "hydrogenic.h"
22 #include "vectorize.h"
23 
24 STATIC double LeidenCollRate(long, long, const TransitionProxy& ,double);
25 STATIC double StoutCollRate(long ipSpecies, long ipCollider, const TransitionProxy&, double ftemp);
26 STATIC double ChiantiCollRate(long ipSpecies, long ipCollider, const TransitionProxy&, double ftemp);
27 STATIC void setXtraRatesO1(const TransitionProxy& tr, double &xtraExRate, double &xtraDxRate);
28 STATIC void setXtraRatesCa2(const TransitionProxy& tr, double &xtraDxRate);
29 STATIC void setXtraRatesFe2(const TransitionProxy& tr, double &xtraExRate, double &xtraDxRate);
30 
31 static const bool DEBUGSTATE = false;
32 
33 static realnum dBaseAbund(long ipSpecies)
34 {
35  realnum abund;
36  /* first find current density (cm-3) of species */
37  if( dBaseSpecies[ipSpecies].lgMolecular )
38  {
41  molezone *SpeciesCurrent = findspecieslocal(dBaseSpecies[ipSpecies].chLabel);
42  if( !exists( SpeciesCurrent ) )
43  {
44  /* did not find the species - print warning for now */
45  if( !conv.nTotalIoniz )
46  fprintf(ioQQQ," PROBLEM dBase_solve did not find molecular species %li\n",ipSpecies);
47  }
48  abund = (realnum)SpeciesCurrent->den;
49  }
50  else
51  {
52  /* an atom or ion */
53  ASSERT( dBaseStates[ipSpecies][0].nelem()<=LIMELM && dBaseStates[ipSpecies][0].IonStg()<=dBaseStates[ipSpecies][0].nelem()+1 );
54  abund = dense.xIonDense[ dBaseStates[ipSpecies][0].nelem()-1 ][ dBaseStates[ipSpecies][0].IonStg()-1 ];
55  }
56 
57  abund *= dBaseSpecies[ipSpecies].fracType * dBaseSpecies[ipSpecies].fracIsotopologue;
58  return abund;
59 }
60 
61 void dBaseTrim(void)
62 {
63  DEBUG_ENTRY( "dBaseTrim()" );
64  // initialization at start of each iteration
65  if( conv.nTotalIoniz == 0)
66  {
67  for( long ipSpecies=0; ipSpecies<nSpecies; ipSpecies++ )
68  {
69  dBaseSpecies[ipSpecies].lgActive = true;
70  }
71  }
72 
73  for( long ipSpecies=0; ipSpecies<nSpecies; ipSpecies++ )
74  {
75  realnum abund = dBaseAbund(ipSpecies);
76  bool lgMakeInActive = (abund <= 1e-20 * dense.xNucleiTotal);
77  if( lgMakeInActive && dBaseSpecies[ipSpecies].lgActive )
78  {
79  // zero out populations and intensities, if previously not set
80  dBaseStates[ipSpecies][0].Pop() = 0.;
81  for(long ipHi = 1; ipHi < dBaseSpecies[ipSpecies].numLevels_max; ipHi++ )
82  {
83  dBaseStates[ipSpecies][ipHi].Pop() = 0.;
84  }
85  for (TransitionList::iterator tr=dBaseTrans[ipSpecies].begin();
86  tr != dBaseTrans[ipSpecies].end(); ++tr)
87  {
88  (*tr).Emis().xIntensity() = 0.;
89  (*tr).Emis().xObsIntensity() = 0.;
90  (*tr).Coll().col_str() = 0.;
91  (*tr).Coll().cool() = 0.;
92  (*tr).Coll().heat() = 0.;
93  }
94  dBaseSpecies[ipSpecies].lgActive = false;
95  }
96 
97  if( !lgMakeInActive )
98  dBaseSpecies[ipSpecies].lgActive = true;
99  }
100 }
101 
103 {
104  DEBUG_ENTRY( "dBaseUpdateCollCoeffs()" );
105  for( long ipSpecies=0; ipSpecies<nSpecies; ipSpecies++ )
106  {
107  if( !dBaseSpecies[ipSpecies].lgActive )
108  continue;
109 
110  /*Setting all the collision strengths and collision rate to zero*/
111  for(TransitionList::iterator tr = dBaseTrans[ipSpecies].begin();
112  tr != dBaseTrans[ipSpecies].end(); ++tr)
113  {
114  int ipHi = (*tr).ipHi();
115  if (ipHi >= dBaseSpecies[ipSpecies].numLevels_local)
116  continue;
117  for( long k=0; k<ipNCOLLIDER; ++k )
118  (*tr).Coll().rate_coef_ul_set()[k] = 0.f;
119  }
120  /* update the collision rates */
121  /* molecule */
122  if( dBaseSpecies[ipSpecies].database == "LAMDA" )
123  {
124  for(TransitionList::iterator tr = dBaseTrans[ipSpecies].begin();
125  tr != dBaseTrans[ipSpecies].end(); ++tr)
126  {
127  int ipHi = (*tr).ipHi();
128  if (ipHi >= dBaseSpecies[ipSpecies].numLevels_local)
129  continue;
130  for( long intCollNo=0; intCollNo<ipNCOLLIDER; intCollNo++)
131  {
132  /*using the collision rate coefficients directly*/
133  (*tr).Coll().rate_coef_ul_set()[intCollNo] =
134  LeidenCollRate(ipSpecies, intCollNo, *tr, phycon.te);
135  }
136  tr->Coll().is_gbar() = 0;
137  }
138  }
139  /* Chianti */
140  else if( dBaseSpecies[ipSpecies].database == "Chianti" )
141  {
142  for(TransitionList::iterator tr = dBaseTrans[ipSpecies].begin();
143  tr != dBaseTrans[ipSpecies].end(); ++tr)
144  {
145  if ((*tr).ipHi() >= dBaseSpecies[ipSpecies].numLevels_local)
146  continue;
147  for( long intCollNo=0; intCollNo<ipNCOLLIDER; intCollNo++)
148  {
149  (*tr).Coll().rate_coef_ul_set()[intCollNo] =
150  ChiantiCollRate(ipSpecies, intCollNo, *tr, phycon.te);
151  }
152  tr->Coll().is_gbar() = 0;
153  }
154  }
155  /* Stout */
156  else if( dBaseSpecies[ipSpecies].database == "Stout" )
157  {
158  for(TransitionList::iterator tr = dBaseTrans[ipSpecies].begin();
159  tr != dBaseTrans[ipSpecies].end(); ++tr)
160  {
161  if ((*tr).ipHi() >= dBaseSpecies[ipSpecies].numLevels_local)
162  continue;
163  for( long intCollNo=0; intCollNo<ipNCOLLIDER; intCollNo++)
164  {
165  (*tr).Coll().rate_coef_ul_set()[intCollNo] =
166  StoutCollRate(ipSpecies, intCollNo, *tr, phycon.te);
167  }
168  tr->Coll().is_gbar() = 0;
169  }
170  }
171  else
172  TotalInsanity();
173 
174  /* guess some missing data */
175  for(TransitionList::iterator tr = dBaseTrans[ipSpecies].begin();
176  tr != dBaseTrans[ipSpecies].end(); ++tr)
177  {
178  int ipHi = (*tr).ipHi();
179  if (ipHi >= dBaseSpecies[ipSpecies].numLevels_local)
180  continue;
181  const CollisionProxy &coll_temp = (*tr).Coll();
182 
183  /* make educated guesses for some missing data */
184  if( dBaseSpecies[ipSpecies].lgMolecular )
185  {
186  /*The collision rate coefficients for helium should not be present and that for molecular hydrogen should be present*/
187  if( AtmolCollRateCoeff[ipSpecies][ipATOM_HE].temps.size() == 0 &&
188  AtmolCollRateCoeff[ipSpecies][ipH2].temps.size() != 0 )
189  {
190  coll_temp.rate_coef_ul_set()[ipATOM_HE] = 0.7f * coll_temp.rate_coef_ul()[ipH2];
191  }
192 
193  /* Put in something for hydrogen collisions if not in database */
194  if( AtmolCollRateCoeff[ipSpecies][ipATOM_H].temps.size() == 0 )
195  {
196  if( AtmolCollRateCoeff[ipSpecies][ipATOM_HE].temps.size() != 0 ) //He0
197  {
198  coll_temp.rate_coef_ul_set()[ipATOM_H] = 2.0f * coll_temp.rate_coef_ul()[ipATOM_HE];
199  }
200  else if( AtmolCollRateCoeff[ipSpecies][ipH2_ORTHO].temps.size() != 0 ) //ortho-H2
201  {
202  coll_temp.rate_coef_ul_set()[ipATOM_H] = 1.4f * coll_temp.rate_coef_ul()[ipH2_ORTHO];
203  }
204  else if( AtmolCollRateCoeff[ipSpecies][ipH2_PARA].temps.size() != 0 ) //para-H2
205  {
206  coll_temp.rate_coef_ul_set()[ipATOM_H] = 1.4f * coll_temp.rate_coef_ul()[ipH2_PARA];
207  }
208  else if( AtmolCollRateCoeff[ipSpecies][ipH2].temps.size() != 0 ) // total H2
209  {
210  coll_temp.rate_coef_ul_set()[ipATOM_H] = 1.4f * coll_temp.rate_coef_ul()[ipH2];
211  }
212  else
213  coll_temp.rate_coef_ul_set()[ipATOM_H] = 1e-13f * (*(*tr).Lo()).g();
214  }
215 
216  /* Put in something for proton collisions if not in database */
217  if( AtmolCollRateCoeff[ipSpecies][ipPROTON].temps.size() == 0 )
218  {
219  if( AtmolCollRateCoeff[ipSpecies][ipHE_PLUS].temps.size() != 0 ) //He+
220  {
221  coll_temp.rate_coef_ul_set()[ipPROTON] = 2.0f * coll_temp.rate_coef_ul()[ipHE_PLUS];
222  }
223  else
224  coll_temp.rate_coef_ul_set()[ipPROTON] = 1e-13f * (*(*tr).Lo()).g();
225 
226  }
227 
228 #if 0
229  /* if nothing else has been done, just put a small rate coefficient in */
230  for( long intCollNo=0; intCollNo<ipNCOLLIDER; intCollNo++)
231  {
232  if( coll_temp.rate_coef_ul()[intCollNo] == 0. )
233  coll_temp.rate_coef_ul_set()[intCollNo] = 1e-13;
234  }
235 #endif
236  }
237  else
238  {
239  /* test for transitions without collision data */
240  if( (*tr).Coll().rate_coef_ul_set()[ipELECTRON] == 0. )
241  {
242  if( atmdat.lgGbarOn && (*tr).Emis().gf() != 0.)
243  {
244  /* All transitions without collision data should use gbar if enabled */
245  MakeCS(*tr);
246  }
247  else
248  {
249  //If gbar is off or no Aul, use the default collision strength value.
250  coll_temp.col_str() = atmdat.collstrDefault;
251  }
252  (*tr).Coll().is_gbar() = 1;
253 
254  coll_temp.rate_coef_ul_set()[ipELECTRON] = (COLL_CONST*coll_temp.col_str())/
255  ((*tr->Hi()).g()*phycon.sqrte);
256  }
257 
258  //For some species col_str was showing up as zero in save line data when there was a non-zero coll rate
259  if( tr->Coll().col_str() == -FLT_MAX && tr->Coll().rate_coef_ul()[ipELECTRON] > 0.0 )
260  {
261  tr->Coll().col_str() = (realnum)( tr->Coll().rate_coef_ul()[ipELECTRON] *
262  ((*tr->Hi()).g()*phycon.sqrte)/COLL_CONST);
263  }
264  }
265  }
266  }
267 }
268 
269 /*Solving for the level populations*/
270 
271 void dBase_solve(void )
272 {
273  DEBUG_ENTRY( "dBase_solve()" );
274 
275  if( nSpecies==0 )
276  return;
277 
278  static bool lgFirstPass = true;
279  static long maxNumLevels = 1;
280  static double *g, *ex, *pops, *depart, *source, *sink;
281  static double **AulEscp, **AulDest, **AulPump, **CollRate;
282 
283  if( lgFirstPass )
284  {
285  for( long ipSpecies=0; ipSpecies<nSpecies; ipSpecies++ )
286  maxNumLevels = MAX2( maxNumLevels, dBaseSpecies[ipSpecies].numLevels_max );
287 
288  g = (double *)MALLOC( (unsigned long)(maxNumLevels)*sizeof(double));
289  ex = (double *)MALLOC( (unsigned long)(maxNumLevels)*sizeof(double));
290  pops = (double *)MALLOC( (unsigned long)(maxNumLevels)*sizeof(double));
291  depart = (double *)MALLOC( (unsigned long)(maxNumLevels)*sizeof(double));
292  source = (double *)MALLOC( (unsigned long)(maxNumLevels)*sizeof(double));
293  sink = (double *)MALLOC( (unsigned long)(maxNumLevels)*sizeof(double));
294 
295  AulEscp = (double **)MALLOC( (unsigned long)(maxNumLevels)*sizeof(double *));
296  AulDest = (double **)MALLOC( (unsigned long)(maxNumLevels)*sizeof(double *));
297  AulPump = (double **)MALLOC( (unsigned long)(maxNumLevels)*sizeof(double *));
298  CollRate = (double **)MALLOC( (unsigned long)(maxNumLevels)*sizeof(double *));
299 
300  AulEscp[0] = (double *)MALLOC( (unsigned long)(maxNumLevels*maxNumLevels)*sizeof(double));
301  AulDest[0] = (double *)MALLOC( (unsigned long)(maxNumLevels*maxNumLevels)*sizeof(double));
302  AulPump[0] = (double *)MALLOC( (unsigned long)(maxNumLevels*maxNumLevels)*sizeof(double));
303  CollRate[0] = (double *)MALLOC( (unsigned long)(maxNumLevels*maxNumLevels)*sizeof(double));
304  for( long j=1; j< maxNumLevels; j++ )
305  {
306  AulEscp[j]=AulEscp[j-1]+maxNumLevels;
307  AulDest[j]=AulDest[j-1]+maxNumLevels;
308  AulPump[j]=AulPump[j-1]+maxNumLevels;
309  CollRate[j]=CollRate[j-1]+maxNumLevels;
310  }
311 
312  lgFirstPass = false;
313  }
314 
315  // zero all of these values
316  memset( g, 0, (unsigned long)(maxNumLevels)*sizeof(double) );
317  memset( ex, 0, (unsigned long)(maxNumLevels)*sizeof(double) );
318  memset( pops, 0, (unsigned long)(maxNumLevels)*sizeof(double) );
319  memset( depart, 0, (unsigned long)(maxNumLevels)*sizeof(double) );
320  memset( source, 0, (unsigned long)(maxNumLevels)*sizeof(double) );
321  memset( sink, 0, (unsigned long)(maxNumLevels)*sizeof(double) );
322  for( long j=0; j< maxNumLevels; j++ )
323  {
324  memset( AulEscp[j], 0, (unsigned long)(maxNumLevels)*sizeof(double) );
325  memset( AulDest[j], 0, (unsigned long)(maxNumLevels)*sizeof(double) );
326  memset( AulPump[j], 0, (unsigned long)(maxNumLevels)*sizeof(double) );
327  memset( CollRate[j], 0, (unsigned long)(maxNumLevels)*sizeof(double) );
328  }
329 
330  avx_ptr<double> arg(1,maxNumLevels), bstep(1,maxNumLevels);
331 
332  double totalHeating = 0.;
333  for( long ipSpecies=0; ipSpecies<nSpecies; ipSpecies++ )
334  {
335  dBaseSpecies[ipSpecies].CoolTotal = 0.;
336 
337  if( !dBaseSpecies[ipSpecies].lgActive )
338  continue;
339 
340 #if 0
341  //limit for now to small number of levels
342  dBaseSpecies[ipSpecies].numLevels_local = MIN2( dBaseSpecies[ipSpecies].numLevels_local, 10 );
343 #endif
344 
345  // we always hit search phase first, reset number of levels
346  if( conv.lgSearch )
347  dBaseSpecies[ipSpecies].numLevels_local = dBaseSpecies[ipSpecies].numLevels_max;
348 
349  realnum abund = dBaseAbund(ipSpecies);
350 
351  const char *spName = dBaseSpecies[ipSpecies].chLabel;
352  for( long ipLo = 0; ipLo < dBaseSpecies[ipSpecies].numLevels_local; ipLo++ )
353  {
354  /* statistical weights & Excitation Energies*/
355  g[ipLo] = dBaseStates[ipSpecies][ipLo].g() ;
356  // parts of the code assert that ground is at zero energy - this is
357  // not true for the stored molecular data - so rescale to zero
358  ex[ipLo] = dBaseStates[ipSpecies][ipLo].energy().WN() -
359  dBaseStates[ipSpecies][0].energy().WN();
360  /* zero some rates */
361  source[ipLo] = 0.;
362  sink[ipLo] = 0.;
363  }
364 
365  // non-zero was due to roundoff errors on 32-bit
366  if( ex[0] <= dBaseStates[ipSpecies][0].energy().WN()* 10. *DBL_EPSILON )
367  ex[0] = 0.;
368  else
369  TotalInsanity();
370 
371  for( long ipHi= 0; ipHi<dBaseSpecies[ipSpecies].numLevels_local; ipHi++)
372  {
373  for( long ipLo= 0; ipLo<dBaseSpecies[ipSpecies].numLevels_local; ipLo++)
374  {
375  AulEscp[ipHi][ipLo] = 0.;
376  }
377  }
378  for( long ipHi= 0; ipHi<dBaseSpecies[ipSpecies].numLevels_local; ipHi++)
379  {
380  for( long ipLo= 0; ipLo<dBaseSpecies[ipSpecies].numLevels_local; ipLo++)
381  {
382  AulDest[ipHi][ipLo] = 0.;
383  }
384  }
385  for( long ipLo= 0; ipLo<dBaseSpecies[ipSpecies].numLevels_local; ipLo++)
386  {
387  for( long ipHi= 0; ipHi<dBaseSpecies[ipSpecies].numLevels_local; ipHi++)
388  {
389  AulPump[ipLo][ipHi] = 0.;
390  }
391  }
392 
393  const bool isO1 = ( strcmp( spName,"O 1" ) == 0 ),
394  isO3 = (strcmp(spName, "O 3") == 0),
395  isCa2 = (strcmp(spName, "Ca 2") == 0),
396  isN1 = (strcmp(spName, "N 1") == 0),
397  isMg2 = (strcmp(spName, "Mg 2") == 0);
398 
399  for(TransitionList::iterator tr = dBaseTrans[ipSpecies].begin();
400  tr != dBaseTrans[ipSpecies].end(); ++tr)
401  {
402  int ipHi = (*tr).ipHi();
403  if (ipHi >= dBaseSpecies[ipSpecies].numLevels_local || (*tr).ipCont() <= 0)
404  continue;
405  int ipLo = (*tr).ipLo();
406  AulEscp[ipHi][ipLo] = (*tr).Emis().Aul()*(*tr).Emis().Pesc_total();
407  AulDest[ipHi][ipLo] = (*tr).Emis().Aul()*(*tr).Emis().Pdest();
408  AulPump[ipLo][ipHi] = (*tr).Emis().pump();
409  AulPump[ipHi][ipLo] = AulPump[ipLo][ipHi]*g[ipLo]/g[ipHi];
410 
411  //Zero out xtra Ex and Dx rates before filling
412  double xtraExRate = 0.;
413  double xtraDxRate = 0.;
414 
415  //Set the extra Ex and Dx rates and add to AulPump
416  if( isO1 )
417  {
418  static const double lo1_nrg = 0.;
419  static const double lo2_nrg = 158.265;
420  static const double lo3_nrg = 226.977;
421 
422  static const double hi1_nrg = 97488.378;
423  static const double hi2_nrg = 97488.448;
424  static const double hi3_nrg = 97488.538;
425 
426  if( ( fp_equal( tr->Lo()->energy().WN(),lo1_nrg ) ||
427  fp_equal( tr->Lo()->energy().WN(),lo2_nrg ) ||
428  fp_equal( tr->Lo()->energy().WN(),lo3_nrg ) ) &&
429  ( fp_equal( tr->Hi()->energy().WN(),hi1_nrg ) ||
430  fp_equal( tr->Hi()->energy().WN(),hi2_nrg ) ||
431  fp_equal( tr->Hi()->energy().WN(),hi3_nrg ) ) )
432  {
433  setXtraRatesO1(*tr, xtraExRate, xtraDxRate);
434  }
435 
436  // add deexcitation rate from level 3 to all 3 below it
437  if( tr->ipHi() == 3 )
438  xtraDxRate += oxy.d6300/3;
439  }
440  else if( isO3 )
441  {
442  // add deexcitation rate from level 3 to all 3 below it
443  if( tr->ipHi() == 3 )
444  xtraDxRate += oxy.d5007r/3;
445  }
446  else if( isCa2 )
447  {
448  //Deexcitation of levels 2 through 5 to the ground state
449  if( tr->ipLo() == 0 && tr->ipHi() < 5 )
450  setXtraRatesCa2(*tr, xtraDxRate);
451  }
452  else if( isN1 )
453  {
454  //Deexcitation of levels 2 and 3 of N 1 to the ground state
455  if( tr->ipLo() == 0 && (tr->ipHi() == 1 || tr->ipHi() == 2) )
456  xtraDxRate += atoms.d5200r;
457  }
458  else if( isMg2 )
459  {
460  //Deexcitation of levels 4 and 5 of Mg 2
461  if( tr->ipLo() == 0 && (tr->ipHi() == 4 || tr->ipHi() == 5) )
462  xtraDxRate += atoms.rateMg2;
463  }
464  else if( strcmp(spName, "Fe 2") == 0 )
465  {
466  setXtraRatesFe2(*tr, xtraExRate, xtraDxRate);
467  }
468 
469  AulPump[ipLo][ipHi] += xtraExRate;
470  AulPump[ipHi][ipLo] += xtraDxRate;
471  }
472 
473  /*Set all the heating and cooling to zero*/
474  for(TransitionList::iterator tr = dBaseTrans[ipSpecies].begin();
475  tr != dBaseTrans[ipSpecies].end(); ++tr)
476  {
477  int ipHi = (*tr).ipHi();
478  if (ipHi >= dBaseSpecies[ipSpecies].numLevels_local)
479  continue;
480  CollisionZero( (*tr).Coll() );
481  }
482 
483  /*Updating the CollRate*/
484 
485  /*Set all the collision rates to zero*/
486  for( long ipHi= 0; ipHi<dBaseSpecies[ipSpecies].numLevels_local; ipHi++)
487  {
488  for( long ipLo= 0; ipLo<dBaseSpecies[ipSpecies].numLevels_local; ipLo++)
489  {
490  CollRate[ipHi][ipLo] = 0.;
491  }
492  }
493 
495  for(TransitionList::iterator tr = dBaseTrans[ipSpecies].begin();
496  tr != dBaseTrans[ipSpecies].end(); ++tr)
497  {
498  int ipHi = (*tr).ipHi();
499  if (ipHi >= dBaseSpecies[ipSpecies].numLevels_local)
500  continue;
501  int ipLo = (*tr).ipLo();
502  CollRate[ipHi][ipLo] = (*tr).Coll().ColUL( colld );
503  }
504 
505  for(int ipHi=1; ipHi<dBaseSpecies[ipSpecies].numLevels_local; ++ipHi)
506  arg[ipHi] = -(ex[ipHi]-ex[ipHi-1])*T1CM / phycon.te;
507  vexp( arg.ptr0(), bstep.ptr0(), 1, dBaseSpecies[ipSpecies].numLevels_local );
508  for (int ipLo=0; ipLo<dBaseSpecies[ipSpecies].numLevels_local-1; ++ipLo)
509  {
510  double boltz_over_glo=1./ g[ipLo];
511  for (int ipHi=ipLo+1; ipHi<dBaseSpecies[ipSpecies].numLevels_local; ++ipHi)
512  {
513  boltz_over_glo *= bstep[ipHi];
514  CollRate[ipLo][ipHi] = CollRate[ipHi][ipLo] * g[ipHi] * boltz_over_glo;
515  }
516  }
517 
518  /* now add in excitations resulting from cosmic ray secondaries */
519  // \todo 2 add branch to do forbidden transitions
520  // this g-bar only works for permitted lines
521 
522  /* get secondaries for all permitted lines by scaling LyA
523  * excitation by ratio of cross section (oscillator strength/energy)
524  * Born approximation or plane-wave approximation */
525  double sfac = secondaries.x12tot *
528 
529  for(TransitionList::iterator tr = dBaseTrans[ipSpecies].begin();
530  tr != dBaseTrans[ipSpecies].end(); ++tr)
531  {
532  if( (*tr).ipCont() > 0 )
533  {
534  int ipHi = (*tr).ipHi();
535  if (ipHi >= dBaseSpecies[ipSpecies].numLevels_local)
536  continue;
537  int ipLo = (*tr).ipLo();
538  (*tr).Coll().rate_lu_nontherm_set() = sfac *
539  ((*tr).Emis().gf()/(*tr).EnergyWN());
540 
541  CollRate[ipLo][ipHi] += (*tr).Coll().rate_lu_nontherm();
542  CollRate[ipHi][ipLo] += (*tr).Coll().rate_lu_nontherm() * g[ipLo] / g[ipHi];
543  }
544  }
545 
546  {
547  // debug dump one line
548  enum {DEBUG_LOC=false};
549  if( DEBUG_LOC && (nzone>110) )
550  {
551 
552  //static bool runOnce = false;
553  if( atmdat.ipSpecIon[ipIRON][1] == ipSpecies)
554  {
555  //runOnce = false;
556  for(TransitionList::iterator tr = dBaseTrans[ipSpecies].begin();
557  tr != dBaseTrans[ipSpecies].end(); ++tr)
558  {
559  int ipLo = tr->ipLo();
560  int ipHi = tr->ipHi();
561  if( ipLo == 39 && ipHi == 139 )
562  {
563  fprintf(ioQQQ,"DUMPING LINE:%i:%i\n",ipLo+1,ipHi+1);
564  DumpLine(*tr);
565  }
566  if( ipLo == 36 && ipHi == 133 )
567  {
568  fprintf(ioQQQ,"DUMPING LINE:%i:%i\n",ipLo+1,ipHi+1);
569  DumpLine(*tr);
570  }
571  }
572  }
573  }
574  }
575 
576  multi_arr<double,2> Cool(dBaseSpecies[ipSpecies].numLevels_local, dBaseSpecies[ipSpecies].numLevels_local);
577  double grnd_excit = 0.0;
578  double cooltl, coolder;
579  int nNegPop;
580  bool lgZeroPop, lgDeBug = false;
581 
582  /* solve the n-level atom */
583  static Atom_LevelN atom_levelN;
584  /* dBaseSpecies[ipSpecies].numLevels_local is the number of levels to compute*/
585 
586  atom_levelN(
587  dBaseSpecies[ipSpecies].numLevels_local,
588  /* ABUND is total abundance of species, used for nth equation
589  * if balance equations are homogeneous */
590  abund,
591  /* g(dBaseSpecies[ipSpecies].numLevels_local) is statistical weight of levels */
592  g,
593  /* EX(dBaseSpecies[ipSpecies].numLevels_local) is excitation potential of levels, deg K or wavenumbers
594  * 0 for lowest level, all are energy rel to ground NOT d(ENER) */
595  ex,
596  /* this is 'K' for ex[] as Kelvin deg, is 'w' for wavenumbers */
597  'w',
598  /* populations [cm-3] of each level as deduced here */
599  pops,
600  /* departure coefficient, derived below */
601  depart,
602  /* net transition rate, A * esc prob, s-1 */
603  AulEscp,
604  /* AulDest[ihi][ilo] is destruction rate, trans from ihi to ilo, A * dest prob,
605  * asserts confirm that [ihi][ilo] is zero */
606  AulDest,
607  /* AulPump[ilo][ihi] is pumping rate from lower to upper level (s^-1), (hi,lo) must be zero */
608  AulPump,
609  /* collision rates (s^-1), evaluated here and returned for cooling by calling function,
610  * unless following flag is true. If true then calling function has already filled
611  * in these rates. CollRate[ipSpecies][j] is rate from ipSpecies to j */
612  CollRate,
613  /* this is an additional creation rate from continuum, normally zero, units cm-3 s-1 */
614  source,
615  /* this is an additional destruction rate to continuum, normally zero, units s-1 */
616  sink,
617  /* total cooling and its derivative, set here but nothing done with it*/
618  &cooltl,
619  &coolder,
620  /* string used to identify calling program in case of error */
621  spName,
622  dBaseSpecies[ipSpecies].lgPrtMatrix,
623  /* nNegPop flag indicating what we have done
624  * positive if negative populations occurred
625  * zero if normal calculation done
626  * negative if too cold (for some atoms other routine will be called in this case) */
627  &nNegPop,
628  /* true if populations are zero, either due to zero abundance of very low temperature */
629  &lgZeroPop,
630  /* option to print debug information */
631  lgDeBug,
632  /* option to do the molecule in LTE */
633  dBaseSpecies[ipSpecies].lgLTE,
634  /* cooling per line */
635  &Cool,
636  NULL,
637  /* excitation rate out of the ground state */
638  &grnd_excit);
639 
640 
641  if ( ! dBaseSpecies[ipSpecies].lgMolecular )
643  [dBaseStates[ipSpecies][0].nelem()-1]
644  [(*(*dBaseTrans[ipSpecies].begin()).Lo()).IonStg()-1]
645  += grnd_excit;
646 
647 
648  if( nNegPop > 0 )
649  {
650  /* negative populations occurred */
651  if( conv.lgSearch )
652  fprintf(ioQQQ," dBase_solve fixup: negative pops set to zero during search phase, continuing.\n");
653  else
654  {
655  fprintf(ioQQQ," PROBLEM in dBase_solve, atom_levelN returned negative population .\n");
656  cdEXIT( EXIT_FAILURE );
657  }
658  }
659 
660  // highest levels may have no population
661  while( (pops[dBaseSpecies[ipSpecies].numLevels_local-1]<=0 ) &&
662  (dBaseSpecies[ipSpecies].numLevels_local > 1) )
663  --dBaseSpecies[ipSpecies].numLevels_local;
664 
665  for( long j=0;j< dBaseSpecies[ipSpecies].numLevels_local; j++ )
666  {
667  dBaseStates[ipSpecies][j].Pop() = pops[j];
668  dBaseStates[ipSpecies][j].DepartCoef() = depart[j];
669  dBaseStates[ipSpecies][j].status() = LEVEL_ACTIVE;
670  }
671  for( long j=dBaseSpecies[ipSpecies].numLevels_local;
672  j< dBaseSpecies[ipSpecies].numLevels_max; j++ )
673  {
674  dBaseStates[ipSpecies][j].Pop() = 0.;
675  dBaseStates[ipSpecies][j].DepartCoef() = 0.;
676  dBaseStates[ipSpecies][j].status() = LEVEL_INACTIVE;
677  }
678 
679  /*Atmol line*/
680  for(TransitionList::iterator tr = dBaseTrans[ipSpecies].begin();
681  tr != dBaseTrans[ipSpecies].end(); ++tr)
682  {
683  int ipHi = (*tr).ipHi();
684  if (ipHi >= dBaseSpecies[ipSpecies].numLevels_local)
685  continue;
686  int ipLo = (*tr).ipLo();
687  (*tr).Coll().cool() = max(Cool[ipHi][ipLo],0.);
688  (*tr).Coll().heat() = max(-Cool[ipHi][ipLo],0.);
689 
690  if ( (*tr).ipCont() > 0 )
691  {
692  /* population of lower level rel to ion, corrected for stim em */
693  (*tr).Emis().PopOpc() = (*(*tr).Lo()).Pop() - (*(*tr).Hi()).Pop()*
694  (*(*tr).Lo()).g()/(*(*tr).Hi()).g();
695 
696  set_xIntensity(*tr);
697 
698  /* it's possible for this sum to be zero. Set ratio to zero in that case. */
699  if( CollRate[ipLo][ipHi]+AulPump[ipLo][ipHi] > 0. )
700  {
701  (*tr).Emis().ColOvTot() = CollRate[ipLo][ipHi]/
702  (CollRate[ipLo][ipHi]+AulPump[ipLo][ipHi]);
703  }
704  else
705  (*tr).Emis().ColOvTot() = 0.;
706 
707  // this is only used for save line printout. Maybe colliders may be involved, but
708  // this simple approximation of a "collision strength" should be good enough for
709  // the purposes of that printout.
710  (*tr).Coll().col_str() = (realnum)( (*tr).Coll().rate_coef_ul()[ipELECTRON] *
711  (g[ipHi]*phycon.sqrte)/COLL_CONST);
712  }
713  }
714 
715  //LyA destruction by Fe II
716  if( ipSpecies == atmdat.ipSpecIon[ipIRON][1])
717  {
718  /* the hydrogen Lya destruction rate, then probability */
719  hydro.dstfe2lya = 0.;
720  /* count how many photons were removed-added */
721  for( TransitionList::iterator tr = dBaseTrans[ipSpecies].begin(); tr != dBaseTrans[ipSpecies].end();++tr)
722  {
723  double exRate = 0.;
724  double dxRate = 0.;
725  setXtraRatesFe2(*tr,exRate,dxRate);
726  hydro.dstfe2lya += (realnum)(
727  tr->Lo()->Pop()*exRate -
728  tr->Hi()->Pop()*dxRate);
729  }
730 
731  /* the destruction prob comes from
732  * dest rate = n(2p) * A(21) * PDest */
733  double pop = iso_sp[ipH_LIKE][ipHYDROGEN].st[ipH2p].Pop();
734  if( pop > SMALLFLOAT )
735  {
736  hydro.dstfe2lya /= (realnum)(pop * iso_sp[ipH_LIKE][ipHYDROGEN].trans(ipH2p,ipH1s).Emis().Aul());
737  }
738  else
739  {
740  hydro.dstfe2lya = 0.;
741  }
742  /* NB - do not update hydro.dstfe2lya here if large FeII not on since
743  * done in FeII overlap */
744  }
745 
746  for(TransitionList::iterator tr = dBaseTrans[ipSpecies].begin();
747  tr != dBaseTrans[ipSpecies].end(); ++tr)
748  {
749  int ipHi = (*tr).ipHi();
750  if (ipHi < dBaseSpecies[ipSpecies].numLevels_local)
751  continue;
752  EmLineZero( (*tr).Emis() );
753  }
754 
755  dBaseSpecies[ipSpecies].CoolTotal = cooltl;
756  CoolAdd( dBaseSpecies[ipSpecies].chLabel, 0., max(0.,dBaseSpecies[ipSpecies].CoolTotal) );
757  if( dBaseSpecies[ipSpecies].lgMolecular )
758  thermal.elementcool[LIMELM] += max(0.,dBaseSpecies[ipSpecies].CoolTotal);
759  else
760  thermal.elementcool[dBaseStates[ipSpecies][0].nelem()-1] += max(0.,dBaseSpecies[ipSpecies].CoolTotal);
761  totalHeating += max(0., -dBaseSpecies[ipSpecies].CoolTotal);
762  thermal.dCooldT += coolder;
763 
764  /* option to print departure coefficients */
765  {
766  enum {DEBUG_LOC=false};
767 
768  if( DEBUG_LOC )
769  {
770  fprintf( ioQQQ, " Departure coefficients for species %li\n", ipSpecies );
771  for( long j=0; j< dBaseSpecies[ipSpecies].numLevels_local; j++ )
772  {
773  fprintf( ioQQQ, " level %li \t Depar Coef %e\n", j, depart[j] );
774  }
775  }
776  }
777  }
778 
779  // total heating for all dBase dBaseSpecies
780  thermal.setHeating(0,27,totalHeating);
781 
782  return;
783 }
784 
785 /*Leiden*/
786 STATIC double LeidenCollRate(long ipSpecies, long ipCollider, const TransitionProxy& tr, double ftemp)
787 {
788  DEBUG_ENTRY( "LeidenCollRate()" );
789  double ret_collrate = InterpCollRate( AtmolCollRateCoeff[ipSpecies][ipCollider], tr.ipHi(), tr.ipLo(), ftemp);
790  return ret_collrate;
791 }
792 
793 /*STOUT*/
794 STATIC double StoutCollRate(long ipSpecies, long ipCollider, const TransitionProxy& tr, double ftemp)
795 {
796  DEBUG_ENTRY( "StoutCollRate()" );
797 
798  double rate = 0.;
799  int n = StoutCollData[ipSpecies].ntemps(tr.ipHi(),tr.ipLo(),ipCollider);
800  if( n < 2)
801  return 0.;
802 
803  const double *x = StoutCollData[ipSpecies].temps(tr.ipHi(),tr.ipLo(),ipCollider);
804  const double *y = StoutCollData[ipSpecies].collstrs(tr.ipHi(),tr.ipLo(),ipCollider);
805  for(int j = 0; j < n; j ++)
806  {
807  ASSERT( x[j] > 0. && y[j] > 0.);
808  }
809 
810  //If the temperature is above or below the temperature range, use the CS from the closest temperature.
811  //Otherwise, do the linear interpolation.
812  double fupsilon = 0.;
813  if( ftemp < x[0] )
814  {
815  fupsilon = y[0];
816  }
817  else if( ftemp > x[n-1] )
818  {
819  fupsilon = y[n-1];
820  }
821  else
822  {
823  fupsilon = linint(&x[0],&y[0],n,ftemp);
824  }
825 
826  ASSERT(fupsilon > 0);
827 
828  // deexcitation rate coefficient or collision strength?
829  bool lgIsRate = StoutCollData[ipSpecies].lgIsRate(tr.ipHi(),tr.ipLo(),ipCollider);
830  /* We can deal with deexcitation rate coefficients and collision strengths currently */
831  if( lgIsRate )
832  {
833  rate = fupsilon;
834  tr.Coll().col_str() = (rate * (*tr.Hi()).g()*sqrt(ftemp))/COLL_CONST;
835  }
836  else
837  {
838  /* convert the collision strength to a collision rate coefficient */
839  /* This formula converting collision strength to collision rate coefficient works fine for the electrons*/
840  /* For any other collider the mass would be different*/
841  if( ipCollider == ipELECTRON )
842  {
843  rate = (COLL_CONST*fupsilon)/((*tr.Hi()).g()*sqrt(ftemp));
844  tr.Coll().col_str() = fupsilon;
845  }
846  else
847  {
848  fprintf(ioQQQ,"PROBLEM: Stout data format does not support using collision strengths with "
849  "non-electron colliders.\n");
851  }
852  }
853 
854  return rate;
855 }
856 
857 /*CHIANTI*/
858 STATIC double ChiantiCollRate(long ipSpecies, long ipCollider, const TransitionProxy& tr, double ftemp)
859 {
860  DEBUG_ENTRY( "ChiantiCollRate()" );
861 
862  double rate = 0.;
863  double fupsilon = CHIANTI_Upsilon(ipSpecies, ipCollider, tr.ipHi(), tr.ipLo(), ftemp);
864 
865  /* NB NB - if proton colliders, the upsilons returned here are actually already rate coefficients. */
866  /* these are designated by a collider index and a transition type */
867  if( ipCollider == ipPROTON )
868  {
869  rate = fupsilon;
870  }
871  else if( ipCollider == ipELECTRON )
872  {
873  /* convert the collision strength to a collision rate coefficient */
874  /*This formula converting collision strength to collision rate coefficient works fine for the electrons*/
875  /*For any other collider the mass would be different*/
876  rate = (COLL_CONST*fupsilon)/((*tr.Hi()).g()*sqrt(ftemp));
877  }
878  else
879  rate = 0.;
880 
881  return rate;
882 }
883 
884 double CHIANTI_Upsilon(long ipSpecies, long ipCollider, long ipHi, long ipLo, double ftemp)
885 {
886  double fdeltae,fscalingparam,fkte,fxt,fsups,fups;
887  int intxs,inttype,intsplinepts;
888 
889  DEBUG_ENTRY( "CHIANTI_Upsilon()" );
890 
891  if( AtmolCollSplines[ipSpecies][ipHi][ipLo][ipCollider].collspline == NULL )
892  {
893  return 0.;
894  }
895 
896  intsplinepts = AtmolCollSplines[ipSpecies][ipHi][ipLo][ipCollider].nSplinePts;
897  inttype = AtmolCollSplines[ipSpecies][ipHi][ipLo][ipCollider].intTranType;
898  fdeltae = AtmolCollSplines[ipSpecies][ipHi][ipLo][ipCollider].EnergyDiff;
899  fscalingparam = AtmolCollSplines[ipSpecies][ipHi][ipLo][ipCollider].ScalingParam;
900 
901  fkte = ftemp/fdeltae/1.57888e5;
902 
903  /*Way the temperature is scaled*/
904  /*Burgess&Tully 1992:Paper gives only types 1 to 4*/
905  /*Found that the expressions were the same for 5 & 6 from the associated routine DESCALE_ALL*/
906  /*What about 7,8&9?*/
907  if( inttype ==1 || inttype==4 )
908  {
909  fxt = 1-(log(fscalingparam)/(log(fkte+fscalingparam)));
910  }
911  else if(inttype == 2 || inttype == 3||inttype == 5 || inttype == 6)
912  {
913  fxt = fkte/(fkte+fscalingparam);
914  }
915  else
916  TotalInsanity();
917 
918  double xs[9],*spl;
919  /*Creating spline points array*/
920  spl = AtmolCollSplines[ipSpecies][ipHi][ipLo][ipCollider].collspline;
921  for(intxs=0;intxs<intsplinepts;intxs++)
922  {
923  double coeff = (double)1/(intsplinepts-1);
924  xs[intxs] = coeff*intxs;
925  if(DEBUGSTATE)
926  {
927  printf("The xs and spl values are %f and %f \n",xs[intxs],spl[intxs]);
928  getchar();
929  }
930  }
931 
932  const bool SPLINE_INTERP=false;
933  if (! SPLINE_INTERP)
934  {
935  fsups = linint( xs, spl, intsplinepts, fxt);
936  }
937  else
938  {
939  /*Finding the second derivative*/
940  double *spl2 =
941  AtmolCollSplines[ipSpecies][ipHi][ipLo][ipCollider].SplineSecDer;
942 
943  if(DEBUGSTATE)
944  {
945  printf("\n");
946  for(intxs=0;intxs<intsplinepts;intxs++)
947  {
948  printf("The %d value of 2nd derivative is %f \n",intxs+1,spl2[intxs]);
949  }
950  }
951 
952  /*Extracting out the value*/
953  splint(xs,spl,spl2,intsplinepts,fxt,&fsups);
954  }
955 
956  /*Finding upsilon*/
957  if(inttype == 1)
958  {
959  fups = fsups*log(fkte+EE);
960  }
961  else if(inttype == 2)
962  {
963  fups = fsups;
964  }
965  else if(inttype == 3)
966  {
967  fups = fsups/(fkte+1.0) ;
968  }
969  else if(inttype == 4)
970  {
971  fups = fsups*log(fkte+fscalingparam) ;
972  }
973  else if(inttype == 5)
974  {
975  fups = fsups/fkte ;
976  }
977  else if(inttype == 6)
978  {
979  fups = exp10(fsups) ;
980  }
981  else
982  {
983  TotalInsanity();
984  }
985 
986  if( fups < 0. )
987  {
988  fprintf( ioQQQ," WARNING: Negative upsilon in species %s, collider %li, indices %4li %4li, Te = %e.\n",
989  dBaseSpecies[ipSpecies].chLabel, ipCollider, ipHi, ipLo, ftemp );
990  fups = 0.;
991  }
992  ASSERT(fups>=0);
993  return(fups);
994 }
995 
996 STATIC void setXtraRatesO1(const TransitionProxy& tr, double &xtraExRate, double &xtraDxRate)
997 {
998  DEBUG_ENTRY("setXtraRatesO1()");
999 
1000  double esab,
1001  eslb,
1002  esoi,
1003  flb,
1004  foi,
1005  opaclb,
1006  opacoi,
1007  xlb,
1008  xoi;
1009  double aoi = tr.Emis().Aul();
1010  double alb = iso_sp[ipH_LIKE][ipHYDROGEN].trans(ipH3p,ipH1s).Emis().Aul();
1011 
1012  fixit("ticket #78 refers");
1013  // The code below should be calculating the O I 1025 pumping by H Ly beta, as well as
1014  // the inverse process (this can become important in hydrogen-deficient environments).
1015  // It now uses the Elitzur & Netzer (1985, ApJ, 291, 464) theory, which is no longer
1016  // valid since the line overlap code prevents us from getting at the escape probability
1017  // of individual lines.
1018 
1019  /* A's from Pradhan; OI pump line; Ly beta, 8446 */
1020 
1021  // line overlap code makes this the escape probability of the combined lines
1022  esab = tr.Emis().Pesc_total();
1023 
1024  // these two are no longer correct, the line overlap code makes it impossible
1025  // to get at the escape probabilities of the individual lines...
1026  esoi = tr.Emis().Pesc_total();
1028 
1029  /* all trace output turned on with "trace ly beta command' */
1030  if( trace.lgTr8446 && trace.lgTrace )
1031  {
1032  fprintf( ioQQQ,
1033  " P8446 finds Lbeta, OI widths=%10.2e%10.2e and esc prob=%10.2e%10.2e esAB=%10.2e\n",
1035  }
1036 
1037  /* find relative opacities for two lines */
1038  opacoi = 2.92e-9*dense.xIonDense[ipOXYGEN][0]*0.5556/GetDopplerWidth(dense.AtomicWeight[ipOXYGEN]);
1040 
1041  /* these are x sub a (OI) and x sub b (ly beta) defined in Elitz+Netz */
1042  xoi = opacoi/(opacoi + opaclb);
1043  xlb = opaclb/(opacoi + opaclb);
1044 
1045  /* find relative line-widths, assume same rest freq */
1048  MAX2(0.,1.- tr.Emis().Pesc_total());
1049 
1050  if( trace.lgTr8446 && trace.lgTrace )
1051  {
1052  fprintf( ioQQQ,
1053  " P8446 opac Lb, OI=%10.2e%10.2e X Lb, OI=%10.2e%10.2e FLb, OI=%10.2e%10.2e\n",
1054  opaclb, opacoi, xlb, xoi, flb, foi );
1055  }
1056 
1057  /* pumping of OI by L-beta - this goes into OI matrix as 1-5 rate
1058  * lgInducProcess set false with no induced command, usually true */
1059  if( rfield.lgInducProcess )
1060  {
1061  xtraExRate += (realnum)((flb*alb*iso_sp[ipH_LIKE][ipHYDROGEN].st[ipH3p].Pop()*
1062  xoi*(1. - esab)/dense.xIonDense[ipOXYGEN][0]));
1063  /* net decay rate from upper level */
1064  xtraDxRate += (realnum)(aoi*(1. - (1. - foi)*(1. - esoi) - xoi*(1. - esab)*foi));
1065  }
1066  else
1067  {
1068  xtraExRate += 0.;
1069  xtraDxRate += 0.;
1070  }
1071  return;
1072 }
1073 
1074 STATIC void setXtraRatesCa2(const TransitionProxy& tr, double &xtraDxRate )
1075 {
1076  DEBUG_ENTRY( "setXtraRatesCa2()" );
1077 
1078  double hlgam,
1079  PhotoRate2,
1080  PhotoRate3,
1081  PhotoRate4,
1082  PhotoRate5;
1083 
1084  /* photoionization of evcited levels by Ly-alpha */
1086  PhotoRate5 = 1.7e-18*hlgam;
1087  PhotoRate4 = 8.4e-19*hlgam;
1088  PhotoRate3 = 7.0e-18*hlgam;
1089  PhotoRate2 = 4.8e-18*hlgam;
1090 
1091  if( tr.ipHi() + 1 == 2)
1092  {
1093  xtraDxRate += PhotoRate2;
1094  }
1095  else if( tr.ipHi() + 1 == 3 )
1096  {
1097  xtraDxRate += PhotoRate3;
1098  }
1099  else if( tr.ipHi() + 1 == 4 )
1100  {
1101  xtraDxRate += PhotoRate4;
1102  }
1103  else if( tr.ipHi() + 1 == 5 )
1104  {
1105  xtraDxRate += PhotoRate5;
1106  }
1107  else
1108  {
1109  xtraDxRate += 0.;
1110  }
1111  return;
1112 }
1113 /*setXtraRatesFe2 find rate of Lya excitation of the FeII atom */
1114 STATIC void setXtraRatesFe2(const TransitionProxy& tr, double &xtraExRate, double &xtraDxRate)
1115 {
1116  DEBUG_ENTRY( "setXtraRatesFe2()" );
1117 
1118  if( ! hydro.lgLyaFeIIPumpOn )
1119  return;
1120 
1121  /* get rates FeII atom is pumped */
1122 
1123  /* elsewhere in this file the dest prob hydro.dstfe2lya is defined from
1124  * quantites derived here, and the resulting populations */
1125 
1126  /*************trapeze form La profile:de,EnerLyaProf1,EnerLyaProf2,EnerLyaProf3,EnerLyaProf4*************************
1127  * */
1128  /* width of Lya in cm^-1 */
1129  /* HLineWidth has units of cm/s, as was evaluated in PresTotCurrent */
1130  /* the factor is 1/2 of E(Lya, cm^-1_/c */
1131  double de = 1.37194e-06*hydro.HLineWidth*2.0/3.0;
1132  /* 82259 is energy of Lya in wavenumbers, so these are the form of the trapezoid */
1133  double EnerLyaProf1 = 82259.0 - de*2.0;
1134  double EnerLyaProf2 = 82259.0 - de;
1135  double EnerLyaProf3 = 82259.0 + de;
1136  double EnerLyaProf4 = 82259.0 + de*2.0;
1137 
1138  double PhotOccNumLyaCenter = 0.;
1139 
1140  /* find Lya photon occupation number */
1141  if( iso_sp[ipH_LIKE][ipHYDROGEN].st[ipH2p].Pop() > SMALLFLOAT )
1142  {
1143  /* This is the photon occupation number at the Lya line center */
1144  PhotOccNumLyaCenter =
1145  MAX2(0.,1.0-
1146  iso_sp[ipH_LIKE][ipHYDROGEN].trans(ipH2p,ipH1s).Emis().Pesc_total())/
1147  (iso_sp[ipH_LIKE][ipHYDROGEN].st[ipH1s].Pop()/iso_sp[ipH_LIKE][ipHYDROGEN].st[ipH2p].Pop()*3. - 1.0);
1148  }
1149 
1150 
1151  /* on first iteration optical depth in line is inward only, on later
1152  * iterations is total optical depth */
1153  double taux = 0.;
1154  if( iteration == 1 )
1155  {
1156  taux = tr.Emis().TauIn();
1157  }
1158  else
1159  {
1160  taux = tr.Emis().TauTot();
1161  }
1162 
1163  /* the energy of the FeII line */
1164  double EnergyWN = tr.EnergyWN();
1165 
1166  if( EnergyWN >= EnerLyaProf1 && EnergyWN <= EnerLyaProf4 && taux > 1 )
1167  {
1168  /* this branch, line is within the Lya profile */
1169 
1170  /*
1171  * Lya source function, at peak is PhotOccNumLyaCenter,
1172  *
1173  * Prof2 Prof3
1174  * ----------
1175  * / \
1176  * / \
1177  * / \
1178  * ======================
1179  * Prof1 Prof4
1180  *
1181  */
1182 
1183  double PhotOccNum_at_nu = 0.,
1184  PumpRate = 0.;
1185  if( EnergyWN < EnerLyaProf2 )
1186  {
1187  /* linear interpolation on edge of trapazoid */
1188  PhotOccNum_at_nu = PhotOccNumLyaCenter*(EnergyWN - EnerLyaProf1)/ de;
1189  }
1190  else if( EnergyWN < EnerLyaProf3 )
1191  {
1192  /* this is the central plateau */
1193  PhotOccNum_at_nu = PhotOccNumLyaCenter;
1194  }
1195  else
1196  {
1197  /* linear interpolation on edge of trapazoid */
1198  PhotOccNum_at_nu = PhotOccNumLyaCenter*(EnerLyaProf4 - EnergyWN)/de;
1199  }
1200 
1201  /* at this point Lya source function at FeII line energy is defined, but
1202  * we need to multiply by line width in Hz,
1203  * >>refer Fe2 pump Netzer, H., Elitzur, M., & Ferland, G. J. 1985, ApJ, 299, 752-762*/
1204 
1205 #if 0
1206 
1207  /* width of FeII line in Hz */
1208  double FeIILineWidthHz = 1.e8/(EnergyWN*299792.5)*sqrt(log(taux))*GetDopplerWidth(dense.AtomicWeight[ipIRON]);
1209 
1210  /* final Lya pumping rate, s^-1*/
1211  PumpRate = FeIILineWidthHz * PhotOccNum_at_nu * tr.Emis().Aul()*
1212  powi(82259.0f/EnergyWN,3);
1213 #endif
1214  /* above must be bogus, use just occ num times A */
1215  PumpRate = tr.Emis().Aul()* PhotOccNum_at_nu;
1216 
1217  /* Lya pumping rate from ipHi to lower n */
1218  xtraDxRate += PumpRate;
1219 
1220  /* Lya pumping rate from n to upper ipHi */
1221  xtraExRate += PumpRate * (*tr.Hi()).g()/(*tr.Lo()).g();
1222  }
1223 
1224  return;
1225 }
void CoolAdd(const char *chLabel, realnum lambda, double cool)
Definition: cool_etc.cpp:13
realnum x12tot
Definition: secondaries.h:65
void DumpLine(const TransitionProxy &t)
Definition: transition.cpp:138
vector< StoutCollArray > StoutCollData
Definition: taulines.cpp:21
t_atmdat atmdat
Definition: atmdat.cpp:6
t_thermal thermal
Definition: thermal.cpp:6
qList st
Definition: iso.h:482
T * ptr0()
Definition: vectorize.h:240
double exp10(double x)
Definition: cddefines.h:1368
NORETURN void TotalInsanity(void)
Definition: service.cpp:971
double depart(const genericState &gs)
const realnum SMALLFLOAT
Definition: cpu.h:246
realnum xNucleiTotal
Definition: dense.h:114
STATIC void setXtraRatesO1(const TransitionProxy &tr, double &xtraExRate, double &xtraDxRate)
Definition: species2.cpp:996
void set_xIntensity(const TransitionProxy &t)
realnum HLineWidth
Definition: hydrogenic.h:100
const int ipOXYGEN
Definition: cddefines.h:356
realnum & TauTot() const
Definition: emission.h:490
double CHIANTI_Upsilon(long, long, long, long, double)
Definition: species2.cpp:884
t_conv conv
Definition: conv.cpp:5
realnum d5200r
Definition: atoms.h:126
t_phycon phycon
Definition: phycon.cpp:6
FILE * ioQQQ
Definition: cddefines.cpp:7
molezone * findspecieslocal(const char buf[])
bool lgGbarOn
Definition: atmdat.h:421
long int nzone
Definition: cddefines.cpp:14
STATIC double LeidenCollRate(long, long, const TransitionProxy &, double)
Definition: species2.cpp:786
Definition: mole.h:378
bool lgLyaFeIIPumpOn
Definition: hydrogenic.h:93
multi_arr< CollRateCoeffArray, 2 > AtmolCollRateCoeff
Definition: taulines.cpp:19
#define MIN2(a, b)
Definition: cddefines.h:803
long int nSpecies
Definition: taulines.cpp:22
static const bool DEBUGSTATE
Definition: species2.cpp:31
void splint(const double xa[], const double ya[], const double y2a[], long int n, double x, double *y)
Definition: thirdparty.h:173
double * rate_coef_ul_set() const
Definition: collision.h:202
t_dense dense
Definition: global.cpp:15
double ** ExcitationGround
Definition: ionbal.h:124
t_iso_sp iso_sp[NISO][LIMELM]
Definition: iso.cpp:11
void vexp(const double x[], double y[], long nlo, long nhi)
double xIonDense[LIMELM][LIMELM+1]
Definition: dense.h:135
long int iteration
Definition: cddefines.cpp:16
realnum * otslin
Definition: rfield.h:183
bool lgSearch
Definition: conv.h:168
t_trace trace
Definition: trace.cpp:5
bool fp_equal(sys_float x, sys_float y, int n=3)
Definition: cddefines.h:854
#define MALLOC(exp)
Definition: cddefines.h:554
t_ionbal ionbal
Definition: ionbal.cpp:8
t_abund abund
Definition: abund.cpp:5
realnum rateMg2
Definition: atoms.h:129
const int ipIRON
Definition: cddefines.h:374
ColliderList colliders(dense)
realnum & gf() const
Definition: emission.h:570
realnum & EnergyWN() const
Definition: transition.h:477
int & ipHi() const
Definition: transition.h:505
double energy(const genericState &gs)
const int ipH1s
Definition: iso.h:29
#define STATIC
Definition: cddefines.h:118
bool lgTrace
Definition: trace.h:12
EmissionList::reference Emis() const
Definition: transition.h:447
STATIC void setXtraRatesFe2(const TransitionProxy &tr, double &xtraExRate, double &xtraDxRate)
Definition: species2.cpp:1114
STATIC void setXtraRatesCa2(const TransitionProxy &tr, double &xtraDxRate)
Definition: species2.cpp:1074
t_rfield rfield
Definition: rfield.cpp:9
void dBase_solve(void)
Definition: species2.cpp:271
double InterpCollRate(const CollRateCoeffArray &rate_table, const long &ipHi, const long &ipLo, const double &ftemp)
Definition: atmdat.cpp:187
double collstrDefault
Definition: atmdat.h:426
long & ipCont() const
Definition: transition.h:489
t_atoms atoms
Definition: atoms.cpp:5
float realnum
Definition: cddefines.h:124
#define EXIT_FAILURE
Definition: cddefines.h:168
long max(int a, long b)
Definition: cddefines.h:817
qList::iterator Hi() const
Definition: transition.h:435
t_hydro hydro
Definition: hydrogenic.cpp:5
#define cdEXIT(FAIL)
Definition: cddefines.h:482
double powi(double, long int)
Definition: service.cpp:594
string database() const
static realnum dBaseAbund(long ipSpecies)
Definition: species2.cpp:33
realnum GetDopplerWidth(realnum massAMU)
int & ipLo() const
Definition: transition.h:497
bool exists(const molecule *m)
Definition: mole.h:258
vector< multi_arr< CollSplinesArray, 3 > > AtmolCollSplines
Definition: taulines.cpp:20
long int nTotalIoniz
Definition: conv.h:159
TransitionProxy trans(const long ipHi, const long ipLo)
Definition: iso.h:473
void setHeating(long nelem, long ion, double heating)
Definition: thermal.h:190
const int ipH2p
Definition: iso.h:31
realnum AtomicWeight[LIMELM]
Definition: dense.h:80
#define ASSERT(exp)
Definition: cddefines.h:613
qList::iterator Lo() const
Definition: transition.h:431
void EmLineZero(EmissionList::reference t)
Definition: emission.cpp:95
realnum Pesc_total() const
Definition: emission.h:127
STATIC double StoutCollRate(long ipSpecies, long ipCollider, const TransitionProxy &, double ftemp)
Definition: species2.cpp:794
const int ipH_LIKE
Definition: iso.h:64
const int LIMELM
Definition: cddefines.h:308
double den
Definition: mole.h:421
realnum & col_str() const
Definition: collision.h:191
realnum dstfe2lya
Definition: hydrogenic.h:97
CollisionProxy Coll() const
Definition: transition.h:463
vector< vector< long > > ipSpecIon
Definition: atmdat.h:455
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:723
double linint(const double x[], const double y[], long n, double xval)
vector< qList > dBaseStates
Definition: taulines.cpp:16
realnum d6300
Definition: oxy.h:19
double elementcool[LIMELM+1]
Definition: thermal.h:111
const double * rate_coef_ul() const
Definition: collision.h:206
vector< species > dBaseSpecies
Definition: taulines.cpp:15
t_oxy oxy
Definition: oxy.cpp:5
#define MAX2(a, b)
Definition: cddefines.h:824
bool lgInducProcess
Definition: rfield.h:233
int fprintf(const Output &stream, const char *format,...)
Definition: service.cpp:1121
void dBaseTrim(void)
Definition: species2.cpp:61
realnum d5007r
Definition: oxy.h:19
double sqrte
Definition: phycon.h:58
STATIC double ChiantiCollRate(long ipSpecies, long ipCollider, const TransitionProxy &, double ftemp)
Definition: species2.cpp:858
#define fixit(a)
Definition: cddefines.h:417
t_secondaries secondaries
Definition: secondaries.cpp:5
void CollisionZero(const CollisionProxy &t)
Definition: collision.cpp:88
vector< TransitionList > dBaseTrans
Definition: taulines.cpp:18
double te
Definition: phycon.h:21
double dCooldT
Definition: thermal.h:139
const int ipHYDROGEN
Definition: cddefines.h:349
realnum & Aul() const
Definition: emission.h:690
const int ipH3p
Definition: iso.h:33
realnum & TauIn() const
Definition: emission.h:470
void dBaseUpdateCollCoeffs(void)
Definition: species2.cpp:102
bool lgTr8446
Definition: trace.h:70
Definition: collision.h:19
void MakeCS(const TransitionProxy &t)
Definition: transition.cpp:579