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-2017 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  tpe_comment = "";
186  if( LineSave.ipass == 0 )
187  {
188  string comment_trans = iso_comment_tran_levels( ipISO, nelem, ipHe1s1S, ipHe2s1S );
189  tpc_comment = " two photon continuum, " + comment_trans;
190  tpe_comment = " induced two photon emission, " + comment_trans;
191  }
192 
193  linadd( tnu->AulTotal * tnu->E2nu * EN1RYD * (*tnu->Pop),
194  0, chLabel.c_str(), 'r', tpc_comment.c_str() );
195 
196  linadd( tnu->induc_dn * tnu->E2nu * EN1RYD * (*tnu->Pop),
197  22, chLabel.c_str() ,'i', tpe_comment.c_str() );
198  }
199 
200  /* here we will create an entry for the three lines
201  * coming from 2 3P to 1 1S - the entry called TOTL will
202  * appear before the lines of the multiplet */
203 
204  for( long i=ipHe2p3P0; i <= ipHe2p3P2; i++ )
205  {
206  set_xIntensity( sp->trans(i, ipHe1s1S) );
207  insert_trans( multiplet, sp->trans(i, ipHe1s1S) );
208  if( nelem == ipHELIUM )
209  HeTrList.push_back( sp->trans(i, ipHe1s1S) );
210  }
211 
212  multiplet_sum( multiplet, av_wl, sumxInt, sumxObsInt, sumcool, sumheat );
213  multiplet.resize( 0 );
214 
215  linadd(sumxObsInt, sp->trans(ipHe2p3P1,ipHe1s1S).WLAng(), "TOTL", 'i',
216  " total emission in He-like intercombination lines from 2p3P to ground ");
217 
218 
219  /* set number of levels we want to print, first is default,
220  * only print real levels, second is set with "print line
221  * iso collapsed" command */
222  long int nLoop = sp->numLevels_max - sp->nCollapsed_max;
223  if( prt.lgPrnIsoCollapsed )
224  nLoop = sp->numLevels_max;
225 
226  /* now do real permitted lines */
227  /* NB NB - low and high must be in this order so that all balmer, paschen,
228  * etc series line up correctly in final printout */
229  /* >>chng 01 jun 13, bring 23P lines back together */
230  for( long ipLo=0; ipLo < ipHe2p3P0; ipLo++ )
231  {
232  vector<long> EnterTheseLast;
233  for( long ipHi=ipLo+1; ipHi < nLoop; ipHi++ )
234  {
235  /* >>chng 01 may 30, do not add fake he-like lines (majority) to line stack */
236  /* >>chng 01 dec 11, use variable for smallest A */
237  if( sp->trans(ipHi,ipLo).ipCont() < 1 )
238  continue;
239 
240  /* recombine fine-structure lines since the energies are
241  * not resolved anyway. */
242  if( iso_ctrl.lgFSM[ipISO] && ( abs(sp->st[ipHi].l() -
243  sp->st[ipLo].l())==1 )
244  && (sp->st[ipLo].l()>1)
245  && (sp->st[ipHi].l()>1)
246  && ( sp->st[ipHi].n() ==
247  sp->st[ipLo].n() ) )
248  {
249  /* skip if both singlets. */
250  if( (sp->st[ipHi].S()==1)
251  && (sp->st[ipLo].S()==1) )
252  {
253  continue;
254  }
255  else if( (sp->st[ipHi].S()==3)
256  && (sp->st[ipLo].S()==3) )
257  {
258  string comment_trans = "";
259 
260  /* singlet to singlet*/
261  insert_trans( multiplet, sp->trans(ipHi ,ipLo+1) );
262  insert_trans( multiplet, sp->trans(ipHi+1,ipLo+1) );
263  if( nelem == ipHELIUM )
264  {
265  HeTrList.push_back( sp->trans(ipHi , ipLo+1) );
266  HeTrList.push_back( sp->trans(ipHi+1, ipLo+1) );
267  }
268  if( multiplet.size() == 0 ) continue;
269 
270  multiplet_sum( multiplet, av_wl, sumxInt, sumxObsInt, sumcool, sumheat );
271  multiplet.resize( 0 );
272 
273  if( LineSave.ipass == 0 )
274  {
275  comment_trans = "singlet to singlet: ";
276  comment_trans += iso_comment_tran_levels( ipISO, nelem, ipLo+1, ipHi );
277  comment_trans += "; ";
278  comment_trans += iso_comment_tran_levels( ipISO, nelem, ipLo+1, ipHi+1 );
279  }
280  PutLineSum( sp->trans(ipHi+1,ipLo+1),
281  sp->trans(ipHi+1,ipLo+1).WLAng(), sumxInt, sumxObsInt, sumcool, sumheat,
282  comment_trans.c_str() );
283 
284  /* triplet to triplet */
285  insert_trans( multiplet, sp->trans(ipHi ,ipLo) );
286  insert_trans( multiplet, sp->trans(ipHi+1,ipLo) );
287  if( nelem == ipHELIUM )
288  {
289  HeTrList.push_back( sp->trans(ipHi , ipLo) );
290  HeTrList.push_back( sp->trans(ipHi+1, ipLo) );
291  }
292  if( multiplet.size() == 0 ) continue;
293 
294  multiplet_sum( multiplet, av_wl, sumxInt, sumxObsInt, sumcool, sumheat );
295  multiplet.resize( 0 );
296 
297  if( LineSave.ipass == 0 )
298  {
299  comment_trans = "triplet to triplet: ";
300  comment_trans += iso_comment_tran_levels( ipISO, nelem, ipLo, ipHi );
301  comment_trans += "; ";
302  comment_trans += iso_comment_tran_levels( ipISO, nelem, ipLo, ipHi+1 );
303  }
304  PutLineSum( sp->trans(ipHi,ipLo),
305  sp->trans(ipHi,ipLo).WLAng(), sumxInt, sumxObsInt, sumcool, sumheat,
306  comment_trans.c_str() );
307  }
308  }
309 
310  else if( ipLo==ipHe2s3S && ipHi == ipHe2p3P0 )
311  {
312  /* here we will create an entry for the three lines
313  * coming from 2 3P to 2 3S - the entry called TOTL will
314  * appear before the lines of the multiplet
315  * for He I this is 10830 */
316 
317  for( long i=ipHe2p3P0; i <= ipHe2p3P2; i++ )
318  {
319  set_xIntensity( sp->trans(i, ipLo) );
320 
321  /* >>chng 13-jun-06
322  * correct for isotropic continuum before applying error randomization
323  * to avoid shutting off emission lines (if the correction is applied _after_
324  * the error is computed, it is likely to be higher than the updated intensity)
325  */
326  if( iso_ctrl.lgRandErrGen[ipISO] )
327  {
328  randomize_inten( sp, ipLo, i );
329  }
330 
331  insert_trans( multiplet, sp->trans(i, ipLo) );
332  if( nelem == ipHELIUM )
333  HeTrList.push_back( sp->trans(i, ipLo) );
334  }
335 
336  if( multiplet.size() == 0 ) continue;
337  multiplet_sum( multiplet, av_wl, sumxInt, sumxObsInt, sumcool, sumheat );
338  multiplet.resize( 0 );
339 
340 
341  linadd(sumxObsInt, av_wl, "TOTL", 'i',
342  "total emission in He-like lines, use average of three line wavelengths " );
343 
344 
345  if( nelem == ipHELIUM )
346  {
347  string comment_trans = "";
348  if( LineSave.ipass == 0 )
349  {
350  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipLo, ipHi );
351  }
352  PutLineSum( sp->trans(ipHi,ipLo), av_wl, sumxInt, sumxObsInt, sumcool, sumheat,
353  comment_trans.c_str() );
354  }
355  else
356  {
357  string comment_trans = "";
358  if( LineSave.ipass == 0 )
359  {
360  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipLo, ipHi );
361  }
362  PutLine( sp->trans(ipHi,ipLo), comment_trans.c_str() );
363 
364  /* also add this with the regular label, so it is correctly picked up by assert case-b command */
365  linadd(sumxObsInt, av_wl, chLabel.c_str(), 'i',
366  "total emission in He-like lines, use average of three line wavelengths " );
367  }
368  }
369  else if( ipLo==ipHe2s3S && (ipHi == ipHe2p3P1 || ipHi==ipHe2p3P2) )
370  {
371  /* >>chng 13-aug-01
372  * If He, do nothing.
373  * The transitions have been computed already in the loop above.
374  * N.B. If this block is removed, the transitions will be rentered
375  * by the block below.
376  */
377  if( nelem > ipHELIUM )
378  {
379  string comment_trans = "";
380  if( LineSave.ipass == 0 )
381  {
382  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipLo, ipHi );
383  }
384  PutLine( sp->trans(ipHi,ipLo), comment_trans.c_str() );
385  }
386  }
387  else
388  {
389  set_xIntensity( sp->trans(ipHi,ipLo) );
390 
391  if( iso_ctrl.lgRandErrGen[ipISO] )
392  {
393  randomize_inten( sp, ipLo, ipHi );
394  }
395 
396  if( abs( L_(ipHi) - L_(ipLo) ) != 1 )
397  {
398  EnterTheseLast.push_back( ipHi );
399  continue;
400  }
401 
402  /*
403  fprintf(ioQQQ,"1 loop %li %li %.1f\n", ipLo,ipHi,
404  sp->trans(ipHi,ipLo).WLAng() ); */
405  string comment_trans = "";
406  if( LineSave.ipass == 0 )
407  {
408  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipLo, ipHi );
409  }
410  PutLine( sp->trans(ipHi,ipLo), comment_trans.c_str() );
411 
412  {
413  /* option to print particulars of some line when called
414  * a prettier print statement is near where chSpin is defined below*/
415  enum {DEBUG_LOC=false};
416  if( DEBUG_LOC )
417  {
418  if( nelem==1 && ipLo==0 && ipHi==1 )
419  {
420  fprintf(ioQQQ,"he1 626 %.2e %.2e \n",
421  sp->trans(ipHi,ipLo).Emis().TauIn(),
422  sp->trans(ipHi,ipLo).Emis().TauTot()
423  );
424  }
425  }
426  }
427  }
428  }
429 
430  // enter these lines last because they are generally weaker quadrupole transitions.
431  for( vector<long>::iterator it = EnterTheseLast.begin(); it != EnterTheseLast.end(); it++ )
432  {
433  string comment_trans = "";
434  if( LineSave.ipass == 0 )
435  {
436  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipLo, *it );
437  }
438  PutLine( sp->trans(*it,ipLo), comment_trans.c_str() );
439  }
440  }
441 
442  /* this sum will bring together the three lines going to J levels within 23P */
443  for( long ipHi=ipHe2p3P2+1; ipHi < nLoop; ipHi++ )
444  {
445  for( long ipLo=ipHe2p3P0; ipLo <= ipHe2p3P2; ++ipLo )
446  {
447  if( sp->trans(ipHi,ipLo).ipCont() < 1 )
448  continue;
449 
450  set_xIntensity( sp->trans(ipHi, ipLo) );
451 
452  if( iso_ctrl.lgRandErrGen[ipISO] )
453  {
454  randomize_inten( sp, ipLo, ipHi );
455  }
456 
457  insert_trans( multiplet, sp->trans(ipHi, ipLo) );
458  if( nelem == ipHELIUM )
459  HeTrList.push_back( sp->trans(ipHi, ipLo) );
460  }
461 
462  if( sp->trans(ipHi,ipHe2p3P2).ipCont() < 1 )
463  {
464  multiplet.resize( 0 );
465  continue;
466  }
467 
468  if( multiplet.size() == 0 ) continue;
469  multiplet_sum( multiplet, av_wl, sumxInt, sumxObsInt, sumcool, sumheat );
470  multiplet.resize( 0 );
471 
472  string comment_trans = "";
473  if( LineSave.ipass == 0 )
474  {
475  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipHe2p3P2, ipHi );
476  }
477  PutLineSum( sp->trans(ipHi,ipHe2p3P2), av_wl, sumxInt, sumxObsInt, sumcool, sumheat,
478  comment_trans.c_str() );
479  }
480  for( long ipLo=ipHe2p3P2+1; ipLo < nLoop-1; ipLo++ )
481  {
482  vector<long> EnterTheseLast;
483  for( long ipHi=ipLo+1; ipHi < nLoop; ipHi++ )
484  {
485  /* skip non-radiative lines */
486  if( sp->trans(ipHi,ipLo).ipCont() < 1 )
487  continue;
488 
489  set_xIntensity( sp->trans(ipHi,ipLo) );
490 
491  if( iso_ctrl.lgRandErrGen[ipISO] )
492  {
493  randomize_inten( sp, ipLo, ipHi );
494  }
495 
496  if( N_(ipHi) > sp->n_HighestResolved_max || abs( L_(ipHi) - L_(ipLo) ) != 1 )
497  {
498  EnterTheseLast.push_back( ipHi );
499  continue;
500  }
501 
502  string comment_trans = "";
503  if( LineSave.ipass == 0 )
504  {
505  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipLo, ipHi );
506  }
507  PutLine(sp->trans(ipHi,ipLo), comment_trans.c_str() );
508  }
509 
510  // enter these lines last because they are generally weaker quadrupole transitions.
511  for( vector<long>::iterator it = EnterTheseLast.begin(); it != EnterTheseLast.end(); it++ )
512  {
513  string comment_trans = "";
514  if( LineSave.ipass == 0 )
515  {
516  comment_trans = iso_comment_tran_levels( ipISO, nelem, ipLo, *it );
517  }
518  PutLine( sp->trans(*it,ipLo), comment_trans.c_str() );
519  }
520  }
521 
522  /* Now put the satellite lines in */
523  if( iso_ctrl.lgDielRecom[ipISO] )
524  DoSatelliteLines(nelem);
525 
526  if( nelem == ipHELIUM )
527  {
528  for( long i=0; i< NumLines; i++ )
529  {
530  double intens_at_Te[NUMDENS];
531  for( long ipDens = 0; ipDens < NUMDENS; ++ipDens )
532  intens_at_Te[ipDens] = TempInterp2( CaBTemps, CaBIntensity[nelem][i][ipDens], NUMTEMPS, phycon.te );
533  double intens = linint( CaBDensities, intens_at_Te, NUMDENS, log10_eden );
534  intens = exp10( intens ) * dense.xIonDense[nelem][nelem+1-ipISO]*dense.eden;
535  ASSERT( intens >= 0. );
536 
537  realnum wvlg = get_multiplet_wl( HeTrList, CaBLines[nelem][i].ipHi, CaBLines[nelem][i].ipLo );
538  if( wvlg <= 0.0) wvlg = atmdat.CaseBWlHeI[i];
539  linadd( intens, wvlg, CaBLines[nelem][i].label, 'i', "Case B intensity " );
540  }
541  }
542  }
543  }
544 
545 
546  if( iso_sp[ipHE_LIKE][ipHELIUM].n_HighestResolved_max >= 4 &&
547  ( iso_sp[ipH_LIKE][ipHYDROGEN].n_HighestResolved_max + iso_sp[ipH_LIKE][ipHYDROGEN].nCollapsed_max ) >=8 )
548  {
550  const long ipHe4s3S = iso_sp[ipHE_LIKE][ipHELIUM].QuantumNumbers2Index[4][0][3];
551  const long ipHe4p3P = iso_sp[ipHE_LIKE][ipHELIUM].QuantumNumbers2Index[4][1][3];
552 
553  /* For info only, add the total photon flux in 3889 and 7065,
554  * and 3188, 4713, and 5876. */
555  double photons_3889_plus_7065 =
556  /* to 2p3P2 */
557  phots( sp->trans(ipHe3s3S,ipHe2p3P2) ) +
558  phots( sp->trans(ipHe3d3D,ipHe2p3P2) ) +
559  phots( sp->trans(ipHe4s3S,ipHe2p3P2) ) +
560  /* to 2p3P1 */
561  phots( sp->trans(ipHe3s3S,ipHe2p3P1) ) +
562  phots( sp->trans(ipHe3d3D,ipHe2p3P1) ) +
563  phots( sp->trans(ipHe4s3S,ipHe2p3P1) ) +
564  /* to 2p3P0 */
565  phots( sp->trans(ipHe3s3S,ipHe2p3P0) ) +
566  phots( sp->trans(ipHe3d3D,ipHe2p3P0) ) +
567  phots( sp->trans(ipHe4s3S,ipHe2p3P0) ) +
568  /* to 2s3S */
569  phots( sp->trans(ipHe3p3P,ipHe2s3S) ) +
570  phots( sp->trans(ipHe4p3P,ipHe2s3S) ) ;
571 
572  long upperIndexofH8 = iso_sp[ipH_LIKE][ipHYDROGEN].QuantumNumbers2Index[8][1][2];
573 
574  /* Add in photon flux of H8 3889 */
575  photons_3889_plus_7065 +=
576  phots( iso_sp[ipH_LIKE][ipHYDROGEN].trans(upperIndexofH8,1) );
577 
578  /* now multiply by ergs of normalization line, so that relative flux of
579  * this line will be ratio of photon fluxes. */
580  if( LineSave.WavLNorm > 0 )
581  photons_3889_plus_7065 *= (ERG1CM*1.e8)/LineSave.WavLNorm;
582  linadd( photons_3889_plus_7065, 3889., "Pho+", 'i',
583  "photon sum given in Porter et al. 2007 (astro-ph/0611579)");
584  }
585 
586  /* ====================================================
587  * end helium
588  * ====================================================*/
589 
590  if( trace.lgTrace )
591  {
592  fprintf( ioQQQ, " lines_helium returns\n" );
593  }
594  return;
595 }
596 
597 // Searches through for next non-comment line, returns NULL on failure
598 inline char* read_data_line( char *chLine, int size, FILE *ioDATA )
599 {
600  char *b;
601  do
602  {
603  b = read_whole_line( chLine , size , ioDATA );
604  if ( b == NULL)
605  break;
606  }
607  while (chLine[0] == '#');
608  return b;
609 }
610 
612 {
613  FILE *ioDATA;
614  bool lgEOL;
615  long i, i1, i2;
616 
617 # define chLine_LENGTH 1000
618  char chLine[chLine_LENGTH];
619 
620  DEBUG_ENTRY( "GetStandardHeLines()" );
621 
622  if( trace.lgTrace )
623  fprintf( ioQQQ," lines_helium opening he1_case_b.dat\n");
624 
625  ioDATA = open_data( "he1_case_b.dat", "r" );
626 
627  /* check that magic number is ok */
628  if( read_whole_line( chLine , (int)sizeof(chLine) , ioDATA ) == NULL )
629  {
630  fprintf( ioQQQ, " lines_helium could not read first line of he1_case_b.dat.\n");
632  }
633  i = 1;
634  /* first number is magic number, second is number of lines in file */
635  i1 = (long)FFmtRead(chLine,&i,sizeof(chLine),&lgEOL);
636  i2 = (long)FFmtRead(chLine,&i,sizeof(chLine),&lgEOL);
637  NumLines = i2;
638 
639  /* the following is to check the numbers that appear at the start of he1_case_b.dat */
640  if( i1 !=CASEBMAGIC )
641  {
642  fprintf( ioQQQ,
643  " lines_helium: the version of he1_case_ab.dat is not the current version.\n" );
644  fprintf( ioQQQ,
645  " lines_helium: I expected to find the number %i and got %li instead.\n" ,
646  CASEBMAGIC, i1 );
647  fprintf( ioQQQ, "Here is the line image:\n==%s==\n", chLine );
649  }
650 
651  /* get the array of densities */
652  if ( read_data_line( chLine , (int)sizeof(chLine) , ioDATA ) == NULL )
653  {
654  fprintf( ioQQQ, " lines_helium could not find line of densities in he1_case_ab.dat.\n");
656  }
657 
658  lgEOL = false;
659  i = 1;
660  for( long j=0; !lgEOL && j < NUMDENS; ++j)
661  {
662  CaBDensities[j] = FFmtRead(chLine,&i,sizeof(chLine),&lgEOL);
663  }
664 
665  /* get the array of temperatures */
666  if ( read_data_line( chLine , (int)sizeof(chLine) , ioDATA ) == NULL )
667  {
668  fprintf( ioQQQ, " lines_helium could not find line of temperatures in he1_case_ab.dat.\n");
670  }
671 
672  lgEOL = false;
673  i = 1;
674  for (long j=0; !lgEOL && j < NUMTEMPS; ++j)
675  {
676  CaBTemps[j] = FFmtRead(chLine,&i,sizeof(chLine),&lgEOL);
677  }
678 
679  /* create space for array of values, if not already done */
680  CaBIntensity = (double ****)MALLOC(sizeof(double ***)*(unsigned)LIMELM );
681  /* create space for array of values, if not already done */
682  CaBLines = (stdLines **)MALLOC(sizeof(stdLines *)*(unsigned)LIMELM );
683 
684  for( long nelem=ipHELIUM; nelem<LIMELM; ++nelem )
685  {
689  if( nelem != ipHELIUM )
690  continue;
691 
692  /* only grab core for elements that are turned on */
693  if( nelem == ipHELIUM || dense.lgElmtOn[nelem])
694  {
695  /* create space for array of values, if not already done */
696  CaBIntensity[nelem] = (double ***)MALLOC(sizeof(double **)*(unsigned)(i2) );
697  CaBLines[nelem] = (stdLines *)MALLOC(sizeof(stdLines )*(unsigned)(i2) );
698 
699  /* avoid allocating 0 bytes, some OS return NULL pointer, PvH
700  CaBIntensity[nelem][0] = NULL;*/
701  for( long j = 0; j < i2; ++j )
702  {
703  CaBIntensity[nelem][j] = (double **)MALLOC(sizeof(double*)*(unsigned)NUMDENS );
704 
705  CaBLines[nelem][j].ipHi = -1;
706  CaBLines[nelem][j].ipLo = -1;
707  strcpy( CaBLines[nelem][j].label , " " );
708 
709  for( long k = 0; k < NUMDENS; ++k )
710  {
711  CaBIntensity[nelem][j][k] = (double *)MALLOC(sizeof(double)*(unsigned)NUMTEMPS );
712  for( long l = 0; l < NUMTEMPS; ++l )
713  {
714  CaBIntensity[nelem][j][k][l] = 0.;
715  }
716  }
717  }
718  }
719  }
720 
721  /* now read in the case B line data */
722  long nelem = ipHELIUM;
723  int line = 0;
724  while( read_data_line( chLine , (int)sizeof(chLine) , ioDATA ) != NULL )
725  {
726  /* only look at lines without '#' in first col */
727  if( (chLine[0] == ' ') || (chLine[0]=='\n') )
728  break;
729 
730  /* get lower and upper level index */
731  long j = 1;
732  // the first number is the wavelength, which is only used if the
733  // model atom is too small to include this transition
734  realnum wl = (realnum)FFmtRead(chLine,&j,sizeof(chLine),&lgEOL);
735  long ipLo = (long)FFmtRead(chLine,&j,sizeof(chLine),&lgEOL);
736  long ipHi = (long)FFmtRead(chLine,&j,sizeof(chLine),&lgEOL);
737  CaBLines[nelem][line].ipLo = ipLo;
738  CaBLines[nelem][line].ipHi = ipHi;
739 
740  ASSERT( CaBLines[nelem][line].ipLo < CaBLines[nelem][line].ipHi );
741 
742  strncpy( CaBLines[nelem][line].label, "Ca B" , 4 );
743  CaBLines[nelem][line].label[4] = 0;
744 
745  t_iso_sp* sp = &iso_sp[ipHE_LIKE][nelem];
746  if( ipHi < sp->numLevels_max - sp->nCollapsed_max )
747  atmdat.CaseBWlHeI.push_back( sp->trans(ipHi,ipLo).WLAng() );
748  else
749  atmdat.CaseBWlHeI.push_back( wl );
750 
751  for( long ipDens = 0; ipDens < NUMDENS; ++ipDens )
752  {
753  if( read_whole_line( chLine, (int)sizeof(chLine) , ioDATA ) == NULL )
754  {
755  fprintf( ioQQQ, " lines_helium could not scan case B lines, current indices: %li %li\n",
756  CaBLines[nelem][line].ipHi,
757  CaBLines[nelem][line].ipLo );
759  }
760 
761  char *chTemp = chLine;
762  j = 1;
763  long den = (long)FFmtRead(chTemp,&j,sizeof(chTemp),&lgEOL);
764  if( den != ipDens + 1 )
765  TotalInsanity();
766  for( long ipTe = 0; ipTe < NUMTEMPS; ++ipTe )
767  {
768  double b;
769  if( (chTemp = strchr_s( chTemp, '\t' )) == NULL )
770  {
771  fprintf( ioQQQ, " lines_helium could not scan case B lines, current indices: %li %li\n",
772  CaBLines[nelem][line].ipHi,
773  CaBLines[nelem][line].ipLo );
775  }
776  ++chTemp;
777  sscanf( chTemp, "%le" , &b );
778  CaBIntensity[nelem][line][ipDens][ipTe] = b;
779  }
780  }
781  line++;
782  }
783 
784  ASSERT( line == NumLines );
785  ASSERT( atmdat.CaseBWlHeI.size() == (unsigned)line );
786 
787  /* close the data file */
788  fclose( ioDATA );
789  return;
790 }
791 
793 STATIC double TempInterp2( double* TempArray , double* ValueArray, long NumElements, double Te )
794 {
795  long int ipTe=-1;
796  double rate = 0.;
797  long i0;
798 
799  DEBUG_ENTRY( "TempInterp2()" );
800 
801  /* te totally unknown */
802  if( Te <= TempArray[0] )
803  {
804  return ValueArray[0] + log10( TempArray[0]/Te );
805  }
806  else if( Te >= TempArray[NumElements-1] )
807  {
808  return ValueArray[NumElements-1];
809  }
810 
811  /* now search for temperature */
812  ipTe = hunt_bisect( TempArray, NumElements, Te );
813 
814  ASSERT( (ipTe >=0) && (ipTe < NumElements-1) );
815 
816  /* Do a four-point interpolation */
817  const int ORDER = 3; /* order of the fitting polynomial */
818  i0 = max(min(ipTe-ORDER/2,NumElements-ORDER-1),0);
819  rate = lagrange( &TempArray[i0], &ValueArray[i0], ORDER+1, Te );
820 
821  return rate;
822 }
823 
825 /* For double-ionization discussions, see Lindsay, Rejoub, & Stebbings 2002 */
826 /* Also read Itza-Ortiz, Godunov, Wang, and McGuire 2001. */
827 STATIC void DoSatelliteLines( long nelem )
828 {
829  long ipISO = ipHE_LIKE;
830 
831  DEBUG_ENTRY( "DoSatelliteLines()" );
832 
833  ASSERT( iso_ctrl.lgDielRecom[ipISO] && dense.lgElmtOn[nelem] );
834 
835  for( long i=0; i < iso_sp[ipISO][nelem].numLevels_max; i++ )
836  {
837  double dr_rate = iso_sp[ipISO][nelem].fb[i].DielecRecomb;
838  TransitionProxy tr = SatelliteLines[ipISO][nelem][ipSatelliteLines[ipISO][nelem][i]];
839 
840  tr.Emis().xObsIntensity() =
841  tr.Emis().xIntensity() =
842  dr_rate * dense.eden * dense.xIonDense[nelem][nelem+1-ipISO] * ERG1CM * tr.EnergyWN();
843  tr.Emis().pump() = 0.;
844 
845  PutLine( tr, "iso satellite line" );
846  }
847 
848  return;
849 }
static double **** CaBIntensity
FILE * open_data(const char *fname, const char *mode, access_scheme scheme)
Definition: cpu.cpp:751
t_atmdat atmdat
Definition: atmdat.cpp:6
string chIonLbl(const TransitionProxy &t)
Definition: transition.cpp:230
qList st
Definition: iso.h:482
double exp10(double x)
Definition: cddefines.h:1383
const int ipHE_LIKE
Definition: iso.h:65
NORETURN void TotalInsanity(void)
Definition: service.cpp:1067
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:478
long int nCollapsed_max
Definition: iso.h:518
t_phycon phycon
Definition: phycon.cpp:6
t_LineSave LineSave
Definition: lines.cpp:9
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:556
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
const int ipHe2s1S
Definition: iso.h:47
#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:528
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:821
const int ipHe3s3S
Definition: iso.h:54
#define cdEXIT(FAIL)
Definition: cddefines.h:484
long min(int a, long b)
Definition: cddefines.h:766
const char * strchr_s(const char *s, int c)
Definition: cddefines.h:1325
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:315
#define ASSERT(exp)
Definition: cddefines.h:617
const int ipH_LIKE
Definition: iso.h:64
static double CaBTemps[NUMTEMPS]
const int LIMELM
Definition: cddefines.h:307
const int ipHe2p3P2
Definition: iso.h:50
CollisionProxy Coll() const
Definition: transition.h:463
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:729
double & cool() const
Definition: collision.h:220
const int ipHELIUM
Definition: cddefines.h:349
#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:1217
#define chLine_LENGTH
double & xObsIntensity() const
Definition: emission.h:533
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:72
STATIC double TempInterp2(double *TempArray, double *ValueArray, long NumElements, double Te)
#define fixit(a)
Definition: cddefines.h:416
double te
Definition: phycon.h:21
const int ipHYDROGEN
Definition: cddefines.h:348
realnum & WLAng() const
Definition: transition.h:468
realnum & TauIn() const
Definition: emission.h:458
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:393
long int StuffComment(const char *chComment)
Definition: prt_final.cpp:1943
double & pump() const
Definition: emission.h:518