MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_mhd_phys.t
Go to the documentation of this file.
1!> Magneto-hydrodynamics module
3
4#include "amrvac.h"
5
6 use mod_global_parameters, only: std_len, const_c
10 use mod_fld, only: fld_fluid
11 use mod_physics
12 use mod_eos
13 use mod_rt_sc_slab, only: rt_sc_activate, rt_sc_setup, rt_sc_add_source
14 use mod_comm_lib, only: mpistop
16
17 implicit none
18 private
19
20 !> The adiabatic constant
21 double precision, public :: mhd_adiab = 1.0d0
22 !> The MHD resistivity
23 double precision, public :: mhd_eta = 0.0d0
24 !> The MHD hyper-resistivity
25 double precision, public :: mhd_eta_hyper = 0.0d0
26 !> Hall resistivity
27 double precision, public :: mhd_etah = 0.0d0
28 !> The MHD ambipolar coefficient
29 double precision, public :: mhd_eta_ambi = 0.0d0
30 !> Height of the mask used in the TRAC method
31 double precision, public, protected :: mhd_trac_mask = 0.d0
32 !> GLM-MHD parameter: ratio of the diffusive and advective time scales for div b
33 !> taking values within [0, 1]
34 double precision, public :: mhd_glm_alpha = 0.5d0
35 !> Reduced speed of light for semirelativistic MHD: 2% of light speed
36 double precision, public, protected :: mhd_reduced_c = 0.02d0*const_c
37 !> The thermal conductivity kappa in hyperbolic thermal conduction
38 double precision, public :: mhd_hyperbolic_tc_kappa = 0.d0
39 !> Relative perpendicular hyperbolic-TC coefficient in fixed/strong-field limit:
40 !> kappa_perp0 = mhd_hyperbolic_tc_kappa_perp_factor * kappa_parallel
41 double precision, public, protected :: mhd_hyperbolic_tc_kappa_perp_factor = 0.d0
42 !> Field-strength transition scale for perpendicular closure
43 double precision, public, protected :: mhd_hyperbolic_tc_bmin = 0.d0
44 !> Constant Coulomb logarithm used by the simplified electron-magnetization
45 !> closure. It is a namelist parameter so changing it has no per-cell cost.
46 double precision, public, protected :: mhd_hyperbolic_tc_coulomb_log = 20.d0
47 !> Coefficient of diffusive divB cleaning
48 double precision, public :: divbdiff = 0.8d0
49 !> Ionization fraction of H
50 !> H_ion_fr = H+/(H+ + H)
51 double precision, public, protected :: h_ion_fr=1d0
52 !> Ionization fraction of He
53 !> He_ion_fr = (He2+ + He+)/(He2+ + He+ + He)
54 double precision, public, protected :: he_ion_fr=1d0
55 !> Ratio of number He2+ / number He+ + He2+
56 !> He_ion_fr2 = He2+/(He2+ + He+)
57 double precision, public, protected :: he_ion_fr2=1d0
58 ! used for eq of state when it is not defined by units,
59 ! the units do not contain terms related to ionization fraction
60 ! and it is p = RR * rho * T
61 double precision, public, protected :: rr=1d0
62 !> inverse of squared speed of light c0 and reduced speed of light c
63 double precision :: inv_squared_c0=0.d0, inv_squared_c=0.d0
64 !> squared reduced speed of light c
65 double precision, public, protected :: mhd_squared_c
66 !> Dimensionless multiplier in the one_dimensional_gradient source only
67 double precision, public, protected :: mhd_uawsom_sigma = 0.d0
68 !> Base transverse density contrast and filling factor
69 double precision, public, protected :: mhd_uawsom_zeta0 = 5.d0
70 double precision, public, protected :: mhd_uawsom_filling_factor = 0.1d0
71 !> Physical input scales (lengths and magnetic field); converted to code units at init
72 double precision, public, protected :: mhd_uawsom_zeta_scale = -1.d0
73 double precision, public, protected :: mhd_uawsom_thread_radius0 = -1.d0
74 double precision, public, protected :: mhd_uawsom_bref = -1.d0
75 double precision, public, protected :: mhd_uawsom_alfven_corr_length0 = -1.d0
76 !> equi vars indices in the state%equi_vars array
77 integer, public :: equi_rho0_ = -1
78 integer, public :: equi_pe0_ = -1
79 !> Number of tracer species
80 integer, public, protected :: mhd_n_tracer = 0
81 !> Index of the density (in the w array)
82 integer, public, protected :: rho_
83 !> Indices of the momentum density
84 integer, allocatable, public, protected :: mom(:)
85 !> Indices of the momentum density for the form of better vectorization
86 integer, public, protected :: ^c&m^C_
87 !> Index of the energy density (-1 if not present)
88 integer, public, protected :: e_
89 !> Indices of the magnetic field for the form of better vectorization
90 integer, public, protected :: ^c&b^C_
91 !> Index of the gas pressure (-1 if not present) should equal e_
92 integer, public, protected :: p_
93 !> Index of the electron number density for LTE module
94 integer, public, protected :: ne_
95 !> Index of the field-aligned heat flux q_parallel
96 integer, public, protected :: qpar_
97 !> Index of the perpendicular heat flux q_perp
98 integer, public, protected :: qperp_
99 !> Indices of the GLM psi
100 integer, public, protected :: psi_
101 !> Index of the radiation energy
102 integer, public, protected :: r_e
103 !> Indices of temperature
104 integer, public, protected :: te_
105 !> Index of the FIP passive scalar rho*fip in conserved form, fip in primitive form
106 integer, public, protected :: fip_ = -1
107 ! Parse the input mode once; source kernels use the private integer ID.
108 integer, parameter :: uawsom_reflection_1d=1, uawsom_reflection_cartesian=2
109 integer :: uawsom_reflection_id=uawsom_reflection_1d
110 !> Enable conservative kink-wave reflection from the kink-speed gradient
111 !> Cartesian direction used for the prescribed density contrast and reflection gradient
112 integer, public, protected :: mhd_uawsom_height_dim = -1
113 !> Conserved wave-energy indices. The plus variables propagate against B.
114 integer, public, protected :: waplus_=-1, waminus_=-1, wkplus_=-1, wkminus_=-1
115 !> Index of the cutoff temperature for the TRAC method
116 integer, public, protected :: tcoff_
117 integer, public, protected :: tweight_
118 !> Indices of the tracers
119 integer, allocatable, public, protected :: tracer(:)
120 !> The number of waves
121 integer :: nwwave=8
122 !> Method type of divb in a integer for good performance
123 integer :: type_divb
124 !> To skip * layer of ghost cells during divB=0 fix for boundary
125 integer, public, protected :: boundary_divbfix_skip(2*^nd)=0
126 ! DivB cleaning methods
127 integer, parameter :: divb_none = 0
128 integer, parameter :: divb_multigrid = -1
129 integer, parameter :: divb_glm = 1
130 integer, parameter :: divb_powel = 2
131 integer, parameter :: divb_janhunen = 3
132 integer, parameter :: divb_linde = 4
133 integer, parameter :: divb_lindejanhunen = 5
134 integer, parameter :: divb_lindepowel = 6
135 integer, parameter :: divb_lindeglm = 7
136 integer, parameter :: divb_ct = 8
137 !> Whether divB is computed with a fourth order approximation
138 integer, public, protected :: mhd_divb_nth = 1
139 ! Parsed once during initialization; runtime kernels do not inspect strings.
140 integer, parameter :: htc_perp_disabled=0, htc_perp_fixed=1, &
141 htc_perp_weak_field=2, htc_perp_electron=3
142 integer :: htc_perp_mode_id=htc_perp_disabled
143 !> Whether an energy equation is used
144 logical, public, protected :: mhd_energy = .true.
145 !> Whether thermal conduction is used
146 logical, public, protected :: mhd_thermal_conduction = .false.
147 !> Whether radiative cooling is added
148 logical, public, protected :: mhd_radiative_cooling = .false.
149 !> Whether the short-characteristics radiative transfer is used, with its settings in &rt_list
150 logical, public, protected :: mhd_radiative_transfer = .false.
151 !> Whether thermal conduction is used
152 logical, public, protected :: mhd_hyperbolic_tc = .false.
153 !> Whether saturation is considered for hyperbolic TC. When the perpendicular
154 !> channel is active, the limiter uses sqrt(q_parallel^2+q_perp^2).
155 logical, public, protected :: mhd_hyperbolic_tc_sat = .false.
156 !> Whether the perpendicular hyperbolic-TC channel is enabled
157 logical, public, protected :: mhd_hyperbolic_tc_use_perp = .false.
158 logical, public :: mhd_hyperbolic_tc_constant = .false.
159 !> Whether viscosity is added
160 logical, public, protected :: mhd_viscosity = .false.
161 !> Whether gravity is added
162 logical, public, protected :: mhd_gravity = .false.
163 !> Whether rotating frame is activated
164 logical, public, protected :: mhd_rotating_frame = .false.
165 !> Whether Hall-MHD is used
166 logical, public, protected :: mhd_hall = .false.
167 !> Whether Ambipolar term is used
168 logical, public, protected :: mhd_ambipolar = .false.
169 !> Whether Ambipolar term is implemented using supertimestepping
170 logical, public, protected :: mhd_ambipolar_sts = .false.
171 !> Whether Ambipolar term is implemented explicitly
172 logical, public, protected :: mhd_ambipolar_exp = .false.
173 !> Whether particles module is added
174 logical, public, protected :: mhd_particles = .false.
175 !> Whether magnetofriction is added
176 logical, public, protected :: mhd_magnetofriction = .false.
177 !> Whether GLM-MHD is used to control div B
178 logical, public, protected :: mhd_glm = .false.
179 !> Whether extended GLM-MHD is used with additional sources
180 logical, public, protected :: mhd_glm_extended = .true.
181 !> Whether TRAC method is used
182 logical, public, protected :: mhd_trac = .false.
183 !> Which TRAC method is used
184 integer, public, protected :: mhd_trac_type=1
185 !> Distance between two adjacent traced magnetic field lines (in finest cell size)
186 integer, public, protected :: mhd_trac_finegrid=4
187 !> TRAC-7 (Johnston 2021, A&A 654 A2): target number of cells resolving the TR
188 double precision, public, protected :: mhd_trac_delta=0.5d0
189 !> Whether internal energy is solved instead of total energy
190 logical, public, protected :: mhd_internal_e = .false.
191 !> Whether hydrodynamic energy is solved instead of total energy
192 logical, public, protected :: mhd_hydrodynamic_e = .false.
193 !> Whether divB cleaning sources are added splitting from fluid solver
194 logical, public, protected :: source_split_divb = .false.
195 !> Whether semirelativistic MHD equations (Gombosi 2002 JCP) are solved
196 logical, public, protected :: mhd_semirelativistic = .false.
197 !> Whether plasma is partially ionized
198 !> Whether CAK radiation line force is activated
199 logical, public, protected :: mhd_cak_force = .false.
200 !> Whether radiation-gas interaction is handled using flux limited diffusion
201 logical, public, protected :: mhd_radiation_fld = .false.
202 logical, public, protected :: mhd_fld_pradtensor= .true.
203 !> Radiation fluid object (gas-EoS callbacks for FLD), wired in mhd_link_eos
204 type(fld_fluid), allocatable, public :: fld_fl
205 !> whether split off equilibrium density and pressure
206 logical, public :: has_equi_rho_and_p = .false.
207 logical, public :: mhd_equi_thermal = .false.
208 !> whether dump full variables (when splitting is used) in a separate dat file
209 logical, public, protected :: mhd_dump_full_vars = .false.
210 !> Whether FIP passive scalar is enabled
211 logical, public, protected :: mhd_fip = .false.
212 !> Enable the Uniturbulence and Alfven Wave Solar Model extension
213 logical, public, protected :: mhd_uawsom = .false.
214 !> Enable conservative Alfvén reflection (one-dimensional or Cartesian gradient-vorticity)
215 logical, public, protected :: mhd_uawsom_reflection = .false.
216 logical, public, protected :: mhd_uawsom_kink_reflection = .false.
217 !> Add divB wave in Roe solver
218 logical, public :: divbwave = .true.
219 !> clean initial divB
220 logical, public :: clean_initial_divb = .false.
221 !> To control divB=0 fix for boundary
222 logical, public, protected :: boundary_divbfix(2*^nd)=.true.
223 !> B0 field is force-free
224 logical, public, protected :: b0field_forcefree=.true.
225 !> Whether an total energy equation is used
226 logical :: total_energy = .true.
227 !> Whether numerical resistive heating is included when solving partial energy equation
228 logical, public :: numerical_resistive_heating = .false.
229 !> Whether gravity work is included in energy equation
230 logical :: gravity_energy
231 !> Keep E x curl(E) in the semirelativistic source: eq (26) of Gombosi et al. 2002 keeps it,
232 !> their eq (28) and wave tests drop it (steady-state limit)
233 logical, public, protected :: mhd_semirelati_curl_e = .true.
234 !> Algebra used by the Alfven-wave reflection source. one_dimensional_gradient
235 !> retains the original one-dimensional source; cartesian_gradient_vorticity
236 !> is the multidimensional gradient/vorticity closure.
237 character(len=std_len), public, protected :: mhd_uawsom_reflection_mode = 'one_dimensional_gradient'
238 !> Perpendicular hyperbolic-TC closure mode:
239 !> Enable/disable with mhd_hyperbolic_tc_use_perp, not the mode string.
240 !> 'fixed_reference' = fixed classical ratio at the reference normalisation
241 !> 'weak_field_isotropization' = empirical transition using a prescribed Bmin
242 !> 'electron_magnetization' = simplified ratio 1/(1+chi_e^2)
243 character(len=std_len), public, protected :: mhd_hyperbolic_tc_perp_mode = 'fixed_reference'
244 !> Method type to clean divergence of B
245 character(len=std_len), public, protected :: typedivbfix = 'linde'
246 !> Method type of constrained transport
247 character(len=std_len), public, protected :: type_ct = 'uct_contact'
248 !> Update all equations due to divB cleaning
249 character(len=std_len) :: typedivbdiff = 'all'
250 !> type of fluid for thermal conduction
251 type(tc_fluid), public, allocatable :: tc_fl
252 !> type of fluid for thermal emission synthesis
253 type(te_fluid), public, allocatable :: te_fl_mhd
254 !> type of fluid for radiative cooling
255 type(rc_fluid), public, allocatable :: rc_fl
256
257 !define the subroutine interface for the ambipolar mask
258 abstract interface
259
260 subroutine mask_subroutine(ixI^L,ixO^L,w,x,res)
262 integer, intent(in) :: ixi^l, ixo^l
263 double precision, intent(in) :: x(ixi^s,1:ndim)
264 double precision, intent(in) :: w(ixi^s,1:nw)
265 double precision, intent(inout) :: res(ixi^s)
266 end subroutine mask_subroutine
267
268 end interface
269
270 procedure(mask_subroutine), pointer :: usr_mask_ambipolar => null()
271 procedure(sub_small_values), pointer :: mhd_handle_small_values => null()
272 !> The energy conversion that mhd_e_to_ei_cache_log_nH wraps
273 procedure(sub_e_to_ei), pointer :: mhd_e_to_ei_base => null()
274 ! Public methods
275 public :: usr_mask_ambipolar
276 public :: mhd_phys_init
277 public :: mhd_get_v
278 public :: mhd_get_rho
279 public :: mhd_e_to_ei
280 public :: mhd_ei_to_e
282 public :: mhd_face_to_center
283 public :: get_divb
284 public :: get_current
285 !> needed public if we want to use the ambipolar coefficient in the user file
286 public :: multiplyambicoef
287 public :: get_normalized_divb
289 public :: mhd_mag_en_all
291 public :: mhd_uawsom_rho2_factor
294 {^nooned
296 }
297 ! Begin: following relevant for radiative MHD using FLD
298 ! first four are local and only of interest for mod_usr applications
299 ! where they can be used in diagnostics
300 ! NOTE those with _prim expect primitives on entry
302 public :: mhd_get_csrad2
303 public :: mhd_get_trad
305 ! as pointer phys_get_csrad2
306 public :: mhd_get_csrad2_prim
307 ! End: following relevant for radiative MHD using FLD
308 ! Removed orphan public declarations: mhd_get_Rfactor, mhd_get_temperature_from_prim,
309 ! mhd_get_temperature_from_etot. These functions live in mod_mhd_eos.t and are
310 ! reached via the eos% / phys_get_Rfactor / phys_get_tgas / phys_get_temperature
311 ! procedure pointers bound by mod_mhd_eos:bind_eos_to_source.
312
313contains
314
315 !> Read this module"s parameters from a file
316 subroutine mhd_read_params(files)
318 use mod_particles, only: particles_eta, particles_etah
319 character(len=*), intent(in) :: files(:)
320 integer :: n
321
322 namelist /mhd_list/ mhd_energy, mhd_n_tracer, mhd_adiab,&
326 typedivbdiff, type_ct, divbwave, &
329 particles_eta, particles_etah,has_equi_rho_and_p,mhd_equi_thermal,&
344
345 do n = 1, size(files)
346 open(unitpar, file=trim(files(n)), status="old")
347 read(unitpar, mhd_list, end=111)
348111 close(unitpar)
349 end do
350
351 ! He_abundance is set in eos_list and accessed via eos%He_abundance
352
353 end subroutine mhd_read_params
354
355 !> Write this module's parameters to a snapsoht
356 subroutine mhd_write_info(fh)
358 integer, intent(in) :: fh
359
360 integer :: er
361 integer, parameter :: n_par = 1
362 double precision :: values(n_par)
363 integer, dimension(MPI_STATUS_SIZE) :: st
364 character(len=name_len) :: names(n_par)
365
366 call mpi_file_write(fh, n_par, 1, mpi_integer, st, er)
367
368 names(1) = "gamma"
369 values(1) = eos%gamma
370 call mpi_file_write(fh, values, n_par, mpi_double_precision, st, er)
371 call mpi_file_write(fh, names, n_par * name_len, mpi_character, st, er)
372 end subroutine mhd_write_info
373
374 subroutine mhd_phys_init()
379 use mod_gravity, only: gravity_init
384 use mod_cak_force, only: cak_init
386 use mod_geometry
388 {^nooned
390 }
391 use mod_fld
392
393 integer :: itr, idir
394
395 call mhd_read_params(par_files)
397
398 if(mhd_internal_e) then
399 if(mhd_hydrodynamic_e) then
400 mhd_hydrodynamic_e=.false.
401 if(mype==0) write(*,*) 'WARNING: set mhd_hydrodynamic_e=F when mhd_internal_e=T'
402 end if
403 if(has_equi_rho_and_p) then
404 has_equi_rho_and_p=.false.
405 if(mype==0) write(*,*) 'WARNING: set has_equi_rho_and_p=F when mhd_internal_e=T'
406 end if
407 end if
408
409 if(mhd_hydrodynamic_e) then
410 if(mhd_internal_e) then
411 mhd_internal_e=.false.
412 if(mype==0) write(*,*) 'WARNING: set mhd_internal_e=F when mhd_hydrodynamic_e=T'
413 end if
414 if(b0field) then
415 b0field=.false.
416 if(mype==0) write(*,*) 'WARNING: set B0field=F when mhd_hydrodynamic_e=T'
417 end if
418 if(has_equi_rho_and_p) then
419 has_equi_rho_and_p=.false.
420 if(mype==0) write(*,*) 'WARNING: set has_equi_rho_and_p=F when mhd_hydrodynamic_e=T'
421 end if
422 end if
423
424 if(mhd_semirelativistic) then
425 ! B0 splitting follows Gombosi et al. 2002 eqs (92)-(97); its j0xB0 and E.j0 sources carry
426 ! no factor of c and are the ones add_source_B0split applies
427 if(b0field .and. .not.mhd_energy) &
428 call mpistop('B0 splitting needs mhd_energy=T when mhd_semirelativistic=T')
429 ! mhd_add_source_geom_semirelati carries no eq (97) split of the BB stress
430 if(b0field .and. coordinate/=cartesian) &
431 call mpistop('B0 splitting with mhd_semirelativistic=T needs Cartesian geometry')
432 ! the momentum inversion reads B0, and bc_prolong reaches it on the coarse buffer
433 if(b0field) b0fieldalloccoarse=.true.
434 if(has_equi_rho_and_p) then
435 has_equi_rho_and_p=.false.
436 if(mype==0) write(*,*) 'WARNING: set has_equi_rho_and_p=F when mhd_semirelativistic=T'
437 end if
438 if(mhd_hydrodynamic_e) then
439 mhd_hydrodynamic_e=.false.
440 if(mype==0) write(*,*) 'WARNING: set mhd_hydrodynamic_e=F when mhd_semirelativistic=T'
441 end if
442 end if
443
444 if(.not. mhd_energy) then
445 if(mhd_internal_e) then
446 mhd_internal_e=.false.
447 if(mype==0) write(*,*) 'WARNING: set mhd_internal_e=F when mhd_energy=F'
448 end if
449 if(mhd_hydrodynamic_e) then
450 mhd_hydrodynamic_e=.false.
451 if(mype==0) write(*,*) 'WARNING: set mhd_hydrodynamic_e=F when mhd_energy=F'
452 end if
455 if(mype==0) write(*,*) 'WARNING: set mhd_thermal_conduction=F when mhd_energy=F'
456 end if
457 if(mhd_hyperbolic_tc) then
458 mhd_hyperbolic_tc=.false.
459 if(mype==0) write(*,*) 'WARNING: set mhd_hyperbolic_tc=F when mhd_energy=F'
460 end if
461 if(mhd_radiative_cooling) then
463 if(mype==0) write(*,*) 'WARNING: set mhd_radiative_cooling=F when mhd_energy=F'
464 end if
465 if(mhd_trac) then
466 mhd_trac=.false.
467 if(mype==0) write(*,*) 'WARNING: set mhd_trac=F when mhd_energy=F'
468 end if
469 if(b0field) then
470 b0field=.false.
471 if(mype==0) write(*,*) 'WARNING: set B0field=F when mhd_energy=F'
472 end if
473 if(has_equi_rho_and_p) then
474 has_equi_rho_and_p=.false.
475 if(mype==0) write(*,*) 'WARNING: set has_equi_rho_and_p=F when mhd_energy=F'
476 end if
477 end if
478
481 if(mype==0) write(*,*) 'WARNING: set either parabolic TC or hyperbolic TC to F'
482 if(mype==0) write(*,*) 'WARNING: defaulting to only mhd_hyperbolic_tc=T'
483 end if
484 {^ifoned
486 call mpistop("mhd_hyperbolic_tc_use_perp is not supported in 1D")
487 end if
488 }
489
490 physics_type = "mhd"
491 phys_energy=mhd_energy
492 phys_internal_e=mhd_internal_e
495
496 phys_gamma = eos%gamma
498
499 if(mhd_energy) then
501 total_energy=.false.
502 else
504 total_energy=.true.
505 end if
506 else
507 total_energy=.false.
508 end if
509 phys_total_energy=total_energy
510 if(mhd_energy) then
511 if(mhd_internal_e) then
512 gravity_energy=.false.
513 else
514 gravity_energy=.true.
515 end if
516 else
517 gravity_energy=.false.
518 end if
519
520 {^ifoned
521 if(mhd_trac .and. mhd_trac_type .gt. 2) then
523 if(mype==0) write(*,*) 'WARNING: reset mhd_trac_type=1 for 1D simulation'
524 end if
525 }
526 if(mhd_trac .and. mhd_trac_type .le. 4) then
527 mhd_trac_mask=bigdouble
528 if(mype==0) write(*,*) 'WARNING: set mhd_trac_mask==bigdouble for global TRAC method'
529 end if
530 if(mhd_trac .and. mhd_trac_type==7) then
531 ! per-cell local TRAC (Johnston 2021) needs the background heating rate for the
532 ! steady-state balance; it is field-aligned and per-cell, so no global trac_mask is used.
533 if(.not. associated(usr_get_heating)) &
534 call mpistop("mhd_trac_type=7 requires usr_get_heating to be set in mod_usr.t")
535 end if
537
539 if(ndim==1) typedivbfix='none'
540 select case (typedivbfix)
541 case ('none')
542 type_divb = divb_none
543 {^nooned
544 case ('multigrid')
545 if(mhd_radiation_fld) call mpistop('To verify whether mg usage for FLD versus divB can be combined')
546 type_divb = divb_multigrid
547 use_multigrid = .true.
548 mg%operator_type = mg_laplacian
549 phys_global_source_after => mhd_clean_divb_multigrid
550 }
551 case ('glm')
552 mhd_glm = .true.
553 need_global_cmax = .true.
554 type_divb = divb_glm
555 case ('powel', 'powell')
556 type_divb = divb_powel
557 case ('janhunen')
558 type_divb = divb_janhunen
559 case ('linde')
560 type_divb = divb_linde
561 case ('lindejanhunen')
562 type_divb = divb_lindejanhunen
563 case ('lindepowel')
564 type_divb = divb_lindepowel
565 case ('lindeglm')
566 mhd_glm = .true.
567 need_global_cmax = .true.
568 type_divb = divb_lindeglm
569 case ('ct')
570 type_divb = divb_ct
571 stagger_grid = .true.
572 case default
573 call mpistop('Unknown divB fix')
574 end select
575
576 allocate(start_indices(number_species),stop_indices(number_species))
577 ! set the index of the first flux variable for species 1
578 start_indices(1)=1
579 ! Determine flux variables
580 rho_ = var_set_rho()
581
582 allocate(mom(ndir))
583 mom(:) = var_set_momentum(ndir)
584 m^c_=mom(^c);
585
586 ! Set index of energy variable
587 if (mhd_energy) then
588 nwwave = 8
589 e_ = var_set_energy() ! energy density
590 p_ = e_ ! gas pressure
591 else
592 nwwave = 7
593 e_ = -1
594 p_ = -1
595 end if
596
597 allocate(mag(ndir))
598 mag(:) = var_set_bfield(ndir)
599 b^c_=mag(^c);
600
601 if (mhd_glm) then
602 psi_ = var_set_fluxvar('psi', 'psi', need_bc=.false.)
603 else
604 psi_ = -1
605 end if
606
607 if(mhd_hyperbolic_tc) then
608 qpar_ = var_set_fluxvar('q', 'q', need_bc=.false.)
610 qperp_ = var_set_fluxvar('qperp', 'qperp', need_bc=.false.)
611 else
612 qperp_ = -1
613 end if
614 !need_global_cmax=.true.
615 else
616 qpar_ = -1
617 qperp_ = -1
618 end if
619
620 if (mhd_fip) then
621 fip_ = var_set_fluxvar('rho_fip', 'fip', need_bc=.false.)
622 else
623 fip_ = -1
624 end if
625
626 if (mhd_uawsom) then
627 waplus_ = var_set_fluxvar('wAplus', 'wAplus')
628 waminus_ = var_set_fluxvar('wAminus', 'wAminus')
629 wkplus_ = var_set_fluxvar('wkplus', 'wkplus')
630 wkminus_ = var_set_fluxvar('wkminus', 'wkminus')
631 else
632 waplus_=-1; waminus_=-1; wkplus_=-1; wkminus_=-1
633 end if
634
635 allocate(tracer(mhd_n_tracer))
636 ! Set starting index of tracers
637 do itr = 1, mhd_n_tracer
638 tracer(itr) = var_set_fluxvar("trc", "trp", itr, need_bc=.false.)
639 end do
640
641 if(mhd_radiation_fld)then
642 if(mhd_cak_force)then
643 if(mype==0) then
644 write(*,*)'Warning: CAK force addition together with FLD radiation'
645 endif
646 endif
648 if(mype==0) then
649 write(*,*)'Warning: Optically thin cooling together with FLD radiation'
650 endif
651 endif
652 if(.not.mhd_energy)then
653 call mpistop('using FLD implies the use of an energy equation, set mhd_energy=T')
654 else
656 call mpistop('using FLD not yet with semirelativistic energy formalism')
657 endif
659 call mpistop('using FLD not yet with hydrodynamic or internal energy formalism')
660 endif
661 if(has_equi_rho_and_p)then
662 call mpistop('using FLD not yet with split off rho and p')
663 endif
664 ! Note: so far ok with total energy equation but allow both split or unsplit B0
665 !> set added variable and equation for radiation energy
666 r_e = var_set_radiation_energy()
667 phys_get_csrad2 => mhd_get_csrad2_prim
668 !> Radiation fluid object: its EoS callbacks are wired in mhd_link_eos
669 allocate(fld_fl)
670 !> Initiate radiation-closure module
671 call fld_init(eos%eos_type, eos%ionE, eos%pi_table)
672 !> The implicit (MG diffusion) hooks need the fld_fl object, so they
673 !> are wired here to physics-module wrappers that inject it.
674 if(use_multigrid)then
675 phys_implicit_update => mhd_fld_implicit_update
676 phys_evaluate_implicit => mhd_fld_evaluate_implicit
677 endif
678 endif
679 else
680 r_e=-1
681 endif
682
683 ! LTE/PI electron-density and temperature aux variables. These must be registered
684 ! after every flux variable (including the FLD radiation energy r_e and any tracers):
685 ! var_set_radiation_energy indexes by nwflux, so if r_e is registered after these
686 ! nw-indexed aux vars its nwflux slot collides with Ne_ (iw_r_e==iw_ne) and overwrites
687 ! its name. Registering them here keeps the flux block contiguous.
688 if (eos%eos_type == 'LTE') then
689 ne_ = var_set_ne()
690 te_ = var_set_te()
691 else if (eos%eos_type == 'PI') then
692 ne_ = -1
693 te_ = var_set_te()
694 else
695 ne_ = -1
696 te_ = -1
697 end if
698
699 ! The radiative transfer reads &rt_list and registers its output variables here, and is set up
700 ! in mhd_check_params once the units and the domain are known
702 if(.not. mhd_energy) call mpistop("radiative transfer needs mhd_energy=T")
703 call rt_sc_activate()
704 end if
705
706 ! set number of variables which need update ghostcells
707 ! set number of variables which need update ghostcells.
708 ! The EoS-derived slots Ne/Te are derived state that must stay consistent with (rho,e)
709 ! wherever the conserved state is valid, so they have to travel with it. var_set_ne /
710 ! var_set_te bump nw but neither nwflux nor nwaux, so the historic nwflux+nwaux silently
711 ! excluded them: they were never communicated, only derived, and every ghost cell held
712 ! zero until something derived it. Extend the window to whichever of them exist (they are
713 ! registered contiguously just above); FI leaves both at -1 and the window is unchanged.
714 ! bc_phys is unaffected -- it iterates nwflux+nwaux independently.
715 nwgc=nwflux+nwaux
716 if (iw_ne > 0) nwgc = max(nwgc, iw_ne)
717 if (iw_te > 0) nwgc = max(nwgc, iw_te)
718
719 ! set the index of the last flux variable for species 1
720 stop_indices(1)=nwflux
721
722 ! set cutoff temperature when using the TRAC method, as well as an auxiliary weight
723 tweight_ = -1
724 if(mhd_trac) then
725 tcoff_ = var_set_wextra()
726 iw_tcoff=tcoff_
727 if(mhd_trac_type .ge. 3) then
728 tweight_ = var_set_wextra()
729 endif
730 else
731 tcoff_ = -1
732 end if
733
734 ! set indices of equi vars and update number_equi_vars
736 if(has_equi_rho_and_p) then
739 iw_equi_rho = equi_rho0_
742 iw_equi_p = equi_pe0_
743 endif
744 ! determine number of stagger variables
745 nws=ndim
746
747 nvector = 2 ! No. vector vars
748 allocate(iw_vector(nvector))
749 iw_vector(1) = mom(1) - 1
750 iw_vector(2) = mag(1) - 1
751
752 ! Check whether custom flux types have been defined
753 if (.not. allocated(flux_type)) then
754 allocate(flux_type(ndir, nwflux))
755 flux_type = flux_default
756 else if (any(shape(flux_type) /= [ndir, nwflux])) then
757 call mpistop("phys_check error: flux_type has wrong shape")
758 end if
759
760 if(nwflux>mag(ndir)) then
761 ! for flux of tracers, using hll flux
762 flux_type(:,mag(ndir)+1:nwflux)=flux_hll
763 end if
764
765 if(ndim>1) then
766 if(mhd_glm) then
767 flux_type(:,psi_)=flux_special
768 do idir=1,ndir
769 flux_type(idir,mag(idir))=flux_special
770 end do
771 else
772 do idir=1,ndir
773 flux_type(idir,mag(idir))=flux_tvdlf
774 end do
775 end if
776 end if
777
778 phys_get_rho => mhd_get_rho
779 phys_get_dt => mhd_get_dt
780 if(mhd_semirelativistic) then
781 if(mhd_energy) then
782 phys_get_cmax => mhd_get_cmax_semirelati
783 else
784 phys_get_cmax => mhd_get_cmax_semirelati_noe
785 end if
786 else
787 if(mhd_energy) then
788 phys_get_cmax => mhd_get_cmax_origin
789 else
790 phys_get_cmax => mhd_get_cmax_origin_noe
791 end if
792 end if
793 phys_get_tcutoff => mhd_get_tcutoff
794 phys_get_h_speed => mhd_get_h_speed
795 if(has_equi_rho_and_p) then
796 phys_get_cbounds => mhd_get_cbounds_split_rho
797 else if(mhd_semirelativistic) then
798 phys_get_cbounds => mhd_get_cbounds_semirelati
799 else
800 phys_get_cbounds => mhd_get_cbounds
801 end if
802
803 !> EOS module: phys_to_primitive / phys_to_conserved are bound by
804 !> mod_mhd_eos:bind_eos_to_source to route through the EoS layer;
805 !> mhd_to_primitive / mhd_to_conserved pointers are NOT used - every
806 !> call to convert state goes through eos%to_primitive / eos%to_conserved.
807 !> eos%inv_squared_c{0,} are set in mhd_physical_units (called below)
808 !> after inv_squared_c{0,} are computed.
809 if(mhd_hydrodynamic_e) then
810 phys_get_flux => mhd_get_flux_hde
811 else if(mhd_semirelativistic) then
812 if(mhd_energy) then
813 phys_get_flux => mhd_get_flux_semirelati
814 else
815 phys_get_flux => mhd_get_flux_semirelati_noe
816 end if
817 else
818 if(b0field.or.has_equi_rho_and_p) then
819 phys_get_flux => mhd_get_flux_split
820 else if(mhd_energy) then
821 phys_get_flux => mhd_get_flux
822 else
823 phys_get_flux => mhd_get_flux_noe
824 end if
825 end if
826 phys_get_v => mhd_get_v
827 if(mhd_semirelativistic) then
828 phys_add_source_geom => mhd_add_source_geom_semirelati
829 else if(b0field.or.has_equi_rho_and_p) then
830 phys_add_source_geom => mhd_add_source_geom_split
831 else
832 phys_add_source_geom => mhd_add_source_geom
833 end if
834 phys_add_source => mhd_add_source
835 phys_check_params => mhd_check_params
836 phys_write_info => mhd_write_info
837
838 if(mhd_internal_e) then
839 phys_handle_small_values => mhd_handle_small_values_inte
840 mhd_handle_small_values => mhd_handle_small_values_inte
841 phys_check_w => mhd_check_w_inte
842 else if(mhd_hydrodynamic_e) then
843 phys_handle_small_values => mhd_handle_small_values_hde
844 mhd_handle_small_values => mhd_handle_small_values_hde
845 phys_check_w => mhd_check_w_hde
846 else if(mhd_semirelativistic) then
847 phys_handle_small_values => mhd_handle_small_values_semirelati
848 mhd_handle_small_values => mhd_handle_small_values_semirelati
849 phys_check_w => mhd_check_w_semirelati
850 else if(has_equi_rho_and_p) then
851 phys_handle_small_values => mhd_handle_small_values_split
852 mhd_handle_small_values => mhd_handle_small_values_split
853 phys_check_w => mhd_check_w_split
854 else if(mhd_energy) then
855 phys_handle_small_values => mhd_handle_small_values_origin
856 mhd_handle_small_values => mhd_handle_small_values_origin
857 phys_check_w => mhd_check_w_origin
858 else
859 phys_handle_small_values => mhd_handle_small_values_noe
860 mhd_handle_small_values => mhd_handle_small_values_noe
861 phys_check_w => mhd_check_w_noe
862 end if
863
864 ! phys_get_pthermal is set by mhd_link_eos
865
866 if(number_equi_vars>0) then
867 phys_set_equi_vars => set_equi_vars_grid
868 endif
869
870 if(type_divb==divb_glm) then
871 phys_modify_wlr => mhd_modify_wlr
872 end if
873
874 ! Rfactor / temperature / pthermal pointers are bound by
875 ! mod_mhd_eos:bind_eos_to_source (called by mhd_link_eos immediately
876 ! after mhd_phys_init). No EoS machinery in mod_mhd_phys.t.
877
878 ! if using ct stagger grid, boundary divb=0 is not done here
879 if(stagger_grid) then
880 select case(type_ct)
881 case('average')
882 transverse_ghost_cells = 1
883 phys_get_ct_velocity => mhd_get_ct_velocity_average
884 phys_update_faces => mhd_update_faces_average
885 case('uct_contact')
886 transverse_ghost_cells = 1
887 phys_get_ct_velocity => mhd_get_ct_velocity_contact
888 phys_update_faces => mhd_update_faces_contact
889 case('uct_hll')
890 transverse_ghost_cells = 2
891 phys_get_ct_velocity => mhd_get_ct_velocity_hll
892 phys_update_faces => mhd_update_faces_hll
893 case default
894 call mpistop('choose average, uct_contact,or uct_hll for type_ct!')
895 end select
896 phys_face_to_center => mhd_face_to_center
897 phys_modify_wlr => mhd_modify_wlr
898 else if(ndim>1) then
899 phys_boundary_adjust => mhd_boundary_adjust
900 end if
901
902 {^nooned
903 ! clean initial divb
905 call mpistop('To verify whether mg usage for FLD versus divB can be combined')
906 if(clean_initial_divb) phys_clean_divb => mhd_clean_divb_multigrid
907 }
908
909 ! derive units from basic units
910 call mhd_physical_units()
911
912 if(mhd_uawsom) then
913 ! Namelist inputs are physical quantities, following the normal AMRVAC
914 ! convention for this module (cm/G in cgs, m/T in SI).
915 if(mhd_uawsom_zeta_scale == -one) &
916 mhd_uawsom_zeta_scale=merge(3.4805d9,3.4805d11,si_unit)
917 if(mhd_uawsom_thread_radius0 == -one) &
918 mhd_uawsom_thread_radius0=merge(1.d5,1.d7,si_unit)
919 if(mhd_uawsom_bref == -one) &
920 mhd_uawsom_bref=merge(1.d-3,10.d0,si_unit)
922 mhd_uawsom_alfven_corr_length0=merge(1.5d7,1.5d9,si_unit)
928 end if
929
930 htc_perp_mode_id=htc_perp_disabled
931 if(mhd_hyperbolic_tc) then
932 if(mhd_hyperbolic_tc_kappa==0.d0) then
933 if(si_unit) then
935 else
937 end if
938 else
940 end if
941
943 select case(trim(mhd_hyperbolic_tc_perp_mode))
944 case('fixed_reference')
945 htc_perp_mode_id=htc_perp_fixed
947 if(si_unit) then
950 else
953 end if
954 end if
955 case('weak_field_isotropization')
956 htc_perp_mode_id=htc_perp_weak_field
957 if(mhd_hyperbolic_tc_bmin==0.d0) then
959 end if
960 case('electron_magnetization')
961 htc_perp_mode_id=htc_perp_electron
962 if(mhd_hyperbolic_tc_coulomb_log<=zero) then
963 call mpistop("mhd_hyperbolic_tc_coulomb_log must be positive")
964 end if
965 case default
966 call mpistop("invalid mhd_hyperbolic_tc_perp_mode: "// &
968 end select
969 end if
970 end if
971 if(.not. mhd_energy .and. mhd_thermal_conduction) then
972 call mpistop("thermal conduction needs mhd_energy=T")
973 end if
974 if(.not. mhd_energy .and. mhd_hyperbolic_tc) then
975 call mpistop("hyperbolic thermal conduction needs mhd_energy=T")
976 end if
977 if(.not. mhd_energy .and. mhd_radiative_cooling) then
978 call mpistop("radiative cooling needs mhd_energy=T")
979 end if
980
981 !> Cache log10(nH) in wextra for LTE+IonE TC (density invariant during STS)
982 if (eos%eos_type == 'LTE' .and. eos%ionE .and. mhd_thermal_conduction) then
983 iw_log_nh = var_set_wextra()
984 end if
985
986 if(mhd_equi_thermal)then
987 if((.not.has_equi_rho_and_p).or.(.not.total_energy))then
988 mhd_equi_thermal=.false.
989 if(mype==0) write(*,*) 'WARNING: turning mhd_equi_thermal=F as no splitting or total e in use'
990 else
992 if(mype==0) write(*,*) 'Will subtract thermal balance in TC or RC with mhd_equi_thermal=T'
993 else
994 mhd_equi_thermal=.false.
995 if(mype==0) write(*,*) 'WARNING: turning mhd_equi_thermal=F as no TC or RC in use'
996 endif
997 endif
998 endif
999
1000 ! Energy conversion pointers needed by EOS module regardless of TC method
1001 if(mhd_internal_e) then
1002 phys_e_to_ei => mhd_ei_unchanged
1003 phys_ei_to_e => mhd_ei_unchanged
1004 else if(mhd_hydrodynamic_e) then
1005 phys_e_to_ei => mhd_e_to_ei_hde
1006 phys_ei_to_e => mhd_ei_to_e_hde
1007 else if(mhd_semirelativistic) then
1008 phys_e_to_ei => mhd_e_to_ei_semirelati
1009 phys_ei_to_e => mhd_ei_to_e_semirelati
1010 else
1011 phys_e_to_ei => mhd_e_to_ei
1012 phys_ei_to_e => mhd_ei_to_e
1013 end if
1014 ! The LTE conduction temperature reads a log10(nH) cache written with each conversion
1015 if(iw_log_nh > 0 .and. .not.mhd_hydrodynamic_e) then
1016 mhd_e_to_ei_base => phys_e_to_ei
1017 phys_e_to_ei => mhd_e_to_ei_cache_log_nh
1018 end if
1019
1020 ! initialize thermal conduction module
1021 if (mhd_thermal_conduction) then
1022 call sts_init()
1023 call tc_init_params(eos%gamma)
1024
1025 allocate(tc_fl)
1026 call tc_get_mhd_params(tc_fl,tc_params_read_mhd)
1027 if(ndim==1) then
1028 call add_sts_method(mhd_get_tc_dt_hd,mhd_sts_set_source_tc_hd,e_,1,e_,1,.false.)
1029 else
1030 call add_sts_method(mhd_get_tc_dt_mhd,mhd_sts_set_source_tc_mhd,e_,1,e_,1,.false.)
1031 end if
1032 if(.not.mhd_internal_e) call set_conversion_methods_to_head(phys_e_to_ei, phys_ei_to_e)
1033 call set_error_handling_to_head(mhd_tc_handle_small_e)
1034 tc_fl%e_ = e_
1035 tc_fl%Tcoff_ = tcoff_
1036 end if
1037
1038
1039 ! Initialize radiative cooling module
1040 if (mhd_radiative_cooling) then
1041 call radiative_cooling_init_params(eos%gamma,eos%He_abundance)
1042 allocate(rc_fl)
1043 rc_fl%fip_ = fip_
1044 call radiative_cooling_init(rc_fl,rc_params_read)
1045 rc_fl%e_ = e_
1046 rc_fl%Tcoff_ = tcoff_
1047 rc_fl%subtract_equi = has_equi_rho_and_p
1048 ! rc_fl EoS pointers (get_rho, get_pthermal, get_var_Rfactor,
1049 ! equi accessors, subtract_equi) are bound by bind_eos_to_source
1050 ! in mod_mhd_eos.t. No EoS machinery in mod_mhd_phys.t.
1051 end if
1052 allocate(te_fl_mhd)
1053 ! te_fl_mhd EoS pointers are bound by bind_eos_to_source in mod_mhd_eos.t
1054{^ifthreed
1055 phys_te_images => mhd_te_images
1056}
1057
1058 ! consistency check for hyperresistivity implementation
1059 if (mhd_eta_hyper>0.0d0) then
1060 if(mype==0) then
1061 write(*,*) '*****Using hyperresistivity: with mhd_eta_hyper :', mhd_eta_hyper
1062 endif
1063 if(b0field) then
1064 ! hyperresistivity not ok yet with splitting
1065 call mpistop("Must have B0field=F when using hyperresistivity")
1066 end if
1067 endif
1068 if (mhd_eta_hyper<0.0d0) then
1069 call mpistop("Must have mhd_eta_hyper positive when using hyperresistivity")
1070 endif
1071
1072 ! Initialize viscosity module
1073 if (mhd_viscosity) then
1074 call viscosity_init(phys_wider_stencil)
1075 end if
1076
1077 ! Initialize gravity module
1078 if(mhd_gravity) then
1079 call gravity_init()
1080 end if
1081
1082 ! Initialize rotating frame module
1083 if(mhd_rotating_frame) then
1084 if(has_equi_rho_and_p) then
1085 ! mod_rotating_frame does not handle splitting of density
1086 call mpistop("Must have has_equi_rho_and_p=F when mhd_rotating_frame=T")
1087 end if
1088 call rotating_frame_init()
1089 endif
1090
1091
1092 ! initialize magnetofriction module
1093 if(mhd_magnetofriction) then
1095 end if
1096
1097 if(mhd_hall) then
1098 if(mhd_semirelativistic) then
1099 ! semirelativistic does not incorporate hall terms
1100 call mpistop("Must have mhd_hall=F when mhd_semirelativistic=T")
1101 end if
1102 if(coordinate>1)then
1103 ! normal unsplit case or split cases do not have geometric sources for Hall included
1104 call mpistop("Must have Cartesian coordinates for Hall")
1105 endif
1106 ! For Hall, we need one more reconstructed layer since currents are computed
1107 ! in mhd_get_flux: assuming one additional ghost layer added in nghostcells.
1108 phys_wider_stencil = 1
1109 end if
1110
1111 ! The perpendicular HTC geometry evaluates a fourth-order centred
1112 ! temperature gradient inside the reconstructed flux layer. It therefore
1113 ! needs one layer beyond the default two ghost cells.
1115 phys_wider_stencil=max(phys_wider_stencil,1)
1116 end if
1117
1118 if(mhd_ambipolar) then
1119 if(mhd_ambipolar_sts) then
1120 call sts_init()
1122 call add_sts_method(get_ambipolar_dt,sts_set_source_ambipolar,mag(1),&
1123 ndir,mag(1),ndir,.true.)
1124 else
1125 ! any total energy or no energy at all case is handled here
1126 call add_sts_method(get_ambipolar_dt,sts_set_source_ambipolar,mom(ndir)+1,&
1127 mag(ndir)-mom(ndir),mag(1),ndir,.true.)
1128 end if
1129 else
1130 mhd_ambipolar_exp=.true.
1131 ! For flux ambipolar term, we need one more reconstructed layer since currents are computed
1132 ! in mhd_get_flux: assuming one additional ghost layer added in nghostcells.
1133 phys_wider_stencil = 1
1134 end if
1135 end if
1136
1137 ! ionization-degree table init now lives in eos_finalise (eos% owns
1138 ! thermodynamic-backend init, parallel to LTE tables); see mod_eos_pi.
1139
1140 ! Initialize CAK radiation force module
1141 if (mhd_cak_force) then
1143 call mpistop("CAK implementation not available in internal or semirelativistic variants")
1144 endif
1145 if(has_equi_rho_and_p) then
1146 call mpistop("CAK force implementation not available for split off pressure and density")
1147 endif
1148 call cak_init(eos%gamma)
1149 endif
1150
1151 end subroutine mhd_phys_init
1152
1153{^ifthreed
1154 subroutine mhd_te_images
1157
1158 select case(convert_type)
1159 case('EIvtiCCmpi','EIvtuCCmpi')
1161 case('ESvtiCCmpi','ESvtuCCmpi')
1163 case('SIvtiCCmpi','SIvtuCCmpi')
1165 case('WIvtiCCmpi','WIvtuCCmpi')
1167 case default
1168 call mpistop("Error in synthesize emission: Unknown convert_type")
1169 end select
1170 end subroutine mhd_te_images
1171}
1172
1173!!start th cond
1174 ! wrappers for STS functions in thermal_conductivity module
1175 ! which take as argument the tc_fluid (defined in the physics module)
1176 subroutine mhd_sts_set_source_tc_mhd(ixI^L,ixO^L,w,x,wres,fix_conserve_at_step,my_dt,igrid,nflux)
1180 integer, intent(in) :: ixi^l, ixo^l, igrid, nflux
1181 double precision, intent(in) :: x(ixi^s,1:ndim)
1182 double precision, intent(inout) :: wres(ixi^s,1:nw), w(ixi^s,1:nw)
1183 double precision, intent(in) :: my_dt
1184 logical, intent(in) :: fix_conserve_at_step
1185 call sts_set_source_tc_mhd(ixi^l,ixo^l,w,x,wres,fix_conserve_at_step,my_dt,igrid,nflux,tc_fl)
1186 end subroutine mhd_sts_set_source_tc_mhd
1187
1188 subroutine mhd_sts_set_source_tc_hd(ixI^L,ixO^L,w,x,wres,fix_conserve_at_step,my_dt,igrid,nflux)
1192 integer, intent(in) :: ixi^l, ixo^l, igrid, nflux
1193 double precision, intent(in) :: x(ixi^s,1:ndim)
1194 double precision, intent(inout) :: wres(ixi^s,1:nw), w(ixi^s,1:nw)
1195 double precision, intent(in) :: my_dt
1196 logical, intent(in) :: fix_conserve_at_step
1197 call sts_set_source_tc_hd(ixi^l,ixo^l,w,x,wres,fix_conserve_at_step,my_dt,igrid,nflux,tc_fl)
1198 end subroutine mhd_sts_set_source_tc_hd
1199
1200 function mhd_get_tc_dt_mhd(w,ixI^L,ixO^L,dx^D,x) result(dtnew)
1201 !Check diffusion time limit dt < dx_i**2/((gamma-1)*tc_k_para_i/rho)
1202 !where tc_k_para_i=tc_k_para*B_i**2/B**2
1203 !and T=p/rho
1206
1207 integer, intent(in) :: ixi^l, ixo^l
1208 double precision, intent(in) :: dx^d, x(ixi^s,1:ndim)
1209 double precision, intent(in) :: w(ixi^s,1:nw)
1210 double precision :: dtnew
1211
1212 dtnew=get_tc_dt_mhd(w,ixi^l,ixo^l,dx^d,x,tc_fl)
1213 end function mhd_get_tc_dt_mhd
1214
1215 function mhd_get_tc_dt_hd(w,ixI^L,ixO^L,dx^D,x) result(dtnew)
1216 !Check diffusion time limit dt < dx_i**2/((gamma-1)*tc_k_para_i/rho)
1217 !where tc_k_para_i=tc_k_para*B_i**2/B**2
1218 !and T=p/rho
1221
1222 integer, intent(in) :: ixi^l, ixo^l
1223 double precision, intent(in) :: dx^d, x(ixi^s,1:ndim)
1224 double precision, intent(in) :: w(ixi^s,1:nw)
1225 double precision :: dtnew
1226
1227 dtnew=get_tc_dt_hd(w,ixi^l,ixo^l,dx^d,x,tc_fl)
1228 end function mhd_get_tc_dt_hd
1229
1230 subroutine mhd_tc_handle_small_e(w, x, ixI^L, ixO^L, step)
1232
1233 integer, intent(in) :: ixi^l,ixo^l
1234 double precision, intent(inout) :: w(ixi^s,1:nw)
1235 double precision, intent(in) :: x(ixi^s,1:ndim)
1236 integer, intent(in) :: step
1237 character(len=140) :: error_msg
1238
1239 ! tc_patch_eint repairs w(:, e_) in place during the source-term call,
1240 ! but the Chebyshev recurrence can still produce residual negative
1241 ! e_int in the updated state. Run mhd_handle_small_ei every substep
1242 ! (patch on or off) so STS does not propagate negative e through.
1243 write(error_msg,"(a,i3)") "Thermal conduction step ", step
1244 call mhd_handle_small_ei(w,x,ixi^l,ixo^l,e_,error_msg)
1245 end subroutine mhd_tc_handle_small_e
1246
1247 ! fill in tc_fluid fields from namelist
1248 subroutine tc_params_read_mhd(fl)
1250 type(tc_fluid), intent(inout) :: fl
1251
1252 double precision :: tc_k_para=0d0
1253 double precision :: tc_k_perp=0d0
1254 integer :: n
1255 ! list parameters
1256 logical :: tc_perpendicular=.false.
1257 logical :: tc_saturate=.false.
1258 logical :: tc_patch_eint=.false.
1259 logical :: tc_frozen_ion=.true.
1260 double precision :: trac_t_floor=0.d0
1261 character(len=std_len) :: tc_slope_limiter="MC"
1262
1263 namelist /tc_list/ tc_perpendicular, tc_saturate, tc_slope_limiter, tc_k_para, tc_k_perp, &
1264 tc_patch_eint, trac_t_floor, tc_frozen_ion
1265
1266 do n = 1, size(par_files)
1267 open(unitpar, file=trim(par_files(n)), status="old")
1268 read(unitpar, tc_list, end=111)
1269111 close(unitpar)
1270 end do
1271
1272 fl%tc_perpendicular = tc_perpendicular
1273 fl%tc_saturate = tc_saturate
1274 fl%tc_patch_eint = tc_patch_eint
1275 fl%frozen_ion = tc_frozen_ion .and. .not. tc_patch_eint
1276 fl%tc_k_para = tc_k_para
1277 fl%tc_k_perp = tc_k_perp
1278 fl%trac_T_floor = trac_t_floor / unit_temperature
1279 select case(tc_slope_limiter)
1280 case ('no','none')
1281 fl%tc_slope_limiter = 0
1282 case ('MC')
1283 ! monotonized central limiter Woodward and Collela limiter (eq.3.51h)
1284 fl%tc_slope_limiter = 1
1285 case('minmod')
1286 ! minmod limiter
1287 fl%tc_slope_limiter = 2
1288 case ('superbee')
1289 ! Roes superbee limiter (eq.3.51i)
1290 fl%tc_slope_limiter = 3
1291 case ('koren')
1292 ! Barry Koren Right variant
1293 fl%tc_slope_limiter = 4
1294 case ('vanleer')
1295 ! van Leer limiter
1296 fl%tc_slope_limiter = 5
1297 case default
1298 call mpistop("Unknown tc_slope_limiter, choose MC, minmod, superbee, koren, vanleer")
1299 end select
1300 end subroutine tc_params_read_mhd
1301!!end th cond
1302
1303!!rad cool
1304 subroutine rc_params_read(fl)
1306 use mod_constants, only: bigdouble
1307 type(rc_fluid), intent(inout) :: fl
1308
1309 !> Lower limit of temperature
1310 double precision :: tlow=bigdouble
1311 double precision :: rad_damp_height=0.5d0
1312 double precision :: rad_damp_scale=0.15d0
1313 integer :: n
1314 ! list parameters
1315 integer :: ncool = 4000
1316 !> Fixed temperature not lower than tlow
1317 logical :: tfix=.false.
1318 !> Add cooling source in a split way (.true.) or un-split way (.false.)
1319 logical :: rc_split=.false.
1320 logical :: rad_damp=.false.
1321 !> Name of cooling curve
1322 character(len=std_len) :: coolcurve='JCcorona'
1323 logical :: rad_newton = .false.
1324 double precision :: rad_newton_trad = 0.006d0
1325 double precision :: rad_newton_rhosurf = 1.d4
1326 double precision :: rad_newton_pthick = 25.d0
1327 !> HEAD-side cooling parameters (missing from common content after merge)
1328 double precision :: cfrac=0.1d0
1329 double precision :: rad_cut_hgt=0.5d0
1330 double precision :: rad_cut_dey=0.15d0
1331 !> Variable-c_V Townsend extension (Y_mod): quadrature and sub-intervals
1332 character(len=8) :: rc_y_mod_quadrature='boole'
1333 integer :: rc_y_mod_n_sub=16
1334 !> Smooth SC<->thin blend (mirrors hd): thin cooling *= 0.5(1+tanh((T-T0)/dT))*0.5(1+tanh((y-y0)/dy))
1335
1336 namelist /rc_list/ coolcurve, ncool, cfrac, tlow, tfix, rc_split, &
1337 rad_cut_hgt, rad_cut_dey, &
1338 rc_y_mod_quadrature, rc_y_mod_n_sub, &
1339 rad_newton, rad_newton_trad, rad_newton_rhosurf, &
1340 rad_newton_pthick, rad_damp, rad_damp_height, rad_damp_scale
1341
1342 do n = 1, size(par_files)
1343 open(unitpar, file=trim(par_files(n)), status="old")
1344 read(unitpar, rc_list, end=111)
1345111 close(unitpar)
1346 end do
1347
1348 fl%ncool=ncool
1349 fl%coolcurve=coolcurve
1350 fl%tlow=tlow
1351 fl%Tfix=tfix
1352 fl%rc_split=rc_split
1353 fl%cfrac=cfrac
1354 fl%rad_cut_hgt=rad_cut_hgt
1355 fl%rad_cut_dey=rad_cut_dey
1356 fl%Y_mod_quadrature=rc_y_mod_quadrature
1357 fl%Y_mod_N_sub=rc_y_mod_n_sub
1358 fl%rad_damp=rad_damp
1359 fl%rad_damp_height=rad_damp_height
1360 fl%rad_damp_scale=rad_damp_scale
1361 fl%rad_newton=rad_newton
1362 fl%rad_newton_trad=rad_newton_trad
1363 fl%rad_newton_rhosurf=rad_newton_rhosurf
1364 fl%rad_newton_pthick=rad_newton_pthick
1365 end subroutine rc_params_read
1366
1367 !> sets the equilibrium variables
1368 subroutine set_equi_vars_grid_faces(igrid,x,ixI^L,ixO^L)
1370 use mod_usr_methods
1371 integer, intent(in) :: igrid, ixi^l, ixo^l
1372 double precision, intent(in) :: x(ixi^s,1:ndim)
1373
1374 double precision :: delx(ixi^s,1:ndim)
1375 double precision :: xc(ixi^s,1:ndim),xshift^d
1376 integer :: idims, ixc^l, hxo^l, ix, idims2
1377
1378 if(slab_uniform)then
1379 ^d&delx(ixi^s,^d)=rnode(rpdx^d_,igrid)\
1380 else
1381 ! for all non-cartesian and stretched cartesian coordinates
1382 delx(ixi^s,1:ndim)=ps(igrid)%dx(ixi^s,1:ndim)
1383 endif
1384
1385 do idims=1,ndim
1386 hxo^l=ixo^l-kr(idims,^d);
1387 if(stagger_grid) then
1388 ! ct needs all transverse cells
1389 ixcmax^d=ixomax^d+nghostcells-nghostcells*kr(idims,^d); ixcmin^d=hxomin^d-nghostcells+nghostcells*kr(idims,^d);
1390 else
1391 ! ixC is centered index in the idims direction from ixOmin-1/2 to ixOmax+1/2
1392 ixcmax^d=ixomax^d; ixcmin^d=hxomin^d;
1393 end if
1394 ! always xshift=0 or 1/2
1395 xshift^d=half*(one-kr(^d,idims));
1396 do idims2=1,ndim
1397 select case(idims2)
1398 {case(^d)
1399 do ix = ixc^lim^d
1400 ! xshift=half: this is the cell center coordinate
1401 ! xshift=0: this is the cell edge i+1/2 coordinate
1402 xc(ix^d%ixC^s,^d)=x(ix^d%ixC^s,^d)+(half-xshift^d)*delx(ix^d%ixC^s,^d)
1403 end do\}
1404 end select
1405 end do
1406 call usr_set_equi_vars(ixi^l,ixc^l,xc,ps(igrid)%equi_vars(ixi^s,1:number_equi_vars,idims))
1407 end do
1408 end subroutine set_equi_vars_grid_faces
1409
1410 !> sets the equilibrium variables
1411 subroutine set_equi_vars_grid(igrid)
1413 use mod_usr_methods
1414
1415 integer, intent(in) :: igrid
1416
1417 !values at the center
1418 call usr_set_equi_vars(ixg^ll,ixg^ll,ps(igrid)%x,ps(igrid)%equi_vars(ixg^t,1:number_equi_vars,0))
1419
1420 !values at the interfaces
1421 call set_equi_vars_grid_faces(igrid,ps(igrid)%x,ixg^ll,ixm^ll)
1422
1423 end subroutine set_equi_vars_grid
1424
1425 ! w, wnew conserved, add splitted variables back to wnew
1426 function convert_vars_splitting(ixI^L,ixO^L, w, x, nwc) result(wnew)
1428 integer, intent(in) :: ixi^l,ixo^l, nwc
1429 double precision, intent(in) :: w(ixi^s, 1:nw)
1430 double precision, intent(in) :: x(ixi^s,1:ndim)
1431 double precision :: wnew(ixo^s, 1:nwc)
1432
1433 if(has_equi_rho_and_p) then
1434 wnew(ixo^s,rho_)=w(ixo^s,rho_)+block%equi_vars(ixo^s,equi_rho0_,0)
1435 else
1436 wnew(ixo^s,rho_)=w(ixo^s,rho_)
1437 endif
1438 wnew(ixo^s,mom(:))=w(ixo^s,mom(:))
1439
1440 if (b0field) then
1441 ! add background magnetic field B0 to B
1442 wnew(ixo^s,mag(1:ndir))=w(ixo^s,mag(1:ndir))+block%B0(ixo^s,1:ndir,0)
1443 else
1444 wnew(ixo^s,mag(1:ndir))=w(ixo^s,mag(1:ndir))
1445 end if
1446
1447 if(mhd_energy) then
1448 wnew(ixo^s,e_)=w(ixo^s,e_)
1449 if(has_equi_rho_and_p) then
1450 wnew(ixo^s,e_)=wnew(ixo^s,e_)+block%equi_vars(ixo^s,equi_pe0_,0)*eos%inv_gamma_minus_1
1451 end if
1452 if(b0field .and. total_energy) then
1453 wnew(ixo^s,e_)=wnew(ixo^s,e_)+0.5d0*sum(block%B0(ixo^s,:,0)**2,dim=ndim+1) &
1454 + sum(w(ixo^s,mag(:))*block%B0(ixo^s,:,0),dim=ndim+1)
1455 end if
1456 end if
1457
1458 end function convert_vars_splitting
1459
1460 subroutine mhd_check_params
1462 use mod_usr_methods
1465 use mod_particles, only: particles_init, particles_eta, particles_etah
1466 use mod_particles, only: npayload,nusrpayload, &
1467 ngridvars,num_particles,physics_type_particles
1468 use mod_fld
1469
1470 double precision :: a,b,xfrac,yfrac
1471
1472 if(mhd_radiative_transfer) call rt_sc_setup()
1473
1474 ! mhd_get_cbounds_semirelati has a single estimator, and pvrs is hd only
1475 if(mhd_semirelativistic .and. mype==0 .and. (boundspeed/=2 .or. h_correction)) write(*,*) &
1476 'WARNING: typeboundspeed and H_correction are ignored', ' when mhd_semirelativistic=T'
1477 if(boundspeed==4) call mpistop("typeboundspeed='pvrs' is not implemented for mhd")
1478
1479 if(mhd_uawsom) then
1480 if(coordinate /= cartesian .or. .not.slab_uniform) &
1481 call mpistop('mhd_uawsom currently requires a uniform Cartesian grid')
1482 if(.not.mhd_energy .or. .not.total_energy) &
1483 call mpistop('mhd_uawsom requires the standard total-energy MHD formulation')
1486 call mpistop('mhd_uawsom: unsupported MHD option')
1487 if(trim(eos%eos_type) /= 'FI') &
1488 call mpistop("mhd_uawsom currently supports eos_type='FI' only")
1490 call mpistop('mhd_uawsom_height_dim must be between 1 and ndim')
1491 if(mhd_uawsom_zeta0 <= one .or. mhd_uawsom_filling_factor <= zero .or. &
1493 call mpistop('mhd_uawsom requires zeta0>1 and 0<filling_factor<1')
1494 if(mhd_uawsom_zeta_scale <= zero .or. &
1495 mhd_uawsom_thread_radius0 <= zero .or. &
1496 mhd_uawsom_alfven_corr_length0 <= zero .or. mhd_uawsom_bref <= zero) &
1497 call mpistop('mhd_uawsom: scales must be positive')
1498 if(mhd_uawsom_sigma < zero) &
1499 call mpistop('mhd_uawsom_sigma must be non-negative')
1500 select case(trim(mhd_uawsom_reflection_mode))
1501 case('one_dimensional_gradient')
1502 uawsom_reflection_id=uawsom_reflection_1d
1503 case('cartesian_gradient_vorticity')
1504 uawsom_reflection_id=uawsom_reflection_cartesian
1505 case default
1506 call mpistop('invalid mhd_uawsom_reflection_mode')
1507 end select
1508 end if
1509
1510 ! Initialize particles module here, so all extra and user vars are sample
1511 if(mhd_particles) then
1512 call particles_init()
1513 if (particles_eta < zero) particles_eta = mhd_eta
1514 if (particles_etah < zero) particles_eta = mhd_etah
1515 end if
1516
1517 ! gamma, gamma_minus_1, inv_gamma_minus_1 are set by eos_init
1518 if (.not. mhd_energy) then
1519 if (eos%gamma <= 0.0d0) call mpistop ("Error: gamma <= 0")
1520 if (mhd_adiab < 0.0d0) call mpistop ("Error: mhd_adiab < 0")
1522 else
1523 if (eos%gamma <= 0.0d0 .or. eos%gamma == 1.0d0) &
1524 call mpistop ("Error: gamma <= 0 or gamma == 1")
1525 small_e = small_pressure * eos%inv_gamma_minus_1
1526 small_r_e = small_pressure * eos%inv_gamma_minus_1
1527 end if
1528
1529 if (number_equi_vars > 0 .and. .not. associated(usr_set_equi_vars)) then
1530 call mpistop("usr_set_equi_vars has to be implemented in the user file")
1531 endif
1532
1533 if(has_equi_rho_and_p) then
1534 ! The Roe and HLLC solvers read the rho slot as a physical density, which
1535 ! under splitting holds only the perturbation. Neither has a split variant,
1536 ! so refuse the combination rather than return a quietly wrong wave speed.
1537 if(any(flux_method(:)==fs_tvdmu) .or. any(flux_method(:)==fs_hllc) &
1538 .or. any(flux_method(:)==fs_hllcd)) then
1539 call mpistop("Must have has_equi_rho_and_p=F with the roe/hllc/hllcd flux schemes")
1540 end if
1541 ! mhd_get_tcutoff forms Te from the perturbation pressure and density.
1542 if(mhd_trac) then
1543 call mpistop("Must have has_equi_rho_and_p=F when mhd_trac=T")
1544 end if
1545 end if
1546 if(convert .or. autoconvert) then
1547 if(convert_type .eq. 'dat_generic_mpi') then
1548 if(mhd_dump_full_vars) then
1549 if(mype .eq. 0) print*, " add conversion method: split -> full "
1550 call add_convert_method(convert_vars_splitting, nw, cons_wnames, "new")
1551 endif
1552 endif
1553 endif
1554
1555 if(mhd_radiation_fld) then
1556 if(.not.use_imex_scheme)then
1557 call mpistop('select IMEX scheme for FLD radiation use')
1558 endif
1559 if(use_multigrid)then
1560 call phys_set_mg_bounds()
1561 else
1562 if(.not.fld_no_mg)call mpistop('multigrid must have BCs for IMEX and FLD radiation use')
1563 endif
1564 if(mype==0)then
1565 write(*,*)'==FLD SETUP======================'
1566 write(*,*)'Using FLD with settings:'
1567 write(*,*)'Using FLD with settings: mhd_radiation_fld=',mhd_radiation_fld
1568 write(*,*)'Using FLD with settings: mhd_fld_pradtensor=',mhd_fld_pradtensor
1569 write(*,*)'Using FLD with settings: fld_fluxlimiter=',fld_fluxlimiter
1570 write(*,*)'Using FLD with settings: fld_bound_diff=',fld_bound_diff
1571 write(*,*)'Using FLD with settings: fld_interaction_method=',fld_interaction_method
1572 write(*,*)'Using FLD with settings: fld_opacity_law=',fld_opacity_law
1573 write(*,*)'Using FLD with settings: fld_kappa0=',fld_kappa0
1574 write(*,*)'Using FLD with settings: fld_opal_table=',fld_opal_table
1575 write(*,*)'Using FLD with settings: fld_Radforce_split=',fld_radforce_split
1576 write(*,*)'Using FLD with settings: fld_bisect_tol=',fld_bisect_tol
1577 write(*,*)'Using FLD with settings: fld_diff_tol=',fld_diff_tol
1578 write(*,*)'Using FLD with settings: nth_for_diff_mg=',nth_for_diff_mg
1579 write(*,*)' FLD has use_imex_scheme and use_multigrid=',use_imex_scheme,use_multigrid
1580 write(*,*)' FLD has fld_no_mg=',fld_no_mg
1581 if(fld_no_mg)then
1582 print *,'WARNING: cheating with FLD diffusion ***********************'
1583 print *,'WARNING: No MG-diffusion for radiative energy at all!!!!!!!'
1584 print *,'WARNING: cheating with FLD diffusion ***********************'
1585 endif
1586 print *,'const_rad_a =',const_rad_a
1587 print *,'NORMALIZED arad_norm=',arad_norm
1588 print *,'NORMALIZED c_norm=',c_norm
1589 if(fld_cnorm>0.0d0)then
1590 print *,'WARNING: cheating with c_norm ***********************'
1591 print *,'WARNING: c_norm reset to=',fld_cnorm
1593 print *,'WARNING: cheating with c_norm ***********************'
1594 endif
1595 print *,'const_kappae =',const_kappae
1596 if(trim(fld_opacity_law).eq.'const_norm')then
1597 print *,'NORMALIZED fld_kappa0 =',fld_kappa0
1598 print *,'physical value (in cgs or SI) =',fld_kappa0*unit_opacity
1599 endif
1600 if(trim(fld_opacity_law).eq.'const')then
1601 print *,'physical fld_kappa (in cgs or SI) =',fld_kappa0
1602 print *,'NORMALIZED value =',fld_kappa0/unit_opacity
1603 endif
1604 write(*,*)'===FLD SETUP====================='
1605 endif
1606 endif
1607
1608 if(mype==0)then
1609 write(*,*)'====MHD run with settings===================='
1610 write(*,*)'Using mod_mhd_phys with settings:'
1611 write(*,*)'SI_unit=',si_unit
1612 write(*,*)'Dimensionality :',ndim
1613 write(*,*)'vector components:',ndir
1614 write(*,*)'coordinate set to type,slab:',coordinate,slab
1615 write(*,*)'number of variables nw=',nw
1616 write(*,*)' start index iwstart=',iwstart
1617 write(*,*)'number of vector variables=',nvector
1618 write(*,*)'number of stagger variables nws=',nws
1619 write(*,*)'number of variables with BCs=',nwgc
1620 write(*,*)'number of vars with fluxes=',nwflux
1621 write(*,*)'number of vars with flux + BC=',nwfluxbc
1622 write(*,*)'number of auxiliary variables=',nwaux
1623 write(*,*)'number of extra vars without flux=',nwextra
1624 write(*,*)'number of extra vars for wextra=',nw_extra
1625 write(*,*)'number of auxiliary I/O variables=',nwauxio
1626 write(*,*)'number of mhd_n_tracer=',mhd_n_tracer
1627 write(*,*)' mhd_energy=',mhd_energy,' with total_energy=',total_energy
1628 write(*,*)' mhd_semirelativistic=',mhd_semirelativistic
1629 write(*,*)' mhd_internal_e=',mhd_internal_e
1630 write(*,*)' mhd_hydrodynamic_e=',mhd_hydrodynamic_e
1631 write(*,*)' mhd_gravity=',mhd_gravity
1632 write(*,*)' mhd_eta=',mhd_eta,' nonzero implies resistivity'
1633 write(*,*)' mhd_viscosity=',mhd_viscosity
1634 write(*,*)' mhd_radiative_cooling=',mhd_radiative_cooling
1635 write(*,*)' mhd_cak_force=',mhd_cak_force
1636 write(*,*)' mhd_radiation_fld=',mhd_radiation_fld
1637 write(*,*)' mhd_thermal_conduction=',mhd_thermal_conduction
1638 write(*,*)' mhd_hyperbolic_tc=',mhd_hyperbolic_tc
1639 write(*,*)' mhd_trac=',mhd_trac
1640 write(*,*)' mhd_hall=',mhd_hall
1641 write(*,*)' mhd_ambipolar=',mhd_ambipolar
1642 write(*,*)' mhd_eta_hyper=',mhd_eta_hyper
1643 write(*,*)' mhd_rotating_frame=',mhd_rotating_frame
1644 write(*,*)' mhd_particles=',mhd_particles
1645 if(mhd_particles) then
1646 write(*,*) '*****Using particles: with mhd_eta, mhd_etah :', mhd_eta, mhd_etah
1647 write(*,*) '*****Using particles: particles_eta, particles_etah :', particles_eta, particles_etah
1648 write(*,*) '*****Using particles: npayload,ngridvars :', npayload,ngridvars
1649 write(*,*) '*****Using particles: nusrpayload :', nusrpayload
1650 write(*,*) '*****Using particles: num_particles :', num_particles
1651 write(*,*) '*****Using particles: physics_type_particles=',physics_type_particles
1652 end if
1653 write(*,*)'number of ghostcells=',nghostcells
1654 write(*,*)'number due to phys_wider_stencil=',phys_wider_stencil
1655 write(*,*)'==========================================='
1656 print *,'========EOS and UNITS==========='
1657 print *,'SI_unit =',si_unit
1658 print *,'gamma=',eos%gamma
1659 print *,'He_abundance =',eos%He_abundance
1660 print *,'RR =',rr
1661 print *,'========EOS and UNITS==========='
1662 print *,'unit_time =',unit_time
1663 print *,'unit_length =',unit_length
1664 print *,'unit_velocity =',unit_velocity
1665 print *,'unit_pressure =',unit_pressure
1666 print *,'unit_numberdensity =',unit_numberdensity
1667 print *,'unit_density =',unit_density
1668 print *,'unit_temperature =',unit_temperature
1669 print *,'unit_mass =',unit_mass
1670 print *,'unit_Erad =',unit_erad
1671 print *,'unit_radflux =',unit_radflux
1672 print *,'unit_magneticfield =',unit_magneticfield
1673 if(si_unit)then
1674 print *,'CHECK that p_u',unit_pressure,' equals ',unit_magneticfield**2/miu0_si
1675 else
1676 print *,'CHECK that p_u',unit_pressure,' equals ',unit_magneticfield**2/(4.0d0*dpi)
1677 endif
1678 print *, 'CHECK that p_u ',unit_pressure,' equals ',unit_density*unit_velocity**2
1679 print *, 'CHECK that L_u ',unit_length,' equals ',unit_velocity*unit_time
1680 print *, 'CHECK that M_u',unit_mass,' equals ',unit_density*unit_length**3
1681 print *, 'density to numberdensity has factor ',unit_density/unit_numberdensity
1682 if(si_unit)then
1683 print *, ' compare this to ',mp_si*(1.d0+4.d0*eos%He_abundance)
1684 else
1685 print *, ' compare this to ',mp_cgs*(1.d0+4.d0*eos%He_abundance)
1686 endif
1687 print *, 'pressure to n T has factor ',unit_pressure/(unit_numberdensity*unit_temperature)
1688 if(si_unit)then
1689 print *, ' compare this to ',kb_si*(2.d0+3.d0*eos%He_abundance)
1692 else
1693 print *, ' compare this to ',kb_cgs*(2.d0+3.d0*eos%He_abundance)
1696 endif
1697 if(eos%eos_type /= 'LTE')then
1698 print *, 'mean molecular weight mu is =',a/b,' = ', (1.d0+4.d0*eos%He_abundance)/(2.d0+3.d0*eos%He_abundance)
1699 xfrac=1.d0/a
1700 yfrac=4.d0*eos%He_abundance/(1.d0+4.d0*eos%He_abundance)
1701 print *, 'mass fraction hydrogen X is =',1/a,' and this equals ', 1.d0/(1.d0+4.d0*eos%He_abundance)
1702 print *, 'mass fraction helium Y is =',yfrac
1703 print *, ' check that 1/mu', b/a,' is equal to 2X+3Y/4=',2.d0*xfrac+3.d0*yfrac/4.d0
1704 print *, ' ratio n_e/n_p=',1.d0+2.0d0*eos%He_abundance
1705 endif
1706 print *,'========UNITS==========='
1707 endif
1708
1709 end subroutine mhd_check_params
1710
1711 subroutine mhd_physical_units()
1713 double precision :: mp,kb,miu0,c_lightspeed,xfrac,sigma_telectron
1714 double precision :: a,b
1715 ! Derive scaling units
1716 if(si_unit) then
1717 mp=mp_si
1718 kb=kb_si
1719 miu0=miu0_si
1720 const_sigmasb=sigma_sb_si
1721 c_lightspeed=c_si
1722 sigma_telectron=sigma_te_si
1723 else
1724 mp=mp_cgs
1725 kb=kb_cgs
1726 miu0=4.d0*dpi ! G^2 cm^2 dyne^-1
1727 const_sigmasb=sigma_sb_cgs
1728 c_lightspeed=const_c
1729 sigma_telectron=sigma_te_cgs
1730 end if
1731 ! Normalisation dispatch keyed solely on eos%eos_type (FI is the default, so
1732 ! legacy parfiles land in the FI/PI absorbed-(a,b), RR=1 branch -- the former
1733 ! eq_state_units=.true. result).
1734 if (eos%eos_type == 'LTE') then
1735 !> Remove the assumed FI normalisation from the units and handle in EoS
1736 a=1d0
1737 b=1d0
1738 eos%nH2rhoFactor = 1d0+4d0*eos%He_abundance
1739 rr=(2d0+3d0*eos%He_abundance) / (1d0+4d0*eos%He_abundance)
1740 xfrac=1.d0/(1.d0+4.d0*eos%He_abundance)
1741 else
1742 !> FI / PI: absorbed-(a,b), RR=1 (a=b=1 with RR=1 would be wrong physics
1743 !> for He>0). PI shares FI's normalisation exactly; the partial b lives here.
1744 a=1d0+4d0*eos%He_abundance
1745 if(eos%eos_type=='PI') then
1746 b=1d0+h_ion_fr+eos%He_abundance*(he_ion_fr*(he_ion_fr2+1d0)+1d0)
1747 else
1748 b=2d0+3d0*eos%He_abundance
1749 end if
1750 rr=1d0
1751 xfrac=1.d0/a
1752 end if
1753 if(unit_density/=1.d0 .or. unit_numberdensity/=1.d0) then
1754 if(unit_density/=1.d0) then
1756 else if(unit_numberdensity/=1.d0) then
1758 end if
1759 if(unit_temperature/=1.d0) then
1763 if(unit_length/=1.d0) then
1765 else if(unit_time/=1.d0) then
1767 end if
1768 else if(unit_magneticfield/=1.d0) then
1772 if(unit_length/=1.d0) then
1774 else if(unit_time/=1.d0) then
1776 end if
1777 else if(unit_pressure/=1.d0) then
1781 if(unit_length/=1.d0) then
1783 else if(unit_time/=1.d0) then
1785 end if
1786 else if(unit_velocity/=1.d0) then
1790 if(unit_length/=1.d0) then
1792 else if(unit_time/=1.d0) then
1794 end if
1795 else if(unit_time/=1.d0) then
1800 end if
1801 else if(unit_temperature/=1.d0) then
1802 ! units of temperature and velocity are dependent
1803 if(unit_magneticfield/=1.d0) then
1808 if(unit_length/=1.d0) then
1810 else if(unit_time/=1.d0) then
1812 end if
1813 else if(unit_pressure/=1.d0) then
1818 if(unit_length/=1.d0) then
1820 else if(unit_time/=1.d0) then
1822 end if
1823 end if
1824 else if(unit_magneticfield/=1.d0) then
1825 ! units of magnetic field and pressure are dependent
1826 if(unit_velocity/=1.d0) then
1831 if(unit_length/=1.d0) then
1833 else if(unit_time/=1.d0) then
1835 end if
1836 else if(unit_time/=0.d0) then
1842 end if
1843 else if(unit_pressure/=1.d0) then
1844 if(unit_velocity/=1.d0) then
1849 if(unit_length/=1.d0) then
1851 else if(unit_time/=1.d0) then
1853 end if
1854 else if(unit_time/=0.d0) then
1860 end if
1861 end if
1862 ! Additional units needed for the particles
1863 c_norm=c_lightspeed/unit_velocity
1865 if (.not. si_unit) unit_charge = unit_charge*const_c
1867
1868 if(mhd_semirelativistic) then
1869 if(mhd_reduced_c<1.d0) then
1870 ! dimensionless speed
1871 inv_squared_c0=1.d0
1872 inv_squared_c=1.d0/mhd_reduced_c**2
1873 else
1874 inv_squared_c0=(unit_velocity/c_lightspeed)**2
1875 inv_squared_c=(unit_velocity/mhd_reduced_c)**2
1876 end if
1878 ! Propagate to the EoS container. Must happen AFTER inv_squared_c{0,}
1879 ! are set above; the assignment earlier in mhd_phys_init runs before
1880 ! mhd_physical_units and would store uninitialised values.
1881 eos%inv_squared_c0 = inv_squared_c0
1882 eos%inv_squared_c = inv_squared_c
1883 end if
1884
1885 !> Units for radiative flux and opacity as used in FLD
1886 ! this is the radiation constant in either cgs or SI units
1887 const_rad_a=4.d0*const_sigmasb/c_lightspeed
1888 ! this is the dimensionless conversion factor for Erad to Trad
1890 ! This is the Thomson scattering opacity in the correct units
1891 ! note that the hydrogen mass fraction X=1/a in eq_state_units
1892 const_kappae=sigma_telectron*(1.d0+xfrac)/(2.0d0*mp)
1893 ! these are the units
1897
1898 end subroutine mhd_physical_units
1899
1900 subroutine mhd_check_w_semirelati(primitive,ixI^L,ixO^L,w,flag)
1902
1903 logical, intent(in) :: primitive
1904 logical, intent(inout) :: flag(ixi^s,1:nw)
1905 integer, intent(in) :: ixi^l, ixo^l
1906 double precision, intent(in) :: w(ixi^s,nw)
1907
1908 double precision :: vel(ixo^s,1:ndir),emech(ixo^s)
1909
1910 flag=.false.
1911 where(w(ixo^s,rho_) < small_density) flag(ixo^s,rho_) = .true.
1912
1913 if(mhd_energy) then
1914 if(primitive) then
1915 where(w(ixo^s,p_) < small_pressure) flag(ixo^s,e_) = .true.
1916 else
1917 call mhd_semirelati_vel_emech(w,ixi^l,ixo^l,vel,emech)
1918 where(w(ixo^s,e_)-emech(ixo^s) < small_e) flag(ixo^s,e_) = .true.
1919 end if
1920 end if
1921
1922 end subroutine mhd_check_w_semirelati
1923
1924 subroutine mhd_check_w_origin(primitive,ixI^L,ixO^L,w,flag)
1926
1927 logical, intent(in) :: primitive
1928 integer, intent(in) :: ixi^l, ixo^l
1929 double precision, intent(in) :: w(ixi^s,nw)
1930 logical, intent(inout) :: flag(ixi^s,1:nw)
1931
1932 integer :: ix^d, igrp
1933
1934 flag=.false.
1935 {do ix^db=ixomin^db,ixomax^db\}
1936 if(w(ix^d,rho_)<small_density) flag(ix^d,rho_) = .true.
1937 if(primitive) then
1938 if(w(ix^d,p_)<small_pressure) flag(ix^d,e_) = .true.
1939 else
1940 if(w(ix^d,e_)-half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)+&
1941 (^c&w(ix^d,b^c_)**2+))-mhd_uawsom_wave_energy_cell(w(ix^d,:))&
1942 <small_e) flag(ix^d,e_) = .true.
1943 if(mhd_uawsom) then
1944 if(w(ix^d,waplus_)<zero) flag(ix^d,waplus_)=.true.
1945 if(w(ix^d,waminus_)<zero) flag(ix^d,waminus_)=.true.
1946 if(w(ix^d,wkplus_)<zero) flag(ix^d,wkplus_)=.true.
1947 if(w(ix^d,wkminus_)<zero) flag(ix^d,wkminus_)=.true.
1948 end if
1949 end if
1950 if(mhd_radiation_fld)then
1951 if(w(ix^d,r_e)<small_r_e) flag(ix^d,r_e) = .true.
1952 endif
1953 {end do\}
1954
1955 end subroutine mhd_check_w_origin
1956
1957 subroutine mhd_check_w_split(primitive,ixI^L,ixO^L,w,flag)
1959
1960 logical, intent(in) :: primitive
1961 integer, intent(in) :: ixi^l, ixo^l
1962 double precision, intent(in) :: w(ixi^s,nw)
1963 logical, intent(inout) :: flag(ixi^s,1:nw)
1964
1965 double precision :: tmp
1966 integer :: ix^d
1967
1968 flag=.false.
1969 {do ix^db=ixomin^db,ixomax^db\}
1970 tmp=w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0)
1971 if(tmp<small_density) flag(ix^d,rho_) = .true.
1972 if(primitive) then
1973 if(w(ix^d,p_)+block%equi_vars(ix^d,equi_pe0_,0)<small_pressure) flag(ix^d,e_) = .true.
1974 else
1975 tmp=w(ix^d,e_)-half*((^c&w(ix^d,m^c_)**2+)/tmp+(^c&w(ix^d,b^c_)**2+))
1976 if(tmp+block%equi_vars(ix^d,equi_pe0_,0)*eos%inv_gamma_minus_1<small_e) flag(ix^d,e_) = .true.
1977 end if
1978 {end do\}
1979
1980 end subroutine mhd_check_w_split
1981
1982 subroutine mhd_check_w_noe(primitive,ixI^L,ixO^L,w,flag)
1984
1985 logical, intent(in) :: primitive
1986 integer, intent(in) :: ixi^l, ixo^l
1987 double precision, intent(in) :: w(ixi^s,nw)
1988 logical, intent(inout) :: flag(ixi^s,1:nw)
1989
1990 integer :: ix^d
1991
1992 flag=.false.
1993 {do ix^db=ixomin^db,ixomax^db\}
1994 if(w(ix^d,rho_)<small_density) flag(ix^d,rho_) = .true.
1995 {end do\}
1996
1997 end subroutine mhd_check_w_noe
1998
1999 subroutine mhd_check_w_inte(primitive,ixI^L,ixO^L,w,flag)
2001
2002 logical, intent(in) :: primitive
2003 integer, intent(in) :: ixi^l, ixo^l
2004 double precision, intent(in) :: w(ixi^s,nw)
2005 logical, intent(inout) :: flag(ixi^s,1:nw)
2006
2007 integer :: ix^d
2008
2009 flag=.false.
2010 {do ix^db=ixomin^db,ixomax^db\}
2011 if(w(ix^d,rho_)<small_density) flag(ix^d,rho_) = .true.
2012 if(primitive) then
2013 if(w(ix^d,p_) < small_pressure) flag(ix^d,e_) = .true.
2014 else
2015 if(w(ix^d,e_)<small_e) flag(ix^d,e_) = .true.
2016 end if
2017 {end do\}
2018
2019 end subroutine mhd_check_w_inte
2020
2021 subroutine mhd_check_w_hde(primitive,ixI^L,ixO^L,w,flag)
2023
2024 logical, intent(in) :: primitive
2025 integer, intent(in) :: ixi^l, ixo^l
2026 double precision, intent(in) :: w(ixi^s,nw)
2027 logical, intent(inout) :: flag(ixi^s,1:nw)
2028
2029 integer :: ix^d
2030
2031 flag=.false.
2032 {do ix^db=ixomin^db,ixomax^db\}
2033 if(w(ix^d,rho_)<small_density) flag(ix^d,rho_) = .true.
2034 if(primitive) then
2035 if(w(ix^d,p_)<small_pressure) flag(ix^d,e_) = .true.
2036 else
2037 if(w(ix^d,e_)-half*(^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)<small_e) flag(ix^d,e_) = .true.
2038 end if
2039 {end do\}
2040
2041 end subroutine mhd_check_w_hde
2042
2043 subroutine mhd_bound_fip(primitive, ixI^L, ixO^L, w)
2045 logical, intent(in) :: primitive
2046 integer, intent(in) :: ixi^l, ixo^l
2047 double precision, intent(inout) :: w(ixi^s,1:nw)
2048
2049 double precision :: rho_safe(ixi^s), fip_prim(ixi^s)
2050
2051 if (.not. mhd_fip) return
2052
2053 if (primitive) then
2054 w(ixo^s,fip_) = min(maxfip, max(minfip, w(ixo^s,fip_)))
2055 else
2056 if (has_equi_rho_and_p) then
2057 rho_safe(ixo^s) = max(w(ixo^s,rho_) + block%equi_vars(ixo^s,equi_rho0_,b0i), small_density)
2058 else
2059 rho_safe(ixo^s) = max(w(ixo^s,rho_), small_density)
2060 end if
2061 fip_prim(ixo^s) = w(ixo^s,fip_) / rho_safe(ixo^s)
2062 fip_prim(ixo^s) = min(maxfip, max(minfip, fip_prim(ixo^s)))
2063 w(ixo^s,fip_) = rho_safe(ixo^s) * fip_prim(ixo^s)
2064 end if
2065 end subroutine mhd_bound_fip
2066
2067 !> Return local UAWSoM closure coefficients in code units. User cases may
2068 !> replace all three profiles through usr_uawsom_coefficients.
2069 subroutine mhd_uawsom_get_coefficients(w,x,ixI^L,ixO^L,primitive,zeta,radius,lperp_A)
2072 integer, intent(in) :: ixi^l, ixo^l
2073 double precision, intent(in) :: w(ixi^s,1:nw), x(ixi^s,1:ndim)
2074 logical, intent(in) :: primitive
2075 double precision, intent(out) :: zeta(ixi^s), radius(ixi^s), lperp_a(ixi^s)
2076 double precision :: bmag(ixi^s), btotal(ixi^s,1:ndir), height_base
2077 integer :: idir
2078
2079 if(associated(usr_uawsom_coefficients)) then
2080 call usr_uawsom_coefficients(w,x,ixi^l,ixo^l,primitive,zeta,radius,lperp_a)
2081 else
2082 do idir=1,ndir
2083 if(b0field) then
2084 btotal(ixo^s,idir)=w(ixo^s,mag(idir))+block%B0(ixo^s,idir,b0i)
2085 else
2086 btotal(ixo^s,idir)=w(ixo^s,mag(idir))
2087 end if
2088 end do
2089 bmag(ixo^s)=dsqrt(sum(btotal(ixo^s,1:ndir)**2,dim=ndim+1))
2090 select case(mhd_uawsom_height_dim)
2091 case(1)
2092 height_base=xprobmin1
2093 {^nooned
2094 case(2)
2095 height_base=xprobmin2
2096 }
2097 {^ifthreed
2098 case(3)
2099 height_base=xprobmin3
2100 }
2101 end select
2102 zeta(ixo^s)=one+(mhd_uawsom_zeta0-one)*dexp(&
2103 -max(x(ixo^s,mhd_uawsom_height_dim)-height_base,zero)/&
2105 radius(ixo^s)=mhd_uawsom_thread_radius0*dsqrt(&
2106 mhd_uawsom_bref/max(bmag(ixo^s),smalldouble))
2107 lperp_a(ixo^s)=mhd_uawsom_alfven_corr_length0*dsqrt(&
2108 mhd_uawsom_bref/max(bmag(ixo^s),smalldouble))
2109 end if
2110 if(any(zeta(ixo^s)<=one) .or. any(radius(ixo^s)<=zero) .or. &
2111 any(lperp_a(ixo^s)<=zero)) &
2112 call mpistop('mhd_uawsom coefficients require zeta>1 and positive lengths')
2113 end subroutine mhd_uawsom_get_coefficients
2114
2115 subroutine mhd_uawsom_rho2_factor(ixI^L,ixO^L,w,x,factor)
2117 integer, intent(in) :: ixi^l, ixo^l
2118 double precision, intent(in) :: w(ixi^s,1:nw), x(ixi^s,1:ndim)
2119 double precision, intent(out) :: factor(ixi^s)
2120 double precision :: zeta(ixi^s), radius(ixi^s), lperp_a(ixi^s)
2121 call mhd_uawsom_get_coefficients(w,x,ixi^l,ixo^l,.false.,zeta,radius,lperp_a)
2122 factor(ixo^s)=mhd_uawsom_rho2_factor_cell(zeta(ixo^s))
2123 end subroutine mhd_uawsom_rho2_factor
2124
2125 elemental pure double precision function mhd_uawsom_rho2_factor_cell(zeta)
2126 double precision, intent(in) :: zeta
2127 double precision :: f
2129 mhd_uawsom_rho2_factor_cell=1.d0+f*(1.d0-f)*(zeta-1.d0)**2/&
2130 (1.d0+f*zeta-f)**2
2131 end function mhd_uawsom_rho2_factor_cell
2132
2133 pure double precision function mhd_uawsom_wave_pressure_cell(wcell,zeta)
2134 double precision, intent(in) :: wcell(:),zeta
2135 mhd_uawsom_wave_pressure_cell=0.5d0*(wcell(waplus_)+wcell(waminus_))+&
2136 0.25d0*(zeta+1.d0)*(wcell(wkplus_)+wcell(wkminus_))
2138
2139 pure double precision function mhd_uawsom_wave_energy_cell(wcell)
2140 use mod_constants, only: zero
2141 double precision, intent(in) :: wcell(:)
2142 if(mhd_uawsom) then
2144 wcell(wkplus_)+wcell(wkminus_)
2145 else
2147 end if
2148 end function mhd_uawsom_wave_energy_cell
2149
2150 !> Signed population imbalance used by the Cartesian gradient-vorticity reflection model.
2151 !> A positive value transfers W_A^+ (or W_k^+) to the minus population;
2152 !> a negative value transfers the minus population to the plus population.
2153 !> The factor is zero for a balanced state and remains bounded in [-1,1]
2154 !> for an arbitrarily imbalanced state.
2155 elemental pure double precision function mhd_uawsom_imbalance_factor(wplus,wminus)
2156 double precision, intent(in) :: wplus,wminus
2157
2158 if(wplus<=0.d0 .or. wminus<=0.d0) then
2159 mhd_uawsom_imbalance_factor=0.d0
2160 else if(4.d0*wminus<=wplus) then
2161 mhd_uawsom_imbalance_factor=1.d0-2.d0*dsqrt(wminus/wplus)
2162 else if(4.d0*wplus<=wminus) then
2163 mhd_uawsom_imbalance_factor=-(1.d0-2.d0*dsqrt(wplus/wminus))
2164 else
2165 mhd_uawsom_imbalance_factor=0.d0
2166 end if
2167 end function mhd_uawsom_imbalance_factor
2168
2169 !> UAWSoM compression, nonlinear damping, and optional Alfven reflection.
2170 !> Total energy is intentionally unchanged by damping: because it contains
2171 !> the four W variables, their loss is recovered as gas internal energy.
2172 subroutine mhd_add_source_uawsom(qdt,ixI^L,ixO^L,wCT,wCTprim,w,x)
2175 integer, intent(in) :: ixi^l, ixo^l
2176 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), &
2177 wctprim(ixi^s,1:nw), x(ixi^s,1:ndim)
2178 double precision, intent(inout) :: w(ixi^s,1:nw)
2179 double precision :: v(ixi^s,1:ndir), divv(ixi^s)
2180 double precision :: zeta(ixi^s), radius(ixi^s), lperp_a(ixi^s)
2181 double precision :: lperp_k(ixi^s), rho_e(ixi^s)
2182 double precision :: gamma_plus(ixi^s), gamma_minus(ixi^s)
2183 double precision :: gamma_kplus(ixi^s), gamma_kminus(ixi^s)
2184 double precision :: dampap(ixi^s), dampam(ixi^s), dampkp(ixi^s), dampkm(ixi^s)
2185 double precision :: btotal(ixi^s,1:ndir), bunit(ixi^s,1:ndir), bmag(ixi^s)
2186 double precision :: va(ixi^s), vk(ixi^s), lnva(ixi^s), lnvk(ixi^s)
2187 double precision :: grad_component(ixi^s), grad_a(ixi^s), grad_k(ixi^s)
2188 double precision :: curlv(ixi^s,1:3), vorticity(ixi^s)
2189 double precision :: rimb_a(ixi^s), rlim_a(ixi^s), rlim_k(ixi^s)
2190 double precision :: refl_rate(ixi^s), donor_reflection(ixi^s)
2191 double precision :: transfer_a(ixi^s), transfer_k(ixi^s), donor(ixi^s)
2192 double precision :: imbalance_a(ixi^s), imbalance_k(ixi^s)
2193 double precision :: wave_plus(ixi^s), wave_minus(ixi^s)
2194 double precision :: wave_kplus(ixi^s), wave_kminus(ixi^s)
2195 double precision :: f, kink_pressure(ixi^s)
2196 integer :: idir, idirmin
2197 logical :: have_reflection, have_alfven_reflection, have_kink_reflection
2198
2199 v(ixi^s,1:ndir)=wctprim(ixi^s,mom(1:ndir))
2200 call divvector(v,ixi^l,ixo^l,divv)
2201 ! Reflection gradients need one-cell ghost values, so obtain the closure
2202 ! profiles on ixI rather than only on the source update region.
2203 call mhd_uawsom_get_coefficients(wct,x,ixi^l,ixi^l,.false.,zeta,radius,lperp_a)
2205 rho_e(ixi^s)=max(wct(ixi^s,rho_),small_density)/&
2206 max(one+f*zeta(ixi^s)-f,smalldouble)
2207 lperp_k(ixi^s)=dsqrt(10.d0)*dsqrt(f*dpi)*radius(ixi^s)*&
2208 (zeta(ixi^s)+one-f)**1.5d0/&
2209 ((zeta(ixi^s)-one)*(one-f**2.5d0))
2210 gamma_plus(ixo^s)=two/lperp_a(ixo^s)*dsqrt(&
2211 max(wct(ixo^s,waminus_),zero)/max(wct(ixo^s,rho_),small_density))
2212 gamma_minus(ixo^s)=two/lperp_a(ixo^s)*dsqrt(&
2213 max(wct(ixo^s,waplus_),zero)/max(wct(ixo^s,rho_),small_density))
2214 dampap(ixo^s)=gamma_plus(ixo^s)*max(wct(ixo^s,waplus_),zero)
2215 dampam(ixo^s)=gamma_minus(ixo^s)*max(wct(ixo^s,waminus_),zero)
2216 dampkp(ixo^s)=max(wct(ixo^s,wkplus_),zero)**1.5d0/&
2217 (dsqrt(rho_e(ixo^s))*lperp_k(ixo^s))
2218 dampkm(ixo^s)=max(wct(ixo^s,wkminus_),zero)**1.5d0/&
2219 (dsqrt(rho_e(ixo^s))*lperp_k(ixo^s))
2220 ! These rates limit kink reflection; ordinary kink damping uses dampkp/km.
2222 gamma_kplus(ixo^s)=dsqrt(max(wct(ixo^s,wkplus_),zero)/&
2223 max(rho_e(ixo^s),small_density))/max(lperp_k(ixo^s),smalldouble)
2224 gamma_kminus(ixo^s)=dsqrt(max(wct(ixo^s,wkminus_),zero)/&
2225 max(rho_e(ixo^s),small_density))/max(lperp_k(ixo^s),smalldouble)
2226 end if
2227
2228 w(ixo^s,waplus_)=w(ixo^s,waplus_)-qdt*(&
2229 half*divv(ixo^s)*wct(ixo^s,waplus_)+dampap(ixo^s))
2230 w(ixo^s,waminus_)=w(ixo^s,waminus_)-qdt*(&
2231 half*divv(ixo^s)*wct(ixo^s,waminus_)+dampam(ixo^s))
2232 w(ixo^s,wkplus_)=w(ixo^s,wkplus_)-qdt*(&
2233 half*divv(ixo^s)*wct(ixo^s,wkplus_)+dampkp(ixo^s))
2234 w(ixo^s,wkminus_)=w(ixo^s,wkminus_)-qdt*(&
2235 half*divv(ixo^s)*wct(ixo^s,wkminus_)+dampkm(ixo^s))
2236
2237 kink_pressure(ixo^s)=quarter*(zeta(ixo^s)+one)*&
2238 (wct(ixo^s,wkplus_)+wct(ixo^s,wkminus_))
2239 w(ixo^s,e_)=w(ixo^s,e_)+qdt*(zeta(ixo^s)-one)/&
2240 (zeta(ixo^s)+one)*kink_pressure(ixo^s)*divv(ixo^s)
2241
2242 have_alfven_reflection=.false.
2243 if(mhd_uawsom_reflection) then
2244 select case(uawsom_reflection_id)
2245 case(uawsom_reflection_1d)
2246 have_alfven_reflection=(mhd_uawsom_sigma>zero)
2247 case(uawsom_reflection_cartesian)
2248 have_alfven_reflection=.true.
2249 end select
2250 end if
2251 have_kink_reflection=mhd_uawsom_kink_reflection
2252 have_reflection=have_alfven_reflection .or. have_kink_reflection
2253 if(have_reflection) then
2254 do idir=1,ndir
2255 if(b0field) then
2256 btotal(ixi^s,idir)=wct(ixi^s,mag(idir))+block%B0(ixi^s,idir,b0i)
2257 else
2258 btotal(ixi^s,idir)=wct(ixi^s,mag(idir))
2259 end if
2260 end do
2261 bmag(ixi^s)=dsqrt(sum(btotal(ixi^s,1:ndir)**2,dim=ndim+1))
2262 if(have_kink_reflection .or. &
2263 (have_alfven_reflection .and. uawsom_reflection_id==uawsom_reflection_cartesian)) then
2264 do idir=1,ndir
2265 bunit(ixi^s,idir)=btotal(ixi^s,idir)/max(bmag(ixi^s),smalldouble)
2266 end do
2267 end if
2268 if(have_alfven_reflection) then
2269 va(ixi^s)=bmag(ixi^s)/dsqrt(max(wct(ixi^s,rho_),small_density))
2270 if(uawsom_reflection_id==uawsom_reflection_cartesian) &
2271 lnva(ixi^s)=dlog(max(va(ixi^s),smalldouble))
2272 end if
2273 if(have_kink_reflection) then
2274 vk(ixi^s)=bmag(ixi^s)/dsqrt(max(rho_e(ixi^s)*&
2275 (zeta(ixi^s)+one)/two,smalldouble))
2276 lnvk(ixi^s)=dlog(max(vk(ixi^s),smalldouble))
2277 end if
2278
2279 ! Apply exchange after the ordinary source update. The donor cap below
2280 ! uses that actual post-damping state; it does not silently repair a
2281 ! negative state produced by compression or damping. The standard
2282 ! small-value handling remains responsible for such a state.
2283 if(have_alfven_reflection) then
2284 wave_plus(ixo^s)=max(wct(ixo^s,waplus_),zero)
2285 wave_minus(ixo^s)=max(wct(ixo^s,waminus_),zero)
2286
2287 select case(uawsom_reflection_id)
2288 case(uawsom_reflection_1d)
2289 ! Retain the original one-dimensional source and sign convention:
2290 ! a positive transfer removes W_A^+ and adds W_A^-.
2291 call gradient(va,ixi^l,ixo^l,mhd_uawsom_height_dim,grad_component)
2292 refl_rate(ixo^s)=mhd_uawsom_sigma*&
2293 (wctprim(ixo^s,mom(mhd_uawsom_height_dim))+va(ixo^s))/&
2294 max(va(ixo^s),smalldouble)*grad_component(ixo^s)
2295 donor_reflection(ixo^s)=merge(max(wct(ixo^s,waplus_),zero),&
2296 max(wct(ixo^s,waminus_),zero),refl_rate(ixo^s)>=zero)
2297 transfer_a(ixo^s)=refl_rate(ixo^s)*donor_reflection(ixo^s)
2298 case(uawsom_reflection_cartesian)
2299 grad_a(ixo^s)=zero
2300 do idir=1,ndim
2301 call gradient(lnva,ixi^l,ixo^l,idir,grad_component)
2302 grad_a(ixo^s)=grad_a(ixo^s)+va(ixo^s)*bunit(ixo^s,idir)*&
2303 grad_component(ixo^s)
2304 end do
2305 call curlvector(v,ixi^l,ixo^l,curlv,idirmin,1,ndir)
2306 vorticity(ixo^s)=zero
2307 do idir=1,ndir
2308 vorticity(ixo^s)=vorticity(ixo^s)+bunit(ixo^s,idir)*&
2309 curlv(ixo^s,idir)
2310 end do
2311 ! Sigma belongs to the one-dimensional gradient source only; it is
2312 ! absent from the Cartesian gradient-vorticity R_imb/R_lim closure.
2313 rimb_a(ixo^s)=dsqrt(grad_a(ixo^s)**2+vorticity(ixo^s)**2)
2314 rlim_a(ixo^s)=min(rimb_a(ixo^s),&
2315 max(gamma_plus(ixo^s),gamma_minus(ixo^s)))
2316 imbalance_a(ixo^s)=mhd_uawsom_imbalance_factor(&
2317 wave_plus(ixo^s),wave_minus(ixo^s))
2318 transfer_a(ixo^s)=rlim_a(ixo^s)*imbalance_a(ixo^s)*&
2319 dsqrt(wave_plus(ixo^s)*wave_minus(ixo^s))
2320 end select
2321
2322 if(qdt>zero) then
2323 donor(ixo^s)=merge(max(w(ixo^s,waplus_),zero),&
2324 max(w(ixo^s,waminus_),zero),transfer_a(ixo^s)>=zero)
2325 transfer_a(ixo^s)=sign(min(abs(transfer_a(ixo^s)),&
2326 donor(ixo^s)/qdt),transfer_a(ixo^s))
2327 else
2328 transfer_a(ixo^s)=zero
2329 end if
2330 w(ixo^s,waplus_)=w(ixo^s,waplus_)-qdt*transfer_a(ixo^s)
2331 w(ixo^s,waminus_)=w(ixo^s,waminus_)+qdt*transfer_a(ixo^s)
2332 end if
2333
2334 if(have_kink_reflection) then
2335 wave_kplus(ixo^s)=max(wct(ixo^s,wkplus_),zero)
2336 wave_kminus(ixo^s)=max(wct(ixo^s,wkminus_),zero)
2337 grad_k(ixo^s)=zero
2338 do idir=1,ndim
2339 call gradient(lnvk,ixi^l,ixo^l,idir,grad_component)
2340 grad_k(ixo^s)=grad_k(ixo^s)+vk(ixo^s)*bunit(ixo^s,idir)*&
2341 grad_component(ixo^s)
2342 end do
2343 ! Kink reflection deliberately uses only the kink-speed gradient;
2344 ! the field-aligned velocity-vorticity term belongs to Alfvén waves,
2345 ! and sigma is not part of this limiter.
2346 rlim_k(ixo^s)=min(abs(grad_k(ixo^s)),&
2347 max(gamma_kplus(ixo^s),gamma_kminus(ixo^s)))
2348 imbalance_k(ixo^s)=mhd_uawsom_imbalance_factor(&
2349 wave_kplus(ixo^s),wave_kminus(ixo^s))
2350 transfer_k(ixo^s)=rlim_k(ixo^s)*imbalance_k(ixo^s)*&
2351 dsqrt(wave_kplus(ixo^s)*wave_kminus(ixo^s))
2352 if(qdt>zero) then
2353 donor(ixo^s)=merge(max(w(ixo^s,wkplus_),zero),&
2354 max(w(ixo^s,wkminus_),zero),transfer_k(ixo^s)>=zero)
2355 transfer_k(ixo^s)=sign(min(abs(transfer_k(ixo^s)),&
2356 donor(ixo^s)/qdt),transfer_k(ixo^s))
2357 else
2358 transfer_k(ixo^s)=zero
2359 end if
2360 w(ixo^s,wkplus_)=w(ixo^s,wkplus_)-qdt*transfer_k(ixo^s)
2361 w(ixo^s,wkminus_)=w(ixo^s,wkminus_)+qdt*transfer_k(ixo^s)
2362 end if
2363 end if
2364 end subroutine mhd_add_source_uawsom
2365
2366 !> Transform internal energy to total energy
2367 subroutine mhd_ei_to_e(ixI^L,ixO^L,w,x)
2369 integer, intent(in) :: ixi^l, ixo^l
2370 double precision, intent(inout) :: w(ixi^s, nw)
2371 double precision, intent(in) :: x(ixi^s, 1:ndim)
2372
2373 integer :: ix^d
2374
2375 if(has_equi_rho_and_p) then
2376 {do ix^db=ixomin^db,ixomax^db\}
2377 ! Calculate e = ei + ek + eb
2378 w(ix^d,e_)=w(ix^d,e_)&
2379 +half*((^c&w(ix^d,m^c_)**2+)/&
2380 (w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0))&
2381 +(^c&w(ix^d,b^c_)**2+))
2382 {end do\}
2383 else
2384 {!dir$ ivdep
2385 do ix^db=ixomin^db,ixomax^db\}
2386 ! Calculate e = ei + ek + eb
2387 w(ix^d,e_)=w(ix^d,e_)&
2388 +half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)&
2389 +(^c&w(ix^d,b^c_)**2+))&
2390 +mhd_uawsom_wave_energy_cell(w(ix^d,:))
2391 {end do\}
2392 end if
2393 end subroutine mhd_ei_to_e
2394
2395 !> Transform internal energy to hydrodynamic energy
2396 subroutine mhd_ei_to_e_hde(ixI^L,ixO^L,w,x)
2398 integer, intent(in) :: ixi^l, ixo^l
2399 double precision, intent(inout) :: w(ixi^s, nw)
2400 double precision, intent(in) :: x(ixi^s, 1:ndim)
2401
2402 integer :: ix^d
2403
2404 {do ix^db=ixomin^db,ixomax^db\}
2405 ! Calculate e = ei + ek
2406 w(ix^d,e_)=w(ix^d,e_)&
2407 +half*(^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)
2408 {end do\}
2409
2410 end subroutine mhd_ei_to_e_hde
2411
2412 !> Velocity from the semirelativistic momentum (Gombosi et al. 2002 eqs 15-16) and emech, the
2413 !> energy variable less the internal energy: ekin + B^2/2 + E^2/(2c^2) for total energy and zero
2414 !> with mhd_internal_e. The Alfven factor and E take the total field, the magnetic energy the
2415 !> stored slot (B1 under splitting, eq 92)
2416 subroutine mhd_semirelati_vel_emech(w,ixI^L,ixO^L,vel,emech)
2418 integer, intent(in) :: ixi^l, ixo^l
2419 double precision, intent(in) :: w(ixi^s,nw)
2420 double precision, intent(out) :: vel(ixo^s,1:ndir)
2421 double precision, intent(out), optional :: emech(ixo^s)
2422
2423 double precision :: btot(1:ndir), efd(1:ndir)
2424 double precision :: b2, rhoc2, tmp, inv_rho
2425 integer :: ix^d
2426
2427 {do ix^db=ixomin^db,ixomax^db\}
2428 ^c&btot(^c)=w(ix^d,b^c_)\
2429 if(b0field) then
2430 ^c&btot(^c)=btot(^c)+block%B0(ix^d,^c,b0i)\
2431 end if
2432 ! Keppens's formula to convert momentum to velocity
2433 b2=(^c&btot(^c)**2+)
2434 tmp=(^c&btot(^c)*w(ix^d,m^c_)+)
2435 rhoc2=w(ix^d,rho_)*mhd_squared_c
2436 inv_rho=1.d0/(w(ix^d,rho_)*(rhoc2+b2))
2437 ^c&vel(ix^d,^c)=(rhoc2*w(ix^d,m^c_)+btot(^c)*tmp)*inv_rho\
2438 if(present(emech)) then
2439 if(mhd_internal_e) then
2440 emech(ix^d)=zero
2441 else
2442 ! E=Bxv
2443 {^ifthreec
2444 efd(1)=btot(2)*vel(ix^d,3)-btot(3)*vel(ix^d,2)
2445 efd(2)=btot(3)*vel(ix^d,1)-btot(1)*vel(ix^d,3)
2446 efd(3)=btot(1)*vel(ix^d,2)-btot(2)*vel(ix^d,1)
2447 }
2448 {^iftwoc
2449 efd(1)=zero
2450 efd(2)=btot(1)*vel(ix^d,2)-btot(2)*vel(ix^d,1)
2451 }
2452 {^ifonec
2453 efd(1)=zero
2454 }
2455 emech(ix^d)=half*((^c&vel(ix^d,^c)**2+)*w(ix^d,rho_)&
2456 +(^c&w(ix^d,b^c_)**2+)&
2457 +(^c&efd(^c)**2+)*inv_squared_c)
2458 end if
2459 end if
2460 {end do\}
2461
2462 end subroutine mhd_semirelati_vel_emech
2463
2464 !> Thermal pressure from internal energy in one cell for the active EoS
2465 double precision function mhd_semirelati_p(eint,rho) result(p)
2467 use mod_eos_lte, only: lte_p_from_eint
2469 double precision, intent(in) :: eint, rho
2470 double precision :: nh, t, rfac
2471
2472 if(eos%type_id==eos_type_lte) then
2473 nh=rho/eos%nH2rhoFactor
2474 p=lte_p_from_eint(eint,nh,dlog10(nh),1.d0/rho)
2475 else if(eos%type_id==eos_type_pi .and. eos%ionE) then
2476 call state_from_eint_pi(rho,eint,t,p,rfac)
2477 else
2478 p=eos%gamma_minus_1*eint
2479 end if
2480
2481 end function mhd_semirelati_p
2482
2483 !> Internal energy from thermal pressure in one cell, the inverse of mhd_semirelati_p
2484 double precision function mhd_semirelati_eint(p,rho) result(eint)
2486 use mod_eos_lte, only: lte_eint_from_p
2488 double precision, intent(in) :: p, rho
2489 double precision :: nh
2490
2491 if(eos%type_id==eos_type_lte) then
2492 nh=rho/eos%nH2rhoFactor
2493 eint=lte_eint_from_p(p,nh,dlog10(nh),rho)
2494 else if(eos%type_id==eos_type_pi .and. eos%ionE) then
2495 call eint_from_rho_p_pi(rho,p,eint)
2496 else
2497 eint=p*eos%inv_gamma_minus_1
2498 end if
2499
2500 end function mhd_semirelati_eint
2501
2502 !> Transform internal energy to total energy
2503 subroutine mhd_ei_to_e_semirelati(ixI^L,ixO^L,w,x)
2505 integer, intent(in) :: ixi^l, ixo^l
2506 double precision, intent(inout) :: w(ixi^s, nw)
2507 double precision, intent(in) :: x(ixi^s, 1:ndim)
2508
2509 double precision :: vel(ixo^s,1:ndir), emech(ixo^s)
2510
2511 call mhd_semirelati_vel_emech(w,ixi^l,ixo^l,vel,emech)
2512 w(ixo^s,e_)=w(ixo^s,e_)+emech(ixo^s)
2513
2514 end subroutine mhd_ei_to_e_semirelati
2515
2516 !> Transform total energy to internal energy
2517 subroutine mhd_e_to_ei(ixI^L,ixO^L,w,x)
2519 integer, intent(in) :: ixi^l, ixo^l
2520 double precision, intent(inout) :: w(ixi^s, nw)
2521 double precision, intent(in) :: x(ixi^s, 1:ndim)
2522
2523 integer :: ix^d
2524
2525 if(has_equi_rho_and_p) then
2526 {do ix^db=ixomin^db,ixomax^db\}
2527 ! Calculate ei = e - ek - eb
2528 w(ix^d,e_)=w(ix^d,e_)&
2529 -half*((^c&w(ix^d,m^c_)**2+)/&
2530 (w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0))&
2531 +(^c&w(ix^d,b^c_)**2+))
2532 {end do\}
2533 else
2534 {!dir$ ivdep
2535 do ix^db=ixomin^db,ixomax^db\}
2536 ! Calculate ei = e - ek - eb
2537 w(ix^d,e_)=w(ix^d,e_)&
2538 -half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)&
2539 +(^c&w(ix^d,b^c_)**2+))&
2540 -mhd_uawsom_wave_energy_cell(w(ix^d,:))
2541 {end do\}
2542 end if
2543
2544 if(fix_small_values) then
2545 call mhd_handle_small_ei(w,x,ixi^l,ixi^l,e_,'mhd_e_to_ei')
2546 end if
2547
2548 end subroutine mhd_e_to_ei
2549
2550 !> phys_e_to_ei plus the log10(nH) cache read by the LTE conduction temperature. During STS
2551 !> substeps the density is invariant, so the cache is written once per STS cycle (in the
2552 !> sts_before_first_cycle hook) and reused across all substeps
2553 subroutine mhd_e_to_ei_cache_log_nh(ixI^L,ixO^L,w,x)
2555 integer, intent(in) :: ixi^l, ixo^l
2556 double precision, intent(inout) :: w(ixi^s, nw)
2557 double precision, intent(in) :: x(ixi^s, 1:ndim)
2558
2559 call mhd_e_to_ei_base(ixi^l,ixo^l,w,x)
2560 block%wextra(ixo^s, iw_log_nh) = dlog10(w(ixo^s, rho_) / eos%nH2rhoFactor)
2561 end subroutine mhd_e_to_ei_cache_log_nh
2562
2563 !> no conversion: with mhd_internal_e the energy variable is already internal
2564 subroutine mhd_ei_unchanged(ixI^L,ixO^L,w,x)
2566 integer, intent(in) :: ixi^l, ixo^l
2567 double precision, intent(inout) :: w(ixi^s, nw)
2568 double precision, intent(in) :: x(ixi^s, 1:ndim)
2569 end subroutine mhd_ei_unchanged
2570
2571 !> Transform hydrodynamic energy to internal energy
2572 subroutine mhd_e_to_ei_hde(ixI^L,ixO^L,w,x)
2574 integer, intent(in) :: ixi^l, ixo^l
2575 double precision, intent(inout) :: w(ixi^s, nw)
2576 double precision, intent(in) :: x(ixi^s, 1:ndim)
2577
2578 integer :: ix^d
2579
2580 {do ix^db=ixomin^db,ixomax^db\}
2581 ! Calculate ei = e - ek
2582 w(ix^d,e_)=w(ix^d,e_)&
2583 -half*(^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)
2584 {end do\}
2585
2586 if(fix_small_values) then
2587 call mhd_handle_small_ei(w,x,ixi^l,ixi^l,e_,'mhd_e_to_ei_hde')
2588 end if
2589
2590 end subroutine mhd_e_to_ei_hde
2591
2592 !> Transform total energy to internal energy
2593 subroutine mhd_e_to_ei_semirelati(ixI^L,ixO^L,w,x)
2595 integer, intent(in) :: ixi^l, ixo^l
2596 double precision, intent(inout) :: w(ixi^s, nw)
2597 double precision, intent(in) :: x(ixi^s, 1:ndim)
2598
2599 double precision :: vel(ixo^s,1:ndir), emech(ixo^s)
2600
2601 call mhd_semirelati_vel_emech(w,ixi^l,ixo^l,vel,emech)
2602 w(ixo^s,e_)=w(ixo^s,e_)-emech(ixo^s)
2603
2604 if(fix_small_values) then
2605 call mhd_handle_small_ei(w,x,ixi^l,ixo^l,e_,'mhd_e_to_ei_semirelati')
2606 end if
2607
2608 end subroutine mhd_e_to_ei_semirelati
2609
2610 subroutine mhd_handle_small_values_semirelati(primitive, w, x, ixI^L, ixO^L, subname)
2613 logical, intent(in) :: primitive
2614 integer, intent(in) :: ixi^l,ixo^l
2615 double precision, intent(inout) :: w(ixi^s,1:nw)
2616 double precision, intent(in) :: x(ixi^s,1:ndim)
2617 character(len=*), intent(in) :: subname
2618
2619 double precision :: v(ixi^s,1:ndir), emech(ixi^s), small_rho(ixi^s)
2620 double precision, allocatable :: work(:^d&,:)
2621 integer :: ix^d, ixc^l
2622 logical :: flag(ixi^s,1:nw)
2623
2624 ! Averaging may read donors outside ixO; their flags and pressures must
2625 ! refer to the same representation as the cells being repaired.
2626 ixc^l=ixo^l;
2627 if(small_values_method=="average") then
2628 ixc^l=ixi^l;
2629 end if
2630 flag=.false.
2631 where(w(ixc^s,rho_) < small_density)
2632 small_rho(ixc^s)=w(ixc^s,rho_)
2633 w(ixc^s,rho_)=small_density+smalldouble
2634 flag(ixc^s,rho_) = .true.
2635 end where
2636
2637 if(.not.primitive) then
2638 call mhd_semirelati_vel_emech(w,ixi^l,ixc^l,v(ixc^s,1:ndir),emech(ixc^s))
2639 end if
2640 if(mhd_energy) then
2641 if(primitive) then
2642 where(w(ixc^s,p_) < small_pressure) flag(ixc^s,e_) = .true.
2643 else
2644 {do ix^db=ixcmin^db,ixcmax^db\}
2645 if(w(ix^d,e_)-emech(ix^d) < small_e) flag(ix^d,e_) = .true.
2646 {end do\}
2647 end if
2648 end if
2649
2650 if(any(flag(ixo^s,:))) then
2651 select case (small_values_method)
2652 case ("replace")
2653 {do ix^db=ixomin^db,ixomax^db\}
2654 if(flag(ix^d,rho_)) then
2655 ^c&w(ix^d,m^c_)=zero\
2656 end if
2657 if(mhd_energy .and. flag(ix^d,e_)) then
2658 if(primitive) then
2659 w(ix^d,p_) = small_pressure
2660 else
2661 w(ix^d,e_) = small_e + emech(ix^d)
2662 end if
2663 end if
2664 {end do\}
2665 case ("average")
2666 call small_values_average(ixi^l,ixo^l,w,x,flag,rho_)
2667 if(mhd_energy) then
2668 if(primitive) then
2669 call small_values_average(ixi^l,ixo^l,w,x,flag,p_)
2670 else
2671 ! go to internal energy
2672 w(ixc^s,e_)=w(ixc^s,e_)-emech(ixc^s)
2673 ! averaging internal energy
2674 call small_values_average(ixi^l,ixo^l,work,x,flag,e_)
2675 ! back to total energy
2676 w(ixc^s,e_)=w(ixc^s,e_)+emech(ixc^s)
2677 deallocate(work)
2678 end if
2679 end if
2680 case default
2681 if(.not.primitive) then
2682 ! to show velocity and internal energy
2683 w(ixo^s,mom(1:ndir))=v(ixo^s,1:ndir)
2684 if(mhd_energy) w(ixo^s,e_)=w(ixo^s,e_)-emech(ixo^s)
2685 end if
2686 where(flag(ixc^s,rho_))
2687 w(ixc^s,rho_)=small_rho(ixc^s)
2688 end where
2689 call small_values_error(w,x,ixi^l,ixo^l,flag,subname)
2690 end select
2691 end if
2692 if (mhd_fip) call mhd_bound_fip(primitive, ixi^l, ixo^l, w)
2693 end subroutine mhd_handle_small_values_semirelati
2694
2695 subroutine mhd_handle_small_values_origin(primitive, w, x, ixI^L, ixO^L, subname)
2698 logical, intent(in) :: primitive
2699 integer, intent(in) :: ixi^l,ixo^l
2700 double precision, intent(inout) :: w(ixi^s,1:nw)
2701 double precision, intent(in) :: x(ixi^s,1:ndim)
2702 character(len=*), intent(in) :: subname
2703
2704 integer :: ix^d, igrp
2705 logical :: flag(ixi^s,1:nw)
2706
2707 call phys_check_w(primitive, ixi^l, ixo^l, w, flag)
2708
2709 if(any(flag)) then
2710 select case (small_values_method)
2711 case ("replace")
2712 {do ix^db=ixomin^db,ixomax^db\}
2713 if(flag(ix^d,rho_)) w(ix^d,rho_)=small_density
2714 {
2715 if(small_values_fix_iw(m^c_)) then
2716 if(flag({ix^d},rho_)) w({ix^d},m^c_)=0.0d0
2717 end if
2718 \}
2719 if(primitive) then
2720 if(flag(ix^d,e_)) w(ix^d,p_)=small_pressure
2721 else
2722 if(mhd_uawsom) then
2723 if(flag(ix^d,waplus_)) w(ix^d,waplus_)=zero
2724 if(flag(ix^d,waminus_)) w(ix^d,waminus_)=zero
2725 if(flag(ix^d,wkplus_)) w(ix^d,wkplus_)=zero
2726 if(flag(ix^d,wkminus_)) w(ix^d,wkminus_)=zero
2727 end if
2728 if(flag(ix^d,e_)) &
2729 w(ix^d,e_)=small_e+half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)+(^c&w(ix^d,b^c_)**2+))&
2731 end if
2732 if(mhd_radiation_fld)then
2733 if(small_values_fix_iw(r_e)) then
2734 if(flag(ix^d,r_e)) w(ix^d,r_e)=small_r_e
2735 endif
2736 endif
2737 {end do\}
2738 case ("average")
2739 ! do averaging of density
2740 call small_values_average(ixi^l, ixo^l, w, x, flag, rho_)
2741 if(primitive)then
2742 call small_values_average(ixi^l, ixo^l, w, x, flag, p_)
2743 else
2744 if(mhd_uawsom) then
2745 call small_values_average(ixi^l,ixo^l,w,x,flag,waplus_)
2746 call small_values_average(ixi^l,ixo^l,w,x,flag,waminus_)
2747 call small_values_average(ixi^l,ixo^l,w,x,flag,wkplus_)
2748 call small_values_average(ixi^l,ixo^l,w,x,flag,wkminus_)
2749 end if
2750 ! do averaging of internal energy
2751 {do ix^db=iximin^db,iximax^db\}
2752 w(ix^d,e_)=w(ix^d,e_)&
2753 -half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)+(^c&w(ix^d,b^c_)**2+))&
2754 -mhd_uawsom_wave_energy_cell(w(ix^d,:))
2755 {end do\}
2756 call small_values_average(ixi^l, ixo^l, w, x, flag, e_)
2757 ! convert back
2758 {do ix^db=iximin^db,iximax^db\}
2759 w(ix^d,e_)=w(ix^d,e_)&
2760 +half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)+(^c&w(ix^d,b^c_)**2+))&
2761 +mhd_uawsom_wave_energy_cell(w(ix^d,:))
2762 {end do\}
2763 end if
2764 if(mhd_radiation_fld) then
2765 call small_values_average(ixi^l, ixo^l, w, x, flag, r_e)
2766 endif
2767 case default
2768 if(.not.primitive) then
2769 !convert w to primitive
2770 {do ix^db=ixomin^db,ixomax^db\}
2771 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)/w(ix^d,rho_)\
2772 w(ix^d,p_)=eos%gamma_minus_1*(w(ix^d,e_)&
2773 -half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)+(^c&w(ix^d,b^c_)**2+))&
2774 -mhd_uawsom_wave_energy_cell(w(ix^d,:)))
2775 {end do\}
2776 end if
2777 call small_values_error(w, x, ixi^l, ixo^l, flag, subname)
2778 end select
2779 end if
2780 if (mhd_fip) call mhd_bound_fip(primitive, ixi^l, ixo^l, w)
2781 end subroutine mhd_handle_small_values_origin
2782
2783 subroutine mhd_handle_small_values_split(primitive, w, x, ixI^L, ixO^L, subname)
2786 logical, intent(in) :: primitive
2787 integer, intent(in) :: ixi^l,ixo^l
2788 double precision, intent(inout) :: w(ixi^s,1:nw)
2789 double precision, intent(in) :: x(ixi^s,1:ndim)
2790 character(len=*), intent(in) :: subname
2791
2792 double precision :: rho
2793 integer :: ix^d, igrp
2794 logical :: flag(ixi^s,1:nw)
2795
2796 call phys_check_w(primitive, ixi^l, ixo^l, w, flag)
2797
2798 if(any(flag)) then
2799 select case (small_values_method)
2800 case ("replace")
2801 {do ix^db=ixomin^db,ixomax^db\}
2802 rho=w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0)
2803 if(flag(ix^d,rho_)) w(ix^d,rho_)=small_density-block%equi_vars(ix^d,equi_rho0_,0)
2804 {
2805 if(small_values_fix_iw(m^c_)) then
2806 if(flag({ix^d},rho_)) w({ix^d},m^c_)=0.0d0
2807 end if
2808 \}
2809 if(primitive) then
2810 if(flag(ix^d,e_)) w(ix^d,p_)=small_pressure-block%equi_vars(ix^d,equi_pe0_,0)
2811 else
2812 if(flag(ix^d,e_)) &
2813 w(ix^d,e_)=small_e+half*((^c&w(ix^d,m^c_)**2+)/rho+(^c&w(ix^d,b^c_)**2+))&
2814 -block%equi_vars(ix^d,equi_pe0_,0)*eos%inv_gamma_minus_1
2815 end if
2816 {end do\}
2817 case ("average")
2818 ! do averaging of density
2819 call small_values_average(ixi^l, ixo^l, w, x, flag, rho_)
2820 if(primitive)then
2821 call small_values_average(ixi^l, ixo^l, w, x, flag, p_)
2822 else
2823 ! do averaging of internal energy
2824 {do ix^db=iximin^db,iximax^db\}
2825 rho=w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0)
2826 w(ix^d,e_)=w(ix^d,e_)&
2827 -half*((^c&w(ix^d,m^c_)**2+)/rho+(^c&w(ix^d,b^c_)**2+))
2828 {end do\}
2829 call small_values_average(ixi^l, ixo^l, w, x, flag, e_)
2830 ! convert back
2831 {do ix^db=iximin^db,iximax^db\}
2832 rho=w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0)
2833 w(ix^d,e_)=w(ix^d,e_)&
2834 +half*((^c&w(ix^d,m^c_)**2+)/rho+(^c&w(ix^d,b^c_)**2+))
2835 {end do\}
2836 end if
2837 case default
2838 if(.not.primitive) then
2839 !convert w to primitive
2840 {do ix^db=ixomin^db,ixomax^db\}
2841 rho=w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0)
2842 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)/rho\
2843 w(ix^d,p_)=eos%gamma_minus_1*(w(ix^d,e_)&
2844 -half*((^c&w(ix^d,m^c_)**2+)*rho+(^c&w(ix^d,b^c_)**2+)))
2845 {end do\}
2846 end if
2847 call small_values_error(w, x, ixi^l, ixo^l, flag, subname)
2848 end select
2849 end if
2850 if (mhd_fip) call mhd_bound_fip(primitive, ixi^l, ixo^l, w)
2851 end subroutine mhd_handle_small_values_split
2852
2853 subroutine mhd_handle_small_values_inte(primitive, w, x, ixI^L, ixO^L, subname)
2856 logical, intent(in) :: primitive
2857 integer, intent(in) :: ixi^l,ixo^l
2858 double precision, intent(inout) :: w(ixi^s,1:nw)
2859 double precision, intent(in) :: x(ixi^s,1:ndim)
2860 character(len=*), intent(in) :: subname
2861
2862 integer :: ix^d, igrp
2863 logical :: flag(ixi^s,1:nw)
2864
2865 call phys_check_w(primitive, ixi^l, ixo^l, w, flag)
2866
2867 if(any(flag)) then
2868 select case (small_values_method)
2869 case ("replace")
2870 {do ix^db=ixomin^db,ixomax^db\}
2871 if(flag(ix^d,rho_)) then
2872 w(ix^d,rho_)=small_density
2873 ^c&w(ix^d,m^c_)=0.d0\
2874 end if
2875 if(primitive) then
2876 if(flag(ix^d,e_)) w(ix^d,p_)=small_pressure
2877 else
2878 if(flag(ix^d,e_)) w(ix^d,e_)=small_e
2879 end if
2880 {end do\}
2881 case ("average")
2882 ! do averaging of density
2883 call small_values_average(ixi^l, ixo^l, w, x, flag, rho_)
2884 ! do averaging of internal energy
2885 call small_values_average(ixi^l, ixo^l, w, x, flag, p_)
2886 case default
2887 if(.not.primitive) then
2888 !convert w to primitive
2889 {do ix^db=ixomin^db,ixomax^db\}
2890 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)/w(ix^d,rho_)\
2891 w(ix^d,p_)=eos%gamma_minus_1*w(ix^d,e_)
2892 {end do\}
2893 end if
2894 call small_values_error(w, x, ixi^l, ixo^l, flag, subname)
2895 end select
2896 end if
2897 if (mhd_fip) call mhd_bound_fip(primitive, ixi^l, ixo^l, w)
2898 end subroutine mhd_handle_small_values_inte
2899
2900 subroutine mhd_handle_small_values_noe(primitive, w, x, ixI^L, ixO^L, subname)
2903 logical, intent(in) :: primitive
2904 integer, intent(in) :: ixi^l,ixo^l
2905 double precision, intent(inout) :: w(ixi^s,1:nw)
2906 double precision, intent(in) :: x(ixi^s,1:ndim)
2907 character(len=*), intent(in) :: subname
2908
2909 integer :: ix^d, igrp
2910 logical :: flag(ixi^s,1:nw)
2911
2912 call phys_check_w(primitive, ixi^l, ixo^l, w, flag)
2913
2914 if(any(flag)) then
2915 select case (small_values_method)
2916 case ("replace")
2917 {do ix^db=ixomin^db,ixomax^db\}
2918 if(flag(ix^d,rho_)) w(ix^d,rho_)=small_density
2919 {
2920 if(small_values_fix_iw(m^c_)) then
2921 if(flag({ix^d},rho_)) w({ix^d},m^c_)=0.0d0
2922 end if
2923 \}
2924 {end do\}
2925 case ("average")
2926 ! do averaging of density
2927 call small_values_average(ixi^l, ixo^l, w, x, flag, rho_)
2928 case default
2929 if(.not.primitive) then
2930 !convert w to primitive
2931 {do ix^db=ixomin^db,ixomax^db\}
2932 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)/w(ix^d,rho_)\
2933 {end do\}
2934 end if
2935 call small_values_error(w, x, ixi^l, ixo^l, flag, subname)
2936 end select
2937 end if
2938 if (mhd_fip) call mhd_bound_fip(primitive, ixi^l, ixo^l, w)
2939 end subroutine mhd_handle_small_values_noe
2940
2941 subroutine mhd_handle_small_values_hde(primitive, w, x, ixI^L, ixO^L, subname)
2944 logical, intent(in) :: primitive
2945 integer, intent(in) :: ixi^l,ixo^l
2946 double precision, intent(inout) :: w(ixi^s,1:nw)
2947 double precision, intent(in) :: x(ixi^s,1:ndim)
2948 character(len=*), intent(in) :: subname
2949
2950 integer :: ix^d, igrp
2951 logical :: flag(ixi^s,1:nw)
2952
2953 call phys_check_w(primitive, ixi^l, ixo^l, w, flag)
2954
2955 if(any(flag)) then
2956 select case (small_values_method)
2957 case ("replace")
2958 {do ix^db=ixomin^db,ixomax^db\}
2959 if(flag(ix^d,rho_)) then
2960 w(ix^d,rho_)=small_density
2961 ^c&w(ix^d,m^c_)=0.d0\
2962 end if
2963 if(primitive) then
2964 if(flag(ix^d,e_)) w(ix^d,p_)=small_pressure
2965 else
2966 if(flag(ix^d,e_)) w(ix^d,e_)=small_e+half*(^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)
2967 end if
2968 {end do\}
2969 case ("average")
2970 ! do averaging of density
2971 call small_values_average(ixi^l, ixo^l, w, x, flag, rho_)
2972 ! do averaging of energy
2973 call small_values_average(ixi^l, ixo^l, w, x, flag, e_)
2974 case default
2975 if(.not.primitive) then
2976 !convert w to primitive
2977 {do ix^db=ixomin^db,ixomax^db\}
2978 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)/w(ix^d,rho_)\
2979 w(ix^d,p_)=eos%gamma_minus_1*(w(ix^d,e_)-half*(^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_))
2980 {end do\}
2981 end if
2982 call small_values_error(w, x, ixi^l, ixo^l, flag, subname)
2983 end select
2984 end if
2985 if (mhd_fip) call mhd_bound_fip(primitive, ixi^l, ixo^l, w)
2986 end subroutine mhd_handle_small_values_hde
2987
2988 !> Calculate v vector
2989 subroutine mhd_get_v(w,x,ixI^L,ixO^L,v)
2991
2992 integer, intent(in) :: ixi^l, ixo^l
2993 double precision, intent(in) :: w(ixi^s,nw), x(ixi^s,1:ndim)
2994 double precision, intent(out) :: v(ixi^s,ndir)
2995
2996 double precision :: rho(ixi^s)
2997 integer :: idir
2998
2999 if(mhd_semirelativistic) then
3000 call mhd_semirelati_vel_emech(w,ixi^l,ixo^l,v(ixo^s,1:ndir))
3001 return
3002 end if
3003
3004 call mhd_get_rho(w,x,ixi^l,ixo^l,rho)
3005
3006 rho(ixo^s)=1.d0/rho(ixo^s)
3007 ! Convert momentum to velocity
3008 do idir = 1, ndir
3009 v(ixo^s, idir) = w(ixo^s, mom(idir))*rho(ixo^s)
3010 end do
3011
3012 end subroutine mhd_get_v
3013
3014 !> Comoving UAWSoM signal-speed bound shared by CFL and HLL (primitive input).
3015 !> For locally fixed closure coefficients the ideal-MHD/wave principal matrix
3016 !> is symmetric after rescaling. Its blocks have bounds cfast and awave,
3017 !> and coupling norm sqrt(Pwave/rho). The final expression bounds both signs.
3018 !> This does not bound reflection-gradient closures or state-dependent zeta.
3019 subroutine mhd_uawsom_get_signal_speed(w,x,ixI^L,ixO^L,idim,csignal)
3021 integer, intent(in) :: ixi^l, ixo^l, idim
3022 double precision, intent(in) :: w(ixi^s,nw), x(ixi^s,1:ndim)
3023 double precision, intent(out) :: csignal(ixo^s)
3024 double precision :: cs2(ixi^s), zeta(ixi^s), radius(ixi^s), lperp(ixi^s)
3025 double precision :: btotal(1:ndir), b2, inv_rho, qwave, sound2
3026 double precision :: cfast2, discriminant, cfast, awave, kmax
3027 integer :: ix^d
3028
3029 call eos%get_csound2(w,x,ixi^l,ixo^l,cs2)
3030 call mhd_uawsom_get_coefficients(w,x,ixi^l,ixo^l,.true.,zeta,radius,lperp)
3031 if(mhd_hall) kmax=dpi/min({dxlevel(^d)},bigdouble)*half
3032 ! UAWSoM parameter checks exclude equilibrium density/pressure splitting.
3033 {do ix^db=ixomin^db,ixomax^db\}
3034 inv_rho=one/w(ix^d,rho_)
3035 ^c&btotal(^c)=w(ix^d,b^c_)\
3036 if(b0field) then
3037 ^c&btotal(^c)=btotal(^c)+block%B0(ix^d,^c,b0i)\
3038 end if
3039 b2=(^c&btotal(^c)**2+)
3040 qwave=mhd_uawsom_wave_pressure_cell(w(ix^d,:),zeta(ix^d))*inv_rho
3041 ! Only the signal estimate changes; the thermal EOS sound speed is intact.
3042 sound2=cs2(ix^d)+half*qwave
3043 cfast2=b2*inv_rho+sound2
3044 discriminant=max(zero,cfast2**2-4.d0*sound2*btotal(idim)**2*inv_rho)
3045 cfast=sqrt(half*(cfast2+sqrt(discriminant)))
3046 ! Retain the existing Hall estimate; the ideal-MHD proof excludes Hall.
3047 if(mhd_hall) cfast=max(cfast,mhd_etah*sqrt(b2)*inv_rho*kmax)
3048 ! Use the same filling-factor dependence as the kink energy flux.
3049 awave=abs(btotal(idim))*sqrt(inv_rho)*max(one,&
3050 sqrt(two*(one+mhd_uawsom_filling_factor*zeta(ix^d)-&
3051 mhd_uawsom_filling_factor)/(zeta(ix^d)+one)))
3052 csignal(ix^d)=half*(cfast+awave+sqrt((cfast-awave)**2+4.d0*qwave))
3053 {end do\}
3054 end subroutine mhd_uawsom_get_signal_speed
3055
3056 !> Calculate cmax_idim=csound+abs(v_idim) within ixO^L
3057 subroutine mhd_get_cmax_origin(w,x,ixI^L,ixO^L,idim,cmax)
3059
3060 integer, intent(in) :: ixi^l, ixo^l, idim
3061 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
3062 double precision, intent(inout) :: cmax(ixi^s)
3063
3064 double precision :: rho, inv_rho, cfast2, avmincs2, b2, kmax
3065 double precision :: cs2(ixi^s)
3066 integer :: ix^d
3067
3068 if(mhd_uawsom) then
3069 call mhd_uawsom_get_signal_speed(w,x,ixi^l,ixo^l,idim,cmax(ixo^s))
3070 cmax(ixo^s)=abs(w(ixo^s,mom(idim)))+cmax(ixo^s)
3071 return
3072 end if
3073
3074 if(mhd_hall) kmax = dpi/min({dxlevel(^d)},bigdouble)*half
3075
3076 ! Sound speed squared via EoS dispatch (LTE+ionE -> Gamma_1 table; FI -> const gamma).
3077 call eos%get_csound2(w, x, ixi^l, ixo^l, cs2)
3078
3079 if(b0field) then
3080 {do ix^db=ixomin^db,ixomax^db \}
3081 if(has_equi_rho_and_p) then
3082 rho=(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
3083 else
3084 rho=w(ix^d,rho_)
3085 end if
3086 inv_rho=1.d0/rho
3087 ! store |B|^2 in v
3088 b2=(^c&(w(ix^d,b^c_)+block%B0(ix^d,^c,b0i))**2+)
3089 cfast2=b2*inv_rho+cs2(ix^d)
3090 avmincs2=cfast2**2-4.0d0*cs2(ix^d)*(w(ix^d,mag(idim))+block%B0(ix^d,idim,b0i))**2*inv_rho
3091 if(avmincs2<zero) avmincs2=zero
3092 cmax(ix^d)=sqrt(half*(cfast2+sqrt(avmincs2)))
3093 if(mhd_hall) then
3094 ! take the Hall velocity into account: most simple estimate, high k limit:
3095 ! largest wavenumber supported by grid: Nyquist (in practise can reduce by some factor)
3096 cmax(ix^d)=max(cmax(ix^d),mhd_etah*sqrt(b2)*inv_rho*kmax)
3097 end if
3098 cmax(ix^d)=abs(w(ix^d,mom(idim)))+cmax(ix^d)
3099 {end do\}
3100 else
3101 {do ix^db=ixomin^db,ixomax^db \}
3102 if(has_equi_rho_and_p) then
3103 rho=(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
3104 else
3105 rho=w(ix^d,rho_)
3106 end if
3107 inv_rho=1.d0/rho
3108 ! store |B|^2 in v
3109 b2=(^c&w(ix^d,b^c_)**2+)
3110 cfast2=b2*inv_rho+cs2(ix^d)
3111 avmincs2=cfast2**2-4.0d0*cs2(ix^d)*w(ix^d,mag(idim))**2*inv_rho
3112 if(avmincs2<zero) avmincs2=zero
3113 cmax(ix^d)=sqrt(half*(cfast2+sqrt(avmincs2)))
3114 if(mhd_hall) then
3115 ! take the Hall velocity into account: most simple estimate, high k limit:
3116 ! largest wavenumber supported by grid: Nyquist (in practise can reduce by some factor)
3117 cmax(ix^d)=max(cmax(ix^d),mhd_etah*sqrt(b2)*inv_rho*kmax)
3118 end if
3119 cmax(ix^d)=abs(w(ix^d,mom(idim)))+cmax(ix^d)
3120 {end do\}
3121 end if
3122
3123 end subroutine mhd_get_cmax_origin
3124
3125 !> Calculate cmax_idim=csound+abs(v_idim) within ixO^L
3126 subroutine mhd_get_cmax_origin_noe(w,x,ixI^L,ixO^L,idim,cmax)
3129
3130 integer, intent(in) :: ixi^l, ixo^l, idim
3131 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
3132 double precision, intent(inout) :: cmax(ixi^s)
3133
3134 double precision :: inv_rho, cfast2, avmincs2, b2, kmax
3135 double precision :: adiabs(ixi^s), gammas(ixi^s)
3136 integer :: ix^d
3137
3138 if(mhd_hall) kmax = dpi/min({dxlevel(^d)},bigdouble)*half
3139
3140 if(associated(usr_set_adiab)) then
3141 call usr_set_adiab(w,x,ixi^l,ixo^l,adiabs)
3142 else
3143 adiabs=mhd_adiab
3144 end if
3145 if(associated(usr_set_gamma)) then
3146 call usr_set_gamma(w,x,ixi^l,ixo^l,gammas)
3147 else
3148 gammas=eos%gamma
3149 end if
3150 {do ix^db=ixomin^db,ixomax^db \}
3151 inv_rho=1.d0/w(ix^d,rho_)
3152 ! sound speed**2
3153 cmax(ix^d)=gammas(ix^d)*adiabs(ix^d)*w(ix^d,rho_)**(gammas(ix^d)-1.d0)
3154 ! store |B|^2 in v
3155 b2=(^c&w(ix^d,b^c_)**2+)
3156 cfast2=b2*inv_rho+cmax(ix^d)
3157 avmincs2=cfast2**2-4.0d0*cmax(ix^d)*w(ix^d,mag(idim))**2*inv_rho
3158 if(avmincs2<zero) avmincs2=zero
3159 cmax(ix^d)=sqrt(half*(cfast2+sqrt(avmincs2)))
3160 if(mhd_hall) then
3161 ! take the Hall velocity into account: most simple estimate, high k limit:
3162 ! largest wavenumber supported by grid: Nyquist (in practise can reduce by some factor)
3163 cmax(ix^d)=max(cmax(ix^d),mhd_etah*sqrt(b2)*inv_rho*kmax)
3164 end if
3165 cmax(ix^d)=abs(w(ix^d,mom(idim)))+cmax(ix^d)
3166 {end do\}
3167
3168 end subroutine mhd_get_cmax_origin_noe
3169
3170 !> Calculate cmax_idim for semirelativistic MHD, the fast speed plus the advection speed
3171 subroutine mhd_get_cmax_semirelati(w,x,ixI^L,ixO^L,idim,cmax)
3173
3174 integer, intent(in) :: ixi^l, ixo^l, idim
3175 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
3176 double precision, intent(inout):: cmax(ixi^s)
3177
3178 double precision :: csound(ixo^s), gamma2(ixo^s)
3179
3180 call mhd_get_csound_semirelati(w,x,ixi^l,ixo^l,idim,csound,gamma2)
3181 ! equation (68), cmax = gamma^2 abs(v_idim) + c_fast
3182 cmax(ixo^s)=gamma2(ixo^s)*abs(w(ixo^s,mom(idim)))+csound(ixo^s)
3183
3184 end subroutine mhd_get_cmax_semirelati
3185
3186 !> Calculate cmax_idim for semirelativistic MHD
3187 subroutine mhd_get_cmax_semirelati_noe(w,x,ixI^L,ixO^L,idim,cmax)
3190
3191 integer, intent(in) :: ixi^l, ixo^l, idim
3192 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
3193 double precision, intent(inout):: cmax(ixi^s)
3194
3195 double precision :: adiabs(ixi^s), gammas(ixi^s)
3196 double precision :: csound, avmincs2, idim_alfven_speed2
3197 double precision :: inv_rho, alfven_speed2, gamma2
3198 integer :: ix^d
3199
3200 if(associated(usr_set_adiab)) then
3201 call usr_set_adiab(w,x,ixi^l,ixo^l,adiabs)
3202 else
3203 adiabs=mhd_adiab
3204 end if
3205 if(associated(usr_set_gamma)) then
3206 call usr_set_gamma(w,x,ixi^l,ixo^l,gammas)
3207 else
3208 gammas=eos%gamma
3209 end if
3210
3211 {do ix^db=ixomin^db,ixomax^db \}
3212 inv_rho=1.d0/w(ix^d,rho_)
3213 alfven_speed2=(^c&w(ix^d,b^c_)**2+)*inv_rho
3214 gamma2=1.0d0/(1.d0+alfven_speed2*inv_squared_c)
3215 cmax(ix^d)=1.d0-gamma2*w(ix^d,mom(idim))**2*inv_squared_c
3216 ! sound speed squared
3217 csound=gammas(ix^d)*adiabs(ix^d)*w(ix^d,rho_)**(gammas(ix^d)-1.d0)
3218 idim_alfven_speed2=w(ix^d,mag(idim))**2*inv_rho
3219 ! Va_hat^2+a_hat^2 equation (57)
3220 ! equation (69)
3221 alfven_speed2=alfven_speed2*cmax(ix^d)+csound*(1.d0+idim_alfven_speed2*inv_squared_c)
3222 avmincs2=(gamma2*alfven_speed2)**2-4.0d0*gamma2*csound*idim_alfven_speed2*cmax(ix^d)
3223 if(avmincs2<zero) avmincs2=zero
3224 ! equation (68) fast magnetosonic wave speed
3225 cmax(ix^d)=gamma2*abs(w(ix^d,mom(idim)))+sqrt(half*(gamma2*alfven_speed2+sqrt(avmincs2)))
3226 {end do\}
3227
3228 end subroutine mhd_get_cmax_semirelati_noe
3229
3230 !> get adaptive cutoff temperature for TRAC (Johnston 2019 ApJL, 873, L22)
3231 subroutine mhd_get_tcutoff(ixI^L,ixO^L,w,x,Tco_local,Tmax_local)
3233 use mod_geometry
3236 integer, intent(in) :: ixi^l,ixo^l
3237 double precision, intent(in) :: x(ixi^s,1:ndim)
3238 ! in primitive form
3239 double precision, intent(inout) :: w(ixi^s,1:nw)
3240 double precision, intent(out) :: tco_local,tmax_local
3241
3242 double precision, parameter :: trac_delta=0.25d0
3243 double precision :: te(ixi^s),lts(ixi^s)
3244 double precision, dimension(1:ndim) :: bdir, bunitvec
3245 double precision, dimension(ixI^S,1:ndim) :: gradt
3246 double precision :: ltrc,ltrp,altr
3247 ! TRAC-7 (Johnston et al. 2021, A&A 654 A2) field-aligned local method
3248 double precision :: q_heat(ixi^s), ne(ixi^s), nh_arr(ixi^s)
3249 double precision :: bvec(1:ndir)
3250 double precision :: bmin_c, bmag, gnorm, bgtd, l_t, dl_eff, vmag, bdotv, binp2
3251 double precision :: v_n, a_coeff, l1, cooling, net_cool
3252 double precision :: kappa_par, disc, dx_over_delta, kappa_trac, kappa_eff, tcoff_eff
3253 integer :: idims,ix^d,jxo^l,hxo^l,ixa^d,ixb^d
3254 integer :: jxp^l,hxp^l,ixp^l,ixq^l
3255
3256 if (eos%eos_type == 'LTE' .or. eos%eos_type == 'PI') then
3257 te(ixi^s) = w(ixi^s, te_)
3258 else
3259 call mhd_get_rho(w,x,ixi^l,ixi^l,ne)
3260 call eos%get_Rfactor(w,x,ixi^l,ixi^l,te)
3261 te(ixi^s)=w(ixi^s,p_)/(te(ixi^s)*ne(ixi^s))
3262 end if
3263 tco_local=zero
3264 tmax_local=maxval(te(ixo^s))
3265
3266 {^ifoned
3267 select case(mhd_trac_type)
3268 case(0)
3269 !> test case, fixed cutoff temperature
3270 block%wextra(ixi^s,tcoff_)=2.5d5/unit_temperature
3271 case(1)
3272 do ix1=ixomin1,ixomax1
3273 lts(ix1)=0.5d0*abs(te(ix1+1)-te(ix1-1))/te(ix1)
3274 if(lts(ix1)>trac_delta) then
3275 tco_local=max(tco_local,te(ix1))
3276 end if
3277 end do
3278 case(2)
3279 !> iijima et al. 2021, LTRAC method
3280 ltrc=1.5d0
3281 ltrp=4.d0
3282 ixp^l=ixo^l^ladd1;
3283 hxo^l=ixo^l-1;
3284 jxo^l=ixo^l+1;
3285 hxp^l=ixp^l-1;
3286 jxp^l=ixp^l+1;
3287 lts(ixp^s)=0.5d0*abs(te(jxp^s)-te(hxp^s))/te(ixp^s)
3288 lts(ixp^s)=max(one, (exp(lts(ixp^s))/ltrc)**ltrp)
3289 lts(ixo^s)=0.25d0*(lts(jxo^s)+two*lts(ixo^s)+lts(hxo^s))
3290 block%wextra(ixo^s,tcoff_)=te(ixo^s)*lts(ixo^s)**0.4d0
3291 case default
3292 call mpistop("mhd_trac_type not allowed for 1D simulation")
3293 end select
3294 }
3295 {^nooned
3296 select case(mhd_trac_type)
3297 case(0)
3298 !> test case, fixed cutoff temperature
3299 block%wextra(ixi^s,tcoff_)=2.5d5/unit_temperature
3300 case(1,4,6)
3301 ! temperature gradient at cell centers
3302 do idims=1,ndim
3303 call gradient(te,ixi^l,ixo^l,idims,gradt(ixi^s,idims))
3304 end do
3305 if(mhd_trac_type .gt. 1) then
3306 ! B direction at block center
3307 bdir=zero
3308 if(b0field) then
3309 {do ixa^d=0,1\}
3310 ixb^d=(ixomin^d+ixomax^d-1)/2+ixa^d;
3311 bdir(1:ndim)=bdir(1:ndim)+w(ixb^d,iw_mag(1:ndim))+block%B0(ixb^d,1:ndim,0)
3312 {end do\}
3313 else
3314 {do ixa^d=0,1\}
3315 ixb^d=(ixomin^d+ixomax^d-1)/2+ixa^d;
3316 bdir(1:ndim)=bdir(1:ndim)+w(ixb^d,iw_mag(1:ndim))
3317 {end do\}
3318 end if
3319 {^iftwod
3320 if(bdir(1)/=0.d0) then
3321 block%special_values(3)=sign(1.d0,bdir(1))/dsqrt(1.d0+(bdir(2)/bdir(1))**2)
3322 else
3323 block%special_values(3)=0.d0
3324 end if
3325 if(bdir(2)/=0.d0) then
3326 block%special_values(4)=sign(1.d0,bdir(2))/dsqrt(1.d0+(bdir(1)/bdir(2))**2)
3327 else
3328 block%special_values(4)=0.d0
3329 end if
3330 }
3331 {^ifthreed
3332 if(bdir(1)/=0.d0) then
3333 block%special_values(3)=sign(1.d0,bdir(1))/dsqrt(1.d0+(bdir(2)/bdir(1))**2+&
3334 (bdir(3)/bdir(1))**2)
3335 else
3336 block%special_values(3)=0.d0
3337 end if
3338 if(bdir(2)/=0.d0) then
3339 block%special_values(4)=sign(1.d0,bdir(2))/dsqrt(1.d0+(bdir(1)/bdir(2))**2+&
3340 (bdir(3)/bdir(2))**2)
3341 else
3342 block%special_values(4)=0.d0
3343 end if
3344 if(bdir(3)/=0.d0) then
3345 block%special_values(5)=sign(1.d0,bdir(3))/dsqrt(1.d0+(bdir(1)/bdir(3))**2+&
3346 (bdir(2)/bdir(3))**2)
3347 else
3348 block%special_values(5)=0.d0
3349 end if
3350 }
3351 end if
3352 ! b unit vector: magnetic field direction vector
3353 block%special_values(1)=zero
3354 {do ix^db=ixomin^db,ixomax^db\}
3355 if(b0field) then
3356 ^d&bdir(^d)=w({ix^d},iw_mag(^d))+block%B0({ix^d},^d,0)\
3357 else
3358 ^d&bdir(^d)=w({ix^d},iw_mag(^d))\
3359 end if
3360 {^iftwod
3361 if(bdir(1)/=0.d0) then
3362 bunitvec(1)=sign(1.d0,bdir(1))/dsqrt(1.d0+(bdir(2)/bdir(1))**2)
3363 else
3364 bunitvec(1)=0.d0
3365 end if
3366 if(bdir(2)/=0.d0) then
3367 bunitvec(2)=sign(1.d0,bdir(2))/dsqrt(1.d0+(bdir(1)/bdir(2))**2)
3368 else
3369 bunitvec(2)=0.d0
3370 end if
3371 ! temperature length scale inversed
3372 lts(ix^d)=min(block%ds(ix^d,1),block%ds(ix^d,2))*&
3373 abs(^d&gradt({ix^d},^d)*bunitvec(^d)+)/te(ix^d)
3374 }
3375 {^ifthreed
3376 if(bdir(1)/=0.d0) then
3377 bunitvec(1)=sign(1.d0,bdir(1))/dsqrt(1.d0+(bdir(2)/bdir(1))**2+(bdir(3)/bdir(1))**2)
3378 else
3379 bunitvec(1)=0.d0
3380 end if
3381 if(bdir(2)/=0.d0) then
3382 bunitvec(2)=sign(1.d0,bdir(2))/dsqrt(1.d0+(bdir(1)/bdir(2))**2+(bdir(3)/bdir(2))**2)
3383 else
3384 bunitvec(2)=0.d0
3385 end if
3386 if(bdir(3)/=0.d0) then
3387 bunitvec(3)=sign(1.d0,bdir(3))/dsqrt(1.d0+(bdir(1)/bdir(3))**2+(bdir(2)/bdir(3))**2)
3388 else
3389 bunitvec(3)=0.d0
3390 end if
3391 ! temperature length scale inversed
3392 lts(ix^d)=min(block%ds(ix^d,1),block%ds(ix^d,2),block%ds(ix^d,3))*&
3393 abs(^d&gradt({ix^d},^d)*bunitvec(^d)+)/te(ix^d)
3394 }
3395 if(lts(ix^d)>trac_delta) then
3396 block%special_values(1)=max(block%special_values(1),te(ix^d))
3397 end if
3398 {end do\}
3399 block%special_values(2)=tmax_local
3400 case(2)
3401 !> iijima et al. 2021, LTRAC method
3402 ltrc=1.5d0
3403 ltrp=4.d0
3404 ixp^l=ixo^l^ladd2;
3405 ! temperature gradient at cell centers
3406 do idims=1,ndim
3407 ixq^l=ixp^l;
3408 hxp^l=ixp^l;
3409 jxp^l=ixp^l;
3410 select case(idims)
3411 {case(^d)
3412 ixqmin^d=ixqmin^d+1
3413 ixqmax^d=ixqmax^d-1
3414 hxpmax^d=ixpmin^d
3415 jxpmin^d=ixpmax^d
3416 \}
3417 end select
3418 call gradient(te,ixi^l,ixq^l,idims,gradt(ixi^s,idims))
3419 ! The outermost ghost cell has no room for a fourth-order one-sided
3420 ! stencil. A first-order one-sided gradient keeps the two-ghost-layer
3421 ! LTRAC coverage without reading outside ixI.
3422 call gradientf(te,x,ixi^l,hxp^l,idims,gradt(ixi^s,idims),1,.true.)
3423 call gradientf(te,x,ixi^l,jxp^l,idims,gradt(ixi^s,idims),1,.false.)
3424 end do
3425 ! b unit vector: magnetic field direction vector
3426 if(b0field) then
3427 {do ix^db=ixpmin^db,ixpmax^db\}
3428 ^d&bdir(^d)=w({ix^d},iw_mag(^d))+block%B0({ix^d},^d,0)\
3429 {^iftwod
3430 if(bdir(1)/=0.d0) then
3431 bunitvec(1)=sign(1.d0,bdir(1))/dsqrt(1.d0+(bdir(2)/bdir(1))**2)
3432 else
3433 bunitvec(1)=0.d0
3434 end if
3435 if(bdir(2)/=0.d0) then
3436 bunitvec(2)=sign(1.d0,bdir(2))/dsqrt(1.d0+(bdir(1)/bdir(2))**2)
3437 else
3438 bunitvec(2)=0.d0
3439 end if
3440 }
3441 {^ifthreed
3442 if(bdir(1)/=0.d0) then
3443 bunitvec(1)=sign(1.d0,bdir(1))/dsqrt(1.d0+(bdir(2)/bdir(1))**2+(bdir(3)/bdir(1))**2)
3444 else
3445 bunitvec(1)=0.d0
3446 end if
3447 if(bdir(2)/=0.d0) then
3448 bunitvec(2)=sign(1.d0,bdir(2))/dsqrt(1.d0+(bdir(1)/bdir(2))**2+(bdir(3)/bdir(2))**2)
3449 else
3450 bunitvec(2)=0.d0
3451 end if
3452 if(bdir(3)/=0.d0) then
3453 bunitvec(3)=sign(1.d0,bdir(3))/dsqrt(1.d0+(bdir(1)/bdir(3))**2+(bdir(2)/bdir(3))**2)
3454 else
3455 bunitvec(3)=0.d0
3456 end if
3457 }
3458 ! temperature length scale inversed
3459 lts(ix^d)=abs(^d&gradt({ix^d},^d)*bunitvec(^d)+)/te(ix^d)
3460 ! fraction of cells size to temperature length scale
3461 lts(ix^d)=min(^d&block%ds({ix^d},^d))*lts(ix^d)
3462 lts(ix^d)=max(one,(exp(lts(ix^d))/ltrc)**ltrp)
3463 {end do\}
3464 else
3465 {do ix^db=ixpmin^db,ixpmax^db\}
3466 {^iftwod
3467 if(w(ix^d,iw_mag(1))/=0.d0) then
3468 bunitvec(1)=sign(1.d0,w(ix^d,iw_mag(1)))/dsqrt(1.d0+(w(ix^d,iw_mag(2))/w(ix^d,iw_mag(1)))**2)
3469 else
3470 bunitvec(1)=0.d0
3471 end if
3472 if(w(ix^d,iw_mag(2))/=0.d0) then
3473 bunitvec(2)=sign(1.d0,w(ix^d,iw_mag(2)))/dsqrt(1.d0+(w(ix^d,iw_mag(1))/w(ix^d,iw_mag(2)))**2)
3474 else
3475 bunitvec(2)=0.d0
3476 end if
3477 }
3478 {^ifthreed
3479 if(w(ix^d,iw_mag(1))/=0.d0) then
3480 bunitvec(1)=sign(1.d0,w(ix^d,iw_mag(1)))/dsqrt(1.d0+(w(ix^d,iw_mag(2))/w(ix^d,iw_mag(1)))**2+&
3481 (w(ix^d,iw_mag(3))/w(ix^d,iw_mag(1)))**2)
3482 else
3483 bunitvec(1)=0.d0
3484 end if
3485 if(w(ix^d,iw_mag(2))/=0.d0) then
3486 bunitvec(2)=sign(1.d0,w(ix^d,iw_mag(2)))/dsqrt(1.d0+(w(ix^d,iw_mag(1))/w(ix^d,iw_mag(2)))**2+&
3487 (w(ix^d,iw_mag(3))/w(ix^d,iw_mag(2)))**2)
3488 else
3489 bunitvec(2)=0.d0
3490 end if
3491 if(w(ix^d,iw_mag(3))/=0.d0) then
3492 bunitvec(3)=sign(1.d0,w(ix^d,iw_mag(3)))/dsqrt(1.d0+(w(ix^d,iw_mag(1))/w(ix^d,iw_mag(3)))**2+&
3493 (w(ix^d,iw_mag(2))/w(ix^d,iw_mag(3)))**2)
3494 else
3495 bunitvec(3)=0.d0
3496 end if
3497 }
3498 ! temperature length scale inversed
3499 lts(ix^d)=abs(^d&gradt({ix^d},^d)*bunitvec(^d)+)/te(ix^d)
3500 ! fraction of cells size to temperature length scale
3501 lts(ix^d)=min(^d&block%ds({ix^d},^d))*lts(ix^d)
3502 lts(ix^d)=max(one,(exp(lts(ix^d))/ltrc)**ltrp)
3503 {end do\}
3504 end if
3505
3506 ! need one ghost layer for thermal conductivity
3507 ixp^l=ixo^l^ladd1;
3508 {do ix^db=ixpmin^db,ixpmax^db\}
3509 {^iftwod
3510 altr=0.25d0*((lts(ix1-1,ix2)+two*lts(ix^d)+lts(ix1+1,ix2))*bunitvec(1)**2+&
3511 (lts(ix1,ix2-1)+two*lts(ix^d)+lts(ix1,ix2+1))*bunitvec(2)**2)
3512 block%wextra(ix^d,tcoff_)=te(ix^d)*altr**0.4d0
3513 }
3514 {^ifthreed
3515 altr=0.25d0*((lts(ix1-1,ix2,ix3)+two*lts(ix^d)+lts(ix1+1,ix2,ix3))*bunitvec(1)**2+&
3516 (lts(ix1,ix2-1,ix3)+two*lts(ix^d)+lts(ix1,ix2+1,ix3))*bunitvec(2)**2+&
3517 (lts(ix1,ix2,ix3-1)+two*lts(ix^d)+lts(ix1,ix2,ix3+1))*bunitvec(3)**2)
3518 block%wextra(ix^d,tcoff_)=te(ix^d)*altr**0.4d0
3519 }
3520 {end do\}
3521 case(7)
3522 ! Johnston et al. 2021 (A&A 654, A2) local field-aligned TRAC for MHD.
3523 ! Anisotropic conduction broadens the TR along B, so the length scale (Eq.18) and the
3524 ! enthalpy flux (Eq.16) are projected on the field, regularized by b_min=0.1 G (paper
3525 ! value) as B->0 or B perpendicular to grad T. Per-cell adaptive parallel conductivity from the
3526 ! steady-state balance (Eq.11/12) with the Eq.13 selection; general-EoS cooling n_e n_H L.
3527 ! (HD isotropic version used the grad-T direction; here it is the magnetic field.)
3528 call usr_get_heating(q_heat, ixi^l, ixo^l, w, x)
3529 call eos%get_ne_nH(ixi^l, ixo^l, w, x, ne, nh_arr)
3530 block%wextra(ixi^s,tcoff_) = te(ixi^s) ! default (incl. ghost layer): no broadening
3531 do idims=1,ndim
3532 call gradient(te,ixi^l,ixo^l,idims,gradt(ixi^s,idims))
3533 end do
3534 bmin_c = 0.1d0/unit_magneticfield ! 0.1 G in code units
3535 {do ix^db=ixomin^db,ixomax^db\}
3536 ! full field B-magnitude (Eq.18 numerator, regularized), and B.v, v-magnitude over ndir
3537 bmag=bmin_c**2; bdotv=0.d0; vmag=0.d0
3538 do idims=1,ndir
3539 bvec(idims)=w(ix^d,iw_mag(idims))
3540 if(b0field) bvec(idims)=bvec(idims)+block%B0(ix^d,idims,0)
3541 bmag =bmag +bvec(idims)**2
3542 bdotv=bdotv+bvec(idims)*w(ix^d,mom(idims))
3543 vmag =vmag +w(ix^d,mom(idims))**2
3544 end do
3545 bmag=dsqrt(bmag); vmag=dsqrt(vmag)
3546 ! in-plane grad-T norm, in-plane field^2, and in-plane B.grad T (grad T has no z-part in 2D)
3547 gnorm=0.d0; binp2=0.d0; bgtd=0.d0
3548 do idims=1,ndim
3549 gnorm=gnorm+gradt(ix^d,idims)**2
3550 binp2=binp2+bvec(idims)**2
3551 bgtd =bgtd +bvec(idims)*gradt(ix^d,idims)
3552 end do
3553 gnorm=dsqrt(gnorm)
3554 if(gnorm<smalldouble .or. binp2<smalldouble) then
3555 block%wextra(ix^d,tcoff_)=te(ix^d) ! uniform T or no in-plane field: no TR
3556 else
3557 ! Eq.18 field-aligned length scale (dabs: conduction is bidirectional along B)
3558 l_t=te(ix^d)*bmag/(dabs(bgtd)+bmin_c*gnorm)
3559 ! grid spacing along the (in-plane) field direction b = Bvec/Bmag
3560 dl_eff=0.d0
3561 do idims=1,ndim
3562 dl_eff=dl_eff+(bvec(idims)/block%ds(ix^d,idims))**2
3563 end do
3564 dl_eff=bmag/dsqrt(dl_eff)
3565 ! Eq.16 field-aligned enthalpy (mass) flux
3566 v_n=(dabs(bdotv)+bmin_c*vmag)/bmag
3567 a_coeff=2.5d0*w(ix^d,p_)*v_n/te(ix^d)
3568 ! optically-thin cooling n_e n_H Lambda(T), guarded to the table range (positive
3569 ! in-range test so a non-finite Te falls to zero rather than indexing findL with NaN)
3570 if(te(ix^d)>rc_fl%tcoolmin .and. te(ix^d)<rc_fl%tcoolmax) then
3571 call findl(te(ix^d),l1,rc_fl); cooling=l1*ne(ix^d)*nh_arr(ix^d)
3572 else if(te(ix^d)>=rc_fl%tcoolmax) then
3573 call calc_l_extended(te(ix^d),l1,rc_fl); cooling=l1*ne(ix^d)*nh_arr(ix^d)
3574 else
3575 cooling=0.d0
3576 end if
3577 net_cool=dabs(cooling-q_heat(ix^d))
3578 kappa_par=tc_fl%tc_k_para*te(ix^d)**2.5d0
3579 disc=a_coeff**2+4.d0*tc_fl%tc_k_para*te(ix^d)**1.5d0*net_cool
3580 dx_over_delta=dl_eff/mhd_trac_delta
3581 ! Eq.13 selection: under-resolved (Eq.11, keep mass flux) vs over-resolved (Eq.12, limiter)
3582 if(l_t<=2.d0*dx_over_delta) then
3583 kappa_trac=(a_coeff+dsqrt(disc))/(2.d0/dx_over_delta)
3584 else
3585 kappa_trac=dsqrt(4.d0*tc_fl%tc_k_para*te(ix^d)**1.5d0*net_cool)/(2.d0/dx_over_delta)
3586 end if
3587 kappa_eff=max(kappa_trac,kappa_par)
3588 tcoff_eff=(kappa_eff/tc_fl%tc_k_para)**0.4d0
3589 block%wextra(ix^d,tcoff_)=max(te(ix^d),tcoff_eff)
3590 end if
3591 {end do\}
3592 case(3,5)
3593 !> do nothing here
3594 case default
3595 call mpistop("unknown mhd_trac_type")
3596 end select
3597 }
3598 end subroutine mhd_get_tcutoff
3599
3600 !> get H speed for H-correction to fix the carbuncle problem at grid-aligned shock front
3601 subroutine mhd_get_h_speed(wprim,x,ixI^L,ixO^L,idim,Hspeed)
3603
3604 integer, intent(in) :: ixi^l, ixo^l, idim
3605 double precision, intent(in) :: wprim(ixi^s, nw)
3606 double precision, intent(in) :: x(ixi^s,1:ndim)
3607 double precision, intent(out) :: hspeed(ixi^s,1:number_species)
3608
3609 double precision :: csound(ixi^s,ndim)
3610 double precision, allocatable :: tmp(:^d&)
3611 integer :: jxc^l, ixc^l, ixa^l, id, ix^d
3612
3613 hspeed=0.d0
3614 ixa^l=ixo^l^ladd1;
3615 allocate(tmp(ixa^s))
3616 do id=1,ndim
3617 if(has_equi_rho_and_p) then
3618 call mhd_get_csound_prim_split(wprim,x,ixi^l,ixa^l,id,tmp)
3619 else
3620 call mhd_get_csound_prim(wprim,x,ixi^l,ixa^l,id,tmp)
3621 endif
3622 csound(ixa^s,id)=tmp(ixa^s)
3623 end do
3624 ixcmax^d=ixomax^d;
3625 ixcmin^d=ixomin^d+kr(idim,^d)-1;
3626 jxcmax^d=ixcmax^d+kr(idim,^d);
3627 jxcmin^d=ixcmin^d+kr(idim,^d);
3628 hspeed(ixc^s,1)=0.5d0*abs(wprim(jxc^s,mom(idim))+csound(jxc^s,idim)-wprim(ixc^s,mom(idim))+csound(ixc^s,idim))
3629
3630 do id=1,ndim
3631 if(id==idim) cycle
3632 ixamax^d=ixcmax^d+kr(id,^d);
3633 ixamin^d=ixcmin^d+kr(id,^d);
3634 hspeed(ixc^s,1)=max(hspeed(ixc^s,1),0.5d0*abs(wprim(ixa^s,mom(id))+csound(ixa^s,id)-wprim(ixc^s,mom(id))+csound(ixc^s,id)))
3635 ixamax^d=ixcmax^d-kr(id,^d);
3636 ixamin^d=ixcmin^d-kr(id,^d);
3637 hspeed(ixc^s,1)=max(hspeed(ixc^s,1),0.5d0*abs(wprim(ixc^s,mom(id))+csound(ixc^s,id)-wprim(ixa^s,mom(id))+csound(ixa^s,id)))
3638 end do
3639
3640 do id=1,ndim
3641 if(id==idim) cycle
3642 ixamax^d=jxcmax^d+kr(id,^d);
3643 ixamin^d=jxcmin^d+kr(id,^d);
3644 hspeed(ixc^s,1)=max(hspeed(ixc^s,1),0.5d0*abs(wprim(ixa^s,mom(id))+csound(ixa^s,id)-wprim(jxc^s,mom(id))+csound(jxc^s,id)))
3645 ixamax^d=jxcmax^d-kr(id,^d);
3646 ixamin^d=jxcmin^d-kr(id,^d);
3647 hspeed(ixc^s,1)=max(hspeed(ixc^s,1),0.5d0*abs(wprim(jxc^s,mom(id))+csound(jxc^s,id)-wprim(ixa^s,mom(id))+csound(ixa^s,id)))
3648 end do
3649 deallocate(tmp)
3650
3651 end subroutine mhd_get_h_speed
3652
3653 !> Estimating bounds for the minimum and maximum signal velocities without split
3654 subroutine mhd_get_cbounds(wLC,wRC,wLp,wRp,x,ixI^L,ixO^L,idim,Hspeed,cmax,cmin)
3656 !!use mod_fld, only: fld_bound_diff, fld_get_local_invtauc
3657
3658 integer, intent(in) :: ixi^l, ixo^l, idim
3659 double precision, intent(in) :: wlc(ixi^s, nw), wrc(ixi^s, nw)
3660 double precision, intent(in) :: wlp(ixi^s, nw), wrp(ixi^s, nw)
3661 double precision, intent(in) :: x(ixi^s,1:ndim)
3662 double precision, intent(inout) :: cmax(ixi^s,1:number_species)
3663 double precision, intent(inout), optional :: cmin(ixi^s,1:number_species)
3664 double precision, intent(in) :: hspeed(ixi^s,1:number_species)
3665
3666 double precision :: wmean(ixi^s,nw), csoundl(ixo^s), csoundr(ixo^s)
3667 !!double precision :: invtaucL(ixI^S),invtaucR(ixI^S),invtauc
3668 double precision :: umean, dmean, tmp1, tmp2, tmp3
3669 integer :: ix^d
3670
3671 select case (boundspeed)
3672 case (1)
3673 ! This implements formula (10.52) from "Riemann Solvers and Numerical
3674 ! Methods for Fluid Dynamics" by Toro.
3675 call mhd_get_csound_prim(wlp,x,ixi^l,ixo^l,idim,csoundl)
3676 call mhd_get_csound_prim(wrp,x,ixi^l,ixo^l,idim,csoundr)
3677 !!if(mhd_radiation_fld.and.fld_bound_diff)then
3678 !! call fld_get_local_invtauc(wLp,ixI^L,ixO^L,{dxlevel(^D)},x,invtaucL,fld_fl)
3679 !! call fld_get_local_invtauc(wRp,ixI^L,ixO^L,{dxlevel(^D)},x,invtaucR,fld_fl)
3680 !!endif
3681 if(present(cmin)) then
3682 {do ix^db=ixomin^db,ixomax^db\}
3683 tmp1=sqrt(wlp(ix^d,rho_))
3684 tmp2=sqrt(wrp(ix^d,rho_))
3685 tmp3=1.d0/(tmp1+tmp2)
3686 umean=(wlp(ix^d,mom(idim))*tmp1+wrp(ix^d,mom(idim))*tmp2)*tmp3
3687 dmean=sqrt((tmp1*csoundl(ix^d)**2+tmp2*csoundr(ix^d)**2)*tmp3+&
3688 half*tmp1*tmp2*tmp3**2*(wrp(ix^d,mom(idim))-wlp(ix^d,mom(idim)))**2)
3689 cmin(ix^d,1)=umean-dmean
3690 cmax(ix^d,1)=umean+dmean
3691 !!if(mhd_radiation_fld.and.fld_bound_diff)then
3692 !! invtauc=min(invtaucL(ix^D),invtaucR(ix^D))
3693 !! cmax(ix^D,1)=min(cmax(ix^D,1),+invtauc)
3694 !! cmin(ix^D,1)=max(cmin(ix^D,1),-invtauc)
3695 !!endif
3696 {end do\}
3697 if(h_correction) then
3698 {do ix^db=ixomin^db,ixomax^db\}
3699 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
3700 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
3701 {end do\}
3702 end if
3703 else
3704 {do ix^db=ixomin^db,ixomax^db\}
3705 tmp1=sqrt(wlp(ix^d,rho_))
3706 tmp2=sqrt(wrp(ix^d,rho_))
3707 tmp3=1.d0/(tmp1+tmp2)
3708 umean=(wlp(ix^d,mom(idim))*tmp1+wrp(ix^d,mom(idim))*tmp2)*tmp3
3709 dmean=sqrt((tmp1*csoundl(ix^d)**2+tmp2*csoundr(ix^d)**2)*tmp3+&
3710 half*tmp1*tmp2*tmp3**2*(wrp(ix^d,mom(idim))-wlp(ix^d,mom(idim)))**2)
3711 cmax(ix^d,1)=abs(umean)+dmean
3712 !!if(mhd_radiation_fld.and.fld_bound_diff)then
3713 !! invtauc=min(invtaucL(ix^D),invtaucR(ix^D))
3714 !! cmax(ix^D,1)=min(cmax(ix^D,1),+invtauc)
3715 !!endif
3716 {end do\}
3717 end if
3718 case (2)
3719 wmean(ixo^s,1:nwflux)=0.5d0*(wlp(ixo^s,1:nwflux)+wrp(ixo^s,1:nwflux))
3720 call mhd_get_csound_prim(wmean,x,ixi^l,ixo^l,idim,csoundr)
3721 !!if(mhd_radiation_fld.and.fld_bound_diff)then
3722 !! call fld_get_local_invtauc(wmean,ixI^L,ixO^L,{dxlevel(^D)},x,invtaucL,fld_fl)
3723 !!endif
3724 if(present(cmin)) then
3725 {do ix^db=ixomin^db,ixomax^db\}
3726 cmax(ix^d,1)=max(wmean(ix^d,mom(idim))+csoundr(ix^d),zero)
3727 cmin(ix^d,1)=min(wmean(ix^d,mom(idim))-csoundr(ix^d),zero)
3728 !!if(mhd_radiation_fld.and.fld_bound_diff)then
3729 !! invtauc=invtaucL(ix^D)
3730 !! cmax(ix^D,1)=min(cmax(ix^D,1),+invtauc)
3731 !! cmin(ix^D,1)=max(cmin(ix^D,1),-invtauc)
3732 !!endif
3733 {end do\}
3734 if(h_correction) then
3735 {do ix^db=ixomin^db,ixomax^db\}
3736 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
3737 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
3738 {end do\}
3739 end if
3740 else
3741 cmax(ixo^s,1)=abs(wmean(ixo^s,mom(idim)))+csoundr(ixo^s)
3742 !!if(mhd_radiation_fld.and.fld_bound_diff)then
3743 !! invtauc=invtaucL(ix^D)
3744 !! cmax(ix^D,1)=min(cmax(ix^D,1),+invtauc)
3745 !!endif
3746 end if
3747 case (3)
3748 ! Miyoshi 2005 JCP 208, 315 equation (67)
3749 call mhd_get_csound_prim(wlp,x,ixi^l,ixo^l,idim,csoundl)
3750 call mhd_get_csound_prim(wrp,x,ixi^l,ixo^l,idim,csoundr)
3751 !!if(mhd_radiation_fld.and.fld_bound_diff)then
3752 !! call fld_get_local_invtauc(wLp,ixI^L,ixO^L,{dxlevel(^D)},x,invtaucL,fld_fl)
3753 !! call fld_get_local_invtauc(wRp,ixI^L,ixO^L,{dxlevel(^D)},x,invtaucR,fld_fl)
3754 !!endif
3755 if(present(cmin)) then
3756 {do ix^db=ixomin^db,ixomax^db\}
3757 csoundl(ix^d)=max(csoundl(ix^d),csoundr(ix^d))
3758 cmin(ix^d,1)=min(wlp(ix^d,mom(idim)),wrp(ix^d,mom(idim)))-csoundl(ix^d)
3759 cmax(ix^d,1)=max(wlp(ix^d,mom(idim)),wrp(ix^d,mom(idim)))+csoundl(ix^d)
3760 !!if(mhd_radiation_fld.and.fld_bound_diff)then
3761 !! invtauc=min(invtaucL(ix^D),invtaucR(ix^D))
3762 !! cmax(ix^D,1)=min(cmax(ix^D,1),+invtauc)
3763 !! cmin(ix^D,1)=max(cmin(ix^D,1),-invtauc)
3764 !!endif
3765 {end do\}
3766 if(h_correction) then
3767 {do ix^db=ixomin^db,ixomax^db\}
3768 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
3769 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
3770 {end do\}
3771 end if
3772 else
3773 {do ix^db=ixomin^db,ixomax^db\}
3774 csoundl(ix^d)=max(csoundl(ix^d),csoundr(ix^d))
3775 cmax(ix^d,1)=max(wlp(ix^d,mom(idim)),wrp(ix^d,mom(idim)))+csoundl(ix^d)
3776 !!if(mhd_radiation_fld.and.fld_bound_diff)then
3777 !! invtauc=min(invtaucL(ix^D),invtaucR(ix^D))
3778 !! cmax(ix^D,1)=min(cmax(ix^D,1),+invtauc)
3779 !!endif
3780 {end do\}
3781 end if
3782 end select
3783
3784 end subroutine mhd_get_cbounds
3785
3786 !> Estimating bounds for the minimum and maximum signal velocities without split
3787 subroutine mhd_get_cbounds_semirelati(wLC,wRC,wLp,wRp,x,ixI^L,ixO^L,idim,Hspeed,cmax,cmin)
3789
3790 integer, intent(in) :: ixi^l, ixo^l, idim
3791 double precision, intent(in) :: wlc(ixi^s, nw), wrc(ixi^s, nw)
3792 double precision, intent(in) :: wlp(ixi^s, nw), wrp(ixi^s, nw)
3793 double precision, intent(in) :: x(ixi^s,1:ndim)
3794 double precision, intent(inout) :: cmax(ixi^s,1:number_species)
3795 double precision, intent(inout), optional :: cmin(ixi^s,1:number_species)
3796 double precision, intent(in) :: hspeed(ixi^s,1:number_species)
3797
3798 double precision, dimension(ixO^S) :: csoundl, csoundr, gamma2l, gamma2r
3799 integer :: ix^d
3800
3801 ! Miyoshi 2005 JCP 208, 315 equation (67)
3802 if(mhd_energy) then
3803 call mhd_get_csound_semirelati(wlp,x,ixi^l,ixo^l,idim,csoundl,gamma2l)
3804 call mhd_get_csound_semirelati(wrp,x,ixi^l,ixo^l,idim,csoundr,gamma2r)
3805 else
3806 call mhd_get_csound_semirelati_noe(wlp,x,ixi^l,ixo^l,idim,csoundl,gamma2l)
3807 call mhd_get_csound_semirelati_noe(wrp,x,ixi^l,ixo^l,idim,csoundr,gamma2r)
3808 end if
3809 if(present(cmin)) then
3810 {do ix^db=ixomin^db,ixomax^db\}
3811 csoundl(ix^d)=max(csoundl(ix^d),csoundr(ix^d))
3812 cmin(ix^d,1)=min(gamma2l(ix^d)*wlp(ix^d,mom(idim)),gamma2r(ix^d)*wrp(ix^d,mom(idim)))-csoundl(ix^d)
3813 cmax(ix^d,1)=max(gamma2l(ix^d)*wlp(ix^d,mom(idim)),gamma2r(ix^d)*wrp(ix^d,mom(idim)))+csoundl(ix^d)
3814 {end do\}
3815 else
3816 {do ix^db=ixomin^db,ixomax^db\}
3817 csoundl(ix^d)=max(csoundl(ix^d),csoundr(ix^d))
3818 cmax(ix^d,1)=max(gamma2l(ix^d)*wlp(ix^d,mom(idim)),gamma2r(ix^d)*wrp(ix^d,mom(idim)))+csoundl(ix^d)
3819 {end do\}
3820 end if
3821
3822 end subroutine mhd_get_cbounds_semirelati
3823
3824 !> Estimating bounds for the minimum and maximum signal velocities with rho split
3825 subroutine mhd_get_cbounds_split_rho(wLC,wRC,wLp,wRp,x,ixI^L,ixO^L,idim,Hspeed,cmax,cmin)
3827
3828 integer, intent(in) :: ixi^l, ixo^l, idim
3829 double precision, intent(in) :: wlc(ixi^s, nw), wrc(ixi^s, nw)
3830 double precision, intent(in) :: wlp(ixi^s, nw), wrp(ixi^s, nw)
3831 double precision, intent(in) :: x(ixi^s,1:ndim)
3832 double precision, intent(inout) :: cmax(ixi^s,1:number_species)
3833 double precision, intent(inout), optional :: cmin(ixi^s,1:number_species)
3834 double precision, intent(in) :: hspeed(ixi^s,1:number_species)
3835
3836 double precision :: wmean(ixi^s,nw), csoundl(ixo^s), csoundr(ixo^s)
3837 double precision :: umean, dmean, tmp1, tmp2, tmp3
3838 integer :: ix^d
3839
3840 select case (boundspeed)
3841 case (1)
3842 ! This implements formula (10.52) from "Riemann Solvers and Numerical
3843 ! Methods for Fluid Dynamics" by Toro.
3844 call mhd_get_csound_prim_split(wlp,x,ixi^l,ixo^l,idim,csoundl)
3845 call mhd_get_csound_prim_split(wrp,x,ixi^l,ixo^l,idim,csoundr)
3846 if(present(cmin)) then
3847 {do ix^db=ixomin^db,ixomax^db\}
3848 tmp1=sqrt(wlp(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
3849 tmp2=sqrt(wrp(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
3850 tmp3=1.d0/(tmp1+tmp2)
3851 umean=(wlp(ix^d,mom(idim))*tmp1+wrp(ix^d,mom(idim))*tmp2)*tmp3
3852 dmean=sqrt((tmp1*csoundl(ix^d)**2+tmp2*csoundr(ix^d)**2)*tmp3+&
3853 half*tmp1*tmp2*tmp3**2*(wrp(ix^d,mom(idim))-wlp(ix^d,mom(idim)))**2)
3854 cmin(ix^d,1)=umean-dmean
3855 cmax(ix^d,1)=umean+dmean
3856 {end do\}
3857 if(h_correction) then
3858 {do ix^db=ixomin^db,ixomax^db\}
3859 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
3860 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
3861 {end do\}
3862 end if
3863 else
3864 {do ix^db=ixomin^db,ixomax^db\}
3865 tmp1=sqrt(wlp(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
3866 tmp2=sqrt(wrp(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
3867 tmp3=1.d0/(tmp1+tmp2)
3868 umean=(wlp(ix^d,mom(idim))*tmp1+wrp(ix^d,mom(idim))*tmp2)*tmp3
3869 dmean=sqrt((tmp1*csoundl(ix^d)**2+tmp2*csoundr(ix^d)**2)*tmp3+&
3870 half*tmp1*tmp2*tmp3**2*(wrp(ix^d,mom(idim))-wlp(ix^d,mom(idim)))**2)
3871 cmax(ix^d,1)=abs(umean)+dmean
3872 {end do\}
3873 end if
3874 case (2)
3875 wmean(ixo^s,1:nwflux)=0.5d0*(wlp(ixo^s,1:nwflux)+wrp(ixo^s,1:nwflux))
3876 call mhd_get_csound_prim_split(wmean,x,ixi^l,ixo^l,idim,csoundr)
3877 if(present(cmin)) then
3878 {do ix^db=ixomin^db,ixomax^db\}
3879 cmax(ix^d,1)=max(wmean(ix^d,mom(idim))+csoundr(ix^d),zero)
3880 cmin(ix^d,1)=min(wmean(ix^d,mom(idim))-csoundr(ix^d),zero)
3881 {end do\}
3882 if(h_correction) then
3883 {do ix^db=ixomin^db,ixomax^db\}
3884 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
3885 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
3886 {end do\}
3887 end if
3888 else
3889 cmax(ixo^s,1)=abs(wmean(ixo^s,mom(idim)))+csoundr(ixo^s)
3890 end if
3891 case (3)
3892 ! Miyoshi 2005 JCP 208, 315 equation (67)
3893 call mhd_get_csound_prim_split(wlp,x,ixi^l,ixo^l,idim,csoundl)
3894 call mhd_get_csound_prim_split(wrp,x,ixi^l,ixo^l,idim,csoundr)
3895 if(present(cmin)) then
3896 {do ix^db=ixomin^db,ixomax^db\}
3897 csoundl(ix^d)=max(csoundl(ix^d),csoundr(ix^d))
3898 cmin(ix^d,1)=min(wlp(ix^d,mom(idim)),wrp(ix^d,mom(idim)))-csoundl(ix^d)
3899 cmax(ix^d,1)=max(wlp(ix^d,mom(idim)),wrp(ix^d,mom(idim)))+csoundl(ix^d)
3900 {end do\}
3901 if(h_correction) then
3902 {do ix^db=ixomin^db,ixomax^db\}
3903 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
3904 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
3905 {end do\}
3906 end if
3907 else
3908 {do ix^db=ixomin^db,ixomax^db\}
3909 csoundl(ix^d)=max(csoundl(ix^d),csoundr(ix^d))
3910 cmax(ix^d,1)=max(wlp(ix^d,mom(idim)),wrp(ix^d,mom(idim)))+csoundl(ix^d)
3911 {end do\}
3912 end if
3913 end select
3914
3915 end subroutine mhd_get_cbounds_split_rho
3916
3917 !> prepare velocities for ct methods
3918 subroutine mhd_get_ct_velocity_average(vcts,wLp,wRp,ixI^L,ixO^L,idim,cmax,cmin)
3920
3921 integer, intent(in) :: ixi^l, ixo^l, idim
3922 double precision, intent(in) :: wlp(ixi^s, nw), wrp(ixi^s, nw)
3923 double precision, intent(in) :: cmax(ixi^s)
3924 double precision, intent(in), optional :: cmin(ixi^s)
3925 type(ct_velocity), intent(inout):: vcts
3926
3927 end subroutine mhd_get_ct_velocity_average
3928
3929 subroutine mhd_get_ct_velocity_contact(vcts,wLp,wRp,ixI^L,ixO^L,idim,cmax,cmin)
3931
3932 integer, intent(in) :: ixi^l, ixo^l, idim
3933 double precision, intent(in) :: wlp(ixi^s, nw), wrp(ixi^s, nw)
3934 double precision, intent(in) :: cmax(ixi^s)
3935 double precision, intent(in), optional :: cmin(ixi^s)
3936 type(ct_velocity), intent(inout):: vcts
3937
3938 if(.not.allocated(vcts%vnorm)) allocate(vcts%vnorm(ixi^s,1:ndim))
3939 ! get average normal velocity at cell faces
3940 vcts%vnorm(ixo^s,idim)=0.5d0*(wlp(ixo^s,mom(idim))+wrp(ixo^s,mom(idim)))
3941
3942 end subroutine mhd_get_ct_velocity_contact
3943
3944 subroutine mhd_get_ct_velocity_hll(vcts,wLp,wRp,ixI^L,ixO^L,idim,cmax,cmin)
3946
3947 integer, intent(in) :: ixi^l, ixo^l, idim
3948 double precision, intent(in) :: wlp(ixi^s, nw), wrp(ixi^s, nw)
3949 double precision, intent(in) :: cmax(ixi^s)
3950 double precision, intent(in), optional :: cmin(ixi^s)
3951 type(ct_velocity), intent(inout):: vcts
3952
3953 integer :: idime,idimn
3954
3955 if(.not.allocated(vcts%vbarC)) then
3956 allocate(vcts%vbarC(ixi^s,1:ndir,2),vcts%vbarLC(ixi^s,1:ndir,2),vcts%vbarRC(ixi^s,1:ndir,2))
3957 allocate(vcts%cbarmin(ixi^s,1:ndim),vcts%cbarmax(ixi^s,1:ndim))
3958 end if
3959 ! Store magnitude of characteristics
3960 if(present(cmin)) then
3961 vcts%cbarmin(ixo^s,idim)=max(-cmin(ixo^s),zero)
3962 vcts%cbarmax(ixo^s,idim)=max( cmax(ixo^s),zero)
3963 else
3964 vcts%cbarmax(ixo^s,idim)=max( cmax(ixo^s),zero)
3965 vcts%cbarmin(ixo^s,idim)=vcts%cbarmax(ixo^s,idim)
3966 end if
3967
3968 idimn=mod(idim,ndir)+1 ! 'Next' direction
3969 idime=mod(idim+1,ndir)+1 ! Electric field direction
3970 ! Store velocities
3971 vcts%vbarLC(ixo^s,idim,1)=wlp(ixo^s,mom(idimn))
3972 vcts%vbarRC(ixo^s,idim,1)=wrp(ixo^s,mom(idimn))
3973 vcts%vbarC(ixo^s,idim,1)=(vcts%cbarmax(ixo^s,idim)*vcts%vbarLC(ixo^s,idim,1) &
3974 +vcts%cbarmin(ixo^s,idim)*vcts%vbarRC(ixo^s,idim,1))&
3975 /(vcts%cbarmax(ixo^s,idim)+vcts%cbarmin(ixo^s,idim))
3976
3977 vcts%vbarLC(ixo^s,idim,2)=wlp(ixo^s,mom(idime))
3978 vcts%vbarRC(ixo^s,idim,2)=wrp(ixo^s,mom(idime))
3979 vcts%vbarC(ixo^s,idim,2)=(vcts%cbarmax(ixo^s,idim)*vcts%vbarLC(ixo^s,idim,2) &
3980 +vcts%cbarmin(ixo^s,idim)*vcts%vbarRC(ixo^s,idim,2))&
3981 /(vcts%cbarmax(ixo^s,idim)+vcts%cbarmin(ixo^s,idim))
3982
3983 end subroutine mhd_get_ct_velocity_hll
3984
3985 !> Calculate modified squared sound speed for FLD
3986 !> NOTE: only for diagnostic purposes, unused subroutine
3987 subroutine mhd_get_csrad2(w,x,ixI^L,ixO^L,csound)
3989
3990 integer, intent(in) :: ixi^l, ixo^l
3991 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
3992 double precision, intent(out):: csound(ixi^s)
3993
3994 double precision :: wprim(ixi^s, nw)
3995
3996 wprim(ixi^s,1:nw)=w(ixi^s,1:nw)
3997 call eos%to_primitive(ixi^l,ixo^l,wprim,x)
3998 call mhd_get_csrad2_prim(wprim,x,ixi^l,ixo^l,csound)
3999
4000 end subroutine mhd_get_csrad2
4001
4002
4003 !> Calculate modified squared fast wave speed for FLD
4004 !> NOTE: w is primitive on entry here!
4005 !> NOTE: used in FLD module as phys_get_csrad2
4006 subroutine mhd_get_csrad2_prim(w,x,ixI^L,ixO^L,csound)
4008
4009 integer, intent(in) :: ixi^l, ixo^l
4010 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
4011 double precision, intent(out):: csound(ixi^s)
4012
4013 double precision :: inv_rho, b2
4014 double precision :: prad_tensor(ixi^s, 1:ndim, 1:ndim)
4015 double precision :: prad_max(ixi^s)
4016 integer :: ix^d,idim
4017
4018 if(mhd_fld_pradtensor) then
4019 call mhd_get_pradiation_from_prim(w, x, ixi^l, ixo^l, prad_tensor)
4020 else
4021 prad_tensor=zero
4022 do idim=1,ndim
4023 prad_tensor(ixo^s,idim,idim)=w(ixo^s,r_e)/3.0d0
4024 enddo
4025 endif
4026
4027 if(b0field) then
4028 {do ix^db=ixomin^db,ixomax^db \}
4029 inv_rho=1.d0/w(ix^d,rho_)
4030 prad_max(ix^d) = (4.0d0/3.0d0)*maxval(prad_tensor(ix^d,:,:))
4031 b2=(^c&(w(ix^d,b^c_)+block%B0(ix^d,^c,b0i))**2+)
4032 csound(ix^d)=(eos%gamma*w(ix^d,p_)+b2+prad_max(ix^d))*inv_rho
4033 {end do\}
4034 else
4035 {do ix^db=ixomin^db,ixomax^db \}
4036 inv_rho=1.d0/w(ix^d,rho_)
4037 prad_max(ix^d) = (4.0d0/3.0d0)*maxval(prad_tensor(ix^d,:,:))
4038 b2=(^c&w(ix^d,b^c_)**2+)
4039 csound(ix^d)=(eos%gamma*w(ix^d,p_)+b2+prad_max(ix^d))*inv_rho
4040 {end do\}
4041 end if
4042
4043 if(minval(csound(ixo^s))<smalldouble)then
4044 print *,'issue with squared speed and rad pressure'
4045 print *,minval(csound(ixo^s))
4046 print *,minval(prad_max(ixo^s))
4047 call mpistop("negative squared speed in get_csrad2 for dt")
4048 endif
4049
4050 end subroutine mhd_get_csrad2_prim
4051
4052 !> Calculate fast magnetosonic wave speed
4053 subroutine mhd_get_csound_prim(w,x,ixI^L,ixO^L,idim,csound)
4056 use mod_fld, only: fld_bound_diff
4057
4058 integer, intent(in) :: ixi^l, ixo^l, idim
4059 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
4060 double precision, intent(out):: csound(ixo^s)
4061
4062 double precision :: adiabs(ixi^s), gammas(ixi^s)
4063 double precision :: inv_rho, cfast2, avmincs2, b2, kmax
4064 double precision :: cs2(ixi^s)
4065 integer :: ix^d
4066
4067 if(mhd_uawsom) then
4068 call mhd_uawsom_get_signal_speed(w,x,ixi^l,ixo^l,idim,csound)
4069 return
4070 end if
4071
4072 if(mhd_hall) kmax = dpi/min({dxlevel(^d)},bigdouble)*half
4073
4074 if(mhd_energy) then
4075 ! Sound speed squared via EoS dispatch (honours LTE+ionE Gamma_1 table).
4076 call eos%get_csound2(w, x, ixi^l, ixo^l, cs2)
4077 else
4078 if(associated(usr_set_adiab)) then
4079 call usr_set_adiab(w,x,ixi^l,ixo^l,adiabs)
4080 else
4081 adiabs=mhd_adiab
4082 end if
4083 if(associated(usr_set_gamma)) then
4084 call usr_set_gamma(w,x,ixi^l,ixo^l,gammas)
4085 else
4086 gammas=eos%gamma
4087 end if
4088 end if
4089
4090 ! store |B|^2 in v
4091 if(b0field) then
4092 {do ix^db=ixomin^db,ixomax^db \}
4093 inv_rho=1.d0/w(ix^d,rho_)
4094 if(mhd_energy) then
4095 if(mhd_radiation_fld.and.fld_bound_diff)cs2(ix^d)=cs2(ix^d)+4.0d0*w(ix^d,r_e)*inv_rho/9.0d0
4096 else
4097 cs2(ix^d)=gammas(ix^d)*adiabs(ix^d)*w(ix^d,rho_)**(gammas(ix^d)-1.d0)
4098 end if
4099 b2=(^c&(w(ix^d,b^c_)+block%B0(ix^d,^c,b0i))**2+)
4100 cfast2=b2*inv_rho+cs2(ix^d)
4101 avmincs2=cfast2**2-4.0d0*cs2(ix^d)*(w(ix^d,mag(idim))+&
4102 block%B0(ix^d,idim,b0i))**2*inv_rho
4103 if(avmincs2<zero) avmincs2=zero
4104 csound(ix^d)=sqrt(half*(cfast2+sqrt(avmincs2)))
4105 if(mhd_hall) then
4106 csound(ix^d)=max(csound(ix^d),mhd_etah*sqrt(b2)*inv_rho*kmax)
4107 end if
4108 {end do\}
4109 else
4110 {do ix^db=ixomin^db,ixomax^db \}
4111 inv_rho=1.d0/w(ix^d,rho_)
4112 if(mhd_energy) then
4113 if(mhd_radiation_fld.and.fld_bound_diff)cs2(ix^d)=cs2(ix^d)+4.0d0*w(ix^d,r_e)*inv_rho/9.0d0
4114 else
4115 cs2(ix^d)=gammas(ix^d)*adiabs(ix^d)*w(ix^d,rho_)**(gammas(ix^d)-1.d0)
4116 end if
4117 b2=(^c&w(ix^d,b^c_)**2+)
4118 cfast2=b2*inv_rho+cs2(ix^d)
4119 avmincs2=cfast2**2-4.0d0*cs2(ix^d)*w(ix^d,mag(idim))**2*inv_rho
4120 if(avmincs2<zero) avmincs2=zero
4121 csound(ix^d)=sqrt(half*(cfast2+sqrt(avmincs2)))
4122 if(mhd_hall) then
4123 csound(ix^d)=max(csound(ix^d),mhd_etah*sqrt(b2)*inv_rho*kmax)
4124 end if
4125 {end do\}
4126 end if
4127
4128 end subroutine mhd_get_csound_prim
4129
4130 !> Calculate fast magnetosonic wave speed when rho and p are split
4131 !> hence has_equi_rho_and_p=T
4132 subroutine mhd_get_csound_prim_split(w,x,ixI^L,ixO^L,idim,csound)
4134
4135 integer, intent(in) :: ixi^l, ixo^l, idim
4136 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
4137 double precision, intent(out):: csound(ixo^s)
4138
4139 double precision :: rho, inv_rho, cfast2, avmincs2, b2, kmax
4140 integer :: ix^d
4141
4142 if(mhd_hall) kmax = dpi/min({dxlevel(^d)},bigdouble)*half
4143
4144 ! store |B|^2 in v
4145 if(b0field) then
4146 {do ix^db=ixomin^db,ixomax^db \}
4147 rho=(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
4148 inv_rho=1.d0/rho
4149 csound(ix^d)=eos%gamma*(w(ix^d,p_)+block%equi_vars(ix^d,equi_pe0_,b0i))*inv_rho
4150 b2=(^c&(w(ix^d,b^c_)+block%B0(ix^d,^c,b0i))**2+)
4151 cfast2=b2*inv_rho+csound(ix^d)
4152 avmincs2=cfast2**2-4.0d0*csound(ix^d)*(w(ix^d,mag(idim))+&
4153 block%B0(ix^d,idim,b0i))**2*inv_rho
4154 if(avmincs2<zero) avmincs2=zero
4155 csound(ix^d)=sqrt(half*(cfast2+sqrt(avmincs2)))
4156 if(mhd_hall) then
4157 csound(ix^d)=max(csound(ix^d),mhd_etah*sqrt(b2)*inv_rho*kmax)
4158 end if
4159 {end do\}
4160 else
4161 {do ix^db=ixomin^db,ixomax^db \}
4162 rho=(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
4163 inv_rho=1.d0/rho
4164 csound(ix^d)=eos%gamma*(w(ix^d,p_)+block%equi_vars(ix^d,equi_pe0_,b0i))*inv_rho
4165 b2=(^c&w(ix^d,b^c_)**2+)
4166 cfast2=b2*inv_rho+csound(ix^d)
4167 avmincs2=cfast2**2-4.0d0*csound(ix^d)*w(ix^d,mag(idim))**2*inv_rho
4168 if(avmincs2<zero) avmincs2=zero
4169 csound(ix^d)=sqrt(half*(cfast2+sqrt(avmincs2)))
4170 if(mhd_hall) then
4171 csound(ix^d)=max(csound(ix^d),mhd_etah*sqrt(b2)*inv_rho*kmax)
4172 end if
4173 {end do\}
4174 end if
4175
4176 end subroutine mhd_get_csound_prim_split
4177
4178 !> Calculate cmax_idim for semirelativistic MHD
4179 subroutine mhd_get_csound_semirelati(w,x,ixI^L,ixO^L,idim,csound,gamma2)
4181
4182 integer, intent(in) :: ixi^l, ixo^l, idim
4183 ! here w is primitive variables
4184 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
4185 double precision, intent(out):: csound(ixo^s), gamma2(ixo^s)
4186
4187 double precision :: avmincs2, inv_rho, alfven_speed2, idim_alfven_speed2
4188 double precision :: cs2(ixi^s)
4189 integer :: ix^d
4190
4191 ! Sound speed squared via EoS dispatch (LTE+ionE -> Gamma_1 table; FI -> const gamma).
4192 call eos%get_csound2(w, x, ixi^l, ixo^l, cs2)
4193
4194 {do ix^db=ixomin^db,ixomax^db\}
4195 inv_rho = 1.d0/w(ix^d,rho_)
4196 ! V_A and V_A,idim take the total field
4197 if(b0field) then
4198 alfven_speed2=(^c&(w(ix^d,b^c_)+block%B0(ix^d,^c,b0i))**2+)*inv_rho
4199 idim_alfven_speed2=(w(ix^d,mag(idim))+block%B0(ix^d,idim,b0i))**2*inv_rho
4200 else
4201 alfven_speed2=(^c&w(ix^d,b^c_)**2+)*inv_rho
4202 idim_alfven_speed2=w(ix^d,mag(idim))**2*inv_rho
4203 end if
4204 gamma2(ix^d) = 1.0d0/(1.d0+alfven_speed2*inv_squared_c)
4205 avmincs2=1.d0-gamma2(ix^d)*w(ix^d,mom(idim))**2*inv_squared_c
4206 ! Va_hat^2+a_hat^2 equation (57)
4207 ! equation (69)
4208 alfven_speed2=alfven_speed2*avmincs2+cs2(ix^d)*(1.d0+idim_alfven_speed2*inv_squared_c)
4209 avmincs2=(gamma2(ix^d)*alfven_speed2)**2-4.0d0*gamma2(ix^d)*cs2(ix^d)*idim_alfven_speed2*avmincs2
4210 if(avmincs2<zero) avmincs2=zero
4211 ! equation (68) fast magnetosonic speed
4212 csound(ix^d) = sqrt(half*(gamma2(ix^d)*alfven_speed2+sqrt(avmincs2)))
4213 {end do\}
4214
4215 end subroutine mhd_get_csound_semirelati
4216
4217 !> Calculate cmax_idim for semirelativistic MHD
4218 subroutine mhd_get_csound_semirelati_noe(w,x,ixI^L,ixO^L,idim,csound,gamma2)
4221
4222 integer, intent(in) :: ixi^l, ixo^l, idim
4223 ! here w is primitive variables
4224 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
4225 double precision, intent(out):: csound(ixo^s), gamma2(ixo^s)
4226
4227 double precision :: adiabs(ixi^s), gammas(ixi^s)
4228 double precision :: avmincs2, inv_rho, alfven_speed2, idim_alfven_speed2
4229 integer :: ix^d
4230
4231 if(associated(usr_set_adiab)) then
4232 call usr_set_adiab(w,x,ixi^l,ixo^l,adiabs)
4233 else
4234 adiabs=mhd_adiab
4235 end if
4236 if(associated(usr_set_gamma)) then
4237 call usr_set_gamma(w,x,ixi^l,ixo^l,gammas)
4238 else
4239 gammas=eos%gamma
4240 end if
4241 {do ix^db=ixomin^db,ixomax^db\}
4242 inv_rho = 1.d0/w(ix^d,rho_)
4243 ! squared sound speed
4244 csound(ix^d)=gammas(ix^d)*adiabs(ix^d)*w(ix^d,rho_)**(gammas(ix^d)-1.d0)
4245 alfven_speed2=(^c&w(ix^d,b^c_)**2+)*inv_rho
4246 gamma2(ix^d) = 1.0d0/(1.d0+alfven_speed2*inv_squared_c)
4247 avmincs2=1.d0-gamma2(ix^d)*w(ix^d,mom(idim))**2*inv_squared_c
4248 idim_alfven_speed2=w(ix^d,mag(idim))**2*inv_rho
4249 ! Va_hat^2+a_hat^2 equation (57)
4250 ! equation (69)
4251 alfven_speed2=alfven_speed2*avmincs2+csound(ix^d)*(1.d0+idim_alfven_speed2*inv_squared_c)
4252 avmincs2=(gamma2(ix^d)*alfven_speed2)**2-4.0d0*gamma2(ix^d)*csound(ix^d)*idim_alfven_speed2*avmincs2
4253 if(avmincs2<zero) avmincs2=zero
4254 ! equation (68) fast magnetosonic speed
4255 csound(ix^d) = sqrt(half*(gamma2(ix^d)*alfven_speed2+sqrt(avmincs2)))
4256 {end do\}
4257
4258 end subroutine mhd_get_csound_semirelati_noe
4259
4260 ! Thermal pressure and temperature subroutines (mhd_get_pthermal_noe,
4261 ! _inte, _origin, _semirelati, _hde, _LTE, mhd_get_temperature_from_eint,
4262 ! _from_etot, _from_etot_LTE, _with_equi, _equi, mhd_get_rho_equi,
4263 ! mhd_get_pe_equi) are defined in mod_mhd_eos.t. eos%get_temperature_from_{etot,eint}
4264 ! and tc_fl / rc_fl / te_fl_mhd hooks are bound by bind_eos_to_source.
4265
4266
4267 !> Calculate radiation pressure within ixO^L
4268 subroutine mhd_get_pradiation_from_prim(w, x, ixI^L, ixO^L, prad)
4270 use mod_fld
4271 integer, intent(in) :: ixi^l, ixo^l
4272 double precision, intent(in) :: w(ixi^s, 1:nw)
4273 double precision, intent(in) :: x(ixi^s, 1:ndim)
4274 double precision, intent(out):: prad(ixi^s, 1:ndim, 1:ndim)
4275
4276 call fld_get_radpress(w, x, ixi^l, ixo^l, prad, fld_fl)
4277
4278 end subroutine mhd_get_pradiation_from_prim
4279
4280 !> Calculates the sum of the gas pressure and the max Prad tensor element
4281 subroutine mhd_get_pthermal_plus_pradiation(w, x, ixI^L, ixO^L, pth_plus_prad)
4283 integer, intent(in) :: ixi^l, ixo^l
4284 double precision, intent(in) :: w(ixi^s, 1:nw)
4285 double precision, intent(in) :: x(ixi^s, 1:ndim)
4286 double precision, intent(out) :: pth_plus_prad(ixi^s)
4287
4288 double precision :: wprim(ixi^s, 1:nw)
4289 double precision :: prad_tensor(ixi^s, 1:ndim, 1:ndim)
4290 double precision :: prad_max(ixi^s)
4291 integer :: ix^d
4292
4293 wprim(ixi^s,1:nw)=w(ixi^s,1:nw)
4294 call eos%to_primitive(ixi^l,ixo^l,wprim,x)
4295 call mhd_get_pradiation_from_prim(wprim, x, ixi^l, ixo^l, prad_tensor)
4296 {do ix^d = ixomin^d,ixomax^d\}
4297 prad_max(ix^d) = maxval(prad_tensor(ix^d,:,:))
4298 {enddo\}
4299 pth_plus_prad(ixo^s) = wprim(ixo^s,p_) + prad_max(ixo^s)
4301
4302 !> Calculates radiation temperature
4303 subroutine mhd_get_trad(w, x, ixI^L, ixO^L, trad)
4305 use mod_constants
4306
4307 integer, intent(in) :: ixi^l, ixo^l
4308 double precision, intent(in) :: w(ixi^s, 1:nw)
4309 double precision, intent(in) :: x(ixi^s, 1:ndim)
4310 double precision, intent(out):: trad(ixi^s)
4311
4312 trad(ixi^s) = (w(ixi^s,r_e)/arad_norm)**(1.d0/4.d0)
4313
4314 end subroutine mhd_get_trad
4315
4316 !> Calculate fluxes within ixO^L without any splitting
4317 subroutine mhd_get_flux(wC,w,x,ixI^L,ixO^L,idim,f)
4319 use mod_geometry
4320
4321 integer, intent(in) :: ixi^l, ixo^l, idim
4322 ! conservative w
4323 double precision, intent(in) :: wc(ixi^s,nw)
4324 ! primitive w
4325 double precision, intent(in) :: w(ixi^s,nw)
4326 double precision, intent(in) :: x(ixi^s,1:ndim)
4327 double precision,intent(out) :: f(ixi^s,nwflux)
4328
4329 double precision :: vhall(ixi^s,1:ndir)
4330 double precision :: ptotal
4331 double precision :: nperp(ixi^s,1:ndir)
4332 double precision :: uawsom_zeta(ixi^s), uawsom_radius(ixi^s)
4333 double precision :: uawsom_lperp(ixi^s), uawsom_denom
4334 double precision :: uawsom_pwave, uawsom_bi
4335 double precision :: bunit_tc(ndir)
4336 integer :: iw, ix^d
4337
4338 if(mhd_uawsom) call mhd_uawsom_get_coefficients(w,x,ixi^l,ixo^l,.true.,&
4339 uawsom_zeta,uawsom_radius,uawsom_lperp)
4340
4341 if(mhd_internal_e) then
4342 {do ix^db=ixomin^db,ixomax^db\}
4343 ! Get flux of density
4344 f(ix^d,rho_)=w(ix^d,mom(idim))*w(ix^d,rho_)
4345 ! f_i[m_k]=v_i*m_k-b_k*b_i
4346 ^c&f(ix^d,m^c_)=wc(ix^d,mom(idim))*w(ix^d,m^c_)-w(ix^d,mag(idim))*w(ix^d,b^c_)\
4347 ! normal one includes total pressure
4348 f(ix^d,mom(idim))=f(ix^d,mom(idim))+w(ix^d,p_)+half*(^c&w(ix^d,b^c_)**2+)
4349 ! Get flux of internal energy
4350 f(ix^d,e_)=w(ix^d,mom(idim))*wc(ix^d,e_)
4351 ! f_i[b_k]=v_i*b_k-v_k*b_i
4352 ^c&f(ix^d,b^c_)=w(ix^d,mom(idim))*w(ix^d,b^c_)-w(ix^d,mag(idim))*w(ix^d,m^c_)\
4353 {end do\}
4354 else
4355 {do ix^db=ixomin^db,ixomax^db\}
4356 ! Get flux of density
4357 f(ix^d,rho_)=w(ix^d,mom(idim))*w(ix^d,rho_)
4358 ! f_i[m_k]=v_i*m_k-b_k*b_i
4359 ^c&f(ix^d,m^c_)=wc(ix^d,mom(idim))*w(ix^d,m^c_)-w(ix^d,mag(idim))*w(ix^d,b^c_)\
4360 ptotal=w(ix^d,p_)+half*(^c&w(ix^d,b^c_)**2+)
4361 if(mhd_uawsom) then
4362 uawsom_pwave=mhd_uawsom_wave_pressure_cell(w(ix^d,:),uawsom_zeta(ix^d))
4363 ptotal=ptotal+uawsom_pwave
4364 end if
4365 ! normal one includes total pressure
4366 f(ix^d,mom(idim))=f(ix^d,mom(idim))+ptotal
4367 ! Get flux of total energy
4368 ! f_i[e]=v_i*e+v_i*ptotal-b_i*(b_k*v_k)
4369 f(ix^d,e_)=w(ix^d,mom(idim))*(wc(ix^d,e_)+ptotal)&
4370 -w(ix^d,mag(idim))*(^c&w(ix^d,b^c_)*w(ix^d,m^c_)+)
4371 if(mhd_uawsom) then
4372 uawsom_denom=dsqrt(w(ix^d,rho_)*(uawsom_zeta(ix^d)+one)/&
4373 (two*(one+mhd_uawsom_filling_factor*uawsom_zeta(ix^d)-&
4375 uawsom_bi=w(ix^d,mag(idim))
4376 f(ix^d,e_)=f(ix^d,e_)+uawsom_bi*(&
4377 (w(ix^d,waminus_)-w(ix^d,waplus_))/dsqrt(w(ix^d,rho_))+&
4378 (w(ix^d,wkminus_)-w(ix^d,wkplus_))/uawsom_denom)
4379 end if
4380 ! f_i[b_k]=v_i*b_k-v_k*b_i
4381 ^c&f(ix^d,b^c_)=w(ix^d,mom(idim))*w(ix^d,b^c_)-w(ix^d,mag(idim))*w(ix^d,m^c_)\
4382 {end do\}
4383 end if
4384 if(mhd_hall) then
4385 call mhd_getv_hall(w,x,ixi^l,ixo^l,vhall)
4386 {do ix^db=ixomin^db,ixomax^db\}
4387 if(total_energy) then
4388 ! f_i[e]= f_i[e] + vHall_i*(b_k*b_k) - b_i*(vHall_k*b_k)
4389 f(ix^d,e_)=f(ix^d,e_)+vhall(ix^d,idim)*(^c&w(ix^d,b^c_)**2+)&
4390 -w(ix^d,mag(idim))*(^c&vhall(ix^d,^c)*w(ix^d,b^c_)+)
4391 end if
4392 ! f_i[b_k] = f_i[b_k] + vHall_i*b_k - vHall_k*b_i
4393 ^c&f(ix^d,b^c_)=f(ix^d,b^c_)+vhall(ix^d,idim)*w(ix^d,b^c_)-vhall(ix^d,^c)*w(ix^d,mag(idim))\
4394 {end do\}
4395 end if
4396
4397 if(mhd_glm) then
4398 {do ix^db=ixomin^db,ixomax^db\}
4399 f(ix^d,mag(idim))=w(ix^d,psi_)
4400 !f_i[psi]=Ch^2*b_{i} Eq. 24e and Eq. 38c Dedner et al 2002 JCP, 175, 645
4401 f(ix^d,psi_) = cmax_global**2*w(ix^d,mag(idim))
4402 {end do\}
4403 end if
4404
4405 if(mhd_radiation_fld) then
4406 {do ix^db=ixomin^db,ixomax^db\}
4407 f(ix^d,r_e)=w(ix^d,mom(idim))*wc(ix^d,r_e)
4408 {end do\}
4409 endif
4410
4411 if (mhd_fip) then
4412 f(ixo^s,fip_) = w(ixo^s,mom(idim)) * wc(ixo^s,fip_)
4413 end if
4414 if(mhd_uawsom) then
4415 {do ix^db=ixomin^db,ixomax^db\}
4416 uawsom_denom=dsqrt(w(ix^d,rho_)*(uawsom_zeta(ix^d)+one)/&
4417 (two*(one+mhd_uawsom_filling_factor*uawsom_zeta(ix^d)-&
4419 uawsom_bi=w(ix^d,mag(idim))
4420 f(ix^d,waplus_)=w(ix^d,waplus_)*(w(ix^d,mom(idim))-&
4421 uawsom_bi/dsqrt(w(ix^d,rho_)))
4422 f(ix^d,waminus_)=w(ix^d,waminus_)*(w(ix^d,mom(idim))+&
4423 uawsom_bi/dsqrt(w(ix^d,rho_)))
4424 f(ix^d,wkplus_)=w(ix^d,wkplus_)*(w(ix^d,mom(idim))-uawsom_bi/uawsom_denom)
4425 f(ix^d,wkminus_)=w(ix^d,wkminus_)*(w(ix^d,mom(idim))+uawsom_bi/uawsom_denom)
4426 {end do\}
4427 end if
4428 ! Get flux of tracer
4429 do iw=1,mhd_n_tracer
4430 {do ix^db=ixomin^db,ixomax^db\}
4431 f(ix^d,tracer(iw))=w(ix^d,mom(idim))*w(ix^d,tracer(iw))
4432 {end do\}
4433 end do
4434
4435 if(mhd_hyperbolic_tc) then
4436 {do ix^db=ixomin^db,ixomax^db\}
4437 bunit_tc=b_unit_vector(w(ix^d,mag(1:ndir)))
4438 f(ix^d,e_)=f(ix^d,e_)+w(ix^d,qpar_)*bunit_tc(idim)
4439 f(ix^d,qpar_)=zero
4440 {end do\}
4442 call mhd_get_hyperbolic_tc_nperp(w,x,ixi^l,ixo^l,w(ixi^s,mag(1:ndir)),nperp)
4443 {do ix^db=ixomin^db,ixomax^db\}
4444 f(ix^d,e_)=f(ix^d,e_)+w(ix^d,qperp_)*nperp(ix^d,idim)
4445 f(ix^d,qperp_)=zero
4446 {end do\}
4447 end if
4448 end if
4449 end subroutine mhd_get_flux
4450
4451 !> Calculate fluxes within ixO^L for case without energy equation, hence without splitting
4452 !> and assuming polytropic closure
4453 subroutine mhd_get_flux_noe(wC,w,x,ixI^L,ixO^L,idim,f)
4455 use mod_geometry
4457
4458 integer, intent(in) :: ixi^l, ixo^l, idim
4459 ! conservative w
4460 double precision, intent(in) :: wc(ixi^s,nw)
4461 ! primitive w
4462 double precision, intent(in) :: w(ixi^s,nw)
4463 double precision, intent(in) :: x(ixi^s,1:ndim)
4464 double precision,intent(out) :: f(ixi^s,nwflux)
4465
4466 double precision :: vhall(ixi^s,1:ndir)
4467 double precision :: adiabs(ixi^s), gammas(ixi^s)
4468 integer :: iw, ix^d
4469
4470 if(associated(usr_set_adiab)) then
4471 call usr_set_adiab(w,x,ixi^l,ixo^l,adiabs)
4472 else
4473 adiabs=mhd_adiab
4474 end if
4475 if(associated(usr_set_gamma)) then
4476 call usr_set_gamma(w,x,ixi^l,ixo^l,gammas)
4477 else
4478 gammas=eos%gamma
4479 end if
4480 {do ix^db=ixomin^db,ixomax^db\}
4481 ! Get flux of density
4482 f(ix^d,rho_)=w(ix^d,mom(idim))*w(ix^d,rho_)
4483 ! f_i[m_k]=v_i*m_k-b_k*b_i
4484 ^c&f(ix^d,m^c_)=wc(ix^d,mom(idim))*w(ix^d,m^c_)-w(ix^d,mag(idim))*w(ix^d,b^c_)\
4485 ! normal one includes total pressure
4486 f(ix^d,mom(idim))=f(ix^d,mom(idim))+adiabs(ix^d)*w(ix^d,rho_)**gammas(ix^d)+half*(^c&w(ix^d,b^c_)**2+)
4487 ! f_i[b_k]=v_i*b_k-v_k*b_i
4488 ^c&f(ix^d,b^c_)=w(ix^d,mom(idim))*w(ix^d,b^c_)-w(ix^d,mag(idim))*w(ix^d,m^c_)\
4489 {end do\}
4490 if(mhd_hall) then
4491 call mhd_getv_hall(w,x,ixi^l,ixo^l,vhall)
4492 {do ix^db=ixomin^db,ixomax^db\}
4493 ! f_i[b_k] = f_i[b_k] + vHall_i*b_k - vHall_k*b_i
4494 ^c&f(ix^d,b^c_)=f(ix^d,b^c_)+vhall(ix^d,idim)*w(ix^d,b^c_)-vhall(ix^d,^c)*w(ix^d,mag(idim))\
4495 {end do\}
4496 end if
4497 if(mhd_glm) then
4498 {do ix^db=ixomin^db,ixomax^db\}
4499 f(ix^d,mag(idim))=w(ix^d,psi_)
4500 !f_i[psi]=Ch^2*b_{i} Eq. 24e and Eq. 38c Dedner et al 2002 JCP, 175, 645
4501 f(ix^d,psi_) = cmax_global**2*w(ix^d,mag(idim))
4502 {end do\}
4503 end if
4504 if (mhd_fip) then
4505 f(ixo^s,fip_) = w(ixo^s,mom(idim)) * wc(ixo^s,fip_)
4506 end if
4507 ! Get flux of tracer
4508 do iw=1,mhd_n_tracer
4509 {do ix^db=ixomin^db,ixomax^db\}
4510 f(ix^d,tracer(iw))=w(ix^d,mom(idim))*w(ix^d,tracer(iw))
4511 {end do\}
4512 end do
4513 end subroutine mhd_get_flux_noe
4514
4515 !> Calculate fluxes with hydrodynamic energy equation
4516 subroutine mhd_get_flux_hde(wC,w,x,ixI^L,ixO^L,idim,f)
4518 use mod_geometry
4519
4520 integer, intent(in) :: ixi^l, ixo^l, idim
4521 ! conservative w
4522 double precision, intent(in) :: wc(ixi^s,nw)
4523 ! primitive w
4524 double precision, intent(in) :: w(ixi^s,nw)
4525 double precision, intent(in) :: x(ixi^s,1:ndim)
4526 double precision,intent(out) :: f(ixi^s,nwflux)
4527
4528 double precision :: vhall(ixi^s,1:ndir)
4529 double precision :: nperp(ixi^s,1:ndir)
4530 double precision :: bunit_tc(ndir)
4531 integer :: iw, ix^d
4532
4533 {do ix^db=ixomin^db,ixomax^db\}
4534 ! Get flux of density
4535 f(ix^d,rho_)=w(ix^d,mom(idim))*w(ix^d,rho_)
4536 ! f_i[m_k]=v_i*m_k-b_k*b_i
4537 ^c&f(ix^d,m^c_)=wc(ix^d,mom(idim))*w(ix^d,m^c_)-w(ix^d,mag(idim))*w(ix^d,b^c_)\
4538 ! normal one includes total pressure
4539 f(ix^d,mom(idim))=f(ix^d,mom(idim))+w(ix^d,p_)+half*(^c&w(ix^d,b^c_)**2+)
4540 ! Get flux of energy
4541 f(ix^d,e_)=w(ix^d,mom(idim))*(wc(ix^d,e_)+w(ix^d,p_))
4542 ! f_i[b_k]=v_i*b_k-v_k*b_i
4543 ^c&f(ix^d,b^c_)=w(ix^d,mom(idim))*w(ix^d,b^c_)-w(ix^d,mag(idim))*w(ix^d,m^c_)\
4544 {end do\}
4545 if(mhd_hall) then
4546 call mhd_getv_hall(w,x,ixi^l,ixo^l,vhall)
4547 {do ix^db=ixomin^db,ixomax^db\}
4548 ! f_i[b_k] = f_i[b_k] + vHall_i*b_k - vHall_k*b_i
4549 ^c&f(ix^d,b^c_)=f(ix^d,b^c_)+vhall(ix^d,idim)*w(ix^d,b^c_)-vhall(ix^d,^c)*w(ix^d,mag(idim))\
4550 {end do\}
4551 end if
4552 if(mhd_glm) then
4553 {do ix^db=ixomin^db,ixomax^db\}
4554 f(ix^d,mag(idim))=w(ix^d,psi_)
4555 !f_i[psi]=Ch^2*b_{i} Eq. 24e and Eq. 38c Dedner et al 2002 JCP, 175, 645
4556 f(ix^d,psi_) = cmax_global**2*w(ix^d,mag(idim))
4557 {end do\}
4558 end if
4559 if (mhd_fip) then
4560 f(ixo^s,fip_) = w(ixo^s,mom(idim)) * wc(ixo^s,fip_)
4561 end if
4562 ! Get flux of tracer
4563 do iw=1,mhd_n_tracer
4564 {do ix^db=ixomin^db,ixomax^db\}
4565 f(ix^d,tracer(iw))=w(ix^d,mom(idim))*w(ix^d,tracer(iw))
4566 {end do\}
4567 end do
4568
4569 if(mhd_hyperbolic_tc) then
4570 {do ix^db=ixomin^db,ixomax^db\}
4571 bunit_tc=b_unit_vector(w(ix^d,mag(1:ndir)))
4572 f(ix^d,e_)=f(ix^d,e_)+w(ix^d,qpar_)*bunit_tc(idim)
4573 f(ix^d,qpar_)=zero
4574 {end do\}
4576 call mhd_get_hyperbolic_tc_nperp(w,x,ixi^l,ixo^l,w(ixi^s,mag(1:ndir)),nperp)
4577 {do ix^db=ixomin^db,ixomax^db\}
4578 f(ix^d,e_)=f(ix^d,e_)+w(ix^d,qperp_)*nperp(ix^d,idim)
4579 f(ix^d,qperp_)=zero
4580 {end do\}
4581 end if
4582 end if
4583
4584 end subroutine mhd_get_flux_hde
4585
4586 !> Calculate fluxes within ixO^L with possible splitting
4587 !> this covers four cases: B0field=T and mhd_internal_e=T (where has_equi_rho_and_p=F)
4588 !> B0field=T and has_equi_rho_and_p=F for total_energy=T
4589 !> B0field=F and has_equi_rho_and_p=T for total_energy=T
4590 !> B0field=T and has_equi_rho_and_p=T for total_energy=T
4591 subroutine mhd_get_flux_split(wC,w,x,ixI^L,ixO^L,idim,f)
4593 use mod_geometry
4594
4595 integer, intent(in) :: ixi^l, ixo^l, idim
4596 ! conservative w
4597 double precision, intent(in) :: wc(ixi^s,nw)
4598 ! primitive w
4599 double precision, intent(in) :: w(ixi^s,nw)
4600 double precision, intent(in) :: x(ixi^s,1:ndim)
4601 double precision,intent(out) :: f(ixi^s,nwflux)
4602
4603 double precision :: vhall(ixi^s,1:ndir)
4604 double precision :: ptotal, btotal(ixo^s,1:ndir)
4605 double precision :: nperp(ixi^s,1:ndir)
4606 double precision :: uawsom_zeta(ixi^s), uawsom_radius(ixi^s)
4607 double precision :: uawsom_lperp(ixi^s), uawsom_denom
4608 double precision :: uawsom_pwave, uawsom_bi
4609 double precision :: bunit_tc(ndir)
4610 logical :: use_perp_flux
4611 integer :: iw, ix^d
4612
4613 if(mhd_uawsom) call mhd_uawsom_get_coefficients(w,x,ixi^l,ixo^l,.true.,&
4614 uawsom_zeta,uawsom_radius,uawsom_lperp)
4615
4616 {do ix^db=ixomin^db,ixomax^db\}
4617 ! Get flux of density
4618 if(has_equi_rho_and_p) then
4619 f(ix^d,rho_)=w(ix^d,mom(idim))*(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
4620 else
4621 f(ix^d,rho_)=w(ix^d,mom(idim))*w(ix^d,rho_)
4622 end if
4623
4624 ptotal=w(ix^d,p_)+half*(^c&w(ix^d,b^c_)**2+)
4625
4626 if(b0field) then
4627 ^c&btotal(ix^d,^c)=w(ix^d,b^c_)+block%B0(ix^d,^c,idim)\
4628 ptotal=ptotal+(^c&w(ix^d,b^c_)*block%B0(ix^d,^c,idim)+)
4629 ! Get flux of momentum and magnetic field
4630 ! f_i[m_k]=v_i*m_k-b_k*b_i
4631 ^c&f(ix^d,m^c_)=wc(ix^d,mom(idim))*w(ix^d,m^c_)-&
4632 btotal(ix^d,idim)*w(ix^d,b^c_)-w(ix^d,mag(idim))*block%B0(ix^d,^c,idim)\
4633 f(ix^d,mom(idim))=f(ix^d,mom(idim))+ptotal
4634 else
4635 ^c&btotal(ix^d,^c)=w(ix^d,b^c_)\
4636 ! Get flux of momentum and magnetic field
4637 ! f_i[m_k]=v_i*m_k-b_k*b_i
4638 ^c&f(ix^d,m^c_)=wc(ix^d,mom(idim))*w(ix^d,m^c_)-w(ix^d,mag(idim))*w(ix^d,b^c_)\
4639 f(ix^d,mom(idim))=f(ix^d,mom(idim))+ptotal
4640 end if
4641 if(mhd_uawsom) then
4642 uawsom_pwave=mhd_uawsom_wave_pressure_cell(w(ix^d,:),uawsom_zeta(ix^d))
4643 f(ix^d,mom(idim))=f(ix^d,mom(idim))+uawsom_pwave
4644 end if
4645 ! f_i[b_k]=v_i*b_k-v_k*b_i
4646 ^c&f(ix^d,b^c_)=w(ix^d,mom(idim))*btotal(ix^d,^c)-btotal(ix^d,idim)*w(ix^d,m^c_)\
4647
4648 ! Get flux of energy
4649 ! f_i[e]=v_i*e+v_i*ptotal-b_i*(b_k*v_k)
4650 if(mhd_internal_e) then
4651 f(ix^d,e_)=w(ix^d,mom(idim))*wc(ix^d,e_)
4652 else
4653 f(ix^d,e_)=w(ix^d,mom(idim))*(wc(ix^d,e_)+ptotal)&
4654 -btotal(ix^d,idim)*(^c&w(ix^d,b^c_)*w(ix^d,m^c_)+)
4655 if(mhd_uawsom) then
4656 uawsom_denom=dsqrt(w(ix^d,rho_)*(uawsom_zeta(ix^d)+one)/&
4657 (two*(one+mhd_uawsom_filling_factor*uawsom_zeta(ix^d)-&
4659 uawsom_bi=btotal(ix^d,idim)
4660 f(ix^d,e_)=f(ix^d,e_)+w(ix^d,mom(idim))*uawsom_pwave+uawsom_bi*(&
4661 (w(ix^d,waminus_)-w(ix^d,waplus_))/dsqrt(w(ix^d,rho_))+&
4662 (w(ix^d,wkminus_)-w(ix^d,wkplus_))/uawsom_denom)
4663 end if
4664 end if
4665 {end do\}
4666
4667 if(mhd_glm) then
4668 {do ix^db=ixomin^db,ixomax^db\}
4669 f(ix^d,mag(idim))=w(ix^d,psi_)
4670 !f_i[psi]=Ch^2*b_{i} Eq. 24e and Eq. 38c Dedner et al 2002 JCP, 175, 645
4671 f(ix^d,psi_) = cmax_global**2*w(ix^d,mag(idim))
4672 {end do\}
4673 end if
4674
4675 if(mhd_radiation_fld) then
4676 {do ix^db=ixomin^db,ixomax^db\}
4677 f(ix^d,r_e)=w(ix^d,mom(idim))*wc(ix^d,r_e)
4678 {end do\}
4679 endif
4680
4681 if(mhd_hall) then
4682 call mhd_getv_hall(w,x,ixi^l,ixo^l,vhall)
4683 {do ix^db=ixomin^db,ixomax^db\}
4684 ! f_i[b_k] = f_i[b_k] + vHall_i*b_k - vHall_k*b_i
4685 ^c&f(ix^d,b^c_)=f(ix^d,b^c_)+vhall(ix^d,idim)*btotal(ix^d,^c)-btotal(ix^d,idim)*vhall(ix^d,^c)\
4686 if(total_energy) then
4687 ! f_i[e]= f_i[e] + vHall_i*(b_k*b_k) - b_i*(vHall_k*b_k)
4688 f(ix^d,e_)=f(ix^d,e_)+vhall(ix^d,idim)*(^c&w(ix^d,b^c_)*btotal(ix^d,^c)+)&
4689 -btotal(ix^d,idim)*(^c&vhall(ix^d,^c)*w(ix^d,b^c_)+)
4690 end if
4691 {end do\}
4692 end if
4693 if (mhd_fip) then
4694 f(ixo^s,fip_) = w(ixo^s,mom(idim)) * wc(ixo^s,fip_)
4695 end if
4696 if(mhd_uawsom) then
4697 {do ix^db=ixomin^db,ixomax^db\}
4698 uawsom_denom=dsqrt(w(ix^d,rho_)*(uawsom_zeta(ix^d)+one)/&
4699 (two*(one+mhd_uawsom_filling_factor*uawsom_zeta(ix^d)-&
4701 uawsom_bi=btotal(ix^d,idim)
4702 f(ix^d,waplus_)=w(ix^d,waplus_)*(w(ix^d,mom(idim))-&
4703 uawsom_bi/dsqrt(w(ix^d,rho_)))
4704 f(ix^d,waminus_)=w(ix^d,waminus_)*(w(ix^d,mom(idim))+&
4705 uawsom_bi/dsqrt(w(ix^d,rho_)))
4706 f(ix^d,wkplus_)=w(ix^d,wkplus_)*(w(ix^d,mom(idim))-uawsom_bi/uawsom_denom)
4707 f(ix^d,wkminus_)=w(ix^d,wkminus_)*(w(ix^d,mom(idim))+uawsom_bi/uawsom_denom)
4708 {end do\}
4709 end if
4710 ! Get flux of tracer
4711 do iw=1,mhd_n_tracer
4712 {do ix^db=ixomin^db,ixomax^db\}
4713 f(ix^d,tracer(iw))=w(ix^d,mom(idim))*w(ix^d,tracer(iw))
4714 {end do\}
4715 end do
4716
4717 if(mhd_hyperbolic_tc) then
4718 {do ix^db=ixomin^db,ixomax^db\}
4719 bunit_tc=b_unit_vector(btotal(ix^d,1:ndir))
4720 f(ix^d,e_)=f(ix^d,e_)+w(ix^d,qpar_)*bunit_tc(idim)
4721 f(ix^d,qpar_)=zero
4722 {end do\}
4724 call mhd_get_hyperbolic_tc_nperp(w,x,ixi^l,ixo^l,btotal,nperp)
4725 {do ix^db=ixomin^db,ixomax^db\}
4726 f(ix^d,e_)=f(ix^d,e_)+w(ix^d,qperp_)*nperp(ix^d,idim)
4727 f(ix^d,qperp_)=zero
4728 {end do\}
4729 end if
4730 end if
4731
4732 end subroutine mhd_get_flux_split
4733
4734 !> Semirelativistic fluxes within ixO^L, with B0 splitting per Gombosi et al. 2002 eqs (92)-(97)
4735 subroutine mhd_get_flux_semirelati(wC,w,x,ixI^L,ixO^L,idim,f)
4737 use mod_geometry
4738
4739 integer, intent(in) :: ixi^l, ixo^l, idim
4740 ! conservative w
4741 double precision, intent(in) :: wc(ixi^s,nw)
4742 ! primitive w
4743 double precision, intent(in) :: w(ixi^s,nw)
4744 double precision, intent(in) :: x(ixi^s,1:ndim)
4745 double precision,intent(out) :: f(ixi^s,nwflux)
4746 double precision :: sa(ixo^s,1:ndir),e(ixo^s,1:ndir),e2
4747 double precision :: btf(ixi^s,1:ndir),b0sq,btsq
4748 double precision :: nperp(ixi^s,1:ndir)
4749 double precision :: bunit_tc(ndir)
4750 integer :: iw, ix^d
4751
4752 {do ix^db=ixomin^db,ixomax^db\}
4753 ! Get flux of density
4754 f(ix^d,rho_)=w(ix^d,mom(idim))*w(ix^d,rho_)
4755 ! total field for E, the induction flux and BB; the stored slot (B1 under splitting)
4756 ! for the magnetic energy and the Poynting vector, Gombosi et al. 2002 eqs (92), (96)
4757 ^c&btf(ix^d,^c)=w(ix^d,b^c_)\
4758 if(b0field) then
4759 ^c&btf(ix^d,^c)=btf(ix^d,^c)+block%B0(ix^d,^c,idim)\
4760 end if
4761 ! E=Bxv
4762 {^ifthreec
4763 e(ix^d,1)=btf(ix^d,2)*w(ix^d,m3_)-btf(ix^d,3)*w(ix^d,m2_)
4764 e(ix^d,2)=btf(ix^d,3)*w(ix^d,m1_)-btf(ix^d,1)*w(ix^d,m3_)
4765 e(ix^d,3)=btf(ix^d,1)*w(ix^d,m2_)-btf(ix^d,2)*w(ix^d,m1_)
4766 }
4767 {^iftwoc
4768 e(ix^d,1)=zero
4769 ! switch 2 and 3 to add 3 when ^C is from 1 to 2
4770 e(ix^d,2)=btf(ix^d,1)*w(ix^d,m2_)-btf(ix^d,2)*w(ix^d,m1_)
4771 }
4772 {^ifonec
4773 e(ix^d,1)=zero
4774 }
4775 e2=(^c&e(ix^d,^c)**2+)
4776 if(mhd_internal_e) then
4777 ! Get flux of internal energy
4778 f(ix^d,e_)=w(ix^d,mom(idim))*wc(ix^d,e_)
4779 else
4780 ! S=ExB
4781 {^ifthreec
4782 sa(ix^d,1)=e(ix^d,2)*w(ix^d,b3_)-e(ix^d,3)*w(ix^d,b2_)
4783 sa(ix^d,2)=e(ix^d,3)*w(ix^d,b1_)-e(ix^d,1)*w(ix^d,b3_)
4784 sa(ix^d,3)=e(ix^d,1)*w(ix^d,b2_)-e(ix^d,2)*w(ix^d,b1_)
4785 }
4786 {^iftwoc
4787 sa(ix^d,1)=-e(ix^d,2)*w(ix^d,b2_)
4788 sa(ix^d,2)=e(ix^d,2)*w(ix^d,b1_)
4789 ! set E2 back to 0, after e^2 is stored
4790 e(ix^d,2)=zero
4791 }
4792 {^ifonec
4793 sa(ix^d,1)=zero
4794 }
4795 ! v*(eK+eint+p) + Poynting, with eK+eint = e-eB-eE from the conserved state so that
4796 ! the flux holds for any EoS
4797 f(ix^d,e_)=w(ix^d,mom(idim))*(wc(ix^d,e_)&
4798 -half*((^c&w(ix^d,b^c_)**2+)+e2*inv_squared_c)&
4799 +w(ix^d,p_))+sa(ix^d,idim)
4800 end if
4801 ! Get flux of momentum
4802 if(b0field) then
4803 ! eq (97): -(BB-B0B0) and 0.5*(B^2-B0^2); the electric terms are not split
4804 ^c&f(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,mom(idim))*w(ix^d,m^c_)&
4805 -btf(ix^d,idim)*btf(ix^d,^c)+block%B0(ix^d,idim,idim)*block%B0(ix^d,^c,idim)&
4806 -e(ix^d,idim)*e(ix^d,^c)*inv_squared_c\
4807 b0sq=(^c&block%B0(ix^d,^c,idim)**2+)
4808 btsq=(^c&btf(ix^d,^c)**2+)
4809 f(ix^d,mom(idim))=f(ix^d,mom(idim))+w(ix^d,p_)+half*(btsq-b0sq+e2*inv_squared_c)
4810 else
4811 ^c&f(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,mom(idim))*w(ix^d,m^c_)&
4812 -w(ix^d,mag(idim))*w(ix^d,b^c_)-e(ix^d,idim)*e(ix^d,^c)*inv_squared_c\
4813 ! gas pressure + magnetic pressure + electric pressure
4814 f(ix^d,mom(idim))=f(ix^d,mom(idim))+w(ix^d,p_)+half*((^c&w(ix^d,b^c_)**2+)+e2*inv_squared_c)
4815 end if
4816 ! compute flux of magnetic field
4817 ! f_i[b_k]=v_i*b_k-v_k*b_i
4818 ^c&f(ix^d,b^c_)=w(ix^d,mom(idim))*btf(ix^d,^c)-btf(ix^d,idim)*w(ix^d,m^c_)\
4819 {end do\}
4820
4821 if(mhd_glm) then
4822 {do ix^db=ixomin^db,ixomax^db\}
4823 f(ix^d,mag(idim))=w(ix^d,psi_)
4824 !f_i[psi]=Ch^2*b_{i} Eq. 24e and Eq. 38c Dedner et al 2002 JCP, 175, 645
4825 f(ix^d,psi_)=cmax_global**2*w(ix^d,mag(idim))
4826 {end do\}
4827 end if
4828 if (mhd_fip) then
4829 f(ixo^s,fip_) = w(ixo^s,mom(idim)) * wc(ixo^s,fip_)
4830 end if
4831 ! Get flux of tracer
4832 do iw=1,mhd_n_tracer
4833 {do ix^db=ixomin^db,ixomax^db\}
4834 f(ix^d,tracer(iw))=w(ix^d,mom(idim))*w(ix^d,tracer(iw))
4835 {end do\}
4836 end do
4837
4838 if(mhd_hyperbolic_tc) then
4839 {do ix^db=ixomin^db,ixomax^db\}
4840 bunit_tc=b_unit_vector(btf(ix^d,1:ndir))
4841 f(ix^d,e_)=f(ix^d,e_)+w(ix^d,qpar_)*bunit_tc(idim)
4842 f(ix^d,qpar_)=zero
4843 {end do\}
4845 call mhd_get_hyperbolic_tc_nperp(w,x,ixi^l,ixo^l,btf,nperp)
4846 {do ix^db=ixomin^db,ixomax^db\}
4847 f(ix^d,e_)=f(ix^d,e_)+w(ix^d,qperp_)*nperp(ix^d,idim)
4848 f(ix^d,qperp_)=zero
4849 {end do\}
4850 end if
4851 end if
4852 end subroutine mhd_get_flux_semirelati
4853
4854 subroutine mhd_get_flux_semirelati_noe(wC,w,x,ixI^L,ixO^L,idim,f)
4856 use mod_geometry
4858
4859 integer, intent(in) :: ixi^l, ixo^l, idim
4860 ! conservative w
4861 double precision, intent(in) :: wc(ixi^s,nw)
4862 ! primitive w
4863 double precision, intent(in) :: w(ixi^s,nw)
4864 double precision, intent(in) :: x(ixi^s,1:ndim)
4865 double precision,intent(out) :: f(ixi^s,nwflux)
4866
4867 double precision :: adiabs(ixi^s), gammas(ixi^s)
4868 double precision :: e(ixo^s,1:ndir),e2
4869 integer :: iw, ix^d
4870
4871 if(associated(usr_set_adiab)) then
4872 call usr_set_adiab(w,x,ixi^l,ixo^l,adiabs)
4873 else
4874 adiabs=mhd_adiab
4875 end if
4876 if(associated(usr_set_gamma)) then
4877 call usr_set_gamma(w,x,ixi^l,ixo^l,gammas)
4878 else
4879 gammas=eos%gamma
4880 end if
4881 {do ix^db=ixomin^db,ixomax^db\}
4882 ! Get flux of density
4883 f(ix^d,rho_)=w(ix^d,mom(idim))*w(ix^d,rho_)
4884 ! E=Bxv
4885 {^ifthreec
4886 e(ix^d,1)=w(ix^d,b2_)*w(ix^d,m3_)-w(ix^d,b3_)*w(ix^d,m2_)
4887 e(ix^d,2)=w(ix^d,b3_)*w(ix^d,m1_)-w(ix^d,b1_)*w(ix^d,m3_)
4888 e(ix^d,3)=w(ix^d,b1_)*w(ix^d,m2_)-w(ix^d,b2_)*w(ix^d,m1_)
4889 e2=(^c&e(ix^d,^c)**2+)
4890 }
4891 {^iftwoc
4892 e(ix^d,1)=zero
4893 ! switch 2 and 3 to add 3 when ^C is from 1 to 2
4894 e(ix^d,2)=w(ix^d,b1_)*w(ix^d,m2_)-w(ix^d,b2_)*w(ix^d,m1_)
4895 e2=e(ix^d,2)**2
4896 e(ix^d,2)=zero
4897 }
4898 {^ifonec
4899 e(ix^d,1)=zero
4900 e2=zero
4901 }
4902 ! Get flux of momentum
4903 ^c&f(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,mom(idim))*w(ix^d,m^c_)&
4904 -w(ix^d,mag(idim))*w(ix^d,b^c_)-e(ix^d,idim)*e(ix^d,^c)*inv_squared_c\
4905 ! gas pressure + magnetic pressure + electric pressure
4906 f(ix^d,mom(idim))=f(ix^d,mom(idim))+adiabs(ix^d)*w(ix^d,rho_)**gammas(ix^d)+half*((^c&w(ix^d,b^c_)**2+)+e2*inv_squared_c)
4907 ! compute flux of magnetic field
4908 ! f_i[b_k]=v_i*b_k-v_k*b_i
4909 ^c&f(ix^d,b^c_)=w(ix^d,mom(idim))*w(ix^d,b^c_)-w(ix^d,mag(idim))*w(ix^d,m^c_)\
4910 {end do\}
4911
4912 if(mhd_glm) then
4913 {do ix^db=ixomin^db,ixomax^db\}
4914 f(ix^d,mag(idim))=w(ix^d,psi_)
4915 !f_i[psi]=Ch^2*b_{i} Eq. 24e and Eq. 38c Dedner et al 2002 JCP, 175, 645
4916 f(ix^d,psi_)=cmax_global**2*w(ix^d,mag(idim))
4917 {end do\}
4918 end if
4919 if (mhd_fip) then
4920 f(ixo^s,fip_) = w(ixo^s,mom(idim)) * wc(ixo^s,fip_)
4921 end if
4922 ! Get flux of tracer
4923 do iw=1,mhd_n_tracer
4924 {do ix^db=ixomin^db,ixomax^db\}
4925 f(ix^d,tracer(iw))=w(ix^d,mom(idim))*w(ix^d,tracer(iw))
4926 {end do\}
4927 end do
4928 end subroutine mhd_get_flux_semirelati_noe
4929
4930 !> Source term J.E_ambi in internal energy
4931 !> For the ambipolar electric field we have E_ambi = -eta_A * JxBxB= eta_A * B^2 (J_perpB)
4932 !> and eta_A is mhd_ambi_coef/rho^2 or is user-defined
4933 !> the source term J.E_ambi = eta_A * B^2 * J_perpB^2 = eta_A * [(JxB)xB]^2/B^2
4934 !> note that J_perpB= - (JxB)xB/B^2
4935 !> multiplyAmbiCoef is actually doing multiplication with -mhd_ambi_coef/rho^2
4936 subroutine add_source_ambipolar_internal_energy(qdt,ixI^L,ixO^L,wCT,w,x)
4938 integer, intent(in) :: ixi^l, ixo^l
4939 double precision, intent(in) :: qdt
4940 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
4941 double precision, intent(inout) :: w(ixi^s,1:nw)
4942
4943 double precision :: tmp(ixi^s),btot2(ixi^s)
4944 double precision :: jxbxb(ixi^s,1:3)
4945
4946 call mhd_get_jxbxb(wct,x,ixi^l,ixo^l,jxbxb)
4947 ! avoiding nulls here
4948 btot2(ixo^s)=mhd_mag_en_all(wct,ixi^l,ixo^l)
4949 where (btot2(ixo^s)>smalldouble )
4950 tmp(ixo^s) = sum(jxbxb(ixo^s,1:3)**2,dim=ndim+1) / btot2(ixo^s)
4951 elsewhere
4952 tmp(ixo^s) = zero
4953 endwhere
4954 call multiplyambicoef(ixi^l,ixo^l,tmp,wct,x)
4955 ! multiplyAmbiCoef is actually doing multiplication with -mhd_ambi_coef/rho^2
4956 ! hence minus sign here
4957 w(ixo^s,e_)=w(ixo^s,e_)- qdt*tmp(ixo^s)
4958
4959 end subroutine add_source_ambipolar_internal_energy
4960
4961 !> this subroutine computes -J_perpB= (J x B) x B= B(J.B) - J B^2
4962 subroutine mhd_get_jxbxb(w,x,ixI^L,ixO^L,res)
4964
4965 integer, intent(in) :: ixi^l, ixo^l
4966 double precision, intent(in) :: w(ixi^s,nw)
4967 double precision, intent(in) :: x(ixi^s,1:ndim)
4968 double precision, intent(out) :: res(ixi^s,1:3)
4969
4970 double precision :: btot(ixi^s,1:3)
4971 double precision :: current(ixi^s,7-2*ndir:3)
4972 double precision :: tmp(ixi^s),b2(ixi^s)
4973 integer :: idir, idirmin
4974
4975 res=0.d0
4976 ! Calculate current density and idirmin
4977 ! current has nonzero values only for components in the range idirmin, 3
4978 call get_current(w,ixi^l,ixo^l,idirmin,current)
4979
4980 btot=0.d0
4981 if(b0field) then
4982 do idir=1,ndir
4983 btot(ixo^s, idir) = w(ixo^s,mag(idir)) + block%B0(ixo^s,idir,b0i)
4984 enddo
4985 else
4986 do idir=1,ndir
4987 btot(ixo^s, idir) = w(ixo^s,mag(idir))
4988 enddo
4989 endif
4990
4991 tmp(ixo^s)= sum(current(ixo^s,idirmin:3)*btot(ixo^s,idirmin:3),dim=ndim+1) !J.B
4992 b2(ixo^s) = sum(btot(ixo^s,1:3)**2,dim=ndim+1) !B^2
4993 do idir=1,idirmin-1
4994 res(ixo^s,idir) = btot(ixo^s,idir) * tmp(ixo^s)
4995 enddo
4996 do idir=idirmin,3
4997 res(ixo^s,idir) = btot(ixo^s,idir) * tmp(ixo^s) - current(ixo^s,idir) * b2(ixo^s)
4998 enddo
4999
5000 ! avoid possible issues at nulls
5001 do idir=1,3
5002 where (b2(ixo^s)<smalldouble )
5003 res(ixo^s,idir) = zero
5004 endwhere
5005 enddo
5006 end subroutine mhd_get_jxbxb
5007
5008 !> Sets the sources for the ambipolar terms for the STS method
5009 !> The sources are added directly (instead of fluxes as in the explicit)
5010 !> at the corresponding indices
5011 !> store_flux_var is explicitly called for each of the fluxes one by one
5012 subroutine sts_set_source_ambipolar(ixI^L,ixO^L,w,x,wres,fix_conserve_at_step,my_dt,igrid,nflux)
5015
5016 integer, intent(in) :: ixi^l,ixo^l,igrid,nflux
5017 double precision, intent(in) :: x(ixi^s,1:ndim)
5018 double precision, intent(inout) :: wres(ixi^s,1:nw), w(ixi^s,1:nw)
5019 double precision, intent(in) :: my_dt
5020 logical, intent(in) :: fix_conserve_at_step
5021
5022 double precision, dimension(ixI^S,1:3) :: tmp,ff
5023 double precision :: fluxall(ixi^s,1:nflux,1:ndim)
5024 double precision :: fe(ixi^s,sdim:3)
5025 double precision :: btot(ixi^s,1:3),tmp2(ixi^s)
5026 integer :: i, ixa^l, ie_
5027
5028 ixa^l=ixo^l^ladd1;
5029
5030 fluxall=zero
5031
5032 ! here we compute (JxB)xB= - B^2 J_perpB
5033 call mhd_get_jxbxb(w,x,ixi^l,ixa^l,tmp)
5034
5035 ! set ambipolar electric field in tmp: E_ambi = -eta_A * JxBxB= eta_A * B^2 (J_perpB)
5036 ! and eta_A is mhd_ambi_coef/rho^2 or is user-defined
5037 ! multiplyAmbiCoef is actually doing multiplication with -mhd_ambi_coef/rho^2
5038 do i=1,3
5039 call multiplyambicoef(ixi^l,ixa^l,tmp(ixi^s,i),w,x)
5040 enddo
5041
5042 ! Note: internal energy case is handled through add_source_internal_e
5043 ! Note: hydrodynamic energy case is handled through add_source_hydrodynamic_e
5044 ! both of the above use add_source_ambipolar_internal_energy
5045 !
5046 ! Note: total energy case without B0field split is ok here and adds div(BxE_ambi)
5047 ! Note: total energy case in semirelativistic variant (hence no B0field split) is ok here
5048 ! Note: total energy with B0field=T here adds div(B_1xE_ambi) which needs correction in add_source_B0split
5049 if(mhd_energy .and. .not.(mhd_internal_e.or.mhd_hydrodynamic_e)) then
5050 btot(ixa^s,1:3) = 0.d0
5051 ! HERE: only uses B_1 if split, otherwise this is B
5052 btot(ixa^s,1:ndir) = w(ixa^s,mag(1:ndir))
5053 ! compute ff= E_ambi x B (where B can be B_1 if B0field=T)
5054 call cross_product(ixi^l,ixa^l,tmp,btot,ff)
5055 ! compute actual cell face fluxes in ff and their divergence in tmp2
5056 call get_flux_on_cell_face(ixi^l,ixo^l,ff,tmp2)
5057 if(fix_conserve_at_step) fluxall(ixi^s,1,1:ndim)=ff(ixi^s,1:ndim)
5058 ! - sign as the source is actually div(BxE_ambi) and we have div(E_ambi x B) in tmp2
5059 wres(ixo^s,e_)=-tmp2(ixo^s)
5060 endif
5061
5062 if(stagger_grid) then
5063 ! always 2D or more (2.5/3D)
5064 if(ndir>ndim) then
5065 !!!Bz
5066 ff(ixa^s,1) = tmp(ixa^s,2)
5067 ff(ixa^s,2) = -tmp(ixa^s,1)
5068 ff(ixa^s,3) = 0.d0
5069 call get_flux_on_cell_face(ixi^l,ixo^l,ff,tmp2)
5070 if(fix_conserve_at_step) fluxall(ixi^s,1+ndir,1:ndim)=ff(ixi^s,1:ndim)
5071 wres(ixo^s,mag(ndir))=-tmp2(ixo^s)
5072 end if
5073 fe=0.d0
5074 call update_faces_ambipolar(ixi^l,ixo^l,w,x,tmp,fe,btot)
5075 ixamax^d=ixomax^d;
5076 ixamin^d=ixomin^d-1;
5077 wres(ixa^s,mag(1:ndim))=-btot(ixa^s,1:ndim)
5078 else
5079 !write curl(ele) as the divergence
5080 !m1={0,ele[[3]],-ele[[2]]}
5081 !m2={-ele[[3]],0,ele[[1]]}
5082 !m3={ele[[2]],-ele[[1]],0}
5083
5084 {^ifoned
5085 !!!Bx
5086 ff(ixa^s,1) = 0.d0
5087 ff(ixa^s,2) = tmp(ixa^s,3)
5088 ff(ixa^s,3) = -tmp(ixa^s,2)
5089 call get_flux_on_cell_face(ixi^l,ixo^l,ff,tmp2)
5090 if(fix_conserve_at_step) fluxall(ixi^s,2,1:ndim)=ff(ixi^s,1:ndim)
5091 !flux divergence is a source now
5092 wres(ixo^s,mag(1))=-tmp2(ixo^s)
5093 if(ndir==2.or.ndir==3)then
5094 !!!By
5095 ff(ixa^s,1) = -tmp(ixa^s,3)
5096 ff(ixa^s,2) = 0.d0
5097 ff(ixa^s,3) = tmp(ixa^s,1)
5098 call get_flux_on_cell_face(ixi^l,ixo^l,ff,tmp2)
5099 if(fix_conserve_at_step) fluxall(ixi^s,3,1:ndim)=ff(ixi^s,1:ndim)
5100 wres(ixo^s,mag(2))=-tmp2(ixo^s)
5101 endif
5102 }
5103 {^nooned
5104 !!!Bx
5105 ff(ixa^s,1) = 0.d0
5106 ff(ixa^s,2) = tmp(ixa^s,3)
5107 ff(ixa^s,3) = -tmp(ixa^s,2)
5108 call get_flux_on_cell_face(ixi^l,ixo^l,ff,tmp2)
5109 if(fix_conserve_at_step) fluxall(ixi^s,2,1:ndim)=ff(ixi^s,1:ndim)
5110 !flux divergence is a source now
5111 wres(ixo^s,mag(1))=-tmp2(ixo^s)
5112 !!!By
5113 ff(ixa^s,1) = -tmp(ixa^s,3)
5114 ff(ixa^s,2) = 0.d0
5115 ff(ixa^s,3) = tmp(ixa^s,1)
5116 call get_flux_on_cell_face(ixi^l,ixo^l,ff,tmp2)
5117 if(fix_conserve_at_step) fluxall(ixi^s,3,1:ndim)=ff(ixi^s,1:ndim)
5118 wres(ixo^s,mag(2))=-tmp2(ixo^s)
5119 }
5120
5121 if(ndir==3) then
5122 !!!Bz
5123 ff(ixa^s,1) = tmp(ixa^s,2)
5124 ff(ixa^s,2) = -tmp(ixa^s,1)
5125 ff(ixa^s,3) = 0.d0
5126 call get_flux_on_cell_face(ixi^l,ixo^l,ff,tmp2)
5127 if(fix_conserve_at_step) fluxall(ixi^s,1+ndir,1:ndim)=ff(ixi^s,1:ndim)
5128 wres(ixo^s,mag(ndir))=-tmp2(ixo^s)
5129 end if
5130
5131 end if
5132
5133 if(fix_conserve_at_step) then
5134 fluxall=my_dt*fluxall
5135 call store_flux(igrid,fluxall,1,ndim,nflux)
5136 if(stagger_grid) then
5137 call store_edge(igrid,ixi^l,my_dt*fe,1,ndim)
5138 end if
5139 end if
5140
5141 end subroutine sts_set_source_ambipolar
5142
5143 !> get ambipolar electric field and the integrals around cell faces
5144 subroutine update_faces_ambipolar(ixI^L,ixO^L,w,x,ECC,fE,circ)
5146
5147 integer, intent(in) :: ixi^l, ixo^l
5148 double precision, intent(in) :: w(ixi^s,1:nw)
5149 double precision, intent(in) :: x(ixi^s,1:ndim)
5150 ! amibipolar electric field at cell centers
5151 double precision, intent(in) :: ecc(ixi^s,1:3)
5152 double precision, intent(out) :: fe(ixi^s,sdim:3)
5153 double precision, intent(out) :: circ(ixi^s,1:ndim)
5154
5155 integer :: hxc^l,ixc^l,ixa^l
5156 integer :: idim1,idim2,idir,ix^d
5157
5158 fe=zero
5159 ! calculate ambipolar electric field on cell edges from cell centers
5160 do idir=sdim,3
5161 ixcmax^d=ixomax^d;
5162 ixcmin^d=ixomin^d+kr(idir,^d)-1;
5163 {do ix^db=0,1\}
5164 if({ ix^d==1 .and. ^d==idir | .or.}) cycle
5165 ixamin^d=ixcmin^d+ix^d;
5166 ixamax^d=ixcmax^d+ix^d;
5167 fe(ixc^s,idir)=fe(ixc^s,idir)+ecc(ixa^s,idir)
5168 {end do\}
5169 fe(ixc^s,idir)=fe(ixc^s,idir)*0.25d0*block%dsC(ixc^s,idir)
5170 end do
5171
5172 ! Calculate circulation on each face to get value of line integral of
5173 ! electric field in the positive idir direction.
5174 ixcmax^d=ixomax^d;
5175 ixcmin^d=ixomin^d-1;
5176
5177 circ=zero
5178 do idim1=1,ndim ! Coordinate perpendicular to face
5179 do idim2=1,ndim
5180 do idir=sdim,3 ! Direction of line integral
5181 ! Assemble indices
5182 hxc^l=ixc^l-kr(idim2,^d);
5183 ! Add line integrals in direction idir
5184 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
5185 +lvc(idim1,idim2,idir)&
5186 *(fe(ixc^s,idir)&
5187 -fe(hxc^s,idir))
5188 end do
5189 end do
5190 circ(ixc^s,idim1)=circ(ixc^s,idim1)/block%surfaceC(ixc^s,idim1)
5191 end do
5192
5193 end subroutine update_faces_ambipolar
5194
5195 !> use cell-center flux vector to get cell-face flux vector
5196 !> which will be used to add the source term as the divergence of the flux
5197 !> we return fluxes at all faces as well as the divergence of the flux
5198 !> Note that for ndir>ndim, we do not modify the input cell center flux
5199 subroutine get_flux_on_cell_face(ixI^L,ixO^L,ff,src)
5201
5202 integer, intent(in) :: ixi^l, ixo^l
5203 double precision, dimension(ixI^S,1:3), intent(inout) :: ff
5204 double precision, intent(out) :: src(ixi^s)
5205
5206 double precision :: ffc(ixi^s,1:ndim)
5207 double precision :: dxinv(ndim)
5208 integer :: idims, ix^d, ixa^l, ixb^l, ixc^l
5209
5210 ixa^l=ixo^l^ladd1;
5211 dxinv=1.d0/dxlevel
5212 ! cell corner flux in ffc
5213 ! TO BE GENERALIZED FOR NON-UNIFORM NON-CARTESIAN MESH
5214 if (slab_uniform)then
5215 ffc=0.d0
5216 ixcmax^d=ixomax^d; ixcmin^d=ixomin^d-1;
5217 {do ix^db=0,1\}
5218 ixbmin^d=ixcmin^d+ix^d;
5219 ixbmax^d=ixcmax^d+ix^d;
5220 ffc(ixc^s,1:ndim)=ffc(ixc^s,1:ndim)+ff(ixb^s,1:ndim)
5221 {end do\}
5222 ffc(ixc^s,1:ndim)=0.5d0**ndim*ffc(ixc^s,1:ndim)
5223 else
5224 call mpistop("to generalize using volume averaging")
5225 endif
5226 ! now get flux at cell face from corner fluxes in fcc
5227 ff(ixi^s,1:ndim)=0.d0
5228 do idims=1,ndim
5229 ixb^l=ixo^l-kr(idims,^d);
5230 ixcmax^d=ixomax^d; ixcmin^d=ixbmin^d;
5231 {do ix^db=0,1 \}
5232 if({ ix^d==0 .and. ^d==idims | .or.}) then
5233 ixbmin^d=ixcmin^d-ix^d;
5234 ixbmax^d=ixcmax^d-ix^d;
5235 ff(ixc^s,idims)=ff(ixc^s,idims)+ffc(ixb^s,idims)
5236 end if
5237 {end do\}
5238 ff(ixc^s,idims)=ff(ixc^s,idims)*0.5d0**(ndim-1)
5239 end do
5240 src=0.d0
5241 if(slab_uniform) then
5242 do idims=1,ndim
5243 ff(ixa^s,idims)=dxinv(idims)*ff(ixa^s,idims)
5244 ixb^l=ixo^l-kr(idims,^d);
5245 src(ixo^s)=src(ixo^s)+ff(ixo^s,idims)-ff(ixb^s,idims)
5246 end do
5247 else
5248 do idims=1,ndim
5249 ff(ixa^s,idims)=ff(ixa^s,idims)*block%surfaceC(ixa^s,idims)
5250 ixb^l=ixo^l-kr(idims,^d);
5251 src(ixo^s)=src(ixo^s)+ff(ixo^s,idims)-ff(ixb^s,idims)
5252 end do
5253 src(ixo^s)=src(ixo^s)/block%dvolume(ixo^s)
5254 end if
5255 end subroutine get_flux_on_cell_face
5256
5257 !> Calculates the explicit dt for the ambipolar term
5258 !> This function is used by both explicit scheme and STS method
5259 function get_ambipolar_dt(w,ixI^L,ixO^L,dx^D,x) result(dtnew)
5261
5262 integer, intent(in) :: ixi^l, ixo^l
5263 double precision, intent(in) :: dx^d, x(ixi^s,1:ndim)
5264 double precision, intent(in) :: w(ixi^s,1:nw)
5265 double precision :: dtnew
5266
5267 double precision :: coef
5268 double precision :: dxarr(ndim)
5269 double precision :: tmp(ixi^s)
5270
5271 ^d&dxarr(^d)=dx^d;
5272 tmp(ixo^s) = mhd_mag_en_all(w, ixi^l, ixo^l)
5273 call multiplyambicoef(ixi^l,ixo^l,tmp,w,x)
5274 ! now we have -mhd_eta_ambi B^2 /rho^2 in tmp
5275 coef = maxval(dabs(tmp(ixo^s)))
5276 if(coef/=0.d0) then
5277 coef=1.d0/coef
5278 else
5279 coef=bigdouble
5280 end if
5281 if(slab_uniform) then
5282 dtnew=minval(dxarr(1:ndim))**2.0d0*coef
5283 else
5284 dtnew=minval(block%ds(ixo^s,1:ndim))**2.0d0*coef
5285 end if
5286
5287 end function get_ambipolar_dt
5288
5289 !> multiply res by the ambipolar coefficient
5290 !> The ambipolar coefficient is calculated as -mhd_eta_ambi/rho^2
5291 !> The user may mask its value in the user file
5292 !> by implementing usr_mask_ambipolar subroutine
5293 subroutine multiplyambicoef(ixI^L,ixO^L,res,w,x)
5295 integer, intent(in) :: ixi^l, ixo^l
5296 double precision, intent(in) :: w(ixi^s,1:nw), x(ixi^s,1:ndim)
5297 double precision, intent(inout) :: res(ixi^s)
5298 double precision :: tmp(ixi^s)
5299 double precision :: rho(ixi^s)
5300
5301 call mhd_get_rho(w,x,ixi^l,ixi^l,rho)
5302 tmp(ixi^s)=-mhd_eta_ambi/rho(ixi^s)**2
5303 if (associated(usr_mask_ambipolar)) then
5304 call usr_mask_ambipolar(ixi^l,ixo^l,w,x,tmp)
5305 end if
5306 res(ixo^s) = tmp(ixo^s) * res(ixo^s)
5307
5308 end subroutine multiplyambicoef
5309
5310 !> w[iws]=w[iws]+qdt*S[iws,wCT] where S is the source based on wCT within ixO
5311 subroutine mhd_add_source(qdt,dtfactor,ixI^L,ixO^L,wCT,wCTprim,w,x,qsourcesplit,active)
5316 use mod_cak_force, only: cak_add_source
5317
5318 integer, intent(in) :: ixi^l, ixo^l
5319 double precision, intent(in) :: qdt,dtfactor
5320 double precision, intent(in) :: wct(ixi^s,1:nw),wctprim(ixi^s,1:nw), x(ixi^s,1:ndim)
5321 double precision, intent(inout) :: w(ixi^s,1:nw)
5322 logical, intent(in) :: qsourcesplit
5323 logical, intent(inout) :: active
5324
5325 !TODO local_timestep support is only added for splitting
5326 ! but not for other nonideal terms such gravity, RC, viscosity,..
5327 ! it will also only work for divbfix 'linde', which does not require
5328 ! modification as it does not use dt in the update
5329
5330 if (.not. qsourcesplit) then
5331 if(mhd_uawsom) then
5332 active = .true.
5333 call mhd_add_source_uawsom(qdt,ixi^l,ixo^l,wct,wctprim,w,x)
5334 end if
5335 if(mhd_internal_e) then
5336 ! Source for solving internal energy
5337 active = .true.
5338 call add_source_internal_e(qdt,ixi^l,ixo^l,wct,w,x,wctprim)
5339 else
5340 if(has_equi_rho_and_p) then
5341 active = .true.
5342 call add_equi_terms(qdt,dtfactor,ixi^l,ixo^l,wct,w,x,wctprim)
5343 end if
5344 end if
5345
5346 if(mhd_hyperbolic_tc) then
5347 active = .true.
5348 call add_source_hyperbolic_tc(qdt,ixi^l,ixo^l,wct,w,x,wctprim)
5349 end if
5350
5351 ! Source for B0 splitting
5352 if (b0field) then
5353 active = .true.
5354 ! this adds source to momentum of type J0 x B0 and to energy equation
5355 ! latter always + J0 * E (electric field being E_ideal, E_hall, E_ambi)
5356 ! used for total energy variants
5357 call add_source_b0split(qdt,dtfactor,ixi^l,ixo^l,wct,w,x,wctprim)
5358 end if
5359
5360 ! Sources for resistivity in eqs. for e, B1, B2 and B3
5361 if (abs(mhd_eta)>smalldouble)then
5362 active = .true.
5363 call add_source_res_exp(qdt,ixi^l,ixo^l,wct,w,x)
5364 end if
5365
5366 if (mhd_ambipolar_exp)then
5367 active = .true.
5368 call add_source_ambi_exp(qdt,ixi^l,ixo^l,wct,w,x)
5369 end if
5370
5371 if (mhd_eta_hyper>0.d0)then
5372 active = .true.
5373 call add_source_hyperres(qdt,ixi^l,ixo^l,wct,w,x)
5374 end if
5375
5376 if(mhd_hydrodynamic_e) then
5377 ! Source for solving hydrodynamic energy
5378 active = .true.
5379 call add_source_hydrodynamic_e(qdt,ixi^l,ixo^l,wct,w,x,wctprim)
5380 else if (mhd_semirelativistic) then
5381 ! add sources for semirelativistic MHD
5382 active = .true.
5383 call add_source_semirelativistic(qdt,ixi^l,ixo^l,wct,w,x,wctprim)
5384 end if
5385 end if
5386
5387 {^nooned
5388 if(source_split_divb .eqv. qsourcesplit) then
5389 ! Sources related to div B
5390 select case (type_divb)
5391 case (divb_ct)
5392 continue ! Do nothing
5393 case (divb_linde)
5394 active = .true.
5395 call add_source_linde(qdt,ixi^l,ixo^l,wct,w,x)
5396 case (divb_glm)
5397 active = .true.
5398 call add_source_glm(qdt,ixi^l,ixo^l,wct,w,x)
5399 case (divb_powel)
5400 active = .true.
5401 call add_source_powel(qdt,ixi^l,ixo^l,wctprim,w,x)
5402 case (divb_janhunen)
5403 active = .true.
5404 call add_source_janhunen(qdt,ixi^l,ixo^l,wctprim,w,x)
5405 case (divb_lindejanhunen)
5406 active = .true.
5407 call add_source_linde(qdt,ixi^l,ixo^l,wct,w,x)
5408 call add_source_janhunen(qdt,ixi^l,ixo^l,wctprim,w,x)
5409 case (divb_lindepowel)
5410 active = .true.
5411 call add_source_linde(qdt,ixi^l,ixo^l,wct,w,x)
5412 call add_source_powel(qdt,ixi^l,ixo^l,wctprim,w,x)
5413 case (divb_lindeglm)
5414 active = .true.
5415 call add_source_linde(qdt,ixi^l,ixo^l,wct,w,x)
5416 call add_source_glm(qdt,ixi^l,ixo^l,wct,w,x)
5417 case (divb_multigrid)
5418 continue ! Do nothing
5419 case (divb_none)
5420 ! Do nothing
5421 case default
5422 call mpistop('Unknown divB fix')
5423 end select
5424 end if
5425 }
5426
5427 if(mhd_radiative_cooling) then
5428 call radiative_cooling_add_source(qdt,ixi^l,ixo^l,wct,wctprim,&
5429 w,x,qsourcesplit,active, rc_fl)
5430 end if
5431
5432 ! The radiative transfer heating, solved once per step, is added unsplit at every stage
5433 if(mhd_radiative_transfer .and. .not. qsourcesplit) then
5434 active = .true.
5435 call rt_sc_add_source(qdt,ixi^l,ixo^l,wct,w,x,mhd_radiative_cooling,mhd_internal_e)
5436 end if
5437
5438 if(mhd_viscosity) then
5439 call viscosity_add_source(qdt,ixi^l,ixo^l,wct,wctprim,&
5440 w,x,mhd_energy,qsourcesplit,active)
5441 end if
5442
5443 if(mhd_gravity) then
5444 call gravity_add_source(qdt,ixi^l,ixo^l,wct,wctprim,&
5445 w,x,gravity_energy,qsourcesplit,active)
5446 end if
5447
5448 if (mhd_cak_force) then
5449 call cak_add_source(qdt,ixi^l,ixo^l,wct,w,x,mhd_energy,qsourcesplit,active)
5450 end if
5451
5452 ! This is where the radiation force and heating/cooling are added
5453 if (mhd_radiation_fld) then
5454 call mhd_add_radiation_source(qdt,ixi^l,ixo^l,wct,wctprim,w,x,qsourcesplit,active)
5455 endif
5456
5457 end subroutine mhd_add_source
5458
5459 subroutine mhd_add_radiation_source(qdt,ixI^L,ixO^L,wCT,wCTprim,w,x,qsourcesplit,active)
5460 use mod_constants
5462 use mod_usr_methods
5463 use mod_fld
5464
5465 integer, intent(in) :: ixi^l, ixo^l
5466 double precision, intent(in) :: qdt, x(ixi^s,1:ndim)
5467 double precision, intent(in) :: wct(ixi^s,1:nw),wctprim(ixi^s,1:nw)
5468 double precision, intent(inout) :: w(ixi^s,1:nw)
5469 logical, intent(in) :: qsourcesplit
5470 logical, intent(inout) :: active
5471
5472 ! add radiation force and work done by it, changes momentum and gas energy
5473 ! handle photon tiring, heating and cooling exchange between gas and radiation field
5474 call add_fld_rad_force(qdt,ixi^l,ixo^l,wct,wctprim,w,x,qsourcesplit,active,fld_fl)
5475
5476 end subroutine mhd_add_radiation_source
5477
5478 !> add some source terms to total energy related to has_equi_rho_and_p=T
5479 subroutine add_equi_terms(qdt,dtfactor,ixI^L,ixO^L,wCT,w,x,wCTprim)
5481 use mod_geometry
5482 use mod_usr_methods
5483
5484 integer, intent(in) :: ixi^l, ixo^l
5485 double precision, intent(in) :: qdt,dtfactor
5486 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
5487 double precision, intent(in) :: wctprim(ixi^s,1:nw)
5488 double precision, intent(inout) :: w(ixi^s,1:nw)
5489
5490 double precision :: divv(ixi^s)
5491 double precision :: a(ixi^s,3), b(ixi^s,3), axb(ixi^s,3)
5492 double precision :: gravity_field(ixi^s,1:ndim)
5493 integer :: idir
5494
5495 if(slab_uniform) then
5496 if(nghostcells .gt. 2) then
5497 call divvector(wctprim(ixi^s,mom(1:ndir)),ixi^l,ixo^l,divv,3)
5498 else
5499 call divvector(wctprim(ixi^s,mom(1:ndir)),ixi^l,ixo^l,divv,2)
5500 end if
5501 else
5502 call divvector(wctprim(ixi^s,mom(1:ndir)),ixi^l,ixo^l,divv)
5503 end if
5504 divv(ixo^s)=divv(ixo^s)*eos%gamma*eos%inv_gamma_minus_1
5505 if(local_timestep) then
5506 w(ixo^s,e_)=w(ixo^s,e_)-dtfactor*block%dt(ixo^s)*block%equi_vars(ixo^s,equi_pe0_,0)*divv(ixo^s)
5507 else
5508 w(ixo^s,e_)=w(ixo^s,e_)-qdt*block%equi_vars(ixo^s,equi_pe0_,0)*divv(ixo^s)
5509 end if
5510 if(b0field)then
5511 if(b0field_forcefree.and.mhd_gravity)then
5512 ! add -v dot(rho_0 g)/(gamma-1)
5513 call usr_gravity(ixi^l,ixo^l,wct,x,gravity_field)
5514 do idir=1,ndim
5515 w(ixo^s,e_)=w(ixo^s,e_)-qdt*wctprim(ixo^s,mom(idir))*block%equi_vars(ixo^s,equi_rho0_,0)*gravity_field(ixo^s,idir)*eos%inv_gamma_minus_1
5516 enddo
5517 else
5518 a=0.d0
5519 b=0.d0
5520 ! store B0 magnetic field in b
5521 b(ixo^s,1:ndir)=block%B0(ixo^s,1:ndir,0)
5522 ! store J0 current in a
5523 do idir=7-2*ndir,3
5524 a(ixo^s,idir)=block%J0(ixo^s,idir)
5525 end do
5526 call cross_product(ixi^l,ixo^l,a,b,axb)
5527 ! add -v dot(rho_0 g + J0 x B_0)/(gamma-1)
5528 do idir=1,ndir
5529 w(ixo^s,e_)=w(ixo^s,e_)-qdt*wctprim(ixo^s,mom(idir))*axb(ixo^s,idir)*eos%inv_gamma_minus_1
5530 enddo
5531 if(mhd_gravity)then
5532 ! add -v dot(rho_0 g)/(gamma-1)
5533 call usr_gravity(ixi^l,ixo^l,wct,x,gravity_field)
5534 do idir=1,ndim
5535 w(ixo^s,e_)=w(ixo^s,e_)-qdt*wctprim(ixo^s,mom(idir))*block%equi_vars(ixo^s,equi_rho0_,0)*gravity_field(ixo^s,idir)*eos%inv_gamma_minus_1
5536 enddo
5537 endif
5538 endif
5539 else
5540 if(mhd_gravity)then
5541 ! add -v dot(rho_0 g)/(gamma-1)
5542 call usr_gravity(ixi^l,ixo^l,wct,x,gravity_field)
5543 do idir=1,ndim
5544 w(ixo^s,e_)=w(ixo^s,e_)-qdt*wctprim(ixo^s,mom(idir))*block%equi_vars(ixo^s,equi_rho0_,0)*gravity_field(ixo^s,idir)*eos%inv_gamma_minus_1
5545 enddo
5546 endif
5547 endif
5548 end subroutine add_equi_terms
5549
5550 !> Common direction for hyperbolic heat-flux relaxation and energy transport.
5551 !> B must contain the total field, including B0 when splitting is enabled.
5552 !> Normalize every finite nonzero field with max-component scaling.
5553 !> Only the zero field has no direction; no weak-field cutoff is applied.
5554 pure function b_unit_vector(B) result(bunit)
5555 ! Accept strided cell slices without forcing a contiguous input copy.
5556 ! Callers supply exactly ndir total-field components.
5557 double precision, intent(in) :: b(:)
5558 double precision :: bunit(^nc), maxm, scaled_norm
5559
5560 maxm=maxval(abs(b))
5561 if(maxm==0.d0) then
5562 bunit=0.d0
5563 else
5564 ^c&bunit(^c)=b(^c)/maxm\
5565 scaled_norm=dsqrt(^c&bunit(^c)**2+)
5566 ! At least one scaled component has magnitude one, so scaled_norm >= 1.
5567 ^c&bunit(^c)=bunit(^c)/scaled_norm\
5568 end if
5569 end function b_unit_vector
5570
5571 subroutine mhd_get_hyperbolic_tc_nperp(w,x,ixI^L,ixO^L,Bvec,nperp)
5573 use mod_geometry, only: gradient
5574 integer, intent(in) :: ixi^l,ixo^l
5575 double precision, intent(in) :: w(ixi^s,nw) ! primitive w
5576 double precision, intent(in) :: x(ixi^s,1:ndim)
5577 double precision, intent(in) :: bvec(ixi^s,1:ndir)
5578 double precision, intent(out) :: nperp(ixi^s,1:ndir)
5579
5580 double precision :: r(ixi^s), te(ixi^s), bgradt
5581 double precision :: bunitvec(ndir), gradt(ixi^s,ndir), gradt_perp(ndir)
5582 integer :: ix^d, idir
5583
5584 call mhd_get_rho(w,x,ixi^l,ixi^l,te)
5585 call eos%get_Rfactor(w,x,ixi^l,ixi^l,r)
5586 te(ixi^s)=w(ixi^s,p_)/(te(ixi^s)*r(ixi^s))
5587 if(.not. slab_uniform) then
5588 do idir=1,ndim
5589 call gradient(te,ixi^l,ixo^l,idir,gradt(ixi^s,idir))
5590 end do
5591 if(ndir>ndim) gradt(ixi^s,ndir)=0.d0
5592 end if
5593
5594 {^iftwod
5595 do ix2=ixomin2,ixomax2
5596 do ix1=ixomin1,ixomax1
5597 bunitvec=b_unit_vector(bvec(ix^d,1:ndir))
5598 if(slab_uniform) then
5599 gradt(ix^d,1)=((8.d0*(te(ix1+1,ix2)-te(ix1-1,ix2))-te(ix1+2,ix2)+te(ix1-2,ix2))/12.d0)/block%ds(ix^d,1)
5600 gradt(ix^d,2)=((8.d0*(te(ix1,ix2+1)-te(ix1,ix2-1))-te(ix1,ix2+2)+te(ix1,ix2-2))/12.d0)/block%ds(ix^d,2)
5601 if(ndir>2) gradt(ix^d,ndir)=zero
5602 end if
5603
5604 bgradt=(^c&bunitvec(^c)*gradt(ix^d,^c)+)
5605 {^c& gradt_perp(^c)=gradt(ix^d,^c)-bgradt*bunitvec(^c)\}
5606 nperp(ix^d,1:ndir)=b_unit_vector(gradt_perp(1:ndir))
5607 end do
5608 end do
5609 }
5610 {^ifthreed
5611 do ix3=ixomin3,ixomax3
5612 do ix2=ixomin2,ixomax2
5613 do ix1=ixomin1,ixomax1
5614 bunitvec=b_unit_vector(bvec(ix^d,1:ndir))
5615
5616 if(slab_uniform) then
5617 gradt(ix^d,1)=((8.d0*(te(ix1+1,ix2,ix3)-te(ix1-1,ix2,ix3))-te(ix1+2,ix2,ix3)+te(ix1-2,ix2,ix3))/12.d0)/block%ds(ix^d,1)
5618 gradt(ix^d,2)=((8.d0*(te(ix1,ix2+1,ix3)-te(ix1,ix2-1,ix3))-te(ix1,ix2+2,ix3)+te(ix1,ix2-2,ix3))/12.d0)/block%ds(ix^d,2)
5619 gradt(ix^d,3)=((8.d0*(te(ix1,ix2,ix3+1)-te(ix1,ix2,ix3-1))-te(ix1,ix2,ix3+2)+te(ix1,ix2,ix3-2))/12.d0)/block%ds(ix^d,3)
5620 end if
5621
5622 bgradt=(^c&bunitvec(^c)*gradt(ix^d,^c)+)
5623 {^c& gradt_perp(^c)=gradt(ix^d,^c)-bgradt*bunitvec(^c)\}
5624 nperp(ix^d,1:ndir)=b_unit_vector(gradt_perp(1:ndir))
5625 end do
5626 end do
5627 end do
5628 }
5629 end subroutine mhd_get_hyperbolic_tc_nperp
5630
5631 subroutine mhd_get_hyperbolic_tc_geometry(ixI^L,ixO^L,Te,Bvec,bgradT,gradTperp_mag,nperp)
5633 use mod_geometry, only: gradient
5634 integer, intent(in) :: ixi^l,ixo^l
5635 double precision, intent(in) :: te(ixi^s)
5636 double precision, intent(in) :: bvec(ixi^s,1:ndir)
5637 double precision, intent(out) :: bgradt(ixi^s), gradtperp_mag(ixi^s)
5638 double precision, intent(out) :: nperp(ixi^s,1:ndir)
5639
5640 double precision :: bunitvec(ndir), gradt(ixi^s,ndir), gradt_perp(ndir)
5641 double precision :: maxm
5642 integer :: ix^d, idir
5643
5644 if(.not. slab_uniform) then
5645 do idir=1,ndim
5646 call gradient(te,ixi^l,ixo^l,idir,gradt(ixi^s,idir))
5647 end do
5648 if(ndir>ndim) gradt(ixi^s,ndir)=0.d0
5649 end if
5650
5651 {^iftwod
5652 do ix2=ixomin2,ixomax2
5653 do ix1=ixomin1,ixomax1
5654 bunitvec=b_unit_vector(bvec(ix^d,1:ndir))
5655 if(slab_uniform) then
5656 gradt(ix^d,1)=((8.d0*(te(ix1+1,ix2)-te(ix1-1,ix2))-te(ix1+2,ix2)+te(ix1-2,ix2))/12.d0)/block%ds(ix^d,1)
5657 gradt(ix^d,2)=((8.d0*(te(ix1,ix2+1)-te(ix1,ix2-1))-te(ix1,ix2+2)+te(ix1,ix2-2))/12.d0)/block%ds(ix^d,2)
5658 if(ndir>2) gradt(ix^d,3)=zero
5659 end if
5660
5661 bgradt(ix^d)=(^c&bunitvec(^c)*gradt(ix^d,^c)+)
5662 {^c& gradt_perp(^c)=gradt(ix^d,^c)-bgradt(ix^d)*bunitvec(^c)\}
5663 maxm=maxval(abs(gradt_perp))
5664 if(maxm==0.d0) then
5665 gradtperp_mag(ix^d)=zero
5666 {^c& nperp(ix^d,^c)=zero\}
5667 else
5668 ^c&nperp(ix^d,^c)=gradt_perp(^c)/maxm\
5669 gradtperp_mag(ix^d)=dsqrt(^c&nperp(ix^d,^c)**2+)
5670 ^c&nperp(ix^d,^c)=nperp(ix^d,^c)/gradtperp_mag(ix^d)\
5671 gradtperp_mag(ix^d)=gradtperp_mag(ix^d)*maxm
5672 end if
5673 end do
5674 end do
5675 }
5676 {^ifthreed
5677 do ix3=ixomin3,ixomax3
5678 do ix2=ixomin2,ixomax2
5679 do ix1=ixomin1,ixomax1
5680 bunitvec=b_unit_vector(bvec(ix^d,1:ndir))
5681
5682 if(slab_uniform) then
5683 gradt(ix^d,1)=((8.d0*(te(ix1+1,ix2,ix3)-te(ix1-1,ix2,ix3))-te(ix1+2,ix2,ix3)+te(ix1-2,ix2,ix3))/12.d0)/block%ds(ix^d,1)
5684 gradt(ix^d,2)=((8.d0*(te(ix1,ix2+1,ix3)-te(ix1,ix2-1,ix3))-te(ix1,ix2+2,ix3)+te(ix1,ix2-2,ix3))/12.d0)/block%ds(ix^d,2)
5685 gradt(ix^d,3)=((8.d0*(te(ix1,ix2,ix3+1)-te(ix1,ix2,ix3-1))-te(ix1,ix2,ix3+2)+te(ix1,ix2,ix3-2))/12.d0)/block%ds(ix^d,3)
5686 end if
5687
5688 bgradt(ix^d)=(^c&bunitvec(^c)*gradt(ix^d,^c)+)
5689 {^c& gradt_perp(^c)=gradt(ix^d,^c)-bgradt(ix^d)*bunitvec(^c)\}
5690 maxm=maxval(abs(gradt_perp))
5691 if(maxm==0.d0) then
5692 gradtperp_mag(ix^d)=zero
5693 {^c& nperp(ix^d,^c)=zero\}
5694 else
5695 ^c&nperp(ix^d,^c)=gradt_perp(^c)/maxm\
5696 gradtperp_mag(ix^d)=dsqrt(^c&nperp(ix^d,^c)**2+)
5697 ^c&nperp(ix^d,^c)=nperp(ix^d,^c)/gradtperp_mag(ix^d)\
5698 gradtperp_mag(ix^d)=gradtperp_mag(ix^d)*maxm
5699 end if
5700 end do
5701 end do
5702 end do
5703 }
5704 end subroutine mhd_get_hyperbolic_tc_geometry
5705
5706 subroutine add_source_hyperbolic_tc(qdt,ixI^L,ixO^L,wCT,w,x,wCTprim)
5708 use mod_geometry, only: gradient
5709 integer, intent(in) :: ixi^l,ixo^l
5710 double precision, intent(in) :: qdt
5711 double precision, dimension(ixI^S,1:ndim), intent(in) :: x
5712 double precision, dimension(ixI^S,1:nw), intent(in) :: wct,wctprim
5713 double precision, dimension(ixI^S,1:nw), intent(inout) :: w
5714
5715 double precision, dimension(ixI^S) :: r,te,rho_loc,pth_loc,cs2_loc
5716 double precision, dimension(ixI^S) :: ne_loc,nh_dummy
5717 double precision, dimension(ixI^S,1:ndir) :: bvec
5718 double precision, dimension(ixI^S) :: bgradt, gradtperp_mag
5719 double precision, dimension(ixI^S,1:ndir) :: nperp
5720 double precision, dimension(ixI^S) :: gradt_geom
5721 double precision, parameter :: xe_prefac_cgs = 4.753567596681522d6
5722 double precision :: kappa_t5,kappa_t5_perp,kappa_t5_perp_eff
5723 double precision :: kappa_t7,f_sat,kappat5_bgradt,kappat5_gradtperp,tau,b2,fb,gradt1
5724 double precision :: bunit_tc(ndir)
5725 double precision :: qclass_diss
5726 double precision :: bmag_loc,tloc,tcond,nloc_code,cchi,chi
5727 double precision :: cmax(ndim),c2,cfast2,avmincs2(ndim),inv_rho
5728 logical :: use_perp_source
5729 integer :: ix^d,idir
5730
5731 ! xe_prefac_cgs expects B [G], T [K], and ne [cm^-3]. Convert the
5732 ! normalisation units explicitly in SI mode, while retaining code-unit
5733 ! state variables in the cell loop below.
5734 cchi=zero
5735 if(htc_perp_mode_id==htc_perp_electron) then
5736 if(si_unit) then
5737 cchi = (xe_prefac_cgs/mhd_hyperbolic_tc_coulomb_log) * &
5738 (1.d4*unit_magneticfield)*unit_temperature**1.5d0 / &
5739 (1.d-6*unit_numberdensity)
5740 else
5741 cchi = (xe_prefac_cgs/mhd_hyperbolic_tc_coulomb_log) * &
5743 end if
5744 end if
5745 if (eos%eos_type == 'LTE' .or. eos%eos_type == 'PI') then
5746 te(ixi^s) = wct(ixi^s, te_)
5747 else
5748 call eos%get_Rfactor(wct,x,ixi^l,ixi^l,r)
5749 {do ix^db=iximin^db,iximax^db\}
5750 if(has_equi_rho_and_p) then
5751 rho_loc(ix^d)=wctprim(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0)
5752 pth_loc(ix^d)=wctprim(ix^d,p_)+block%equi_vars(ix^d,equi_pe0_,0)
5753 else
5754 rho_loc(ix^d)=wctprim(ix^d,rho_)
5755 pth_loc(ix^d)=wctprim(ix^d,p_)
5756 end if
5757 te(ix^d)=pth_loc(ix^d)/(r(ix^d)*rho_loc(ix^d))
5758 {end do\}
5759 end if
5760 ! sound speed from the EoS (table Gamma_1 under LTE); the equi split keeps gamma p/rho
5761 if(has_equi_rho_and_p) then
5762 cs2_loc(ixi^s)=eos%gamma*pth_loc(ixi^s)/rho_loc(ixi^s)
5763 else
5764 call eos%get_csound2(wctprim,x,ixi^l,ixi^l,cs2_loc)
5765 end if
5766 ! Electron number density in code units. Under the FI/PI normalisation,
5767 ! rho_code equals nH_code. FI uses the composition-dependent fully ionised
5768 ! electron count. PI obtains the local electron count from its R factor:
5769 ! R*(2+3 A_He) = (n_nuclei+n_e)/n_H. LTE stores ne explicitly.
5770 if(htc_perp_mode_id==htc_perp_electron) then
5771 if(eos%eos_type=='LTE') then
5772 call eos%get_ne_nH(ixi^l,ixi^l,wct, x,ne_loc,nh_dummy)
5773 else if(eos%eos_type=='PI') then
5774 ne_loc(ixi^s)=rho_loc(ixi^s)*max(r(ixi^s)*(2.d0+3.d0*eos%He_abundance) &
5775 -(1.d0+eos%He_abundance),smalldouble)
5776 else
5777 ne_loc(ixi^s)=rho_loc(ixi^s)*(1.d0+2.d0*eos%He_abundance)
5778 end if
5779 end if
5780 use_perp_source = mhd_hyperbolic_tc_use_perp
5781 if(b0field) then
5782 {do ix^db=ixomin^db,ixomax^db\}
5783 {^c& bvec(ix^d,^c)=wct(ix^d,mag(^c))+block%B0(ix^d,^c,0)\}
5784 {end do\}
5785 else
5786 {do ix^db=ixomin^db,ixomax^db\}
5787 {^c& bvec(ix^d,^c)=wct(ix^d,mag(^c))\}
5788 {end do\}
5789 end if
5790 {^nooned
5791 call mhd_get_hyperbolic_tc_geometry(ixi^l,ixo^l,te,bvec,bgradt,gradtperp_mag,nperp)
5792 }
5793 {^ifoned
5794 gradt_geom=zero
5795 if(.not.slab_uniform) then
5796 call gradient(te,ixi^l,ixo^l,1,gradt_geom)
5797 end if
5798 do ix1=ixomin1,ixomax1
5801 kappa_t7=kappa_t5*te(ix1)
5802 else
5803 tcond = te(ix1)
5804 if(mhd_trac) then
5805 tcond = max(tcond, block%wextra(ix1,tcoff_))
5806 end if
5807 kappa_t5=mhd_hyperbolic_tc_kappa*sqrt(tcond**5)
5808 kappa_t7=kappa_t5*tcond
5809 end if
5810 if(slab_uniform) then
5811 gradt1=((8.d0*(te(ix1+1)-te(ix1-1))-te(ix1+2)+te(ix1-2))/12.d0)/block%ds(ix1,1)
5812 else
5813 gradt1=gradt_geom(ix1)
5814 end if
5815 b2=zero
5816 {^c& b2=b2+bvec(ix1,^c)**2\}
5817 bunit_tc=b_unit_vector(bvec(ix1,1:ndir))
5818 bgradt(ix1)=bunit_tc(1)*gradt1
5819 kappat5_bgradt=kappa_t5*bgradt(ix1)
5820 inv_rho=1.d0/rho_loc(ix1)
5821 c2=cs2_loc(ix1)
5822 cfast2 = b2*inv_rho + c2
5823 avmincs2(1) = cfast2**2 - 4.0d0*c2*bvec(ix1,1)**2*inv_rho
5824 cmax(1) = sqrt(half*(cfast2 + sqrt(dabs(avmincs2(1)))))
5825 if(mhd_hyperbolic_tc_sat) then
5826 f_sat=one/(one+dabs(kappat5_bgradt)/(1.5d0*rho_loc(ix^d)*(pth_loc(ix^d)/rho_loc(ix^d))**1.5d0))
5827 tau=max(4.d0*dt, f_sat*kappa_t7*courantpar**2/(pth_loc(ix^d)*eos%inv_gamma_minus_1*cmax(1)**2))
5828 w(ix^d,qpar_)=w(ix^d,qpar_)-qdt*(f_sat*kappat5_bgradt+wct(ix^d,qpar_))/tau
5829 else
5830 w(ix^d,qpar_)=w(ix^d,qpar_)-qdt*(kappat5_bgradt+wct(ix^d,qpar_))/&
5831 max(4.d0*dt, kappa_t7*courantpar**2/(pth_loc(ix^d)*eos%inv_gamma_minus_1*cmax(1)**2))
5832 end if
5833 end do
5834 }
5835 {^iftwod
5836 do ix2=ixomin2,ixomax2
5837 do ix1=ixomin1,ixomax1
5840 kappa_t7=kappa_t5*te(ix^d)
5841 else
5842 tcond=te(ix^d)
5843 if(mhd_trac) then
5844 tcond=max(tcond, block%wextra(ix^d,tcoff_))
5845 end if
5846 kappa_t5=mhd_hyperbolic_tc_kappa*sqrt(tcond**5)
5847 kappa_t7 = kappa_t5*tcond
5848 end if
5849 kappat5_bgradt=kappa_t5*bgradt(ix^d)
5850 b2 = zero
5851 {^c& b2 = b2 + bvec(ix^d,^c)**2\}
5852 if(use_perp_source) then
5853 select case(htc_perp_mode_id)
5854 case(htc_perp_fixed)
5855 kappa_t5_perp=mhd_hyperbolic_tc_kappa_perp_factor*kappa_t5
5856 case(htc_perp_weak_field)
5857 if(mhd_hyperbolic_tc_bmin>zero) then
5858 fb=b2/(b2+mhd_hyperbolic_tc_bmin**2)
5859 else
5860 fb=one
5861 end if
5862 kappa_t5_perp_eff=(one-fb)*kappa_t5
5863 kappa_t5_perp=kappa_t5_perp_eff
5864 case(htc_perp_electron)
5865 bmag_loc = dsqrt(b2)
5866 tloc = max(te(ix^d), smalldouble)
5867 nloc_code = max(ne_loc(ix^d), smalldouble)
5868 chi = cchi*bmag_loc*tloc**1.5d0/nloc_code
5869 kappa_t5_perp_eff = kappa_t5/(one+chi**2)
5870 kappa_t5_perp = kappa_t5_perp_eff
5871 case default
5872 kappa_t5_perp=zero
5873 end select
5874 kappat5_gradtperp=kappa_t5_perp*gradtperp_mag(ix^d)
5875 end if
5876 inv_rho=1.d0/rho_loc(ix^d)
5877 c2=cs2_loc(ix^d)
5878 cfast2 = b2*inv_rho + c2
5879 {avmincs2(^d)=cfast2**2-4.0d0*c2*bvec(ix^dd,^d)**2*inv_rho; cmax(^d)=sqrt(half*(cfast2+sqrt(dabs(avmincs2(^d)))))\}
5880 if(mhd_hyperbolic_tc_sat) then
5881 qclass_diss=dabs(kappat5_bgradt)
5882 if(use_perp_source) &
5883 qclass_diss=dsqrt(kappat5_bgradt**2+kappat5_gradtperp**2)
5884 f_sat=one/(one+qclass_diss/(1.5d0*rho_loc(ix^d)*(pth_loc(ix^d)/rho_loc(ix^d))**1.5d0))
5885 tau=max(4.d0*dt, f_sat*kappa_t7*courantpar**2/(pth_loc(ix^d)*eos%inv_gamma_minus_1*maxval(cmax(:))**2))
5886 w(ix^d,qpar_)=w(ix^d,qpar_)-qdt*(f_sat*kappat5_bgradt+wct(ix^d,qpar_))/tau
5887 if(use_perp_source) then
5888 w(ix^d,qperp_)=w(ix^d,qperp_)-qdt*(f_sat*kappat5_gradtperp+wct(ix^d,qperp_))/tau
5889 end if
5890 else
5891 tau=max(4.d0*dt, kappa_t7*courantpar**2/(pth_loc(ix^d)*eos%inv_gamma_minus_1*maxval(cmax(:))**2))
5892 w(ix^d,qpar_)=w(ix^d,qpar_)-qdt*(kappat5_bgradt+wct(ix^d,qpar_))/tau
5893 if(use_perp_source) then
5894 w(ix^d,qperp_)=w(ix^d,qperp_)-qdt*(kappat5_gradtperp+wct(ix^d,qperp_))/tau
5895 end if
5896 end if
5897 end do
5898 end do
5899 }
5900 {^ifthreed
5901 do ix3=ixomin3,ixomax3
5902 do ix2=ixomin2,ixomax2
5903 do ix1=ixomin1,ixomax1
5906 kappa_t7=kappa_t5*te(ix^d)
5907 else
5908 tcond=te(ix^d)
5909 if(mhd_trac) then
5910 tcond=max(tcond, block%wextra(ix^d,tcoff_))
5911 end if
5912 kappa_t5=mhd_hyperbolic_tc_kappa*sqrt(tcond**5)
5913 kappa_t7 = kappa_t5*tcond
5914 end if
5915 kappat5_bgradt=kappa_t5*bgradt(ix^d)
5916 b2 = zero
5917 {^c& b2 = b2 + bvec(ix^d,^c)**2\}
5918 if(use_perp_source) then
5919 select case(htc_perp_mode_id)
5920 case(htc_perp_fixed)
5921 kappa_t5_perp=mhd_hyperbolic_tc_kappa_perp_factor*kappa_t5
5922 case(htc_perp_weak_field)
5923 if(mhd_hyperbolic_tc_bmin>zero) then
5924 fb=b2/(b2+mhd_hyperbolic_tc_bmin**2)
5925 else
5926 fb=one
5927 end if
5928 kappa_t5_perp_eff=(one-fb)*kappa_t5
5929 kappa_t5_perp=kappa_t5_perp_eff
5930 case(htc_perp_electron)
5931 bmag_loc = dsqrt(b2)
5932 tloc = max(te(ix^d), smalldouble)
5933 nloc_code = max(ne_loc(ix^d), smalldouble)
5934 chi = cchi*bmag_loc*tloc**1.5d0/nloc_code
5935 kappa_t5_perp_eff = kappa_t5/(one+chi**2)
5936 kappa_t5_perp = kappa_t5_perp_eff
5937 case default
5938 kappa_t5_perp=zero
5939 end select
5940 kappat5_gradtperp=kappa_t5_perp*gradtperp_mag(ix^d)
5941 end if
5942 inv_rho=1.d0/rho_loc(ix^d)
5943 c2=cs2_loc(ix^d)
5944 cfast2 = b2*inv_rho + c2
5945 {avmincs2(^d)=cfast2**2-4.0d0*c2*bvec(ix^dd,^d)**2*inv_rho; cmax(^d)=sqrt(half*(cfast2+sqrt(dabs(avmincs2(^d)))))\}
5946 if(mhd_hyperbolic_tc_sat) then
5947 qclass_diss=dabs(kappat5_bgradt)
5948 if(use_perp_source) &
5949 qclass_diss=dsqrt(kappat5_bgradt**2+kappat5_gradtperp**2)
5950 f_sat=one/(one+qclass_diss/(1.5d0*rho_loc(ix^d)*(pth_loc(ix^d)/rho_loc(ix^d))**1.5d0))
5951 tau=max(4.d0*dt, f_sat*kappa_t7*courantpar**2/(pth_loc(ix^d)*eos%inv_gamma_minus_1*maxval(cmax(:))**2))
5952 w(ix^d,qpar_)=w(ix^d,qpar_)-qdt*(f_sat*kappat5_bgradt+wct(ix^d,qpar_))/tau
5953 if(use_perp_source) then
5954 w(ix^d,qperp_)=w(ix^d,qperp_)-qdt*(f_sat*kappat5_gradtperp+wct(ix^d,qperp_))/tau
5955 end if
5956 else
5957 tau=max(4.d0*dt, kappa_t7*courantpar**2/(pth_loc(ix^d)*eos%inv_gamma_minus_1*maxval(cmax(:))**2))
5958 w(ix^d,qpar_)=w(ix^d,qpar_)-qdt*(kappat5_bgradt+wct(ix^d,qpar_))/tau
5959 if(use_perp_source) then
5960 w(ix^d,qperp_)=w(ix^d,qperp_)-qdt*(kappat5_gradtperp+wct(ix^d,qperp_))/tau
5961 end if
5962 end if
5963 end do
5964 end do
5965 end do
5966 }
5967 end subroutine add_source_hyperbolic_tc
5968
5969 !> Compute the Lorentz force (JxB) Note: Unused subroutine
5970 !> perhaps useful for post-processing when made public
5971 subroutine get_lorentz_force(ixI^L,ixO^L,w,JxB)
5973 integer, intent(in) :: ixi^l, ixo^l
5974 double precision, intent(in) :: w(ixi^s,1:nw)
5975 double precision, intent(inout) :: jxb(ixi^s,3)
5976 double precision :: a(ixi^s,3), b(ixi^s,3)
5977 ! For ndir=2 only 3rd component of J can exist, ndir=1 is impossible for MHD
5978 double precision :: current(ixi^s,7-2*ndir:3)
5979 integer :: idir, idirmin
5980
5981 b=0.0d0
5982 if(b0field) then
5983 do idir = 1, ndir
5984 b(ixo^s, idir) = w(ixo^s,mag(idir))+block%B0(ixo^s,idir,0)
5985 end do
5986 else
5987 do idir = 1, ndir
5988 b(ixo^s, idir) = w(ixo^s,mag(idir))
5989 end do
5990 end if
5991
5992 ! store J current in a
5993 call get_current(w,ixi^l,ixo^l,idirmin,current)
5994
5995 a=0.0d0
5996 do idir=7-2*ndir,3
5997 a(ixo^s,idir)=current(ixo^s,idir)
5998 end do
5999
6000 call cross_product(ixi^l,ixo^l,a,b,jxb)
6001 end subroutine get_lorentz_force
6002
6003 subroutine mhd_get_rho(w,x,ixI^L,ixO^L,rho)
6005 integer, intent(in) :: ixi^l, ixo^l
6006 double precision, intent(in) :: w(ixi^s,1:nw),x(ixi^s,1:ndim)
6007 double precision, intent(out) :: rho(ixi^s)
6008
6009 if(has_equi_rho_and_p) then
6010 rho(ixo^s) = w(ixo^s,rho_) + block%equi_vars(ixo^s,equi_rho0_,b0i)
6011 else
6012 rho(ixo^s) = w(ixo^s,rho_)
6013 endif
6014
6015 end subroutine mhd_get_rho
6016
6017 !> handle small or negative internal energy
6018 subroutine mhd_handle_small_ei(w, x, ixI^L, ixO^L, ie, subname)
6021 integer, intent(in) :: ixi^l,ixo^l, ie
6022 double precision, intent(inout) :: w(ixi^s,1:nw)
6023 double precision, intent(in) :: x(ixi^s,1:ndim)
6024 character(len=*), intent(in) :: subname
6025
6026 double precision :: rho(ixi^s)
6027 integer :: idir
6028 logical :: flag(ixi^s,1:nw)
6029
6030 flag=.false.
6031 if(has_equi_rho_and_p) then
6032 where(w(ixo^s,ie)+block%equi_vars(ixo^s,equi_pe0_,0)*eos%inv_gamma_minus_1<small_e)&
6033 flag(ixo^s,ie)=.true.
6034 else
6035 where(w(ixo^s,ie)<small_e) flag(ixo^s,ie)=.true.
6036 endif
6037 if(any(flag(ixo^s,ie))) then
6038 select case (small_values_method)
6039 case ("replace")
6040 if(has_equi_rho_and_p) then
6041 where(flag(ixo^s,ie)) w(ixo^s,ie)=small_e - &
6042 block%equi_vars(ixo^s,equi_pe0_,0)*eos%inv_gamma_minus_1
6043 else
6044 where(flag(ixo^s,ie)) w(ixo^s,ie)=small_e
6045 endif
6046 case ("average")
6047 call small_values_average(ixi^l, ixo^l, w, x, flag, ie)
6048 case default
6049 ! small values error shows primitive variables
6050 w(ixo^s,e_)=w(ixo^s,e_)*eos%gamma_minus_1
6051 call mhd_get_rho(w,x,ixi^l,ixo^l,rho)
6052 do idir = 1, ndir
6053 w(ixo^s, mom(idir)) = w(ixo^s, mom(idir))/rho(ixo^s)
6054 end do
6055 call small_values_error(w, x, ixi^l, ixo^l, flag, subname)
6056 end select
6057 end if
6058
6059 end subroutine mhd_handle_small_ei
6060
6061
6062 !> Source terms after split off time-independent magnetic field
6063 subroutine add_source_b0split(qdt,dtfactor,ixI^L,ixO^L,wCT,w,x,wCTprim)
6065
6066 integer, intent(in) :: ixi^l, ixo^l
6067 double precision, intent(in) :: qdt, dtfactor,wct(ixi^s,1:nw), x(ixi^s,1:ndim)
6068 double precision, intent(in) :: wctprim(ixi^s,1:nw)
6069 double precision, intent(inout) :: w(ixi^s,1:nw)
6070
6071 double precision :: a(ixi^s,3), b(ixi^s,3), axb(ixi^s,3)
6072 integer :: idir
6073
6074 a=0.d0
6075 b=0.d0
6076 ! for force-free field J0xB0 =0
6077 if((.not.b0field_forcefree).and.(.not.has_equi_rho_and_p)) then
6078 ! store B0 magnetic field in b
6079 b(ixo^s,1:ndir)=block%B0(ixo^s,1:ndir,0)
6080
6081 ! store J0 current in a
6082 do idir=7-2*ndir,3
6083 a(ixo^s,idir)=block%J0(ixo^s,idir)
6084 end do
6085 call cross_product(ixi^l,ixo^l,a,b,axb)
6086 if(local_timestep) then
6087 do idir=1,3
6088 axb(ixo^s,idir)=axb(ixo^s,idir)*block%dt(ixo^s)*dtfactor
6089 enddo
6090 else
6091 axb(ixo^s,:)=axb(ixo^s,:)*qdt
6092 endif
6093 ! add J0xB0 source term in momentum equations
6094 w(ixo^s,mom(1:ndir))=w(ixo^s,mom(1:ndir))+axb(ixo^s,1:ndir)
6095 end if
6096
6097 if(total_energy) then
6098 a=0.d0
6099 ! for free-free field -(vxB0) dot J0 =0
6100 b(ixo^s,:)=wctprim(ixo^s,mag(:))
6101 ! store full magnetic field B0+B1 in b
6102 if((.not.b0field_forcefree).and.(.not.has_equi_rho_and_p)) b(ixo^s,:)=b(ixo^s,:)+block%B0(ixo^s,:,0)
6103 ! store velocity in a
6104 a(ixi^s,1:ndir)=wctprim(ixi^s,mom(1:ndir))
6105 ! -E = a x b
6106 call cross_product(ixi^l,ixo^l,a,b,axb)
6107 if(local_timestep) then
6108 do idir=1,3
6109 axb(ixo^s,idir)=axb(ixo^s,idir)*block%dt(ixo^s)*dtfactor
6110 enddo
6111 else
6112 axb(ixo^s,:)=axb(ixo^s,:)*qdt
6113 endif
6114 ! add -(vxB) dot J0 source term in energy equation
6115 ! where it is adding -J0 dot (vxB_1) when appropriate
6116 do idir=7-2*ndir,3
6117 w(ixo^s,e_)=w(ixo^s,e_)-axb(ixo^s,idir)*block%J0(ixo^s,idir)
6118 end do
6119 if(mhd_hall) then
6120 ! store hall velocity in a, only partial current is needed
6121 call mhd_getv_hall(wct,x,ixi^l,ixo^l,a,.true.)
6122 ! -E = a x b
6123 call cross_product(ixi^l,ixo^l,a,b,axb)
6124 if(local_timestep) then
6125 do idir=1,3
6126 axb(ixo^s,idir)=axb(ixo^s,idir)*block%dt(ixo^s)*dtfactor
6127 enddo
6128 else
6129 axb(ixo^s,:)=axb(ixo^s,:)*qdt
6130 endif
6131 ! add -(vxB) dot J0 source term in energy equation
6132 do idir=7-2*ndir,3
6133 w(ixo^s,e_)=w(ixo^s,e_)-axb(ixo^s,idir)*block%J0(ixo^s,idir)
6134 end do
6135 endif
6136 if(mhd_ambipolar_sts) then
6137 ! in STS variant of ambipolar, we added for split B the term div(B_1xE_ambi)
6138 ! hence needs to add J_0 dot E_ambi
6139 ! to get finally the term etaA (J_perpB)^/B^2-B_1 dot (curl Eambi)
6140 !reuse axb
6141 call mhd_get_jxbxb(wct,x,ixi^l,ixo^l,axb)
6142 ! source J0 * E
6143 do idir=sdim,3
6144 !set electric field in jxbxb: E=nuA * jxbxb, where nuA=-etaA/rho^2
6145 call multiplyambicoef(ixi^l,ixo^l,axb(ixi^s,idir),wct,x)
6146 w(ixo^s,e_)=w(ixo^s,e_)+qdt*axb(ixo^s,idir)*block%J0(ixo^s,idir)
6147 enddo
6148 endif
6149 end if
6150
6151
6152 if (fix_small_values) call mhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_B0')
6153
6154 end subroutine add_source_b0split
6155
6156 !> Source terms for semirelativistic MHD Gombosi 2002 JCP 177, 176
6157 subroutine add_source_semirelativistic(qdt,ixI^L,ixO^L,wCT,w,x,wCTprim)
6159 use mod_geometry
6160
6161 integer, intent(in) :: ixi^l, ixo^l
6162 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), x(ixi^s,1:ndim)
6163 double precision, intent(inout) :: w(ixi^s,1:nw)
6164 double precision, intent(in), optional :: wctprim(ixi^s,1:nw)
6165
6166 double precision :: e(ixi^s,1:3),curle(ixi^s,1:3),dive(ixi^s),btot(1:3)
6167 integer :: idir, idirmin, ix^d
6168
6169 ! if ndir<3 the source is zero
6170 {^ifthreec
6171 {do ix^db=iximin^db,iximax^db\}
6172 ! E=Bxv with the total field, B taken from wCT like the velocity in wCTprim
6173 ^c&btot(^c)=wct(ix^d,b^c_)\
6174 if(b0field) then
6175 ^c&btot(^c)=btot(^c)+block%B0(ix^d,^c,0)\
6176 end if
6177 e(ix^d,1)=btot(2)*wctprim(ix^d,m3_)-btot(3)*wctprim(ix^d,m2_)
6178 e(ix^d,2)=btot(3)*wctprim(ix^d,m1_)-btot(1)*wctprim(ix^d,m3_)
6179 e(ix^d,3)=btot(1)*wctprim(ix^d,m2_)-btot(2)*wctprim(ix^d,m1_)
6180 {end do\}
6181 call divvector(e,ixi^l,ixo^l,dive)
6182 if(mhd_semirelati_curl_e) then
6183 call curlvector(e,ixi^l,ixo^l,curle,idirmin,1,3)
6184 else
6185 ! eq (28): E divE only, the steady-state limit of eq (26)
6186 curle=zero
6187 end if
6188 ! add source term in momentum equations (1/c0^2-1/c^2)(E divE - E x curlE)
6189 ! equation (26) and (27)
6190 {do ix^db=ixomin^db,ixomax^db\}
6191 w(ix^d,m1_)=w(ix^d,m1_)+qdt*(inv_squared_c0-inv_squared_c)*&
6192 (e(ix^d,1)*dive(ix^d)-e(ix^d,2)*curle(ix^d,3)+e(ix^d,3)*curle(ix^d,2))
6193 w(ix^d,m2_)=w(ix^d,m2_)+qdt*(inv_squared_c0-inv_squared_c)*&
6194 (e(ix^d,2)*dive(ix^d)-e(ix^d,3)*curle(ix^d,1)+e(ix^d,1)*curle(ix^d,3))
6195 w(ix^d,m3_)=w(ix^d,m3_)+qdt*(inv_squared_c0-inv_squared_c)*&
6196 (e(ix^d,3)*dive(ix^d)-e(ix^d,1)*curle(ix^d,2)+e(ix^d,2)*curle(ix^d,1) )
6197 {end do\}
6198 }
6199
6200 end subroutine add_source_semirelativistic
6201
6202 !> Source terms for internal energy version of MHD
6203 subroutine add_source_internal_e(qdt,ixI^L,ixO^L,wCT,w,x,wCTprim)
6205 use mod_geometry
6206
6207 integer, intent(in) :: ixi^l, ixo^l
6208 double precision, intent(in) :: qdt
6209 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
6210 double precision, intent(inout) :: w(ixi^s,1:nw)
6211 double precision, intent(in) :: wctprim(ixi^s,1:nw)
6212
6213 double precision :: divv(ixi^s), tmp
6214 integer :: ix^d
6215
6216 if(slab_uniform) then
6217 if(nghostcells .gt. 2) then
6218 call divvector(wctprim(ixi^s,mom(:)),ixi^l,ixo^l,divv,3)
6219 else
6220 call divvector(wctprim(ixi^s,mom(:)),ixi^l,ixo^l,divv,2)
6221 end if
6222 else
6223 call divvector(wctprim(ixi^s,mom(:)),ixi^l,ixo^l,divv)
6224 end if
6225 {do ix^db=ixomin^db,ixomax^db\}
6226 tmp=w(ix^d,e_)
6227 w(ix^d,e_)=w(ix^d,e_)-qdt*wctprim(ix^d,p_)*divv(ix^d)
6228 if(w(ix^d,e_)<small_e) then
6229 w(ix^d,e_)=tmp
6230 end if
6231 {end do\}
6232 if(mhd_ambipolar_sts)then
6233 call add_source_ambipolar_internal_energy(qdt,ixi^l,ixo^l,wct,w,x)
6234 end if
6235
6236 if(fix_small_values) then
6237 call mhd_handle_small_ei(w,x,ixi^l,ixo^l,e_,'add_source_internal_e')
6238 end if
6239 end subroutine add_source_internal_e
6240
6241 !> Source terms for hydrodynamic energy version of MHD
6242 subroutine add_source_hydrodynamic_e(qdt,ixI^L,ixO^L,wCT,w,x,wCTprim)
6244 use mod_geometry
6245 use mod_usr_methods, only: usr_gravity
6246
6247 integer, intent(in) :: ixi^l, ixo^l
6248 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), x(ixi^s,1:ndim)
6249 double precision, intent(inout) :: w(ixi^s,1:nw)
6250 double precision, intent(in), optional :: wctprim(ixi^s,1:nw)
6251
6252 double precision :: b(ixi^s,3), j(ixi^s,3), jxb(ixi^s,3)
6253 double precision :: current(ixi^s,7-2*ndir:3)
6254 double precision :: bu(ixo^s,1:ndir), tmp(ixo^s), b2(ixo^s)
6255 double precision :: gravity_field(ixi^s,1:ndir), vaoc
6256 integer :: idir, idirmin, idims, ix^d
6257
6258 {^nothreed
6259 b=0.0d0
6260 do idir = 1, ndir
6261 b(ixo^s, idir) = wct(ixo^s,mag(idir))
6262 end do
6263
6264 if(slab_uniform)then
6265 ! get current in fourth order accuracy in Cartesian
6266 call curlvector(wct(ixi^s,mag(1:ndir)),ixi^l,ixo^l,current,idirmin,7-2*ndir,ndir,.true.)
6267 else
6268 call get_current(wct,ixi^l,ixo^l,idirmin,current)
6269 endif
6270
6271 j=0.0d0
6272 do idir=7-2*ndir,3
6273 j(ixo^s,idir)=current(ixo^s,idir)
6274 end do
6275
6276 ! get Lorentz force JxB
6277 call cross_product(ixi^l,ixo^l,j,b,jxb)
6278 }
6279 {^ifthreed
6280 if(slab_uniform)then
6281 ! get current in fourth order accuracy in Cartesian
6282 call curlvector(wct(ixi^s,mag(1:ndir)),ixi^l,ixo^l,current,idirmin,1,ndir,.true.)
6283 else
6284 call get_current(wct,ixi^l,ixo^l,idirmin,current)
6285 endif
6286 ! get Lorentz force JxB
6287 call cross_product(ixi^l,ixo^l,current,wct(ixi^s,mag(1:ndir)),jxb)
6288 }
6289
6290 ! mhd_semirelativistic does not combine with mhd_hydrodynamic_e
6291 !!if(mhd_semirelativistic) then
6292 !! ! (v . nabla) v
6293 !! do idir=1,ndir
6294 !! do idims=1,ndim
6295 !! call gradient(wCTprim(ixI^S,mom(idir)),ixI^L,ixO^L,idims,J(ixI^S,idims))
6296 !! end do
6297 !! B(ixO^S,idir)=sum(wCTprim(ixO^S,mom(1:ndir))*J(ixO^S,1:ndir),dim=ndim+1)
6298 !! end do
6299 !! ! nabla p
6300 !! do idir=1,ndir
6301 !! call gradient(wCTprim(ixI^S,p_),ixI^L,ixO^L,idir,J(ixI^S,idir))
6302 !! end do
6303 !! if(mhd_gravity) then
6304 !! gravity_field=0.d0
6305 !! call usr_gravity(ixI^L,ixO^L,wCT,x,gravity_field(ixI^S,1:ndim))
6306 !! do idir=1,ndir
6307 !! B(ixO^S,idir)=wCT(ixO^S,rho_)*(B(ixO^S,idir)-gravity_field(ixO^S,idir))+J(ixO^S,idir)-JxB(ixO^S,idir)
6308 !! end do
6309 !! else
6310 !! do idir=1,ndir
6311 !! B(ixO^S,idir)=wCT(ixO^S,rho_)*B(ixO^S,idir)+J(ixO^S,idir)-JxB(ixO^S,idir)
6312 !! end do
6313 !! end if
6314 !! b2(ixO^S)=sum(wCT(ixO^S,mag(:))**2,dim=ndim+1)
6315 !! tmp(ixO^S)=sqrt(b2(ixO^S))
6316 !! where(tmp(ixO^S)>smalldouble)
6317 !! tmp(ixO^S)=1.d0/tmp(ixO^S)
6318 !! else where
6319 !! tmp(ixO^S)=0.d0
6320 !! end where
6321 !! ! unit vector of magnetic field
6322 !! do idir=1,ndir
6323 !! bu(ixO^S,idir)=wCT(ixO^S,mag(idir))*tmp(ixO^S)
6324 !! end do
6325 !! !b2(ixO^S)=b2(ixO^S)/w(ixO^S,rho_)*inv_squared_c
6326 !! !b2(ixO^S)=b2(ixO^S)/(1.d0+b2(ixO^S))
6327 !! {do ix^DB=ixOmin^DB,ixOmax^DB\}
6328 !! ! Va^2/c^2
6329 !! Vaoc=b2(ix^D)/w(ix^D,rho_)*inv_squared_c
6330 !! ! Va^2/c^2 / (1+Va^2/c^2)
6331 !! b2(ix^D)=Vaoc/(1.d0+Vaoc)
6332 !! {end do\}
6333 !! ! bu . F
6334 !! tmp(ixO^S)=sum(bu(ixO^S,1:ndir)*B(ixO^S,1:ndir),dim=ndim+1)
6335 !! ! Rempel 2017 ApJ 834, 10 equation (54)
6336 !! do idir=1,ndir
6337 !! J(ixO^S,idir)=b2(ixO^S)*(B(ixO^S,idir)-bu(ixO^S,idir)*tmp(ixO^S))
6338 !! end do
6339 !! !! Rempel 2017 ApJ 834, 10 equation (29) add SR force at momentum equation
6340 !! do idir=1,ndir
6341 !! w(ixO^S,mom(idir))=w(ixO^S,mom(idir))+qdt*J(ixO^S,idir)
6342 !! end do
6343 !! ! Rempel 2017 ApJ 834, 10 equation (30) add work of Lorentz force and SR force
6344 !! w(ixO^S,e_)=w(ixO^S,e_)+qdt*sum(wCTprim(ixO^S,mom(1:ndir))*&
6345 !! (JxB(ixO^S,1:ndir)+J(ixO^S,1:ndir)),dim=ndim+1)
6346 !!else
6347 ! add work of Lorentz force
6348 w(ixo^s,e_)=w(ixo^s,e_)+qdt*sum(wctprim(ixo^s,mom(1:ndir))*jxb(ixo^s,1:ndir),dim=ndim+1)
6349 !!end if
6350
6351 if(mhd_ambipolar_sts)then
6352 call add_source_ambipolar_internal_energy(qdt,ixi^l,ixo^l,wct,w,x)
6353 end if
6354
6355 if (fix_small_values) call mhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_hydrodynamic_e')
6356
6357 end subroutine add_source_hydrodynamic_e
6358
6359 !> Add resistive source to w within ixO Uses 3 point stencil (1 neighbour) in
6360 !> each direction, non-conservative. Uses the generic Laplacian
6361 !> with fourth order central difference (on uniform cartesian) for the laplacian. Then the
6362 !> stencil is 5 (2 neighbours). NOTE: Unused subroutine!
6363 subroutine add_source_res1(qdt,ixI^L,ixO^L,wCT,w,x)
6365 use mod_usr_methods
6366 use mod_geometry
6367
6368 integer, intent(in) :: ixi^l, ixo^l
6369 double precision, intent(in) :: qdt
6370 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
6371 double precision, intent(inout) :: w(ixi^s,1:nw)
6372
6373 integer :: ixa^l,idir,jdir,kdir,idirmin,idim
6374 double precision :: tmp(ixi^s),tmp2(ixi^s)
6375
6376 ! For ndir=2 only 3rd component of J can exist, ndir=1 is impossible for MHD
6377 double precision :: current(ixi^s,7-2*ndir:3),eta(ixi^s)
6378 double precision :: gradeta(ixi^s,1:ndim), bf(ixi^s,1:ndir)
6379 double precision :: lapl_vec(ixi^s,1:ndir)
6380
6381 ! Calculating resistive sources involves one extra layer
6382 ! asking here for two, so Cartesian works with 4th order CD
6383 ixa^l=ixo^l^ladd2;
6384
6385 if (iximin^d>ixamin^d.or.iximax^d<ixamax^d|.or.) &
6386 call mpistop("Error in add_source_res1: Non-conforming input limits")
6387
6388 ! Calculate current density and idirmin
6389 call get_current(wct,ixi^l,ixo^l,idirmin,current)
6390
6391 if (mhd_eta>zero)then
6392 eta(ixa^s)=mhd_eta
6393 gradeta(ixo^s,1:ndim)=zero
6394 else
6395 call usr_special_resistivity(wct,ixi^l,ixa^l,idirmin,x,current,eta)
6396 do idim=1,ndim
6397 call gradient(eta,ixi^l,ixo^l,idim,tmp)
6398 gradeta(ixo^s,idim)=tmp(ixo^s)
6399 end do
6400 end if
6401
6402 if(b0field) then
6403 bf(ixi^s,1:ndir)=wct(ixi^s,mag(1:ndir))+block%B0(ixi^s,1:ndir,0)
6404 else
6405 bf(ixi^s,1:ndir)=wct(ixi^s,mag(1:ndir))
6406 end if
6407
6408 call laplacian_of_vector(bf,ixi^l,ixo^l,lapl_vec)
6409
6410 do idir=1,ndir
6411 ! Multiply by eta to store eta*Laplace B_idir
6412 tmp(ixo^s)=lapl_vec(ixo^s,idir)*eta(ixo^s)
6413
6414 ! Subtract grad(eta) x J = eps_ijk d_j eta J_k if eta is non-constant
6415 if (mhd_eta<zero)then
6416 do jdir=1,ndim; do kdir=idirmin,3
6417 if (lvc(idir,jdir,kdir)/=0)then
6418 if (lvc(idir,jdir,kdir)==1)then
6419 tmp(ixo^s)=tmp(ixo^s)-gradeta(ixo^s,jdir)*current(ixo^s,kdir)
6420 else
6421 tmp(ixo^s)=tmp(ixo^s)+gradeta(ixo^s,jdir)*current(ixo^s,kdir)
6422 end if
6423 end if
6424 end do; end do
6425 end if
6426
6427 ! Add sources related to eta*laplB-grad(eta) x J to B and e
6428 w(ixo^s,mag(idir))=w(ixo^s,mag(idir))+qdt*tmp(ixo^s)
6429 if(total_energy) then
6430 w(ixo^s,e_)=w(ixo^s,e_)+qdt*tmp(ixo^s)*bf(ixo^s,idir)
6431 end if
6432 end do ! idir
6433
6434 if(mhd_energy) then
6435 ! de/dt+=eta*J**2
6436 w(ixo^s,e_)=w(ixo^s,e_)+qdt*eta(ixo^s)*sum(current(ixo^s,:)**2,dim=ndim+1)
6437 end if
6438
6439 if (fix_small_values) call mhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_res1')
6440
6441 end subroutine add_source_res1
6442
6443 !> Add resistive source to w within ixO in an explicit fashion
6444 !> Uses 5 point stencil (2 neighbours) in each direction, conservative
6445 subroutine add_source_res_exp(qdt,ixI^L,ixO^L,wCT,w,x)
6447 use mod_usr_methods
6448 use mod_geometry
6449
6450 integer, intent(in) :: ixi^l, ixo^l
6451 double precision, intent(in) :: qdt
6452 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
6453 double precision, intent(inout) :: w(ixi^s,1:nw)
6454
6455 ! For ndir=2 only 3rd component of J can exist, ndir=1 is impossible for MHD
6456 double precision :: current(ixi^s,7-2*ndir:3),eta(ixi^s),curlj(ixi^s,1:3)
6457 double precision :: tmpvec(ixi^s,1:3),tmp(ixo^s)
6458 integer :: ixa^l,idir,idirmin,idirmin1
6459
6460 ixa^l=ixo^l^ladd2;
6461
6462 if (iximin^d>ixamin^d.or.iximax^d<ixamax^d|.or.) &
6463 call mpistop("Error in add_source_res_exp: Non-conforming input limits")
6464
6465 ixa^l=ixo^l^ladd1;
6466 ! Calculate current density within ixL: J=curl B, thus J_i=eps_ijk*d_j B_k
6467 ! Determine exact value of idirmin while doing the loop.
6468 call get_current(wct,ixi^l,ixa^l,idirmin,current)
6469
6470 tmpvec=zero
6471 if(mhd_eta>zero)then
6472 do idir=idirmin,3
6473 tmpvec(ixa^s,idir)=current(ixa^s,idir)*mhd_eta
6474 end do
6475 else
6476 call usr_special_resistivity(wct,ixi^l,ixa^l,idirmin,x,current,eta)
6477 do idir=idirmin,3
6478 tmpvec(ixa^s,idir)=current(ixa^s,idir)*eta(ixa^s)
6479 end do
6480 end if
6481
6482 ! dB/dt= -curl(J*eta), thus B_i=B_i-eps_ijk d_j Jeta_k
6483 call curlvector(tmpvec,ixi^l,ixo^l,curlj,idirmin1,1,3)
6484 if(stagger_grid) then
6485 if(ndim==2.and.ndir==3) then
6486 ! if 2.5D
6487 w(ixo^s,mag(ndir)) = w(ixo^s,mag(ndir))-qdt*curlj(ixo^s,ndir)
6488 end if
6489 else
6490 w(ixo^s,mag(1:ndir)) = w(ixo^s,mag(1:ndir))-qdt*curlj(ixo^s,1:ndir)
6491 end if
6492
6493 if(mhd_energy) then
6494 if(mhd_eta>zero)then
6495 tmp(ixo^s)=qdt*mhd_eta*sum(current(ixo^s,:)**2,dim=ndim+1)
6496 else
6497 tmp(ixo^s)=qdt*eta(ixo^s)*sum(current(ixo^s,:)**2,dim=ndim+1)
6498 end if
6499 if(total_energy) then
6500 ! de/dt= +div(B x Jeta) = eta J^2 - B dot curl(eta J)
6501 ! de1/dt= eta J^2 - B1 dot curl(eta J)
6502 w(ixo^s,e_)=w(ixo^s,e_)+tmp(ixo^s)-&
6503 qdt*sum(wct(ixo^s,mag(1:ndir))*curlj(ixo^s,1:ndir),dim=ndim+1)
6504 else
6505 ! add eta*J**2 source term in the internal energy equation
6506 w(ixo^s,e_)=w(ixo^s,e_)+tmp(ixo^s)
6507 end if
6508 end if
6509
6510 if (fix_small_values) call mhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_res_exp')
6511 end subroutine add_source_res_exp
6512
6513
6514 !> Add ambipolar source to w within ixO in an explicit fashion
6515 !> Uses 5 point stencil (2 neighbours) in each direction, conservative
6516 subroutine add_source_ambi_exp(qdt,ixI^L,ixO^L,wCT,w,x)
6518 use mod_usr_methods
6519 use mod_geometry
6520
6521 integer, intent(in) :: ixi^l, ixo^l
6522 double precision, intent(in) :: qdt
6523 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
6524 double precision, intent(inout) :: w(ixi^s,1:nw)
6525
6526 double precision :: current(ixi^s,1:3),curlj(ixi^s,1:3)
6527 double precision :: tmpvec(ixi^s,1:3),tmp(ixi^s),btot2(ixi^s)
6528 integer :: ixa^l,idir,idirmin1
6529
6530 ixa^l=ixo^l^ladd2;
6531
6532 if (iximin^d>ixamin^d.or.iximax^d<ixamax^d|.or.) &
6533 call mpistop("Error in add_source_ambi_exp: Non-conforming input limits")
6534
6535 ixa^l=ixo^l^ladd1;
6536 ! Calculate -J_perpB = (JxB)xB
6537 call mhd_get_jxbxb(wct,x,ixi^l,ixa^l,current)
6538
6539 tmpvec=current
6540 do idir=1,3
6541 !set electric field in tmpvec : E=nuA * jxbxb, where nuA=-etaA/rho^2
6542 !tmpvec(ixA^S,i) = -(mhd_eta_ambi/w(ixA^S, rho_)**2) * jxbxb(ixA^S,i)
6543 call multiplyambicoef(ixi^l,ixa^l,tmpvec(ixi^s,idir),wct,x)
6544 end do
6545
6546 ! dB/dt= -curl(J_perpB*etaA), thus B_i=B_i-eps_ijk d_j Jeta_k
6547 call curlvector(tmpvec,ixi^l,ixo^l,curlj,idirmin1,1,3)
6548 if(stagger_grid) then
6549 if(ndim==2.and.ndir==3) then
6550 ! if 2.5D
6551 w(ixo^s,mag(ndir)) = w(ixo^s,mag(ndir))-qdt*curlj(ixo^s,ndir)
6552 end if
6553 else
6554 w(ixo^s,mag(1:ndir)) = w(ixo^s,mag(1:ndir))-qdt*curlj(ixo^s,1:ndir)
6555 end if
6556
6557 if(mhd_energy) then
6558 ! compute ambipolar heating term: nuA* J_perpB^2/ B^2
6559 ! avoiding nulls here
6560 btot2(ixa^s)=mhd_mag_en_all(wct,ixi^l,ixa^l)
6561 where (btot2(ixa^s)>smalldouble )
6562 tmp(ixa^s) = sum(current(ixa^s,1:3)**2,dim=ndim+1) / btot2(ixa^s)
6563 elsewhere
6564 tmp(ixa^s) = zero
6565 endwhere
6566 ! multiply with nuA where nuA=-etaA/rho^2
6567 call multiplyambicoef(ixi^l,ixa^l,tmp,wct,x)
6568 ! compensate - sign and add timestep
6569 tmp(ixo^s)=-qdt*tmp(ixo^s)
6570 if(total_energy) then
6571 ! de/dt= +div(B x E_ambi) = eta J^2 - B dot curl(eta J)
6572 ! de1/dt= eta J^2 - B1 dot curl(eta J)
6573 w(ixo^s,e_)=w(ixo^s,e_)+tmp(ixo^s)-&
6574 qdt*sum(wct(ixo^s,mag(1:ndir))*curlj(ixo^s,1:ndir),dim=ndim+1)
6575 else
6576 ! add eta*J**2 source term in the internal or hydrodynamic energy equation
6577 w(ixo^s,e_)=w(ixo^s,e_)+tmp(ixo^s)
6578 end if
6579 end if
6580
6581 if (fix_small_values) call mhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_ambi_exp')
6582 end subroutine add_source_ambi_exp
6583
6584 !> Add Hyper-resistive source to w within ixO
6585 !> Uses 9 point stencil (4 neighbours) in each direction.
6586 subroutine add_source_hyperres(qdt,ixI^L,ixO^L,wCT,w,x)
6588 use mod_geometry
6589
6590 integer, intent(in) :: ixi^l, ixo^l
6591 double precision, intent(in) :: qdt
6592 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
6593 double precision, intent(inout) :: w(ixi^s,1:nw)
6594 !.. local ..
6595 double precision :: current(ixi^s,7-2*ndir:3)
6596 double precision :: tmpvec(ixi^s,1:3),tmpvec2(ixi^s,1:3),tmp(ixi^s),ehyper(ixi^s,1:3)
6597 integer :: ixa^l,idir,jdir,kdir,idirmin,idirmin1
6598
6599 ixa^l=ixo^l^ladd3;
6600 if (iximin^d>ixamin^d.or.iximax^d<ixamax^d|.or.) &
6601 call mpistop("Error in add_source_hyperres: Non-conforming input limits")
6602
6603 call get_current(wct,ixi^l,ixa^l,idirmin,current)
6604 tmpvec(ixa^s,1:ndir)=zero
6605 do jdir=idirmin,3
6606 tmpvec(ixa^s,jdir)=current(ixa^s,jdir)
6607 end do
6608
6609 ixa^l=ixo^l^ladd2;
6610 call curlvector(tmpvec,ixi^l,ixa^l,tmpvec2,idirmin1,1,3)
6611
6612 ixa^l=ixo^l^ladd1;
6613 tmpvec(ixa^s,1:ndir)=zero
6614 call curlvector(tmpvec2,ixi^l,ixa^l,tmpvec,idirmin1,1,3)
6615 ehyper(ixa^s,1:ndir) = - tmpvec(ixa^s,1:ndir)*mhd_eta_hyper
6616
6617 ixa^l=ixo^l;
6618 tmpvec2(ixa^s,1:ndir)=zero
6619 call curlvector(ehyper,ixi^l,ixa^l,tmpvec2,idirmin1,1,3)
6620
6621 do idir=1,ndir
6622 w(ixo^s,mag(idir)) = w(ixo^s,mag(idir))-tmpvec2(ixo^s,idir)*qdt
6623 end do
6624
6625 if(total_energy) then
6626 ! de/dt= +div(B x Ehyper)
6627 ixa^l=ixo^l^ladd1;
6628 tmpvec2(ixa^s,1:ndir)=zero
6629 do idir=1,ndir; do jdir=1,ndir; do kdir=idirmin,3
6630 tmpvec2(ixa^s,idir) = tmpvec(ixa^s,idir)&
6631 + lvc(idir,jdir,kdir)*wct(ixa^s,mag(jdir))*ehyper(ixa^s,kdir)
6632 end do; end do; end do
6633 tmp(ixo^s)=zero
6634 call divvector(tmpvec2,ixi^l,ixo^l,tmp)
6635 w(ixo^s,e_)=w(ixo^s,e_)+tmp(ixo^s)*qdt
6636 end if
6637
6638 if (fix_small_values) call mhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_hyperres')
6639
6640 end subroutine add_source_hyperres
6641
6642 subroutine add_source_glm(qdt,ixI^L,ixO^L,wCT,w,x)
6643 ! Add divB related sources to w within ixO
6644 ! corresponding to Dedner JCP 2002, 175, 645 _equation 24_
6645 ! giving the EGLM-MHD scheme or GLM-MHD scheme
6647 use mod_geometry
6648
6649 integer, intent(in) :: ixi^l, ixo^l
6650 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), x(ixi^s,1:ndim)
6651 double precision, intent(inout) :: w(ixi^s,1:nw)
6652
6653 double precision:: divb(ixi^s), gradpsi(ixi^s), ba(ixo^s,1:ndir)
6654 integer :: idir
6655
6656
6657 ! dPsi/dt = - Ch^2/Cp^2 Psi
6658 if (mhd_glm_alpha < zero) then
6659 w(ixo^s,psi_) = abs(mhd_glm_alpha)*wct(ixo^s,psi_)
6660 else
6661 ! implicit update of Psi variable
6662 ! equation (27) in Mignone 2010 J. Com. Phys. 229, 2117
6663 if(slab_uniform) then
6664 w(ixo^s,psi_) = dexp(-qdt*cmax_global*mhd_glm_alpha/minval(dxlevel(:)))*w(ixo^s,psi_)
6665 else
6666 w(ixo^s,psi_) = dexp(-qdt*cmax_global*mhd_glm_alpha/minval(block%ds(ixo^s,:),dim=ndim+1))*w(ixo^s,psi_)
6667 end if
6668 end if
6669
6670 if(mhd_glm_extended) then
6671 if(b0field) then
6672 ba(ixo^s,1:ndir)=wct(ixo^s,mag(1:ndir))+block%B0(ixo^s,1:ndir,0)
6673 else
6674 ba(ixo^s,1:ndir)=wct(ixo^s,mag(1:ndir))
6675 end if
6676 ! gradient of Psi
6677 if(total_energy) then
6678 do idir=1,ndim
6679 select case(typegrad)
6680 case("central")
6681 call gradient(wct(ixi^s,psi_),ixi^l,ixo^l,idir,gradpsi)
6682 case("limited")
6683 call gradientl(wct(ixi^s,psi_),ixi^l,ixo^l,idir,gradpsi)
6684 end select
6685 ! e = e - qdt (b1 . grad(Psi)): e carries only the b1 magnetic energy
6686 w(ixo^s,e_) = w(ixo^s,e_)-qdt*wct(ixo^s,mag(idir))*gradpsi(ixo^s)
6687 end do
6688 end if
6689
6690 ! We calculate now div B
6691 call get_divb(wct,ixi^l,ixo^l,divb, mhd_divb_nth)
6692
6693 ! m = m - qdt b div b
6694 do idir=1,ndir
6695 w(ixo^s,mom(idir))=w(ixo^s,mom(idir))-qdt*ba(ixo^s,idir)*divb(ixo^s)
6696 end do
6697 end if
6698
6699 if (fix_small_values) call mhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_glm')
6700
6701 end subroutine add_source_glm
6702
6703 !> Add divB related sources to w within ixO corresponding to Powel
6704 subroutine add_source_powel(qdt,ixI^L,ixO^L,wCT,w,x)
6706
6707 integer, intent(in) :: ixi^l, ixo^l
6708 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), x(ixi^s,1:ndim)
6709 double precision, intent(inout) :: w(ixi^s,1:nw)
6710
6711 double precision :: divb(ixi^s), ba(1:ndir)
6712 integer :: idir, ix^d
6713
6714 ! calculate div B
6715 call get_divb(wct,ixi^l,ixo^l,divb, mhd_divb_nth)
6716
6717 if(b0field) then
6718 {do ix^db=ixomin^db,ixomax^db\}
6719 ! b = b - qdt v * div b
6720 ^c&w(ix^d,b^c_)=w(ix^d,b^c_)-qdt*wct(ix^d,m^c_)*divb(ix^d)\
6721 ! m = m - qdt b div b
6722 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)-qdt*(wct(ix^d,b^c_)+block%B0(ix^d,^c,0))*divb(ix^d)\
6723 if (total_energy) then
6724 ! e = e - qdt (v . b) * div b
6725 w(ix^d,e_)=w(ix^d,e_)-qdt*(^c&wct(ix^d,m^c_)*(wct(ix^d,b^c_)+block%B0(ix^d,^c,0))+)*divb(ix^d)
6726 end if
6727 {end do\}
6728 else
6729 {do ix^db=ixomin^db,ixomax^db\}
6730 ! b = b - qdt v * div b
6731 ^c&w(ix^d,b^c_)=w(ix^d,b^c_)-qdt*wct(ix^d,m^c_)*divb(ix^d)\
6732 ! m = m - qdt b div b
6733 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)-qdt*wct(ix^d,b^c_)*divb(ix^d)\
6734 if (total_energy) then
6735 ! e = e - qdt (v . b) * div b
6736 w(ix^d,e_)=w(ix^d,e_)-qdt*(^c&wct(ix^d,m^c_)*wct(ix^d,b^c_)+)*divb(ix^d)
6737 end if
6738 {end do\}
6739 end if
6740
6741 if (fix_small_values) call mhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_powel')
6742
6743 end subroutine add_source_powel
6744
6745 subroutine add_source_janhunen(qdt,ixI^L,ixO^L,wCT,w,x)
6746 ! Add divB related sources to w within ixO
6747 ! corresponding to Janhunen, just the term in the induction equation.
6749
6750 integer, intent(in) :: ixi^l, ixo^l
6751 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), x(ixi^s,1:ndim)
6752 double precision, intent(inout) :: w(ixi^s,1:nw)
6753
6754 double precision :: divb(ixi^s)
6755 integer :: idir, ix^d
6756
6757 ! calculate div B
6758 call get_divb(wct,ixi^l,ixo^l,divb, mhd_divb_nth)
6759
6760 {do ix^db=ixomin^db,ixomax^db\}
6761 ! b = b - qdt v * div b
6762 ^c&w(ix^d,b^c_)=w(ix^d,b^c_)-qdt*wct(ix^d,m^c_)*divb(ix^d)\
6763 {end do\}
6764
6765 if (fix_small_values) call mhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_janhunen')
6766
6767 end subroutine add_source_janhunen
6768
6769 subroutine add_source_linde(qdt,ixI^L,ixO^L,wCT,w,x)
6770 ! Add Linde's divB related sources to wnew within ixO
6772 use mod_geometry
6773
6774 integer, intent(in) :: ixi^l, ixo^l
6775 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), x(ixi^s,1:ndim)
6776 double precision, intent(inout) :: w(ixi^s,1:nw)
6777
6778 double precision :: divb(ixi^s),graddivb(ixi^s)
6779 integer :: idim, idir, ixp^l, i^d, iside
6780 logical, dimension(-1:1^D&) :: leveljump
6781
6782 ! Calculate div B
6783 ixp^l=ixo^l^ladd1;
6784 call get_divb(wct,ixi^l,ixp^l,divb, mhd_divb_nth)
6785
6786 ! for AMR stability, retreat one cell layer from the boarders of level jump
6787 {do i^db=-1,1\}
6788 if(i^d==0|.and.) cycle
6789 if(neighbor_type(i^d,block%igrid)==2 .or. neighbor_type(i^d,block%igrid)==4) then
6790 leveljump(i^d)=.true.
6791 else
6792 leveljump(i^d)=.false.
6793 end if
6794 {end do\}
6795
6796 ixp^l=ixo^l;
6797 do idim=1,ndim
6798 select case(idim)
6799 {case(^d)
6800 do iside=1,2
6801 i^dd=kr(^dd,^d)*(2*iside-3);
6802 if (leveljump(i^dd)) then
6803 if (iside==1) then
6804 ixpmin^d=ixomin^d-i^d
6805 else
6806 ixpmax^d=ixomax^d-i^d
6807 end if
6808 end if
6809 end do
6810 \}
6811 end select
6812 end do
6813
6814 ! Add Linde's diffusive terms
6815 do idim=1,ndim
6816 ! Calculate grad_idim(divb)
6817 call gradient(divb,ixi^l,ixp^l,idim,graddivb)
6818
6819 {do i^db=ixpmin^db,ixpmax^db\}
6820 ! Multiply by Linde's eta*dt = divbdiff*(c_max*dx)*dt = divbdiff*dx**2
6821 graddivb(i^d)=graddivb(i^d)*divbdiff/(^d&1.0d0/block%ds({i^d},^d)**2+)
6822
6823 w(i^d,mag(idim))=w(i^d,mag(idim))+graddivb(i^d)
6824
6825 if (typedivbdiff=='all' .and. total_energy) then
6826 ! e += B_idim*eta*grad_idim(divb)
6827 ! the classical term, without the O(V_A^2/c^2) semirelativistic correction
6828 w(i^d,e_)=w(i^d,e_)+wct(i^d,mag(idim))*graddivb(i^d)
6829 end if
6830 {end do\}
6831 end do
6832
6833 if (fix_small_values) call mhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_linde')
6834
6835 end subroutine add_source_linde
6836
6837 !> get dimensionless div B = |divB| * volume / area / |B|
6838 subroutine get_normalized_divb(w,ixI^L,ixO^L,divb)
6839
6841
6842 integer, intent(in) :: ixi^l, ixo^l
6843 double precision, intent(in) :: w(ixi^s,1:nw)
6844 double precision :: divb(ixi^s), dsurface(ixi^s)
6845
6846 double precision :: invb(ixo^s)
6847 integer :: ixa^l,idims
6848
6849 call get_divb(w,ixi^l,ixo^l,divb)
6850 invb(ixo^s)=sqrt(mhd_mag_en_all(w,ixi^l,ixo^l))
6851 where(invb(ixo^s)/=0.d0)
6852 invb(ixo^s)=1.d0/invb(ixo^s)
6853 end where
6854 if(slab_uniform) then
6855 divb(ixo^s)=0.5d0*abs(divb(ixo^s))*invb(ixo^s)/sum(1.d0/dxlevel(:))
6856 else
6857 ixamin^d=ixomin^d-1;
6858 ixamax^d=ixomax^d-1;
6859 dsurface(ixo^s)= sum(block%surfaceC(ixo^s,:),dim=ndim+1)
6860 do idims=1,ndim
6861 ixa^l=ixo^l-kr(idims,^d);
6862 dsurface(ixo^s)=dsurface(ixo^s)+block%surfaceC(ixa^s,idims)
6863 end do
6864 divb(ixo^s)=abs(divb(ixo^s))*invb(ixo^s)*&
6865 block%dvolume(ixo^s)/dsurface(ixo^s)
6866 end if
6867
6868 end subroutine get_normalized_divb
6869
6870 !> Calculate idirmin and the idirmin:3 components of the common current array
6871 !> make sure that dxlevel(^D) is set correctly.
6872 subroutine get_current(w,ixI^L,ixO^L,idirmin,current)
6874 use mod_geometry
6875
6876 integer, intent(in) :: ixo^l, ixi^l
6877 double precision, intent(in) :: w(ixi^s,1:nw)
6878 integer, intent(out) :: idirmin
6879
6880 ! For ndir=2 only 3rd component of J can exist, ndir=1 is impossible for MHD
6881 double precision :: current(ixi^s,7-2*ndir:3)
6882 integer :: idir, idirmin0
6883
6884 idirmin0 = 7-2*ndir
6885
6886 call curlvector(w(ixi^s,mag(1:ndir)),ixi^l,ixo^l,current,idirmin,idirmin0,ndir)
6887
6888 if(b0field) current(ixo^s,idirmin0:3)=current(ixo^s,idirmin0:3)+&
6889 block%J0(ixo^s,idirmin0:3)
6890 end subroutine get_current
6891
6892 !> If resistivity is not zero, check diffusion time limit for dt and similar other effects
6893 subroutine mhd_get_dt(wprim,ixI^L,ixO^L,dtnew,dx^D,x)
6895 use mod_usr_methods
6897 use mod_gravity, only: gravity_get_dt
6898 use mod_cak_force, only: cak_get_dt
6899 use mod_fld, only: fld_radforce_get_dt
6900
6901 integer, intent(in) :: ixi^l, ixo^l
6902 double precision, intent(inout) :: dtnew
6903 double precision, intent(in) :: dx^d
6904 double precision, intent(in) :: wprim(ixi^s,1:nw)
6905 double precision, intent(in) :: x(ixi^s,1:ndim)
6906
6907 double precision :: dxarr(ndim)
6908 double precision :: current(ixi^s,7-2*ndir:3),eta(ixi^s)
6909 integer :: idirmin,idim
6910
6911 dtnew = bigdouble
6912
6913 ^d&dxarr(^d)=dx^d;
6914 if (mhd_eta>zero)then
6915 if(slab_uniform) then
6916 dtnew=dtdiffpar*minval(dxarr(1:ndim))**2/mhd_eta
6917 else
6918 dtnew=dtdiffpar*minval(block%ds(ixo^s,1:ndim))**2/mhd_eta
6919 end if
6920 else if (mhd_eta<zero)then
6921 call get_current(wprim,ixi^l,ixo^l,idirmin,current)
6922 call usr_special_resistivity(wprim,ixi^l,ixo^l,idirmin,x,current,eta)
6923 dtnew=bigdouble
6924 do idim=1,ndim
6925 if(slab_uniform) then
6926 dtnew=min(dtnew,&
6927 dtdiffpar/(smalldouble+maxval(eta(ixo^s)/dxarr(idim)**2)))
6928 else
6929 dtnew=min(dtnew,&
6930 dtdiffpar/(smalldouble+maxval(eta(ixo^s)/block%ds(ixo^s,idim)**2)))
6931 end if
6932 end do
6933 end if
6934
6935 if(mhd_eta_hyper>zero) then
6936 if(slab_uniform) then
6937 dtnew=min(dtdiffpar*minval(dxarr(1:ndim))**4/mhd_eta_hyper,dtnew)
6938 else
6939 dtnew=min(dtdiffpar*minval(block%ds(ixo^s,1:ndim))**4/mhd_eta_hyper,dtnew)
6940 end if
6941 end if
6942
6943 if(mhd_viscosity) then
6944 call viscosity_get_dt(wprim,ixi^l,ixo^l,dtnew,dx^d,x)
6945 end if
6946
6947 if(mhd_gravity) then
6948 call gravity_get_dt(wprim,ixi^l,ixo^l,dtnew,dx^d,x)
6949 end if
6950
6951 if(mhd_ambipolar_exp) then
6952 dtnew=min(dtdiffpar*get_ambipolar_dt(wprim,ixi^l,ixo^l,dx^d,x),dtnew)
6953 endif
6954
6955 if (mhd_cak_force) then
6956 call cak_get_dt(wprim,ixi^l,ixo^l,dtnew,dx^d,x)
6957 end if
6958
6959 if(mhd_radiation_fld) then
6960 call fld_radforce_get_dt(wprim,ixi^l,ixo^l,dtnew,dx^d,x,fld_fl)
6961 endif
6962
6963 end subroutine mhd_get_dt
6964
6965 !> Wrappers for the FLD implicit (MG diffusion) hooks: phys_implicit_update /
6966 !> phys_evaluate_implicit have fixed interfaces with no fluid argument, so
6967 !> these inject the module's fld_fl object into the threaded fld routines.
6968 subroutine mhd_fld_implicit_update(dtfactor,qdt,qtC,psa,psb)
6970 use mod_fld, only: fld_implicit_update
6971 type(state), target :: psa(max_blocks)
6972 type(state), target :: psb(max_blocks)
6973 double precision, intent(in) :: qdt
6974 double precision, intent(in) :: qtc
6975 double precision, intent(in) :: dtfactor
6976
6977 call fld_implicit_update(dtfactor,qdt,qtc,psa,psb,fld_fl)
6978 end subroutine mhd_fld_implicit_update
6979
6980 subroutine mhd_fld_evaluate_implicit(qtC,psa)
6983 type(state), target :: psa(max_blocks)
6984 double precision, intent(in) :: qtc
6985
6986 call fld_evaluate_implicit(qtc,psa,fld_fl)
6987 end subroutine mhd_fld_evaluate_implicit
6988
6989 ! Add geometrical source terms to w
6990 ! Geometric sources to momentum and induction
6991 ! for the regular case, not semi-relativistic, nor any splitting active
6992 ! but possibly no energy equation at all
6993 ! NOTE: Hall terms in induction not handled yet
6994 subroutine mhd_add_source_geom(qdt,dtfactor,ixI^L,ixO^L,wCT,wprim,w,x)
6996 use mod_geometry
6999
7000 integer, intent(in) :: ixi^l, ixo^l
7001 double precision, intent(in) :: qdt, dtfactor,x(ixi^s,1:ndim)
7002 double precision, intent(inout) :: wct(ixi^s,1:nw),wprim(ixi^s,1:nw),w(ixi^s,1:nw)
7003
7004 double precision :: adiabs(ixi^s), gammas(ixi^s)
7005 double precision :: tmp,tmp1,invr,cot
7006 integer :: ix^d
7007 integer :: mr_,mphi_ ! Polar var. names
7008 integer :: br_,bphi_
7009
7010 mr_=mom(1); mphi_=mom(1)-1+phi_ ! Polar var. names
7011 br_=mag(1); bphi_=mag(1)-1+phi_
7012
7013 if(.not.mhd_energy) then
7014 if(associated(usr_set_adiab)) then
7015 call usr_set_adiab(w,x,ixi^l,ixo^l,adiabs)
7016 else
7017 adiabs=mhd_adiab
7018 end if
7019 if(associated(usr_set_gamma)) then
7020 call usr_set_gamma(w,x,ixi^l,ixo^l,gammas)
7021 else
7022 gammas=eos%gamma
7023 end if
7024 end if
7025
7026 select case (coordinate)
7027 case (cylindrical)
7028 {do ix^db=ixomin^db,ixomax^db\}
7029 ! include dt in invr, invr is always used with qdt
7030 if(local_timestep) then
7031 invr=block%dt(ix^d) * dtfactor/x(ix^d,1)
7032 else
7033 invr=qdt/x(ix^d,1)
7034 end if
7035 if(mhd_energy) then
7036 tmp=wprim(ix^d,p_)+half*(^c&wprim(ix^d,b^c_)**2+)
7037 else
7038 tmp=adiabs(ix^d)*wprim(ix^d,rho_)**gammas(ix^d)+half*(^c&wprim(ix^d,b^c_)**2+)
7039 end if
7040 if(phi_>0) then
7041 w(ix^d,mr_)=w(ix^d,mr_)+invr*(tmp-&
7042 wprim(ix^d,bphi_)**2+wprim(ix^d,mphi_)*wct(ix^d,mphi_))
7043 w(ix^d,mphi_)=w(ix^d,mphi_)+invr*(&
7044 -wct(ix^d,mphi_)*wprim(ix^d,mr_) &
7045 +wprim(ix^d,bphi_)*wprim(ix^d,br_))
7046 if(.not.stagger_grid) then
7047 w(ix^d,bphi_)=w(ix^d,bphi_)+invr*&
7048 (wprim(ix^d,bphi_)*wprim(ix^d,mr_) &
7049 -wprim(ix^d,br_)*wprim(ix^d,mphi_))
7050 end if
7051 else
7052 w(ix^d,mr_)=w(ix^d,mr_)+invr*tmp
7053 end if
7054 if(mhd_glm) w(ix^d,br_)=w(ix^d,br_)+wprim(ix^d,psi_)*invr
7055 {end do\}
7056 case (spherical)
7057 {do ix^db=ixomin^db,ixomax^db\}
7058 ! include dt in invr, invr is always used with qdt
7059 if(local_timestep) then
7060 invr=block%dt(ix^d) * dtfactor/x(ix^d,1)
7061 else
7062 invr=qdt/x(ix^d,1)
7063 end if
7064 if(mhd_energy) then
7065 tmp1=wprim(ix^d,p_)+half*(^c&wprim(ix^d,b^c_)**2+)
7066 else
7067 tmp1=adiabs(ix^d)*wprim(ix^d,rho_)**gammas(ix^d)+half*(^c&wprim(ix^d,b^c_)**2+)
7068 end if
7069 ! m1
7070 {^ifonec
7071 w(ix^d,mom(1))=w(ix^d,mom(1))+two*tmp1*invr
7072 }
7073 {^noonec
7074 w(ix^d,mom(1))=w(ix^d,mom(1))+invr*&
7075 (two*tmp1+(^ce&wprim(ix^d,m^ce_)*wct(ix^d,m^ce_)-wprim(ix^d,b^ce_)**2+))
7076 }
7077 ! b1
7078 if(mhd_glm) then
7079 w(ix^d,mag(1))=w(ix^d,mag(1))+invr*2.0d0*wprim(ix^d,psi_)
7080 end if
7081 {^ifoned
7082 cot=0.d0
7083 }
7084 {^nooned
7085 cot=1.d0/tan(x(ix^d,2))
7086 }
7087 {^iftwoc
7088 ! m2
7089 w(ix^d,mom(2))=w(ix^d,mom(2))+invr*(tmp1*cot-wprim(ix^d,m1_)*wct(ix^d,m2_)&
7090 +wprim(ix^d,b1_)*wprim(ix^d,b2_))
7091 ! b2
7092 if(.not.stagger_grid) then
7093 tmp=wprim(ix^d,m1_)*wprim(ix^d,b2_)-wprim(ix^d,m2_)*wprim(ix^d,b1_)
7094 if(mhd_glm) then
7095 tmp=tmp+wprim(ix^d,psi_)*cot
7096 end if
7097 w(ix^d,mag(2))=w(ix^d,mag(2))+tmp*invr
7098 end if
7099 }
7100 {^ifthreec
7101 ! m2
7102 w(ix^d,mom(2))=w(ix^d,mom(2))+invr*(tmp1*cot-wprim(ix^d,m1_)*wct(ix^d,m2_)&
7103 +wprim(ix^d,b1_)*wprim(ix^d,b2_)&
7104 +(wprim(ix^d,m3_)*wct(ix^d,m3_)-wprim(ix^d,b3_)**2)*cot)
7105 ! b2
7106 if(.not.stagger_grid) then
7107 tmp=wprim(ix^d,m1_)*wprim(ix^d,b2_)-wprim(ix^d,m2_)*wprim(ix^d,b1_)
7108 if(mhd_glm) then
7109 tmp=tmp+wprim(ix^d,psi_)*cot
7110 end if
7111 w(ix^d,mag(2))=w(ix^d,mag(2))+tmp*invr
7112 end if
7113 ! m3
7114 w(ix^d,mom(3))=w(ix^d,mom(3))-invr*&
7115 (wprim(ix^d,m3_)*wct(ix^d,m1_) &
7116 -wprim(ix^d,b3_)*wprim(ix^d,b1_) &
7117 +(wprim(ix^d,m2_)*wct(ix^d,m3_) &
7118 -wprim(ix^d,b2_)*wprim(ix^d,b3_))*cot)
7119 ! b3
7120 if(.not.stagger_grid) then
7121 w(ix^d,mag(3))=w(ix^d,mag(3))+invr*&
7122 (wprim(ix^d,m1_)*wprim(ix^d,b3_) &
7123 -wprim(ix^d,m3_)*wprim(ix^d,b1_) &
7124 -(wprim(ix^d,m3_)*wprim(ix^d,b2_) &
7125 -wprim(ix^d,m2_)*wprim(ix^d,b3_))*cot)
7126 end if
7127 }
7128 {end do\}
7129 end select
7130
7131 if (mhd_rotating_frame) then
7132 call rotating_frame_add_source(qdt,dtfactor,ixi^l,ixo^l,wprim,w,x)
7133 end if
7134
7135 end subroutine mhd_add_source_geom
7136
7137 ! Add geometrical source terms to w
7138 ! Geometric sources to momentum and induction
7139 ! for the semi-relativistic, hence no splitting active
7140 ! but possibly no energy equation at all
7141 ! NOTE: Hall terms in induction not handled yet
7142 subroutine mhd_add_source_geom_semirelati(qdt,dtfactor,ixI^L,ixO^L,wCT,wprim,w,x)
7144 use mod_geometry
7147
7148 integer, intent(in) :: ixi^l, ixo^l
7149 double precision, intent(in) :: qdt, dtfactor,x(ixi^s,1:ndim)
7150 double precision, intent(inout) :: wct(ixi^s,1:nw),wprim(ixi^s,1:nw),w(ixi^s,1:nw)
7151
7152 double precision :: adiabs(ixi^s), gammas(ixi^s)
7153 double precision :: tmp,tmp1,tmp2,invr,cot,ef(ixo^s,1:ndir)
7154 integer :: ix^d
7155 integer :: mr_,mphi_ ! Polar var. names
7156 integer :: br_,bphi_
7157
7158 mr_=mom(1); mphi_=mom(1)-1+phi_ ! Polar var. names
7159 br_=mag(1); bphi_=mag(1)-1+phi_
7160
7161 if(.not.mhd_energy) then
7162 if(associated(usr_set_adiab)) then
7163 call usr_set_adiab(w,x,ixi^l,ixo^l,adiabs)
7164 else
7165 adiabs=mhd_adiab
7166 end if
7167 if(associated(usr_set_gamma)) then
7168 call usr_set_gamma(w,x,ixi^l,ixo^l,gammas)
7169 else
7170 gammas=eos%gamma
7171 end if
7172 end if
7173
7174 select case (coordinate)
7175 case (cylindrical)
7176 {do ix^db=ixomin^db,ixomax^db\}
7177 ! include dt in invr, invr is always used with qdt
7178 if(local_timestep) then
7179 invr=block%dt(ix^d) * dtfactor/x(ix^d,1)
7180 else
7181 invr=qdt/x(ix^d,1)
7182 end if
7183 if(mhd_energy) then
7184 tmp=wprim(ix^d,p_)
7185 else
7186 tmp=adiabs(ix^d)*wprim(ix^d,rho_)**gammas(ix^d)
7187 end if
7188 ! E=Bxv
7189 {^ifthreec
7190 ef(ix^d,1)=wprim(ix^d,b2_)*wprim(ix^d,m3_)-wprim(ix^d,b3_)*wprim(ix^d,m2_)
7191 ef(ix^d,2)=wprim(ix^d,b3_)*wprim(ix^d,m1_)-wprim(ix^d,b1_)*wprim(ix^d,m3_)
7192 ef(ix^d,3)=wprim(ix^d,b1_)*wprim(ix^d,m2_)-wprim(ix^d,b2_)*wprim(ix^d,m1_)
7193 }
7194 {^iftwoc
7195 ef(ix^d,1)=zero
7196 ! store e3 in e2 to count e3 when ^C is from 1 to 2
7197 ef(ix^d,2)=wprim(ix^d,b1_)*wprim(ix^d,m2_)-wprim(ix^d,b2_)*wprim(ix^d,m1_)
7198 }
7199 {^ifonec
7200 ef(ix^d,1)=zero
7201 }
7202 if(phi_>0) then
7203 w(ix^d,mr_)=w(ix^d,mr_)+invr*(tmp+&
7204 half*((^c&wprim(ix^d,b^c_)**2+)+(^c&ef(ix^d,^c)**2+)*inv_squared_c) -&
7205 wprim(ix^d,bphi_)**2+wprim(ix^d,rho_)*wprim(ix^d,mphi_)**2)
7206 w(ix^d,mphi_)=w(ix^d,mphi_)+invr*(&
7207 -wprim(ix^d,rho_)*wprim(ix^d,mphi_)*wprim(ix^d,mr_) &
7208 +wprim(ix^d,bphi_)*wprim(ix^d,br_)+ef(ix^d,phi_)*ef(ix^d,1)*inv_squared_c)
7209 if(.not.stagger_grid) then
7210 w(ix^d,bphi_)=w(ix^d,bphi_)+invr*&
7211 (wprim(ix^d,bphi_)*wprim(ix^d,mr_) &
7212 -wprim(ix^d,br_)*wprim(ix^d,mphi_))
7213 end if
7214 else
7215 w(ix^d,mr_)=w(ix^d,mr_)+invr*(tmp+half*((^c&wprim(ix^d,b^c_)**2+)+&
7216 (^c&ef(ix^d,^c)**2+)*inv_squared_c))
7217 end if
7218 if(mhd_glm) w(ix^d,br_)=w(ix^d,br_)+wprim(ix^d,psi_)*invr
7219 {end do\}
7220 case (spherical)
7221 {do ix^db=ixomin^db,ixomax^db\}
7222 ! include dt in invr, invr is always used with qdt
7223 if(local_timestep) then
7224 invr=block%dt(ix^d)*dtfactor/x(ix^d,1)
7225 else
7226 invr=qdt/x(ix^d,1)
7227 end if
7228 ! E=Bxv
7229 {^ifthreec
7230 ef(ix^d,1)=wprim(ix^d,b2_)*wprim(ix^d,m3_)-wprim(ix^d,b3_)*wprim(ix^d,m2_)
7231 ef(ix^d,2)=wprim(ix^d,b3_)*wprim(ix^d,m1_)-wprim(ix^d,b1_)*wprim(ix^d,m3_)
7232 ef(ix^d,3)=wprim(ix^d,b1_)*wprim(ix^d,m2_)-wprim(ix^d,b2_)*wprim(ix^d,m1_)
7233 }
7234 {^iftwoc
7235 ! store e3 in e1 to count e3 when ^C is from 1 to 2
7236 ef(ix^d,1)=wprim(ix^d,b1_)*wprim(ix^d,m2_)-wprim(ix^d,b2_)*wprim(ix^d,m1_)
7237 ef(ix^d,2)=zero
7238 }
7239 {^ifonec
7240 ef(ix^d,1)=zero
7241 }
7242 if(mhd_energy) then
7243 tmp1=wprim(ix^d,p_)+half*((^c&wprim(ix^d,b^c_)**2+)+(^c&ef(ix^d,^c)**2+)*inv_squared_c)
7244 else
7245 tmp1=adiabs(ix^d)*wprim(ix^d,rho_)**gammas(ix^d)+half*((^c&wprim(ix^d,b^c_)**2+)+(^c&ef(ix^d,^c)**2+)*inv_squared_c)
7246 end if
7247 ! m1
7248 {^ifonec
7249 w(ix^d,m1_)=w(ix^d,m1_)+two*tmp1*invr
7250 }
7251 {^noonec
7252 w(ix^d,m1_)=w(ix^d,m1_)+invr*&
7253 (two*tmp1+(^ce&wprim(ix^d,rho_)*wprim(ix^d,m^ce_)**2-&
7254 wprim(ix^d,b^ce_)**2-ef(ix^d,^ce)**2*inv_squared_c+))
7255 }
7256 ! b1
7257 if(mhd_glm) then
7258 w(ix^d,b1_)=w(ix^d,b1_)+invr*2.0d0*wprim(ix^d,psi_)
7259 end if
7260 {^ifoned
7261 cot=0.d0
7262 }
7263 {^nooned
7264 cot=1.d0/tan(x(ix^d,2))
7265 }
7266 {^iftwoc
7267 ! m2
7268 w(ix^d,m2_)=w(ix^d,m2_)+invr*(tmp1*cot-wprim(ix^d,rho_)*wprim(ix^d,m1_)*wprim(ix^d,m2_)&
7269 +wprim(ix^d,b1_)*wprim(ix^d,b2_)+ef(ix^d,1)*ef(ix^d,2)*inv_squared_c)
7270 ! b2
7271 if(.not.stagger_grid) then
7272 tmp=wprim(ix^d,m1_)*wprim(ix^d,b2_)-wprim(ix^d,m2_)*wprim(ix^d,b1_)
7273 if(mhd_glm) then
7274 tmp=tmp+wprim(ix^d,psi_)*cot
7275 end if
7276 w(ix^d,b2_)=w(ix^d,b2_)+tmp*invr
7277 end if
7278 }
7279
7280 {^ifthreec
7281 ! m2
7282 w(ix^d,m2_)=w(ix^d,m2_)+invr*(tmp1*cot-wprim(ix^d,rho_)*wprim(ix^d,m1_)*wprim(ix^d,m2_) &
7283 +wprim(ix^d,b1_)*wprim(ix^d,b2_)+ef(ix^d,1)*ef(ix^d,2)*inv_squared_c&
7284 +(wprim(ix^d,rho_)*wprim(ix^d,m3_)**2&
7285 -wprim(ix^d,b3_)**2-ef(ix^d,3)**2*inv_squared_c)*cot)
7286 ! b2
7287 if(.not.stagger_grid) then
7288 tmp=wprim(ix^d,m1_)*wprim(ix^d,b2_)-wprim(ix^d,m2_)*wprim(ix^d,b1_)
7289 if(mhd_glm) then
7290 tmp=tmp+wprim(ix^d,psi_)*cot
7291 end if
7292 w(ix^d,b2_)=w(ix^d,b2_)+tmp*invr
7293 end if
7294 ! m3
7295 w(ix^d,m3_)=w(ix^d,m3_)+invr*&
7296 (-wprim(ix^d,m3_)*wprim(ix^d,m1_)*wprim(ix^d,rho_) &
7297 +wprim(ix^d,b3_)*wprim(ix^d,b1_) &
7298 +ef(ix^d,3)*ef(ix^d,1)*inv_squared_c&
7299 +(-wprim(ix^d,m2_)*wprim(ix^d,m3_)*wprim(ix^d,rho_) &
7300 +wprim(ix^d,b2_)*wprim(ix^d,b3_)&
7301 +ef(ix^d,2)*ef(ix^d,3)*inv_squared_c)*cot)
7302 ! b3
7303 if(.not.stagger_grid) then
7304 w(ix^d,b3_)=w(ix^d,b3_)+invr*&
7305 (wprim(ix^d,m1_)*wprim(ix^d,b3_) &
7306 -wprim(ix^d,m3_)*wprim(ix^d,b1_) &
7307 -(wprim(ix^d,m3_)*wprim(ix^d,b2_) &
7308 -wprim(ix^d,m2_)*wprim(ix^d,b3_))*cot)
7309 end if
7310 }
7311 {end do\}
7312 end select
7313
7314 if (mhd_rotating_frame) then
7315 call rotating_frame_add_source(qdt,dtfactor,ixi^l,ixo^l,wprim,w,x)
7316 end if
7317
7318 end subroutine mhd_add_source_geom_semirelati
7319
7320 ! Add geometrical source terms to w
7321 ! Geometric sources to momentum and induction
7322 ! for those cases where any kind of splitting (B0field or has_equi_rho_and_p) is active
7323 ! This implies that there is an energy equation included for sure
7324 ! B0field impacts terms in induction equation and geometric sources for them
7325 ! both flags affect the terms in momentum equation, in three variants (TF, TT, FT)
7326 ! NOTE: Hall terms in induction not handled yet
7327 subroutine mhd_add_source_geom_split(qdt,dtfactor,ixI^L,ixO^L,wCT,wprim,w,x)
7329 use mod_geometry
7332
7333 integer, intent(in) :: ixi^l, ixo^l
7334 double precision, intent(in) :: qdt, dtfactor,x(ixi^s,1:ndim)
7335 double precision, intent(inout) :: wct(ixi^s,1:nw),wprim(ixi^s,1:nw),w(ixi^s,1:nw)
7336
7337 double precision :: tmp,tmp1,tmp2,invr,cot
7338 double precision :: adiabs(ixi^s), gammas(ixi^s)
7339 integer :: ix^d
7340 integer :: mr_,mphi_ ! Polar var. names
7341 integer :: br_,bphi_
7342
7343 if(.not.mhd_energy) then
7344 if(associated(usr_set_adiab)) then
7345 call usr_set_adiab(wprim,x,ixi^l,ixo^l,adiabs)
7346 else
7347 adiabs=mhd_adiab
7348 end if
7349 if(associated(usr_set_gamma)) then
7350 call usr_set_gamma(wprim,x,ixi^l,ixo^l,gammas)
7351 else
7352 gammas=eos%gamma
7353 end if
7354 end if
7355
7356 mr_=mom(1); mphi_=mom(1)-1+phi_ ! Polar var. names
7357 br_=mag(1); bphi_=mag(1)-1+phi_
7358
7359
7360 select case (coordinate)
7361 case (cylindrical)
7362 {do ix^db=ixomin^db,ixomax^db\}
7363 ! include dt in invr, invr is always used with qdt
7364 if(local_timestep) then
7365 invr=block%dt(ix^d) * dtfactor/x(ix^d,1)
7366 else
7367 invr=qdt/x(ix^d,1)
7368 end if
7369 if(mhd_energy) then
7370 tmp=wprim(ix^d,p_)+half*(^c&wprim(ix^d,b^c_)**2+)
7371 else
7372 tmp=adiabs(ix^d)*wprim(ix^d,rho_)**gammas(ix^d)+half*(^c&wprim(ix^d,b^c_)**2+)
7373 end if
7374 if(b0field) tmp=tmp+(^c&block%B0(ix^d,^c,0)*wprim(ix^d,b^c_)+)
7375 if(phi_>0) then
7376 w(ix^d,mr_)=w(ix^d,mr_)+invr*(tmp-&
7377 wprim(ix^d,bphi_)**2+wprim(ix^d,mphi_)*wct(ix^d,mphi_))
7378 if(b0field) then
7379 w(ix^d,mr_)=w(ix^d,mr_)+invr*(-block%B0(ix^d,phi_,0)*wprim(ix^d,bphi_)-wprim(ix^d,bphi_)*block%B0(ix^d,phi_,0))
7380 endif
7381 w(ix^d,mphi_)=w(ix^d,mphi_)+invr*(&
7382 -wct(ix^d,mphi_)*wprim(ix^d,mr_) &
7383 +wprim(ix^d,bphi_)*wprim(ix^d,br_))
7384 if(b0field) then
7385 w(ix^d,mphi_)=w(ix^d,mphi_)+invr*(block%B0(ix^d,phi_,0)*wprim(ix^d,br_)+wprim(ix^d,bphi_)*block%B0(ix^d,r_,0))
7386 endif
7387 if(.not.stagger_grid) then
7388 w(ix^d,bphi_)=w(ix^d,bphi_)+invr*&
7389 (wprim(ix^d,bphi_)*wprim(ix^d,mr_) &
7390 -wprim(ix^d,br_)*wprim(ix^d,mphi_))
7391 if(b0field) then
7392 w(ix^d,bphi_)=w(ix^d,bphi_)+invr*&
7393 (block%B0(ix^d,phi_,0)*wprim(ix^d,mr_) &
7394 -block%B0(ix^d,r_,0)*wprim(ix^d,mphi_))
7395 endif
7396 end if
7397 else
7398 w(ix^d,mr_)=w(ix^d,mr_)+invr*tmp
7399 end if
7400 if(mhd_glm) w(ix^d,br_)=w(ix^d,br_)+wprim(ix^d,psi_)*invr
7401 {end do\}
7402 case (spherical)
7403 {do ix^db=ixomin^db,ixomax^db\}
7404 ! include dt in invr, invr is always used with qdt
7405 if(local_timestep) then
7406 invr=block%dt(ix^d) * dtfactor/x(ix^d,1)
7407 else
7408 invr=qdt/x(ix^d,1)
7409 end if
7410 tmp1=wprim(ix^d,p_)+half*(^c&wprim(ix^d,b^c_)**2+)
7411 if(b0field) tmp2=(^c&block%B0(ix^d,^c,0)*wprim(ix^d,b^c_)+)
7412 ! m1
7413 {^ifonec
7414 w(ix^d,mom(1))=w(ix^d,mom(1))+two*tmp1*invr
7415 if(b0field) w(ix^d,mom(1))=w(ix^d,mom(1))+two*tmp2*invr
7416 }
7417 {^noonec
7418 if(b0field) then
7419 w(ix^d,mom(1))=w(ix^d,mom(1))+invr*&
7420 (two*(tmp1+tmp2)+(^ce&wprim(ix^d,m^ce_)*wct(ix^d,m^ce_)-wprim(ix^d,b^ce_)**2+)- &
7421 (^ce&two*block%B0(ix^d,^ce,0)*wprim(ix^d,b^ce_)+))
7422 else
7423 w(ix^d,mom(1))=w(ix^d,mom(1))+invr*&
7424 (two*tmp1+(^ce&wprim(ix^d,m^ce_)*wct(ix^d,m^ce_)-wprim(ix^d,b^ce_)**2+))
7425 end if
7426 }
7427 ! b1
7428 if(mhd_glm) then
7429 w(ix^d,mag(1))=w(ix^d,mag(1))+invr*2.0d0*wprim(ix^d,psi_)
7430 end if
7431 {^ifoned
7432 cot=0.d0
7433 }
7434 {^nooned
7435 cot=1.d0/tan(x(ix^d,2))
7436 }
7437 {^iftwoc
7438 ! m2
7439 if(b0field) then
7440 w(ix^d,mom(2))=w(ix^d,mom(2))+invr*((tmp1+tmp2)*cot-wprim(ix^d,m1_)*wct(ix^d,m2_)&
7441 +wprim(ix^d,b1_)*wprim(ix^d,b2_)+block%B0(ix^d,1,0)*wprim(ix^d,b2_)&
7442 +wprim(ix^d,b1_)*block%B0(ix^d,2,0))
7443 else
7444 w(ix^d,mom(2))=w(ix^d,mom(2))+invr*(tmp1*cot-wprim(ix^d,m1_)*wct(ix^d,m2_)&
7445 +wprim(ix^d,b1_)*wprim(ix^d,b2_))
7446 end if
7447 ! b2
7448 if(.not.stagger_grid) then
7449 if(b0field) then
7450 tmp=wprim(ix^d,m1_)*wprim(ix^d,b2_)-wprim(ix^d,m2_)*wprim(ix^d,b1_)&
7451 +wprim(ix^d,m1_)*block%B0(ix^d,2,0)-wprim(ix^d,m2_)*block%B0(ix^d,1,0)
7452 else
7453 tmp=wprim(ix^d,m1_)*wprim(ix^d,b2_)-wprim(ix^d,m2_)*wprim(ix^d,b1_)
7454 end if
7455 if(mhd_glm) then
7456 tmp=tmp+wprim(ix^d,psi_)*cot
7457 end if
7458 w(ix^d,mag(2))=w(ix^d,mag(2))+tmp*invr
7459 end if
7460 }
7461 {^ifthreec
7462 ! m2
7463 if(b0field) then
7464 w(ix^d,mom(2))=w(ix^d,mom(2))+invr*((tmp1+tmp2)*cot-wprim(ix^d,m1_)*wct(ix^d,m2_)&
7465 +wprim(ix^d,b1_)*wprim(ix^d,b2_)+block%B0(ix^d,1,0)*wprim(ix^d,b2_)&
7466 +wprim(ix^d,b1_)*block%B0(ix^d,2,0)&
7467 +(wprim(ix^d,m3_)*wct(ix^d,m3_)-wprim(ix^d,b3_)**2-two*block%B0(ix^d,3,0)*wprim(ix^d,b3_))*cot)
7468 else
7469 w(ix^d,mom(2))=w(ix^d,mom(2))+invr*(tmp1*cot-wprim(ix^d,m1_)*wct(ix^d,m2_)&
7470 +wprim(ix^d,b1_)*wprim(ix^d,b2_)&
7471 +(wprim(ix^d,m3_)*wct(ix^d,m3_)-wprim(ix^d,b3_)**2)*cot)
7472 end if
7473 ! b2
7474 if(.not.stagger_grid) then
7475 if(b0field) then
7476 tmp=wprim(ix^d,m1_)*wprim(ix^d,b2_)-wprim(ix^d,m2_)*wprim(ix^d,b1_)&
7477 +wprim(ix^d,m1_)*block%B0(ix^d,2,0)-wprim(ix^d,m2_)*block%B0(ix^d,1,0)
7478 else
7479 tmp=wprim(ix^d,m1_)*wprim(ix^d,b2_)-wprim(ix^d,m2_)*wprim(ix^d,b1_)
7480 end if
7481 if(mhd_glm) then
7482 tmp=tmp+wprim(ix^d,psi_)*cot
7483 end if
7484 w(ix^d,mag(2))=w(ix^d,mag(2))+tmp*invr
7485 end if
7486 ! m3
7487 if(b0field) then
7488 w(ix^d,mom(3))=w(ix^d,mom(3))-invr*&
7489 (wprim(ix^d,m3_)*wct(ix^d,m1_) &
7490 -wprim(ix^d,b3_)*wprim(ix^d,b1_) &
7491 +block%B0(ix^d,1,0)*wprim(ix^d,b3_) &
7492 +wprim(ix^d,b1_)*block%B0(ix^d,3,0) &
7493 +(wprim(ix^d,m2_)*wct(ix^d,m3_) &
7494 -wprim(ix^d,b2_)*wprim(ix^d,b3_) &
7495 +block%B0(ix^d,2,0)*wprim(ix^d,b3_) &
7496 +wprim(ix^d,b2_)*block%B0(ix^d,3,0))*cot)
7497 else
7498 w(ix^d,mom(3))=w(ix^d,mom(3))-invr*&
7499 (wprim(ix^d,m3_)*wct(ix^d,m1_) &
7500 -wprim(ix^d,b3_)*wprim(ix^d,b1_) &
7501 +(wprim(ix^d,m2_)*wct(ix^d,m3_) &
7502 -wprim(ix^d,b2_)*wprim(ix^d,b3_))*cot)
7503 end if
7504 ! b3
7505 if(.not.stagger_grid) then
7506 if(b0field) then
7507 w(ix^d,mag(3))=w(ix^d,mag(3))+invr*&
7508 (wprim(ix^d,m1_)*wprim(ix^d,b3_) &
7509 -wprim(ix^d,m3_)*wprim(ix^d,b1_) &
7510 +wprim(ix^d,m1_)*block%B0(ix^d,3,0) &
7511 -wprim(ix^d,m3_)*block%B0(ix^d,1,0) &
7512 -(wprim(ix^d,m3_)*wprim(ix^d,b2_) &
7513 -wprim(ix^d,m2_)*wprim(ix^d,b3_) &
7514 +wprim(ix^d,m3_)*block%B0(ix^d,2,0) &
7515 -wprim(ix^d,m2_)*block%B0(ix^d,3,0))*cot)
7516 else
7517 w(ix^d,mag(3))=w(ix^d,mag(3))+invr*&
7518 (wprim(ix^d,m1_)*wprim(ix^d,b3_) &
7519 -wprim(ix^d,m3_)*wprim(ix^d,b1_) &
7520 -(wprim(ix^d,m3_)*wprim(ix^d,b2_) &
7521 -wprim(ix^d,m2_)*wprim(ix^d,b3_))*cot)
7522 end if
7523 end if
7524 }
7525 {end do\}
7526 end select
7527
7528 if (mhd_rotating_frame) then
7529 call rotating_frame_add_source(qdt,dtfactor,ixi^l,ixo^l,wprim,w,x)
7530 end if
7531
7532 end subroutine mhd_add_source_geom_split
7533
7534 !> Compute 2 times total magnetic energy
7535 function mhd_mag_en_all(w, ixI^L, ixO^L) result(mge)
7537 integer, intent(in) :: ixi^l, ixo^l
7538 double precision, intent(in) :: w(ixi^s, nw)
7539 double precision :: mge(ixo^s)
7540
7541 if (b0field) then
7542 mge = sum((w(ixo^s, mag(:))+block%B0(ixo^s,:,b0i))**2, dim=ndim+1)
7543 else
7544 mge = sum(w(ixo^s, mag(:))**2, dim=ndim+1)
7545 end if
7546 end function mhd_mag_en_all
7547
7548 subroutine mhd_getv_hall(w,x,ixI^L,ixO^L,vHall,partial)
7550 use mod_geometry
7551
7552 integer, intent(in) :: ixi^l, ixo^l
7553 double precision, intent(in) :: w(ixi^s,nw)
7554 double precision, intent(in) :: x(ixi^s,1:ndim)
7555 double precision, intent(inout) :: vhall(ixi^s,1:ndir)
7556 logical, intent(in), optional :: partial
7557
7558 double precision :: current(ixi^s,7-2*ndir:3)
7559 double precision :: rho(ixi^s)
7560 integer :: idir, idirmin, ix^d
7561 logical :: use_partial
7562
7563 use_partial=.false.
7564 if(present(partial)) use_partial=partial
7565 call mhd_get_rho(w,x,ixi^l,ixo^l,rho)
7566 if(.not.use_partial)then
7567 ! Calculate current density and idirmin, including J0 when split
7568 call get_current(w,ixi^l,ixo^l,idirmin,current)
7569 else
7570 if(slab_uniform) then
7571 ! fourth order CD in cartesian
7572 call curlvector(w(ixi^s,mag(1:ndir)),ixi^l,ixo^l,current,idirmin,7-2*ndir,ndir,.true.)
7573 else
7574 call curlvector(w(ixi^s,mag(1:ndir)),ixi^l,ixo^l,current,idirmin,7-2*ndir,ndir)
7575 endif
7576 endif
7577 do idir = idirmin, ndir
7578 {do ix^db=ixomin^db,ixomax^db\}
7579 vhall(ix^d,idir)=-mhd_etah*current(ix^d,idir)/rho(ix^d)
7580 {end do\}
7581 end do
7582
7583 end subroutine mhd_getv_hall
7584
7585 subroutine mhd_modify_wlr(ixI^L,ixO^L,qt,wLC,wRC,wLp,wRp,s,idir)
7587 use mod_usr_methods
7588 integer, intent(in) :: ixi^l, ixo^l, idir
7589 double precision, intent(in) :: qt
7590 double precision, intent(inout) :: wlc(ixi^s,1:nw), wrc(ixi^s,1:nw)
7591 double precision, intent(inout) :: wlp(ixi^s,1:nw), wrp(ixi^s,1:nw)
7592 type(state) :: s
7593
7594 double precision :: db(ixo^s), dpsi(ixo^s)
7595 integer :: ix^d, saved_b0i
7596
7597 if(stagger_grid) then
7598 {do ix^db=ixomin^db,ixomax^db\}
7599 wlc(ix^d,mag(idir))=s%ws(ix^d,idir)
7600 wrc(ix^d,mag(idir))=s%ws(ix^d,idir)
7601 wlp(ix^d,mag(idir))=s%ws(ix^d,idir)
7602 wrp(ix^d,mag(idir))=s%ws(ix^d,idir)
7603 {end do\}
7604 else
7605 ! Solve the Riemann problem for the linear 2x2 system for normal
7606 ! B-field and GLM_Psi according to Dedner 2002:
7607 ! This implements eq. (42) in Dedner et al. 2002 JcP 175
7608 ! Gives the Riemann solution on the interface
7609 ! for the normal B component and Psi in the GLM-MHD system.
7610 ! 23/04/2013 Oliver Porth
7611 {do ix^db=ixomin^db,ixomax^db\}
7612 db(ix^d)=wrp(ix^d,mag(idir))-wlp(ix^d,mag(idir))
7613 dpsi(ix^d)=wrp(ix^d,psi_)-wlp(ix^d,psi_)
7614 wlp(ix^d,mag(idir))=half*(wrp(ix^d,mag(idir))+wlp(ix^d,mag(idir))-dpsi(ix^d)/cmax_global)
7615 wlp(ix^d,psi_)=half*(wrp(ix^d,psi_)+wlp(ix^d,psi_)-db(ix^d)*cmax_global)
7616 wrp(ix^d,mag(idir))=wlp(ix^d,mag(idir))
7617 wrp(ix^d,psi_)=wlp(ix^d,psi_)
7618 if(total_energy .and. .not.mhd_semirelativistic) then
7619 wrc(ix^d,e_)=wrc(ix^d,e_)-half*wrc(ix^d,mag(idir))**2
7620 wlc(ix^d,e_)=wlc(ix^d,e_)-half*wlc(ix^d,mag(idir))**2
7621 end if
7622 wrc(ix^d,mag(idir))=wlp(ix^d,mag(idir))
7623 wrc(ix^d,psi_)=wlp(ix^d,psi_)
7624 wlc(ix^d,mag(idir))=wlp(ix^d,mag(idir))
7625 wlc(ix^d,psi_)=wlp(ix^d,psi_)
7626 ! modify total energy according to the change of magnetic field
7627 if(total_energy .and. .not.mhd_semirelativistic) then
7628 wrc(ix^d,e_)=wrc(ix^d,e_)+half*wrc(ix^d,mag(idir))**2
7629 wlc(ix^d,e_)=wlc(ix^d,e_)+half*wlc(ix^d,mag(idir))**2
7630 end if
7631 {end do\}
7632 end if
7633
7634 if(mhd_semirelativistic) then
7635 ! CT/GLM changed the face field. Rebuild both momentum and energy from
7636 ! the same primitive state, including the electric terms and B0 split.
7637 saved_b0i=b0i
7638 b0i=idir
7639 wlc(ixo^s,1:nw)=wlp(ixo^s,1:nw)
7640 wrc(ixo^s,1:nw)=wrp(ixo^s,1:nw)
7641 call phys_to_conserved(ixi^l,ixo^l,wlc,s%x)
7642 call phys_to_conserved(ixi^l,ixo^l,wrc,s%x)
7643 b0i=saved_b0i
7644 end if
7645
7646 ! User overrides receive consistent states and remain responsible for
7647 ! keeping their primitive/conserved outputs mutually consistent.
7648 if(associated(usr_set_wlr)) call usr_set_wlr(ixi^l,ixo^l,qt,wlc,wrc,wlp,wrp,s,idir)
7649
7650 end subroutine mhd_modify_wlr
7651
7652 subroutine mhd_boundary_adjust(igrid,psb)
7654 integer, intent(in) :: igrid
7655 type(state), target :: psb(max_blocks)
7656
7657 integer :: ib, idims, iside, ixo^l, i^d
7658
7659 block=>ps(igrid)
7660 ^d&dxlevel(^d)=rnode(rpdx^d_,igrid);
7661 do idims=1,ndim
7662 ! to avoid using as yet unknown corner info in more than 1D, we
7663 ! fill only interior mesh ranges of the ghost cell ranges at first,
7664 ! and progressively enlarge the ranges to include corners later
7665 do iside=1,2
7666 i^d=kr(^d,idims)*(2*iside-3);
7667 if (neighbor_type(i^d,igrid)/=1) cycle
7668 ib=(idims-1)*2+iside
7669 if(.not.boundary_divbfix(ib)) cycle
7670 if(any(typeboundary(:,ib)==bc_special)) then
7671 ! MF nonlinear force-free B field extrapolation and data driven
7672 ! require normal B of the first ghost cell layer to be untouched by
7673 ! fixdivB=0 process, set boundary_divbfix_skip(iB)=1 in par file
7674 select case (idims)
7675 {case (^d)
7676 if (iside==2) then
7677 ! maximal boundary
7678 ixomin^dd=ixghi^d+1-nghostcells+boundary_divbfix_skip(2*^d)^d%ixOmin^dd=ixglo^dd;
7679 ixomax^dd=ixghi^dd;
7680 else
7681 ! minimal boundary
7682 ixomin^dd=ixglo^dd;
7683 ixomax^dd=ixglo^d-1+nghostcells-boundary_divbfix_skip(2*^d-1)^d%ixOmax^dd=ixghi^dd;
7684 end if \}
7685 end select
7686 call fixdivb_boundary(ixg^ll,ixo^l,psb(igrid)%w,psb(igrid)%x,ib)
7687 end if
7688 end do
7689 end do
7690
7691 end subroutine mhd_boundary_adjust
7692
7693 subroutine fixdivb_boundary(ixG^L,ixO^L,w,x,iB)
7695
7696 integer, intent(in) :: ixg^l,ixo^l,ib
7697 double precision, intent(inout) :: w(ixg^s,1:nw)
7698 double precision, intent(in) :: x(ixg^s,1:ndim)
7699
7700 double precision :: dx1x2,dx1x3,dx2x1,dx2x3,dx3x1,dx3x2
7701 integer :: ix^d,ixf^l
7702
7703 select case(ib)
7704 case(1)
7705 ! 2nd order CD for divB=0 to set normal B component better
7706 ! if(total_energy) call eos%to_primitive(ixG^L,ixO^L,w,x)
7707 if(total_energy) call eos%to_primitive(ixg^l,ixo^l,w,x)
7708 {^iftwod
7709 ixfmin1=ixomin1+1
7710 ixfmax1=ixomax1+1
7711 ixfmin2=ixomin2+1
7712 ixfmax2=ixomax2-1
7713 if(slab_uniform) then
7714 dx1x2=dxlevel(1)/dxlevel(2)
7715 do ix1=ixfmax1,ixfmin1,-1
7716 w(ix1-1,ixfmin2:ixfmax2,mag(1))=w(ix1+1,ixfmin2:ixfmax2,mag(1)) &
7717 +dx1x2*(w(ix1,ixfmin2+1:ixfmax2+1,mag(2))-&
7718 w(ix1,ixfmin2-1:ixfmax2-1,mag(2)))
7719 enddo
7720 else
7721 do ix1=ixfmax1,ixfmin1,-1
7722 w(ix1-1,ixfmin2:ixfmax2,mag(1))=( (w(ix1+1,ixfmin2:ixfmax2,mag(1))+&
7723 w(ix1,ixfmin2:ixfmax2,mag(1)))*block%surfaceC(ix1,ixfmin2:ixfmax2,1)&
7724 +(w(ix1,ixfmin2+1:ixfmax2+1,mag(2))+w(ix1,ixfmin2:ixfmax2,mag(2)))*&
7725 block%surfaceC(ix1,ixfmin2:ixfmax2,2)&
7726 -(w(ix1,ixfmin2:ixfmax2,mag(2))+w(ix1,ixfmin2-1:ixfmax2-1,mag(2)))*&
7727 block%surfaceC(ix1,ixfmin2-1:ixfmax2-1,2) )&
7728 /block%surfaceC(ix1-1,ixfmin2:ixfmax2,1)-w(ix1,ixfmin2:ixfmax2,mag(1))
7729 end do
7730 end if
7731 }
7732 {^ifthreed
7733 ixfmin1=ixomin1+1
7734 ixfmax1=ixomax1+1
7735 ixfmin2=ixomin2+1
7736 ixfmax2=ixomax2-1
7737 ixfmin3=ixomin3+1
7738 ixfmax3=ixomax3-1
7739 if(slab_uniform) then
7740 dx1x2=dxlevel(1)/dxlevel(2)
7741 dx1x3=dxlevel(1)/dxlevel(3)
7742 do ix1=ixfmax1,ixfmin1,-1
7743 w(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))=&
7744 w(ix1+1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1)) &
7745 +dx1x2*(w(ix1,ixfmin2+1:ixfmax2+1,ixfmin3:ixfmax3,mag(2))-&
7746 w(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,mag(2))) &
7747 +dx1x3*(w(ix1,ixfmin2:ixfmax2,ixfmin3+1:ixfmax3+1,mag(3))-&
7748 w(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,mag(3)))
7749 end do
7750 else
7751 do ix1=ixfmax1,ixfmin1,-1
7752 w(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))=&
7753 ( (w(ix1+1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))+&
7754 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1)))*&
7755 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,1)&
7756 +(w(ix1,ixfmin2+1:ixfmax2+1,ixfmin3:ixfmax3,mag(2))+&
7757 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(2)))*&
7758 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,2)&
7759 -(w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(2))+&
7760 w(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,mag(2)))*&
7761 block%surfaceC(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,2)&
7762 +(w(ix1,ixfmin2:ixfmax2,ixfmin3+1:ixfmax3+1,mag(3))+&
7763 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(3)))*&
7764 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,3)&
7765 -(w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(3))+&
7766 w(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,mag(3)))*&
7767 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,3) )&
7768 /block%surfaceC(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,1)-&
7769 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))
7770 end do
7771 end if
7772 }
7773 ! if(total_energy) call eos%to_conserved(ixG^L,ixO^L,w,x)
7774 if(total_energy) call eos%to_conserved(ixg^l,ixo^l,w,x)
7775 case(2)
7776 ! if(total_energy) call eos%to_primitive(ixG^L,ixO^L,w,x)
7777 if(total_energy) call eos%to_primitive(ixg^l,ixo^l,w,x)
7778 {^iftwod
7779 ixfmin1=ixomin1-1
7780 ixfmax1=ixomax1-1
7781 ixfmin2=ixomin2+1
7782 ixfmax2=ixomax2-1
7783 if(slab_uniform) then
7784 dx1x2=dxlevel(1)/dxlevel(2)
7785 do ix1=ixfmin1,ixfmax1
7786 w(ix1+1,ixfmin2:ixfmax2,mag(1))=w(ix1-1,ixfmin2:ixfmax2,mag(1)) &
7787 -dx1x2*(w(ix1,ixfmin2+1:ixfmax2+1,mag(2))-&
7788 w(ix1,ixfmin2-1:ixfmax2-1,mag(2)))
7789 enddo
7790 else
7791 do ix1=ixfmin1,ixfmax1
7792 w(ix1+1,ixfmin2:ixfmax2,mag(1))=( (w(ix1-1,ixfmin2:ixfmax2,mag(1))+&
7793 w(ix1,ixfmin2:ixfmax2,mag(1)))*block%surfaceC(ix1-1,ixfmin2:ixfmax2,1)&
7794 -(w(ix1,ixfmin2+1:ixfmax2+1,mag(2))+w(ix1,ixfmin2:ixfmax2,mag(2)))*&
7795 block%surfaceC(ix1,ixfmin2:ixfmax2,2)&
7796 +(w(ix1,ixfmin2:ixfmax2,mag(2))+w(ix1,ixfmin2-1:ixfmax2-1,mag(2)))*&
7797 block%surfaceC(ix1,ixfmin2-1:ixfmax2-1,2) )&
7798 /block%surfaceC(ix1,ixfmin2:ixfmax2,1)-w(ix1,ixfmin2:ixfmax2,mag(1))
7799 end do
7800 end if
7801 }
7802 {^ifthreed
7803 ixfmin1=ixomin1-1
7804 ixfmax1=ixomax1-1
7805 ixfmin2=ixomin2+1
7806 ixfmax2=ixomax2-1
7807 ixfmin3=ixomin3+1
7808 ixfmax3=ixomax3-1
7809 if(slab_uniform) then
7810 dx1x2=dxlevel(1)/dxlevel(2)
7811 dx1x3=dxlevel(1)/dxlevel(3)
7812 do ix1=ixfmin1,ixfmax1
7813 w(ix1+1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))=&
7814 w(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1)) &
7815 -dx1x2*(w(ix1,ixfmin2+1:ixfmax2+1,ixfmin3:ixfmax3,mag(2))-&
7816 w(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,mag(2))) &
7817 -dx1x3*(w(ix1,ixfmin2:ixfmax2,ixfmin3+1:ixfmax3+1,mag(3))-&
7818 w(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,mag(3)))
7819 end do
7820 else
7821 do ix1=ixfmin1,ixfmax1
7822 w(ix1+1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))=&
7823 ( (w(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))+&
7824 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1)))*&
7825 block%surfaceC(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,1)&
7826 -(w(ix1,ixfmin2+1:ixfmax2+1,ixfmin3:ixfmax3,mag(2))+&
7827 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(2)))*&
7828 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,2)&
7829 +(w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(2))+&
7830 w(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,mag(2)))*&
7831 block%surfaceC(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,2)&
7832 -(w(ix1,ixfmin2:ixfmax2,ixfmin3+1:ixfmax3+1,mag(3))+&
7833 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(3)))*&
7834 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,3)&
7835 +(w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(3))+&
7836 w(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,mag(3)))*&
7837 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,3) )&
7838 /block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,1)-&
7839 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))
7840 end do
7841 end if
7842 }
7843 ! if(total_energy) call eos%to_conserved(ixG^L,ixO^L,w,x)
7844 if(total_energy) call eos%to_conserved(ixg^l,ixo^l,w,x)
7845 case(3)
7846 ! if(total_energy) call eos%to_primitive(ixG^L,ixO^L,w,x)
7847 if(total_energy) call eos%to_primitive(ixg^l,ixo^l,w,x)
7848 {^iftwod
7849 ixfmin1=ixomin1+1
7850 ixfmax1=ixomax1-1
7851 ixfmin2=ixomin2+1
7852 ixfmax2=ixomax2+1
7853 if(slab_uniform) then
7854 dx2x1=dxlevel(2)/dxlevel(1)
7855 do ix2=ixfmax2,ixfmin2,-1
7856 w(ixfmin1:ixfmax1,ix2-1,mag(2))=w(ixfmin1:ixfmax1,ix2+1,mag(2)) &
7857 +dx2x1*(w(ixfmin1+1:ixfmax1+1,ix2,mag(1))-&
7858 w(ixfmin1-1:ixfmax1-1,ix2,mag(1)))
7859 enddo
7860 else
7861 do ix2=ixfmax2,ixfmin2,-1
7862 w(ixfmin1:ixfmax1,ix2-1,mag(2))=( (w(ixfmin1:ixfmax1,ix2+1,mag(2))+&
7863 w(ixfmin1:ixfmax1,ix2,mag(2)))*block%surfaceC(ixfmin1:ixfmax1,ix2,2)&
7864 +(w(ixfmin1+1:ixfmax1+1,ix2,mag(1))+w(ixfmin1:ixfmax1,ix2,mag(1)))*&
7865 block%surfaceC(ixfmin1:ixfmax1,ix2,1)&
7866 -(w(ixfmin1:ixfmax1,ix2,mag(1))+w(ixfmin1-1:ixfmax1-1,ix2,mag(1)))*&
7867 block%surfaceC(ixfmin1-1:ixfmax1-1,ix2,1) )&
7868 /block%surfaceC(ixfmin1:ixfmax1,ix2-1,2)-w(ixfmin1:ixfmax1,ix2,mag(2))
7869 end do
7870 end if
7871 }
7872 {^ifthreed
7873 ixfmin1=ixomin1+1
7874 ixfmax1=ixomax1-1
7875 ixfmin3=ixomin3+1
7876 ixfmax3=ixomax3-1
7877 ixfmin2=ixomin2+1
7878 ixfmax2=ixomax2+1
7879 if(slab_uniform) then
7880 dx2x1=dxlevel(2)/dxlevel(1)
7881 dx2x3=dxlevel(2)/dxlevel(3)
7882 do ix2=ixfmax2,ixfmin2,-1
7883 w(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,mag(2))=w(ixfmin1:ixfmax1,&
7884 ix2+1,ixfmin3:ixfmax3,mag(2)) &
7885 +dx2x1*(w(ixfmin1+1:ixfmax1+1,ix2,ixfmin3:ixfmax3,mag(1))-&
7886 w(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,mag(1))) &
7887 +dx2x3*(w(ixfmin1:ixfmax1,ix2,ixfmin3+1:ixfmax3+1,mag(3))-&
7888 w(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,mag(3)))
7889 end do
7890 else
7891 do ix2=ixfmax2,ixfmin2,-1
7892 w(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,mag(2))=&
7893 ( (w(ixfmin1:ixfmax1,ix2+1,ixfmin3:ixfmax3,mag(2))+&
7894 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(2)))*&
7895 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,2)&
7896 +(w(ixfmin1+1:ixfmax1+1,ix2,ixfmin3:ixfmax3,mag(1))+&
7897 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(1)))*&
7898 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,1)&
7899 -(w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(1))+&
7900 w(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,mag(1)))*&
7901 block%surfaceC(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,1)&
7902 +(w(ixfmin1:ixfmax1,ix2,ixfmin3+1:ixfmax3+1,mag(3))+&
7903 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(3)))*&
7904 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,3)&
7905 -(w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(3))+&
7906 w(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,mag(3)))*&
7907 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,3) )&
7908 /block%surfaceC(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,2)-&
7909 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(2))
7910 end do
7911 end if
7912 }
7913 ! if(total_energy) call eos%to_conserved(ixG^L,ixO^L,w,x)
7914 if(total_energy) call eos%to_conserved(ixg^l,ixo^l,w,x)
7915 case(4)
7916 ! if(total_energy) call eos%to_primitive(ixG^L,ixO^L,w,x)
7917 if(total_energy) call eos%to_primitive(ixg^l,ixo^l,w,x)
7918 {^iftwod
7919 ixfmin1=ixomin1+1
7920 ixfmax1=ixomax1-1
7921 ixfmin2=ixomin2-1
7922 ixfmax2=ixomax2-1
7923 if(slab_uniform) then
7924 dx2x1=dxlevel(2)/dxlevel(1)
7925 do ix2=ixfmin2,ixfmax2
7926 w(ixfmin1:ixfmax1,ix2+1,mag(2))=w(ixfmin1:ixfmax1,ix2-1,mag(2)) &
7927 -dx2x1*(w(ixfmin1+1:ixfmax1+1,ix2,mag(1))-&
7928 w(ixfmin1-1:ixfmax1-1,ix2,mag(1)))
7929 end do
7930 else
7931 do ix2=ixfmin2,ixfmax2
7932 w(ixfmin1:ixfmax1,ix2+1,mag(2))=( (w(ixfmin1:ixfmax1,ix2-1,mag(2))+&
7933 w(ixfmin1:ixfmax1,ix2,mag(2)))*block%surfaceC(ixfmin1:ixfmax1,ix2-1,2)&
7934 -(w(ixfmin1+1:ixfmax1+1,ix2,mag(1))+w(ixfmin1:ixfmax1,ix2,mag(1)))*&
7935 block%surfaceC(ixfmin1:ixfmax1,ix2,1)&
7936 +(w(ixfmin1:ixfmax1,ix2,mag(1))+w(ixfmin1-1:ixfmax1-1,ix2,mag(1)))*&
7937 block%surfaceC(ixfmin1-1:ixfmax1-1,ix2,1) )&
7938 /block%surfaceC(ixfmin1:ixfmax1,ix2,2)-w(ixfmin1:ixfmax1,ix2,mag(2))
7939 end do
7940 end if
7941 }
7942 {^ifthreed
7943 ixfmin1=ixomin1+1
7944 ixfmax1=ixomax1-1
7945 ixfmin3=ixomin3+1
7946 ixfmax3=ixomax3-1
7947 ixfmin2=ixomin2-1
7948 ixfmax2=ixomax2-1
7949 if(slab_uniform) then
7950 dx2x1=dxlevel(2)/dxlevel(1)
7951 dx2x3=dxlevel(2)/dxlevel(3)
7952 do ix2=ixfmin2,ixfmax2
7953 w(ixfmin1:ixfmax1,ix2+1,ixfmin3:ixfmax3,mag(2))=w(ixfmin1:ixfmax1,&
7954 ix2-1,ixfmin3:ixfmax3,mag(2)) &
7955 -dx2x1*(w(ixfmin1+1:ixfmax1+1,ix2,ixfmin3:ixfmax3,mag(1))-&
7956 w(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,mag(1))) &
7957 -dx2x3*(w(ixfmin1:ixfmax1,ix2,ixfmin3+1:ixfmax3+1,mag(3))-&
7958 w(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,mag(3)))
7959 end do
7960 else
7961 do ix2=ixfmin2,ixfmax2
7962 w(ixfmin1:ixfmax1,ix2+1,ixfmin3:ixfmax3,mag(2))=&
7963 ( (w(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,mag(2))+&
7964 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(2)))*&
7965 block%surfaceC(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,2)&
7966 -(w(ixfmin1+1:ixfmax1+1,ix2,ixfmin3:ixfmax3,mag(1))+&
7967 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(1)))*&
7968 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,1)&
7969 +(w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(1))+&
7970 w(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,mag(1)))*&
7971 block%surfaceC(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,1)&
7972 -(w(ixfmin1:ixfmax1,ix2,ixfmin3+1:ixfmax3+1,mag(3))+&
7973 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(3)))*&
7974 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,3)&
7975 +(w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(3))+&
7976 w(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,mag(3)))*&
7977 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,3) )&
7978 /block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,2)-&
7979 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(2))
7980 end do
7981 end if
7982 }
7983 ! if(total_energy) call eos%to_conserved(ixG^L,ixO^L,w,x)
7984 if(total_energy) call eos%to_conserved(ixg^l,ixo^l,w,x)
7985 {^ifthreed
7986 case(5)
7987 ! if(total_energy) call eos%to_primitive(ixG^L,ixO^L,w,x)
7988 if(total_energy) call eos%to_primitive(ixg^l,ixo^l,w,x)
7989 ixfmin1=ixomin1+1
7990 ixfmax1=ixomax1-1
7991 ixfmin2=ixomin2+1
7992 ixfmax2=ixomax2-1
7993 ixfmin3=ixomin3+1
7994 ixfmax3=ixomax3+1
7995 if(slab_uniform) then
7996 dx3x1=dxlevel(3)/dxlevel(1)
7997 dx3x2=dxlevel(3)/dxlevel(2)
7998 do ix3=ixfmax3,ixfmin3,-1
7999 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,mag(3))=w(ixfmin1:ixfmax1,&
8000 ixfmin2:ixfmax2,ix3+1,mag(3)) &
8001 +dx3x1*(w(ixfmin1+1:ixfmax1+1,ixfmin2:ixfmax2,ix3,mag(1))-&
8002 w(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,mag(1))) &
8003 +dx3x2*(w(ixfmin1:ixfmax1,ixfmin2+1:ixfmax2+1,ix3,mag(2))-&
8004 w(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,mag(2)))
8005 end do
8006 else
8007 do ix3=ixfmax3,ixfmin3,-1
8008 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,mag(3))=&
8009 ( (w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3+1,mag(3))+&
8010 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(3)))*&
8011 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,3)&
8012 +(w(ixfmin1+1:ixfmax1+1,ixfmin2:ixfmax2,ix3,mag(1))+&
8013 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(1)))*&
8014 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,1)&
8015 -(w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(1))+&
8016 w(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,mag(1)))*&
8017 block%surfaceC(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,1)&
8018 +(w(ixfmin1:ixfmax1,ixfmin2+1:ixfmax2+1,ix3,mag(2))+&
8019 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(2)))*&
8020 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,2)&
8021 -(w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(2))+&
8022 w(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,mag(2)))*&
8023 block%surfaceC(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,2) )&
8024 /block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,3)-&
8025 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(3))
8026 end do
8027 end if
8028 ! if(total_energy) call eos%to_conserved(ixG^L,ixO^L,w,x)
8029 if(total_energy) call eos%to_conserved(ixg^l,ixo^l,w,x)
8030 case(6)
8031 ! if(total_energy) call eos%to_primitive(ixG^L,ixO^L,w,x)
8032 if(total_energy) call eos%to_primitive(ixg^l,ixo^l,w,x)
8033 ixfmin1=ixomin1+1
8034 ixfmax1=ixomax1-1
8035 ixfmin2=ixomin2+1
8036 ixfmax2=ixomax2-1
8037 ixfmin3=ixomin3-1
8038 ixfmax3=ixomax3-1
8039 if(slab_uniform) then
8040 dx3x1=dxlevel(3)/dxlevel(1)
8041 dx3x2=dxlevel(3)/dxlevel(2)
8042 do ix3=ixfmin3,ixfmax3
8043 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3+1,mag(3))=w(ixfmin1:ixfmax1,&
8044 ixfmin2:ixfmax2,ix3-1,mag(3)) &
8045 -dx3x1*(w(ixfmin1+1:ixfmax1+1,ixfmin2:ixfmax2,ix3,mag(1))-&
8046 w(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,mag(1))) &
8047 -dx3x2*(w(ixfmin1:ixfmax1,ixfmin2+1:ixfmax2+1,ix3,mag(2))-&
8048 w(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,mag(2)))
8049 end do
8050 else
8051 do ix3=ixfmin3,ixfmax3
8052 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3+1,mag(3))=&
8053 ( (w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,mag(3))+&
8054 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(3)))*&
8055 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,3)&
8056 -(w(ixfmin1+1:ixfmax1+1,ixfmin2:ixfmax2,ix3,mag(1))+&
8057 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(1)))*&
8058 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,1)&
8059 +(w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(1))+&
8060 w(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,mag(1)))*&
8061 block%surfaceC(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,1)&
8062 -(w(ixfmin1:ixfmax1,ixfmin2+1:ixfmax2+1,ix3,mag(2))+&
8063 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(2)))*&
8064 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,2)&
8065 +(w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(2))+&
8066 w(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,mag(2)))*&
8067 block%surfaceC(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,2) )&
8068 /block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,3)-&
8069 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(3))
8070 end do
8071 end if
8072 ! if(total_energy) call eos%to_conserved(ixG^L,ixO^L,w,x)
8073 if(total_energy) call eos%to_conserved(ixg^l,ixo^l,w,x)
8074 }
8075 case default
8076 call mpistop("Special boundary is not defined for this region")
8077 end select
8078
8079 end subroutine fixdivb_boundary
8080
8081 {^nooned
8082 subroutine mhd_clean_divb_multigrid(qdt, qt, active)
8083 use mod_forest
8086 use mod_geometry
8087
8088 double precision, intent(in) :: qdt !< Current time step
8089 double precision, intent(in) :: qt !< Current time
8090 logical, intent(inout) :: active !< Output if the source is active
8091
8092 integer :: id
8093 integer, parameter :: max_its = 50
8094 double precision :: residual_it(max_its), max_divb
8095 double precision :: tmp(ixg^t), grad(ixg^t, ndim)
8096 double precision :: res
8097 double precision, parameter :: max_residual = 1d-3
8098 double precision, parameter :: residual_reduction = 1d-10
8099 integer :: iigrid, igrid
8100 integer :: n, nc, lvl, ix^l, ixc^l, idim
8101 type(tree_node), pointer :: pnode
8102
8103 mg%operator_type = mg_laplacian
8104
8105 ! Set boundary conditions
8106 do n = 1, 2*ndim
8107 idim = (n+1)/2
8108 select case (typeboundary(mag(idim), n))
8109 case (bc_symm)
8110 ! d/dx B = 0, take phi = 0
8111 mg%bc(n, mg_iphi)%bc_type = mg_bc_dirichlet
8112 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
8113 case (bc_asymm)
8114 ! B = 0, so grad(phi) = 0
8115 mg%bc(n, mg_iphi)%bc_type = mg_bc_neumann
8116 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
8117 case (bc_cont)
8118 mg%bc(n, mg_iphi)%bc_type = mg_bc_dirichlet
8119 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
8120 case (bc_special)
8121 ! Assume Dirichlet boundary conditions, derivative zero
8122 mg%bc(n, mg_iphi)%bc_type = mg_bc_dirichlet
8123 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
8124 case (bc_periodic)
8125 ! Nothing to do here
8126 case default
8127 write(*,*) "mhd_clean_divb_multigrid warning: unknown boundary type"
8128 mg%bc(n, mg_iphi)%bc_type = mg_bc_dirichlet
8129 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
8130 end select
8131 end do
8132
8133 ix^l=ixm^ll^ladd1;
8134 max_divb = 0.0d0
8135
8136 ! Store divergence of B as right-hand side
8137 do iigrid = 1, igridstail
8138 igrid = igrids(iigrid);
8139 pnode => igrid_to_node(igrid, mype)%node
8140 id = pnode%id
8141 lvl = mg%boxes(id)%lvl
8142 nc = mg%box_size_lvl(lvl)
8143
8144 ! Geometry subroutines expect this to be set
8145 block => ps(igrid)
8146 ^d&dxlevel(^d)=rnode(rpdx^d_,igrid);
8147
8148 call get_divb(ps(igrid)%w(ixg^t, 1:nw), ixg^ll, ixm^ll, tmp, &
8150 mg%boxes(id)%cc({1:nc}, mg_irhs) = tmp(ixm^t)
8151 max_divb = max(max_divb, maxval(abs(tmp(ixm^t))))
8152 end do
8153
8154 ! Solve laplacian(phi) = divB
8155 if(stagger_grid) then
8156 call mpi_allreduce(mpi_in_place, max_divb, 1, mpi_double_precision, &
8157 mpi_max, icomm, ierrmpi)
8158
8159 if (mype == 0) print *, "Performing multigrid divB cleaning"
8160 if (mype == 0) print *, "iteration vs residual"
8161 ! Solve laplacian(phi) = divB
8162 do n = 1, max_its
8163 call mg_fas_fmg(mg, n>1, max_res=residual_it(n))
8164 if (mype == 0) write(*, "(I4,E11.3)") n, residual_it(n)
8165 if (residual_it(n) < residual_reduction * max_divb) exit
8166 end do
8167 if (mype == 0 .and. n > max_its) then
8168 print *, "divb_multigrid warning: not fully converged"
8169 print *, "current amplitude of divb: ", residual_it(max_its)
8170 print *, "multigrid smallest grid: ", &
8171 mg%domain_size_lvl(:, mg%lowest_lvl)
8172 print *, "note: smallest grid ideally has <= 8 cells"
8173 print *, "multigrid dx/dy/dz ratio: ", mg%dr(:, 1)/mg%dr(1, 1)
8174 print *, "note: dx/dy/dz should be similar"
8175 end if
8176 else
8177 do n = 1, max_its
8178 call mg_fas_vcycle(mg, max_res=res)
8179 if (res < max_residual) exit
8180 end do
8181 if (res > max_residual) call mpistop("divb_multigrid: no convergence")
8182 end if
8183
8184
8185 ! Correct the magnetic field
8186 do iigrid = 1, igridstail
8187 igrid = igrids(iigrid);
8188 pnode => igrid_to_node(igrid, mype)%node
8189 id = pnode%id
8190
8191 ! Geometry subroutines expect this to be set
8192 block => ps(igrid)
8193 ^d&dxlevel(^d)=rnode(rpdx^d_,igrid);
8194
8195 ! Compute the gradient of phi
8196 tmp(ix^s) = mg%boxes(id)%cc({:,}, mg_iphi)
8197
8198 if(stagger_grid) then
8199 do idim =1, ndim
8200 ixcmin^d=ixmlo^d-kr(idim,^d);
8201 ixcmax^d=ixmhi^d;
8202 call gradientf(tmp,ps(igrid)%x,ixg^ll,ixc^l,idim,grad(ixg^t,idim))
8203 ! Apply the correction B* = B - gradient(phi)
8204 ps(igrid)%ws(ixc^s,idim)=ps(igrid)%ws(ixc^s,idim)-grad(ixc^s,idim)
8205 end do
8206 ! store cell-center magnetic energy
8207 tmp(ixm^t) = sum(ps(igrid)%w(ixm^t, mag(1:ndim))**2, dim=ndim+1)
8208 ! change cell-center magnetic field
8209 call mhd_face_to_center(ixm^ll,ps(igrid))
8210 else
8211 do idim = 1, ndim
8212 call gradient(tmp,ixg^ll,ixm^ll,idim,grad(ixg^t, idim))
8213 end do
8214 ! store cell-center magnetic energy
8215 tmp(ixm^t) = sum(ps(igrid)%w(ixm^t, mag(1:ndim))**2, dim=ndim+1)
8216 ! Apply the correction B* = B - gradient(phi)
8217 ps(igrid)%w(ixm^t, mag(1:ndim)) = &
8218 ps(igrid)%w(ixm^t, mag(1:ndim)) - grad(ixm^t, :)
8219 end if
8220
8221 if(total_energy) then
8222 ! Determine magnetic energy difference
8223 tmp(ixm^t) = 0.5_dp * (sum(ps(igrid)%w(ixm^t, &
8224 mag(1:ndim))**2, dim=ndim+1) - tmp(ixm^t))
8225 ! Keep thermal pressure the same
8226 ps(igrid)%w(ixm^t, e_) = ps(igrid)%w(ixm^t, e_) + tmp(ixm^t)
8227 end if
8228 end do
8229
8230 active = .true.
8231
8232 end subroutine mhd_clean_divb_multigrid
8233 }
8234
8235 !> get electric field through averaging neighors to update faces in CT
8236 subroutine mhd_update_faces_average(ixI^L,ixO^L,qt,qdt,wp,fC,fE,sCT,s,vcts)
8238 use mod_usr_methods
8239
8240 integer, intent(in) :: ixi^l, ixo^l
8241 double precision, intent(in) :: qt,qdt
8242 ! cell-center primitive variables
8243 double precision, intent(in) :: wp(ixi^s,1:nw)
8244 type(state) :: sct, s
8245 type(ct_velocity) :: vcts
8246 double precision, intent(in) :: fc(ixi^s,1:nwflux,1:ndim)
8247 double precision, intent(inout) :: fe(ixi^s,sdim:3)
8248
8249 double precision :: circ(ixi^s,1:ndim)
8250 ! non-ideal electric field on cell edges
8251 double precision, dimension(ixI^S,sdim:3) :: e_resi, e_ambi
8252 integer :: ix^d,ixc^l,ixa^l,i1kr^d,i2kr^d
8253 integer :: idim1,idim2,idir,iwdim1,iwdim2
8254
8255 associate(bfaces=>s%ws,x=>s%x)
8256
8257 ! Calculate contribution to FEM of each edge,
8258 ! that is, estimate value of line integral of
8259 ! electric field in the positive idir direction.
8260
8261 ! if there is resistivity, get eta J
8262 if(mhd_eta/=zero) call get_resistive_electric_field(ixi^l,ixo^l,wp,sct,s,e_resi)
8263
8264 ! if there is ambipolar diffusion, get E_ambi
8265 if(mhd_ambipolar_exp) call get_ambipolar_electric_field(ixi^l,ixo^l,sct%w,x,e_ambi)
8266
8267 do idim1=1,ndim
8268 iwdim1 = mag(idim1)
8269 i1kr^d=kr(idim1,^d);
8270 do idim2=1,ndim
8271 iwdim2 = mag(idim2)
8272 i2kr^d=kr(idim2,^d);
8273 do idir=sdim,3! Direction of line integral
8274 ! Allow only even permutations
8275 if (lvc(idim1,idim2,idir)==1) then
8276 ixcmax^d=ixomax^d;
8277 ixcmin^d=ixomin^d+kr(idir,^d)-1;
8278 ! average cell-face electric field to cell edges
8279 {do ix^db=ixcmin^db,ixcmax^db\}
8280 fe(ix^d,idir)=quarter*&
8281 (fc(ix^d,iwdim1,idim2)+fc({ix^d+i1kr^d},iwdim1,idim2)&
8282 -fc(ix^d,iwdim2,idim1)-fc({ix^d+i2kr^d},iwdim2,idim1))
8283 ! add resistive electric field at cell edges E=-vxB+eta J
8284 if(mhd_eta/=zero) fe(ix^d,idir)=fe(ix^d,idir)+e_resi(ix^d,idir)
8285 ! add ambipolar electric field
8286 if(mhd_ambipolar_exp) fe(ix^d,idir)=fe(ix^d,idir)+e_ambi(ix^d,idir)
8287
8288 ! times time step and edge length
8289 fe(ix^d,idir)=fe(ix^d,idir)*qdt*s%dsC(ix^d,idir)
8290 {end do\}
8291 end if
8292 end do
8293 end do
8294 end do
8295
8296 ! allow user to change inductive electric field, especially for boundary driven applications
8297 if(associated(usr_set_electric_field)) &
8298 call usr_set_electric_field(ixi^l,ixo^l,qt,qdt,fe,sct)
8299
8300 circ(ixi^s,1:ndim)=zero
8301
8302 ! Calculate circulation on each face
8303 do idim1=1,ndim ! Coordinate perpendicular to face
8304 ixcmax^d=ixomax^d;
8305 ixcmin^d=ixomin^d-kr(idim1,^d);
8306 do idim2=1,ndim
8307 ixa^l=ixc^l-kr(idim2,^d);
8308 do idir=sdim,3 ! Direction of line integral
8309 ! Assemble indices
8310 if(lvc(idim1,idim2,idir)==1) then
8311 ! Add line integrals in direction idir
8312 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
8313 +(fe(ixc^s,idir)&
8314 -fe(ixa^s,idir))
8315 else if(lvc(idim1,idim2,idir)==-1) then
8316 ! Add line integrals in direction idir
8317 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
8318 -(fe(ixc^s,idir)&
8319 -fe(ixa^s,idir))
8320 end if
8321 end do
8322 end do
8323 {do ix^db=ixcmin^db,ixcmax^db\}
8324 ! Divide by the area of the face to get dB/dt
8325 if(s%surfaceC(ix^d,idim1) > smalldouble) then
8326 ! Time update cell-face magnetic field component
8327 bfaces(ix^d,idim1)=bfaces(ix^d,idim1)-circ(ix^d,idim1)/s%surfaceC(ix^d,idim1)
8328 end if
8329 {end do\}
8330 end do
8331
8332 end associate
8333
8334 end subroutine mhd_update_faces_average
8335
8336 !> update faces using UCT contact mode by Gardiner and Stone 2005 JCP 205, 509
8337 subroutine mhd_update_faces_contact(ixI^L,ixO^L,qt,qdt,wp,fC,fE,sCT,s,vcts)
8339 use mod_usr_methods
8340 use mod_geometry
8341
8342 integer, intent(in) :: ixi^l, ixo^l
8343 double precision, intent(in) :: qt, qdt
8344 ! cell-center primitive variables
8345 double precision, intent(in) :: wp(ixi^s,1:nw)
8346 type(state) :: sct, s
8347 type(ct_velocity) :: vcts
8348 double precision, intent(in) :: fc(ixi^s,1:nwflux,1:ndim)
8349 double precision, intent(inout) :: fe(ixi^s,sdim:3)
8350
8351 double precision :: circ(ixi^s,1:ndim)
8352 ! electric field at cell centers
8353 double precision :: ecc(ixi^s,sdim:3)
8354 double precision :: ein(ixi^s,sdim:3)
8355 ! gradient of E at left and right side of a cell face
8356 double precision :: el(ixi^s),er(ixi^s)
8357 ! gradient of E at left and right side of a cell corner
8358 double precision :: elc,erc
8359 ! non-ideal electric field on cell edges
8360 double precision, dimension(ixI^S,sdim:3) :: e_resi, e_ambi
8361 ! current on cell edges
8362 double precision :: jce(ixi^s,sdim:3)
8363 ! location at cell faces
8364 double precision :: xs(ixgs^t,1:ndim)
8365 double precision :: gradi(ixgs^t)
8366 double precision :: resis_heat
8367 integer :: ixc^l,ixa^l
8368 integer :: idim1,idim2,idir,iwdim1,iwdim2,ix^d,i1kr^d,i2kr^d
8369
8370 associate(bfaces=>s%ws,x=>s%x,w=>s%w,vnorm=>vcts%vnorm,wcts=>sct%ws)
8371
8372 ! if there is resistivity, get eta J
8373 if(mhd_eta/=zero) call get_resistive_electric_field(ixi^l,ixo^l,wp,sct,s,e_resi)
8374
8375 ! if there is ambipolar diffusion, get E_ambi
8376 if(mhd_ambipolar_exp) call get_ambipolar_electric_field(ixi^l,ixo^l,sct%w,x,e_ambi)
8377
8378 if(b0field) then
8379 {do ix^db=iximin^db,iximax^db\}
8380 ! Calculate electric field at cell centers
8381 {^ifthreed
8382 ecc(ix^d,1)=(wp(ix^d,b2_)+block%B0(ix^d,2,0))*wp(ix^d,m3_)-(wp(ix^d,b3_)+block%B0(ix^d,3,0))*wp(ix^d,m2_)
8383 ecc(ix^d,2)=(wp(ix^d,b3_)+block%B0(ix^d,3,0))*wp(ix^d,m1_)-(wp(ix^d,b1_)+block%B0(ix^d,1,0))*wp(ix^d,m3_)
8384 ecc(ix^d,3)=(wp(ix^d,b1_)+block%B0(ix^d,1,0))*wp(ix^d,m2_)-(wp(ix^d,b2_)+block%B0(ix^d,2,0))*wp(ix^d,m1_)
8385 }
8386 {^iftwod
8387 ecc(ix^d,3)=(wp(ix^d,b1_)+block%B0(ix^d,1,0))*wp(ix^d,m2_) &
8388 -(wp(ix^d,b2_)+block%B0(ix^d,2,0))*wp(ix^d,m1_)
8389 }
8390 {^ifoned
8391 ecc(ix^d,3)=0.d0
8392 }
8393 {end do\}
8394 else
8395 {do ix^db=iximin^db,iximax^db\}
8396 ! Calculate electric field at cell centers
8397 {^ifthreed
8398 ecc(ix^d,1)=wp(ix^d,b2_)*wp(ix^d,m3_)-wp(ix^d,b3_)*wp(ix^d,m2_)
8399 ecc(ix^d,2)=wp(ix^d,b3_)*wp(ix^d,m1_)-wp(ix^d,b1_)*wp(ix^d,m3_)
8400 ecc(ix^d,3)=wp(ix^d,b1_)*wp(ix^d,m2_)-wp(ix^d,b2_)*wp(ix^d,m1_)
8401 }
8402 {^iftwod
8403 ecc(ix^d,3)=wp(ix^d,b1_)*wp(ix^d,m2_)-wp(ix^d,b2_)*wp(ix^d,m1_)
8404 }
8405 {^ifoned
8406 ecc(ix^d,3)=0.d0
8407 }
8408 {end do\}
8409 end if
8410
8411 ! Calculate contribution to FEM of each edge,
8412 ! that is, estimate value of line integral of
8413 ! electric field in the positive idir direction.
8414 ! evaluate electric field along cell edges according to equation (41)
8415 do idim1=1,ndim
8416 iwdim1 = mag(idim1)
8417 i1kr^d=kr(idim1,^d);
8418 do idim2=1,ndim
8419 iwdim2 = mag(idim2)
8420 i2kr^d=kr(idim2,^d);
8421 do idir=sdim,3 ! Direction of line integral
8422 ! Allow only even permutations
8423 if (lvc(idim1,idim2,idir)==1) then
8424 ixcmax^d=ixomax^d;
8425 ixcmin^d=ixomin^d+kr(idir,^d)-1;
8426 ! Assemble indices
8427 ! average cell-face electric field to cell edges
8428 {do ix^db=ixcmin^db,ixcmax^db\}
8429 fe(ix^d,idir)=quarter*&
8430 (fc(ix^d,iwdim1,idim2)+fc({ix^d+i1kr^d},iwdim1,idim2)&
8431 -fc(ix^d,iwdim2,idim1)-fc({ix^d+i2kr^d},iwdim2,idim1))
8432 if(numerical_resistive_heating) ein(ix^d,idir)=fe(ix^d,idir)
8433 {end do\}
8434 ! add slope in idim2 direction from equation (50)
8435 ixamin^d=ixcmin^d;
8436 ixamax^d=ixcmax^d+i1kr^d;
8437 {do ix^db=ixamin^db,ixamax^db\}
8438 el(ix^d)=fc(ix^d,iwdim1,idim2)-ecc(ix^d,idir)
8439 er(ix^d)=fc(ix^d,iwdim1,idim2)-ecc({ix^d+i2kr^d},idir)
8440 {end do\}
8441 {!dir$ ivdep
8442 do ix^db=ixcmin^db,ixcmax^db\}
8443 if(vnorm(ix^d,idim1)>0.d0) then
8444 elc=el(ix^d)
8445 else if(vnorm(ix^d,idim1)<0.d0) then
8446 elc=el({ix^d+i1kr^d})
8447 else
8448 elc=0.5d0*(el(ix^d)+el({ix^d+i1kr^d}))
8449 end if
8450 if(vnorm({ix^d+i2kr^d},idim1)>0.d0) then
8451 erc=er(ix^d)
8452 else if(vnorm({ix^d+i2kr^d},idim1)<0.d0) then
8453 erc=er({ix^d+i1kr^d})
8454 else
8455 erc=0.5d0*(er(ix^d)+er({ix^d+i1kr^d}))
8456 end if
8457 fe(ix^d,idir)=fe(ix^d,idir)+0.25d0*(elc+erc)
8458 {end do\}
8459
8460 ! add slope in idim1 direction from equation (50)
8461 ixamin^d=ixcmin^d;
8462 ixamax^d=ixcmax^d+i2kr^d;
8463 {do ix^db=ixamin^db,ixamax^db\}
8464 el(ix^d)=-fc(ix^d,iwdim2,idim1)-ecc(ix^d,idir)
8465 er(ix^d)=-fc(ix^d,iwdim2,idim1)-ecc({ix^d+i1kr^d},idir)
8466 {end do\}
8467 {!dir$ ivdep
8468 do ix^db=ixcmin^db,ixcmax^db\}
8469 if(vnorm(ix^d,idim2)>0.d0) then
8470 elc=el(ix^d)
8471 else if(vnorm(ix^d,idim2)<0.d0) then
8472 elc=el({ix^d+i2kr^d})
8473 else
8474 elc=0.5d0*(el(ix^d)+el({ix^d+i2kr^d}))
8475 end if
8476 if(vnorm({ix^d+i1kr^d},idim2)>0.d0) then
8477 erc=er(ix^d)
8478 else if(vnorm({ix^d+i1kr^d},idim2)<0.d0) then
8479 erc=er({ix^d+i2kr^d})
8480 else
8481 erc=0.5d0*(er(ix^d)+er({ix^d+i2kr^d}))
8482 end if
8483 fe(ix^d,idir)=fe(ix^d,idir)+0.25d0*(elc+erc)
8484 ! difference between average and upwind interpolated E
8485 if(numerical_resistive_heating) ein(ix^d,idir)=fe(ix^d,idir)-ein(ix^d,idir)
8486 ! add resistive electric field at cell edges E=-vxB+eta J
8487 if(mhd_eta/=zero) fe(ix^d,idir)=fe(ix^d,idir)+e_resi(ix^d,idir)
8488 ! add ambipolar electric field
8489 if(mhd_ambipolar_exp) fe(ix^d,idir)=fe(ix^d,idir)+e_ambi(ix^d,idir)
8490
8491 ! times time step and edge length
8492 fe(ix^d,idir)=fe(ix^d,idir)*qdt*s%dsC(ix^d,idir)
8493 {end do\}
8494 end if
8495 end do
8496 end do
8497 end do
8498
8500 ! add upwind diffused magnetic energy back to energy
8501 ! calculate current density at cell edges
8502 jce=0.d0
8503 do idim1=1,ndim
8504 do idim2=1,ndim
8505 do idir=sdim,3
8506 if (lvc(idim1,idim2,idir)==0) cycle
8507 ixcmax^d=ixomax^d;
8508 ixcmin^d=ixomin^d+kr(idir,^d)-1;
8509 ixamax^d=ixcmax^d-kr(idir,^d)+1;
8510 ixamin^d=ixcmin^d;
8511 ! current at transverse faces
8512 xs(ixa^s,:)=x(ixa^s,:)
8513 xs(ixa^s,idim2)=x(ixa^s,idim2)+half*s%dx(ixa^s,idim2)
8514 call gradientf(wcts(ixgs^t,idim2),xs,ixgs^ll,ixc^l,idim1,gradi)
8515 if (lvc(idim1,idim2,idir)==1) then
8516 jce(ixc^s,idir)=jce(ixc^s,idir)+gradi(ixc^s)
8517 else
8518 jce(ixc^s,idir)=jce(ixc^s,idir)-gradi(ixc^s)
8519 end if
8520 end do
8521 end do
8522 end do
8523 {^ifthreed
8524 {do ix^db=ixomin^db,ixomax^db\}
8525 resis_heat=0.25d0*(ein(ix^d,1)*jce(ix^d,1)+ein(ix1,ix2-1,ix3,1)*jce(ix1,ix2-1,ix3,1)&
8526 +ein(ix1,ix2,ix3-1,1)*jce(ix1,ix2,ix3-1,1)+ein(ix1,ix2-1,ix3-1,1)*jce(ix1,ix2-1,ix3-1,1))&
8527 +0.25d0*(ein(ix^d,2)*jce(ix^d,2)+ein(ix1-1,ix2,ix3,2)*jce(ix1-1,ix2,ix3,2)&
8528 +ein(ix1,ix2,ix3-1,2)*jce(ix1,ix2,ix3-1,2)+ein(ix1-1,ix2,ix3-1,2)*jce(ix1-1,ix2,ix3-1,2))&
8529 +0.25d0*(ein(ix^d,3)*jce(ix^d,3)+ein(ix1-1,ix2,ix3,3)*jce(ix1-1,ix2,ix3,3)&
8530 +ein(ix1,ix2-1,ix3,3)*jce(ix1,ix2-1,ix3,3)+ein(ix1-1,ix2-1,ix3,3)*jce(ix1-1,ix2-1,ix3,3))
8531 if(resis_heat<0.d0) resis_heat=0.d0
8532 w(ix^d,e_)=w(ix^d,e_)+qdt*resis_heat
8533 !if(nwextra>0) block%w(ix^D,nw)=resis_heat
8534 {end do\}
8535 }
8536 {^iftwod
8537 {do ix^db=ixomin^db,ixomax^db\}
8538 resis_heat=0.25d0*(ein(ix^d,3)*jce(ix^d,3)+ein(ix1-1,ix2,3)*jce(ix1-1,ix2,3)&
8539 +ein(ix1,ix2-1,3)*jce(ix1,ix2-1,3)+ein(ix1-1,ix2-1,3)*jce(ix1-1,ix2-1,3))
8540 if(resis_heat<0.d0) resis_heat=0.d0
8541 w(ix^d,e_)=w(ix^d,e_)+qdt*resis_heat
8542 !if(nwextra>0) block%w(ix^D,nw)=resis_heat
8543 {end do\}
8544 }
8545 end if
8546
8547 ! allow user to change inductive electric field, especially for boundary driven applications
8548 if(associated(usr_set_electric_field)) &
8549 call usr_set_electric_field(ixi^l,ixo^l,qt,qdt,fe,sct)
8550
8551 circ(ixi^s,1:ndim)=zero
8552
8553 ! Calculate circulation on each face
8554 do idim1=1,ndim ! Coordinate perpendicular to face
8555 ixcmax^d=ixomax^d;
8556 ixcmin^d=ixomin^d-kr(idim1,^d);
8557 do idim2=1,ndim
8558 ixa^l=ixc^l-kr(idim2,^d);
8559 do idir=sdim,3 ! Direction of line integral
8560 ! Assemble indices
8561 if(lvc(idim1,idim2,idir)==1) then
8562 ! Add line integrals in direction idir
8563 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
8564 +(fe(ixc^s,idir)&
8565 -fe(ixa^s,idir))
8566 else if(lvc(idim1,idim2,idir)==-1) then
8567 ! Add line integrals in direction idir
8568 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
8569 -(fe(ixc^s,idir)&
8570 -fe(ixa^s,idir))
8571 end if
8572 end do
8573 end do
8574 {do ix^db=ixcmin^db,ixcmax^db\}
8575 ! Divide by the area of the face to get dB/dt
8576 if(s%surfaceC(ix^d,idim1) > smalldouble) then
8577 ! Time update cell-face magnetic field component
8578 bfaces(ix^d,idim1)=bfaces(ix^d,idim1)-circ(ix^d,idim1)/s%surfaceC(ix^d,idim1)
8579 end if
8580 {end do\}
8581 end do
8582
8583 end associate
8584
8585 end subroutine mhd_update_faces_contact
8586
8587 !> update faces
8588 subroutine mhd_update_faces_hll(ixI^L,ixO^L,qt,qdt,wp,fC,fE,sCT,s,vcts)
8590 use mod_usr_methods
8592
8593 integer, intent(in) :: ixi^l, ixo^l
8594 double precision, intent(in) :: qt, qdt
8595 ! cell-center primitive variables
8596 double precision, intent(in) :: wp(ixi^s,1:nw)
8597 type(state) :: sct, s
8598 type(ct_velocity) :: vcts
8599 double precision, intent(in) :: fc(ixi^s,1:nwflux,1:ndim)
8600 double precision, intent(inout) :: fe(ixi^s,sdim:3)
8601
8602 double precision :: vtill(ixi^s,2)
8603 double precision :: vtilr(ixi^s,2)
8604 double precision :: bfacetot(ixi^s,ndim)
8605 double precision :: btill(ixi^s,ndim)
8606 double precision :: btilr(ixi^s,ndim)
8607 double precision :: cp(ixi^s,2)
8608 double precision :: cm(ixi^s,2)
8609 double precision :: circ(ixi^s,1:ndim)
8610 ! non-ideal electric field on cell edges
8611 double precision, dimension(ixI^S,sdim:3) :: e_resi, e_ambi
8612 integer :: hxc^l,ixc^l,ixcp^l,jxc^l,ixcm^l
8613 integer :: idim1,idim2,idir,ix^d
8614
8615 associate(bfaces=>s%ws,bfacesct=>sct%ws,x=>s%x,vbarc=>vcts%vbarC,cbarmin=>vcts%cbarmin,&
8616 cbarmax=>vcts%cbarmax)
8617
8618 ! Calculate contribution to FEM of each edge,
8619 ! that is, estimate value of line integral of
8620 ! electric field in the positive idir direction.
8621
8622 ! Loop over components of electric field
8623
8624 ! idir: electric field component we need to calculate
8625 ! idim1: directions in which we already performed the reconstruction
8626 ! idim2: directions in which we perform the reconstruction
8627
8628 ! if there is resistivity, get eta J
8629 if(mhd_eta/=zero) call get_resistive_electric_field(ixi^l,ixo^l,wp,sct,s,e_resi)
8630
8631 ! if there is ambipolar diffusion, get E_ambi
8632 if(mhd_ambipolar_exp) call get_ambipolar_electric_field(ixi^l,ixo^l,sct%w,x,e_ambi)
8633
8634 do idir=sdim,3
8635 ! Indices
8636 ! idir: electric field component
8637 ! idim1: one surface
8638 ! idim2: the other surface
8639 ! cyclic permutation: idim1,idim2,idir=1,2,3
8640 ! Velocity components on the surface
8641 ! follow cyclic premutations:
8642 ! Sx(1),Sx(2)=y,z ; Sy(1),Sy(2)=z,x ; Sz(1),Sz(2)=x,y
8643
8644 ixcmax^d=ixomax^d;
8645 ixcmin^d=ixomin^d-1+kr(idir,^d);
8646
8647 ! Set indices and directions
8648 idim1=mod(idir,3)+1
8649 idim2=mod(idir+1,3)+1
8650
8651 jxc^l=ixc^l+kr(idim1,^d);
8652 ixcp^l=ixc^l+kr(idim2,^d);
8653
8654 ! Reconstruct transverse transport velocities
8655 call reconstruct(ixi^l,ixc^l,idim2,vbarc(ixi^s,idim1,1),&
8656 vtill(ixi^s,2),vtilr(ixi^s,2))
8657
8658 call reconstruct(ixi^l,ixc^l,idim1,vbarc(ixi^s,idim2,2),&
8659 vtill(ixi^s,1),vtilr(ixi^s,1))
8660
8661 ! Reconstruct magnetic fields
8662 ! Eventhough the arrays are larger, reconstruct works with
8663 ! the limits ixG.
8664 if(b0field) then
8665 bfacetot(ixi^s,idim1)=bfacesct(ixi^s,idim1)+block%B0(ixi^s,idim1,idim1)
8666 bfacetot(ixi^s,idim2)=bfacesct(ixi^s,idim2)+block%B0(ixi^s,idim2,idim2)
8667 else
8668 bfacetot(ixi^s,idim1)=bfacesct(ixi^s,idim1)
8669 bfacetot(ixi^s,idim2)=bfacesct(ixi^s,idim2)
8670 end if
8671 call reconstruct(ixi^l,ixc^l,idim2,bfacetot(ixi^s,idim1),&
8672 btill(ixi^s,idim1),btilr(ixi^s,idim1))
8673
8674 call reconstruct(ixi^l,ixc^l,idim1,bfacetot(ixi^s,idim2),&
8675 btill(ixi^s,idim2),btilr(ixi^s,idim2))
8676
8677 ! Take the maximum characteristic
8678
8679 cm(ixc^s,1)=max(cbarmin(ixcp^s,idim1),cbarmin(ixc^s,idim1))
8680 cp(ixc^s,1)=max(cbarmax(ixcp^s,idim1),cbarmax(ixc^s,idim1))
8681
8682 cm(ixc^s,2)=max(cbarmin(jxc^s,idim2),cbarmin(ixc^s,idim2))
8683 cp(ixc^s,2)=max(cbarmax(jxc^s,idim2),cbarmax(ixc^s,idim2))
8684
8685
8686 ! Calculate eletric field
8687 fe(ixc^s,idir)=-(cp(ixc^s,1)*vtill(ixc^s,1)*btill(ixc^s,idim2) &
8688 + cm(ixc^s,1)*vtilr(ixc^s,1)*btilr(ixc^s,idim2) &
8689 - cp(ixc^s,1)*cm(ixc^s,1)*(btilr(ixc^s,idim2)-btill(ixc^s,idim2)))&
8690 /(cp(ixc^s,1)+cm(ixc^s,1)) &
8691 +(cp(ixc^s,2)*vtill(ixc^s,2)*btill(ixc^s,idim1) &
8692 + cm(ixc^s,2)*vtilr(ixc^s,2)*btilr(ixc^s,idim1) &
8693 - cp(ixc^s,2)*cm(ixc^s,2)*(btilr(ixc^s,idim1)-btill(ixc^s,idim1)))&
8694 /(cp(ixc^s,2)+cm(ixc^s,2))
8695
8696 ! add resistive electric field at cell edges E=-vxB+eta J
8697 if(mhd_eta/=zero) fe(ixc^s,idir)=fe(ixc^s,idir)+e_resi(ixc^s,idir)
8698 ! add ambipolar electric field
8699 if(mhd_ambipolar_exp) fe(ixc^s,idir)=fe(ixc^s,idir)+e_ambi(ixc^s,idir)
8700
8701 fe(ixc^s,idir)=qdt*s%dsC(ixc^s,idir)*fe(ixc^s,idir)
8702
8703 if (.not.slab) then
8704 where(abs(x(ixc^s,r_)+half*dxlevel(r_)).lt.1.0d-9)
8705 fe(ixc^s,idir)=zero
8706 end where
8707 end if
8708
8709 end do
8710
8711 ! allow user to change inductive electric field, especially for boundary driven applications
8712 if(associated(usr_set_electric_field)) &
8713 call usr_set_electric_field(ixi^l,ixo^l,qt,qdt,fe,sct)
8714
8715 circ(ixi^s,1:ndim)=zero
8716
8717 ! Calculate circulation on each face: interal(fE dot dl)
8718 do idim1=1,ndim ! Coordinate perpendicular to face
8719 ixcmax^d=ixomax^d;
8720 ixcmin^d=ixomin^d-kr(idim1,^d);
8721 do idim2=1,ndim
8722 do idir=sdim,3 ! Direction of line integral
8723 ! Assemble indices
8724 if(lvc(idim1,idim2,idir)/=0) then
8725 hxc^l=ixc^l-kr(idim2,^d);
8726 ! Add line integrals in direction idir
8727 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
8728 +lvc(idim1,idim2,idir)&
8729 *(fe(ixc^s,idir)&
8730 -fe(hxc^s,idir))
8731 end if
8732 end do
8733 end do
8734 {do ix^db=ixcmin^db,ixcmax^db\}
8735 ! Divide by the area of the face to get dB/dt
8736 if(s%surfaceC(ix^d,idim1) > smalldouble) then
8737 ! Time update cell-face magnetic field component
8738 bfaces(ix^d,idim1)=bfaces(ix^d,idim1)-circ(ix^d,idim1)/s%surfaceC(ix^d,idim1)
8739 end if
8740 {end do\}
8741 end do
8742
8743 end associate
8744 end subroutine mhd_update_faces_hll
8745
8746 !> calculate eta J at cell edges
8747 subroutine get_resistive_electric_field(ixI^L,ixO^L,wp,sCT,s,jce)
8749 use mod_usr_methods
8750 use mod_geometry
8751
8752 integer, intent(in) :: ixi^l, ixo^l
8753 ! cell-center primitive variables
8754 double precision, intent(in) :: wp(ixi^s,1:nw)
8755 type(state), intent(in) :: sct, s
8756 ! current on cell edges
8757 double precision :: jce(ixi^s,sdim:3)
8758
8759 ! current on cell centers
8760 double precision :: jcc(ixi^s,7-2*ndir:3)
8761 ! location at cell faces
8762 double precision :: xs(ixgs^t,1:ndim)
8763 ! resistivity
8764 double precision :: eta(ixi^s)
8765 double precision :: gradi(ixgs^t)
8766 integer :: ix^d,ixc^l,ixa^l,ixb^l,idir,idirmin,idim1,idim2
8767
8768 associate(x=>s%x,dx=>s%dx,w=>s%w,wct=>sct%w,wcts=>sct%ws)
8769 ! calculate current density at cell edges
8770 jce=0.d0
8771 do idim1=1,ndim
8772 do idim2=1,ndim
8773 do idir=sdim,3
8774 if (lvc(idim1,idim2,idir)==0) cycle
8775 ixcmax^d=ixomax^d;
8776 ixcmin^d=ixomin^d+kr(idir,^d)-1;
8777 ixbmax^d=ixcmax^d-kr(idir,^d)+1;
8778 ixbmin^d=ixcmin^d;
8779 ! current at transverse faces
8780 xs(ixb^s,:)=x(ixb^s,:)
8781 xs(ixb^s,idim2)=x(ixb^s,idim2)+half*dx(ixb^s,idim2)
8782 call gradientf(wcts(ixgs^t,idim2),xs,ixgs^ll,ixc^l,idim1,gradi,2)
8783 if (lvc(idim1,idim2,idir)==1) then
8784 jce(ixc^s,idir)=jce(ixc^s,idir)+gradi(ixc^s)
8785 else
8786 jce(ixc^s,idir)=jce(ixc^s,idir)-gradi(ixc^s)
8787 end if
8788 end do
8789 end do
8790 end do
8791 ! get resistivity
8792 if(mhd_eta>zero)then
8793 jce(ixi^s,:)=jce(ixi^s,:)*mhd_eta
8794 else
8795 ixa^l=ixo^l^ladd1;
8796 call get_current(wct,ixi^l,ixa^l,idirmin,jcc)
8797 call usr_special_resistivity(wp,ixi^l,ixa^l,idirmin,x,jcc,eta)
8798 ! calculate eta on cell edges
8799 do idir=sdim,3
8800 ixcmax^d=ixomax^d;
8801 ixcmin^d=ixomin^d+kr(idir,^d)-1;
8802 jcc(ixc^s,idir)=0.d0
8803 {do ix^db=0,1\}
8804 if({ ix^d==1 .and. ^d==idir | .or.}) cycle
8805 ixamin^d=ixcmin^d+ix^d;
8806 ixamax^d=ixcmax^d+ix^d;
8807 jcc(ixc^s,idir)=jcc(ixc^s,idir)+eta(ixa^s)
8808 {end do\}
8809 jcc(ixc^s,idir)=jcc(ixc^s,idir)*0.25d0
8810 jce(ixc^s,idir)=jce(ixc^s,idir)*jcc(ixc^s,idir)
8811 end do
8812 end if
8813
8814 end associate
8815 end subroutine get_resistive_electric_field
8816
8817 !> get ambipolar electric field on cell edges
8818 subroutine get_ambipolar_electric_field(ixI^L,ixO^L,w,x,fE)
8820
8821 integer, intent(in) :: ixi^l, ixo^l
8822 double precision, intent(in) :: w(ixi^s,1:nw)
8823 double precision, intent(in) :: x(ixi^s,1:ndim)
8824 double precision, intent(out) :: fe(ixi^s,sdim:3)
8825
8826 double precision :: jxbxb(ixi^s,1:3)
8827 integer :: idir,ixa^l,ixc^l,ix^d
8828
8829 ixa^l=ixo^l^ladd1;
8830 call mhd_get_jxbxb(w,x,ixi^l,ixa^l,jxbxb)
8831 ! calculate electric field on cell edges from cell centers
8832 do idir=sdim,3
8833 ! set ambipolar electric field in jxbxb: E=nuA * jxbxb, where nuA=-etaA/rho^2
8834 ! E_ambi(ixA^S,i) = -(mhd_eta_ambi/w(ixA^S, rho_)**2) * jxbxb(ixA^S,i)
8835 call multiplyambicoef(ixi^l,ixa^l,jxbxb(ixi^s,idir),w,x)
8836 ixcmax^d=ixomax^d;
8837 ixcmin^d=ixomin^d+kr(idir,^d)-1;
8838 fe(ixc^s,idir)=0.d0
8839 {do ix^db=0,1\}
8840 if({ ix^d==1 .and. ^d==idir | .or.}) cycle
8841 ixamin^d=ixcmin^d+ix^d;
8842 ixamax^d=ixcmax^d+ix^d;
8843 fe(ixc^s,idir)=fe(ixc^s,idir)+jxbxb(ixa^s,idir)
8844 {end do\}
8845 fe(ixc^s,idir)=fe(ixc^s,idir)*0.25d0
8846 end do
8847
8848 end subroutine get_ambipolar_electric_field
8849
8850 !> calculate cell-center values from face-center values
8851 subroutine mhd_face_to_center(ixO^L,s)
8853 ! Non-staggered interpolation range
8854 integer, intent(in) :: ixo^l
8855 type(state) :: s
8856
8857 integer :: ix^d
8858
8859 ! calculate cell-center values from face-center values in 2nd order
8860 ! because the staggered arrays have an additional place to the left.
8861 ! Interpolate to cell barycentre using arithmetic average
8862 ! This might be done better later, to make the method less diffusive.
8863 {!dir$ ivdep
8864 do ix^db=ixomin^db,ixomax^db\}
8865 {^ifthreed
8866 s%w(ix^d,b1_)=half/s%surface(ix^d,1)*(s%ws(ix^d,1)*s%surfaceC(ix^d,1)&
8867 +s%ws(ix1-1,ix2,ix3,1)*s%surfaceC(ix1-1,ix2,ix3,1))
8868 s%w(ix^d,b2_)=half/s%surface(ix^d,2)*(s%ws(ix^d,2)*s%surfaceC(ix^d,2)&
8869 +s%ws(ix1,ix2-1,ix3,2)*s%surfaceC(ix1,ix2-1,ix3,2))
8870 s%w(ix^d,b3_)=half/s%surface(ix^d,3)*(s%ws(ix^d,3)*s%surfaceC(ix^d,3)&
8871 +s%ws(ix1,ix2,ix3-1,3)*s%surfaceC(ix1,ix2,ix3-1,3))
8872 }
8873 {^iftwod
8874 s%w(ix^d,b1_)=half/s%surface(ix^d,1)*(s%ws(ix^d,1)*s%surfaceC(ix^d,1)&
8875 +s%ws(ix1-1,ix2,1)*s%surfaceC(ix1-1,ix2,1))
8876 s%w(ix^d,b2_)=half/s%surface(ix^d,2)*(s%ws(ix^d,2)*s%surfaceC(ix^d,2)&
8877 +s%ws(ix1,ix2-1,2)*s%surfaceC(ix1,ix2-1,2))
8878 }
8879 {end do\}
8880
8881 ! calculate cell-center values from face-center values in 4th order
8882 !do idim=1,ndim
8883 ! gxO^L=ixO^L-2*kr(idim,^D);
8884 ! hxO^L=ixO^L-kr(idim,^D);
8885 ! jxO^L=ixO^L+kr(idim,^D);
8886
8887 ! ! Interpolate to cell barycentre using fourth order central formula
8888 ! w(ixO^S,mag(idim))=(0.0625d0/s%surface(ixO^S,idim))*&
8889 ! ( -ws(gxO^S,idim)*s%surfaceC(gxO^S,idim) &
8890 ! +9.0d0*ws(hxO^S,idim)*s%surfaceC(hxO^S,idim) &
8891 ! +9.0d0*ws(ixO^S,idim)*s%surfaceC(ixO^S,idim) &
8892 ! -ws(jxO^S,idim)*s%surfaceC(jxO^S,idim) )
8893 !end do
8894
8895 ! calculate cell-center values from face-center values in 6th order
8896 !do idim=1,ndim
8897 ! fxO^L=ixO^L-3*kr(idim,^D);
8898 ! gxO^L=ixO^L-2*kr(idim,^D);
8899 ! hxO^L=ixO^L-kr(idim,^D);
8900 ! jxO^L=ixO^L+kr(idim,^D);
8901 ! kxO^L=ixO^L+2*kr(idim,^D);
8902
8903 ! ! Interpolate to cell barycentre using sixth order central formula
8904 ! w(ixO^S,mag(idim))=(0.00390625d0/s%surface(ixO^S,idim))* &
8905 ! ( +3.0d0*ws(fxO^S,idim)*s%surfaceC(fxO^S,idim) &
8906 ! -25.0d0*ws(gxO^S,idim)*s%surfaceC(gxO^S,idim) &
8907 ! +150.0d0*ws(hxO^S,idim)*s%surfaceC(hxO^S,idim) &
8908 ! +150.0d0*ws(ixO^S,idim)*s%surfaceC(ixO^S,idim) &
8909 ! -25.0d0*ws(jxO^S,idim)*s%surfaceC(jxO^S,idim) &
8910 ! +3.0d0*ws(kxO^S,idim)*s%surfaceC(kxO^S,idim) )
8911 !end do
8912
8913 end subroutine mhd_face_to_center
8914
8915 !> calculate magnetic field from vector potential
8916 subroutine b_from_vector_potential(ixIs^L, ixI^L, ixO^L, ws, x)
8919
8920 integer, intent(in) :: ixis^l, ixi^l, ixo^l
8921 double precision, intent(inout) :: ws(ixis^s,1:nws)
8922 double precision, intent(in) :: x(ixi^s,1:ndim)
8923
8924 double precision :: adummy(ixis^s,1:3)
8925
8926 call b_from_vector_potentiala(ixis^l, ixi^l, ixo^l, ws, x, adummy)
8927
8928 end subroutine b_from_vector_potential
8929
8930end module mod_mhd_phys
Module to include CAK radiation line force in (magneto)hydrodynamic models Computes both the force fr...
subroutine, public cak_add_source(qdt, ixil, ixol, wct, w, x, energy, qsourcesplit, active)
w[iw]=w[iw]+qdt*S[wCT,qtC,x] where S is the source based on wCT within ixO
subroutine, public cak_get_dt(wprim, ixil, ixol, dtnew, dxd, x)
Check time step for total radiation contribution.
subroutine, public cak_init(phys_gamma)
Initialize the module.
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
Module for physical and numeric constants.
double precision, parameter bigdouble
A very large real number.
double precision, parameter zero
some frequently used numbers
subroutine reconstruct(ixil, ixcl, idir, q, ql, qr)
Reconstruct scalar q within ixO^L to 1/2 dx in direction idir Return both left and right reconstructe...
subroutine b_from_vector_potentiala(ixisl, ixil, ixol, ws, x, a)
calculate magnetic field from vector potential A at cell edges
subroutine add_convert_method(phys_convert_vars, nwc, dataset_names, file_suffix)
Definition mod_convert.t:59
EoS state container – the single thermodynamic authority for AMRVAC.
integer, parameter, public eos_type_pi
type(eos_container), allocatable, public eos
The single EoS state object, allocated in eos_init and shared (read-mostly) across all EoS sub-module...
integer, parameter, public eos_type_lte
LTE (Saha-table) EoS kernels and finalise for the eos% family.
Definition mod_eos_lte.t:12
double precision function, public lte_eint_from_p(pres, nh_val, log_nh_val, rho_val)
LTE internal energy from pressure in one cell: FI bypass, else eint(nH, p).
double precision function, public lte_p_from_eint(eint_val, nh_val, log_nh_val, inv_rho)
LTE pressure from internal energy in one cell: ideal without ionE, else the table floor,...
PI (partial-ionisation) ionisation-degree backend for the eos% family.
PI (partial-ionisation, eos_type='PI') arm of the eos% family.
Definition mod_eos_pi.t:27
subroutine, public eint_from_rho_p_pi(rho, p, eint)
Primitive pressure -> GAS internal energy (prim -> conserved direction): invert (rho,...
Definition mod_eos_pi.t:285
subroutine, public state_from_eint_pi(rho, eint, t, p, rfactor, iz_h, iz_he)
Conserved gas internal energy -> temperature, thermal pressure, R. Inverse of p_eint_from_rho_T_PI....
Definition mod_eos_pi.t:260
Equation of state for AMRVAC, handled through a single eos_container object.
Definition mod_eos.t:30
Module for flux conservation near refinement boundaries.
subroutine, public store_flux(igrid, fc, idimlim, nwfluxin)
subroutine, public store_edge(igrid, ixil, fe, idimlim)
Module for flux limited diffusion (FLD)-approximation in Radiation-(Magneto)hydrodynamics simulations...
Definition mod_fld.t:13
logical fld_no_mg
Definition mod_fld.t:35
double precision, public fld_bisect_tol
Tolerance for bisection method for Energy sourceterms This is a percentage of the minimum of gas- and...
Definition mod_fld.t:25
subroutine, public fld_radforce_get_dt(w, ixil, ixol, dtnew, dxd, x, fl)
get dt limit for radiation force and FLD explicit source additions NOTE: w is primitive on entry
Definition mod_fld.t:399
double precision, public fld_diff_tol
Tolerance for radiative Energy diffusion.
Definition mod_fld.t:27
character(len=40) fld_fluxlimiter
flux limiter choice
Definition mod_fld.t:41
character(len=40) fld_opal_table
Definition mod_fld.t:39
double precision, public fld_cnorm
Definition mod_fld.t:36
double precision, public fld_kappa0
Opacity value when using constant opacity.
Definition mod_fld.t:22
subroutine, public add_fld_rad_force(qdt, ixil, ixol, wct, wctprim, w, x, qsourcesplit, active, fl)
w[iw]=w[iw]+qdt*S[wCT,qtC,x] where S is the source based on wCT within ixO This subroutine handles th...
Definition mod_fld.t:242
character(len=40) fld_opacity_law
switches for opacity
Definition mod_fld.t:38
character(len=40) fld_interaction_method
Which method to find the root for the energy interaction polynomial.
Definition mod_fld.t:47
subroutine, public fld_get_radpress(w, x, ixil, ixol, rad_pressure, fl)
Returns Radiation Pressure as tensor NOTE: w is primitive on entry.
Definition mod_fld.t:542
logical fld_radforce_split
source split for energy interact and radforce:
Definition mod_fld.t:18
logical fld_bound_diff
switches for using changed cmax-cmin bounds
Definition mod_fld.t:32
subroutine, public fld_implicit_update(dtfactor, qdt, qtc, psa, psb, fl)
Calling all subroutines to perform the multigrid method Communicates rad_e and diff_coeff to multigri...
Definition mod_fld.t:802
subroutine, public fld_evaluate_implicit(qtc, psa, fl)
inplace update of psa==>F_im(psa)
Definition mod_fld.t:950
subroutine, public fld_init(gas_eos_type, gas_ion_energy, gas_pi_table)
Initialising FLD-module Read opacities Initialise Multigrid and adimensionalise kappa.
Definition mod_fld.t:116
integer nth_for_diff_mg
diffusion coefficient stencil control
Definition mod_fld.t:45
Module with basic grid data structures.
Definition mod_forest.t:2
type(tree_node_ptr), dimension(:,:), allocatable, save igrid_to_node
Array to go from an [igrid, ipe] index to a node pointer.
Definition mod_forest.t:35
subroutine, public get_divb(w, ixil, ixol, divb, nth_in)
Calculate div B within ixO.
integer, dimension(:), allocatable, public mag
Indices of the magnetic field.
Module with geometry-related routines (e.g., divergence, curl).
Definition mod_geometry.t:2
subroutine divvector(qvec, ixil, ixol, divq, nth_in)
integer coordinate
Definition mod_geometry.t:7
integer, parameter cartesian
Definition mod_geometry.t:8
subroutine laplacian_of_vector(qvec, ixil, ixol, lapl_qvec)
integer, parameter cylindrical
subroutine curlvector(qvec, ixil, ixol, curlvec, idirmin, idirmin0, ndir0, fourthorder)
Calculate curl of a vector qvec within ixL Options to employ standard second order CD evaluations use...
subroutine gradient(q, ixil, ixol, idir, gradq, nth_in)
subroutine gradientf(q, x, ixil, ixol, idir, gradq, nth_in, pm_in)
subroutine gradientl(q, ixil, ixol, idir, gradq)
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 dtdiffpar
For resistive MHD, the time step is also limited by the diffusion time: .
character(len=std_len) typegrad
double precision unit_charge
Physical scaling factor for charge.
integer ixghi
Upper index of grid block arrays.
pure subroutine cross_product(ixil, ixol, a, b, axb)
Cross product of two vectors.
integer, dimension(3, 3, 3) lvc
Levi-Civita tensor.
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.
integer, dimension(:, :), allocatable typeboundary
Array indicating the type of boundary condition per variable and per physical boundary.
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 stagger_grid
True for using stagger grid.
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 icomm
The MPI communicator.
double precision bdip
amplitude of background dipolar, quadrupolar, octupolar, user's field
integer b0i
background magnetic field location indicator
integer mype
The rank of the current MPI task.
logical local_timestep
each cell has its own timestep or not
integer ndir
Number of spatial dimensions (components) for vector variables.
integer ixm
the mesh range of a physical block without ghost cells
integer ierrmpi
A global MPI error return code.
logical autoconvert
If true, already convert to output format during the run.
integer, dimension(:), allocatable flux_method
Which flux scheme of spatial discretization to use (per grid level).
double precision, dimension(:), allocatable, parameter d
logical slab
Cartesian geometry or not.
integer, parameter bc_periodic
integer, parameter bc_special
boundary condition types
double precision unit_magneticfield
Physical scaling factor for magnetic field.
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.
logical b0field
split magnetic field as background B0 field
double precision, dimension(:,:), allocatable rnode
Corner coordinates.
double precision unit_temperature
Physical scaling factor for temperature.
double precision unit_radflux
Physical scaling factor for radiation flux.
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.
integer, parameter sdim
starting dimension for electric field
logical phys_trac
Use TRAC for MHD or 1D HD.
logical need_global_cmax
need global maximal wave speed
logical convert
If true and restart_from_file is given, convert snapshots to other file formats.
logical fix_small_values
fix small values with average or replace methods
double precision, dimension(^nd) dxlevel
store unstretched cell size of current level
logical use_multigrid
Use multigrid (only available in 2D and 3D).
logical slab_uniform
uniform Cartesian geometry or not (stretched Cartesian)
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.
integer number_equi_vars
number of equilibrium set variables, besides the mag field
integer, parameter ixglo
Lower index of grid block arrays (always 1).
Module for including gravity in (magneto)hydrodynamics simulations.
Definition mod_gravity.t:2
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
module mod_magnetofriction.t Purpose: use magnetofrictional method to relax 3D magnetic field to forc...
subroutine magnetofriction_init()
Initialize the module.
Magneto-hydrodynamics module.
Definition mod_mhd_phys.t:2
subroutine, public mhd_get_trad(w, x, ixil, ixol, trad)
Calculates radiation temperature.
integer, public, protected c_
logical, public, protected mhd_gravity
Whether gravity is added.
integer, public, protected fip_
Index of the FIP passive scalar rho*fip in conserved form, fip in primitive form.
logical, public, protected mhd_internal_e
Whether internal energy is solved instead of total energy.
double precision, public, protected mhd_trac_delta
TRAC-7 (Johnston 2021, A&A 654 A2): target number of cells resolving the TR.
logical, public, protected mhd_glm_extended
Whether extended GLM-MHD is used with additional sources.
logical, public mhd_hyperbolic_tc_constant
character(len=std_len), public, protected type_ct
Method type of constrained transport.
integer, dimension(:), allocatable, public, protected mom
Indices of the momentum density.
subroutine, public mhd_clean_divb_multigrid(qdt, qt, active)
integer, public, protected qpar_
Index of the field-aligned heat flux q_parallel.
logical, public, protected mhd_radiative_cooling
Whether radiative cooling is added.
subroutine, public mhd_e_to_ei(ixil, ixol, w, x)
Transform total energy to internal energy.
double precision, public mhd_adiab
The adiabatic constant.
double precision, public divbdiff
Coefficient of diffusive divB cleaning.
double precision, public mhd_eta_hyper
The MHD hyper-resistivity.
character(len=std_len), public, protected mhd_uawsom_reflection_mode
Algebra used by the Alfven-wave reflection source. one_dimensional_gradient retains the original one-...
double precision, public, protected mhd_hyperbolic_tc_bmin
Field-strength transition scale for perpendicular closure.
double precision, public, protected rr
double precision, public, protected h_ion_fr
Ionization fraction of H H_ion_fr = H+/(H+ + H).
double precision, public, protected mhd_uawsom_sigma
Dimensionless multiplier in the one_dimensional_gradient source only.
integer, public, protected mhd_trac_finegrid
Distance between two adjacent traced magnetic field lines (in finest cell size).
integer, public, protected wkminus_
double precision, public, protected mhd_uawsom_thread_radius0
double precision function, public mhd_semirelati_eint(p, rho)
Internal energy from thermal pressure in one cell, the inverse of mhd_semirelati_p.
subroutine, public get_normalized_divb(w, ixil, ixol, divb)
get dimensionless div B = |divB| * volume / area / |B|
integer, public, protected mhd_uawsom_height_dim
Enable conservative kink-wave reflection from the kink-speed gradient Cartesian direction used for th...
logical, public numerical_resistive_heating
Whether numerical resistive heating is included when solving partial energy equation.
type(tc_fluid), allocatable, public tc_fl
type of fluid for thermal conduction
logical, public, protected mhd_rotating_frame
Whether rotating frame is activated.
logical, public, protected mhd_semirelativistic
Whether semirelativistic MHD equations (Gombosi 2002 JCP) are solved.
integer, public, protected mhd_divb_nth
Whether divB is computed with a fourth order approximation.
integer, public, protected mhd_n_tracer
Number of tracer species.
integer, public, protected te_
Indices of temperature.
integer, public, protected m
integer, public equi_rho0_
equi vars indices in the stateequi_vars array
integer, public, protected mhd_trac_type
Which TRAC method is used.
logical, public, protected mhd_cak_force
Whether plasma is partially ionized Whether CAK radiation line force is activated.
logical, public, protected source_split_divb
Whether divB cleaning sources are added splitting from fluid solver.
double precision, public, protected mhd_uawsom_zeta0
Base transverse density contrast and filling factor.
integer, public, protected ne_
Index of the electron number density for LTE module.
logical, public, protected mhd_hall
Whether Hall-MHD is used.
type(te_fluid), allocatable, public te_fl_mhd
type of fluid for thermal emission synthesis
logical, public, protected mhd_ambipolar
Whether Ambipolar term is used.
logical, public, protected mhd_hyperbolic_tc
Whether thermal conduction is used.
logical, public, protected mhd_hyperbolic_tc_sat
Whether saturation is considered for hyperbolic TC. When the perpendicular channel is active,...
double precision, public, protected mhd_hyperbolic_tc_kappa_perp_factor
Relative perpendicular hyperbolic-TC coefficient in fixed/strong-field limit: kappa_perp0 = mhd_hyper...
logical, public has_equi_rho_and_p
whether split off equilibrium density and pressure
double precision, public mhd_glm_alpha
GLM-MHD parameter: ratio of the diffusive and advective time scales for div b taking values within [0...
double precision function, dimension(ixo^s), public mhd_mag_en_all(w, ixil, ixol)
Compute 2 times total magnetic energy.
pure double precision function, public mhd_uawsom_wave_energy_cell(wcell)
logical, public, protected mhd_radiation_fld
Whether radiation-gas interaction is handled using flux limited diffusion.
subroutine, public multiplyambicoef(ixil, ixol, res, w, x)
multiply res by the ambipolar coefficient The ambipolar coefficient is calculated as -mhd_eta_ambi/rh...
subroutine, public b_from_vector_potential(ixisl, ixil, ixol, ws, x)
calculate magnetic field from vector potential
double precision, public, protected he_ion_fr
Ionization fraction of He He_ion_fr = (He2+ + He+)/(He2+ + He+ + He).
double precision, public, protected mhd_squared_c
squared reduced speed of light c
integer, public, protected waminus_
logical, public, protected mhd_viscosity
Whether viscosity is added.
subroutine, public mhd_get_pradiation_from_prim(w, x, ixil, ixol, prad)
Calculate radiation pressure within ixO^L.
double precision, public, protected mhd_reduced_c
Reduced speed of light for semirelativistic MHD: 2% of light speed.
logical, public, protected mhd_energy
Whether an energy equation is used.
logical, public, protected mhd_ambipolar_exp
Whether Ambipolar term is implemented explicitly.
logical, public, protected mhd_radiative_transfer
Whether the short-characteristics radiative transfer is used, with its settings in &rt_list.
double precision, public mhd_hyperbolic_tc_kappa
The thermal conductivity kappa in hyperbolic thermal conduction.
logical, public, protected mhd_glm
Whether GLM-MHD is used to control div B.
double precision, public, protected mhd_uawsom_zeta_scale
Physical input scales (lengths and magnetic field); converted to code units at init.
type(fld_fluid), allocatable, public fld_fl
Radiation fluid object (gas-EoS callbacks for FLD), wired in mhd_link_eos.
logical, public clean_initial_divb
clean initial divB
double precision, public mhd_eta
The MHD resistivity.
logical, public divbwave
Add divB wave in Roe solver.
logical, public, protected mhd_uawsom_reflection
Enable conservative Alfvén reflection (one-dimensional or Cartesian gradient-vorticity).
logical, public, protected mhd_magnetofriction
Whether magnetofriction is added.
double precision, public, protected mhd_trac_mask
Height of the mask used in the TRAC method.
subroutine, public mhd_semirelati_vel_emech(w, ixil, ixol, vel, emech)
Velocity from the semirelativistic momentum (Gombosi et al. 2002 eqs 15-16) and emech,...
procedure(mask_subroutine), pointer, public usr_mask_ambipolar
logical, public, protected mhd_semirelati_curl_e
Keep E x curl(E) in the semirelativistic source: eq (26) of Gombosi et al. 2002 keeps it,...
elemental pure double precision function, public mhd_uawsom_rho2_factor_cell(zeta)
character(len=std_len), public, protected typedivbfix
Method type to clean divergence of B.
logical, public, protected mhd_uawsom_kink_reflection
double precision, public, protected mhd_uawsom_filling_factor
integer, public, protected waplus_
Conserved wave-energy indices. The plus variables propagate against B.
subroutine, public mhd_uawsom_rho2_factor(ixil, ixol, w, x, factor)
double precision, public, protected mhd_uawsom_alfven_corr_length0
logical, public, protected mhd_thermal_conduction
Whether thermal conduction is used.
integer, public equi_pe0_
subroutine, public mhd_get_csrad2_prim(w, x, ixil, ixol, csound)
Calculate modified squared fast wave speed for FLD NOTE: w is primitive on entry here!...
integer, public, protected qperp_
Index of the perpendicular heat flux q_perp.
integer, public, protected p_
Index of the gas pressure (-1 if not present) should equal e_.
integer, public, protected c
Indices of the momentum density for the form of better vectorization.
character(len=std_len), public, protected mhd_hyperbolic_tc_perp_mode
Perpendicular hyperbolic-TC closure mode: Enable/disable with mhd_hyperbolic_tc_use_perp,...
double precision, public, protected he_ion_fr2
Ratio of number He2+ / number He+ + He2+ He_ion_fr2 = He2+/(He2+ + He+).
pure double precision function, public mhd_uawsom_wave_pressure_cell(wcell, zeta)
logical, public, protected mhd_dump_full_vars
whether dump full variables (when splitting is used) in a separate dat file
logical, public, protected mhd_particles
Whether particles module is added.
integer, public, protected b
subroutine, public mhd_face_to_center(ixol, s)
calculate cell-center values from face-center values
logical, dimension(2 *^nd), public, protected boundary_divbfix
To control divB=0 fix for boundary.
subroutine, public get_current(w, ixil, ixol, idirmin, current)
Calculate idirmin and the idirmin:3 components of the common current array make sure that dxlevel(^D)...
double precision, public mhd_etah
Hall resistivity.
subroutine, public mhd_get_v(w, x, ixil, ixol, v)
Calculate v vector.
double precision, public mhd_eta_ambi
The MHD ambipolar coefficient.
logical, public, protected mhd_fip
Whether FIP passive scalar is enabled.
logical, public, protected mhd_hydrodynamic_e
Whether hydrodynamic energy is solved instead of total energy.
integer, public, protected r_e
Index of the radiation energy.
subroutine, public mhd_phys_init()
logical, public, protected mhd_trac
Whether TRAC method is used.
subroutine, public mhd_get_csrad2(w, x, ixil, ixol, csound)
Calculate modified squared sound speed for FLD NOTE: only for diagnostic purposes,...
subroutine, public mhd_get_pthermal_plus_pradiation(w, x, ixil, ixol, pth_plus_prad)
Calculates the sum of the gas pressure and the max Prad tensor element.
type(rc_fluid), allocatable, public rc_fl
type of fluid for radiative cooling
integer, public, protected wkplus_
integer, dimension(:), allocatable, public, protected tracer
Indices of the tracers.
double precision, public, protected mhd_hyperbolic_tc_coulomb_log
Constant Coulomb logarithm used by the simplified electron-magnetization closure. It is a namelist pa...
integer, public, protected rho_
Index of the density (in the w array).
double precision, public, protected mhd_uawsom_bref
logical, public, protected b0field_forcefree
B0 field is force-free.
integer, dimension(2 *^nd), public, protected boundary_divbfix_skip
To skip * layer of ghost cells during divB=0 fix for boundary.
integer, public, protected tweight_
logical, public, protected mhd_ambipolar_sts
Whether Ambipolar term is implemented using supertimestepping.
logical, public, protected mhd_hyperbolic_tc_use_perp
Whether the perpendicular hyperbolic-TC channel is enabled.
subroutine, public mhd_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).
double precision function, public mhd_semirelati_p(eint, rho)
Thermal pressure from internal energy in one cell for the active EoS.
integer, public, protected tcoff_
Index of the cutoff temperature for the TRAC method.
subroutine, public mhd_get_rho(w, x, ixil, ixol, rho)
logical, public, protected mhd_uawsom
Enable the Uniturbulence and Alfven Wave Solar Model extension.
logical, public, protected mhd_fld_pradtensor
integer, public, protected psi_
Indices of the GLM psi.
logical, public mhd_equi_thermal
Module to couple the octree-mg library to AMRVAC. This file uses the VACPP preprocessor,...
type(mg_t) mg
Data structure containing the multigrid tree.
Module containing all the particle routines.
subroutine particles_init()
Initialize particle data and parameters.
This module defines the procedures of a physics module. It contains function pointers for the various...
Definition mod_physics.t:4
module radiative cooling – add optically thin radiative cooling
subroutine radiative_cooling_init_params(phys_gamma, he_abund)
Radiative cooling initialization.
subroutine findl(tpoint, lpoint, fl)
subroutine radiative_cooling_init(fl, read_params)
subroutine radiative_cooling_add_source(qdt, ixil, ixol, wct, wctprim, w, x, qsourcesplit, active, fl)
subroutine calc_l_extended(tpoint, lpoint, fl)
Module for including rotating frame in (magneto)hydrodynamics simulations The rotation vector is assu...
subroutine rotating_frame_add_source(qdt, dtfactor, ixil, ixol, wct, w, x)
w[iw]=w[iw]+qdt*S[wCT,qtC,x] where S is the source based on wCT within ixO
subroutine rotating_frame_init()
Initialize the module.
Public face of the short-characteristics (SC) radiative transfer and its coupling to the (M)HD.
subroutine, public rt_sc_add_source(qdt, ixil, ixol, wct, w, x, thin_share, e_internal)
Unsplit heating source of one block and stage, e += qdt Q, with Q read from i_qrad,...
subroutine, public rt_sc_activate()
Switch the RT on. The physics module calls this from its initialisation when hd_radiative_transfer or...
Module for handling problematic values in simulations, such as negative pressures.
subroutine, public small_values_average(ixil, ixol, w, x, w_flag, windex)
subroutine, public small_values_error(wprim, x, ixil, ixol, w_flag, subname)
logical, dimension(:), allocatable, public small_values_fix_iw
Whether to apply small value fixes to certain variables.
character(len=20), public small_values_method
How to handle small values.
Generic supertimestepping method which can be used for multiple source terms in the governing equatio...
subroutine, public add_sts_method(sts_getdt, sts_set_sources, startvar, nflux, startwbc, nwbc, evolve_b)
subroutine which added programatically a term to be calculated using STS Params: sts_getdt function c...
subroutine, public set_conversion_methods_to_head(sts_before_first_cycle, sts_after_last_cycle)
Set the hooks called before the first cycle and after the last cycle in the STS update This method sh...
subroutine, public set_error_handling_to_head(sts_error_handling)
Set the hook of error handling in the STS update. This method is called before updating the BC....
subroutine, public sts_init()
Initialize sts module.
Thermal conduction for HD and MHD or RHD and RMHD or twofl (plasma-neutral) module Adaptation of mod_...
double precision function, public get_tc_dt_mhd(w, ixil, ixol, dxd, x, fl)
Get the explicit timestep for the TC (mhd implementation) Note: for multi-D MHD (1D MHD will use HD f...
double precision function, public get_tc_dt_hd(w, ixil, ixol, dxd, x, fl)
Get the explicit timestep for the TC (hd implementation) Note: also used in 1D MHD (or for neutrals i...
subroutine tc_init_params(phys_gamma)
subroutine, public sts_set_source_tc_hd(ixil, ixol, w, x, wres, fix_conserve_at_step, my_dt, igrid, nflux, fl)
subroutine, public sts_set_source_tc_mhd(ixil, ixol, w, x, wres, fix_conserve_at_step, my_dt, igrid, nflux, fl)
anisotropic thermal conduction with slope limited symmetric scheme Sharma 2007 Journal of Computation...
subroutine, public tc_get_mhd_params(fl, read_mhd_params)
Init TC coefficients: MHD case.
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(special_resistivity), pointer usr_special_resistivity
procedure(set_adiab), pointer usr_set_adiab
procedure(set_adiab), pointer usr_set_gamma
procedure(sub_get_heating), pointer usr_get_heating
procedure(phys_gravity), pointer usr_gravity
procedure(uawsom_coefficients), pointer usr_uawsom_coefficients
Optionally provide local UAWSoM closure coefficients. The returned thread radius and Alfven correlati...
procedure(set_equi_vars), pointer usr_set_equi_vars
procedure(set_electric_field), pointer usr_set_electric_field
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
The data structure that contains information about a tree node/grid block.
Definition mod_forest.t:14