cloudy  trunk
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
rt_diffuse.cpp
Go to the documentation of this file.
1 /* This file is part of Cloudy and is copyright (C)1978-2013 by Gary J. Ferland and
2  * others. For conditions of distribution and use see copyright notice in license.txt */
3 /*RT_diffuse evaluate local diffuse emission for this zone,
4  * fill in ConEmitLocal[depth][energy] with diffuse emission,
5  * called by Cloudy, this routine adds energy to the outward beam
6  * OTS rates for this zone were set in RT_OTS - not here */
7 #include "cddefines.h"
8 #include "physconst.h"
9 #include "taulines.h"
10 #include "grains.h"
11 #include "grainvar.h"
12 #include "iso.h"
13 #include "dense.h"
14 #include "opacity.h"
15 #include "trace.h"
16 #include "coolheavy.h"
17 #include "rfield.h"
18 #include "phycon.h"
19 #include "hmi.h"
20 #include "radius.h"
21 #include "atmdat.h"
22 #include "heavy.h"
23 #include "atomfeii.h"
24 #include "lines_service.h"
25 #include "h2.h"
26 #include "ipoint.h"
27 #include "rt.h"
28 #include "mole.h"
29 #include "conv.h"
30 
31 #if defined (__ICC) && defined(__ia64) && __INTEL_COMPILER < 910
32 #pragma optimization_level 0
33 #endif
34 void RT_diffuse(void)
35 {
36  /* arrays used in this routine
37  * rfield.ConEmitLocal[depth][energy] local emission per unit vol
38  * rfield.DiffuseEscape is the spectrum of diffuse emission that escapes this zone,
39  * at end of this routine part is thrown into the outward beam
40  * by adding to rfield.ConInterOut
41  * units are photons s-1 cm-3
42  * one-time init done on first call */
43 
44  /* rfield.DiffuseEscape and rfield.ConEmitLocal are same except that
45  * rfield.ConEmitLocal is local emission, would be source function if div by opac
46  * rfield.DiffuseEscape is part that escapes so has RT built into it
47  * rfield.DiffuseEscape is used to define rfield.ConInterOut below as per this statement
48  * rfield.ConInterOut[nu] += rfield.DiffuseEscape[nu]*(realnum)radius.dVolOutwrd;
49  */
50  /* \todo 0 define only rfield.ConEmitLocal as it is now done,
51  * do not define rfield.DiffuseEscape at all
52  * at bottom of this routine use inward and outward optical depths to define
53  * local and escaping parts
54  * this routine only defines
55  * rfield.ConInterOut - set to rfield.DiffuseEscape times vol element
56  * so this is only var that
57  * needs to be set
58  */
59 
60  long int ip=-100000,
61  ipla=-100000,
62  limit=-100000,
63  nu=-10000;
64 
65  double EdenAbund,
66  difflya,
67  fac,
68  factor,
69  gamma,
70  gion,
71  gn,
72  photon;
73 
74  DEBUG_ENTRY( "RT_diffuse()" );
75 
76  /* many arrays were malloced to nupper, and we will add unit flux to [nflux] -
77  8 this must be true to work */
79 
80  /* this routine evaluates the local diffuse fields
81  * it fills in all of the following vectors */
82  memset(rfield.DiffuseEscape , 0 , (unsigned)rfield.nupper*sizeof(realnum) );
83  memset(rfield.ConEmitLocal[nzone] , 0 , (unsigned)rfield.nupper*sizeof(realnum) );
84  memset(rfield.TotDiff2Pht , 0 , (unsigned)rfield.nupper*sizeof(realnum) );
85  memset(rfield.DiffuseLineEmission , 0 , (unsigned)rfield.nupper*sizeof(realnum) );
86 
87  /* must abort after setting all of above to zero because some may be
88  * used in various ways before abort is complete */
89  if( lgAbort )
90  {
91  /* quit if we are aborting */
92  return;
93  }
94 
95  /* loop over iso-sequences of all elements
96  * to add all recombination continua and lines*/
97  for( long ipISO=ipH_LIKE; ipISO<NISO; ++ipISO )
98  {
99  /* >>chng 01 sep 23, rewrote for iso sequences */
100  for( long nelem=ipISO; nelem < LIMELM; nelem++ )
101  {
102  // calculate recombination spectra and cooling
103  RT_iso_integrate_RRC( ipISO, nelem, true );
104 
105  /* the product of the densities of the parent ion and electrons */
106  EdenAbund = dense.eden*dense.xIonDense[nelem][nelem+1-ipISO];
107 
108  /* recombination continua for all iso seq -
109  * if this stage of ionization exists */
110  if( dense.IonHigh[nelem] >= nelem+1-ipISO )
111  {
112  t_iso_sp* sp = &iso_sp[ipISO][nelem];
113 
114  // add line emission from the model iso atoms
115  for( long ipHi=1; ipHi < sp->numLevels_local; ipHi++ )
116  {
117  for( long ipLo=0; ipLo < ipHi; ipLo++ )
118  {
119  // skip non-radiative transitions
120  if( sp->trans(ipHi,ipLo).ipCont() < 1 )
121  continue;
122 
123  /* number of photons in the line has not been defined up until now,
124  * do so now. this is redone in lines. */
125  sp->trans(ipHi,ipLo).Emis().phots() =
126  sp->trans(ipHi,ipLo).Emis().Aul()*
127  sp->st[ipHi].Pop()*
128  sp->trans(ipHi,ipLo).Emis().Pesc();
129 
130  // Would be better to enable checks (and remove argument) --
131  // present state is to ensure backwards compatibility with previous
132  // unchecked code.
133  // First argument is fraction of line not emitted by scattering --
134  // would be better to do this on the basis of line physics rather than
135  // fiat...
136  const bool lgDoChecks = false;
137  sp->trans(ipHi,ipLo).outline(1.0, lgDoChecks );
138  }
139  }
140 
141  /*Iso treatment of two photon emission. */
142  /* NISO could in the future be increased, but we want this assert to blow
143  * so that it is understood this may not be correct for other iso sequences,
144  * probably should break since will not be present */
145  ASSERT( ipISO <= ipHE_LIKE );
146 
147  /* upper limit to 2-phot is energy of 2s to ground */
148  for( vector<two_photon>::iterator tnu = sp->TwoNu.begin(); tnu != sp->TwoNu.end(); ++tnu )
149  {
151 
152  for( nu=0; nu < tnu->ipTwoPhoE; nu++ )
153  {
154  /* information - only used in save output */
155  rfield.TotDiff2Pht[nu] += tnu->local_emis[nu];
156 
157  /* total local diffuse emission */
158  rfield.ConEmitLocal[nzone][nu] += tnu->local_emis[nu];
159 
160  /* this is escaping part of two-photon emission,
161  * as determined from optical depth to illuminated face */
162  rfield.DiffuseEscape[nu] += tnu->local_emis[nu] * opac.ExpmTau[nu];
163  }
164  enum {DEBUG_LOC=false};
165  if( DEBUG_LOC )
166  {
167  fprintf( ioQQQ, "Two-photon emission coefficients - ipISO, nelem = %2li, %2li\n", ipISO, nelem );
168  PrtTwoPhotonEmissCoef( *tnu, EdenAbund );
169  }
170  }
171  }
172  }
173  }
174 
175  /* add recombination continua for elements heavier than those done with iso seq */
176  for( long nelem=NISO; nelem < LIMELM; nelem++ )
177  {
178  // zero out all stages since dense.IonLow[nelem] may have been lower last time around
179  for( long ion=0; ion < nelem-NISO+1; ion++ )
180  {
181  Heavy.RadRecCon[nelem][ion] = 0.;
182  }
183 
184  /* do not include species with iso-sequence in following */
185  /* >>chng 03 sep 09, upper bound was wrong, did not include NISO */
186  for( long ion=dense.IonLow[nelem]; ion < nelem-NISO+1; ion++ )
187  {
188  if( dense.xIonDense[nelem][ion+1] > 0. )
189  {
190  long int ns, nshell,igRec , igIon,
191  iplow , iphi , ipop;
192 
193  ip = Heavy.ipHeavy[nelem][ion]-1;
194  ASSERT( ip >= 0 );
195 
196  /* nflux was reset upward in ConvInitSolution to encompass all
197  * possible line and continuum emission. this test should not
198  * possibly fail. It could if the ionization were to increase with depth
199  * although the continuum mesh is designed to deal with this.
200  * This test is important because the nflux cell in ConInterOut
201  * is used to carry out the unit integration, and if it gets
202  * clobbered by diffuse emission the code will declare
203  * insanity in PrtComment */
204  if( ip >= rfield.nflux )
205  continue;
206 
207  /* get shell number, stat weights for this species */
208  atmdat_outer_shell( nelem+1 , nelem+1-ion , &nshell, &igRec , &igIon );
209  gn = (double)igRec;
210  gion = (double)igIon;
211 
212  /* shell number */
213  ns = Heavy.nsShells[nelem][ion]-1;
214  ASSERT( ns == (nshell-1) );
215 
216  /* lower and upper energies, and offset for opacity stack */
217  iplow = opac.ipElement[nelem][ion][ns][0]-1;
218  iphi = opac.ipElement[nelem][ion][ns][1];
219  iphi = MIN2( iphi , rfield.nflux );
220  ipop = opac.ipElement[nelem][ion][ns][2];
221 
222  /* now convert ipop to the offset in the opacity stack from threshold */
223  ipop = ipop - iplow;
224 
225  EdenAbund = dense.eden*dense.xIonDense[nelem][ion+1];
226  gamma = 0.5*MILNE_CONST*gn/gion/phycon.te/phycon.sqrte;
227 
228  /* this is ground state continuum from stored opacities */
229  if( rfield.ContBoltz[iplow] > SMALLFLOAT )
230  {
231  for( nu=iplow; nu < iphi; ++nu )
232  {
233  photon = gamma*rfield.ContBoltz[nu]/rfield.ContBoltz[iplow]*
234  rfield.widflx[nu]*opac.OpacStack[nu+ipop]*rfield.anu2[nu];
235  /* add heavy rec to ground in active beam,*/
240  rfield.ConEmitLocal[nzone][nu] += (realnum)photon*EdenAbund;
241  rfield.DiffuseEscape[nu] += (realnum)photon*EdenAbund*opac.ExpmTau[nu];
242 
243  // escaping RRC
244  Heavy.RadRecCon[nelem][ion] += rfield.anu[nu] *
245  emergent_line( photon*EdenAbund/2. , photon*EdenAbund/2. ,
246  // energy on fortran scale
247  nu+1 );
248  }
249  }
250  // units erg cm-3 s-1
251  Heavy.RadRecCon[nelem][ion] *= EN1RYD;
252 
253  /* now do the recombination Lya */
254  ipla = Heavy.ipLyHeavy[nelem][ion]-1;
255  ASSERT( ipla >= 0 );
256  /* xLyaHeavy is set to a fraction of the total rad rec in ion_recomb, includes eden */
257  difflya = Heavy.xLyaHeavy[nelem][ion]*dense.xIonDense[nelem][ion+1];
258  rfield.DiffuseLineEmission[ipla] += (realnum)difflya;
259 
260  /* >>chng 03 jul 10, here and below, use outlin_noplot */
261  rfield.outlin_noplot[ipla] += (realnum)(difflya*radius.dVolOutwrd*opac.tmn[ipla]*opac.ExpmTau[ipla]);
262 
263  /* now do the recombination Balmer photons */
264  ipla = Heavy.ipBalHeavy[nelem][ion]-1;
265  ASSERT( ipla >= 0 );
266  /* xLyaHeavy is set to fraction of total rad rec in ion_recomb, includes eden */
267  difflya = Heavy.xLyaHeavy[nelem][ion]*dense.xIonDense[nelem][ion+1];
268  rfield.outlin_noplot[ipla] += (realnum)(difflya*radius.dVolOutwrd*opac.tmn[ipla]*opac.ExpmTau[ipla]);
269  }
270  }
271  }
272 
273  /* free-free free free brems emission for all ions */
274  limit = MIN2( rfield.ipMaxBolt , rfield.nflux );
275  for( nu=0; nu < limit; nu++ )
276  {
277  double TotBremsAllIons = 0., BremsThisIon;
278 
279  /* First add H- brems. Reaction is H(1s) + e -> H(1s) + e + hnu.
280  * OpacStack contains the ratio of the H- to H brems cross section.
281  * Multiply H brems by this and the population of H(1s). */
282  TotBremsAllIons += rfield.gff[1][nu] * opac.OpacStack[nu-1+opac.iphmra] * iso_sp[ipH_LIKE][ipHYDROGEN].st[ipH1s].Pop();
283 
284  /* chng 02 may 16, by Ryan...do all brems for all ions in one fell swoop,
285  * using gaunt factors from rfield.gff. */
286  for( long nelem=ipHYDROGEN; nelem < LIMELM; nelem++ )
287  {
288  /* MAX2 occurs because we want to start at first ion (or above)
289  * and do not want atom */
290  for( long ion=MAX2(1,dense.IonLow[nelem]); ion<=dense.IonHigh[nelem]; ++ion )
291  {
292  /* eff. charge is ion, so first rfield.gff argument must be "ion". */
293  BremsThisIon = POW2( (realnum)ion )*dense.xIonDense[nelem][ion]*rfield.gff[ion][nu];
294  TotBremsAllIons += BremsThisIon;
295  }
296  }
297 
298  /* add molecular ions */
299  for( long ipMol = 0; ipMol<mole_global.num_calc; ipMol++ )
300  {
301  if( !mole_global.list[ipMol]->isMonatomic() && mole_global.list[ipMol]->charge > 0 && mole_global.list[ipMol]->parentLabel.empty()
302  // H2+ and H3+ do not appear to be included above.
303  /* && mole_global.list[ipMol] != findspecies("H2+") &&
304  mole_global.list[ipMol] != findspecies("H3+") */ )
305  {
306  /* eff. charge is ion, so first rfield.gff argument must be "ion". */
307  long ion = mole_global.list[ipMol]->charge;
308  BremsThisIon = POW2( (double)ion )*mole.species[ipMol].den*rfield.gff[ion][nu];
309  TotBremsAllIons += BremsThisIon;
310  }
311  }
312 
314  /* >>chng 06 apr 05, no free free also turns off emission */
315  TotBremsAllIons *= dense.eden*1.032e-11*rfield.widflx[nu]*rfield.ContBoltz[nu]/rfield.anu[nu]/phycon.sqrte *
317  ASSERT( TotBremsAllIons >= 0.);
318 
319  /* >>chng 01 jul 01, move thick brems back to ConEmitLocal but do not add
320  * to outward beam - ConLocNoInter array removed as result
321  * if problems develop with very dense blr clouds, this may be reason */
322  /*rfield.ConLocNoInter[nu] += (realnum)fac;*/
323  /*rfield.ConEmitLocal[nzone][nu] += (realnum)TotBremsAllIons;*/
324 
325  if( nu >= rfield.ipEnergyBremsThin )
326  {
327  /* >>chng 05 feb 20, move into this test on brems opacity - should not be
328  * needed since would use expmtau to limit outward beam */
329  /* >>chng 01 jul 01, move thick brems back to ConEmitLocal but do not add
330  * to outward beam - ConLocNoInter array removed as result
331  * if problems develop with very dense BLR clouds, this may be reason */
332  /*rfield.ConLocNoInter[nu] += (realnum)fac;*/
333  rfield.ConEmitLocal[nzone][nu] += (realnum)TotBremsAllIons;
334 
335  /* do not add optically thick part to outward beam since self absorbed
336  * >>chng 96 feb 27, put back into outward beam since do not integrate
337  * over it anyway. */
338  /* >>chng 99 may 28, take back out of beam since DO integrate over it
339  * in very dense BLR clouds */
340  /* >>chng 01 jul 10, add here, in only one loop, where optically thin */
341  rfield.DiffuseEscape[nu] += (realnum)TotBremsAllIons;
342  }
343  }
344 
345  /* grain dust emission */
346  /* >>chng 01 nov 22, moved calculation of grain flux to qheat.c, PvH */
347  if( gv.lgDustOn() && gv.lgGrainPhysicsOn )
348  {
349  /* this calculates diffuse emission from grains,
350  * and stores the result in gv.GrainEmission */
352 
353  for( nu=0; nu < rfield.nflux; nu++ )
354  {
357  }
358  }
359 
360  /* hminus emission */
361  fac = dense.eden*(double)dense.xIonDense[ipHYDROGEN][0];
362  gn = 1.;
363  gion = 2.;
364  gamma = 0.5*MILNE_CONST*gn/gion/phycon.te/phycon.sqrte;
365  /* >>chng 00 dec 15 change limit to -1 of H edge */
366  limit = MIN2(iso_sp[ipH_LIKE][ipHYDROGEN].fb[ipH1s].ipIsoLevNIonCon-1,rfield.nflux);
367 
368  if( rfield.ContBoltz[hmi.iphmin-1] > 0. )
369  {
370  for( nu=hmi.iphmin-1; nu < limit; nu++ )
371  {
372  /* H- flux photons cm-3 s-1
373  * ContBoltz is ratio of Boltzmann factor for each freq */
374  factor = gamma*rfield.ContBoltz[nu]/rfield.ContBoltz[hmi.iphmin-1]*rfield.widflx[nu]*
376  rfield.anu2[nu]*fac;
377  rfield.ConEmitLocal[nzone][nu] += (realnum)factor;
378  rfield.DiffuseEscape[nu] += (realnum)factor;
379  }
380  }
381  else
382  {
383  for( nu=hmi.iphmin-1; nu < limit; nu++ )
384  {
385  double arg = MAX2(0.,TE1RYD*(rfield.anu[nu]-0.05544)/phycon.te);
386  /* this is the limit sexp normally uses */
387  if( arg > SEXP_LIMIT )
388  break;
389  /* H- flux photons cm-3 s-1
390  * flux is in photons per sec per ryd */
391  factor = gamma*exp(-arg)*rfield.widflx[nu]*
393  rfield.anu2[nu]*fac;
394  rfield.ConEmitLocal[nzone][nu] += (realnum)factor;
395  rfield.DiffuseEscape[nu] += (realnum)factor;
396  }
397  }
398 
399  /* outward level 1 line photons, 0 is dummy line */
400  for( long i=1; i <= nLevel1; i++ )
401  TauLines[i].outline_resonance();
402 
403  for( long ipISO = ipHE_LIKE; ipISO < NISO; ipISO++ )
404  {
405  for( long nelem = ipISO; nelem < LIMELM; nelem++ )
406  {
407  if( dense.lgElmtOn[nelem] && iso_ctrl.lgDielRecom[ipISO] )
408  {
409  for( long i=0; i<iso_sp[ipISO][nelem].numLevels_local; i++ )
410  {
411  const TransitionList::iterator& tr = SatelliteLines[ipISO][nelem].begin()+ipSatelliteLines[ipISO][nelem][i];
412  (*tr).Emis().phots() =
413  (*tr).Emis().Aul()*
414  (*(*tr).Hi()).Pop()*
415  ((*tr).Emis().Pesc()+
416  (*tr).Emis().Pelec_esc());
417 
418  (*tr).outline_resonance();
419  }
420  }
421  }
422  }
423 
424  /* outward level 2 line photons */
425  for( long i=0; i < nWindLine; i++ )
426  {
427  /* must not also do lines that were already done as part
428  * of the isoelectronic sequences */
429  if( (*TauLine2[i].Hi()).IonStg() < (*TauLine2[i].Hi()).nelem()+1-NISO )
430  {
431  {
432  enum {DEBUG_LOC=false};
433  if( DEBUG_LOC /*&& nzone > 10*/ && i==4821 )
434  {
435  /* set up to dump the Fe 9 169A line */
436  fprintf(ioQQQ,"DEBUG dump lev2 line %li\n", i );
437  DumpLine( TauLine2[i] );
438  fprintf(ioQQQ,"DEBUG dump %.3e %.3e %.3e\n",
439  rfield.outlin[0][TauLine2[i].ipCont()-1],
440  TauLine2[i].Emis().phots()*TauLine2[i].Emis().FracInwd()*radius.BeamInOut*opac.tmn[i]*TauLine2[i].Emis().ColOvTot(),
441  TauLine2[i].Emis().phots()*(1. - TauLine2[i].Emis().FracInwd())*radius.BeamOutOut* TauLine2[i].Emis().ColOvTot() );
442  }
443  }
444  TauLine2[i].outline_resonance();
445  /*if( i==2576 ) fprintf(ioQQQ,"DEBUG dump %.3e %.3e \n",
446  rfield.outlin[0][TauLine2[i].ipCont()-1] , rfield.outlin_noplot[TauLine2[i].ipCont()-1]);*/
447  }
448  }
449 
450  /* outward hyperfine structure line photons */
451  for( long i=0; i < nHFLines; i++ )
452  {
453  HFLines[i].outline_resonance();
454  }
455 
456  /* external database lines */
457  for( long ipSpecies=0; ipSpecies<nSpecies; ipSpecies++ )
458  {
459  if( dBaseSpecies[ipSpecies].lgActive )
460  {
461  for (TransitionList::iterator tr=dBaseTrans[ipSpecies].begin();
462  tr != dBaseTrans[ipSpecies].end(); ++tr)
463  {
464  int ipHi = (*tr).ipHi();
465  if (ipHi >= dBaseSpecies[ipSpecies].numLevels_local || (*tr).ipCont() <= 0)
466  continue;
467  (*tr).outline_resonance();
468  }
469  }
470  }
471 
472  /* H2 emission */
473  for( diatom_iter diatom = diatoms.begin(); diatom != diatoms.end(); ++diatom )
474  (*diatom)->H2_RT_diffuse();
475 
476  /* do outward parts of FeII lines, if large atom is turned on */
477  FeII_RT_Out();
481  if( trace.lgTrace )
482  fprintf( ioQQQ, " RT_diffuse returns.\n" );
483 
484  /* >>chng 02 jul 25, zero out all light below plasma freq */
485  for( nu=0; nu < rfield.ipPlasma-1; nu++ )
486  {
487  rfield.flux_beam_const[nu] = 0.;
488  rfield.flux_beam_time[nu] = 0.;
489  rfield.flux_isotropic[nu] = 0.;
490  rfield.flux[0][nu] = 0.;
491  rfield.ConEmitLocal[nzone][nu] = 0.;
492  rfield.otscon[nu] = 0.;
493  rfield.otslin[nu] = 0.;
494  rfield.outlin[0][nu] = 0.;
495  rfield.outlin_noplot[nu] = 0.;
496  rfield.reflin[0][nu] = 0.;
497  rfield.TotDiff2Pht[nu] = 0.;
498  rfield.ConInterOut[nu] = 0.;
499  }
500 
501  /* find occupation number, also assert that no continua are negative */
502  for( nu=0; nu < rfield.nflux; nu++ )
503  {
504  /* >>chng 00 oct 03, add diffuse continua */
505  /* local diffuse continua */
506  rfield.OccNumbDiffCont[nu] =
508 
509  /* units are photons cell-1 cm-2 s-1 */
511  /* units photons cm-3 s-1 cell-1, */
512  (realnum)safe_div( (double)rfield.ConEmitLocal[nzone][nu],
513  /* units cm-1 */
514  opac.opacity_abs[nu] );
515 
516  /* confirm that all are non-negative */
517  ASSERT( rfield.flux_beam_const[nu] >= 0.);
518  ASSERT( rfield.flux_beam_time[nu] >= 0.);
519  ASSERT( rfield.flux_isotropic[nu] >= 0.);
520  ASSERT( rfield.flux[0][nu] >= 0.);
521  ASSERT( rfield.ConEmitLocal[nzone][nu] >= 0.);
522  ASSERT( rfield.otscon[nu] >= 0.);
523  ASSERT( rfield.otslin[nu] >= 0.);
524  ASSERT( rfield.outlin[0][nu] >= 0.);
525  ASSERT( rfield.outlin_noplot[nu] >= 0.);
526  ASSERT( rfield.reflin[0][nu] >= 0.);
527  ASSERT( rfield.TotDiff2Pht[nu] >= 0.);
528  ASSERT( rfield.ConInterOut[nu] >= 0.);
529  }
530 
531  /* option to kill outward lines with no outward lines command*/
532  if( rfield.lgKillOutLine )
533  {
534  for( nu=0; nu < rfield.nflux; nu++ )
535  {
536  rfield.outlin[0][nu] = 0.;
537  rfield.outlin_noplot[nu] = 0.;
538  }
539  }
540 
541  /* option to kill outward continua with no outward continua command*/
542  if( rfield.lgKillOutCont )
543  {
544  for( nu=0; nu < rfield.nflux; nu++ )
545  {
546  rfield.ConInterOut[nu] = 0.;
547  }
548  }
549  return;
550 }
551 
552 void RT_iso_integrate_RRC( const long ipISO, const long nelem, const bool lgUpdateContinuum )
553 {
554  DEBUG_ENTRY( "RT_iso_integrate_RRC()" );
555 
556  // this array stores the last temperature at which cooling coefficients were evaluated
557  static double TeUsed[NISO][LIMELM]={
558  {0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0},
559  {0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0.,0} };
560 
561  if( !lgUpdateContinuum && fp_equal( phycon.te, TeUsed[ipISO][nelem] ) && conv.nTotalIoniz )
562  return;
563 
564  ASSERT( nelem >= ipISO );
565  ASSERT( nelem < LIMELM );
566 
567  /* this will be the sum of recombinations to all excited levels */
568  double SumCaseB = 0.;
569 
570  /* the product of the densities of the parent ion and electrons */
571  double EdenAbund = dense.eden*dense.xIonDense[nelem][nelem+1-ipISO];
572 
573  // recombination continua for all iso seq -
574  // if this stage of ionization exists
575  if( dense.IonHigh[nelem] >= nelem+1-ipISO )
576  {
577  t_iso_sp* sp = &iso_sp[ipISO][nelem];
578 
579  // loop over all levels to include recombination diffuse continua,
580  // pick highest energy continuum point that opacities extend to
581  long ipHi = rfield.nflux;
582  // >>chng 06 aug 17, should go to numLevels_local instead of _max.
583  for( long n=0; n < sp->numLevels_local; n++ )
584  {
585  double Sum1level = 0.;
586  sp->fb[n].RadRecCon = 0.;
587  sp->fb[n].RadRecCoolCoef = 0.;
588  // the number is (2 pi me k/h^2) ^ -3/2 * 8 pi/c^2 / ge - it includes
589  // the stat weight of the free electron in the demominator
590  double gamma = 0.5*MILNE_CONST*sp->st[n].g()/iso_ctrl.stat_ion[ipISO]/phycon.te/phycon.sqrte;
591 
592  // loop over all recombination continua
593  // escaping part of recombinations are added to rfield.ConEmitLocal
594  // added to ConInterOut at end of routine
595  for( long nu=sp->fb[n].ipIsoLevNIonCon-1; nu < ipHi; nu++ )
596  {
597  // dwid used to adjust where within WIDFLX exp is evaluated -
598  // weighted to lower energy due to exp(-energy/T)
599  double dwid = 0.2;
600 
601  // this is the term in the negative exponential Boltzmann factor
602  // for continuum emission
603  double arg = (rfield.anu[nu]-sp->fb[n].xIsoLevNIonRyd+
604  rfield.widflx[nu]*dwid)/phycon.te_ryd;
605  arg = MAX2(0.,arg);
606  // don't bother evaluating for this or higher energies if
607  // Boltzmann factor is tiny.
608  if( arg > SEXP_LIMIT )
609  break;
610 
611  /* photon is in photons cm^3 s^-1 per cell */
612  double photon = gamma*exp(-arg)*rfield.widflx[nu]*
613  opac.OpacStack[ nu-sp->fb[n].ipIsoLevNIonCon + sp->fb[n].ipOpac ] *
614  rfield.anu2[nu];
615 
616  Sum1level += photon;
617 
618  fixit(); // We need to include induced recombination in diffuse spectrum.
619  // Probably best just to do the whole thing here, then no need for induced terms in iso_photo.
620 
621  if( lgUpdateContinuum )
622  {
623  /* total local diffuse emission units photons cm-3 s-1 cell-1,*/
624  rfield.ConEmitLocal[nzone][nu] += (realnum)(photon*EdenAbund);
625 
626  // sp->fb[n].RadRecomb[ipRecEsc] is escape probability
627  // rfield.DiffuseEscape is local emission that escapes this zone
628  rfield.DiffuseEscape[nu] +=
629  (realnum)(photon*EdenAbund*sp->fb[n].RadRecomb[ipRecEsc]);
630  }
631 
632  // total RRC radiative recombination continuum
633  sp->fb[n].RadRecCon += rfield.anu[nu] *
634  emergent_line( photon*EdenAbund/2. , photon*EdenAbund/2. ,
635  // energy on fortran scale
636  nu+1 );
637 
638  double energyAboveThresh = rfield.anu[nu] - sp->fb[n].xIsoLevNIonRyd;
639  energyAboveThresh = MAX2( 0., energyAboveThresh );
640  sp->fb[n].RadRecCoolCoef += energyAboveThresh * photon *
641  sp->fb[n].RadRecomb[ipRecNetEsc];
642  }
643 
644  // convert to erg cm-3 s-1
645  sp->fb[n].RadRecCon *= EN1RYD;
646  sp->fb[n].RadRecCoolCoef *= EN1RYD;
647  /* this will be used below to confirm case B sum */
648  if( n > 0 )
649  {
650  /* SumCaseB will be sum to all excited */
651  SumCaseB += Sum1level;
652  }
653  }
654 
655  // no RRC emission from levels that do not exist
656  for( long n=sp->numLevels_local;n<sp->numLevels_max; n++ )
657  {
658  sp->fb[n].RadRecCon = 0.;
659  sp->fb[n].RadRecCoolCoef = 0.;
660  }
661 
662  /* this is check on self-consistency */
663  sp->CaseBCheck = MAX2(sp->CaseBCheck,
664  (realnum)(SumCaseB/sp->RadRec_caseB));
665  }
666 
667  TeUsed[ipISO][nelem] = phycon.te;
668 
669  return;
670 }
671 
#define MIN2
Definition: cddefines.h:765
long int iphmin
Definition: hmi.h:117
realnum ** gff
Definition: rfield.h:227
realnum ** ConSourceFcnLocal
Definition: rfield.h:152
double RadRecCon[LIMELM][LIMELM]
Definition: heavy.h:18
t_mole_global mole_global
Definition: mole.cpp:6
realnum * widflx
Definition: rfield.h:65
void DumpLine(const TransitionProxy &t)
Definition: transition.cpp:100
long int ipElement[LIMELM][LIMELM][7][3]
Definition: opacity.h:210
void GrainMakeDiffuse(void)
realnum * flux_isotropic
Definition: rfield.h:89
double * OpacStack
Definition: opacity.h:151
double * opacity_abs
Definition: opacity.h:95
qList st
Definition: iso.h:453
const int ipHE_LIKE
Definition: iso.h:63
bool lgKillOutLine
Definition: rfield.h:434
long int ipEnergyBremsThin
Definition: rfield.h:245
t_opac opac
Definition: opacity.cpp:5
multi_arr< int, 3 > ipSatelliteLines
Definition: taulines.cpp:37
int num_calc
Definition: mole.h:314
long int nLevel1
Definition: taulines.cpp:28
realnum ** flux
Definition: rfield.h:86
realnum * DiffuseLineEmission
Definition: rfield.h:203
t_Heavy Heavy
Definition: heavy.cpp:5
double RadRec_caseB
Definition: iso.h:513
realnum * DiffuseEscape
Definition: rfield.h:184
const realnum SMALLFLOAT
Definition: cpu.h:178
t_isoCTRL iso_ctrl
Definition: iso.cpp:6
const int NISO
Definition: cddefines.h:265
realnum * outlin_noplot
Definition: rfield.h:199
#define MAX2
Definition: cddefines.h:786
long int ipMaxBolt
Definition: rfield.h:249
realnum xLyaHeavy[LIMELM][LIMELM]
Definition: heavy.h:21
t_conv conv
Definition: conv.cpp:5
const double MILNE_CONST
Definition: physconst.h:233
TransitionList HFLines("HFLines",&AnonStates)
t_phycon phycon
Definition: phycon.cpp:6
t_dense dense
Definition: dense.cpp:24
bool lgKillOutCont
Definition: rfield.h:437
const int ipRecNetEsc
Definition: cddefines.h:285
t_CoolHeavy CoolHeavy
Definition: coolheavy.cpp:5
realnum ** outlin
Definition: rfield.h:199
FILE * ioQQQ
Definition: cddefines.cpp:7
long int nzone
Definition: cddefines.cpp:14
vector< freeBound > fb
Definition: iso.h:452
TransitionList TauLine2("TauLine2",&AnonStates)
long int nSpecies
Definition: taulines.cpp:21
double xIonDense[LIMELM][LIMELM+1]
Definition: dense.h:125
double & phots() const
Definition: emission.h:503
long int iphmra
Definition: opacity.h:210
t_iso_sp iso_sp[NISO][LIMELM]
Definition: iso.cpp:8
long int nupper
Definition: rfield.h:46
realnum * otslin
Definition: rfield.h:193
t_trace trace
Definition: trace.cpp:5
bool fp_equal(sys_float x, sys_float y, int n=3)
Definition: cddefines.h:816
realnum ** ConEmitLocal
Definition: rfield.h:149
vector< two_photon > TwoNu
Definition: iso.h:586
long int nsShells[LIMELM][LIMELM]
Definition: heavy.h:28
vector< realnum > GrainEmission
Definition: grainvar.h:578
#define POW2
Definition: cddefines.h:933
const int ipH1s
Definition: iso.h:27
bool lgTrace
Definition: trace.h:12
void PrtTwoPhotonEmissCoef(const two_photon &tnu, const double &densityProduct)
Definition: two_photon.cpp:157
long int ipPlasma
Definition: rfield.h:453
EmissionList::reference Emis() const
Definition: transition.h:408
void FeII_RT_Out(void)
Definition: atom_feii.cpp:2542
t_mole_local mole
Definition: mole.cpp:7
t_rfield rfield
Definition: rfield.cpp:8
long int IonHigh[LIMELM+1]
Definition: dense.h:120
realnum * anu2
Definition: rfield.h:77
long & ipCont() const
Definition: transition.h:450
bool lgDielRecom[NISO]
Definition: iso.h:365
realnum * convoc
Definition: rfield.h:134
realnum * ConInterOut
Definition: rfield.h:164
float realnum
Definition: cddefines.h:107
valarray< class molezone > species
Definition: mole.h:355
realnum & Pesc() const
Definition: emission.h:523
vector< diatomics * > diatoms
Definition: h2.cpp:8
bool lgElmtOn[LIMELM]
Definition: dense.h:146
realnum * otscon
Definition: rfield.h:193
double * ContBoltz
Definition: rfield.h:145
bool lgGrainPhysicsOn
Definition: grainvar.h:475
sys_float safe_div(sys_float x, sys_float y, sys_float res_0by0)
Definition: cddefines.h:965
const int ipRecEsc
Definition: cddefines.h:283
vector< vector< TransitionList > > SatelliteLines
Definition: taulines.cpp:38
long nWindLine
Definition: cdinit.cpp:19
species * dBaseSpecies
Definition: taulines.cpp:14
const double EN1RYD
Definition: physconst.h:179
long int iphmop
Definition: opacity.h:210
t_radius radius
Definition: radius.cpp:5
void atmdat_outer_shell(long int iz, long int in, long int *imax, long int *ig0, long int *ig1)
realnum ** reflin
Definition: rfield.h:206
long int nTotalIoniz
Definition: conv.h:166
TransitionProxy trans(const long ipHi, const long ipLo)
Definition: iso.h:444
Definition: iso.h:441
bool lgInd2nu_On
Definition: iso.h:355
double dVolOutwrd
Definition: radius.h:97
long int ipLyHeavy[LIMELM][LIMELM-1]
Definition: heavy.h:11
double BeamInOut
Definition: radius.h:105
long int IonLow[LIMELM+1]
Definition: dense.h:119
#define ASSERT(exp)
Definition: cddefines.h:582
long int ipBalHeavy[LIMELM][LIMELM-1]
Definition: heavy.h:11
double * anu
Definition: rfield.h:58
TransitionList TauLines("TauLines",&AnonStates)
const int ipH_LIKE
Definition: iso.h:62
const int LIMELM
Definition: cddefines.h:262
void outline(double nonScatteredFraction, bool lgDoChecks) const
Definition: transition.cpp:44
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:688
void RT_iso_integrate_RRC(const long ipISO, const long nelem, const bool lgUpdateContinuum)
Definition: rt_diffuse.cpp:552
double BeamOutOut
Definition: radius.h:108
double te_ryd
Definition: phycon.h:17
realnum * OccNumbDiffCont
Definition: rfield.h:141
double eden
Definition: dense.h:190
realnum * ExpmTau
Definition: opacity.h:132
bool lgInducProcess
Definition: rfield.h:252
long int nHFLines
Definition: taulines.cpp:31
realnum stat_ion[NISO]
Definition: iso.h:362
MoleculeList list
Definition: mole.h:317
double emergent_line(double emissivity_in, double emissivity_out, long int ipCont)
realnum * TotDiff2Pht
Definition: rfield.h:187
double sqrte
Definition: phycon.h:48
realnum * tmn
Definition: opacity.h:136
realnum * flux_beam_time
Definition: rfield.h:92
GrainVar gv
Definition: grainvar.cpp:5
realnum CaseBCheck
Definition: iso.h:510
t_hmi hmi
Definition: hmi.cpp:5
vector< TransitionList > dBaseTrans
Definition: taulines.cpp:17
realnum * flux_beam_const
Definition: rfield.h:92
void RT_diffuse(void)
Definition: rt_diffuse.cpp:34
double te
Definition: phycon.h:11
const double SEXP_LIMIT
Definition: cddefines.h:1480
void CalcTwoPhotonEmission(two_photon &tnu, bool lgDoInduced)
Definition: two_photon.cpp:125
bool lgDustOn() const
Definition: grainvar.h:471
const int ipHYDROGEN
Definition: cddefines.h:309
void fixit(void)
Definition: service.cpp:991
bool lgFreeOn
Definition: coolheavy.h:116
long int nflux
Definition: rfield.h:43
realnum & Aul() const
Definition: emission.h:613
long int numLevels_local
Definition: iso.h:498
EmissionList & Emis()
Definition: transition.h:329
vector< diatomics * >::iterator diatom_iter
Definition: h2.h:13
long int ipHeavy[LIMELM][LIMELM]
Definition: heavy.h:11
bool lgAbort
Definition: cddefines.cpp:10
const double TE1RYD
Definition: physconst.h:183