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