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