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