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