MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_hd_phys.t
Go to the documentation of this file.
1!> Hydrodynamics physics module
6 use mod_fld, only: fld_fluid
8 use mod_eos
9 use mod_rt_sc_slab, only: rt_sc_activate, rt_sc_setup, rt_sc_add_source
10 use mod_comm_lib, only: mpistop
11 implicit none
12 private
13
14 !> Whether an energy equation is used
15 logical, public, protected :: hd_energy = .true.
16
17 !> Whether thermal conduction is added
18 logical, public, protected :: hd_thermal_conduction = .false.
19 !> Whether hyperbolic thermal conduction (Cattaneo relaxation) is used.
20 !> 1D only : the q-variable is treated as a scalar carrying the
21 !> heat flux along the only spatial direction.
22 logical, public, protected :: hd_hyperbolic_thermal_conduction = .false.
23 !> Whether saturation is considered for hyperbolic TC
24 logical, public, protected :: hd_htc_sat = .false.
25 type(tc_fluid), allocatable, public :: tc_fl
26 type(te_fluid), allocatable, public :: te_fl_hd
27
28 !> Whether radiative cooling is added
29 logical, public, protected :: hd_radiative_cooling = .false.
30 !> Whether the short-characteristics radiative transfer is used, with its settings in &rt_list
31 logical, public, protected :: hd_radiative_transfer = .false.
32 type(rc_fluid), allocatable, public :: rc_fl
33
34 !> Whether dust is added
35 logical, public, protected :: hd_dust = .false.
36
37 !> Whether dust is added using and implicit update in IMEX
38 logical, public, protected :: hd_dust_implicit = .false.
39
40 !> Whether radiation-gas interaction is handled using flux limited diffusion
41 logical, public, protected :: hd_radiation_fld = .false.
42 logical, public, protected :: hd_fld_pradtensor= .true.
43 !> Radiation fluid object (gas-EoS callbacks for FLD), wired in hd_link_eos
44 type(fld_fluid), allocatable, public :: fld_fl
45
46 !> Whether viscosity is added
47 logical, public, protected :: hd_viscosity = .false.
48
49 !> Whether gravity is added
50 logical, public, protected :: hd_gravity = .false.
51
52 !> Whether particles module is added
53 logical, public, protected :: hd_particles = .false.
54
55 !> Whether rotating frame is activated
56 logical, public, protected :: hd_rotating_frame = .false.
57
58 !> Whether CAK radiation line force is activated
59 logical, public, protected :: hd_cak_force = .false.
60
61 !> Number of tracer species
62 integer, public, protected :: hd_n_tracer = 0
63
64 !> Whether plasma is partially ionized
65
66 !> Index of the density (in the w array)
67 integer, public, protected :: rho_
68
69 !> Indices of the momentum density
70 integer, allocatable, public, protected :: mom(:)
71
72 !> Indices of the momentum density for the form of better vectorization
73 integer, public, protected :: ^c&m^C_
74
75 !> Indices of the tracers
76 integer, allocatable, public, protected :: tracer(:)
77
78 !> Index of the energy density (-1 if not present)
79 integer, public, protected :: e_
80
81 !> Index of the gas pressure (-1 if not present) should equal e_
82 integer, public, protected :: p_
83
84 !> Index of the electron number density for LTE module
85 integer, public, protected :: ne_
86
87 !> Index of the radiation energy (when fld active)
88 integer, public, protected :: r_e
89
90 !> Indices of temperature
91 integer, public, protected :: te_
92
93 !> Index of the FIP passive scalar rho*fip in conserved form, fip in primitive form
94 integer, public, protected :: fip_ = -1
95
96 !> Whether FIP passive scalar is enabled
97 logical, public, protected :: hd_fip = .false.
98
99 !> Index of the cutoff temperature for the TRAC method
100 integer, public, protected :: tcoff_
101
102 !> Index of the hyperbolic-TC heat-flux variable (-1 if not present)
103 integer, public, protected :: q_
104
105 !> Thermal-conductivity prefactor in hyperbolic TC, set in hd_physical_units.
106 !> Spitzer form: κ(T) = hypertc_kappa · T^{5/2}.
107 double precision, public :: hypertc_kappa
108
109 !> Optional parfile override for hypertc_kappa (e.g. to match a constant-κ
110 !> parabolic TC run for benchmarking). Default -1.0 leaves the Spitzer
111 !> value computed from physical units.
112 double precision, public, protected :: hd_htc_kappa_override = -1.0d0
113
114 !> Hyperdiffusion coefficient applied to the cell-refreshed q at the
115 !> end of each face-recipe substep. 4th-order undivided difference
116 !> smoother: q -= alpha * (qdt/dt) * (q_{i+2} + q_{i-2}
117 !> - 4(q_{i+1} + q_{i-1})
118 !> + 6 q_i).
119 !> MURaM (Rempel 2017) uses 0.02. Damps cell-scale q oscillations
120 !> arising from kappa(T) amplifying small persistent T bumps in the
121 !> EoS-derived cell-centred T profile; without it, the face-recipe
122 !> q field has visible 5-cell-scale wiggles even where T is smooth.
123 !> This is the only one of the OLD code's three q-smoothers (HLL
124 !> diffusion, Koren reconstruction, hyperdiff) that the face-recipe
125 !> needs -- the architectural problems with the other two are not
126 !> reintroduced.
127 double precision, public, protected :: hd_htc_hyp_diff = 0.02d0
128
129 !> Face-recipe heat-wave speed scaling: c_HTC,f = hd_htc_beta * c_max,f.
130 !> Higher value -> closer to diffusion limit (q tracks Spitzer noise
131 !> aggressively, AMR-triggering corona noise). Lower value -> more
132 !> hyperbolic (q lags Spitzer target by Delta_t/tau ~ 1/beta^2 per
133 !> step, dampening high-frequency noise). At our resolution beta=2-3
134 !> is the practical sweet spot: q evolves slowly enough that T-table
135 !> round-off noise doesn't propagate, but fast enough that real TR
136 !> conduction equilibrates within O(100) hydro timesteps.
137 double precision, public, protected :: hd_htc_beta = 2.0d0
138
139 !> Cowie-McKee saturation coefficient: q_sat = hd_htc_sat_alpha *
140 !> rho * c_s^3. Standard convention is alpha ~ 1 (absorbs the
141 !> sqrt(m_p/m_e) factor that would appear if c_s were replaced by
142 !> the electron thermal speed). Default 1.0.
143 double precision, public, protected :: hd_htc_sat_alpha = 1.0d0
144
145 !> Per-face energy-positivity safety fraction: |q_f^{n+1/2} dt A_f|
146 !> <= hd_htc_pos_eta * min(e_int_L V_L, e_int_R V_R). Default 0.5
147 !> leaves headroom against simultaneous PdV and cooling decrements.
148 double precision, public, protected :: hd_htc_pos_eta = 0.5d0
149
150 !> Validity-monitor threshold for l_r,f / Delta_x_f. Warn if any face
151 !> exceeds this in a given block (printed once per dtsave_log step).
152 !> Default 0.1: above this, HTC is modifying the physics beyond pure
153 !> Spitzer; above 1.0 the local Spitzer approximation breaks down.
154 double precision, public, protected :: hd_htc_validity_warn = 0.1d0
155
156 !> Gradient deadband: zero out the Spitzer face flux when
157 !> abs(T_R - T_L) / max(T_L, T_R) < hd_htc_gradT_floor
158 !> This suppresses sign-flipping q noise driven by EoS-table round-off
159 !> (~1e-4 in T) being amplified by huge coronal kappa. Default 1.0e-3
160 !> sits 10x above the table noise floor but 5x below typical coronal
161 !> gradients (Delta_x / L_T ~ 5e-3 at our resolution), so it kills the
162 !> noise without suppressing real conduction. Set to 0.0 to disable.
163 double precision, public, protected :: hd_htc_gradt_floor = 1.0d-3
164
165 !> Running max of l_r,f / dx_f across all face-recipe calls since
166 !> simulation start. Inspect post-hoc via debugger or dump alongside
167 !> dat files. Not reset per timestep -- monotonic non-decreasing.
168 double precision, public :: hd_htc_validity_max_runtime = 0.0d0
169
170 !> Index into wextra for escape probability column mass
171 integer, public, protected :: iw_colmass = -1
172
173 !> gamma is set in &eos_list and accessed via eos%gamma
174
175 !> The adiabatic constant
176 double precision, public :: hd_adiab = 1.0d0
177
178 !> Whether TRAC method is used
179 logical, public, protected :: hd_trac = .false.
180 integer, public, protected :: hd_trac_type = 1
181 integer, public, protected :: hd_trac_nzones = 1
182 double precision, public, protected :: hd_trac_zone_splits(10) = -1.d0
183 !> Johnston 2021 resolution parameter delta (default 0.5)
184 double precision, public, protected :: hd_trac_delta = 0.5d0
185 !> fixed cutoff temperature in K for hd_trac_type=0 (Lionello et al. 2009, Mikic et al. 2013)
186 double precision, public, protected :: hd_trac_tfix = 3.d5
187 !> Whether well-balanced reconstruction is used (Kaeppeli & Mishra style)
188 logical, public, protected :: hd_well_balanced = .false.
189
190
191 !> Helium abundance over Hydrogen
192 !> He_abundance is set in &eos_list and accessed via eos%He_abundance
193 !> Ionization fraction of H
194 !> H_ion_fr = H+/(H+ + H)
195 double precision, public, protected :: h_ion_fr=1d0
196 !> Ionization fraction of He
197 !> He_ion_fr = (He2+ + He+)/(He2+ + He+ + He)
198 double precision, public, protected :: he_ion_fr=1d0
199 !> Ratio of number He2+ / number He+ + He2+
200 !> He_ion_fr2 = He2+/(He2+ + He+)
201 double precision, public, protected :: he_ion_fr2=1d0
202 ! used for eq of state when it is not defined by units,
203 ! the units do not contain terms related to ionization fraction
204 ! and it is p = RR * rho * T
205 double precision, public, protected :: rr=1d0
206 ! remove the below flag and assume default value = .false.
207 ! procedure(sub_get_pthermal), pointer :: hd_get_Rfactor => null()
208 ! Public methods
209 public :: hd_phys_init
210 public :: hd_kin_en
211 public :: hd_get_csound2
212 public :: hd_check_params
213 public :: hd_check_w
214 public :: hd_handle_small_values
215 public :: hd_e_to_ei
216 public :: hd_ei_to_e
217 ! hd_get_Rfactor was the FI pointer; FLD uses phys_get_Rfactor which is
218 ! bound by mod_hd_eos:bind_eos_to_source to eos%get_Rfactor.
219 ! Begin: following relevant for radiative hydro using FLD
220 ! first four are local and only of interest for mod_usr applications
221 ! where they can be used in diagnostics
222 ! NOTE those with _prim expect primitives on entry
224 public :: hd_get_csrad2
225 public :: hd_get_trad
227 ! the following used in FLD modules
228 ! as pointer phys_get_csrad2
229 public :: hd_get_csrad2_prim
230 ! End: following relevant for radiative hydro using FLD
232
233contains
234
235 !> Read this module's parameters from a file
236 subroutine hd_read_params(files)
238 character(len=*), intent(in) :: files(:)
239 integer :: n
240
241 namelist /hd_list/ hd_energy, hd_n_tracer, hd_adiab, &
253
254 do n = 1, size(files)
255 open(unitpar, file=trim(files(n)), status="old")
256 read(unitpar, hd_list, end=111)
257111 close(unitpar)
258 end do
259
260 end subroutine hd_read_params
261
262 !> Write this module's parameters to a snapsoht
263 subroutine hd_write_info(fh)
265 integer, intent(in) :: fh
266 integer, parameter :: n_par = 1
267 double precision :: values(n_par)
268 character(len=name_len) :: names(n_par)
269 integer, dimension(MPI_STATUS_SIZE) :: st
270 integer :: er
271
272 call mpi_file_write(fh, n_par, 1, mpi_integer, st, er)
273
274 names(1) = "gamma"
275 values(1) = eos%gamma
276 call mpi_file_write(fh, values, n_par, mpi_double_precision, st, er)
277 call mpi_file_write(fh, names, n_par * name_len, mpi_character, st, er)
278 end subroutine hd_write_info
279
280 !> Initialize the module
281 subroutine hd_phys_init()
285 use mod_dust, only: dust_init
287 use mod_gravity, only: gravity_init
289 use mod_cak_force, only: cak_init
295 use mod_fld
296
297 integer :: itr, idir
298
299 call hd_read_params(par_files)
300
301 physics_type = "hd"
302 phys_energy = hd_energy
303 phys_total_energy = hd_energy
304 phys_internal_e = .false.
305 phys_gamma = eos%gamma
306
308 if(phys_trac) then
309 if(ndim .eq. 1) then
310 if(hd_trac_type .gt. 2 .and. hd_trac_type .ne. 7) then
312 if(mype==0) write(*,*) 'WARNING: set hd_trac_type=1'
313 end if
314 if(hd_trac_type == 7) then
315 if(.not. associated(usr_get_heating)) then
316 call mpistop("hd_trac_type=7 requires usr_get_heating to be set in mod_usr.t")
317 end if
318 end if
322 else
323 ! multi-D: only the local (per-cell) Johnston-2021 TRAC (type 7) is supported here;
324 ! the column-sweep variants (1,2) need field-line/vertical tracing not implemented for HD.
325 if(hd_trac_type == 7) then
326 if(.not. associated(usr_get_heating)) then
327 call mpistop("hd_trac_type=7 requires usr_get_heating to be set in mod_usr.t")
328 end if
330 else
331 phys_trac=.false.
332 if(mype==0) write(*,*) 'WARNING: hd_trac disabled for ndim>=2 (only trac_type=7 supported)'
333 end if
334 end if
335 end if
336
337 ! set default gamma for polytropic/isothermal process
338 if(.not.hd_energy) then
339 if(hd_thermal_conduction) then
341 if(mype==0) write(*,*) 'WARNING: set hd_thermal_conduction=F when hd_energy=F'
342 end if
345 if(mype==0) write(*,*) 'WARNING: set hd_hyperbolic_thermal_conduction=F when hd_energy=F'
346 end if
347 if(hd_radiative_cooling) then
349 if(mype==0) write(*,*) 'WARNING: set hd_radiative_cooling=F when hd_energy=F'
350 end if
351 end if
353
354 allocate(start_indices(number_species),stop_indices(number_species))
355
356 ! set the index of the first flux variable for species 1
357 start_indices(1)=1
358 ! Determine flux variables
359 rho_ = var_set_rho()
360
361 allocate(mom(ndir))
362 mom(:) = var_set_momentum(ndir)
363 m^c_=mom(^c);
364
365 ! Set index of energy variable
366 if (hd_energy) then
367 e_ = var_set_energy()
368 p_ = e_
369 else
370 e_ = -1
371 p_ = -1
372 end if
373
374 ! HTC q-variable must be allocated BEFORE any wextra slots (Ne, Te, ...)
375 ! so its iw_q index doesn't collide with the wextra indices, which are
376 ! assigned from nw (not nwflux). ffHD does the same ordering.
378 q_ = var_set_q()
379 need_global_cmax=.true.
380 ! q is a noisy diagnostic-like quantity (face-recipe construction
381 ! produces cell-to-cell sign flips in nearly-uniform-T regions
382 ! where -kappa*grad(T) is round-off-dominated). Excluding it from
383 ! AMR refinement avoids triggering full-domain refinement everywhere.
384 ! Lohner already refines on rho and E.
385 if (allocated(w_refine_weight)) w_refine_weight(q_) = 0.0d0
386 else
387 q_=-1
388 end if
389
390 ! fip and the tracers are flux variables (index nwflux) and must precede the extra slots Ne/Te
391 ! (index nw): registered after them, the first one shared Ne's index and the EoS overwrote it
392 if (hd_fip) then
393 fip_ = var_set_fluxvar('rho_fip', 'fip', need_bc=.false.)
394 else
395 fip_ = -1
396 end if
397
398 allocate(tracer(hd_n_tracer))
399
400 ! Set starting index of tracers
401 do itr = 1, hd_n_tracer
402 tracer(itr) = var_set_fluxvar("trc", "trp", itr, need_bc=.false.)
403 end do
404
405 if (eos%eos_type == 'LTE') then
406 ne_ = var_set_ne()
407 te_ = var_set_te()
408 else if (eos%eos_type == 'PI') then ! PI stores Te via var_set_te (sets iw_te) so the generic mod_eos_pi getters address it like LTE
409 ne_ = -1
410 te_ = var_set_te()
411 else
412 ne_ = -1
413 te_ = -1
414 end if
415
416 ! The radiative transfer reads &rt_list and registers its output variables here, and is set up
417 ! in hd_check_params once the units and the domain are known
418 if(hd_radiative_transfer) then
419 if(.not. hd_energy) call mpistop("radiative transfer needs hd_energy=T")
420 call rt_sc_activate()
421 end if
422
423 if(hd_radiation_fld)then
424 if(hd_cak_force)then
425 if(mype==0) then
426 write(*,*)'Warning: CAK force addition together with FLD radiation'
427 endif
428 endif
430 if(mype==0) then
431 write(*,*)'Warning: Optically thin cooling together with FLD radiation'
432 endif
433 endif
434 if(hd_dust.and.hd_dust_implicit)then
435 call mpistop('implicit dust addition not compatible with FLD radiation')
436 endif
437 if(.not.hd_energy)then
438 call mpistop('using FLD implies the use of an energy equation, set hd_energy=T')
439 else
440 !> set added variable and equation for radiation energy
441 r_e = var_set_radiation_energy()
442 phys_get_csrad2 => hd_get_csrad2_prim
443 !> Radiation fluid object: its EoS callbacks are wired in hd_link_eos
444 allocate(fld_fl)
445 !> Initiate radiation-closure module
446 call fld_init(eos%eos_type, eos%ionE, eos%pi_table)
447 !> The implicit (MG diffusion) hooks need the fld_fl object, so they
448 !> are wired here to physics-module wrappers that inject it.
449 if(use_multigrid)then
450 phys_implicit_update => hd_fld_implicit_update
451 phys_evaluate_implicit => hd_fld_evaluate_implicit
452 endif
453 endif
454 else
455 r_e=-1
456 endif
457
458 phys_get_dt => hd_get_dt
459 phys_get_cmax => hd_get_cmax
460 phys_get_tcutoff => hd_get_tcutoff
461 phys_get_cbounds => hd_get_cbounds
462 phys_get_flux => hd_get_flux
463 phys_add_source_geom => hd_add_source_geom
464 phys_add_source => hd_add_source
465 phys_modify_wlr => hd_modify_wlr
466 phys_check_params => hd_check_params
467 phys_check_w => hd_check_w
468 ! phys_get_pthermal is set by hd_link_eos
469 phys_get_v => hd_get_v
470 ! phys_get_rho => hd_get_rho
471 phys_write_info => hd_write_info
472 phys_handle_small_values => hd_handle_small_values
473 phys_e_to_ei => hd_e_to_ei
474 phys_ei_to_e => hd_ei_to_e
475 phys_get_ei => hd_get_ei
476
477 ! derive units from basic units
478 call hd_physical_units()
479
480 ! Spitzer prefactor for hyperbolic TC in code units. κ(T) = κ_0 · T^{5/2}
481 ! with κ_0 = 8e-12 (SI) or 8e-7 (CGS), unless the user provides an override
482 ! (e.g. to match a constant-κ parabolic TC run for benchmarking).
484 if (hd_htc_kappa_override > 0.0d0) then
486 if (mype == 0) write(*,*) ' HTC: using hd_htc_kappa_override =', hypertc_kappa
487 else if(si_unit) then
489 else
491 end if
492 end if
493
494 if (hd_dust) then
495 call dust_init(rho_, mom(:), e_)
496 endif
497
498
499 ! set number of variables which need update ghostcells.
500 ! The EoS-derived slots Ne/Te are derived state that must stay consistent with (rho,e)
501 ! wherever the conserved state is valid, so they have to travel with it. var_set_ne /
502 ! var_set_te bump nw but neither nwflux nor nwaux, so the historic nwflux+nwaux silently
503 ! excluded them: they were never communicated, only derived, and every ghost cell held
504 ! zero until something derived it. Extend the window to whichever of them exist (they are
505 ! registered contiguously just above); FI leaves both at -1 and the window is unchanged.
506 ! bc_phys is unaffected -- it iterates nwflux+nwaux independently.
507 nwgc=nwflux+nwaux
508 if (iw_ne > 0) nwgc = max(nwgc, iw_ne)
509 if (iw_te > 0) nwgc = max(nwgc, iw_te)
510
511 ! set the index of the last flux variable for species 1
512 stop_indices(1)=nwflux
513
514 if(hd_trac) then
515 tcoff_ = var_set_wextra()
516 iw_tcoff=tcoff_
517 else
518 tcoff_ = -1
519 end if
520
521 !> Cache log10(nH) in wextra for LTE+IonE TC (density invariant during STS)
522 if (eos%eos_type == 'LTE' .and. eos%ionE .and. hd_thermal_conduction) then
523 iw_log_nh = var_set_wextra()
524 end if
525
526 ! phys_get_Rfactor is bound by mod_hd_eos:bind_eos_to_source to eos%get_Rfactor.
527
528 ! initialize thermal conduction module
529 if (hd_thermal_conduction) then
530 if (.not. hd_energy) &
531 call mpistop("thermal conduction needs hd_energy=T")
532
533 call sts_init()
534 call tc_init_params(eos%gamma)
535
536 allocate(tc_fl)
537 call tc_get_hd_params(tc_fl,tc_params_read_hd)
538 call add_sts_method(hd_get_tc_dt_hd,hd_sts_set_source_tc_hd,e_,1,e_,1,.false.)
539 if (iw_log_nh > 0) then
540 call set_conversion_methods_to_head(hd_e_to_ei_and_cache_log_nh, hd_ei_to_e)
541 else
543 end if
544 call set_error_handling_to_head(hd_tc_handle_small_e)
545 ! tc_fl%get_temperature_from_conserved => hd_get_temperature_from_etot
546 ! tc_fl%get_temperature_from_eint => hd_get_temperature_from_eint
547 ! tc_fl%get_temperature_from_conserved => eos%get_temperature_from_etot
548 ! tc_fl%get_temperature_from_eint => eos%get_temperature_from_eint
549 ! ! tc_fl%get_rho => hd_get_rho
550 ! tc_fl%get_rho => eos%get_rho
551 tc_fl%e_ = e_
552 tc_fl%Tcoff_ = tcoff_
553 else if (hd_hyperbolic_thermal_conduction .and. hd_trac) then
554 ! HTC + TRAC: allocate a stub tc_fl so TRAC type 7 can read tc_k_para.
555 ! Use hypertc_kappa (Spitzer prefactor, same form as tc_k_para) so the
556 ! TRAC kappa-effective formula reduces to the same expression as in
557 ! the PTC branch. No STS init; no flux routine; this is read-only.
558 call tc_init_params(eos%gamma)
559 allocate(tc_fl)
560 tc_fl%tc_k_para = hypertc_kappa
561 tc_fl%tc_saturate = hd_htc_sat
562 tc_fl%e_ = e_
563 tc_fl%Tcoff_ = tcoff_
564 end if
565
566 ! Initialize radiative cooling module
567 if (hd_radiative_cooling) then
568 if (.not. hd_energy) &
569 call mpistop("radiative cooling needs hd_energy=T")
570 call radiative_cooling_init_params(eos%gamma,eos%He_abundance)
571 allocate(rc_fl)
572 rc_fl%fip_ = fip_
573 call radiative_cooling_init(rc_fl,rc_params_read)
574 rc_fl%e_ = e_
575 rc_fl%Tcoff_ = tcoff_
576 ! Initialize escape probability if requested
577 if (rc_fl%rad_escape_prob) then
578 iw_colmass = var_set_wextra()
579 rc_fl%iw_colmass_ = iw_colmass
580 phys_escape_prob = .true.
581 call escape_prob_init(iw_colmass, rc_fl%rad_modify_sym, rc_fl%rad_escape_height)
582 end if
583 end if
584 allocate(te_fl_hd)
585
586{^ifthreed
587 phys_te_images => hd_te_images
588}
589 ! Initialize viscosity module
590 if (hd_viscosity) call viscosity_init(phys_wider_stencil)
591
592 ! Initialize gravity module
593 if (hd_gravity) call gravity_init()
594
595 ! Well-balanced reconstruction: only meaningful with gravity
596 if (hd_well_balanced) then
597 if (.not. hd_gravity) then
598 hd_well_balanced = .false.
599 if(mype==0) write(*,*) 'WARNING: set hd_well_balanced=F (requires hd_gravity=T)'
600 else
601 phys_wb_transform => hd_wb_transform
602 phys_wb_inverse => hd_wb_inverse
603 phys_wb_prolong => hd_wb_prolong
604 ! the WB transform needs p -> e to invert e -> p exactly, which the bisection does for
605 ! the tables and the entropy method alike
606 if (eos%ionE .and. eos%p2eint_method /= 'bisect') then
607 eos%p2eint_method = 'bisect'
608 if(mype==0) write(*,*) 'WB + ionE: forcing p2eint_method = bisect'
609 end if
610 if(mype==0) write(*,*) 'Well-balanced reconstruction enabled'
611 end if
612 end if
613
614 ! Initialize rotating_frame module
616
617 ! Initialize CAK radiation force module
618 if (hd_cak_force) call cak_init(eos%gamma)
619
620
621 ! Check whether custom flux types have been defined
622 if (.not. allocated(flux_type)) then
623 allocate(flux_type(ndir, nw))
624 flux_type = flux_default
625 else if (any(shape(flux_type) /= [ndir, nw])) then
626 call mpistop("phys_check error: flux_type has wrong shape")
627 end if
628
629 nvector = 1 ! No. vector vars
630 allocate(iw_vector(nvector))
631 iw_vector(1) = mom(1) - 1
632 ! ionization-degree table init now lives in eos_finalise (eos% owns
633 ! thermodynamic-backend init); see mod_eos_pi.
634
635 end subroutine hd_phys_init
636
637 !> allow the user to control the left/right states (and hence the flux) at
638 !> physical boundary interfaces, as in the MHD module
639 subroutine hd_modify_wlr(ixI^L,ixO^L,qt,wLC,wRC,wLp,wRp,s,idir)
642 integer, intent(in) :: ixi^l, ixo^l, idir
643 double precision, intent(in) :: qt
644 double precision, intent(inout) :: wlc(ixi^s,1:nw), wrc(ixi^s,1:nw)
645 double precision, intent(inout) :: wlp(ixi^s,1:nw), wrp(ixi^s,1:nw)
646 type(state) :: s
647
648 if(associated(usr_set_wlr)) call usr_set_wlr(ixi^l,ixo^l,qt,wlc,wrc,wlp,wrp,s,idir)
649
650 end subroutine hd_modify_wlr
651
652{^ifthreed
653 subroutine hd_te_images
656 select case(convert_type)
657 case('EIvtiCCmpi','EIvtuCCmpi')
659 case('ESvtiCCmpi','ESvtuCCmpi')
661 case('SIvtiCCmpi','SIvtuCCmpi')
663 case('WIvtiCCmpi','WIvtuCCmpi')
665 case default
666 call mpistop("Error in synthesize emission: Unknown convert_type")
667 end select
668 end subroutine hd_te_images
669}
670!!start th cond
671 ! wrappers for STS functions in thermal_conductivity module
672 ! which take as argument the tc_fluid (defined in the physics module)
673 subroutine hd_sts_set_source_tc_hd(ixI^L,ixO^L,w,x,wres,fix_conserve_at_step,my_dt,igrid,nflux)
677 integer, intent(in) :: ixi^l, ixo^l, igrid, nflux
678 double precision, intent(in) :: x(ixi^s,1:ndim)
679 double precision, intent(inout) :: wres(ixi^s,1:nw), w(ixi^s,1:nw)
680 double precision, intent(in) :: my_dt
681 logical, intent(in) :: fix_conserve_at_step
682 call sts_set_source_tc_hd(ixi^l,ixo^l,w,x,wres,fix_conserve_at_step,my_dt,igrid,nflux,tc_fl)
683 end subroutine hd_sts_set_source_tc_hd
684
685 function hd_get_tc_dt_hd(w,ixI^L,ixO^L,dx^D,x) result(dtnew)
686 !Check diffusion time limit dt < dx_i**2/((gamma-1)*tc_k_para/rho)
687 !and tc_k_para can depend on T=p/rho
690
691 integer, intent(in) :: ixi^l, ixo^l
692 double precision, intent(in) :: dx^d, x(ixi^s,1:ndim)
693 double precision, intent(in) :: w(ixi^s,1:nw)
694 double precision :: dtnew
695
696 dtnew=get_tc_dt_hd(w,ixi^l,ixo^l,dx^d,x,tc_fl)
697 end function hd_get_tc_dt_hd
698
699 subroutine hd_tc_handle_small_e(w, x, ixI^L, ixO^L, step)
700 ! move this in a different routine as in mhd if needed in more places
703
704 integer, intent(in) :: ixi^l,ixo^l
705 double precision, intent(inout) :: w(ixi^s,1:nw)
706 double precision, intent(in) :: x(ixi^s,1:ndim)
707 integer, intent(in) :: step
708
709 integer :: idir
710 logical :: flag(ixi^s,1:nw)
711 character(len=140) :: error_msg
712
713 flag=.false.
714 where(w(ixo^s,e_)<small_e) flag(ixo^s,e_)=.true.
715 if(any(flag(ixo^s,e_))) then
716 select case (small_values_method)
717 case ("replace")
718 where(flag(ixo^s,e_)) w(ixo^s,e_)=small_e
719 case ("average")
720 call small_values_average(ixi^l, ixo^l, w, x, flag, e_)
721 case default
722 ! small values error shows primitive variables
723 w(ixo^s,e_)=w(ixo^s,e_)*(eos%gamma - 1.0d0)
724 do idir = 1, ndir
725 w(ixo^s, iw_mom(idir)) = w(ixo^s, iw_mom(idir))/w(ixo^s,rho_)
726 end do
727 write(error_msg,"(a,i3)") "Thermal conduction step ", step
728 call small_values_error(w, x, ixi^l, ixo^l, flag, error_msg)
729 end select
730 end if
731 end subroutine hd_tc_handle_small_e
732
733 ! fill in tc_fluid fields from namelist
734 subroutine tc_params_read_hd(fl)
736 type(tc_fluid), intent(inout) :: fl
737 integer :: n
738 logical :: tc_saturate=.false.
739 logical :: tc_patch_eint=.false.
740 logical :: tc_frozen_ion=.true.
741 double precision :: tc_k_para=0d0
742 double precision :: trac_t_floor=0.d0
743
744 namelist /tc_list/ tc_saturate, tc_k_para, trac_t_floor, tc_patch_eint, tc_frozen_ion
745
746 do n = 1, size(par_files)
747 open(unitpar, file=trim(par_files(n)), status="old")
748 read(unitpar, tc_list, end=111)
749111 close(unitpar)
750 end do
751 fl%tc_saturate = tc_saturate
752 fl%tc_patch_eint = tc_patch_eint
753 fl%frozen_ion = tc_frozen_ion
754 fl%tc_k_para = tc_k_para
755 fl%trac_T_floor = trac_t_floor / unit_temperature
756
757 end subroutine tc_params_read_hd
758
759 ! subroutine hd_get_rho(w,x,ixI^L,ixO^L,rho)
760 ! use mod_global_parameters
761 ! integer, intent(in) :: ixI^L, ixO^L
762 ! double precision, intent(in) :: w(ixI^S,1:nw),x(ixI^S,1:ndim)
763 ! double precision, intent(out) :: rho(ixI^S)
764
765 ! rho(ixO^S) = w(ixO^S,rho_) * eos%nH2rhoFactor
766
767 ! end subroutine hd_get_rho
768
769!!end th cond
770!!rad cool
771 subroutine rc_params_read(fl)
773 use mod_constants, only: bigdouble
774 use mod_basic_types, only: std_len
775 type(rc_fluid), intent(inout) :: fl
776 integer :: n
777 ! list parameters
778 integer :: ncool = 4000
779
780 !> Name of cooling curve
781 character(len=std_len) :: coolcurve='JCcorona'
782
783 !> Fixed temperature not lower than tlow
784 logical :: tfix=.false.
785
786 !> Lower limit of temperature
787 double precision :: tlow=bigdouble
788
789 !> Add cooling source in a split way (.true.) or un-split way (.false.)
790 logical :: rc_split=.false.
791
792 !> Cooling fraction (HEAD addition; used for explicit-mode dt scaling, kept for compat)
793 double precision :: cfrac=0.1d0
794
795 !> Master switch for radiative loss modification (spatial + density taper)
796 logical :: rad_modify=.false.
797 !> Apply spatial taper at both boundaries (default: lower only)
798 logical :: rad_modify_sym=.false.
799 !> Spatial taper: height from boundary below which taper applies
800 double precision :: rad_cut_hgt=0.0d0
801 !> Spatial taper: Gaussian decay width
802 double precision :: rad_cut_dey=0.15d0
803 !> Density taper: threshold above which taper applies
804 double precision :: rad_taper_rho=bigdouble
805 !> Density taper: Gaussian decay width
806 double precision :: rad_taper_dey=0.0d0
807 !> Suppress cooling below this temperature (Kelvin) within rad_cut_hgt.
808 !> Cells inside rad_cut_hgt with T < rad_suppress_temp get factor=0.
809 double precision :: rad_suppress_temp=0.0d0
810 !> Enable escape probability cooling modification
811 logical :: rad_escape_prob=.false.
812 !> Effective opacity for escape probability (code units)
813 double precision :: rad_kappa_eff=0.0d0
814 !> Temperature above which kappa goes to 0 (Kelvin); 0 = constant kappa
815 double precision :: rad_kappa_tcutoff=0.0d0
816 !> Sigmoid sharpness exponent for kappa(T) cutoff
817 double precision :: rad_kappa_alpha=4.0d0
818 !> Escape probability type: 'slab' or 'voigt'
819 character(len=10) :: rad_escape_type='slab'
820 !> Exponential cutoff scale: E *= exp(-tau/tau_cutoff); 0 = disabled
821 double precision :: rad_escape_tau_cutoff=0.0d0
822 !> Max height from footpoint for escape probability column mass (cm); 0 = no limit
823 double precision :: rad_escape_height=0.0d0
824 !> Variable-c_V Townsend extension (Y_mod): quadrature and sub-intervals
825 character(len=8) :: rc_y_mod_quadrature='boole'
826 integer :: rc_y_mod_n_sub=16
827 !> Upstream: cutoff radiative cooling below rad_damp_height (Gaussian damp)
828 logical :: rad_damp=.false.
829 double precision :: rad_damp_height=0.5d0
830 double precision :: rad_damp_scale=0.15d0
831 !> Upstream: Newton-radiative damping (surface treatment)
832 logical :: rad_newton=.false.
833 double precision :: rad_newton_trad=0.006d0
834 double precision :: rad_newton_rhosurf=1.d4
835 double precision :: rad_newton_pthick=25.d0
836
837 namelist /rc_list/ coolcurve, ncool, cfrac, tlow, tfix, rc_split, &
838 rad_modify, rad_modify_sym, rad_suppress_temp, &
839 rad_cut_hgt, rad_cut_dey, rad_taper_rho, rad_taper_dey, &
840 rad_escape_prob, rad_kappa_eff, rad_kappa_tcutoff, rad_kappa_alpha, &
841 rad_escape_type, rad_escape_tau_cutoff, rad_escape_height, &
842 rc_y_mod_quadrature, rc_y_mod_n_sub, &
843 rad_damp, rad_damp_height, rad_damp_scale, &
844 rad_newton, rad_newton_trad, rad_newton_rhosurf, rad_newton_pthick
845
846 do n = 1, size(par_files)
847 open(unitpar, file=trim(par_files(n)), status="old")
848 read(unitpar, rc_list, end=111)
849111 close(unitpar)
850 end do
851
852 fl%ncool=ncool
853 fl%coolcurve=coolcurve
854 fl%tlow=tlow
855 fl%Tfix=tfix
856 fl%rc_split=rc_split
857 fl%cfrac=cfrac
858 fl%rad_modify=rad_modify
859 fl%rad_modify_sym=rad_modify_sym
860 fl%rad_suppress_temp=rad_suppress_temp
861 fl%rad_cut_hgt=rad_cut_hgt
862 fl%rad_cut_dey=rad_cut_dey
863 fl%rad_taper_rho=rad_taper_rho
864 fl%rad_taper_dey=rad_taper_dey
865 fl%rad_escape_prob=rad_escape_prob
866 fl%rad_kappa_eff=rad_kappa_eff
867 fl%rad_kappa_Tcutoff=rad_kappa_tcutoff/unit_temperature
868 fl%rad_kappa_alpha=rad_kappa_alpha
869 fl%rad_escape_type=rad_escape_type
870 fl%rad_escape_tau_cutoff=rad_escape_tau_cutoff
871 fl%rad_escape_height=rad_escape_height/unit_length
872 fl%Y_mod_quadrature=rc_y_mod_quadrature
873 fl%Y_mod_N_sub=rc_y_mod_n_sub
874 fl%rad_damp = rad_damp
875 fl%rad_damp_height = rad_damp_height
876 fl%rad_damp_scale = rad_damp_scale
877 fl%rad_newton = rad_newton
878 fl%rad_newton_trad = rad_newton_trad
879 fl%rad_newton_rhosurf = rad_newton_rhosurf
880 fl%rad_newton_pthick = rad_newton_pthick
881 end subroutine rc_params_read
882!! end rad cool
883
886 use mod_geometry, only: coordinate
889 use mod_particles, only: npayload,nusrpayload,ngridvars,num_particles,physics_type_particles
890 use mod_fld
891
892 double precision :: a,b,xfrac,yfrac
893
894 ! Initialize particles module, put here so additional gridvars and user payloads are known
895 if (hd_particles) then
896 call particles_init()
897 end if
898
899 if (hd_radiative_transfer) call rt_sc_setup()
900
901 if (.not. hd_energy) then
902 if (eos%gamma <= 0.0d0) call mpistop ("Error: eos%gamma <= 0")
903 if (hd_adiab < 0.0d0) call mpistop ("Error: hd_adiab < 0")
905 else
906 if (eos%gamma <= 0.0d0 .or. eos%gamma == 1.0d0) &
907 call mpistop ("Error: eos%gamma <= 0 or eos%gamma == 1.0")
908 ! For LTE+ionE, this floor excludes ionisation energy. At tlow (~1000 K)
909 ! ionisation is negligible, so the thermal-only floor is adequate.
910 small_e = small_pressure * eos%inv_gamma_minus_1
911 small_r_e = small_pressure * eos%inv_gamma_minus_1
912 ! gamma_minus_1 and inv_gamma_minus_1 are set by eos_init
913 end if
914
915 if (hd_dust) call dust_check_params()
916
917 if(hd_dust_implicit) then
918 if(.not.use_imex_scheme)then
919 call mpistop('select IMEX scheme for implicit dust update')
920 endif
921 ! implicit dust update
922 phys_implicit_update => dust_implicit_update
923 phys_evaluate_implicit => dust_evaluate_implicit
924 endif
925
926 ! Hyperbolic TC in HD is only implemented for 1D: q is a scalar carrying
927 ! the heat flux along the single spatial direction. For ndim>1 the user
928 ! should use the ffHD module (B-aligned conduction) or the parabolic
929 ! tc_init path. Restrict here rather than silently giving wrong fluxes.
930 if (hd_hyperbolic_thermal_conduction .and. ndim /= 1) then
931 call mpistop("hd_hyperbolic_thermal_conduction is implemented for ndim=1 only;" // &
932 " for ndim>1 use mod_ffhd or parabolic mod_thermal_conduction.")
933 end if
935 call mpistop("hd_hyperbolic_thermal_conduction and hd_thermal_conduction are mutually exclusive;" // &
936 " choose one TC implementation.")
937 end if
938
939 if(hd_radiation_fld) then
940 if(.not.use_imex_scheme)then
941 call mpistop('select IMEX scheme for FLD radiation use')
942 endif
943 if(use_multigrid)then
944 call phys_set_mg_bounds()
945 else
946 if(.not.fld_no_mg)call mpistop('multigrid must have BCs for IMEX and FLD radiation use')
947 endif
948 if(mype==0)then
949 write(*,*)'==FLD SETUP======================'
950 write(*,*)'Using FLD with settings:'
951 write(*,*)'Using FLD with settings: hd_radiation_fld=',hd_radiation_fld
952 write(*,*)'Using FLD with settings: hd_fld_pradtensor=',hd_fld_pradtensor
953 write(*,*)'Using FLD with settings: fld_fluxlimiter=',fld_fluxlimiter
954 write(*,*)'Using FLD with settings: fld_bound_diff=',fld_bound_diff
955 write(*,*)'Using FLD with settings: fld_interaction_method=',fld_interaction_method
956 write(*,*)'Using FLD with settings: fld_opacity_law=',fld_opacity_law
957 write(*,*)'Using FLD with settings: fld_kappa0=',fld_kappa0
958 write(*,*)'Using FLD with settings: fld_opal_table=',fld_opal_table
959 write(*,*)'Using FLD with settings: fld_Radforce_split=',fld_radforce_split
960 write(*,*)'Using FLD with settings: fld_bisect_tol=',fld_bisect_tol
961 write(*,*)'Using FLD with settings: fld_diff_tol=',fld_diff_tol
962 write(*,*)'Using FLD with settings: nth_for_diff_mg=',nth_for_diff_mg
963 write(*,*)' FLD has use_imex_scheme and use_multigrid=',use_imex_scheme,use_multigrid
964 write(*,*)' FLD has fld_no_mg=',fld_no_mg
965 if(fld_no_mg)then
966 print *,'WARNING: cheating with FLD diffusion ***********************'
967 print *,'WARNING: No MG-diffusion for radiative energy at all!!!!!!!'
968 print *,'WARNING: cheating with FLD diffusion ***********************'
969 endif
970 print *,'const_rad_a =',const_rad_a
971 print *,'NORMALIZED arad_norm=',arad_norm
972 print *,'NORMALIZED c_norm=',c_norm
973 if(fld_cnorm>0.0d0)then
974 print *,'WARNING: cheating with c_norm ***********************'
975 print *,'WARNING: c_norm reset to=',fld_cnorm
977 print *,'WARNING: cheating with c_norm ***********************'
978 endif
979 print *,'const_kappae =',const_kappae
980 if(trim(fld_opacity_law).eq.'const_norm')then
981 print *,'NORMALIZED fld_kappa0 =',fld_kappa0
982 print *,'physical value (in cgs or SI) =',fld_kappa0*unit_opacity
983 endif
984 if(trim(fld_opacity_law).eq.'const')then
985 print *,'physical fld_kappa (in cgs or SI) =',fld_kappa0
986 print *,'NORMALIZED value =',fld_kappa0/unit_opacity
987 endif
988 write(*,*)'===FLD SETUP====================='
989 endif
990 endif
991 if(mype==0)then
992 write(*,*)'====HD run with settings===================='
993 write(*,*)'Using mod_hd_phys with settings:'
994 write(*,*)'SI_unit=',si_unit
995 write(*,*)'Dimensionality :',ndim
996 write(*,*)'vector components:',ndir
997 write(*,*)'coordinate set to type,slab:',coordinate,slab
998 write(*,*)'number of variables nw=',nw
999 write(*,*)' start index iwstart=',iwstart
1000 write(*,*)'number of vector variables=',nvector
1001 write(*,*)'number of stagger variables nws=',nws
1002 write(*,*)'number of variables with BCs=',nwgc
1003 write(*,*)'number of vars with fluxes=',nwflux
1004 write(*,*)'number of vars with flux + BC=',nwfluxbc
1005 write(*,*)'number of auxiliary variables=',nwaux
1006 write(*,*)'number of extra vars without flux=',nwextra
1007 write(*,*)'number of extra vars for wextra=',nw_extra
1008 write(*,*)'number of auxiliary I/O variables=',nwauxio
1009 write(*,*)'number of hd_n_tracer=',hd_n_tracer
1010 write(*,*)' hd_energy=',hd_energy
1011 write(*,*)' hd_gravity=',hd_gravity
1012 write(*,*)' hd_viscosity=',hd_viscosity
1013 write(*,*)' hd_radiative_cooling=',hd_radiative_cooling
1014 write(*,*)' hd_cak_force=',hd_cak_force
1015 write(*,*)' hd_radiation_fld=',hd_radiation_fld
1016 write(*,*)' hd_thermal_conduction=',hd_thermal_conduction
1017 write(*,*)' hd_hyperbolic_thermal_conduction=',hd_hyperbolic_thermal_conduction
1018 write(*,*)' hd_trac=',hd_trac
1019 write(*,*)' hd_dust=',hd_dust
1020 write(*,*)' hd_rotating_frame=',hd_rotating_frame
1021 write(*,*)' hd_particles=',hd_particles
1022 if(hd_particles) then
1023 write(*,*) '*****Using particles: npayload,ngridvars :', npayload,ngridvars
1024 write(*,*) '*****Using particles: nusrpayload :', nusrpayload
1025 write(*,*) '*****Using particles: num_particles :', num_particles
1026 write(*,*) '*****Using particles: physics_type_particles=',physics_type_particles
1027 end if
1028 write(*,*)'number of ghostcells=',nghostcells
1029 write(*,*)'number due to phys_wider_stencil=',phys_wider_stencil
1030 write(*,*)'==========================================='
1031 print *,'========EOS and UNITS==========='
1032 print *,'SI_unit =',si_unit
1033 print *,'gamma=',eos%gamma
1034 print *,'He_abundance =',eos%He_abundance
1035 print *,'RR =',rr
1036 print *,'========EOS and UNITS==========='
1037 print *,'unit_time =',unit_time
1038 print *,'unit_length =',unit_length
1039 print *,'unit_velocity =',unit_velocity
1040 print *,'unit_pressure =',unit_pressure
1041 print *,'unit_numberdensity =',unit_numberdensity
1042 print *,'unit_density =',unit_density
1043 print *,'unit_temperature =',unit_temperature
1044 print *,'unit_mass =',unit_mass
1045 print *,'unit_Erad =',unit_erad
1046 print *,'unit_radflux =',unit_radflux
1047 print *, 'CHECK that p_u ',unit_pressure,' equals ',unit_density*unit_velocity**2
1048 print *, 'CHECK that L_u ',unit_length,' equals ',unit_velocity*unit_time
1049 print *, 'CHECK that M_u',unit_mass,' equals ',unit_density*unit_length**3
1050 print *, 'density to numberdensity has factor ',unit_density/unit_numberdensity
1051 if(si_unit)then
1052 print *, ' compare this to ',mp_si*(1.d0+4.d0*eos%He_abundance)
1053 else
1054 print *, ' compare this to ',mp_cgs*(1.d0+4.d0*eos%He_abundance)
1055 endif
1056 print *, 'pressure to n T has factor ',unit_pressure/(unit_numberdensity*unit_temperature)
1057 if(si_unit)then
1058 print *, ' compare this to ',kb_si*(2.d0+3.d0*eos%He_abundance)
1061 else
1062 print *, ' compare this to ',kb_cgs*(2.d0+3.d0*eos%He_abundance)
1065 endif
1066 if(eos%eos_type /= 'LTE')then
1067 print *, 'mean molecular weight mu is =',a/b,' = ', (1.d0+4.d0*eos%He_abundance)/(2.d0+3.d0*eos%He_abundance)
1068 xfrac=1.d0/a
1069 yfrac=4.d0*eos%He_abundance/(1.d0+4.d0*eos%He_abundance)
1070 print *, 'mass fraction hydrogen X is =',1/a,' and this equals ', 1.d0/(1.d0+4.d0*eos%He_abundance)
1071 print *, 'mass fraction helium Y is =',yfrac
1072 print *, ' check that 1/mu', b/a,' is equal to 2X+3Y/4=',2.d0*xfrac+3.d0*yfrac/4.d0
1073 print *, ' ratio n_e/n_p=',1.d0+2.0d0*eos%He_abundance
1074 endif
1075 print *,'========UNITS==========='
1076 endif
1077
1078 end subroutine hd_check_params
1079
1080 subroutine hd_physical_units
1082 double precision :: mp,kb,c_lightspeed,xfrac,sigma_telectron
1083 double precision :: a,b
1084 ! Derive scaling units
1085 if(si_unit) then
1086 mp=mp_si
1087 kb=kb_si
1088 const_sigmasb=sigma_sb_si
1089 c_lightspeed=c_si
1090 sigma_telectron=sigma_te_si
1091 else
1092 mp=mp_cgs
1093 kb=kb_cgs
1094 const_sigmasb=sigma_sb_cgs
1095 c_lightspeed=const_c
1096 sigma_telectron=sigma_te_cgs
1097 end if
1098 ! Normalisation dispatch keyed solely on eos%eos_type (FI is the default, so
1099 ! legacy parfiles land in the FI/PI absorbed-(a,b), RR=1 branch -- the former
1100 ! eq_state_units=.true. result).
1101 if (eos%eos_type == 'LTE') then
1102 !> Remove the assumed FI normalisation from the units and handle in EoS
1103 a=1d0
1104 b=1d0
1105 eos%nH2rhoFactor = 1d0+4d0*eos%He_abundance
1106 rr=(2d0+3d0*eos%He_abundance) / (1d0+4d0*eos%He_abundance)
1107 xfrac=1.d0/(1.d0+4.d0*eos%He_abundance)
1108 else
1109 !> FI / PI: absorbed-(a,b), RR=1 (a=b=1 with RR=1 would be wrong physics
1110 !> for He>0). PI shares FI's normalisation exactly.
1111 a=1d0+4d0*eos%He_abundance
1112 if(eos%eos_type=='PI') then
1113 b=1d0+h_ion_fr+eos%He_abundance*(he_ion_fr*(he_ion_fr2+1d0)+1d0)
1114 else
1115 b=2d0+3d0*eos%He_abundance
1116 end if
1117 rr=1d0
1118 xfrac=1.d0/a
1119 end if
1120 if(unit_density/=1.d0 .or. unit_numberdensity/=1.d0) then
1121 if(unit_density/=1.d0) then
1123 else if(unit_numberdensity/=1.d0) then
1125 end if
1126 if(unit_temperature/=1.d0) then
1129 if(unit_length/=1.d0) then
1131 else if(unit_time/=1.d0) then
1133 end if
1134 else if(unit_pressure/=1.d0) then
1137 if(unit_length/=1.d0) then
1139 else if(unit_time/=1.d0) then
1141 end if
1142 else if(unit_velocity/=1.d0) then
1145 if(unit_length/=1.d0) then
1147 else if(unit_time/=1.d0) then
1149 end if
1150 else if(unit_time/=1.d0) then
1154 end if
1155 else if(unit_temperature/=1.d0) then
1156 ! units of temperature and velocity are dependent
1157 if(unit_pressure/=1.d0) then
1161 if(unit_length/=1.d0) then
1163 else if(unit_time/=1.d0) then
1165 end if
1166 end if
1167 else if(unit_pressure/=1.d0) then
1168 if(unit_velocity/=1.d0) then
1172 if(unit_length/=1.d0) then
1174 else if(unit_time/=1.d0) then
1176 end if
1177 else if(unit_time/=0.d0) then
1182 end if
1183 end if
1185
1186 !> Units needed for radiative flux and opacity as used in FLD
1187 ! normalized light speed
1188 c_norm=c_lightspeed/unit_velocity
1189 ! this is the radiation constant in either cgs or SI units
1190 const_rad_a=4.d0*const_sigmasb/c_lightspeed
1191 ! this is the dimensionless conversion factor for Erad to Trad
1193 ! This is the Thomson scattering opacity in the correct units
1194 ! hydrogen mass fraction X=1/a in the absorbed-(a,b) normalisation
1195 const_kappae=sigma_telectron*(1.d0+xfrac)/(2.0d0*mp)
1196 ! these are the units
1200
1201 end subroutine hd_physical_units
1202
1203 !> Returns logical argument flag where values are ok
1204 subroutine hd_check_w(primitive, ixI^L, ixO^L, w, flag)
1206 use mod_dust, only: dust_check_w
1207
1208 logical, intent(in) :: primitive
1209 integer, intent(in) :: ixi^l, ixo^l
1210 double precision, intent(in) :: w(ixi^s, nw)
1211 logical, intent(inout) :: flag(ixi^s,1:nw)
1212
1213 double precision :: tmp(ixi^s)
1214 double precision :: x(ixi^s, 1:ndim)
1215
1216 flag=.false.
1217 x(ixi^s,1:ndim)=block%x(ixi^s,1:ndim) !> Rather than redefining the hd_check_w and limiter procedure interfaces
1218 if (hd_energy) then
1219 if (primitive) then
1220 where(w(ixo^s, e_) < small_pressure) flag(ixo^s,e_) = .true.
1221 else
1222 ! Inline (gamma-1)*(e - KE) to avoid eos%get_thermal_pressure side
1223 ! effects (fix_small_values clipping / crash=.true.) that would
1224 ! suppress the flag or abort before remediation could run.
1225 tmp(ixo^s)=(eos%gamma-1.0d0)*(w(ixo^s,e_)-&
1226 half*(^c&w(ixo^s,m^c_)**2+)/w(ixo^s,rho_))
1227 where(tmp(ixo^s) < small_pressure) flag(ixo^s,e_) = .true.
1228 endif
1229 if(hd_radiation_fld)then
1230 where(w(ixo^s, r_e) < small_r_e) flag(ixo^s,r_e) = .true.
1231 endif
1232 end if
1233
1234 where(w(ixo^s, rho_) < small_density) flag(ixo^s,rho_) = .true.
1235
1236 if(hd_dust) call dust_check_w(ixi^l,ixo^l,w,x,flag)
1237
1238 end subroutine hd_check_w
1239
1240 subroutine hd_bound_fip(primitive, ixI^L, ixO^L, w)
1242 logical, intent(in) :: primitive
1243 integer, intent(in) :: ixi^l, ixo^l
1244 double precision, intent(inout) :: w(ixi^s,1:nw)
1245
1246 double precision :: rho_safe(ixi^s), fip_prim(ixi^s)
1247
1248 if (.not. hd_fip) return
1249
1250 if (primitive) then
1251 w(ixo^s,fip_) = min(maxfip, max(minfip, w(ixo^s,fip_)))
1252 else
1253 rho_safe(ixo^s) = max(w(ixo^s,rho_), small_density)
1254 fip_prim(ixo^s) = w(ixo^s,fip_) / rho_safe(ixo^s)
1255 fip_prim(ixo^s) = min(maxfip, max(minfip, fip_prim(ixo^s)))
1256 w(ixo^s,fip_) = rho_safe(ixo^s) * fip_prim(ixo^s)
1257 end if
1258 end subroutine hd_bound_fip
1259
1260 ! !> Transform primitive variables into conservative ones
1261 ! subroutine hd_to_conserved(ixI^L, ixO^L, w, x)
1262 ! use mod_global_parameters
1263 ! use mod_dust, only: dust_to_conserved
1264 ! integer, intent(in) :: ixI^L, ixO^L
1265 ! double precision, intent(inout) :: w(ixI^S, nw)
1266 ! double precision, intent(in) :: x(ixI^S, 1:ndim)
1267
1268 ! integer :: ix^D
1269
1270 ! {do ix^DB=ixOmin^DB,ixOmax^DB\}
1271 ! if (hd_energy) then
1272 ! ! Calculate total energy from pressure and kinetic energy
1273 ! w(ix^D,e_)=w(ix^D, e_)*inv_gamma_1+&
1274 ! half*(^C&w(ix^D,m^C_)**2+)*w(ix^D,rho_)
1275 ! end if
1276 ! ! Convert velocity to momentum
1277 ! ^C&w(ix^D,m^C_)=w(ix^D,rho_)*w(ix^D,m^C_)\
1278 ! {end do\}
1279
1280 ! if (hd_dust) then
1281 ! call dust_to_conserved(ixI^L, ixO^L, w, x)
1282 ! end if
1283
1284 ! end subroutine hd_to_conserved
1285
1286 ! !> Transform conservative variables into primitive ones
1287 ! subroutine hd_to_primitive(ixI^L, ixO^L, w, x)
1288 ! use mod_global_parameters
1289 ! use mod_dust, only: dust_to_primitive
1290 ! integer, intent(in) :: ixI^L, ixO^L
1291 ! double precision, intent(inout) :: w(ixI^S, nw)
1292 ! double precision, intent(in) :: x(ixI^S, 1:ndim)
1293
1294 ! double precision :: inv_rho
1295 ! integer :: ix^D
1296
1297 ! if (fix_small_values) then
1298 ! call hd_handle_small_values(.false., w, x, ixI^L, ixO^L, 'hd_to_primitive')
1299 ! end if
1300
1301 ! {do ix^DB=ixOmin^DB,ixOmax^DB\}
1302 ! inv_rho = 1.d0/w(ix^D,rho_)
1303 ! ! Convert momentum to velocity
1304 ! ^C&w(ix^D,m^C_)=w(ix^D,m^C_)*inv_rho\
1305 ! ! Calculate pressure = (gamma-1) * (e-ek)
1306 ! if(hd_energy) then
1307 ! ! Compute pressure
1308 ! w(ix^D,p_)=(hd_gamma-1.d0)*(w(ix^D,e_)&
1309 ! -half*w(ix^D,rho_)*(^C&w(ix^D,m^C_)**2+))
1310 ! end if
1311 ! {end do\}
1312
1313 ! ! Convert dust momentum to dust velocity
1314 ! if (hd_dust) then
1315 ! call dust_to_primitive(ixI^L, ixO^L, w, x)
1316 ! end if
1317
1318 ! end subroutine hd_to_primitive
1319
1320 !> Transform internal energy to total energy
1321 subroutine hd_ei_to_e(ixI^L,ixO^L,w,x)
1323 integer, intent(in) :: ixi^l, ixo^l
1324 double precision, intent(inout) :: w(ixi^s, nw)
1325 double precision, intent(in) :: x(ixi^s, 1:ndim)
1326
1327 ! Calculate total energy from internal and kinetic energy
1328 w(ixo^s,e_)=w(ixo^s,e_)+half*(^c&w(ixo^s,m^c_)**2+)/w(ixo^s,rho_)
1329
1330 end subroutine hd_ei_to_e
1331
1332 !> Transform total energy to internal energy
1333 subroutine hd_e_to_ei(ixI^L,ixO^L,w,x)
1335 integer, intent(in) :: ixi^l, ixo^l
1336 double precision, intent(inout) :: w(ixi^s, nw)
1337 double precision, intent(in) :: x(ixi^s, 1:ndim)
1338
1339 ! Calculate ei = e - ek
1340 w(ixo^s,e_)=w(ixo^s,e_)-half*(^c&w(ixo^s,m^c_)**2+)/w(ixo^s,rho_)
1341
1342 end subroutine hd_e_to_ei
1343
1344 !> Wrapper: e_to_ei + cache log10(nH) in wextra for LTE TC fast path.
1345 !> During STS substeps density is invariant, so log10(nH) is computed once
1346 !> per STS cycle (in sts_before_first_cycle hook) and reused across all substeps.
1347 subroutine hd_e_to_ei_and_cache_log_nh(ixI^L,ixO^L,w,x)
1349 integer, intent(in) :: ixi^l, ixo^l
1350 double precision, intent(inout) :: w(ixi^s, nw)
1351 double precision, intent(in) :: x(ixi^s, 1:ndim)
1352
1353 call hd_e_to_ei(ixi^l,ixo^l,w,x)
1354 block%wextra(ixo^s, iw_log_nh) = dlog10(w(ixo^s, rho_) / eos%nH2rhoFactor)
1355 end subroutine hd_e_to_ei_and_cache_log_nh
1356
1357 !> Calculate internal energy from total energy (non-modifying version)
1358 function hd_get_ei(w, ixI^L, ixO^L) result(ei)
1360 integer, intent(in) :: ixi^l, ixo^l
1361 double precision, intent(in) :: w(ixi^s, nw)
1362 double precision :: ei(ixo^s)
1363
1364 ! ei = e_total - e_kinetic
1365 ei(ixo^s) = w(ixo^s,e_) - half*(^c&w(ixo^s,m^c_)**2+)/w(ixo^s,rho_)
1366 end function hd_get_ei
1367
1368 !> Calculate v_i = m_i / rho within ixO^L
1369 subroutine hd_get_v_idim(w, x, ixI^L, ixO^L, idim, v)
1371 integer, intent(in) :: ixi^l, ixo^l, idim
1372 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s, 1:ndim)
1373 double precision, intent(out) :: v(ixi^s)
1374
1375 v(ixo^s) = w(ixo^s, mom(idim)) / w(ixo^s, rho_)
1376 end subroutine hd_get_v_idim
1377
1378 !> Calculate velocity vector v_i = m_i / rho within ixO^L
1379 subroutine hd_get_v(w,x,ixI^L,ixO^L,v)
1381
1382 integer, intent(in) :: ixi^l, ixo^l
1383 double precision, intent(in) :: w(ixi^s,nw), x(ixi^s,1:^nd)
1384 double precision, intent(out) :: v(ixi^s,1:ndir)
1385
1386 integer :: idir
1387
1388 do idir=1,ndir
1389 v(ixo^s,idir) = w(ixo^s, mom(idir)) / w(ixo^s, rho_)
1390 end do
1391
1392 end subroutine hd_get_v
1393
1394 !> Calculate cmax_idim = csound + abs(v_idim) within ixO^L
1395 subroutine hd_get_cmax(w, x, ixI^L, ixO^L, idim, cmax)
1397 use mod_dust, only: dust_get_cmax_prim
1399
1400 integer, intent(in) :: ixi^l, ixo^l, idim
1401 ! w in primitive form
1402 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s, 1:ndim)
1403 double precision, intent(inout) :: cmax(ixi^s)
1404 double precision :: csound2(ixi^s)
1405
1406 if(hd_energy) then
1407 call eos%get_csound2(w, x, ixi^l, ixo^l, csound2)
1408 cmax(ixo^s)=dabs(w(ixo^s,mom(idim)))+dsqrt(csound2(ixo^s))
1409 else
1410 if (.not. associated(usr_set_pthermal)) then
1411 cmax(ixo^s) = hd_adiab * w(ixo^s, rho_)**eos%gamma
1412 else
1413 call usr_set_pthermal(w,x,ixi^l,ixo^l,cmax)
1414 end if
1415 cmax(ixo^s)=dabs(w(ixo^s,mom(idim)))+dsqrt(eos%gamma*cmax(ixo^s)/w(ixo^s,rho_))
1416 end if
1417
1418 if (hd_dust) then
1419 call dust_get_cmax_prim(w, x, ixi^l, ixo^l, idim, cmax)
1420 end if
1421 end subroutine hd_get_cmax
1422
1423 !> get adaptive cutoff temperature for TRAC (Johnston 2019 ApJL, 873, L22)
1424 subroutine hd_get_tcutoff(ixI^L,ixO^L,w,x,tco_local,Tmax_local)
1428 use mod_eos, only: eos
1429 use mod_geometry, only: gradient
1430 integer, intent(in) :: ixi^l,ixo^l
1431 double precision, intent(in) :: x(ixi^s,1:ndim)
1432 ! in primitive form
1433 double precision, intent(inout) :: w(ixi^s,1:nw)
1434 double precision, intent(out) :: tco_local, tmax_local
1435
1436 double precision, parameter :: trac_delta=0.25d0
1437 double precision :: tmp1(ixi^s),te(ixi^s),lts(ixi^s), r(ixi^s)
1438 double precision :: ltrc,ltrp
1439 integer :: jxo^l,hxo^l
1440 integer :: jxp^l,hxp^l,ixp^l
1441 logical :: lrlt(ixi^s)
1442 ! Johnston 2021 type 7 variables
1443 double precision :: dtdx, l_t, a_coeff, l1, cooling, net_cool
1444 double precision :: kappa_par, disc, kappa_trac, kappa_eff, tcoff_eff
1445 double precision :: dx_over_delta, v_abs, v_n, gnorm, dl_eff
1446 double precision :: q_heat(ixi^s), ne(ixi^s), nh_arr(ixi^s)
1447 double precision :: gradtd(ixi^s,1:ndim), nhat(1:ndim)
1448 integer :: ix^d, idims
1449
1450 call eos%get_Rfactor(w,x,ixi^l,ixi^l,r)
1451 te(ixi^s)=w(ixi^s,p_)/(r(ixi^s)*w(ixi^s,rho_))
1452
1453 if (eos%eos_type == 'LTE') then
1454 te(ixi^s) = w(ixi^s, te_)
1455 endif
1456
1457 tco_local=zero
1458 tmax_local=maxval(te(ixo^s))
1459 select case(hd_trac_type)
1460 {^ifoned
1461 case(0)
1463 ! below trac_T_floor conductivity, losses and heating are left unmodified (Tcoff = Te)
1464 where(te(ixi^s) < tc_fl%trac_T_floor) block%wextra(ixi^s,tcoff_)=te(ixi^s)
1465 case(1)
1466 hxo^l=ixo^l-1;
1467 jxo^l=ixo^l+1;
1468 lts(ixo^s)=0.5d0*dabs(te(jxo^s)-te(hxo^s))/te(ixo^s)
1469 lrlt=.false.
1470 where(lts(ixo^s) > trac_delta)
1471 lrlt(ixo^s)=.true.
1472 end where
1473 if(any(lrlt(ixo^s))) then
1474 tco_local=maxval(te(ixo^s), mask=lrlt(ixo^s))
1475 end if
1476 case(2)
1477 !> iijima et al. 2021, LTRAC method
1478 ltrc=1.5d0
1479 ltrp=2.5d0
1480 ixp^l=ixo^l^ladd1;
1481 hxo^l=ixo^l-1;
1482 jxo^l=ixo^l+1;
1483 hxp^l=ixp^l-1;
1484 jxp^l=ixp^l+1;
1485 lts(ixp^s)=0.5d0*abs(te(jxp^s)-te(hxp^s))/te(ixp^s)
1486 lts(ixp^s)=max(one, (exp(lts(ixp^s))/ltrc)**ltrp)
1487 ! Smoothed Tcoff for interior cells
1488 lts(ixo^s)=0.25d0*(lts(jxo^s)+two*lts(ixo^s)+lts(hxo^s))
1489 block%wextra(ixo^s,tcoff_)=te(ixo^s)*lts(ixo^s)**0.4d0
1490 ! Fill one ghost cell on each side with unsmoothed Tcoff.
1491 ! The thermal conduction routine reads Tcoff at ixO +/- 1;
1492 ! wextra ghost cells are NOT halo-communicated, so without this
1493 ! the conduction sees stale values and breaks symmetry.
1494 block%wextra(ixomin1-1,tcoff_)=te(ixomin1-1)*lts(ixomin1-1)**0.4d0
1495 block%wextra(ixomax1+1,tcoff_)=te(ixomax1+1)*lts(ixomax1+1)**0.4d0
1496 }
1497 case(7)
1498 {^ifoned
1499 !> Johnston et al. 2021 local TRAC (A&A 654, A2) -- 1D
1500 !> Per-cell kappa_TRAC from steady-state energy balance
1501
1502 ! Tcoff also on the first ghost layer, so both blocks at a face see the same conductivity
1503 ixp^l=ixo^l^ladd1;
1504
1505 ! Get background heating Q (once per block)
1506 call usr_get_heating(q_heat, ixi^l, ixp^l, w, x)
1507
1508 ! Get n_e and n_H for cooling rate: Q = n_e * n_H * Lambda(T)
1509 ! For FI: n_e = n_H * neOnH_FI. For LTE: n_e from Saha EoS.
1510 call eos%get_ne_nH(ixi^l, ixp^l, w, x, ne, nh_arr)
1511
1512 hxo^l=ixo^l-1;
1513 jxo^l=ixo^l+1;
1514 dx_over_delta = dxlevel(1) / hd_trac_delta
1515
1516 do ix1=ixpmin1,ixpmax1
1517 ! Temperature gradient: L_T = T / abs(dT/dx)
1518 dtdx = abs(te(ix1+1) - te(ix1-1)) / (2.d0 * dxlevel(1))
1519 ! Uniform temperature, or below trac_T_floor: no broadening (Tcoff = Te)
1520 if(dtdx < smalldouble .or. te(ix1) < tc_fl%trac_T_floor) then
1521 block%wextra(ix1,tcoff_) = te(ix1)
1522 cycle
1523 end if
1524 l_t = te(ix1) / dtdx
1525
1526 ! Eq.11 mass flux coefficient a = (5/2) p v / T, which is (5/2) kB J for
1527 ! an ideal gas since p = n kB T.
1528 v_abs = abs(w(ix1,m1_))
1529 a_coeff = 2.5d0 * w(ix1,p_) * v_abs / te(ix1)
1530
1531 ! Radiative cooling: n_e * n_H * Lambda(T), guarded to the table range (positive in-range
1532 ! test so a non-finite Te falls to zero rather than indexing findL with int(NaN)).
1533 if(te(ix1) > rc_fl%tcoolmin .and. te(ix1) < rc_fl%tcoolmax) then
1534 call findl(te(ix1), l1, rc_fl); cooling = ne(ix1) * nh_arr(ix1) * l1
1535 else if(te(ix1) >= rc_fl%tcoolmax) then
1536 call calc_l_extended(te(ix1), l1, rc_fl); cooling = ne(ix1) * nh_arr(ix1) * l1
1537 else
1538 cooling = 0.d0
1539 end if
1540
1541 ! Net cooling - heating
1542 net_cool = abs(cooling - q_heat(ix1))
1543
1544 ! Spitzer conductivity at this T
1545 kappa_par = tc_fl%tc_k_para * te(ix1)**2.5d0
1546
1547 ! Johnston Eq. 11 discriminant
1548 disc = a_coeff**2 + 4.d0 * tc_fl%tc_k_para * te(ix1)**1.5d0 * net_cool
1549
1550 if(l_t <= 2.d0 * dx_over_delta) then
1551 ! Under-resolved: full TRAC formula (Eq. 11)
1552 kappa_trac = (a_coeff + dsqrt(disc)) / (2.d0 / dx_over_delta)
1553 else
1554 ! Over-resolved: limiter only (Eq. 12, drops mass flux)
1555 kappa_trac = dsqrt(4.d0 * tc_fl%tc_k_para * te(ix1)**1.5d0 * net_cool) &
1556 / (2.d0 / dx_over_delta)
1557 end if
1558
1559 ! kappa' = max(kappa_TRAC, kappa_par)
1560 kappa_eff = max(kappa_trac, kappa_par)
1561
1562 ! Convert to effective Tcoff: Tcoff = (kappa'/kappa_0)**(2/5)
1563 tcoff_eff = (kappa_eff / tc_fl%tc_k_para)**0.4d0
1564
1565 ! Store max(Te, Tcoff_eff) -- Tcoff must be >= Te
1566 block%wextra(ix1,tcoff_) = max(te(ix1), tcoff_eff)
1567 end do
1568 }
1569 {^nooned
1570 ! Johnston et al. 2021 local TRAC, A and A 654 A2 -- multi-D, per-cell.
1571 ! Isotropic Spitzer conduction, so the TR normal is the unit temperature
1572 ! gradient direction nhat, and the broadening is applied along it.
1573 call usr_get_heating(q_heat, ixi^l, ixo^l, w, x)
1574 call eos%get_ne_nH(ixi^l, ixo^l, w, x, ne, nh_arr)
1575 ! default (incl. non-communicated ghost layer): Tcoff=Te -> no broadening; interior overwritten
1576 block%wextra(ixi^s,tcoff_) = te(ixi^s)
1577 do idims=1,ndim
1578 call gradient(te,ixi^l,ixo^l,idims,gradtd(ixi^s,idims))
1579 end do
1580 {do ix^db=ixomin^db,ixomax^db\}
1581 gnorm = dsqrt(^d&gradtd({ix^d},^d)**2+ )
1582 if(gnorm < smalldouble .or. te(ix^d) < tc_fl%trac_T_floor) then
1583 ! locally uniform T, or below trac_T_floor: no broadening
1584 block%wextra(ix^d,tcoff_) = te(ix^d)
1585 else
1586 ^d&nhat(^d)=gradtd({ix^d},^d)/gnorm\
1587 ! grid spacing measured along the gradient direction
1588 dl_eff = 1.d0/dsqrt(^d&(nhat(^d)/block%ds({ix^d},^d))**2+ )
1589 l_t = te(ix^d)/gnorm
1590 ! Eq.16 enthalpy (mass) flux along the gradient
1591 v_n = ^d&w({ix^d},mom(^d))*nhat(^d)+
1592 a_coeff = 2.5d0*w(ix^d,p_)*dabs(v_n)/te(ix^d)
1593 ! optically-thin cooling n_e n_H Lambda(T), guarded to the table range with a positive
1594 ! in-range test so a non-finite Te (NaN from an under-resolved collapse) falls to zero
1595 ! rather than indexing findL with int(NaN) -> out of bounds.
1596 if(te(ix^d) > rc_fl%tcoolmin .and. te(ix^d) < rc_fl%tcoolmax) then
1597 call findl(te(ix^d),l1,rc_fl); cooling = l1*ne(ix^d)*nh_arr(ix^d)
1598 else if(te(ix^d) >= rc_fl%tcoolmax) then
1599 call calc_l_extended(te(ix^d),l1,rc_fl); cooling = l1*ne(ix^d)*nh_arr(ix^d)
1600 else
1601 cooling = 0.d0
1602 end if
1603 net_cool = dabs(cooling-q_heat(ix^d))
1604 kappa_par = tc_fl%tc_k_para*te(ix^d)**2.5d0
1605 disc = a_coeff**2 + 4.d0*tc_fl%tc_k_para*te(ix^d)**1.5d0*net_cool
1606 dx_over_delta = dl_eff/hd_trac_delta
1607 if(l_t <= 2.d0*dx_over_delta) then
1608 kappa_trac = (a_coeff+dsqrt(disc))/(2.d0/dx_over_delta)
1609 else
1610 kappa_trac = dsqrt(4.d0*tc_fl%tc_k_para*te(ix^d)**1.5d0*net_cool)/(2.d0/dx_over_delta)
1611 end if
1612 kappa_eff = max(kappa_trac,kappa_par)
1613 tcoff_eff = (kappa_eff/tc_fl%tc_k_para)**0.4d0
1614 block%wextra(ix^d,tcoff_) = max(te(ix^d),tcoff_eff)
1615 end if
1616 {end do\}
1617 }
1618 case default
1619 call mpistop("hd_trac_type not allowed")
1620 end select
1621 end subroutine hd_get_tcutoff
1622
1623 !> Calculate cmax_idim = csound + abs(v_idim) within ixO^L
1624 subroutine hd_get_cbounds(wLC, wRC, wLp, wRp, x, ixI^L, ixO^L, idim,Hspeed,cmax, cmin)
1626 use mod_dust, only: dust_get_cmax
1627 use mod_fld, only: fld_bound_diff
1628 use mod_variables
1629
1630 integer, intent(in) :: ixi^l, ixo^l, idim
1631 ! conservative left and right status
1632 double precision, intent(in) :: wlc(ixi^s, nw), wrc(ixi^s, nw)
1633 ! primitive left and right status
1634 double precision, intent(in) :: wlp(ixi^s, nw), wrp(ixi^s, nw)
1635 double precision, intent(in) :: x(ixi^s, 1:ndim)
1636 double precision, intent(inout) :: cmax(ixi^s,1:number_species)
1637 double precision, intent(inout), optional :: cmin(ixi^s,1:number_species)
1638 double precision, intent(in) :: hspeed(ixi^s,1:number_species)
1639
1640 double precision :: wmean(ixi^s,nw)
1641 double precision, dimension(ixI^S) :: umean, dmean, csoundl, csoundr, tmp1,tmp2,tmp3
1642 integer :: ix^d
1643
1644 select case(boundspeed)
1645 case (1)
1646 ! This implements formula (10.52) from "Riemann Solvers and Numerical
1647 ! Methods for Fluid Dynamics" by Toro.
1648
1649 tmp1(ixo^s)=dsqrt(wlp(ixo^s,rho_))
1650 tmp2(ixo^s)=dsqrt(wrp(ixo^s,rho_))
1651 tmp3(ixo^s)=1.d0/(tmp1(ixo^s)+tmp2(ixo^s))
1652 umean(ixo^s)=(wlp(ixo^s,mom(idim))*tmp1(ixo^s)+wrp(ixo^s,mom(idim))*tmp2(ixo^s))*tmp3(ixo^s)
1653
1654 if(hd_energy) then
1655 call eos%get_csound2(wlp, x, ixi^l, ixo^l, csoundl)
1656 call eos%get_csound2(wrp, x, ixi^l, ixo^l, csoundr)
1658 csoundl(ixo^s)=csoundl(ixo^s)+(4.0d0/9.0d0)*wlp(ixo^s,r_e)/wlp(ixo^s,rho_)
1659 csoundr(ixo^s)=csoundr(ixo^s)+(4.0d0/9.0d0)*wrp(ixo^s,r_e)/wrp(ixo^s,rho_)
1660 endif
1661 else
1662 ! note usage of conservatives here
1663 call hd_get_csound2(wlc,x,ixi^l,ixo^l,csoundl)
1664 call hd_get_csound2(wrc,x,ixi^l,ixo^l,csoundr)
1665 end if
1666
1667 dmean(ixo^s) = (tmp1(ixo^s)*csoundl(ixo^s)+tmp2(ixo^s)*csoundr(ixo^s)) * &
1668 tmp3(ixo^s) + 0.5d0*tmp1(ixo^s)*tmp2(ixo^s)*tmp3(ixo^s)**2 * &
1669 (wrp(ixo^s,mom(idim))-wlp(ixo^s,mom(idim)))**2
1670
1671 dmean(ixo^s)=dsqrt(dmean(ixo^s))
1672 if(present(cmin)) then
1673 cmin(ixo^s,1)=umean(ixo^s)-dmean(ixo^s)
1674 cmax(ixo^s,1)=umean(ixo^s)+dmean(ixo^s)
1675 if(h_correction) then
1676 {do ix^db=ixomin^db,ixomax^db\}
1677 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
1678 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
1679 {end do\}
1680 end if
1681 else
1682 cmax(ixo^s,1)=dabs(umean(ixo^s))+dmean(ixo^s)
1683 end if
1684
1685 if (hd_dust) then
1686 wmean(ixo^s,1:nwflux)=0.5d0*(wlc(ixo^s,1:nwflux)+wrc(ixo^s,1:nwflux))
1687 call dust_get_cmax(wmean, x, ixi^l, ixo^l, idim, cmax, cmin)
1688 end if
1689
1690 case (2)
1691 !if(hd_energy) then
1692 ! ! note usage of primitives here
1693 ! wmean(ixO^S,1:nwflux)=0.5d0*(wLp(ixO^S,1:nwflux)+wRp(ixO^S,1:nwflux))
1694 ! tmp1(ixO^S)=wmean(ixO^S,mom(idim))
1695 ! csoundR(ixO^S)=hd_gamma*wmean(ixO^S,p_)/wmean(ixO^S,rho_)
1696 !else
1697 ! note usage of conservatives here
1698 wmean(ixo^s,1:nwflux)=0.5d0*(wlc(ixo^s,1:nwflux)+wrc(ixo^s,1:nwflux))
1699 tmp1(ixo^s)=wmean(ixo^s,mom(idim))/wmean(ixo^s,rho_)
1700 call hd_get_csound2(wmean,x,ixi^l,ixo^l,csoundr)
1701 if(hd_radiation_fld.and.fld_bound_diff)csoundr(ixo^s)=csoundr(ixo^s)+(4.0d0/9.0d0)*wmean(ixo^s,r_e)/wmean(ixo^s,rho_)
1702 !endif
1703 csoundr(ixo^s) = dsqrt(csoundr(ixo^s))
1704
1705 if(present(cmin)) then
1706 cmax(ixo^s,1)=max(tmp1(ixo^s)+csoundr(ixo^s),zero)
1707 cmin(ixo^s,1)=min(tmp1(ixo^s)-csoundr(ixo^s),zero)
1708 if(h_correction) then
1709 {do ix^db=ixomin^db,ixomax^db\}
1710 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
1711 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
1712 {end do\}
1713 end if
1714 else
1715 cmax(ixo^s,1)=dabs(tmp1(ixo^s))+csoundr(ixo^s)
1716 end if
1717
1718 if (hd_dust) then
1719 call dust_get_cmax(wmean, x, ixi^l, ixo^l, idim, cmax, cmin)
1720 end if
1721 case (3)
1722 ! Miyoshi 2005 JCP 208, 315 equation (67)
1723 if(hd_energy) then
1724 call eos%get_csound2(wlp, x, ixi^l, ixo^l, csoundl)
1725 call eos%get_csound2(wrp, x, ixi^l, ixo^l, csoundr)
1726 if(hd_radiation_fld.and.fld_bound_diff)then
1727 csoundl(ixo^s)=csoundl(ixo^s)+(4.0d0/9.0d0)*wlp(ixo^s,r_e)/wlp(ixo^s,rho_)
1728 csoundr(ixo^s)=csoundr(ixo^s)+(4.0d0/9.0d0)*wrp(ixo^s,r_e)/wrp(ixo^s,rho_)
1729 endif
1730 else
1731 ! note usage of conservatives here
1732 call hd_get_csound2(wlc,x,ixi^l,ixo^l,csoundl)
1733 call hd_get_csound2(wrc,x,ixi^l,ixo^l,csoundr)
1734 end if
1735 csoundl(ixo^s)=max(dsqrt(csoundl(ixo^s)),dsqrt(csoundr(ixo^s)))
1736 if(present(cmin)) then
1737 cmin(ixo^s,1)=min(wlp(ixo^s,mom(idim)),wrp(ixo^s,mom(idim)))-csoundl(ixo^s)
1738 cmax(ixo^s,1)=max(wlp(ixo^s,mom(idim)),wrp(ixo^s,mom(idim)))+csoundl(ixo^s)
1739 if(h_correction) then
1740 {do ix^db=ixomin^db,ixomax^db\}
1741 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
1742 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
1743 {end do\}
1744 end if
1745 else
1746 cmax(ixo^s,1)=max(wlp(ixo^s,mom(idim)),wrp(ixo^s,mom(idim)))+csoundl(ixo^s)
1747 end if
1748 if (hd_dust) then
1749 wmean(ixo^s,1:nwflux)=0.5d0*(wlc(ixo^s,1:nwflux)+wrc(ixo^s,1:nwflux))
1750 call dust_get_cmax(wmean, x, ixi^l, ixo^l, idim, cmax, cmin)
1751 end if
1752 case (4)
1753 !> PVRS pressure-based wave speed estimate (Toro 1999, Section 10.5.2)
1754 !> Recommended by Coleman 2020 for general EoS given limitations of constant gamma approximation.
1755 !> Estimates star pressure from linearised Riemann problem, then uses
1756 !> RH to detect shock vs rarefaction on each side.
1757
1758 if(hd_energy) then
1759 call eos%get_csound2(wlp, x, ixi^l, ixo^l, csoundl)
1760 call eos%get_csound2(wrp, x, ixi^l, ixo^l, csoundr)
1761 if(hd_radiation_fld.and.fld_bound_diff)then
1762 csoundl(ixo^s)=csoundl(ixo^s)+(4.0d0/9.0d0)*wlp(ixo^s,r_e)/wlp(ixo^s,rho_)
1763 csoundr(ixo^s)=csoundr(ixo^s)+(4.0d0/9.0d0)*wrp(ixo^s,r_e)/wrp(ixo^s,rho_)
1764 endif
1765 else
1766 call hd_get_csound2(wlc,x,ixi^l,ixo^l,csoundl)
1767 call hd_get_csound2(wrc,x,ixi^l,ixo^l,csoundr)
1768 end if
1769 csoundl(ixo^s) = dsqrt(csoundl(ixo^s))
1770 csoundr(ixo^s) = dsqrt(csoundr(ixo^s))
1771 if(present(cmin)) then
1772 {do ix^db=ixomin^db,ixomax^db\}
1773 !> PVRS star pressure estimate (Toro Eq. 9.28)
1774 !> cup = rho_bar * a_bar
1775 tmp1(ix^d) = 0.25d0*(wlp(ix^d,rho_)+wrp(ix^d,rho_)) &
1776 *(csoundl(ix^d)+csoundr(ix^d))
1777 !> p* = max(0, p_avg + 0.5*(u_L - u_R)*cup)
1778 tmp2(ix^d) = max(zero, 0.5d0*(wlp(ix^d,e_)+wrp(ix^d,e_)) &
1779 + 0.5d0*(wlp(ix^d,mom(idim))-wrp(ix^d,mom(idim))) &
1780 *tmp1(ix^d))
1781 !> Left wave speed: S_L = u_L - a_L * q_L
1782 if(tmp2(ix^d) > wlp(ix^d,e_) .and. wlp(ix^d,e_) > zero) then
1783 !> Left shock: q_L from R-H with local Gamma1 = a^2*rho/p
1784 tmp3(ix^d) = csoundl(ix^d)**2*wlp(ix^d,rho_)/wlp(ix^d,e_)
1785 dmean(ix^d) = dsqrt(1.0d0 + (tmp3(ix^d)+1.0d0) &
1786 /(2.0d0*tmp3(ix^d)) &
1787 *(tmp2(ix^d)/wlp(ix^d,e_) - 1.0d0))
1788 else
1789 !> Left rarefaction
1790 dmean(ix^d) = 1.0d0
1791 end if
1792 cmin(ix^d,1) = wlp(ix^d,mom(idim)) - csoundl(ix^d)*dmean(ix^d)
1793 !> Right wave speed: S_R = u_R + a_R * q_R
1794 if(tmp2(ix^d) > wrp(ix^d,e_) .and. wrp(ix^d,e_) > zero) then
1795 !> Right shock: q_R from R-H with local Gamma1
1796 tmp3(ix^d) = csoundr(ix^d)**2*wrp(ix^d,rho_)/wrp(ix^d,e_)
1797 dmean(ix^d) = dsqrt(1.0d0 + (tmp3(ix^d)+1.0d0) &
1798 /(2.0d0*tmp3(ix^d)) &
1799 *(tmp2(ix^d)/wrp(ix^d,e_) - 1.0d0))
1800 else
1801 !> Right rarefaction
1802 dmean(ix^d) = 1.0d0
1803 end if
1804 cmax(ix^d,1) = wrp(ix^d,mom(idim)) + csoundr(ix^d)*dmean(ix^d)
1805 {end do\}
1806 if(h_correction) then
1807 {do ix^db=ixomin^db,ixomax^db\}
1808 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
1809 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
1810 {end do\}
1811 end if
1812 else
1813 {do ix^db=ixomin^db,ixomax^db\}
1814 tmp1(ix^d) = 0.25d0*(wlp(ix^d,rho_)+wrp(ix^d,rho_)) &
1815 *(csoundl(ix^d)+csoundr(ix^d))
1816 tmp2(ix^d) = max(zero, 0.5d0*(wlp(ix^d,e_)+wrp(ix^d,e_)) &
1817 + 0.5d0*(wlp(ix^d,mom(idim))-wrp(ix^d,mom(idim))) &
1818 *tmp1(ix^d))
1819 if(tmp2(ix^d) > wlp(ix^d,e_) .and. wlp(ix^d,e_) > zero) then
1820 tmp3(ix^d) = csoundl(ix^d)**2*wlp(ix^d,rho_)/wlp(ix^d,e_)
1821 dmean(ix^d) = dsqrt(1.0d0 + (tmp3(ix^d)+1.0d0) &
1822 /(2.0d0*tmp3(ix^d)) &
1823 *(tmp2(ix^d)/wlp(ix^d,e_) - 1.0d0))
1824 else
1825 dmean(ix^d) = 1.0d0
1826 end if
1827 umean(ix^d) = dabs(wlp(ix^d,mom(idim)) &
1828 - csoundl(ix^d)*dmean(ix^d))
1829 if(tmp2(ix^d) > wrp(ix^d,e_) .and. wrp(ix^d,e_) > zero) then
1830 tmp3(ix^d) = csoundr(ix^d)**2*wrp(ix^d,rho_)/wrp(ix^d,e_)
1831 dmean(ix^d) = dsqrt(1.0d0 + (tmp3(ix^d)+1.0d0) &
1832 /(2.0d0*tmp3(ix^d)) &
1833 *(tmp2(ix^d)/wrp(ix^d,e_) - 1.0d0))
1834 else
1835 dmean(ix^d) = 1.0d0
1836 end if
1837 cmax(ix^d,1) = max(umean(ix^d), &
1838 wrp(ix^d,mom(idim))+csoundr(ix^d)*dmean(ix^d))
1839 {end do\}
1840 end if
1841 if (hd_dust) then
1842 wmean(ixo^s,1:nwflux)=0.5d0*(wlc(ixo^s,1:nwflux)+wrc(ixo^s,1:nwflux))
1843 call dust_get_cmax(wmean, x, ixi^l, ixo^l, idim, cmax, cmin)
1844 end if
1845 end select
1846
1847 end subroutine hd_get_cbounds
1848
1849 !> Calculate the square of the thermal sound speed csound2 within ixO^L.
1850 !> For conserved w: extracts pthermal first, then applies Gamma_1.
1851 !> Squared sound speed of a conserved face state, Gamma_1 p/rho. Face states carry no Ne/Te, so
1852 !> under LTE the pressure comes from the internal energy through lte_p_from_eint, and Gamma_1
1853 !> from the pressure-indexed table, since p is continuous at contact discontinuities
1854 subroutine hd_get_csound2(w,x,ixI^L,ixO^L,csound2)
1859 integer, intent(in) :: ixi^l, ixo^l
1860 double precision, intent(in) :: w(ixi^s,nw)
1861 double precision, intent(in) :: x(ixi^s,1:ndim)
1862 double precision, intent(out) :: csound2(ixi^s)
1863 double precision :: pthermal(ixi^s)
1864 double precision :: inv_rho, nh_val, pres, g1
1865 logical :: tab_g1
1866 integer :: ix^d
1867
1868 ! called directly by the wave speeds, so timed here; the pressure inside counts once
1869 call eos_timer_start()
1870 if (hd_energy .and. eos%type_id == eos_type_lte) then
1871 tab_g1 = eos%ionE .and. eos%gamma1_method /= 'constant'
1872 {do ix^db=ixomin^db,ixomax^db\}
1873 inv_rho = 1.0d0/w(ix^d,rho_)
1874 nh_val = w(ix^d,rho_)/eos%nH2rhoFactor
1875 pres = lte_p_from_eint(w(ix^d,e_) - half*(^c&w(ix^d,m^c_)**2+)*inv_rho, &
1876 nh_val, dlog10(nh_val), inv_rho)
1877 g1 = eos%gamma
1878 if (tab_g1) g1 = lte_gamma1_from_p(pres, w(ix^d,rho_))
1879 csound2(ix^d) = g1*pres*inv_rho
1880 {end do\}
1881 else
1882 call eos%get_thermal_pressure(w, x, ixi^l, ixo^l, pthermal)
1883 csound2(ixo^s) = eos%gamma * pthermal(ixo^s) / w(ixo^s,rho_)
1884 end if
1885 call eos_timer_stop(timeeos_csound)
1886
1887 end subroutine hd_get_csound2
1888
1889
1890 !> Calculate modified squared sound speed for FLD
1891 !> NOTE: only for diagnostic purposes, unused subroutine
1892 subroutine hd_get_csrad2(w,x,ixI^L,ixO^L,csound)
1894
1895 integer, intent(in) :: ixi^l, ixo^l
1896 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
1897 double precision, intent(out):: csound(ixi^s)
1898
1899 double precision :: wprim(ixi^s, nw)
1900
1901 wprim(ixi^s,1:nw)=w(ixi^s,1:nw)
1902 call eos%to_primitive(ixi^l,ixo^l,wprim,x)
1903 call hd_get_csrad2_prim(wprim,x,ixi^l,ixo^l,csound)
1904
1905 end subroutine hd_get_csrad2
1906
1907 !> Calculate modified squared sound speed for FLD
1908 !> NOTE: w is primitive on entry here!
1909 !> NOTE: used in FLD module as phys_get_csrad2
1910 subroutine hd_get_csrad2_prim(w,x,ixI^L,ixO^L,csound)
1912
1913 integer, intent(in) :: ixi^l, ixo^l
1914 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
1915 double precision, intent(out):: csound(ixi^s)
1916
1917 integer :: ix^d
1918 double precision :: inv_rho
1919 double precision :: prad_tensor(ixi^s, 1:ndim, 1:ndim)
1920 double precision :: prad_max(ixi^s)
1921 integer :: idim
1922
1923 if(hd_fld_pradtensor) then
1924 call hd_get_pradiation_from_prim(w, x, ixi^l, ixo^l, prad_tensor)
1925 else
1926 prad_tensor=zero
1927 do idim=1,ndim
1928 prad_tensor(ixo^s,idim,idim)=w(ixo^s,r_e)/3.0d0
1929 enddo
1930 endif
1931
1932 {do ix^db=ixomin^db,ixomax^db \}
1933 inv_rho=1.d0/w(ix^d,rho_)
1934 prad_max(ix^d) = (4.0d0/3.0d0)*maxval(prad_tensor(ix^d,:,:))
1935 csound(ix^d)=(eos%gamma*w(ix^d,p_)+prad_max(ix^d))*inv_rho
1936 {end do\}
1937
1938 if(minval(csound(ixo^s))<smalldouble)then
1939 print *,'issue with squared speed and rad pressure'
1940 print *,minval(csound(ixo^s))
1941 print *,minval(prad_max(ixo^s))
1942 call mpistop("negative squared speed in get_csrad2 for dt")
1943 endif
1944
1945 end subroutine hd_get_csrad2_prim
1946
1947 !> Calculate radiation pressure within ixO^L
1948 !> NOTE: w is primitive on entry here!
1949 !> NOTE: used in FLD module as it is called from phys_get_csrad2
1950 subroutine hd_get_pradiation_from_prim(w, x, ixI^L, ixO^L, prad)
1952 use mod_fld
1953 integer, intent(in) :: ixi^l, ixo^l
1954 double precision, intent(in) :: w(ixi^s, 1:nw)
1955 double precision, intent(in) :: x(ixi^s, 1:ndim)
1956 double precision, intent(out):: prad(ixi^s, 1:ndim, 1:ndim)
1957
1958 call fld_get_radpress(w, x, ixi^l, ixo^l, prad, fld_fl)
1959
1960 end subroutine hd_get_pradiation_from_prim
1961
1962 !> calculates the sum of the gas pressure and max Prad tensor element
1963 !> NOTE: only for diagnostic purposes, unused subroutine
1964 subroutine hd_get_pthermal_plus_pradiation(w, x, ixI^L, ixO^L, pth_plus_prad)
1966 integer, intent(in) :: ixi^l, ixo^l
1967 double precision, intent(in) :: w(ixi^s, 1:nw)
1968 double precision, intent(in) :: x(ixi^s, 1:ndim)
1969 double precision, intent(out):: pth_plus_prad(ixi^s)
1970
1971 double precision :: wprim(ixi^s, 1:nw)
1972 double precision :: prad_tensor(ixi^s, 1:ndim, 1:ndim)
1973 double precision :: prad_max(ixi^s)
1974 integer :: ix^d
1975
1976 wprim(ixi^s,1:nw)=w(ixi^s,1:nw)
1977 call eos%to_primitive(ixi^l,ixo^l,wprim,x)
1978 call hd_get_pradiation_from_prim(wprim, x, ixi^l, ixo^l, prad_tensor)
1979 {do ix^d = ixomin^d,ixomax^d\}
1980 prad_max(ix^d) = maxval(prad_tensor(ix^d,:,:))
1981 {enddo\}
1982 pth_plus_prad(ixo^s) = wprim(ixo^s,p_) + prad_max(ixo^s)
1983 end subroutine hd_get_pthermal_plus_pradiation
1984
1985 !> Calculates radiation temperature
1986 subroutine hd_get_trad(w, x, ixI^L, ixO^L, trad)
1988 use mod_constants
1989
1990 integer, intent(in) :: ixi^l, ixo^l
1991 double precision, intent(in) :: w(ixi^s, 1:nw)
1992 double precision, intent(in) :: x(ixi^s, 1:ndim)
1993 double precision, intent(out):: trad(ixi^s)
1994
1995 trad(ixi^s) = (w(ixi^s,r_e)/arad_norm)**(1.d0/4.d0)
1996
1997 end subroutine hd_get_trad
1998
1999 !> Calculate temperature=p/rho when in e_ the total energy is stored
2000 subroutine hd_get_temperature_from_etot(w, x, ixI^L, ixO^L, res)
2002 integer, intent(in) :: ixi^l, ixo^l
2003 double precision, intent(in) :: w(ixi^s, 1:nw)
2004 double precision, intent(in) :: x(ixi^s, 1:ndim)
2005 double precision, intent(out):: res(ixi^s)
2006
2007 double precision :: r(ixi^s)
2008
2009 call eos%get_Rfactor(w,x,ixi^l,ixo^l,r)
2010 call eos%get_thermal_pressure(w, x, ixi^l, ixo^l, res)
2011 res(ixo^s)=res(ixo^s)/(r(ixo^s)*w(ixo^s,rho_))
2012 end subroutine hd_get_temperature_from_etot
2013
2014 !> Calculate temperature=p/rho when in e_ the internal energy is stored
2015 subroutine hd_get_temperature_from_eint(w, x, ixI^L, ixO^L, res)
2017 integer, intent(in) :: ixi^l, ixo^l
2018 double precision, intent(in) :: w(ixi^s, 1:nw)
2019 double precision, intent(in) :: x(ixi^s, 1:ndim)
2020 double precision, intent(out):: res(ixi^s)
2021
2022 double precision :: r(ixi^s)
2023
2024 call eos%get_Rfactor(w,x,ixi^l,ixo^l,r)
2025 res(ixo^s) = (eos%gamma - 1.0d0) * w(ixo^s, e_)/(w(ixo^s,rho_)*r(ixo^s))
2026 end subroutine hd_get_temperature_from_eint
2027
2028 ! Calculate flux f_idim[iw]
2029 subroutine hd_get_flux(wC, w, x, ixI^L, ixO^L, idim, f)
2031 use mod_dust, only: dust_get_flux_prim
2032
2033 integer, intent(in) :: ixi^l, ixo^l, idim
2034 ! conservative w
2035 double precision, intent(in) :: wc(ixi^s, 1:nw)
2036 ! primitive w
2037 double precision, intent(in) :: w(ixi^s, 1:nw)
2038 double precision, intent(in) :: x(ixi^s, 1:ndim)
2039 double precision, intent(out) :: f(ixi^s, nwflux)
2040
2041 double precision :: pth(ixi^s)
2042 integer :: ix^db
2043
2044 if (hd_energy) then
2045 {do ix^db=ixomin^db,ixomax^db\}
2046 f(ix^d,rho_)=w(ix^d,mom(idim))*w(ix^d,rho_)
2047 ! Momentum flux is v_i*m_i, +p in direction idim
2048 ^c&f(ix^d,m^c_)=w(ix^d,mom(idim))*wc(ix^d,m^c_)\
2049 f(ix^d,mom(idim))=f(ix^d,mom(idim))+w(ix^d,p_)
2050 ! Energy flux is v_i*(e + p)
2051 f(ix^d,e_)=w(ix^d,mom(idim))*(wc(ix^d,e_)+w(ix^d,p_))
2052 {end do\}
2053
2054 ! FACE-RECIPE: q is NOT contributing to the Riemann energy flux.
2055 ! The conductive heat-flux contribution is added by the face-
2056 ! recipe in add_hypertc_source as a post-Riemann sweep, computed
2057 ! from cell-centred Te (read from the cached Te_ field, which
2058 ! update_eos sets correctly each substep using the proper
2059 ! internal-energy/EoS path). q's own advective flux is zero.
2061 {do ix^db=ixomin^db,ixomax^db\}
2062 f(ix^d,q_)=zero
2063 {end do\}
2064 end if
2065 else
2066 call eos%get_thermal_pressure(wc, x, ixi^l, ixo^l, pth)
2067 {do ix^db=ixomin^db,ixomax^db\}
2068 f(ix^d,rho_)=w(ix^d,mom(idim))*w(ix^d,rho_)
2069 ! Momentum flux is v_i*m_i, +p in direction idim
2070 ^c&f(ix^d,m^c_)=w(ix^d,mom(idim))*wc(ix^d,m^c_)\
2071 f(ix^d,mom(idim))=f(ix^d,mom(idim))+pth(ix^d)
2072 {end do\}
2073 end if
2074
2075 if(hd_radiation_fld)then
2076 {do ix^db=ixomin^db,ixomax^db\}
2077 ! advection of radiation enery v_i*r_e
2078 f(ix^d,r_e)=w(ix^d,mom(idim))*wc(ix^d,r_e)
2079 {end do\}
2080 endif
2081
2082 do ix1 = 1, hd_n_tracer
2083 f(ixo^s, tracer(ix1)) = w(ixo^s,mom(idim)) * w(ixo^s, tracer(ix1))
2084 end do
2085
2086 if (hd_fip) then
2087 f(ixo^s,fip_) = w(ixo^s,mom(idim)) * wc(ixo^s,fip_)
2088 end if
2089
2090 ! Dust fluxes
2091 if (hd_dust) then
2092 call dust_get_flux_prim(w, x, ixi^l, ixo^l, idim, f)
2093 end if
2094
2095 end subroutine hd_get_flux
2096
2097 !> Add geometrical source terms to w
2098 !>
2099 !> Notice that the expressions of the geometrical terms depend only on ndir,
2100 !> not ndim. Eg, they are the same in 2.5D and in 3D, for any geometry.
2101 !>
2102 subroutine hd_add_source_geom(qdt, dtfactor, ixI^L, ixO^L, wCT, wprim, w, x)
2107 use mod_geometry
2108 integer, intent(in) :: ixi^l, ixo^l
2109 double precision, intent(in) :: qdt, dtfactor, x(ixi^s, 1:ndim)
2110 double precision, intent(inout) :: wct(ixi^s, 1:nw), wprim(ixi^s,1:nw),w(ixi^s, 1:nw)
2111 double precision :: pth(ixi^s), source(ixi^s), minrho
2112 integer :: iw,idir, h1x^l{^nooned, h2x^l}
2113 integer :: mr_,mphi_ ! Polar var. names
2114 integer :: irho, ifluid, n_fluids
2115 double precision :: exp_factor(ixi^s), del_exp_factor(ixi^s), exp_factor_primitive(ixi^s)
2116
2117 if (hd_dust) then
2118 n_fluids = 1 + dust_n_species
2119 else
2120 n_fluids = 1
2121 end if
2122
2123 select case (coordinate)
2124
2126 !the user provides the functions of exp_factor and del_exp_factor
2127 if(associated(usr_set_surface)) call usr_set_surface(ixi^l,x,block%dx,exp_factor,del_exp_factor,exp_factor_primitive)
2128 if(hd_energy) then
2129 source(ixo^s)=wprim(ixo^s, p_)
2130 else
2131 if(.not. associated(usr_set_pthermal)) then
2132 source(ixo^s)=hd_adiab * wprim(ixo^s, rho_)**eos%gamma
2133 else
2134 call usr_set_pthermal(wct,x,ixi^l,ixo^l,source)
2135 end if
2136 end if
2137 source(ixo^s) = source(ixo^s)*del_exp_factor(ixo^s)/exp_factor(ixo^s)
2138 w(ixo^s,mom(1)) = w(ixo^s,mom(1)) + qdt*source(ixo^s)
2139
2140 case (cylindrical)
2141 do ifluid = 0, n_fluids-1
2142 ! s[mr]=(pthermal+mphi**2/rho)/radius
2143 if (ifluid == 0) then
2144 ! gas
2145 irho = rho_
2146 mr_ = mom(r_)
2147 if(phi_>0) mphi_ = mom(phi_)
2148 if(hd_energy) then
2149 source(ixo^s)=wprim(ixo^s, p_)
2150 else
2151 if(.not. associated(usr_set_pthermal)) then
2152 source(ixo^s)=hd_adiab * wprim(ixo^s, rho_)**eos%gamma
2153 else
2154 call usr_set_pthermal(wct,x,ixi^l,ixo^l,source)
2155 end if
2156 end if
2157 minrho = 0.0d0
2158 else
2159 ! dust : no pressure
2160 irho = dust_rho(ifluid)
2161 mr_ = dust_mom(r_, ifluid)
2162 if(phi_>0) mphi_ = dust_mom(phi_, ifluid)
2163 source(ixi^s) = zero
2164 minrho = 0.0d0
2165 end if
2166 if(phi_ > 0) then
2167 where (wct(ixo^s, irho) > minrho)
2168 source(ixo^s) = source(ixo^s) + wct(ixo^s,mphi_)*wprim(ixo^s,mphi_)
2169 w(ixo^s, mr_) = w(ixo^s, mr_) + qdt*source(ixo^s)/x(ixo^s,r_)
2170 end where
2171 ! s[mphi]=(-mphi*vr)/radius
2172 where (wct(ixo^s, irho) > minrho)
2173 source(ixo^s) = -wct(ixo^s, mphi_) * wprim(ixo^s, mr_)
2174 w(ixo^s, mphi_) = w(ixo^s, mphi_) + qdt * source(ixo^s) / x(ixo^s, r_)
2175 end where
2176 else
2177 ! s[mr]=2pthermal/radius
2178 w(ixo^s, mr_) = w(ixo^s, mr_) + qdt * source(ixo^s) / x(ixo^s, r_)
2179 end if
2180 end do
2181 case (spherical)
2182 if (hd_dust) then
2183 call mpistop("Dust geom source terms not implemented yet with spherical geometries")
2184 end if
2185 mr_ = mom(r_)
2186 if(phi_>0) mphi_ = mom(phi_)
2187 h1x^l=ixo^l-kr(1,^d); {^nooned h2x^l=ixo^l-kr(2,^d);}
2188 if(hd_energy) then
2189 pth(ixo^s)=wprim(ixo^s, p_)
2190 else
2191 if(.not. associated(usr_set_pthermal)) then
2192 pth(ixo^s)=hd_adiab * wprim(ixo^s, rho_)**eos%gamma
2193 else
2194 call usr_set_pthermal(wct,x,ixi^l,ixo^l,pth)
2195 end if
2196 end if
2197 ! s[mr]=((vtheta**2+vphi**2)*rho+2*p)/r
2198 source(ixo^s) = pth(ixo^s) * x(ixo^s, 1) &
2199 *(block%surfaceC(ixo^s, 1) - block%surfaceC(h1x^s, 1)) &
2200 /block%dvolume(ixo^s)
2201 do idir = 2, ndir
2202 source(ixo^s) = source(ixo^s) + wprim(ixo^s, mom(idir))**2 * wprim(ixo^s, rho_)
2203 end do
2204 w(ixo^s, mr_) = w(ixo^s, mr_) + qdt * source(ixo^s) / x(ixo^s, 1)
2205
2206 {^nooned
2207 ! s[mtheta]=-(vr*vtheta*rho)/r+cot(theta)*(vphi**2*rho+p)/r
2208 source(ixo^s) = pth(ixo^s) * x(ixo^s, 1) &
2209 * (block%surfaceC(ixo^s, 2) - block%surfaceC(h2x^s, 2)) &
2210 / block%dvolume(ixo^s)
2211 if (ndir == 3) then
2212 source(ixo^s) = source(ixo^s) + (wprim(ixo^s, mom(3))**2 * wprim(ixo^s, rho_)) / tan(x(ixo^s, 2))
2213 end if
2214 source(ixo^s) = source(ixo^s) - (wprim(ixo^s, mom(2)) * wprim(ixo^s, mr_)) * wprim(ixo^s, rho_)
2215 w(ixo^s, mom(2)) = w(ixo^s, mom(2)) + qdt * source(ixo^s) / x(ixo^s, 1)
2216
2217 if (ndir == 3) then
2218 ! s[mphi]=-(vphi*vr/rho)/r-cot(theta)*(vtheta*vphi/rho)/r
2219 source(ixo^s) = -(wprim(ixo^s, mom(3)) * wprim(ixo^s, mr_)) * wprim(ixo^s, rho_)&
2220 - (wprim(ixo^s, mom(2)) * wprim(ixo^s, mom(3))) * wprim(ixo^s, rho_) / tan(x(ixo^s, 2))
2221 w(ixo^s, mom(3)) = w(ixo^s, mom(3)) + qdt * source(ixo^s) / x(ixo^s, 1)
2222 end if
2223 }
2224 end select
2225
2226 if (hd_rotating_frame) then
2227 if (hd_dust) then
2228 call mpistop("Rotating frame not implemented yet with dust")
2229 else
2230 call rotating_frame_add_source(qdt,dtfactor,ixi^l,ixo^l,wprim,w,x)
2231 end if
2232 end if
2233
2234 end subroutine hd_add_source_geom
2235
2236 ! w[iw]= w[iw]+qdt*S[wCT, qtC, x] where S is the source based on wCT within ixO
2237 subroutine hd_add_source(qdt,dtfactor, ixI^L,ixO^L,wCT,wCTprim,w,x,qsourcesplit,active)
2242 use mod_usr_methods, only: usr_gravity
2244 use mod_cak_force, only: cak_add_source
2245
2246 integer, intent(in) :: ixi^l, ixo^l
2247 double precision, intent(in) :: qdt, dtfactor
2248 double precision, intent(in) :: wct(ixi^s, 1:nw),wctprim(ixi^s,1:nw), x(ixi^s, 1:ndim)
2249 double precision, intent(inout) :: w(ixi^s, 1:nw)
2250 logical, intent(in) :: qsourcesplit
2251 logical, intent(inout) :: active
2252
2253 double precision :: gravity_field(ixi^s, 1:ndim)
2254 integer :: idust, idim, ix^d
2255
2256 if(hd_dust .and. .not. hd_dust_implicit) then
2257 call dust_add_source(qdt,ixi^l,ixo^l,wct,w,x,qsourcesplit,active)
2258 end if
2259
2260
2261 ! if (mype == 0) then
2262 ! {do ix^DB = ixI^LIM^DB\}
2263 ! if (abs(x(ix^D,1) - xprobmax1/2.0d0) > (xprobmax1/2.0d0 - 1.0d8/unit_length)) then
2264 ! write(*,*) x(ix^D,1), ' ' , wCT(ix^D,e_)
2265 ! endif
2266 ! {end do\}
2267 ! endif
2268 if(hd_radiative_cooling) then
2269 call radiative_cooling_add_source(qdt,ixi^l,ixo^l,wct,wctprim,w,x,&
2270 qsourcesplit,active, rc_fl)
2271 end if
2272
2273 ! The radiative transfer heating, solved once per step, is added unsplit at every stage
2274 if(hd_radiative_transfer .and. .not. qsourcesplit) then
2275 active = .true.
2276 call rt_sc_add_source(qdt,ixi^l,ixo^l,wct,w,x,hd_radiative_cooling,.false.)
2277 end if
2278
2279 if(hd_viscosity) then
2280 call viscosity_add_source(qdt,ixi^l,ixo^l,wct,wctprim,w,x,&
2281 hd_energy,qsourcesplit,active)
2282 end if
2283
2284 if (hd_gravity) then
2285 call gravity_add_source(qdt,ixi^l,ixo^l,wct,wctprim,w,x,&
2286 hd_energy,qsourcesplit,active)
2287
2288 if (hd_dust .and. qsourcesplit .eqv. grav_split) then
2289 active = .true.
2290
2291 call usr_gravity(ixi^l, ixo^l, wct, x, gravity_field)
2292 do idust = 1, dust_n_species
2293 do idim = 1, ndim
2294 w(ixo^s, dust_mom(idim, idust)) = w(ixo^s, dust_mom(idim, idust)) &
2295 + qdt * gravity_field(ixo^s, idim) * wct(ixo^s, dust_rho(idust))
2296 end do
2297 end do
2298 end if
2299 end if
2300
2301 if (hd_cak_force) then
2302 call cak_add_source(qdt,ixi^l,ixo^l,wct,w,x,hd_energy,qsourcesplit,active)
2303 end if
2304
2305 ! This is where the radiation force and heating/cooling are added
2306 if (hd_radiation_fld) then
2307 call hd_add_radiation_source(qdt,ixi^l,ixo^l,wct,wctprim,w,x,qsourcesplit,active)
2308 endif
2309
2310 if(eos%eos_type == 'PI') then
2311 if(.not.qsourcesplit) then
2312 active = .true.
2313 call eos%update_eos(ixi^l,ixo^l,w,x)
2314 end if
2315 end if
2316
2317 ! Hyperbolic TC: cell-centred Cattaneo relaxation source for q.
2318 if(hd_hyperbolic_thermal_conduction .and. .not. qsourcesplit) then
2319 active = .true.
2320 call add_hypertc_source(qdt,ixi^l,ixo^l,wct,w,x,wctprim)
2321 end if
2322
2323 end subroutine hd_add_source
2324
2325 !> FACE-RECIPE for hyperbolic thermal conduction.
2326 !>
2327 !> Replaces the OLD cell-centred Cattaneo source. Architecture:
2328 !> 1. q is removed from the Riemann pipeline (f(e_) does NOT get +q
2329 !> in hd_get_flux); q has no advective flux of its own.
2330 !> 2. Each face i+1/2 computes a Cattaneo-relaxed face heat flux
2331 !> q_f from the local centred T gradient. Te is read from the
2332 !> cached Te_ field (set by update_eos using the correct
2333 !> internal-energy/EoS path; calling get_temperature_from_eint
2334 !> directly on the conservative wCT inflates Te by KE/(R*rho)
2335 !> at fast-flow cells, hence the cache read).
2336 !> 3. Energy update: E_i -= qdt * (q_{i+1/2} - q_{i-1/2}) / dx_i.
2337 !> 4. Cell-centred q refreshed from the face values for next step.
2338 !>
2339 !> Why face-recipe vs HLL-on-q: avoids reconstruction (Koren limiter
2340 !> applied to q at faces) and HLL wave-speed bias (q weighted by
2341 !> hydro signal speeds during HLL combination, which has no physical
2342 !> justification for a scalar conductive flux).
2343 !>
2344 !> 1D only.
2345 subroutine add_hypertc_source(qdt,ixI^L,ixO^L,wCT,w,x,wCTprim)
2347
2348 integer, intent(in) :: ixi^l,ixo^l
2349 double precision, intent(in) :: qdt
2350 double precision, dimension(ixI^S,1:ndim), intent(in) :: x
2351 double precision, dimension(ixI^S,1:nw), intent(in) :: wct,wctprim
2352 double precision, dimension(ixI^S,1:nw), intent(inout) :: w
2353
2354 double precision :: te(ixi^s), rfactor(ixi^s)
2355 double precision :: qf_half(ixglo1:ixghi1)
2356 double precision :: qf_full(ixglo1:ixghi1)
2357 double precision :: t_l, t_r, t_cool_l, t_cool_r, t_kap_l, t_kap_r
2358 double precision :: kappa_l, kappa_r, kappa_f
2359 double precision :: sigma_t7_f, dt_face, dx_f
2360 double precision :: rho_f, p_l, p_r, p_f, cs_f, q_sat, q_sp, q_sp_star
2361 double precision :: f_sat, eint_loc_l, eint_loc_r, eint_loc_f
2362 double precision :: qn_face, tau_f, ratio, decay, ave_factor
2363 double precision :: q_face_max
2364 double precision, parameter :: ratio_cap = 50.0d0
2365 integer :: ix1, nface_lo, nface_hi
2366
2367 ! Te via cached field (correct EoS path) or primitives (FI fallback).
2368 if (te_ > 0) then
2369 te(ixi^s) = wct(ixi^s, te_)
2370 else
2371 call eos%get_Rfactor(wctprim, x, ixi^l, ixi^l, rfactor)
2372 te(ixi^s) = wctprim(ixi^s, p_) / (rfactor(ixi^s) * wctprim(ixi^s, rho_))
2373 end if
2374
2375 {^ifoned
2376 nface_lo = ixomin1 - 1
2377 nface_hi = ixomax1
2378 qf_half(:) = 0.0d0
2379 qf_full(:) = 0.0d0
2380
2381 do ix1 = nface_lo, nface_hi
2382 ! Cell-centred T on either side of face, with TRAC clip
2383 t_l = te(ix1)
2384 t_r = te(ix1 + 1)
2385 if (hd_trac) then
2386 t_cool_l = block%wextra(ix1, tcoff_)
2387 t_cool_r = block%wextra(ix1 + 1, tcoff_)
2388 else
2389 t_cool_l = 0.0d0
2390 t_cool_r = 0.0d0
2391 end if
2392 t_kap_l = max(t_l, t_cool_l)
2393 t_kap_r = max(t_r, t_cool_r)
2394 kappa_l = hypertc_kappa * dsqrt(t_kap_l**5)
2395 kappa_r = hypertc_kappa * dsqrt(t_kap_r**5)
2396
2397 ! Harmonic-mean face kappa: limits to cold side at sharp jumps,
2398 ! preserves flux conservation across kappa discontinuities.
2399 if (kappa_l + kappa_r > smalldouble) then
2400 kappa_f = 2.0d0 * kappa_l * kappa_r / (kappa_l + kappa_r)
2401 else
2402 kappa_f = 0.0d0
2403 end if
2404
2405 dx_f = 0.5d0 * (block%ds(ix1, 1) + block%ds(ix1 + 1, 1))
2406 dt_face = t_r - t_l
2407 q_sp = -kappa_f * dt_face / dx_f
2408
2409 ! Face-averaged primitives for saturation
2410 rho_f = 0.5d0 * (wctprim(ix1, rho_) + wctprim(ix1 + 1, rho_))
2411 p_l = wctprim(ix1, p_)
2412 p_r = wctprim(ix1 + 1, p_)
2413 p_f = 0.5d0 * (p_l + p_r)
2414 cs_f = dsqrt(max(smalldouble, eos%gamma * p_f / max(rho_f, smalldouble)))
2415
2416 ! Cowie-McKee saturation cap on the Spitzer target
2417 if (hd_htc_sat) then
2418 q_sat = 1.5d0 * rho_f * (p_f / max(rho_f, smalldouble))**1.5d0
2419 if (q_sat > smalldouble) then
2420 f_sat = 1.0d0 / (1.0d0 + dabs(q_sp) / q_sat)
2421 else
2422 f_sat = 1.0d0
2423 end if
2424 q_sp_star = f_sat * q_sp
2425 else
2426 q_sp_star = q_sp
2427 end if
2428
2429 ! Internal energy from conservatives (matches eint fix)
2430 eint_loc_l = wct(ix1, e_) &
2431 - 0.5d0 * wct(ix1, m1_)**2 / max(wct(ix1, rho_), smalldouble)
2432 eint_loc_r = wct(ix1 + 1, e_) &
2433 - 0.5d0 * wct(ix1 + 1, m1_)**2 / max(wct(ix1 + 1, rho_), smalldouble)
2434 eint_loc_l = max(eint_loc_l, smalldouble)
2435 eint_loc_r = max(eint_loc_r, smalldouble)
2436 eint_loc_f = 0.5d0 * (eint_loc_l + eint_loc_r)
2437
2438 ! MHD-style tau formula, using face quantities + global cmax.
2439 ! sigma_T7_f = kappa_f * T_f acts as the conductivity*temperature
2440 ! product entering tau. Floor tau at 4*dt for stability.
2441 sigma_t7_f = kappa_f * 0.5d0 * (t_l + t_r)
2442 tau_f = max(4.0d0 * dt, &
2443 sigma_t7_f * courantpar**2 / (eint_loc_f * cmax_global**2))
2444
2445 ! Cattaneo relaxation: closed-form exponential
2446 ratio = min(qdt / tau_f, ratio_cap)
2447 decay = dexp(-ratio)
2448 if (ratio > 1.0d-6) then
2449 ave_factor = (1.0d0 - decay) / ratio
2450 else
2451 ave_factor = 1.0d0 - 0.5d0 * ratio + ratio * ratio / 6.0d0
2452 end if
2453
2454 qn_face = 0.5d0 * (wct(ix1, q_) + wct(ix1 + 1, q_))
2455
2456 qf_full(ix1) = q_sp_star + (qn_face - q_sp_star) * decay
2457 qf_half(ix1) = q_sp_star + (qn_face - q_sp_star) * ave_factor
2458
2459 ! Energy-positivity clip on the n+1/2 flux
2460 q_face_max = hd_htc_pos_eta * min(eint_loc_l, eint_loc_r) * &
2461 block%ds(ix1, 1) / max(qdt, smalldouble)
2462 qf_half(ix1) = sign(min(dabs(qf_half(ix1)), q_face_max), qf_half(ix1))
2463 end do
2464
2465 ! Conservative energy update + cell-centred q refresh
2466 do ix1 = ixomin1, ixomax1
2467 w(ix1, e_) = w(ix1, e_) &
2468 - qdt * (qf_half(ix1) - qf_half(ix1 - 1)) / block%ds(ix1, 1)
2469 w(ix1, q_) = 0.5d0 * (qf_full(ix1 - 1) + qf_full(ix1))
2470 end do
2471 }
2472
2473 end subroutine add_hypertc_source
2474
2475 subroutine hd_add_radiation_source(qdt,ixI^L,ixO^L,wCT,wCTprim,w,x,qsourcesplit,active)
2476 use mod_constants
2478 use mod_usr_methods
2479 use mod_fld
2480
2481 integer, intent(in) :: ixi^l, ixo^l
2482 double precision, intent(in) :: qdt, x(ixi^s,1:ndim)
2483 double precision, intent(in) :: wct(ixi^s,1:nw),wctprim(ixi^s,1:nw)
2484 double precision, intent(inout) :: w(ixi^s,1:nw)
2485 logical, intent(in) :: qsourcesplit
2486 logical, intent(inout) :: active
2487
2488 ! add radiation force and work done by it, changes momentum and gas energy
2489 ! handle photon tiring, heating and cooling exchange between gas and radiation field
2490 call add_fld_rad_force(qdt,ixi^l,ixo^l,wct,wctprim,w,x,qsourcesplit,active,fld_fl)
2491
2492 end subroutine hd_add_radiation_source
2493
2494 subroutine hd_get_dt(wprim, ixI^L, ixO^L, dtnew, dx^D, x)
2496 use mod_dust, only: dust_get_dt
2498 use mod_gravity, only: gravity_get_dt
2499 use mod_cak_force, only: cak_get_dt
2500 use mod_fld, only: fld_radforce_get_dt
2501
2502 integer, intent(in) :: ixi^l, ixo^l
2503 double precision, intent(in) :: dx^d, x(ixi^s, 1:^nd)
2504 double precision, intent(in) :: wprim(ixi^s, 1:nw)
2505 double precision, intent(inout) :: dtnew
2506
2507 dtnew = bigdouble
2508
2509 if(hd_dust) then
2510 call dust_get_dt(wprim, ixi^l, ixo^l, dtnew, dx^d, x)
2511 end if
2512
2513 if(hd_viscosity) then
2514 call viscosity_get_dt(wprim,ixi^l,ixo^l,dtnew,dx^d,x)
2515 end if
2516
2517 if(hd_gravity) then
2518 call gravity_get_dt(wprim,ixi^l,ixo^l,dtnew,dx^d,x)
2519 end if
2520
2521 if (hd_cak_force) then
2522 call cak_get_dt(wprim,ixi^l,ixo^l,dtnew,dx^d,x)
2523 end if
2524
2525 if(hd_radiation_fld) then
2526 call fld_radforce_get_dt(wprim,ixi^l,ixo^l,dtnew,dx^d,x,fld_fl)
2527 endif
2528
2529 end subroutine hd_get_dt
2530
2531 !> Wrappers for the FLD implicit (MG diffusion) hooks: phys_implicit_update /
2532 !> phys_evaluate_implicit have fixed interfaces with no fluid argument, so
2533 !> these inject the module's fld_fl object into the threaded fld routines.
2534 subroutine hd_fld_implicit_update(dtfactor,qdt,qtC,psa,psb)
2536 use mod_fld, only: fld_implicit_update
2537 type(state), target :: psa(max_blocks)
2538 type(state), target :: psb(max_blocks)
2539 double precision, intent(in) :: qdt
2540 double precision, intent(in) :: qtc
2541 double precision, intent(in) :: dtfactor
2542
2543 call fld_implicit_update(dtfactor,qdt,qtc,psa,psb,fld_fl)
2544 end subroutine hd_fld_implicit_update
2545
2546 subroutine hd_fld_evaluate_implicit(qtC,psa)
2549 type(state), target :: psa(max_blocks)
2550 double precision, intent(in) :: qtc
2551
2552 call fld_evaluate_implicit(qtc,psa,fld_fl)
2553 end subroutine hd_fld_evaluate_implicit
2554
2555 function hd_kin_en(w, ixI^L, ixO^L, inv_rho) result(ke)
2556 use mod_global_parameters, only: nw, ndim
2557 integer, intent(in) :: ixi^l, ixo^l
2558 double precision, intent(in) :: w(ixi^s, nw)
2559 double precision :: ke(ixo^s)
2560 double precision, intent(in), optional :: inv_rho(ixo^s)
2561
2562 if (present(inv_rho)) then
2563 ke = 0.5d0 * sum(w(ixo^s, mom(:))**2, dim=ndim+1) * inv_rho
2564 else
2565 ke = 0.5d0 * sum(w(ixo^s, mom(:))**2, dim=ndim+1) / w(ixo^s, rho_)
2566 end if
2567 end function hd_kin_en
2568
2569 function hd_inv_rho(w, ixI^L, ixO^L) result(inv_rho)
2570 use mod_global_parameters, only: nw, ndim
2571 integer, intent(in) :: ixi^l, ixo^l
2572 double precision, intent(in) :: w(ixi^s, nw)
2573 double precision :: inv_rho(ixo^s)
2574
2575 ! Can make this more robust
2576 inv_rho = 1.0d0 / w(ixo^s, rho_)
2577 end function hd_inv_rho
2578
2579 subroutine hd_handle_small_values(primitive, w, x, ixI^L, ixO^L, subname)
2580 ! handles hydro (density,pressure,velocity) bootstrapping
2581 ! any negative dust density is flagged as well (and throws an error)
2582 ! small_values_method=replace also for dust
2586 logical, intent(in) :: primitive
2587 integer, intent(in) :: ixi^l,ixo^l
2588 double precision, intent(inout) :: w(ixi^s,1:nw)
2589 double precision, intent(in) :: x(ixi^s,1:ndim)
2590 character(len=*), intent(in) :: subname
2591
2592 integer :: n,idir
2593 logical :: flag(ixi^s,1:nw)
2594
2595 call hd_check_w(primitive, ixi^l, ixo^l, w, flag)
2596
2597 if (any(flag)) then
2598 select case (small_values_method)
2599 case ("replace")
2600 where(flag(ixo^s,rho_)) w(ixo^s,rho_) = small_density
2601 do idir = 1, ndir
2602 if(small_values_fix_iw(mom(idir))) then
2603 where(flag(ixo^s,rho_)) w(ixo^s, mom(idir)) = 0.0d0
2604 end if
2605 end do
2606 if(hd_radiation_fld)then
2607 if (small_values_fix_iw(r_e)) then
2608 where(flag(ixo^s,r_e)) w(ixo^s,r_e) = small_r_e
2609 end if
2610 end if
2611 if(hd_energy)then
2612 if(small_values_fix_iw(e_)) then
2613 if(primitive) then
2614 where(flag(ixo^s,rho_)) w(ixo^s, p_) = small_pressure
2615 else
2616 where(flag(ixo^s,rho_)) w(ixo^s, e_) = small_e + hd_kin_en(w,ixi^l,ixo^l)
2617 endif
2618 end if
2619 endif
2620
2621 if(hd_energy) then
2622 if(primitive) then
2623 where(flag(ixo^s,e_)) w(ixo^s,p_) = small_pressure
2624 else
2625 where(flag(ixo^s,e_))
2626 ! Add kinetic energy
2627 w(ixo^s,e_) = small_e + hd_kin_en(w,ixi^l,ixo^l)
2628 end where
2629 end if
2630 end if
2631
2632 if(hd_dust)then
2633 do n=1,dust_n_species
2634 where(flag(ixo^s,dust_rho(n))) w(ixo^s,dust_rho(n)) = 0.0d0
2635 do idir = 1, ndir
2636 where(flag(ixo^s,dust_rho(n))) w(ixo^s,dust_mom(idir,n)) = 0.0d0
2637 enddo
2638 enddo
2639 endif
2640 case ("average")
2641 if(primitive)then
2642 ! averaging for all primitive fields, including dust
2643 call small_values_average(ixi^l, ixo^l, w, x, flag)
2644 else
2645 ! do averaging of density
2646 call small_values_average(ixi^l, ixo^l, w, x, flag, rho_)
2647 if(hd_energy) then
2648 ! do averaging of pressure
2649 ! w(ixI^S,p_)=(hd_gamma-1.d0)*(w(ixI^S,e_) &
2650 ! -0.5d0*sum(w(ixI^S, mom(:))**2, dim=ndim+1)/w(ixI^S,rho_))
2651 call eos%get_thermal_pressure(w, x, ixi^l, ixo^l, w(ixo^s,p_))
2652 call small_values_average(ixi^l, ixo^l, w, x, flag, p_)
2653 do idir = 1, ndir
2654 w(ixo^s,mom(idir)) = w(ixo^s,mom(idir))/w(ixo^s,rho_) !> Convert to velocity to be compliant with p_to_e
2655 end do
2656 call eos%p_to_e(ixi^l, ixo^l, w, x)
2657 do idir = 1, ndir
2658 w(ixo^s,mom(idir)) = w(ixo^s,mom(idir))*w(ixo^s,rho_) !> Restore momentum (conserved-form invariant)
2659 end do
2660 ! w(ixI^S,e_)=w(ixI^S,p_)/(hd_gamma-1.d0) &
2661 ! +0.5d0*sum(w(ixI^S, mom(:))**2, dim=ndim+1)/w(ixI^S,rho_)
2662 end if
2663 if(hd_radiation_fld) then
2664 ! do averaging of radiative energy density
2665 call small_values_average(ixi^l, ixo^l, w, x, flag, r_e)
2666 endif
2667 if(hd_dust)then
2668 do n=1,dust_n_species
2669 where(flag(ixo^s,dust_rho(n))) w(ixo^s,dust_rho(n)) = 0.0d0
2670 do idir = 1, ndir
2671 where(flag(ixo^s,dust_rho(n))) w(ixo^s,dust_mom(idir,n)) = 0.0d0
2672 enddo
2673 enddo
2674 endif
2675 endif
2676 case default
2677 if(.not.primitive) then
2678 !convert w to primitive
2679 ! Calculate pressure = (gamma-1) * (e-ek)
2680 if(hd_energy) then
2681 call eos%get_thermal_pressure(w, x, ixi^l, ixo^l, w(ixo^s,p_))
2682 ! w(ixO^S,p_)=(hd_gamma-1.d0)*(w(ixO^S,e_)-hd_kin_en(w,ixI^L,ixO^L))
2683 end if
2684 ! Convert gas momentum to velocity
2685 do idir = 1, ndir
2686 w(ixo^s, mom(idir)) = w(ixo^s, mom(idir))/w(ixo^s,rho_)
2687 end do
2688 end if
2689 ! NOTE: dust entries may still have conserved values here
2690 call small_values_error(w, x, ixi^l, ixo^l, flag, subname)
2691 end select
2692 end if
2693 if (hd_fip) call hd_bound_fip(primitive, ixi^l, ixo^l, w)
2694 end subroutine hd_handle_small_values
2695
2696 !> Well-balanced transform: (T, v, q) variable change.
2697 !>
2698 !> Replaces (ρ, v, p) with (T, v, q) where:
2699 !> T(i) = p(i) / ρ(i) [temperature — smooth, locally computed]
2700 !> q(i) = p(i) / p_eq(i) [pressure ratio — ≈1 in HSE]
2701 !> Well-balanced post-prolongation correction for AMR.
2702 !>
2703 !> After prolongation interpolates to a fine grid, the pressure does not
2704 !> satisfy the discrete HSE recurrence at the fine resolution (linear
2705 !> interpolation of an exponential profile introduces O(dx^2/H^2) error).
2706 !>
2707 !> This routine rebuilds p from the multiplicative HSE recurrence using the
2708 !> interpolated T = p/rho (which IS smooth and well-interpolated). The
2709 !> density is updated as rho = p_new / T for consistency.
2710 !>
2711 !> On entry: w contains primitive (rho, v, p).
2712 !> On exit: w contains HSE-corrected primitive (rho_new, v, p_new).
2713 !>
2714 !> The recurrence is anchored at the block midpoint, where the parent
2715 !> cell-centre value is exact (no interpolation error).
2716 subroutine hd_wb_prolong(ixI^L, ixO^L, w, x)
2718 use mod_usr_methods, only: usr_gravity
2719
2720 integer, intent(in) :: ixi^l, ixo^l
2721 double precision, intent(inout) :: w(ixi^s, 1:nw)
2722 double precision, intent(in) :: x(ixi^s, 1:ndim)
2723
2724 double precision :: gravity_field(ixi^s, 1:ndim)
2725 double precision :: wb_t(ixi^s), p_eq(ixi^s)
2726 double precision :: dx_idims
2727 double precision :: alpha(ixi^s), beta(ixi^s)
2728 {^ifoned
2729 integer :: ix1, ix_mid
2730 }
2731
2732 ! T = p/rho at all fine cells (smooth from interpolation)
2733 wb_t(ixo^s) = w(ixo^s, p_) / w(ixo^s, rho_)
2734
2735 ! Get gravity at fine cell centres
2736 call usr_gravity(ixi^l, ixo^l, w, x, gravity_field)
2737
2738 dx_idims = dxlevel(1)
2739
2740 {^ifoned
2741 ! α_i = (dx/2) · g_i / T_i (vectorised over ixO)
2742 alpha(ixomin1:ixomax1) = 0.5d0 * dx_idims &
2743 * gravity_field(ixomin1:ixomax1, 1) / wb_t(ixomin1:ixomax1)
2744
2745 ! Anchor at block midpoint (least interpolation error)
2746 ix_mid = (ixomin1 + ixomax1) / 2
2747 p_eq(ix_mid) = w(ix_mid, p_)
2748
2749 ! Forward: β_i = (1 + α_i) / (1 - α_{i+1}) (vectorised)
2750 beta(ix_mid:ixomax1-1) = (1.0d0 + alpha(ix_mid:ixomax1-1)) &
2751 / (1.0d0 - alpha(ix_mid+1:ixomax1))
2752 do ix1 = ix_mid + 1, ixomax1
2753 p_eq(ix1) = p_eq(ix1 - 1) * beta(ix1 - 1)
2754 end do
2755
2756 ! Backward: β_i = (1 - α_{i+1}) / (1 + α_i) (vectorised)
2757 beta(ixomin1:ix_mid-1) = (1.0d0 - alpha(ixomin1+1:ix_mid)) &
2758 / (1.0d0 + alpha(ixomin1:ix_mid-1))
2759 do ix1 = ix_mid - 1, ixomin1, -1
2760 p_eq(ix1) = p_eq(ix1 + 1) * beta(ix1)
2761 end do
2762
2763 ! Replace interpolated p with recurrence-derived p, update rho = p/T
2764 w(ixomin1:ixomax1, p_) = p_eq(ixomin1:ixomax1)
2765 w(ixomin1:ixomax1, rho_) = p_eq(ixomin1:ixomax1) / wb_t(ixomin1:ixomax1)
2766 }
2767
2768 end subroutine hd_wb_prolong
2769
2770 !> Well-balanced transform for reconstruction.
2771 !>
2772 !> On entry: w contains primitive (rho, v, p).
2773 !> On exit: w(rho_) = T = p/rho, w(p_) = q = p/p_eq.
2774 !> wb_T returns the saved cell-centre temperature.
2775 !> wb_phi returns the cell-centre equilibrium pressure.
2776 !> wb_phi_face returns the face-centre equilibrium pressure.
2777 !>
2778 !> p_eq from multiplicative trapezoidal recurrence (always positive).
2779 !> In HSE: q = 1, T varies smoothly -> limiter sees flat q -> exact balance.
2780 subroutine hd_wb_transform(ixI^L, ixO^L, idims, w, x, wb_phi, &
2781 wb_phi_face, wb_T)
2783 use mod_usr_methods, only: usr_gravity
2784
2785 integer, intent(in) :: ixi^l, ixo^l, idims
2786 double precision, intent(inout) :: w(ixi^s, 1:nw)
2787 double precision, intent(in) :: x(ixi^s, 1:ndim)
2788 double precision, intent(out) :: wb_phi(ixi^s)
2789 double precision, intent(out) :: wb_phi_face(ixi^s)
2790 double precision, intent(out) :: wb_t(ixi^s)
2791
2792 double precision :: gravity_field(ixi^s, 1:ndim)
2793 double precision :: dx_idims
2794 double precision :: alpha(ixi^s), beta(ixi^s)
2795 {^ifoned
2796 integer :: ix1
2797 }
2798
2799 ! Save T = p/ρ at all cell centers
2800 wb_t(ixi^s) = w(ixi^s, p_) / w(ixi^s, rho_)
2801
2802 ! Get gravity acceleration at all cell centers
2803 call usr_gravity(ixi^l, ixi^l, w, x, gravity_field)
2804
2805 dx_idims = dxlevel(idims)
2806
2807 ! Multiplicative trapezoidal recurrence for p_eq.
2808 ! p_eq(i+1) = p_eq(i) · (1 + α(i)) / (1 - α(i+1))
2809 ! Always positive for dx < 2H.
2810 !
2811 ! Vectorised: precompute α and β factors, then sequential cumulative product.
2812 {^ifoned
2813 ! α_i = (dx/2) · g_i / T_i (vectorised)
2814 alpha(iximin1:iximax1) = 0.5d0 * dx_idims &
2815 * gravity_field(iximin1:iximax1, idims) / wb_t(iximin1:iximax1)
2816
2817 ! β_i = (1 + α_i) / (1 - α_{i+1}) (vectorised)
2818 beta(iximin1:iximax1-1) = (1.0d0 + alpha(iximin1:iximax1-1)) &
2819 / (1.0d0 - alpha(iximin1+1:iximax1))
2820
2821 ! Cumulative product (sequential, unavoidable data dependency)
2822 wb_phi(iximin1) = w(iximin1, p_)
2823 do ix1 = iximin1 + 1, iximax1
2824 wb_phi(ix1) = wb_phi(ix1 - 1) * beta(ix1 - 1)
2825 end do
2826 }
2827
2828 ! Face equilibrium pressure: isothermal half-step from cell center
2829 wb_phi_face(ixi^s) = wb_phi(ixi^s) * (1.0d0 + 0.5d0 * dx_idims * &
2830 gravity_field(ixi^s, idims) / wb_t(ixi^s))
2831
2832 ! Transform: w(rho_) = T, w(p_) = q = p/p_eq
2833 w(ixi^s, rho_) = wb_t(ixi^s)
2834 w(ixi^s, p_) = w(ixi^s, p_) / wb_phi(ixi^s)
2835
2836 end subroutine hd_wb_transform
2837
2838 !> Well-balanced inverse: restore physical (rho, v, p) at interfaces.
2839 !>
2840 !> On entry:
2841 !> wLp/wRp(rho_) = T (reconstructed), wLp/wRp(p_) = q (reconstructed)
2842 !> w(rho_) = T (cell-centre), w(p_) = q (cell-centre)
2843 !> wb_phi/wb_phi_face = cell/face equilibrium pressures from transform
2844 !> wb_T = saved cell-centre temperature from transform
2845 !>
2846 !> On exit:
2847 !> wLp/wRp(rho_) = rho_face, wLp/wRp(p_) = p_face (physical primitives)
2848 !> w(rho_) = rho_cell, w(p_) = p_cell (restored cell-centre primitives)
2849 !>
2850 !> Pressure: p_face = q_face * p_eq_face (multiplicative, well-balanced)
2851 !> Density: rho_face = p_face / T_blended
2852 !>
2853 !> Blends between shared T (well-balanced, zero HLL dissipation) and
2854 !> individual limiter-reconstructed T (non-WB, full dissipation) using
2855 !> a contact detector sigma = |T_faceL - T_faceR| / T_avg.
2856 !> In HSE: T is smooth → sigma ≈ 0 → pure WB (ρ_L = ρ_R).
2857 !> At contacts: T jump → sigma > 0 → ρ_L ≠ ρ_R → dissipation restored.
2858 subroutine hd_wb_inverse(ixI^L, ixL^L, ixR^L, idims, wLp, wRp, w, &
2859 wb_phi, wb_phi_face, wb_T)
2861
2862 integer, intent(in) :: ixi^l, ixl^l, ixr^l, idims
2863 double precision, intent(inout) :: wlp(ixi^s, 1:nw), wrp(ixi^s, 1:nw)
2864 double precision, intent(inout) :: w(ixi^s, 1:nw)
2865 double precision, intent(in) :: wb_phi(ixi^s), wb_phi_face(ixi^s)
2866 double precision, intent(in) :: wb_t(ixi^s)
2867
2868 double precision :: t_shared(ixi^s)
2869 double precision :: t_face_l(ixi^s), t_face_r(ixi^s)
2870 double precision :: sigma(ixi^s), t_for_rhol(ixi^s), t_for_rhor(ixi^s)
2871
2872 ! Shared face temperature: T_face(i) = ½(T(i) + T(i+1))
2873 {^ifoned
2874 t_shared(iximin1:iximax1-1) = 0.5d0 * (wb_t(iximin1:iximax1-1) &
2875 + wb_t(iximin1+1:iximax1))
2876 t_shared(iximax1) = wb_t(iximax1)
2877 }
2878
2879 ! Save limiter-reconstructed T before rho_ slot is overwritten
2880 ! (rho_ still holds T from the WB transform at this point)
2881 t_face_l(ixl^s) = wlp(ixl^s, rho_)
2882 t_face_r(ixr^s) = wrp(ixr^s, rho_)
2883
2884 ! Contact detector: relative T jump across interface
2885 ! sigma = 0 in HSE (smooth T), sigma > 0 at contacts (T discontinuity)
2886 sigma(ixl^s) = dabs(t_face_l(ixl^s) - t_face_r(ixr^s)) &
2887 / (0.5d0 * (t_face_l(ixl^s) + t_face_r(ixr^s)) + smalldouble)
2888 sigma(ixl^s) = min(sigma(ixl^s), 1.0d0)
2889
2890 ! Blended T for density recovery
2891 t_for_rhol(ixl^s) = (1.d0 - sigma(ixl^s)) * t_shared(ixl^s) &
2892 + sigma(ixl^s) * t_face_l(ixl^s)
2893 t_for_rhor(ixr^s) = (1.d0 - sigma(ixl^s)) * t_shared(ixr^s) &
2894 + sigma(ixl^s) * t_face_r(ixr^s)
2895
2896 ! Pressure inverse: p = q · p_eq_face (multiplicative)
2897 wlp(ixl^s, p_) = wlp(ixl^s, p_) * wb_phi_face(ixl^s)
2898 wrp(ixr^s, p_) = wrp(ixr^s, p_) * wb_phi_face(ixr^s)
2899
2900 ! Density inverse: ρ = p / T_blended
2901 wlp(ixl^s, rho_) = wlp(ixl^s, p_) / t_for_rhol(ixl^s)
2902 wrp(ixr^s, rho_) = wrp(ixr^s, p_) / t_for_rhor(ixr^s)
2903
2904 ! Restore cell-center values
2905 w(ixi^s, p_) = w(ixi^s, p_) * wb_phi(ixi^s)
2906 w(ixi^s, rho_) = w(ixi^s, p_) / wb_t(ixi^s)
2907
2908 end subroutine hd_wb_inverse
2909
2910end module mod_hd_phys
Calculate w(iw)=w(iw)+qdt*SOURCE[wCT,qtC,x] within ixO for all indices iw=iwmin......
Module with basic data types used in amrvac.
integer, parameter std_len
Default length for strings.
Module to include CAK radiation line force in (magneto)hydrodynamic models Computes both the force fr...
subroutine, public cak_add_source(qdt, ixil, ixol, wct, w, x, energy, qsourcesplit, active)
w[iw]=w[iw]+qdt*S[wCT,qtC,x] where S is the source based on wCT within ixO
subroutine, public cak_get_dt(wprim, ixil, ixol, dtnew, dxd, x)
Check time step for total radiation contribution.
subroutine, public cak_init(phys_gamma)
Initialize the module.
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
Module for physical and numeric constants.
double precision, parameter bigdouble
A very large real number.
Module for including dust species, which interact with the gas through a drag force.
Definition mod_dust.t:3
subroutine, public dust_add_source(qdt, ixil, ixol, wct, w, x, qsourcesplit, active)
w[iw]= w[iw]+qdt*S[wCT, x] where S is the source based on wCT within ixO
Definition mod_dust.t:533
subroutine, public dust_evaluate_implicit(qtc, psa)
inplace update of psa==>F_im(psa)
Definition mod_dust.t:586
integer, dimension(:, :), allocatable, public, protected dust_mom
Indices of the dust momentum densities.
Definition mod_dust.t:47
integer, public, protected dust_n_species
The number of dust species.
Definition mod_dust.t:37
subroutine, public dust_get_flux_prim(w, x, ixil, ixol, idim, f)
Definition mod_dust.t:276
integer, dimension(:), allocatable, public, protected dust_rho
Indices of the dust densities.
Definition mod_dust.t:44
subroutine, public dust_get_cmax(w, x, ixil, ixol, idim, cmax, cmin)
Definition mod_dust.t:1019
subroutine, public dust_check_w(ixil, ixol, w, x, flag)
Definition mod_dust.t:194
subroutine, public dust_check_params()
Definition mod_dust.t:154
subroutine, public dust_get_cmax_prim(w, x, ixil, ixol, idim, cmax, cmin)
Definition mod_dust.t:1043
subroutine, public dust_get_dt(wprim, ixil, ixol, dtnew, dxd, x)
Get dt related to dust and gas stopping time (Laibe 2011).
Definition mod_dust.t:901
subroutine, public dust_init(g_rho, g_mom, g_energy)
Definition mod_dust.t:95
subroutine, public dust_implicit_update(dtfactor, qdt, qtc, psb, psa)
Implicit solve of psb=psa+dtfactor*dt*F_im(psb).
Definition mod_dust.t:655
EoS state container – the single thermodynamic authority for AMRVAC.
type(eos_container), allocatable, public eos
The single EoS state object, allocated in eos_init and shared (read-mostly) across all EoS sub-module...
integer, parameter, public eos_type_lte
LTE (Saha-table) EoS kernels and finalise for the eos% family.
Definition mod_eos_lte.t:12
double precision function, public lte_gamma1_from_p(pres, rho_val)
Gamma_1 of one cell from (p, rho): the ideal value where fully ionised, else the pressure- indexed ta...
double precision function, public lte_p_from_eint(eint_val, nh_val, log_nh_val, inv_rho)
LTE pressure from internal energy in one cell: ideal without ionE, else the table floor,...
PI (partial-ionisation) ionisation-degree backend for the eos% family.
Equation of state for AMRVAC, handled through a single eos_container object.
Definition mod_eos.t:30
Module for escape probability radiative cooling modification.
subroutine, public escape_prob_init(iw_colmass, escape_sym, escape_height)
Register escape probability parameters. Called during hd_phys_init (before mesh parameters are availa...
Module for flux conservation near refinement boundaries.
Module for flux limited diffusion (FLD)-approximation in Radiation-(Magneto)hydrodynamics simulations...
Definition mod_fld.t:13
logical fld_no_mg
Definition mod_fld.t:35
double precision, public fld_bisect_tol
Tolerance for bisection method for Energy sourceterms This is a percentage of the minimum of gas- and...
Definition mod_fld.t:25
subroutine, public fld_radforce_get_dt(w, ixil, ixol, dtnew, dxd, x, fl)
get dt limit for radiation force and FLD explicit source additions NOTE: w is primitive on entry
Definition mod_fld.t:397
double precision, public fld_diff_tol
Tolerance for radiative Energy diffusion.
Definition mod_fld.t:27
character(len=40) fld_fluxlimiter
flux limiter choice
Definition mod_fld.t:41
character(len=40) fld_opal_table
Definition mod_fld.t:39
double precision, public fld_cnorm
Definition mod_fld.t:36
double precision, public fld_kappa0
Opacity value when using constant opacity.
Definition mod_fld.t:22
subroutine, public add_fld_rad_force(qdt, ixil, ixol, wct, wctprim, w, x, qsourcesplit, active, fl)
w[iw]=w[iw]+qdt*S[wCT,qtC,x] where S is the source based on wCT within ixO This subroutine handles th...
Definition mod_fld.t:240
character(len=40) fld_opacity_law
switches for opacity
Definition mod_fld.t:38
character(len=40) fld_interaction_method
Which method to find the root for the energy interaction polynomial.
Definition mod_fld.t:47
subroutine, public fld_get_radpress(w, x, ixil, ixol, rad_pressure, fl)
Returns Radiation Pressure as tensor NOTE: w is primitive on entry.
Definition mod_fld.t:540
logical fld_radforce_split
source split for energy interact and radforce:
Definition mod_fld.t:18
logical fld_bound_diff
switches for using changed cmax-cmin bounds
Definition mod_fld.t:32
subroutine, public fld_implicit_update(dtfactor, qdt, qtc, psa, psb, fl)
Calling all subroutines to perform the multigrid method Communicates rad_e and diff_coeff to multigri...
Definition mod_fld.t:800
subroutine, public fld_evaluate_implicit(qtc, psa, fl)
inplace update of psa==>F_im(psa)
Definition mod_fld.t:948
subroutine, public fld_init(gas_eos_type, gas_ion_energy, gas_pi_table)
Initialising FLD-module Read opacities Initialise Multigrid and adimensionalise kappa.
Definition mod_fld.t:116
integer nth_for_diff_mg
diffusion coefficient stencil control
Definition mod_fld.t:45
Module with geometry-related routines (e.g., divergence, curl).
Definition mod_geometry.t:2
integer coordinate
Definition mod_geometry.t:7
integer, parameter spherical
integer, parameter cylindrical
integer, parameter cartesian_expansion
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.
logical h_correction
If true, do H-correction to fix the carbuncle problem at grid-aligned shocks.
double precision arad_norm
Normalised radiation constant.
double precision unit_time
Physical scaling factor for time.
double precision unit_density
Physical scaling factor for density.
double precision unit_opacity
Physical scaling factor for Opacity.
integer, parameter unitpar
file handle for IO
double precision unit_mass
Physical scaling factor for mass.
logical use_imex_scheme
whether IMEX in use or not
integer, dimension(3, 3) kr
Kronecker delta tensor.
double precision unit_numberdensity
Physical scaling factor for number density.
character(len=std_len) convert_type
Which format to use when converting.
double precision unit_pressure
Physical scaling factor for pressure.
integer, parameter ndim
Number of spatial dimensions for grid variables.
double precision unit_length
Physical scaling factor for length.
logical phys_escape_prob
Use escape probability for radiative cooling modification.
double precision const_rad_a
Physical factors useful for radiation fld.
double precision cmax_global
global fastest wave speed needed in fd scheme and glm method
logical use_particles
Use particles module or not.
character(len=std_len), dimension(:), allocatable par_files
Which par files are used as input.
integer mype
The rank of the current MPI task.
double precision dt
global time step
integer ndir
Number of spatial dimensions (components) for vector variables.
double precision courantpar
The Courant (CFL) number used for the simulation.
double precision, dimension(:), allocatable, parameter d
logical slab
Cartesian geometry or not.
integer nwauxio
Number of auxiliary variables that are only included in output.
double precision unit_velocity
Physical scaling factor for velocity.
double precision c_norm
Normalised speed of light.
double precision unit_temperature
Physical scaling factor for temperature.
double precision unit_radflux
Physical scaling factor for radiation flux.
double precision, dimension(10) phys_trac_zone_splits
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
integer nghostcells
Number of ghost cells surrounding a grid.
double precision, dimension(:), allocatable w_refine_weight
Weights of variables used to calculate error for mesh refinement.
logical phys_trac
Use TRAC for MHD or 1D HD.
logical need_global_cmax
need global maximal wave speed
double precision, dimension(^nd) dxlevel
store unstretched cell size of current level
logical use_multigrid
Use multigrid (only available in 2D and 3D).
integer max_blocks
The maximum number of grid blocks in a processor.
integer r_
Indices for cylindrical coordinates FOR TESTS, negative value when not used:
integer boundspeed
bound (left/min and right.max) speed of Riemann fan
integer, parameter unitconvert
double precision unit_erad
Physical scaling factor for radiation energy density.
Module for including gravity in (magneto)hydrodynamics simulations.
Definition mod_gravity.t:2
logical grav_split
source split or not
Definition mod_gravity.t:6
subroutine gravity_get_dt(wprim, ixil, ixol, dtnew, dxd, x)
Definition mod_gravity.t:81
subroutine gravity_init()
Initialize the module.
Definition mod_gravity.t:26
subroutine gravity_add_source(qdt, ixil, ixol, wct, wctprim, w, x, energy, qsourcesplit, active)
w[iw]=w[iw]+qdt*S[wCT,qtC,x] where S is the source based on wCT within ixO
Definition mod_gravity.t:43
Hydrodynamics physics module.
Definition mod_hd_phys.t:2
integer, public, protected m
Definition mod_hd_phys.t:73
subroutine, public hd_check_params
logical, public, protected hd_energy
Whether an energy equation is used.
Definition mod_hd_phys.t:15
logical, public, protected hd_dust
Whether dust is added.
Definition mod_hd_phys.t:35
subroutine, public hd_get_pthermal_plus_pradiation(w, x, ixil, ixol, pth_plus_prad)
calculates the sum of the gas pressure and max Prad tensor element NOTE: only for diagnostic purposes...
subroutine, public hd_ei_to_e(ixil, ixol, w, x)
Transform internal energy to total energy.
integer, public, protected e_
Index of the energy density (-1 if not present).
Definition mod_hd_phys.t:79
logical, public, protected hd_radiative_cooling
Whether radiative cooling is added.
Definition mod_hd_phys.t:29
double precision, public, protected rr
subroutine, public hd_get_temperature_from_etot(w, x, ixil, ixol, res)
Calculate temperature=p/rho when in e_ the total energy is stored.
logical, public, protected hd_fld_pradtensor
Definition mod_hd_phys.t:42
integer, public, protected hd_trac_type
logical, public, protected hd_particles
Whether particles module is added.
Definition mod_hd_phys.t:53
logical, public, protected hd_fip
Whether FIP passive scalar is enabled.
Definition mod_hd_phys.t:97
double precision, public hypertc_kappa
Thermal-conductivity prefactor in hyperbolic TC, set in hd_physical_units. Spitzer form: κ(T) = hyper...
logical, public, protected hd_radiation_fld
Whether radiation-gas interaction is handled using flux limited diffusion.
Definition mod_hd_phys.t:41
double precision, public, protected hd_htc_beta
Face-recipe heat-wave speed scaling: c_HTC,f = hd_htc_beta * c_max,f. Higher value -> closer to diffu...
double precision, public, protected hd_trac_delta
Johnston 2021 resolution parameter delta (default 0.5).
type(tc_fluid), allocatable, public tc_fl
Definition mod_hd_phys.t:25
subroutine, public hd_check_w(primitive, ixil, ixol, w, flag)
Returns logical argument flag where values are ok.
logical, public, protected hd_viscosity
Whether viscosity is added.
Definition mod_hd_phys.t:47
integer, public, protected r_e
Index of the radiation energy (when fld active).
Definition mod_hd_phys.t:88
integer, public, protected c
Indices of the momentum density for the form of better vectorization.
Definition mod_hd_phys.t:73
subroutine, public hd_get_csound2(w, x, ixil, ixol, csound2)
Calculate the square of the thermal sound speed csound2 within ixO^L. For conserved w: extracts pther...
integer, public, protected tcoff_
Index of the cutoff temperature for the TRAC method.
double precision, public, protected he_ion_fr2
Ratio of number He2+ / number He+ + He2+ He_ion_fr2 = He2+/(He2+ + He+).
double precision, public, protected hd_htc_gradt_floor
Gradient deadband: zero out the Spitzer face flux when abs(T_R - T_L) / max(T_L, T_R) < hd_htc_gradT_...
integer, public, protected te_
Indices of temperature.
Definition mod_hd_phys.t:91
integer, dimension(:), allocatable, public, protected mom
Indices of the momentum density.
Definition mod_hd_phys.t:70
subroutine, public hd_get_pradiation_from_prim(w, x, ixil, ixol, prad)
Calculate radiation pressure within ixO^L NOTE: w is primitive on entry here! NOTE: used in FLD modul...
double precision, public, protected hd_trac_tfix
fixed cutoff temperature in K for hd_trac_type=0 (Lionello et al. 2009, Mikic et al....
double precision, public, protected hd_htc_hyp_diff
Hyperdiffusion coefficient applied to the cell-refreshed q at the end of each face-recipe substep....
logical, public, protected hd_hyperbolic_thermal_conduction
Whether hyperbolic thermal conduction (Cattaneo relaxation) is used. 1D only : the q-variable is trea...
Definition mod_hd_phys.t:22
double precision, public, protected h_ion_fr
Helium abundance over Hydrogen He_abundance is set in &eos_list and accessed via eosHe_abundance Ioni...
double precision function, dimension(ixo^s), public hd_kin_en(w, ixil, ixol, inv_rho)
logical, public, protected hd_cak_force
Whether CAK radiation line force is activated.
Definition mod_hd_phys.t:59
subroutine, public hd_phys_init()
Initialize the module.
integer, dimension(:), allocatable, public, protected tracer
Indices of the tracers.
Definition mod_hd_phys.t:76
subroutine, public hd_get_csrad2(w, x, ixil, ixol, csound)
Calculate modified squared sound speed for FLD NOTE: only for diagnostic purposes,...
logical, public, protected hd_thermal_conduction
Whether thermal conduction is added.
Definition mod_hd_phys.t:18
integer, public, protected q_
Index of the hyperbolic-TC heat-flux variable (-1 if not present).
double precision, public hd_htc_validity_max_runtime
Running max of l_r,f / dx_f across all face-recipe calls since simulation start. Inspect post-hoc via...
double precision, public hd_adiab
gamma is set in &eos_list and accessed via eosgamma
subroutine, public hd_get_trad(w, x, ixil, ixol, trad)
Calculates radiation temperature.
subroutine, public hd_get_csrad2_prim(w, x, ixil, ixol, csound)
Calculate modified squared sound speed for FLD NOTE: w is primitive on entry here!...
integer, public, protected rho_
Whether plasma is partially ionized.
Definition mod_hd_phys.t:67
subroutine, public hd_handle_small_values(primitive, w, x, ixil, ixol, subname)
double precision, public, protected hd_htc_sat_alpha
Cowie-McKee saturation coefficient: q_sat = hd_htc_sat_alpha * rho * c_s^3. Standard convention is al...
double precision, public, protected hd_htc_pos_eta
Per-face energy-positivity safety fraction: |q_f^{n+1/2} dt A_f| <= hd_htc_pos_eta * min(e_int_L V_L,...
logical, public, protected hd_radiative_transfer
Whether the short-characteristics radiative transfer is used, with its settings in &rt_list.
Definition mod_hd_phys.t:31
double precision, public, protected he_ion_fr
Ionization fraction of He He_ion_fr = (He2+ + He+)/(He2+ + He+ + He).
logical, public, protected hd_gravity
Whether gravity is added.
Definition mod_hd_phys.t:50
type(fld_fluid), allocatable, public fld_fl
Radiation fluid object (gas-EoS callbacks for FLD), wired in hd_link_eos.
Definition mod_hd_phys.t:44
double precision, public, protected hd_htc_kappa_override
Optional parfile override for hypertc_kappa (e.g. to match a constant-κ parabolic TC run for benchmar...
integer, public, protected hd_trac_nzones
integer, public, protected c_
Definition mod_hd_phys.t:73
type(rc_fluid), allocatable, public rc_fl
Definition mod_hd_phys.t:32
logical, public, protected hd_dust_implicit
Whether dust is added using and implicit update in IMEX.
Definition mod_hd_phys.t:38
logical, public, protected hd_trac
Whether TRAC method is used.
integer, public, protected fip_
Index of the FIP passive scalar rho*fip in conserved form, fip in primitive form.
Definition mod_hd_phys.t:94
double precision, public, protected hd_htc_validity_warn
Validity-monitor threshold for l_r,f / Delta_x_f. Warn if any face exceeds this in a given block (pri...
integer, public, protected hd_n_tracer
Number of tracer species.
Definition mod_hd_phys.t:62
type(te_fluid), allocatable, public te_fl_hd
Definition mod_hd_phys.t:26
double precision, dimension(10), public, protected hd_trac_zone_splits
logical, public, protected hd_rotating_frame
Whether rotating frame is activated.
Definition mod_hd_phys.t:56
logical, public, protected hd_htc_sat
Whether saturation is considered for hyperbolic TC.
Definition mod_hd_phys.t:24
logical, public, protected hd_well_balanced
Whether well-balanced reconstruction is used (Kaeppeli & Mishra style).
integer, public, protected iw_colmass
Index into wextra for escape probability column mass.
integer, public, protected p_
Index of the gas pressure (-1 if not present) should equal e_.
Definition mod_hd_phys.t:82
integer, public, protected ne_
Index of the electron number density for LTE module.
Definition mod_hd_phys.t:85
subroutine, public hd_e_to_ei(ixil, ixol, w, x)
Transform total energy to internal energy.
Module containing all the particle routines.
subroutine particles_init()
Initialize particle data and parameters.
This module defines the procedures of a physics module. It contains function pointers for the various...
Definition mod_physics.t:4
module radiative cooling – add optically thin radiative cooling
subroutine radiative_cooling_init_params(phys_gamma, he_abund)
Radiative cooling initialization.
subroutine findl(tpoint, lpoint, fl)
subroutine radiative_cooling_init(fl, read_params)
subroutine radiative_cooling_add_source(qdt, ixil, ixol, wct, wctprim, w, x, qsourcesplit, active, fl)
subroutine calc_l_extended(tpoint, lpoint, fl)
Module for including rotating frame in (magneto)hydrodynamics simulations The rotation vector is assu...
subroutine rotating_frame_add_source(qdt, dtfactor, ixil, ixol, wct, w, x)
w[iw]=w[iw]+qdt*S[wCT,qtC,x] where S is the source based on wCT within ixO
subroutine rotating_frame_init()
Initialize the module.
Public face of the short-characteristics (SC) radiative transfer and its coupling to the (M)HD.
subroutine, public rt_sc_add_source(qdt, ixil, ixol, wct, w, x, thin_share, e_internal)
Unsplit heating source of one block and stage, e += qdt Q, with Q read from i_qrad,...
subroutine, public rt_sc_activate()
Switch the RT on. The physics module calls this from its initialisation when hd_radiative_transfer or...
Module for handling problematic values in simulations, such as negative pressures.
subroutine, public small_values_average(ixil, ixol, w, x, w_flag, windex)
subroutine, public small_values_error(wprim, x, ixil, ixol, w_flag, subname)
logical, dimension(:), allocatable, public small_values_fix_iw
Whether to apply small value fixes to certain variables.
character(len=20), public small_values_method
How to handle small values.
Generic supertimestepping method which can be used for multiple source terms in the governing equatio...
subroutine, public add_sts_method(sts_getdt, sts_set_sources, startvar, nflux, startwbc, nwbc, evolve_b)
subroutine which added programatically a term to be calculated using STS Params: sts_getdt function c...
subroutine, public set_conversion_methods_to_head(sts_before_first_cycle, sts_after_last_cycle)
Set the hooks called before the first cycle and after the last cycle in the STS update This method sh...
subroutine, public set_error_handling_to_head(sts_error_handling)
Set the hook of error handling in the STS update. This method is called before updating the BC....
subroutine, public sts_init()
Initialize sts module.
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.
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, public sts_set_source_tc_hd(ixil, ixol, w, x, wres, fix_conserve_at_step, my_dt, igrid, nflux, fl)
subroutine get_euv_image(qunit, fl)
subroutine get_sxr_image(qunit, fl)
subroutine get_euv_spectrum(qunit, fl)
subroutine get_whitelight_image(qunit, fl)
subroutine eos_timer_start()
Open an EoS timing window; inside an OpenMP parallel region the EoS is not timed.
Definition mod_timing.t:45
subroutine eos_timer_stop(acc)
Close an EoS timing window, adding its time to acc when it is the outermost.
Definition mod_timing.t:52
double precision timeeos_csound
Definition mod_timing.t:13
Module with all the methods that users can customize in AMRVAC.
procedure(rfactor), pointer usr_rfactor
procedure(sub_get_heating), pointer usr_get_heating
procedure(set_surface), pointer usr_set_surface
procedure(phys_gravity), pointer usr_gravity
procedure(hd_pthermal), pointer usr_set_pthermal
procedure(set_wlr), pointer usr_set_wlr
integer nw
Total number of variables.
integer number_species
number of species: each species has different characterictic speeds and should be used accordingly in...
The module add viscous source terms and check time step.
subroutine, public viscosity_get_dt(wprim, ixil, ixol, dtnew, dxd, x)
procedure(sub_add_source), pointer, public viscosity_add_source
subroutine, public viscosity_init(phys_wider_stencil)
Initialize the module.
Radiation fluid object: gas-EoS callbacks the FLD module needs, wired by the physics module at link t...
Definition mod_fld.t:63