27 double precision,
private :: he_abundance
30 double precision,
private :: rc_gamma
33 double precision,
private :: rc_gamma_1
36 double precision,
private :: invgam
47 integer,
parameter,
private :: n_voigt_table = 500
48 double precision,
parameter,
private :: voigt_logtau_min = -2.0d0
49 double precision,
parameter,
private :: voigt_logtau_max = 8.0d0
50 double precision,
parameter,
private :: voigt_a_ref = 4.7
d-4
51 double precision,
parameter,
private :: voigt_xmax = 6.0d0
52 double precision,
private :: voigt_e_table(n_voigt_table)
53 double precision,
private :: voigt_logtau_step
54 logical,
private :: voigt_table_ready = .false.
59 integer,
intent(in) :: ixI^L, ixO^L
60 double precision,
intent(in) :: w(ixI^S,nw)
61 double precision,
intent(in) :: x(ixI^S,1:ndim)
62 double precision,
intent(out):: res(ixI^S)
67 integer,
intent(in) :: ixI^L, ixO^L
68 double precision,
intent(in) :: w(ixI^S, nw)
69 double precision,
intent(in) :: x(ixI^S, 1:ndim)
70 double precision,
intent(out):: ne(ixI^S), nH(ixI^S)
76 integer,
intent(in) :: ixI^L, ixO^L
77 double precision,
intent(in) :: w(ixI^S,1:nw), x(ixI^S,1:ndim)
78 double precision,
intent(out) :: factor(ixI^S)
83 double precision,
intent(in) :: a, b
89 double precision :: rad_damp_height
90 double precision :: rad_damp_scale
93 double precision,
allocatable :: tcool(:), lcool(:), dldtcool(:)
94 double precision,
allocatable :: yc(:)
95 double precision :: tref, lref, tcoolmin,tcoolmax
96 double precision :: lgtcoolmin, lgtcoolmax, lgstep
100 double precision,
allocatable :: y_ppl(:), t_ppl(:), l_ppl(:), a_ppl(:)
103 double precision :: tlow
122 logical :: isppl = .false.
128 double precision :: rad_suppress_temp = 0.0d0
131 double precision :: suppress_temp_code = 0.0d0
133 logical :: rad_modify
135 logical :: rad_modify_sym
137 double precision :: rad_cut_hgt = 0.0d0
139 double precision :: rad_cut_dey = 0.15d0
141 double precision :: cfrac = 0.1d0
146 logical :: has_equi = .false.
148 logical :: subtract_equi = .false.
150 double precision,
allocatable :: frac_lowfip(:)
154 logical :: rad_newton = .false.
155 double precision :: rad_newton_pthick = 25.d0
156 double precision :: rad_newton_trad = 0.006d0
157 double precision :: rad_newton_rhosurf = 1.d4
160 double precision :: rad_taper_rho
162 double precision :: rad_taper_dey
165 logical :: rad_escape_prob = .false.
167 double precision :: rad_kappa_eff = 0.0d0
169 double precision :: rad_kappa_tcutoff = 0.0d0
171 double precision :: rad_kappa_alpha = 4.0d0
173 character(len=10) :: rad_escape_type =
'slab'
175 double precision :: rad_escape_tau_cutoff = 0.0d0
177 double precision :: rad_escape_height = 0.0d0
179 integer :: iw_colmass_ = -1
182 character(len=std_len) :: coolcurve
184 procedure(
get_subr1),
pointer,
nopass :: get_rho => null()
185 procedure(
get_subr1),
pointer,
nopass :: get_te => null()
186 procedure(
get_subr1),
pointer,
nopass :: get_rho_equi => null()
187 procedure(
get_subr1),
pointer,
nopass :: get_pthermal => null()
188 procedure(
get_subr1),
pointer,
nopass :: get_pthermal_equi => null()
189 procedure(
get_subr1),
pointer,
nopass :: get_var_rfactor => null()
193 procedure(
get_subr1),
pointer,
nopass :: get_temperature_equi => null()
196 logical :: ione = .false.
197 character(len=20) :: method =
'tables'
198 double precision :: inv_gamma_minus_1
199 double precision :: nh2rhofactor
200 double precision :: eion_per_nh
225 double precision,
allocatable :: y_mod(:,:)
226 double precision,
allocatable :: y_mod_max_per_row(:)
227 integer :: y_mod_n_nh = 0
228 double precision :: y_mod_lg_nh_min = 0.0d0
229 double precision :: y_mod_lg_nh_max = 0.0d0
230 double precision :: y_mod_lg_nh_step_inv = 0.0d0
233 logical :: y_mod_built = .false.
235 character(len=8) :: y_mod_quadrature =
'boole'
237 integer :: y_mod_n_sub = 16
265 logical :: lambda_needs_nenh_table = .false.
266 double precision,
allocatable :: nenh_eq_table(:)
274 integer,
intent(in) :: ixI^L, ixO^L
275 double precision,
intent(in) :: w(ixI^S,1:nw), x(ixI^S,1:ndim)
277 double precision,
intent(out) :: factor(ixI^S)
279 if(
associated(fl%get_rho2_factor)) &
280 call fl%get_rho2_factor(ixi^l,ixo^l,w,x,factor)
286 double precision,
intent(in) :: phys_gamma,He_abund
289 he_abundance=he_abund
300 integer,
parameter :: nquad = 64
301 double precision :: xq(nquad), wq(nquad)
302 double precision :: logtau, tau0, phi0, gx, integrand, E_val
303 double precision :: a_rep
306 if(voigt_table_ready)
return
309 voigt_logtau_step = (voigt_logtau_max - voigt_logtau_min) / dble(n_voigt_table - 1)
318 phi0 = 1.0d0 / sqrt(dpi)
326 do i = 1, n_voigt_table
327 logtau = voigt_logtau_min + dble(i-1) * voigt_logtau_step
328 tau0 = 10.0d0**logtau
330 if(tau0 < 1.0
d-6)
then
331 voigt_e_table(i) = 1.0d0
339 if(tau0 * gx > 500.0d0)
then
340 integrand = integrand + wq(k) * 1.0d0
341 else if(tau0 * gx < 1.0
d-10)
then
342 integrand = integrand + wq(k) * tau0 * gx
344 integrand = integrand + wq(k) * (1.0d0 - exp(-tau0 * gx))
348 e_val = 2.0d0 * phi0 / tau0 * integrand
350 voigt_e_table(i) = max(0.0d0, min(1.0d0, e_val))
353 voigt_table_ready = .true.
364 double precision,
intent(in) :: a, x
365 double precision :: gauss_part, lorentz_part, phi_x, phi_0
368 phi_0 = 1.0d0 / sqrt(dpi)
373 gauss_part = exp(-x*x) / sqrt(dpi)
374 if(x*x + a*a > 1.0
d-30)
then
375 lorentz_part = a / (dpi * (x*x + a*a))
379 phi_x = gauss_part + lorentz_part
389 double precision,
intent(in) :: tau
390 double precision :: logtau, frac
393 if(tau < 1.0d-6)
then
400 if(logtau <= voigt_logtau_min)
then
405 if(logtau >= voigt_logtau_max)
then
408 * (10.0d0**voigt_logtau_max) / tau
413 frac = (logtau - voigt_logtau_min) / voigt_logtau_step
415 idx = max(1, min(idx, n_voigt_table - 1))
416 frac = frac - dble(idx - 1)
419 + voigt_e_table(idx + 1) * frac
428 double precision,
intent(in) :: a, b
429 integer,
intent(in) :: n
430 double precision,
intent(out) :: x(n), w(n)
431 double precision :: xi, wi, p0, p1, p2, pp, z, z1
432 integer :: i, j, k, m
438 z = cos(dpi * (dble(i) - 0.25d0) / (dble(n) + 0.5d0))
447 p0 = ((2.0d0*dble(k) - 1.0d0) * z * p1 - (dble(k) - 1.0d0) * p2) / dble(k)
450 pp = dble(n) * (z * p0 - p1) / (z*z - 1.0d0)
453 if(abs(z - z1) < 1.0
d-15)
exit
457 xi = 0.5d0 * ((b - a) * z + (b + a))
458 wi = (b - a) / ((1.0d0 - z*z) * pp*pp)
462 x(n + 1 - i) = a + b - xi
471 subroutine read_params(fl)
476 end subroutine read_params
481 double precision,
dimension(:),
allocatable :: t_table
482 double precision,
dimension(:),
allocatable :: L_table
483 double precision,
dimension(:),
allocatable :: f_table
484 double precision :: ratt, fact1, fact2, fact3, dL1, dL2
485 double precision :: tstep, Lstep
486 integer :: ntable, i, j
488 Character(len=65) :: PPL_curves(1:6)
491 fl%coolcurve=
'JCcorona'
495 fl%rad_suppress_temp=0.0d0
497 fl%rad_cut_dey=0.15d0
498 fl%rad_modify=.false.
499 fl%rad_modify_sym=.false.
500 fl%rad_taper_rho=bigdouble
501 fl%rad_taper_dey=0.0d0
503 fl%rad_damp_height=0.5d0
504 fl%rad_damp_scale=0.15d0
508 if(fl%rad_escape_prob .and. fl%rad_escape_type ==
'voigt')
then
511 write(*,
'(A,I0,A,ES9.2,A,F4.1,A)') &
512 ' Voigt escape table: ', n_voigt_table,
' points, a_ref=', &
513 voigt_a_ref,
', x_max=', voigt_xmax,
' Doppler widths'
517 if (fl%fip_ > 0)
then
518 select case (trim(fl%coolcurve))
519 case (
'Dere_photo',
'Dere_photo_DM')
521 call mpistop(
"FIP cooling requires coolcurve='Dere_photo' or 'Dere_photo_DM'")
525 if(fl%rc_split) any_source_split=.true.
528 ppl_curves = [
Character(len=65) ::
'Hildner',
'FM',
'Rosner',
'Klimchuk',
'SPEX_DM_rough',
'SPEX_DM_fine']
529 do i=1,
size(ppl_curves)
530 if (ppl_curves(i)==fl%coolcurve)
then
538 select case(fl%coolcurve)
542 print *,
'Use Hildner (1974) piecewise power law'
544 allocate(fl%t_PPL(1:fl%n_PPL+1), fl%l_PPL(1:fl%n_PPL+1))
545 allocate(fl%a_PPL(1:fl%n_PPL))
548 fl%l_PPL(1:fl%n_PPL) = 10.d0**
x_hildner(1:
n_hildner) * (10.d0**fl%t_PPL(1:fl%n_PPL))**fl%a_PPL(1:fl%n_PPL)
552 print *,
'Use Forbes and Malherbe (1991)-like piecewise power law'
554 allocate(fl%t_PPL(1:fl%n_PPL+1), fl%l_PPL(1:fl%n_PPL+1))
555 allocate(fl%a_PPL(1:fl%n_PPL))
556 fl%t_PPL(1:fl%n_PPL+1) =
t_fm(1:
n_fm+1)
558 fl%l_PPL(1:fl%n_PPL) = 10.d0**
x_fm(1:
n_fm) * (10.d0**fl%t_PPL(1:fl%n_PPL))**fl%a_PPL(1:fl%n_PPL)
562 print *,
'Use piecewise power law according to Rosner (1978)'
564 print *,
'and extended by Priest (1982) from Van Der Linden (1991)'
566 allocate(fl%t_PPL(1:fl%n_PPL+1), fl%l_PPL(1:fl%n_PPL+1))
567 allocate(fl%a_PPL(1:fl%n_PPL))
570 fl%l_PPL(1:fl%n_PPL) = 10.d0**
x_rosner(1:
n_rosner) * (10.d0**fl%t_PPL(1:fl%n_PPL))**fl%a_PPL(1:fl%n_PPL)
574 print *,
'Use Klimchuk (2008) piecewise power law'
576 allocate(fl%t_PPL(1:fl%n_PPL+1), fl%l_PPL(1:fl%n_PPL+1))
577 allocate(fl%a_PPL(1:fl%n_PPL))
580 fl%l_PPL(1:fl%n_PPL) = 10.d0**
x_klimchuk(1:
n_klimchuk) * (10.d0**fl%t_PPL(1:fl%n_PPL))**fl%a_PPL(1:fl%n_PPL)
582 case(
'SPEX_DM_rough')
584 print *,
'Use the rough piece wise power law fit to the SPEX_DM curve (2009)'
586 allocate(fl%t_PPL(1:fl%n_PPL+1), fl%l_PPL(1:fl%n_PPL+1))
587 allocate(fl%a_PPL(1:fl%n_PPL))
594 print *,
'Use the fine, detailed piece wise power law fit to the SPEX_DM curve (2009)'
596 allocate(fl%t_PPL(1:fl%n_PPL+1), fl%l_PPL(1:fl%n_PPL+1))
597 allocate(fl%a_PPL(1:fl%n_PPL))
603 call mpistop(
"This piecewise power law is unknown")
607 fl%t_PPL(1:fl%n_PPL+1) = 10.d0**fl%t_PPL(1:fl%n_PPL+1)
609 if (si_unit) fl%l_PPL(1:fl%n_PPL) = fl%l_PPL(1:fl%n_PPL) * 10.0d0**(-13)
612 fl%t_PPL(1:fl%n_PPL+1) = fl%t_PPL(1:fl%n_PPL+1) / unit_temperature
613 fl%l_PPL(1:fl%n_PPL) = fl%l_PPL(1:fl%n_PPL) * unit_numberdensity**2 * unit_time / unit_pressure
616 fl%l_PPL(fl%n_PPL+1) = fl%l_PPL(fl%n_PPL) * ( fl%t_PPL(fl%n_PPL+1) / fl%t_PPL(fl%n_PPL) )**fl%a_PPL(fl%n_PPL)
617 fl%lref = fl%l_PPL(fl%n_PPL+1)
618 fl%tref = fl%t_PPL(fl%n_PPL+1)
621 fl%tcoolmin = fl%t_PPL(1)
622 fl%tcoolmax = fl%t_PPL(fl%n_PPL+1)
624 if (fl%tlow==bigdouble) fl%tlow=fl%tcoolmin
631 allocate(fl%tcool(1:fl%ncool), fl%Lcool(1:fl%ncool), fl%dLdtcool(1:fl%ncool))
632 allocate(fl%Yc(1:fl%ncool))
633 if(fl%fip_ > 0)
allocate(fl%frac_lowFIP(1:fl%ncool))
635 fl%tcool(1:fl%ncool) = zero
636 fl%Lcool(1:fl%ncool) = zero
637 fl%dLdtcool(1:fl%ncool) = zero
640 select case(fl%coolcurve)
644 print *,
'Use Colgan & Feldman (2008) cooling curve'
646 print *,
'This version only till 10000 K, beware for floor T treatment'
648 allocate(t_table(1:ntable))
649 allocate(l_table(1:ntable))
655 print *,
'Use Dalgarno & McCray (1972) cooling curve'
657 allocate(t_table(1:ntable))
658 allocate(l_table(1:ntable))
664 write(*,
'(3a)')
'Use MacDonald & Bailey (1981) cooling curve '&
665 ,
'as implemented in ZEUS-3D, with the values '&
666 ,
'from Dalgarno & McCRay (1972) for low temperatures.'
668 allocate(t_table(1:ntable))
669 allocate(l_table(1:ntable))
670 t_table(1:ntable) =
t_dm(1:21)
671 l_table(1:ntable) =
l_dm(1:21)
677 print *,
'Use Mellema & Lundqvist (2002) cooling curve '&
678 ,
'for zero metallicity '
680 allocate(t_table(1:ntable))
681 allocate(l_table(1:ntable))
687 print *,
'Use Mellema & Lundqvist (2002) cooling curve '&
688 ,
'for WC-star metallicity '
690 allocate(t_table(1:ntable))
691 allocate(l_table(1:ntable))
697 print *,
'Use Mellema & Lundqvist (2002) cooling curve '&
698 ,
'for solar metallicity '
700 allocate(t_table(1:ntable))
701 allocate(l_table(1:ntable))
707 print *,
'Use Cloudy based cooling curve '&
708 ,
'for ism metallicity '
710 allocate(t_table(1:ntable))
711 allocate(l_table(1:ntable))
717 print *,
'Use Cloudy based cooling curve '&
718 ,
'for solar metallicity '
720 allocate(t_table(1:ntable))
721 allocate(l_table(1:ntable))
725 case(
'composite_solar')
727 print *,
'Use composite cooling curve for solar metallicity:'
728 print *,
' T > 12 kK: Dere/Colgan/SPEX weighted average'
729 print *,
' 7-12 kK: SPEX CIE through the Lya transition'
730 print *,
' T < 7 kK: cloudy_solar fine-structure/molecular'
733 allocate(t_table(1:ntable))
734 allocate(l_table(1:ntable))
740 print *,
'Use SPEX cooling curve (Schure et al. 2009) '&
741 ,
'for solar metallicity '
743 allocate(t_table(1:ntable))
744 allocate(l_table(1:ntable))
755 fl%lambda_needs_nenh_table = .true.
759 print *,
'Use SPEX cooling curve for solar metallicity above 10^4 K. '
760 print *,
'At lower temperatures,use Dalgarno & McCray (1972), '
761 print *,
'with a pre-set ionization fraction of 10^-3. '
762 print *,
'as described by Schure et al. (2009). '
765 allocate(t_table(1:ntable))
766 allocate(l_table(1:ntable))
779 fl%lambda_needs_nenh_table = .true.
783 print *,
'Use Dere (2009) cooling curve for solar corona'
785 allocate(t_table(1:ntable))
786 allocate(l_table(1:ntable))
790 case(
'Dere_corona_DM')
792 print *,
'Combination of Dere_corona (2009) for high temperatures and'
794 print *,
'Dalgarno & McCray (1972), DM2, for low temperatures'
796 allocate(t_table(1:ntable))
797 allocate(l_table(1:ntable))
805 print *,
'Use Dere (2009) cooling curve for solar photophere'
807 allocate(t_table(1:ntable))
808 allocate(l_table(1:ntable))
809 if (fl%fip_ > 0)
allocate(f_table(1:ntable))
814 case(
'Dere_photo_DM')
816 print *,
'Combination of Dere_photo (2009) for high temperatures and'
818 print *,
'Dalgarno & McCray (1972), DM2, for low temperatures'
820 allocate(t_table(1:ntable))
821 allocate(l_table(1:ntable))
822 if (fl%fip_ > 0)
allocate(f_table(1:ntable))
827 if (fl%fip_ > 0)
then
828 f_table(1:
n_dm_2-1) = zero
834 print *,
'Use Colgan (2008) cooling curve'
836 allocate(t_table(1:ntable))
837 allocate(l_table(1:ntable))
843 print *,
'Combination of Colgan (2008) for high temperatures and'
845 print *,
'Dalgarno & McCray (1972), DM2, for low temperatures'
847 allocate(t_table(1:ntable))
848 allocate(l_table(1:ntable))
855 call mpistop(
"This coolingcurve is unknown")
860 fl%tcoolmax = t_table(ntable)
861 fl%tcoolmin = t_table(1)
862 ratt = (fl%tcoolmax-fl%tcoolmin)/( dble(fl%ncool-1) + smalldouble)
864 fl%tcool(1) = fl%tcoolmin
865 fl%Lcool(1) = l_table(1)
867 fl%tcool(fl%ncool) = fl%tcoolmax
868 fl%Lcool(fl%ncool) = l_table(ntable)
870 if (fl%fip_ > 0)
then
871 fl%frac_lowFIP(1) = f_table(1)
872 fl%frac_lowFIP(fl%ncool) = f_table(ntable)
876 fl%tcool(i) = fl%tcool(i-1)+ratt
880 if(fl%tcool(i) < t_table(j+1))
then
881 if(j.eq. ntable-1 )
then
882 fact1 = (fl%tcool(i)-t_table(j+1)) &
883 /(t_table(j)-t_table(j+1))
884 fact2 = (fl%tcool(i)-t_table(j)) &
885 /(t_table(j+1)-t_table(j))
886 fl%Lcool(i) = l_table(j)*fact1 + l_table(j+1)*fact2
887 if (fl%fip_ > 0)
then
888 fl%frac_lowFIP(i) = f_table(j)*fact1 + f_table(j+1)*fact2
892 dl1 = l_table(j+1)-l_table(j)
893 dl2 = l_table(j+2)-l_table(j+1)
894 jump =(max(dabs(dl1),dabs(dl2)) > 2*min(dabs(dl1),dabs(dl2)))
897 fact1 = (fl%tcool(i)-t_table(j+1)) &
898 /(t_table(j)-t_table(j+1))
899 fact2 = (fl%tcool(i)-t_table(j)) &
900 /(t_table(j+1)-t_table(j))
901 fl%Lcool(i) = l_table(j)*fact1 + l_table(j+1)*fact2
902 if (fl%fip_ > 0)
then
903 fl%frac_lowFIP(i) = f_table(j)*fact1 + f_table(j+1)*fact2
907 fact1 = ((fl%tcool(i)-t_table(j+1)) &
908 * (fl%tcool(i)-t_table(j+2))) &
909 / ((t_table(j)-t_table(j+1)) &
910 * (t_table(j)-t_table(j+2)))
911 fact2 = ((fl%tcool(i)-t_table(j)) &
912 * (fl%tcool(i)-t_table(j+2))) &
913 / ((t_table(j+1)-t_table(j)) &
914 * (t_table(j+1)-t_table(j+2)))
915 fact3 = ((fl%tcool(i)-t_table(j)) &
916 * (fl%tcool(i)-t_table(j+1))) &
917 / ((t_table(j+2)-t_table(j)) &
918 * (t_table(j+2)-t_table(j+1)))
919 fl%Lcool(i) = l_table(j)*fact1 + l_table(j+1)*fact2 &
921 if (fl%fip_ > 0)
then
922 fl%frac_lowFIP(i) = f_table(j)*fact1 + f_table(j+1)*fact2 &
932 fl%tcool(1:fl%ncool) = 10.0d0**fl%tcool(1:fl%ncool)
933 fl%Lcool(1:fl%ncool) = 10.0d0**fl%Lcool(1:fl%ncool)
936 if (si_unit) fl%Lcool(1:fl%ncool) = fl%Lcool(1:fl%ncool) * 10.0d0**(-13)
939 fl%tcool(1:fl%ncool) = fl%tcool(1:fl%ncool) / unit_temperature
940 fl%Lcool(1:fl%ncool) = fl%Lcool(1:fl%ncool) * unit_numberdensity**2 * unit_time / unit_pressure
942 fl%tcoolmin = fl%tcool(1)+smalldouble
944 if(fl%rad_suppress_temp > 0.0d0)
then
945 fl%suppress_temp_code = fl%rad_suppress_temp / unit_temperature
947 write(*,
'(A,ES10.3,A)')
' Cooling suppression active: disabled below T = ', &
948 fl%rad_suppress_temp,
' K within rad_cut_hgt'
952 if (fl%tlow==bigdouble) fl%tlow=fl%tcoolmin
953 fl%tcoolmax = fl%tcool(fl%ncool)
954 fl%lgtcoolmin = dlog10(fl%tcoolmin)
955 fl%lgtcoolmax = dlog10(fl%tcoolmax)
956 fl%lgstep = (fl%lgtcoolmax-fl%lgtcoolmin) * 1.d0 / (fl%ncool-1)
957 fl%dLdtcool(1) = (fl%Lcool(2)-fl%Lcool(1))/(fl%tcool(2)-fl%tcool(1))
958 fl%dLdtcool(fl%ncool) = (fl%Lcool(fl%ncool)-fl%Lcool(fl%ncool-1))/(fl%tcool(fl%ncool)-fl%tcool(fl%ncool-1))
961 fl%dLdtcool(i) = (fl%Lcool(i+1)-fl%Lcool(i-1))/(fl%tcool(i+1)-fl%tcool(i-1))
966 if (
allocated(f_table))
deallocate(f_table)
968 fl%tref = fl%tcoolmax
969 fl%lref = fl%Lcool(fl%ncool)
970 fl%Yc(fl%ncool) = zero
971 do i=fl%ncool-1, 1, -1
972 fl%Yc(i) = fl%Yc(i+1)
974 tstep = 1.0d-2*(fl%tcool(i+1)-fl%tcool(i))
975 call findl(fl%tcool(i+1)-j*tstep, lstep, fl)
976 fl%Yc(i) = fl%Yc(i) + fl%lref/fl%tref*tstep/lstep
981 rc_gamma_1=rc_gamma-1.d0
982 invgam = 1.d0/rc_gamma_1
992 double precision :: y_extra, factor
995 allocate(fl%y_PPL(1:fl%n_PPL+1))
997 fl%y_PPL(1:fl%n_PPL+1) = zero
1000 factor = fl%l_PPL(fl%n_PPL+1) * fl%t_PPL(i) / (fl%l_PPL(i) * fl%t_PPL(fl%n_PPL+1))
1001 if (fl%a_PPL(i) == 1.d0)
then
1002 y_extra = log( fl%t_PPL(i) / fl%t_PPL(i+1) )
1004 y_extra = 1 / (1 - fl%a_PPL(i)) * (1 - ( fl%t_PPL(i) / fl%t_PPL(i+1) )**(fl%a_PPL(i)-1) )
1006 fl%y_PPL(i) = fl%y_PPL(i+1) - factor*y_extra
1017 integer,
intent(in) :: ixI^L,ixO^L
1018 double precision,
intent(in) :: x(ixI^S,1:ndim)
1019 double precision :: w(ixI^S,1:nw)
1020 double precision,
intent(out):: coolrate(ixI^S)
1023 double precision :: pth(ixI^S),rho(ixI^S)
1024 double precision :: L1,Te(ixI^S),Rfactor(ixI^S)
1025 double precision :: ne(ixI^S), nH_arr(ixI^S), rho2_factor(ixI^S)
1026 double precision :: taper
1030 call fl%get_rho(w,x,ixi^l,ixo^l,rho)
1033 call fl%get_Te(w,x,ixi^l,ixo^l,te)
1034 call fl%get_ne_nH(ixi^l, ixo^l, w, x, ne, nh_arr)
1037 {
do ix^db = ixo^lim^db\}
1039 if(te(ix^d) <= fl%tcoolmin)
then
1041 else if(te(ix^d) >= fl%tcoolmax)
then
1043 l1 = l1*ne(ix^d)*nh_arr(ix^d)
1045 call findl(te(ix^d),l1,fl)
1046 l1 = l1*ne(ix^d)*nh_arr(ix^d)
1048 if(
slab_uniform .and. fl%rad_damp .and. x(ix^d,ndim) .le. fl%rad_damp_height)
then
1049 l1 = l1*exp(-(x(ix^d,ndim)-fl%rad_damp_height)**2/fl%rad_damp_scale**2)
1053 coolrate(ix^d) = l1*rho2_factor(ix^d)
1061 integer,
intent(in) :: ixI^L, ixO^L
1062 double precision,
intent(in) :: qdt, x(ixI^S, 1:ndim), wCT(ixI^S, 1:nw)
1063 double precision :: w(ixI^S, 1:nw)
1064 double precision,
intent(out) :: coolrate(ixI^S)
1066 double precision :: y1, y2, l1, tlocal2
1067 double precision :: Te(ixI^S), pnew(ixI^S), rho(ixI^S), rhonew(ixI^S)
1068 double precision :: emin, Lmax, fact, Rfactor(ixI^S), pth(ixI^S)
1069 double precision :: ne(ixI^S), nH_arr(ixI^S), rho2_factor(ixI^S)
1070 double precision :: taper
1072 double precision :: nH_val, log_nH, log_p_nH
1073 double precision :: eint_current
1074 double precision :: y_l, T_l
1077 call fl%get_pthermal(wct, x, ixi^l, ixo^l, pth)
1078 call fl%get_rho(wct, x, ixi^l, ixo^l, rho)
1079 call fl%get_var_Rfactor(wct,x,ixi^l,ixo^l,rfactor)
1080 call fl%get_Te(wct, x, ixi^l, ixo^l, te)
1081 call fl%get_ne_nH(ixi^l, ixo^l, wct, x, ne, nh_arr)
1085 call fl%get_pthermal(w, x, ixi^l, ixo^l, pnew)
1086 call fl%get_rho(w, x, ixi^l, ixo^l, rhonew)
1088 fact=fl%lref*qdt/fl%tref
1090 {
do ix^db = ixo^lim^db\}
1091 emin = rhonew(ix^d) * fl%tlow * rfactor(ix^d) * invgam
1093 nh_val = rhonew(ix^d) / fl%nH2rhoFactor
1094 log_nh = dlog10(nh_val)
1095 if (fl%method ==
'analytic')
then
1097 y_l = wct(ix^d, iw_ne) / nh_val
1098 eint_current = fl%inv_gamma_minus_1 * (1.0d0 + y_l) * nh_val * t_l &
1099 + y_l * fl%eion_per_nH * nh_val
1101 log_p_nh = dlog10(pnew(ix^d) / nh_val)
1102 eint_current = pnew(ix^d) * fl%p2eint(log_nh, log_p_nh)
1104 lmax = max(zero, (eint_current - emin) / qdt)
1106 lmax = max(zero, ( pnew(ix^d)*invgam - emin ) / qdt)
1111 if( te(ix^d)<= fl%tcoolmin)
then
1113 else if( te(ix^d)>= fl%tcoolmax )
then
1115 if (fl%lambda_needs_nenh_table)
then
1116 l1 = l1 * nh_arr(ix^d) * nh_arr(ix^d)
1118 l1 = l1 * ne(ix^d) * nh_arr(ix^d)
1120 l1 = min(l1*rho2_factor(ix^d), lmax)
1124 call findy(te(ix^d), y1, fl)
1125 if (fl%lambda_needs_nenh_table)
then
1126 y2 = y1 + rho2_factor(ix^d)*fact * nh_arr(ix^d) * nh_arr(ix^d) * rc_gamma_1 &
1127 / (rho(ix^d) * rfactor(ix^d))
1129 y2 = y1 + rho2_factor(ix^d)*fact * ne(ix^d) * nh_arr(ix^d) * rc_gamma_1 &
1130 / (rho(ix^d) * rfactor(ix^d))
1132 call findt(tlocal2, y2, fl)
1134 if (fl%ionE .and. fl%Y_mod_built .and. &
1135 dabs(te(ix^d) - tlocal2) > 1.0d-4 * te(ix^d))
then
1136 y1 =
findy_mod(te(ix^d), nh_arr(ix^d), fl)
1137 if (y1 == y1 .and. abs(y1) < huge(1.0d0))
then
1138 y2 = y1 + rho2_factor(ix^d)*qdt
1139 tlocal2 =
findt_mod(y2, nh_arr(ix^d), fl)
1143 if( tlocal2 <= fl%tcoolmin )
then
1145 else if (fl%ionE .and. &
1146 dabs(te(ix^d) - tlocal2) > 1.0d-4 * te(ix^d))
then
1148 l1 = ((fl%eint_from_T(log_nh, dlog10(te(ix^d))) &
1149 - fl%eint_from_T(log_nh, dlog10(tlocal2))) * nh_val) / qdt
1153 l1 = (te(ix^d)- tlocal2)*rho(ix^d)*rfactor(ix^d)*invgam/qdt
1159 if(
slab_uniform .and. fl%rad_damp .and. x(ix^d,ndim) .le. fl%rad_damp_height)
then
1160 l1 = l1*exp(-(x(ix^d,ndim)-fl%rad_damp_height)**2/fl%rad_damp_scale**2)
1167 qsourcesplit,active,fl)
1170 integer,
intent(in) :: ixI^L, ixO^L
1171 double precision,
intent(in) :: qdt, x(ixI^S,1:ndim), wCT(ixI^S,1:nw), wCTprim(ixI^S,1:nw)
1172 double precision,
intent(inout) :: w(ixI^S,1:nw)
1173 logical,
intent(in) :: qsourcesplit
1174 logical,
intent(inout) :: active
1176 double precision,
allocatable,
dimension(:^D&) :: Lequi
1177 double precision :: lb_t0_cool
1180 if(qsourcesplit .eqv.fl%rc_split)
then
1182 call cool_exact(qdt,ixi^l,ixo^l,wct,wctprim,w,x,fl)
1183 if(fl%subtract_equi)
then
1184 allocate(lequi(ixi^s))
1186 w(ixo^s,fl%e_) = w(ixo^s,fl%e_)+lequi(ixo^s)
1197 integer,
intent(in) :: ixI^L, ixO^L
1198 double precision,
intent(in) :: qdt, x(ixI^S,1:ndim), wCT(ixI^S,1:nw)
1199 double precision,
intent(inout) :: w(ixI^S,1:nw)
1201 double precision :: etherm(ixI^S), rho(ixI^S), Rfactor(ixI^S),emin
1204 call fl%get_pthermal(w,x,ixi^l,ixo^l,etherm)
1205 call fl%get_rho(w,x,ixi^l,ixo^l,rho)
1206 call fl%get_var_Rfactor(wct,x,ixi^l,ixo^l,rfactor)
1207 {
do ix^db = ixo^lim^db\}
1208 emin = rho(ix^d)*fl%tlow*rfactor(ix^d)
1209 if(etherm(ix^d) < emin)
then
1210 w(ix^d,fl%e_)=w(ix^d,fl%e_)+(emin-etherm(ix^d))*invgam
1220 integer,
intent(in) :: ix^D
1221 double precision,
intent(in) :: x_ndim, rho_val, Te_val
1223 double precision,
intent(out) :: factor
1224 double precision :: d_boundary, tau, kappa_local
1231 if(fl%rad_modify_sym)
then
1232 d_boundary = min(x_ndim - xprobmin^nd, xprobmax^nd - x_ndim)
1234 d_boundary = x_ndim - xprobmin^nd
1236 if(d_boundary .le. fl%rad_cut_hgt)
then
1237 if(fl%suppress_temp_code > 0.0d0)
then
1240 if(te_val .lt. fl%suppress_temp_code)
then
1246 factor = factor * exp(-((d_boundary - fl%rad_cut_hgt) / fl%rad_cut_dey)**2)
1251 if(rho_val .gt. fl%rad_taper_rho)
then
1252 factor = factor * exp(-((rho_val - fl%rad_taper_rho) / fl%rad_taper_dey)**2)
1258 if(fl%rad_escape_prob .and. fl%iw_colmass_ > 0)
then
1259 kappa_local = fl%rad_kappa_eff
1260 if(fl%rad_kappa_Tcutoff > 0.0d0)
then
1261 kappa_local = kappa_local &
1262 / (1.0d0 + (te_val / fl%rad_kappa_Tcutoff)**fl%rad_kappa_alpha)
1264 tau = kappa_local *
block%wextra(ix^d, fl%iw_colmass_)
1265 if(tau > 1.0d-6)
then
1266 select case(fl%rad_escape_type)
1269 factor = factor * (1.0d0 - exp(-tau)) / tau
1275 call mpistop(
"Unknown rad_escape_type: use 'slab' or 'voigt'")
1279 if(fl%rad_escape_tau_cutoff > 0.0d0)
then
1280 factor = factor * exp(-tau / fl%rad_escape_tau_cutoff)
1289 integer,
intent(in) :: ixI^L, ixO^L
1290 double precision,
intent(in) :: qdt, x(ixI^S,1:ndim), wCT(ixI^S,1:nw)
1291 double precision,
intent(inout) :: w(ixI^S,1:nw)
1293 double precision,
intent(out) :: res(ixI^S)
1295 double precision :: pth(ixI^S),rho(ixI^S),Rfactor(ixI^S),L1,Tlocal2
1296 double precision :: Te(ixI^S)
1297 double precision :: emin, Lmax
1298 double precision :: Y1, Y2
1299 double precision :: de, emax,fact
1300 double precision :: ne(ixI^S), nH_arr(ixI^S), rho2_factor(ixI^S)
1301 double precision :: taper
1303 double precision :: nH_val, log_nH, log_p_nH
1304 double precision :: eint_current
1305 double precision :: y_l, T_l
1308 call fl%get_pthermal_equi(wct,x,ixi^l,ixo^l,pth)
1309 call fl%get_rho_equi(wct,x,ixi^l,ixo^l,rho)
1310 call fl%get_var_Rfactor(wct,x,ixi^l,ixo^l,rfactor)
1313 call fl%get_temperature_equi(wct,x,ixi^l,ixo^l,te)
1314 call fl%get_ne_nH_equi(ixi^l, ixo^l, wct, x, ne, nh_arr)
1319 fact = fl%lref*qdt/fl%tref
1320 {
do ix^db = ixo^lim^db\}
1321 emin = rho(ix^d)*fl%tlow*rfactor(ix^d)*invgam
1323 nh_val = rho(ix^d) / fl%nH2rhoFactor
1324 log_nh = dlog10(nh_val)
1325 if (fl%method ==
'analytic')
then
1327 y_l = wct(ix^d, iw_ne) / nh_val
1328 eint_current = 1.5d0 * (1.0d0 + y_l) * nh_val * t_l &
1329 + y_l * fl%eion_per_nH * nh_val
1331 log_p_nh = dlog10(pth(ix^d) / nh_val)
1332 eint_current = pth(ix^d) * fl%p2eint(log_nh, log_p_nh)
1334 lmax = max(zero, (eint_current - emin) / qdt)
1335 emax = max(zero, eint_current - emin)
1337 lmax = max(zero,(pth(ix^d)*invgam-emin)/qdt)
1338 emax = max(zero, pth(ix^d)*invgam-emin)
1345 if( te(ix^d)<=fl%tcoolmin )
then
1347 else if( te(ix^d)>=fl%tcoolmax )
then
1349 if (fl%lambda_needs_nenh_table)
then
1350 l1 = l1 * nh_arr(ix^d) * nh_arr(ix^d)
1352 l1 = l1 * ne(ix^d) * nh_arr(ix^d)
1355 if(te(ix^d)<
block%wextra(ix^d,fl%Tcoff_))
then
1356 l1=l1*sqrt((te(ix^d)/
block%wextra(ix^d,fl%Tcoff_))**5)
1359 l1 = min(rho2_factor(ix^d)*l1,lmax)
1365 call findy(te(ix^d),y1,fl)
1366 if (fl%lambda_needs_nenh_table)
then
1367 y2 = y1 + rho2_factor(ix^d)*fact * nh_arr(ix^d) * nh_arr(ix^d) * rc_gamma_1 &
1368 / (rho(ix^d) * rfactor(ix^d))
1370 y2 = y1 + rho2_factor(ix^d)*fact * ne(ix^d) * nh_arr(ix^d) * rc_gamma_1 &
1371 / (rho(ix^d) * rfactor(ix^d))
1373 call findt(tlocal2,y2,fl)
1375 if (fl%ionE .and. fl%Y_mod_built .and. &
1376 dabs(te(ix^d) - tlocal2) > 1.0d-4 * te(ix^d))
then
1377 y1 =
findy_mod(te(ix^d), nh_arr(ix^d), fl)
1378 if (y1 == y1 .and. abs(y1) < huge(1.0d0))
then
1379 y2 = y1 + rho2_factor(ix^d)*qdt
1380 tlocal2 =
findt_mod(y2, nh_arr(ix^d), fl)
1384 if(tlocal2<=fl%tcoolmin)
then
1386 else if (fl%ionE .and. &
1387 dabs(te(ix^d) - tlocal2) > 1.0d-4 * te(ix^d))
then
1388 de = (fl%eint_from_T(log_nh, dlog10(te(ix^d))) &
1389 - fl%eint_from_T(log_nh, dlog10(tlocal2))) * nh_val
1392 de = (te(ix^d)-tlocal2)*rho(ix^d)*rfactor(ix^d)*invgam
1395 if(te(ix^d)<
block%wextra(ix^d,fl%Tcoff_))
then
1396 de=de*sqrt((te(ix^d)/
block%wextra(ix^d,fl%Tcoff_))**5)
1403 res(ix^d) = res(ix^d) * taper
1411 integer,
intent(in) :: ixI^L, ixO^L
1412 double precision,
intent(in) :: qdt, x(ixI^S,1:ndim), wCT(ixI^S,1:nw), wCTprim(ixI^S,1:nw)
1413 double precision,
intent(inout) :: w(ixI^S,1:nw)
1415 double precision :: Y1, Y2
1416 double precision :: L1, pth(ixI^S), Tlocal2, pnew(ixI^S)
1417 double precision :: rho(ixI^S), Te(ixI^S), rhonew(ixI^S), Rfactor(ixI^S)
1418 double precision :: emin, Lmax, fact
1419 double precision :: de, emax
1420 double precision :: ne(ixI^S), nH_arr(ixI^S), rho2_factor(ixI^S)
1421 double precision :: taper
1423 double precision :: nH_val, log_nH, log_p_nH
1424 double precision :: eint_current
1425 double precision :: eint_w(ixI^S)
1426 double precision :: de_thin, de_thick, emax_rem
1427 double precision :: T1, T2, p1(ixI^S), tau, xi
1428 double precision :: xi_arr(ixI^S), emax_rem_arr(ixI^S)
1429 double precision :: cool_fac, fip_prim, frac_lowFIP, fip_factor
1430 double precision :: y_loc, T_loc
1433 call fl%get_rho(wct,x,ixi^l,ixo^l,rho)
1434 call fl%get_var_Rfactor(wct,x,ixi^l,ixo^l,rfactor)
1435 call fl%get_Te(wct,x,ixi^l,ixo^l,te)
1436 call fl%get_ne_nH(ixi^l, ixo^l, wct, x, ne, nh_arr)
1438 call fl%get_pthermal(w,x,ixi^l,ixo^l,pnew)
1439 call fl%get_rho(w,x,ixi^l,ixo^l,rhonew)
1440 if (fl%ionE) eint_w(ixo^s) =
phys_get_ei(w, ixi^l, ixo^l)
1442 fact = fl%lref*qdt/fl%tref
1446 {
do ix^db = ixo^lim^db\}
1449 emin = rhonew(ix^d)*fl%tlow*rfactor(ix^d)*invgam
1452 nh_val = rhonew(ix^d) / fl%nH2rhoFactor
1453 log_nh = dlog10(nh_val)
1454 if (fl%method ==
'analytic')
then
1457 y_loc = wct(ix^d, iw_ne) / nh_val
1458 eint_current = fl%inv_gamma_minus_1 * (1.0d0 + y_loc) * nh_val * t_loc &
1459 + y_loc * fl%eion_per_nH * nh_val
1461 log_p_nh = dlog10(pnew(ix^d) / nh_val)
1462 eint_current = pnew(ix^d) * fl%p2eint(log_nh, log_p_nh)
1464 lmax = max(zero, eint_w(ix^d) - emin) / qdt
1465 emax = max(zero, eint_w(ix^d) - emin)
1467 lmax = max(zero,pnew(ix^d)*invgam-emin)/qdt
1468 emax = max(zero,pnew(ix^d)*invgam-emin)
1472 if (te(ix^d) <= fl%tcoolmin) cycle
1477 if (fl%rad_newton)
then
1478 xi = exp(-pnew(ix^d) / fl%rad_newton_pthick)
1479 xi = min(max(xi, zero), one)
1483 cool_fac = xi*rho2_factor(ix^d)
1485 if (fl%fip_ > 0)
then
1486 fip_prim = min(
maxfip, max(
minfip, wctprim(ix^d,fl%fip_)))
1489 fip_factor = one - frac_lowfip + fip_prim * frac_lowfip
1490 cool_fac = cool_fac * fip_factor
1494 if (
slab_uniform .and. fl%rad_damp .and. x(ix^d,ndim) <= xprobmin1 + fl%rad_damp_height)
then
1495 cool_fac = cool_fac * exp(-(x(ix^d,ndim)-xprobmin1-fl%rad_damp_height)**2/fl%rad_damp_scale**2)
1498 if (
slab_uniform .and. fl%rad_damp .and. x(ix^d,ndim) >= xprobmax1 - fl%rad_damp_height)
then
1499 cool_fac = cool_fac * exp(-(x(ix^d,ndim)-xprobmax1+fl%rad_damp_height)**2/fl%rad_damp_scale**2)
1503 if( te(ix^d)>=fl%tcoolmax )
then
1505 if (fl%lambda_needs_nenh_table)
then
1506 l1 = l1 * nh_arr(ix^d) * nh_arr(ix^d)
1508 l1 = l1 * ne(ix^d) * nh_arr(ix^d)
1512 if(te(ix^d)<
block%wextra(ix^d,fl%Tcoff_))
then
1513 l1=l1*sqrt((te(ix^d)/
block%wextra(ix^d,fl%Tcoff_))**5)
1520 w(ix^d,fl%e_) = w(ix^d,fl%e_) - de_thin
1528 call findy(te(ix^d),y1,fl)
1529 if (fl%lambda_needs_nenh_table)
then
1530 y2 = y1 + cool_fac * fact * nh_arr(ix^d) * nh_arr(ix^d) * rc_gamma_1 &
1531 / (rho(ix^d) * rfactor(ix^d))
1533 y2 = y1 + cool_fac * fact * ne(ix^d) * nh_arr(ix^d) * rc_gamma_1 &
1534 / (rho(ix^d) * rfactor(ix^d))
1536 call findt(tlocal2,y2,fl)
1541 if (fl%ionE .and. fl%Y_mod_built .and. &
1542 dabs(te(ix^d) - tlocal2) > 1.0d-4 * te(ix^d))
then
1543 y1 =
findy_mod(te(ix^d), nh_arr(ix^d), fl)
1544 if (y1 == y1 .and. abs(y1) < huge(1.0d0))
then
1545 y2 = y1 + cool_fac * qdt
1546 tlocal2 =
findt_mod(y2, nh_arr(ix^d), fl)
1550 if(tlocal2<=fl%tcoolmin)
then
1552 else if (fl%ionE .and. &
1553 dabs(te(ix^d) - tlocal2) > 1.0d-4 * te(ix^d))
then
1555 de = (fl%eint_from_T(log_nh, dlog10(te(ix^d))) &
1556 - fl%eint_from_T(log_nh, dlog10(tlocal2))) * nh_val
1560 de = (te(ix^d)-tlocal2)*rho(ix^d)*rfactor(ix^d)*invgam
1563 if(te(ix^d)<
block%wextra(ix^d,fl%Tcoff_))
then
1564 de=de*sqrt((te(ix^d)/
block%wextra(ix^d,fl%Tcoff_))**5)
1571 w(ix^d,fl%e_) = w(ix^d,fl%e_) - de_thin
1575 if (fl%rad_newton)
then
1577 emax_rem_arr(ix^d) = max(zero, emax - de_thin)
1582 if (fl%rad_newton)
then
1583 call fl%get_pthermal(w, x, ixi^l, ixo^l, p1)
1584 {
do ix^db = ixo^lim^db\}
1585 t1 = p1(ix^d) / (rho(ix^d) * rfactor(ix^d))
1586 tau = max(0.1d0 * sqrt( fl%rad_newton_rhosurf / rho(ix^d)), 4.d0 * qdt)
1587 t2 = fl%rad_newton_trad + (t1 - fl%rad_newton_trad) * exp(-qdt / tau)
1588 de_thick = min((one - xi_arr(ix^d)) * (t1 - t2) * rho(ix^d) * rfactor(ix^d) * invgam, emax_rem_arr(ix^d))
1589 w(ix^d,fl%e_) = w(ix^d,fl%e_) - de_thick
1598 double precision,
intent(IN) :: tpoint
1599 double precision,
intent(OUT) :: lpoint
1603 lpoint =fl%l_PPL(fl%n_PPL) * ( tpoint / fl%t_PPL(fl%n_PPL) )**fl%a_PPL(fl%n_PPL)
1605 lpoint = fl%Lcool(fl%ncool) * sqrt( tpoint / fl%tcoolmax)
1612 double precision,
intent(in) :: tpoint
1615 double precision :: lgtp
1618 if (tpoint <= fl%tcool(1))
then
1621 else if (tpoint >= fl%tcool(fl%ncool))
then
1626 lgtp = dlog10(tpoint)
1627 jl = int((lgtp - fl%lgtcoolmin) / fl%lgstep) + 1
1628 jl = max(1, min(fl%ncool-1, jl))
1631 + (tpoint - fl%tcool(jl)) &
1632 * (fl%frac_lowFIP(jl+1) - fl%frac_lowFIP(jl)) &
1633 / (fl%tcool(jl+1) - fl%tcool(jl))
1641 double precision,
intent(IN) :: tpoint
1642 double precision,
intent(OUT) :: Lpoint
1645 double precision :: lgtp
1649 i = maxloc(fl%t_PPL, dim=1, mask=fl%t_PPL<tpoint)
1650 lpoint = fl%l_PPL(i) * (tpoint / fl%t_PPL(i))**fl%a_PPL(i)
1652 lgtp = dlog10(tpoint)
1653 jl = int((lgtp - fl%lgtcoolmin) /fl%lgstep) + 1
1654 lpoint = fl%Lcool(jl)+ (tpoint-fl%tcool(jl)) &
1655 * (fl%Lcool(jl+1)-fl%Lcool(jl)) &
1656 / (fl%tcool(jl+1)-fl%tcool(jl))
1659 end subroutine findl
1665 double precision,
intent(IN) :: tpoint
1666 double precision,
intent(OUT) :: Ypoint
1669 double precision :: lgtp
1670 double precision :: y_extra,factor
1674 i = maxloc(fl%t_PPL, dim=1, mask=fl%t_PPL<tpoint)
1675 factor = fl%l_PPL(fl%n_PPL+1) * fl%t_PPL(i) / (fl%l_PPL(i) * fl%t_PPL(fl%n_PPL+1))
1676 if(fl%a_PPL(i)==1.d0)
then
1677 y_extra = log( fl%t_PPL(i) / tpoint )
1679 y_extra = 1 / (1 - fl%a_PPL(i)) * (1 - ( fl%t_PPL(i) / tpoint )**(fl%a_PPL(i)-1) )
1681 ypoint = fl%y_PPL(i) + factor*y_extra
1683 lgtp = dlog10(tpoint)
1684 jl = int((lgtp - fl%lgtcoolmin) / fl%lgstep) + 1
1686 if(jl < 1 .or. jl >= fl%ncool)
then
1687 write(*,
'(a,es14.6,a,i0,a,2es14.6)') &
1688 'findY: tpoint=',tpoint,
' jl=',jl,
' out of bounds [1,ncool-1]; tcoolmin/max=', &
1689 fl%tcoolmin,fl%tcoolmax
1690 call mpistop(
'findY: temperature index out of bounds')
1692 ypoint = fl%Yc(jl)+ (tpoint-fl%tcool(jl)) &
1693 * (fl%Yc(jl+1)-fl%Yc(jl)) &
1694 / (fl%tcool(jl+1)-fl%tcool(jl))
1697 end subroutine findy
1706 double precision,
intent(OUT) :: tpoint
1707 double precision,
intent(IN) :: Ypoint
1710 double precision :: factor
1711 integer :: jl,jc,jh,i
1714 i = minloc(fl%y_PPL, dim=1, mask=fl%y_PPL>ypoint)
1715 factor = fl%l_PPL(i) * fl%t_PPL(fl%n_PPL+1) / (fl%l_PPL(fl%n_PPL+1) * fl%t_PPL(i))
1716 if(fl%a_PPL(i)==1.d0)
then
1717 tpoint = fl%t_PPL(i) * exp( -1.d0 * factor * ( ypoint - fl%y_PPL(i)))
1719 tpoint = fl%t_PPL(i) * (1 - (1 - fl%a_PPL(i)) * factor * (ypoint - fl%y_PPL(i)))**(1 / (1 - fl%a_PPL(i)))
1722 if(ypoint >= fl%Yc(1))
then
1723 tpoint = fl%tcoolmin
1724 else if (ypoint == fl%Yc(fl%ncool))
then
1725 tpoint = fl%tcoolmax
1732 if(ypoint <= fl%Yc(jc))
then
1739 tpoint = fl%tcool(jl)+ (ypoint-fl%Yc(jl)) &
1740 * (fl%tcool(jl+1)-fl%tcool(jl)) &
1741 / (fl%Yc(jl+1)-fl%Yc(jl))
1744 end subroutine findt
1752 double precision,
intent(IN) :: tpoint
1753 double precision,
intent(OUT) :: dLpoint
1756 double precision :: lgtp
1759 lgtp = dlog10(tpoint)
1760 jl = int((lgtp -fl%lgtcoolmin) / fl%lgstep) + 1
1761 dlpoint = fl%dLdtcool(jl)+ (tpoint-fl%tcool(jl)) &
1762 * (fl%dLdtcool(jl+1)-fl%dLdtcool(jl)) &
1763 / (fl%tcool(jl+1)-fl%tcool(jl))
1824 type(
rc_fluid),
intent(inout) :: fl
1826 integer :: i, j, k, n_nH, ncool, N_sub
1827 double precision :: log_nH_j, nH_j_code, u_lo, u_hi, du_total, du_step
1828 double precision :: u_s, log_u_s, T_s, y_s, ne_s, Lambda_s, integ
1829 double precision,
allocatable :: u_at_T(:), f_node(:)
1830 double precision :: Y_max_global, Y_min_global
1840 if (.not. fl%ionE)
return
1841 if (fl%isPPL)
return
1842 if (fl%Y_mod_built)
return
1848 n_nh = fl%Y_mod_n_nH
1850 if (
mype == 0)
write(*,*)
' build_Y_mod_table: no (rho,T) inverse table allocated; skipping'
1855 select case (trim(fl%Y_mod_quadrature))
1856 case (
'simpson',
'boole')
1859 call mpistop(
'build_Y_mod_table: rc_Y_mod_quadrature must be simpson or boole')
1863 n_sub = max(2, fl%Y_mod_N_sub)
1865 if (trim(fl%Y_mod_quadrature) ==
'boole')
then
1866 if (mod(n_sub, 4) /= 0) n_sub = n_sub + (4 - mod(n_sub, 4))
1869 if (mod(n_sub, 2) /= 0) n_sub = n_sub + 1
1872 fl%Y_mod_n_nH = n_nh
1874 fl%Y_mod_lg_nH_step_inv = dble(n_nh - 1) &
1875 / (fl%Y_mod_lg_nH_max - fl%Y_mod_lg_nH_min)
1877 fl%Y_mod_lg_nH_step_inv = 0.0d0
1880 allocate(fl%Y_mod(n_nh, ncool))
1881 allocate(fl%Y_mod_max_per_row(n_nh))
1882 allocate(u_at_t(ncool))
1883 allocate(f_node(0:n_sub))
1886 log_nh_j = fl%Y_mod_lg_nH_min &
1887 + dble(j - 1) * (fl%Y_mod_lg_nH_max - fl%Y_mod_lg_nH_min) / dble(max(1, n_nh - 1))
1888 nh_j_code = 10.0d0**log_nh_j
1892 u_at_t(i) = fl%eint_from_T(log_nh_j, dlog10(fl%tcool(i)))
1895 fl%Y_mod(j, ncool) = 0.0d0
1898 do i = ncool - 1, 1, -1
1900 u_hi = u_at_t(i + 1)
1901 du_total = u_hi - u_lo
1902 if (du_total <= 0.0d0)
then
1904 fl%Y_mod(j, i) = fl%Y_mod(j, i + 1)
1907 du_step = du_total / dble(n_sub)
1911 u_s = u_lo + dble(k) * du_step
1912 if (u_s <= 0.0d0)
then
1916 log_u_s = dlog10(u_s)
1917 t_s = fl%T_from_eint(log_nh_j, log_u_s)
1918 if (fl%lambda_needs_nenh_table)
then
1927 y_s = fl%y_from_eint(log_nh_j, log_u_s)
1928 ne_s = y_s * nh_j_code
1930 if (t_s <= fl%tcoolmin)
then
1934 else if (t_s >= fl%tcoolmax)
then
1937 call findl(t_s, lambda_s, fl)
1939 if (ne_s * lambda_s > 0.0d0)
then
1940 f_node(k) = 1.0d0 / (ne_s * lambda_s)
1946 select case (trim(fl%Y_mod_quadrature))
1953 fl%Y_mod(j, i) = fl%Y_mod(j, i + 1) + integ
1956 fl%Y_mod_max_per_row(j) = fl%Y_mod(j, 1)
1962 fl%Y_mod_built = .true.
1965 y_max_global = maxval(fl%Y_mod_max_per_row)
1966 y_min_global = minval(fl%Y_mod_max_per_row)
1967 write(*,
'(A,I0,A,I0,A,A,A,I0)') &
1968 ' Y_mod table built: ', n_nh,
' nH x ', ncool,
' T quadrature=', &
1969 trim(fl%Y_mod_quadrature),
' N_sub=', n_sub
1970 write(*,
'(A,F8.4,A,F8.4)') &
1971 ' log10 nH range = ', fl%Y_mod_lg_nH_min,
' to ', fl%Y_mod_lg_nH_max
1972 write(*,
'(A,ES12.4,A,ES12.4,A)') &
1973 ' Y_max per row range = [', y_min_global,
', ', y_max_global,
'] code time'
1974 write(*,
'(A)')
' inverse=bisect (row-interpolated, O(log ncool))'
1981 integer,
intent(in) :: n
1982 double precision,
intent(in) :: f(0:n), h
1983 double precision :: s
1987 s = s + 4.0d0 * f(k)
1990 s = s + 2.0d0 * f(k)
1998 integer,
intent(in) :: n
1999 double precision,
intent(in) :: f(0:n), h
2000 double precision :: s
2004 s = s + 7.0d0 * f(k) &
2005 + 32.0d0 * f(k + 1) &
2006 + 12.0d0 * f(k + 2) &
2007 + 32.0d0 * f(k + 3) &
2010 s = s * 2.0d0 * h / 45.0d0
2018 integer,
intent(in) :: ncool
2019 double precision,
intent(in) :: y_row(ncool), t_grid(ncool), y_target
2020 double precision :: log_t_out
2021 integer :: jl, jh, jc
2022 double precision :: f_lo, f_hi
2024 if (y_target <= y_row(ncool))
then
2025 log_t_out = dlog10(t_grid(ncool))
2028 if (y_target >= y_row(1))
then
2029 log_t_out = dlog10(t_grid(1))
2037 if (jh - jl <= 1)
exit
2039 if (y_row(jc) >= y_target)
then
2047 if (f_lo == f_hi)
then
2048 log_t_out = 0.5d0 * (dlog10(t_grid(jl)) + dlog10(t_grid(jh)))
2051 log_t_out = dlog10(t_grid(jl)) &
2052 + (y_target - f_lo) / (f_hi - f_lo) &
2053 * (dlog10(t_grid(jh)) - dlog10(t_grid(jl)))
2060 double precision,
intent(in) :: te_loc, nh_loc
2062 double precision :: y_out
2063 double precision :: log_nh, log_t, ry, rx
2064 integer :: jy, jy1, jx, jx1
2065 double precision :: fy, fx
2067 log_nh = dlog10(nh_loc)
2068 log_t = dlog10(te_loc)
2071 ry = (log_nh - fl%Y_mod_lg_nH_min) * fl%Y_mod_lg_nH_step_inv
2072 ry = max(0.0d0, min(ry, dble(fl%Y_mod_n_nH - 1)))
2074 jy1 = min(jy + 1, fl%Y_mod_n_nH - 1)
2077 rx = (log_t - fl%lgtcoolmin) / fl%lgstep
2078 rx = max(0.0d0, min(rx, dble(fl%ncool - 1)))
2080 jx1 = min(jx + 1, fl%ncool - 1)
2083 y_out = (1.0d0 - fy) * ((1.0d0 - fx) * fl%Y_mod(jy + 1, jx + 1) &
2084 + fx * fl%Y_mod(jy + 1, jx1 + 1)) &
2085 + fy * ((1.0d0 - fx) * fl%Y_mod(jy1 + 1, jx + 1) &
2086 + fx * fl%Y_mod(jy1 + 1, jx1 + 1))
2095 double precision,
intent(in) :: y_target, nh_loc
2097 double precision :: t_out
2098 double precision :: log_nh, ry, fy
2100 double precision :: log_t_lo, log_t_hi
2101 integer :: jl, jh, jc, ncool
2102 double precision :: yc_lo, yc_hi
2104 log_nh = dlog10(nh_loc)
2105 ry = (log_nh - fl%Y_mod_lg_nH_min) * fl%Y_mod_lg_nH_step_inv
2106 ry = max(0.0d0, min(ry, dble(fl%Y_mod_n_nH - 1)))
2108 jy1 = min(jy + 1, fl%Y_mod_n_nH - 1)
2112 yc_lo = (1.0d0 - fy) * fl%Y_mod(jy + 1, 1) + fy * fl%Y_mod(jy1 + 1, 1)
2113 yc_hi = (1.0d0 - fy) * fl%Y_mod(jy + 1, ncool) + fy * fl%Y_mod(jy1 + 1, ncool)
2114 if (y_target >= yc_lo)
then
2118 if (y_target <= yc_hi)
then
2125 if (jh - jl <= 1)
exit
2127 if (((1.0d0 - fy) * fl%Y_mod(jy + 1, jc) + fy * fl%Y_mod(jy1 + 1, jc)) &
2134 yc_lo = (1.0d0 - fy) * fl%Y_mod(jy + 1, jl) + fy * fl%Y_mod(jy1 + 1, jl)
2135 yc_hi = (1.0d0 - fy) * fl%Y_mod(jy + 1, jh) + fy * fl%Y_mod(jy1 + 1, jh)
2136 log_t_lo = dlog10(fl%tcool(jl))
2137 log_t_hi = dlog10(fl%tcool(jh))
2138 if (yc_lo == yc_hi)
then
2139 t_out = fl%tcool(jl)
2141 t_out = 10.0d0**(log_t_lo &
2142 + (y_target - yc_lo) / (yc_hi - yc_lo) * (log_t_hi - log_t_lo))
Scalar EoS inverse, e.g. fleint_from_T(log_nH, log_T)
Optional local multiplier for density-squared radiative losses.
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
This module contains definitions of global parameters and variables and some generic functions/subrou...
type(state), pointer block
Block pointer for using one block and its previous state.
logical lb_diagnose
Per-rank load-balance timing diagnostic toggle (off by default). When .true., per-rank wall times are...
integer, parameter unitpar
file handle for IO
character(len=std_len), dimension(:), allocatable par_files
Which par files are used as input.
integer mype
The rank of the current MPI task.
double precision, dimension(:), allocatable, parameter d
logical phys_trac
Use TRAC for MHD or 1D HD.
logical slab_uniform
uniform Cartesian geometry or not (stretched Cartesian)
This module defines the procedures of a physics module. It contains function pointers for the various...
procedure(sub_get_ei), pointer phys_get_ei
module radiative cooling – add optically thin radiative cooling
double precision function voigt_profile_ratio(a, x)
Voigt profile ratio phi(x)/phi(0) using Humlicek (1982) Region I/II approx. For the small-a regime (a...
subroutine voigt_gauss_legendre(a, b, n, x, w)
Gauss-Legendre quadrature nodes and weights on [a,b]. Uses the Golub-Welsch algorithm for n points.
subroutine getvar_cooling(ixil, ixol, w, x, coolrate, fl)
double precision function invert_row_bisect(y_row, t_grid, ncool, y_target)
Bisection on a single Y_mod row to find log10(T) such that Y_mod_row(i) = y_target....
subroutine radiative_cooling_rho2_factor(ixil, ixol, w, x, fl, factor)
subroutine radiative_cooling_init_params(phys_gamma, he_abund)
Radiative cooling initialization.
subroutine findl(tpoint, lpoint, fl)
double precision function findt_mod(y_target, nh_loc, fl)
Inverse Y_mod lookup: given Y_target and nH, return T such that Y_mod(log10 nH, log10 T) = Y_target....
subroutine get_cool_equi(qdt, ixil, ixol, wct, w, x, fl, res)
subroutine radiative_cooling_init(fl, read_params)
subroutine finddldt(tpoint, dlpoint, fl)
subroutine voigt_escape_init_table()
Build the Voigt escape probability lookup table. Called once (guarded by voigt_table_ready flag)....
double precision, public lb_cool_accum
Per-rank cooling-only compute accumulator for lb_diagnose. Sums the wall time spent inside radiative_...
subroutine radiative_cooling_taper(ixd, x_ndim, rho_val, te_val, fl, factor)
subroutine create_y_ppl(fl)
double precision function boole_composite(f, n, h)
Composite Boole's rule on (N+1) equally spaced samples (N a multiple of 4). Each 4-step block contrib...
subroutine build_y_mod_table(fl)
===================================================================
subroutine radiative_cooling_add_source(qdt, ixil, ixol, wct, wctprim, w, x, qsourcesplit, active, fl)
subroutine calc_l_extended(tpoint, lpoint, fl)
double precision function lowfip_fraction(tpoint, fl)
double precision function findy_mod(te_loc, nh_loc, fl)
Forward Y_mod lookup: bilinear interpolation in (log10 nH, log10 T) on the precomputed Y_mod table....
subroutine cool_exact(qdt, ixil, ixol, wct, wctprim, w, x, fl)
subroutine findt(tpoint, ypoint, fl)
double precision function voigt_escape_lookup(tau)
Look up the Voigt escape probability for a given tau. Uses linear interpolation in log10(tau) space.
subroutine floortemperature(qdt, ixil, ixol, wct, w, x, fl)
subroutine getvar_cooling_exact(qdt, ixil, ixol, wct, w, x, coolrate, fl)
double precision function simpson_composite(f, n, h)
Composite Simpson's rule on (N+1) equally spaced samples (N even). N must be a positive even integer;...
subroutine findy(tpoint, ypoint, fl)
module containing all optically thin radiative cooling tables
double precision, dimension(1:101) l_dere_corona
double precision, dimension(1:71) t_mlsolar1
double precision, dimension(1:151) l_cl_solar
double precision, dimension(1:5) t_fm
double precision, dimension(1:14) a_spex_dm_fine
double precision, dimension(1:9) a_rosner
double precision, dimension(1:110) l_spex
double precision, dimension(1:51) l_mb
double precision, dimension(1:10) t_rosner
double precision, dimension(1:5) a_hildner
double precision, dimension(1:9) x_rosner
double precision, dimension(1:7) x_klimchuk
double precision, dimension(1:151) l_composite
double precision, dimension(1:151) l_cl_ism
double precision, dimension(1:8) t_spex_dm_rough
double precision, dimension(1:110) nenh_spex
double precision, dimension(1:110) t_spex
double precision, dimension(1:76) l_dm_2
double precision, dimension(1:151) t_composite
double precision, dimension(1:15) t_spex_dm_fine
double precision, dimension(1:7) x_spex_dm_rough
double precision, dimension(1:14) x_spex_dm_fine
double precision, dimension(1:71) l_mlsolar1
double precision, dimension(1:45) t_jccorona
double precision, dimension(1:5) x_hildner
double precision, dimension(1:71) t_mlcosmol
double precision, dimension(1:151) t_cl_ism
double precision, dimension(1:151) t_cl_solar
double precision, dimension(1:51) t_mb
double precision, dimension(1:8) t_klimchuk
double precision, dimension(1:55) t_colgan
double precision, dimension(1:55) l_colgan
double precision, dimension(1:4) a_fm
double precision, dimension(1:101) l_dere_photo
double precision, dimension(1:45) l_jccorona
double precision, dimension(1:71) l_mlwc
double precision, dimension(1:71) l_dm
double precision, dimension(1:71) t_mlwc
double precision, dimension(1:7) a_spex_dm_rough
double precision, dimension(1:71) t_dm
double precision, dimension(1:7) a_klimchuk
double precision, dimension(1:71) l_mlcosmol
double precision, dimension(1:76) t_dm_2
double precision, dimension(1:6) t_hildner
double precision, dimension(1:4) x_fm
double precision, dimension(1:101) t_dere
double precision, dimension(1:101) lowfip_frac