cloudy  trunk
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
lines_service.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 /*GetGF convert Einstein A into oscillator strength */
4 /*abscf convert gf into absorption coefficient */
5 /*RefIndex calculates the index of refraction of air using the line energy in wavenumbers,
6  * used to convert vacuum wavelengths to air wavelengths. */
7 /*eina convert a gf into an Einstein A */
8 /*WavlenErrorGet - find difference between two wavelengths */
9 /*linadd enter lines into the line storage array, called once per zone */
10 /*lindst add local line intensity to line luminosity stack */
11 /*PntForLine generate pointer for forbidden line */
12 /*totlin sum total intensity of cooling, recombination, or intensity lines */
13 /*FndLineHt search through line heat arrays to find the strongest heat source */
14 /*ConvRate2CS convert down coll rate back into electron cs in case other parts of code need this for reference */
15 #include "cddefines.h"
16 #include "lines_service.h"
17 #include "dense.h"
18 #include "geometry.h"
19 #include "ipoint.h"
20 #include "lines.h"
21 #include "trace.h"
22 #include "opacity.h"
23 #include "radius.h"
24 #include "rfield.h"
25 #include "rt.h"
26 #include "prt.h"
27 #include "taulines.h"
28 #include "save.h"
29 
31 {
32  DEBUG_ENTRY( "LineStackCreate()" );
33 
34  // set up emission line intensity stack
35  /* there are three types of calls to lines()
36  * ipass = -1, first call, only count number of lines
37  * ipass = 0, second pass, save labels and wavelengths
38  * ipass = 1, integrate intensity*/
39  LineSave.ipass = -1;
40  lines();
41  ASSERT( LineSave.nsum > 0 );
42 
43  /* in a grid or MPI run this may not be the first time here,
44  * return old memory and grab new appropriate for this size,
45  * since number of lines to be stored can change */
46  LineSave.clear();
47 
48  /* this is the large main line array */
50 
51  /* this is only done on first iteration since will integrate over time */
52  for( long i=0; i < LineSave.nsum; i++ )
53  {
54  LineSave.lines[i].SumLineZero();
55  LineSave.lines[i].SumLineZeroAccum();
56  }
57 
58  /* there are three calls to lines()
59  * first call with ipass = -1 was above, only counted number
60  * of lines and malloced space. This is second call and will do
61  * one-time creation of line labels */
62  LineSave.ipass = 0;
63  lines();
64  /* has to be positive */
65  ASSERT( LineSave.nsum > 0);
66  /* in the future calls to lines will result in integrations */
67  LineSave.ipass = 1;
68 
69  if( trace.lgTrace )
70  fprintf( ioQQQ, "%7ld lines printed in main line array\n",
71  LineSave.nsum );
72 }
73 
74 /*eina convert a gf into an Einstein A */
75 double eina(double gf,
76  double enercm,
77  double gup)
78 {
79  double eina_v;
80 
81  DEBUG_ENTRY( "eina()" );
82 
83  /* derive the transition prob, given the following
84  * call to function is gf, energy in cm^-1, g_up
85  * gf is product of g and oscillator strength
86  * eina = ( gf / 1.499e-8 ) / (wl/1e4)**2 / gup */
87  eina_v = (gf/gup)*TRANS_PROB_CONST*POW2(enercm);
88  return( eina_v );
89 }
90 
91 /*GetGF convert Einstein A into oscillator strength */
92 double GetGF(double trans_prob,
93  double enercm,
94  double gup)
95 {
96  double GetGF_v;
97 
98  DEBUG_ENTRY( "GetGF()" );
99 
100  ASSERT( enercm > 0. );
101  ASSERT( trans_prob > 0. );
102  ASSERT( gup > 0.);
103 
104  /* derive the transition prob, given the following
105  * call to function is gf, energy in cm^-1, g_up
106  * gf is product of g and oscillator strength
107  * trans_prob = ( GetGF/gup) / 1.499e-8 / ( 1e4/enercm )**2 */
108  GetGF_v = trans_prob*gup/TRANS_PROB_CONST/POW2(enercm);
109  return( GetGF_v );
110 }
111 
112 /*abscf convert gf into absorption coefficient */
113 double abscf(double gf,
114  double enercm,
115  double gl)
116 {
117  double abscf_v;
118 
119  DEBUG_ENTRY( "abscf()" );
120 
121  ASSERT(gl > 0. && enercm > 0. && gf >= 0.0 );
122 
123  /* derive line absorption coefficient, given the following:
124  * gf, enercm, g_low
125  * gf is product of g and oscillator strength */
126  abscf_v = 1.4974e-6*(gf/gl)*(1e4/enercm);
127  return( abscf_v );
128 }
129 
130 /* compute wavelength in air or vacuum given hardcoded air wavelengths,
131  * option set by parse option PRINT WAVELENGTH VACUUM
132  * this allows hardwired air wavelengths (which should not be there
133  * in the first place) to be converted air/vacuum automatically
134  */
135 realnum wlAirVac( double wlAir )
136 {
137  DEBUG_ENTRY( "wlAirVac()" );
138 
139  // Iterate since EnergyWN depends on wlVac not wlAir but
140  // difference should be small
141  double RefIndex_v = 1.;
142  if( !prt.lgPrintLineAirWavelengths && wlAir > 2000.)
143  {
144  double wlVacuum = wlAir;
145  for( int i=0; i<2; ++i )
146  {
147  /* WN is wavenumber in microns^-1, WN2 is this squared */
148  double WN = 1e4 / wlVacuum;
149  double WN2 = WN*WN;
150 
151  /* use a formula from
152  *>>refer air index refraction Peck & Reeder 1972, JOSA, 62, 8, 958 */
153  RefIndex_v = 1. +
154  1e-8 * (8060.51 + 2480990.0 / (132.274 - WN2) + 17455.7 / (39.32957 - WN2));
155 
156  wlVacuum = wlAir * RefIndex_v;
157  }
158  }
159 
160  return( (realnum)(wlAir * RefIndex_v) );
161 }
162 
163 /*RefIndex calculates the index of refraction of air using the line energy in wavenumbers,
164  * by default for STP air, returns index of refraction in vacuum (1) when
165  * print line vacuum set
166  * used to convert vacuum wavelengths to air wavelengths. */
167 double RefIndex(double EnergyWN )
168 {
169  DEBUG_ENTRY( "RefIndex()" );
170 
171  ASSERT( EnergyWN > 0. );
172 
173  double RefIndex_v = 1.0;
174 
175  /* only do index of refraction if longward of 2000A */
176  if( EnergyWN < 5e4 && prt.lgPrintLineAirWavelengths )
177  {
178  /* xl is wavenumber in microns^-1, squared */
179  double xl = EnergyWN * 1e-4;
180  xl *= xl;
181 
182  /* use a formula from
183  *>>refer air index refraction Peck & Reeder 1972, JOSA, 62, 8, 958 */
184  RefIndex_v += 1e-8 * (8060.51 + 2480990.0 / (132.274 - xl) + 17455.7 / (39.32957 - xl));
185  }
186 
187  ASSERT( RefIndex_v >= 1. );
188  return( RefIndex_v );
189 }
190 
191 /*WavlenErrorGet - given the real wavelength in A for a line
192  * routine will find the error expected between the real
193  * wavelength and the wavelength printed in the output, with 4 sig figs,
194  * function returns difference between exact and 4 sig fig wl, so
195  * we have found correct line is fabs(d wl) < return */
197 {
198  double a;
199  realnum errorwave;
200 
201  DEBUG_ENTRY( "WavlenErrorGet()" );
202 
203  ASSERT( sig_figs <= LineSave.sig_figs_max );
204 
205  if( wavelength > 0. )
206  {
207  /* normal case, positive (non zero) wavelength */
208  a = log10( wavelength+FLT_EPSILON);
209  a = floor(a);
210  }
211  else
212  {
213  /* might be called with wl of zero, this is that case */
214  /* errorwave = 1e-4f; */
215  a = 0.;
216  }
217 
218  errorwave = 5.f * (realnum)exp10( a - (double)sig_figs );
219  return errorwave;
220 }
221 
222 /*linadd enter lines into the line storage array, called once per zone for each line*/
224  double xEmiss, /* xEmiss - local emissivity per unit vol, no fill fac */
225  double xEmissIsoBkg, /* xEmissIsoBkg - local emissivity corrected for isotropic backgrounds per unit vol, no fill fac */
226  realnum wavelength, /* realnum wavelength */
227  const char *chLab,/* string label for ion */
228  // ipnt offset of line in continuum mesh
229  long int ipnt,
230  char chInfo, /* character type of entry for line - given below */
231  /* 'c' cooling, 'h' heating, 'i' info only, 'r' recom line, 't' transferred line */
232  // *chComment string explaining line
233  const char *chComment,
234  // lgAdd says whether we've come in via linadd (true) or lindst (false)
235  bool lgAdd,
236  const TransitionProxy& tr)
237 {
238  DEBUG_ENTRY( "lincom()" );
239 
240  /* main routine to actually enter lines into the line storage array
241  * called at top level within routine lines
242  * called series of times in routine PutLine for lines transferred
243  */
244 
245  /* three values, -1 is just counting, 0 if init, 1 for calculation */
246  if( LineSave.ipass > 0 )
247  {
248  /* not first pass, sum lines only
249  * total emission from vol */
250  /* LineSave.ipass > 0, integration across simulation, sum lines only
251  * emissivity, emission per unit vol, for this zone */
252  LineSave.lines[LineSave.nsum].SumLineAdd(0,xEmissIsoBkg*radius.dVeffAper);
253  /* local emissivity in line */
254  /* integrated intensity or luminosity, the emissivity times the volume */
255  LineSave.lines[LineSave.nsum].emslinSet(0,xEmiss);
256 
257  if (lgAdd)
258  {
259  if (wavelength > 0 && chInfo == 't' )
260  {
261  /* no need to increment or set [1] version since this is called with no continuum
262  * index, don't know what to do */
263  /* only put informational lines, like "Q(H) 4861", in this stack
264  * many continua have a wavelength of zero and are proper intensities,
265  * it would be wrong to predict their transferred intensity */
266  LineSave.lines[LineSave.nsum].emslinThin();
267  LineSave.lines[LineSave.nsum].SumLineThin();
268  }
269  }
270  else
271  {
272  if ( ipnt <= rfield.nflux && chInfo == 't' )
273  {
274  /* emergent_line accounts for destruction by absorption outside
275  * the line-forming region */
276  const double saveemis = emergent_line(
277  xEmiss*rt.fracin , xEmiss*(1.-rt.fracin) , ipnt );
278  LineSave.lines[LineSave.nsum].emslinSet(1,saveemis);
279 
280  const double saveemis_isobkg = emergent_line(
281  xEmissIsoBkg*rt.fracin , xEmissIsoBkg*(1.-rt.fracin) , ipnt );
282  LineSave.lines[LineSave.nsum].SumLineAdd(1,saveemis_isobkg*radius.dVeffAper);
283  }
284  }
285  }
286 
287  else if( LineSave.ipass == 0 )
288  {
289  LineSave.init(LineSave.nsum,(char) chInfo,chComment,chLab,lgAdd,wavelength,tr);
290  if (!lgAdd)
291  {
292  // check that line wavelength and continuum index agree to some extent
293  // this check cannot be very precise because some lines have
294  // "wavelengths" that are set by common usage rather than the correct
295  // wavelength derived from energy and index of refraction of air
296  ASSERT( ipnt > 0 );
297 # ifndef NDEBUG
298  double error = MAX2(0.1*rfield.anu(ipnt-1) , rfield.widflx(ipnt-1) );
299  ASSERT( wavelength<=0 ||
300  fabs( rfield.anu(ipnt-1) - RYDLAM / wavelength) < error );
301 # endif
302  }
303  }
304 
305  /* increment the line counter */
306  ++LineSave.nsum;
307 
308  if (LineSave.ipass == -1)
309  return NULL;
310 
311  return &LineSave.lines[LineSave.nsum-1];
312 
313  /* routine can be called with negative LineSave.ipass, in this case
314  * we are just counting number of lines for current setup */
315 }
316 
317 /*linadd enter lines into the line storage array, called once per zone for each line*/
319  double xEmiss, /* xEmiss - local emissivity per unit vol, no fill fac */
320  realnum wavelength, /* realnum wavelength */
321  const char *chLab,/* string label for ion */
322  char chInfo, /* character type of entry for line - given below */
323  /* 'c' cooling, 'h' heating, 'i' info only, 'r' recom line, 't' transferred line */
324  const char *chComment )
325 {
326  DEBUG_ENTRY( "linadd()" );
327 
328  // Values added to get common interface with lindst
329  const long int ipnt = LONG_MAX;
330 
331  return lincom( xEmiss, xEmiss, wavelength, chLab, ipnt, chInfo, chComment, true, TransitionProxy() );
332 }
333 
334 
335 /*emergent_line find emission from surface of cloud after correcting for
336  * extinction due to continuous opacity for inward & outward directed emission */
338  /* emiemission in inward direction */
339  double emissivity_in ,
340  /* emission in outward direction */
341  double emissivity_out ,
342  /* array index for continuum frequency on fortran scale */
343  long int ipCont )
344 {
345  double emergent_in , emergent_out;
346  long int i = ipCont-1;
347 
348  DEBUG_ENTRY( "emergent_line()" );
349 
350  ASSERT( i >= 0 && i < rfield.nflux_with_check-1 );
351 
352  /* do nothing if first iteration since we do not know the outward-looking
353  * optical depths. In version C07.02.00 we assumed an infinite optical
354  * depth in the outward direction, which would be appropriate for a
355  * HII region on the surface of a molecular cloud. This converged onto
356  * the correct solution in later iterations, but on the first iteration
357  * this underestimated total emission if the infinite cloud were not
358  * present. With C07.02.01 we make no assuptions about what is in the
359  * outward direction and simply use the local emission.
360  * Behavior is unchanged on later iterations */
361  if( iteration == 1 )
362  {
363  /* first iteration - do not know outer optical depths so assume very large
364  * optical depths */
365  emergent_in = emissivity_in*opac.E2TauAbsFace[i];
366  emergent_out = emissivity_out;
367  }
368  else
369  {
370  if( geometry.lgSphere )
371  {
372  /* second or later iteration in closed or spherical geometry */
373  /* inwardly directed emission must get to central hole then across entire
374  * far side of shell */
375  emergent_in = emissivity_in * opac.E2TauAbsFace[i] *opac.E2TauAbsTotal[i];
376 
377  /* E2 is outwardly directed emission to get to outer edge of cloud */
378  emergent_out = emissivity_out * opac.E2TauAbsOut[i];
379  }
380  else
381  {
382  /* open geometry in second or later iteration, outer optical depths are known
383  * this is light emitted into the outer direction and backscattered
384  * into the inner */
385  double reflected = emissivity_out * opac.albedo[i] * (1.-opac.E2TauAbsOut[i]);
386  /* E2 is to get to central hole */
387  emergent_in = (emissivity_in + reflected) * opac.E2TauAbsFace[i];
388  /* E2 is to get to outer edge */
389  emergent_out = (emissivity_out - reflected) * opac.E2TauAbsOut[i];
390  }
391  }
392  /* return the net emission that makes it to the surface */
393  return( emergent_in + emergent_out );
394 }
395 
396 /* outline_base - calls outline_base_bin after deciding whether to add impulse or resolved line */
397 void outline_base(double dampXvel, double damp, bool lgTransStackLine, long int ip, double phots, realnum inwd,
398  double nonScatteredFraction)
399 {
400  DEBUG_ENTRY( "outline_base()" );
401 
402  // Need to consider finite optical depth effects if this option is
403  // enabled (see fixit below)
404  static const bool DO_PROFILE = false;
405 
406  if( !DO_PROFILE || !rfield.lgDoLineTrans || dampXvel == 0.0 )
407  {
408  outline_base_bin(lgTransStackLine, ip, phots, inwd, nonScatteredFraction);
409  }
410  else
411  {
412  ASSERT( damp > 0. );
413  double LineWidth = dampXvel/damp;
414  LineWidth = MIN2( 0.1 * SPEEDLIGHT, LineWidth );
415  double sigma = (LineWidth/SPEEDLIGHT);
416  long ip3SigmaRed = ipoint( MAX2( rfield.emm(), rfield.anu(ip) - 3.*sigma*rfield.anu(ip) ) );
417  long ip3SigmaBlue = ipoint( MIN2( rfield.egamry(), rfield.anu(ip) + 3.*sigma*rfield.anu(ip) ) );
418  ASSERT( ip3SigmaBlue >= ip3SigmaRed );
419  long numBins = ip3SigmaBlue - ip3SigmaRed + 1;
420 
421  if( numBins < 3 )
422  {
423  outline_base_bin(lgTransStackLine, ip, phots, inwd, nonScatteredFraction);
424  }
425  else
426  {
427  valarray<realnum> x(numBins);
428  valarray<realnum> profile(numBins);
429 
430  for( long ipBin=ip3SigmaRed; ipBin<=ip3SigmaBlue; ipBin++ )
431  x[ipBin] = (rfield.anu(ip) - rfield.anu(ipBin))/rfield.anu(ip)/sigma;
432  fixit("Escape from zone will be dominated by line wings at"
433  " finite optical depth, so Voigt profile may be incorrect");
434  // this profile must have unit normalization to conserve energy -> use U(a,v)
435  VoigtU(damp,get_ptr(x),get_ptr(profile),numBins);
436 
437  for( long ipBin=ip3SigmaRed; ipBin<=ip3SigmaBlue; ipBin++ )
438  outline_base_bin(lgTransStackLine, ipBin, phots*profile[ipBin-ip3SigmaRed], inwd, nonScatteredFraction);
439  }
440  }
441 }
442 
443 /*outline_base_bin - adds line photons to bins of reflin and outlin */
444 void outline_base_bin(bool lgTransStackLine, long int ip, double phots, realnum inwd,
445  double nonScatteredFraction)
446 {
447  DEBUG_ENTRY( "outline_base_bin()" );
448 
449  if (lgTransStackLine)
450  {
452  (realnum)phots;
453 
454  /* the reflected part */
455  rfield.reflin[0][ip] +=
456  (realnum)(inwd*phots*radius.BeamInIn);
457 
458  /* inward beam that goes out since sphere set */
459  rfield.outlin[0][ip] +=
460  (realnum)(inwd*phots*radius.BeamInOut*opac.tmn[ip]*nonScatteredFraction);
461 
462  /* outward part */
463  rfield.outlin[0][ip] +=
464  (realnum)((1.-inwd)*phots*radius.BeamOutOut*opac.tmn[ip]*nonScatteredFraction);
465  }
466  else
467  {
468  rfield.reflin[0][ip] +=
469  (realnum)(phots*radius.dVolReflec);
470 
471  rfield.outlin[0][ip] +=
472  (realnum)(phots*radius.dVolOutwrd*opac.ExpZone[ip]);
473  }
474 }
475 
476 /*lindst add line with destruction and outward */
477 static void lindst1(
478  double dampXvel,
479  double damp,
480  // xEmiss - local emissivity per unit vol
481  double xEmiss,
482  double xEmissIsoBkg, /* xEmissIsoBkg - local emissivity corrected for isotropic backgrounds per unit vol, no fill fac */
483  // wavelength of line in Angstroms
485  // *chLab string label for ion
486  const char *chLab,
487  // ipnt offset of line in continuum mesh
488  long int ipnt,
489  // chInfo character type of entry for line - 'c' cooling, 'h' heating, 'i' info only, 'r' recom line, 't' transferred line
490  char chInfo,
491  // lgOutToo should line be included in outward beam?
492  bool lgOutToo,
493  // *chComment string explaining line
494  const char *chComment,
495  const TransitionProxy& tr )
496 {
497  DEBUG_ENTRY( "lindst1()" );
498 
499  // do not add information lines to outward beam
500  ASSERT( !lgOutToo || chInfo!='i' );
501 
502  lincom(xEmiss, xEmissIsoBkg, wavelength, chLab, ipnt, chInfo, chComment, false, tr );
503 
504  if( LineSave.ipass > 0 )
505  {
506  /* >>chng 06 feb 08, add test on xEmiss positive, no need to evaluate
507  * for majority of zero */
508  if (lgOutToo && xEmiss > 0.)
509  {
510  /* add line to outward beam
511  * there are lots of lines that are sums of other lines, or
512  * just for info of some sort. These have flag lgOutToo false.
513  * Note that the EnergyRyd variable only has a rational
514  * value if PntForLine was called just before this routine - in all
515  * cases where this did not happen the flag is false. */
516  const bool lgTransStackLine = false;
517  const long int ip = ipnt - 1;
518  const double phots = xEmiss/(rfield.anu(ipnt-1)*EN1RYD);
519  const realnum inwd = (realnum)(1.0-(1.+geometry.covrt)/2.);
520  const double nonScatteredFraction = 1.;
521 
522  outline_base(dampXvel, damp, lgTransStackLine, ip, phots, inwd, nonScatteredFraction);
523  }
524  }
525 }
526 
527 /*lindst add line with destruction and outward */
528 void lindst(
529  // xEmiss - local emissivity per unit vol
530  double xEmiss,
531  // wavelength of line in Angstroms
533  // *chLab string label for ion
534  const char *chLab,
535  // ipnt offset of line in continuum mesh
536  long int ipnt,
537  // chInfo character type of entry for line - 'c' cooling, 'h' heating, 'i' info only, 'r' recom line, 't' transferred line
538  char chInfo,
539  // lgOutToo should line be included in outward beam?
540  bool lgOutToo,
541  // *chComment string explaining line
542  const char *chComment )
543 {
544  DEBUG_ENTRY( "lindst()" );
545  lindst(0.0, 0.0, xEmiss, wavelength, chLab, ipnt, chInfo, lgOutToo, chComment);
546 }
547 
548 /*lindst add line with destruction and outward */
549 void lindst(
550  double dampXvel,
551  double damp,
552  // xEmiss - local emissivity per unit vol
553  double xEmiss,
554  // wavelength of line in Angstroms
556  // *chLab string label for ion
557  const char *chLab,
558  // ipnt offset of line in continuum mesh
559  long int ipnt,
560  // chInfo character type of entry for line - 'c' cooling, 'h' heating, 'i' info only, 'r' recom line, 't' transferred line
561  char chInfo,
562  // lgOutToo should line be included in outward beam?
563  bool lgOutToo,
564  // *chComment string explaining line
565  const char *chComment )
566 {
567  DEBUG_ENTRY( "lindst()" );
568  lindst1(dampXvel,damp,xEmiss,xEmiss,wavelength,chLab,ipnt,chInfo,lgOutToo,chComment,TransitionProxy());
569 }
570 
571 /*lindst add line with destruction and outward */
572 void lindst(
573  const TransitionProxy& t,
574  const ExtraInten& extra,
575  // *chLab string label for ion
576  const char *chLab,
577  // chInfo character type of entry for line - 'c' cooling, 'h' heating, 'i' info only, 'r' recom line, 't' transferred line
578  char chInfo,
579  // lgOutToo should line be included in outward beam?
580  bool lgOutToo,
581  // *chComment string explaining line
582  const char *chComment)
583 {
584  DEBUG_ENTRY( "lindst()" );
585 
586  // H2O 212.468m
587  if (0 && LineSave.ipass > 0)
588  if (strncmp(LineSave.lines[LineSave.nsum].chALab(),"H2O ",4) == 0 &&
589  fabs(LineSave.lines[LineSave.nsum].wavelength()-212.468e4) < 1e4)
590  fprintf(ioQQQ,"DEBUG lindst: %ld %4ld %15.8e %15.8e %15.8e %15.8e %15.8e\n",
592  phots( t ),(*t.Hi()).Pop(),t.Emis().Pesc_total());
593  lindst1(t.Emis().dampXvel(),
594  t.Emis().damp(),
595  t.Emis().xIntensity()+extra.v,
596  t.Emis().xObsIntensity()+extra.v,
597  t.WLAng(), chLab, t.ipCont(), chInfo, lgOutToo, chComment, t );
598 
599 }
600 
601 /*PntForLine generate pointer for forbidden line */
603  /* wavelength of transition in Angstroms */
604  double wavelength,
605  /* label for this line */
606  const char *chLabel,
607  /* this is array index on the f, not c scale,
608  * for the continuum cell holding the line */
609  long int *ipnt)
610 {
611  /*
612  * maximum number of forbidden lines - this is a good bet since
613  * new lines do not go into this group, and lines are slowly
614  * moving to level 1
615  */
616  const int MAXFORLIN = 1000;
617  static long int ipForLin[MAXFORLIN]={0};
618 
619  /* number of forbidden lines entered into continuum array */
620  static long int nForLin;
621 
622  DEBUG_ENTRY( "PntForLine()" );
623 
624  /* must be 0 or greater */
625  ASSERT( wavelength >= 0. );
626 
627  if( wavelength == 0. )
628  {
629  /* zero is special flag to initialize */
630  nForLin = 0;
631  }
632  else
633  {
634 
635  if( LineSave.ipass > 0 )
636  {
637  /* not first pass, sum lines only */
638  *ipnt = ipForLin[nForLin];
639  }
640  else if( LineSave.ipass == 0 )
641  {
642  /* check if number of lines in arrays exceeded */
643  if( nForLin >= MAXFORLIN )
644  {
645  fprintf( ioQQQ, "PROBLEM %5ld lines is too many for PntForLine.\n",
646  nForLin );
647  fprintf( ioQQQ, " Increase the value of maxForLine everywhere in the code.\n" );
649  }
650 
651  /* ipLineEnergy will only put in line label if nothing already there */
652  const double EnergyRyd = RYDLAM/wavelength;
653  ipForLin[nForLin] = ipLineEnergy(EnergyRyd,chLabel , 0);
654  *ipnt = ipForLin[nForLin];
655  }
656  else
657  {
658  /* this is case where we are only counting lines */
659  *ipnt = 0;
660  }
661  ++nForLin;
662  }
663  return;
664 }
665 
666 /*ConvRate2CS convert down coll rate back into electron cs in case other parts of code need this for reference */
667 double ConvRate2CS( realnum gHi , realnum rate )
668 {
669 
670  double cs;
671 
672  DEBUG_ENTRY( "ConvRate2CS()" );
673 
674  /* return is collision strength, convert from collision rate from
675  * upper to lower, this assumes pure electron collisions, but that will
676  * also be assumed by anything that uses cs, for self-consistency */
677  cs = rate * gHi / dense.cdsqte;
678 
679  /* change assert to non-negative - there can be cases (Iin H2) where cs has
680  * underflowed to 0 on some platforms */
681  ASSERT( cs >= 0. );
682  return cs;
683 }
684 
685 /*ConvCrossSect2CollStr convert collisional deexcitation cross section for into collision strength */
686 double ConvCrossSect2CollStr( double CrsSectCM2, double gLo, double E_ProjectileRyd, double reduced_mass_grams )
687 {
688  // See e.g. Burgess & Tully 1992 A&A 254, 436, S2.1
689  double CollisionStrength;
690 
691  DEBUG_ENTRY( "ConvCrossSect2CollStr()" );
692 
693  if( ! ( CrsSectCM2 >= 0. && gLo >= 0. && E_ProjectileRyd >= 0. && reduced_mass_grams >= 0. ) )
694  {
695  fprintf( ioQQQ, "invalid parameter for ConvCrossSect2CollStr\n" );
697  }
698 
699  CollisionStrength = CrsSectCM2 * gLo * E_ProjectileRyd / (PI*BOHR_RADIUS_CM*BOHR_RADIUS_CM);
700 
701  // this part is being tested.
702 #if 1
703  CollisionStrength *= reduced_mass_grams / ELECTRON_MASS;
704 #endif
705 
706  ASSERT( CollisionStrength >= 0. );
707  return CollisionStrength;
708 }
709 
710 /*totlin sum total intensity of cooling, recombination, or intensity lines */
711 double totlin(
712  /* chInfor is 1 char,
713  'i' information,
714  'r' recombination or
715  'c' collision */
716  int chInfo)
717 {
718  long int i;
719  double totlin_v;
720 
721  DEBUG_ENTRY( "totlin()" );
722 
723  /* routine goes through set of entered line
724  * intensities and picks out those which have
725  * types agreeing with chInfo. Valid types are
726  * 'c', 'r', and 'i'
727  *begin sanity check */
728  if( (chInfo != 'i' && chInfo != 'r') && chInfo != 'c' )
729  {
730  fprintf( ioQQQ, " TOTLIN does not understand chInfo=%c\n",
731  chInfo );
733  }
734  /*end sanity check */
735 
736  /* now find sum of lines of type chInfo */
737  totlin_v = 0.;
738  for( i=0; i < LineSave.nsum; i++ )
739  {
740  if( LineSave.lines[i].chSumTyp() == chInfo )
741  {
742  totlin_v += LineSave.lines[i].SumLine(0);
743  }
744  }
745  return( totlin_v );
746 }
747 
748 
749 /*FndLineHt search through line heat arrays to find the strongest heat source */
750 const TransitionProxy FndLineHt(long int *level)
751 {
752  TransitionProxy t;
753  DEBUG_ENTRY( "FndLineHt()" );
754 
755  double Strong = -1.;
756  *level = 0;
757 
758  /* now do the level 2 lines */
759  for( long i=0; i < nWindLine; i++ )
760  {
761  if( (*TauLine2[i].Hi()).IonStg() < (*TauLine2[i].Hi()).nelem()+1-NISO )
762  {
763  /* check if a line was the major heat agent */
764  if( TauLine2[i].Coll().heat() > Strong )
765  {
766  *level = 2;
767  t = TauLine2[i];
768  Strong = TauLine2[i].Coll().heat();
769  }
770  }
771  }
772 
773  /* now do the hyperfine structure lines */
774  for( size_t i=0; i < HFLines.size(); i++ )
775  {
776  /* check if a line was the major heat agent */
777  if( HFLines[i].Coll().heat() > Strong )
778  {
779  *level = 3;
780  t = HFLines[i];
781  Strong = HFLines[i].Coll().heat();
782  }
783  }
784 
785  /* lines from external databases */
786  for (int ipSpecies=0; ipSpecies < nSpecies; ++ipSpecies)
787  {
788  for( EmissionList::iterator em=dBaseTrans[ipSpecies].Emis().begin();
789  em != dBaseTrans[ipSpecies].Emis().end(); ++em)
790  {
791  /* check if a line was the major heat agent */
792  if( (*em).Tran().Coll().heat() > Strong )
793  {
794  *level = 4;
795  t = (*em).Tran();
796  Strong = t.Coll().heat();
797  }
798  }
799  }
800 
801  fixit("all other line stacks need to be included here.");
802  // can we just sweep over line stack? Is that ready yet?
803 
804  ASSERT( t.associated() );
805  return t;
806 }
807 
808 
809 
810 /*set_xIntensity: compute gross and net number of emitted line photons */
812 {
813  DEBUG_ENTRY( "set_xIntensity()" );
814 
815  t.Emis().xIntensity() =
816  t.Emis().xObsIntensity() = 0.;
817 
818  int j = t.ipCont() - 1;
819  if( j < 0 )
820  return;
821 
822  double Pesc = MIN2(1.0, t.Emis().Pesc_total());
823  double nphot = t.Emis().Aul() * Pesc * (*t.Hi()).Pop();
824  nphot = MAX2(0., nphot);
825 
826  t.Emis().xObsIntensity() =
827  t.Emis().xIntensity() = nphot * t.EnergyErg();
828 
829  if( 0 && t.chLabel() == "H 1 1215.67A" )
830  {
831  fprintf(ioQQQ,
832  "\"%s\"\t %10g\t %10g\t %10g\t %10g\t %10g\t %10g\t %10g\n",
833  t.chLabel().c_str(),
834  t.Emis().Aul(),
835  (*t.Hi()).Pop(),
836  (*t.Lo()).Pop(),
837  Pesc,
838  nphot,
839  t.Emis().xIntensity(),
840  t.Emis().xObsIntensity() );
841  }
842 
843  if( ! save.lgSubtrCont )
844  return;
845 
846  double dnphot = t.Emis().Aul() * Pesc * rfield.flux_isotropic[j]*rfield.convoc[j]
847  * ((*t.Hi()).Pop() - (*t.Lo()).Pop() * (*t.Hi()).g() / (*t.Lo()).g());
848 
849  /* Test if emitting a comment in the main output is appropriate. */
850  if( ! LineSave.lgIsoContSubSignif && fabs( dnphot ) > 0.5 * nphot )
852 
853  nphot += dnphot;
854  nphot = MAX2(0., nphot);
855 
856  t.Emis().xObsIntensity() = nphot * t.EnergyErg();
857 
858  return;
859 }
double emm() const
Definition: mesh.h:93
double depth
Definition: radius.h:31
realnum * flux_isotropic
Definition: rfield.h:71
size_t size(void) const
Definition: transition.h:331
realnum EnergyErg() const
Definition: transition.h:90
realnum WavlenErrorGet(realnum wavelength, long sig_figs)
double * albedo
Definition: opacity.h:113
double exp10(double x)
Definition: cddefines.h:1368
T * get_ptr(T *v)
Definition: cddefines.h:1109
double widflx(size_t i) const
Definition: mesh.h:156
t_opac opac
Definition: opacity.cpp:5
realnum * DiffuseLineEmission
Definition: rfield.h:193
double abscf(double gf, double enercm, double gl)
const int NISO
Definition: cddefines.h:311
void clear()
Definition: lines.h:135
double eina(double gf, double enercm, double gup)
void set_xIntensity(const TransitionProxy &t)
double cdsqte
Definition: dense.h:246
void lindst(double xEmiss, realnum wavelength, const char *chLab, long int ipnt, char chInfo, bool lgOutToo, const char *chComment)
TransitionList HFLines("HFLines",&AnonStates)
t_LineSave LineSave
Definition: lines.cpp:10
bool lgPrintLineAirWavelengths
Definition: prt.h:274
double RefIndex(double EnergyWN)
static void lindst1(double dampXvel, double damp, double xEmiss, double xEmissIsoBkg, realnum wavelength, const char *chLab, long int ipnt, char chInfo, bool lgOutToo, const char *chComment, const TransitionProxy &tr)
realnum ** outlin
Definition: rfield.h:189
FILE * ioQQQ
Definition: cddefines.cpp:7
realnum * E2TauAbsTotal
Definition: opacity.h:139
long int nzone
Definition: cddefines.cpp:14
TransitionList TauLine2("TauLine2",&AnonStates)
#define MIN2(a, b)
Definition: cddefines.h:803
double phots(const TransitionProxy &t)
Definition: transition.h:670
double anu(size_t i) const
Definition: mesh.h:120
long int nSpecies
Definition: taulines.cpp:22
vector< LinSv > lines
Definition: lines.h:132
void PntForLine(double wavelength, const char *chLabel, long int *ipnt)
t_dense dense
Definition: global.cpp:15
bool associated() const
Definition: transition.h:54
bool lgIsoContSubSignif
Definition: lines.h:150
double dVolReflec
Definition: radius.h:104
long int nflux_with_check
Definition: rfield.h:49
void lines(void)
Definition: prt_lines.cpp:35
bool lgSphere
Definition: geometry.h:34
long int iteration
Definition: cddefines.cpp:16
void outline_base_bin(bool lgTransStackLine, long int ip, double phots, realnum inwd, double nonScatteredFraction)
t_trace trace
Definition: trace.cpp:5
t_geometry geometry
Definition: geometry.cpp:5
long ipoint(double energy_ryd)
Definition: cont_ipoint.cpp:15
#define POW2
Definition: cddefines.h:979
double ConvRate2CS(realnum gHi, realnum rate)
double & heat() const
Definition: collision.h:224
#define STATIC
Definition: cddefines.h:118
bool lgTrace
Definition: trace.h:12
realnum & dampXvel() const
Definition: emission.h:610
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)
long ipLineEnergy(double energy, const char *chLabel, long ipIonEnergy)
Definition: cont_ipoint.cpp:68
realnum * E2TauAbsOut
Definition: opacity.h:140
t_rfield rfield
Definition: rfield.cpp:9
long & ipCont() const
Definition: transition.h:489
const TransitionProxy FndLineHt(long int *level)
realnum * convoc
Definition: rfield.h:113
float realnum
Definition: cddefines.h:124
#define EXIT_FAILURE
Definition: cddefines.h:168
double BeamInIn
Definition: radius.h:108
void LineStackCreate(void)
qList::iterator Hi() const
Definition: transition.h:435
#define cdEXIT(FAIL)
Definition: cddefines.h:482
realnum covrt
Definition: geometry.h:61
realnum wlAirVac(double wlAir)
realnum fracin
Definition: rt.h:174
long nWindLine
Definition: cdinit.cpp:19
t_radius radius
Definition: radius.cpp:5
double v
Definition: transition.h:629
t_prt prt
Definition: prt.cpp:14
realnum ** reflin
Definition: rfield.h:196
bool lgDoLineTrans
Definition: rfield.h:99
string chLabel() const
Definition: transition.cpp:274
double totlin(int chInfo)
double dVolOutwrd
Definition: radius.h:103
double BeamInOut
Definition: radius.h:111
#define ASSERT(exp)
Definition: cddefines.h:613
qList::iterator Lo() const
Definition: transition.h:431
realnum Pesc_total() const
Definition: emission.h:127
void resize(long nlines)
Definition: lines.h:450
double GetGF(double trans_prob, double enercm, double gup)
void VoigtU(realnum a, const realnum v[], realnum y[], int n)
Definition: thirdparty.h:431
vector< double, allocator_avx< double > > ExpZone
Definition: opacity.h:133
CollisionProxy Coll() const
Definition: transition.h:463
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:723
realnum * E2TauAbsFace
Definition: opacity.h:137
double BeamOutOut
Definition: radius.h:114
void outline_base(double dampXvel, double damp, bool lgTransStackLine, long int ip, double phots, realnum inwd, double nonScatteredFraction)
static vector< realnum > wavelength
double egamry() const
Definition: mesh.h:97
double ConvCrossSect2CollStr(double CrsSectCM2, double gLo, double E_ProjectileRyd, double reduced_mass_grams)
#define MAX2(a, b)
Definition: cddefines.h:824
realnum & damp() const
Definition: emission.h:620
int fprintf(const Output &stream, const char *format,...)
Definition: service.cpp:1121
double emergent_line(double emissivity_in, double emissivity_out, long int ipCont)
double & xObsIntensity() const
Definition: emission.h:545
long int nsum
Definition: lines.h:87
static const long sig_figs_max
Definition: lines.h:110
#define fixit(a)
Definition: cddefines.h:417
realnum * tmn
Definition: opacity.h:149
bool lgSubtrCont
Definition: save.h:312
void init(long index, char chSumTyp, const char *chComment, const char *label, bool lgAdd, realnum wavelength, const TransitionProxy &tr)
Definition: lines.h:436
vector< TransitionList > dBaseTrans
Definition: taulines.cpp:18
t_save save
Definition: save.cpp:5
STATIC LinSv * lincom(double xEmiss, double xEmissIsoBkg, realnum wavelength, const char *chLab, long int ipnt, char chInfo, const char *chComment, bool lgAdd, const TransitionProxy &tr)
long int nflux
Definition: rfield.h:46
realnum & Aul() const
Definition: emission.h:690
Definition: lines.h:157
realnum & WLAng() const
Definition: transition.h:468
long int ipass
Definition: lines.h:96
double dVeffAper
Definition: radius.h:93
t_rt rt
Definition: rt.cpp:5