cloudy  trunk
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
prt_lines_helium.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 /*lines_helium put He-like iso sequence into line intensity stack */
4 /*TempInterp interpolates on a grid of values to produce predicted value at current Te.*/
5 #include "cddefines.h"
6 #include "dense.h"
7 #include "prt.h"
8 #include "helike.h"
9 #include "iso.h"
10 #include "atmdat.h"
11 #include "lines.h"
12 #include "phycon.h"
13 #include "taulines.h"
14 #include "thirdparty.h"
15 #include "trace.h"
16 #include "freebound.h"
17 #include "two_photon.h"
18 #include "lines_service.h"
19 
20 #define NUMTEMPS 21
21 #define NUMDENS 14
22 typedef struct
23 {
24  /* index for upper and lower levels of line */
25  long int ipHi;
26  long int ipLo;
27 
28  char label[5];
29 } stdLines;
30 
31 STATIC void GetStandardHeLines(void);
32 STATIC double TempInterp2( double* TempArray , double* ValueArray, long NumElements, double Te );
33 STATIC void DoSatelliteLines( long nelem );
34 
35 static bool lgFirstRun = true;
36 static double CaBDensities[NUMDENS];
37 static double CaBTemps[NUMTEMPS];
38 static long NumLines;
39 static double ****CaBIntensity;
40 static stdLines **CaBLines;
41 
42 
43 
44 STATIC void insert_trans( vector<TransitionProxy> &trList, TransitionProxy tr )
45 {
46  if( tr.ipCont() < 1 )
47  return;
48  trList.push_back( tr );
49  return;
50 }
51 
52 
53 
54 STATIC void multiplet_sum( vector<TransitionProxy> &trList, realnum &av_wl,
55  double &sum_inten, double &sum_obs_inten, double &sum_cool, double &sum_heat )
56 {
57  av_wl = -1.;
58  sum_inten = sum_obs_inten = sum_cool = sum_heat = 0.;
59 
60  if( trList.size() == 0)
61  return;
62 
63  vector<realnum> wl;
64  for( vector<TransitionProxy>::iterator tr = trList.begin(); tr != trList.end(); tr++ )
65  {
66  sum_obs_inten += (*tr).Emis().xObsIntensity();
67  sum_inten += (*tr).Emis().xIntensity();
68  sum_cool += (*tr).Coll().cool();
69  sum_heat += (*tr).Coll().heat();
70  wl.push_back( (*tr).WLAng() );
71  }
72 
73  if( wl.size() == 0 )
74  return;
75 
76  std::sort( wl.begin(), wl.end(), std::less<realnum>() );
77  if( wl.size() >= 1)
78  av_wl = wl[1];
79  else av_wl = wl[0];
80 
81  return;
82 }
83 
84 
85 STATIC inline void PutLineSum ( const TransitionProxy &tr, const realnum av_wl,
86  const double sumxInt, const double sumxObsInt,
87  const double sumcool, const double sumheat,
88  const char *chComment )
89 {
90  if( av_wl < 0. )
91  return;
92 
93  TransitionProxy tr2 = tr;
94 
95  tr2.WLAng() = av_wl;
96  tr2.Emis().xIntensity() = sumxInt;
97  tr2.Emis().xObsIntensity() = sumxObsInt;
98  tr2.Coll().cool() = sumcool;
99  tr2.Coll().heat() = sumheat;
100 
101  PutLine( tr2, chComment );
102 
103  return;
104 }
105 
106 STATIC inline realnum get_multiplet_wl( vector<TransitionProxy> &multiHe, long ipHi, long ipLo )
107 {
108  realnum wl = -1.;
109 
110  for( vector<TransitionProxy>::iterator tr = multiHe.begin(); tr != multiHe.end(); tr++ )
111  {
112  if( (*tr).ipHi() == ipHi && (*tr).ipLo() == ipLo )
113  {
114  wl = (*tr).WLAng();
115  break;
116  }
117  }
118 
119  return wl;
120 }
121 
122 STATIC inline void randomize_inten( t_iso_sp* sp, long ipLo, long ipHi )
123 {
124  sp->trans(ipHi,ipLo).Emis().xIntensity() *= sp->ex[ipHi][ipLo].ErrorFactor[IPRAD];
125  sp->trans(ipHi,ipLo).Emis().xObsIntensity() *= sp->ex[ipHi][ipLo].ErrorFactor[IPRAD];
126  return;
127 }
128 
129 
130 void lines_helium(void)
131 {
132  long ipISO = ipHE_LIKE;
133  string chLabel=" ";
134 
135  double log10_eden = log10(dense.eden);
136 
137  DEBUG_ENTRY( "lines_helium()" );
138 
139  if( trace.lgTrace )
140  fprintf( ioQQQ, " prt_lines_helium called\n" );
141 
142  // this can be changed with the atom levels command but must be at
143  // least 3.
144  ASSERT( iso_sp[ipHE_LIKE][ipHELIUM].n_HighestResolved_max >= 3 );
145 
146  long i = StuffComment( "He-like iso-sequence" );
147  linadd( 0., (realnum)i , "####", 'i',
148  " start He-like iso sequence");
149 
150  /* read in Case A and B lines from data file */
151  if( lgFirstRun )
152  {
154  lgFirstRun = false;
155  }
156 
157  /* this is the main printout, where line intensities are entered into the stack */
158  for( long nelem=ipISO; nelem < LIMELM; nelem++ )
159  {
160  vector<TransitionProxy> multiplet;
161  vector<TransitionProxy> HeTrList;
162  double sumxInt = 0., sumxObsInt = 0., sumcool = 0., sumheat = 0.;
163  realnum av_wl = 0.;
164 
165 
166  if( dense.lgElmtOn[nelem] )
167  {
168  t_iso_sp* sp = &iso_sp[ipHE_LIKE][nelem];
169 
170  ASSERT( sp->n_HighestResolved_max >= 3 );
171 
172  // add two-photon details here
173  if( LineSave.ipass == 0 )
174  {
175  /* chIonLbl is function that generates a null terminated 4 char string, of form "C 2"
176  * the result, chLable, is only used when ipass == 0, can be undefined otherwise */
177  chLabel = chIonLbl(nelem+1, nelem+1-ipISO);
178  }
179  for( vector<two_photon>::iterator tnu = sp->TwoNu.begin(); tnu != sp->TwoNu.end(); ++tnu )
180  {
181  fixit("This was multiplied by Pesc when treated as a line, now what? Only used for printout?");
182  fixit("below should be 'i' instead of 'r' ?");
183 
184  string tpc_comment = "";
185  if( LineSave.ipass == 0 )
186  {
187  tpc_comment = " two photon continuum, " +
188  iso_comment_tran_levels( ipISO, nelem, (*tnu).ipLo, (*tnu).ipHi );
189  }
190 
191  linadd( tnu->AulTotal * tnu->E2nu * EN1RYD * (*tnu->Pop),
192  2. * wn2ang( (*sp).trans( (*tnu).ipHi, (*tnu).ipLo ).EnergyWN() ),
193  chLabel.c_str(), 'r', tpc_comment.c_str() );
194  }
195 
196  /* here we will create an entry for the three lines
197  * coming from 2 3P to 1 1S - the entry called TOTL will
198  * appear before the lines of the multiplet */
199 
200  for( long i=ipHe2p3P0; i <= ipHe2p3P2; i++ )
201  {
202  set_xIntensity( sp->trans(i, ipHe1s1S) );
203  insert_trans( multiplet, sp->trans(i, ipHe1s1S) );
204  if( nelem == ipHELIUM )
205  HeTrList.push_back( sp->trans(i, ipHe1s1S) );
206  }
207 
208  multiplet_sum( multiplet, av_wl, sumxInt, sumxObsInt, sumcool, sumheat );
209  multiplet.resize( 0 );
210 
211  linadd(sumxObsInt, sp->trans(ipHe2p3P1,ipHe1s1S).WLAng(), "TOTL", 'i',
212  " total emission in He-like intercombination lines from 2p3P to ground ");
213 
214 
215  /* set number of levels we want to print, first is default,
216  * only print real levels, second is set with "print line
217  * iso collapsed" command */
218  long int nLoop = sp->numLevels_max - sp->nCollapsed_max;
219  if( prt.lgPrnIsoCollapsed )
220  nLoop = sp->numLevels_max;
221 
222  /* now do real permitted lines */
223  /* NB NB - low and high must be in this order so that all balmer, paschen,
224  * etc series line up correctly in final printout */
225  /* >>chng 01 jun 13, bring 23P lines back together */
226  for( long ipLo=0; ipLo < ipHe2p3P0; ipLo++ )
227  {
228  vector<long> EnterTheseLast;
229  for( long ipHi=ipLo+1; ipHi < nLoop; ipHi++ )
230  {
231  /* >>chng 01 may 30, do not add fake he-like lines (majority) to line stack */
232  /* >>chng 01 dec 11, use variable for smallest A */
233  if( sp->trans(ipHi,ipLo).ipCont() < 1 )
234  continue;
235 
236  /* recombine fine-structure lines since the energies are
237  * not resolved anyway. */
238  if( iso_ctrl.lgFSM[ipISO] && ( abs(sp->st[ipHi].l() -
239  sp->st[ipLo].l())==1 )
240  && (sp->st[ipLo].l()>1)
241  && (sp->st[ipHi].l()>1)
242  && ( sp->st[ipHi].n() ==
243  sp->st[ipLo].n() ) )
244  {
245  /* skip if both singlets. */
246  if( (sp->st[ipHi].S()==1)
247  && (sp->st[ipLo].S()==1) )
248  {
249  continue;
250  }
251  else if( (sp->st[ipHi].S()==3)
252  && (sp->st[ipLo].S()==3) )
253  {
254  string comment_trans = "";
255 
256  /* singlet to singlet*/
257  insert_trans( multiplet, sp->trans(ipHi ,ipLo+1) );
258  insert_trans( multiplet, sp->trans(ipHi+1,ipLo+1) );
259  if( nelem == ipHELIUM )
260  {
261  HeTrList.push_back( sp->trans(ipHi , ipLo+1) );
262  HeTrList.push_back( sp->trans(ipHi+1, ipLo+1) );
263  }
264  if( multiplet.size() == 0 ) continue;
265 
266  multiplet_sum( multiplet, av_wl, sumxInt, sumxObsInt, sumcool, sumheat );
267  multiplet.resize( 0 );
268 
269  if( LineSave.ipass == 0 )
270  {
271  comment_trans = "singlet to singlet: ";
272  comment_trans += iso_comment_tran_levels( ipISO, nelem, ipLo+1, ipHi );
273  comment_trans += "; ";
274  comment_trans += iso_comment_tran_levels( ipISO, nelem, ipLo+1, ipHi+1 );
275  }
276  PutLineSum( sp->trans(ipHi+1,ipLo+1),
277  sp->trans(ipHi+1,ipLo+1).WLAng(), sumxInt, sumxObsInt, sumcool, sumheat,
278  comment_trans.c_str() );
279 
280  /* triplet to triplet */
281  insert_trans( multiplet, sp->trans(ipHi ,ipLo) );
282  insert_trans( multiplet, sp->trans(ipHi+1,ipLo) );
283  if( nelem == ipHELIUM )
284  {
285  HeTrList.push_back( sp->trans(ipHi , ipLo) );
286  HeTrList.push_back( sp->trans(ipHi+1, ipLo) );
287  }
288  if( multiplet.size() == 0 ) continue;
289 
290  multiplet_sum( multiplet, av_wl, sumxInt, sumxObsInt, sumcool, sumheat );
291  multiplet.resize( 0 );
292 
293  if( LineSave.ipass == 0 )
294  {
295  comment_trans = "triplet to triplet: ";
296  comment_trans += iso_comment_tran_levels( ipISO, nelem, ipLo, ipHi );
297  comment_trans += "; ";
298  comment_trans += iso_comment_tran_levels( ipISO, nelem, ipLo, ipHi+1 );
299  }
300  PutLineSum( sp->trans(ipHi,ipLo),
301  sp->trans(ipHi,ipLo).WLAng(), sumxInt, sumxObsInt, sumcool, sumheat,
302  comment_trans.c_str() );
303  }
304  }
305 
306  else if( ipLo==ipHe2s3S && ipHi == ipHe2p3P0 )
307  {
308  /* here we will create an entry for the three lines
309  * coming from 2 3P to 2 3S - the entry called TOTL will
310  * appear before the lines of the multiplet
311  * for He I this is 10830 */
312 
313  for( long i=ipHe2p3P0; i <= ipHe2p3P2; i++ )
314  {
315  set_xIntensity( sp->trans(i, ipLo) );
316 
317  /* >>chng 13-jun-06
318  * correct for isotropic continuum before applying error randomization
319  * to avoid shutting off emission lines (if the correction is applied _after_
320  * the error is computed, it is likely to be higher than the updated intensity)
321  */
322  if( iso_ctrl.lgRandErrGen[ipISO] )
323  {
324  randomize_inten( sp, ipLo, i );
325  }
326 
327  insert_trans( multiplet, sp->trans(i, ipLo) );
328  if( nelem == ipHELIUM )
329  HeTrList.push_back( sp->trans(i, ipLo) );
330  }
331 
332  if( multiplet.size() == 0 ) continue;
333  multiplet_sum( multiplet, av_wl, sumxInt, sumxObsInt, sumcool, sumheat );
334  multiplet.resize( 0 );
335 
336 
337  linadd(sumxObsInt, av_wl, "TOTL", 'i',
338  "total emission in He-like lines, use average of three line wavelengths " );
339 
340 
341  if( nelem == ipHELIUM )
342  {
343  string comment_trans = "";
344  if( LineSave.ipass == 0 )
345  {
346  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipLo, ipHi );
347  }
348  PutLineSum( sp->trans(ipHi,ipLo), av_wl, sumxInt, sumxObsInt, sumcool, sumheat,
349  comment_trans.c_str() );
350  }
351  else
352  {
353  string comment_trans = "";
354  if( LineSave.ipass == 0 )
355  {
356  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipLo, ipHi );
357  }
358  PutLine( sp->trans(ipHi,ipLo), comment_trans.c_str() );
359 
360  /* also add this with the regular label, so it is correctly picked up by assert case-b command */
361  linadd(sumxObsInt, av_wl, chLabel.c_str(), 'i',
362  "total emission in He-like lines, use average of three line wavelengths " );
363  }
364  }
365  else if( ipLo==ipHe2s3S && (ipHi == ipHe2p3P1 || ipHi==ipHe2p3P2) )
366  {
367  /* >>chng 13-aug-01
368  * If He, do nothing.
369  * The transitions have been computed already in the loop above.
370  * N.B. If this block is removed, the transitions will be rentered
371  * by the block below.
372  */
373  if( nelem > ipHELIUM )
374  {
375  string comment_trans = "";
376  if( LineSave.ipass == 0 )
377  {
378  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipLo, ipHi );
379  }
380  PutLine( sp->trans(ipHi,ipLo), comment_trans.c_str() );
381  }
382  }
383  else
384  {
385  set_xIntensity( sp->trans(ipHi,ipLo) );
386 
387  if( iso_ctrl.lgRandErrGen[ipISO] )
388  {
389  randomize_inten( sp, ipLo, ipHi );
390  }
391 
392  if( abs( L_(ipHi) - L_(ipLo) ) != 1 )
393  {
394  EnterTheseLast.push_back( ipHi );
395  continue;
396  }
397 
398  /*
399  fprintf(ioQQQ,"1 loop %li %li %.1f\n", ipLo,ipHi,
400  sp->trans(ipHi,ipLo).WLAng() ); */
401  string comment_trans = "";
402  if( LineSave.ipass == 0 )
403  {
404  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipLo, ipHi );
405  }
406  PutLine( sp->trans(ipHi,ipLo), comment_trans.c_str() );
407 
408  {
409  /* option to print particulars of some line when called
410  * a prettier print statement is near where chSpin is defined below*/
411  enum {DEBUG_LOC=false};
412  if( DEBUG_LOC )
413  {
414  if( nelem==1 && ipLo==0 && ipHi==1 )
415  {
416  fprintf(ioQQQ,"he1 626 %.2e %.2e \n",
417  sp->trans(ipHi,ipLo).Emis().TauIn(),
418  sp->trans(ipHi,ipLo).Emis().TauTot()
419  );
420  }
421  }
422  }
423  }
424  }
425 
426  // enter these lines last because they are generally weaker quadrupole transitions.
427  for( vector<long>::iterator it = EnterTheseLast.begin(); it != EnterTheseLast.end(); it++ )
428  {
429  string comment_trans = "";
430  if( LineSave.ipass == 0 )
431  {
432  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipLo, *it );
433  }
434  PutLine( sp->trans(*it,ipLo), comment_trans.c_str() );
435  }
436  }
437 
438  /* this sum will bring together the three lines going to J levels within 23P */
439  for( long ipHi=ipHe2p3P2+1; ipHi < nLoop; ipHi++ )
440  {
441  for( long ipLo=ipHe2p3P0; ipLo <= ipHe2p3P2; ++ipLo )
442  {
443  if( sp->trans(ipHi,ipLo).ipCont() < 1 )
444  continue;
445 
446  set_xIntensity( sp->trans(ipHi, ipLo) );
447 
448  if( iso_ctrl.lgRandErrGen[ipISO] )
449  {
450  randomize_inten( sp, ipLo, ipHi );
451  }
452 
453  insert_trans( multiplet, sp->trans(ipHi, ipLo) );
454  if( nelem == ipHELIUM )
455  HeTrList.push_back( sp->trans(ipHi, ipLo) );
456  }
457 
458  if( sp->trans(ipHi,ipHe2p3P2).ipCont() < 1 )
459  {
460  multiplet.resize( 0 );
461  continue;
462  }
463 
464  if( multiplet.size() == 0 ) continue;
465  multiplet_sum( multiplet, av_wl, sumxInt, sumxObsInt, sumcool, sumheat );
466  multiplet.resize( 0 );
467 
468  string comment_trans = "";
469  if( LineSave.ipass == 0 )
470  {
471  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipHe2p3P2, ipHi );
472  }
473  PutLineSum( sp->trans(ipHi,ipHe2p3P2), av_wl, sumxInt, sumxObsInt, sumcool, sumheat,
474  comment_trans.c_str() );
475  }
476  for( long ipLo=ipHe2p3P2+1; ipLo < nLoop-1; ipLo++ )
477  {
478  vector<long> EnterTheseLast;
479  for( long ipHi=ipLo+1; ipHi < nLoop; ipHi++ )
480  {
481  /* skip non-radiative lines */
482  if( sp->trans(ipHi,ipLo).ipCont() < 1 )
483  continue;
484 
485  set_xIntensity( sp->trans(ipHi,ipLo) );
486 
487  if( iso_ctrl.lgRandErrGen[ipISO] )
488  {
489  randomize_inten( sp, ipLo, ipHi );
490  }
491 
492  if( N_(ipHi) > sp->n_HighestResolved_max || abs( L_(ipHi) - L_(ipLo) ) != 1 )
493  {
494  EnterTheseLast.push_back( ipHi );
495  continue;
496  }
497 
498  string comment_trans = "";
499  if( LineSave.ipass == 0 )
500  {
501  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipLo, ipHi );
502  }
503  PutLine(sp->trans(ipHi,ipLo), comment_trans.c_str() );
504  }
505 
506  // enter these lines last because they are generally weaker quadrupole transitions.
507  for( vector<long>::iterator it = EnterTheseLast.begin(); it != EnterTheseLast.end(); it++ )
508  {
509  string comment_trans = "";
510  if( LineSave.ipass == 0 )
511  {
512  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipLo, *it );
513  }
514  PutLine( sp->trans(*it,ipLo), comment_trans.c_str() );
515  }
516  }
517 
518  /* Now put the satellite lines in */
519  if( iso_ctrl.lgDielRecom[ipISO] )
520  DoSatelliteLines(nelem);
521 
522  if( nelem == ipHELIUM )
523  {
524  for( long i=0; i< NumLines; i++ )
525  {
526  double intens_at_Te[NUMDENS];
527  for( long ipDens = 0; ipDens < NUMDENS; ++ipDens )
528  intens_at_Te[ipDens] = TempInterp2( CaBTemps, CaBIntensity[nelem][i][ipDens], NUMTEMPS, phycon.te );
529  double intens = linint( CaBDensities, intens_at_Te, NUMDENS, log10_eden );
530  intens = exp10( intens ) * dense.xIonDense[nelem][nelem+1-ipISO]*dense.eden;
531  ASSERT( intens >= 0. );
532 
533  realnum wvlg = get_multiplet_wl( HeTrList, CaBLines[nelem][i].ipHi, CaBLines[nelem][i].ipLo );
534  if( wvlg <= 0.0) wvlg = atmdat.CaseBWlHeI[i];
535  linadd( intens, wvlg, CaBLines[nelem][i].label, 'i', "Case B intensity " );
536  }
537  }
538  }
539  }
540 
541 
542  if( iso_sp[ipHE_LIKE][ipHELIUM].n_HighestResolved_max >= 4 &&
543  ( iso_sp[ipH_LIKE][ipHYDROGEN].n_HighestResolved_max + iso_sp[ipH_LIKE][ipHYDROGEN].nCollapsed_max ) >=8 )
544  {
546  const long ipHe4s3S = iso_sp[ipHE_LIKE][ipHELIUM].QuantumNumbers2Index[4][0][3];
547  const long ipHe4p3P = iso_sp[ipHE_LIKE][ipHELIUM].QuantumNumbers2Index[4][1][3];
548 
549  /* For info only, add the total photon flux in 3889 and 7065,
550  * and 3188, 4713, and 5876. */
551  double photons_3889_plus_7065 =
552  /* to 2p3P2 */
553  phots( sp->trans(ipHe3s3S,ipHe2p3P2) ) +
554  phots( sp->trans(ipHe3d3D,ipHe2p3P2) ) +
555  phots( sp->trans(ipHe4s3S,ipHe2p3P2) ) +
556  /* to 2p3P1 */
557  phots( sp->trans(ipHe3s3S,ipHe2p3P1) ) +
558  phots( sp->trans(ipHe3d3D,ipHe2p3P1) ) +
559  phots( sp->trans(ipHe4s3S,ipHe2p3P1) ) +
560  /* to 2p3P0 */
561  phots( sp->trans(ipHe3s3S,ipHe2p3P0) ) +
562  phots( sp->trans(ipHe3d3D,ipHe2p3P0) ) +
563  phots( sp->trans(ipHe4s3S,ipHe2p3P0) ) +
564  /* to 2s3S */
565  phots( sp->trans(ipHe3p3P,ipHe2s3S) ) +
566  phots( sp->trans(ipHe4p3P,ipHe2s3S) ) ;
567 
568  long upperIndexofH8 = iso_sp[ipH_LIKE][ipHYDROGEN].QuantumNumbers2Index[8][1][2];
569 
570  /* Add in photon flux of H8 3889 */
571  photons_3889_plus_7065 +=
572  phots( iso_sp[ipH_LIKE][ipHYDROGEN].trans(upperIndexofH8,1) );
573 
574  /* now multiply by ergs of normalization line, so that relative flux of
575  * this line will be ratio of photon fluxes. */
576  if( LineSave.WavLNorm > 0 )
577  photons_3889_plus_7065 *= (ERG1CM*1.e8)/LineSave.WavLNorm;
578  linadd( photons_3889_plus_7065, 3889., "Pho+", 'i',
579  "photon sum given in Porter et al. 2007 (astro-ph/0611579)");
580  }
581 
582  /* ====================================================
583  * end helium
584  * ====================================================*/
585 
586  if( trace.lgTrace )
587  {
588  fprintf( ioQQQ, " lines_helium returns\n" );
589  }
590  return;
591 }
592 
593 // Searches through for next non-comment line, returns NULL on failure
594 inline char* read_data_line( char *chLine, int size, FILE *ioDATA )
595 {
596  char *b;
597  do
598  {
599  b = read_whole_line( chLine , size , ioDATA );
600  if ( b == NULL)
601  break;
602  }
603  while (chLine[0] == '#');
604  return b;
605 }
606 
608 {
609  FILE *ioDATA;
610  bool lgEOL;
611  long i, i1, i2;
612 
613 # define chLine_LENGTH 1000
614  char chLine[chLine_LENGTH];
615 
616  DEBUG_ENTRY( "GetStandardHeLines()" );
617 
618  if( trace.lgTrace )
619  fprintf( ioQQQ," lines_helium opening he1_case_b.dat\n");
620 
621  ioDATA = open_data( "he1_case_b.dat", "r" );
622 
623  /* check that magic number is ok */
624  if( read_whole_line( chLine , (int)sizeof(chLine) , ioDATA ) == NULL )
625  {
626  fprintf( ioQQQ, " lines_helium could not read first line of he1_case_b.dat.\n");
628  }
629  i = 1;
630  /* first number is magic number, second is number of lines in file */
631  i1 = (long)FFmtRead(chLine,&i,sizeof(chLine),&lgEOL);
632  i2 = (long)FFmtRead(chLine,&i,sizeof(chLine),&lgEOL);
633  NumLines = i2;
634 
635  /* the following is to check the numbers that appear at the start of he1_case_b.dat */
636  if( i1 !=CASEBMAGIC )
637  {
638  fprintf( ioQQQ,
639  " lines_helium: the version of he1_case_ab.dat is not the current version.\n" );
640  fprintf( ioQQQ,
641  " lines_helium: I expected to find the number %i and got %li instead.\n" ,
642  CASEBMAGIC, i1 );
643  fprintf( ioQQQ, "Here is the line image:\n==%s==\n", chLine );
645  }
646 
647  /* get the array of densities */
648  if ( read_data_line( chLine , (int)sizeof(chLine) , ioDATA ) == NULL )
649  {
650  fprintf( ioQQQ, " lines_helium could not find line of densities in he1_case_ab.dat.\n");
652  }
653 
654  lgEOL = false;
655  i = 1;
656  for( long j=0; !lgEOL && j < NUMDENS; ++j)
657  {
658  CaBDensities[j] = FFmtRead(chLine,&i,sizeof(chLine),&lgEOL);
659  }
660 
661  /* get the array of temperatures */
662  if ( read_data_line( chLine , (int)sizeof(chLine) , ioDATA ) == NULL )
663  {
664  fprintf( ioQQQ, " lines_helium could not find line of temperatures in he1_case_ab.dat.\n");
666  }
667 
668  lgEOL = false;
669  i = 1;
670  for (long j=0; !lgEOL && j < NUMTEMPS; ++j)
671  {
672  CaBTemps[j] = FFmtRead(chLine,&i,sizeof(chLine),&lgEOL);
673  }
674 
675  /* create space for array of values, if not already done */
676  CaBIntensity = (double ****)MALLOC(sizeof(double ***)*(unsigned)LIMELM );
677  /* create space for array of values, if not already done */
678  CaBLines = (stdLines **)MALLOC(sizeof(stdLines *)*(unsigned)LIMELM );
679 
680  for( long nelem=ipHELIUM; nelem<LIMELM; ++nelem )
681  {
685  if( nelem != ipHELIUM )
686  continue;
687 
688  /* only grab core for elements that are turned on */
689  if( nelem == ipHELIUM || dense.lgElmtOn[nelem])
690  {
691  /* create space for array of values, if not already done */
692  CaBIntensity[nelem] = (double ***)MALLOC(sizeof(double **)*(unsigned)(i2) );
693  CaBLines[nelem] = (stdLines *)MALLOC(sizeof(stdLines )*(unsigned)(i2) );
694 
695  /* avoid allocating 0 bytes, some OS return NULL pointer, PvH
696  CaBIntensity[nelem][0] = NULL;*/
697  for( long j = 0; j < i2; ++j )
698  {
699  CaBIntensity[nelem][j] = (double **)MALLOC(sizeof(double*)*(unsigned)NUMDENS );
700 
701  CaBLines[nelem][j].ipHi = -1;
702  CaBLines[nelem][j].ipLo = -1;
703  strcpy( CaBLines[nelem][j].label , " " );
704 
705  for( long k = 0; k < NUMDENS; ++k )
706  {
707  CaBIntensity[nelem][j][k] = (double *)MALLOC(sizeof(double)*(unsigned)NUMTEMPS );
708  for( long l = 0; l < NUMTEMPS; ++l )
709  {
710  CaBIntensity[nelem][j][k][l] = 0.;
711  }
712  }
713  }
714  }
715  }
716 
717  /* now read in the case B line data */
718  long nelem = ipHELIUM;
719  int line = 0;
720  while( read_data_line( chLine , (int)sizeof(chLine) , ioDATA ) != NULL )
721  {
722  /* only look at lines without '#' in first col */
723  if( (chLine[0] == ' ') || (chLine[0]=='\n') )
724  break;
725 
726  /* get lower and upper level index */
727  long j = 1;
728  // the first number is the wavelength, which is only used if the
729  // model atom is too small to include this transition
730  realnum wl = (realnum)FFmtRead(chLine,&j,sizeof(chLine),&lgEOL);
731  long ipLo = (long)FFmtRead(chLine,&j,sizeof(chLine),&lgEOL);
732  long ipHi = (long)FFmtRead(chLine,&j,sizeof(chLine),&lgEOL);
733  CaBLines[nelem][line].ipLo = ipLo;
734  CaBLines[nelem][line].ipHi = ipHi;
735 
736  ASSERT( CaBLines[nelem][line].ipLo < CaBLines[nelem][line].ipHi );
737 
738  strcpy( CaBLines[nelem][line].label, "Ca B" );
739 
740  t_iso_sp* sp = &iso_sp[ipHE_LIKE][nelem];
741  if( ipHi < sp->numLevels_max - sp->nCollapsed_max )
742  atmdat.CaseBWlHeI.push_back( sp->trans(ipHi,ipLo).WLAng() );
743  else
744  atmdat.CaseBWlHeI.push_back( wl );
745 
746  for( long ipDens = 0; ipDens < NUMDENS; ++ipDens )
747  {
748  if( read_whole_line( chLine, (int)sizeof(chLine) , ioDATA ) == NULL )
749  {
750  fprintf( ioQQQ, " lines_helium could not scan case B lines, current indices: %li %li\n",
751  CaBLines[nelem][line].ipHi,
752  CaBLines[nelem][line].ipLo );
754  }
755 
756  char *chTemp = chLine;
757  j = 1;
758  long den = (long)FFmtRead(chTemp,&j,sizeof(chTemp),&lgEOL);
759  if( den != ipDens + 1 )
760  TotalInsanity();
761  for( long ipTe = 0; ipTe < NUMTEMPS; ++ipTe )
762  {
763  double b;
764  if( (chTemp = strchr_s( chTemp, '\t' )) == NULL )
765  {
766  fprintf( ioQQQ, " lines_helium could not scan case B lines, current indices: %li %li\n",
767  CaBLines[nelem][line].ipHi,
768  CaBLines[nelem][line].ipLo );
770  }
771  ++chTemp;
772  sscanf( chTemp, "%le" , &b );
773  CaBIntensity[nelem][line][ipDens][ipTe] = b;
774  }
775  }
776  line++;
777  }
778 
779  ASSERT( line == NumLines );
780  ASSERT( atmdat.CaseBWlHeI.size() == (unsigned)line );
781 
782  /* close the data file */
783  fclose( ioDATA );
784  return;
785 }
786 
788 STATIC double TempInterp2( double* TempArray , double* ValueArray, long NumElements, double Te )
789 {
790  long int ipTe=-1;
791  double rate = 0.;
792  long i0;
793 
794  DEBUG_ENTRY( "TempInterp2()" );
795 
796  /* te totally unknown */
797  if( Te <= TempArray[0] )
798  {
799  return ValueArray[0] + log10( TempArray[0]/Te );
800  }
801  else if( Te >= TempArray[NumElements-1] )
802  {
803  return ValueArray[NumElements-1];
804  }
805 
806  /* now search for temperature */
807  ipTe = hunt_bisect( TempArray, NumElements, Te );
808 
809  ASSERT( (ipTe >=0) && (ipTe < NumElements-1) );
810 
811  /* Do a four-point interpolation */
812  const int ORDER = 3; /* order of the fitting polynomial */
813  i0 = max(min(ipTe-ORDER/2,NumElements-ORDER-1),0);
814  rate = lagrange( &TempArray[i0], &ValueArray[i0], ORDER+1, Te );
815 
816  return rate;
817 }
818 
820 /* For double-ionization discussions, see Lindsay, Rejoub, & Stebbings 2002 */
821 /* Also read Itza-Ortiz, Godunov, Wang, and McGuire 2001. */
822 STATIC void DoSatelliteLines( long nelem )
823 {
824  long ipISO = ipHE_LIKE;
825 
826  DEBUG_ENTRY( "DoSatelliteLines()" );
827 
828  ASSERT( iso_ctrl.lgDielRecom[ipISO] && dense.lgElmtOn[nelem] );
829 
830  for( long i=0; i < iso_sp[ipISO][nelem].numLevels_max; i++ )
831  {
832  double dr_rate = iso_sp[ipISO][nelem].fb[i].DielecRecomb;
833  TransitionProxy tr = SatelliteLines[ipISO][nelem][ipSatelliteLines[ipISO][nelem][i]];
834 
835  tr.Emis().xObsIntensity() =
836  tr.Emis().xIntensity() =
837  dr_rate * dense.eden * dense.xIonDense[nelem][nelem+1-ipISO] * ERG1CM * tr.EnergyWN();
838  tr.Emis().pump() = 0.;
839 
840  PutLine( tr, "iso satellite line" );
841  }
842 
843  return;
844 }
static double **** CaBIntensity
double wn2ang(double fenergyWN)
FILE * open_data(const char *fname, const char *mode, access_scheme scheme)
Definition: cpu.cpp:765
t_atmdat atmdat
Definition: atmdat.cpp:6
string chIonLbl(const TransitionProxy &t)
Definition: transition.cpp:233
qList st
Definition: iso.h:482
double exp10(double x)
Definition: cddefines.h:1368
const int ipHE_LIKE
Definition: iso.h:65
NORETURN void TotalInsanity(void)
Definition: service.cpp:971
multi_arr< int, 3 > ipSatelliteLines
Definition: taulines.cpp:34
t_isoCTRL iso_ctrl
Definition: iso.cpp:9
const int ipHe2p3P1
Definition: iso.h:49
const int ipHe3d3D
Definition: iso.h:57
bool lgFSM[NISO]
Definition: iso.h:426
const int ipHe2p3P0
Definition: iso.h:48
STATIC void randomize_inten(t_iso_sp *sp, long ipLo, long ipHi)
void set_xIntensity(const TransitionProxy &t)
const int ipHe2s3S
Definition: iso.h:46
realnum & TauTot() const
Definition: emission.h:490
long int nCollapsed_max
Definition: iso.h:518
t_phycon phycon
Definition: phycon.cpp:6
t_LineSave LineSave
Definition: lines.cpp:10
string iso_comment_tran_levels(long ipISO, long nelem, long ipLo, long ipHi)
static double CaBDensities[NUMDENS]
const int ipHe3p3P
Definition: iso.h:56
FILE * ioQQQ
Definition: cddefines.cpp:7
bool lgRandErrGen[NISO]
Definition: iso.h:430
const int ipHe1s1S
Definition: iso.h:43
vector< freeBound > fb
Definition: iso.h:481
double phots(const TransitionProxy &t)
Definition: transition.h:670
static long NumLines
#define IPRAD
Definition: iso.h:88
long hunt_bisect(const T x[], long n, T xval)
Definition: thirdparty.h:303
t_dense dense
Definition: global.cpp:15
t_iso_sp iso_sp[NISO][LIMELM]
Definition: iso.cpp:11
STATIC void insert_trans(vector< TransitionProxy > &trList, TransitionProxy tr)
#define NUMTEMPS
double xIonDense[LIMELM][LIMELM+1]
Definition: dense.h:135
t_trace trace
Definition: trace.cpp:5
#define MALLOC(exp)
Definition: cddefines.h:554
char * read_data_line(char *chLine, int size, FILE *ioDATA)
bool lgPrnIsoCollapsed
Definition: prt.h:195
vector< two_photon > TwoNu
Definition: iso.h:598
long int n_HighestResolved_max
Definition: iso.h:536
#define L_(A_)
Definition: iso.h:23
realnum & EnergyWN() const
Definition: transition.h:477
double & heat() const
Definition: collision.h:224
STATIC realnum get_multiplet_wl(vector< TransitionProxy > &multiHe, long ipHi, long ipLo)
#define STATIC
Definition: cddefines.h:118
bool lgTrace
Definition: trace.h:12
STATIC void multiplet_sum(vector< TransitionProxy > &trList, realnum &av_wl, double &sum_inten, double &sum_obs_inten, double &sum_cool, double &sum_heat)
double & xIntensity() const
Definition: emission.h:540
EmissionList::reference Emis() const
Definition: transition.h:447
LinSv * linadd(double xEmiss, realnum wavelength, const char *chLab, char chInfo, const char *chComment)
#define N_(A_)
Definition: iso.h:22
long int ipHi
void lines_helium(void)
long & ipCont() const
Definition: transition.h:489
bool lgDielRecom[NISO]
Definition: iso.h:385
float realnum
Definition: cddefines.h:124
#define EXIT_FAILURE
Definition: cddefines.h:168
realnum WavLNorm
Definition: lines.h:105
long max(int a, long b)
Definition: cddefines.h:817
const int ipHe3s3S
Definition: iso.h:54
#define cdEXIT(FAIL)
Definition: cddefines.h:482
long min(int a, long b)
Definition: cddefines.h:762
const char * strchr_s(const char *s, int c)
Definition: cddefines.h:1310
vector< vector< TransitionList > > SatelliteLines
Definition: taulines.cpp:35
#define CASEBMAGIC
Definition: helike.h:26
STATIC void PutLineSum(const TransitionProxy &tr, const realnum av_wl, const double sumxInt, const double sumxObsInt, const double sumcool, const double sumheat, const char *chComment)
STATIC void GetStandardHeLines(void)
static bool lgFirstRun
multi_arr< long, 3 > QuantumNumbers2Index
Definition: iso.h:490
t_prt prt
Definition: prt.cpp:14
bool lgElmtOn[LIMELM]
Definition: dense.h:160
TransitionProxy trans(const long ipHi, const long ipLo)
Definition: iso.h:473
Definition: iso.h:470
double lagrange(const double x[], const double y[], long n, double xval)
STATIC void DoSatelliteLines(long nelem)
multi_arr< extra_tr, 2 > ex
Definition: iso.h:478
void PutLine(const TransitionProxy &t, const char *chComment, const char *chLabelTemp, const ExtraInten &extra)
Definition: transition.cpp:318
#define ASSERT(exp)
Definition: cddefines.h:613
const int ipH_LIKE
Definition: iso.h:64
static double CaBTemps[NUMTEMPS]
const int LIMELM
Definition: cddefines.h:308
const int ipHe2p3P2
Definition: iso.h:50
CollisionProxy Coll() const
Definition: transition.h:463
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:723
double & cool() const
Definition: collision.h:220
const int ipHELIUM
Definition: cddefines.h:350
#define NUMDENS
double linint(const double x[], const double y[], long n, double xval)
double eden
Definition: dense.h:201
int fprintf(const Output &stream, const char *format,...)
Definition: service.cpp:1121
#define chLine_LENGTH
double & xObsIntensity() const
Definition: emission.h:545
static stdLines ** CaBLines
vector< realnum > CaseBWlHeI
Definition: atmdat.h:358
long int numLevels_max
Definition: iso.h:524
char * read_whole_line(char *chLine, int nChar, FILE *ioIN)
Definition: service.cpp:70
STATIC double TempInterp2(double *TempArray, double *ValueArray, long NumElements, double Te)
#define fixit(a)
Definition: cddefines.h:417
double te
Definition: phycon.h:21
const int ipHYDROGEN
Definition: cddefines.h:349
realnum & WLAng() const
Definition: transition.h:468
realnum & TauIn() const
Definition: emission.h:470
long int ipass
Definition: lines.h:96
long int ipLo
double FFmtRead(const char *chCard, long int *ipnt, long int last, bool *lgEOL)
Definition: service.cpp:381
long int StuffComment(const char *chComment)
Definition: prt_final.cpp:1938
double & pump() const
Definition: emission.h:530