cloudy  trunk
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
rt_escprob.cpp
Go to the documentation of this file.
1 /* This file is part of Cloudy and is copyright (C)1978-2017 by Gary J. Ferland and
2  * others. For conditions of distribution and use see copyright notice in license.txt */
3 /*esc_CRDwing_1side fundamental escape probability radiative transfer routine, for complete redistribution */
4 /*esc_PRD_1side fundamental escape probability radiative transfer routine for incomplete redistribution */
5 /*RTesc_lya escape prob for hydrogen atom Lya, using Hummer and Kunasz results,
6  * called by hydropesc */
7 /*esc_PRD escape probability radiative transfer for incomplete redistribution */
8 /*esc_CRDwing escape probability for CRD with wings */
9 /*esc_CRDcore escape probability for CRD with no wings */
10 /*esca0k2 derive Hummer's K2 escape probability for Doppler core only */
11 /*RT_DestProb returns line destruction probability due to continuum opacity */
12 /*RT_line_electron_scatter evaluate electron scattering escape probability */
13 /*RT_LineWidth determine half width of any line with known optical depths */
14 #include "cddefines.h"
15 
16 
17 #include "dense.h"
18 #include "conv.h"
19 #include "opacity.h"
20 #include "taulines.h"
21 #include "pressure.h"
22 #include "wind.h"
23 #include "rt.h"
24 #include "iso.h"
25 #include "rfield.h"
26 #include "rt_escprob.h"
27 #include "integrate.h"
28 
33 STATIC double RT_DestHummer(
34  double beta);
35 /*RT_line_electron_scatter evaluate electron scattering escape probability */
37  /* the em line we will work on */
38  const TransitionProxy& t ,
39  realnum DopplerWidth);
40 
41 // NEW_MASE_ESCAPE needs further checking, + reconsideration of
42 // various (1-Pesc) factors
43 const bool NEW_PELEC_ESC=false, NEW_MASE_ESCAPE=false;
44 
45 namespace
46 {
47  inline double tau_from_here(double tau_tot, double tau_in)
48  {
49  const double SCALE = 10.;
50  double tt = tau_tot - tau_in;
51  if (0)
52  {
53  /* help convergence by not letting tau go to zero at back edge of
54  * when there was a bad guess for the total optical depth
55  * note that this test is seldom hit since RTMakeStat does check
56  * for overrun */
57  if( tt < 0. )
58  {
59  tt = tau_in/SCALE;
60  }
61  }
62  else
63  {
64  // Alternatively, allow tau_from_here to go to zero, and then
65  // increase again. This will give more or less the right
66  // distribution of tau values, though with tau_out estimated to
67  // be zero somewhere inside the layer rather than at the edge.
68  //
69  // The iteration 1 inward-only treatment is equivalent to this,
70  // if the estimated tau_out is set to be zero.
71  //
72  // I'm playing all the right notes -- just not
73  // necessarily in the right order.
74  //
75  // -- Eric Morecambe, 1971
76  tt = fabs(tt);
77  }
78  return tt;
79  }
80 
81  /*escConE2 one of the forms of the continuum escape probability */
82  class my_Integrand_escConE2
83  {
84  public:
85  double chnukt_ContTkt, chnukt_ctau;
86 
87  double operator() (double x) const
88  {
89  return exp(-chnukt_ContTkt*(x-1.))/x*e2(chnukt_ctau/POW3(x));
90  }
91  };
92 
93  /* a continuum escape probability */
94  class my_Integrand_conrec
95  {
96  public:
97  double chnukt_ContTkt;
98 
99  double operator() (double x) const
100  {
101  return exp(-chnukt_ContTkt*(x-1.))/x;
102  }
103  };
104 }
105 
106 /*escmase escape probability for negative (masing) optical depths,*/
107 STATIC double escmase(double tau);
108 /*RTesc_lya_1side fit Hummer and Kunasz escape probability for hydrogen atom Lya */
109 STATIC void RTesc_lya_1side(double taume,
110  double beta,
111  realnum *esc,
112  realnum *dest,
113  /* position of line in frequency array on c scale */
114  long ipLine );
115 
116 double esc_PRD_1side(double tau,
117  double a)
118 {
119  double atau,
120  b,
121  escinc_v;
122 
123  DEBUG_ENTRY( "esc_PRD_1side()" );
124  ASSERT( a>0.0 );
125 
126  /* this is one of the three fundamental escape probability routines
127  * the three are esc_CRDwing_1side, esc_PRD_1side, and RTesc_lya
128  * it computes esc prob for incomplete redistribution
129  * */
130 # if 0
131  if( strcmp(rt.chEscFunSubord,"SIMP") == 0 )
132  {
133  /* this set with "escape simple" command, used for debugging */
134  escinc_v = 1./(1. + tau);
135  return escinc_v;
136  }
137 # endif
138 
139  if( tau < 0. )
140  {
141  /* line mased */
142  escinc_v = escmase(tau);
143  }
144  else
145  {
146  /* first find coefficient b(tau) */
147  atau = a*tau;
148  if( atau > 1. )
149  {
150  b = 1.6 + (3.*pow(2.*a,-0.12))/(1. + atau);
151  }
152  else
153  {
154  double sqrtatau = sqrt(atau);
155  b = 1.6 + (3.*pow(2.*a,-0.12))*sqrtatau/(1. + sqrtatau);
156  }
157  b = MIN2(6.,b);
158 
159  escinc_v = 1./(1. + b*tau);
160  }
161  return escinc_v;
162 }
163 
164 /*esc_CRDwing_1side fundamental escape probability radiative transfer routine, for complete redistribution */
165 double esc_CRDwing_1side(double tau,
166  double a )
167 {
168  DEBUG_ENTRY( "esc_CRDwing_1side()" );
169 
170  /* this is one of the three fundamental escape probability routines
171  * the three are esc_CRDwing_1side, esc_PRD_1side, and RTesc_lya
172  * it computes esc prob for complete redistribution with wings
173  * computes escape prob for complete redistribution in one direction
174  * */
175 
176  /* this is the only case that this routine computes,
177  * and is the usual case for subordinate lines,
178  * complete redistribution with damping wings */
179 
180  double esccom_v = esca0k2(tau);
181 
182  // Escape probability correction for finite damping
183  // Results agree to +/- 20% from a=1e-3->1e3, no change for a->0
184 
185  double sqrta = sqrt(a);
186  double scal = a*(1.0+a+tau)/(POW2(1.0+a)+a*tau);
187  double pwing = scal*((tau > 0.0) ? sqrta/sqrt(a+2.25*SQRTPI*tau) : 1.0);
188  return esccom_v*(1.0-pwing)+pwing;
189 }
190 
191 /*RTesc_lya escape prob for hydrogen atom Lya, using
192  >>refer La escp Hummer, D.G., & Kunasz, P.B., 1980, ApJ, 236, 609
193  * called by hydropesc, return value is escape probability */
194 double RTesc_lya(
195  /* the inward escape probability */
196  double *esin,
197  /* the destruction probility */
198  double *dest,
199  /* abundance of the species */
200  double abund,
201  const TransitionProxy& t,
202  realnum DopplerWidth)
203 {
204  double beta,
205  conopc,
206  escla_v;
207  realnum dstin,
208  dstout;
209 
210  DEBUG_ENTRY( "RTesc_lya()" );
211 
212  /*
213  * this is one of the three fundamental escape probability functions
214  * the three are esc_CRDwing_1side, esc_PRD_1side, and RTesc_lya
215  * evaluate esc prob for LA
216  * optical depth in outer direction always defined
217  */
218 
219  /* incomplete redistribution */
220  conopc = opac.opacity_abs[ t.ipCont()-1 ];
222  conopc += dense.eden*SIGMA_THOMSON;
223 
224  if( abund > 0. )
225  {
226  /* the continuous opacity is positive, we have a valid soln */
227  beta = conopc/(abund/SQRTPI*t.Emis().opacity()/
228  DopplerWidth + conopc);
229  }
230  else
231  {
232  /* abundance is zero, set miniumum dest prob */
233  beta = 1e-10;
234  }
235 
236  /* find rt.wayin, the escape prob in inward direction */
238  t.Emis().TauIn(),
239  beta,
240  &rt.wayin,
241  &dstin,
242  /* position of line in energy array on C scale */
243  t.ipCont()-1);
244 
245  ASSERT( (rt.wayin <= 1.) && (rt.wayin >= 0.) && (dstin <= 1.) && (dstin >= 0.) );
246 
247  /* find rt.wayout, the escape prob in outward direction */
249  tau_from_here(t.Emis().TauTot(),t.Emis().TauIn()),
250  beta,
251  &rt.wayout,
252  &dstout,
253  t.ipCont()-1);
254 
255  ASSERT( (rt.wayout <= 1.) && (rt.wayout >= 0.) && (dstout <= 1.) && (dstout >= 0.) );
256 
257  /* esc prob is mean of in and out */
258  escla_v = (rt.wayin + rt.wayout)/2.;
259  /* the inward escaping part of the line */
260  *esin = rt.wayin;
261 
262  /* dest prob is mean of in and out */
263  *dest = (dstin + dstout)/2.f;
264  /* >>chng 02 oct 02, sum of escape and dest prob must be less then unity,
265  * for very thin models this forces dest prob to go to zero,
266  * rather than the value of DEST0, caught by Jon Slavin */
267  // \todo Dest prob can be > 0 for maser lines, see Elitzur, 1990ApJ...363..638E
268  *dest = (realnum)MIN2( *dest , 1.-escla_v );
269  /* but dest prob can't be negative */
270  *dest = (realnum)MAX2(0., *dest );
271 
272  /* fraction of line emitted in inward direction */
273  rt.fracin = rt.wayin/(rt.wayin + rt.wayout);
274  ASSERT( escla_v >=0. && *dest>=0. && *esin>=0. );
275  return escla_v;
276 }
277 
278 /*esc_PRD escape probability radiative transfer for incomplete redistribution */
279 inline double esc_2side_base(
280  double tau,
281  double tau_out,
282  double damp,
283  double (*esc_1side)(double,double))
284 {
285 
286  DEBUG_ENTRY( "esc_2side_base()" );
287 
288  ASSERT( damp > 0. );
289 
290  /* find escape prob for incomp redis, average of two 1-sided probs*/
291 
292  rt.wayin = (realnum)esc_1side(tau,damp);
293 
294  double escgrd_v;
295  if( iteration > 1 )
296  {
297  double tt = tau_from_here(tau_out, tau);
298 
299  rt.wayout = (realnum)esc_1side(tt,damp);
300  rt.fracin = rt.wayin/(rt.wayin + rt.wayout);
301  escgrd_v = 0.5*(rt.wayin + rt.wayout);
302  }
303  else
304  {
305  /* outward optical depth not defined, dont estimate fraction out */
306  rt.fracin = 0.5;
307  rt.wayout = rt.wayin;
308  escgrd_v = rt.wayin;
309  }
310 
311  ASSERT( escgrd_v > 0. );
312  return escgrd_v;
313 }
314 
315 double esc_PRD(double tau,
316  double tau_out,
317  double damp )
318 {
319  return esc_2side_base(tau, tau_out, damp, esc_PRD_1side);
320 }
321 
322 /*esc_CRDwing escape probability radiative transfer for CRDS in core only */
323 double esc_CRDwing(double tau_in,
324  double tau_out,
325  double damp)
326 {
327  return esc_2side_base(tau_in, tau_out, damp, esc_CRDwing_1side);
328 }
329 
330 /*esc_CRDwing escape probability radiative transfer for incomplete redistribution */
331 double esc_CRDcore(double tau_in,
332  double tau_out)
333 {
334  double escgrd_v,
335  tt;
336 
337  DEBUG_ENTRY( "esc_CRDcore()" );
338 
339  /* find escape prob for CRD with damping wings, average of two 1-sided probs*/
340 
341  /* crd with wings */
342 
343  rt.wayin = (realnum)esca0k2(tau_in);
344 
345  if( iteration > 1 )
346  {
347  /* outward optical depth if defined */
348  /* >>chng 03 jun 07, add test for masers here */
349  if( tau_out <0 || tau_in < 0. )
350  {
351  /* we have a maser, use smallest optical depth to damp it out */
352  tt = MIN2( tau_out , tau_in );
353  }
354  else
355  {
356  tt = tau_from_here(tau_out, tau_in);
357  }
358 
359  rt.wayout = (realnum)esca0k2(tt);
360  rt.fracin = rt.wayin/(rt.wayin + rt.wayout);
361  escgrd_v = 0.5*(rt.wayin + rt.wayout);
362  }
363  else
364  {
365  /* outward optical depth not defined, dont estimate fraction out */
366  rt.fracin = 0.5;
367  rt.wayout = rt.wayin;
368  escgrd_v = rt.wayin;
369  }
370 
371  ASSERT( escgrd_v > 0. );
372  return escgrd_v;
373 }
374 
375 /*esca0k2 derive Hummer's K2 escape probability for Doppler core only */
376 double esca0k2(double taume)
377 {
378  double arg,
379  esca0k2_v,
380  suma,
381  sumb,
382  sumc,
383  sumd,
384  tau;
385  static const double a[5]={1.00,-0.1117897,-0.1249099917,-9.136358767e-3,
386  -3.370280896e-4};
387  static const double b[6]={1.00,0.1566124168,9.013261660e-3,1.908481163e-4,
388  -1.547417750e-7,-6.657439727e-9};
389  static const double c[5]={1.000,19.15049608,100.7986843,129.5307533,-31.43372468};
390  static const double d[6]={1.00,19.68910391,110.2576321,169.4911399,-16.69969409,
391  -36.664480000};
392 
393  DEBUG_ENTRY( "esca0k2()" );
394 
395  /* compute Hummer's K2 escape probability function for a=0
396  * using approx from
397  * >>refer line escp Hummer, D.G., 1981, JQRST, 26, 187.
398  *
399  * convert to David's opacity */
400  tau = taume*SQRTPI;
401 
402  if( tau < 0. )
403  {
404  /* the line mased */
405  esca0k2_v = escmase(taume);
406 
407  }
408  else if( tau < 0.01 )
409  {
410  esca0k2_v = 1. - 2.*tau;
411 
412  }
413  else if( tau <= 11. )
414  {
415  suma = a[0] + tau*(a[1] + tau*(a[2] + tau*(a[3] + a[4]*tau)));
416  sumb = b[0] + tau*(b[1] + tau*(b[2] + tau*(b[3] + tau*(b[4] +
417  b[5]*tau))));
418  esca0k2_v = tau/2.5066283*log(tau/SQRTPI) + suma/sumb;
419 
420  }
421  else
422  {
423  /* large optical depth limit */
424  arg = 1./log(tau/SQRTPI);
425  sumc = c[0] + arg*(c[1] + arg*(c[2] + arg*(c[3] + c[4]*arg)));
426  sumd = d[0] + arg*(d[1] + arg*(d[2] + arg*(d[3] + arg*(d[4] +
427  d[5]*arg))));
428  esca0k2_v = (sumc/sumd)/(2.*tau*sqrt(log(tau/SQRTPI)));
429  }
430  return esca0k2_v;
431 }
432 
433 /*escmase escape probability for negative (masing) optical depths */
434 STATIC void FindNeg( void )
435 {
436  DEBUG_ENTRY( "FindNeg()" );
437 
438  /* Generic atoms & molecules from databases
439  * added by Humeshkar Nemala*/
440  for (int ipSpecies=0; ipSpecies < nSpecies; ++ipSpecies)
441  {
442  for( EmissionList::iterator em=dBaseTrans[ipSpecies].Emis().begin();
443  em != dBaseTrans[ipSpecies].Emis().end(); ++em)
444  {
445  if((*em).TauIn() < -1. )
446  DumpLine((*em).Tran());
447  }
448  }
449 
450  /* now do the level 2 lines */
451  for( long i=0; i < nWindLine; i++ )
452  {
453  if( (*TauLine2[i].Hi()).IonStg() < (*TauLine2[i].Hi()).nelem()+1-NISO )
454  {
455  /* check if a line was a strong maser */
456  if( TauLine2[i].Emis().TauIn() < -1. )
457  DumpLine(TauLine2[i]);
458  }
459  }
460 
461  /* now do the hyperfine structure lines */
462  for( size_t i=0; i < HFLines.size(); i++ )
463  {
464  /* check if a line was a strong maser */
465  if( HFLines[i].Emis().TauIn() < -1. )
466  DumpLine(HFLines[i]);
467  }
468 
469  return;
470 }
471 
472 STATIC double escmase(double tau)
473 {
474  double escmase_v;
475 
476  DEBUG_ENTRY( "escmase()" );
477 
478  /* this is the only routine that computes maser escape probabilities */
479  ASSERT( tau <= 0. );
480 
481  if( tau > -0.1 )
482  {
483  escmase_v = 1. - tau*(0.5 - tau/6.);
484  }
485  else if( tau > -30. )
486  {
487  escmase_v = (1. - exp(-tau))/tau;
488  }
489  else
490  {
491  fprintf( ioQQQ, " DISASTER escmase called with 2big tau%10.2e\n",
492  tau );
493  fprintf( ioQQQ, " This is zone number%4ld\n", nzone );
494  FindNeg();
495  ShowMe();
497  }
498 
499  ASSERT( escmase_v >= 1. );
500  return escmase_v;
501 }
502 
503 /*esccon continuum escape probability */
504 double esccon(double tau,
505  double hnukt)
506 {
507  double dinc,
508  escpcn_v,
509  sumesc,
510  sumrec;
511 
512  DEBUG_ENTRY( "esccon()" );
513 
514  /* computes continuum escape probabilities */
515  if( tau < 0.01 )
516  {
517  escpcn_v = 1.;
518  return escpcn_v;
519  }
520 
521  else if( hnukt > 1. && tau > 100. )
522  {
523  escpcn_v = 1e-20;
524  return escpcn_v;
525  }
526 
527  my_Integrand_conrec func_conrec;
528  func_conrec.chnukt_ContTkt = hnukt;
529  Integrator<my_Integrand_conrec,Gaussian32> conrec(func_conrec);
530 
531  my_Integrand_escConE2 func_escConE2;
532  func_escConE2.chnukt_ContTkt = hnukt;
533  func_escConE2.chnukt_ctau = tau;
534  Integrator<my_Integrand_escConE2,Gaussian32> escConE2(func_escConE2);
535 
536  dinc = 10./hnukt;
537  sumrec = conrec.sum(1.,1.+dinc);
538  sumesc = escConE2.sum(1.,1.+dinc);
539 
540  if( sumrec > 0. )
541  {
542  escpcn_v = sumesc/sumrec;
543  }
544  else
545  {
546  escpcn_v = 0.;
547  }
548  return escpcn_v;
549 }
550 
551 /*RTesc_lya_1side fit Hummer and Kunasz escape probability for hydrogen atom Lya */
552 STATIC void RTesc_lya_1side(double taume,
553  double beta,
554  realnum *esc,
555  realnum *dest,
556  /* position of line in frequency array on c scale */
557  long ipLine )
558 {
559  double esc0,
560  fac,
561  fac1,
562  fac2,
563  tau,
564  taucon,
565  taulog;
566 
567  /* DEST0 is the smallest destruction probability to return
568  * in high metallicity models, in rt.h
569  const double DEST0=1e-8;*/
570 
571  DEBUG_ENTRY( "RTesc_lya_1side()" );
572 
573  /* fits to numerical results of Hummer and Kunasz Ap.J. 80 */
574  tau = taume*SQRTPI;
575 
576  /* this is the real escape probability */
577  esc0 = 1./((0.6451 + tau)*(0.47 + 1.08/(1. + 7.3e-6*tau)));
578 
579  esc0 = MAX2(0.,esc0);
580  esc0 = MIN2(1.,esc0);
581 
582  if( tau > 0. )
583  {
584  taulog = log10(MIN2(1e8,tau));
585  }
586  else
587  {
588  /* the line mased
589  *>>chng 06 sep 08, kill xLaMase
590  hydro.xLaMase = MIN2(hydro.xLaMase,(realnum)tau);*/
591  taulog = 0.;
592  *dest = 0.;
593  *esc = (realnum)esc0;
594  }
595 
596  if( beta > 0. )
597  {
598  taucon = MIN2(2.,beta*tau);
599 
600  if( taucon > 1e-3 )
601  {
602  fac1 = -1.25 + 0.475*taulog;
603  fac2 = -0.485 + 0.1615*taulog;
604  fac = -fac1*taucon + fac2*POW2(taucon);
605  fac = exp10(fac);
606  fac = MIN2(1.,fac);
607  }
608  else
609  {
610  fac = 1.;
611  }
612 
613  *esc = (realnum)(esc0*fac);
614  /* MIN puts cat at 50 */
615  *dest = (realnum)(beta/(0.30972 - MIN2(.28972,0.03541667*taulog)));
616  }
617 
618  else
619  {
620  *dest = 0.;
621  *esc = (realnum)esc0;
622  }
623 
624  *dest = MIN2(*dest,1.f-*esc);
625  *dest = MAX2(0.f,*dest);
626 
627  /* >>chng 99 apr 12, limit destruction prob in case where gas dominated by scattering.
628  * in this case scattering is much more likely than absorption on this event */
629  *dest = (realnum)( (1. - opac.albedo[ipLine]) * *dest + opac.albedo[ipLine]*DEST0);
630  /* this is for debugging H Lya */
631  {
632  /*@-redef@*/
633  enum {BUG=false};
634  /*@+redef@*/
635  if( BUG )
636  {
637  fprintf(ioQQQ,"scatdest tau %.2e beta%.2e 1-al%.2e al%.2e dest%.2e \n",
638  taume,
639  beta,
640  (1. - opac.albedo[ipLine]),
641  opac.albedo[ipLine] ,
642  *dest
643  );
644  }
645  }
646  return;
647 }
648 
649 namespace
650 {
651  double destfit(double beta)
652  {
653  if (NEW_PELEC_ESC)
654  {
655  // 'plausible' function that captures HK limit for small beta,
656  // and tends to 1 for beta -> infinity
657  return 7.0*beta/(1+7.0*beta);
658  }
659  else
660  {
661  /* fits to
662  * >>>refer la esc Hummer and Kunasz 1980 Ap.J. 236,609.
663  * the max value of 1e-3 is so that we do not go too far
664  * beyond what Hummer and Kunasz did, discussed in
665  * >>refer rt esc proc Ferland, G.J., 1999, ApJ, 512, 247 */
668  return MIN2(1e-3,8.5*beta);
669  }
670  }
671 }
672 
673 /*RT_DestProb returns line destruction probability due to continuum opacity */
675  const TransitionProxy& t,
676  /* line width */
677  double DopplerWidth,
678  /* type of redistribution function */
679  const DestType& nCore)
680 {
681  double abund = t.Emis().PopOpc();
682  double crsec = t.Emis().opacity(); /* its line absorption cross section */
683  long int ipanu = t.ipCont();/* pointer to energy within continuum array, to get background opacity,
684  * this is on the f not c scale */
685  double escp = t.Emis().Pesc(); // Escape probability
686 
687  /* this will be the value we shall return */
688  double eovrlp_v;
689 
690  /* DEST0 is the smallest destruction probability to return
691  * in high metallicity models
692  * this was set to 1e-8 until 99nov18,
693  * in cooling flow model the actual Lya ots dest prob was 1e-16,
694  * and this lower limit of 1e-8 caused energy balance problems,
695  * since dest prob was 8 orders of magnitude too great.
696  * >>chng 99 nov 18, to 1e-20, but beware that comments indicate that
697  * this will cause problems with high metallicity clouds(?) */
698  /* >>chng 00 jun 04, to 0 since very feeble ionization clouds, with almost zero opacity,
699  * this was a LARGE number */
700  /*const double DEST0=1e-20;
701  const double DEST0=0.;*/
702 
703  DEBUG_ENTRY( "RT_DestProb()" );
704 
705  if (nCore.t == DestType::ipDEST_LYA)
706  {
707  eovrlp_v = (realnum) nCore.dest;
708  }
709  /* computes "escape probability" due to continuum destruction of
710  *
711  * if esc prob gt 1 then line is masing - return small number for dest prob */
712  /* >>>chng 99 apr 10, return min dest when scattering greater than abs */
713  /* no idea of opacity whatsoever, on very first soln for this model */
714  /* >>chng 05 mar 20, add test on line being above upper bound of frequency
715  * do not want to evaluate opacity in this case since off scale */
716  else if( (!NEW_MASE_ESCAPE && escp >= 1.0) || !conv.nTotalIoniz || ipanu >= rfield.nflux )
717  {
718  eovrlp_v = 0.;
719  }
720  else
721  {
722  /* find continuum opacity */
723  double conopc = opac.opacity_abs[ipanu-1];
725  conopc += dense.eden*SIGMA_THOMSON;
726 
727  ASSERT( crsec > 0. );
728 
729  /* may be no population, cannot use above test since return 0 not DEST0 */
730  if( abund <= 0. || conopc <= 0. )
731  {
732  /* do not set this to DEST0 since energy not then conserved */
733  eovrlp_v = 0.;
734  }
735  else
736  {
737  double opac_line = abund*crsec/DopplerWidth;
738  // Use multiplet opacity where positive
739  if( t.Emis().mult_opac() > 0.f )
740  opac_line = t.Emis().mult_opac();
741  /* fac of 1.7 convert to Hummer convention for line opacity */
742  double beta = conopc/(SQRTPI*opac_line + conopc);
743  if (NEW_PELEC_ESC)
744  beta = conopc/max(SQRTPI*opac_line,1e-6*conopc);
745  /* >>chng 04 may 10, rm * 1-pesc)
746  beta = MIN2(beta,(1.-escp)); */
747 
748  if( nCore.t == DestType::ipDEST_INCOM )
749  {
750  eovrlp_v = destfit(beta);
751  }
752  else if( nCore.t == DestType::ipDEST_K2 )
753  {
754  /* Doppler core only; a=0., Hummer 68 */
755  if (0 || NEW_PELEC_ESC)
756  {
757  // INACTIVE BRANCH
758  eovrlp_v = RT_DestHummer(beta);
759  }
760  else
761  {
762  eovrlp_v = destfit(beta);
763  }
764  }
765  else if( nCore.t == DestType::ipDEST_SIMPL )
766  {
767  /* this for debugging only
768  eovrlp_v = 8.5*beta;*/
769  /* >>chng 04 may 13, use same min function */
770  eovrlp_v = destfit(beta);
771  }
772  else
773  {
774  fprintf( ioQQQ, " chCore of %i not understood by RT_DestProb.\n",
775  nCore.t );
777  }
778 
779  if (!NEW_PELEC_ESC)
780  {
781  /* renorm to unity */
782  eovrlp_v /= 1. + eovrlp_v;
783  }
784 
785  /* multiply by 1-escape prob, since no destruction when optically thin */
786  if (NEW_MASE_ESCAPE)
787  {
788  double tau = tau_from_here(t.Emis().TauTot(), t.Emis().TauIn());
789  eovrlp_v *= MAX2(1. - escp, -escp*tau);
790  ASSERT(eovrlp_v >= 0.0); // need tau < 0 when escp goes through 1 for a maser
791  }
792  else
793  {
794  eovrlp_v *= 1. - escp;
795  }
796 
797  /*check results in bounds */
798  ASSERT( eovrlp_v >= 0. );
799  ASSERT( eovrlp_v <= 1. );
800 
801  {
802  /* debugging code for Lya problems */
803  /*@-redef@*/
804  enum {DEBUG_LOC=false};
805  /*@+redef@*/
806  if( DEBUG_LOC )
807  {
808  if( rfield.anu(ipanu-1)>0.73 && rfield.anu(ipanu-1)<0.76 &&
809  fp_equal( abund,
810  iso_sp[ipH_LIKE][ipHYDROGEN].trans(ipH2p,ipH1s).Emis().PopOpc()
811  ) )
812  {
813  fprintf(ioQQQ,"%li RT_DestProb\t%g\n",
814  nzone, eovrlp_v );
815  }
816  }
817  }
818  if (0 && t.chLabel() == "He 1 3888.63A")
819  {
820  static int i;
821  ++i;
822  fprintf(ioQQQ,"Found it %g %g %g %g %ld\n",
823  eovrlp_v,abund,conopc,escp,ipanu);
824  }
825 
826  /* >>chng 04 may 10, rm min */
827  /* this hack removed since no fundamental reason for it to be here,
828  * this should be added to scattering escape, if included at all */
829 # if 0
830  /* >>chng 99 apr 12, limit destruction prob in case where gas dominated by scattering.
831  * in this case scattering is much more likely than absorption on this event
832  eovrlp_v = (1. - opac.albedo[ipanu-1]) * eovrlp_v +
833  opac.albedo[ipanu-1]*DEST0; */
834  /* >>chng 01 aug 11, add factor of 3 for increase in mean free path, and min on 0 */
835  /*eovrlp_v = MAX2(DEST0,1. - 3.*opac.albedo[ipanu-1]) * eovrlp_v +
836  opac.albedo[ipanu-1]*DEST0;*/
837  eovrlp_v = POW2(1. - opac.albedo[ipanu-1]) * eovrlp_v +
838  opac.albedo[ipanu-1]*0.;
839 # endif
840  }
841  }
842  t.Emis().Pdest() = eovrlp_v;
843 
844  RT_line_electron_scatter( t , DopplerWidth );
845 }
846 
847 /*RT_line_electron_scatter evaluate electron scattering escape probability */
849  /* the em line we will work on */
850  const TransitionProxy& t ,
851  realnum DopplerWidth)
852 {
853 
854  DEBUG_ENTRY( "RT_line_electron_scatter()" );
855 
856  /* escape following scattering off an electron */
857  /* this is turned off with the no scattering escape command */
858  if( !rt.lgElecScatEscape )
859  {
860  t.Emis().Pelec_esc() = 0.;
861  return;
862  }
863 
864  if (NEW_PELEC_ESC)
865  {
866  double opac_electron = dense.eden*SIGMA_THOMSON;
867  double conopc = opac.opacity_abs[ t.ipCont()-1 ];
868  double conopc_tot = opac_electron+conopc;
869  if (conopc_tot > 0.0)
870  {
871  double fdest = conopc/conopc_tot;
872  t.Emis().Pelec_esc() = (1.-fdest)*t.Emis().Pdest();
873  t.Emis().Pdest() *= fdest;
874  }
875  }
876  else
877  {
878  // Use the lower level population rather than PopOpc, with correction
879  // for stimulated emission, to avoid negative opacities when line mases.
880  // Theory in form implemented does not apply for masing transition
881  double opac_line = (*t.Lo()).Pop() * t.Emis().opacity()/DopplerWidth;
882  if( t.Emis().mult_opac() > 0.f )
883  opac_line = t.Emis().mult_opac();
884 
885  /* the opacity in electron scattering */
886  double opac_electron = dense.eden*SIGMA_THOMSON;
887  /* this is equation 5 of
888  *>>refer line desp Netzer, H., Elitzur, M., & Ferland, G. J. 1985, ApJ, 299, 752*/
889  double opacity_ratio = opac_electron/(opac_electron+opac_line);
890  /* keep total probability less than 0.1 */
891  t.Emis().Pelec_esc() = (realnum)opacity_ratio * MAX2(0.f,1.f-t.Emis().Pesc()-t.Emis().Pdest());
892  }
893  return;
894 }
895 
896 /*RT_LineWidth compute line width (cm/sec), using optical depth array information
897  * this is where effects of wind are done */
898 double RT_LineWidth(const TransitionProxy& t, realnum DopplerWidth)
899 {
900  double RT_LineWidth_v,
901  aa,
902  atau,
903  b,
904  r,
905  vth;
906  realnum tau;
907 
908  DEBUG_ENTRY( "RT_LineWidth()" );
909 
910  /* uses line width from
911  * >>refer esc prob Bonilha et al. Ap.J. (1979) 233 649
912  * >>refer esc prob Elitzur Ferland 1986ApJ...305...35E.pdf
913  * return value is half velocity width*(1-ESC PROB) [cm s-1]
914  * this assumes incomplete redistribution, damp.tau^1/3 width */
915 
916  /* thermal width */
917  vth = DopplerWidth;
918 
919  /* optical depth in outer direction is defined
920  * on second and later iterations.
921  * smaller of inner and outer optical depths is chosen for esc prob */
922  if( iteration > 1 )
923  {
924  /* optical depth to shielded face */
925  realnum tauout = tau_from_here(t.Emis().TauTot(), t.Emis().TauIn());
926 
927  /* >>chng 99 apr 22 use smaller optical depth */
928  tau = MIN2( t.Emis().TauIn() , tauout );
929  }
930  else
931  {
932  tau = t.Emis().TauIn();
933  }
934  /* do not evaluate line width if quite optically thin - will be dominated
935  * by noise in this case */
936  if( tau <1e-3 )
937  return 0;
938 
939  t.Emis().damp() = t.Emis().dampXvel() / DopplerWidth;
940  ASSERT( t.Emis().damp() > 0. );
941 
942  double Pesc = esc_PRD_1side( tau , t.Emis().damp());
943 
944  /* max optical depth is thermalization length */
945  realnum therm = (realnum)(5.3e16/MAX2(1e-15,dense.eden));
946  if( tau > therm )
947  {
948  /* \todo 2 this seems to create an inconsistency as it changes tau
949  * for the purposes of this routine (to return the line-width),
950  * but this leaves the actual optical depth unchanged. */
951  pressure.lgPradDen = true;
952  tau = therm;
953  }
954 
955  /* >>chng 01 jun 23, use wind vel instead of rt since rt deleted */
956  /* >>chng 04 may 13, use thermal for subsonic cases */
957  if( ! wind.lgBallistic() )
958  {
959  /* static geometry */
960  /* esc prob has noise if smaller than FLT_EPSILON, or is masing */
961  if( (tau-opac.taumin)/100. < FLT_EPSILON )
962  {
963  RT_LineWidth_v = 0.;
964  }
965  else
966  {
967  if( tau <= 20. )
968  {
969  atau = -6.907755;
970  if( tau > 1e-3 )
971  atau = log(tau);
972  aa = 4.8 + 5.2*tau + (4.*tau - 1.)*atau;
973  b = 6.5*tau - atau;
974  }
975  else
976  {
977  ASSERT( t.Emis().damp()*tau >= 0.);
978  atau = log(MAX2(0.0001,tau));
979  aa = 1. + 2.*atau/pow(1. + 0.3*t.Emis().damp()*tau,0.6667) +
980  pow(6.5*t.Emis().damp()*tau,0.333);
981  b = 1.6 + 1.5/(1. + 0.20*t.Emis().damp()*tau);
982  }
983 
984  double escProb = Pesc + t.Emis().Pelec_esc() + t.Emis().Pdest();
985  RT_LineWidth_v = vth*0.8862*aa/b*(1. - MIN2( 1. , escProb) );
986 
987  /* small number roundoff can dominate this process */
988  if( escProb >= 1. - 100. * FLT_EPSILON )
989  RT_LineWidth_v = 0.;
990  }
991 
992  /* we want full width, not half width */
993  RT_LineWidth_v *= 2.;
994 
995  }
996  else
997  {
998  /* ballistic wind */
999  r = t.Emis().damp()*tau/PI;
1000  if( r <= 1. )
1001  {
1002  RT_LineWidth_v = vth*sqrt(log(MAX2(1.,tau))*.2821);
1003  }
1004  else
1005  {
1006  RT_LineWidth_v = 2.*fabs(wind.windv0);
1007  if( r*vth <= RT_LineWidth_v )
1008  {
1009  RT_LineWidth_v = vth*r*log(RT_LineWidth_v/(r*vth));
1010  }
1011  }
1012  }
1013 
1014  ASSERT( RT_LineWidth_v >= 0. );
1015  return RT_LineWidth_v;
1016 }
1017 
1018 /*RT_DestHummer evaluate Hummer's betaF(beta) function */
1019 STATIC double RT_DestHummer(double beta) /* beta is ratio of continuum to mean line opacity,
1020  * returns dest prob = beta F(beta) */
1021 {
1022  double fhummr_v,
1023  x;
1024 
1025  DEBUG_ENTRY( "RT_DestHummer()" );
1026 
1027  /* evaluates Hummer's F(beta) function for case where damping
1028  * constant is zero, are returns beta*F(beta)
1029  * fit to Table 1, page 80, of Hummer MNRAS 138, 73-108.
1030  * beta is ratio of continuum to line opacity; FUMMER is
1031  * product of his F() times beta; the total destruction prob
1032  * this beta is Hummer's normalization of the Voigt function */
1033 
1034  ASSERT( beta >= 0.);/* non-positive is unphysical */
1035  if( beta <= 0. )
1036  {
1037  fhummr_v = 0.;
1038  }
1039  else if( beta > 1.0 ) // Outside range of fit, set to asymptotic value
1040  {
1041  fhummr_v = 1.;
1042  }
1043  else
1044  {
1045  x = log10(beta);
1046  if( x < -5.5 )
1047  {
1048  fhummr_v = 3.8363 - 0.56329*x;
1049  }
1050  else if( x < -3.5 )
1051  {
1052  fhummr_v = 2.79153 - 0.75325*x;
1053  }
1054  else if( x < -2. )
1055  {
1056  fhummr_v = 1.8446 - 1.0238*x;
1057  }
1058  else
1059  {
1060  fhummr_v = 0.72500 - 1.5836*x;
1061  }
1062  fhummr_v *= beta;
1063  }
1064  return fhummr_v;
1065 }
1066 
1067 double RT_EscLVG( double tau, double sigma )
1068 {
1069  if (sigma == 0.0)
1070  {
1071  if (tau < 1e-5)
1072  return 1.0-tau/2.0;
1073  else
1074  return (1.0-exp(tau))/tau;
1075  }
1076  else
1077  {
1078  // Formula for LVG/Sobolev escape from Castor, Radiation
1079  // Hydrodynamics, p129
1080  const complex<double> z1 ( -1.915394, 1.201751 ),
1081  rz1 ( -0.492975, 0.216820),
1082  z2 (-0.048093, 3.655564),
1083  rz2 (-0.007025, 0.050338);
1084  const complex<double> t1 = sqrt( (tau/z1-1.0)/sigma), // (6.114)
1085  t2 = sqrt( (tau/z2-1.0)/sigma);
1086 
1087  return -2.0*real(
1088  rz1*(1.0+tau/(2.0*t1*sigma*z1)*log((t1-1.0)/(t1+1.0)))+
1089  rz2*(1.0+tau/(2.0*t2*sigma*z2)*log((t2-1.0)/(t2+1.0)))); // (6.113)
1090  }
1091 }
#define DEST0
Definition: rt.h:154
void DumpLine(const TransitionProxy &t)
Definition: transition.cpp:138
realnum & opacity() const
Definition: emission.h:638
size_t size(void) const
Definition: transition.h:331
realnum & Pelec_esc() const
Definition: emission.h:578
double * opacity_abs
Definition: opacity.h:103
double esc_CRDcore(double tau_in, double tau_out)
Definition: rt_escprob.cpp:331
double * albedo
Definition: opacity.h:112
double exp10(double x)
Definition: cddefines.h:1383
realnum wayout
Definition: rt.h:167
double esc_CRDwing(double tau_in, double tau_out, double damp)
Definition: rt_escprob.cpp:323
t_opac opac
Definition: opacity.cpp:5
const bool NEW_MASE_ESCAPE
Definition: rt_escprob.cpp:43
const int NISO
Definition: cddefines.h:310
realnum windv0
Definition: wind.h:11
double RT_EscLVG(double tau, double sigma)
double dest
Definition: rt_escprob.h:24
realnum & TauTot() const
Definition: emission.h:478
t_conv conv
Definition: conv.cpp:5
TransitionList HFLines("HFLines",&AnonStates)
bool lgPradDen
Definition: pressure.h:113
FILE * ioQQQ
Definition: cddefines.cpp:7
long int nzone
Definition: cddefines.cpp:14
STATIC void RT_line_electron_scatter(const TransitionProxy &t, realnum DopplerWidth)
Definition: rt_escprob.cpp:848
TransitionList TauLine2("TauLine2",&AnonStates)
#define MIN2(a, b)
Definition: cddefines.h:807
double anu(size_t i) const
Definition: mesh.h:111
long int nSpecies
Definition: taulines.cpp:22
t_dense dense
Definition: global.cpp:15
t_iso_sp iso_sp[NISO][LIMELM]
Definition: iso.cpp:11
bool lgElecScatEscape
Definition: rt.h:193
Wind wind
Definition: wind.cpp:5
long int iteration
Definition: cddefines.cpp:16
bool fp_equal(sys_float x, sys_float y, int n=3)
Definition: cddefines.h:858
bool lgBallistic(void) const
Definition: wind.h:31
void RT_DestProb(const TransitionProxy &t, double DopplerWidth, const DestType &nCore)
Definition: rt_escprob.cpp:674
t_abund abund
Definition: abund.cpp:5
double esccon(double tau, double hnukt)
Definition: rt_escprob.cpp:504
#define POW2
Definition: cddefines.h:983
const int ipH1s
Definition: iso.h:29
#define STATIC
Definition: cddefines.h:118
realnum & dampXvel() const
Definition: emission.h:598
EmissionList::reference Emis() const
Definition: transition.h:447
t_pressure pressure
Definition: pressure.cpp:9
t_rfield rfield
Definition: rfield.cpp:9
long & ipCont() const
Definition: transition.h:489
float realnum
Definition: cddefines.h:124
#define EXIT_FAILURE
Definition: cddefines.h:168
realnum & Pesc() const
Definition: emission.h:568
long max(int a, long b)
Definition: cddefines.h:821
#define cdEXIT(FAIL)
Definition: cddefines.h:484
double sum(double min, double max) const
Definition: integrate.h:44
realnum fracin
Definition: rt.h:174
long nWindLine
Definition: cdinit.cpp:19
double esc_PRD_1side(double tau, double a)
Definition: rt_escprob.cpp:116
long int nTotalIoniz
Definition: conv.h:159
string chLabel() const
Definition: transition.cpp:271
const int ipH2p
Definition: iso.h:31
realnum & Pdest() const
Definition: emission.h:588
STATIC double escmase(double tau)
Definition: rt_escprob.cpp:472
#define ASSERT(exp)
Definition: cddefines.h:617
qList::iterator Lo() const
Definition: transition.h:431
const int ipH_LIKE
Definition: iso.h:64
enum dest_t t
Definition: rt_escprob.h:23
double & mult_opac() const
Definition: emission.h:648
#define DEBUG_ENTRY(funcname)
Definition: cddefines.h:729
double & PopOpc() const
Definition: emission.h:658
STATIC void RTesc_lya_1side(double taume, double beta, realnum *esc, realnum *dest, long ipLine)
Definition: rt_escprob.cpp:552
double eden
Definition: dense.h:201
#define MAX2(a, b)
Definition: cddefines.h:828
STATIC double RT_DestHummer(double beta)
realnum & damp() const
Definition: emission.h:608
int fprintf(const Output &stream, const char *format,...)
Definition: service.cpp:1217
STATIC void FindNeg(void)
Definition: rt_escprob.cpp:434
const bool NEW_PELEC_ESC
Definition: rt_escprob.cpp:43
double e2(double x)
double pow(double x, int i)
Definition: cddefines.h:782
double RTesc_lya(double *esin, double *dest, double abund, const TransitionProxy &t, realnum DopplerWidth)
Definition: rt_escprob.cpp:194
double esca0k2(double taume)
Definition: rt_escprob.cpp:376
double esc_PRD(double tau, double tau_out, double damp)
Definition: rt_escprob.cpp:315
#define POW3
Definition: cddefines.h:990
void ShowMe(void)
Definition: service.cpp:205
vector< TransitionList > dBaseTrans
Definition: taulines.cpp:18
double esc_CRDwing_1side(double tau, double a)
Definition: rt_escprob.cpp:165
const int ipHYDROGEN
Definition: cddefines.h:348
double RT_LineWidth(const TransitionProxy &t, realnum DopplerWidth)
Definition: rt_escprob.cpp:898
long int nflux
Definition: rfield.h:48
static long int * ipLine
Definition: prt_linesum.cpp:14
realnum & TauIn() const
Definition: emission.h:458
realnum taumin
Definition: opacity.h:166
double esc_2side_base(double tau, double tau_out, double damp, double(*esc_1side)(double, double))
Definition: rt_escprob.cpp:279
realnum wayin
Definition: rt.h:167
t_rt rt
Definition: rt.cpp:5