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  /* do not include species with iso-sequence in following */
179  /* >>chng 03 sep 09, upper bound was wrong, did not include NISO */
180  for( long ion=dense.IonLow[nelem]; ion < nelem-NISO+1; ion++ )
181  {
182  if( dense.xIonDense[nelem][ion+1] > 0. )
183  {
184  long int ns, nshell,igRec , igIon,
185  iplow , iphi , ipop;
186 
187  ip = Heavy.ipHeavy[nelem][ion]-1;
188  ASSERT( ip >= 0 );
189 
190  /* nflux was reset upward in ConvInitSolution to encompass all
191  * possible line and continuum emission. this test should not
192  * possibly fail. It could if the ionization were to increase with depth
193  * although the continuum mesh is designed to deal with this.
194  * This test is important because the nflux cell in ConInterOut
195  * is used to carry out the unit integration, and if it gets
196  * clobbered by diffuse emission the code will declare
197  * insanity in PrtComment */
198  if( ip >= rfield.nflux )
199  continue;
200 
201  /* get shell number, stat weights for this species */
202  atmdat_outer_shell( nelem+1 , nelem+1-ion , &nshell, &igRec , &igIon );
203  gn = (double)igRec;
204  gion = (double)igIon;
205 
206  /* shell number */
207  ns = Heavy.nsShells[nelem][ion]-1;
208  ASSERT( ns == (nshell-1) );
209 
210  /* lower and upper energies, and offset for opacity stack */
211  iplow = opac.ipElement[nelem][ion][ns][0]-1;
212  iphi = opac.ipElement[nelem][ion][ns][1];
213  iphi = MIN2( iphi , rfield.nflux );
214  ipop = opac.ipElement[nelem][ion][ns][2];
215 
216  /* now convert ipop to the offset in the opacity stack from threshold */
217  ipop = ipop - iplow;
218 
219  EdenAbund = dense.eden*dense.xIonDense[nelem][ion+1];
220  gamma = 0.5*MILNE_CONST*gn/gion/phycon.te/phycon.sqrte;
221 
222  /* this is ground state continuum from stored opacities */
223  Heavy.RadRecCon[nelem][ion] = 0;
224  if( rfield.ContBoltz[iplow] > SMALLFLOAT )
225  {
226  for( nu=iplow; nu < iphi; ++nu )
227  {
228  photon = gamma*rfield.ContBoltz[nu]/rfield.ContBoltz[iplow]*
229  rfield.widflx[nu]*opac.OpacStack[nu+ipop]*rfield.anu2[nu];
230  /* add heavy rec to ground in active beam,*/
235  rfield.ConEmitLocal[nzone][nu] += (realnum)photon*EdenAbund;
236  rfield.DiffuseEscape[nu] += (realnum)photon*EdenAbund*opac.ExpmTau[nu];
237 
238  // escaping RRC
239  Heavy.RadRecCon[nelem][ion] += rfield.anu[nu] *
240  emergent_line( photon*EdenAbund/2. , photon*EdenAbund/2. ,
241  // energy on fortran scale
242  nu+1 );
243  }
244  }
245  // units erg cm-3 s-1
246  Heavy.RadRecCon[nelem][ion] *= EN1RYD;
247 
248  /* now do the recombination Lya */
249  ipla = Heavy.ipLyHeavy[nelem][ion]-1;
250  ASSERT( ipla >= 0 );
251  /* xLyaHeavy is set to a fraction of the total rad rec in ion_recomb, includes eden */
252  difflya = Heavy.xLyaHeavy[nelem][ion]*dense.xIonDense[nelem][ion+1];
253  rfield.DiffuseLineEmission[ipla] += (realnum)difflya;
254 
255  /* >>chng 03 jul 10, here and below, use outlin_noplot */
256  rfield.outlin_noplot[ipla] += (realnum)(difflya*radius.dVolOutwrd*opac.tmn[ipla]*opac.ExpmTau[ipla]);
257 
258  /* now do the recombination Balmer photons */
259  ipla = Heavy.ipBalHeavy[nelem][ion]-1;
260  ASSERT( ipla >= 0 );
261  /* xLyaHeavy is set to fraction of total rad rec in ion_recomb, includes eden */
262  difflya = Heavy.xLyaHeavy[nelem][ion]*dense.xIonDense[nelem][ion+1];
263  rfield.outlin_noplot[ipla] += (realnum)(difflya*radius.dVolOutwrd*opac.tmn[ipla]*opac.ExpmTau[ipla]);
264  }
265  }
266  }
267 
268  /* free-free free free brems emission for all ions */
269  limit = MIN2( rfield.ipMaxBolt , rfield.nflux );
270  for( nu=0; nu < limit; nu++ )
271  {
272  double TotBremsAllIons = 0., BremsThisIon;
273 
274  /* First add H- brems. Reaction is H(1s) + e -> H(1s) + e + hnu.
275  * OpacStack contains the ratio of the H- to H brems cross section.
276  * Multiply H brems by this and the population of H(1s). */
277  TotBremsAllIons += rfield.gff[1][nu] * opac.OpacStack[nu-1+opac.iphmra] * iso_sp[ipH_LIKE][ipHYDROGEN].st[ipH1s].Pop();
278 
279  /* chng 02 may 16, by Ryan...do all brems for all ions in one fell swoop,
280  * using gaunt factors from rfield.gff. */
281  for( long nelem=ipHYDROGEN; nelem < LIMELM; nelem++ )
282  {
283  /* MAX2 occurs because we want to start at first ion (or above)
284  * and do not want atom */
285  for( long ion=MAX2(1,dense.IonLow[nelem]); ion<=dense.IonHigh[nelem]; ++ion )
286  {
287  /* eff. charge is ion, so first rfield.gff argument must be "ion". */
288  BremsThisIon = POW2( (realnum)ion )*dense.xIonDense[nelem][ion]*rfield.gff[ion][nu];
289  TotBremsAllIons += BremsThisIon;
290  }
291  }
292 
293  /* add molecular ions */
294  for( long ipMol = 0; ipMol<mole_global.num_calc; ipMol++ )
295  {
296  if( !mole_global.list[ipMol]->isMonatomic() && mole_global.list[ipMol]->charge > 0 && mole_global.list[ipMol]->parentLabel.empty()
297  // H2+ and H3+ do not appear to be included above.
298  /* && mole_global.list[ipMol] != findspecies("H2+") &&
299  mole_global.list[ipMol] != findspecies("H3+") */ )
300  {
301  /* eff. charge is ion, so first rfield.gff argument must be "ion". */
302  long ion = mole_global.list[ipMol]->charge;
303  BremsThisIon = POW2( (double)ion )*mole.species[ipMol].den*rfield.gff[ion][nu];
304  TotBremsAllIons += BremsThisIon;
305  }
306  }
307 
309  /* >>chng 06 apr 05, no free free also turns off emission */
310  TotBremsAllIons *= dense.eden*1.032e-11*rfield.widflx[nu]*rfield.ContBoltz[nu]/rfield.anu[nu]/phycon.sqrte *
312  ASSERT( TotBremsAllIons >= 0.);
313 
314  /* >>chng 01 jul 01, move thick brems back to ConEmitLocal but do not add
315  * to outward beam - ConLocNoInter array removed as result
316  * if problems develop with very dense blr clouds, this may be reason */
317  /*rfield.ConLocNoInter[nu] += (realnum)fac;*/
318  /*rfield.ConEmitLocal[nzone][nu] += (realnum)TotBremsAllIons;*/
319 
320  if( nu >= rfield.ipEnergyBremsThin )
321  {
322  /* >>chng 05 feb 20, move into this test on brems opacity - should not be
323  * needed since would use expmtau to limit outward beam */
324  /* >>chng 01 jul 01, move thick brems back to ConEmitLocal but do not add
325  * to outward beam - ConLocNoInter array removed as result
326  * if problems develop with very dense BLR clouds, this may be reason */
327  /*rfield.ConLocNoInter[nu] += (realnum)fac;*/
328  rfield.ConEmitLocal[nzone][nu] += (realnum)TotBremsAllIons;
329 
330  /* do not add optically thick part to outward beam since self absorbed
331  * >>chng 96 feb 27, put back into outward beam since do not integrate
332  * over it anyway. */
333  /* >>chng 99 may 28, take back out of beam since DO integrate over it
334  * in very dense BLR clouds */
335  /* >>chng 01 jul 10, add here, in only one loop, where optically thin */
336  rfield.DiffuseEscape[nu] += (realnum)TotBremsAllIons;
337  }
338  }
339 
340  /* grain dust emission */
341  /* >>chng 01 nov 22, moved calculation of grain flux to qheat.c, PvH */
342  if( gv.lgDustOn() && gv.lgGrainPhysicsOn )
343  {
344  /* this calculates diffuse emission from grains,
345  * and stores the result in gv.GrainEmission */
347 
348  for( nu=0; nu < rfield.nflux; nu++ )
349  {
352  }
353  }
354 
355  /* hminus emission */
356  fac = dense.eden*(double)dense.xIonDense[ipHYDROGEN][0];
357  gn = 1.;
358  gion = 2.;
359  gamma = 0.5*MILNE_CONST*gn/gion/phycon.te/phycon.sqrte;
360  /* >>chng 00 dec 15 change limit to -1 of H edge */
361  limit = MIN2(iso_sp[ipH_LIKE][ipHYDROGEN].fb[ipH1s].ipIsoLevNIonCon-1,rfield.nflux);
362 
363  if( rfield.ContBoltz[hmi.iphmin-1] > 0. )
364  {
365  for( nu=hmi.iphmin-1; nu < limit; nu++ )
366  {
367  /* H- flux photons cm-3 s-1
368  * ContBoltz is ratio of Boltzmann factor for each freq */
369  factor = gamma*rfield.ContBoltz[nu]/rfield.ContBoltz[hmi.iphmin-1]*rfield.widflx[nu]*
371  rfield.anu2[nu]*fac;
372  rfield.ConEmitLocal[nzone][nu] += (realnum)factor;
373  rfield.DiffuseEscape[nu] += (realnum)factor;
374  }
375  }
376  else
377  {
378  for( nu=hmi.iphmin-1; nu < limit; nu++ )
379  {
380  double arg = MAX2(0.,TE1RYD*(rfield.anu[nu]-0.05544)/phycon.te);
381  /* this is the limit sexp normally uses */
382  if( arg > SEXP_LIMIT )
383  break;
384  /* H- flux photons cm-3 s-1
385  * flux is in photons per sec per ryd */
386  factor = gamma*exp(-arg)*rfield.widflx[nu]*
388  rfield.anu2[nu]*fac;
389  rfield.ConEmitLocal[nzone][nu] += (realnum)factor;
390  rfield.DiffuseEscape[nu] += (realnum)factor;
391  }
392  }
393 
394  /* outward level 1 line photons, 0 is dummy line */
395  for( long i=1; i <= nLevel1; i++ )
396  TauLines[i].outline_resonance();
397 
398  for( long ipISO = ipHE_LIKE; ipISO < NISO; ipISO++ )
399  {
400  for( long nelem = ipISO; nelem < LIMELM; nelem++ )
401  {
402  if( dense.lgElmtOn[nelem] && iso_ctrl.lgDielRecom[ipISO] )
403  {
404  for( long i=0; i<iso_sp[ipISO][nelem].numLevels_local; i++ )
405  {
406  const TransitionList::iterator& tr = SatelliteLines[ipISO][nelem].begin()+ipSatelliteLines[ipISO][nelem][i];
407  (*tr).Emis().phots() =
408  (*tr).Emis().Aul()*
409  (*(*tr).Hi()).Pop()*
410  ((*tr).Emis().Pesc()+
411  (*tr).Emis().Pelec_esc());
412 
413  (*tr).outline_resonance();
414  }
415  }
416  }
417  }
418 
419  /* outward level 2 line photons */
420  for( long i=0; i < nWindLine; i++ )
421  {
422  /* must not also do lines that were already done as part
423  * of the isoelectronic sequences */
424  if( (*TauLine2[i].Hi()).IonStg() < (*TauLine2[i].Hi()).nelem()+1-NISO )
425  {
426  {
427  enum {DEBUG_LOC=false};
428  if( DEBUG_LOC /*&& nzone > 10*/ && i==4821 )
429  {
430  /* set up to dump the Fe 9 169A line */
431  fprintf(ioQQQ,"DEBUG dump lev2 line %li\n", i );
432  DumpLine( TauLine2[i] );
433  fprintf(ioQQQ,"DEBUG dump %.3e %.3e %.3e\n",
434  rfield.outlin[0][TauLine2[i].ipCont()-1],
435  TauLine2[i].Emis().phots()*TauLine2[i].Emis().FracInwd()*radius.BeamInOut*opac.tmn[i]*TauLine2[i].Emis().ColOvTot(),
436  TauLine2[i].Emis().phots()*(1. - TauLine2[i].Emis().FracInwd())*radius.BeamOutOut* TauLine2[i].Emis().ColOvTot() );
437  }
438  }
439  TauLine2[i].outline_resonance();
440  /*if( i==2576 ) fprintf(ioQQQ,"DEBUG dump %.3e %.3e \n",
441  rfield.outlin[0][TauLine2[i].ipCont()-1] , rfield.outlin_noplot[TauLine2[i].ipCont()-1]);*/
442  }
443  }
444 
445  /* outward hyperfine structure line photons */
446  for( long i=0; i < nHFLines; i++ )
447  {
448  HFLines[i].outline_resonance();
449  }
450 
451  /* external database lines */
452  for( long ipSpecies=0; ipSpecies<nSpecies; ipSpecies++ )
453  {
454  if( dBaseSpecies[ipSpecies].lgActive )
455  {
456  for (TransitionList::iterator tr=dBaseTrans[ipSpecies].begin();
457  tr != dBaseTrans[ipSpecies].end(); ++tr)
458  {
459  int ipHi = (*tr).ipHi();
460  if (ipHi >= dBaseSpecies[ipSpecies].numLevels_local || (*tr).ipCont() <= 0)
461  continue;
462  (*tr).outline_resonance();
463  }
464  }
465  }
466 
467  /* H2 emission */
468  for( diatom_iter diatom = diatoms.begin(); diatom != diatoms.end(); ++diatom )
469  (*diatom)->H2_RT_diffuse();
470 
471  /* do outward parts of FeII lines, if large atom is turned on */
472  FeII_RT_Out();
476  if( trace.lgTrace )
477  fprintf( ioQQQ, " RT_diffuse returns.\n" );
478 
479  /* >>chng 02 jul 25, zero out all light below plasma freq */
480  for( nu=0; nu < rfield.ipPlasma-1; nu++ )
481  {
482  rfield.flux_beam_const[nu] = 0.;
483  rfield.flux_beam_time[nu] = 0.;
484  rfield.flux_isotropic[nu] = 0.;
485  rfield.flux[0][nu] = 0.;
486  rfield.ConEmitLocal[nzone][nu] = 0.;
487  rfield.otscon[nu] = 0.;
488  rfield.otslin[nu] = 0.;
489  rfield.outlin[0][nu] = 0.;
490  rfield.outlin_noplot[nu] = 0.;
491  rfield.reflin[0][nu] = 0.;
492  rfield.TotDiff2Pht[nu] = 0.;
493  rfield.ConInterOut[nu] = 0.;
494  }
495 
496  /* find occupation number, also assert that no continua are negative */
497  for( nu=0; nu < rfield.nflux; nu++ )
498  {
499  /* >>chng 00 oct 03, add diffuse continua */
500  /* local diffuse continua */
501  rfield.OccNumbDiffCont[nu] =
503 
504  /* units are photons cell-1 cm-2 s-1 */
506  /* units photons cm-3 s-1 cell-1, */
507  (realnum)safe_div( (double)rfield.ConEmitLocal[nzone][nu],
508  /* units cm-1 */
509  opac.opacity_abs[nu] );
510 
511  /* confirm that all are non-negative */
512  ASSERT( rfield.flux_beam_const[nu] >= 0.);
513  ASSERT( rfield.flux_beam_time[nu] >= 0.);
514  ASSERT( rfield.flux_isotropic[nu] >= 0.);
515  ASSERT( rfield.flux[0][nu] >= 0.);
516  ASSERT( rfield.ConEmitLocal[nzone][nu] >= 0.);
517  ASSERT( rfield.otscon[nu] >= 0.);
518  ASSERT( rfield.otslin[nu] >= 0.);
519  ASSERT( rfield.outlin[0][nu] >= 0.);
520  ASSERT( rfield.outlin_noplot[nu] >= 0.);
521  ASSERT( rfield.reflin[0][nu] >= 0.);
522  ASSERT( rfield.TotDiff2Pht[nu] >= 0.);
523  ASSERT( rfield.ConInterOut[nu] >= 0.);
524  }
525 
526  /* option to kill outward lines with no outward lines command*/
527  if( rfield.lgKillOutLine )
528  {
529  for( nu=0; nu < rfield.nflux; nu++ )
530  {
531  rfield.outlin[0][nu] = 0.;
532  rfield.outlin_noplot[nu] = 0.;
533  }
534  }
535 
536  /* option to kill outward continua with no outward continua command*/
537  if( rfield.lgKillOutCont )
538  {
539  for( nu=0; nu < rfield.nflux; nu++ )
540  {
541  rfield.ConInterOut[nu] = 0.;
542  }
543  }
544  return;
545 }
546 
547 void RT_iso_integrate_RRC( const long ipISO, const long nelem, const bool lgUpdateContinuum )
548 {
549  DEBUG_ENTRY( "RT_iso_integrate_RRC()" );
550 
551  // this array stores the last temperature at which cooling coefficients were evaluated
552  static double TeUsed[NISO][LIMELM]={
553  {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},
554  {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} };
555 
556  if( !lgUpdateContinuum && fp_equal( phycon.te, TeUsed[ipISO][nelem] ) && conv.nTotalIoniz )
557  return;
558 
559  ASSERT( nelem >= ipISO );
560  ASSERT( nelem < LIMELM );
561 
562  /* this will be the sum of recombinations to all excited levels */
563  double SumCaseB = 0.;
564 
565  /* the product of the densities of the parent ion and electrons */
566  double EdenAbund = dense.eden*dense.xIonDense[nelem][nelem+1-ipISO];
567 
568  // recombination continua for all iso seq -
569  // if this stage of ionization exists
570  if( dense.IonHigh[nelem] >= nelem+1-ipISO )
571  {
572  t_iso_sp* sp = &iso_sp[ipISO][nelem];
573 
574  // loop over all levels to include recombination diffuse continua,
575  // pick highest energy continuum point that opacities extend to
576  long ipHi = rfield.nflux;
577  // >>chng 06 aug 17, should go to numLevels_local instead of _max.
578  for( long n=0; n < sp->numLevels_local; n++ )
579  {
580  double Sum1level = 0.;
581  sp->fb[n].RadRecCon = 0.;
582  sp->fb[n].RadRecCoolCoef = 0.;
583  // the number is (2 pi me k/h^2) ^ -3/2 * 8 pi/c^2 / ge - it includes
584  // the stat weight of the free electron in the demominator
585  double gamma = 0.5*MILNE_CONST*sp->st[n].g()/iso_ctrl.stat_ion[ipISO]/phycon.te/phycon.sqrte;
586 
587  // loop over all recombination continua
588  // escaping part of recombinations are added to rfield.ConEmitLocal
589  // added to ConInterOut at end of routine
590  for( long nu=sp->fb[n].ipIsoLevNIonCon-1; nu < ipHi; nu++ )
591  {
592  // dwid used to adjust where within WIDFLX exp is evaluated -
593  // weighted to lower energy due to exp(-energy/T)
594  double dwid = 0.2;
595 
596  // this is the term in the negative exponential Boltzmann factor
597  // for continuum emission
598  double arg = (rfield.anu[nu]-sp->fb[n].xIsoLevNIonRyd+
599  rfield.widflx[nu]*dwid)/phycon.te_ryd;
600  arg = MAX2(0.,arg);
601  // don't bother evaluating for this or higher energies if
602  // Boltzmann factor is tiny.
603  if( arg > SEXP_LIMIT )
604  break;
605 
606  /* photon is in photons cm^3 s^-1 per cell */
607  double photon = gamma*exp(-arg)*rfield.widflx[nu]*
608  opac.OpacStack[ nu-sp->fb[n].ipIsoLevNIonCon + sp->fb[n].ipOpac ] *
609  rfield.anu2[nu];
610 
611  Sum1level += photon;
612 
613  fixit(); // We need to include induced recombination in diffuse spectrum.
614  // Probably best just to do the whole thing here, then no need for induced terms in iso_photo.
615 
616  if( lgUpdateContinuum )
617  {
618  /* total local diffuse emission units photons cm-3 s-1 cell-1,*/
619  rfield.ConEmitLocal[nzone][nu] += (realnum)(photon*EdenAbund);
620 
621  // sp->fb[n].RadRecomb[ipRecEsc] is escape probability
622  // rfield.DiffuseEscape is local emission that escapes this zone
623  rfield.DiffuseEscape[nu] +=
624  (realnum)(photon*EdenAbund*sp->fb[n].RadRecomb[ipRecEsc]);
625  }
626 
627  // total RRC radiative recombination continuum
628  sp->fb[n].RadRecCon += rfield.anu[nu] *
629  emergent_line( photon*EdenAbund/2. , photon*EdenAbund/2. ,
630  // energy on fortran scale
631  nu+1 );
632 
633  double energyAboveThresh = rfield.anu[nu] - sp->fb[n].xIsoLevNIonRyd;
634  energyAboveThresh = MAX2( 0., energyAboveThresh );
635  sp->fb[n].RadRecCoolCoef += energyAboveThresh * photon *
636  sp->fb[n].RadRecomb[ipRecNetEsc];
637  }
638 
639  // convert to erg cm-3 s-1
640  sp->fb[n].RadRecCon *= EN1RYD;
641  sp->fb[n].RadRecCoolCoef *= EN1RYD;
642  /* this will be used below to confirm case B sum */
643  if( n > 0 )
644  {
645  /* SumCaseB will be sum to all excited */
646  SumCaseB += Sum1level;
647  }
648  }
649 
650  // no RRC emission from levels that do not exist
651  for( long n=sp->numLevels_local;n<sp->numLevels_max; n++ )
652  {
653  sp->fb[n].RadRecCon = 0.;
654  sp->fb[n].RadRecCoolCoef = 0.;
655  }
656 
657  /* this is check on self-consistency */
658  sp->CaseBCheck = MAX2(sp->CaseBCheck,
659  (realnum)(SumCaseB/sp->RadRec_caseB));
660  }
661 
662  TeUsed[ipISO][nelem] = phycon.te;
663 
664  return;
665 }
666 
#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
void RT_diffuse(void)
Definition: rt_diffuse.cpp:34
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
void RT_iso_integrate_RRC(const long ipISO, const long nelem, const bool lgUpdateContinuum)
Definition: rt_diffuse.cpp:547
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
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
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