MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_thermal_conduction.t
Go to the documentation of this file.
1!> Thermal conduction for HD and MHD or RHD and RMHD or twofl (plasma-neutral) module
2!> Adaptation of mod_thermal_conduction for the mod_supertimestepping
3!>
4!> The TC is set by calling
5!> tc_init_params()
6!>
7!> Organized such that it can call either isotropic (HD) or anisotropic (MHD) variants
8!> it adds a heat conduction source to each energy equation
9!> and can be recycled within a multi-fluid context (such as plasma-neutral twofl module)
10!>
11!>
12!> 10.07.2011 developed by Chun Xia and Rony Keppens
13!> 01.09.2012 moved to modules folder by Oliver Porth
14!> 13.10.2013 optimized further by Chun Xia
15!> 12.03.2014 implemented RKL2 super timestepping scheme to reduce iterations
16!> and improve stability and accuracy up to second order in time by Chun Xia.
17!> 23.08.2014 implemented saturation and perpendicular TC by Chun Xia
18!> 12.01.2017 modulized by Chun Xia
19!> adapted by Beatrice Popescu to twofluid settings
20!> 06.09.2024 cleaned up for use in rhd and rmhd modules (Nishant Narechania and Rony Keppens)
21!> 30.11.2025 Minor cleanup (for consistency between hd and mhd)
22!>
23!> PURPOSE:
24!> IN MHD ADD THE HEAT CONDUCTION SOURCE TO THE ENERGY EQUATION
25!> S=DIV(KAPPA_i,j . GRAD_j T)
26!> where KAPPA_i,j = tc_k_para b_i b_j + tc_k_perp (I - b_i b_j)
27!> b_i b_j = B_i B_j / B**2, I is the unit matrix, and i, j= 1, 2, 3 for 3D
28!> IN HD ADD THE HEAT CONDUCTION SOURCE TO THE ENERGY EQUATION
29!> S=DIV(tc_k_para . GRAD T)
30!> USAGE:
31!> 1. in mod_usr.t -> subroutine usr_init(), add
32!> unit_length=your length unit
33!> unit_numberdensity=your number density unit
34!> unit_velocity=your velocity unit
35!> unit_temperature=your temperature unit
36!> before call (m)hd_activate()
37!> 2. to switch on thermal conduction in the (r)(m)hd_list of amrvac.par add:
38!> (r)(m)hd_thermal_conduction=.true.
39!> 3. in the tc_list of amrvac.par :
40!> tc_perpendicular=.true. ! (default .false.) turn on thermal conduction perpendicular to magnetic field
41!> tc_saturate=.true. ! (default .false. ) turn on thermal conduction saturate effect
42!> tc_slope_limiter='MC' ! choose limiter for slope-limited anisotropic thermal conduction in MHD
43!> note: twofl_list incorporates instances for charges and neutrals
44
46 use mod_global_parameters, only: std_len
47 use mod_geometry
48 use mod_comm_lib, only: mpistop
49 implicit none
50
51 !> The adiabatic index
52 double precision :: tc_gamma_1
53
54 abstract interface
55 subroutine get_var_subr(w,x,ixI^L,ixO^L,res)
57 integer, intent(in) :: ixI^L, ixO^L
58 double precision, intent(in) :: w(ixI^S,nw)
59 double precision, intent(in) :: x(ixI^S,1:ndim)
60 double precision, intent(out):: res(ixI^S)
61 end subroutine get_var_subr
62
63 subroutine get_2var_subr(ixI^L, ixO^L, w, x, ne, nH)
65 integer, intent(in) :: ixI^L, ixO^L
66 double precision, intent(in) :: w(ixI^S, nw)
67 double precision, intent(in) :: x(ixI^S, 1:ndim)
68 double precision, intent(out):: ne(ixI^S), nH(ixI^S)
69 end subroutine get_2var_subr
70
71 !> Scalar EoS inverse, e.g. eint_nH_from_T(log_nH, log_T)
72 double precision function eos_scalar2_func(a, b)
73 double precision, intent(in) :: a, b
74 end function eos_scalar2_func
75 end interface
76
78
79 ! BEGIN the following are read from param file or set in tc_read_hd_params or tc_read_mhd_params
80 !> Coefficient of thermal conductivity (parallel to magnetic field)
81 double precision :: tc_k_para
82
83 !> Coefficient of thermal conductivity perpendicular to magnetic field
84 double precision :: tc_k_perp
85
86 !> Indices of the variables
87 integer :: e_=-1
88 !> Index of cut off temperature for TRAC
89 integer :: tcoff_
90 !> Name of slope limiter for transverse component of thermal flux
91 integer :: tc_slope_limiter
92
93 ! if subtract_equi = .true. get_temperature_equi and get_rho_equi have to be set
94 logical :: subtract_equi=.false.
95
96 !> Logical switch for test constant conductivity
97 logical :: tc_constant=.false.
98
99 !> Calculate thermal conduction perpendicular to magnetic field (.true.) or not (.false.)
100 logical :: tc_perpendicular=.false.
101
102 !> Consider thermal conduction saturation effect (.true.) or not (.false.)
103 logical :: tc_saturate=.false.
104
105 !> Patch cells where e_int < 0 during STS Chebyshev substeps by
106 !> neighbor-averaging the temperature. Prevents NaN from sqrt(T<0)
107 !> in the Spitzer conductivity when the RKL2 polynomial overshoots.
108 logical :: tc_patch_eint=.false.
109
110 !> LTE with ionisation energy: hold the ionisation state fixed through each conduction
111 !> step. T comes from the full EoS and R = p/(rho T) from the cached ionisation at the
112 !> first STS stage; the later stages map the internal energy to T with the frozen-composition
113 !> heat capacity. The physics modules set it from tc_frozen_ion, default true, and eos_bind_tc
114 !> clears it for every other closure
115 logical :: frozen_ion=.false.
116
117 !> R**1.5 of the fully ionised gas, which scales T**1.5 to c**3 in the saturated flux
118 double precision :: sat_fac=1.d0
119
120 !> LTE: the saturated flux follows the local electron density, with ne/nH of the fully
121 !> ionised gas as the reference
122 logical :: sat_ne=.false.
123 double precision :: neonh_fi=1.d0
124
125 !> Minimum temperature (code units) below which TRAC does not modify conductivity.
126 !> Below this T, the energy balance is dominated by optically thick radiation
127 !> and recombination, not optically thin cooling + Spitzer conduction, so TRAC's
128 !> broadening assumption does not apply. Read in Kelvin via tc_list (trac_T_floor),
129 !> converted to code units at init. Default 0 (no floor). Only the HD TRAC cutoff (types 0
130 !> and 7) reads it, with Tcoff = Te below it, so conduction, losses and heating are left
131 !> unmodified there
132 double precision :: trac_t_floor=0.d0
133 ! END the following are read from param file or set in tc_read_hd_params or tc_read_mhd_params
134 procedure(get_var_subr), pointer, nopass :: get_rho => null()
135 procedure(get_var_subr), pointer, nopass :: get_rho_equi => null()
136 procedure(get_var_subr), pointer,nopass :: get_temperature_from_eint => null()
137 procedure(get_var_subr), pointer,nopass :: get_temperature_from_conserved => null()
138 procedure(get_var_subr), pointer,nopass :: get_temperature_equi => null()
139 procedure(get_2var_subr), pointer,nopass :: get_ne_nh => null()
140 procedure(get_var_subr), pointer,nopass :: get_var_rfactor => null()
141 !> EoS snapshots + inverse accessor (set in bind_eos_to_source); let TC reach
142 !> thermodynamics only through this object, never mod_eos directly.
143 double precision :: inv_gamma_minus_1
144 double precision :: nh2rhofactor
145 double precision :: log_t_floor
146 procedure(eos_scalar2_func), pointer, nopass :: eint_from_t => null()
147 end type tc_fluid
148
149 public :: tc_get_mhd_params
150 public :: tc_get_hd_params
151 public :: get_tc_dt_mhd
152 public :: get_tc_dt_hd
153 public :: sts_set_source_tc_mhd
154 public :: sts_set_source_tc_hd
155 public :: tc_patch_negative_eint
156
157 !> Per-block anchor of the frozen-ionisation conduction step: T and the internal energy
158 !> at the step start, and the inverse frozen-composition heat capacity per volume
160 integer :: sts_id = -1 !< STS application the anchor belongs to
161 double precision, allocatable :: t0(:^d&), e0(:^D&), cvinv(:^D&)
162 end type tc_anchor_t
163 type(tc_anchor_t), allocatable :: tc_anchor(:)
164
165contains
166
167 subroutine tc_init_params(phys_gamma)
169 double precision, intent(in) :: phys_gamma
170
171 tc_gamma_1=phys_gamma-1d0
172 end subroutine tc_init_params
173
174 !> Init TC coefficients: MHD case
175 subroutine tc_get_mhd_params(fl,read_mhd_params)
177
178 interface
179 subroutine read_mhd_params(fl)
181 import tc_fluid
182 type(tc_fluid), intent(inout) :: fl
183
184 end subroutine read_mhd_params
185 end interface
186 type(tc_fluid), intent(inout) :: fl
187
188 fl%tc_slope_limiter=1
189 fl%tc_k_para=0.d0
190 fl%tc_k_perp=0.d0
191
192 !> Read tc module parameters from par file: MHD case
193 call read_mhd_params(fl)
194
195 if(fl%tc_k_para==0.d0 .and. fl%tc_k_perp==0.d0) then
196 if(si_unit) then
197 ! Spitzer thermal conductivity with SI units
198 fl%tc_k_para=8.d-12*unit_temperature**3.5d0/unit_length/unit_density/unit_velocity**3
199 ! thermal conductivity perpendicular to magnetic field
200 fl%tc_k_perp=4.d-30*unit_numberdensity**2/unit_magneticfield**2/unit_temperature**3*fl%tc_k_para
201 else
202 ! Spitzer thermal conductivity with cgs units
203 fl%tc_k_para=8.d-7*unit_temperature**3.5d0/unit_length/unit_density/unit_velocity**3
204 ! thermal conductivity perpendicular to magnetic field
205 fl%tc_k_perp=4.d-10*unit_numberdensity**2/unit_magneticfield**2/unit_temperature**3*fl%tc_k_para
206 end if
207 if(mype .eq. 0) print*, "Spitzer MHD: par: ",fl%tc_k_para, &
208 " ,perp: ",fl%tc_k_perp
209 else
210 fl%tc_constant=.true.
211 if(mype .eq. 0) print*, "Constant thermal conduction coefficients with values: ",fl%tc_k_para,fl%tc_k_perp
212 end if
213 end subroutine tc_get_mhd_params
214
215 !> Init TC coefficients: HD case
216 subroutine tc_get_hd_params(fl,read_hd_params)
218
219 interface
220 subroutine read_hd_params(fl)
222 import tc_fluid
223 type(tc_fluid), intent(inout) :: fl
224
225 end subroutine read_hd_params
226 end interface
227 type(tc_fluid), intent(inout) :: fl
228
229 fl%tc_k_para=0.d0
230
231 !> Read tc parameters from par file: HD case
232 call read_hd_params(fl)
233
234 if(fl%tc_k_para==0.d0) then
235 if(si_unit) then
236 ! Spitzer thermal conductivity with SI units
237 fl%tc_k_para=8.d-12*unit_temperature**3.5d0/unit_length/unit_density/unit_velocity**3
238 else
239 ! Spitzer thermal conductivity with cgs units
240 fl%tc_k_para=8.d-7*unit_temperature**3.5d0/unit_length/unit_density/unit_velocity**3
241 end if
242 if(mype .eq. 0) print*, "Spitzer HD par: ",fl%tc_k_para
243 else
244 fl%tc_constant=.true.
245 if(mype .eq. 0) print*, "Constant thermal conduction coefficient at value: ",fl%tc_k_para
246 end if
247
248 end subroutine tc_get_hd_params
249
250 !> Get the explicit timestep for the TC (mhd implementation)
251 !> Note: for multi-D MHD (1D MHD will use HD fall-back)
252 function get_tc_dt_mhd(w,ixI^L,ixO^L,dx^D,x,fl) result(dtnew)
253 !Check diffusion time limit dt < dx_i**2/((gamma-1)*tc_k_para_i/(rho R))
254 !where tc_k_para_i=tc_k_para*B_i**2/B**2
255 !and T=p/rho
257
258 type(tc_fluid), intent(in) :: fl
259 integer, intent(in) :: ixi^l, ixo^l
260 double precision, intent(in) :: dx^d, x(ixi^s,1:ndim)
261 double precision, intent(in) :: w(ixi^s,1:nw)
262 double precision :: dtnew
263
264 double precision :: mf(ixo^s,1:ndir),te(ixi^s),rho(ixi^s),gradt(ixi^s)
265 double precision :: ne(ixi^s), nh_arr(ixi^s), rf(ixi^s)
266 double precision :: tmp(ixo^s),hfs(ixo^s),blocal(1:ndir),bmag
267 double precision :: dtdiff_tcond,maxtmp2
268 integer :: idims,ix^d
269
270
271 ! B
272 if(allocated(iw_mag)) then
273 if(b0field) then
274 {do ix^db=ixomin^db,ixomax^db\}
275 ^c&blocal(^c)=w({ix^d},iw_mag(^c))+block%B0({ix^d},^c,0)\
276 bmag=dsqrt(^c&blocal(^c)**2+)+smalldouble
277 ^c&mf(ix^d,^c)=blocal(^c)/bmag\
278 {end do\}
279 else
280 {do ix^db=ixomin^db,ixomax^db\}
281 bmag=dsqrt(^c&w(ix^d,iw_mag(^c))**2+)+smalldouble
282 ^c&mf(ix^d,^c)=w(ix^d,iw_mag(^c))/bmag\
283 {end do\}
284 end if
285 else
286 ! this if for ffhd or other physics modules without B components
287 mf(ixo^s,1:ndim)=block%B0(ixo^s,1:ndim,0)
288 end if
289
290 ! Constant conductivity does not use Te; retain ghost temperatures when
291 ! needed by the temperature-dependent conductivity and saturation gradient.
292 if(.not.fl%tc_constant) then
293 call fl%get_temperature_from_conserved(w,x,ixi^l,ixi^l,te)
294 end if
295 call fl%get_rho(w,x,ixi^l,ixo^l,rho)
296 ! Electron density is used only by the non-constant saturation correction.
297 if(.not.fl%tc_constant .and. fl%tc_saturate) then
298 call fl%get_ne_nH(ixi^l, ixo^l, w, x, ne, nh_arr)
299 end if
300 ! the diffusivity is kappa/(rho c_v) with c_v = R/(gamma-1): weight rho by R
301 if(associated(fl%get_var_Rfactor)) then
302 call fl%get_var_Rfactor(w, x, ixi^l, ixo^l, rf)
303 rho(ixo^s)=rho(ixo^s)*rf(ixo^s)
304 end if
305
306 !tc_k_para_i
307 if(fl%tc_constant) then
308 tmp(ixo^s)=fl%tc_k_para
309 else
310 if(fl%tc_saturate) then
311 ! Kannan 2016 MN 458, 410
312 ! l_mfpe = 3^1.5*kB^2/(4*sqrt(pi)*e^4*lnLambda) * T^2/n_e
313 if(si_unit) then
314 ! 5.730205638843984e27 = 3^1.5*kB_SI^2/(4*sqrt(pi)*e_SI^4*37)
315 tmp(ixo^s)=te(ixo^s)**2/ne(ixo^s)*5.730205638843984d27*unit_temperature**2/(unit_numberdensity*unit_length)
316 else
317 ! 7093.9239487765044 = 3^1.5*kB_cgs^2/(4*sqrt(pi)*e_cgs^4*37)
318 tmp(ixo^s)=te(ixo^s)**2/ne(ixo^s)*7093.9239487765044d0*unit_temperature**2/(unit_numberdensity*unit_length)
319 end if
320 do idims=1,ndim
321 call gradient(te,ixi^l,ixo^l,idims,gradt)
322 if(idims==1) then
323 hfs(ixo^s)=gradt(ixo^s)*mf(ixo^s,idims)
324 else
325 hfs(ixo^s)=hfs(ixo^s)+gradt(ixo^s)*mf(ixo^s,idims)
326 end if
327 end do
328 ! kappa=kappa_Spitzer/(1+4.2*l_mfpe/(T/|gradT.b|))
329 tmp(ixo^s)=fl%tc_k_para*te(ixo^s)*te(ixo^s)*dsqrt(te(ixo^s))/(1.d0+4.2d0*tmp(ixo^s)*dabs(hfs(ixo^s))/te(ixo^s))
330 else
331 ! kappa=kappa_Spitzer
332 tmp(ixo^s)=fl%tc_k_para*te(ixo^s)*te(ixo^s)*dsqrt(te(ixo^s))
333 end if
334 end if
335
336 dtnew=bigdouble
337 do idims=1,ndim
338 ! approximate thermal conduction flux: tc_k_para_i/rho/dx*B_i**2/B**2
339 maxtmp2=maxval(tmp(ixo^s)*mf(ixo^s,idims)**2/(rho(ixo^s)*block%ds(ixo^s,idims)**2))
340 ! dt< dx_idim**2/((gamma-1)*tc_k_para_i/rho*B_i**2/B**2)
341 dtdiff_tcond=1.d0/(tc_gamma_1*maxtmp2+smalldouble)
342 ! limit the time step
343 dtnew=min(dtnew,dtdiff_tcond)
344 end do
345 dtnew=dtnew/dble(ndim)
346 end function get_tc_dt_mhd
347
348 !> anisotropic thermal conduction with slope limited symmetric scheme
349 !> Sharma 2007 Journal of Computational Physics 227, 123
350 subroutine sts_set_source_tc_mhd(ixI^L,ixO^L,w,x,wres,fix_conserve_at_step,my_dt,igrid,nflux,fl)
353 integer, intent(in) :: ixi^l, ixo^l, igrid, nflux
354 double precision, intent(in) :: x(ixi^s,1:ndim)
355 ! intent(inout) so tc_patch_negative_eint can repair w(:, ie) in place
356 ! when tc_patch_eint = .true.; legacy path (patch off) leaves w untouched.
357 double precision, intent(inout) :: w(ixi^s,1:nw)
358 double precision, intent(inout) :: wres(ixi^s,1:nw)
359 double precision, intent(in) :: my_dt
360 logical, intent(in) :: fix_conserve_at_step
361 type(tc_fluid), intent(in) :: fl
362
363 !! qd store the heat conduction energy changing rate
364 double precision :: qd(ixo^s)
365 double precision :: rho(ixi^s),te(ixi^s)
366 double precision :: qvec(ixi^s,1:ndim)
367 double precision :: fluxall(ixi^s,1,1:ndim)
368 double precision :: alpha,dxinv(ndim)
369 double precision, allocatable, dimension(:^D&,:) :: qvec_equi
370 integer :: idims,ixa^l
371
372 ! coefficient of limiting on normal component
373 if(ndim<3) then
374 alpha=0.75d0
375 else
376 alpha=0.85d0
377 end if
378
379 dxinv=1.d0/dxlevel
380
381 call tc_get_te(ixi^l,w,x,fl,igrid,te)
382 call fl%get_rho(w, x, ixi^l, ixi^l, rho)
383 call tc_flux_mhd(ixi^l,ixo^l,w,x,fl,qvec,rho,te,alpha)
384 if(fl%subtract_equi) then
385 allocate(qvec_equi(ixi^s,1:ndim))
386 call fl%get_temperature_equi(w, x, ixi^l, ixi^l, te)
387 call fl%get_rho_equi(w, x, ixi^l, ixi^l, rho)
388 call tc_flux_mhd(ixi^l,ixo^l,w,x,fl,qvec_equi,rho,te,alpha)
389 do idims=1,ndim
390 ixamax^d=ixomax^d; ixamin^d=ixomin^d-kr(idims,^d);
391 qvec(ixa^s,idims)=qvec(ixa^s,idims)-qvec_equi(ixa^s,idims)
392 end do
393 deallocate(qvec_equi)
394 end if
395
396 if(slab_uniform) then
397 do idims=1,ndim
398 ixamax^d=ixomax^d; ixamin^d=ixomin^d-kr(idims,^d);
399 qvec(ixa^s,idims)=dxinv(idims)*qvec(ixa^s,idims)
400 ixa^l=ixo^l-kr(idims,^d);
401 if(idims==1) then
402 qd(ixo^s)=qvec(ixo^s,idims)-qvec(ixa^s,idims)
403 else
404 qd(ixo^s)=qd(ixo^s)+qvec(ixo^s,idims)-qvec(ixa^s,idims)
405 end if
406 end do
407 else
408 do idims=1,ndim
409 ixamax^d=ixomax^d; ixamin^d=ixomin^d-kr(idims,^d);
410 qvec(ixa^s,idims)=qvec(ixa^s,idims)*block%surfaceC(ixa^s,idims)
411 ixa^l=ixo^l-kr(idims,^d);
412 if(idims==1) then
413 qd(ixo^s)=qvec(ixo^s,idims)-qvec(ixa^s,idims)
414 else
415 qd(ixo^s)=qd(ixo^s)+qvec(ixo^s,idims)-qvec(ixa^s,idims)
416 end if
417 end do
418 qd(ixo^s)=qd(ixo^s)/block%dvolume(ixo^s)
419 end if
420
421 if(fix_conserve_at_step) then
422 do idims=1,ndim
423 ixamax^d=ixomax^d; ixamin^d=ixomin^d-kr(idims,^d);
424 fluxall(ixa^s,1,idims)=my_dt*qvec(ixa^s,idims)
425 end do
426 call store_flux(igrid,fluxall,1,ndim,nflux)
427 end if
428
429 wres(ixo^s,fl%e_)=qd(ixo^s)
430 end subroutine sts_set_source_tc_mhd
431
432 subroutine set_source_tc_mhd(ixI^L,ixO^L,w,x,fl,qvec,rho,Te,alpha)
434 integer, intent(in) :: ixI^L, ixO^L
435 double precision, intent(in) :: x(ixI^S,1:ndim)
436 double precision, intent(in) :: w(ixI^S,1:nw)
437 type(tc_fluid), intent(in) :: fl
438 double precision, intent(in) :: rho(ixI^S),Te(ixI^S)
439 double precision, intent(in) :: alpha
440 double precision, intent(out) :: qvec(ixI^S,1:ndim)
441 double precision :: rhos(ixI^S)
442
443 !! qdd store the heat conduction energy changing rate
444 double precision, dimension(ixI^S,1:ndim) :: mf,Bc,Bcf,gradT
445 double precision, dimension(ixI^S) :: ka,kaf,ke,kef,qdd,Bnorm
446 double precision :: minq,maxq,qd(ixI^S,2**(ndim-1)), blocal(ndir)
447 integer :: idims,idir,ix^D,ix^L,ixC^L,ixA^L,ixB^L
448
449 ix^l=ixo^l^ladd1;
450 if(fl%tc_saturate) call tc_sat_rho(ixi^l,w,x,fl,rho,rhos)
451
452 ! T gradient at cell faces
453 ! b unit vector mf: magnetic field direction vector
454 if(allocated(iw_mag)) then
455 if(b0field) then
456 {do ix^db=ixmin^db,ixmax^db\}
457 ^c&blocal(^c)=w({ix^d},iw_mag(^c))+block%B0({ix^d},^c,0)\
458 {^iftwod
459 if(blocal(1)/=0.d0) then
460 mf(ix^d,1)=sign(1.d0,blocal(1))/dsqrt(1.d0+(^ce&(blocal(^ce)/blocal(1))**2+))
461 else
462 mf(ix^d,1)=0.d0
463 end if
464 if(blocal(2)/=0.d0) then
465 mf(ix^d,2)=sign(1.d0,blocal(2))/dsqrt(1.d0+(^cf&(blocal(^cf)/blocal(2))**2+))
466 else
467 mf(ix^d,2)=0.d0
468 end if
469 }
470 {^ifthreed
471 if(blocal(1)/=0.d0) then
472 mf(ix^d,1)=sign(1.d0,blocal(1))/dsqrt(1.d0+(blocal(2)/blocal(1))**2+(blocal(3)/blocal(1))**2)
473 else
474 mf(ix^d,1)=0.d0
475 end if
476 if(blocal(2)/=0.d0) then
477 mf(ix^d,2)=sign(1.d0,blocal(2))/dsqrt(1.d0+(blocal(1)/blocal(2))**2+(blocal(3)/blocal(2))**2)
478 else
479 mf(ix^d,2)=0.d0
480 end if
481 if(blocal(3)/=0.d0) then
482 mf(ix^d,3)=sign(1.d0,blocal(3))/dsqrt(1.d0+(blocal(1)/blocal(3))**2+(blocal(2)/blocal(3))**2)
483 else
484 mf(ix^d,3)=0.d0
485 end if
486 }
487 {end do\}
488 else
489 {do ix^db=ixmin^db,ixmax^db\}
490 {^iftwod
491 if(w(ix^d,iw_mag(1))/=0.d0) then
492 mf(ix^d,1)=sign(1.d0,w(ix^d,iw_mag(1)))/dsqrt(1.d0+(^ce&(w(ix^d,iw_mag(^ce))/w(ix^d,iw_mag(1)))**2+))
493 else
494 mf(ix^d,1)=0.d0
495 end if
496 if(w(ix^d,iw_mag(2))/=0.d0) then
497 mf(ix^d,2)=sign(1.d0,w(ix^d,iw_mag(2)))/dsqrt(1.d0+(^cf&(w(ix^d,iw_mag(^cf))/w(ix^d,iw_mag(2)))**2+))
498 else
499 mf(ix^d,2)=0.d0
500 end if
501 }
502 {^ifthreed
503 if(w(ix^d,iw_mag(1))/=0.d0) then
504 mf(ix^d,1)=sign(1.d0,w(ix^d,iw_mag(1)))/dsqrt(1.d0+(w(ix^d,iw_mag(2))/w(ix^d,iw_mag(1)))**2+&
505 (w(ix^d,iw_mag(3))/w(ix^d,iw_mag(1)))**2)
506 else
507 mf(ix^d,1)=0.d0
508 end if
509 if(w(ix^d,iw_mag(2))/=0.d0) then
510 mf(ix^d,2)=sign(1.d0,w(ix^d,iw_mag(2)))/dsqrt(1.d0+(w(ix^d,iw_mag(1))/w(ix^d,iw_mag(2)))**2+&
511 (w(ix^d,iw_mag(3))/w(ix^d,iw_mag(2)))**2)
512 else
513 mf(ix^d,2)=0.d0
514 end if
515 if(w(ix^d,iw_mag(3))/=0.d0) then
516 mf(ix^d,3)=sign(1.d0,w(ix^d,iw_mag(3)))/dsqrt(1.d0+(w(ix^d,iw_mag(1))/w(ix^d,iw_mag(3)))**2+&
517 (w(ix^d,iw_mag(2))/w(ix^d,iw_mag(3)))**2)
518 else
519 mf(ix^d,3)=0.d0
520 end if
521 }
522 {end do\}
523 end if
524 else
525 mf(ix^s,1:ndim)=block%B0(ix^s,1:ndim,0)
526 endif
527 ! ixC is cell-corner index
528 ixcmax^d=ixomax^d; ixcmin^d=ixomin^d-1;
529 ! b unit vector at cell corner
530 {^ifthreed
531 do idims=1,3
532 {do ix^db=ixcmin^db,ixcmax^db\}
533 bc(ix^d,idims)=0.125d0*(mf(ix1,ix2,ix3,idims)+mf(ix1+1,ix2,ix3,idims)&
534 +mf(ix1,ix2+1,ix3,idims)+mf(ix1+1,ix2+1,ix3,idims)&
535 +mf(ix1,ix2,ix3+1,idims)+mf(ix1+1,ix2,ix3+1,idims)&
536 +mf(ix1,ix2+1,ix3+1,idims)+mf(ix1+1,ix2+1,ix3+1,idims))
537 {end do\}
538 end do
539 }
540 {^iftwod
541 do idims=1,2
542 {do ix^db=ixcmin^db,ixcmax^db\}
543 bc(ix^d,idims)=0.25d0*(mf(ix1,ix2,idims)+mf(ix1+1,ix2,idims)&
544 +mf(ix1,ix2+1,idims)+mf(ix1+1,ix2+1,idims))
545 {end do\}
546 end do
547 }
548 ! T gradient at cell faces
549 do idims=1,ndim
550 ixbmin^d=ixmin^d;
551 ixbmax^d=ixmax^d-kr(idims,^d);
552 call gradientf(te,x,ixi^l,ixb^l,idims,gradt(ixi^s,idims))
553 end do
554 if(fl%tc_constant) then
555 if(fl%tc_perpendicular) then
556 ka(ixc^s)=fl%tc_k_para-fl%tc_k_perp
557 ke(ixc^s)=fl%tc_k_perp
558 else
559 ka(ixc^s)=fl%tc_k_para
560 end if
561 else
562 ! conductivity at cell center
563 if(phys_trac) then
564 {do ix^db=ixmin^db,ixmax^db\}
565 if(te(ix^d) < block%wextra(ix^d,fl%Tcoff_)) then
566 qdd(ix^d)=fl%tc_k_para*dsqrt(block%wextra(ix^d,fl%Tcoff_)**5)
567 else
568 qdd(ix^d)=fl%tc_k_para*dsqrt(te(ix^d)**5)
569 end if
570 {end do\}
571 else
572 qdd(ix^s)=fl%tc_k_para*dsqrt(te(ix^s)**5)
573 end if
574 ! cell corner parallel conductivity in ka
575 {^ifthreed
576 {do ix^db=ixcmin^db,ixcmax^db\}
577 ka(ix^d)=0.125d0*(qdd(ix1,ix2,ix3)+qdd(ix1+1,ix2,ix3)&
578 +qdd(ix1,ix2+1,ix3)+qdd(ix1+1,ix2+1,ix3)&
579 +qdd(ix1,ix2,ix3+1)+qdd(ix1+1,ix2,ix3+1)&
580 +qdd(ix1,ix2+1,ix3+1)+qdd(ix1+1,ix2+1,ix3+1))
581 {end do\}
582 }
583 {^iftwod
584 {do ix^db=ixcmin^db,ixcmax^db\}
585 ka(ix^d)=0.25d0*(qdd(ix1,ix2)+qdd(ix1+1,ix2)&
586 +qdd(ix1,ix2+1)+qdd(ix1+1,ix2+1))
587 {end do\}
588 }
589 ! compensate with perpendicular conductivity
590 if(fl%tc_perpendicular) then
591 if(b0field) then
592 qdd(ix^s)=fl%tc_k_perp*rho(ix^s)**2/((^c&(w(ix^s,iw_mag(^c))+block%B0(ix^s,^c,0))**2+)*dsqrt(te(ix^s))+smalldouble)
593 else
594 qdd(ix^s)=fl%tc_k_perp*rho(ix^s)**2/((^c&w(ix^s,iw_mag(^c))**2+)*dsqrt(te(ix^s))+smalldouble)
595 end if
596 {^ifthreed
597 {do ix^db=ixcmin^db,ixcmax^db\}
598 ke(ix^d)=0.125d0*(qdd(ix1,ix2,ix3)+qdd(ix1+1,ix2,ix3)&
599 +qdd(ix1,ix2+1,ix3)+qdd(ix1+1,ix2+1,ix3)&
600 +qdd(ix1,ix2,ix3+1)+qdd(ix1+1,ix2,ix3+1)&
601 +qdd(ix1,ix2+1,ix3+1)+qdd(ix1+1,ix2+1,ix3+1))
602 if(ke(ix^d)<ka(ix^d)) then
603 ka(ix^d)=ka(ix^d)-ke(ix^d)
604 else
605 ke(ix^d)=ka(ix^d)
606 ka(ix^d)=0.d0
607 end if
608 {end do\}
609 }
610 {^iftwod
611 {do ix^db=ixcmin^db,ixcmax^db\}
612 ke(ix^d)=0.25d0*(qdd(ix1,ix2)+qdd(ix1+1,ix2)&
613 +qdd(ix1,ix2+1)+qdd(ix1+1,ix2+1))
614 if(ke(ix^d)<ka(ix^d)) then
615 ka(ix^d)=ka(ix^d)-ke(ix^d)
616 else
617 ke(ix^d)=ka(ix^d)
618 ka(ix^d)=0.d0
619 end if
620 {end do\}
621 }
622 end if
623 end if
624 ! calculate thermal conduction flux with slope-limited symmetric scheme
625 do idims=1,ndim
626 ixamax^d=ixomax^d; ixamin^d=ixomin^d-kr(idims,^d);
627 {^ifthreed
628 if(idims==1) then
629 {do ix^db=ixamin^db,ixamax^db\}
630 ! averaged b at face centers
631 ^d&bcf({ix^d},^d)=0.25d0*(bc({ix^d},^d)+bc(ix1,ix2-1,ix3,^d)&
632 +bc(ix1,ix2,ix3-1,^d)+bc(ix1,ix2-1,ix3-1,^d))\
633 kaf(ix^d)=0.25d0*(ka(ix1,ix2,ix3)+ka(ix1,ix2-1,ix3)&
634 +ka(ix1,ix2,ix3-1)+ka(ix1,ix2-1,ix3-1))
635 ! averaged thermal conductivity at face centers
636 if(fl%tc_perpendicular) &
637 kef(ix^d)=0.25d0*(ke(ix1,ix2,ix3)+ke(ix1,ix2-1,ix3)&
638 +ke(ix1,ix2,ix3-1)+ke(ix1,ix2-1,ix3-1))
639 {end do\}
640 else if(idims==2) then
641 {do ix^db=ixamin^db,ixamax^db\}
642 ^d&bcf({ix^d},^d)=0.25d0*(bc({ix^d},^d)+bc(ix1-1,ix2,ix3,^d)&
643 +bc(ix1,ix2,ix3-1,^d)+bc(ix1-1,ix2,ix3-1,^d))\
644 kaf(ix^d)=0.25d0*(ka(ix1,ix2,ix3)+ka(ix1-1,ix2,ix3)&
645 +ka(ix1,ix2,ix3-1)+ka(ix1-1,ix2,ix3-1))
646 if(fl%tc_perpendicular) &
647 kef(ix^d)=0.25d0*(ke(ix1,ix2,ix3)+ke(ix1-1,ix2,ix3)&
648 +ke(ix1,ix2,ix3-1)+ke(ix1-1,ix2,ix3-1))
649 {end do\}
650 else
651 {do ix^db=ixamin^db,ixamax^db\}
652 ^d&bcf({ix^d},^d)=0.25d0*(bc({ix^d},^d)+bc(ix1,ix2-1,ix3,^d)&
653 +bc(ix1-1,ix2,ix3,^d)+bc(ix1-1,ix2-1,ix3,^d))\
654 kaf(ix^d)=0.25d0*(ka(ix1,ix2,ix3)+ka(ix1,ix2-1,ix3)&
655 +ka(ix1-1,ix2,ix3)+ka(ix1-1,ix2-1,ix3))
656 if(fl%tc_perpendicular) &
657 kef(ix^d)=0.25d0*(ke(ix1,ix2,ix3)+ke(ix1,ix2-1,ix3)&
658 +ke(ix1-1,ix2,ix3)+ke(ix1-1,ix2-1,ix3))
659 {end do\}
660 end if
661 }
662 {^iftwod
663 if(idims==1) then
664 {do ix^db=ixamin^db,ixamax^db\}
665 ^d&bcf({ix^d},^d)=0.5d0*(bc(ix1,ix2,^d)+bc(ix1,ix2-1,^d))\
666 kaf(ix^d)=0.5d0*(ka(ix1,ix2)+ka(ix1,ix2-1))
667 if(fl%tc_perpendicular) &
668 kef(ix^d)=0.5d0*(ke(ix1,ix2)+ke(ix1,ix2-1))
669 {end do\}
670 else
671 {do ix^db=ixamin^db,ixamax^db\}
672 ^d&bcf({ix^d},^d)=0.5d0*(bc(ix1,ix2,^d)+bc(ix1-1,ix2,^d))\
673 kaf(ix^d)=0.5d0*(ka(ix1,ix2)+ka(ix1-1,ix2))
674 if(fl%tc_perpendicular) &
675 kef(ix^d)=0.5d0*(ke(ix1,ix2)+ke(ix1-1,ix2))
676 {end do\}
677 end if
678 }
679 ! eq (19)
680 ! temperature gradient at cell corner
681 {^ifthreed
682 if(idims==1) then
683 {do ix^db=ixcmin^db,ixcmax^db\}
684 qdd(ix^d)=0.25d0*(gradt(ix1,ix2,ix3,idims)+gradt(ix1,ix2+1,ix3,idims)&
685 +gradt(ix1,ix2,ix3+1,idims)+gradt(ix1,ix2+1,ix3+1,idims))
686 {end do\}
687 else if(idims==2) then
688 {do ix^db=ixcmin^db,ixcmax^db\}
689 qdd(ix^d)=0.25d0*(gradt(ix1,ix2,ix3,idims)+gradt(ix1+1,ix2,ix3,idims)&
690 +gradt(ix1,ix2,ix3+1,idims)+gradt(ix1+1,ix2,ix3+1,idims))
691 {end do\}
692 else
693 {do ix^db=ixcmin^db,ixcmax^db\}
694 qdd(ix^d)=0.25d0*(gradt(ix1,ix2,ix3,idims)+gradt(ix1+1,ix2,ix3,idims)&
695 +gradt(ix1,ix2+1,ix3,idims)+gradt(ix1+1,ix2+1,ix3,idims))
696 {end do\}
697 end if
698 }
699 {^iftwod
700 if(idims==1) then
701 {do ix^db=ixcmin^db,ixcmax^db\}
702 qdd(ix^d)=0.5d0*(gradt(ix1,ix2,idims)+gradt(ix1,ix2+1,idims))
703 {end do\}
704 else
705 {do ix^db=ixcmin^db,ixcmax^db\}
706 qdd(ix^d)=0.5d0*(gradt(ix1,ix2,idims)+gradt(ix1+1,ix2,idims))
707 {end do\}
708 end if
709 }
710 ! eq (21)
711 {^ifthreed
712 if(idims==1) then
713 {do ix^db=ixamin^db,ixamax^db\}
714 minq=min(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
715 maxq=max(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
716 if(qdd(ix^d)<minq) then
717 qd(ix^d,1)=minq
718 else if(qdd(ix^d)>maxq) then
719 qd(ix^d,1)=maxq
720 else
721 qd(ix^d,1)=qdd(ix^d)
722 end if
723 if(qdd(ix1,ix2-1,ix3)<minq) then
724 qd(ix^d,2)=minq
725 else if(qdd(ix1,ix2-1,ix3)>maxq) then
726 qd(ix^d,2)=maxq
727 else
728 qd(ix^d,2)=qdd(ix1,ix2-1,ix3)
729 end if
730 if(qdd(ix1,ix2,ix3-1)<minq) then
731 qd(ix^d,3)=minq
732 else if(qdd(ix1,ix2,ix3-1)>maxq) then
733 qd(ix^d,3)=maxq
734 else
735 qd(ix^d,3)=qdd(ix1,ix2,ix3-1)
736 end if
737 if(qdd(ix1,ix2-1,ix3-1)<minq) then
738 qd(ix^d,4)=minq
739 else if(qdd(ix1,ix2-1,ix3-1)>maxq) then
740 qd(ix^d,4)=maxq
741 else
742 qd(ix^d,4)=qdd(ix1,ix2-1,ix3-1)
743 end if
744 qvec(ix^d,idims)=kaf(ix^d)*0.25d0*(bc(ix^d,idims)**2*qd(ix^d,1)+bc(ix1,ix2-1,ix3,idims)**2*qd(ix^d,2)&
745 +bc(ix1,ix2,ix3-1,idims)**2*qd(ix^d,3)+bc(ix1,ix2-1,ix3-1,idims)**2*qd(ix^d,4))
746 if(fl%tc_perpendicular) &
747 qvec(ix^d,idims)=qvec(ix^d,idims)+kef(ix^d)*0.25d0*(qd(ix^d,1)+qd(ix^d,2)+qd(ix^d,3)+qd(ix^d,4))
748 {end do\}
749 else if(idims==2) then
750 {do ix^db=ixamin^db,ixamax^db\}
751 minq=min(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
752 maxq=max(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
753 if(qdd(ix^d)<minq) then
754 qd(ix^d,1)=minq
755 else if(qdd(ix^d)>maxq) then
756 qd(ix^d,1)=maxq
757 else
758 qd(ix^d,1)=qdd(ix^d)
759 end if
760 if(qdd(ix1-1,ix2,ix3)<minq) then
761 qd(ix^d,2)=minq
762 else if(qdd(ix1-1,ix2,ix3)>maxq) then
763 qd(ix^d,2)=maxq
764 else
765 qd(ix^d,2)=qdd(ix1-1,ix2,ix3)
766 end if
767 if(qdd(ix1,ix2,ix3-1)<minq) then
768 qd(ix^d,3)=minq
769 else if(qdd(ix1,ix2,ix3-1)>maxq) then
770 qd(ix^d,3)=maxq
771 else
772 qd(ix^d,3)=qdd(ix1,ix2,ix3-1)
773 end if
774 if(qdd(ix1-1,ix2,ix3-1)<minq) then
775 qd(ix^d,4)=minq
776 else if(qdd(ix1-1,ix2,ix3-1)>maxq) then
777 qd(ix^d,4)=maxq
778 else
779 qd(ix^d,4)=qdd(ix1-1,ix2,ix3-1)
780 end if
781 qvec(ix^d,idims)=kaf(ix^d)*0.25d0*(bc(ix^d,idims)**2*qd(ix^d,1)+bc(ix1-1,ix2,ix3,idims)**2*qd(ix^d,2)&
782 +bc(ix1,ix2,ix3-1,idims)**2*qd(ix^d,3)+bc(ix1-1,ix2,ix3-1,idims)**2*qd(ix^d,4))
783 if(fl%tc_perpendicular) &
784 qvec(ix^d,idims)=qvec(ix^d,idims)+kef(ix^d)*0.25d0*(qd(ix^d,1)+qd(ix^d,2)+qd(ix^d,3)+qd(ix^d,4))
785 {end do\}
786 else
787 {do ix^db=ixamin^db,ixamax^db\}
788 minq=min(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
789 maxq=max(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
790 if(qdd(ix^d)<minq) then
791 qd(ix^d,1)=minq
792 else if(qdd(ix^d)>maxq) then
793 qd(ix^d,1)=maxq
794 else
795 qd(ix^d,1)=qdd(ix^d)
796 end if
797 if(qdd(ix1-1,ix2,ix3)<minq) then
798 qd(ix^d,2)=minq
799 else if(qdd(ix1-1,ix2,ix3)>maxq) then
800 qd(ix^d,2)=maxq
801 else
802 qd(ix^d,2)=qdd(ix1-1,ix2,ix3)
803 end if
804 if(qdd(ix1,ix2-1,ix3)<minq) then
805 qd(ix^d,3)=minq
806 else if(qdd(ix1,ix2-1,ix3)>maxq) then
807 qd(ix^d,3)=maxq
808 else
809 qd(ix^d,3)=qdd(ix1,ix2-1,ix3)
810 end if
811 if(qdd(ix1-1,ix2-1,ix3)<minq) then
812 qd(ix^d,4)=minq
813 else if(qdd(ix1-1,ix2-1,ix3)>maxq) then
814 qd(ix^d,4)=maxq
815 else
816 qd(ix^d,4)=qdd(ix1-1,ix2-1,ix3)
817 end if
818 qvec(ix^d,idims)=kaf(ix^d)*0.25d0*(bc(ix^d,idims)**2*qd(ix^d,1)+bc(ix1-1,ix2,ix3,idims)**2*qd(ix^d,2)&
819 +bc(ix1,ix2-1,ix3,idims)**2*qd(ix^d,3)+bc(ix1-1,ix2-1,ix3,idims)**2*qd(ix^d,4))
820 if(fl%tc_perpendicular) &
821 qvec(ix^d,idims)=qvec(ix^d,idims)+kef(ix^d)*0.25d0*(qd(ix^d,1)+qd(ix^d,2)+qd(ix^d,3)+qd(ix^d,4))
822 {end do\}
823 end if
824 }
825 {^iftwod
826 if(idims==1) then
827 {do ix^db=ixamin^db,ixamax^db\}
828 minq=min(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
829 maxq=max(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
830 if(qdd(ix^d)<minq) then
831 qd(ix^d,1)=minq
832 else if(qdd(ix^d)>maxq) then
833 qd(ix^d,1)=maxq
834 else
835 qd(ix^d,1)=qdd(ix^d)
836 end if
837 if(qdd(ix1,ix2-1)<minq) then
838 qd(ix^d,2)=minq
839 else if(qdd(ix1,ix2-1)>maxq) then
840 qd(ix^d,2)=maxq
841 else
842 qd(ix^d,2)=qdd(ix1,ix2-1)
843 end if
844 qvec(ix^d,idims)=kaf(ix^d)*0.5d0*(bc(ix1,ix2,idims)**2*qd(ix^d,1)+bc(ix1,ix2-1,idims)**2*qd(ix^d,2))
845 if(fl%tc_perpendicular) &
846 qvec(ix^d,idims)=qvec(ix^d,idims)+kef(ix^d)*0.5d0*(qd(ix^d,1)+qd(ix^d,2))
847 {end do\}
848 else
849 {do ix^db=ixamin^db,ixamax^db\}
850 minq=min(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
851 maxq=max(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
852 if(qdd(ix^d)<minq) then
853 qd(ix^d,1)=minq
854 else if(qdd(ix^d)>maxq) then
855 qd(ix^d,1)=maxq
856 else
857 qd(ix^d,1)=qdd(ix^d)
858 end if
859 if(qdd(ix1-1,ix2)<minq) then
860 qd(ix^d,2)=minq
861 else if(qdd(ix1-1,ix2)>maxq) then
862 qd(ix^d,2)=maxq
863 else
864 qd(ix^d,2)=qdd(ix1-1,ix2)
865 end if
866 qvec(ix^d,idims)=kaf(ix^d)*0.5d0*(bc(ix1,ix2,idims)**2*qd(ix^d,1)+bc(ix1-1,ix2,idims)**2*qd(ix^d,2))
867 if(fl%tc_perpendicular) &
868 qvec(ix^d,idims)=qvec(ix^d,idims)+kef(ix^d)*0.5d0*(qd(ix^d,1)+qd(ix^d,2))
869 {end do\}
870 end if
871 }
872 ! calculate normal of magnetic field
873 ixb^l=ixa^l+kr(idims,^d);
874 bnorm(ixa^s)=0.5d0*(mf(ixa^s,idims)+mf(ixb^s,idims))
875 ! limited transverse component, eq (17)
876 ixbmin^d=ixamin^d;
877 ixbmax^d=ixamax^d+kr(idims,^d);
878 do idir=1,ndim
879 if(idir==idims) cycle
880 qdd(ixi^s)=slope_limiter(gradt(ixi^s,idir),ixi^l,ixb^l,idir,-1,fl%tc_slope_limiter)
881 qdd(ixi^s)=slope_limiter(qdd,ixi^l,ixa^l,idims,1,fl%tc_slope_limiter)
882 qvec(ixa^s,idims)=qvec(ixa^s,idims)+kaf(ixa^s)*bnorm(ixa^s)*bcf(ixa^s,idir)*qdd(ixa^s)
883 end do
884 if(fl%tc_saturate) then
885 ! consider saturation (Cowie and Mckee 1977 ApJ, 211, 135: phi=1.1, Balbus and Mckee 1982 ApJ, 252, 529: phi=0.3)
886 ! unsigned saturated TC flux = 5 phi rho c**3, c**2=p/rho=R T of the fully ionised gas, phi=0.3,
887 ! with rho scaled by the local over the fully ionised ne/nH under LTE, which is Cowie and McKee eq 7
888 ixb^l=ixa^l+kr(idims,^d);
889 qdd(ixa^s)=0.75d0*fl%sat_fac*(rhos(ixa^s)+rhos(ixb^s))*dsqrt(0.5d0*(te(ixa^s)+te(ixb^s)))**3*dabs(bnorm(ixa^s))
890 {do ix^db=ixamin^db,ixamax^db\}
891 if(dabs(qvec(ix^d,idims))>qdd(ix^d)) then
892 qvec(ix^d,idims)=sign(1.d0,qvec(ix^d,idims))*qdd(ix^d)
893 end if
894 {end do\}
895 end if
896 end do
897 end subroutine set_source_tc_mhd
898 subroutine set_source_tc_mhd_geo(ixI^L,ixO^L,w,x,fl,qvec,rho,Te,alpha)
900 integer, intent(in) :: ixI^L, ixO^L
901 double precision, intent(in) :: x(ixI^S,1:ndim)
902 double precision, intent(in) :: w(ixI^S,1:nw)
903 type(tc_fluid), intent(in) :: fl
904 double precision, intent(in) :: rho(ixI^S),Te(ixI^S)
905 double precision, intent(in) :: alpha
906 double precision, intent(out) :: qvec(ixI^S,1:ndim)
907 double precision :: rhos(ixI^S)
908
909 !! qdd store the heat conduction energy changing rate
910 double precision, dimension(ixI^S,1:ndim) :: mf,Bc,Bcf,gradT
911 double precision, dimension(ixI^S) :: ka,kaf,ke,kef,qdd,Bnorm
912 double precision :: minq,maxq,qd(ixI^S,2**(ndim-1)), blocal(ndir)
913 integer :: idims,idir,ix^D,ix^L,ixC^L,ixA^L,ixB^L
914
915 ix^l=ixo^l^ladd1;
916 if(fl%tc_saturate) call tc_sat_rho(ixi^l,w,x,fl,rho,rhos)
917
918 ! T gradient at cell faces
919 ! b unit vector mf: magnetic field direction vector
920 if(allocated(iw_mag)) then
921 if(b0field) then
922 {do ix^db=ixmin^db,ixmax^db\}
923 ^c&blocal(^c)=w({ix^d},iw_mag(^c))+block%B0({ix^d},^c,0)\
924 {^iftwod
925 if(blocal(1)/=0.d0) then
926 mf(ix^d,1)=sign(1.d0,blocal(1))/dsqrt(1.d0+(^ce&(blocal(^ce)/blocal(1))**2+))
927 else
928 mf(ix^d,1)=0.d0
929 end if
930 if(blocal(2)/=0.d0) then
931 mf(ix^d,2)=sign(1.d0,blocal(2))/dsqrt(1.d0+(^cf&(blocal(^cf)/blocal(2))**2+))
932 else
933 mf(ix^d,2)=0.d0
934 end if
935 }
936 {^ifthreed
937 if(blocal(1)/=0.d0) then
938 mf(ix^d,1)=sign(1.d0,blocal(1))/dsqrt(1.d0+(blocal(2)/blocal(1))**2+(blocal(3)/blocal(1))**2)
939 else
940 mf(ix^d,1)=0.d0
941 end if
942 if(blocal(2)/=0.d0) then
943 mf(ix^d,2)=sign(1.d0,blocal(2))/dsqrt(1.d0+(blocal(1)/blocal(2))**2+(blocal(3)/blocal(2))**2)
944 else
945 mf(ix^d,2)=0.d0
946 end if
947 if(blocal(3)/=0.d0) then
948 mf(ix^d,3)=sign(1.d0,blocal(3))/dsqrt(1.d0+(blocal(1)/blocal(3))**2+(blocal(2)/blocal(3))**2)
949 else
950 mf(ix^d,3)=0.d0
951 end if
952 }
953 {end do\}
954 else
955 {do ix^db=ixmin^db,ixmax^db\}
956 {^iftwod
957 if(w(ix^d,iw_mag(1))/=0.d0) then
958 mf(ix^d,1)=sign(1.d0,w(ix^d,iw_mag(1)))/dsqrt(1.d0+(^ce&(w(ix^d,iw_mag(^ce))/w(ix^d,iw_mag(1)))**2+))
959 else
960 mf(ix^d,1)=0.d0
961 end if
962 if(w(ix^d,iw_mag(2))/=0.d0) then
963 mf(ix^d,2)=sign(1.d0,w(ix^d,iw_mag(2)))/dsqrt(1.d0+(^cf&(w(ix^d,iw_mag(^cf))/w(ix^d,iw_mag(2)))**2+))
964 else
965 mf(ix^d,2)=0.d0
966 end if
967 }
968 {^ifthreed
969 if(w(ix^d,iw_mag(1))/=0.d0) then
970 mf(ix^d,1)=sign(1.d0,w(ix^d,iw_mag(1)))/dsqrt(1.d0+(w(ix^d,iw_mag(2))/w(ix^d,iw_mag(1)))**2+&
971 (w(ix^d,iw_mag(3))/w(ix^d,iw_mag(1)))**2)
972 else
973 mf(ix^d,1)=0.d0
974 end if
975 if(w(ix^d,iw_mag(2))/=0.d0) then
976 mf(ix^d,2)=sign(1.d0,w(ix^d,iw_mag(2)))/dsqrt(1.d0+(w(ix^d,iw_mag(1))/w(ix^d,iw_mag(2)))**2+&
977 (w(ix^d,iw_mag(3))/w(ix^d,iw_mag(2)))**2)
978 else
979 mf(ix^d,2)=0.d0
980 end if
981 if(w(ix^d,iw_mag(3))/=0.d0) then
982 mf(ix^d,3)=sign(1.d0,w(ix^d,iw_mag(3)))/dsqrt(1.d0+(w(ix^d,iw_mag(1))/w(ix^d,iw_mag(3)))**2+&
983 (w(ix^d,iw_mag(2))/w(ix^d,iw_mag(3)))**2)
984 else
985 mf(ix^d,3)=0.d0
986 end if
987 }
988 {end do\}
989 end if
990 else
991 mf(ix^s,1:ndim)=block%B0(ix^s,1:ndim,0)
992 endif
993 ! ixC is cell-corner index
994 ixcmax^d=ixomax^d; ixcmin^d=ixomin^d-1;
995 ! b unit vector at cell corner
996 {^ifthreed
997 do idims=1,3
998 {do ix^db=ixcmin^db,ixcmax^db\}
999 bc(ix^d,idims)=(mf(ix1,ix2,ix3,idims)*block%dvolume(ix1,ix2,ix3)+mf(ix1+1,ix2,ix3,idims)*block%dvolume(ix1+1,ix2,ix3)&
1000 +mf(ix1,ix2+1,ix3,idims)*block%dvolume(ix1,ix2+1,ix3)+mf(ix1+1,ix2+1,ix3,idims)*block%dvolume(ix1+1,ix2+1,ix3)&
1001 +mf(ix1,ix2,ix3+1,idims)*block%dvolume(ix1,ix2,ix3+1)+mf(ix1+1,ix2,ix3+1,idims)*block%dvolume(ix1+1,ix2,ix3+1)&
1002 +mf(ix1,ix2+1,ix3+1,idims)*block%dvolume(ix1,ix2+1,ix3+1)+mf(ix1+1,ix2+1,ix3+1,idims)*block%dvolume(ix1+1,ix2+1,ix3+1))&
1003 /(block%dvolume(ix1,ix2,ix3)+block%dvolume(ix1+1,ix2,ix3)+block%dvolume(ix1,ix2+1,ix3)+block%dvolume(ix1+1,ix2+1,ix3)&
1004 +block%dvolume(ix1,ix2,ix3+1)+block%dvolume(ix1+1,ix2,ix3+1)+block%dvolume(ix1,ix2+1,ix3+1)+block%dvolume(ix1+1,ix2+1,ix3+1))
1005 {end do\}
1006 end do
1007 }
1008 {^iftwod
1009 do idims=1,2
1010 {do ix^db=ixcmin^db,ixcmax^db\}
1011 bc(ix^d,idims)=(mf(ix1,ix2,idims)*block%dvolume(ix1,ix2)+mf(ix1+1,ix2,idims)*block%dvolume(ix1+1,ix2)&
1012 +mf(ix1,ix2+1,idims)*block%dvolume(ix1,ix2+1)+mf(ix1+1,ix2+1,idims)*block%dvolume(ix1+1,ix2+1))&
1013 /(block%dvolume(ix1,ix2)+block%dvolume(ix1+1,ix2)+block%dvolume(ix1,ix2+1)+block%dvolume(ix1+1,ix2+1))
1014 {end do\}
1015 end do
1016 }
1017 ! T gradient at cell faces
1018 do idims=1,ndim
1019 ixbmin^d=ixmin^d;
1020 ixbmax^d=ixmax^d-kr(idims,^d);
1021 call gradientf(te,x,ixi^l,ixb^l,idims,gradt(ixi^s,idims))
1022 end do
1023 if(fl%tc_constant) then
1024 if(fl%tc_perpendicular) then
1025 ka(ixc^s)=fl%tc_k_para-fl%tc_k_perp
1026 ke(ixc^s)=fl%tc_k_perp
1027 else
1028 ka(ixc^s)=fl%tc_k_para
1029 end if
1030 else
1031 ! conductivity at cell center
1032 if(phys_trac) then
1033 {do ix^db=ixmin^db,ixmax^db\}
1034 if(te(ix^d) < block%wextra(ix^d,fl%Tcoff_)) then
1035 qdd(ix^d)=fl%tc_k_para*dsqrt(block%wextra(ix^d,fl%Tcoff_)**5)
1036 else
1037 qdd(ix^d)=fl%tc_k_para*dsqrt(te(ix^d)**5)
1038 end if
1039 {end do\}
1040 else
1041 qdd(ix^s)=fl%tc_k_para*dsqrt(te(ix^s)**5)
1042 end if
1043 ! cell corner parallel conductivity in ka
1044 {^ifthreed
1045 {do ix^db=ixcmin^db,ixcmax^db\}
1046 ka(ix^d)=(qdd(ix1,ix2,ix3)*block%dvolume(ix1,ix2,ix3)+qdd(ix1+1,ix2,ix3)*block%dvolume(ix1+1,ix2,ix3)&
1047 +qdd(ix1,ix2+1,ix3)*block%dvolume(ix1,ix2+1,ix3)+qdd(ix1+1,ix2+1,ix3)*block%dvolume(ix1+1,ix2+1,ix3)&
1048 +qdd(ix1,ix2,ix3+1)*block%dvolume(ix1,ix2,ix3+1)+qdd(ix1+1,ix2,ix3+1)*block%dvolume(ix1+1,ix2,ix3+1)&
1049 +qdd(ix1,ix2+1,ix3+1)*block%dvolume(ix1,ix2+1,ix3+1)+qdd(ix1+1,ix2+1,ix3+1)*block%dvolume(ix1+1,ix2+1,ix3+1))&
1050 /(block%dvolume(ix1,ix2,ix3)+block%dvolume(ix1+1,ix2,ix3)+block%dvolume(ix1,ix2+1,ix3)+block%dvolume(ix1+1,ix2+1,ix3)&
1051 +block%dvolume(ix1,ix2,ix3+1)+block%dvolume(ix1+1,ix2,ix3+1)+block%dvolume(ix1,ix2+1,ix3+1)+block%dvolume(ix1+1,ix2+1,ix3+1))
1052 {end do\}
1053 }
1054 {^iftwod
1055 {do ix^db=ixcmin^db,ixcmax^db\}
1056 ka(ix^d)=(qdd(ix1,ix2)*block%dvolume(ix1,ix2)+qdd(ix1+1,ix2)*block%dvolume(ix1+1,ix2)&
1057 +qdd(ix1,ix2+1)*block%dvolume(ix1,ix2+1)+qdd(ix1+1,ix2+1)*block%dvolume(ix1+1,ix2+1))&
1058 /(block%dvolume(ix1,ix2)+block%dvolume(ix1+1,ix2)+block%dvolume(ix1,ix2+1)+block%dvolume(ix1+1,ix2+1))
1059 {end do\}
1060 }
1061 ! compensate with perpendicular conductivity
1062 if(fl%tc_perpendicular) then
1063 if(b0field) then
1064 qdd(ix^s)=fl%tc_k_perp*rho(ix^s)**2/((^c&(w(ix^s,iw_mag(^c))+block%B0(ix^s,^c,0))**2+)*dsqrt(te(ix^s))+smalldouble)
1065 else
1066 qdd(ix^s)=fl%tc_k_perp*rho(ix^s)**2/((^c&w(ix^s,iw_mag(^c))**2+)*dsqrt(te(ix^s))+smalldouble)
1067 end if
1068 {^ifthreed
1069 {do ix^db=ixcmin^db,ixcmax^db\}
1070 ke(ix^d)=(qdd(ix1,ix2,ix3)*block%dvolume(ix1,ix2,ix3)+qdd(ix1+1,ix2,ix3)*block%dvolume(ix1+1,ix2,ix3)&
1071 +qdd(ix1,ix2+1,ix3)*block%dvolume(ix1,ix2+1,ix3)+qdd(ix1+1,ix2+1,ix3)*block%dvolume(ix1+1,ix2+1,ix3)&
1072 +qdd(ix1,ix2,ix3+1)*block%dvolume(ix1,ix2,ix3+1)+qdd(ix1+1,ix2,ix3+1)*block%dvolume(ix1+1,ix2,ix3+1)&
1073 +qdd(ix1,ix2+1,ix3+1)*block%dvolume(ix1,ix2+1,ix3+1)+qdd(ix1+1,ix2+1,ix3+1)*block%dvolume(ix1+1,ix2+1,ix3+1))&
1074 /(block%dvolume(ix1,ix2,ix3)+block%dvolume(ix1+1,ix2,ix3)+block%dvolume(ix1,ix2+1,ix3)+block%dvolume(ix1+1,ix2+1,ix3)&
1075 +block%dvolume(ix1,ix2,ix3+1)+block%dvolume(ix1+1,ix2,ix3+1)+block%dvolume(ix1,ix2+1,ix3+1)+block%dvolume(ix1+1,ix2+1,ix3+1))
1076 if(ke(ix^d)<ka(ix^d)) then
1077 ka(ix^d)=ka(ix^d)-ke(ix^d)
1078 else
1079 ke(ix^d)=ka(ix^d)
1080 ka(ix^d)=0.d0
1081 end if
1082 {end do\}
1083 }
1084 {^iftwod
1085 {do ix^db=ixcmin^db,ixcmax^db\}
1086 ke(ix^d)=(qdd(ix1,ix2)*block%dvolume(ix1,ix2)+qdd(ix1+1,ix2)*block%dvolume(ix1+1,ix2)&
1087 +qdd(ix1,ix2+1)*block%dvolume(ix1,ix2+1)+qdd(ix1+1,ix2+1)*block%dvolume(ix1+1,ix2+1))&
1088 /(block%dvolume(ix1,ix2)+block%dvolume(ix1+1,ix2)+block%dvolume(ix1,ix2+1)+block%dvolume(ix1+1,ix2+1))
1089 if(ke(ix^d)<ka(ix^d)) then
1090 ka(ix^d)=ka(ix^d)-ke(ix^d)
1091 else
1092 ke(ix^d)=ka(ix^d)
1093 ka(ix^d)=0.d0
1094 end if
1095 {end do\}
1096 }
1097 end if
1098 end if
1099 ! calculate thermal conduction flux with slope-limited symmetric scheme
1100 do idims=1,ndim
1101 ixamax^d=ixomax^d; ixamin^d=ixomin^d-kr(idims,^d);
1102 {^ifthreed
1103 if(idims==1) then
1104 {do ix^db=ixamin^db,ixamax^db\}
1105 ! averaged b at face centers
1106 ^d&bcf({ix^d},^d)=0.25d0*(bc({ix^d},^d)+bc(ix1,ix2-1,ix3,^d)&
1107 +bc(ix1,ix2,ix3-1,^d)+bc(ix1,ix2-1,ix3-1,^d))\
1108 kaf(ix^d)=0.25d0*(ka(ix1,ix2,ix3)+ka(ix1,ix2-1,ix3)&
1109 +ka(ix1,ix2,ix3-1)+ka(ix1,ix2-1,ix3-1))
1110 ! averaged thermal conductivity at face centers
1111 if(fl%tc_perpendicular) &
1112 kef(ix^d)=0.25d0*(ke(ix1,ix2,ix3)+ke(ix1,ix2-1,ix3)&
1113 +ke(ix1,ix2,ix3-1)+ke(ix1,ix2-1,ix3-1))
1114 {end do\}
1115 else if(idims==2) then
1116 {do ix^db=ixamin^db,ixamax^db\}
1117 ^d&bcf({ix^d},^d)=0.25d0*(bc({ix^d},^d)+bc(ix1-1,ix2,ix3,^d)&
1118 +bc(ix1,ix2,ix3-1,^d)+bc(ix1-1,ix2,ix3-1,^d))\
1119 kaf(ix^d)=0.25d0*(ka(ix1,ix2,ix3)+ka(ix1-1,ix2,ix3)&
1120 +ka(ix1,ix2,ix3-1)+ka(ix1-1,ix2,ix3-1))
1121 if(fl%tc_perpendicular) &
1122 kef(ix^d)=0.25d0*(ke(ix1,ix2,ix3)+ke(ix1-1,ix2,ix3)&
1123 +ke(ix1,ix2,ix3-1)+ke(ix1-1,ix2,ix3-1))
1124 {end do\}
1125 else
1126 {do ix^db=ixamin^db,ixamax^db\}
1127 ^d&bcf({ix^d},^d)=0.25d0*(bc({ix^d},^d)+bc(ix1,ix2-1,ix3,^d)&
1128 +bc(ix1-1,ix2,ix3,^d)+bc(ix1-1,ix2-1,ix3,^d))\
1129 kaf(ix^d)=0.25d0*(ka(ix1,ix2,ix3)+ka(ix1,ix2-1,ix3)&
1130 +ka(ix1-1,ix2,ix3)+ka(ix1-1,ix2-1,ix3))
1131 if(fl%tc_perpendicular) &
1132 kef(ix^d)=0.25d0*(ke(ix1,ix2,ix3)+ke(ix1,ix2-1,ix3)&
1133 +ke(ix1-1,ix2,ix3)+ke(ix1-1,ix2-1,ix3))
1134 {end do\}
1135 end if
1136 }
1137 {^iftwod
1138 if(idims==1) then
1139 {do ix^db=ixamin^db,ixamax^db\}
1140 ^d&bcf({ix^d},^d)=0.5d0*(bc(ix1,ix2,^d)+bc(ix1,ix2-1,^d))\
1141 kaf(ix^d)=0.5d0*(ka(ix1,ix2)+ka(ix1,ix2-1))
1142 if(fl%tc_perpendicular) &
1143 kef(ix^d)=0.5d0*(ke(ix1,ix2)+ke(ix1,ix2-1))
1144 {end do\}
1145 else
1146 {do ix^db=ixamin^db,ixamax^db\}
1147 ^d&bcf({ix^d},^d)=0.5d0*(bc(ix1,ix2,^d)+bc(ix1-1,ix2,^d))\
1148 kaf(ix^d)=0.5d0*(ka(ix1,ix2)+ka(ix1-1,ix2))
1149 if(fl%tc_perpendicular) &
1150 kef(ix^d)=0.5d0*(ke(ix1,ix2)+ke(ix1-1,ix2))
1151 {end do\}
1152 end if
1153 }
1154 ! eq (19)
1155 ! temperature gradient at cell corner
1156 {^ifthreed
1157 if(idims==1) then
1158 {do ix^db=ixcmin^db,ixcmax^db\}
1159 qdd(ix^d)=(gradt(ix1,ix2,ix3,idims)*block%surfaceC(ix1,ix2,ix3,1)&
1160 +gradt(ix1,ix2+1,ix3,idims)*block%surfaceC(ix1,ix2+1,ix3,1)&
1161 +gradt(ix1,ix2,ix3+1,idims)*block%surfaceC(ix1,ix2,ix3+1,1)&
1162 +gradt(ix1,ix2+1,ix3+1,idims)*block%surfaceC(ix1,ix2+1,ix3+1,1))/&
1163 (block%surfaceC(ix1,ix2,ix3,1)+block%surfaceC(ix1,ix2+1,ix3,1)&
1164 +block%surfaceC(ix1,ix2,ix3+1,1)+block%surfaceC(ix1,ix2+1,ix3+1,1)+smalldouble**2)
1165 {end do\}
1166 else if(idims==2) then
1167 {do ix^db=ixcmin^db,ixcmax^db\}
1168 qdd(ix^d)=(gradt(ix1,ix2,ix3,idims)*block%surfaceC(ix1,ix2,ix3,2)&
1169 +gradt(ix1+1,ix2,ix3,idims)*block%surfaceC(ix1+1,ix2,ix3,2)&
1170 +gradt(ix1,ix2,ix3+1,idims)*block%surfaceC(ix1,ix2,ix3+1,2)&
1171 +gradt(ix1+1,ix2,ix3+1,idims)*block%surfaceC(ix1+1,ix2,ix3+1,2))/&
1172 (block%surfaceC(ix1,ix2,ix3,2)+block%surfaceC(ix1+1,ix2,ix3,2)&
1173 +block%surfaceC(ix1,ix2,ix3+1,2)+block%surfaceC(ix1+1,ix2,ix3+1,2)+smalldouble**2)
1174 {end do\}
1175 else
1176 {do ix^db=ixcmin^db,ixcmax^db\}
1177 qdd(ix^d)=(gradt(ix1,ix2,ix3,idims)*block%surfaceC(ix1,ix2,ix3,3)&
1178 +gradt(ix1+1,ix2,ix3,idims)*block%surfaceC(ix1+1,ix2,ix3,3)&
1179 +gradt(ix1,ix2+1,ix3,idims)*block%surfaceC(ix1,ix2+1,ix3,3)&
1180 +gradt(ix1+1,ix2+1,ix3,idims)*block%surfaceC(ix1+1,ix2+1,ix3,3))/&
1181 (block%surfaceC(ix1,ix2,ix3,3)+block%surfaceC(ix1+1,ix2,ix3,3)&
1182 +block%surfaceC(ix1,ix2+1,ix3,3)+block%surfaceC(ix1+1,ix2+1,ix3,3))
1183 {end do\}
1184 end if
1185 }
1186 {^iftwod
1187 if(idims==1) then
1188 {do ix^db=ixcmin^db,ixcmax^db\}
1189 qdd(ix^d)=(gradt(ix1,ix2,idims)*block%surfaceC(ix1,ix2,1)&
1190 +gradt(ix1,ix2+1,idims)*block%surfaceC(ix1,ix2+1,1))/&
1191 (block%surfaceC(ix1,ix2,1)+block%surfaceC(ix1,ix2+1,1))
1192 {end do\}
1193 else
1194 {do ix^db=ixcmin^db,ixcmax^db\}
1195 qdd(ix^d)=(gradt(ix1,ix2,idims)*block%surfaceC(ix1,ix2,2)&
1196 +gradt(ix1+1,ix2,idims)*block%surfaceC(ix1+1,ix2,2))/&
1197 (block%surfaceC(ix1,ix2,2)+block%surfaceC(ix1+1,ix2,2)+smalldouble)
1198 {end do\}
1199 end if
1200 }
1201 ! eq (21)
1202 {^ifthreed
1203 if(idims==1) then
1204 {do ix^db=ixamin^db,ixamax^db\}
1205 minq=min(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
1206 maxq=max(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
1207 if(qdd(ix^d)<minq) then
1208 qd(ix^d,1)=minq
1209 else if(qdd(ix^d)>maxq) then
1210 qd(ix^d,1)=maxq
1211 else
1212 qd(ix^d,1)=qdd(ix^d)
1213 end if
1214 if(qdd(ix1,ix2-1,ix3)<minq) then
1215 qd(ix^d,2)=minq
1216 else if(qdd(ix1,ix2-1,ix3)>maxq) then
1217 qd(ix^d,2)=maxq
1218 else
1219 qd(ix^d,2)=qdd(ix1,ix2-1,ix3)
1220 end if
1221 if(qdd(ix1,ix2,ix3-1)<minq) then
1222 qd(ix^d,3)=minq
1223 else if(qdd(ix1,ix2,ix3-1)>maxq) then
1224 qd(ix^d,3)=maxq
1225 else
1226 qd(ix^d,3)=qdd(ix1,ix2,ix3-1)
1227 end if
1228 if(qdd(ix1,ix2-1,ix3-1)<minq) then
1229 qd(ix^d,4)=minq
1230 else if(qdd(ix1,ix2-1,ix3-1)>maxq) then
1231 qd(ix^d,4)=maxq
1232 else
1233 qd(ix^d,4)=qdd(ix1,ix2-1,ix3-1)
1234 end if
1235 qvec(ix^d,idims)=kaf(ix^d)*0.25d0*(bc(ix^d,idims)**2*qd(ix^d,1)+bc(ix1,ix2-1,ix3,idims)**2*qd(ix^d,2)&
1236 +bc(ix1,ix2,ix3-1,idims)**2*qd(ix^d,3)+bc(ix1,ix2-1,ix3-1,idims)**2*qd(ix^d,4))
1237 if(fl%tc_perpendicular) &
1238 qvec(ix^d,idims)=qvec(ix^d,idims)+kef(ix^d)*0.25d0*(qd(ix^d,1)+qd(ix^d,2)+qd(ix^d,3)+qd(ix^d,4))
1239 {end do\}
1240 else if(idims==2) then
1241 {do ix^db=ixamin^db,ixamax^db\}
1242 minq=min(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
1243 maxq=max(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
1244 if(qdd(ix^d)<minq) then
1245 qd(ix^d,1)=minq
1246 else if(qdd(ix^d)>maxq) then
1247 qd(ix^d,1)=maxq
1248 else
1249 qd(ix^d,1)=qdd(ix^d)
1250 end if
1251 if(qdd(ix1-1,ix2,ix3)<minq) then
1252 qd(ix^d,2)=minq
1253 else if(qdd(ix1-1,ix2,ix3)>maxq) then
1254 qd(ix^d,2)=maxq
1255 else
1256 qd(ix^d,2)=qdd(ix1-1,ix2,ix3)
1257 end if
1258 if(qdd(ix1,ix2,ix3-1)<minq) then
1259 qd(ix^d,3)=minq
1260 else if(qdd(ix1,ix2,ix3-1)>maxq) then
1261 qd(ix^d,3)=maxq
1262 else
1263 qd(ix^d,3)=qdd(ix1,ix2,ix3-1)
1264 end if
1265 if(qdd(ix1-1,ix2,ix3-1)<minq) then
1266 qd(ix^d,4)=minq
1267 else if(qdd(ix1-1,ix2,ix3-1)>maxq) then
1268 qd(ix^d,4)=maxq
1269 else
1270 qd(ix^d,4)=qdd(ix1-1,ix2,ix3-1)
1271 end if
1272 qvec(ix^d,idims)=kaf(ix^d)*0.25d0*(bc(ix^d,idims)**2*qd(ix^d,1)+bc(ix1-1,ix2,ix3,idims)**2*qd(ix^d,2)&
1273 +bc(ix1,ix2,ix3-1,idims)**2*qd(ix^d,3)+bc(ix1-1,ix2,ix3-1,idims)**2*qd(ix^d,4))
1274 if(fl%tc_perpendicular) &
1275 qvec(ix^d,idims)=qvec(ix^d,idims)+kef(ix^d)*0.25d0*(qd(ix^d,1)+qd(ix^d,2)+qd(ix^d,3)+qd(ix^d,4))
1276 {end do\}
1277 else
1278 {do ix^db=ixamin^db,ixamax^db\}
1279 minq=min(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
1280 maxq=max(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
1281 if(qdd(ix^d)<minq) then
1282 qd(ix^d,1)=minq
1283 else if(qdd(ix^d)>maxq) then
1284 qd(ix^d,1)=maxq
1285 else
1286 qd(ix^d,1)=qdd(ix^d)
1287 end if
1288 if(qdd(ix1-1,ix2,ix3)<minq) then
1289 qd(ix^d,2)=minq
1290 else if(qdd(ix1-1,ix2,ix3)>maxq) then
1291 qd(ix^d,2)=maxq
1292 else
1293 qd(ix^d,2)=qdd(ix1-1,ix2,ix3)
1294 end if
1295 if(qdd(ix1,ix2-1,ix3)<minq) then
1296 qd(ix^d,3)=minq
1297 else if(qdd(ix1,ix2-1,ix3)>maxq) then
1298 qd(ix^d,3)=maxq
1299 else
1300 qd(ix^d,3)=qdd(ix1,ix2-1,ix3)
1301 end if
1302 if(qdd(ix1-1,ix2-1,ix3)<minq) then
1303 qd(ix^d,4)=minq
1304 else if(qdd(ix1-1,ix2-1,ix3)>maxq) then
1305 qd(ix^d,4)=maxq
1306 else
1307 qd(ix^d,4)=qdd(ix1-1,ix2-1,ix3)
1308 end if
1309 qvec(ix^d,idims)=kaf(ix^d)*0.25d0*(bc(ix^d,idims)**2*qd(ix^d,1)+bc(ix1-1,ix2,ix3,idims)**2*qd(ix^d,2)&
1310 +bc(ix1,ix2-1,ix3,idims)**2*qd(ix^d,3)+bc(ix1-1,ix2-1,ix3,idims)**2*qd(ix^d,4))
1311 if(fl%tc_perpendicular) &
1312 qvec(ix^d,idims)=qvec(ix^d,idims)+kef(ix^d)*0.25d0*(qd(ix^d,1)+qd(ix^d,2)+qd(ix^d,3)+qd(ix^d,4))
1313 {end do\}
1314 end if
1315 }
1316 {^iftwod
1317 if(idims==1) then
1318 {do ix^db=ixamin^db,ixamax^db\}
1319 minq=min(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
1320 maxq=max(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
1321 if(qdd(ix^d)<minq) then
1322 qd(ix^d,1)=minq
1323 else if(qdd(ix^d)>maxq) then
1324 qd(ix^d,1)=maxq
1325 else
1326 qd(ix^d,1)=qdd(ix^d)
1327 end if
1328 if(qdd(ix1,ix2-1)<minq) then
1329 qd(ix^d,2)=minq
1330 else if(qdd(ix1,ix2-1)>maxq) then
1331 qd(ix^d,2)=maxq
1332 else
1333 qd(ix^d,2)=qdd(ix1,ix2-1)
1334 end if
1335 qvec(ix^d,idims)=kaf(ix^d)*0.5d0*(bc(ix1,ix2,idims)**2*qd(ix^d,1)+bc(ix1,ix2-1,idims)**2*qd(ix^d,2))
1336 if(fl%tc_perpendicular) &
1337 qvec(ix^d,idims)=qvec(ix^d,idims)+kef(ix^d)*0.5d0*(qd(ix^d,1)+qd(ix^d,2))
1338 {end do\}
1339 else
1340 {do ix^db=ixamin^db,ixamax^db\}
1341 minq=min(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
1342 maxq=max(alpha*gradt(ix^d,idims),gradt(ix^d,idims)/alpha)
1343 if(qdd(ix^d)<minq) then
1344 qd(ix^d,1)=minq
1345 else if(qdd(ix^d)>maxq) then
1346 qd(ix^d,1)=maxq
1347 else
1348 qd(ix^d,1)=qdd(ix^d)
1349 end if
1350 if(qdd(ix1-1,ix2)<minq) then
1351 qd(ix^d,2)=minq
1352 else if(qdd(ix1-1,ix2)>maxq) then
1353 qd(ix^d,2)=maxq
1354 else
1355 qd(ix^d,2)=qdd(ix1-1,ix2)
1356 end if
1357 qvec(ix^d,idims)=kaf(ix^d)*0.5d0*(bc(ix1,ix2,idims)**2*qd(ix^d,1)+bc(ix1-1,ix2,idims)**2*qd(ix^d,2))
1358 if(fl%tc_perpendicular) &
1359 qvec(ix^d,idims)=qvec(ix^d,idims)+kef(ix^d)*0.5d0*(qd(ix^d,1)+qd(ix^d,2))
1360 {end do\}
1361 end if
1362 }
1363 ! calculate normal of magnetic field
1364 ixb^l=ixa^l+kr(idims,^d);
1365 bnorm(ixa^s)=0.5d0*(mf(ixa^s,idims)+mf(ixb^s,idims))
1366 ! limited transverse component, eq (17)
1367 ixbmin^d=ixamin^d;
1368 ixbmax^d=ixamax^d+kr(idims,^d);
1369 do idir=1,ndim
1370 if(idir==idims) cycle
1371 qdd(ixi^s)=slope_limiter(gradt(ixi^s,idir),ixi^l,ixb^l,idir,-1,fl%tc_slope_limiter)
1372 qdd(ixi^s)=slope_limiter(qdd,ixi^l,ixa^l,idims,1,fl%tc_slope_limiter)
1373 qvec(ixa^s,idims)=qvec(ixa^s,idims)+kaf(ixa^s)*bnorm(ixa^s)*bcf(ixa^s,idir)*qdd(ixa^s)
1374 end do
1375 if(fl%tc_saturate) then
1376 ! consider saturation (Cowie and Mckee 1977 ApJ, 211, 135: phi=1.1, Balbus and Mckee 1982 ApJ, 252, 529: phi=0.3)
1377 ! unsigned saturated TC flux = 5 phi rho c**3, c**2=p/rho=R T of the fully ionised gas, phi=0.3,
1378 ! with rho scaled by the local over the fully ionised ne/nH under LTE, which is Cowie and McKee eq 7
1379 ixb^l=ixa^l+kr(idims,^d);
1380 qdd(ixa^s)=0.75d0*fl%sat_fac*(rhos(ixa^s)+rhos(ixb^s))*dsqrt(0.5d0*(te(ixa^s)+te(ixb^s)))**3*dabs(bnorm(ixa^s))
1381 {do ix^db=ixamin^db,ixamax^db\}
1382 if(dabs(qvec(ix^d,idims))>qdd(ix^d)) then
1383 qvec(ix^d,idims)=sign(1.d0,qvec(ix^d,idims))*qdd(ix^d)
1384 end if
1385 {end do\}
1386 end if
1387 end do
1388 end subroutine set_source_tc_mhd_geo
1389
1390 function slope_limiter(f,ixI^L,ixO^L,idims,pm,tc_slope_limiter) result(lf)
1392 integer, intent(in) :: ixi^l, ixo^l, idims, pm
1393 double precision, intent(in) :: f(ixi^s)
1394 double precision :: lf(ixi^s)
1395 integer, intent(in) :: tc_slope_limiter
1396
1397 double precision, parameter :: qsmall=1.d-12
1398 double precision :: signf(ixi^s)
1399 integer :: ixb^l
1400
1401 ixb^l=ixo^l+pm*kr(idims,^d);
1402 signf(ixo^s)=sign(1.d0,f(ixo^s))
1403 select case(tc_slope_limiter)
1404 case(1)
1405 ! 'MC' monotonized central limiter Woodward and Collela limiter (eq.3.51h), a factor of 2 is pulled out
1406 lf(ixo^s)=two*signf(ixo^s)* &
1407 max(zero,min(dabs(f(ixo^s)),signf(ixo^s)*f(ixb^s),&
1408 signf(ixo^s)*quarter*(f(ixb^s)+f(ixo^s))))
1409 case(2)
1410 ! 'minmod' limiter
1411 lf(ixo^s)=signf(ixo^s)*max(0.d0,min(abs(f(ixo^s)),signf(ixo^s)*f(ixb^s)))
1412 case(3)
1413 ! 'superbee' Roe superbee limiter (eq.3.51i)
1414 lf(ixo^s)=signf(ixo^s)* &
1415 max(zero,min(two*dabs(f(ixo^s)),signf(ixo^s)*f(ixb^s)),&
1416 min(dabs(f(ixo^s)),two*signf(ixo^s)*f(ixb^s)))
1417 case(4)
1418 ! 'koren' Barry Koren Right variant
1419 lf(ixo^s)=signf(ixo^s)* &
1420 max(zero,min(two*dabs(f(ixo^s)),two*signf(ixo^s)*f(ixb^s),&
1421 (two*f(ixb^s)*signf(ixo^s)+dabs(f(ixo^s)))*third))
1422 case(5)
1423 ! van Leer limiter
1424 lf(ixo^s)=two*max(f(ixb^s)*f(ixo^s),zero)/(f(ixo^s)+f(ixb^s)+qsmall)
1425 case default
1426 call mpistop("Unknown slope limiter for thermal conduction")
1427 end select
1428 end function slope_limiter
1429
1430 !> Get the explicit timestep for the TC (hd implementation)
1431 !> Note: also used in 1D MHD (or for neutrals in twofl)
1432 function get_tc_dt_hd(w,ixI^L,ixO^L,dx^D,x,fl) result(dtnew)
1433 ! Check diffusion time limit dt < dx_i**2 / ((gamma-1)*tc_k_para/(rho R))
1435
1436 integer, intent(in) :: ixi^l, ixo^l
1437 double precision, intent(in) :: dx^d, x(ixi^s,1:ndim)
1438 double precision, intent(in) :: w(ixi^s,1:nw)
1439 type(tc_fluid), intent(in) :: fl
1440 double precision :: dtnew
1441
1442 double precision :: tmp(ixo^s),tmp2(ixo^s),te(ixi^s),rho(ixi^s),hfs(ixo^s),gradt(ixi^s)
1443 double precision :: ne(ixi^s), nh_arr(ixi^s), rf(ixi^s)
1444 double precision :: dtdiff_tcond,maxtmp2
1445 integer :: idim
1446
1447 ! Constant conductivity does not use Te; retain ghost temperatures when
1448 ! needed by the temperature-dependent conductivity and saturation gradient.
1449 if(.not.fl%tc_constant) then
1450 call fl%get_temperature_from_conserved(w,x,ixi^l,ixi^l,te)
1451 end if
1452 call fl%get_rho(w,x,ixi^l,ixo^l,rho)
1453 ! Electron density is used only by the non-constant saturation correction.
1454 if(.not.fl%tc_constant .and. fl%tc_saturate) then
1455 call fl%get_ne_nH(ixi^l, ixo^l, w, x, ne, nh_arr)
1456 end if
1457 ! the diffusivity is kappa/(rho c_v) with c_v = R/(gamma-1): weight rho by R
1458 if(associated(fl%get_var_Rfactor)) then
1459 call fl%get_var_Rfactor(w, x, ixi^l, ixo^l, rf)
1460 rho(ixo^s)=rho(ixo^s)*rf(ixo^s)
1461 end if
1462
1463 if(fl%tc_constant) then
1464 tmp(ixo^s)=fl%tc_k_para/rho(ixo^s)
1465 else
1466 if(fl%tc_saturate) then
1467 ! Kannan 2016 MN 458, 410
1468 ! l_mfpe = 3^1.5*kB^2/(4*sqrt(pi)*e^4*lnLambda) * T^2/n_e
1469 if(si_unit) then
1470 ! 5.730205638843984e27 = 3^1.5*kB_SI^2/(4*sqrt(pi)*e_SI^4*37)
1471 tmp2(ixo^s)=te(ixo^s)**2/ne(ixo^s)*5.730205638843984d27*unit_temperature**2/(unit_numberdensity*unit_length)
1472 else
1473 ! 7093.9239487765044 = 3^1.5*kB_cgs^2/(4*sqrt(pi)*e_cgs^4*37)
1474 tmp2(ixo^s)=te(ixo^s)**2/ne(ixo^s)*7093.9239487765044d0*unit_temperature**2/(unit_numberdensity*unit_length)
1475 end if
1476 hfs=0.d0
1477 do idim=1,ndim
1478 call gradient(te,ixi^l,ixo^l,idim,gradt)
1479 hfs(ixo^s)=hfs(ixo^s)+gradt(ixo^s)**2
1480 end do
1481 ! kappa=kappa_Spitzer/(1+4.2*l_mfpe/(T/|gradT|))
1482 tmp(ixo^s)=fl%tc_k_para*te(ixo^s)*te(ixo^s)*dsqrt(te(ixo^s))/(rho(ixo^s)*(1.d0+4.2d0*tmp2(ixo^s)*dsqrt(hfs(ixo^s))/te(ixo^s)))
1483 else
1484 tmp(ixo^s)=fl%tc_k_para*te(ixo^s)*te(ixo^s)*dsqrt(te(ixo^s))/rho(ixo^s)
1485 end if
1486 end if
1487
1488 dtnew = bigdouble
1489 do idim=1,ndim
1490 ! approximate thermal conduction flux: tc_k_para/rho/dx**2
1491 maxtmp2=maxval(tmp(ixo^s)/(block%ds(ixo^s,idim)**2))
1492 ! dt< dx_idim**2/((gamma-1)*tc_k_para/rho)
1493 dtdiff_tcond=1.d0/(tc_gamma_1*maxtmp2+smalldouble)
1494 ! limit the time step
1495 dtnew=min(dtnew,dtdiff_tcond)
1496 end do
1497 dtnew=dtnew/dble(ndim)
1498 end function get_tc_dt_hd
1499
1500 !> rho in the saturated flux: under LTE scaled by the local over the fully ionised ne/nH
1501 subroutine tc_sat_rho(ixI^L,w,x,fl,rho,rhos)
1503 integer, intent(in) :: ixI^L
1504 double precision, intent(in) :: w(ixI^S,1:nw),x(ixI^S,1:ndim),rho(ixI^S)
1505 type(tc_fluid), intent(in) :: fl
1506 double precision, intent(out) :: rhos(ixI^S)
1507 double precision :: ne(ixI^S),nH(ixI^S)
1508
1509 if(fl%sat_ne) then
1510 call fl%get_ne_nH(ixi^l,ixi^l,w,x,ne,nh)
1511 rhos(ixi^s)=rho(ixi^s)*ne(ixi^s)/(nh(ixi^s)*fl%neOnH_FI)
1512 else
1513 rhos(ixi^s)=rho(ixi^s)
1514 end if
1515 end subroutine tc_sat_rho
1516
1517 !> Te over the block (+ghosts): the frozen-ionisation map within a conduction step with
1518 !> frozen_ion, else the full EoS with the optional negative-eint repair
1519 subroutine tc_get_te(ixI^L,w,x,fl,igrid,Te)
1522 integer, intent(in) :: ixI^L, igrid
1523 double precision, intent(inout) :: w(ixI^S,1:nw)
1524 double precision, intent(in) :: x(ixI^S,1:ndim)
1525 type(tc_fluid), intent(in) :: fl
1526 double precision, intent(out) :: Te(ixI^S)
1527
1528 if(fl%frozen_ion) then
1529 ! The anchors are allocated once, by the first thread to arrive
1530 !$OMP CRITICAL (tc_anchor_alloc)
1531 if(.not. allocated(tc_anchor)) allocate(tc_anchor(max_blocks))
1532 !$OMP END CRITICAL (tc_anchor_alloc)
1533 ! the first stage of each conduction step anchors on the full EoS
1534 if(tc_anchor(igrid)%sts_id /= sts_app_id) then
1535 tc_anchor(igrid)%sts_id = sts_app_id
1536 call tc_frozen_anchor(ixi^l, w, x, fl, igrid)
1537 end if
1538 associate(a => tc_anchor(igrid))
1539 te(ixi^s) = a%T0(ixi^s) + (w(ixi^s,fl%e_) - a%e0(ixi^s))*a%cvinv(ixi^s)
1540 end associate
1541 if(all(te(ixi^s) > 0.d0)) return
1542 ! a non-positive frozen T: this block takes the full EoS for this stage
1543 end if
1544
1545 call fl%get_temperature_from_eint(w, x, ixi^l, ixi^l, te)
1546 if(fl%tc_patch_eint) call tc_patch_negative_eint(w, x, ixi^l, ixi^l, te, fl%e_, fl)
1547 end subroutine tc_get_te
1548
1549 !> Anchor of the frozen-ionisation conduction step for block igrid, on the step-start
1550 !> state (internal energy in w(:,e_)): T from the full EoS, the energy, and the inverse
1551 !> frozen-composition heat capacity with R from the cached ionisation. Within the step
1552 !> T = T0 + (e - e0)/(R rho/(gamma-1)), exact where the gas is fully ionised or neutral
1553 subroutine tc_frozen_anchor(ixI^L, w, x, fl, igrid)
1555 integer, intent(in) :: ixI^L, igrid
1556 double precision, intent(in) :: w(ixI^S,1:nw), x(ixI^S,1:ndim)
1557 type(tc_fluid), intent(in) :: fl
1558 double precision :: rho(ixI^S), R0(ixI^S)
1559
1560 associate(a => tc_anchor(igrid))
1561 if(.not. allocated(a%T0)) allocate(a%T0(ixi^s), a%e0(ixi^s), a%cvinv(ixi^s))
1562 call fl%get_temperature_from_eint(w, x, ixi^l, ixi^l, a%T0)
1563 call fl%get_var_Rfactor(w, x, ixi^l, ixi^l, r0)
1564 call fl%get_rho(w, x, ixi^l, ixi^l, rho)
1565 a%e0(ixi^s) = w(ixi^s,fl%e_)
1566 a%cvinv(ixi^s) = 1.d0/(fl%inv_gamma_minus_1*r0(ixi^s)*rho(ixi^s))
1567 end associate
1568 end subroutine tc_frozen_anchor
1569
1570 !> Face fluxes for the HD STS source, uniform or general geometry
1571 subroutine tc_flux_hd(ixI^L,ixO^L,w,x,fl,qvec,rho,Te)
1573 integer, intent(in) :: ixI^L, ixO^L
1574 double precision, intent(in) :: x(ixI^S,1:ndim), w(ixI^S,1:nw)
1575 type(tc_fluid), intent(in) :: fl
1576 double precision, intent(in) :: rho(ixI^S), Te(ixI^S)
1577 double precision, intent(out) :: qvec(ixI^S,1:ndim)
1578
1579 if(slab_uniform) then
1580 call set_source_tc_hd(ixi^l,ixo^l,w,x,fl,qvec,rho,te)
1581 else
1582 call set_source_tc_hd_geo(ixi^l,ixo^l,w,x,fl,qvec,rho,te)
1583 end if
1584 end subroutine tc_flux_hd
1585
1586 !> Face fluxes for the MHD STS source, uniform or general geometry
1587 subroutine tc_flux_mhd(ixI^L,ixO^L,w,x,fl,qvec,rho,Te,alpha)
1589 integer, intent(in) :: ixI^L, ixO^L
1590 double precision, intent(in) :: x(ixI^S,1:ndim), w(ixI^S,1:nw)
1591 type(tc_fluid), intent(in) :: fl
1592 double precision, intent(in) :: rho(ixI^S), Te(ixI^S), alpha
1593 double precision, intent(out) :: qvec(ixI^S,1:ndim)
1594
1595 if(slab_uniform) then
1596 call set_source_tc_mhd(ixi^l,ixo^l,w,x,fl,qvec,rho,te,alpha)
1597 else
1598 call set_source_tc_mhd_geo(ixi^l,ixo^l,w,x,fl,qvec,rho,te,alpha)
1599 end if
1600 end subroutine tc_flux_mhd
1601
1602 subroutine sts_set_source_tc_hd(ixI^L,ixO^L,w,x,wres,fix_conserve_at_step,my_dt,igrid,nflux,fl)
1605
1606 integer, intent(in) :: ixi^l, ixo^l, igrid, nflux
1607 double precision, intent(in) :: x(ixi^s,1:ndim)
1608 ! intent(inout) so tc_patch_negative_eint can repair w(:, ie) in place
1609 ! when tc_patch_eint = .true.; legacy path (patch off) leaves w untouched.
1610 double precision, intent(inout) :: w(ixi^s,1:nw)
1611 double precision, intent(inout) :: wres(ixi^s,1:nw)
1612 double precision, intent(in) :: my_dt
1613 logical, intent(in) :: fix_conserve_at_step
1614 type(tc_fluid), intent(in) :: fl
1615
1616 double precision :: te(ixi^s),rho(ixi^s)
1617 double precision :: qvec(ixi^s,1:ndim),qd(ixi^s)
1618 double precision, allocatable, dimension(:^D&,:) :: qvec_equi
1619 double precision :: fluxall(ixi^s,1,1:ndim)
1620
1621 double precision :: dxinv(ndim)
1622 integer :: idims,ix^l,ixb^l,ixa^l
1623
1624 ix^l=ixo^l^ladd1;
1625
1626 dxinv=1.d0/dxlevel
1627
1628 call tc_get_te(ixi^l,w,x,fl,igrid,te)
1629 call fl%get_rho(w, x, ixi^l, ixi^l, rho)
1630 call tc_flux_hd(ixi^l,ixo^l,w,x,fl,qvec,rho,te)
1631 if(fl%subtract_equi) then
1632 allocate(qvec_equi(ixi^s,1:ndim))
1633 call fl%get_temperature_equi(w, x, ixi^l, ixi^l, te)
1634 call fl%get_rho_equi(w, x, ixi^l, ixi^l, rho)
1635 call tc_flux_hd(ixi^l,ixo^l,w,x,fl,qvec_equi,rho,te)
1636 do idims=1,ndim
1637 ! operate only on the per-idims face range that set_source_tc_hd fills
1638 ! (ixOmin-kr .. ixOmax); ix^S (=ixO+1) over-reaches into the
1639 ! uninitialised ixOmax+1 layer -> snan/Inf -> SIGFPE. Matches tc_mhd.
1640 ixamax^d=ixomax^d; ixamin^d=ixomin^d-kr(idims,^d);
1641 qvec(ixa^s,idims)=qvec(ixa^s,idims) - qvec_equi(ixa^s,idims)
1642 end do
1643 deallocate(qvec_equi)
1644 endif
1645
1646 qd=0.d0
1647 if(slab_uniform) then
1648 do idims=1,ndim
1649 ixamax^d=ixomax^d; ixamin^d=ixomin^d-kr(idims,^d);
1650 qvec(ixa^s,idims)=dxinv(idims)*qvec(ixa^s,idims)
1651 ixb^l=ixo^l-kr(idims,^d);
1652 qd(ixo^s)=qd(ixo^s)+qvec(ixo^s,idims)-qvec(ixb^s,idims)
1653 end do
1654 else
1655 do idims=1,ndim
1656 ixamax^d=ixomax^d; ixamin^d=ixomin^d-kr(idims,^d);
1657 qvec(ixa^s,idims)=qvec(ixa^s,idims)*block%surfaceC(ixa^s,idims)
1658 ixb^l=ixo^l-kr(idims,^d);
1659 qd(ixo^s)=qd(ixo^s)+qvec(ixo^s,idims)-qvec(ixb^s,idims)
1660 end do
1661 qd(ixo^s)=qd(ixo^s)/block%dvolume(ixo^s)
1662 end if
1663
1664 if(fix_conserve_at_step) then
1665 do idims=1,ndim
1666 ixamax^d=ixomax^d; ixamin^d=ixomin^d-kr(idims,^d);
1667 fluxall(ixa^s,1,idims)=my_dt*qvec(ixa^s,idims)
1668 end do
1669 call store_flux(igrid,fluxall,1,ndim,nflux)
1670 end if
1671
1672 wres(ixo^s,fl%e_)=qd(ixo^s)
1673 end subroutine sts_set_source_tc_hd
1674
1675 subroutine set_source_tc_hd(ixI^L,ixO^L,w,x,fl,qvec,rho,Te)
1677 integer, intent(in) :: ixI^L, ixO^L
1678 double precision, intent(in) :: x(ixI^S,1:ndim)
1679 double precision, intent(in) :: w(ixI^S,1:nw)
1680 type(tc_fluid), intent(in) :: fl
1681 double precision, intent(in) :: Te(ixI^S),rho(ixI^S)
1682 double precision, intent(out) :: qvec(ixI^S,1:ndim)
1683 double precision :: rhos(ixI^S)
1684 double precision :: gradT(ixI^S,1:ndim),ke(ixI^S),qd(ixI^S)
1685 integer :: idims,ix^D,ix^L,ixC^L,ixA^L,ixB^L
1686
1687 ix^l=ixo^l^ladd1;
1688 if(fl%tc_saturate) call tc_sat_rho(ixi^l,w,x,fl,rho,rhos)
1689 ! ixC is cell-corner index
1690 ixcmax^d=ixomax^d; ixcmin^d=ixomin^d-1;
1691
1692 ! calculate thermal conduction flux with symmetric scheme
1693 ! T gradient (central difference) at cell corners
1694 do idims=1,ndim
1695 ixbmin^d=ixmin^d;
1696 ixbmax^d=ixmax^d-kr(idims,^d);
1697 call gradientf(te,x,ixi^l,ixb^l,idims,ke)
1698 {^ifthreed
1699 if(idims==1) then
1700 {do ix^db=ixcmin^db,ixcmax^db\}
1701 qvec(ix^d,idims)=0.25d0*(ke(ix1,ix2,ix3)+ke(ix1,ix2+1,ix3)&
1702 +ke(ix1,ix2,ix3+1)+ke(ix1,ix2+1,ix3+1))
1703 {end do\}
1704 else if(idims==2) then
1705 {do ix^db=ixcmin^db,ixcmax^db\}
1706 qvec(ix^d,idims)=0.25d0*(ke(ix1,ix2,ix3)+ke(ix1+1,ix2,ix3)&
1707 +ke(ix1,ix2,ix3+1)+ke(ix1+1,ix2,ix3+1))
1708 {end do\}
1709 else
1710 {do ix^db=ixcmin^db,ixcmax^db\}
1711 qvec(ix^d,idims)=0.25d0*(ke(ix1,ix2,ix3)+ke(ix1+1,ix2,ix3)&
1712 +ke(ix1,ix2+1,ix3)+ke(ix1+1,ix2+1,ix3))
1713 {end do\}
1714 end if
1715 }
1716 {^iftwod
1717 if(idims==1) then
1718 {do ix^db=ixcmin^db,ixcmax^db\}
1719 qvec(ix^d,idims)=0.5d0*(ke(ix1,ix2)+ke(ix1,ix2+1))
1720 {end do\}
1721 else
1722 {do ix^db=ixcmin^db,ixcmax^db\}
1723 qvec(ix^d,idims)=0.5d0*(ke(ix1,ix2)+ke(ix1+1,ix2))
1724 {end do\}
1725 end if
1726 }
1727 {^ifoned
1728 do ix1=ixcmin1,ixcmax1
1729 qvec(ix1,idims)=ke(ix1)
1730 end do
1731 }
1732 end do
1733 ! conductivity at cell center
1734 if(fl%tc_constant) then
1735 qd(ix^s)=fl%tc_k_para
1736 else
1737 if(phys_trac) then
1738 {do ix^db=ixmin^db,ixmax^db\}
1739 if(te(ix^d) < block%wextra(ix^d,fl%Tcoff_)) then
1740 qd(ix^d)=fl%tc_k_para*dsqrt(block%wextra(ix^d,fl%Tcoff_)**5)
1741 else
1742 qd(ix^d)=fl%tc_k_para*dsqrt(te(ix^d)**5)
1743 end if
1744 {end do\}
1745 else
1746 qd(ix^s)=fl%tc_k_para*dsqrt(te(ix^s)**5)
1747 end if
1748 end if
1749 ! conductivity Ke at cell corner
1750 ! cell corner conduction flux gradT
1751 {^ifthreed
1752 {do ix^db=ixcmin^db,ixcmax^db\}
1753 ke(ix^d)=0.125d0*(qd(ix1,ix2,ix3)+qd(ix1+1,ix2,ix3)&
1754 +qd(ix1,ix2+1,ix3)+qd(ix1+1,ix2+1,ix3)&
1755 +qd(ix1,ix2,ix3+1)+qd(ix1+1,ix2,ix3+1)&
1756 +qd(ix1,ix2+1,ix3+1)+qd(ix1+1,ix2+1,ix3+1))
1757 gradt(ix^d,1)=ke(ix^d)*qvec(ix^d,1)
1758 gradt(ix^d,2)=ke(ix^d)*qvec(ix^d,2)
1759 gradt(ix^d,3)=ke(ix^d)*qvec(ix^d,3)
1760 {end do\}
1761 }
1762 {^iftwod
1763 {do ix^db=ixcmin^db,ixcmax^db\}
1764 ke(ix^d)=0.25d0*(qd(ix1,ix2)+qd(ix1+1,ix2)+qd(ix1,ix2+1)+qd(ix1+1,ix2+1))
1765 gradt(ix^d,1)=ke(ix^d)*qvec(ix^d,1)
1766 gradt(ix^d,2)=ke(ix^d)*qvec(ix^d,2)
1767 {end do\}
1768 }
1769 {^ifoned
1770 do ix1=ixcmin1,ixcmax1
1771 gradt(ix^d,1)=0.5d0*(qd(ix1)+qd(ix1+1))*qvec(ix^d,1)
1772 end do
1773 }
1774
1775 ! conduction flux qvec at cell face
1776 do idims=1,ndim
1777 ixamax^d=ixomax^d; ixamin^d=ixomin^d-kr(idims,^d);
1778 {^ifthreed
1779 if(idims==1) then
1780 {do ix^db=ixamin^db,ixamax^db\}
1781 qvec(ix^d,idims)=0.25d0*(gradt(ix1,ix2,ix3,idims)+gradt(ix1,ix2-1,ix3,idims)&
1782 +gradt(ix1,ix2,ix3-1,idims)+gradt(ix1,ix2-1,ix3-1,idims))
1783 {end do\}
1784 else if(idims==2) then
1785 {do ix^db=ixamin^db,ixamax^db\}
1786 qvec(ix^d,idims)=0.25d0*(gradt(ix1,ix2,ix3,idims)+gradt(ix1-1,ix2,ix3,idims)&
1787 +gradt(ix1,ix2,ix3-1,idims)+gradt(ix1-1,ix2,ix3-1,idims))
1788 {end do\}
1789 else
1790 {do ix^db=ixamin^db,ixamax^db\}
1791 qvec(ix^d,idims)=0.25d0*(gradt(ix1,ix2,ix3,idims)+gradt(ix1,ix2-1,ix3,idims)&
1792 +gradt(ix1-1,ix2,ix3,idims)+gradt(ix1-1,ix2-1,ix3,idims))
1793 {end do\}
1794 end if
1795 }
1796 {^iftwod
1797 if(idims==1) then
1798 {do ix^db=ixamin^db,ixamax^db\}
1799 qvec(ix^d,idims)=0.5d0*(gradt(ix1,ix2,idims)+gradt(ix1,ix2-1,idims))
1800 {end do\}
1801 else
1802 {do ix^db=ixamin^db,ixamax^db\}
1803 qvec(ix^d,idims)=0.5d0*(gradt(ix1,ix2,idims)+gradt(ix1-1,ix2,idims))
1804 {end do\}
1805 end if
1806 }
1807 {^ifoned
1808 do ix1=ixamin1,ixamax1
1809 qvec(ix1,idims)=gradt(ix1,idims)
1810 end do
1811 }
1812 if(fl%tc_saturate) then
1813 ! consider saturation (Cowie and Mckee 1977 ApJ, 211, 135: phi=1.1, Balbus and Mckee 1982 ApJ, 252, 529: phi=0.3)
1814 ! unsigned saturated TC flux = 5 phi rho c**3, c**2=p/rho=R T of the fully ionised gas, phi=0.3,
1815 ! with rho scaled by the local over the fully ionised ne/nH under LTE, which is Cowie and McKee eq 7
1816 ixb^l=ixa^l+kr(idims,^d);
1817 qd(ixa^s)=0.75d0*fl%sat_fac*(rhos(ixa^s)+rhos(ixb^s))*dsqrt(0.5d0*(te(ixa^s)+te(ixb^s)))**3
1818 {do ix^db=ixamin^db,ixamax^db\}
1819 if(dabs(qvec(ix^d,idims))>qd(ix^d)) then
1820 qvec(ix^d,idims)=sign(1.d0,qvec(ix^d,idims))*qd(ix^d)
1821 end if
1822 {end do\}
1823 end if
1824 end do
1825 end subroutine set_source_tc_hd
1826 subroutine set_source_tc_hd_geo(ixI^L,ixO^L,w,x,fl,qvec,rho,Te)
1828 integer, intent(in) :: ixI^L, ixO^L
1829 double precision, intent(in) :: x(ixI^S,1:ndim)
1830 double precision, intent(in) :: w(ixI^S,1:nw)
1831 type(tc_fluid), intent(in) :: fl
1832 double precision, intent(in) :: Te(ixI^S),rho(ixI^S)
1833 double precision, intent(out) :: qvec(ixI^S,1:ndim)
1834 double precision :: rhos(ixI^S)
1835 double precision :: gradT(ixI^S,1:ndim),ke(ixI^S),qd(ixI^S)
1836 integer :: idims,ix^D,ix^L,ixC^L,ixA^L,ixB^L
1837
1838 ix^l=ixo^l^ladd1;
1839 if(fl%tc_saturate) call tc_sat_rho(ixi^l,w,x,fl,rho,rhos)
1840 ! ixC is cell-corner index
1841 ixcmax^d=ixomax^d; ixcmin^d=ixomin^d-1;
1842
1843 ! calculate thermal conduction flux with symmetric scheme
1844 ! T gradient from face centers to cell corners: surface weighted average
1845 do idims=1,ndim
1846 ixbmin^d=ixmin^d;
1847 ixbmax^d=ixmax^d-kr(idims,^d);
1848 call gradientf(te,x,ixi^l,ixb^l,idims,ke)
1849 {^ifthreed
1850 if(idims==1) then
1851 {do ix^db=ixcmin^db,ixcmax^db\}
1852 qvec(ix^d,1)=(ke(ix1,ix2,ix3)*block%surfaceC(ix1,ix2,ix3,1)&
1853 +ke(ix1,ix2+1,ix3)*block%surfaceC(ix1,ix2+1,ix3,1)&
1854 +ke(ix1,ix2,ix3+1)*block%surfaceC(ix1,ix2,ix3+1,1)&
1855 +ke(ix1,ix2+1,ix3+1)*block%surfaceC(ix1,ix2+1,ix3+1,1))/&
1856 (block%surfaceC(ix1,ix2,ix3,1)+block%surfaceC(ix1,ix2+1,ix3,1)&
1857 +block%surfaceC(ix1,ix2,ix3+1,1)+block%surfaceC(ix1,ix2+1,ix3+1,1)+smalldouble**2)
1858 {end do\}
1859 else if(idims==2) then
1860 {do ix^db=ixcmin^db,ixcmax^db\}
1861 qvec(ix^d,2)=(ke(ix1,ix2,ix3)*block%surfaceC(ix1,ix2,ix3,2)&
1862 +ke(ix1+1,ix2,ix3)*block%surfaceC(ix1+1,ix2,ix3,2)&
1863 +ke(ix1,ix2,ix3+1)*block%surfaceC(ix1,ix2,ix3+1,2)&
1864 +ke(ix1+1,ix2,ix3+1)*block%surfaceC(ix1+1,ix2,ix3+1,2))/&
1865 (block%surfaceC(ix1,ix2,ix3,2)+block%surfaceC(ix1+1,ix2,ix3,2)&
1866 +block%surfaceC(ix1,ix2,ix3+1,2)+block%surfaceC(ix1+1,ix2,ix3+1,2)+smalldouble**2)
1867 ! zero theta-normal surface area at pole axis
1868 {end do\}
1869 else
1870 {do ix^db=ixcmin^db,ixcmax^db\}
1871 qvec(ix^d,3)=(ke(ix1,ix2,ix3)*block%surfaceC(ix1,ix2,ix3,3)&
1872 +ke(ix1+1,ix2,ix3)*block%surfaceC(ix1+1,ix2,ix3,3)&
1873 +ke(ix1,ix2+1,ix3)*block%surfaceC(ix1,ix2+1,ix3,3)&
1874 +ke(ix1+1,ix2+1,ix3)*block%surfaceC(ix1+1,ix2+1,ix3,3))/&
1875 (block%surfaceC(ix1,ix2,ix3,3)+block%surfaceC(ix1+1,ix2,ix3,3)&
1876 +block%surfaceC(ix1,ix2+1,ix3,3)+block%surfaceC(ix1+1,ix2+1,ix3,3))
1877 {end do\}
1878 end if
1879 }
1880 {^iftwod
1881 if(idims==1) then
1882 {do ix^db=ixcmin^db,ixcmax^db\}
1883 qvec(ix^d,1)=(ke(ix1,ix2)*block%surfaceC(ix1,ix2,1)+ke(ix1,ix2+1)*block%surfaceC(ix1,ix2+1,1))&
1884 /(block%surfaceC(ix1,ix2,1)+block%surfaceC(ix1,ix2+1,1))
1885 {end do\}
1886 else
1887 {do ix^db=ixcmin^db,ixcmax^db\}
1888 qvec(ix^d,2)=(ke(ix1,ix2)*block%surfaceC(ix1,ix2,2)+ke(ix1+1,ix2)*block%surfaceC(ix1+1,ix2,2))&
1889 /(block%surfaceC(ix1,ix2,2)+block%surfaceC(ix1+1,ix2,2))
1890 {end do\}
1891 end if
1892 }
1893 {^ifoned
1894 do ix1=ixcmin1,ixcmax1
1895 qvec(ix1,idims)=ke(ix1)
1896 end do
1897 }
1898 end do
1899 ! conductivity at cell center
1900 if(fl%tc_constant) then
1901 qd(ix^s)=fl%tc_k_para
1902 else
1903 if(phys_trac) then
1904 {do ix^db=ixmin^db,ixmax^db\}
1905 if(te(ix^d) < block%wextra(ix^d,fl%Tcoff_)) then
1906 qd(ix^d)=fl%tc_k_para*dsqrt(block%wextra(ix^d,fl%Tcoff_)**5)
1907 else
1908 qd(ix^d)=fl%tc_k_para*dsqrt(te(ix^d)**5)
1909 end if
1910 {end do\}
1911 else
1912 qd(ix^s)=fl%tc_k_para*dsqrt(te(ix^s)**5)
1913 end if
1914 end if
1915 ! conductivity Ke at cell corner
1916 ! cell corner conduction flux gradT
1917 {^ifthreed
1918 {do ix^db=ixcmin^db,ixcmax^db\}
1919 ke(ix^d)=(qd(ix1,ix2,ix3)*block%dvolume(ix1,ix2,ix3)+qd(ix1+1,ix2,ix3)*block%dvolume(ix1+1,ix2,ix3)&
1920 +qd(ix1,ix2+1,ix3)*block%dvolume(ix1,ix2+1,ix3)+qd(ix1+1,ix2+1,ix3)*block%dvolume(ix1+1,ix2+1,ix3)&
1921 +qd(ix1,ix2,ix3+1)*block%dvolume(ix1,ix2,ix3+1)+qd(ix1+1,ix2,ix3+1)*block%dvolume(ix1+1,ix2,ix3+1)&
1922 +qd(ix1,ix2+1,ix3+1)*block%dvolume(ix1,ix2+1,ix3+1)+qd(ix1+1,ix2+1,ix3+1)*block%dvolume(ix1+1,ix2+1,ix3+1))&
1923 /(block%dvolume(ix1,ix2,ix3)+block%dvolume(ix1+1,ix2,ix3)+block%dvolume(ix1,ix2+1,ix3)+block%dvolume(ix1+1,ix2+1,ix3)&
1924 +block%dvolume(ix1,ix2,ix3+1)+block%dvolume(ix1+1,ix2,ix3+1)+block%dvolume(ix1,ix2+1,ix3+1)+block%dvolume(ix1+1,ix2+1,ix3+1))
1925 gradt(ix^d,1)=ke(ix^d)*qvec(ix^d,1)
1926 gradt(ix^d,2)=ke(ix^d)*qvec(ix^d,2)
1927 gradt(ix^d,3)=ke(ix^d)*qvec(ix^d,3)
1928 {end do\}
1929 }
1930 {^iftwod
1931 {do ix^db=ixcmin^db,ixcmax^db\}
1932 ke(ix^d)=(qd(ix1,ix2)*block%dvolume(ix1,ix2)+qd(ix1+1,ix2)*block%dvolume(ix1+1,ix2)&
1933 +qd(ix1,ix2+1)*block%dvolume(ix1,ix2+1)+qd(ix1+1,ix2+1)*block%dvolume(ix1+1,ix2+1))&
1934 /(block%dvolume(ix1,ix2)+block%dvolume(ix1+1,ix2)+block%dvolume(ix1,ix2+1)+block%dvolume(ix1+1,ix2+1))
1935 gradt(ix^d,1)=ke(ix^d)*qvec(ix^d,1)
1936 gradt(ix^d,2)=ke(ix^d)*qvec(ix^d,2)
1937 {end do\}
1938 }
1939 {^ifoned
1940 do ix1=ixcmin1,ixcmax1
1941 gradt(ix^d,1)=(qd(ix1)*block%dvolume(ix1)+qd(ix1+1)*block%dvolume(ix1+1))/(block%dvolume(ix1)+block%dvolume(ix1+1))*qvec(ix^d,1)
1942 end do
1943 }
1944
1945 ! conduction flux qvec at cell face
1946 do idims=1,ndim
1947 ixamax^d=ixomax^d; ixamin^d=ixomin^d-kr(idims,^d);
1948 {^ifthreed
1949 if(idims==1) then
1950 {do ix^db=ixamin^db,ixamax^db\}
1951 qvec(ix^d,idims)=0.25d0*(gradt(ix1,ix2,ix3,idims)+gradt(ix1,ix2-1,ix3,idims)&
1952 +gradt(ix1,ix2,ix3-1,idims)+gradt(ix1,ix2-1,ix3-1,idims))
1953 {end do\}
1954 else if(idims==2) then
1955 {do ix^db=ixamin^db,ixamax^db\}
1956 qvec(ix^d,idims)=0.25d0*(gradt(ix1,ix2,ix3,idims)+gradt(ix1-1,ix2,ix3,idims)&
1957 +gradt(ix1,ix2,ix3-1,idims)+gradt(ix1-1,ix2,ix3-1,idims))
1958 {end do\}
1959 else
1960 {do ix^db=ixamin^db,ixamax^db\}
1961 qvec(ix^d,idims)=0.25d0*(gradt(ix1,ix2,ix3,idims)+gradt(ix1,ix2-1,ix3,idims)&
1962 +gradt(ix1-1,ix2,ix3,idims)+gradt(ix1-1,ix2-1,ix3,idims))
1963 {end do\}
1964 end if
1965 }
1966 {^iftwod
1967 if(idims==1) then
1968 {do ix^db=ixamin^db,ixamax^db\}
1969 qvec(ix^d,idims)=0.5d0*(gradt(ix1,ix2,idims)+gradt(ix1,ix2-1,idims))
1970 {end do\}
1971 else
1972 {do ix^db=ixamin^db,ixamax^db\}
1973 qvec(ix^d,idims)=0.5d0*(gradt(ix1,ix2,idims)+gradt(ix1-1,ix2,idims))
1974 {end do\}
1975 end if
1976 }
1977 {^ifoned
1978 do ix1=ixamin1,ixamax1
1979 qvec(ix1,idims)=gradt(ix1,idims)
1980 end do
1981 }
1982 if(fl%tc_saturate) then
1983 ! consider saturation (Cowie and Mckee 1977 ApJ, 211, 135: phi=1.1, Balbus and Mckee 1982 ApJ, 252, 529: phi=0.3)
1984 ! unsigned saturated TC flux = 5 phi rho c**3, c**2=p/rho=R T of the fully ionised gas, phi=0.3,
1985 ! with rho scaled by the local over the fully ionised ne/nH under LTE, which is Cowie and McKee eq 7
1986 ixb^l=ixa^l+kr(idims,^d);
1987 qd(ixa^s)=0.75d0*fl%sat_fac*(rhos(ixa^s)+rhos(ixb^s))*dsqrt(0.5d0*(te(ixa^s)+te(ixb^s)))**3
1988 {do ix^db=ixamin^db,ixamax^db\}
1989 if(dabs(qvec(ix^d,idims))>qd(ix^d)) then
1990 qvec(ix^d,idims)=sign(1.d0,qvec(ix^d,idims))*qd(ix^d)
1991 end if
1992 {end do\}
1993 end if
1994 end do
1995 end subroutine set_source_tc_hd_geo
1996
1997 !> Patch cells where e_int <= 0 by neighbor-averaging the temperature
1998 !> AND repairing the conserved internal energy w(:, ie) in place.
1999 !> Called after get_temperature_from_eint in the TC source routines when
2000 !> tc_patch_eint is .true. During STS RKL2 Chebyshev substeps the
2001 !> polynomial can overshoot e_int to negative values at low-density
2002 !> coronal cells. A negative T would produce NaN via sqrt(T) in the
2003 !> Spitzer conductivity. This routine replaces those cells' T with
2004 !> the average of valid (e_int > 0) neighbors and writes the matching
2005 !> e_int back to w(ix, ie) via the EoS inverse helper eint_nH_from_T,
2006 !> so the next STS substep does not re-read negative w and amplify it.
2007 subroutine tc_patch_negative_eint(w, x, ixI^L, ixO^L, Te, ie, fl)
2009 use iso_fortran_env, only: int64
2011 integer, intent(in) :: ixi^l, ixo^l, ie
2012 double precision, intent(inout) :: w(ixi^s, 1:nw)
2013 double precision, intent(in) :: x(ixi^s, 1:ndim)
2014 double precision, intent(inout) :: te(ixi^s)
2015 type(tc_fluid), intent(in) :: fl
2016
2017 integer :: ix^d, count
2018 integer :: ipatch(ixi^s)
2019 double precision :: t_avg, t_use, rho_cell, nh_cell, log_nh, log_t
2020 double precision :: eint_new, small_e_local, log_t_min, t_floor
2021 double precision :: rho(ixi^s)
2022
2023 type(repair_warning_state), save :: warning_state
2024 integer(int64) :: repair_events, edge_events
2025 double precision :: first_x(ndim), first_before, first_after
2026
2027 repair_events=0_int64
2028 edge_events=0_int64
2029 ipatch(ixi^s) = 0
2030
2031 ! Mark cells with negative e_int
2032 {do ix^db=ixomin^db,ixomax^db\}
2033 if (w(ix^d, ie) <= 0.0d0) ipatch(ix^d) = 1
2034 {end do\}
2035
2036 ! Quick exit if no cells need patching
2037 if (.not. any(ipatch(ixo^s) /= 0)) return
2038
2039 ! Need rho per-cell to back out e_int from a prescribed T via the EoS.
2040 call fl%get_rho(w, x, ixi^l, ixi^l, rho)
2041
2042 ! Floor on log_T for the (rho, T) inverse table, from the EoS via the port
2043 ! (eos_get_log_T_floor encapsulates the method->container choice; picking the
2044 ! wrong container would leave var2_min=0 and clobber cold cells).
2045 log_t_min = fl%log_T_floor
2046
2047 ! Local minimum e_int (code units): equivalent to small_pressure / (gamma-1).
2048 small_e_local = small_pressure * fl%inv_gamma_minus_1
2049
2050 ! Fallback T (code units) for cells that have no valid neighbour.
2051 t_floor = 1.0d0 / unit_temperature
2052
2053 ! Replace marked cells with neighbor-averaged T, then back out e_int
2054 ! and floor w(:, ie) so subsequent STS substeps see a positive state.
2055 {do ix^db=ixomin^db+1,ixomax^db-1\}
2056 if (ipatch(ix^d) == 1) then
2057 repair_events=repair_events+1_int64
2058 if(repair_events==1_int64) then
2059 first_x=x(ix^d,1:ndim)
2060 first_before=w(ix^d,ie)
2061 end if
2062 t_avg = 0.0d0
2063 count = 0
2064 {^ifoned
2065 if (ipatch(ix1-1)==0) then; t_avg=t_avg+te(ix1-1); count=count+1; end if
2066 if (ipatch(ix1+1)==0) then; t_avg=t_avg+te(ix1+1); count=count+1; end if
2067 }
2068 {^iftwod
2069 if (ipatch(ix1-1,ix2)==0) then; t_avg=t_avg+te(ix1-1,ix2); count=count+1; end if
2070 if (ipatch(ix1+1,ix2)==0) then; t_avg=t_avg+te(ix1+1,ix2); count=count+1; end if
2071 if (ipatch(ix1,ix2-1)==0) then; t_avg=t_avg+te(ix1,ix2-1); count=count+1; end if
2072 if (ipatch(ix1,ix2+1)==0) then; t_avg=t_avg+te(ix1,ix2+1); count=count+1; end if
2073 }
2074 {^ifthreed
2075 if (ipatch(ix1-1,ix2,ix3)==0) then; t_avg=t_avg+te(ix1-1,ix2,ix3); count=count+1; end if
2076 if (ipatch(ix1+1,ix2,ix3)==0) then; t_avg=t_avg+te(ix1+1,ix2,ix3); count=count+1; end if
2077 if (ipatch(ix1,ix2-1,ix3)==0) then; t_avg=t_avg+te(ix1,ix2-1,ix3); count=count+1; end if
2078 if (ipatch(ix1,ix2+1,ix3)==0) then; t_avg=t_avg+te(ix1,ix2+1,ix3); count=count+1; end if
2079 if (ipatch(ix1,ix2,ix3-1)==0) then; t_avg=t_avg+te(ix1,ix2,ix3-1); count=count+1; end if
2080 if (ipatch(ix1,ix2,ix3+1)==0) then; t_avg=t_avg+te(ix1,ix2,ix3+1); count=count+1; end if
2081 }
2082 if (count > 0) then
2083 t_use = t_avg / dble(count)
2084 else
2085 t_use = t_floor
2086 end if
2087
2088 ! Repair the conserved internal energy in place. Use the same
2089 ! EoS-aware inverse used by hd/mhd_from_prolong_LTE: e_int =
2090 ! nH * eint_nH_from_T(log_nH, log_T). Floor with small_e.
2091 rho_cell = max(rho(ix^d), small_density)
2092 nh_cell = rho_cell / fl%nH2rhoFactor
2093 log_nh = dlog10(max(nh_cell, smalldouble))
2094 log_t = dlog10(max(t_use, 10.0d0**log_t_min))
2095 eint_new = nh_cell * fl%eint_from_T(log_nh, log_t)
2096 w(ix^d, ie) = max(small_e_local, eint_new)
2097
2098 ! Keep Te consistent with the floored w(:, ie): if small_e
2099 ! kicked in, the implied T is slightly different from T_use.
2100 ! Use the prescribed T_use here (Te is only consumed within
2101 ! this TC substep; the next call recomputes it from w).
2102 te(ix^d) = t_use
2103
2104 if(repair_events==1_int64) first_after=w(ix^d,ie)
2105 ipatch(ix^d) = 2 ! mark as handled
2106 end if
2107 {end do\}
2108
2109 ! Cells at the block boundary that the interior loop above skipped
2110 ! (ixOmin/ixOmax edges). Fall back to T = 1/unit_temperature and
2111 ! repair w(:, ie) consistently so STS does not re-read negative e.
2112 {do ix^db=ixomin^db,ixomax^db\}
2113 if (ipatch(ix^d) == 1) then
2114 repair_events=repair_events+1_int64
2115 edge_events=edge_events+1_int64
2116 if(repair_events==1_int64) then
2117 first_x=x(ix^d,1:ndim)
2118 first_before=w(ix^d,ie)
2119 end if
2120 t_use = t_floor
2121 rho_cell = max(rho(ix^d), small_density)
2122 nh_cell = rho_cell / fl%nH2rhoFactor
2123 log_nh = dlog10(max(nh_cell, smalldouble))
2124 log_t = dlog10(max(t_use, 10.0d0**log_t_min))
2125 eint_new = nh_cell * fl%eint_from_T(log_nh, log_t)
2126 w(ix^d, ie) = max(small_e_local, eint_new)
2127 te(ix^d) = t_use
2128 if(repair_events==1_int64) first_after=w(ix^d,ie)
2129 end if
2130 {end do\}
2131
2132 call report_repair_warning(warning_state,'tc_patch_eint',repair_events, &
2133 edge_events,ie,first_x,first_before,first_after)
2134 end subroutine tc_patch_negative_eint
2135
2136end module mod_thermal_conduction
Scalar EoS inverse, e.g. eint_nH_from_T(log_nH, log_T).
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
Module for flux conservation near refinement boundaries.
subroutine, public store_flux(igrid, fc, idimlim, nwfluxin)
Module with geometry-related routines (e.g., divergence, curl).
Definition mod_geometry.t:2
subroutine gradient(q, ixil, ixol, idir, gradq, nth_in)
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.
integer, parameter unitpar
file handle for IO
integer, dimension(3, 3) kr
Kronecker delta tensor.
double precision unit_numberdensity
Physical scaling factor for number density.
integer, parameter ndim
Number of spatial dimensions for grid variables.
double precision unit_length
Physical scaling factor for length.
character(len=std_len), dimension(:), allocatable par_files
Which par files are used as input.
integer ndir
Number of spatial dimensions (components) for vector variables.
double precision, dimension(:), allocatable, parameter d
logical b0field
split magnetic field as background B0 field
double precision unit_temperature
Physical scaling factor for temperature.
logical si_unit
Use SI units (.true.) or use cgs units (.false.).
double precision, dimension(:,:), allocatable dx
spatial steps for all dimensions at all levels
double precision, dimension(^nd) dxlevel
store unstretched cell size of current level
logical slab_uniform
uniform Cartesian geometry or not (stretched Cartesian)
integer max_blocks
The maximum number of grid blocks in a processor.
Module for handling problematic values in simulations, such as negative pressures.
subroutine, public report_repair_warning(state, label, events, edges, iw, xfirst, before, after)
Generic supertimestepping method which can be used for multiple source terms in the governing equatio...
integer, public sts_app_id
Counts STS applications (one per term per call of sts_add_source), so a term can tell the first stage...
Thermal conduction for HD and MHD or RHD and RMHD or twofl (plasma-neutral) module Adaptation of mod_...
subroutine, public tc_get_hd_params(fl, read_hd_params)
Init TC coefficients: HD case.
subroutine tc_flux_hd(ixil, ixol, w, x, fl, qvec, rho, te)
Face fluxes for the HD STS source, uniform or general geometry.
double precision function, public get_tc_dt_mhd(w, ixil, ixol, dxd, x, fl)
Get the explicit timestep for the TC (mhd implementation) Note: for multi-D MHD (1D MHD will use HD f...
double precision function, public get_tc_dt_hd(w, ixil, ixol, dxd, x, fl)
Get the explicit timestep for the TC (hd implementation) Note: also used in 1D MHD (or for neutrals i...
subroutine tc_init_params(phys_gamma)
subroutine tc_frozen_anchor(ixil, w, x, fl, igrid)
Anchor of the frozen-ionisation conduction step for block igrid, on the step-start state (internal en...
subroutine tc_sat_rho(ixil, w, x, fl, rho, rhos)
rho in the saturated flux: under LTE scaled by the local over the fully ionised ne/nH
subroutine, public tc_patch_negative_eint(w, x, ixil, ixol, te, ie, fl)
Patch cells where e_int <= 0 by neighbor-averaging the temperature AND repairing the conserved intern...
subroutine, public sts_set_source_tc_hd(ixil, ixol, w, x, wres, fix_conserve_at_step, my_dt, igrid, nflux, fl)
subroutine, public sts_set_source_tc_mhd(ixil, ixol, w, x, wres, fix_conserve_at_step, my_dt, igrid, nflux, fl)
anisotropic thermal conduction with slope limited symmetric scheme Sharma 2007 Journal of Computation...
type(tc_anchor_t), dimension(:), allocatable tc_anchor
double precision tc_gamma_1
The adiabatic index.
subroutine, public tc_get_mhd_params(fl, read_mhd_params)
Init TC coefficients: MHD case.
subroutine set_source_tc_hd_geo(ixil, ixol, w, x, fl, qvec, rho, te)
double precision function, dimension(ixi^s) slope_limiter(f, ixil, ixol, idims, pm, tc_slope_limiter)
subroutine tc_flux_mhd(ixil, ixol, w, x, fl, qvec, rho, te, alpha)
Face fluxes for the MHD STS source, uniform or general geometry.
subroutine tc_get_te(ixil, w, x, fl, igrid, te)
Te over the block (+ghosts): the frozen-ionisation map within a conduction step with frozen_ion,...
subroutine set_source_tc_hd(ixil, ixol, w, x, fl, qvec, rho, te)
subroutine set_source_tc_mhd_geo(ixil, ixol, w, x, fl, qvec, rho, te, alpha)
subroutine set_source_tc_mhd(ixil, ixol, w, x, fl, qvec, rho, te, alpha)
Per-block anchor of the frozen-ionisation conduction step: T and the internal energy at the step star...