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