MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_rt_sc_opacity.t
Go to the documentation of this file.
1!=============================================================================
2!> Opacity and Planck source of the short-characteristics (SC) radiative transfer
3!>
4!> The solvers never evaluate the gas themselves. They call the rt_sc_chiB callback, which
5!> returns chi = kappa rho and B = a T^4 on a block, with the temperature and gas pressure taken
6!> from the EoS, so that the opacity follows whichever EoS the run uses. rt_sc_setup points the
7!> callback at one of three models, chosen by rt_sc_opacity:
8!> Rosseland the tabulated Rosseland mean, shifted to code units when it is read, so that a
9!> cell needs one logarithm each of T and rho and one power of ten;
10!> Kramers an analytic H-minus and Kramers fit with electron scattering added;
11!> usr the user routine usr_rt_sc_chiB.
12!> The built-in models also apply the transition switch rt_sc_switch. It scales chi and B
13!> together, so that the hot, tenuous corona becomes both transparent and dark to the SC
14!> transfer, and the thin cooling takes over there. The module depends only on
15!> mod_global_parameters and mod_eos
16!>
17!> Jack Jenkins (10.2026); More or less finished in v1 state for amrvac4.0
18!=============================================================================
20 use mod_global_parameters, only: std_len
21 implicit none
22
23 !> The opacity model, either 'Rosseland' (the table), 'Kramers' (the analytic fit) or 'usr',
24 !> which uses the usr_rt_sc_chiB callback
25 character(len=10), public :: rt_sc_opacity = 'Rosseland'
26 !> The Rosseland table file, read in rt_sc_setup when the table model is chosen
27 character(len=std_len), public :: rt_sc_kappa_table = 'rosseland_combined.tab'
28 !> The built-in opacities keep the SC transfer on where the gas is cool (T < Tcap) or dense
29 !> (p > p_TR), and switch it off smoothly in the hot, tenuous corona. The transition bands Tcap
30 !> and dTcap are in K, p_TR is in dyn/cm^2 and its width dp_TR in dex
31 double precision, public :: rt_sc_tcap = 2.0d4, rt_sc_dtcap = 5.0d3
32 double precision, public :: rt_sc_p_tr = 1.0d0, rt_sc_dp_tr = 0.5d0
33 !> log10 of p_TR in code units, set by rt_sc_setup
34 double precision, public :: rt_sc_lp_tr = 0.0d0
35 !> The Rosseland table, shifted to code units when it is read. It holds log10 kappa on a grid
36 !> of log10 T and log10 R, where R = rho/T^3, all three in code units
37 double precision, allocatable :: op_logt(:), op_logr(:), op_logk(:,:)
38 integer :: nt_op = 0, nr_op = 0
39
40 !> The opacity and source the solvers call. rt_sc_setup points it at one of the built-in
41 !> routines, or at usr_rt_sc_chiB for 'usr'
42 abstract interface
43 subroutine rt_sc_chib_iface(ixI^L, ixO^L, w, x, chi, bpl)
45 integer, intent(in) :: ixI^L, ixO^L
46 double precision, intent(in) :: w(ixI^S,1:nw), x(ixI^S,1:ndim)
47 double precision, intent(out):: chi(ixI^S), bpl(ixI^S)
48 end subroutine rt_sc_chib_iface
49 end interface
50 procedure(rt_sc_chib_iface), pointer :: rt_sc_chib => null()
51
52 public :: rt_sc_chib
53 public :: rt_sc_chib_rosseland
54 public :: rt_sc_chib_kramers
55 public :: read_opacity_table
56 public :: rt_sc_switch
57
58contains
59
60 !> Table Rosseland opacity, chi = kappa_R(rho, T) rho, and source B = a T^4, both multiplied by
61 !> the transition switch
62 subroutine rt_sc_chib_rosseland(ixI^L, ixO^L, w, x, chi, bpl)
64 integer, intent(in) :: ixi^l, ixo^l
65 double precision, intent(in) :: w(ixi^s,1:nw), x(ixi^s,1:ndim)
66 double precision, intent(out):: chi(ixi^s), bpl(ixi^s)
67 call chib_builtin(ixi^l, ixo^l, w, x, chi, bpl, .true.)
68 end subroutine rt_sc_chib_rosseland
69
70 !> Analytic H-minus and Kramers opacity, with the source and the switch as in
71 !> rt_sc_chiB_rosseland
72 subroutine rt_sc_chib_kramers(ixI^L, ixO^L, w, x, chi, bpl)
74 integer, intent(in) :: ixi^l, ixo^l
75 double precision, intent(in) :: w(ixi^s,1:nw), x(ixi^s,1:ndim)
76 double precision, intent(out):: chi(ixi^s), bpl(ixi^s)
77 call chib_builtin(ixi^l, ixo^l, w, x, chi, bpl, .false.)
78 end subroutine rt_sc_chib_kramers
79
80 !> chi and B of the built-in opacities, the table when tabulated is set and the analytic fit
81 !> otherwise. Temperature and gas pressure come from the EoS, so the opacity follows whichever
82 !> EoS the run uses, and the switch scales chi and B together, so that the switched-off gas is
83 !> both transparent and dark
84 subroutine chib_builtin(ixI^L, ixO^L, w, x, chi, bpl, tabulated)
86 use mod_eos, only: eos
87 integer, intent(in) :: ixI^L, ixO^L
88 double precision, intent(in) :: w(ixI^S,1:nw), x(ixI^S,1:ndim)
89 double precision, intent(out):: chi(ixI^S), bpl(ixI^S)
90 logical, intent(in) :: tabulated
91 double precision :: Tloc(ixI^S), pgas(ixI^S), Tc, rc, kcgs, scap
92 integer :: ix^D
93 call eos%get_Te(w, x, ixi^l, ixo^l, tloc)
94 call eos%get_thermal_pressure(w, x, ixi^l, ixo^l, pgas)
95 {do ix^db = ixomin^db, ixomax^db\}
96 tc = tloc(ix^d)*unit_temperature
97 if(tabulated) then
98 ! The table is in code units, so the opacity is ready to be multiplied by the code density
99 chi(ix^d) = kappa_code_cell(tloc(ix^d), w(ix^d,iw_rho))*w(ix^d,iw_rho)
100 else
101 ! The fit is written in cgs. The H-minus term dominates in cool gas and the Kramers term in
102 ! hot gas, and electron scattering at 0.34 cm^2/g sets a floor under both. The density is
103 ! kept positive for the fractional powers
104 ! kappa = 1e-25 rho^(1/2) T^7.7 + 4e25 rho T^(-3.5) + 0.34, in cgs
105 rc = w(ix^d,iw_rho)*unit_density
106 kcgs = 1.0d-25*dsqrt(max(rc,1.0d-30))*tc**7.7d0 &
107 + 4.0d25*max(rc,1.0d-30)*tc**(-3.5d0) + 0.34d0
108 chi(ix^d) = (kcgs/unit_opacity)*w(ix^d,iw_rho)
109 end if
110 ! chi = s kappa rho, B = s a T^4
111 bpl(ix^d) = arad_norm*tloc(ix^d)**4
112 scap = rt_sc_switch(tc, pgas(ix^d))
113 chi(ix^d) = chi(ix^d)*scap
114 bpl(ix^d) = bpl(ix^d)*scap
115 {end do\}
116 end subroutine chib_builtin
117
118 !> Transition switch of the built-in opacities, from the temperature in K and the gas pressure in
119 !> code units. The temperature gate is one in cool gas and falls to zero across dTcap above
120 !> Tcap, while the pressure gate rises from zero to one across dp_TR dex around p_TR. A cell
121 !> keeps its transfer when either holds, so the larger gate wins
122 pure double precision function rt_sc_switch(T_K, p_code) result(scap)
123 double precision, intent(in) :: t_k, p_code
124 double precision :: tsw, psw
125 ! s_T = (1 - tanh((T - Tcap)/dTcap))/2
126 ! s_p = (1 + tanh((log10 p - log10 p_TR)/dp_TR))/2
127 tsw = 0.5d0*(1.0d0 - tanh((t_k - rt_sc_tcap)/rt_sc_dtcap))
128 psw = 0.5d0*(1.0d0 + tanh((dlog10(max(p_code,1.0d-99)) - rt_sc_lp_tr)/rt_sc_dp_tr))
129 ! s = max(s_T, s_p)
130 scap = max(tsw, psw)
131 end function rt_sc_switch
132
133 !> Rosseland opacity of one cell from the table, with temperature, density and opacity all in
134 !> code units
135 double precision function kappa_code_cell(T_code, rho_code) result(kap)
136 double precision, intent(in) :: t_code, rho_code
137 double precision :: lt, lr
138 ! The table axes are log10 T and log10 R, and log10 R = log10 rho - 3 log10 T
139 lt = dlog10(t_code)
140 lr = dlog10(rho_code) - 3.0d0*lt
141 kap = 10.0d0**interp_logkappa(lt, lr)
142 end function kappa_code_cell
143
144 !> Bilinear interpolation of log10 kappa in (log10 T, log10 R)
145 double precision function interp_logkappa(lT, lR) result(logK)
146 double precision, intent(in) :: lt, lr
147 integer :: it, ir
148 double precision :: tt, tr, ka, kb
149 ! op_locate gives the lower corner of the table cell that holds the point
150 it = op_locate(op_logt, nt_op, lt)
151 ir = op_locate(op_logr, nr_op, lr)
152 ! tT = (lT - lT_i)/(lT_(i+1) - lT_i) and tR likewise, then linear in log R and in log T
153 tt = (lt - op_logt(it)) / (op_logt(it+1) - op_logt(it))
154 tr = (lr - op_logr(ir)) / (op_logr(ir+1) - op_logr(ir))
155 tt = min(max(tt, 0.d0), 1.d0); tr = min(max(tr, 0.d0), 1.d0)
156 ! We interpolate along R on the two bracketing rows of T, and then along T between them
157 ka = op_logk(it, ir) + tr*(op_logk(it, ir+1) - op_logk(it, ir))
158 kb = op_logk(it+1,ir) + tr*(op_logk(it+1,ir+1) - op_logk(it+1,ir))
159 logk = ka + tt*(kb - ka)
160 end function interp_logkappa
161
162 !> Index jl of the table interval [xx(jl), xx(jl+1)] that holds xq, found by bisection. A point
163 !> beyond either end returns the first or the last interval
164 integer function op_locate(xx, n, xq) result(jl)
165 integer, intent(in) :: n
166 double precision, intent(in) :: xx(n), xq
167 integer :: ju, jm
168 if (xq <= xx(1)) then; jl = 1; return; end if
169 if (xq >= xx(n)) then; jl = n-1; return; end if
170 jl = 1; ju = n
171 do while (ju - jl > 1)
172 jm = (ju + jl)/2
173 if (xq >= xx(jm)) then; jl = jm; else; ju = jm; end if
174 end do
175 end function op_locate
176
177 !> Read the Rosseland table and shift it to code units. The file is in cgs, with T in K and
178 !> R = rho/T6^3, where T6 is the temperature in 1e6 K
181 use mod_comm_lib, only: mpistop
182 integer :: i, funit
183 logical :: alive
184 character(len=256) :: dum
185
186 inquire(file=trim(rt_sc_kappa_table), exist=alive)
187 if (.not. alive) call mpistop('read_opacity_table: Rosseland table not found: '//trim(rt_sc_kappa_table))
188 open(newunit=funit, file=trim(rt_sc_kappa_table), status='old')
189 read(funit,'(A)') dum
190 read(funit,*) nt_op, nr_op
191 if(allocated(op_logt)) deallocate(op_logt, op_logr, op_logk)
193 read(funit,*) op_logr(1:nr_op)
194 do i = 1, nt_op
195 read(funit,*) op_logt(i), op_logk(i,1:nr_op)
196 end do
197 close(funit)
198 ! Shift the units of the table so it's compliant with the AMRVAC unit system
200 op_logr = op_logr - dlog10(unit_density) + 3.0d0*dlog10(1.0d-6*unit_temperature)
201 op_logk = op_logk - dlog10(unit_opacity)
202 if (mype==0) print *,'read_opacity_table: Rosseland table ',nt_op,' logT x ',nr_op,' logR'
203 end subroutine read_opacity_table
204
205end module mod_rt_sc_opacity
The opacity and source the solvers call. rt_sc_setup points it at one of the built-in routines,...
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
Equation of state for AMRVAC, handled through a single eos_container object.
Definition mod_eos.t:30
This module contains definitions of global parameters and variables and some generic functions/subrou...
double precision arad_norm
Normalised radiation constant.
double precision unit_density
Physical scaling factor for density.
double precision unit_opacity
Physical scaling factor for Opacity.
integer, parameter ndim
Number of spatial dimensions for grid variables.
integer mype
The rank of the current MPI task.
double precision, dimension(:), allocatable, parameter d
double precision unit_temperature
Physical scaling factor for temperature.
Opacity and Planck source of the short-characteristics (SC) radiative transfer.
double precision, dimension(:), allocatable op_logt
The Rosseland table, shifted to code units when it is read. It holds log10 kappa on a grid of log10 T...
double precision, public rt_sc_dtcap
double precision function kappa_code_cell(t_code, rho_code)
Rosseland opacity of one cell from the table, with temperature, density and opacity all in code units...
double precision, public rt_sc_dp_tr
procedure(rt_sc_chib_iface), pointer, public rt_sc_chib
double precision function interp_logkappa(lt, lr)
Bilinear interpolation of log10 kappa in (log10 T, log10 R).
integer function op_locate(xx, n, xq)
Index jl of the table interval [xx(jl), xx(jl+1)] that holds xq, found by bisection....
double precision, public rt_sc_tcap
The built-in opacities keep the SC transfer on where the gas is cool (T < Tcap) or dense (p > p_TR),...
pure double precision function, public rt_sc_switch(t_k, p_code)
Transition switch of the built-in opacities, from the temperature in K and the gas pressure in code u...
character(len=std_len), public rt_sc_kappa_table
The Rosseland table file, read in rt_sc_setup when the table model is chosen.
subroutine, public read_opacity_table()
Read the Rosseland table and shift it to code units. The file is in cgs, with T in K and R = rho/T6^3...
subroutine, public rt_sc_chib_rosseland(ixil, ixol, w, x, chi, bpl)
Table Rosseland opacity, chi = kappa_R(rho, T) rho, and source B = a T^4, both multiplied by the tran...
double precision, public rt_sc_p_tr
double precision, dimension(:,:), allocatable op_logk
double precision, public rt_sc_lp_tr
log10 of p_TR in code units, set by rt_sc_setup
subroutine, public rt_sc_chib_kramers(ixil, ixol, w, x, chi, bpl)
Analytic H-minus and Kramers opacity, with the source and the switch as in rt_sc_chiB_rosseland.
character(len=10), public rt_sc_opacity
The opacity model, either 'Rosseland' (the table), 'Kramers' (the analytic fit) or 'usr',...
subroutine chib_builtin(ixil, ixol, w, x, chi, bpl, tabulated)
chi and B of the built-in opacities, the table when tabulated is set and the analytic fit otherwise....
double precision, dimension(:), allocatable op_logr