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