cloudy  trunk
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
rt_line_one.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_line_escape do line radiative transfer,
4  * evaluates escape and destruction probability */
5 /* NB no wind distinction - that is done where optical depths are incremented
6  * with line opacity - rt_line_one_tauinc and in line width for rad pressure */
7 /*RT_line_fine_opacity do fine opacities for one line */
8 /*RT_line_electron_scatter evaluate electron scattering escape probability */
9 /*RT_line_pumping pumping by external and locally emitted radiation fields */
10 #include "cddefines.h"
11 #include "rfield.h"
12 #include "doppvel.h"
13 #include "dense.h"
14 #include "opacity.h"
15 #include "transition.h"
16 #include "conv.h"
17 #include "radius.h"
18 #include "rt.h"
19 #include "physconst.h"
20 #include "cosmology.h"
21 #include "thirdparty.h"
22 #include "hydrogenic.h"
23 
24 /*RT_line_pumping pumping by external and locally emitted radiation fields */
26  /* the em line we will work on */
27  const TransitionProxy& t ,
28  /* this is option to not include line self shielding across this zone.
29  * this can cause pump to depend on zone thickness, and leads to unstable
30  * feedback in some models with the large H2 molecule, due to Solomon
31  * process depending on zone thickness and level populations. */
32  bool lgShield_this_zone,
33  realnum DopplerWidth)
34 {
35  DEBUG_ENTRY( "RT_line_pumping()" );
36 
37  ASSERT( t.ipCont() >= 1 );
38 
39  /* pumping by incident and diffuse continua */
40  /* option to kill induced processes */
41  if( !rfield.lgInducProcess )
42  {
43  t.Emis().pump() = 0.;
44  }
45  else if( conv.lgFirstSweepThisZone || t.Emis().iRedisFun() == ipLY_A )
46  {
47  double dTau;
48  double shield_continuum;
49 
50  /* continuum shielding into this function */
51  shield_continuum = RT_continuum_shield_fcn( t );
52 
53  /* continuum upward pumping rate, A gu/gl abs prob occnum
54  * the "no induced" command causes continuum pumping to be set to 0
55  * this includes pumping by diffuse continuum */
56  t.Emis().pump() = t.Emis().Aul() * (*t.Hi()).g() / (*t.Lo()).g() * shield_continuum *(
58 
59  dTau = (t.Emis().PopOpc() * t.Emis().opacity() / DopplerWidth +
61 
62  if( lgShield_this_zone && dTau > 1e-3 )
63  {
64  /* correction for line optical depth across zone */
65  t.Emis().pump() *= log(1. + dTau ) / dTau;
66  }
67 
68  /*
69  * This is an option to account for intrinsic absorption or emission line the lyman
70  * lines. This is done without changing the incident coarse continuum.
71  * Check if continuum pumping of H Lyman lines to be multiplied by scale factor
72  * hydro.xLymanPumpingScaleFactor is set with atom h-like Lyman pumping scale command
73  * Lya pump rate is always updated - this is simplest way to finese intrinsic absorption
74  * or emission
75  */
76  if ( t.ipLo() == 0 && t.systemIs(iso_sp[ipH_LIKE][ipHYDROGEN].tr) )
77  {
79  }
80  /* NB NB line pumping by local diffuse emission is not included. */
81  }
82 
83  return;
84 }
85 
86 /*RT_line_electron_scatter evaluate electron scattering escape probability */
88  /* the em line we will work on */
89  const TransitionProxy& t ,
90  realnum DopplerWidth)
91 {
92 
93  DEBUG_ENTRY( "RT_line_electron_scatter()" );
94 
95  /* escape following scattering off an electron */
96  /* this is turned off with the no scattering escape command */
97  if( !rt.lgElecScatEscape )
98  {
99  t.Emis().Pelec_esc() = 0.;
100  return;
101  }
102 
103  // in the transition structure PopOpc is the pop relative to the ion,
104  // but has been converted to a physical population before this routine
105  // was called. No need to convert here */
106  double opac_line = (*t.Lo()).Pop() * t.Emis().opacity()/DopplerWidth;
107 
108  /* the opacity in electron scattering */
109  double opac_electron = dense.eden*6.65e-25;
110  /* this is equation 5 of
111  *>>refer line desp Netzer, H., Elitzur, M., & Ferland, G. J. 1985, ApJ, 299, 752*/
112  double opacity_ratio = opac_electron/(opac_electron+opac_line);
113  /* keep total probability less than 0.1 */
114  t.Emis().Pelec_esc() = (realnum)opacity_ratio * MAX2(0.f,1.f-t.Emis().Pesc()-t.Emis().Pdest());
115 
116  return;
117 }
118 
119 /*RT_line_escape do line radiative transfer escape and destruction probabilities
120  * this routine sets */
122  /* the em line we will work on */
123  const TransitionProxy &t,
124  /* Stark escape probability to be added to Pesc */
125  realnum pestrk,
126  realnum DopplerWidth,
127  bool lgGoodTau)
128 {
129  int nRedis = -1;
130 
131  DEBUG_ENTRY( "RT_line_escape()" );
132 
133  /* a runaway maser */
134  if( t.Emis().TauIn() < -30. )
135  {
136  fprintf( ioQQQ, "PROBLEM RT_line_escape called with large negative "
137  "optical depth, zone %.2f, setting lgAbort true.\n",
138  fnzone );
139  DumpLine(t);
140  /* return busted true instead of exit here, so that code will
141  * not hang under MPI */
142  lgAbort = true;
143  return;
144  }
145 
146  if( cosmology.lgDo )
147  {
148  /* Sobolev escape */
149  if( conv.lgFirstSweepThisZone && lgGoodTau )
150  {
151  realnum tau_Sobolev = t.Emis().TauIn();
152 
153  if( tau_Sobolev < 1E-5 )
154  {
155  t.Emis().Pesc() = 1.;
156  }
157  else
158  {
159  t.Emis().Pesc() = ( 1.f - exp( -1.f * tau_Sobolev ) )/ tau_Sobolev;
160  }
161 
162  /* inward escaping fraction */
163  t.Emis().FracInwd() = rt.fracin;
164  }
165  fixit(); // is this correct?
166  nRedis = ipDEST_K2;
167  }
168  /* static solution - which type of line will determine
169  * which redistribution function */
170  /* iRedisFun() == 1 - alpha resonance line, partial redistribution,
171  * ipPRD == 1 */
172  else if( t.Emis().iRedisFun() == ipPRD )
173  {
174  /* incomplete redistribution with wings */
175  if( conv.lgFirstSweepThisZone && lgGoodTau )
176  {
177  t.Emis().Pesc() = (realnum)esc_PRD( t.Emis().TauIn(), t.Emis().TauTot(), t.Emis().damp() );
178 
179  /* >>chng 03 jun 07, do not clobber esp prob when line is masing -
180  * this had effect of preventing total escape prob from getting larger than 1 */
181  if( pestrk > 0.f && t.Emis().Pesc() < 1.f )
182  t.Emis().Pesc() = min( 1.f, t.Emis().Pesc() + pestrk );
183 
184  /* inward escaping fraction */
185  t.Emis().FracInwd() = rt.fracin;
186  }
187  nRedis = ipDEST_INCOM;
188  }
189 
190  /* complete redistribution without wings - t.ipLnRedis is ipCRD == -1 */
191  else if( t.Emis().iRedisFun() == ipCRD )
192  {
193  if( conv.lgFirstSweepThisZone && lgGoodTau )
194  {
195  /* >>chng 01 mar -6, escsub will call any of several esc prob routines,
196  * depending of how core is set. We always want core-only for this option,
197  * so call esca0k2(tau) directly */
198  t.Emis().Pesc() = (realnum)esc_CRDcore( t.Emis().TauIn(), t.Emis().TauTot() );
199 
200  if( pestrk > 0.f && t.Emis().Pesc() < 1.f )
201  t.Emis().Pesc() = min( 1.f, t.Emis().Pesc() + pestrk );
202 
203  /* inward escaping fraction */
204  t.Emis().FracInwd() = rt.fracin;
205  }
206  nRedis = ipDEST_K2;
207  }
208 
209  /* CRD with wings, = 2 */
210  else if( t.Emis().iRedisFun() == ipCRDW )
211  {
212  /* complete redistribution with damping wings */
213  if( conv.lgFirstSweepThisZone && lgGoodTau )
214  {
215  t.Emis().Pesc() = (realnum)esc_CRDwing( t.Emis().TauIn(), t.Emis().TauTot(), t.Emis().damp() );
216 
217  if( pestrk > 0.f && t.Emis().Pesc() < 1.f )
218  t.Emis().Pesc() = min( 1.f, t.Emis().Pesc() + pestrk );
219 
220  /* inward escaping fraction */
221  t.Emis().FracInwd() = rt.fracin;
222  }
223  nRedis = ipDEST_K2;
224  }
225 
226  /* Lya is special case */
227  else if( t.Emis().iRedisFun() == ipLY_A )
228  {
229  /* incomplete redistribution with wings, for special case of Lya
230  * uses fits to Hummer & Kunasz numerical results
231  * this routine is different because escape and dest probs
232  * are evaluated together, so no test of lgDoEsc */
233  if( lgGoodTau )
234  {
235  double dest , esin;
236 
237  /* this will always evaluate escape prob, no matter what lgDoEsc is.
238  * The destruction prob comes back as dest */
239  t.Emis().Pesc() = (realnum)RTesc_lya( &esin, &dest, t.Emis().PopOpc(), t, DopplerWidth );
240 
241  if( pestrk > 0.f && t.Emis().Pesc() < 1.f )
242  t.Emis().Pesc() = min( 1.f, t.Emis().Pesc() + pestrk );
243 
244  /* this is current destruction rate */
245  t.Emis().Pdest() = (realnum)dest;
246 
247  /* this is fraction of line which is inward escaping */
248  t.Emis().FracInwd() = rt.fracin;
249  }
250  }
251  else
252  {
253  fprintf( ioQQQ, " RT_line_escape called with impossible redistribution function %d\n",
254  t.Emis().iRedisFun());
255  ShowMe();
257  }
258 
259  /* only do this if not Lya special case, since dest prob already done */
260  if( lgGoodTau && t.Emis().iRedisFun() != ipLY_A && t.Emis().opacity() > 0. )
261  {
262  t.Emis().Pdest() = (realnum)RT_DestProb(
263  /* the abundance of the species */
264  t.Emis().PopOpc(),
265  /* line center opacity in funny units (needs a vel) */
266  t.Emis().opacity(),
267  /* array index for line in continuum array,
268  * on f not c scale */
269  t.ipCont(),
270  /* line width in velocity units */
271  DopplerWidth,
272  /* escape probability */
273  t.Emis().Pesc(),
274  /* redistribution function */
275  nRedis);
276  }
277 
278  return;
279 }
280 
281 /*RT_line_fine_opacity do fine opacities for one line */
283  /* the em line we will work on */
284  const TransitionProxy& t ,
285  realnum DopplerWidth)
286 
287 {
288  DEBUG_ENTRY( "RT_line_fine_opacity()" );
289 
290  /* this is line center frequency, including bulk motion of gas */
291  long int ipLineCenter = t.Emis().ipFine() + rfield.ipFineConVelShift;
292 
293  /* define fine opacity fine grid fine mesh */
294  /* rfield.lgOpacityFine flag set false with no fine opacities command */
295  if( !conv.lgLastSweepThisZone || ipLineCenter < 0 || t.Emis().PopOpc() < SMALLFLOAT ||
296  ipLineCenter>rfield.nfine || !rfield.lgOpacityFine )
297  {
298  return;
299  }
300 
301  /* number of fine opacity cells corresponding to one doppler width for current
302  * temperature, velocity field, and nuclear mass,
303  * rfield.fine_opac_velocity_width is width per cell, cm/s */
304  realnum cells_wide_1x = DopplerWidth/rfield.fine_opac_velocity_width;
305 
306  /* line center opacity - type realnum since will add to fine opacity array,
307  * which is realnum */
308  realnum opac_line = (realnum)t.Emis().PopOpc() * t.Emis().opacity() / DopplerWidth;
309 
310  // this is effective optical depth to this point. Do not do line if
311  // this product is less than SMALLFLOAT
312  double dTauEffec = opac_line*radius.depth_x_fillfac;
313  if( dTauEffec < SMALLFLOAT )
314  return;
315 
316  /* core width of optically thick line, do 4x with exponential Doppler core,
317  * must be at least one cell, but profile is symmetric */
318  const bool doDamp = dTauEffec*t.Emis().damp()/9. > 0.1;
319  long int nCells_core = (long)(cells_wide_1x*4.f + 1.5f);
320  /* core is symmetric - make sure both upper and lower bounds
321  * are within continuum energy bin */
322  if( ipLineCenter - nCells_core < 1 )
323  nCells_core = ipLineCenter - 1;
324  if( ipLineCenter + nCells_core > rfield.nfine )
325  nCells_core = ipLineCenter -rfield.nfine - 1;
326 
327  /* want core to be at least one cell wide */
328  nCells_core = MAX2( 1 , nCells_core );
329 
330  long int nCells_damp;
331  /* include damping wings if optical depth is large */
332  if( doDamp )
333  {
334  // find number of cells to extend the damping wings - cells_wide_1x is one dop width
335  // tests with th85orion, stopping at half -h2 point, showed that 0.01 was
336  // needed for outer edge, given the definition of dTauEffec
337  realnum x = (realnum)sqrt( dTauEffec * t.Emis().damp()*100./SQRTPI ) * cells_wide_1x;
338  // test on size of x, which can exceed
339  // limits of long in extreme optical depths */
340  if( x<LONG_MAX )
341  {
342  nCells_damp = (long)x;
343 
344  if( ipLineCenter-nCells_damp < 1 )
345  nCells_damp = ipLineCenter-1;
346 
347  if( ipLineCenter+nCells_damp > rfield.nfine )
348  nCells_damp = rfield.nfine - ipLineCenter-1;
349  }
350  else
351  {
352  /* x was too big, just set to extreme range, which is
353  * number of cells to nearest boundary */
354  nCells_damp = MIN2( rfield.nfine-ipLineCenter , ipLineCenter )-1;
355  }
356  }
357  else
358  {
359  nCells_damp = nCells_core;
360  }
361 
362  static vector<realnum> xprofile, profile;
363  xprofile.resize(nCells_damp);
364  profile.resize(nCells_damp);
365 
366  for( long int i=0; i<nCells_damp; ++i )
367  {
368  /* distance from line center in units of doppler width */
369  xprofile[i] = (realnum) i/cells_wide_1x;
370  }
371 
372  VoigtH(t.Emis().damp(), &xprofile[0], &profile[0], nCells_damp);
373 
374  /* line center itself, must not double count here */
375  rfield.fine_opac_zone[ipLineCenter] += profile[0]*opac_line;
376  for( long int i=1; i<nCells_damp; ++i )
377  {
378  rfield.fine_opac_zone[ipLineCenter+i] += profile[i]*opac_line;
379  rfield.fine_opac_zone[ipLineCenter-i] += profile[i]*opac_line;
380  }
381 
382  return;
383 }
384 
385 /*RT_line_one do rt for emission line structure - calls RT_line_escape or RT_line_wind */
387  /* the em line we will work on */
388  const TransitionProxy &t,
389  /* this is option to not include line self shielding across this zone.
390  * this can cause pump to depend on zone thickness, and leads to unstable
391  * feedback in some models with the large H2 molecule, due to Solomon
392  * process depending on zone thickness and level populations. */
393  bool lgShield_this_zone,
394  /* Stark escape probability to be added to Pesc */
395  realnum pestrk,
396  realnum DopplerWidth )
397 {
398  DEBUG_ENTRY( "RT_line_one()" );
399 
400  // do nothing is population and this is not the very first call
401  // skip line transfer if requested with 'no line transfer' command, but never skip Lya
402  if( !rfield.lgDoLineTrans && (t.Emis().iRedisFun() != ipLY_A) )
403  {
404  return;
405  }
406 
407  /* line damping constant at current temperature */
408  t.Emis().damp() = t.Emis().dampXvel() / DopplerWidth;
409  ASSERT( t.Emis().damp() > 0. );
410 
411  // do not evaluate if no population
412  if( (*t.Lo()).Pop()<=SMALLFLOAT )
413  {
414  /* zero population, return after setting everything with side effects */
415  t.Emis().Pesc() = 1.f;
416 
417  /* inward escaping fraction */
418  t.Emis().FracInwd() = 0.5;
419 
420  /* pumping rate */
421  t.Emis().pump() = 0.;
422 
423  /* destruction probability */
424  t.Emis().Pdest() = 0.;
425  t.Emis().Pelec_esc() = 0.;
426 
427  return;
428  }
429 
430  /* option to keep track of population values during calls,
431  * print out data to make histogram */
432  enum {DEBUG_LOC=false};
433  if( DEBUG_LOC )
434  {
435  static long int nTau[100];
436  long n;
437 
438  if( nzone==0 )
439  {
440  for(n=0; n<100; ++n )
441  nTau[n] = 0;
442  }
443  if( (*t.Lo()).Pop()<=SMALLFLOAT )
444  n = 0;
445  else
446  n = (long)log10( (*t.Lo()).Pop() )+37;
447  n = MIN2( n , 99 );
448  n = MAX2( n , 0 );
449  ++nTau[n];
450  if( nzone > 183 )
451  {
452  for(n=0; n<100; ++n )
453  fprintf(ioQQQ,"%li\t%li\n", n , nTau[n] );
455  }
456  }
457 
458  // transition is below plasma frequency - photons not emitted
459  if( t.EnergyErg() / EN1RYD <= rfield.plsfrq )
460  {
461  t.Emis().Pesc() = SMALLFLOAT;
462  t.Emis().Pdest() = SMALLFLOAT;
463  t.Emis().Pelec_esc() = SMALLFLOAT;
464  t.Emis().pump() = SMALLFLOAT;
465  }
466  else
467  {
468 
469  /* this checks if we have overrun the optical depth scale,
470  * in which case the inward optical depth is greater than the
471  * previous iteration's total optical depth.
472  * We do not reevaluate escape probabilities if the optical depth
473  * scale has been overrun due to huge bogus change in solution
474  * that would result */
475  bool lgGoodTau = lgTauGood( t );
476 
477  // the last sweep through this zone is to do the fine opacities
478  // the populations are not updated on the last sweep so the
479  // line transfer details also should not be updated
481  RT_line_fine_opacity( t , DopplerWidth );
482  else
483  {
484  RT_line_escape( t, pestrk, DopplerWidth , lgGoodTau);
485  RT_line_electron_scatter( t , DopplerWidth );
486  RT_line_pumping( t , lgShield_this_zone , DopplerWidth );
487  }
488  }
489 
490  return;
491 }
492 
#define MIN2
Definition: cddefines.h:765
realnum xLymanPumpingScaleFactor
Definition: hydrogenic.h:116
long int & ipFine() const
Definition: emission.h:413
void DumpLine(const TransitionProxy &t)
Definition: transition.cpp:100
int & iRedisFun() const
Definition: emission.h:403
realnum & opacity() const
Definition: emission.h:593
realnum EnergyErg() const
Definition: transition.h:78
realnum & Pelec_esc() const
Definition: emission.h:533
double * opacity_abs
Definition: opacity.h:95
t_opac opac
Definition: opacity.cpp:5
STATIC void RT_line_pumping(const TransitionProxy &t, bool lgShield_this_zone, realnum DopplerWidth)
Definition: rt_line_one.cpp:25
const realnum SMALLFLOAT
Definition: cpu.h:178
bool lgFirstSweepThisZone
Definition: conv.h:155
double RTesc_lya(double *esin, double *dest, double abund, const TransitionProxy &t, realnum DopplerWidth)
Definition: rt_escprob.cpp:271
#define MAX2
Definition: cddefines.h:786
realnum fine_opac_velocity_width
Definition: rfield.h:386
realnum & TauTot() const
Definition: emission.h:433
void RT_line_one(const TransitionProxy &t, bool lgShield_this_zone, realnum pestrk, realnum DopplerWidth)
t_conv conv
Definition: conv.cpp:5
realnum * OccNumbContEmitOut
Definition: rfield.h:74
bool lgOpacityFine
Definition: rfield.h:421
t_dense dense
Definition: dense.cpp:24
static double * g
Definition: species2.cpp:28
FILE * ioQQQ
Definition: cddefines.cpp:7
long int nzone
Definition: cddefines.cpp:14
double esc_CRDcore(double tau, double tout)
Definition: rt_escprob.cpp:445
bool lgDo
Definition: cosmology.h:44
STATIC void RT_line_fine_opacity(const TransitionProxy &t, realnum DopplerWidth)
double esc_PRD(double tau, double tout, double damp)
Definition: rt_escprob.cpp:363
t_iso_sp iso_sp[NISO][LIMELM]
Definition: iso.cpp:8
double depth_x_fillfac
Definition: radius.h:74
bool lgElecScatEscape
Definition: rt.h:262
#define STATIC
Definition: cddefines.h:101
realnum & dampXvel() const
Definition: emission.h:553
EmissionList::reference Emis() const
Definition: transition.h:408
double esc_CRDwing(double tau, double tout, double damp)
Definition: rt_escprob.cpp:399
t_rfield rfield
Definition: rfield.cpp:8
const int ipCRD
Definition: cddefines.h:296
long & ipCont() const
Definition: transition.h:450
float realnum
Definition: cddefines.h:107
#define EXIT_FAILURE
Definition: cddefines.h:144
const int ipLY_A
Definition: cddefines.h:300
realnum & Pesc() const
Definition: emission.h:523
long int ipFineConVelShift
Definition: rfield.h:418
qList::iterator Hi() const
Definition: transition.h:396
t_hydro hydro
Definition: hydrogenic.cpp:5
#define cdEXIT(FAIL)
Definition: cddefines.h:438
long min(int a, long b)
Definition: cddefines.h:727
realnum * OccNumbIncidCont
Definition: rfield.h:138
int & ipLo() const
Definition: transition.h:458
realnum fracin
Definition: rt.h:239
const double EN1RYD
Definition: physconst.h:179
t_radius radius
Definition: radius.cpp:5
bool lgDoLineTrans
Definition: rfield.h:117
realnum * fine_opac_zone
Definition: rfield.h:408
realnum & Pdest() const
Definition: emission.h:543
#define ASSERT(exp)
Definition: cddefines.h:582
qList::iterator Lo() const
Definition: transition.h:392
const int ipH_LIKE
Definition: iso.h:62
long nfine
Definition: rfield.h:402
double drad_x_fillfac
Definition: radius.h:71
const double SQRTPI
Definition: physconst.h:44
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:688
double & PopOpc() const
Definition: emission.h:603
t_cosmology cosmology
Definition: cosmology.cpp:11
double eden
Definition: dense.h:190
STATIC void RT_line_electron_scatter(const TransitionProxy &t, realnum DopplerWidth)
Definition: rt_line_one.cpp:87
bool lgInducProcess
Definition: rfield.h:252
realnum & damp() const
Definition: emission.h:563
const int ipDEST_K2
Definition: cddefines.h:302
double RT_DestProb(double abund, double crsec, long int ipanu, double widl, double escp, int nCore)
Definition: rt_escprob.cpp:774
const int ipPRD
Definition: cddefines.h:294
bool lgLastSweepThisZone
Definition: conv.h:157
STATIC void RT_line_escape(const TransitionProxy &t, realnum pestrk, realnum DopplerWidth, bool lgGoodTau)
realnum & FracInwd() const
Definition: emission.h:463
bool lgTauGood(const TransitionProxy &t)
Definition: transition.h:570
double fnzone
Definition: cddefines.cpp:15
void ShowMe(void)
Definition: service.cpp:181
const int ipHYDROGEN
Definition: cddefines.h:309
void fixit(void)
Definition: service.cpp:991
double RT_continuum_shield_fcn(const TransitionProxy &t)
realnum plsfrq
Definition: rfield.h:447
bool systemIs(const TransitionList *query) const
Definition: transition.h:343
realnum & Aul() const
Definition: emission.h:613
realnum & TauIn() const
Definition: emission.h:423
const int ipDEST_INCOM
Definition: cddefines.h:304
bool lgAbort
Definition: cddefines.cpp:10
void VoigtH(realnum a, const realnum v[], realnum y[], int n)
Definition: thirdparty.h:350
const int ipCRDW
Definition: cddefines.h:298
#define EXIT_SUCCESS
Definition: cddefines.h:142
t_rt rt
Definition: rt.cpp:5
double & pump() const
Definition: emission.h:473