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