MPI-AMRVAC 3.1
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_rmhd_phys.t
Go to the documentation of this file.
1!> Radiation-magneto-hydrodynamics module
3
4#include "amrvac.h"
5
6 use mod_global_parameters, only: std_len, const_c
10 use mod_comm_lib, only: mpistop
12
13 implicit none
14 private
15
16 !> The adiabatic index
17 double precision, public :: rmhd_gamma = 5.d0/3.0d0
18 !> The adiabatic constant
19 double precision, public :: rmhd_adiab = 1.0d0
20 !> The MHD resistivity
21 double precision, public :: rmhd_eta = 0.0d0
22 !> The MHD hyper-resistivity
23 double precision, public :: rmhd_eta_hyper = 0.0d0
24 !> Hall resistivity
25 double precision, public :: rmhd_etah = 0.0d0
26 !> The small_est allowed energy
27 double precision, protected :: small_e
28 !> The smallest allowed radiation energy
29 double precision, public, protected :: small_r_e = 0.d0
30 !> Height of the mask used in the TRAC method
31 double precision, public, protected :: rmhd_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 :: rmhd_glm_alpha = 0.5d0
35 !> The thermal conductivity kappa in hyperbolic thermal conduction
36 double precision, public :: hypertc_kappa
37 !> Coefficient of diffusive divB cleaning
38 double precision :: divbdiff = 0.8d0
39 !> Helium abundance over Hydrogen
40 double precision, public, protected :: he_abundance=0.1d0
41 !> Ionization fraction of H
42 !> H_ion_fr = H+/(H+ + H)
43 double precision, public, protected :: h_ion_fr=1d0
44 !> Ionization fraction of He
45 !> He_ion_fr = (He2+ + He+)/(He2+ + He+ + He)
46 double precision, public, protected :: he_ion_fr=1d0
47 !> Ratio of number He2+ / number He+ + He2+
48 !> He_ion_fr2 = He2+/(He2+ + He+)
49 double precision, public, protected :: he_ion_fr2=1d0
50 ! used for eq of state when it is not defined by units,
51 ! the units do not contain terms related to ionization fraction
52 ! and it is p = RR * rho * T
53 double precision, public, protected :: rr=1d0
54 !> gamma minus one and its inverse
55 double precision :: gamma_1, inv_gamma_1
56 !> inverse of squared speed of light c0 and reduced speed of light c
57 double precision :: inv_squared_c0, inv_squared_c
58 !> equi vars indices in the state%equi_vars array
59 integer, public :: equi_rho0_ = -1
60 integer, public :: equi_pe0_ = -1
61 !> Number of tracer species
62 integer, public, protected :: rmhd_n_tracer = 0
63 !> Index of the density (in the w array)
64 integer, public, protected :: rho_
65 !> Indices of the momentum density
66 integer, allocatable, public, protected :: mom(:)
67 !> Indices of the momentum density for the form of better vectorization
68 integer, public, protected :: ^c&m^C_
69 !> Index of the energy density (-1 if not present)
70 integer, public, protected :: e_
71 !> Index of the radiation energy
72 integer, public, protected :: r_e
73 !> Indices of the momentum density for the form of better vectorization
74 integer, public, protected :: ^c&b^C_
75 !> Index of the gas pressure (-1 if not present) should equal e_
76 integer, public, protected :: p_
77 !> Index of the heat flux q
78 integer, public, protected :: q_
79 !> Indices of the GLM psi
80 integer, public, protected :: psi_
81 !> Indices of temperature
82 integer, public, protected :: te_
83 !> Index of the cutoff temperature for the TRAC method
84 integer, public, protected :: tcoff_
85 integer, public, protected :: tweight_
86 !> Indices of the tracers
87 integer, allocatable, public, protected :: tracer(:)
88 !> The number of waves
89 integer :: nwwave=8
90 !> Method type in a integer for good performance
91 integer :: type_divb
92 !> To skip * layer of ghost cells during divB=0 fix for boundary
93 integer, public, protected :: boundary_divbfix_skip(2*^nd)=0
94 ! DivB cleaning methods
95 integer, parameter :: divb_none = 0
96 integer, parameter :: divb_multigrid = -1
97 integer, parameter :: divb_glm = 1
98 integer, parameter :: divb_powel = 2
99 integer, parameter :: divb_janhunen = 3
100 integer, parameter :: divb_linde = 4
101 integer, parameter :: divb_lindejanhunen = 5
102 integer, parameter :: divb_lindepowel = 6
103 integer, parameter :: divb_lindeglm = 7
104 integer, parameter :: divb_ct = 8
105 !> Whether an energy equation is used
106 logical, public, protected :: rmhd_energy = .true.
107 !> Whether thermal conduction is used
108 logical, public, protected :: rmhd_thermal_conduction = .false.
109 !> Whether thermal conduction is used
110 logical, public, protected :: rmhd_hyperbolic_thermal_conduction = .false.
111 !> Whether viscosity is added
112 logical, public, protected :: rmhd_viscosity = .false.
113 !> Whether gravity is added
114 logical, public, protected :: rmhd_gravity = .false.
115 !> Whether particles module is added
116 logical, public, protected :: rmhd_particles = .false.
117 !> Whether GLM-MHD is used to control div B
118 logical, public, protected :: rmhd_glm = .false.
119 !> Whether extended GLM-MHD is used with additional sources
120 logical, public, protected :: rmhd_glm_extended = .true.
121 !> Whether TRAC method is used
122 logical, public, protected :: rmhd_trac = .false.
123 !> Which TRAC method is used
124 integer, public, protected :: rmhd_trac_type=1
125 !> Distance between two adjacent traced magnetic field lines (in finest cell size)
126 integer, public, protected :: rmhd_trac_finegrid=4
127 !> Whether divB cleaning sources are added splitting from fluid solver
128 logical, public, protected :: source_split_divb = .false.
129 !> Whether plasma is partially ionized
130 logical, public, protected :: rmhd_partial_ionization = .false.
131 !> Whether CAK radiation line force is activated
132 logical, public, protected :: rmhd_cak_force = .false.
133 !> MHD fourth order
134 logical, public, protected :: rmhd_4th_order = .false.
135 !> whether split off equilibrium density
136 logical, public :: has_equi_rho0 = .false.
137 !> whether split off equilibrium thermal pressure
138 logical, public :: has_equi_pe0 = .false.
139 logical, public :: rmhd_equi_thermal = .false.
140 !> whether dump full variables (when splitting is used) in a separate dat file
141 logical, public, protected :: rmhd_dump_full_vars = .false.
142 !> Whether divB is computed with a fourth order approximation
143 integer, public, protected :: rmhd_divb_nth = 1
144 !> Use a compact way to add resistivity
145 logical :: compactres = .false.
146 !> Add divB wave in Roe solver
147 logical, public :: divbwave = .true.
148 !> clean initial divB
149 logical, public :: clean_initial_divb = .false.
150 !> Formalism to treat radiation
151 character(len=8), public :: rmhd_radiation_formalism = 'fld'
152 !> In the case of no rmhd_energy, how to compute pressure
153 character(len=8), public :: rmhd_pressure = 'Trad'
154 !> Treat radiation fld_Rad_force
155 logical, public, protected :: rmhd_radiation_force = .true.
156 !> Treat radiation-gas energy interaction
157 logical, public, protected :: rmhd_energy_interact = .true.
158 !> Treat radiation energy diffusion
159 logical, public, protected :: rmhd_radiation_diffusion = .true.
160 !> Treat radiation advection
161 logical, public, protected :: rmhd_radiation_advection = .true.
162 !> Do a running mean over the radiation pressure when determining dt
163 logical, protected :: radio_acoustic_filter = .false.
164 integer, protected :: size_ra_filter = 1
165 !> kb/(m_p mu)* 1/a_rad**4,
166 double precision, public :: kbmpmua4
167 !> Use the speed of light to calculate the timestep, usefull for debugging
168 logical :: dt_c = .false.
169 ! remove the below flag and assume default value = .false.
170 ! when eq state properly implemented everywhere
171 ! and not anymore through units
172 logical, public, protected :: eq_state_units = .true.
173 !> To control divB=0 fix for boundary
174 logical, public, protected :: boundary_divbfix(2*^nd)=.true.
175 !> B0 field is force-free
176 logical, public, protected :: b0field_forcefree=.true.
177 !> Whether an total energy equation is used
178 logical :: total_energy = .true.
179 !> Whether an internal or hydrodynamic energy equation is used
180 logical, public :: partial_energy = .false.
181 !> Whether gravity work is included in energy equation
182 logical :: gravity_energy
183 !> gravity work is calculated use density times velocity or conservative momentum
184 logical :: gravity_rhov = .false.
185 !> Method type to clean divergence of B
186 character(len=std_len), public, protected :: typedivbfix = 'linde'
187 !> Method type of constrained transport
188 character(len=std_len), public, protected :: type_ct = 'uct_contact'
189 !> Update all equations due to divB cleaning
190 character(len=std_len) :: typedivbdiff = 'all'
191 !> type of fluid for thermal conduction
192 type(tc_fluid), public, allocatable :: tc_fl
193 !> type of fluid for thermal emission synthesis
194 type(te_fluid), public, allocatable :: te_fl_rmhd
195
196 procedure(sub_convert), pointer :: rmhd_to_primitive => null()
197 procedure(sub_convert), pointer :: rmhd_to_conserved => null()
198 procedure(sub_small_values), pointer :: rmhd_handle_small_values => null()
199 procedure(sub_get_pthermal), pointer :: rmhd_get_pthermal => null()
200 procedure(sub_get_pthermal), pointer :: rmhd_get_rfactor => null()
201 procedure(sub_get_pthermal), pointer :: rmhd_get_temperature=> null()
202 ! Public methods
203 public :: rmhd_phys_init
204 public :: rmhd_get_pthermal
205 public :: rmhd_get_temperature
206 public :: rmhd_get_v
207 public :: rmhd_get_rho
208 public :: rmhd_to_conserved
209 public :: rmhd_to_primitive
210 public :: rmhd_e_to_ei
211 public :: rmhd_ei_to_e
212 public :: rmhd_face_to_center
213 public :: get_divb
214 public :: get_current
215 public :: get_normalized_divb
217 public :: rmhd_mag_en_all
218 {^nooned
220 }
221 public :: rmhd_get_pradiation
223 public :: rmhd_get_tgas
224 public :: rmhd_get_trad
225 public :: rmhd_set_mg_bounds
226
227contains
228
229 !> Read this module"s parameters from a file
230 subroutine rmhd_read_params(files)
232 use mod_particles, only: particles_eta, particles_etah
233 character(len=*), intent(in) :: files(:)
234 integer :: n
235
236 namelist /rmhd_list/ rmhd_energy, rmhd_n_tracer, rmhd_gamma, rmhd_adiab,&
240 typedivbdiff, type_ct, compactres, divbwave, he_abundance,&
243 particles_eta, particles_etah,has_equi_rho0, has_equi_pe0,rmhd_equi_thermal,&
249 rmhd_radiation_advection, radio_acoustic_filter, size_ra_filter, dt_c
250
251 do n = 1, size(files)
252 open(unitpar, file=trim(files(n)), status="old")
253 read(unitpar, rmhd_list, end=111)
254111 close(unitpar)
255 end do
256
257 end subroutine rmhd_read_params
258
259 !> Write this module's parameters to a snapsoht
260 subroutine rmhd_write_info(fh)
262 integer, intent(in) :: fh
263 integer :: er
264 integer, parameter :: n_par = 1
265 double precision :: values(n_par)
266 integer, dimension(MPI_STATUS_SIZE) :: st
267 character(len=name_len) :: names(n_par)
268
269 call mpi_file_write(fh, n_par, 1, mpi_integer, st, er)
270
271 names(1) = "gamma"
272 values(1) = rmhd_gamma
273 call mpi_file_write(fh, values, n_par, mpi_double_precision, st, er)
274 call mpi_file_write(fh, names, n_par * name_len, mpi_character, st, er)
275 end subroutine rmhd_write_info
276
277 subroutine rmhd_phys_init()
281 use mod_gravity, only: gravity_init
282 use mod_particles, only: particles_init, particles_eta, particles_etah
283 use mod_fld
284 use mod_afld
287 use mod_cak_force, only: cak_init
290 {^nooned
292 }
293
294 integer :: itr, idir
295
296 call rmhd_read_params(par_files)
297
298 if(.not.eq_state_units) then
301 if(mype==0) write(*,*) 'WARNING: set rmhd_partial_ionization=F when eq_state_units=F'
302 end if
303 end if
304
307 if(mype==0) write(*,*) 'WARNING: turn off parabolic TC when using hyperbolic TC'
308 end if
309
310 physics_type = "rmhd"
311 phys_energy=rmhd_energy
314 phys_partial_ionization=rmhd_partial_ionization
315
316 phys_gamma = rmhd_gamma
318
319 if(rmhd_energy) then
320 partial_energy=.false.
321 total_energy=.true.
322 else
323 total_energy=.false.
324 end if
325 phys_total_energy=total_energy
326 if(rmhd_energy) then
327 gravity_energy=.true.
328 if(has_equi_rho0) then
329 gravity_rhov=.true.
330 end if
331 else
332 gravity_energy=.false.
333 end if
334
335 {^ifoned
336 if(rmhd_trac .and. rmhd_trac_type .gt. 2) then
338 if(mype==0) write(*,*) 'WARNING: reset rmhd_trac_type=1 for 1D simulation'
339 end if
340 }
341 if(rmhd_trac .and. rmhd_trac_type .le. 4) then
342 rmhd_trac_mask=bigdouble
343 if(mype==0) write(*,*) 'WARNING: set rmhd_trac_mask==bigdouble for global TRAC method'
344 end if
346
347 ! set default gamma for polytropic/isothermal process
349 if(ndim==1) typedivbfix='none'
350 select case (typedivbfix)
351 case ('none')
352 type_divb = divb_none
353 {^nooned
354 case ('multigrid')
355 type_divb = divb_multigrid
356 use_multigrid = .true.
357 mg%operator_type = mg_laplacian
358 phys_global_source_after => rmhd_clean_divb_multigrid
359 }
360 case ('glm')
361 rmhd_glm = .true.
362 need_global_cmax = .true.
363 type_divb = divb_glm
364 case ('powel', 'powell')
365 type_divb = divb_powel
366 case ('janhunen')
367 type_divb = divb_janhunen
368 case ('linde')
369 type_divb = divb_linde
370 case ('lindejanhunen')
371 type_divb = divb_lindejanhunen
372 case ('lindepowel')
373 type_divb = divb_lindepowel
374 case ('lindeglm')
375 rmhd_glm = .true.
376 need_global_cmax = .true.
377 type_divb = divb_lindeglm
378 case ('ct')
379 type_divb = divb_ct
380 stagger_grid = .true.
381 case default
382 call mpistop('Unknown divB fix')
383 end select
384
385 allocate(start_indices(number_species),stop_indices(number_species))
386 ! set the index of the first flux variable for species 1
387 start_indices(1)=1
388 ! Determine flux variables
389 rho_ = var_set_rho()
390
391 allocate(mom(ndir))
392 mom(:) = var_set_momentum(ndir)
393 m^c_=mom(^c);
394
395 ! Set index of energy variable
396 if (rmhd_energy) then
397 nwwave = 8
398 e_ = var_set_energy() ! energy density
399 p_ = e_ ! gas pressure
400 else
401 nwwave = 7
402 e_ = -1
403 p_ = -1
404 end if
405
406 allocate(mag(ndir))
407 mag(:) = var_set_bfield(ndir)
408 b^c_=mag(^c);
409
410 if (rmhd_glm) then
411 psi_ = var_set_fluxvar('psi', 'psi', need_bc=.false.)
412 else
413 psi_ = -1
414 end if
415
416 !> set radiation energy
417 r_e = var_set_radiation_energy()
418
420 ! hyperbolic thermal conduction flux q
421 q_ = var_set_q()
422 need_global_cmax=.true.
423 else
424 q_=-1
425 end if
426
427 allocate(tracer(rmhd_n_tracer))
428 ! Set starting index of tracers
429 do itr = 1, rmhd_n_tracer
430 tracer(itr) = var_set_fluxvar("trc", "trp", itr, need_bc=.false.)
431 end do
432
433 !if(rmhd_hyperbolic_thermal_conduction) then
434 ! ! hyperbolic thermal conduction flux q
435 ! q_ = var_set_auxvar('q','q')
436 ! need_global_cmax=.true.
437 !else
438 ! q_=-1
439 !end if
440
441 ! set temperature as an auxiliary variable to get ionization degree
443 te_ = var_set_auxvar('Te','Te')
444 else
445 te_ = -1
446 end if
447
448 ! set number of variables which need update ghostcells
449 nwgc=nwflux+nwaux
450
451 ! set the index of the last flux variable for species 1
452 stop_indices(1)=nwflux
453
454 ! Number of variables need reconstruction in w
455 nw_recon=nwflux
456
457 ! set cutoff temperature when using the TRAC method, as well as an auxiliary weight
458 tweight_ = -1
459 if(rmhd_trac) then
460 tcoff_ = var_set_wextra()
461 iw_tcoff=tcoff_
462 if(rmhd_trac_type .ge. 3) then
463 tweight_ = var_set_wextra()
464 endif
465 else
466 tcoff_ = -1
467 end if
468
469 ! set indices of equi vars and update number_equi_vars
471 if(has_equi_rho0) then
474 iw_equi_rho = equi_rho0_
475 endif
476 if(has_equi_pe0) then
479 iw_equi_p = equi_pe0_
480 endif
481 ! determine number of stagger variables
482 nws=ndim
483
484 nvector = 2 ! No. vector vars
485 allocate(iw_vector(nvector))
486 iw_vector(1) = mom(1) - 1 ! TODO: why like this?
487 iw_vector(2) = mag(1) - 1 ! TODO: why like this?
488
489 ! Check whether custom flux types have been defined
490 if (.not. allocated(flux_type)) then
491 allocate(flux_type(ndir, nwflux))
492 flux_type = flux_default
493 else if (any(shape(flux_type) /= [ndir, nwflux])) then
494 call mpistop("phys_check error: flux_type has wrong shape")
495 end if
496
497 if(nwflux>mag(ndir)) then
498 ! for flux of tracers, using hll flux
499 flux_type(:,mag(ndir)+1:nwflux)=flux_hll
500 end if
501
502 if(ndim>1) then
503 if(rmhd_glm) then
504 flux_type(:,psi_)=flux_special
505 do idir=1,ndir
506 flux_type(idir,mag(idir))=flux_special
507 end do
508 else
509 do idir=1,ndir
510 flux_type(idir,mag(idir))=flux_tvdlf
511 end do
512 end if
513 end if
514
515 phys_get_rho => rmhd_get_rho
516 phys_get_dt => rmhd_get_dt
517 phys_get_cmax => rmhd_get_cmax_origin
518 phys_get_a2max => rmhd_get_a2max
519 phys_get_tcutoff => rmhd_get_tcutoff
520 phys_get_h_speed => rmhd_get_h_speed
521 if(has_equi_rho0) then
522 phys_get_cbounds => rmhd_get_cbounds_split_rho
523 else
524 phys_get_cbounds => rmhd_get_cbounds
525 end if
526 if(has_equi_rho0) then
527 phys_to_primitive => rmhd_to_primitive_split_rho
528 rmhd_to_primitive => rmhd_to_primitive_split_rho
529 phys_to_conserved => rmhd_to_conserved_split_rho
530 rmhd_to_conserved => rmhd_to_conserved_split_rho
531 else
532 phys_to_primitive => rmhd_to_primitive_origin
533 rmhd_to_primitive => rmhd_to_primitive_origin
534 phys_to_conserved => rmhd_to_conserved_origin
535 rmhd_to_conserved => rmhd_to_conserved_origin
536 end if
537 if(b0field.or.has_equi_rho0.or.has_equi_pe0) then
538 phys_get_flux => rmhd_get_flux_split
539 else
540 phys_get_flux => rmhd_get_flux
541 end if
542 phys_get_v => rmhd_get_v
543 if(b0field.or.has_equi_rho0) then
544 phys_add_source_geom => rmhd_add_source_geom_split
545 else
546 phys_add_source_geom => rmhd_add_source_geom
547 end if
548 phys_add_source => rmhd_add_source
549 phys_check_params => rmhd_check_params
550 phys_write_info => rmhd_write_info
551
552 phys_handle_small_values => rmhd_handle_small_values_origin
553 rmhd_handle_small_values => rmhd_handle_small_values_origin
554 phys_check_w => rmhd_check_w_origin
555
556 phys_set_mg_bounds => rmhd_set_mg_bounds
557 phys_get_trad => rmhd_get_trad
558 phys_get_tgas => rmhd_get_tgas
559
560 phys_get_pthermal => rmhd_get_pthermal_origin
561 rmhd_get_pthermal => rmhd_get_pthermal_origin
562
563 if(number_equi_vars>0) then
564 phys_set_equi_vars => set_equi_vars_grid
565 endif
566
567 if(type_divb==divb_glm) then
568 phys_modify_wlr => rmhd_modify_wlr
569 end if
570
571 ! choose Rfactor in ideal gas law
573 rmhd_get_rfactor=>rfactor_from_temperature_ionization
574 phys_update_temperature => rmhd_update_temperature
575 else if(associated(usr_rfactor)) then
576 rmhd_get_rfactor=>usr_rfactor
577 else
578 rmhd_get_rfactor=>rfactor_from_constant_ionization
579 end if
580
582 rmhd_get_temperature => rmhd_get_temperature_from_te
583 else
584 if(has_equi_pe0 .and. has_equi_rho0) then
585 rmhd_get_temperature => rmhd_get_temperature_from_etot_with_equi
586 else
587 rmhd_get_temperature => rmhd_get_temperature_from_etot
588 end if
589 end if
590
591 ! if using ct stagger grid, boundary divb=0 is not done here
592 if(stagger_grid) then
593 phys_get_ct_velocity => rmhd_get_ct_velocity
594 phys_update_faces => rmhd_update_faces
595 phys_face_to_center => rmhd_face_to_center
596 phys_modify_wlr => rmhd_modify_wlr
597 else if(ndim>1) then
598 phys_boundary_adjust => rmhd_boundary_adjust
599 end if
600
601 {^nooned
602 ! clean initial divb
603 if(clean_initial_divb) phys_clean_divb => rmhd_clean_divb_multigrid
604 }
605
606 ! derive units from basic units
607 call rmhd_physical_units()
608
609 !> Initiate radiation-closure module
610 select case(rmhd_radiation_formalism)
611 case('fld')
613 case('afld')
615 case default
616 call mpistop('Radiation formalism unknown')
617 end select
618
621 end if
622 if(.not. rmhd_energy .and. rmhd_thermal_conduction) then
623 call mpistop("thermal conduction needs rmhd_energy=T")
624 end if
626 call mpistop("hyperbolic thermal conduction needs rmhd_energy=T")
627 end if
628
629 ! initialize thermal conduction module
631 call sts_init()
633
634 allocate(tc_fl)
635 call tc_get_mhd_params(tc_fl,tc_params_read_rmhd)
636 call add_sts_method(rmhd_get_tc_dt_rmhd,rmhd_sts_set_source_tc_rmhd,e_,1,e_,1,.false.)
637 if(has_equi_pe0 .and. has_equi_rho0) then
638 tc_fl%get_temperature_from_conserved => rmhd_get_temperature_from_etot_with_equi
639 else
640 tc_fl%get_temperature_from_conserved => rmhd_get_temperature_from_etot
641 end if
642 if(has_equi_pe0 .and. has_equi_rho0) then
643 tc_fl%get_temperature_from_eint => rmhd_get_temperature_from_eint_with_equi
644 if(rmhd_equi_thermal) then
645 tc_fl%has_equi = .true.
646 tc_fl%get_temperature_equi => rmhd_get_temperature_equi
647 tc_fl%get_rho_equi => rmhd_get_rho_equi
648 else
649 tc_fl%has_equi = .false.
650 end if
651 else
652 tc_fl%get_temperature_from_eint => rmhd_get_temperature_from_eint
653 end if
655 call set_error_handling_to_head(rmhd_tc_handle_small_e)
656 tc_fl%get_rho => rmhd_get_rho
657 tc_fl%e_ = e_
658 tc_fl%Tcoff_ = tcoff_
659 end if
660
661 allocate(te_fl_rmhd)
662 te_fl_rmhd%get_rho=> rmhd_get_rho
663 te_fl_rmhd%get_pthermal=> rmhd_get_pthermal
664 te_fl_rmhd%get_var_Rfactor => rmhd_get_rfactor
665{^ifthreed
666 phys_te_images => rmhd_te_images
667}
668 ! Initialize viscosity module
669 if (rmhd_viscosity) call viscosity_init(phys_wider_stencil)
670
671 ! Initialize gravity module
672 if(rmhd_gravity) then
673 call gravity_init()
674 end if
675
676 ! Initialize particles module
677 if(rmhd_particles) then
678 call particles_init()
679 if (particles_eta < zero) particles_eta = rmhd_eta
680 if (particles_etah < zero) particles_eta = rmhd_etah
681 if(mype==0) then
682 write(*,*) '*****Using particles: with rmhd_eta, rmhd_etah :', rmhd_eta, rmhd_etah
683 write(*,*) '*****Using particles: particles_eta, particles_etah :', particles_eta, particles_etah
684 end if
685 end if
686
687 ! initialize ionization degree table
689
690 ! Initialize CAK radiation force module
692 end subroutine rmhd_phys_init
693
694{^ifthreed
695 subroutine rmhd_te_images
698
699 select case(convert_type)
700 case('EIvtiCCmpi','EIvtuCCmpi')
702 case('ESvtiCCmpi','ESvtuCCmpi')
704 case('SIvtiCCmpi','SIvtuCCmpi')
706 case('WIvtiCCmpi','WIvtuCCmpi')
708 case default
709 call mpistop("Error in synthesize emission: Unknown convert_type")
710 end select
711 end subroutine rmhd_te_images
712}
713
714!!start th cond
715 ! wrappers for STS functions in thermal_conductivity module
716 ! which take as argument the tc_fluid (defined in the physics module)
717 subroutine rmhd_sts_set_source_tc_rmhd(ixI^L,ixO^L,w,x,wres,fix_conserve_at_step,my_dt,igrid,nflux)
721 integer, intent(in) :: ixi^l, ixo^l, igrid, nflux
722 double precision, intent(in) :: x(ixi^s,1:ndim)
723 double precision, intent(inout) :: wres(ixi^s,1:nw), w(ixi^s,1:nw)
724 double precision, intent(in) :: my_dt
725 logical, intent(in) :: fix_conserve_at_step
726 call sts_set_source_tc_mhd(ixi^l,ixo^l,w,x,wres,fix_conserve_at_step,my_dt,igrid,nflux,tc_fl)
727 end subroutine rmhd_sts_set_source_tc_rmhd
728
729 function rmhd_get_tc_dt_rmhd(w,ixI^L,ixO^L,dx^D,x) result(dtnew)
730 !Check diffusion time limit dt < dx_i**2/((gamma-1)*tc_k_para_i/rho)
731 !where tc_k_para_i=tc_k_para*B_i**2/B**2
732 !and T=p/rho
735 integer, intent(in) :: ixi^l, ixo^l
736 double precision, intent(in) :: dx^d, x(ixi^s,1:ndim)
737 double precision, intent(in) :: w(ixi^s,1:nw)
738 double precision :: dtnew
739
740 dtnew=get_tc_dt_mhd(w,ixi^l,ixo^l,dx^d,x,tc_fl)
741 end function rmhd_get_tc_dt_rmhd
742
743 subroutine rmhd_tc_handle_small_e(w, x, ixI^L, ixO^L, step)
745 integer, intent(in) :: ixi^l,ixo^l
746 double precision, intent(inout) :: w(ixi^s,1:nw)
747 double precision, intent(in) :: x(ixi^s,1:ndim)
748 integer, intent(in) :: step
749 character(len=140) :: error_msg
750
751 write(error_msg,"(a,i3)") "Thermal conduction step ", step
752 call rmhd_handle_small_ei(w,x,ixi^l,ixo^l,e_,error_msg)
753 end subroutine rmhd_tc_handle_small_e
754
755 ! fill in tc_fluid fields from namelist
756 subroutine tc_params_read_rmhd(fl)
758 type(tc_fluid), intent(inout) :: fl
759 double precision :: tc_k_para=0d0
760 double precision :: tc_k_perp=0d0
761 integer :: n
762 ! list parameters
763 logical :: tc_perpendicular=.false.
764 logical :: tc_saturate=.false.
765 character(len=std_len) :: tc_slope_limiter="MC"
766
767 namelist /tc_list/ tc_perpendicular, tc_saturate, tc_slope_limiter, tc_k_para, tc_k_perp
768
769 do n = 1, size(par_files)
770 open(unitpar, file=trim(par_files(n)), status="old")
771 read(unitpar, tc_list, end=111)
772111 close(unitpar)
773 end do
774
775 fl%tc_perpendicular = tc_perpendicular
776 fl%tc_saturate = tc_saturate
777 fl%tc_k_para = tc_k_para
778 fl%tc_k_perp = tc_k_perp
779 select case(tc_slope_limiter)
780 case ('no','none')
781 fl%tc_slope_limiter = 0
782 case ('MC')
783 ! montonized central limiter Woodward and Collela limiter (eq.3.51h), a factor of 2 is pulled out
784 fl%tc_slope_limiter = 1
785 case('minmod')
786 ! minmod limiter
787 fl%tc_slope_limiter = 2
788 case ('superbee')
789 ! Roes superbee limiter (eq.3.51i)
790 fl%tc_slope_limiter = 3
791 case ('koren')
792 ! Barry Koren Right variant
793 fl%tc_slope_limiter = 4
794 case default
795 call mpistop("Unknown tc_slope_limiter, choose MC, minmod")
796 end select
797 end subroutine tc_params_read_rmhd
798!!end th cond
799
800 !> sets the equilibrium variables
801 subroutine set_equi_vars_grid_faces(igrid,x,ixI^L,ixO^L)
804 integer, intent(in) :: igrid, ixi^l, ixo^l
805 double precision, intent(in) :: x(ixi^s,1:ndim)
806 double precision :: delx(ixi^s,1:ndim)
807 double precision :: xc(ixi^s,1:ndim),xshift^d
808 integer :: idims, ixc^l, hxo^l, ix, idims2
809
810 if(slab_uniform)then
811 ^d&delx(ixi^s,^d)=rnode(rpdx^d_,igrid)\
812 else
813 ! for all non-cartesian and stretched cartesian coordinates
814 delx(ixi^s,1:ndim)=ps(igrid)%dx(ixi^s,1:ndim)
815 endif
816
817 do idims=1,ndim
818 hxo^l=ixo^l-kr(idims,^d);
819 if(stagger_grid) then
820 ! ct needs all transverse cells
821 ixcmax^d=ixomax^d+nghostcells-nghostcells*kr(idims,^d); ixcmin^d=hxomin^d-nghostcells+nghostcells*kr(idims,^d);
822 else
823 ! ixC is centered index in the idims direction from ixOmin-1/2 to ixOmax+1/2
824 ixcmax^d=ixomax^d; ixcmin^d=hxomin^d;
825 end if
826 ! always xshift=0 or 1/2
827 xshift^d=half*(one-kr(^d,idims));
828 do idims2=1,ndim
829 select case(idims2)
830 {case(^d)
831 do ix = ixc^lim^d
832 ! xshift=half: this is the cell center coordinate
833 ! xshift=0: this is the cell edge i+1/2 coordinate
834 xc(ix^d%ixC^s,^d)=x(ix^d%ixC^s,^d)+(half-xshift^d)*delx(ix^d%ixC^s,^d)
835 end do\}
836 end select
837 end do
838 call usr_set_equi_vars(ixi^l,ixc^l,xc,ps(igrid)%equi_vars(ixi^s,1:number_equi_vars,idims))
839 end do
840 end subroutine set_equi_vars_grid_faces
841
842 !> sets the equilibrium variables
843 subroutine set_equi_vars_grid(igrid)
846 integer, intent(in) :: igrid
847
848 !values at the center
849 call usr_set_equi_vars(ixg^ll,ixg^ll,ps(igrid)%x,ps(igrid)%equi_vars(ixg^t,1:number_equi_vars,0))
850
851 !values at the interfaces
852 call set_equi_vars_grid_faces(igrid,ps(igrid)%x,ixg^ll,ixm^ll)
853 end subroutine set_equi_vars_grid
854
855 ! w, wnew conserved, add splitted variables back to wnew
856 function convert_vars_splitting(ixI^L,ixO^L, w, x, nwc) result(wnew)
858 integer, intent(in) :: ixi^l,ixo^l, nwc
859 double precision, intent(in) :: w(ixi^s, 1:nw)
860 double precision, intent(in) :: x(ixi^s,1:ndim)
861 double precision :: wnew(ixo^s, 1:nwc)
862
863 if(has_equi_rho0) then
864 wnew(ixo^s,rho_)=w(ixo^s,rho_)+block%equi_vars(ixo^s,equi_rho0_,0)
865 else
866 wnew(ixo^s,rho_)=w(ixo^s,rho_)
867 endif
868 wnew(ixo^s,mom(:))=w(ixo^s,mom(:))
869
870 if (b0field) then
871 ! add background magnetic field B0 to B
872 wnew(ixo^s,mag(1:ndir))=w(ixo^s,mag(1:ndir))+block%B0(ixo^s,1:ndir,0)
873 else
874 wnew(ixo^s,mag(1:ndir))=w(ixo^s,mag(1:ndir))
875 end if
876
877 if(rmhd_energy) then
878 wnew(ixo^s,e_)=w(ixo^s,e_)
879 if(has_equi_pe0) then
880 wnew(ixo^s,e_)=wnew(ixo^s,e_)+block%equi_vars(ixo^s,equi_pe0_,0)*inv_gamma_1
881 end if
882 if(b0field .and. total_energy) then
883 wnew(ixo^s,e_)=wnew(ixo^s,e_)+0.5d0*sum(block%B0(ixo^s,:,0)**2,dim=ndim+1) &
884 + sum(w(ixo^s,mag(:))*block%B0(ixo^s,:,0),dim=ndim+1)
885 end if
886 end if
887 end function convert_vars_splitting
888
889 subroutine rmhd_check_params
893
894 ! after user parameter setting
895 gamma_1=rmhd_gamma-1.d0
896 if (.not. rmhd_energy) then
897 if (rmhd_gamma <= 0.0d0) call mpistop ("Error: rmhd_gamma <= 0")
898 if (rmhd_adiab < 0.0d0) call mpistop ("Error: rmhd_adiab < 0")
900 else
901 if (rmhd_gamma <= 0.0d0 .or. rmhd_gamma == 1.0d0) &
902 call mpistop ("Error: rmhd_gamma <= 0 or rmhd_gamma == 1")
903 inv_gamma_1=1.d0/gamma_1
904 small_e = small_pressure * inv_gamma_1
905 end if
906
908
909 if (number_equi_vars > 0 .and. .not. associated(usr_set_equi_vars)) then
910 call mpistop("usr_set_equi_vars has to be implemented in the user file")
911 endif
912 if(convert .or. autoconvert) then
913 if(convert_type .eq. 'dat_generic_mpi') then
914 if(rmhd_dump_full_vars) then
915 if(mype .eq. 0) print*, " add conversion method: split -> full "
916 call add_convert_method(convert_vars_splitting, nw, cons_wnames, "new")
917 endif
918 endif
919 endif
920
922 end subroutine rmhd_check_params
923
924 !> Set the boundaries for the diffusion of E
929 integer :: ib
930
931 ! Set boundary conditions for the multigrid solver
932 do ib = 1, 2*ndim
933 select case (typeboundary(r_e, ib))
934 case (bc_symm)
935 ! d/dx u = 0
936 mg%bc(ib, mg_iphi)%bc_type = mg_bc_neumann
937 mg%bc(ib, mg_iphi)%bc_value = 0.0_dp
938 case (bc_asymm)
939 ! u = 0
940 mg%bc(ib, mg_iphi)%bc_type = mg_bc_dirichlet
941 mg%bc(ib, mg_iphi)%bc_value = 0.0_dp
942 case (bc_cont)
943 ! d/dx u = 0
944 ! mg%bc(iB, mg_iphi)%bc_type = mg_bc_continuous
945 mg%bc(ib, mg_iphi)%bc_type = mg_bc_neumann
946 mg%bc(ib, mg_iphi)%bc_value = 0.0_dp
947 case (bc_periodic)
948 ! Nothing to do here
949 case (bc_noinflow)
950 call usr_special_mg_bc(ib)
951 case (bc_special)
952 call usr_special_mg_bc(ib)
953 case default
954 call mpistop("divE_multigrid warning: unknown b.c. ")
955 end select
956 end do
957 end subroutine rmhd_set_mg_bounds
958
959 subroutine rmhd_physical_units()
961 double precision :: mp,kb,miu0,c_lightspeed
962 double precision :: a,b
963
964 ! Derive scaling units
965 if(si_unit) then
966 mp=mp_si
967 kb=kb_si
968 miu0=miu0_si
969 c_lightspeed=c_si
970 else
971 mp=mp_cgs
972 kb=kb_cgs
973 miu0=4.d0*dpi ! G^2 cm^2 dyne^-1
974 c_lightspeed=const_c
975 end if
976 if(eq_state_units) then
977 a = 1d0 + 4d0 * he_abundance
979 b = 2+.3d0
980 else
981 b = 1d0 + h_ion_fr + he_abundance*(he_ion_fr*(he_ion_fr2 + 1d0)+1d0)
982 end if
983 rr = 1d0
984 else
985 a = 1d0
986 b = 1d0
987 rr = (1d0 + h_ion_fr + he_abundance*(he_ion_fr*(he_ion_fr2 + 1d0)+1d0))/(1d0 + 4d0 * he_abundance)
988 end if
989 if(unit_density/=1.d0) then
991 else
992 ! unit of numberdensity is independent by default
994 end if
995 if(unit_velocity/=1.d0) then
999 else if(unit_pressure/=1.d0) then
1003 else if(unit_magneticfield/=1.d0) then
1007 else if(unit_temperature/=1.d0) then
1011 end if
1012 if(unit_time/=1.d0) then
1014 else
1015 ! unit of length is independent by default
1017 end if
1018 ! Additional units needed for the particles
1019 c_norm=c_lightspeed/unit_velocity
1021 if (.not. si_unit) unit_charge = unit_charge*const_c
1023
1024 !> Units for radiative flux and opacity
1027 end subroutine rmhd_physical_units
1028
1029 subroutine rmhd_check_w_origin(primitive,ixI^L,ixO^L,w,flag)
1031 logical, intent(in) :: primitive
1032 integer, intent(in) :: ixi^l, ixo^l
1033 double precision, intent(in) :: w(ixi^s,nw)
1034 logical, intent(inout) :: flag(ixi^s,1:nw)
1035 double precision :: tmp
1036 integer :: ix^d
1037
1038 flag=.false.
1039 {do ix^db=ixomin^db,ixomax^db\}
1040 if(has_equi_rho0) then
1041 tmp=w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0)
1042 else
1043 tmp=w(ix^d,rho_)
1044 end if
1045 if(tmp<small_density) flag(ix^d,rho_) = .true.
1046 if(primitive) then
1047 if(has_equi_pe0) then
1048 if(w(ix^d,p_)+block%equi_vars(ix^d,equi_pe0_,0)<small_pressure) flag(ix^d,e_) = .true.
1049 else
1050 if(w(ix^d,p_)<small_pressure) flag(ix^d,e_) = .true.
1051 end if
1052 else
1053 tmp=w(ix^d,e_)-half*((^c&w(ix^d,m^c_)**2+)/tmp+(^c&w(ix^d,b^c_)**2+))
1054 if(has_equi_pe0) then
1055 if(tmp+block%equi_vars(ix^d,equi_pe0_,0)*inv_gamma_1<small_e) flag(ix^d,e_) = .true.
1056 else
1057 if(tmp<small_e) flag(ix^d,e_) = .true.
1058 end if
1059 end if
1060 if(w(ix^d,r_e)<small_r_e) flag(ix^d,r_e) = .true.
1061 {end do\}
1062 end subroutine rmhd_check_w_origin
1063
1064 !> Transform primitive variables into conservative ones
1065 subroutine rmhd_to_conserved_origin(ixI^L,ixO^L,w,x)
1067 integer, intent(in) :: ixi^l, ixo^l
1068 double precision, intent(inout) :: w(ixi^s, nw)
1069 double precision, intent(in) :: x(ixi^s, 1:ndim)
1070 integer :: ix^d
1071
1072 {do ix^db=ixomin^db,ixomax^db\}
1073 ! Calculate total energy from pressure, kinetic and magnetic energy
1074 w(ix^d,e_)=w(ix^d,p_)*inv_gamma_1&
1075 +half*((^c&w(ix^d,m^c_)**2+)*w(ix^d,rho_)&
1076 +(^c&w(ix^d,b^c_)**2+))
1077 ! Convert velocity to momentum
1078 ^c&w(ix^d,m^c_)=w(ix^d,rho_)*w(ix^d,m^c_)\
1079 {end do\}
1080 end subroutine rmhd_to_conserved_origin
1081
1082 !> Transform primitive variables into conservative ones
1083 subroutine rmhd_to_conserved_split_rho(ixI^L,ixO^L,w,x)
1085 integer, intent(in) :: ixi^l, ixo^l
1086 double precision, intent(inout) :: w(ixi^s, nw)
1087 double precision, intent(in) :: x(ixi^s, 1:ndim)
1088 double precision :: rho
1089 integer :: ix^d
1090
1091 {do ix^db=ixomin^db,ixomax^db\}
1092 rho=w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i)
1093 ! Calculate total energy from pressure, kinetic and magnetic energy
1094 w(ix^d,e_)=w(ix^d,p_)*inv_gamma_1&
1095 +half*((^c&w(ix^d,m^c_)**2+)*rho&
1096 +(^c&w(ix^d,b^c_)**2+))
1097 ! Convert velocity to momentum
1098 ^c&w(ix^d,m^c_)=rho*w(ix^d,m^c_)\
1099 {end do\}
1100 end subroutine rmhd_to_conserved_split_rho
1101
1102 !> Transform conservative variables into primitive ones
1103 subroutine rmhd_to_primitive_origin(ixI^L,ixO^L,w,x)
1105 integer, intent(in) :: ixi^l, ixo^l
1106 double precision, intent(inout) :: w(ixi^s, nw)
1107 double precision, intent(in) :: x(ixi^s, 1:ndim)
1108 double precision :: inv_rho
1109 integer :: ix^d
1110
1111 if (fix_small_values) then
1112 ! fix small values preventing NaN numbers in the following converting
1113 call rmhd_handle_small_values(.false., w, x, ixi^l, ixo^l, 'rmhd_to_primitive_origin')
1114 end if
1115
1116 {do ix^db=ixomin^db,ixomax^db\}
1117 inv_rho = 1.d0/w(ix^d,rho_)
1118 ! Convert momentum to velocity
1119 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
1120 ! Calculate pressure = (gamma-1) * (e-ek-eb)
1121 w(ix^d,p_)=gamma_1*(w(ix^d,e_)&
1122 -half*(w(ix^d,rho_)*(^c&w(ix^d,m^c_)**2+)&
1123 +(^c&w(ix^d,b^c_)**2+)))
1124 {end do\}
1125 end subroutine rmhd_to_primitive_origin
1126
1127 !> Transform conservative variables into primitive ones
1128 subroutine rmhd_to_primitive_split_rho(ixI^L,ixO^L,w,x)
1130 integer, intent(in) :: ixi^l, ixo^l
1131 double precision, intent(inout) :: w(ixi^s, nw)
1132 double precision, intent(in) :: x(ixi^s, 1:ndim)
1133 double precision :: inv_rho
1134 integer :: ix^d
1135
1136 if (fix_small_values) then
1137 ! fix small values preventing NaN numbers in the following converting
1138 call rmhd_handle_small_values(.false., w, x, ixi^l, ixo^l, 'rmhd_to_primitive_split_rho')
1139 end if
1140
1141 {do ix^db=ixomin^db,ixomax^db\}
1142 inv_rho=1.d0/(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
1143 ! Convert momentum to velocity
1144 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)*inv_rho\
1145 ! Calculate pressure = (gamma-1) * (e-ek-eb)
1146 w(ix^d,p_)=gamma_1*(w(ix^d,e_)&
1147 -half*((w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))*&
1148 (^c&w(ix^d,m^c_)**2+)+(^c&w(ix^d,b^c_)**2+)))
1149 {end do\}
1150 end subroutine rmhd_to_primitive_split_rho
1151
1152 !> Transform internal energy to total energy
1153 subroutine rmhd_ei_to_e(ixI^L,ixO^L,w,x)
1155 integer, intent(in) :: ixi^l, ixo^l
1156 double precision, intent(inout) :: w(ixi^s, nw)
1157 double precision, intent(in) :: x(ixi^s, 1:ndim)
1158
1159 integer :: ix^d
1160
1161 if(has_equi_rho0) then
1162 {do ix^db=ixomin^db,ixomax^db\}
1163 ! Calculate e = ei + ek + eb
1164 w(ix^d,e_)=w(ix^d,e_)&
1165 +half*((^c&w(ix^d,m^c_)**2+)/&
1166 (w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0))&
1167 +(^c&w(ix^d,b^c_)**2+))
1168 {end do\}
1169 else
1170 {do ix^db=ixomin^db,ixomax^db\}
1171 ! Calculate e = ei + ek + eb
1172 w(ix^d,e_)=w(ix^d,e_)&
1173 +half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)&
1174 +(^c&w(ix^d,b^c_)**2+))
1175 {end do\}
1176 end if
1177 end subroutine rmhd_ei_to_e
1178
1179 !> Transform total energy to internal energy
1180 subroutine rmhd_e_to_ei(ixI^L,ixO^L,w,x)
1182 integer, intent(in) :: ixi^l, ixo^l
1183 double precision, intent(inout) :: w(ixi^s, nw)
1184 double precision, intent(in) :: x(ixi^s, 1:ndim)
1185
1186 integer :: ix^d
1187
1188 if(has_equi_rho0) then
1189 {do ix^db=ixomin^db,ixomax^db\}
1190 ! Calculate ei = e - ek - eb
1191 w(ix^d,e_)=w(ix^d,e_)&
1192 -half*((^c&w(ix^d,m^c_)**2+)/&
1193 (w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0))&
1194 +(^c&w(ix^d,b^c_)**2+))
1195 {end do\}
1196 else
1197 {do ix^db=ixomin^db,ixomax^db\}
1198 ! Calculate ei = e - ek - eb
1199 w(ix^d,e_)=w(ix^d,e_)&
1200 -half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)&
1201 +(^c&w(ix^d,b^c_)**2+))
1202 {end do\}
1203 end if
1204
1205 if(fix_small_values) then
1206 call rmhd_handle_small_ei(w,x,ixi^l,ixi^l,e_,'rmhd_e_to_ei')
1207 end if
1208 end subroutine rmhd_e_to_ei
1209
1210 subroutine rmhd_handle_small_values_origin(primitive, w, x, ixI^L, ixO^L, subname)
1213 logical, intent(in) :: primitive
1214 integer, intent(in) :: ixi^l,ixo^l
1215 double precision, intent(inout) :: w(ixi^s,1:nw)
1216 double precision, intent(in) :: x(ixi^s,1:ndim)
1217 character(len=*), intent(in) :: subname
1218 double precision :: rho
1219 integer :: idir, ix^d
1220 logical :: flag(ixi^s,1:nw)
1221
1222 call phys_check_w(primitive, ixi^l, ixi^l, w, flag)
1223 if(any(flag)) then
1224 select case (small_values_method)
1225 case ("replace")
1226 {do ix^db=ixomin^db,ixomax^db\}
1227 if(has_equi_rho0) then
1228 rho=w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0)
1229 if(flag(ix^d,rho_)) w(ix^d,rho_)=small_density-block%equi_vars(ix^d,equi_rho0_,0)
1230 else
1231 rho=w(ix^d,rho_)
1232 if(flag(ix^d,rho_)) w(ix^d,rho_)=small_density
1233 end if
1234 {
1235 if(small_values_fix_iw(m^c_)) then
1236 if(flag({ix^d},rho_)) w({ix^d},m^c_)=0.0d0
1237 end if
1238 \}
1239 if(primitive) then
1240 if(has_equi_pe0) then
1241 if(flag(ix^d,e_)) w(ix^d,p_)=small_pressure-block%equi_vars(ix^d,equi_pe0_,0)
1242 else
1243 if(flag(ix^d,e_)) w(ix^d,p_)=small_pressure
1244 end if
1245 else
1246 if(has_equi_pe0) then
1247 if(flag(ix^d,e_)) &
1248 w(ix^d,e_)=small_e+half*((^c&w(ix^d,m^c_)**2+)/rho+(^c&w(ix^d,b^c_)**2+))&
1249 -block%equi_vars(ix^d,equi_pe0_,0)*inv_gamma_1
1250 else
1251 if(flag(ix^d,e_)) &
1252 w(ix^d,e_)=small_e+half*((^c&w(ix^d,m^c_)**2+)/rho+(^c&w(ix^d,b^c_)**2+))
1253 end if
1254 end if
1255 if(flag(ix^d,r_e)) w(ix^d,r_e)=small_r_e
1256 {end do\}
1257 case ("average")
1258 ! do averaging of density
1259 call small_values_average(ixi^l, ixo^l, w, x, flag, rho_)
1260 if(primitive)then
1261 call small_values_average(ixi^l, ixo^l, w, x, flag, p_)
1262 else
1263 ! do averaging of internal energy
1264 {do ix^db=iximin^db,iximax^db\}
1265 if(has_equi_rho0) then
1266 rho=w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0)
1267 else
1268 rho=w(ix^d,rho_)
1269 end if
1270 w(ix^d,e_)=w(ix^d,e_)&
1271 -half*((^c&w(ix^d,m^c_)**2+)/rho+(^c&w(ix^d,b^c_)**2+))
1272 {end do\}
1273 call small_values_average(ixi^l, ixo^l, w, x, flag, e_)
1274 ! convert back
1275 {do ix^db=iximin^db,iximax^db\}
1276 if(has_equi_rho0) then
1277 rho=w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0)
1278 else
1279 rho=w(ix^d,rho_)
1280 end if
1281 w(ix^d,e_)=w(ix^d,e_)&
1282 +half*((^c&w(ix^d,m^c_)**2+)/rho+(^c&w(ix^d,b^c_)**2+))
1283 {end do\}
1284 end if
1285 call small_values_average(ixi^l, ixo^l, w, x, flag, r_e)
1286 case default
1287 if(.not.primitive) then
1288 !convert w to primitive
1289 ! do averaging of internal energy
1290 {do ix^db=iximin^db,iximax^db\}
1291 if(has_equi_rho0) then
1292 rho=w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0)
1293 else
1294 rho=w(ix^d,rho_)
1295 end if
1296 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)/rho\
1297 w(ix^d,p_)=gamma_1*(w(ix^d,e_)&
1298 -half*((^c&w(ix^d,m^c_)**2+)/rho+(^c&w(ix^d,b^c_)**2+)))
1299 {end do\}
1300 end if
1301 call small_values_error(w, x, ixi^l, ixo^l, flag, subname)
1302 end select
1303 end if
1304 end subroutine rmhd_handle_small_values_origin
1305
1306 !> Calculate v vector
1307 subroutine rmhd_get_v(w,x,ixI^L,ixO^L,v)
1309 integer, intent(in) :: ixi^l, ixo^l
1310 double precision, intent(in) :: w(ixi^s,nw), x(ixi^s,1:ndim)
1311 double precision, intent(out) :: v(ixi^s,ndir)
1312 double precision :: rho(ixi^s)
1313 integer :: idir
1314
1315 call rmhd_get_rho(w,x,ixi^l,ixo^l,rho)
1316 rho(ixo^s)=1.d0/rho(ixo^s)
1317 ! Convert momentum to velocity
1318 do idir = 1, ndir
1319 v(ixo^s, idir) = w(ixo^s, mom(idir))*rho(ixo^s)
1320 end do
1321 end subroutine rmhd_get_v
1322
1323 !> Calculate cmax_idim=csound+abs(v_idim) within ixO^L
1324 subroutine rmhd_get_cmax_origin(w,x,ixI^L,ixO^L,idim,cmax)
1326 integer, intent(in) :: ixi^l, ixo^l, idim
1327 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
1328 double precision, intent(inout) :: cmax(ixi^s)
1329 double precision :: rho, inv_rho, cfast2, avmincs2, b2, kmax
1330 integer :: ix^d
1331
1332 if(b0field) then
1333 {do ix^db=ixomin^db,ixomax^db \}
1334 if(has_equi_rho0) then
1335 rho=(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
1336 else
1337 rho=w(ix^d,rho_)
1338 end if
1339 inv_rho=1.d0/rho
1340 ! sound speed**2
1341 cmax(ix^d)=rmhd_gamma*w(ix^d,p_)*inv_rho
1342 ! store |B|^2 in v
1343 b2=(^c&(w(ix^d,b^c_)+block%B0(ix^d,^c,b0i))**2+)
1344 cfast2=b2*inv_rho+cmax(ix^d)
1345 avmincs2=cfast2**2-4.0d0*cmax(ix^d)*(w(ix^d,mag(idim))+block%B0(ix^d,idim,b0i))**2*inv_rho
1346 if(avmincs2<zero) avmincs2=zero
1347 cmax(ix^d)=sqrt(half*(cfast2+sqrt(avmincs2)))
1348 cmax(ix^d)=abs(w(ix^d,mom(idim)))+cmax(ix^d)
1349 {end do\}
1350 else
1351 {do ix^db=ixomin^db,ixomax^db \}
1352 if(has_equi_rho0) then
1353 rho=(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
1354 else
1355 rho=w(ix^d,rho_)
1356 end if
1357 inv_rho=1.d0/rho
1358 ! sound speed**2
1359 cmax(ix^d)=rmhd_gamma*w(ix^d,p_)*inv_rho
1360 ! store |B|^2 in v
1361 b2=(^c&w(ix^d,b^c_)**2+)
1362 cfast2=b2*inv_rho+cmax(ix^d)
1363 avmincs2=cfast2**2-4.0d0*cmax(ix^d)*w(ix^d,mag(idim))**2*inv_rho
1364 if(avmincs2<zero) avmincs2=zero
1365 cmax(ix^d)=sqrt(half*(cfast2+sqrt(avmincs2)))
1366 cmax(ix^d)=abs(w(ix^d,mom(idim)))+cmax(ix^d)
1367 {end do\}
1368 end if
1369 end subroutine rmhd_get_cmax_origin
1370
1371 subroutine rmhd_get_a2max(w,x,ixI^L,ixO^L,a2max)
1373 integer, intent(in) :: ixi^l, ixo^l
1374 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
1375 double precision, intent(inout) :: a2max(ndim)
1376 double precision :: a2(ixi^s,ndim,nw)
1377 integer :: gxo^l,hxo^l,jxo^l,kxo^l,i,j
1378
1379 a2=zero
1380 do i = 1,ndim
1381 !> 4th order
1382 hxo^l=ixo^l-kr(i,^d);
1383 gxo^l=hxo^l-kr(i,^d);
1384 jxo^l=ixo^l+kr(i,^d);
1385 kxo^l=jxo^l+kr(i,^d);
1386 a2(ixo^s,i,1:nw)=abs(-w(kxo^s,1:nw)+16.d0*w(jxo^s,1:nw)&
1387 -30.d0*w(ixo^s,1:nw)+16.d0*w(hxo^s,1:nw)-w(gxo^s,1:nw))
1388 a2max(i)=maxval(a2(ixo^s,i,1:nw))/12.d0/dxlevel(i)**2
1389 end do
1390 end subroutine rmhd_get_a2max
1391
1392 !> get adaptive cutoff temperature for TRAC (Johnston 2019 ApJL, 873, L22)
1393 subroutine rmhd_get_tcutoff(ixI^L,ixO^L,w,x,Tco_local,Tmax_local)
1395 use mod_geometry
1396 integer, intent(in) :: ixi^l,ixo^l
1397 double precision, intent(in) :: x(ixi^s,1:ndim)
1398 double precision, intent(out) :: tco_local,tmax_local
1399 ! in primitive form
1400 double precision, intent(inout) :: w(ixi^s,1:nw)
1401 double precision, parameter :: trac_delta=0.25d0
1402 double precision :: tmp1(ixi^s),te(ixi^s),lts(ixi^s)
1403 double precision, dimension(ixI^S,1:ndir) :: bunitvec
1404 double precision, dimension(ixI^S,1:ndim) :: gradt
1405 double precision :: bdir(ndim)
1406 double precision :: ltrc,ltrp,altr(ixi^s)
1407 integer :: idims,jxo^l,hxo^l,ixa^d,ixb^d,ix^d
1408 integer :: jxp^l,hxp^l,ixp^l,ixq^l
1409 logical :: lrlt(ixi^s)
1410
1412 call rmhd_get_temperature_from_te(w,x,ixi^l,ixi^l,te)
1413 else
1414 call rmhd_get_rfactor(w,x,ixi^l,ixi^l,te)
1415 te(ixi^s)=w(ixi^s,p_)/(te(ixi^s)*w(ixi^s,rho_))
1416 end if
1417 tco_local=zero
1418 tmax_local=maxval(te(ixo^s))
1419
1420 {^ifoned
1421 select case(rmhd_trac_type)
1422 case(0)
1423 !> test case, fixed cutoff temperature
1424 block%wextra(ixi^s,tcoff_)=2.5d5/unit_temperature
1425 case(1)
1426 hxo^l=ixo^l-1;
1427 jxo^l=ixo^l+1;
1428 lts(ixo^s)=0.5d0*abs(te(jxo^s)-te(hxo^s))/te(ixo^s)
1429 lrlt=.false.
1430 where(lts(ixo^s) > trac_delta)
1431 lrlt(ixo^s)=.true.
1432 end where
1433 if(any(lrlt(ixo^s))) then
1434 tco_local=maxval(te(ixo^s), mask=lrlt(ixo^s))
1435 end if
1436 case(2)
1437 !> iijima et al. 2021, LTRAC method
1438 ltrc=1.5d0
1439 ltrp=4.d0
1440 ixp^l=ixo^l^ladd1;
1441 hxo^l=ixo^l-1;
1442 jxo^l=ixo^l+1;
1443 hxp^l=ixp^l-1;
1444 jxp^l=ixp^l+1;
1445 lts(ixp^s)=0.5d0*abs(te(jxp^s)-te(hxp^s))/te(ixp^s)
1446 lts(ixp^s)=max(one, (exp(lts(ixp^s))/ltrc)**ltrp)
1447 lts(ixo^s)=0.25d0*(lts(jxo^s)+two*lts(ixo^s)+lts(hxo^s))
1448 block%wextra(ixo^s,tcoff_)=te(ixo^s)*lts(ixo^s)**0.4d0
1449 case default
1450 call mpistop("rmhd_trac_type not allowed for 1D simulation")
1451 end select
1452 }
1453 {^nooned
1454 select case(rmhd_trac_type)
1455 case(0)
1456 !> test case, fixed cutoff temperature
1457 block%wextra(ixi^s,tcoff_)=2.5d5/unit_temperature
1458 case(1,4,6)
1459 ! temperature gradient at cell centers
1460 do idims=1,ndim
1461 call gradient(te,ixi^l,ixo^l,idims,tmp1)
1462 gradt(ixo^s,idims)=tmp1(ixo^s)
1463 end do
1464 ! B vector
1465 if(b0field) then
1466 bunitvec(ixo^s,:)=w(ixo^s,iw_mag(:))+block%B0(ixo^s,:,0)
1467 else
1468 bunitvec(ixo^s,:)=w(ixo^s,iw_mag(:))
1469 end if
1470 if(rmhd_trac_type .gt. 1) then
1471 ! B direction at cell center
1472 bdir=zero
1473 {do ixa^d=0,1\}
1474 ixb^d=(ixomin^d+ixomax^d-1)/2+ixa^d;
1475 bdir(1:ndim)=bdir(1:ndim)+bunitvec(ixb^d,1:ndim)
1476 {end do\}
1477 if(sum(bdir(:)**2) .gt. zero) then
1478 bdir(1:ndim)=bdir(1:ndim)/dsqrt(sum(bdir(:)**2))
1479 end if
1480 block%special_values(3:ndim+2)=bdir(1:ndim)
1481 end if
1482 tmp1(ixo^s)=dsqrt(sum(bunitvec(ixo^s,:)**2,dim=ndim+1))
1483 where(tmp1(ixo^s)/=0.d0)
1484 tmp1(ixo^s)=1.d0/tmp1(ixo^s)
1485 elsewhere
1486 tmp1(ixo^s)=bigdouble
1487 end where
1488 ! b unit vector: magnetic field direction vector
1489 do idims=1,ndim
1490 bunitvec(ixo^s,idims)=bunitvec(ixo^s,idims)*tmp1(ixo^s)
1491 end do
1492 ! temperature length scale inversed
1493 lts(ixo^s)=abs(sum(gradt(ixo^s,1:ndim)*bunitvec(ixo^s,1:ndim),dim=ndim+1))/te(ixo^s)
1494 ! fraction of cells size to temperature length scale
1495 if(slab_uniform) then
1496 lts(ixo^s)=minval(dxlevel)*lts(ixo^s)
1497 else
1498 lts(ixo^s)=minval(block%ds(ixo^s,:),dim=ndim+1)*lts(ixo^s)
1499 end if
1500 lrlt=.false.
1501 where(lts(ixo^s) > trac_delta)
1502 lrlt(ixo^s)=.true.
1503 end where
1504 if(any(lrlt(ixo^s))) then
1505 block%special_values(1)=maxval(te(ixo^s), mask=lrlt(ixo^s))
1506 else
1507 block%special_values(1)=zero
1508 end if
1509 block%special_values(2)=tmax_local
1510 case(2)
1511 !> iijima et al. 2021, LTRAC method
1512 ltrc=1.5d0
1513 ltrp=4.d0
1514 ixp^l=ixo^l^ladd2;
1515 ! temperature gradient at cell centers
1516 do idims=1,ndim
1517 ixq^l=ixp^l;
1518 hxp^l=ixp^l;
1519 jxp^l=ixp^l;
1520 select case(idims)
1521 {case(^d)
1522 ixqmin^d=ixqmin^d+1
1523 ixqmax^d=ixqmax^d-1
1524 hxpmax^d=ixpmin^d
1525 jxpmin^d=ixpmax^d
1526 \}
1527 end select
1528 call gradient(te,ixi^l,ixq^l,idims,gradt(ixi^s,idims))
1529 call gradientf(te,x,ixi^l,hxp^l,idims,gradt(ixi^s,idims),nghostcells,.true.)
1530 call gradientf(te,x,ixi^l,jxp^l,idims,gradt(ixi^s,idims),nghostcells,.false.)
1531 end do
1532 ! B vector
1533 {do ix^db=ixpmin^db,ixpmax^db\}
1534 if(b0field) then
1535 ^c&bunitvec(ix^d,^c)=w(ix^d,iw_mag(^c))+block%B0(ix^d,^c,0)\
1536 else
1537 ^c&bunitvec(ix^d,^c)=w(ix^d,iw_mag(^c))\
1538 end if
1539 tmp1(ix^d)=1.d0/(dsqrt(^c&bunitvec(ix^d,^c)**2+)+smalldouble)
1540 ! b unit vector: magnetic field direction vector
1541 ^d&bunitvec({ix^d},^d)=bunitvec({ix^d},^d)*tmp1({ix^d})\
1542 ! temperature length scale inversed
1543 lts(ix^d)=abs(^d&gradt({ix^d},^d)*bunitvec({ix^d},^d)+)/te(ix^d)
1544 ! fraction of cells size to temperature length scale
1545 if(slab_uniform) then
1546 lts(ix^d)=min(^d&dxlevel(^d))*lts(ix^d)
1547 else
1548 lts(ix^d)=min(^d&block%ds({ix^d},^d))*lts(ix^d)
1549 end if
1550 lts(ix^d)=max(one,(exp(lts(ix^d))/ltrc)**ltrp)
1551 {end do\}
1552 ! need one ghost layer for thermal conductivity
1553 ixp^l=ixo^l^ladd1;
1554 do idims=1,ndim
1555 hxo^l=ixp^l-kr(idims,^d);
1556 jxo^l=ixp^l+kr(idims,^d);
1557 if(idims==1) then
1558 altr(ixp^s)=0.25d0*(lts(hxo^s)+two*lts(ixp^s)+lts(jxo^s))*bunitvec(ixp^s,idims)**2
1559 else
1560 altr(ixp^s)=altr(ixp^s)+0.25d0*(lts(hxo^s)+two*lts(ixp^s)+lts(jxo^s))*bunitvec(ixp^s,idims)**2
1561 end if
1562 end do
1563 block%wextra(ixp^s,tcoff_)=te(ixp^s)*altr(ixp^s)**0.4d0
1564 case(3,5)
1565 !> do nothing here
1566 case default
1567 call mpistop("unknown rmhd_trac_type")
1568 end select
1569 }
1570 end subroutine rmhd_get_tcutoff
1571
1572 !> get H speed for H-correction to fix the carbuncle problem at grid-aligned shock front
1573 subroutine rmhd_get_h_speed(wprim,x,ixI^L,ixO^L,idim,Hspeed)
1575
1576 integer, intent(in) :: ixi^l, ixo^l, idim
1577 double precision, intent(in) :: wprim(ixi^s, nw)
1578 double precision, intent(in) :: x(ixi^s,1:ndim)
1579 double precision, intent(out) :: hspeed(ixi^s,1:number_species)
1580
1581 double precision :: csound(ixi^s,ndim)
1582 double precision, allocatable :: tmp(:^d&)
1583 integer :: jxc^l, ixc^l, ixa^l, id, ix^d
1584
1585 hspeed=0.d0
1586 ixa^l=ixo^l^ladd1;
1587 allocate(tmp(ixa^s))
1588 do id=1,ndim
1589 call rmhd_get_csound_prim(wprim,x,ixi^l,ixa^l,id,tmp)
1590 csound(ixa^s,id)=tmp(ixa^s)
1591 end do
1592 ixcmax^d=ixomax^d;
1593 ixcmin^d=ixomin^d+kr(idim,^d)-1;
1594 jxcmax^d=ixcmax^d+kr(idim,^d);
1595 jxcmin^d=ixcmin^d+kr(idim,^d);
1596 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))
1597
1598 do id=1,ndim
1599 if(id==idim) cycle
1600 ixamax^d=ixcmax^d+kr(id,^d);
1601 ixamin^d=ixcmin^d+kr(id,^d);
1602 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)))
1603 ixamax^d=ixcmax^d-kr(id,^d);
1604 ixamin^d=ixcmin^d-kr(id,^d);
1605 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)))
1606 end do
1607
1608 do id=1,ndim
1609 if(id==idim) cycle
1610 ixamax^d=jxcmax^d+kr(id,^d);
1611 ixamin^d=jxcmin^d+kr(id,^d);
1612 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)))
1613 ixamax^d=jxcmax^d-kr(id,^d);
1614 ixamin^d=jxcmin^d-kr(id,^d);
1615 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)))
1616 end do
1617 deallocate(tmp)
1618
1619 end subroutine rmhd_get_h_speed
1620
1621 !> Estimating bounds for the minimum and maximum signal velocities without split
1622 subroutine rmhd_get_cbounds(wLC,wRC,wLp,wRp,x,ixI^L,ixO^L,idim,Hspeed,cmax,cmin)
1624 integer, intent(in) :: ixi^l, ixo^l, idim
1625 double precision, intent(in) :: wlc(ixi^s, nw), wrc(ixi^s, nw)
1626 double precision, intent(in) :: wlp(ixi^s, nw), wrp(ixi^s, nw)
1627 double precision, intent(in) :: x(ixi^s,1:ndim)
1628 double precision, intent(inout) :: cmax(ixi^s,1:number_species)
1629 double precision, intent(inout), optional :: cmin(ixi^s,1:number_species)
1630 double precision, intent(in) :: hspeed(ixi^s,1:number_species)
1631
1632 double precision :: wmean(ixi^s,nw), csoundl(ixo^s), csoundr(ixo^s)
1633 double precision :: umean, dmean, tmp1, tmp2, tmp3
1634 integer :: ix^d
1635
1636 select case (boundspeed)
1637 case (1)
1638 ! This implements formula (10.52) from "Riemann Solvers and Numerical
1639 ! Methods for Fluid Dynamics" by Toro.
1640 call rmhd_get_csound_prim(wlp,x,ixi^l,ixo^l,idim,csoundl)
1641 call rmhd_get_csound_prim(wrp,x,ixi^l,ixo^l,idim,csoundr)
1642 if(present(cmin)) then
1643 {do ix^db=ixomin^db,ixomax^db\}
1644 tmp1=sqrt(wlp(ix^d,rho_))
1645 tmp2=sqrt(wrp(ix^d,rho_))
1646 tmp3=1.d0/(tmp1+tmp2)
1647 umean=(wlp(ix^d,mom(idim))*tmp1+wrp(ix^d,mom(idim))*tmp2)*tmp3
1648 dmean=sqrt((tmp1*csoundl(ix^d)**2+tmp2*csoundr(ix^d)**2)*tmp3+&
1649 half*tmp1*tmp2*tmp3**2*(wrp(ix^d,mom(idim))-wlp(ix^d,mom(idim)))**2)
1650 cmin(ix^d,1)=umean-dmean
1651 cmax(ix^d,1)=umean+dmean
1652 {end do\}
1653 if(h_correction) then
1654 {do ix^db=ixomin^db,ixomax^db\}
1655 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
1656 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
1657 {end do\}
1658 end if
1659 else
1660 {do ix^db=ixomin^db,ixomax^db\}
1661 tmp1=sqrt(wlp(ix^d,rho_))
1662 tmp2=sqrt(wrp(ix^d,rho_))
1663 tmp3=1.d0/(tmp1+tmp2)
1664 umean=(wlp(ix^d,mom(idim))*tmp1+wrp(ix^d,mom(idim))*tmp2)*tmp3
1665 dmean=sqrt((tmp1*csoundl(ix^d)**2+tmp2*csoundr(ix^d)**2)*tmp3+&
1666 half*tmp1*tmp2*tmp3**2*(wrp(ix^d,mom(idim))-wlp(ix^d,mom(idim)))**2)
1667 cmax(ix^d,1)=abs(umean)+dmean
1668 {end do\}
1669 end if
1670 case (2)
1671 wmean(ixo^s,1:nwflux)=0.5d0*(wlp(ixo^s,1:nwflux)+wrp(ixo^s,1:nwflux))
1672 call rmhd_get_csound_prim(wmean,x,ixi^l,ixo^l,idim,csoundr)
1673 if(present(cmin)) then
1674 {do ix^db=ixomin^db,ixomax^db\}
1675 cmax(ix^d,1)=max(wmean(ix^d,mom(idim))+csoundr(ix^d),zero)
1676 cmin(ix^d,1)=min(wmean(ix^d,mom(idim))-csoundr(ix^d),zero)
1677 {end do\}
1678 if(h_correction) then
1679 {do ix^db=ixomin^db,ixomax^db\}
1680 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
1681 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
1682 {end do\}
1683 end if
1684 else
1685 cmax(ixo^s,1)=abs(wmean(ixo^s,mom(idim)))+csoundr(ixo^s)
1686 end if
1687 case (3)
1688 ! Miyoshi 2005 JCP 208, 315 equation (67)
1689 call rmhd_get_csound_prim(wlp,x,ixi^l,ixo^l,idim,csoundl)
1690 call rmhd_get_csound_prim(wrp,x,ixi^l,ixo^l,idim,csoundr)
1691 if(present(cmin)) then
1692 {do ix^db=ixomin^db,ixomax^db\}
1693 csoundl(ix^d)=max(csoundl(ix^d),csoundr(ix^d))
1694 cmin(ix^d,1)=min(wlp(ix^d,mom(idim)),wrp(ix^d,mom(idim)))-csoundl(ix^d)
1695 cmax(ix^d,1)=max(wlp(ix^d,mom(idim)),wrp(ix^d,mom(idim)))+csoundl(ix^d)
1696 {end do\}
1697 if(h_correction) then
1698 {do ix^db=ixomin^db,ixomax^db\}
1699 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
1700 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
1701 {end do\}
1702 end if
1703 else
1704 {do ix^db=ixomin^db,ixomax^db\}
1705 csoundl(ix^d)=max(csoundl(ix^d),csoundr(ix^d))
1706 cmax(ix^d,1)=max(wlp(ix^d,mom(idim)),wrp(ix^d,mom(idim)))+csoundl(ix^d)
1707 {end do\}
1708 end if
1709 end select
1710 end subroutine rmhd_get_cbounds
1711
1712 !> Estimating bounds for the minimum and maximum signal velocities with rho split
1713 subroutine rmhd_get_cbounds_split_rho(wLC,wRC,wLp,wRp,x,ixI^L,ixO^L,idim,Hspeed,cmax,cmin)
1715 integer, intent(in) :: ixi^l, ixo^l, idim
1716 double precision, intent(in) :: wlc(ixi^s, nw), wrc(ixi^s, nw)
1717 double precision, intent(in) :: wlp(ixi^s, nw), wrp(ixi^s, nw)
1718 double precision, intent(in) :: x(ixi^s,1:ndim)
1719 double precision, intent(inout) :: cmax(ixi^s,1:number_species)
1720 double precision, intent(inout), optional :: cmin(ixi^s,1:number_species)
1721 double precision, intent(in) :: hspeed(ixi^s,1:number_species)
1722 double precision :: wmean(ixi^s,nw), csoundl(ixo^s), csoundr(ixo^s)
1723 double precision :: umean, dmean, tmp1, tmp2, tmp3
1724 integer :: ix^d
1725
1726 select case (boundspeed)
1727 case (1)
1728 ! This implements formula (10.52) from "Riemann Solvers and Numerical
1729 ! Methods for Fluid Dynamics" by Toro.
1730 call rmhd_get_csound_prim_split(wlp,x,ixi^l,ixo^l,idim,csoundl)
1731 call rmhd_get_csound_prim_split(wrp,x,ixi^l,ixo^l,idim,csoundr)
1732 if(present(cmin)) then
1733 {do ix^db=ixomin^db,ixomax^db\}
1734 tmp1=sqrt(wlp(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
1735 tmp2=sqrt(wrp(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
1736 tmp3=1.d0/(tmp1+tmp2)
1737 umean=(wlp(ix^d,mom(idim))*tmp1+wrp(ix^d,mom(idim))*tmp2)*tmp3
1738 dmean=sqrt((tmp1*csoundl(ix^d)**2+tmp2*csoundr(ix^d)**2)*tmp3+&
1739 half*tmp1*tmp2*tmp3**2*(wrp(ix^d,mom(idim))-wlp(ix^d,mom(idim)))**2)
1740 cmin(ix^d,1)=umean-dmean
1741 cmax(ix^d,1)=umean+dmean
1742 {end do\}
1743 if(h_correction) then
1744 {do ix^db=ixomin^db,ixomax^db\}
1745 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
1746 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
1747 {end do\}
1748 end if
1749 else
1750 {do ix^db=ixomin^db,ixomax^db\}
1751 tmp1=sqrt(wlp(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
1752 tmp2=sqrt(wrp(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
1753 tmp3=1.d0/(tmp1+tmp2)
1754 umean=(wlp(ix^d,mom(idim))*tmp1+wrp(ix^d,mom(idim))*tmp2)*tmp3
1755 dmean=sqrt((tmp1*csoundl(ix^d)**2+tmp2*csoundr(ix^d)**2)*tmp3+&
1756 half*tmp1*tmp2*tmp3**2*(wrp(ix^d,mom(idim))-wlp(ix^d,mom(idim)))**2)
1757 cmax(ix^d,1)=abs(umean)+dmean
1758 {end do\}
1759 end if
1760 case (2)
1761 wmean(ixo^s,1:nwflux)=0.5d0*(wlp(ixo^s,1:nwflux)+wrp(ixo^s,1:nwflux))
1762 call rmhd_get_csound_prim_split(wmean,x,ixi^l,ixo^l,idim,csoundr)
1763 if(present(cmin)) then
1764 {do ix^db=ixomin^db,ixomax^db\}
1765 cmax(ix^d,1)=max(wmean(ix^d,mom(idim))+csoundr(ix^d),zero)
1766 cmin(ix^d,1)=min(wmean(ix^d,mom(idim))-csoundr(ix^d),zero)
1767 {end do\}
1768 if(h_correction) then
1769 {do ix^db=ixomin^db,ixomax^db\}
1770 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
1771 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
1772 {end do\}
1773 end if
1774 else
1775 cmax(ixo^s,1)=abs(wmean(ixo^s,mom(idim)))+csoundr(ixo^s)
1776 end if
1777 case (3)
1778 ! Miyoshi 2005 JCP 208, 315 equation (67)
1779 call rmhd_get_csound_prim_split(wlp,x,ixi^l,ixo^l,idim,csoundl)
1780 call rmhd_get_csound_prim_split(wrp,x,ixi^l,ixo^l,idim,csoundr)
1781 if(present(cmin)) then
1782 {do ix^db=ixomin^db,ixomax^db\}
1783 csoundl(ix^d)=max(csoundl(ix^d),csoundr(ix^d))
1784 cmin(ix^d,1)=min(wlp(ix^d,mom(idim)),wrp(ix^d,mom(idim)))-csoundl(ix^d)
1785 cmax(ix^d,1)=max(wlp(ix^d,mom(idim)),wrp(ix^d,mom(idim)))+csoundl(ix^d)
1786 {end do\}
1787 if(h_correction) then
1788 {do ix^db=ixomin^db,ixomax^db\}
1789 cmin(ix^d,1)=sign(one,cmin(ix^d,1))*max(abs(cmin(ix^d,1)),hspeed(ix^d,1))
1790 cmax(ix^d,1)=sign(one,cmax(ix^d,1))*max(abs(cmax(ix^d,1)),hspeed(ix^d,1))
1791 {end do\}
1792 end if
1793 else
1794 {do ix^db=ixomin^db,ixomax^db\}
1795 csoundl(ix^d)=max(csoundl(ix^d),csoundr(ix^d))
1796 cmax(ix^d,1)=max(wlp(ix^d,mom(idim)),wrp(ix^d,mom(idim)))+csoundl(ix^d)
1797 {end do\}
1798 end if
1799 end select
1800 end subroutine rmhd_get_cbounds_split_rho
1801
1802 !> prepare velocities for ct methods
1803 subroutine rmhd_get_ct_velocity(vcts,wLp,wRp,ixI^L,ixO^L,idim,cmax,cmin)
1805 integer, intent(in) :: ixi^l, ixo^l, idim
1806 double precision, intent(in) :: wlp(ixi^s, nw), wrp(ixi^s, nw)
1807 double precision, intent(in) :: cmax(ixi^s)
1808 double precision, intent(in), optional :: cmin(ixi^s)
1809 type(ct_velocity), intent(inout) :: vcts
1810 integer :: idime,idimn
1811
1812 ! calculate velocities related to different UCT schemes
1813 select case(type_ct)
1814 case('average')
1815 case('uct_contact')
1816 if(.not.allocated(vcts%vnorm)) allocate(vcts%vnorm(ixi^s,1:ndim))
1817 ! get average normal velocity at cell faces
1818 vcts%vnorm(ixo^s,idim)=0.5d0*(wlp(ixo^s,mom(idim))+wrp(ixo^s,mom(idim)))
1819 case('uct_hll')
1820 if(.not.allocated(vcts%vbarC)) then
1821 allocate(vcts%vbarC(ixi^s,1:ndir,2),vcts%vbarLC(ixi^s,1:ndir,2),vcts%vbarRC(ixi^s,1:ndir,2))
1822 allocate(vcts%cbarmin(ixi^s,1:ndim),vcts%cbarmax(ixi^s,1:ndim))
1823 end if
1824 ! Store magnitude of characteristics
1825 if(present(cmin)) then
1826 vcts%cbarmin(ixo^s,idim)=max(-cmin(ixo^s),zero)
1827 vcts%cbarmax(ixo^s,idim)=max( cmax(ixo^s),zero)
1828 else
1829 vcts%cbarmax(ixo^s,idim)=max( cmax(ixo^s),zero)
1830 vcts%cbarmin(ixo^s,idim)=vcts%cbarmax(ixo^s,idim)
1831 end if
1832
1833 idimn=mod(idim,ndir)+1 ! 'Next' direction
1834 idime=mod(idim+1,ndir)+1 ! Electric field direction
1835 ! Store velocities
1836 vcts%vbarLC(ixo^s,idim,1)=wlp(ixo^s,mom(idimn))
1837 vcts%vbarRC(ixo^s,idim,1)=wrp(ixo^s,mom(idimn))
1838 vcts%vbarC(ixo^s,idim,1)=(vcts%cbarmax(ixo^s,idim)*vcts%vbarLC(ixo^s,idim,1) &
1839 +vcts%cbarmin(ixo^s,idim)*vcts%vbarRC(ixo^s,idim,1))&
1840 /(vcts%cbarmax(ixo^s,idim)+vcts%cbarmin(ixo^s,idim))
1841 vcts%vbarLC(ixo^s,idim,2)=wlp(ixo^s,mom(idime))
1842 vcts%vbarRC(ixo^s,idim,2)=wrp(ixo^s,mom(idime))
1843 vcts%vbarC(ixo^s,idim,2)=(vcts%cbarmax(ixo^s,idim)*vcts%vbarLC(ixo^s,idim,2) &
1844 +vcts%cbarmin(ixo^s,idim)*vcts%vbarRC(ixo^s,idim,1))&
1845 /(vcts%cbarmax(ixo^s,idim)+vcts%cbarmin(ixo^s,idim))
1846 case default
1847 call mpistop('choose average, uct_contact,or uct_hll for type_ct!')
1848 end select
1849 end subroutine rmhd_get_ct_velocity
1850
1851 !> Calculate fast magnetosonic wave speed
1852 subroutine rmhd_get_csound_prim(w,x,ixI^L,ixO^L,idim,csound)
1854 integer, intent(in) :: ixi^l, ixo^l, idim
1855 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
1856 double precision, intent(out):: csound(ixo^s)
1857 double precision :: inv_rho, cfast2, avmincs2, b2, kmax
1858 double precision :: prad_tensor(ixo^s, 1:ndim, 1:ndim)
1859 double precision :: prad_max(ixo^s)
1860 integer :: ix^d
1861
1862 call rmhd_get_pradiation(w, x, ixi^l, ixo^l, prad_tensor, nghostcells-1)
1863 !> filter cmax
1864 if(radio_acoustic_filter) then
1865 call rmhd_radio_acoustic_filter(x, ixi^l, ixo^l, prad_max)
1866 endif
1867 ! store |B|^2 in v
1868 if(b0field) then
1869 {do ix^db=ixomin^db,ixomax^db \}
1870 inv_rho=1.d0/w(ix^d,rho_)
1871 prad_max(ix^d) = maxval(prad_tensor(ix^d,:,:))
1872 if(rmhd_energy) then
1873 csound(ix^d)=max(rmhd_gamma,4.d0/3.d0)*(w(ix^d,p_)+prad_max(ix^d))*inv_rho
1874 else
1875 csound(ix^d)=rmhd_gamma*rmhd_adiab*w(ix^d,rho_)**gamma_1
1876 end if
1877 b2=(^c&(w(ix^d,b^c_)+block%B0(ix^d,^c,b0i))**2+)
1878 cfast2=b2*inv_rho+csound(ix^d)
1879 avmincs2=cfast2**2-4.0d0*csound(ix^d)*(w(ix^d,mag(idim))+&
1880 block%B0(ix^d,idim,b0i))**2*inv_rho
1881 if(avmincs2<zero) avmincs2=zero
1882 csound(ix^d)=sqrt(half*(cfast2+sqrt(avmincs2)))
1883 {end do\}
1884 else
1885 {do ix^db=ixomin^db,ixomax^db \}
1886 inv_rho=1.d0/w(ix^d,rho_)
1887 prad_max(ix^d)=maxval(prad_tensor(ix^d,:,:))
1888 if(rmhd_energy) then
1889 csound(ix^d)=max(rmhd_gamma,4.d0/3.d0)*(w(ix^d,p_)+prad_max(ix^d))*inv_rho
1890 else
1891 csound(ix^d)=rmhd_gamma*rmhd_adiab*w(ix^d,rho_)**gamma_1
1892 end if
1893 b2=(^c&w(ix^d,b^c_)**2+)
1894 cfast2=b2*inv_rho+csound(ix^d)
1895 avmincs2=cfast2**2-4.0d0*csound(ix^d)*w(ix^d,mag(idim))**2*inv_rho
1896 if(avmincs2<zero) avmincs2=zero
1897 csound(ix^d)=sqrt(half*(cfast2+sqrt(avmincs2)))
1898 {end do\}
1899 end if
1900 end subroutine rmhd_get_csound_prim
1901
1902 !> Calculate fast magnetosonic wave speed
1903 subroutine rmhd_get_csound_prim_split(w,x,ixI^L,ixO^L,idim,csound)
1905 integer, intent(in) :: ixi^l, ixo^l, idim
1906 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
1907 double precision, intent(out):: csound(ixo^s)
1908 double precision :: rho, inv_rho, cfast2, avmincs2, b2, kmax
1909 double precision :: prad_tensor(ixo^s, 1:ndim, 1:ndim)
1910 double precision :: prad_max(ixo^s)
1911 integer :: ix^d
1912
1913 call rmhd_get_pradiation(w, x, ixi^l, ixo^l, prad_tensor, nghostcells-1)
1914 !> filter cmax
1915 if (radio_acoustic_filter) then
1916 call rmhd_radio_acoustic_filter(x, ixi^l, ixo^l, prad_max)
1917 endif
1918
1919 ! store |B|^2 in v
1920 if(b0field) then
1921 {do ix^db=ixomin^db,ixomax^db \}
1922 rho=(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
1923 inv_rho=1.d0/rho
1924 prad_max(ix^d) = maxval(prad_tensor(ix^d,:,:))
1925 if(has_equi_pe0) then
1926 csound(ix^d)=max(rmhd_gamma,4.d0/3.d0)*(w(ix^d,p_)+block%equi_vars(ix^d,equi_pe0_,b0i)+prad_max(ix^d))*inv_rho
1927 end if
1928 b2=(^c&(w(ix^d,b^c_)+block%B0(ix^d,^c,b0i))**2+)
1929 cfast2=b2*inv_rho+csound(ix^d)
1930 avmincs2=cfast2**2-4.0d0*csound(ix^d)*(w(ix^d,mag(idim))+&
1931 block%B0(ix^d,idim,b0i))**2*inv_rho
1932 if(avmincs2<zero) avmincs2=zero
1933 csound(ix^d)=sqrt(half*(cfast2+sqrt(avmincs2)))
1934 {end do\}
1935 else
1936 {do ix^db=ixomin^db,ixomax^db \}
1937 rho=(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
1938 inv_rho=1.d0/rho
1939 prad_max(ix^d) = maxval(prad_tensor(ix^d,:,:))
1940 if(has_equi_pe0) then
1941 csound(ix^d)=max(rmhd_gamma,4.d0/3.d0)*(w(ix^d,p_)+block%equi_vars(ix^d,equi_pe0_,b0i)+prad_max(ix^d))*inv_rho
1942 end if
1943 b2=(^c&w(ix^d,b^c_)**2+)
1944 cfast2=b2*inv_rho+csound(ix^d)
1945 avmincs2=cfast2**2-4.0d0*csound(ix^d)*w(ix^d,mag(idim))**2*inv_rho
1946 if(avmincs2<zero) avmincs2=zero
1947 csound(ix^d)=sqrt(half*(cfast2+sqrt(avmincs2)))
1948 {end do\}
1949 end if
1950 end subroutine rmhd_get_csound_prim_split
1951
1952 !> Calculate thermal pressure=(gamma-1)*(e-0.5*m**2/rho-b**2/2) within ixO^L
1953 subroutine rmhd_get_pthermal_origin(w,x,ixI^L,ixO^L,pth)
1956
1957 integer, intent(in) :: ixi^l, ixo^l
1958 double precision, intent(in) :: w(ixi^s,nw)
1959 double precision, intent(in) :: x(ixi^s,1:ndim)
1960 double precision, intent(out):: pth(ixi^s)
1961
1962 integer :: iw, ix^d
1963
1964 {do ix^db=ixomin^db,ixomax^db\}
1965 if(has_equi_rho0) then
1966 pth(ix^d)=gamma_1*(w(ix^d,e_)-half*((^c&w(ix^d,m^c_)**2+)/(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,0))&
1967 +(^c&w(ix^d,b^c_)**2+)))+block%equi_vars(ix^d,equi_pe0_,0)
1968 else
1969 pth(ix^d)=gamma_1*(w(ix^d,e_)-half*((^c&w(ix^d,m^c_)**2+)/w(ix^d,rho_)&
1970 +(^c&w(ix^d,b^c_)**2+)))
1971 end if
1972 if(fix_small_values.and.pth(ix^d)<small_pressure) pth(ix^d)=small_pressure
1973 {end do\}
1974
1975 if(check_small_values.and..not.fix_small_values) then
1976 {do ix^db=ixomin^db,ixomax^db\}
1977 if(pth(ix^d)<small_pressure) then
1978 write(*,*) "Error: small value of gas pressure",pth(ix^d),&
1979 " encountered when call rmhd_get_pthermal"
1980 write(*,*) "Iteration: ", it, " Time: ", global_time
1981 write(*,*) "Location: ", x(ix^d,:)
1982 write(*,*) "Cell number: ", ix^d
1983 do iw=1,nw
1984 write(*,*) trim(cons_wnames(iw)),": ",w(ix^d,iw)
1985 end do
1986 ! use erroneous arithmetic operation to crash the run
1987 if(trace_small_values) write(*,*) sqrt(pth(ix^d)-bigdouble)
1988 write(*,*) "Saving status at the previous time step"
1989 crash=.true.
1990 end if
1991 {end do\}
1992 end if
1993 end subroutine rmhd_get_pthermal_origin
1994
1995 !> copy temperature from stored Te variable
1996 subroutine rmhd_get_temperature_from_te(w, x, ixI^L, ixO^L, res)
1998 integer, intent(in) :: ixi^l, ixo^l
1999 double precision, intent(in) :: w(ixi^s, 1:nw)
2000 double precision, intent(in) :: x(ixi^s, 1:ndim)
2001 double precision, intent(out):: res(ixi^s)
2002 res(ixo^s) = w(ixo^s, te_)
2003 end subroutine rmhd_get_temperature_from_te
2004
2005 !> Calculate temperature=p/rho when in e_ the internal energy is stored
2006 subroutine rmhd_get_temperature_from_eint(w, x, ixI^L, ixO^L, res)
2008 integer, intent(in) :: ixi^l, ixo^l
2009 double precision, intent(in) :: w(ixi^s, 1:nw)
2010 double precision, intent(in) :: x(ixi^s, 1:ndim)
2011 double precision, intent(out):: res(ixi^s)
2012 double precision :: r(ixi^s)
2013
2014 call rmhd_get_rfactor(w,x,ixi^l,ixo^l,r)
2015 res(ixo^s) = gamma_1 * w(ixo^s, e_)/(w(ixo^s,rho_)*r(ixo^s))
2016 end subroutine rmhd_get_temperature_from_eint
2017
2018 !> Calculate temperature=p/rho when in e_ the total energy is stored
2019 subroutine rmhd_get_temperature_from_etot(w, x, ixI^L, ixO^L, res)
2021 integer, intent(in) :: ixi^l, ixo^l
2022 double precision, intent(in) :: w(ixi^s, 1:nw)
2023 double precision, intent(in) :: x(ixi^s, 1:ndim)
2024 double precision, intent(out):: res(ixi^s)
2025 double precision :: r(ixi^s)
2026
2027 call rmhd_get_rfactor(w,x,ixi^l,ixo^l,r)
2028 call rmhd_get_pthermal(w,x,ixi^l,ixo^l,res)
2029 res(ixo^s)=res(ixo^s)/(r(ixo^s)*w(ixo^s,rho_))
2030 end subroutine rmhd_get_temperature_from_etot
2031
2032 subroutine rmhd_get_temperature_from_etot_with_equi(w, x, ixI^L, ixO^L, res)
2034 integer, intent(in) :: ixi^l, ixo^l
2035 double precision, intent(in) :: w(ixi^s, 1:nw)
2036 double precision, intent(in) :: x(ixi^s, 1:ndim)
2037 double precision, intent(out):: res(ixi^s)
2038 double precision :: r(ixi^s)
2039
2040 call rmhd_get_rfactor(w,x,ixi^l,ixo^l,r)
2041 call rmhd_get_pthermal(w,x,ixi^l,ixo^l,res)
2042 res(ixo^s)=res(ixo^s)/(r(ixo^s)*(w(ixo^s,rho_)+block%equi_vars(ixo^s,equi_rho0_,b0i)))
2043 end subroutine rmhd_get_temperature_from_etot_with_equi
2044
2045 subroutine rmhd_get_temperature_from_eint_with_equi(w, x, ixI^L, ixO^L, res)
2047 integer, intent(in) :: ixi^l, ixo^l
2048 double precision, intent(in) :: w(ixi^s, 1:nw)
2049 double precision, intent(in) :: x(ixi^s, 1:ndim)
2050 double precision, intent(out):: res(ixi^s)
2051 double precision :: r(ixi^s)
2052
2053 call rmhd_get_rfactor(w,x,ixi^l,ixo^l,r)
2054 res(ixo^s) = (gamma_1 * w(ixo^s, e_) + block%equi_vars(ixo^s,equi_pe0_,b0i)) /&
2055 ((w(ixo^s,rho_) +block%equi_vars(ixo^s,equi_rho0_,b0i))*r(ixo^s))
2056 end subroutine rmhd_get_temperature_from_eint_with_equi
2057
2058 subroutine rmhd_get_temperature_equi(w,x, ixI^L, ixO^L, res)
2060 integer, intent(in) :: ixi^l, ixo^l
2061 double precision, intent(in) :: w(ixi^s, 1:nw)
2062 double precision, intent(in) :: x(ixi^s, 1:ndim)
2063 double precision, intent(out):: res(ixi^s)
2064 double precision :: r(ixi^s)
2065
2066 call rmhd_get_rfactor(w,x,ixi^l,ixo^l,r)
2067 res(ixo^s)= block%equi_vars(ixo^s,equi_pe0_,b0i)/(block%equi_vars(ixo^s,equi_rho0_,b0i)*r(ixo^s))
2068 end subroutine rmhd_get_temperature_equi
2069
2070 subroutine rmhd_get_rho_equi(w, x, ixI^L, ixO^L, res)
2072 integer, intent(in) :: ixi^l, ixo^l
2073 double precision, intent(in) :: w(ixi^s, 1:nw)
2074 double precision, intent(in) :: x(ixi^s, 1:ndim)
2075 double precision, intent(out):: res(ixi^s)
2076 res(ixo^s) = block%equi_vars(ixo^s,equi_rho0_,b0i)
2077 end subroutine rmhd_get_rho_equi
2078
2079 subroutine rmhd_get_pe_equi(w,x, ixI^L, ixO^L, res)
2081 integer, intent(in) :: ixi^l, ixo^l
2082 double precision, intent(in) :: w(ixi^s, 1:nw)
2083 double precision, intent(in) :: x(ixi^s, 1:ndim)
2084 double precision, intent(out):: res(ixi^s)
2085 res(ixo^s) = block%equi_vars(ixo^s,equi_pe0_,b0i)
2086 end subroutine rmhd_get_pe_equi
2087
2088 !> Calculate total pressure within ixO^L including magnetic pressure
2089 subroutine rmhd_get_p_total(w,x,ixI^L,ixO^L,p)
2091 integer, intent(in) :: ixi^l, ixo^l
2092 double precision, intent(in) :: w(ixi^s,nw)
2093 double precision, intent(in) :: x(ixi^s,1:ndim)
2094 double precision, intent(out) :: p(ixi^s)
2095
2096 call rmhd_get_pthermal(w,x,ixi^l,ixo^l,p)
2097 p(ixo^s) = p(ixo^s) + 0.5d0 * sum(w(ixo^s, mag(:))**2, dim=ndim+1)
2098 end subroutine rmhd_get_p_total
2099
2100 !> Calculate radiation pressure within ixO^L
2101 subroutine rmhd_get_pradiation(w, x, ixI^L, ixO^L, prad, nth)
2103 use mod_fld
2104 use mod_afld
2105 integer, intent(in) :: ixi^l, ixo^l, nth
2106 double precision, intent(in) :: w(ixi^s, 1:nw)
2107 double precision, intent(in) :: x(ixi^s, 1:ndim)
2108 double precision, intent(out):: prad(ixo^s, 1:ndim, 1:ndim)
2109
2110 select case (rmhd_radiation_formalism)
2111 case('fld')
2112 call fld_get_radpress(w, x, ixi^l, ixo^l, prad, nth)
2113 case('afld')
2114 call afld_get_radpress(w, x, ixi^l, ixo^l, prad, nth)
2115 case default
2116 call mpistop('Radiation formalism unknown')
2117 end select
2118 end subroutine rmhd_get_pradiation
2119
2120 !> Calculates the sum of the gas pressure and the max Prad tensor element
2121 subroutine rmhd_get_pthermal_plus_pradiation(w, x, ixI^L, ixO^L, pth_plus_prad)
2123 integer, intent(in) :: ixi^l, ixo^l
2124 double precision, intent(in) :: w(ixi^s, 1:nw)
2125 double precision, intent(in) :: x(ixi^s, 1:ndim)
2126 double precision :: pth(ixi^s)
2127 double precision :: prad_tensor(ixo^s, 1:ndim, 1:ndim)
2128 double precision :: prad_max(ixo^s)
2129 double precision, intent(out) :: pth_plus_prad(ixi^s)
2130 integer :: ix^d
2131
2132 call rmhd_get_pthermal(w, x, ixi^l, ixo^l, pth)
2133 call rmhd_get_pradiation(w, x, ixi^l, ixo^l, prad_tensor, nghostcells)
2134 {do ix^d = ixomin^d,ixomax^d\}
2135 prad_max(ix^d) = maxval(prad_tensor(ix^d,:,:))
2136 {enddo\}
2137 !> filter cmax
2138 if (radio_acoustic_filter) then
2139 call rmhd_radio_acoustic_filter(x, ixi^l, ixo^l, prad_max)
2140 endif
2141 pth_plus_prad(ixo^s) = pth(ixo^s) + prad_max(ixo^s)
2143
2144 !> Filter peaks in cmax due to radiation energy density, used for debugging
2145 subroutine rmhd_radio_acoustic_filter(x, ixI^L, ixO^L, prad_max)
2147 integer, intent(in) :: ixi^l, ixo^l
2148 double precision, intent(in) :: x(ixi^s, 1:ndim)
2149 double precision, intent(inout) :: prad_max(ixo^s)
2150 double precision :: tmp_prad(ixi^s)
2151 integer :: ix^d, filter, idim
2152
2153 if (size_ra_filter .lt. 1) call mpistop("ra filter of size < 1 makes no sense")
2154 if (size_ra_filter .gt. nghostcells) call mpistop("ra filter of size < nghostcells makes no sense")
2155
2156 tmp_prad(ixi^s) = zero
2157 tmp_prad(ixo^s) = prad_max(ixo^s)
2158 do filter = 1,size_ra_filter
2159 do idim = 1,ndim
2160 ! {do ix^D = ixOmin^D+filter,ixOmax^D-filter\}
2161 {do ix^d = ixomin^d,ixomax^d\}
2162 prad_max(ix^d) = min(tmp_prad(ix^d),tmp_prad(ix^d+filter*kr(idim,^d)))
2163 prad_max(ix^d) = min(tmp_prad(ix^d),tmp_prad(ix^d-filter*kr(idim,^d)))
2164 {enddo\}
2165 enddo
2166 enddo
2167 end subroutine rmhd_radio_acoustic_filter
2168
2169 !> Calculates gas temperature
2170 subroutine rmhd_get_tgas(w, x, ixI^L, ixO^L, tgas)
2172 integer, intent(in) :: ixi^l, ixo^l
2173 double precision, intent(in) :: w(ixi^s, 1:nw)
2174 double precision, intent(in) :: x(ixi^s, 1:ndim)
2175 double precision :: pth(ixi^s)
2176 double precision, intent(out):: tgas(ixi^s)
2177
2178 call rmhd_get_pthermal(w, x, ixi^l, ixo^l, pth)
2179 tgas(ixi^s) = pth(ixi^s)/w(ixi^s,rho_)
2180 end subroutine rmhd_get_tgas
2181
2182 !> Calculates radiation temperature
2183 subroutine rmhd_get_trad(w, x, ixI^L, ixO^L, trad)
2185 use mod_constants
2186
2187 integer, intent(in) :: ixi^l, ixo^l
2188 double precision, intent(in) :: w(ixi^s, 1:nw)
2189 double precision, intent(in) :: x(ixi^s, 1:ndim)
2190 double precision, intent(out):: trad(ixi^s)
2191
2192 trad(ixi^s) = (w(ixi^s,r_e)*unit_pressure&
2193 /const_rad_a)**(1.d0/4.d0)/unit_temperature
2194 end subroutine rmhd_get_trad
2195
2196 !> Calculate fluxes within ixO^L without any splitting
2197 subroutine rmhd_get_flux(wC,w,x,ixI^L,ixO^L,idim,f)
2199 use mod_geometry
2200
2201 integer, intent(in) :: ixi^l, ixo^l, idim
2202 ! conservative w
2203 double precision, intent(in) :: wc(ixi^s,nw)
2204 ! primitive w
2205 double precision, intent(in) :: w(ixi^s,nw)
2206 double precision, intent(in) :: x(ixi^s,1:ndim)
2207 double precision,intent(out) :: f(ixi^s,nwflux)
2208 double precision :: vhall(ixi^s,1:ndir)
2209 double precision :: ptotal
2210 integer :: iw, ix^d
2211
2212 {do ix^db=ixomin^db,ixomax^db\}
2213 ! Get flux of density
2214 f(ix^d,rho_)=w(ix^d,mom(idim))*w(ix^d,rho_)
2215 ! f_i[m_k]=v_i*m_k-b_k*b_i
2216 ^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_)\
2217 ptotal=w(ix^d,p_)+half*(^c&w(ix^d,b^c_)**2+)
2218 ! normal one includes total pressure
2219 f(ix^d,mom(idim))=f(ix^d,mom(idim))+ptotal
2220 ! Get flux of total energy
2221 ! f_i[e]=v_i*e+v_i*ptotal-b_i*(b_k*v_k)
2222 f(ix^d,e_)=w(ix^d,mom(idim))*(wc(ix^d,e_)+ptotal)&
2223 -w(ix^d,mag(idim))*(^c&w(ix^d,b^c_)*w(ix^d,m^c_)+)
2224 ! f_i[b_k]=v_i*b_k-v_k*b_i
2225 ^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_)\
2226 {end do\}
2227 if(rmhd_glm) then
2228 {do ix^db=ixomin^db,ixomax^db\}
2229 f(ix^d,mag(idim))=w(ix^d,psi_)
2230 !f_i[psi]=Ch^2*b_{i} Eq. 24e and Eq. 38c Dedner et al 2002 JCP, 175, 645
2231 f(ix^d,psi_)=cmax_global**2*w(ix^d,mag(idim))
2232 {end do\}
2233 end if
2235 {do ix^db=ixomin^db,ixomax^db\}
2236 f(ix^d,r_e)=w(ix^d,mom(idim))*wc(ix^d,r_e)
2237 {end do\}
2238 else
2239 f(ixo^s,r_e)=zero
2240 endif
2241 ! Get flux of tracer
2242 do iw=1,rmhd_n_tracer
2243 {do ix^db=ixomin^db,ixomax^db\}
2244 f(ix^d,tracer(iw))=w(ix^d,mom(idim))*w(ix^d,tracer(iw))
2245 {end do\}
2246 end do
2248 {do ix^db=ixomin^db,ixomax^db\}
2249 f(ix^d,e_)=f(ix^d,e_)+w(ix^d,q_)*w(ix^d,mag(idim))/(dsqrt(^d&w({ix^d},b^d_)**2+)+smalldouble)
2250 f(ix^d,q_)=zero
2251 {end do\}
2252 end if
2253 end subroutine rmhd_get_flux
2254
2255 !> Calculate fluxes within ixO^L with possible splitting
2256 subroutine rmhd_get_flux_split(wC,w,x,ixI^L,ixO^L,idim,f)
2258 use mod_geometry
2259 integer, intent(in) :: ixi^l, ixo^l, idim
2260 ! conservative w
2261 double precision, intent(in) :: wc(ixi^s,nw)
2262 ! primitive w
2263 double precision, intent(in) :: w(ixi^s,nw)
2264 double precision, intent(in) :: x(ixi^s,1:ndim)
2265 double precision,intent(out) :: f(ixi^s,nwflux)
2266 double precision :: vhall(ixi^s,1:ndir)
2267 double precision :: ptotal, btotal(ixo^s,1:ndir)
2268 integer :: iw, ix^d
2269
2270 {do ix^db=ixomin^db,ixomax^db\}
2271 ! Get flux of density
2272 if(has_equi_rho0) then
2273 f(ix^d,rho_)=w(ix^d,mom(idim))*(w(ix^d,rho_)+block%equi_vars(ix^d,equi_rho0_,b0i))
2274 else
2275 f(ix^d,rho_)=w(ix^d,mom(idim))*w(ix^d,rho_)
2276 endif
2277 if(rmhd_energy) then
2278 ptotal=w(ix^d,p_)+half*(^c&w(ix^d,b^c_)**2+)
2279 else
2280 ptotal=rmhd_adiab*w(ix^d,rho_)**rmhd_gamma+half*(^c&w(ix^d,b^c_)**2+)
2281 if(has_equi_pe0) then
2282 ptotal=ptotal-block%equi_vars(ix^d,equi_pe0_,b0i)
2283 end if
2284 end if
2285 if(b0field) then
2286 ^c&btotal(ix^d,^c)=w(ix^d,b^c_)+block%B0(ix^d,^c,idim)\
2287 ptotal=ptotal+(^c&w(ix^d,b^c_)*block%B0(ix^d,^c,idim)+)
2288 ! Get flux of momentum and magnetic field
2289 ! f_i[m_k]=v_i*m_k-b_k*b_i
2290 ^c&f(ix^d,m^c_)=wc(ix^d,mom(idim))*w(ix^d,m^c_)-&
2291 btotal(ix^d,idim)*w(ix^d,b^c_)-w(ix^d,mag(idim))*block%B0(ix^d,^c,idim)\
2292 f(ix^d,mom(idim))=f(ix^d,mom(idim))+ptotal
2293 else
2294 ^c&btotal(ix^d,^c)=w(ix^d,b^c_)\
2295 ! Get flux of momentum and magnetic field
2296 ! f_i[m_k]=v_i*m_k-b_k*b_i
2297 ^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_)\
2298 f(ix^d,mom(idim))=f(ix^d,mom(idim))+ptotal
2299 end if
2300 ! f_i[b_k]=v_i*b_k-v_k*b_i
2301 ^c&f(ix^d,b^c_)=w(ix^d,mom(idim))*btotal(ix^d,^c)-btotal(ix^d,idim)*w(ix^d,m^c_)\
2302 ! Get flux of energy
2303 ! f_i[e]=v_i*e+v_i*ptotal-b_i*(b_k*v_k)
2304 if(rmhd_energy) then
2305 f(ix^d,e_)=w(ix^d,mom(idim))*(wc(ix^d,e_)+ptotal)&
2306 -btotal(ix^d,idim)*(^c&w(ix^d,b^c_)*w(ix^d,m^c_)+)
2307 end if
2308 {end do\}
2309 if(rmhd_glm) then
2310 {do ix^db=ixomin^db,ixomax^db\}
2311 f(ix^d,mag(idim))=w(ix^d,psi_)
2312 !f_i[psi]=Ch^2*b_{i} Eq. 24e and Eq. 38c Dedner et al 2002 JCP, 175, 645
2313 f(ix^d,psi_) = cmax_global**2*w(ix^d,mag(idim))
2314 {end do\}
2315 end if
2317 {do ix^db=ixomin^db,ixomax^db\}
2318 f(ix^d,r_e)=w(ix^d,mom(idim))*wc(ix^d,r_e)
2319 {end do\}
2320 else
2321 f(ixo^s,r_e)=zero
2322 endif
2323 ! Get flux of tracer
2324 do iw=1,rmhd_n_tracer
2325 {do ix^db=ixomin^db,ixomax^db\}
2326 f(ix^d,tracer(iw))=w(ix^d,mom(idim))*w(ix^d,tracer(iw))
2327 {end do\}
2328 end do
2330 {do ix^db=ixomin^db,ixomax^db\}
2331 f(ix^d,e_)=f(ix^d,e_)+w(ix^d,q_)*btotal(ix^d,idim)/(dsqrt(^c&btotal(ix^d,^c)**2+)+smalldouble)
2332 f(ix^d,q_)=zero
2333 {end do\}
2334 end if
2335 end subroutine rmhd_get_flux_split
2336
2337 !> use cell-center flux to get cell-face flux
2338 !> and get the source term as the divergence of the flux
2339 subroutine get_flux_on_cell_face(ixI^L,ixO^L,ff,src)
2341
2342 integer, intent(in) :: ixi^l, ixo^l
2343 double precision, dimension(:^D&,:), intent(inout) :: ff
2344 double precision, intent(out) :: src(ixi^s)
2345
2346 double precision :: ffc(ixi^s,1:ndim)
2347 double precision :: dxinv(ndim)
2348 integer :: idims, ix^d, ixa^l, ixb^l, ixc^l
2349
2350 ixa^l=ixo^l^ladd1;
2351 dxinv=1.d0/dxlevel
2352 ! cell corner flux in ffc
2353 ffc=0.d0
2354 ixcmax^d=ixomax^d; ixcmin^d=ixomin^d-1;
2355 {do ix^db=0,1\}
2356 ixbmin^d=ixcmin^d+ix^d;
2357 ixbmax^d=ixcmax^d+ix^d;
2358 ffc(ixc^s,1:ndim)=ffc(ixc^s,1:ndim)+ff(ixb^s,1:ndim)
2359 {end do\}
2360 ffc(ixc^s,1:ndim)=0.5d0**ndim*ffc(ixc^s,1:ndim)
2361 ! flux at cell face
2362 ff(ixi^s,1:ndim)=0.d0
2363 do idims=1,ndim
2364 ixb^l=ixo^l-kr(idims,^d);
2365 ixcmax^d=ixomax^d; ixcmin^d=ixbmin^d;
2366 {do ix^db=0,1 \}
2367 if({ ix^d==0 .and. ^d==idims | .or.}) then
2368 ixbmin^d=ixcmin^d-ix^d;
2369 ixbmax^d=ixcmax^d-ix^d;
2370 ff(ixc^s,idims)=ff(ixc^s,idims)+ffc(ixb^s,idims)
2371 end if
2372 {end do\}
2373 ff(ixc^s,idims)=ff(ixc^s,idims)*0.5d0**(ndim-1)
2374 end do
2375 src=0.d0
2376 if(slab_uniform) then
2377 do idims=1,ndim
2378 ff(ixa^s,idims)=dxinv(idims)*ff(ixa^s,idims)
2379 ixb^l=ixo^l-kr(idims,^d);
2380 src(ixo^s)=src(ixo^s)+ff(ixo^s,idims)-ff(ixb^s,idims)
2381 end do
2382 else
2383 do idims=1,ndim
2384 ff(ixa^s,idims)=ff(ixa^s,idims)*block%surfaceC(ixa^s,idims)
2385 ixb^l=ixo^l-kr(idims,^d);
2386 src(ixo^s)=src(ixo^s)+ff(ixo^s,idims)-ff(ixb^s,idims)
2387 end do
2388 src(ixo^s)=src(ixo^s)/block%dvolume(ixo^s)
2389 end if
2390 end subroutine get_flux_on_cell_face
2391
2392 !> w[iws]=w[iws]+qdt*S[iws,wCT] where S is the source based on wCT within ixO
2393 subroutine rmhd_add_source(qdt,dtfactor,ixI^L,ixO^L,wCT,wCTprim,w,x,qsourcesplit,active)
2397 use mod_cak_force, only: cak_add_source
2398
2399 integer, intent(in) :: ixi^l, ixo^l
2400 double precision, intent(in) :: qdt,dtfactor
2401 double precision, intent(in) :: wct(ixi^s,1:nw),wctprim(ixi^s,1:nw), x(ixi^s,1:ndim)
2402 double precision, intent(inout) :: w(ixi^s,1:nw)
2403 logical, intent(in) :: qsourcesplit
2404 logical, intent(inout) :: active
2405
2406 ! TODO local_timestep support is only added for splitting
2407 ! but not for other nonideal terms such gravity, RC, viscosity,..
2408 ! it will also only work for divbfix 'linde', which does not require
2409 ! modification as it does not use dt in the update
2410 if (.not. qsourcesplit) then
2411 if(has_equi_pe0) then
2412 active = .true.
2413 call add_pe0_divv(qdt,dtfactor,ixi^l,ixo^l,wctprim,w,x)
2414 end if
2416 call add_hypertc_source(qdt,ixi^l,ixo^l,wct,w,x,wctprim)
2417 end if
2418 ! Source for B0 splitting
2419 if (b0field) then
2420 active = .true.
2421 call add_source_b0split(qdt,dtfactor,ixi^l,ixo^l,wctprim,w,x)
2422 end if
2423 ! Sources for resistivity in eqs. for e, B1, B2 and B3
2424 if (abs(rmhd_eta)>smalldouble)then
2425 active = .true.
2426 call add_source_res2(qdt,ixi^l,ixo^l,wct,w,x)
2427 end if
2428 if (rmhd_eta_hyper>0.d0)then
2429 active = .true.
2430 call add_source_hyperres(qdt,ixi^l,ixo^l,wct,w,x)
2431 end if
2432 end if
2433 {^nooned
2434 if(source_split_divb .eqv. qsourcesplit) then
2435 ! Sources related to div B
2436 select case (type_divb)
2437 case (divb_ct)
2438 continue ! Do nothing
2439 case (divb_linde)
2440 active = .true.
2441 call add_source_linde(qdt,ixi^l,ixo^l,wct,w,x)
2442 case (divb_glm)
2443 active = .true.
2444 call add_source_glm(qdt,ixi^l,ixo^l,wct,w,x)
2445 case (divb_powel)
2446 active = .true.
2447 call add_source_powel(qdt,ixi^l,ixo^l,wctprim,w,x)
2448 case (divb_janhunen)
2449 active = .true.
2450 call add_source_janhunen(qdt,ixi^l,ixo^l,wctprim,w,x)
2451 case (divb_lindejanhunen)
2452 active = .true.
2453 call add_source_linde(qdt,ixi^l,ixo^l,wct,w,x)
2454 call add_source_janhunen(qdt,ixi^l,ixo^l,wctprim,w,x)
2455 case (divb_lindepowel)
2456 active = .true.
2457 call add_source_linde(qdt,ixi^l,ixo^l,wct,w,x)
2458 call add_source_powel(qdt,ixi^l,ixo^l,wctprim,w,x)
2459 case (divb_lindeglm)
2460 active = .true.
2461 call add_source_linde(qdt,ixi^l,ixo^l,wct,w,x)
2462 call add_source_glm(qdt,ixi^l,ixo^l,wct,w,x)
2463 case (divb_multigrid)
2464 continue ! Do nothing
2465 case (divb_none)
2466 ! Do nothing
2467 case default
2468 call mpistop('Unknown divB fix')
2469 end select
2470 end if
2471 }
2472 if(rmhd_viscosity) then
2473 call viscosity_add_source(qdt,ixi^l,ixo^l,wct,&
2474 w,x,rmhd_energy,qsourcesplit,active)
2475 end if
2476 if(rmhd_gravity) then
2477 call gravity_add_source(qdt,ixi^l,ixo^l,wct,wctprim,&
2478 w,x,gravity_energy,gravity_rhov,qsourcesplit,active)
2479 end if
2480 if (rmhd_cak_force) then
2481 call cak_add_source(qdt,ixi^l,ixo^l,wct,w,x,rmhd_energy,qsourcesplit,active)
2482 end if
2483 !> This is where the radiation force and heating/cooling are added
2484 call rmhd_add_radiation_source(qdt,ixi^l,ixo^l,wct,w,x,qsourcesplit,active)
2485 ! update temperature from new pressure, density, and old ionization degree
2487 if(.not.qsourcesplit) then
2488 active = .true.
2489 call rmhd_update_temperature(ixi^l,ixo^l,wct,w,x)
2490 end if
2491 end if
2492 end subroutine rmhd_add_source
2493
2494 subroutine rmhd_add_radiation_source(qdt,ixI^L,ixO^L,wCT,w,x,qsourcesplit,active)
2495 use mod_constants
2497 use mod_usr_methods
2498 use mod_fld
2499 use mod_afld
2500 integer, intent(in) :: ixi^l, ixo^l
2501 double precision, intent(in) :: qdt, x(ixi^s,1:ndim)
2502 double precision, intent(in) :: wct(ixi^s,1:nw)
2503 double precision, intent(inout) :: w(ixi^s,1:nw)
2504 logical, intent(in) :: qsourcesplit
2505 logical, intent(inout) :: active
2506 double precision :: cmax(ixi^s)
2507
2508 select case(rmhd_radiation_formalism)
2509 case('fld')
2510 if(fld_diff_scheme .eq. 'mg') call fld_get_diffcoef_central(w, wct, x, ixi^l, ixo^l)
2511 !> radiation force
2512 if(rmhd_radiation_force) call get_fld_rad_force(qdt,ixi^l,ixo^l,wct,w,x,rmhd_energy,qsourcesplit,active)
2513 call rmhd_handle_small_values(.true., w, x, ixi^l, ixo^l, 'fld_e_interact')
2514 case('afld')
2515 if(fld_diff_scheme .eq. 'mg') call afld_get_diffcoef_central(w, wct, x, ixi^l, ixo^l)
2516 !> radiation force
2517 if(rmhd_radiation_force) call get_afld_rad_force(qdt,ixi^l,ixo^l,wct,w,x,rmhd_energy,qsourcesplit,active)
2518 call rmhd_handle_small_values(.true., w, x, ixi^l, ixo^l, 'fld_e_interact')
2519 !> photon tiring, heating and cooling
2520 if(rmhd_energy) then
2521 if (rmhd_energy_interact) call get_afld_energy_interact(qdt,ixi^l,ixo^l,wct,w,x,rmhd_energy,qsourcesplit,active)
2522 endif
2523 case default
2524 call mpistop('Radiation formalism unknown')
2525 end select
2526 end subroutine rmhd_add_radiation_source
2527
2528 subroutine add_pe0_divv(qdt,dtfactor,ixI^L,ixO^L,wCT,w,x)
2530 use mod_geometry
2531 integer, intent(in) :: ixi^l, ixo^l
2532 double precision, intent(in) :: qdt,dtfactor
2533 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
2534 double precision, intent(inout) :: w(ixi^s,1:nw)
2535 double precision :: divv(ixi^s)
2536
2537 if(slab_uniform) then
2538 if(nghostcells .gt. 2) then
2539 call divvector(wct(ixi^s,mom(1:ndir)),ixi^l,ixo^l,divv,3)
2540 else
2541 call divvector(wct(ixi^s,mom(1:ndir)),ixi^l,ixo^l,divv,2)
2542 end if
2543 else
2544 call divvector(wct(ixi^s,mom(1:ndir)),ixi^l,ixo^l,divv)
2545 end if
2546 if(local_timestep) then
2547 w(ixo^s,e_)=w(ixo^s,e_)-dtfactor*block%dt(ixo^s)*block%equi_vars(ixo^s,equi_pe0_,0)*divv(ixo^s)
2548 else
2549 w(ixo^s,e_)=w(ixo^s,e_)-qdt*block%equi_vars(ixo^s,equi_pe0_,0)*divv(ixo^s)
2550 end if
2551 end subroutine add_pe0_divv
2552
2553 subroutine get_tau(ixI^L,ixO^L,w,Te,tau,sigT5)
2555 integer, intent(in) :: ixi^l, ixo^l
2556 double precision, dimension(ixI^S,1:nw), intent(in) :: w
2557 double precision, dimension(ixI^S), intent(in) :: te
2558 double precision, dimension(ixI^S), intent(out) :: tau,sigt5
2559 double precision :: dxmin,taumin
2560 double precision, dimension(ixI^S) :: sigt7,eint
2561 integer :: ix^d
2562
2563 taumin=4.d0
2564 !> w supposed to be wCTprim here
2565 if(rmhd_trac) then
2566 where(te(ixo^s) .lt. block%wextra(ixo^s,tcoff_))
2567 sigt5(ixo^s)=hypertc_kappa*sqrt(block%wextra(ixo^s,tcoff_)**5)
2568 sigt7(ixo^s)=sigt5(ixo^s)*block%wextra(ixo^s,tcoff_)
2569 else where
2570 sigt5(ixo^s)=hypertc_kappa*sqrt(te(ixo^s)**5)
2571 sigt7(ixo^s)=sigt5(ixo^s)*te(ixo^s)
2572 end where
2573 else
2574 sigt5(ixo^s)=hypertc_kappa*sqrt(te(ixo^s)**5)
2575 sigt7(ixo^s)=sigt5(ixo^s)*te(ixo^s)
2576 end if
2577 eint(ixo^s)=w(ixo^s,p_)/(rmhd_gamma-one)
2578 tau(ixo^s)=max(taumin*dt,sigt7(ixo^s)/eint(ixo^s)/cmax_global**2)
2579 end subroutine get_tau
2580
2581 subroutine add_hypertc_source(qdt,ixI^L,ixO^L,wCT,w,x,wCTprim)
2583 integer, intent(in) :: ixi^l,ixo^l
2584 double precision, intent(in) :: qdt
2585 double precision, dimension(ixI^S,1:ndim), intent(in) :: x
2586 double precision, dimension(ixI^S,1:nw), intent(in) :: wct,wctprim
2587 double precision, dimension(ixI^S,1:nw), intent(inout) :: w
2588 double precision :: invdx
2589 double precision, dimension(ixI^S) :: te,tau,sigt,htc_qsrc,tface,r
2590 double precision, dimension(ixI^S) :: htc_esrc,bsum,bunit
2591 double precision, dimension(ixI^S,1:ndim) :: btot
2592 integer :: idims
2593 integer :: hxc^l,hxo^l,ixc^l,jxc^l,jxo^l,kxc^l
2594
2595 call rmhd_get_rfactor(wctprim,x,ixi^l,ixi^l,r)
2596 !Te(ixI^S)=wCTprim(ixI^S,p_)/wCT(ixI^S,rho_)
2597 te(ixi^s)=wctprim(ixi^s,p_)/(r(ixi^s)*w(ixi^s,rho_))
2598 call get_tau(ixi^l,ixo^l,wctprim,te,tau,sigt)
2599 htc_qsrc=zero
2600 do idims=1,ndim
2601 if(b0field) then
2602 btot(ixo^s,idims)=wct(ixo^s,mag(idims))+block%B0(ixo^s,idims,0)
2603 else
2604 btot(ixo^s,idims)=wct(ixo^s,mag(idims))
2605 endif
2606 enddo
2607 bsum(ixo^s)=sqrt(sum(btot(ixo^s,:)**2,dim=ndim+1))+smalldouble
2608 do idims=1,ndim
2609 invdx=1.d0/dxlevel(idims)
2610 ixc^l=ixo^l;
2611 ixcmin^d=ixomin^d-kr(idims,^d);ixcmax^d=ixomax^d;
2612 jxc^l=ixc^l+kr(idims,^d);
2613 kxc^l=jxc^l+kr(idims,^d);
2614 hxc^l=ixc^l-kr(idims,^d);
2615 hxo^l=ixo^l-kr(idims,^d);
2616 tface(ixc^s)=(7.d0*(te(ixc^s)+te(jxc^s))-(te(hxc^s)+te(kxc^s)))/12.d0
2617 bunit(ixo^s)=btot(ixo^s,idims)/bsum(ixo^s)
2618 htc_qsrc(ixo^s)=htc_qsrc(ixo^s)+sigt(ixo^s)*bunit(ixo^s)*(tface(ixo^s)-tface(hxo^s))*invdx
2619 end do
2620 htc_qsrc(ixo^s)=(htc_qsrc(ixo^s)+wct(ixo^s,q_))/tau(ixo^s)
2621 w(ixo^s,q_)=w(ixo^s,q_)-qdt*htc_qsrc(ixo^s)
2622 end subroutine add_hypertc_source
2623
2624 !> Compute the Lorentz force (JxB)
2625 subroutine get_lorentz_force(ixI^L,ixO^L,w,JxB)
2627 integer, intent(in) :: ixi^l, ixo^l
2628 double precision, intent(in) :: w(ixi^s,1:nw)
2629 double precision, intent(inout) :: jxb(ixi^s,3)
2630 double precision :: a(ixi^s,3), b(ixi^s,3)
2631 ! For ndir=2 only 3rd component of J can exist, ndir=1 is impossible for MHD
2632 double precision :: current(ixi^s,7-2*ndir:3)
2633 integer :: idir, idirmin
2634
2635 b=0.0d0
2636 if(b0field) then
2637 do idir = 1, ndir
2638 b(ixo^s, idir) = w(ixo^s,mag(idir))+block%B0(ixo^s,idir,0)
2639 end do
2640 else
2641 do idir = 1, ndir
2642 b(ixo^s, idir) = w(ixo^s,mag(idir))
2643 end do
2644 end if
2645 ! store J current in a
2646 call get_current(w,ixi^l,ixo^l,idirmin,current)
2647 a=0.0d0
2648 do idir=7-2*ndir,3
2649 a(ixo^s,idir)=current(ixo^s,idir)
2650 end do
2651 call cross_product(ixi^l,ixo^l,a,b,jxb)
2652 end subroutine get_lorentz_force
2653
2654 !> Compute 1/(1+v_A^2/c^2) for semirelativistic MHD, where v_A is the Alfven
2655 !> velocity
2656 subroutine rmhd_gamma2_alfven(ixI^L, ixO^L, w, gamma_A2)
2658 integer, intent(in) :: ixi^l, ixo^l
2659 double precision, intent(in) :: w(ixi^s, nw)
2660 double precision, intent(out) :: gamma_a2(ixo^s)
2661 double precision :: rho(ixi^s)
2662
2663 ! rmhd_get_rho cannot be used as x is not a param
2664 if(has_equi_rho0) then
2665 rho(ixo^s) = w(ixo^s,rho_) + block%equi_vars(ixo^s,equi_rho0_,b0i)
2666 else
2667 rho(ixo^s) = w(ixo^s,rho_)
2668 endif
2669 ! Compute the inverse of 1 + B^2/(rho * c^2)
2670 gamma_a2(ixo^s) = 1.0d0/(1.0d0+rmhd_mag_en_all(w, ixi^l, ixo^l)/rho(ixo^s)*inv_squared_c)
2671 end subroutine rmhd_gamma2_alfven
2672
2673 !> Compute 1/sqrt(1+v_A^2/c^2) for semirelativisitic MHD, where v_A is the
2674 !> Alfven velocity
2675 function rmhd_gamma_alfven(w, ixI^L, ixO^L) result(gamma_A)
2677 integer, intent(in) :: ixi^l, ixo^l
2678 double precision, intent(in) :: w(ixi^s, nw)
2679 double precision :: gamma_a(ixo^s)
2680
2681 call rmhd_gamma2_alfven(ixi^l, ixo^l, w, gamma_a)
2682 gamma_a = sqrt(gamma_a)
2683 end function rmhd_gamma_alfven
2684
2685 subroutine rmhd_get_rho(w,x,ixI^L,ixO^L,rho)
2687 integer, intent(in) :: ixi^l, ixo^l
2688 double precision, intent(in) :: w(ixi^s,1:nw),x(ixi^s,1:ndim)
2689 double precision, intent(out) :: rho(ixi^s)
2690
2691 if(has_equi_rho0) then
2692 rho(ixo^s) = w(ixo^s,rho_) + block%equi_vars(ixo^s,equi_rho0_,b0i)
2693 else
2694 rho(ixo^s) = w(ixo^s,rho_)
2695 endif
2696 end subroutine rmhd_get_rho
2697
2698 !> handle small or negative internal energy
2699 subroutine rmhd_handle_small_ei(w, x, ixI^L, ixO^L, ie, subname)
2702 integer, intent(in) :: ixi^l,ixo^l, ie
2703 double precision, intent(inout) :: w(ixi^s,1:nw)
2704 double precision, intent(in) :: x(ixi^s,1:ndim)
2705 character(len=*), intent(in) :: subname
2706 double precision :: rho(ixi^s)
2707 integer :: idir
2708 logical :: flag(ixi^s,1:nw)
2709
2710 flag=.false.
2711 if(has_equi_pe0) then
2712 where(w(ixo^s,ie)+block%equi_vars(ixo^s,equi_pe0_,0)*inv_gamma_1<small_e)&
2713 flag(ixo^s,ie)=.true.
2714 else
2715 where(w(ixo^s,ie)<small_e) flag(ixo^s,ie)=.true.
2716 endif
2717 if(any(flag(ixo^s,ie))) then
2718 select case (small_values_method)
2719 case ("replace")
2720 if(has_equi_pe0) then
2721 where(flag(ixo^s,ie)) w(ixo^s,ie)=small_e - &
2722 block%equi_vars(ixo^s,equi_pe0_,0)*inv_gamma_1
2723 else
2724 where(flag(ixo^s,ie)) w(ixo^s,ie)=small_e
2725 endif
2726 case ("average")
2727 call small_values_average(ixi^l, ixo^l, w, x, flag, ie)
2728 case default
2729 ! small values error shows primitive variables
2730 w(ixo^s,e_)=w(ixo^s,e_)*gamma_1
2731 call rmhd_get_rho(w,x,ixi^l,ixo^l,rho)
2732 do idir = 1, ndir
2733 w(ixo^s, mom(idir)) = w(ixo^s, mom(idir))/rho(ixo^s)
2734 end do
2735 call small_values_error(w, x, ixi^l, ixo^l, flag, subname)
2736 end select
2737 end if
2738 end subroutine rmhd_handle_small_ei
2739
2740 subroutine rmhd_update_temperature(ixI^L,ixO^L,wCT,w,x)
2743
2744 integer, intent(in) :: ixi^l, ixo^l
2745 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
2746 double precision, intent(inout) :: w(ixi^s,1:nw)
2747
2748 double precision :: iz_h(ixo^s),iz_he(ixo^s), pth(ixi^s)
2749
2750 call ionization_degree_from_temperature(ixi^l,ixo^l,wct(ixi^s,te_),iz_h,iz_he)
2751
2752 call rmhd_get_pthermal(w,x,ixi^l,ixo^l,pth)
2753
2754 w(ixo^s,te_)=(2.d0+3.d0*he_abundance)*pth(ixo^s)/(w(ixo^s,rho_)*(1.d0+iz_h(ixo^s)+&
2755 he_abundance*(iz_he(ixo^s)*(iz_he(ixo^s)+1.d0)+1.d0)))
2756 end subroutine rmhd_update_temperature
2757
2758 !> Source terms after split off time-independent magnetic field
2759 subroutine add_source_b0split(qdt,dtfactor,ixI^L,ixO^L,wCT,w,x)
2761 integer, intent(in) :: ixi^l, ixo^l
2762 double precision, intent(in) :: qdt, dtfactor,wct(ixi^s,1:nw), x(ixi^s,1:ndim)
2763 double precision, intent(inout) :: w(ixi^s,1:nw)
2764 double precision :: a(ixi^s,3), b(ixi^s,3), axb(ixi^s,3)
2765 integer :: idir
2766
2767 a=0.d0
2768 b=0.d0
2769 ! for force-free field J0xB0 =0
2770 if(.not.b0field_forcefree) then
2771 ! store B0 magnetic field in b
2772 b(ixo^s,1:ndir)=block%B0(ixo^s,1:ndir,0)
2773 ! store J0 current in a
2774 do idir=7-2*ndir,3
2775 a(ixo^s,idir)=block%J0(ixo^s,idir)
2776 end do
2777 call cross_product(ixi^l,ixo^l,a,b,axb)
2778 if(local_timestep) then
2779 do idir=1,3
2780 axb(ixo^s,idir)=axb(ixo^s,idir)*block%dt(ixo^s)*dtfactor
2781 enddo
2782 else
2783 axb(ixo^s,:)=axb(ixo^s,:)*qdt
2784 endif
2785 ! add J0xB0 source term in momentum equations
2786 w(ixo^s,mom(1:ndir))=w(ixo^s,mom(1:ndir))+axb(ixo^s,1:ndir)
2787 end if
2788 if(total_energy) then
2789 a=0.d0
2790 ! for free-free field -(vxB0) dot J0 =0
2791 b(ixo^s,:)=wct(ixo^s,mag(:))
2792 ! store full magnetic field B0+B1 in b
2793 if(.not.b0field_forcefree) b(ixo^s,:)=b(ixo^s,:)+block%B0(ixo^s,:,0)
2794 ! store velocity in a
2795 a(ixi^s,1:ndir)=wct(ixi^s,mom(1:ndir))
2796 ! -E = a x b
2797 call cross_product(ixi^l,ixo^l,a,b,axb)
2798 if(local_timestep) then
2799 do idir=1,3
2800 axb(ixo^s,idir)=axb(ixo^s,idir)*block%dt(ixo^s)*dtfactor
2801 enddo
2802 else
2803 axb(ixo^s,:)=axb(ixo^s,:)*qdt
2804 endif
2805 ! add -(vxB) dot J0 source term in energy equation
2806 do idir=7-2*ndir,3
2807 w(ixo^s,e_)=w(ixo^s,e_)-axb(ixo^s,idir)*block%J0(ixo^s,idir)
2808 end do
2809 end if
2810 if (fix_small_values) call rmhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_B0')
2811 end subroutine add_source_b0split
2812
2813 !> Add resistive source to w within ixO Uses 3 point stencil (1 neighbour) in
2814 !> each direction, non-conservative. If the fourthorder precompiler flag is
2815 !> set, uses fourth order central difference for the laplacian. Then the
2816 !> stencil is 5 (2 neighbours).
2817 subroutine add_source_res1(qdt,ixI^L,ixO^L,wCT,w,x)
2819 use mod_usr_methods
2820 use mod_geometry
2821 integer, intent(in) :: ixi^l, ixo^l
2822 double precision, intent(in) :: qdt
2823 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
2824 double precision, intent(inout) :: w(ixi^s,1:nw)
2825 integer :: ixa^l,idir,jdir,kdir,idirmin,idim,jxo^l,hxo^l,ix
2826 integer :: lxo^l, kxo^l
2827 double precision :: tmp(ixi^s),tmp2(ixi^s)
2828 ! For ndir=2 only 3rd component of J can exist, ndir=1 is impossible for MHD
2829 double precision :: current(ixi^s,7-2*ndir:3),eta(ixi^s)
2830 double precision :: gradeta(ixi^s,1:ndim), bf(ixi^s,1:ndir)
2831
2832 ! Calculating resistive sources involve one extra layer
2833 if (rmhd_4th_order) then
2834 ixa^l=ixo^l^ladd2;
2835 else
2836 ixa^l=ixo^l^ladd1;
2837 end if
2838 if (iximin^d>ixamin^d.or.iximax^d<ixamax^d|.or.) &
2839 call mpistop("Error in add_source_res1: Non-conforming input limits")
2840 ! Calculate current density and idirmin
2841 call get_current(wct,ixi^l,ixo^l,idirmin,current)
2842 if (rmhd_eta>zero)then
2843 eta(ixa^s)=rmhd_eta
2844 gradeta(ixo^s,1:ndim)=zero
2845 else
2846 call usr_special_resistivity(wct,ixi^l,ixa^l,idirmin,x,current,eta)
2847 ! assumes that eta is not function of current?
2848 do idim=1,ndim
2849 call gradient(eta,ixi^l,ixo^l,idim,tmp)
2850 gradeta(ixo^s,idim)=tmp(ixo^s)
2851 end do
2852 end if
2853 if(b0field) then
2854 bf(ixi^s,1:ndir)=wct(ixi^s,mag(1:ndir))+block%B0(ixi^s,1:ndir,0)
2855 else
2856 bf(ixi^s,1:ndir)=wct(ixi^s,mag(1:ndir))
2857 end if
2858 do idir=1,ndir
2859 ! Put B_idir into tmp2 and eta*Laplace B_idir into tmp
2860 if (rmhd_4th_order) then
2861 tmp(ixo^s)=zero
2862 tmp2(ixi^s)=bf(ixi^s,idir)
2863 do idim=1,ndim
2864 lxo^l=ixo^l+2*kr(idim,^d);
2865 jxo^l=ixo^l+kr(idim,^d);
2866 hxo^l=ixo^l-kr(idim,^d);
2867 kxo^l=ixo^l-2*kr(idim,^d);
2868 tmp(ixo^s)=tmp(ixo^s)+&
2869 (-tmp2(lxo^s)+16.0d0*tmp2(jxo^s)-30.0d0*tmp2(ixo^s)+16.0d0*tmp2(hxo^s)-tmp2(kxo^s)) &
2870 /(12.0d0 * dxlevel(idim)**2)
2871 end do
2872 else
2873 tmp(ixo^s)=zero
2874 tmp2(ixi^s)=bf(ixi^s,idir)
2875 do idim=1,ndim
2876 jxo^l=ixo^l+kr(idim,^d);
2877 hxo^l=ixo^l-kr(idim,^d);
2878 tmp(ixo^s)=tmp(ixo^s)+&
2879 (tmp2(jxo^s)-2.0d0*tmp2(ixo^s)+tmp2(hxo^s))/dxlevel(idim)**2
2880 end do
2881 end if
2882 ! Multiply by eta
2883 tmp(ixo^s)=tmp(ixo^s)*eta(ixo^s)
2884 ! Subtract grad(eta) x J = eps_ijk d_j eta J_k if eta is non-constant
2885 if (rmhd_eta<zero)then
2886 do jdir=1,ndim; do kdir=idirmin,3
2887 if (lvc(idir,jdir,kdir)/=0)then
2888 if (lvc(idir,jdir,kdir)==1)then
2889 tmp(ixo^s)=tmp(ixo^s)-gradeta(ixo^s,jdir)*current(ixo^s,kdir)
2890 else
2891 tmp(ixo^s)=tmp(ixo^s)+gradeta(ixo^s,jdir)*current(ixo^s,kdir)
2892 end if
2893 end if
2894 end do; end do
2895 end if
2896 ! Add sources related to eta*laplB-grad(eta) x J to B and e
2897 w(ixo^s,mag(idir))=w(ixo^s,mag(idir))+qdt*tmp(ixo^s)
2898 if(total_energy) then
2899 w(ixo^s,e_)=w(ixo^s,e_)+qdt*tmp(ixo^s)*bf(ixo^s,idir)
2900 end if
2901 end do ! idir
2902 if(rmhd_energy) then
2903 ! de/dt+=eta*J**2
2904 w(ixo^s,e_)=w(ixo^s,e_)+qdt*eta(ixo^s)*sum(current(ixo^s,:)**2,dim=ndim+1)
2905 end if
2906 if (fix_small_values) call rmhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_res1')
2907 end subroutine add_source_res1
2908
2909 !> Add resistive source to w within ixO
2910 !> Uses 5 point stencil (2 neighbours) in each direction, conservative
2911 subroutine add_source_res2(qdt,ixI^L,ixO^L,wCT,w,x)
2913 use mod_usr_methods
2914 use mod_geometry
2915 integer, intent(in) :: ixi^l, ixo^l
2916 double precision, intent(in) :: qdt
2917 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
2918 double precision, intent(inout) :: w(ixi^s,1:nw)
2919 ! For ndir=2 only 3rd component of J can exist, ndir=1 is impossible for MHD
2920 double precision :: current(ixi^s,7-2*ndir:3),eta(ixi^s),curlj(ixi^s,1:3)
2921 double precision :: tmpvec(ixi^s,1:3),tmp(ixo^s)
2922 integer :: ixa^l,idir,idirmin,idirmin1
2923
2924 ixa^l=ixo^l^ladd2;
2925 if (iximin^d>ixamin^d.or.iximax^d<ixamax^d|.or.) &
2926 call mpistop("Error in add_source_res2: Non-conforming input limits")
2927 ixa^l=ixo^l^ladd1;
2928 ! Calculate current density within ixL: J=curl B, thus J_i=eps_ijk*d_j B_k
2929 ! Determine exact value of idirmin while doing the loop.
2930 call get_current(wct,ixi^l,ixa^l,idirmin,current)
2931 tmpvec=zero
2932 if(rmhd_eta>zero)then
2933 do idir=idirmin,3
2934 tmpvec(ixa^s,idir)=current(ixa^s,idir)*rmhd_eta
2935 end do
2936 else
2937 call usr_special_resistivity(wct,ixi^l,ixa^l,idirmin,x,current,eta)
2938 do idir=idirmin,3
2939 tmpvec(ixa^s,idir)=current(ixa^s,idir)*eta(ixa^s)
2940 end do
2941 end if
2942 ! dB/dt= -curl(J*eta), thus B_i=B_i-eps_ijk d_j Jeta_k
2943 call curlvector(tmpvec,ixi^l,ixo^l,curlj,idirmin1,1,3)
2944 if(stagger_grid) then
2945 if(ndim==2.and.ndir==3) then
2946 ! if 2.5D
2947 w(ixo^s,mag(ndir)) = w(ixo^s,mag(ndir))-qdt*curlj(ixo^s,ndir)
2948 end if
2949 else
2950 w(ixo^s,mag(1:ndir)) = w(ixo^s,mag(1:ndir))-qdt*curlj(ixo^s,1:ndir)
2951 end if
2952 if(rmhd_energy) then
2953 if(rmhd_eta>zero)then
2954 tmp(ixo^s)=qdt*rmhd_eta*sum(current(ixo^s,:)**2,dim=ndim+1)
2955 else
2956 tmp(ixo^s)=qdt*eta(ixo^s)*sum(current(ixo^s,:)**2,dim=ndim+1)
2957 end if
2958 if(total_energy) then
2959 ! de/dt= +div(B x Jeta) = eta J^2 - B dot curl(eta J)
2960 ! de1/dt= eta J^2 - B1 dot curl(eta J)
2961 w(ixo^s,e_)=w(ixo^s,e_)+tmp(ixo^s)-&
2962 qdt*sum(wct(ixo^s,mag(1:ndir))*curlj(ixo^s,1:ndir),dim=ndim+1)
2963 else
2964 ! add eta*J**2 source term in the internal energy equation
2965 w(ixo^s,e_)=w(ixo^s,e_)+tmp(ixo^s)
2966 end if
2967 end if
2968 if (fix_small_values) call rmhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_res2')
2969 end subroutine add_source_res2
2970
2971 !> Add Hyper-resistive source to w within ixO
2972 !> Uses 9 point stencil (4 neighbours) in each direction.
2973 subroutine add_source_hyperres(qdt,ixI^L,ixO^L,wCT,w,x)
2975 use mod_geometry
2976 integer, intent(in) :: ixi^l, ixo^l
2977 double precision, intent(in) :: qdt
2978 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
2979 double precision, intent(inout) :: w(ixi^s,1:nw)
2980 !.. local ..
2981 double precision :: current(ixi^s,7-2*ndir:3)
2982 double precision :: tmpvec(ixi^s,1:3),tmpvec2(ixi^s,1:3),tmp(ixi^s),ehyper(ixi^s,1:3)
2983 integer :: ixa^l,idir,jdir,kdir,idirmin,idirmin1
2984
2985 ixa^l=ixo^l^ladd3;
2986 if (iximin^d>ixamin^d.or.iximax^d<ixamax^d|.or.) &
2987 call mpistop("Error in add_source_hyperres: Non-conforming input limits")
2988 call get_current(wct,ixi^l,ixa^l,idirmin,current)
2989 tmpvec(ixa^s,1:ndir)=zero
2990 do jdir=idirmin,3
2991 tmpvec(ixa^s,jdir)=current(ixa^s,jdir)
2992 end do
2993 ixa^l=ixo^l^ladd2;
2994 call curlvector(tmpvec,ixi^l,ixa^l,tmpvec2,idirmin1,1,3)
2995 ixa^l=ixo^l^ladd1;
2996 tmpvec(ixa^s,1:ndir)=zero
2997 call curlvector(tmpvec2,ixi^l,ixa^l,tmpvec,idirmin1,1,3)
2998 ehyper(ixa^s,1:ndir) = - tmpvec(ixa^s,1:ndir)*rmhd_eta_hyper
2999 ixa^l=ixo^l;
3000 tmpvec2(ixa^s,1:ndir)=zero
3001 call curlvector(ehyper,ixi^l,ixa^l,tmpvec2,idirmin1,1,3)
3002 do idir=1,ndir
3003 w(ixo^s,mag(idir)) = w(ixo^s,mag(idir))-tmpvec2(ixo^s,idir)*qdt
3004 end do
3005 if(total_energy) then
3006 ! de/dt= +div(B x Ehyper)
3007 ixa^l=ixo^l^ladd1;
3008 tmpvec2(ixa^s,1:ndir)=zero
3009 do idir=1,ndir; do jdir=1,ndir; do kdir=idirmin,3
3010 tmpvec2(ixa^s,idir) = tmpvec(ixa^s,idir)&
3011 + lvc(idir,jdir,kdir)*wct(ixa^s,mag(jdir))*ehyper(ixa^s,kdir)
3012 end do; end do; end do
3013 tmp(ixo^s)=zero
3014 call divvector(tmpvec2,ixi^l,ixo^l,tmp)
3015 w(ixo^s,e_)=w(ixo^s,e_)+tmp(ixo^s)*qdt
3016 end if
3017 if (fix_small_values) call rmhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_hyperres')
3018 end subroutine add_source_hyperres
3019
3020 subroutine add_source_glm(qdt,ixI^L,ixO^L,wCT,w,x)
3021 ! Add divB related sources to w within ixO
3022 ! corresponding to Dedner JCP 2002, 175, 645 _equation 24_
3023 ! giving the EGLM-MHD scheme or GLM-MHD scheme
3025 use mod_geometry
3026 integer, intent(in) :: ixi^l, ixo^l
3027 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), x(ixi^s,1:ndim)
3028 double precision, intent(inout) :: w(ixi^s,1:nw)
3029 double precision:: divb(ixi^s), gradpsi(ixi^s), ba(ixo^s,1:ndir)
3030 integer :: idir
3031
3032 ! dPsi/dt = - Ch^2/Cp^2 Psi
3033 if (rmhd_glm_alpha < zero) then
3034 w(ixo^s,psi_) = abs(rmhd_glm_alpha)*wct(ixo^s,psi_)
3035 else
3036 ! implicit update of Psi variable
3037 ! equation (27) in Mignone 2010 J. Com. Phys. 229, 2117
3038 if(slab_uniform) then
3039 w(ixo^s,psi_) = dexp(-qdt*cmax_global*rmhd_glm_alpha/minval(dxlevel(:)))*w(ixo^s,psi_)
3040 else
3041 w(ixo^s,psi_) = dexp(-qdt*cmax_global*rmhd_glm_alpha/minval(block%ds(ixo^s,:),dim=ndim+1))*w(ixo^s,psi_)
3042 end if
3043 end if
3044 if(rmhd_glm_extended) then
3045 if(b0field) then
3046 ba(ixo^s,1:ndir)=wct(ixo^s,mag(1:ndir))+block%B0(ixo^s,1:ndir,0)
3047 else
3048 ba(ixo^s,1:ndir)=wct(ixo^s,mag(1:ndir))
3049 end if
3050 ! gradient of Psi
3051 if(total_energy) then
3052 do idir=1,ndim
3053 select case(typegrad)
3054 case("central")
3055 call gradient(wct(ixi^s,psi_),ixi^l,ixo^l,idir,gradpsi)
3056 case("limited")
3057 call gradientl(wct(ixi^s,psi_),ixi^l,ixo^l,idir,gradpsi)
3058 end select
3059 ! e = e -qdt (b . grad(Psi))
3060 w(ixo^s,e_) = w(ixo^s,e_)-qdt*ba(ixo^s,idir)*gradpsi(ixo^s)
3061 end do
3062 end if
3063 ! We calculate now div B
3064 call get_divb(wct,ixi^l,ixo^l,divb,rmhd_divb_nth)
3065 ! m = m - qdt b div b
3066 do idir=1,ndir
3067 w(ixo^s,mom(idir))=w(ixo^s,mom(idir))-qdt*ba(ixo^s,idir)*divb(ixo^s)
3068 end do
3069 end if
3070 if (fix_small_values) call rmhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_glm')
3071 end subroutine add_source_glm
3072
3073 !> Add divB related sources to w within ixO corresponding to Powel
3074 subroutine add_source_powel(qdt,ixI^L,ixO^L,wCT,w,x)
3076 integer, intent(in) :: ixi^l, ixo^l
3077 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), x(ixi^s,1:ndim)
3078 double precision, intent(inout) :: w(ixi^s,1:nw)
3079 double precision :: divb(ixi^s), ba(1:ndir)
3080 integer :: idir, ix^d
3081
3082 ! calculate div B
3083 call get_divb(wct,ixi^l,ixo^l,divb,rmhd_divb_nth)
3084 if(b0field) then
3085 {do ix^db=ixomin^db,ixomax^db\}
3086 ! b = b - qdt v * div b
3087 ^c&w(ix^d,b^c_)=w(ix^d,b^c_)-qdt*wct(ix^d,m^c_)*divb(ix^d)\
3088 ! m = m - qdt b div b
3089 ^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)\
3090 if (total_energy) then
3091 ! e = e - qdt (v . b) * div b
3092 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)
3093 end if
3094 {end do\}
3095 else
3096 {do ix^db=ixomin^db,ixomax^db\}
3097 ! b = b - qdt v * div b
3098 ^c&w(ix^d,b^c_)=w(ix^d,b^c_)-qdt*wct(ix^d,m^c_)*divb(ix^d)\
3099 ! m = m - qdt b div b
3100 ^c&w(ix^d,m^c_)=w(ix^d,m^c_)-qdt*wct(ix^d,b^c_)*divb(ix^d)\
3101 if (total_energy) then
3102 ! e = e - qdt (v . b) * div b
3103 w(ix^d,e_)=w(ix^d,e_)-qdt*(^c&wct(ix^d,m^c_)*wct(ix^d,b^c_)+)*divb(ix^d)
3104 end if
3105 {end do\}
3106 end if
3107 if (fix_small_values) call rmhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_powel')
3108 end subroutine add_source_powel
3109
3110 subroutine add_source_janhunen(qdt,ixI^L,ixO^L,wCT,w,x)
3111 ! Add divB related sources to w within ixO
3112 ! corresponding to Janhunen, just the term in the induction equation.
3114 integer, intent(in) :: ixi^l, ixo^l
3115 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), x(ixi^s,1:ndim)
3116 double precision, intent(inout) :: w(ixi^s,1:nw)
3117 double precision :: divb(ixi^s)
3118 integer :: idir, ix^d
3119
3120 ! calculate div B
3121 call get_divb(wct,ixi^l,ixo^l,divb,rmhd_divb_nth)
3122 {do ix^db=ixomin^db,ixomax^db\}
3123 ! b = b - qdt v * div b
3124 ^c&w(ix^d,b^c_)=w(ix^d,b^c_)-qdt*wct(ix^d,m^c_)*divb(ix^d)\
3125 {end do\}
3126 if (fix_small_values) call rmhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_janhunen')
3127 end subroutine add_source_janhunen
3128
3129 subroutine add_source_linde(qdt,ixI^L,ixO^L,wCT,w,x)
3130 ! Add Linde's divB related sources to wnew within ixO
3132 use mod_geometry
3133 integer, intent(in) :: ixi^l, ixo^l
3134 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), x(ixi^s,1:ndim)
3135 double precision, intent(inout) :: w(ixi^s,1:nw)
3136 double precision :: divb(ixi^s),graddivb(ixi^s)
3137 integer :: idim, idir, ixp^l, i^d, iside
3138 logical, dimension(-1:1^D&) :: leveljump
3139
3140 ! Calculate div B
3141 ixp^l=ixo^l^ladd1;
3142 call get_divb(wct,ixi^l,ixp^l,divb,rmhd_divb_nth)
3143 ! for AMR stability, retreat one cell layer from the boarders of level jump
3144 {do i^db=-1,1\}
3145 if(i^d==0|.and.) cycle
3146 if(neighbor_type(i^d,block%igrid)==2 .or. neighbor_type(i^d,block%igrid)==4) then
3147 leveljump(i^d)=.true.
3148 else
3149 leveljump(i^d)=.false.
3150 end if
3151 {end do\}
3152 ixp^l=ixo^l;
3153 do idim=1,ndim
3154 select case(idim)
3155 {case(^d)
3156 do iside=1,2
3157 i^dd=kr(^dd,^d)*(2*iside-3);
3158 if (leveljump(i^dd)) then
3159 if (iside==1) then
3160 ixpmin^d=ixomin^d-i^d
3161 else
3162 ixpmax^d=ixomax^d-i^d
3163 end if
3164 end if
3165 end do
3166 \}
3167 end select
3168 end do
3169 ! Add Linde's diffusive terms
3170 do idim=1,ndim
3171 ! Calculate grad_idim(divb)
3172 select case(typegrad)
3173 case("central")
3174 call gradient(divb,ixi^l,ixp^l,idim,graddivb)
3175 case("limited")
3176 call gradientl(divb,ixi^l,ixp^l,idim,graddivb)
3177 end select
3178 ! Multiply by Linde's eta*dt = divbdiff*(c_max*dx)*dt = divbdiff*dx**2
3179 if (slab_uniform) then
3180 graddivb(ixp^s)=graddivb(ixp^s)*divbdiff/(^d&1.0d0/dxlevel(^d)**2+)
3181 else
3182 graddivb(ixp^s)=graddivb(ixp^s)*divbdiff &
3183 /(^d&1.0d0/block%ds(ixp^s,^d)**2+)
3184 end if
3185 w(ixp^s,mag(idim))=w(ixp^s,mag(idim))+graddivb(ixp^s)
3186
3187 if (typedivbdiff=='all' .and. total_energy) then
3188 ! e += B_idim*eta*grad_idim(divb)
3189 w(ixp^s,e_)=w(ixp^s,e_)+wct(ixp^s,mag(idim))*graddivb(ixp^s)
3190 end if
3191 end do
3192 if (fix_small_values) call rmhd_handle_small_values(.false.,w,x,ixi^l,ixo^l,'add_source_linde')
3193 end subroutine add_source_linde
3194
3195 !> get dimensionless div B = |divB| * volume / area / |B|
3196 subroutine get_normalized_divb(w,ixI^L,ixO^L,divb)
3198 integer, intent(in) :: ixi^l, ixo^l
3199 double precision, intent(in) :: w(ixi^s,1:nw)
3200 double precision :: divb(ixi^s), dsurface(ixi^s)
3201 double precision :: invb(ixo^s)
3202 integer :: ixa^l,idims
3203
3204 call get_divb(w,ixi^l,ixo^l,divb)
3205 invb(ixo^s)=sqrt(rmhd_mag_en_all(w,ixi^l,ixo^l))
3206 where(invb(ixo^s)/=0.d0)
3207 invb(ixo^s)=1.d0/invb(ixo^s)
3208 end where
3209 if(slab_uniform) then
3210 divb(ixo^s)=0.5d0*abs(divb(ixo^s))*invb(ixo^s)/sum(1.d0/dxlevel(:))
3211 else
3212 ixamin^d=ixomin^d-1;
3213 ixamax^d=ixomax^d-1;
3214 dsurface(ixo^s)= sum(block%surfaceC(ixo^s,:),dim=ndim+1)
3215 do idims=1,ndim
3216 ixa^l=ixo^l-kr(idims,^d);
3217 dsurface(ixo^s)=dsurface(ixo^s)+block%surfaceC(ixa^s,idims)
3218 end do
3219 divb(ixo^s)=abs(divb(ixo^s))*invb(ixo^s)*&
3220 block%dvolume(ixo^s)/dsurface(ixo^s)
3221 end if
3222 end subroutine get_normalized_divb
3223
3224 !> Calculate idirmin and the idirmin:3 components of the common current array
3225 !> make sure that dxlevel(^D) is set correctly.
3226 subroutine get_current(w,ixI^L,ixO^L,idirmin,current)
3228 use mod_geometry
3229 integer, intent(in) :: ixo^l, ixi^l
3230 double precision, intent(in) :: w(ixi^s,1:nw)
3231 integer, intent(out) :: idirmin
3232 ! For ndir=2 only 3rd component of J can exist, ndir=1 is impossible for MHD
3233 double precision :: current(ixi^s,7-2*ndir:3)
3234 integer :: idir, idirmin0
3235
3236 idirmin0 = 7-2*ndir
3237 call curlvector(w(ixi^s,mag(1:ndir)),ixi^l,ixo^l,current,idirmin,idirmin0,ndir)
3238 if(b0field) current(ixo^s,idirmin0:3)=current(ixo^s,idirmin0:3)+&
3239 block%J0(ixo^s,idirmin0:3)
3240 end subroutine get_current
3241
3242 !> If resistivity is not zero, check diffusion time limit for dt
3243 subroutine rmhd_get_dt(w,ixI^L,ixO^L,dtnew,dx^D,x)
3245 use mod_usr_methods
3247 use mod_gravity, only: gravity_get_dt
3248 use mod_cak_force, only: cak_get_dt
3249 use mod_fld, only: fld_radforce_get_dt
3251 integer, intent(in) :: ixi^l, ixo^l
3252 double precision, intent(inout) :: dtnew
3253 double precision, intent(in) :: dx^d
3254 double precision, intent(in) :: w(ixi^s,1:nw)
3255 double precision, intent(in) :: x(ixi^s,1:ndim)
3256 double precision :: dxarr(ndim)
3257 double precision :: current(ixi^s,7-2*ndir:3),eta(ixi^s)
3258 integer :: idirmin,idim
3259
3260 dtnew = bigdouble
3261
3262 if (.not. dt_c) then
3263 ^d&dxarr(^d)=dx^d;
3264 if (rmhd_eta>zero)then
3265 dtnew=dtdiffpar*minval(dxarr(1:ndim))**2/rmhd_eta
3266 else if (rmhd_eta<zero)then
3267 call get_current(w,ixi^l,ixo^l,idirmin,current)
3268 call usr_special_resistivity(w,ixi^l,ixo^l,idirmin,x,current,eta)
3269 dtnew=bigdouble
3270 do idim=1,ndim
3271 if(slab_uniform) then
3272 dtnew=min(dtnew,&
3273 dtdiffpar/(smalldouble+maxval(eta(ixo^s)/dxarr(idim)**2)))
3274 else
3275 dtnew=min(dtnew,&
3276 dtdiffpar/(smalldouble+maxval(eta(ixo^s)/block%ds(ixo^s,idim)**2)))
3277 end if
3278 end do
3279 end if
3280 if(rmhd_eta_hyper>zero) then
3281 if(slab_uniform) then
3282 dtnew=min(dtdiffpar*minval(dxarr(1:ndim))**4/rmhd_eta_hyper,dtnew)
3283 else
3284 dtnew=min(dtdiffpar*minval(block%ds(ixo^s,1:ndim))**4/rmhd_eta_hyper,dtnew)
3285 end if
3286 end if
3287 if(rmhd_radiation_force) then
3288 select case(rmhd_radiation_formalism)
3289 case('fld')
3290 call fld_radforce_get_dt(w,ixi^l,ixo^l,dtnew,dx^d,x)
3291 case('afld')
3292 call afld_radforce_get_dt(w,ixi^l,ixo^l,dtnew,dx^d,x)
3293 case default
3294 call mpistop('Radiation formalism unknown')
3295 end select
3296 endif
3297 if(rmhd_viscosity) then
3298 call viscosity_get_dt(w,ixi^l,ixo^l,dtnew,dx^d,x)
3299 end if
3300 if(rmhd_gravity) then
3301 call gravity_get_dt(w,ixi^l,ixo^l,dtnew,dx^d,x)
3302 end if
3303 if (rmhd_cak_force) then
3304 call cak_get_dt(w,ixi^l,ixo^l,dtnew,dx^d,x)
3305 end if
3306 else
3307 {^ifoned dtnew = dx1*unit_velocity/const_c}
3308 {^nooned dtnew = min(dx^d*unit_velocity/const_c)}
3309 endif
3310 end subroutine rmhd_get_dt
3311
3312 ! Add geometrical source terms to w
3313 subroutine rmhd_add_source_geom(qdt,dtfactor,ixI^L,ixO^L,wCT,wprim,w,x)
3315 use mod_geometry
3316 integer, intent(in) :: ixi^l, ixo^l
3317 double precision, intent(in) :: qdt, dtfactor,x(ixi^s,1:ndim)
3318 double precision, intent(inout) :: wct(ixi^s,1:nw),wprim(ixi^s,1:nw),w(ixi^s,1:nw)
3319 double precision :: tmp,tmp1,invr,cot
3320 integer :: ix^d
3321 integer :: mr_,mphi_ ! Polar var. names
3322 integer :: br_,bphi_
3323
3324 mr_=mom(1); mphi_=mom(1)-1+phi_ ! Polar var. names
3325 br_=mag(1); bphi_=mag(1)-1+phi_
3326 select case (coordinate)
3327 case (cylindrical)
3328 {do ix^db=ixomin^db,ixomax^db\}
3329 ! include dt in invr, invr is always used with qdt
3330 if(local_timestep) then
3331 invr=block%dt(ix^d) * dtfactor/x(ix^d,1)
3332 else
3333 invr=qdt/x(ix^d,1)
3334 end if
3335 if(rmhd_energy) then
3336 tmp=wprim(ix^d,p_)+half*(^c&wprim(ix^d,b^c_)**2+)
3337 else
3338 tmp=rmhd_adiab*wprim(ix^d,rho_)**rmhd_gamma+half*(^c&wprim(ix^d,b^c_)**2+)
3339 end if
3340 if(phi_>0) then
3341 w(ix^d,mr_)=w(ix^d,mr_)+invr*(tmp-&
3342 wprim(ix^d,bphi_)**2+wprim(ix^d,mphi_)*wct(ix^d,mphi_))
3343 w(ix^d,mphi_)=w(ix^d,mphi_)+invr*(&
3344 -wct(ix^d,mphi_)*wprim(ix^d,mr_) &
3345 +wprim(ix^d,bphi_)*wprim(ix^d,br_))
3346 if(.not.stagger_grid) then
3347 w(ix^d,bphi_)=w(ix^d,bphi_)+invr*&
3348 (wprim(ix^d,bphi_)*wprim(ix^d,mr_) &
3349 -wprim(ix^d,br_)*wprim(ix^d,mphi_))
3350 end if
3351 else
3352 w(ix^d,mr_)=w(ix^d,mr_)+invr*tmp
3353 end if
3354 if(rmhd_glm) w(ix^d,br_)=w(ix^d,br_)+wprim(ix^d,psi_)*invr
3355 {end do\}
3356 case (spherical)
3357 {do ix^db=ixomin^db,ixomax^db\}
3358 ! include dt in invr, invr is always used with qdt
3359 if(local_timestep) then
3360 invr=block%dt(ix^d) * dtfactor/x(ix^d,1)
3361 else
3362 invr=qdt/x(ix^d,1)
3363 end if
3364 if(rmhd_energy) then
3365 tmp1=wprim(ix^d,p_)+half*(^c&wprim(ix^d,b^c_)**2+)
3366 else
3367 tmp1=rmhd_adiab*wprim(ix^d,rho_)**rmhd_gamma+half*(^c&wprim(ix^d,b^c_)**2+)
3368 end if
3369 ! m1
3370 {^ifonec
3371 w(ix^d,mom(1))=w(ix^d,mom(1))+two*tmp1*invr
3372 }
3373 {^noonec
3374 w(ix^d,mom(1))=w(ix^d,mom(1))+invr*&
3375 (two*tmp1+(^ce&wprim(ix^d,m^ce_)*wct(ix^d,m^ce_)-wprim(ix^d,b^ce_)**2+))
3376 }
3377 ! b1
3378 if(rmhd_glm) then
3379 w(ix^d,mag(1))=w(ix^d,mag(1))+invr*2.0d0*wprim(ix^d,psi_)
3380 end if
3381 {^ifoned
3382 cot=0.d0
3383 }
3384 {^nooned
3385 cot=1.d0/tan(x(ix^d,2))
3386 }
3387 {^iftwoc
3388 ! m2
3389 w(ix^d,mom(2))=w(ix^d,mom(2))+invr*(tmp1*cot-wprim(ix^d,m1_)*wct(ix^d,m2_)&
3390 +wprim(ix^d,b1_)*wprim(ix^d,b2_))
3391 ! b2
3392 if(.not.stagger_grid) then
3393 tmp=wprim(ix^d,m1_)*wprim(ix^d,b2_)-wprim(ix^d,m2_)*wprim(ix^d,b1_)
3394 if(rmhd_glm) then
3395 tmp=tmp+wprim(ix^d,psi_)*cot
3396 end if
3397 w(ix^d,mag(2))=w(ix^d,mag(2))+tmp*invr
3398 end if
3399 }
3400 {^ifthreec
3401 ! m2
3402 w(ix^d,mom(2))=w(ix^d,mom(2))+invr*(tmp1*cot-wprim(ix^d,m1_)*wct(ix^d,m2_)&
3403 +wprim(ix^d,b1_)*wprim(ix^d,b2_)&
3404 +(wprim(ix^d,m3_)*wct(ix^d,m3_)-wprim(ix^d,b3_)**2)*cot)
3405 ! b2
3406 if(.not.stagger_grid) then
3407 tmp=wprim(ix^d,m1_)*wprim(ix^d,b2_)-wprim(ix^d,m2_)*wprim(ix^d,b1_)
3408 if(rmhd_glm) then
3409 tmp=tmp+wprim(ix^d,psi_)*cot
3410 end if
3411 w(ix^d,mag(2))=w(ix^d,mag(2))+tmp*invr
3412 end if
3413 ! m3
3414 w(ix^d,mom(3))=w(ix^d,mom(3))-invr*&
3415 (wprim(ix^d,m3_)*wct(ix^d,m1_) &
3416 -wprim(ix^d,b3_)*wprim(ix^d,b1_) &
3417 +(wprim(ix^d,m2_)*wct(ix^d,m3_) &
3418 -wprim(ix^d,b2_)*wprim(ix^d,b3_))*cot)
3419 ! b3
3420 if(.not.stagger_grid) then
3421 w(ix^d,mag(3))=w(ix^d,mag(3))+invr*&
3422 (wprim(ix^d,m1_)*wprim(ix^d,b3_) &
3423 -wprim(ix^d,m3_)*wprim(ix^d,b1_) &
3424 -(wprim(ix^d,m3_)*wprim(ix^d,b2_) &
3425 -wprim(ix^d,m2_)*wprim(ix^d,b3_))*cot)
3426 end if
3427 }
3428 {end do\}
3429 end select
3430 end subroutine rmhd_add_source_geom
3431
3432 ! Add geometrical source terms to w
3433 subroutine rmhd_add_source_geom_split(qdt,dtfactor, ixI^L,ixO^L,wCT,wprim,w,x)
3435 use mod_geometry
3436 integer, intent(in) :: ixi^l, ixo^l
3437 double precision, intent(in) :: qdt, dtfactor, x(ixi^s,1:ndim)
3438 double precision, intent(inout) :: wct(ixi^s,1:nw), wprim(ixi^s,1:nw),w(ixi^s,1:nw)
3439 double precision :: tmp(ixi^s),tmp1(ixi^s),tmp2(ixi^s),invrho(ixo^s),invr(ixo^s)
3440 integer :: iw,idir, h1x^l{^nooned, h2x^l}
3441 integer :: mr_,mphi_ ! Polar var. names
3442 integer :: br_,bphi_
3443
3444 mr_=mom(1); mphi_=mom(1)-1+phi_ ! Polar var. names
3445 br_=mag(1); bphi_=mag(1)-1+phi_
3446 if(has_equi_rho0) then
3447 invrho(ixo^s) = 1d0/(wct(ixo^s,rho_) + block%equi_vars(ixo^s,equi_rho0_,b0i))
3448 else
3449 invrho(ixo^s) = 1d0/wct(ixo^s,rho_)
3450 end if
3451 ! include dt in invr, invr is always used with qdt
3452 if(local_timestep) then
3453 invr(ixo^s) = block%dt(ixo^s) * dtfactor/x(ixo^s,1)
3454 else
3455 invr(ixo^s) = qdt/x(ixo^s,1)
3456 end if
3457
3458 select case (coordinate)
3459 case (cylindrical)
3460 call rmhd_get_p_total(wct,x,ixi^l,ixo^l,tmp)
3461 if(phi_>0) then
3462 w(ixo^s,mr_)=w(ixo^s,mr_)+invr(ixo^s)*(tmp(ixo^s)-&
3463 wct(ixo^s,bphi_)**2+wct(ixo^s,mphi_)**2*invrho(ixo^s))
3464 w(ixo^s,mphi_)=w(ixo^s,mphi_)+qdt*invr(ixo^s)*(&
3465 -wct(ixo^s,mphi_)*wct(ixo^s,mr_)*invrho(ixo^s) &
3466 +wct(ixo^s,bphi_)*wct(ixo^s,br_))
3467 if(.not.stagger_grid) then
3468 w(ixo^s,bphi_)=w(ixo^s,bphi_)+invr(ixo^s)*&
3469 (wct(ixo^s,bphi_)*wct(ixo^s,mr_) &
3470 -wct(ixo^s,br_)*wct(ixo^s,mphi_)) &
3471 *invrho(ixo^s)
3472 end if
3473 else
3474 w(ixo^s,mr_)=w(ixo^s,mr_)+invr(ixo^s)*tmp(ixo^s)
3475 end if
3476 if(rmhd_glm) w(ixo^s,br_)=w(ixo^s,br_)+wct(ixo^s,psi_)*invr(ixo^s)
3477 case (spherical)
3478 h1x^l=ixo^l-kr(1,^d); {^nooned h2x^l=ixo^l-kr(2,^d);}
3479 call rmhd_get_p_total(wct,x,ixi^l,ixo^l,tmp1)
3480 tmp(ixo^s)=tmp1(ixo^s)
3481 if(b0field) then
3482 tmp2(ixo^s)=sum(block%B0(ixo^s,:,0)*wct(ixo^s,mag(:)),dim=ndim+1)
3483 tmp(ixo^s)=tmp(ixo^s)+tmp2(ixo^s)
3484 end if
3485 ! m1
3486 tmp(ixo^s)=tmp(ixo^s)*x(ixo^s,1) &
3487 *(block%surfaceC(ixo^s,1)-block%surfaceC(h1x^s,1))/block%dvolume(ixo^s)
3488 if(ndir>1) then
3489 do idir=2,ndir
3490 tmp(ixo^s)=tmp(ixo^s)+wct(ixo^s,mom(idir))**2*invrho(ixo^s)-wct(ixo^s,mag(idir))**2
3491 if(b0field) tmp(ixo^s)=tmp(ixo^s)-2.0d0*block%B0(ixo^s,idir,0)*wct(ixo^s,mag(idir))
3492 end do
3493 end if
3494 w(ixo^s,mom(1))=w(ixo^s,mom(1))+tmp(ixo^s)*invr(ixo^s)
3495 ! b1
3496 if(rmhd_glm) then
3497 w(ixo^s,mag(1))=w(ixo^s,mag(1))+invr(ixo^s)*2.0d0*wct(ixo^s,psi_)
3498 end if
3499 {^nooned
3500 ! m2
3501 tmp(ixo^s)=tmp1(ixo^s)
3502 if(b0field) then
3503 tmp(ixo^s)=tmp(ixo^s)+tmp2(ixo^s)
3504 end if
3505 if(local_timestep) then
3506 tmp1(ixo^s) = block%dt(ixo^s) * tmp(ixo^s)
3507 else
3508 tmp1(ixo^s) = qdt * tmp(ixo^s)
3509 endif
3510 ! This will make hydrostatic p=const an exact solution
3511 w(ixo^s,mom(2))=w(ixo^s,mom(2))+tmp1(ixo^s) &
3512 *(block%surfaceC(ixo^s,2)-block%surfaceC(h2x^s,2)) &
3513 /block%dvolume(ixo^s)
3514 tmp(ixo^s)=-(wct(ixo^s,mom(1))*wct(ixo^s,mom(2))*invrho(ixo^s) &
3515 -wct(ixo^s,mag(1))*wct(ixo^s,mag(2)))
3516 if (b0field) then
3517 tmp(ixo^s)=tmp(ixo^s)+block%B0(ixo^s,1,0)*wct(ixo^s,mag(2)) &
3518 +wct(ixo^s,mag(1))*block%B0(ixo^s,2,0)
3519 end if
3520 if(ndir==3) then
3521 tmp(ixo^s)=tmp(ixo^s)+(wct(ixo^s,mom(3))**2*invrho(ixo^s) &
3522 -wct(ixo^s,mag(3))**2)*dcos(x(ixo^s,2))/dsin(x(ixo^s,2))
3523 if (b0field) then
3524 tmp(ixo^s)=tmp(ixo^s)-2.0d0*block%B0(ixo^s,3,0)*wct(ixo^s,mag(3))&
3525 *dcos(x(ixo^s,2))/dsin(x(ixo^s,2))
3526 end if
3527 end if
3528 w(ixo^s,mom(2))=w(ixo^s,mom(2))+tmp(ixo^s)*invr(ixo^s)
3529 ! b2
3530 if(.not.stagger_grid) then
3531 tmp(ixo^s)=(wct(ixo^s,mom(1))*wct(ixo^s,mag(2)) &
3532 -wct(ixo^s,mom(2))*wct(ixo^s,mag(1)))*invrho(ixo^s)
3533 if(b0field) then
3534 tmp(ixo^s)=tmp(ixo^s)+(wct(ixo^s,mom(1))*block%B0(ixo^s,2,0) &
3535 -wct(ixo^s,mom(2))*block%B0(ixo^s,1,0))*invrho(ixo^s)
3536 end if
3537 if(rmhd_glm) then
3538 tmp(ixo^s)=tmp(ixo^s) &
3539 + dcos(x(ixo^s,2))/dsin(x(ixo^s,2))*wct(ixo^s,psi_)
3540 end if
3541 w(ixo^s,mag(2))=w(ixo^s,mag(2))+tmp(ixo^s)*invr(ixo^s)
3542 end if
3543 }
3544 if(ndir==3) then
3545 ! m3
3546 tmp(ixo^s)=-(wct(ixo^s,mom(3))*wct(ixo^s,mom(1))*invrho(ixo^s) &
3547 -wct(ixo^s,mag(3))*wct(ixo^s,mag(1))) {^nooned &
3548 -(wct(ixo^s,mom(2))*wct(ixo^s,mom(3))*invrho(ixo^s) &
3549 -wct(ixo^s,mag(2))*wct(ixo^s,mag(3))) &
3550 *dcos(x(ixo^s,2))/dsin(x(ixo^s,2)) }
3551 if (b0field) then
3552 tmp(ixo^s)=tmp(ixo^s)+block%B0(ixo^s,1,0)*wct(ixo^s,mag(3)) &
3553 +wct(ixo^s,mag(1))*block%B0(ixo^s,3,0) {^nooned &
3554 +(block%B0(ixo^s,2,0)*wct(ixo^s,mag(3)) &
3555 +wct(ixo^s,mag(2))*block%B0(ixo^s,3,0)) &
3556 *dcos(x(ixo^s,2))/dsin(x(ixo^s,2)) }
3557 end if
3558 w(ixo^s,mom(3))=w(ixo^s,mom(3))+tmp(ixo^s)*invr(ixo^s)
3559 ! b3
3560 if(.not.stagger_grid) then
3561 tmp(ixo^s)=(wct(ixo^s,mom(1))*wct(ixo^s,mag(3)) &
3562 -wct(ixo^s,mom(3))*wct(ixo^s,mag(1)))*invrho(ixo^s) {^nooned &
3563 -(wct(ixo^s,mom(3))*wct(ixo^s,mag(2)) &
3564 -wct(ixo^s,mom(2))*wct(ixo^s,mag(3)))*dcos(x(ixo^s,2)) &
3565 *invrho(ixo^s)/dsin(x(ixo^s,2)) }
3566 if (b0field) then
3567 tmp(ixo^s)=tmp(ixo^s)+(wct(ixo^s,mom(1))*block%B0(ixo^s,3,0) &
3568 -wct(ixo^s,mom(3))*block%B0(ixo^s,1,0))*invrho(ixo^s){^nooned &
3569 -(wct(ixo^s,mom(3))*block%B0(ixo^s,2,0) &
3570 -wct(ixo^s,mom(2))*block%B0(ixo^s,3,0))*dcos(x(ixo^s,2)) &
3571 *invrho(ixo^s)/dsin(x(ixo^s,2)) }
3572 end if
3573 w(ixo^s,mag(3))=w(ixo^s,mag(3))+tmp(ixo^s)*invr(ixo^s)
3574 end if
3575 end if
3576 end select
3577 end subroutine rmhd_add_source_geom_split
3578
3579 !> Compute 2 times total magnetic energy
3580 function rmhd_mag_en_all(w, ixI^L, ixO^L) result(mge)
3582 integer, intent(in) :: ixi^l, ixo^l
3583 double precision, intent(in) :: w(ixi^s, nw)
3584 double precision :: mge(ixo^s)
3585
3586 if (b0field) then
3587 mge = sum((w(ixo^s, mag(:))+block%B0(ixo^s,:,b0i))**2, dim=ndim+1)
3588 else
3589 mge = sum(w(ixo^s, mag(:))**2, dim=ndim+1)
3590 end if
3591 end function rmhd_mag_en_all
3592
3593 subroutine rmhd_modify_wlr(ixI^L,ixO^L,qt,wLC,wRC,wLp,wRp,s,idir)
3595 use mod_usr_methods
3596 integer, intent(in) :: ixi^l, ixo^l, idir
3597 double precision, intent(in) :: qt
3598 double precision, intent(inout) :: wlc(ixi^s,1:nw), wrc(ixi^s,1:nw)
3599 double precision, intent(inout) :: wlp(ixi^s,1:nw), wrp(ixi^s,1:nw)
3600 type(state) :: s
3601 double precision :: db(ixo^s), dpsi(ixo^s)
3602 integer :: ix^d
3603
3604 if(stagger_grid) then
3605 {do ix^db=ixomin^db,ixomax^db\}
3606 wlc(ix^d,mag(idir))=s%ws(ix^d,idir)
3607 wrc(ix^d,mag(idir))=s%ws(ix^d,idir)
3608 wlp(ix^d,mag(idir))=s%ws(ix^d,idir)
3609 wrp(ix^d,mag(idir))=s%ws(ix^d,idir)
3610 {end do\}
3611 else
3612 ! Solve the Riemann problem for the linear 2x2 system for normal
3613 ! B-field and GLM_Psi according to Dedner 2002:
3614 ! This implements eq. (42) in Dedner et al. 2002 JcP 175
3615 ! Gives the Riemann solution on the interface
3616 ! for the normal B component and Psi in the GLM-MHD system.
3617 ! 23/04/2013 Oliver Porth
3618 {do ix^db=ixomin^db,ixomax^db\}
3619 db(ix^d)=wrp(ix^d,mag(idir))-wlp(ix^d,mag(idir))
3620 dpsi(ix^d)=wrp(ix^d,psi_)-wlp(ix^d,psi_)
3621 wlp(ix^d,mag(idir))=half*(wrp(ix^d,mag(idir))+wlp(ix^d,mag(idir))-dpsi(ix^d)/cmax_global)
3622 wlp(ix^d,psi_)=half*(wrp(ix^d,psi_)+wlp(ix^d,psi_)-db(ix^d)*cmax_global)
3623 wrp(ix^d,mag(idir))=wlp(ix^d,mag(idir))
3624 wrp(ix^d,psi_)=wlp(ix^d,psi_)
3625 if(total_energy) then
3626 wrc(ix^d,e_)=wrc(ix^d,e_)-half*wrc(ix^d,mag(idir))**2
3627 wlc(ix^d,e_)=wlc(ix^d,e_)-half*wlc(ix^d,mag(idir))**2
3628 end if
3629 wrc(ix^d,mag(idir))=wlp(ix^d,mag(idir))
3630 wrc(ix^d,psi_)=wlp(ix^d,psi_)
3631 wlc(ix^d,mag(idir))=wlp(ix^d,mag(idir))
3632 wlc(ix^d,psi_)=wlp(ix^d,psi_)
3633 ! modify total energy according to the change of magnetic field
3634 if(total_energy) then
3635 wrc(ix^d,e_)=wrc(ix^d,e_)+half*wrc(ix^d,mag(idir))**2
3636 wlc(ix^d,e_)=wlc(ix^d,e_)+half*wlc(ix^d,mag(idir))**2
3637 end if
3638 {end do\}
3639 end if
3640 if(associated(usr_set_wlr)) call usr_set_wlr(ixi^l,ixo^l,qt,wlc,wrc,wlp,wrp,s,idir)
3641 end subroutine rmhd_modify_wlr
3642
3643 subroutine rmhd_boundary_adjust(igrid,psb)
3645 integer, intent(in) :: igrid
3646 type(state), target :: psb(max_blocks)
3647 integer :: ib, idims, iside, ixo^l, i^d
3648
3649 block=>ps(igrid)
3650 ^d&dxlevel(^d)=rnode(rpdx^d_,igrid);
3651 do idims=1,ndim
3652 ! to avoid using as yet unknown corner info in more than 1D, we
3653 ! fill only interior mesh ranges of the ghost cell ranges at first,
3654 ! and progressively enlarge the ranges to include corners later
3655 do iside=1,2
3656 i^d=kr(^d,idims)*(2*iside-3);
3657 if (neighbor_type(i^d,igrid)/=1) cycle
3658 ib=(idims-1)*2+iside
3659 if(.not.boundary_divbfix(ib)) cycle
3660 if(any(typeboundary(:,ib)==bc_special)) then
3661 ! MF nonlinear force-free B field extrapolation and data driven
3662 ! require normal B of the first ghost cell layer to be untouched by
3663 ! fixdivB=0 process, set boundary_divbfix_skip(iB)=1 in par file
3664 select case (idims)
3665 {case (^d)
3666 if (iside==2) then
3667 ! maximal boundary
3668 ixomin^dd=ixghi^d+1-nghostcells+boundary_divbfix_skip(2*^d)^d%ixOmin^dd=ixglo^dd;
3669 ixomax^dd=ixghi^dd;
3670 else
3671 ! minimal boundary
3672 ixomin^dd=ixglo^dd;
3673 ixomax^dd=ixglo^d-1+nghostcells-boundary_divbfix_skip(2*^d-1)^d%ixOmax^dd=ixghi^dd;
3674 end if \}
3675 end select
3676 call fixdivb_boundary(ixg^ll,ixo^l,psb(igrid)%w,psb(igrid)%x,ib)
3677 end if
3678 end do
3679 end do
3680 end subroutine rmhd_boundary_adjust
3681
3682 subroutine fixdivb_boundary(ixG^L,ixO^L,w,x,iB)
3684 integer, intent(in) :: ixg^l,ixo^l,ib
3685 double precision, intent(inout) :: w(ixg^s,1:nw)
3686 double precision, intent(in) :: x(ixg^s,1:ndim)
3687 double precision :: dx1x2,dx1x3,dx2x1,dx2x3,dx3x1,dx3x2
3688 integer :: ix^d,ixf^l
3689
3690 select case(ib)
3691 case(1)
3692 ! 2nd order CD for divB=0 to set normal B component better
3693 if(total_energy) call rmhd_to_primitive(ixg^l,ixo^l,w,x)
3694 {^iftwod
3695 ixfmin1=ixomin1+1
3696 ixfmax1=ixomax1+1
3697 ixfmin2=ixomin2+1
3698 ixfmax2=ixomax2-1
3699 if(slab_uniform) then
3700 dx1x2=dxlevel(1)/dxlevel(2)
3701 do ix1=ixfmax1,ixfmin1,-1
3702 w(ix1-1,ixfmin2:ixfmax2,mag(1))=w(ix1+1,ixfmin2:ixfmax2,mag(1)) &
3703 +dx1x2*(w(ix1,ixfmin2+1:ixfmax2+1,mag(2))-&
3704 w(ix1,ixfmin2-1:ixfmax2-1,mag(2)))
3705 enddo
3706 else
3707 do ix1=ixfmax1,ixfmin1,-1
3708 w(ix1-1,ixfmin2:ixfmax2,mag(1))=( (w(ix1+1,ixfmin2:ixfmax2,mag(1))+&
3709 w(ix1,ixfmin2:ixfmax2,mag(1)))*block%surfaceC(ix1,ixfmin2:ixfmax2,1)&
3710 +(w(ix1,ixfmin2+1:ixfmax2+1,mag(2))+w(ix1,ixfmin2:ixfmax2,mag(2)))*&
3711 block%surfaceC(ix1,ixfmin2:ixfmax2,2)&
3712 -(w(ix1,ixfmin2:ixfmax2,mag(2))+w(ix1,ixfmin2-1:ixfmax2-1,mag(2)))*&
3713 block%surfaceC(ix1,ixfmin2-1:ixfmax2-1,2) )&
3714 /block%surfaceC(ix1-1,ixfmin2:ixfmax2,1)-w(ix1,ixfmin2:ixfmax2,mag(1))
3715 end do
3716 end if
3717 }
3718 {^ifthreed
3719 ixfmin1=ixomin1+1
3720 ixfmax1=ixomax1+1
3721 ixfmin2=ixomin2+1
3722 ixfmax2=ixomax2-1
3723 ixfmin3=ixomin3+1
3724 ixfmax3=ixomax3-1
3725 if(slab_uniform) then
3726 dx1x2=dxlevel(1)/dxlevel(2)
3727 dx1x3=dxlevel(1)/dxlevel(3)
3728 do ix1=ixfmax1,ixfmin1,-1
3729 w(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))=&
3730 w(ix1+1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1)) &
3731 +dx1x2*(w(ix1,ixfmin2+1:ixfmax2+1,ixfmin3:ixfmax3,mag(2))-&
3732 w(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,mag(2))) &
3733 +dx1x3*(w(ix1,ixfmin2:ixfmax2,ixfmin3+1:ixfmax3+1,mag(3))-&
3734 w(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,mag(3)))
3735 end do
3736 else
3737 do ix1=ixfmax1,ixfmin1,-1
3738 w(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))=&
3739 ( (w(ix1+1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))+&
3740 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1)))*&
3741 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,1)&
3742 +(w(ix1,ixfmin2+1:ixfmax2+1,ixfmin3:ixfmax3,mag(2))+&
3743 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(2)))*&
3744 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,2)&
3745 -(w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(2))+&
3746 w(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,mag(2)))*&
3747 block%surfaceC(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,2)&
3748 +(w(ix1,ixfmin2:ixfmax2,ixfmin3+1:ixfmax3+1,mag(3))+&
3749 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(3)))*&
3750 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,3)&
3751 -(w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(3))+&
3752 w(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,mag(3)))*&
3753 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,3) )&
3754 /block%surfaceC(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,1)-&
3755 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))
3756 end do
3757 end if
3758 }
3759 if(total_energy) call rmhd_to_conserved(ixg^l,ixo^l,w,x)
3760 case(2)
3761 if(total_energy) call rmhd_to_primitive(ixg^l,ixo^l,w,x)
3762 {^iftwod
3763 ixfmin1=ixomin1-1
3764 ixfmax1=ixomax1-1
3765 ixfmin2=ixomin2+1
3766 ixfmax2=ixomax2-1
3767 if(slab_uniform) then
3768 dx1x2=dxlevel(1)/dxlevel(2)
3769 do ix1=ixfmin1,ixfmax1
3770 w(ix1+1,ixfmin2:ixfmax2,mag(1))=w(ix1-1,ixfmin2:ixfmax2,mag(1)) &
3771 -dx1x2*(w(ix1,ixfmin2+1:ixfmax2+1,mag(2))-&
3772 w(ix1,ixfmin2-1:ixfmax2-1,mag(2)))
3773 enddo
3774 else
3775 do ix1=ixfmin1,ixfmax1
3776 w(ix1+1,ixfmin2:ixfmax2,mag(1))=( (w(ix1-1,ixfmin2:ixfmax2,mag(1))+&
3777 w(ix1,ixfmin2:ixfmax2,mag(1)))*block%surfaceC(ix1-1,ixfmin2:ixfmax2,1)&
3778 -(w(ix1,ixfmin2+1:ixfmax2+1,mag(2))+w(ix1,ixfmin2:ixfmax2,mag(2)))*&
3779 block%surfaceC(ix1,ixfmin2:ixfmax2,2)&
3780 +(w(ix1,ixfmin2:ixfmax2,mag(2))+w(ix1,ixfmin2-1:ixfmax2-1,mag(2)))*&
3781 block%surfaceC(ix1,ixfmin2-1:ixfmax2-1,2) )&
3782 /block%surfaceC(ix1,ixfmin2:ixfmax2,1)-w(ix1,ixfmin2:ixfmax2,mag(1))
3783 end do
3784 end if
3785 }
3786 {^ifthreed
3787 ixfmin1=ixomin1-1
3788 ixfmax1=ixomax1-1
3789 ixfmin2=ixomin2+1
3790 ixfmax2=ixomax2-1
3791 ixfmin3=ixomin3+1
3792 ixfmax3=ixomax3-1
3793 if(slab_uniform) then
3794 dx1x2=dxlevel(1)/dxlevel(2)
3795 dx1x3=dxlevel(1)/dxlevel(3)
3796 do ix1=ixfmin1,ixfmax1
3797 w(ix1+1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))=&
3798 w(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1)) &
3799 -dx1x2*(w(ix1,ixfmin2+1:ixfmax2+1,ixfmin3:ixfmax3,mag(2))-&
3800 w(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,mag(2))) &
3801 -dx1x3*(w(ix1,ixfmin2:ixfmax2,ixfmin3+1:ixfmax3+1,mag(3))-&
3802 w(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,mag(3)))
3803 end do
3804 else
3805 do ix1=ixfmin1,ixfmax1
3806 w(ix1+1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))=&
3807 ( (w(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))+&
3808 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1)))*&
3809 block%surfaceC(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,1)&
3810 -(w(ix1,ixfmin2+1:ixfmax2+1,ixfmin3:ixfmax3,mag(2))+&
3811 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(2)))*&
3812 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,2)&
3813 +(w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(2))+&
3814 w(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,mag(2)))*&
3815 block%surfaceC(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,2)&
3816 -(w(ix1,ixfmin2:ixfmax2,ixfmin3+1:ixfmax3+1,mag(3))+&
3817 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(3)))*&
3818 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,3)&
3819 +(w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(3))+&
3820 w(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,mag(3)))*&
3821 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,3) )&
3822 /block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,1)-&
3823 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))
3824 end do
3825 end if
3826 }
3827 if(total_energy) call rmhd_to_conserved(ixg^l,ixo^l,w,x)
3828 case(3)
3829 if(total_energy) call rmhd_to_primitive(ixg^l,ixo^l,w,x)
3830 {^iftwod
3831 ixfmin1=ixomin1+1
3832 ixfmax1=ixomax1-1
3833 ixfmin2=ixomin2+1
3834 ixfmax2=ixomax2+1
3835 if(slab_uniform) then
3836 dx2x1=dxlevel(2)/dxlevel(1)
3837 do ix2=ixfmax2,ixfmin2,-1
3838 w(ixfmin1:ixfmax1,ix2-1,mag(2))=w(ixfmin1:ixfmax1,ix2+1,mag(2)) &
3839 +dx2x1*(w(ixfmin1+1:ixfmax1+1,ix2,mag(1))-&
3840 w(ixfmin1-1:ixfmax1-1,ix2,mag(1)))
3841 enddo
3842 else
3843 do ix2=ixfmax2,ixfmin2,-1
3844 w(ixfmin1:ixfmax1,ix2-1,mag(2))=( (w(ixfmin1:ixfmax1,ix2+1,mag(2))+&
3845 w(ixfmin1:ixfmax1,ix2,mag(2)))*block%surfaceC(ixfmin1:ixfmax1,ix2,2)&
3846 +(w(ixfmin1+1:ixfmax1+1,ix2,mag(1))+w(ixfmin1:ixfmax1,ix2,mag(1)))*&
3847 block%surfaceC(ixfmin1:ixfmax1,ix2,1)&
3848 -(w(ixfmin1:ixfmax1,ix2,mag(1))+w(ixfmin1-1:ixfmax1-1,ix2,mag(1)))*&
3849 block%surfaceC(ixfmin1-1:ixfmax1-1,ix2,1) )&
3850 /block%surfaceC(ixfmin1:ixfmax1,ix2-1,2)-w(ixfmin1:ixfmax1,ix2,mag(2))
3851 end do
3852 end if
3853 }
3854 {^ifthreed
3855 ixfmin1=ixomin1+1
3856 ixfmax1=ixomax1-1
3857 ixfmin3=ixomin3+1
3858 ixfmax3=ixomax3-1
3859 ixfmin2=ixomin2+1
3860 ixfmax2=ixomax2+1
3861 if(slab_uniform) then
3862 dx2x1=dxlevel(2)/dxlevel(1)
3863 dx2x3=dxlevel(2)/dxlevel(3)
3864 do ix2=ixfmax2,ixfmin2,-1
3865 w(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,mag(2))=w(ixfmin1:ixfmax1,&
3866 ix2+1,ixfmin3:ixfmax3,mag(2)) &
3867 +dx2x1*(w(ixfmin1+1:ixfmax1+1,ix2,ixfmin3:ixfmax3,mag(1))-&
3868 w(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,mag(1))) &
3869 +dx2x3*(w(ixfmin1:ixfmax1,ix2,ixfmin3+1:ixfmax3+1,mag(3))-&
3870 w(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,mag(3)))
3871 end do
3872 else
3873 do ix2=ixfmax2,ixfmin2,-1
3874 w(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,mag(2))=&
3875 ( (w(ixfmin1:ixfmax1,ix2+1,ixfmin3:ixfmax3,mag(2))+&
3876 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(2)))*&
3877 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,2)&
3878 +(w(ixfmin1+1:ixfmax1+1,ix2,ixfmin3:ixfmax3,mag(1))+&
3879 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(1)))*&
3880 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,1)&
3881 -(w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(1))+&
3882 w(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,mag(1)))*&
3883 block%surfaceC(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,1)&
3884 +(w(ixfmin1:ixfmax1,ix2,ixfmin3+1:ixfmax3+1,mag(3))+&
3885 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(3)))*&
3886 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,3)&
3887 -(w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(3))+&
3888 w(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,mag(3)))*&
3889 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,3) )&
3890 /block%surfaceC(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,2)-&
3891 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(2))
3892 end do
3893 end if
3894 }
3895 if(total_energy) call rmhd_to_conserved(ixg^l,ixo^l,w,x)
3896 case(4)
3897 if(total_energy) call rmhd_to_primitive(ixg^l,ixo^l,w,x)
3898 {^iftwod
3899 ixfmin1=ixomin1+1
3900 ixfmax1=ixomax1-1
3901 ixfmin2=ixomin2-1
3902 ixfmax2=ixomax2-1
3903 if(slab_uniform) then
3904 dx2x1=dxlevel(2)/dxlevel(1)
3905 do ix2=ixfmin2,ixfmax2
3906 w(ixfmin1:ixfmax1,ix2+1,mag(2))=w(ixfmin1:ixfmax1,ix2-1,mag(2)) &
3907 -dx2x1*(w(ixfmin1+1:ixfmax1+1,ix2,mag(1))-&
3908 w(ixfmin1-1:ixfmax1-1,ix2,mag(1)))
3909 end do
3910 else
3911 do ix2=ixfmin2,ixfmax2
3912 w(ixfmin1:ixfmax1,ix2+1,mag(2))=( (w(ixfmin1:ixfmax1,ix2-1,mag(2))+&
3913 w(ixfmin1:ixfmax1,ix2,mag(2)))*block%surfaceC(ixfmin1:ixfmax1,ix2-1,2)&
3914 -(w(ixfmin1+1:ixfmax1+1,ix2,mag(1))+w(ixfmin1:ixfmax1,ix2,mag(1)))*&
3915 block%surfaceC(ixfmin1:ixfmax1,ix2,1)&
3916 +(w(ixfmin1:ixfmax1,ix2,mag(1))+w(ixfmin1-1:ixfmax1-1,ix2,mag(1)))*&
3917 block%surfaceC(ixfmin1-1:ixfmax1-1,ix2,1) )&
3918 /block%surfaceC(ixfmin1:ixfmax1,ix2,2)-w(ixfmin1:ixfmax1,ix2,mag(2))
3919 end do
3920 end if
3921 }
3922 {^ifthreed
3923 ixfmin1=ixomin1+1
3924 ixfmax1=ixomax1-1
3925 ixfmin3=ixomin3+1
3926 ixfmax3=ixomax3-1
3927 ixfmin2=ixomin2-1
3928 ixfmax2=ixomax2-1
3929 if(slab_uniform) then
3930 dx2x1=dxlevel(2)/dxlevel(1)
3931 dx2x3=dxlevel(2)/dxlevel(3)
3932 do ix2=ixfmin2,ixfmax2
3933 w(ixfmin1:ixfmax1,ix2+1,ixfmin3:ixfmax3,mag(2))=w(ixfmin1:ixfmax1,&
3934 ix2-1,ixfmin3:ixfmax3,mag(2)) &
3935 -dx2x1*(w(ixfmin1+1:ixfmax1+1,ix2,ixfmin3:ixfmax3,mag(1))-&
3936 w(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,mag(1))) &
3937 -dx2x3*(w(ixfmin1:ixfmax1,ix2,ixfmin3+1:ixfmax3+1,mag(3))-&
3938 w(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,mag(3)))
3939 end do
3940 else
3941 do ix2=ixfmin2,ixfmax2
3942 w(ixfmin1:ixfmax1,ix2+1,ixfmin3:ixfmax3,mag(2))=&
3943 ( (w(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,mag(2))+&
3944 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(2)))*&
3945 block%surfaceC(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,2)&
3946 -(w(ixfmin1+1:ixfmax1+1,ix2,ixfmin3:ixfmax3,mag(1))+&
3947 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(1)))*&
3948 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,1)&
3949 +(w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(1))+&
3950 w(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,mag(1)))*&
3951 block%surfaceC(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,1)&
3952 -(w(ixfmin1:ixfmax1,ix2,ixfmin3+1:ixfmax3+1,mag(3))+&
3953 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(3)))*&
3954 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,3)&
3955 +(w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(3))+&
3956 w(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,mag(3)))*&
3957 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,3) )&
3958 /block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,2)-&
3959 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(2))
3960 end do
3961 end if
3962 }
3963 if(total_energy) call rmhd_to_conserved(ixg^l,ixo^l,w,x)
3964 {^ifthreed
3965 case(5)
3966 if(total_energy) call rmhd_to_primitive(ixg^l,ixo^l,w,x)
3967 ixfmin1=ixomin1+1
3968 ixfmax1=ixomax1-1
3969 ixfmin2=ixomin2+1
3970 ixfmax2=ixomax2-1
3971 ixfmin3=ixomin3+1
3972 ixfmax3=ixomax3+1
3973 if(slab_uniform) then
3974 dx3x1=dxlevel(3)/dxlevel(1)
3975 dx3x2=dxlevel(3)/dxlevel(2)
3976 do ix3=ixfmax3,ixfmin3,-1
3977 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,mag(3))=w(ixfmin1:ixfmax1,&
3978 ixfmin2:ixfmax2,ix3+1,mag(3)) &
3979 +dx3x1*(w(ixfmin1+1:ixfmax1+1,ixfmin2:ixfmax2,ix3,mag(1))-&
3980 w(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,mag(1))) &
3981 +dx3x2*(w(ixfmin1:ixfmax1,ixfmin2+1:ixfmax2+1,ix3,mag(2))-&
3982 w(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,mag(2)))
3983 end do
3984 else
3985 do ix3=ixfmax3,ixfmin3,-1
3986 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,mag(3))=&
3987 ( (w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3+1,mag(3))+&
3988 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(3)))*&
3989 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,3)&
3990 +(w(ixfmin1+1:ixfmax1+1,ixfmin2:ixfmax2,ix3,mag(1))+&
3991 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(1)))*&
3992 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,1)&
3993 -(w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(1))+&
3994 w(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,mag(1)))*&
3995 block%surfaceC(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,1)&
3996 +(w(ixfmin1:ixfmax1,ixfmin2+1:ixfmax2+1,ix3,mag(2))+&
3997 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(2)))*&
3998 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,2)&
3999 -(w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(2))+&
4000 w(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,mag(2)))*&
4001 block%surfaceC(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,2) )&
4002 /block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,3)-&
4003 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(3))
4004 end do
4005 end if
4006 if(total_energy) call rmhd_to_conserved(ixg^l,ixo^l,w,x)
4007 case(6)
4008 if(total_energy) call rmhd_to_primitive(ixg^l,ixo^l,w,x)
4009 ixfmin1=ixomin1+1
4010 ixfmax1=ixomax1-1
4011 ixfmin2=ixomin2+1
4012 ixfmax2=ixomax2-1
4013 ixfmin3=ixomin3-1
4014 ixfmax3=ixomax3-1
4015 if(slab_uniform) then
4016 dx3x1=dxlevel(3)/dxlevel(1)
4017 dx3x2=dxlevel(3)/dxlevel(2)
4018 do ix3=ixfmin3,ixfmax3
4019 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3+1,mag(3))=w(ixfmin1:ixfmax1,&
4020 ixfmin2:ixfmax2,ix3-1,mag(3)) &
4021 -dx3x1*(w(ixfmin1+1:ixfmax1+1,ixfmin2:ixfmax2,ix3,mag(1))-&
4022 w(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,mag(1))) &
4023 -dx3x2*(w(ixfmin1:ixfmax1,ixfmin2+1:ixfmax2+1,ix3,mag(2))-&
4024 w(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,mag(2)))
4025 end do
4026 else
4027 do ix3=ixfmin3,ixfmax3
4028 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3+1,mag(3))=&
4029 ( (w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,mag(3))+&
4030 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(3)))*&
4031 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,3)&
4032 -(w(ixfmin1+1:ixfmax1+1,ixfmin2:ixfmax2,ix3,mag(1))+&
4033 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(1)))*&
4034 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,1)&
4035 +(w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(1))+&
4036 w(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,mag(1)))*&
4037 block%surfaceC(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,1)&
4038 -(w(ixfmin1:ixfmax1,ixfmin2+1:ixfmax2+1,ix3,mag(2))+&
4039 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(2)))*&
4040 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,2)&
4041 +(w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(2))+&
4042 w(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,mag(2)))*&
4043 block%surfaceC(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,2) )&
4044 /block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,3)-&
4045 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(3))
4046 end do
4047 end if
4048 if(total_energy) call rmhd_to_conserved(ixg^l,ixo^l,w,x)
4049 }
4050 case default
4051 call mpistop("Special boundary is not defined for this region")
4052 end select
4053 end subroutine fixdivb_boundary
4054
4055 {^nooned
4056 subroutine rmhd_clean_divb_multigrid(qdt, qt, active)
4057 use mod_forest
4060 use mod_geometry
4061 double precision, intent(in) :: qdt !< Current time step
4062 double precision, intent(in) :: qt !< Current time
4063 logical, intent(inout) :: active !< Output if the source is active
4064 integer :: id
4065 integer, parameter :: max_its = 50
4066 double precision :: residual_it(max_its), max_divb
4067 double precision :: tmp(ixg^t), grad(ixg^t, ndim)
4068 double precision :: res
4069 double precision, parameter :: max_residual = 1d-3
4070 double precision, parameter :: residual_reduction = 1d-10
4071 integer :: iigrid, igrid
4072 integer :: n, nc, lvl, ix^l, ixc^l, idim
4073 type(tree_node), pointer :: pnode
4074
4075 mg%operator_type = mg_laplacian
4076 ! Set boundary conditions
4077 do n = 1, 2*ndim
4078 idim = (n+1)/2
4079 select case (typeboundary(mag(idim), n))
4080 case (bc_symm)
4081 ! d/dx B = 0, take phi = 0
4082 mg%bc(n, mg_iphi)%bc_type = mg_bc_dirichlet
4083 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
4084 case (bc_asymm)
4085 ! B = 0, so grad(phi) = 0
4086 mg%bc(n, mg_iphi)%bc_type = mg_bc_neumann
4087 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
4088 case (bc_cont)
4089 mg%bc(n, mg_iphi)%bc_type = mg_bc_dirichlet
4090 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
4091 case (bc_special)
4092 ! Assume Dirichlet boundary conditions, derivative zero
4093 mg%bc(n, mg_iphi)%bc_type = mg_bc_dirichlet
4094 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
4095 case (bc_periodic)
4096 ! Nothing to do here
4097 case default
4098 write(*,*) "rmhd_clean_divb_multigrid warning: unknown boundary type"
4099 mg%bc(n, mg_iphi)%bc_type = mg_bc_dirichlet
4100 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
4101 end select
4102 end do
4103
4104 ix^l=ixm^ll^ladd1;
4105 max_divb = 0.0d0
4106 ! Store divergence of B as right-hand side
4107 do iigrid = 1, igridstail
4108 igrid = igrids(iigrid);
4109 pnode => igrid_to_node(igrid, mype)%node
4110 id = pnode%id
4111 lvl = mg%boxes(id)%lvl
4112 nc = mg%box_size_lvl(lvl)
4113
4114 ! Geometry subroutines expect this to be set
4115 block => ps(igrid)
4116 ^d&dxlevel(^d)=rnode(rpdx^d_,igrid);
4117
4118 call get_divb(ps(igrid)%w(ixg^t, 1:nw), ixg^ll, ixm^ll, tmp, &
4120 mg%boxes(id)%cc({1:nc}, mg_irhs) = tmp(ixm^t)
4121 max_divb = max(max_divb, maxval(abs(tmp(ixm^t))))
4122 end do
4123
4124 ! Solve laplacian(phi) = divB
4125 if(stagger_grid) then
4126 call mpi_allreduce(mpi_in_place, max_divb, 1, mpi_double_precision, &
4127 mpi_max, icomm, ierrmpi)
4128
4129 if (mype == 0) print *, "Performing multigrid divB cleaning"
4130 if (mype == 0) print *, "iteration vs residual"
4131 ! Solve laplacian(phi) = divB
4132 do n = 1, max_its
4133 call mg_fas_fmg(mg, n>1, max_res=residual_it(n))
4134 if (mype == 0) write(*, "(I4,E11.3)") n, residual_it(n)
4135 if (residual_it(n) < residual_reduction * max_divb) exit
4136 end do
4137 if (mype == 0 .and. n > max_its) then
4138 print *, "divb_multigrid warning: not fully converged"
4139 print *, "current amplitude of divb: ", residual_it(max_its)
4140 print *, "multigrid smallest grid: ", &
4141 mg%domain_size_lvl(:, mg%lowest_lvl)
4142 print *, "note: smallest grid ideally has <= 8 cells"
4143 print *, "multigrid dx/dy/dz ratio: ", mg%dr(:, 1)/mg%dr(1, 1)
4144 print *, "note: dx/dy/dz should be similar"
4145 end if
4146 else
4147 do n = 1, max_its
4148 call mg_fas_vcycle(mg, max_res=res)
4149 if (res < max_residual) exit
4150 end do
4151 if (res > max_residual) call mpistop("divb_multigrid: no convergence")
4152 end if
4153
4154 ! Correct the magnetic field
4155 do iigrid = 1, igridstail
4156 igrid = igrids(iigrid);
4157 pnode => igrid_to_node(igrid, mype)%node
4158 id = pnode%id
4159 ! Geometry subroutines expect this to be set
4160 block => ps(igrid)
4161 ^d&dxlevel(^d)=rnode(rpdx^d_,igrid);
4162 ! Compute the gradient of phi
4163 tmp(ix^s) = mg%boxes(id)%cc({:,}, mg_iphi)
4164 if(stagger_grid) then
4165 do idim =1, ndim
4166 ixcmin^d=ixmlo^d-kr(idim,^d);
4167 ixcmax^d=ixmhi^d;
4168 call gradientf(tmp,ps(igrid)%x,ixg^ll,ixc^l,idim,grad(ixg^t,idim))
4169 ! Apply the correction B* = B - gradient(phi)
4170 ps(igrid)%ws(ixc^s,idim)=ps(igrid)%ws(ixc^s,idim)-grad(ixc^s,idim)
4171 end do
4172 ! store cell-center magnetic energy
4173 tmp(ixm^t) = sum(ps(igrid)%w(ixm^t, mag(1:ndim))**2, dim=ndim+1)
4174 ! change cell-center magnetic field
4175 call rmhd_face_to_center(ixm^ll,ps(igrid))
4176 else
4177 do idim = 1, ndim
4178 call gradient(tmp,ixg^ll,ixm^ll,idim,grad(ixg^t, idim))
4179 end do
4180 ! store cell-center magnetic energy
4181 tmp(ixm^t) = sum(ps(igrid)%w(ixm^t, mag(1:ndim))**2, dim=ndim+1)
4182 ! Apply the correction B* = B - gradient(phi)
4183 ps(igrid)%w(ixm^t, mag(1:ndim)) = &
4184 ps(igrid)%w(ixm^t, mag(1:ndim)) - grad(ixm^t, :)
4185 end if
4186 if(total_energy) then
4187 ! Determine magnetic energy difference
4188 tmp(ixm^t) = 0.5_dp * (sum(ps(igrid)%w(ixm^t, &
4189 mag(1:ndim))**2, dim=ndim+1) - tmp(ixm^t))
4190 ! Keep thermal pressure the same
4191 ps(igrid)%w(ixm^t, e_) = ps(igrid)%w(ixm^t, e_) + tmp(ixm^t)
4192 end if
4193 end do
4194 active = .true.
4195 end subroutine rmhd_clean_divb_multigrid
4196 }
4197
4198 subroutine rmhd_update_faces(ixI^L,ixO^L,qt,qdt,wprim,fC,fE,sCT,s,vcts)
4200 integer, intent(in) :: ixi^l, ixo^l
4201 double precision, intent(in) :: qt,qdt
4202 ! cell-center primitive variables
4203 double precision, intent(in) :: wprim(ixi^s,1:nw)
4204 type(state) :: sct, s
4205 type(ct_velocity) :: vcts
4206 double precision, intent(in) :: fc(ixi^s,1:nwflux,1:ndim)
4207 double precision, intent(inout) :: fe(ixi^s,sdim:3)
4208
4209 select case(type_ct)
4210 case('average')
4211 call update_faces_average(ixi^l,ixo^l,qt,qdt,fc,fe,sct,s)
4212 case('uct_contact')
4213 call update_faces_contact(ixi^l,ixo^l,qt,qdt,wprim,fc,fe,sct,s,vcts)
4214 case('uct_hll')
4215 call update_faces_hll(ixi^l,ixo^l,qt,qdt,fe,sct,s,vcts)
4216 case default
4217 call mpistop('choose average, uct_contact,or uct_hll for type_ct!')
4218 end select
4219 end subroutine rmhd_update_faces
4220
4221 !> get electric field though averaging neighors to update faces in CT
4222 subroutine update_faces_average(ixI^L,ixO^L,qt,qdt,fC,fE,sCT,s)
4224 use mod_usr_methods
4225 integer, intent(in) :: ixi^l, ixo^l
4226 double precision, intent(in) :: qt, qdt
4227 type(state) :: sct, s
4228 double precision, intent(in) :: fc(ixi^s,1:nwflux,1:ndim)
4229 double precision, intent(inout) :: fe(ixi^s,sdim:3)
4230 double precision :: circ(ixi^s,1:ndim)
4231 ! non-ideal electric field on cell edges
4232 double precision, dimension(ixI^S,sdim:3) :: e_resi
4233 integer :: ix^d,ixc^l,ixa^l,i1kr^d,i2kr^d
4234 integer :: idim1,idim2,idir,iwdim1,iwdim2
4235
4236 associate(bfaces=>s%ws,x=>s%x)
4237 ! Calculate contribution to FEM of each edge,
4238 ! that is, estimate value of line integral of
4239 ! electric field in the positive idir direction.
4240 ! if there is resistivity, get eta J
4241 if(rmhd_eta/=zero) call get_resistive_electric_field(ixi^l,ixo^l,sct,s,e_resi)
4242 do idim1=1,ndim
4243 iwdim1 = mag(idim1)
4244 i1kr^d=kr(idim1,^d);
4245 do idim2=1,ndim
4246 iwdim2 = mag(idim2)
4247 i2kr^d=kr(idim2,^d);
4248 do idir=sdim,3! Direction of line integral
4249 ! Allow only even permutations
4250 if (lvc(idim1,idim2,idir)==1) then
4251 ixcmax^d=ixomax^d;
4252 ixcmin^d=ixomin^d+kr(idir,^d)-1;
4253 ! average cell-face electric field to cell edges
4254 {do ix^db=ixcmin^db,ixcmax^db\}
4255 fe(ix^d,idir)=quarter*&
4256 (fc(ix^d,iwdim1,idim2)+fc({ix^d+i1kr^d},iwdim1,idim2)&
4257 -fc(ix^d,iwdim2,idim1)-fc({ix^d+i2kr^d},iwdim2,idim1))
4258 ! add resistive electric field at cell edges E=-vxB+eta J
4259 if(rmhd_eta/=zero) fe(ix^d,idir)=fe(ix^d,idir)+e_resi(ix^d,idir)
4260 ! times time step and edge length
4261 fe(ix^d,idir)=fe(ix^d,idir)*qdt*s%dsC(ix^d,idir)
4262 {end do\}
4263 end if
4264 end do
4265 end do
4266 end do
4267 ! allow user to change inductive electric field, especially for boundary driven applications
4268 if(associated(usr_set_electric_field)) &
4269 call usr_set_electric_field(ixi^l,ixo^l,qt,qdt,fe,sct)
4270 circ(ixi^s,1:ndim)=zero
4271 ! Calculate circulation on each face
4272 do idim1=1,ndim ! Coordinate perpendicular to face
4273 ixcmax^d=ixomax^d;
4274 ixcmin^d=ixomin^d-kr(idim1,^d);
4275 do idim2=1,ndim
4276 ixa^l=ixc^l-kr(idim2,^d);
4277 do idir=sdim,3 ! Direction of line integral
4278 ! Assemble indices
4279 if(lvc(idim1,idim2,idir)==1) then
4280 ! Add line integrals in direction idir
4281 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
4282 +(fe(ixc^s,idir)&
4283 -fe(ixa^s,idir))
4284 else if(lvc(idim1,idim2,idir)==-1) then
4285 ! Add line integrals in direction idir
4286 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
4287 -(fe(ixc^s,idir)&
4288 -fe(ixa^s,idir))
4289 end if
4290 end do
4291 end do
4292 ! Divide by the area of the face to get dB/dt
4293 where(s%surfaceC(ixc^s,idim1) > 1.0d-9*s%dvolume(ixc^s))
4294 circ(ixc^s,idim1)=circ(ixc^s,idim1)/s%surfaceC(ixc^s,idim1)
4295 elsewhere
4296 circ(ixc^s,idim1)=zero
4297 end where
4298 ! Time update cell-face magnetic field component
4299 bfaces(ixc^s,idim1)=bfaces(ixc^s,idim1)-circ(ixc^s,idim1)
4300 end do
4301 end associate
4302 end subroutine update_faces_average
4303
4304 !> update faces using UCT contact mode by Gardiner and Stone 2005 JCP 205, 509
4305 subroutine update_faces_contact(ixI^L,ixO^L,qt,qdt,wp,fC,fE,sCT,s,vcts)
4307 use mod_usr_methods
4308 use mod_geometry
4309 integer, intent(in) :: ixi^l, ixo^l
4310 double precision, intent(in) :: qt, qdt
4311 ! cell-center primitive variables
4312 double precision, intent(in) :: wp(ixi^s,1:nw)
4313 type(state) :: sct, s
4314 type(ct_velocity) :: vcts
4315 double precision, intent(in) :: fc(ixi^s,1:nwflux,1:ndim)
4316 double precision, intent(inout) :: fe(ixi^s,sdim:3)
4317 double precision :: circ(ixi^s,1:ndim)
4318 ! electric field at cell centers
4319 double precision :: ecc(ixi^s,sdim:3)
4320 double precision :: ein(ixi^s,sdim:3)
4321 ! gradient of E at left and right side of a cell face
4322 double precision :: el(ixi^s),er(ixi^s)
4323 ! gradient of E at left and right side of a cell corner
4324 double precision :: elc,erc
4325 ! non-ideal electric field on cell edges
4326 double precision, dimension(ixI^S,sdim:3) :: e_resi
4327 ! current on cell edges
4328 double precision :: jce(ixi^s,sdim:3)
4329 ! location at cell faces
4330 double precision :: xs(ixgs^t,1:ndim)
4331 double precision :: gradi(ixgs^t)
4332 integer :: ixc^l,ixa^l
4333 integer :: idim1,idim2,idir,iwdim1,iwdim2,ix^d,i1kr^d,i2kr^d
4334
4335 associate(bfaces=>s%ws,x=>s%x,w=>s%w,vnorm=>vcts%vnorm,wcts=>sct%ws)
4336 ! if there is resistivity, get eta J
4337 if(rmhd_eta/=zero) call get_resistive_electric_field(ixi^l,ixo^l,sct,s,e_resi)
4338 if(b0field) then
4339 {do ix^db=iximin^db,iximax^db\}
4340 ! Calculate electric field at cell centers
4341 {^ifthreed
4342 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_)
4343 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_)
4344 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_)
4345 }
4346 {^iftwod
4347 ecc(ix^d,3)=wp(ix^d,b1_)*wp(ix^d,m2_)-wp(ix^d,b2_)*wp(ix^d,m1_)
4348 }
4349 {^ifoned
4350 ecc(ix^d,3)=0.d0
4351 }
4352 {end do\}
4353 else
4354 {do ix^db=iximin^db,iximax^db\}
4355 ! Calculate electric field at cell centers
4356 {^ifthreed
4357 ecc(ix^d,1)=wp(ix^d,b2_)*wp(ix^d,m3_)-wp(ix^d,b3_)*wp(ix^d,m2_)
4358 ecc(ix^d,2)=wp(ix^d,b3_)*wp(ix^d,m1_)-wp(ix^d,b1_)*wp(ix^d,m3_)
4359 ecc(ix^d,3)=wp(ix^d,b1_)*wp(ix^d,m2_)-wp(ix^d,b2_)*wp(ix^d,m1_)
4360 }
4361 {^iftwod
4362 ecc(ix^d,3)=wp(ix^d,b1_)*wp(ix^d,m2_)-wp(ix^d,b2_)*wp(ix^d,m1_)
4363 }
4364 {^ifoned
4365 ecc(ix^d,3)=0.d0
4366 }
4367 {end do\}
4368 end if
4369
4370 ! Calculate contribution to FEM of each edge,
4371 ! that is, estimate value of line integral of
4372 ! electric field in the positive idir direction.
4373 ! evaluate electric field along cell edges according to equation (41)
4374 do idim1=1,ndim
4375 iwdim1 = mag(idim1)
4376 i1kr^d=kr(idim1,^d);
4377 do idim2=1,ndim
4378 iwdim2 = mag(idim2)
4379 i2kr^d=kr(idim2,^d);
4380 do idir=sdim,3 ! Direction of line integral
4381 ! Allow only even permutations
4382 if (lvc(idim1,idim2,idir)==1) then
4383 ixcmax^d=ixomax^d;
4384 ixcmin^d=ixomin^d+kr(idir,^d)-1;
4385 ! Assemble indices
4386 ! average cell-face electric field to cell edges
4387 {do ix^db=ixcmin^db,ixcmax^db\}
4388 fe(ix^d,idir)=quarter*&
4389 (fc(ix^d,iwdim1,idim2)+fc({ix^d+i1kr^d},iwdim1,idim2)&
4390 -fc(ix^d,iwdim2,idim1)-fc({ix^d+i2kr^d},iwdim2,idim1))
4391 if(partial_energy) ein(ix^d,idir)=fe(ix^d,idir)
4392 {end do\}
4393 ! add slope in idim2 direction from equation (50)
4394 ixamin^d=ixcmin^d;
4395 ixamax^d=ixcmax^d+i1kr^d;
4396 {do ix^db=ixamin^db,ixamax^db\}
4397 el(ix^d)=fc(ix^d,iwdim1,idim2)-ecc(ix^d,idir)
4398 er(ix^d)=fc(ix^d,iwdim1,idim2)-ecc({ix^d+i2kr^d},idir)
4399 {end do\}
4400 {!dir$ ivdep
4401 do ix^db=ixcmin^db,ixcmax^db\}
4402 if(vnorm(ix^d,idim1)>0.d0) then
4403 elc=el(ix^d)
4404 else if(vnorm(ix^d,idim1)<0.d0) then
4405 elc=el({ix^d+i1kr^d})
4406 else
4407 elc=0.5d0*(el(ix^d)+el({ix^d+i1kr^d}))
4408 end if
4409 if(vnorm({ix^d+i2kr^d},idim1)>0.d0) then
4410 erc=er(ix^d)
4411 else if(vnorm({ix^d+i2kr^d},idim1)<0.d0) then
4412 erc=er({ix^d+i1kr^d})
4413 else
4414 erc=0.5d0*(er(ix^d)+er({ix^d+i1kr^d}))
4415 end if
4416 fe(ix^d,idir)=fe(ix^d,idir)+0.25d0*(elc+erc)
4417 {end do\}
4418 ! add slope in idim1 direction from equation (50)
4419 ixamin^d=ixcmin^d;
4420 ixamax^d=ixcmax^d+i2kr^d;
4421 {do ix^db=ixamin^db,ixamax^db\}
4422 el(ix^d)=-fc(ix^d,iwdim2,idim1)-ecc(ix^d,idir)
4423 er(ix^d)=-fc(ix^d,iwdim2,idim1)-ecc({ix^d+i1kr^d},idir)
4424 {end do\}
4425 {!dir$ ivdep
4426 do ix^db=ixcmin^db,ixcmax^db\}
4427 if(vnorm(ix^d,idim2)>0.d0) then
4428 elc=el(ix^d)
4429 else if(vnorm(ix^d,idim2)<0.d0) then
4430 elc=el({ix^d+i2kr^d})
4431 else
4432 elc=0.5d0*(el(ix^d)+el({ix^d+i2kr^d}))
4433 end if
4434 if(vnorm({ix^d+i1kr^d},idim2)>0.d0) then
4435 erc=er(ix^d)
4436 else if(vnorm({ix^d+i1kr^d},idim2)<0.d0) then
4437 erc=er({ix^d+i2kr^d})
4438 else
4439 erc=0.5d0*(er(ix^d)+er({ix^d+i2kr^d}))
4440 end if
4441 fe(ix^d,idir)=fe(ix^d,idir)+0.25d0*(elc+erc)
4442 ! difference between average and upwind interpolated E
4443 if(partial_energy) ein(ix^d,idir)=fe(ix^d,idir)-ein(ix^d,idir)
4444 ! add resistive electric field at cell edges E=-vxB+eta J
4445 if(rmhd_eta/=zero) fe(ix^d,idir)=fe(ix^d,idir)+e_resi(ix^d,idir)
4446 ! times time step and edge length
4447 fe(ix^d,idir)=fe(ix^d,idir)*qdt*s%dsC(ix^d,idir)
4448 {end do\}
4449 end if
4450 end do
4451 end do
4452 end do
4453 if(partial_energy) then
4454 ! add upwind diffused magnetic energy back to energy
4455 ! calculate current density at cell edges
4456 jce=0.d0
4457 do idim1=1,ndim
4458 do idim2=1,ndim
4459 do idir=sdim,3
4460 if (lvc(idim1,idim2,idir)==0) cycle
4461 ixcmax^d=ixomax^d;
4462 ixcmin^d=ixomin^d+kr(idir,^d)-1;
4463 ixamax^d=ixcmax^d-kr(idir,^d)+1;
4464 ixamin^d=ixcmin^d;
4465 ! current at transverse faces
4466 xs(ixa^s,:)=x(ixa^s,:)
4467 xs(ixa^s,idim2)=x(ixa^s,idim2)+half*s%dx(ixa^s,idim2)
4468 call gradientf(wcts(ixgs^t,idim2),xs,ixgs^ll,ixc^l,idim1,gradi)
4469 if (lvc(idim1,idim2,idir)==1) then
4470 jce(ixc^s,idir)=jce(ixc^s,idir)+gradi(ixc^s)
4471 else
4472 jce(ixc^s,idir)=jce(ixc^s,idir)-gradi(ixc^s)
4473 end if
4474 end do
4475 end do
4476 end do
4477 do idir=sdim,3
4478 ixcmax^d=ixomax^d;
4479 ixcmin^d=ixomin^d+kr(idir,^d)-1;
4480 ! E dot J on cell edges
4481 ein(ixc^s,idir)=ein(ixc^s,idir)*jce(ixc^s,idir)
4482 ! average from cell edge to cell center
4483 {^ifthreed
4484 if(idir==1) then
4485 {do ix^db=ixomin^db,ixomax^db\}
4486 jce(ix^d,idir)=0.25d0*(ein(ix^d,idir)+ein(ix1,ix2-1,ix3,idir)+ein(ix1,ix2,ix3-1,idir)&
4487 +ein(ix1,ix2-1,ix3-1,idir))
4488 if(jce(ix^d,idir)<0.d0) jce(ix^d,idir)=0.d0
4489 w(ix^d,e_)=w(ix^d,e_)+qdt*jce(ix^d,idir)
4490 {end do\}
4491 else if(idir==2) then
4492 {do ix^db=ixomin^db,ixomax^db\}
4493 jce(ix^d,idir)=0.25d0*(ein(ix^d,idir)+ein(ix1-1,ix2,ix3,idir)+ein(ix1,ix2,ix3-1,idir)&
4494 +ein(ix1-1,ix2,ix3-1,idir))
4495 if(jce(ix^d,idir)<0.d0) jce(ix^d,idir)=0.d0
4496 w(ix^d,e_)=w(ix^d,e_)+qdt*jce(ix^d,idir)
4497 {end do\}
4498 else
4499 {do ix^db=ixomin^db,ixomax^db\}
4500 jce(ix^d,idir)=0.25d0*(ein(ix^d,idir)+ein(ix1-1,ix2,ix3,idir)+ein(ix1,ix2-1,ix3,idir)&
4501 +ein(ix1-1,ix2-1,ix3,idir))
4502 if(jce(ix^d,idir)<0.d0) jce(ix^d,idir)=0.d0
4503 w(ix^d,e_)=w(ix^d,e_)+qdt*jce(ix^d,idir)
4504 {end do\}
4505 end if
4506 }
4507 {^iftwod
4508 !idir=3
4509 {do ix^db=ixomin^db,ixomax^db\}
4510 jce(ix^d,idir)=0.25d0*(ein(ix^d,idir)+ein(ix1-1,ix2,idir)+ein(ix1,ix2-1,idir)&
4511 +ein(ix1-1,ix2-1,idir))
4512 if(jce(ix^d,idir)<0.d0) jce(ix^d,idir)=0.d0
4513 w(ix^d,e_)=w(ix^d,e_)+qdt*jce(ix^d,idir)
4514 {end do\}
4515 }
4516 ! save additional numerical resistive heating to an extra variable
4517 if(nwextra>0) then
4518 block%w(ixo^s,nw)=block%w(ixo^s,nw)+jce(ixo^s,idir)
4519 end if
4520 end do
4521 end if
4522 ! allow user to change inductive electric field, especially for boundary driven applications
4523 if(associated(usr_set_electric_field)) &
4524 call usr_set_electric_field(ixi^l,ixo^l,qt,qdt,fe,sct)
4525 circ(ixi^s,1:ndim)=zero
4526 ! Calculate circulation on each face
4527 do idim1=1,ndim ! Coordinate perpendicular to face
4528 ixcmax^d=ixomax^d;
4529 ixcmin^d=ixomin^d-kr(idim1,^d);
4530 do idim2=1,ndim
4531 ixa^l=ixc^l-kr(idim2,^d);
4532 do idir=sdim,3 ! Direction of line integral
4533 ! Assemble indices
4534 if(lvc(idim1,idim2,idir)==1) then
4535 ! Add line integrals in direction idir
4536 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
4537 +(fe(ixc^s,idir)&
4538 -fe(ixa^s,idir))
4539 else if(lvc(idim1,idim2,idir)==-1) then
4540 ! Add line integrals in direction idir
4541 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
4542 -(fe(ixc^s,idir)&
4543 -fe(ixa^s,idir))
4544 end if
4545 end do
4546 end do
4547 ! Divide by the area of the face to get dB/dt
4548 where(s%surfaceC(ixc^s,idim1) > smalldouble)
4549 circ(ixc^s,idim1)=circ(ixc^s,idim1)/s%surfaceC(ixc^s,idim1)
4550 elsewhere
4551 circ(ixc^s,idim1)=zero
4552 end where
4553 ! Time update cell-face magnetic field component
4554 bfaces(ixc^s,idim1)=bfaces(ixc^s,idim1)-circ(ixc^s,idim1)
4555 end do
4556 end associate
4557 end subroutine update_faces_contact
4558
4559 !> update faces
4560 subroutine update_faces_hll(ixI^L,ixO^L,qt,qdt,fE,sCT,s,vcts)
4563 use mod_usr_methods
4564 integer, intent(in) :: ixi^l, ixo^l
4565 double precision, intent(in) :: qt, qdt
4566 double precision, intent(inout) :: fe(ixi^s,sdim:3)
4567 type(state) :: sct, s
4568 type(ct_velocity) :: vcts
4569 double precision :: vtill(ixi^s,2)
4570 double precision :: vtilr(ixi^s,2)
4571 double precision :: bfacetot(ixi^s,ndim)
4572 double precision :: btill(ixi^s,ndim)
4573 double precision :: btilr(ixi^s,ndim)
4574 double precision :: cp(ixi^s,2)
4575 double precision :: cm(ixi^s,2)
4576 double precision :: circ(ixi^s,1:ndim)
4577 ! non-ideal electric field on cell edges
4578 double precision, dimension(ixI^S,sdim:3) :: e_resi
4579 integer :: hxc^l,ixc^l,ixcp^l,jxc^l,ixcm^l
4580 integer :: idim1,idim2,idir
4581
4582 associate(bfaces=>s%ws,bfacesct=>sct%ws,x=>s%x,vbarc=>vcts%vbarC,cbarmin=>vcts%cbarmin,&
4583 cbarmax=>vcts%cbarmax)
4584 ! Calculate contribution to FEM of each edge,
4585 ! that is, estimate value of line integral of
4586 ! electric field in the positive idir direction.
4587
4588 ! Loop over components of electric field
4589
4590 ! idir: electric field component we need to calculate
4591 ! idim1: directions in which we already performed the reconstruction
4592 ! idim2: directions in which we perform the reconstruction
4593
4594 ! if there is resistivity, get eta J
4595 if(rmhd_eta/=zero) call get_resistive_electric_field(ixi^l,ixo^l,sct,s,e_resi)
4596
4597 do idir=sdim,3
4598 ! Indices
4599 ! idir: electric field component
4600 ! idim1: one surface
4601 ! idim2: the other surface
4602 ! cyclic permutation: idim1,idim2,idir=1,2,3
4603 ! Velocity components on the surface
4604 ! follow cyclic premutations:
4605 ! Sx(1),Sx(2)=y,z ; Sy(1),Sy(2)=z,x ; Sz(1),Sz(2)=x,y
4606 ixcmax^d=ixomax^d;
4607 ixcmin^d=ixomin^d-1+kr(idir,^d);
4608 ! Set indices and directions
4609 idim1=mod(idir,3)+1
4610 idim2=mod(idir+1,3)+1
4611 jxc^l=ixc^l+kr(idim1,^d);
4612 ixcp^l=ixc^l+kr(idim2,^d);
4613 ! Reconstruct transverse transport velocities
4614 call reconstruct(ixi^l,ixc^l,idim2,vbarc(ixi^s,idim1,1),&
4615 vtill(ixi^s,2),vtilr(ixi^s,2))
4616 call reconstruct(ixi^l,ixc^l,idim1,vbarc(ixi^s,idim2,2),&
4617 vtill(ixi^s,1),vtilr(ixi^s,1))
4618 ! Reconstruct magnetic fields
4619 ! Eventhough the arrays are larger, reconstruct works with
4620 ! the limits ixG.
4621 if(b0field) then
4622 bfacetot(ixi^s,idim1)=bfacesct(ixi^s,idim1)+block%B0(ixi^s,idim1,idim1)
4623 bfacetot(ixi^s,idim2)=bfacesct(ixi^s,idim2)+block%B0(ixi^s,idim2,idim2)
4624 else
4625 bfacetot(ixi^s,idim1)=bfacesct(ixi^s,idim1)
4626 bfacetot(ixi^s,idim2)=bfacesct(ixi^s,idim2)
4627 end if
4628 call reconstruct(ixi^l,ixc^l,idim2,bfacetot(ixi^s,idim1),&
4629 btill(ixi^s,idim1),btilr(ixi^s,idim1))
4630 call reconstruct(ixi^l,ixc^l,idim1,bfacetot(ixi^s,idim2),&
4631 btill(ixi^s,idim2),btilr(ixi^s,idim2))
4632 ! Take the maximum characteristic
4633 cm(ixc^s,1)=max(cbarmin(ixcp^s,idim1),cbarmin(ixc^s,idim1))
4634 cp(ixc^s,1)=max(cbarmax(ixcp^s,idim1),cbarmax(ixc^s,idim1))
4635 cm(ixc^s,2)=max(cbarmin(jxc^s,idim2),cbarmin(ixc^s,idim2))
4636 cp(ixc^s,2)=max(cbarmax(jxc^s,idim2),cbarmax(ixc^s,idim2))
4637 ! Calculate eletric field
4638 fe(ixc^s,idir)=-(cp(ixc^s,1)*vtill(ixc^s,1)*btill(ixc^s,idim2) &
4639 + cm(ixc^s,1)*vtilr(ixc^s,1)*btilr(ixc^s,idim2) &
4640 - cp(ixc^s,1)*cm(ixc^s,1)*(btilr(ixc^s,idim2)-btill(ixc^s,idim2)))&
4641 /(cp(ixc^s,1)+cm(ixc^s,1)) &
4642 +(cp(ixc^s,2)*vtill(ixc^s,2)*btill(ixc^s,idim1) &
4643 + cm(ixc^s,2)*vtilr(ixc^s,2)*btilr(ixc^s,idim1) &
4644 - cp(ixc^s,2)*cm(ixc^s,2)*(btilr(ixc^s,idim1)-btill(ixc^s,idim1)))&
4645 /(cp(ixc^s,2)+cm(ixc^s,2))
4646 ! add resistive electric field at cell edges E=-vxB+eta J
4647 if(rmhd_eta/=zero) fe(ixc^s,idir)=fe(ixc^s,idir)+e_resi(ixc^s,idir)
4648 fe(ixc^s,idir)=qdt*s%dsC(ixc^s,idir)*fe(ixc^s,idir)
4649 if (.not.slab) then
4650 where(abs(x(ixc^s,r_)+half*dxlevel(r_)).lt.1.0d-9)
4651 fe(ixc^s,idir)=zero
4652 end where
4653 end if
4654 end do
4655 ! allow user to change inductive electric field, especially for boundary driven applications
4656 if(associated(usr_set_electric_field)) &
4657 call usr_set_electric_field(ixi^l,ixo^l,qt,qdt,fe,sct)
4658 circ(ixi^s,1:ndim)=zero
4659 ! Calculate circulation on each face: interal(fE dot dl)
4660 do idim1=1,ndim ! Coordinate perpendicular to face
4661 ixcmax^d=ixomax^d;
4662 ixcmin^d=ixomin^d-kr(idim1,^d);
4663 do idim2=1,ndim
4664 do idir=sdim,3 ! Direction of line integral
4665 ! Assemble indices
4666 if(lvc(idim1,idim2,idir)/=0) then
4667 hxc^l=ixc^l-kr(idim2,^d);
4668 ! Add line integrals in direction idir
4669 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
4670 +lvc(idim1,idim2,idir)&
4671 *(fe(ixc^s,idir)&
4672 -fe(hxc^s,idir))
4673 end if
4674 end do
4675 end do
4676 ! Divide by the area of the face to get dB/dt
4677 where(s%surfaceC(ixc^s,idim1) > 1.0d-9*s%dvolume(ixc^s))
4678 circ(ixc^s,idim1)=circ(ixc^s,idim1)/s%surfaceC(ixc^s,idim1)
4679 elsewhere
4680 circ(ixc^s,idim1)=zero
4681 end where
4682 ! Time update cell-face magnetic field component
4683 bfaces(ixc^s,idim1)=bfaces(ixc^s,idim1)-circ(ixc^s,idim1)
4684 end do
4685 end associate
4686 end subroutine update_faces_hll
4687
4688 !> calculate eta J at cell edges
4689 subroutine get_resistive_electric_field(ixI^L,ixO^L,sCT,s,jce)
4691 use mod_usr_methods
4692 use mod_geometry
4693 integer, intent(in) :: ixi^l, ixo^l
4694 type(state), intent(in) :: sct, s
4695 ! current on cell edges
4696 double precision :: jce(ixi^s,sdim:3)
4697 ! current on cell centers
4698 double precision :: jcc(ixi^s,7-2*ndir:3)
4699 ! location at cell faces
4700 double precision :: xs(ixgs^t,1:ndim)
4701 ! resistivity
4702 double precision :: eta(ixi^s)
4703 double precision :: gradi(ixgs^t)
4704 integer :: ix^d,ixc^l,ixa^l,ixb^l,idir,idirmin,idim1,idim2
4705
4706 associate(x=>s%x,dx=>s%dx,w=>s%w,wct=>sct%w,wcts=>sct%ws)
4707 ! calculate current density at cell edges
4708 jce=0.d0
4709 do idim1=1,ndim
4710 do idim2=1,ndim
4711 do idir=sdim,3
4712 if (lvc(idim1,idim2,idir)==0) cycle
4713 ixcmax^d=ixomax^d;
4714 ixcmin^d=ixomin^d+kr(idir,^d)-1;
4715 ixbmax^d=ixcmax^d-kr(idir,^d)+1;
4716 ixbmin^d=ixcmin^d;
4717 ! current at transverse faces
4718 xs(ixb^s,:)=x(ixb^s,:)
4719 xs(ixb^s,idim2)=x(ixb^s,idim2)+half*dx(ixb^s,idim2)
4720 call gradientf(wcts(ixgs^t,idim2),xs,ixgs^ll,ixc^l,idim1,gradi,2)
4721 if (lvc(idim1,idim2,idir)==1) then
4722 jce(ixc^s,idir)=jce(ixc^s,idir)+gradi(ixc^s)
4723 else
4724 jce(ixc^s,idir)=jce(ixc^s,idir)-gradi(ixc^s)
4725 end if
4726 end do
4727 end do
4728 end do
4729 ! get resistivity
4730 if(rmhd_eta>zero)then
4731 jce(ixi^s,:)=jce(ixi^s,:)*rmhd_eta
4732 else
4733 ixa^l=ixo^l^ladd1;
4734 call get_current(wct,ixi^l,ixa^l,idirmin,jcc)
4735 call usr_special_resistivity(wct,ixi^l,ixa^l,idirmin,x,jcc,eta)
4736 ! calcuate eta on cell edges
4737 do idir=sdim,3
4738 ixcmax^d=ixomax^d;
4739 ixcmin^d=ixomin^d+kr(idir,^d)-1;
4740 jcc(ixc^s,idir)=0.d0
4741 {do ix^db=0,1\}
4742 if({ ix^d==1 .and. ^d==idir | .or.}) cycle
4743 ixamin^d=ixcmin^d+ix^d;
4744 ixamax^d=ixcmax^d+ix^d;
4745 jcc(ixc^s,idir)=jcc(ixc^s,idir)+eta(ixa^s)
4746 {end do\}
4747 jcc(ixc^s,idir)=jcc(ixc^s,idir)*0.25d0
4748 jce(ixc^s,idir)=jce(ixc^s,idir)*jcc(ixc^s,idir)
4749 end do
4750 end if
4751 end associate
4752 end subroutine get_resistive_electric_field
4753
4754 !> calculate cell-center values from face-center values
4755 subroutine rmhd_face_to_center(ixO^L,s)
4757 ! Non-staggered interpolation range
4758 integer, intent(in) :: ixo^l
4759 type(state) :: s
4760 integer :: ix^d
4761
4762 ! calculate cell-center values from face-center values in 2nd order
4763 ! because the staggered arrays have an additional place to the left.
4764 ! Interpolate to cell barycentre using arithmetic average
4765 ! This might be done better later, to make the method less diffusive.
4766 {!dir$ ivdep
4767 do ix^db=ixomin^db,ixomax^db\}
4768 {^ifthreed
4769 s%w(ix^d,b1_)=half/s%surface(ix^d,1)*(s%ws(ix^d,1)*s%surfaceC(ix^d,1)&
4770 +s%ws(ix1-1,ix2,ix3,1)*s%surfaceC(ix1-1,ix2,ix3,1))
4771 s%w(ix^d,b2_)=half/s%surface(ix^d,2)*(s%ws(ix^d,2)*s%surfaceC(ix^d,2)&
4772 +s%ws(ix1,ix2-1,ix3,2)*s%surfaceC(ix1,ix2-1,ix3,2))
4773 s%w(ix^d,b3_)=half/s%surface(ix^d,3)*(s%ws(ix^d,3)*s%surfaceC(ix^d,3)&
4774 +s%ws(ix1,ix2,ix3-1,3)*s%surfaceC(ix1,ix2,ix3-1,3))
4775 }
4776 {^iftwod
4777 s%w(ix^d,b1_)=half/s%surface(ix^d,1)*(s%ws(ix^d,1)*s%surfaceC(ix^d,1)&
4778 +s%ws(ix1-1,ix2,1)*s%surfaceC(ix1-1,ix2,1))
4779 s%w(ix^d,b2_)=half/s%surface(ix^d,2)*(s%ws(ix^d,2)*s%surfaceC(ix^d,2)&
4780 +s%ws(ix1,ix2-1,2)*s%surfaceC(ix1,ix2-1,2))
4781 }
4782 {end do\}
4783 ! calculate cell-center values from face-center values in 4th order
4784 !do idim=1,ndim
4785 ! gxO^L=ixO^L-2*kr(idim,^D);
4786 ! hxO^L=ixO^L-kr(idim,^D);
4787 ! jxO^L=ixO^L+kr(idim,^D);
4788
4789 ! ! Interpolate to cell barycentre using fourth order central formula
4790 ! w(ixO^S,mag(idim))=(0.0625d0/s%surface(ixO^S,idim))*&
4791 ! ( -ws(gxO^S,idim)*s%surfaceC(gxO^S,idim) &
4792 ! +9.0d0*ws(hxO^S,idim)*s%surfaceC(hxO^S,idim) &
4793 ! +9.0d0*ws(ixO^S,idim)*s%surfaceC(ixO^S,idim) &
4794 ! -ws(jxO^S,idim)*s%surfaceC(jxO^S,idim) )
4795 !end do
4796
4797 ! calculate cell-center values from face-center values in 6th order
4798 !do idim=1,ndim
4799 ! fxO^L=ixO^L-3*kr(idim,^D);
4800 ! gxO^L=ixO^L-2*kr(idim,^D);
4801 ! hxO^L=ixO^L-kr(idim,^D);
4802 ! jxO^L=ixO^L+kr(idim,^D);
4803 ! kxO^L=ixO^L+2*kr(idim,^D);
4804
4805 ! ! Interpolate to cell barycentre using sixth order central formula
4806 ! w(ixO^S,mag(idim))=(0.00390625d0/s%surface(ixO^S,idim))* &
4807 ! ( +3.0d0*ws(fxO^S,idim)*s%surfaceC(fxO^S,idim) &
4808 ! -25.0d0*ws(gxO^S,idim)*s%surfaceC(gxO^S,idim) &
4809 ! +150.0d0*ws(hxO^S,idim)*s%surfaceC(hxO^S,idim) &
4810 ! +150.0d0*ws(ixO^S,idim)*s%surfaceC(ixO^S,idim) &
4811 ! -25.0d0*ws(jxO^S,idim)*s%surfaceC(jxO^S,idim) &
4812 ! +3.0d0*ws(kxO^S,idim)*s%surfaceC(kxO^S,idim) )
4813 !end do
4814 end subroutine rmhd_face_to_center
4815
4816 !> calculate magnetic field from vector potential
4817 subroutine b_from_vector_potential(ixIs^L, ixI^L, ixO^L, ws, x)
4820 integer, intent(in) :: ixis^l, ixi^l, ixo^l
4821 double precision, intent(inout) :: ws(ixis^s,1:nws)
4822 double precision, intent(in) :: x(ixi^s,1:ndim)
4823 double precision :: adummy(ixis^s,1:3)
4824
4825 call b_from_vector_potentiala(ixis^l, ixi^l, ixo^l, ws, x, adummy)
4826 end subroutine b_from_vector_potential
4827
4828 subroutine rfactor_from_temperature_ionization(w,x,ixI^L,ixO^L,Rfactor)
4831 integer, intent(in) :: ixi^l, ixo^l
4832 double precision, intent(in) :: w(ixi^s,1:nw)
4833 double precision, intent(in) :: x(ixi^s,1:ndim)
4834 double precision, intent(out):: rfactor(ixi^s)
4835 double precision :: iz_h(ixo^s),iz_he(ixo^s)
4836
4837 call ionization_degree_from_temperature(ixi^l,ixo^l,w(ixi^s,te_),iz_h,iz_he)
4838 ! assume the first and second ionization of Helium have the same degree
4839 rfactor(ixo^s)=(1.d0+iz_h(ixo^s)+0.1d0*(1.d0+iz_he(ixo^s)*(1.d0+iz_he(ixo^s))))/(2.d0+3.d0*he_abundance)
4840 end subroutine rfactor_from_temperature_ionization
4841
4842 subroutine rfactor_from_constant_ionization(w,x,ixI^L,ixO^L,Rfactor)
4844 integer, intent(in) :: ixi^l, ixo^l
4845 double precision, intent(in) :: w(ixi^s,1:nw)
4846 double precision, intent(in) :: x(ixi^s,1:ndim)
4847 double precision, intent(out):: rfactor(ixi^s)
4848
4849 rfactor(ixo^s)=rr
4850 end subroutine rfactor_from_constant_ionization
4851end module mod_rmhd_phys
Module for including anisotropic flux limited diffusion (AFLD)-approximation in Radiation-hydrodynami...
Definition mod_afld.t:8
subroutine afld_get_diffcoef_central(w, wct, x, ixil, ixol)
Calculates cell-centered diffusion coefficient to be used in multigrid.
Definition mod_afld.t:684
subroutine, public get_afld_rad_force(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 This subroutine handles th...
Definition mod_afld.t:141
subroutine, public afld_init(he_abundance, rhd_radiation_diffusion, afld_gamma)
Initialising FLD-module: Read opacities Initialise Multigrid adimensionalise kappa Add extra variable...
Definition mod_afld.t:93
subroutine, public afld_radforce_get_dt(w, ixil, ixol, dtnew, dxd, x)
Definition mod_afld.t:217
subroutine, public afld_get_radpress(w, x, ixil, ixol, rad_pressure, nth)
Calculate Radiation Pressure Returns Radiation Pressure as tensor.
Definition mod_afld.t:518
subroutine, public get_afld_energy_interact(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 This subroutine handles th...
Definition mod_afld.t:244
Module to include CAK radiation line force in (magneto)hydrodynamic models Computes both the force fr...
subroutine cak_get_dt(w, ixil, ixol, dtnew, dxd, x)
Check time step for total radiation contribution.
subroutine cak_init(phys_gamma)
Initialize the module.
subroutine cak_add_source(qdt, ixil, ixol, wct, w, x, energy, qsourcesplit, active)
w[iw]=w[iw]+qdt*S[wCT,qtC,x] where S is the source based on wCT within ixO
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
Module for physical and numeric constants.
double precision, parameter const_rad_a
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
Module for flux conservation near refinement boundaries.
Nicolas Moens Module for including flux limited diffusion (FLD)-approximation in Radiation-hydrodynam...
Definition mod_fld.t:9
subroutine, public get_fld_rad_force(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 This subroutine handles th...
Definition mod_fld.t:146
subroutine, public fld_get_radpress(w, x, ixil, ixol, rad_pressure, nth)
Calculate Radiation Pressure Returns Radiation Pressure as tensor.
Definition mod_fld.t:456
subroutine, public fld_init(he_abundance, radiation_diffusion, energy_interact, r_gamma)
Initialising FLD-module: Read opacities Initialise Multigrid adimensionalise kappa Add extra variable...
Definition mod_fld.t:94
subroutine fld_get_diffcoef_central(w, wct, x, ixil, ixol)
Calculates cell-centered diffusion coefficient to be used in multigrid.
Definition mod_fld.t:719
character(len=8) fld_diff_scheme
Which method to solve diffusion part.
Definition mod_fld.t:35
subroutine, public fld_radforce_get_dt(w, ixil, ixol, dtnew, dxd, x)
Definition mod_fld.t:181
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:32
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 spherical
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.
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, parameter bc_noinflow
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.
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 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.
double precision, dimension(:), allocatable, parameter d
logical local_timestep
each cell has its own timestep or not
double precision dt
global time step
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.
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.
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
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
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(w, ixil, ixol, dtnew, dxd, x)
Definition mod_gravity.t:87
subroutine gravity_init()
Initialize the module.
Definition mod_gravity.t:26
subroutine gravity_add_source(qdt, ixil, ixol, wct, wctprim, w, x, energy, rhov, 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 ionization degree - get ionization degree for given temperature
subroutine ionization_degree_from_temperature(ixil, ixol, te, iz_h, iz_he)
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
Radiation-magneto-hydrodynamics module.
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)...
integer, public, protected rmhd_trac_type
Which TRAC method is used.
character(len=8), public rmhd_pressure
In the case of no rmhd_energy, how to compute pressure.
subroutine, public rmhd_phys_init()
logical, public, protected rmhd_thermal_conduction
Whether thermal conduction is used.
double precision, public rmhd_gamma
The adiabatic index.
character(len=8), public rmhd_radiation_formalism
Formalism to treat radiation.
logical, public divbwave
Add divB wave in Roe solver.
integer, public, protected rmhd_trac_finegrid
Distance between two adjacent traced magnetic field lines (in finest cell size)
double precision, public, protected h_ion_fr
Ionization fraction of H H_ion_fr = H+/(H+ + H)
integer, dimension(2 *^nd), public, protected boundary_divbfix_skip
To skip * layer of ghost cells during divB=0 fix for boundary.
logical, public, protected rmhd_hyperbolic_thermal_conduction
Whether thermal conduction is used.
double precision, public, protected rr
logical, public rmhd_equi_thermal
double precision, public rmhd_etah
Hall resistivity.
logical, public, protected rmhd_radiation_diffusion
Treat radiation energy diffusion.
subroutine, public rmhd_face_to_center(ixol, s)
calculate cell-center values from face-center values
procedure(sub_get_pthermal), pointer, public rmhd_get_pthermal
integer, public, protected psi_
Indices of the GLM psi.
type(tc_fluid), allocatable, public tc_fl
type of fluid for thermal conduction
integer, public, protected c
Indices of the momentum density for the form of better vectorization.
subroutine, public b_from_vector_potential(ixisl, ixil, ixol, ws, x)
calculate magnetic field from vector potential
logical, public, protected rmhd_cak_force
Whether CAK radiation line force is activated.
logical, public, protected rmhd_radiation_force
Treat radiation fld_Rad_force.
logical, public, protected rmhd_glm
Whether GLM-MHD is used to control div B.
double precision, public, protected small_r_e
The smallest allowed radiation energy.
double precision, public rmhd_eta_hyper
The MHD hyper-resistivity.
logical, public clean_initial_divb
clean initial divB
integer, public, protected rmhd_n_tracer
Number of tracer species.
logical, public, protected rmhd_particles
Whether particles module is added.
integer, public, protected b
double precision, public kbmpmua4
kb/(m_p mu)* 1/a_rad**4,
integer, public, protected m
subroutine, public rmhd_get_trad(w, x, ixil, ixol, trad)
Calculates radiation temperature.
integer, public equi_rho0_
equi vars indices in the stateequi_vars array
double precision, public rmhd_adiab
The adiabatic constant.
subroutine, public rmhd_get_pthermal_plus_pradiation(w, x, ixil, ixol, pth_plus_prad)
Calculates the sum of the gas pressure and the max Prad tensor element.
integer, public, protected q_
Index of the heat flux q.
integer, public, protected tweight_
logical, public has_equi_rho0
whether split off equilibrium density
double precision, public rmhd_eta
The MHD resistivity.
integer, public, protected p_
Index of the gas pressure (-1 if not present) should equal e_.
subroutine, public get_normalized_divb(w, ixil, ixol, divb)
get dimensionless div B = |divB| * volume / area / |B|
integer, public, protected rho_
Index of the density (in the w array)
integer, public, protected c_
double precision function, dimension(ixo^s), public rmhd_mag_en_all(w, ixil, ixol)
Compute 2 times total magnetic energy.
type(te_fluid), allocatable, public te_fl_rmhd
type of fluid for thermal emission synthesis
logical, public, protected rmhd_gravity
Whether gravity is added.
integer, dimension(:), allocatable, public, protected mom
Indices of the momentum density.
logical, public, protected rmhd_glm_extended
Whether extended GLM-MHD is used with additional sources.
logical, public, protected rmhd_viscosity
Whether viscosity is added.
logical, public, protected rmhd_partial_ionization
Whether plasma is partially ionized.
integer, public, protected tcoff_
Index of the cutoff temperature for the TRAC method.
integer, public, protected e_
Index of the energy density (-1 if not present)
logical, public, protected rmhd_radiation_advection
Treat radiation advection.
double precision, public, protected he_ion_fr2
Ratio of number He2+ / number He+ + He2+ He_ion_fr2 = He2+/(He2+ + He+)
subroutine, public rmhd_get_rho(w, x, ixil, ixol, rho)
integer, dimension(:), allocatable, public, protected tracer
Indices of the tracers.
logical, public, protected rmhd_energy_interact
Treat radiation-gas energy interaction.
subroutine, public rmhd_get_tgas(w, x, ixil, ixol, tgas)
Calculates gas temperature.
character(len=std_len), public, protected type_ct
Method type of constrained transport.
procedure(sub_convert), pointer, public rmhd_to_conserved
double precision, public, protected he_abundance
Helium abundance over Hydrogen.
logical, public partial_energy
Whether an internal or hydrodynamic energy equation is used.
procedure(sub_convert), pointer, public rmhd_to_primitive
logical, public, protected rmhd_dump_full_vars
whether dump full variables (when splitting is used) in a separate dat file
logical, public, protected rmhd_trac
Whether TRAC method is used.
subroutine, public rmhd_clean_divb_multigrid(qdt, qt, active)
subroutine, public rmhd_set_mg_bounds
Set the boundaries for the diffusion of E.
subroutine, public rmhd_get_v(w, x, ixil, ixol, v)
Calculate v vector.
double precision, public hypertc_kappa
The thermal conductivity kappa in hyperbolic thermal conduction.
logical, public has_equi_pe0
whether split off equilibrium thermal pressure
subroutine, public rmhd_get_pradiation(w, x, ixil, ixol, prad, nth)
Calculate radiation pressure within ixO^L.
integer, public, protected te_
Indices of temperature.
logical, public, protected source_split_divb
Whether divB cleaning sources are added splitting from fluid solver.
integer, public, protected r_e
Index of the radiation energy.
procedure(sub_get_pthermal), pointer, public rmhd_get_temperature
character(len=std_len), public, protected typedivbfix
Method type to clean divergence of B.
logical, public, protected b0field_forcefree
B0 field is force-free.
double precision, public rmhd_glm_alpha
GLM-MHD parameter: ratio of the diffusive and advective time scales for div b taking values within [0...
integer, public, protected rmhd_divb_nth
Whether divB is computed with a fourth order approximation.
subroutine, public rmhd_ei_to_e(ixil, ixol, w, x)
Transform internal energy to total energy.
integer, public equi_pe0_
double precision, public, protected he_ion_fr
Ionization fraction of He He_ion_fr = (He2+ + He+)/(He2+ + He+ + He)
logical, public, protected rmhd_energy
Whether an energy equation is used.
logical, dimension(2 *^nd), public, protected boundary_divbfix
To control divB=0 fix for boundary.
double precision, public, protected rmhd_trac_mask
Height of the mask used in the TRAC method.
logical, public, protected eq_state_units
subroutine, public rmhd_e_to_ei(ixil, ixol, w, x)
Transform total energy to internal energy.
logical, public, protected rmhd_4th_order
MHD fourth order.
Module for handling problematic values in simulations, such as negative pressures.
subroutine, public small_values_average(ixil, ixol, w, x, w_flag, windex)
logical, public trace_small_values
trace small values in the source file using traceback flag of compiler
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 1) in amrvac.par in sts_list set the following parameters which have...
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 explicut timestep for the TC (mhd implementation)
subroutine tc_init_params(phys_gamma)
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_equi_vars), pointer usr_set_equi_vars
procedure(special_mg_bc), pointer usr_special_mg_bc
procedure(set_electric_field), pointer usr_set_electric_field
The module add viscous source terms and check time step.
subroutine viscosity_add_source(qdt, ixil, ixol, wct, w, x, energy, qsourcesplit, active)
subroutine viscosity_init(phys_wider_stencil)
Initialize the module.
subroutine viscosity_get_dt(w, ixil, ixol, dtnew, dxd, x)
The data structure that contains information about a tree node/grid block.
Definition mod_forest.t:11