cloudy  trunk
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
atom_hyperfine.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 /* HyperfineCreat establish space for hf arrays, reads atomic data from hyperfine.dat */
4 /* HyperfineCS - returns collision strengths for hyperfine struc transitions */
5 /*H21cm computes rate for H 21 cm from upper to lower excitation by atomic hydrogen */
6 /*h21_t_ge_20 compute rate for H 21 cm from upper to lower excitation by atomic hydrogen */
7 /*h21_t_lt_20 compute rate for H 21 cm from upper to lower excitation by atomic hydrogen */
8 /*H21cm_electron compute H 21 cm rate from upper to lower excitation by electrons - call by CoolEvaluate */
9 /*H21cm_H_atom - evaluate H atom spin changing collision rate, called by CoolEvaluate */
10 /*H21cm_proton - evaluate proton spin changing H atom collision rate, */
11 #include "cddefines.h"
12 #include "abund.h"
13 #include "conv.h"
14 #include "phycon.h"
15 #include "dense.h"
16 #include "rfield.h"
17 #include "taulines.h"
18 #include "iso.h"
19 #include "trace.h"
20 #include "hyperfine.h"
21 #include "lines_service.h"
22 #include "service.h"
23 
24 /* H21_cm_pops - fine level populations for 21 cm with Lya pumping included
25  * called in CoolEvaluate */
26 void H21_cm_pops( void )
27 {
28  /*atom_level2( HFLines[0] );*/
29  /*return;*/
30  /*
31  things we know on entry to this routine:
32  total population of 2p: iso_sp[ipH_LIKE][ipHYDROGEN].st[ipH2p].Pop
33  total population of 1s: iso_sp[ipH_LIKE][ipHYDROGEN].st[ipH1s].Pop
34  continuum pumping rate (lo-up) inside 21 cm line: HFLines[0].pump()
35  upper to lower collision rate inside 21 cm line: HFLines[0].cs*dense.cdsqte
36  occupation number inside Lya: OccupationNumberLine( &iso_sp[ipH_LIKE][ipHYDROGEN].trans(ipH2p,ipH1s) )
37 
38  level populations (cm-3) must be computed:
39  population of upper level of 21cm: HFLines[0].Hi->Pop
40  population of lower level of 21cm: (*HFLines[0].Lo()).Pop
41  stimulated emission corrected population of lower level: HFLines[0].Emis->PopOpc()
42  */
43 
44  double PopTot = iso_sp[ipH_LIKE][ipHYDROGEN].st[ipH1s].Pop();
45 
46  /* population can be zero in certain tests where H is turned off,
47  * also if initial solver does not see any obvious source of ionization
48  * also possible to set H0 density to zero with element ionization command,
49  * as is done in func_set_ion test case */
50  if( PopTot <0 )
51  TotalInsanity();
52  else if( PopTot == 0 )
53  {
54  /*return after zeroing local variables */
55  (*HFLines[0].Hi()).Pop() = 0.;
56  (*HFLines[0].Lo()).Pop() = 0.;
57  HFLines[0].Emis().PopOpc() = 0.;
58  HFLines[0].Emis().xIntensity() = 0.;
59  HFLines[0].Emis().xObsIntensity() = 0.;
60  HFLines[0].Emis().ColOvTot() = 0.;
61  hyperfine.Tspin21cm = 0.;
62  return;
63  }
64 
65  double e1 = 0.;
66  double e2 = HFLines[ 0 ].EnergyWN();
67  /* The 2p fine stucture energies are current with NIST as of May 14, 2019. */
68  double e2p12 = 82258.9191133;
69  double e2p32 = 82259.2850014;
70  /* The hyperfine splittings of the 2p fine structure levels are from
71  * >>refer HI Bethe & Salpeter (1977) Section 22, page 110. */
72  double e2p12_splitting = e2 / 24.;
73  double e2p32_splitting = e2 / 60.;
74 
75  /* The hyperfine states have statistical weights 2F+1, so they differ from
76  * the unsplit level energy by:
77  * El = E - dE * gu / (gu+gl)
78  * Eu = E + dE * gl / (gu+gl)
79  * where E is the unsplit energy, dE the hyperfine splitting, and gu, gl the
80  * statistical weights of the hyperfine states.
81  * For 2p1/2: g(F=1) = 3, g(F=0) = 1
82  * For 2p3/2: g(F=2) = 5, g(F=1) = 3
83  * The levels of interest here are the 2p1/2(F=1) and 2p3/2(F=1), the top
84  * and bottom levels of the hyperfine states, resp.
85  * >>refer HI Deguchi & Watson 1985 ApJ, 290, 578
86  * refcon see their Fig. 1
87  */
88  double e3 = e2p12 + 0.25 * e2p12_splitting;
89  double e4 = e2p32 - 0.625 * e2p32_splitting;
90 
91  double de31 = e3 - e1;
92  double de32 = e3 - e2;
93  double de41 = e4 - e1;
94  double de42 = e4 - e2;
95 
96  if( false )
97  {
98  fprintf( ioQQQ, "-------\n" );
99  fprintf( ioQQQ, "de32 = %.9e\n", de32 );
100  fprintf( ioQQQ, "de31 = %.9e\n", de31 );
101  fprintf( ioQQQ, "de42 = %.9e\n", de42 );
102  fprintf( ioQQQ, "de41 = %.9e\n", de41 );
103  fprintf( ioQQQ, "-------\n" );
104  }
105 
106  double a31 = 2.08e8; /* Einstein co-efficient for transition 1p1/2 to 0s1/2 */
107  double a32 = 4.16e8; /* Einstein co-efficient for transition 1p1/2 to 1s1/2 */
108  double a41 = 4.16e8; /* Einstein co-efficient for transition 1p3/2 to 0s1/2 */
109  double a42 = 2.08e8; /* Einstein co-efficient for transition 1p3/2 to 1s1/2 */
110  /* These A values are determined from eqn. 17.64 of "The theory of Atomic structure
111  * and Spectra" by R. D. Cowan
112  * A hyperfine level has degeneracy Gf=(2F + 1)
113  * a2p1s = 6.24e8; Einstein co-efficient for transition 2p to 1s */
114  double a21 = HFLines[0].Emis().Aul(); /* Einstein co-efficient for transition 1s1/2 to 0s1/2 */
115 
116  /* above is spontaneous rate - the net rate is this times escape and destruction
117  * probabilities */
118  a21 *= HFLines[0].Emis().Ploss();
119  ASSERT( a21 >= 0. );
120 
121  /* hyperfine.lgLya_pump_21cm is option to turn off Lya pump
122  * of 21 cm, with no 21cm lya pump command - note that this
123  * may be negative if Lya mases */
124  double occnu_lya = OccupationNumberLine( iso_sp[ipH_LIKE][ipHYDROGEN].trans(ipH2p,ipH1s) ) *
126 
127  if( !conv.lgSearch && occnu_lya < 0. )
128  {
129  occnu_lya = 0.;
130  fixit( "PopOpc <0 but Pesc > 0: We may need to review when Pesc is computed to get non-negative occupation numbers" );
131  }
132 
133  /* Lya occupation number for the hyperfine levels 0S1/2 and 1S1/2 can be different
134  * this is related to the "Wouthuysen-Field coupling",
135  * https://en.wikipedia.org/wiki/Wouthuysen%E2%80%93Field_coupling
136  * which assumes that the variation of the Lya source function is the gas kinetic temperature.
137  * Following Adams 1971 we assume variation is line excitation temperature.
138  * Third possibility is that given in stellar atmosphere texts, S_nu = constant
139  */
140  double occnu_lya_23 = occnu_lya,
141  occnu_lya_13 = 0.,
142  occnu_lya_24 = 0.,
143  occnu_lya_14 = 0.;
144 
145  /* selected with SET LYA 21CM command */
148  {
149  double Temp = phycon.te;
150 
152  Temp = TexcLine( iso_sp[ipH_LIKE][ipHYDROGEN].trans(ipH2p,ipH1s) );
153 
154  if( occnu_lya * Temp > 0. )
155  {
156  /* If the continuum is described by a Planck function, then the continuum
157  * within Lya seen by the two levels is not exactly of the same brightness.
158  * They differ by the exp when Lya is on the Wien tail of black body, which
159  * must be true if 21 cm is important. */
160 
161  double texc1 = sexp( HFLines[0].EnergyK() / Temp );
162  double texc2 = sexp( ((e2p32-e2p12)*T1CM) / Temp );
163 
164  occnu_lya_23 = occnu_lya;
165  occnu_lya_13 = occnu_lya * texc1;
166  occnu_lya_24 = occnu_lya * texc2;
167  occnu_lya_14 = occnu_lya_13 * texc2;
168  }
169 
170  enum { DEBUG_SPEC = false };
171  if( DEBUG_SPEC )
172  {
173  fprintf(ioQQQ,"DEBUG texc %12.3e excitation %12.3e kinetic %12.3e\n",
174  Temp, TexcLine( iso_sp[ipH_LIKE][ipHYDROGEN].trans(ipH2p,ipH1s) ) , phycon.te );
175  }
176  }
178  {
179  occnu_lya_23 = occnu_lya;
180  occnu_lya_13 = powi(de32/de31, 3) * occnu_lya;
181  occnu_lya_24 = powi(de32/de42, 3) * occnu_lya;
182  occnu_lya_14 = powi(de32/de41, 3) * occnu_lya;
183  }
184  else
185  TotalInsanity();
186 
187  if( false )
188  {
189  fprintf( ioQQQ, "=======\n" );
190  fprintf( ioQQQ, "oc32 = %.9e\n", occnu_lya_23 );
191  fprintf( ioQQQ, "oc31 = %.9e\n", occnu_lya_13 );
192  fprintf( ioQQQ, "oc42 = %.9e\n", occnu_lya_24 );
193  fprintf( ioQQQ, "oc41 = %.9e\n", occnu_lya_14 );
194  fprintf( ioQQQ, "=======\n" );
195  }
196 
197  /* this is the 21 cm upward continuum pumping rate [s-1] for the attenuated incident and
198  * local continuum and including line optical depths */
199  double pump12 = HFLines[0].Emis().pump();
200  double pump21 = pump12 * (*HFLines[0].Lo()).g() / (*HFLines[0].Hi()).g();
201 
202  /* collision rates s-1 within 1s,
203  * were multiplied by collider density when evaluated in CoolEvaluate */
204  /* ContBoltz is Boltzmann factor for wavelength of line */
205  ASSERT( HFLines[0].Coll().col_str()>0. );
206  double coll12 = HFLines[0].Coll().col_str()*dense.cdsqte/(*HFLines[0].Lo()).g()*rfield.ContBoltz[HFLines[0].ipCont()-1];
207  double coll21 = HFLines[0].Coll().col_str()*dense.cdsqte/(*HFLines[0].Hi()).g();
208 
209  /* set up rate (s-1) equations
210  * all process out of 1 that eventually go to 2 */
211  double rate12 =
212  /* collision rate (s-1) from 1 to 2 */
213  coll12 +
214  /* direct external continuum pumping (s-1) in 21 cm line - usually dominated by CMB */
215  pump12 +
216  /* pump rate (s-1) up to 3, times fraction that decay to 2, hence net 1-2 */
217  3.*a31*occnu_lya_13 *a32/(a31+a32)+
218  /* pump rate (s-1) up to 4, times fraction that decay to 2, hence net 1-2 */
219  /* >>chng 05 apr 04, GS, degeneracy corrected from 6 to 3 */
220  3.*a41*occnu_lya_14 *a42/(a41+a42);
221 
222  /* set up rate (s-1) equations
223  * all process out of 2 that eventually go to 1 */
224  /* spontaneous + induced 2 -> 1 by external continuum inside 21 cm line */
225  /* >>chng 04 dec 03, do not include spontaneous decay, for numerical stability */
226  double rate21 =
227  /* collisional deexcitation */
228  coll21 +
229  /* net spontaneous decay plus external continuum pumping in 21 cm line */
230  pump21 +
231  /* rate from 2 to 3 time fraction that go back to 1, hence net 2 - 1 */
232  /* >>chng 05 apr 04,GS, degeneracy corrected from 2 to unity */
233  occnu_lya_23*a32 * a31/(a31+a32)+
234  occnu_lya_24*a42*a41/(a41+a42);
235 
236  /* x = (*HFLines[0].Hi()).Pop/(*HFLines[0].Lo()).Pop */
237  double x = rate12 / SDIV(a21 + rate21);
238  ASSERT( x > 0. );
239 
240  /* the Transitions term is the total population of 1s */
241  (*HFLines[0].Hi()).Pop() = (x/(1.+x))* PopTot;
242  (*HFLines[0].Lo()).Pop() = (1./(1.+x))* PopTot;
243 
244  /* the population with correction for stimulated emission */
245  HFLines[0].Emis().PopOpc() = (*HFLines[0].Lo()).Pop()*((3*rate21- rate12) + 3*a21)/SDIV(3*(a21+ rate21));
246 
247  /* ratio of collisional to total (collisional + pumped) excitation */
248  HFLines[0].Emis().ColOvTot() = 0.;
249  if( rate12 > 0. )
250  HFLines[0].Emis().ColOvTot() = coll12 / rate12;
251 
252  /* set number of escaping line photons, used elsewhere for outward beam
253  * and line intensity
254  * NB: continuum subtraction is performed within PutLine() */
256 
257 
258  /* finally save the spin temperature */
260  if( (*HFLines[0].Hi()).Pop() > SMALLFLOAT )
261  {
263  /* this line must be non-zero - it does strongly mase in limit_compton_hi_t sim -
264  * in that sim pop ratio goes to unity for a float and TexcLine ret zero */
265  if( hyperfine.Tspin21cm == 0. )
267  }
268 
269  return;
270 }
271 
272 /*H21cm_electron computes rate for H 21 cm from upper to lower excitation by electrons - call by CoolEvaluate
273  * >>refer H1 CS Smith, F. J. 1966, Planet. Space Sci., 14, 929 */
274 double H21cm_electron( double temp )
275 {
276  temp = MIN2(1e4 , temp );
277 
278  /* following fit is from */
279  /* >>refer H1 21cm Liszt, H. 2001, A&A, 371, 698 */
280  return exp10( -9.607 + log10( sqrt(temp)) * sexp( powpq(log10(temp),9,2) / 1800. ));
281 }
282 
283 /* computes rate for H 21 cm from upper to lower excitation by atomic hydrogen
284  * from
285  * >>refer H1 CS Allison, A. C., & Dalgarno A. 1969, ApJ 158, 423 */
286 /* the following is the best current survey of 21 cm excitation */
287 /* >>refer H1 21cm Liszt, H. 2001, A&A, 371, 698 */
288 #if 0
289 STATIC double h21_t_ge_20( double temp )
290 {
291  double y;
292  double x1,
293  teorginal = temp;
294  /* data go up to 1,000K must not go above this */
295  temp = MIN2( 1000.,temp );
296  x1 =1.0/sqrt(temp);
297  y =-21.70880995483007-13.76259674006133*x1;
298  y = exp(y);
299 
300  /* >>chng 02 feb 14, extrapolate above 1e3 K as per Liszt 2001 recommendation
301  * page 699 of */
302  /* >>refer H1 21cm Liszt, H. 2001, A&A, 371, 698 */
303  if( teorginal > 1e3 )
304  {
305  y *= pow(teorginal/1e3 , 0.33 );
306  }
307 
308  return( y );
309 }
310 
311 /* this branch for T < 20K, data go down to 1 K */
312 STATIC double h21_t_lt_20( double temp )
313 {
314  double y;
315  double x1;
316 
317  /* must not go below 1K */
318  temp = MAX2( 1., temp );
319  x1 =temp*log(temp);
320  y =9.720710314268267E-08+6.325515312006680E-08*x1;
321  return(y*y);
322 }
323 #endif
324 
325 /* >> chng 04 dec 15, GS. The fitted rate co-efficients (cm3s-1) in the temperature range 1K to 300K is from
326  * >>refer H1 CS Zygelman, B. 2005, ApJ, 622, 1356
327  * The rate is 4/3 times the Dalgarno (1969) rate for the
328  temperature range 300K to 1000K. Above 1000K, the rate is extrapolated according to Liszt 2001.*/
329 STATIC double h21_t_ge_10( double temp )
330 {
331  double teorginal = temp;
332 
333  /* data go up to 300K */
334  temp = MIN2( 300., temp );
335 
336  double y = 1.4341127e-9
337  + 9.4161077e-15 * temp
338  - 9.2998995e-9 / log(temp)
339  + 6.9539411e-9 / sqrt(temp)
340  + 1.7742293e-8 * log(temp)/pow2(temp);
341  if( teorginal > 300. )
342  {
343  /* data go up to 1000*/
344  temp = MIN2( 1000., teorginal );
345  y = -21.70880995483007 - 13.76259674006133 / sqrt(temp);
346  y = 1.236686*exp(y);
347  }
348  if( teorginal > 1e3 )
349  {
350  /*data go above 1000*/
351  y *= pow( teorginal/1e3 , 0.33 );
352  }
353  return( y );
354 }
355 /* this branch for T < 10K, data go down to 1 K */
356 STATIC double h21_t_lt_10( double temp )
357 {
358  /* must not go below 1K */
359  temp = MAX2(1., temp );
360  return 8.5622857e-10
361  + 2.331358e-11 * temp
362  + 9.5640586e-11 * pow2(log(temp))
363  - 4.6220869e-10 * sqrt(temp)
364  - 4.1719545e-10 / sqrt(temp);
365 }
366 
367 /*H21cm_H_atom - evaluate H atom spin changing H atom collision rate,
368  * called by CoolEvaluate
369  * >>refer H1 CS Allison, A. C. & Dalgarno, A. 1969, ApJ 158, 423
370  */
371 double H21cm_H_atom( double temp )
372 {
373  double hold;
374  if( temp >= 10. )
375  {
376  hold = h21_t_ge_10( temp );
377  }
378  else
379  {
380  hold = h21_t_lt_10( temp );
381  }
382 
383  return hold;
384 }
385 
386 /*H21cm_proton - evaluate proton spin changing H atom collision rate,
387 * called by CoolEvaluate */
388 double H21cm_proton( double temp )
389 {
390  /*>>refer 21cm p coll Furlanetto, S. R. & Furlanetto, M. R. 2007, MNRAS, 379, 130
391  * previously had used proton rate, which is 3.2 times H0 rate according to
392  *>>refer 21cm CS Liszt, H. 2001, A&A, 371, 698 */
393  /* fit to table 1 of first paper */
394  /*--------------------------------------------------------------*
395  TableCurve Function: c:\storage\litera~1\21cm\atomic~1\p21cm.c Jun 20, 2007 3:37:50 PM
396  proton coll deex
397  X= temperature (K)
398  Y= rate coefficient (1e-9 cm3 s-1)
399  Eqn# 4419 y=a+bx+cx^2+dx^(0.5)+elnx/x
400  r2=0.9999445384690351
401  r2adj=0.9999168077035526
402  StdErr=5.559328579039901E-12
403  Fstat=49581.16793656295
404  a= 9.588389834316704E-11
405  b= -5.158891920816405E-14
406  c= 5.895348443553458E-19
407  d= 2.05304960232429E-11
408  e= 9.122617940315725E-10
409  *--------------------------------------------------------------*/
410 
411  /* only fit this range, did not include T = 1K point which
412  * causes an inflection */
413  temp = MAX2( 2. , temp );
414  temp = MIN2( 2e4 , temp );
415 
416  /* within range of fitted rate coefficients */
417  return 9.588389834316704E-11
418  - 5.158891920816405E-14 * temp
419  + 5.895348443553458E-19 * temp * temp
420  + 2.053049602324290E-11 * sqrt(temp)
421  + 9.122617940315725E-10 * log(temp) / temp;
422 }
423 
424 /*
425  * HyperfineCreate, HyperfineCS written July 2001
426  * William Goddard for Gary Ferland
427  * This code calculates line intensities for known
428  * hyperfine transitions.
429  */
430 
431 /* two products, the transition structure HFLines, which contains all information for the lines,
432  * and nHFLines, the number of these lines.
433  *
434  * these are in taulines.h
435  *
436  * info to create them contained in hyperfine.dat
437  *
438  * abundances of nuclei are also in hyperfine.dat, stored in
439  */
440 
441 /* Ion contains varying temperatures, specified above, used for */
442 /* calculating collision strengths. */
443 STATIC int Ntemp = -1;
444 STATIC double *csTemp;
445 typedef struct
446 {
447  double *cs;
448  double *cs2d;
449 } t_ColStr;
450 
452 
453 const double ENERGY_MIN_WN = 1e-10;
454 
455 
456 /* HyperfineCreate establish space for HFS arrays, reads atomic data from hyperfine.dat */
457 void HyperfineCreate(void)
458 {
459  FILE *ioDATA;
460  char chLine[INPUT_LINE_LENGTH];
461  vector<string> data;
462 
463  DEBUG_ENTRY( "HyperfineCreate()" );
464 
465  /* list of ion collision strengths for the temperatures listed in table */
466  /* HFLines containing all the data in Hyperfine.dat, and transition is */
467  /* defined in cddefines.h */
468 
469  /*transition *HFLines;*/
470 
471  /* get the line data for the hyperfine lines */
472  if( trace.lgTrace )
473  fprintf( ioQQQ," Hyperfine opening hyperfine.dat:");
474 
475  ioDATA = open_data( "hyperfine.dat", "r" );
476 
477  /* first line is a version number and does not count */
478  if( read_whole_line( chLine , (int)sizeof(chLine) , ioDATA ) == NULL )
479  {
480  fprintf( ioQQQ, " Hyperfine could not read first line of hyperfine.dat.\n");
482  }
483 
484  /* count lines in the file, ignoring lines starting with '#',
485  * and get temperature array for HFS collision strengths */
486  size_t nHFLines = 0;
487  while( read_whole_line( chLine , (int)sizeof(chLine) , ioDATA ) != NULL )
488  {
489  if( chLine[0] == '#' )
490  {
491  continue;
492  }
493  else if( strstr(chLine, "TDATA:") == NULL)
494  {
495  Split(chLine, "\t", data, SPM_STRICT);
496  int Aiso = atoi(data[0].c_str());
497  int nelem = atoi(data[1].c_str());
498  double wavelength = atof(data[3].c_str());
499 
500  if( abund.IsoAbn[nelem-1].getAbn( Aiso ) > 0 &&
501  WAVNRYD/wavelength > rfield.emm() )
502  ++nHFLines;
503  }
504  else
505  {
506  Split(chLine, " ", data, SPM_STRICT);
507 
508  if (data.size() <= 1)
509  {
510  fprintf(ioQQQ, "HyperfineCreate: Error: Invalid number of temperatures in 'TDATA:': %d\n",
511  (int) data.size());
513  }
514 
515  Ntemp = data.size() - 1;
516  csTemp = (double *) MALLOC( (size_t)Ntemp*sizeof(double) );
517 
518  int i = 0;
519  for (std::vector<string>::const_iterator it = data.begin(); it != data.end() && i <= Ntemp; it++, i++)
520  {
521  if(i == 0)
522  continue;
523  csTemp[i-1] = atof((*it).c_str());
524  }
525  }
526 
527  data.resize(0);
528  }
529 
530  ASSERT(nHFLines > 0 && Ntemp > 0);
531  for(int i = 0; i < Ntemp; i++)
532  {
533  ASSERT( csTemp[i] > phycon.TEMP_LIMIT_LOW &&
534  csTemp[i] < phycon.TEMP_LIMIT_HIGH );
535  if( i > 0 )
536  ASSERT(csTemp[i] > csTemp[i-1]);
537  // printf("i=%d\t t = %g\n", i, csTemp[i]);
538  }
539 
540  /* allocate the transition HFLines array */
541  HFLines.resize(nHFLines);
542  AllTransitions.push_back(HFLines);
543 
544  /* initialize array to impossible values to make sure eventually done right */
545  for( size_t i=0; i< HFLines.size(); ++i )
546  {
547  HFLines[i].Junk();
548  HFLines[i].AddHiState();
549  HFLines[i].AddLoState();
550  HFLines[i].AddLine2Stack();
551  }
552 
553  colstr = (t_ColStr *)MALLOC( (size_t)(HFLines.size())*sizeof(t_ColStr) );
554  for (size_t j = 0; j < HFLines.size(); j++)
555  {
556  colstr[j].cs = (double *) MALLOC( (size_t)(Ntemp)*sizeof(double) );
557  colstr[j].cs2d= (double *) MALLOC( (size_t)(Ntemp)*sizeof(double) );
558  }
559  hyperfine.HFLabundance = (realnum *)MALLOC( (size_t)(HFLines.size())*sizeof(realnum) );
560 
561 
562  /* now rewind the file so we can read it a second time*/
563  if( fseek( ioDATA , 0 , SEEK_SET ) != 0 )
564  {
565  fprintf( ioQQQ, " Hyperfine could not rewind hyperfine.dat.\n");
567  }
568 
569  /* check that magic number is ok, read the line */
570  if( read_whole_line( chLine , (int)sizeof(chLine) , ioDATA ) == NULL )
571  {
572  fprintf( ioQQQ, " Hyperfine could not read first line of hyperfine.dat.\n");
574  }
575 
576  /* check that magic number is ok, scan it in */
577  {
578  long j = 1;
579  bool lgEOL;
580  int year = (int) FFmtRead(chLine,&j,sizeof(chLine),&lgEOL);
581  int month = (int) FFmtRead(chLine,&j,sizeof(chLine),&lgEOL);
582  int day = (int) FFmtRead(chLine,&j,sizeof(chLine),&lgEOL);
583 
584  /* the following is the set of numbers that appear at the start of hyperfine.dat 13 02 09 */
585  static int iYR=13, iMN=10, iDY=18;
586  if( ( year != iYR ) || ( month != iMN ) || ( day != iDY ) )
587  {
588  fprintf( ioQQQ,
589  " Hyperfine: the version of hyperfine.dat in the data directory is not the current version.\n" );
590  fprintf( ioQQQ,
591  " I expected to find the number %i %i %i and got %i %i %i instead.\n" ,
592  iYR, iMN , iDY ,
593  year , month , day );
594  fprintf( ioQQQ, "Here is the line image:\n==%s==\n", chLine );
596  }
597  }
598 
599  /*
600  * scan the string taken from Hyperfine.dat, parsing into
601  * needed variables.
602  * nelem is the atomic number.
603  * IonStg is the ionization stage. Atom = 1, Z+ = 2, Z++ = 3, etc.
604  * Aul is used to find the oscillator strength in the function GetGF.
605  * most of the variables are floats.
606  */
607 
608  size_t j = 0;
609  while( j < HFLines.size() && read_whole_line( chLine , (int)sizeof(chLine) , ioDATA ) != NULL )
610  {
611  /* skip lines starting with '#' or containing the temperature array */
612  if( chLine[0] == '#' || strstr(chLine, "TDATA:") != NULL)
613  continue;
614 
615  Split(chLine, "\t", data, SPM_STRICT);
616  int Aiso = atoi(data[0].c_str());
617  int nelem = atoi(data[1].c_str());
618  double wavelength = atof(data[3].c_str());
619 
620  /* Ignore lines that fall beyond the lowest energy. */
621  if( ! ( abund.IsoAbn[nelem-1].getAbn( Aiso ) > 0 &&
622  WAVNRYD/wavelength > rfield.emm() ) )
623  {
624  data.resize(0);
625  continue;
626  }
627 
628  (*HFLines[j].Hi()).nelem() = nelem;
629  ASSERT((*HFLines[j].Hi()).nelem() > 0);
630 
631  (*HFLines[j].Hi()).IonStg() = atoi(data[2].c_str());
632  ASSERT((*HFLines[j].Hi()).IonStg() > 0);
633 
634  hyperfine.HFLabundance[j] = abund.IsoAbn[nelem-1].getAbn( Aiso );
635  ASSERT(hyperfine.HFLabundance[j] >= 0.0 && hyperfine.HFLabundance[j] <= 1.0);
636 
637  HFLines[j].Emis().Aul() = (realnum) atof(data[4].c_str());
638  HFLines[j].Emis().damp() = 1e-20f;
639 
640  (*HFLines[j].Hi()).g() = (realnum) (2*(abund.IsoAbn[nelem-1].getSpin( Aiso ) + .5) + 1);
641  (*HFLines[j].Lo()).g() = (realnum) (2*(abund.IsoAbn[nelem-1].getSpin( Aiso ) - .5) + 1);
642 
643  /* account for inverted levels */
644  if( abund.IsoAbn[nelem-1].getMagMom( Aiso ) < 0 )
645  {
646  double tmp = (*HFLines[j].Hi()).g();
647  (*HFLines[j].Hi()).g() = (*HFLines[j].Lo()).g();
648  (*HFLines[j].Lo()).g() = tmp;
649  }
650 
651  double fenergyWN = MAX2(ENERGY_MIN_WN, 1.0 / wavelength);
652  HFLines[j].WLAng() = (realnum)(wavelength * 1e8f);
653  HFLines[j].EnergyWN() = (realnum) fenergyWN;
654 
655  HFLines[j].Emis().gf() = (realnum)(GetGF(HFLines[j].Emis().Aul(), fenergyWN, (*HFLines[j].Hi()).g()));
656  ASSERT(HFLines[j].Emis().gf() > 0.0);
657 
658  (*HFLines[j].Lo()).nelem() = (*HFLines[j].Hi()).nelem();
659  (*HFLines[j].Lo()).IonStg() = (*HFLines[j].Hi()).IonStg();
660 
661  // printf("line %3ld:\t A= %2d\t Z= %2d\t Spin= %3.1f\t MagMom= %8.5f\t IonStg= %2d\t Frac= %6.4f\t"
662  // " Wl= %7.4f\t Aul= %.4e\t glo= %1.0f\t ghi= %1.0f\n",
663  // j, Aiso, nelem,
664  // abund.IsoAbn[nelem-1].getSpin( Aiso ),
665  // abund.IsoAbn[nelem-1].getMagMom( Aiso ),
666  // (*HFLines[j].Hi()).IonStg(),
667  // hyperfine.HFLabundance[j], wavelength, HFLines[j].Emis().Aul(),
668  // (*HFLines[j].Lo()).g(), (*HFLines[j].Hi()).g());
669 
670 
671  if( data.size() > 6 )
672  {
673  // printf("data for line %ld\t %d\t %d:\t", j, nelem, (*HFLines[j].Hi()).IonStg());
674  for (int ij = 6, ii = 0; ij < (int) data.size() && ii < Ntemp; ij++, ii++)
675  {
676  colstr[j].cs[ii] = atof(data[ij].c_str());
677  ASSERT(colstr[j].cs[ii] >= 0.0);
678  // printf("%g\t", colstr[j].cs[ii]);
679  }
680  // printf("\n");
681  spline(csTemp, colstr[j].cs, Ntemp, 2e31, 2e31, colstr[j].cs2d);
682  }
683  else
684  {
685  MakeCS( HFLines[j] );
686  free( colstr[j].cs ); colstr[j].cs = NULL;
687  free( colstr[j].cs2d ); colstr[j].cs2d = NULL;
688  }
689 
690  data.resize(0);
691 
692  j++;
693  }
694  fclose(ioDATA);
695 
696  ASSERT( j == HFLines.size() );
697 
698  /* Discard no-longer needed nuclear data */
699  for( long nelem = 0; nelem < LIMELM; nelem++ )
700  abund.IsoAbn[nelem].rm_nuc_data();
701 
702 
703 # if 0
704  /* for debugging and developing only */
705  /* calculating the luminosity for each isotope */
706  for(int i = 0; i < HFLines.size(); i++)
707  {
708  N = dense.xIonDense[(*HFLines[i].Hi()).nelem()-1][(*HFLines[i].Hi()).IonStg()-1];
709  Ne = dense.eden;
710 
711  h = 6.626076e-27; /* erg * sec */
712  c = 3e10; /* cm / sec */
713  k = 1.380658e-16; /* erg / K */
714 
715  upsilon = HyperfineCS(i);
716  /*statistical weights must still be identified */
717  q21 = COLL_CONST * upsilon / (phycon.sqrte * (*HFLines[i].Hi()).g());
718 
719  q12 = (*HFLines[i].Hi()).g()/ (*HFLines[i].Lo()).g() * q21 * exp(-1 * h * c * HFLines[i].EnergyWN / (k * phycon.te));
720 
721  x = Ne * q12 / (HFLines[i].Emis().Aul() * (1 + Ne * q21 / HFLines[i].Aul()));
722  HFLines[i].xIntensity() = N * HFLines[i].Emis().Aul() * x / (1.0 + x) * h * c / (HFLines[i].EnergyAng() / 1e8);
723 
724  }
725 # endif
726 
727  return;
728 }
729 
730 
731 /*HyperfineCS returns interpolated collision strength for transition index i */
732 double HyperfineCS( size_t i )
733 {
734  double upsilon;
735 
736  DEBUG_ENTRY( "HyperfineCS()" );
737 
738  ASSERT( i >= 0. && i < HFLines.size() );
739 
740  if( colstr[i].cs == NULL )
741  return HFLines[i].Coll().col_str();
742 
743  if( phycon.te <= csTemp[0])
744  {
745  /* constant CS, if temperature below bounds of table */
746  upsilon = colstr[i].cs[0];
747  }
748  else if( phycon.te >= csTemp[Ntemp-1])
749  {
750  /* extrapolate, if temperature above bounds of table */
751  int j = Ntemp - 1;
752  double slope = log10(colstr[i].cs[j-1]/colstr[i].cs[j]) / log10(csTemp[j-1]/csTemp[j]);
753  upsilon = log10(phycon.te/csTemp[j])*slope + log10(colstr[i].cs[j]);
754  upsilon = exp10( upsilon);
755  }
756  else
757  {
758  splint( csTemp, colstr[i].cs, colstr[i].cs2d, Ntemp, phycon.te, &upsilon );
759  }
760 
761  return upsilon;
762 }
double emm() const
Definition: mesh.h:93
double HyperfineCS(size_t i)
double TexcLine(const TransitionProxy &t)
Definition: transition.cpp:207
STATIC double h21_t_ge_10(double temp)
FILE * open_data(const char *fname, const char *mode, access_scheme scheme)
Definition: cpu.cpp:765
vector< double, allocator_avx< double > > ContBoltz
Definition: rfield.h:124
STATIC t_ColStr * colstr
size_t size(void) const
Definition: transition.h:331
qList st
Definition: iso.h:482
double exp10(double x)
Definition: cddefines.h:1368
STATIC double h21_t_lt_10(double temp)
NORETURN void TotalInsanity(void)
Definition: service.cpp:971
static double x1[83]
t_hyperfine hyperfine
Definition: hyperfine.cpp:5
const realnum SMALLFLOAT
Definition: cpu.h:246
double H21cm_H_atom(double temp)
double e1(double x)
STATIC double * csTemp
void set_xIntensity(const TransitionProxy &t)
double cdsqte
Definition: dense.h:246
static const int N
t_conv conv
Definition: conv.cpp:5
TransitionList HFLines("HFLines",&AnonStates)
t_phycon phycon
Definition: phycon.cpp:6
sys_float sexp(sys_float x)
Definition: service.cpp:999
FILE * ioQQQ
Definition: cddefines.cpp:7
void rm_nuc_data()
Definition: abund.h:178
#define MIN2(a, b)
Definition: cddefines.h:803
void HyperfineCreate(void)
double * cs
void splint(const double xa[], const double ya[], const double y2a[], long int n, double x, double *y)
Definition: thirdparty.h:173
t_dense dense
Definition: global.cpp:15
t_iso_sp iso_sp[NISO][LIMELM]
Definition: iso.cpp:11
const double TEMP_LIMIT_LOW
Definition: phycon.h:121
double xIonDense[LIMELM][LIMELM+1]
Definition: dense.h:135
double H21cm_proton(double temp)
bool lgSearch
Definition: conv.h:168
t_trace trace
Definition: trace.cpp:5
#define MALLOC(exp)
Definition: cddefines.h:554
double H21cm_electron(double temp)
t_abund abund
Definition: abund.cpp:5
void resize(size_t newsize)
Definition: transition.h:319
t_isotope IsoAbn[LIMELM]
Definition: abund.h:213
void spline(const double x[], const double y[], long int n, double yp1, double ypn, double y2a[])
Definition: thirdparty.h:150
const int ipH1s
Definition: iso.h:29
#define STATIC
Definition: cddefines.h:118
bool lgTrace
Definition: trace.h:12
realnum getMagMom(int Anum)
Definition: abund.h:104
realnum getSpin(int Anum)
Definition: abund.h:94
const double TEMP_LIMIT_HIGH
Definition: phycon.h:123
t_rfield rfield
Definition: rfield.cpp:9
float realnum
Definition: cddefines.h:124
#define EXIT_FAILURE
Definition: cddefines.h:168
const int INPUT_LINE_LENGTH
Definition: cddefines.h:301
#define cdEXIT(FAIL)
Definition: cddefines.h:482
double powi(double, long int)
Definition: service.cpp:594
const int ipH2p
Definition: iso.h:31
#define ASSERT(exp)
Definition: cddefines.h:613
double OccupationNumberLine(const TransitionProxy &t)
Definition: transition.cpp:177
double Tspin21cm
Definition: hyperfine.h:56
realnum getAbn(int Anum)
Definition: abund.h:121
LyaSourceFunctionShape LyaSourceFunctionShape_assumed
Definition: hyperfine.h:72
double GetGF(double trans_prob, double enercm, double gup)
const int ipH_LIKE
Definition: iso.h:64
const int LIMELM
Definition: cddefines.h:308
T pow2(T a)
Definition: cddefines.h:981
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:723
double powpq(double x, int p, int q)
Definition: service.cpp:630
bool lgLya_pump_21cm
Definition: hyperfine.h:59
static vector< realnum > wavelength
const double ENERGY_MIN_WN
double eden
Definition: dense.h:201
#define MAX2(a, b)
Definition: cddefines.h:824
int fprintf(const Output &stream, const char *format,...)
Definition: service.cpp:1121
double * cs2d
double e2(double x)
sys_float SDIV(sys_float x)
Definition: cddefines.h:1002
void Split(const string &str, const string &sep, vector< string > &lst, split_mode mode)
Definition: service.cpp:106
vector< TransitionList > AllTransitions
Definition: taulines.cpp:9
char * read_whole_line(char *chLine, int nChar, FILE *ioIN)
Definition: service.cpp:70
double sqrte
Definition: phycon.h:58
#define fixit(a)
Definition: cddefines.h:417
double te
Definition: phycon.h:21
const int ipHYDROGEN
Definition: cddefines.h:349
void H21_cm_pops(void)
EmissionList & Emis()
Definition: transition.h:363
STATIC int Ntemp
void MakeCS(const TransitionProxy &t)
Definition: transition.cpp:579
double FFmtRead(const char *chCard, long int *ipnt, long int last, bool *lgEOL)
Definition: service.cpp:381
realnum * HFLabundance
Definition: hyperfine.h:53