MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_mf_phys.t
Go to the documentation of this file.
1!> Magnetofriction module
3 use mod_global_parameters, only: std_len
6 use mod_comm_lib, only: mpistop
7
8
9 implicit none
10 private
11
12 !> viscosity coefficient in s cm^-2 for solar corona (Cheung 2012 ApJ)
13 double precision, public :: mf_nu = 1.d-15
14
15 !> maximal limit of magnetofrictional velocity in cm s^-1 (Pomoell 2019 A&A)
16 double precision, public :: mf_vmax = 3.d6
17
18 !> decay scale of frictional velocity near boundaries
19 double precision, public :: mf_decay_scale(2*^nd)=0.d0
20
21 !> GLM-MHD parameter: ratio of the diffusive and advective time scales for div b
22 !> taking values within [0, 1]
23 double precision, public :: mf_glm_alpha = 0.5d0
24
25 !> The resistivity
26 double precision, public :: mf_eta = 0.0d0
27
28 !> The hyper-resistivity
29 double precision, public :: mf_eta_hyper = 0.0d0
30
31 !> Coefficient of diffusive divB cleaning
32 double precision :: divbdiff = 0.8d0
33
34 !> Helium abundance over Hydrogen
35 double precision, public, protected :: he_abundance=0.1d0
36
37 !> Method type in a integer for good performance
38 integer :: type_divb
39
40 !> Indices of the momentum density
41 integer, allocatable, public, protected :: mom(:)
42
43 !> Indices of the momentum density for the form of better vectorization
44 integer, public, protected :: ^c&m^C_
45
46 !> Indices of the magnetic field for the form of better vectorization
47 integer, public, protected :: ^c&b^C_
48
49 !> Indices of the GLM psi
50 integer, public, protected :: psi_
51
52 !> To skip * layer of ghost cells during divB=0 fix for boundary
53 integer, public, protected :: boundary_divbfix_skip(2*^nd)=0
54
55 ! DivB cleaning methods
56 integer, parameter :: divb_none = 0
57 integer, parameter :: divb_multigrid = -1
58 integer, parameter :: divb_glm = 1
59 integer, parameter :: divb_powel = 2
60 integer, parameter :: divb_janhunen = 3
61 integer, parameter :: divb_linde = 4
62 integer, parameter :: divb_lindejanhunen = 5
63 integer, parameter :: divb_lindepowel = 6
64 integer, parameter :: divb_lindeglm = 7
65 integer, parameter :: divb_ct = 8
66
67 !> Whether particles module is added
68 logical, public, protected :: mf_particles = .false.
69
70 !> Whether GLM-MHD is used
71 logical, public, protected :: mf_glm = .false.
72
73 !> Whether divB cleaning sources are added splitting from fluid solver
74 logical, public, protected :: source_split_divb = .false.
75
76 !> set to true if need to record electric field on cell edges
77 logical, public, protected :: mf_record_electric_field = .false.
78
79 !> Whether divB is computed with a fourth order approximation
80 integer, public, protected :: mf_divb_nth = 1
81
82 !> To control divB=0 fix for boundary
83 logical, public, protected :: boundary_divbfix(2*^nd)=.true.
84
85 !> Use a compact way to add resistivity
86 logical :: compactres = .false.
87
88 !> Add divB wave in Roe solver
89 logical, public :: divbwave = .true.
90
91 !> clean divb in the initial condition
92 logical, public, protected :: clean_initial_divb=.false.
93
94 !> Method type to clean divergence of B
95 character(len=std_len), public, protected :: typedivbfix = 'ct'
96
97 !> Method type of constrained transport
98 character(len=std_len), public, protected :: type_ct = 'average'
99
100 !> Update all equations due to divB cleaning
101 character(len=std_len) :: typedivbdiff = 'all'
102
103
104 ! Public methods
105 public :: mf_phys_init
106 public :: mf_to_conserved
107 public :: mf_to_primitive
108 public :: mf_face_to_center
109 public :: get_divb
110 public :: get_current
111 public :: get_normalized_divb
114 {^nooned
116 }
117
118contains
119
120 !> Read this module's parameters from a file
121 subroutine mf_read_params(files)
123 use mod_particles, only: particles_eta
124 character(len=*), intent(in) :: files(:)
125 integer :: n
126
127 namelist /mf_list/ mf_nu, mf_vmax, mf_decay_scale, &
129 particles_eta, mf_record_electric_field,&
130 typedivbfix, source_split_divb, divbdiff,&
131 typedivbdiff, type_ct, compactres, divbwave, he_abundance, si_unit, &
134
135 do n = 1, size(files)
136 open(unitpar, file=trim(files(n)), status="old")
137 read(unitpar, mf_list, end=111)
138111 close(unitpar)
139 end do
140
141 end subroutine mf_read_params
142
143 !> Write this module's parameters to a snapsoht
144 subroutine mf_write_info(fh)
146 integer, intent(in) :: fh
147 integer, parameter :: n_par = 1
148 double precision :: values(n_par)
149 character(len=name_len) :: names(n_par)
150 integer, dimension(MPI_STATUS_SIZE) :: st
151 integer :: er
152
153 call mpi_file_write(fh, n_par, 1, mpi_integer, st, er)
154
155 names(1) = "nu"
156 values(1) = mf_nu
157 call mpi_file_write(fh, values, n_par, mpi_double_precision, st, er)
158 call mpi_file_write(fh, names, n_par * name_len, mpi_character, st, er)
159 end subroutine mf_write_info
160
161 subroutine mf_phys_init()
163 {^nooned
165 }
166
167 integer :: itr, idir
168
169 call mf_read_params(par_files)
170
171 physics_type = "mf"
172 phys_energy = .false.
174
175 if(ndim==1) typedivbfix='none'
176 select case (typedivbfix)
177 case ('none')
178 type_divb = divb_none
179 {^nooned
180 case ('multigrid')
181 type_divb = divb_multigrid
182 use_multigrid = .true.
183 mg%operator_type = mg_laplacian
184 phys_global_source_after => mf_clean_divb_multigrid
185 }
186 case ('glm')
187 mf_glm = .true.
188 need_global_cmax = .true.
189 type_divb = divb_glm
190 case ('powel', 'powell')
191 type_divb = divb_powel
192 case ('janhunen')
193 type_divb = divb_janhunen
194 case ('linde')
195 type_divb = divb_linde
196 case ('lindejanhunen')
197 type_divb = divb_lindejanhunen
198 case ('lindepowel')
199 type_divb = divb_lindepowel
200 case ('lindeglm')
201 mf_glm = .true.
202 need_global_cmax = .true.
203 type_divb = divb_lindeglm
204 case ('ct')
205 type_divb = divb_ct
206 stagger_grid = .true.
207 case default
208 call mpistop('Unknown divB fix')
209 end select
210
211 ! set velocity field as flux variables
212 allocate(mom(ndir))
213 mom(:) = var_set_momentum(ndir)
214 m^c_=mom(^c);
215
216 ! set magnetic field as flux variables
217 allocate(mag(ndir))
218 mag(:) = var_set_bfield(ndir)
219 b^c_=mag(^c);
220
221 ! start with magnetic field and skip velocity when update ghostcells
222 iwstart=mag(1)
223 allocate(start_indices(number_species),stop_indices(number_species))
224 ! set the index of the first flux variable for species 1
225 start_indices(1)=mag(1)
226
227 if (mf_glm) then
228 psi_ = var_set_fluxvar('psi', 'psi', need_bc=.false.)
229 else
230 psi_ = -1
231 end if
232
233 ! set number of variables which need update ghostcells
234 nwgc=nwflux-ndir
235
236 ! set the index of the last flux variable for species 1
237 stop_indices(1)=nwflux
238
239 ! determine number of stagger variables
240 nws=ndim
241
242 nvector = 2 ! No. vector vars
243 allocate(iw_vector(nvector))
244 iw_vector(1) = mom(1) - 1 ! TODO: why like this?
245 iw_vector(2) = mag(1) - 1 ! TODO: why like this?
246
247 ! Check whether custom flux types have been defined
248 if (.not. allocated(flux_type)) then
249 allocate(flux_type(ndir, nw))
250 flux_type = flux_default
251 else if (any(shape(flux_type) /= [ndir, nw])) then
252 call mpistop("phys_check error: flux_type has wrong shape")
253 end if
254 do idir=1,ndir
255 if(ndim>1) flux_type(idir,mag(idir))=flux_tvdlf
256 end do
257 if(mf_glm .and. ndim>1) flux_type(:,psi_)=flux_tvdlf
258
259 phys_get_dt => mf_get_dt
260 phys_get_cmax => mf_get_cmax
261 phys_get_cbounds => mf_get_cbounds
262 phys_get_flux => mf_get_flux
263 phys_add_source_geom => mf_add_source_geom
264 phys_add_source => mf_add_source
265 phys_to_conserved => mf_to_conserved
266 phys_to_primitive => mf_to_primitive
267 phys_check_params => mf_check_params
268 phys_write_info => mf_write_info
269 phys_special_advance => mf_velocity_update
270
271 if(type_divb==divb_glm) then
272 phys_modify_wlr => mf_modify_wlr
273 end if
274
275 ! pass to global variable to record electric field
277
278
279 ! if using ct stagger grid, boundary divb=0 is not done here
280 if(stagger_grid) then
281 phys_face_to_center => mf_face_to_center
282 select case(type_ct)
283 case('average')
284 transverse_ghost_cells = 1
285 phys_get_ct_velocity => mf_get_ct_velocity_average
286 phys_update_faces => mf_update_faces_average
287 case('uct_contact')
288 transverse_ghost_cells = 1
289 phys_get_ct_velocity => mf_get_ct_velocity_contact
290 phys_update_faces => mf_update_faces_contact
291 case('uct_hll')
292 transverse_ghost_cells = 2
293 phys_get_ct_velocity => mf_get_ct_velocity_hll
294 phys_update_faces => mf_update_faces_hll
295 case default
296 call mpistop('choose average, uct_contact,or uct_hll for type_ct!')
297 end select
298 else if(ndim>1) then
299 phys_boundary_adjust => mf_boundary_adjust
300 end if
301
302 {^nooned
303 ! clean initial divb
304 if(clean_initial_divb) phys_clean_divb => mf_clean_divb_multigrid
305 }
306
307 ! derive units from basic units
308 call mf_physical_units()
309
310 end subroutine mf_phys_init
311
312 subroutine mf_check_params
314 use mod_geometry, only: coordinate
315 use mod_particles, only: particles_init, particles_eta, particles_etah
316 use mod_particles, only: npayload,nusrpayload, &
317 ngridvars,num_particles,physics_type_particles
318
319 ! Initialize particles module
320 if(mf_particles) then
321 call particles_init()
322 ! never allow Hall effects in particles when doing magnetofrictional
323 particles_etah=0.0d0
324 end if
325
326 if(mype==0)then
327 write(*,*)'====MF run with settings===================='
328 write(*,*)'Using mod_mf_phys with settings:'
329 write(*,*)'SI_unit=',si_unit
330 write(*,*)'Dimensionality :',ndim
331 write(*,*)'vector components:',ndir
332 write(*,*)'coordinate set to type,slab:',coordinate,slab
333 write(*,*)'number of variables nw=',nw
334 write(*,*)' start index iwstart=',iwstart
335 write(*,*)'number of vector variables=',nvector
336 write(*,*)'number of stagger variables nws=',nws
337 write(*,*)'number of variables with BCs=',nwgc
338 write(*,*)'number of vars with fluxes=',nwflux
339 write(*,*)'number of vars with flux + BC=',nwfluxbc
340 write(*,*)'number of auxiliary variables=',nwaux
341 write(*,*)'number of extra vars without flux=',nwextra
342 write(*,*)'number of extra vars for wextra=',nw_extra
343 write(*,*)'number of auxiliary I/O variables=',nwauxio
344 write(*,*)' mf_eta=',mf_eta,' nonzero implies resistivity'
345 write(*,*)' mf_eta_hyper=',mf_eta_hyper
346 write(*,*)' mf_particles=',mf_particles
347 if(mf_particles) then
348 write(*,*) '*****Using particles: with mf_eta, mf_eta_hyper :', mf_eta, mf_eta_hyper
349 write(*,*) '*****Using particles: particles_eta, particles_etah :', particles_eta, particles_etah
350 write(*,*) '*****Using particles: npayload,ngridvars :', npayload,ngridvars
351 write(*,*) '*****Using particles: nusrpayload :', nusrpayload
352 write(*,*) '*****Using particles: num_particles :', num_particles
353 write(*,*) '*****Using particles: physics_type_particles=',physics_type_particles
354 end if
355 write(*,*)'number of ghostcells=',nghostcells
356 write(*,*)'number due to phys_wider_stencil=',phys_wider_stencil
357 write(*,*)'==========================================='
358 endif
359
360
361 end subroutine mf_check_params
362
363 subroutine mf_physical_units()
365 double precision :: mp,kb,miu0,c_lightspeed
366 double precision :: a,b
367 ! Derive scaling units
368 if(si_unit) then
369 mp=mp_si
370 kb=kb_si
371 miu0=miu0_si
372 c_lightspeed=c_si
373 else
374 mp=mp_cgs
375 kb=kb_cgs
376 miu0=4.d0*dpi
377 c_lightspeed=const_c
378 end if
379 a=1.d0+4.d0*he_abundance
380 b=2.d0+3.d0*he_abundance
381 if(unit_density/=1.d0 .or. unit_numberdensity/=1.d0) then
382 if(unit_density/=1.d0) then
384 else if(unit_numberdensity/=1.d0) then
386 end if
387 if(unit_temperature/=1.d0) then
391 if(unit_length/=1.d0) then
393 else if(unit_time/=1.d0) then
395 end if
396 else if(unit_magneticfield/=1.d0) then
400 if(unit_length/=1.d0) then
402 else if(unit_time/=1.d0) then
404 end if
405 else if(unit_pressure/=1.d0) then
409 if(unit_length/=1.d0) then
411 else if(unit_time/=1.d0) then
413 end if
414 else if(unit_velocity/=1.d0) then
418 if(unit_length/=1.d0) then
420 else if(unit_time/=1.d0) then
422 end if
423 else if(unit_time/=1.d0) then
428 end if
429 else if(unit_temperature/=1.d0) then
430 ! units of temperature and velocity are dependent
431 if(unit_magneticfield/=1.d0) then
436 if(unit_length/=1.d0) then
438 else if(unit_time/=1.d0) then
440 end if
441 else if(unit_pressure/=1.d0) then
446 if(unit_length/=1.d0) then
448 else if(unit_time/=1.d0) then
450 end if
451 end if
452 else if(unit_magneticfield/=1.d0) then
453 ! units of magnetic field and pressure are dependent
454 if(unit_velocity/=1.d0) then
459 if(unit_length/=1.d0) then
461 else if(unit_time/=1.d0) then
463 end if
464 else if(unit_time/=0.d0) then
470 end if
471 else if(unit_pressure/=1.d0) then
472 if(unit_velocity/=1.d0) then
477 if(unit_length/=1.d0) then
479 else if(unit_time/=1.d0) then
481 end if
482 else if(unit_time/=0.d0) then
488 end if
489 end if
490 ! Additional units needed for the particles
491 c_norm=c_lightspeed/unit_velocity
493 if (.not. si_unit) unit_charge = unit_charge*const_c
495
496 ! get dimensionless mf nu
498
499 ! get dimensionless maximal mf velocity limit
501
502 end subroutine mf_physical_units
503
504 !> Transform primitive variables into conservative ones
505 subroutine mf_to_conserved(ixI^L,ixO^L,w,x)
507 integer, intent(in) :: ixi^l, ixo^l
508 double precision, intent(inout) :: w(ixi^s, nw)
509 double precision, intent(in) :: x(ixi^s, 1:ndim)
510
511 ! nothing to do for mf
512 end subroutine mf_to_conserved
513
514 !> Transform conservative variables into primitive ones
515 subroutine mf_to_primitive(ixI^L,ixO^L,w,x)
517 integer, intent(in) :: ixi^l, ixo^l
518 double precision, intent(inout) :: w(ixi^s, nw)
519 double precision, intent(in) :: x(ixi^s, 1:ndim)
520
521 ! nothing to do for mf
522 end subroutine mf_to_primitive
523
524 !> Calculate cmax_idim=csound+abs(v_idim) within ixO^L
525 subroutine mf_get_cmax(w,x,ixI^L,ixO^L,idim,cmax)
527
528 integer, intent(in) :: ixi^l, ixo^l, idim
529 double precision, intent(in) :: w(ixi^s, nw), x(ixi^s,1:ndim)
530 double precision, intent(inout) :: cmax(ixi^s)
531
532 cmax(ixo^s)=abs(w(ixo^s,mom(idim)))+one
533
534 end subroutine mf_get_cmax
535
536 !> Estimating bounds for the minimum and maximum signal velocities
537 subroutine mf_get_cbounds(wLC,wRC,wLp,wRp,x,ixI^L,ixO^L,idim,Hspeed,cmax,cmin)
539 use mod_variables
540
541 integer, intent(in) :: ixi^l, ixo^l, idim
542 double precision, intent(in) :: wlc(ixi^s, nw), wrc(ixi^s, nw)
543 double precision, intent(in) :: wlp(ixi^s, nw), wrp(ixi^s, nw)
544 double precision, intent(in) :: x(ixi^s,1:ndim)
545 double precision, intent(inout) :: cmax(ixi^s,1:number_species)
546 double precision, intent(inout), optional :: cmin(ixi^s,1:number_species)
547 double precision, intent(in) :: hspeed(ixi^s,1:number_species)
548
549 double precision, dimension(ixI^S) :: tmp1
550
551 tmp1(ixo^s)=0.5d0*(wlc(ixo^s,mom(idim))+wrc(ixo^s,mom(idim)))
552 if(present(cmin)) then
553 cmax(ixo^s,1)=max(tmp1(ixo^s)+one,zero)
554 cmin(ixo^s,1)=min(tmp1(ixo^s)-one,zero)
555 else
556 cmax(ixo^s,1)=abs(tmp1(ixo^s))+one
557 end if
558
559 end subroutine mf_get_cbounds
560
561 !> prepare velocities for ct methods
562 subroutine mf_get_ct_velocity_average(vcts,wLp,wRp,ixI^L,ixO^L,idim,cmax,cmin)
564
565 integer, intent(in) :: ixi^l, ixo^l, idim
566 double precision, intent(in) :: wlp(ixi^s, nw), wrp(ixi^s, nw)
567 double precision, intent(in) :: cmax(ixi^s)
568 double precision, intent(in), optional :: cmin(ixi^s)
569 type(ct_velocity), intent(inout):: vcts
570
571 end subroutine mf_get_ct_velocity_average
572
573 subroutine mf_get_ct_velocity_contact(vcts,wLp,wRp,ixI^L,ixO^L,idim,cmax,cmin)
575
576 integer, intent(in) :: ixi^l, ixo^l, idim
577 double precision, intent(in) :: wlp(ixi^s, nw), wrp(ixi^s, nw)
578 double precision, intent(in) :: cmax(ixi^s)
579 double precision, intent(in), optional :: cmin(ixi^s)
580 type(ct_velocity), intent(inout):: vcts
581
582 ! calculate velocities related to different UCT schemes
583 if(.not.allocated(vcts%vnorm)) allocate(vcts%vnorm(ixi^s,1:ndim))
584 ! get average normal velocity at cell faces
585 vcts%vnorm(ixo^s,idim)=0.5d0*(wlp(ixo^s,mom(idim))+wrp(ixo^s,mom(idim)))
586
587 end subroutine mf_get_ct_velocity_contact
588
589 subroutine mf_get_ct_velocity_hll(vcts,wLp,wRp,ixI^L,ixO^L,idim,cmax,cmin)
591
592 integer, intent(in) :: ixi^l, ixo^l, idim
593 double precision, intent(in) :: wlp(ixi^s, nw), wrp(ixi^s, nw)
594 double precision, intent(in) :: cmax(ixi^s)
595 double precision, intent(in), optional :: cmin(ixi^s)
596 type(ct_velocity), intent(inout):: vcts
597
598 integer :: idime,idimn
599
600 if(.not.allocated(vcts%vbarC)) then
601 allocate(vcts%vbarC(ixi^s,1:ndir,2),vcts%vbarLC(ixi^s,1:ndir,2),vcts%vbarRC(ixi^s,1:ndir,2))
602 allocate(vcts%cbarmin(ixi^s,1:ndim),vcts%cbarmax(ixi^s,1:ndim))
603 end if
604 ! Store magnitude of characteristics
605 if(present(cmin)) then
606 vcts%cbarmin(ixo^s,idim)=max(-cmin(ixo^s),zero)
607 vcts%cbarmax(ixo^s,idim)=max( cmax(ixo^s),zero)
608 else
609 vcts%cbarmax(ixo^s,idim)=max( cmax(ixo^s),zero)
610 vcts%cbarmin(ixo^s,idim)=vcts%cbarmax(ixo^s,idim)
611 end if
612
613 idimn=mod(idim,ndir)+1 ! 'Next' direction
614 idime=mod(idim+1,ndir)+1 ! Electric field direction
615 ! Store velocities
616 vcts%vbarLC(ixo^s,idim,1)=wlp(ixo^s,mom(idimn))
617 vcts%vbarRC(ixo^s,idim,1)=wrp(ixo^s,mom(idimn))
618 vcts%vbarC(ixo^s,idim,1)=(vcts%cbarmax(ixo^s,idim)*vcts%vbarLC(ixo^s,idim,1) &
619 +vcts%cbarmin(ixo^s,idim)*vcts%vbarRC(ixo^s,idim,1))&
620 /(vcts%cbarmax(ixo^s,idim)+vcts%cbarmin(ixo^s,idim))
621
622 vcts%vbarLC(ixo^s,idim,2)=wlp(ixo^s,mom(idime))
623 vcts%vbarRC(ixo^s,idim,2)=wrp(ixo^s,mom(idime))
624 vcts%vbarC(ixo^s,idim,2)=(vcts%cbarmax(ixo^s,idim)*vcts%vbarLC(ixo^s,idim,2) &
625 +vcts%cbarmin(ixo^s,idim)*vcts%vbarRC(ixo^s,idim,1))&
626 /(vcts%cbarmax(ixo^s,idim)+vcts%cbarmin(ixo^s,idim))
627
628 end subroutine mf_get_ct_velocity_hll
629
630 !> Calculate fluxes within ixO^L.
631 subroutine mf_get_flux(wC,w,x,ixI^L,ixO^L,idim,f)
634 use mod_geometry
635
636 integer, intent(in) :: ixi^l, ixo^l, idim
637 ! conservative w
638 double precision, intent(in) :: wc(ixi^s,nw)
639 ! primitive w
640 double precision, intent(in) :: w(ixi^s,nw)
641 double precision, intent(in) :: x(ixi^s,1:ndim)
642 double precision,intent(out) :: f(ixi^s,nwflux)
643
644 integer :: ix^d
645
646 {do ix^db=ixomin^db,ixomax^db\}
647 ! compute flux of magnetic field
648 ! f_i[b_k]=v_i*b_k-v_k*b_i
649 ^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_)\
650 {end do\}
651 if(mf_glm) then
652 {do ix^db=ixomin^db,ixomax^db\}
653 f(ix^d,mag(idim))=w(ix^d,psi_)
654 !f_i[psi]=Ch^2*b_{i} Eq. 24e and Eq. 38c Dedner et al 2002 JCP, 175, 645
655 f(ix^d,psi_)=cmax_global**2*w(ix^d,mag(idim))
656 {end do\}
657 end if
658
659 end subroutine mf_get_flux
660
661 !> Add global source terms to update frictional velocity on complete domain
662 subroutine mf_velocity_update(qt,psa)
665 double precision, intent(in) :: qt !< Current time
666 type(state), target :: psa(max_blocks) !< Compute based on this state
667
668 integer :: iigrid,igrid
669 logical :: stagger_flag
670 logical :: firstmf=.true.
671
672 if(firstmf) then
673 ! point bc mpi datatype to partial type for velocity field
681 firstmf=.false.
682 end if
683
684 !$OMP PARALLEL DO PRIVATE(igrid)
685 do iigrid=1,igridstail_active; igrid=igrids_active(iigrid);
686 block=>psa(igrid)
687 call frictional_velocity(psa(igrid)%w,psa(igrid)%x,ixg^ll,ixm^ll)
688 end do
689 !$OMP END PARALLEL DO
690
691 ! only update mf velocity in ghost cells
692 stagger_flag=stagger_grid
699 stagger_grid=.false.
700 bcphys=.false.
701 call getbc(qt,0.d0,psa,mom(1),ndir)
702 bcphys=.true.
709 stagger_grid=stagger_flag
710
711 end subroutine mf_velocity_update
712
713 !> w[iws]=w[iws]+qdt*S[iws,wCT] where S is the source based on wCT within ixO
714 subroutine mf_add_source(qdt,dtfactor,ixI^L,ixO^L,wCT,wCTprim,w,x,qsourcesplit,active)
716
717 integer, intent(in) :: ixi^l, ixo^l
718 double precision, intent(in) :: qdt, dtfactor
719 double precision, intent(in) :: wct(ixi^s,1:nw),wctprim(ixi^s,1:nw), x(ixi^s,1:ndim)
720 double precision, intent(inout) :: w(ixi^s,1:nw)
721 logical, intent(in) :: qsourcesplit
722 logical, intent(inout) :: active
723
724 if (.not. qsourcesplit) then
725
726 ! Sources for resistivity in eqs. for e, B1, B2 and B3
727 if (abs(mf_eta)>smalldouble)then
728 active = .true.
729 call add_source_res2(qdt,ixi^l,ixo^l,wct,w,x)
730 end if
731
732 if (mf_eta_hyper>0.d0)then
733 active = .true.
734 call add_source_hyperres(qdt,ixi^l,ixo^l,wct,w,x)
735 end if
736
737 end if
738
739 {^nooned
740 if(source_split_divb .eqv. qsourcesplit) then
741 ! Sources related to div B
742 select case (type_divb)
743 case (divb_none)
744 ! Do nothing
745 case (divb_glm)
746 active = .true.
747 call add_source_glm(qdt,ixi^l,ixo^l,wct,w,x)
748 case (divb_powel)
749 active = .true.
750 call add_source_powel(qdt,ixi^l,ixo^l,wct,w,x)
751 case (divb_janhunen)
752 active = .true.
753 call add_source_janhunen(qdt,ixi^l,ixo^l,wct,w,x)
754 case (divb_linde)
755 active = .true.
756 call add_source_linde(qdt,ixi^l,ixo^l,wct,w,x)
757 case (divb_lindejanhunen)
758 active = .true.
759 call add_source_linde(qdt,ixi^l,ixo^l,wct,w,x)
760 call add_source_janhunen(qdt,ixi^l,ixo^l,wct,w,x)
761 case (divb_lindepowel)
762 active = .true.
763 call add_source_linde(qdt,ixi^l,ixo^l,wct,w,x)
764 call add_source_powel(qdt,ixi^l,ixo^l,wct,w,x)
765 case (divb_lindeglm)
766 active = .true.
767 call add_source_linde(qdt,ixi^l,ixo^l,wct,w,x)
768 call add_source_glm(qdt,ixi^l,ixo^l,wct,w,x)
769 case (divb_ct)
770 continue ! Do nothing
771 case (divb_multigrid)
772 continue ! Do nothing
773 case default
774 call mpistop('Unknown divB fix')
775 end select
776 end if
777 }
778
779 end subroutine mf_add_source
780
781 subroutine frictional_velocity(w,x,ixI^L,ixO^L)
783
784 integer, intent(in) :: ixi^l, ixo^l
785 double precision, intent(in) :: x(ixi^s,1:ndim)
786 double precision, intent(inout) :: w(ixi^s,1:nw)
787
788 double precision :: xmin(ndim),xmax(ndim)
789 double precision :: decay(ixo^s)
790 double precision :: current(ixi^s,7-2*ndir:3),tmp(ixo^s)
791 integer :: ix^d, idirmin,idir,jdir,kdir
792
793 call get_current(w,ixi^l,ixo^l,idirmin,current)
794 ! extrapolate current for the outmost layer,
795 ! because extrapolation of B at boundaries introduces artificial current
796{
797 if(block%is_physical_boundary(2*^d-1)) then
798 if(slab) then
799 ! exclude boundary 5 in 3D Cartesian
800 if(2*^d-1==5.and.ndim==3) then
801 else
802 current(ixomin^d^d%ixO^s,:)=current(ixomin^d+1^d%ixO^s,:)
803 end if
804 else
805 ! exclude boundary 1 spherical/cylindrical
806 if(2*^d-1>1) then
807 current(ixomin^d^d%ixO^s,:)=current(ixomin^d+1^d%ixO^s,:)
808 end if
809 end if
810 end if
811 if(block%is_physical_boundary(2*^d)) then
812 current(ixomax^d^d%ixO^s,:)=current(ixomax^d-1^d%ixO^s,:)
813 end if
814\}
815 w(ixo^s,mom(:))=0.d0
816 ! calculate Lorentz force
817 do idir=1,ndir; do jdir=idirmin,3; do kdir=1,ndir
818 if(lvc(idir,jdir,kdir)/=0) then
819 if(lvc(idir,jdir,kdir)==1) then
820 w(ixo^s,mom(idir))=w(ixo^s,mom(idir))+current(ixo^s,jdir)*w(ixo^s,mag(kdir))
821 else
822 w(ixo^s,mom(idir))=w(ixo^s,mom(idir))-current(ixo^s,jdir)*w(ixo^s,mag(kdir))
823 end if
824 end if
825 end do; end do; end do
826
827 tmp(ixo^s)=sum(w(ixo^s,mag(:))**2,dim=ndim+1)
828 ! frictional coefficient
829 where(tmp(ixo^s)/=0.d0)
830 tmp(ixo^s)=1.d0/(tmp(ixo^s)*mf_nu)
831 endwhere
832
833 do idir=1,ndir
834 w(ixo^s,mom(idir))=w(ixo^s,mom(idir))*tmp(ixo^s)
835 end do
836
837 ! decay frictional velocity near selected boundaries
838 ^d&xmin(^d)=xprobmin^d\
839 ^d&xmax(^d)=xprobmax^d\
840 decay(ixo^s)=1.d0
841 do idir=1,ndim
842 if(mf_decay_scale(2*idir-1)>0.d0) decay(ixo^s)=decay(ixo^s)*&
843 (1.d0-exp(-(x(ixo^s,idir)-xmin(idir))/mf_decay_scale(2*idir-1)))
844 if(mf_decay_scale(2*idir)>0.d0) decay(ixo^s)=decay(ixo^s)*&
845 (1.d0-exp((x(ixo^s,idir)-xmax(idir))/mf_decay_scale(2*idir)))
846 end do
847
848 ! saturate mf velocity at mf_vmax
849 tmp(ixo^s)=sqrt(sum(w(ixo^s,mom(:))**2,dim=ndim+1))/mf_vmax+1.d-12
850 tmp(ixo^s)=dtanh(tmp(ixo^s))/tmp(ixo^s)
851 do idir=1,ndir
852 w(ixo^s,mom(idir))=w(ixo^s,mom(idir))*tmp(ixo^s)*decay(ixo^s)
853 end do
854
855 end subroutine frictional_velocity
856
857 !> Add resistive source to w within ixO Uses 3 point stencil (1 neighbour) in
858 !> each direction, non-conservative. If the fourthorder precompiler flag is
859 !> set, uses fourth order central difference for the laplacian. Then the
860 !> stencil is 5 (2 neighbours).
861 subroutine add_source_res1(qdt,ixI^L,ixO^L,wCT,w,x)
864 use mod_geometry
865
866 integer, intent(in) :: ixi^l, ixo^l
867 double precision, intent(in) :: qdt
868 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
869 double precision, intent(inout) :: w(ixi^s,1:nw)
870
871 ! For ndir=2 only 3rd component of J can exist, ndir=1 is impossible for MHD
872 double precision :: current(ixi^s,7-2*ndir:3),eta(ixi^s)
873 double precision :: gradeta(ixi^s,1:ndim), bf(ixi^s,1:ndir)
874 double precision :: tmp(ixi^s),tmp2(ixi^s)
875 integer :: ixa^l,idir,jdir,kdir,idirmin,idim,jxo^l,hxo^l,ix
876 integer :: lxo^l, kxo^l
877
878 ! Calculating resistive sources involve one extra layer
879 ixa^l=ixo^l^ladd2;
880
881 if (iximin^d>ixamin^d.or.iximax^d<ixamax^d|.or.) &
882 call mpistop("Error in add_source_res1: Non-conforming input limits")
883
884 ! Calculate current density and idirmin
885 call get_current(wct,ixi^l,ixo^l,idirmin,current)
886
887 if (mf_eta>zero)then
888 eta(ixa^s)=mf_eta
889 gradeta(ixo^s,1:ndim)=zero
890 else
891 call usr_special_resistivity(wct,ixi^l,ixa^l,idirmin,x,current,eta)
892 ! assumes that eta is not function of current?
893 do idim=1,ndim
894 call gradient(eta,ixi^l,ixo^l,idim,tmp)
895 gradeta(ixo^s,idim)=tmp(ixo^s)
896 end do
897 end if
898
899 bf(ixi^s,1:ndir)=wct(ixi^s,mag(1:ndir))
900
901 do idir=1,ndir
902 ! Put B_idir into tmp2 and eta*Laplace B_idir into tmp
903 tmp(ixo^s)=zero
904 tmp2(ixi^s)=bf(ixi^s,idir)
905 do idim=1,ndim
906 lxo^l=ixo^l+2*kr(idim,^d);
907 jxo^l=ixo^l+kr(idim,^d);
908 hxo^l=ixo^l-kr(idim,^d);
909 kxo^l=ixo^l-2*kr(idim,^d);
910 tmp(ixo^s)=tmp(ixo^s)+&
911 (-tmp2(lxo^s)+16.0d0*tmp2(jxo^s)-30.0d0*tmp2(ixo^s)+16.0d0*tmp2(hxo^s)-tmp2(kxo^s)) &
912 /(12.0d0 * dxlevel(idim)**2)
913 end do
914
915 ! Multiply by eta
916 tmp(ixo^s)=tmp(ixo^s)*eta(ixo^s)
917
918 ! Subtract grad(eta) x J = eps_ijk d_j eta J_k if eta is non-constant
919 if (mf_eta<zero)then
920 do jdir=1,ndim; do kdir=idirmin,3
921 if (lvc(idir,jdir,kdir)/=0)then
922 if (lvc(idir,jdir,kdir)==1)then
923 tmp(ixo^s)=tmp(ixo^s)-gradeta(ixo^s,jdir)*current(ixo^s,kdir)
924 else
925 tmp(ixo^s)=tmp(ixo^s)+gradeta(ixo^s,jdir)*current(ixo^s,kdir)
926 end if
927 end if
928 end do; end do
929 end if
930
931 ! Add sources related to eta*laplB-grad(eta) x J to B and e
932 w(ixo^s,mag(idir))=w(ixo^s,mag(idir))+qdt*tmp(ixo^s)
933
934 end do ! idir
935
936 end subroutine add_source_res1
937
938 !> Add resistive source to w within ixO
939 !> Uses 5 point stencil (2 neighbours) in each direction, conservative
940 subroutine add_source_res2(qdt,ixI^L,ixO^L,wCT,w,x)
943 use mod_geometry
944
945 integer, intent(in) :: ixi^l, ixo^l
946 double precision, intent(in) :: qdt
947 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
948 double precision, intent(inout) :: w(ixi^s,1:nw)
949
950 ! For ndir=2 only 3rd component of J can exist, ndir=1 is impossible for MHD
951 double precision :: current(ixi^s,7-2*ndir:3),eta(ixi^s),curlj(ixi^s,1:3)
952 double precision :: tmpvec(ixi^s,1:3)
953 integer :: ixa^l,idir,idirmin,idirmin1
954
955 ! resistive term already added as an electric field component
956 if(stagger_grid .and. ndim==ndir) return
957
958 ixa^l=ixo^l^ladd2;
959
960 if (iximin^d>ixamin^d.or.iximax^d<ixamax^d|.or.) &
961 call mpistop("Error in add_source_res2: Non-conforming input limits")
962
963 ixa^l=ixo^l^ladd1;
964 ! Calculate current density within ixL: J=curl B, thus J_i=eps_ijk*d_j B_k
965 ! Determine exact value of idirmin while doing the loop.
966 call get_current(wct,ixi^l,ixa^l,idirmin,current)
967
968 if (mf_eta>zero)then
969 eta(ixa^s)=mf_eta
970 else
971 call usr_special_resistivity(wct,ixi^l,ixa^l,idirmin,x,current,eta)
972 end if
973
974 ! dB/dt= -curl(J*eta), thus B_i=B_i-eps_ijk d_j Jeta_k
975 tmpvec(ixa^s,1:ndir)=zero
976 do idir=idirmin,3
977 tmpvec(ixa^s,idir)=current(ixa^s,idir)*eta(ixa^s)
978 end do
979 curlj=0.d0
980 call curlvector(tmpvec,ixi^l,ixo^l,curlj,idirmin1,1,3)
981 if(stagger_grid) then
982 if(ndim==2.and.ndir==3) then
983 ! if 2.5D
984 w(ixo^s,mag(ndir)) = w(ixo^s,mag(ndir))-qdt*curlj(ixo^s,ndir)
985 end if
986 else
987 w(ixo^s,mag(1:ndir)) = w(ixo^s,mag(1:ndir))-qdt*curlj(ixo^s,1:ndir)
988 end if
989
990 end subroutine add_source_res2
991
992 !> Add Hyper-resistive source to w within ixO
993 !> Uses 9 point stencil (4 neighbours) in each direction.
994 subroutine add_source_hyperres(qdt,ixI^L,ixO^L,wCT,w,x)
996 use mod_geometry
997
998 integer, intent(in) :: ixi^l, ixo^l
999 double precision, intent(in) :: qdt
1000 double precision, intent(in) :: wct(ixi^s,1:nw), x(ixi^s,1:ndim)
1001 double precision, intent(inout) :: w(ixi^s,1:nw)
1002 !.. local ..
1003 double precision :: current(ixi^s,7-2*ndir:3)
1004 double precision :: tmpvec(ixi^s,1:3),tmpvec2(ixi^s,1:3),tmp(ixi^s),ehyper(ixi^s,1:3)
1005 integer :: ixa^l,idir,jdir,kdir,idirmin,idirmin1
1006
1007 ixa^l=ixo^l^ladd3;
1008 if (iximin^d>ixamin^d.or.iximax^d<ixamax^d|.or.) &
1009 call mpistop("Error in add_source_hyperres: Non-conforming input limits")
1010
1011 call get_current(wct,ixi^l,ixa^l,idirmin,current)
1012 tmpvec(ixa^s,1:ndir)=zero
1013 do jdir=idirmin,3
1014 tmpvec(ixa^s,jdir)=current(ixa^s,jdir)
1015 end do
1016
1017 ixa^l=ixo^l^ladd2;
1018 call curlvector(tmpvec,ixi^l,ixa^l,tmpvec2,idirmin1,1,3)
1019
1020 ixa^l=ixo^l^ladd1;
1021 tmpvec(ixa^s,1:ndir)=zero
1022 call curlvector(tmpvec2,ixi^l,ixa^l,tmpvec,idirmin1,1,3)
1023 ehyper(ixa^s,1:ndir) = - tmpvec(ixa^s,1:ndir)*mf_eta_hyper
1024
1025 ixa^l=ixo^l;
1026 tmpvec2(ixa^s,1:ndir)=zero
1027 call curlvector(ehyper,ixi^l,ixa^l,tmpvec2,idirmin1,1,3)
1028
1029 do idir=1,ndir
1030 w(ixo^s,mag(idir)) = w(ixo^s,mag(idir))-tmpvec2(ixo^s,idir)*qdt
1031 end do
1032
1033 end subroutine add_source_hyperres
1034
1035 subroutine add_source_glm(qdt,ixI^L,ixO^L,wCT,w,x)
1036 ! Add divB related sources to w within ixO
1037 ! corresponding to Dedner JCP 2002, 175, 645 _equation 24_
1038 ! giving the EGLM-MHD scheme
1040 use mod_geometry
1041
1042 integer, intent(in) :: ixi^l, ixo^l
1043 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), x(ixi^s,1:ndim)
1044 double precision, intent(inout) :: w(ixi^s,1:nw)
1045 double precision:: divb(ixi^s)
1046 double precision :: gradpsi(ixi^s)
1047 integer :: idim,idir
1048
1049 ! We calculate now div B
1050 call get_divb(wct,ixi^l,ixo^l,divb,mf_divb_nth)
1051
1052 ! dPsi/dt = - Ch^2/Cp^2 Psi
1053 if (mf_glm_alpha < zero) then
1054 w(ixo^s,psi_) = abs(mf_glm_alpha)*wct(ixo^s,psi_)
1055 else
1056 ! implicit update of Psi variable
1057 ! equation (27) in Mignone 2010 J. Com. Phys. 229, 2117
1058 if(slab_uniform) then
1059 w(ixo^s,psi_) = dexp(-qdt*cmax_global*mf_glm_alpha/minval(dxlevel(:)))*w(ixo^s,psi_)
1060 else
1061 w(ixo^s,psi_) = dexp(-qdt*cmax_global*mf_glm_alpha/minval(block%ds(ixo^s,:),dim=ndim+1))*w(ixo^s,psi_)
1062 end if
1063 end if
1064
1065 ! gradient of Psi
1066 do idim=1,ndim
1067 select case(typegrad)
1068 case("central")
1069 call gradient(wct(ixi^s,psi_),ixi^l,ixo^l,idim,gradpsi)
1070 case("limited")
1071 call gradientl(wct(ixi^s,psi_),ixi^l,ixo^l,idim,gradpsi)
1072 end select
1073 end do
1074 end subroutine add_source_glm
1075
1076 !> Add divB related sources to w within ixO corresponding to Powel
1077 subroutine add_source_powel(qdt,ixI^L,ixO^L,wCT,w,x)
1079
1080 integer, intent(in) :: ixi^l, ixo^l
1081 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), x(ixi^s,1:ndim)
1082 double precision, intent(inout) :: w(ixi^s,1:nw)
1083
1084 double precision :: divb(ixi^s)
1085 integer :: idir
1086
1087 ! We calculate now div B
1088 call get_divb(wct,ixi^l,ixo^l,divb,mf_divb_nth)
1089
1090 ! b = b - qdt v * div b
1091 do idir=1,ndir
1092 w(ixo^s,mag(idir))=w(ixo^s,mag(idir))-qdt*wct(ixo^s,mom(idir))*divb(ixo^s)
1093 end do
1094
1095 end subroutine add_source_powel
1096
1097 subroutine add_source_janhunen(qdt,ixI^L,ixO^L,wCT,w,x)
1098 ! Add divB related sources to w within ixO
1099 ! corresponding to Janhunen, just the term in the induction equation.
1101
1102 integer, intent(in) :: ixi^l, ixo^l
1103 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), x(ixi^s,1:ndim)
1104 double precision, intent(inout) :: w(ixi^s,1:nw)
1105 double precision :: divb(ixi^s)
1106 integer :: idir
1107
1108 ! We calculate now div B
1109 call get_divb(wct,ixi^l,ixo^l,divb,mf_divb_nth)
1110
1111 ! b = b - qdt v * div b
1112 do idir=1,ndir
1113 w(ixo^s,mag(idir))=w(ixo^s,mag(idir))-qdt*wct(ixo^s,mom(idir))*divb(ixo^s)
1114 end do
1115
1116 end subroutine add_source_janhunen
1117
1118 subroutine add_source_linde(qdt,ixI^L,ixO^L,wCT,w,x)
1119 ! Add Linde's divB related sources to wnew within ixO
1121 use mod_geometry
1122
1123 integer, intent(in) :: ixi^l, ixo^l
1124 double precision, intent(in) :: qdt, wct(ixi^s,1:nw), x(ixi^s,1:ndim)
1125 double precision, intent(inout) :: w(ixi^s,1:nw)
1126 double precision :: divb(ixi^s),graddivb(ixi^s)
1127 integer :: idim, idir, ixp^l, i^d, iside
1128 logical, dimension(-1:1^D&) :: leveljump
1129
1130 ! Calculate div B
1131 ixp^l=ixo^l^ladd1;
1132 call get_divb(wct,ixi^l,ixp^l,divb,mf_divb_nth)
1133
1134 ! for AMR stability, retreat one cell layer from the boarders of level jump
1135 {do i^db=-1,1\}
1136 if(i^d==0|.and.) cycle
1137 if(neighbor_type(i^d,block%igrid)==2 .or. neighbor_type(i^d,block%igrid)==4) then
1138 leveljump(i^d)=.true.
1139 else
1140 leveljump(i^d)=.false.
1141 end if
1142 {end do\}
1143
1144 ixp^l=ixo^l;
1145 do idim=1,ndim
1146 select case(idim)
1147 {case(^d)
1148 do iside=1,2
1149 i^dd=kr(^dd,^d)*(2*iside-3);
1150 if (leveljump(i^dd)) then
1151 if (iside==1) then
1152 ixpmin^d=ixomin^d-i^d
1153 else
1154 ixpmax^d=ixomax^d-i^d
1155 end if
1156 end if
1157 end do
1158 \}
1159 end select
1160 end do
1161
1162 ! Add Linde's diffusive terms
1163 do idim=1,ndim
1164 ! Calculate grad_idim(divb)
1165 select case(typegrad)
1166 case("central")
1167 call gradient(divb,ixi^l,ixp^l,idim,graddivb)
1168 case("limited")
1169 call gradientl(divb,ixi^l,ixp^l,idim,graddivb)
1170 end select
1171
1172 ! Multiply by Linde's eta*dt = divbdiff*(c_max*dx)*dt = divbdiff*dx**2
1173 if (slab_uniform) then
1174 graddivb(ixp^s)=graddivb(ixp^s)*divbdiff/(^d&1.0d0/dxlevel(^d)**2+)
1175 else
1176 graddivb(ixp^s)=graddivb(ixp^s)*divbdiff &
1177 /(^d&1.0d0/block%ds(ixp^s,^d)**2+)
1178 end if
1179
1180 w(ixp^s,mag(idim))=w(ixp^s,mag(idim))+graddivb(ixp^s)
1181 end do
1182
1183 end subroutine add_source_linde
1184
1185
1186 !> get dimensionless div B = |divB| * volume / area / |B|
1187 subroutine get_normalized_divb(w,ixI^L,ixO^L,divb)
1188
1190
1191 integer, intent(in) :: ixi^l, ixo^l
1192 double precision, intent(in) :: w(ixi^s,1:nw)
1193 double precision :: divb(ixi^s), dsurface(ixi^s)
1194
1195 double precision :: invb(ixo^s)
1196 integer :: ixa^l,idims
1197
1198 call get_divb(w,ixi^l,ixo^l,divb)
1199 invb(ixo^s)=sqrt(sum(w(ixo^s, mag(:))**2, dim=ndim+1))
1200 where(invb(ixo^s)/=0.d0)
1201 invb(ixo^s)=1.d0/invb(ixo^s)
1202 end where
1203 if(slab_uniform) then
1204 divb(ixo^s)=0.5d0*abs(divb(ixo^s))*invb(ixo^s)/sum(1.d0/dxlevel(:))
1205 else
1206 ixamin^d=ixomin^d-1;
1207 ixamax^d=ixomax^d-1;
1208 dsurface(ixo^s)= sum(block%surfaceC(ixo^s,:),dim=ndim+1)
1209 do idims=1,ndim
1210 ixa^l=ixo^l-kr(idims,^d);
1211 dsurface(ixo^s)=dsurface(ixo^s)+block%surfaceC(ixa^s,idims)
1212 end do
1213 divb(ixo^s)=abs(divb(ixo^s))*invb(ixo^s)*&
1214 block%dvolume(ixo^s)/dsurface(ixo^s)
1215 end if
1216
1217 end subroutine get_normalized_divb
1218
1219 !> Calculate idirmin and the idirmin:3 components of the common current array
1220 !> make sure that dxlevel(^D) is set correctly.
1221 subroutine get_current(w,ixI^L,ixO^L,idirmin,current,fourthorder)
1223 use mod_geometry
1224
1225 integer, intent(in) :: ixo^l, ixi^l
1226 double precision, intent(in) :: w(ixi^s,1:nw)
1227 integer, intent(out) :: idirmin
1228 logical, intent(in), optional :: fourthorder
1229
1230 ! For ndir=2 only 3rd component of J can exist, ndir=1 is impossible for MHD
1231 double precision :: current(ixi^s,7-2*ndir:3)
1232 integer :: idir, idirmin0
1233
1234 idirmin0 = 7-2*ndir
1235
1236 if(present(fourthorder)) then
1237 call curlvector(w(ixi^s,mag(1:ndir)),ixi^l,ixo^l,current,idirmin,idirmin0,ndir,fourthorder)
1238 else
1239 call curlvector(w(ixi^s,mag(1:ndir)),ixi^l,ixo^l,current,idirmin,idirmin0,ndir)
1240 end if
1241
1242 end subroutine get_current
1243
1244 !> If resistivity is not zero, check diffusion time limit for dt
1245 subroutine mf_get_dt(w,ixI^L,ixO^L,dtnew,dx^D,x)
1247 use mod_usr_methods
1248
1249 integer, intent(in) :: ixi^l, ixo^l
1250 double precision, intent(inout) :: dtnew
1251 double precision, intent(in) :: dx^d
1252 double precision, intent(in) :: w(ixi^s,1:nw)
1253 double precision, intent(in) :: x(ixi^s,1:ndim)
1254
1255 double precision :: dxarr(ndim)
1256 double precision :: current(ixi^s,7-2*ndir:3),eta(ixi^s)
1257 integer :: idirmin,idim
1258
1259 dtnew = bigdouble
1260
1261 ^d&dxarr(^d)=dx^d;
1262 if (mf_eta>zero)then
1263 dtnew=dtdiffpar*minval(dxarr(1:ndim))**2/mf_eta
1264 else if (mf_eta<zero)then
1265 call get_current(w,ixi^l,ixo^l,idirmin,current)
1266 call usr_special_resistivity(w,ixi^l,ixo^l,idirmin,x,current,eta)
1267 dtnew=bigdouble
1268 do idim=1,ndim
1269 if(slab_uniform) then
1270 dtnew=min(dtnew,&
1271 dtdiffpar/(smalldouble+maxval(eta(ixo^s)/dxarr(idim)**2)))
1272 else
1273 dtnew=min(dtnew,&
1274 dtdiffpar/(smalldouble+maxval(eta(ixo^s)/block%ds(ixo^s,idim)**2)))
1275 end if
1276 end do
1277 end if
1278
1279 if(mf_eta_hyper>zero) then
1280 if(slab_uniform) then
1281 dtnew=min(dtdiffpar*minval(dxarr(1:ndim))**4/mf_eta_hyper,dtnew)
1282 else
1283 dtnew=min(dtdiffpar*minval(block%ds(ixo^s,1:ndim))**4/mf_eta_hyper,dtnew)
1284 end if
1285 end if
1286 end subroutine mf_get_dt
1287
1288 ! Add geometrical source terms to w
1289 subroutine mf_add_source_geom(qdt,dtfactor, ixI^L,ixO^L,wCT,wprim,w,x)
1291 use mod_geometry
1292
1293 integer, intent(in) :: ixi^l, ixo^l
1294 double precision, intent(in) :: qdt, dtfactor, x(ixi^s,1:ndim)
1295 double precision, intent(inout) :: wct(ixi^s,1:nw), wprim(ixi^s,1:nw), w(ixi^s,1:nw)
1296
1297 double precision :: tmp,invr,cs2
1298 integer :: iw,idir,ix^d
1299
1300 integer :: mr_,mphi_ ! Polar var. names
1301 integer :: br_,bphi_
1302
1303 mr_=mom(1); mphi_=mom(1)-1+phi_ ! Polar var. names
1304 br_=mag(1); bphi_=mag(1)-1+phi_
1305
1306 select case (coordinate)
1307 case (cylindrical)
1308 if(phi_>0) then
1309 if(.not.stagger_grid) then
1310 {do ix^db=ixomin^db,ixomax^db\}
1311 w(ix^d,bphi_)=w(ix^d,bphi_)+qdt/x(ix^d,1)*&
1312 (wct(ix^d,bphi_)*wct(ix^d,mr_) &
1313 -wct(ix^d,br_)*wct(ix^d,mphi_))
1314 {end do\}
1315 end if
1316 end if
1317 if(mf_glm) w(ixo^s,br_)=w(ixo^s,br_)+qdt*wct(ixo^s,psi_)/x(ixo^s,1)
1318 case (spherical)
1319 {do ix^db=ixomin^db,ixomax^db\}
1320 invr=qdt/x(ix^d,1)
1321 ! b1
1322 if(mf_glm) then
1323 w(ix^d,mag(1))=w(ix^d,mag(1))+invr*2.0d0*wct(ix^d,psi_)
1324 end if
1325
1326 {^nooned
1327 ! b2
1328 if(.not.stagger_grid) then
1329 tmp=wct(ix^d,mom(1))*wct(ix^d,mag(2))-wct(ix^d,mom(2))*wct(ix^d,mag(1))
1330 cs2=1.d0/tan(x(ix^d,2))
1331 if(mf_glm) then
1332 tmp=tmp+cs2*wct(ix^d,psi_)
1333 end if
1334 w(ix^d,mag(2))=w(ix^d,mag(2))+tmp*invr
1335 end if
1336 }
1337
1338 if(ndir==3) then
1339 {^ifoned
1340 ! b3
1341 if(.not.stagger_grid) then
1342 w(ix^d,mag(3))=w(ix^d,mag(3))+invr*&
1343 (wct(ix^d,mom(1))*wct(ix^d,mag(3)) &
1344 -wct(ix^d,mom(3))*wct(ix^d,mag(1)))
1345 end if
1346 }
1347 {^nooned
1348 ! b3
1349 if(.not.stagger_grid) then
1350 w(ix^d,mag(3))=w(ix^d,mag(3))+invr*&
1351 (wct(ix^d,mom(1))*wct(ix^d,mag(3)) &
1352 -wct(ix^d,mom(3))*wct(ix^d,mag(1)) &
1353 -(wct(ix^d,mom(3))*wct(ix^d,mag(2)) &
1354 -wct(ix^d,mom(2))*wct(ix^d,mag(3)))*cs2)
1355 end if
1356 }
1357 end if
1358 {end do\}
1359 end select
1360
1361 end subroutine mf_add_source_geom
1362
1363 subroutine mf_modify_wlr(ixI^L,ixO^L,qt,wLC,wRC,wLp,wRp,s,idir)
1365 use mod_usr_methods
1366 integer, intent(in) :: ixi^l, ixo^l, idir
1367 double precision, intent(in) :: qt
1368 double precision, intent(inout) :: wlc(ixi^s,1:nw), wrc(ixi^s,1:nw)
1369 double precision, intent(inout) :: wlp(ixi^s,1:nw), wrp(ixi^s,1:nw)
1370 type(state) :: s
1371 double precision :: db(ixi^s), dpsi(ixi^s)
1372
1373 ! Solve the Riemann problem for the linear 2x2 system for normal
1374 ! B-field and GLM_Psi according to Dedner 2002:
1375 ! This implements eq. (42) in Dedner et al. 2002 JcP 175
1376 ! Gives the Riemann solution on the interface
1377 ! for the normal B component and Psi in the GLM-MHD system.
1378 ! 23/04/2013 Oliver Porth
1379 db(ixo^s) = wrp(ixo^s,mag(idir)) - wlp(ixo^s,mag(idir))
1380 dpsi(ixo^s) = wrp(ixo^s,psi_) - wlp(ixo^s,psi_)
1381
1382 wlp(ixo^s,mag(idir)) = 0.5d0 * (wrp(ixo^s,mag(idir)) + wlp(ixo^s,mag(idir))) &
1383 - 0.5d0/cmax_global * dpsi(ixo^s)
1384 wlp(ixo^s,psi_) = 0.5d0 * (wrp(ixo^s,psi_) + wlp(ixo^s,psi_)) &
1385 - 0.5d0*cmax_global * db(ixo^s)
1386
1387 wrp(ixo^s,mag(idir)) = wlp(ixo^s,mag(idir))
1388 wrp(ixo^s,psi_) = wlp(ixo^s,psi_)
1389
1390 end subroutine mf_modify_wlr
1391
1392 subroutine mf_boundary_adjust(igrid,psb)
1394 integer, intent(in) :: igrid
1395 type(state), target :: psb(max_blocks)
1396
1397 integer :: ib, idims, iside, ixo^l, i^d
1398
1399 block=>ps(igrid)
1400 ^d&dxlevel(^d)=rnode(rpdx^d_,igrid);
1401 do idims=1,ndim
1402 ! to avoid using as yet unknown corner info in more than 1D, we
1403 ! fill only interior mesh ranges of the ghost cell ranges at first,
1404 ! and progressively enlarge the ranges to include corners later
1405 do iside=1,2
1406 i^d=kr(^d,idims)*(2*iside-3);
1407 if (neighbor_type(i^d,igrid)/=1) cycle
1408 ib=(idims-1)*2+iside
1409 if(.not.boundary_divbfix(ib)) cycle
1410 if(any(typeboundary(:,ib)==bc_special)) then
1411 ! MF nonlinear force-free B field extrapolation and data driven
1412 ! require normal B of the first ghost cell layer to be untouched by
1413 ! fixdivB=0 process, set boundary_divbfix_skip(iB)=1 in par file
1414 select case (idims)
1415 {case (^d)
1416 if (iside==2) then
1417 ! maximal boundary
1418 ixomin^dd=ixghi^d+1-nghostcells+boundary_divbfix_skip(2*^d)^d%ixOmin^dd=ixglo^dd;
1419 ixomax^dd=ixghi^dd;
1420 else
1421 ! minimal boundary
1422 ixomin^dd=ixglo^dd;
1423 ixomax^dd=ixglo^d-1+nghostcells-boundary_divbfix_skip(2*^d-1)^d%ixOmax^dd=ixghi^dd;
1424 end if \}
1425 end select
1426 call fixdivb_boundary(ixg^ll,ixo^l,psb(igrid)%w,psb(igrid)%x,ib)
1427 end if
1428 end do
1429 end do
1430
1431 end subroutine mf_boundary_adjust
1432
1433 subroutine fixdivb_boundary(ixG^L,ixO^L,w,x,iB)
1435
1436 integer, intent(in) :: ixg^l,ixo^l,ib
1437 double precision, intent(inout) :: w(ixg^s,1:nw)
1438 double precision, intent(in) :: x(ixg^s,1:ndim)
1439
1440 double precision :: dx1x2,dx1x3,dx2x1,dx2x3,dx3x1,dx3x2
1441 integer :: ix^d,ixf^l
1442
1443 select case(ib)
1444 case(1)
1445 ! 2nd order CD for divB=0 to set normal B component better
1446 {^iftwod
1447 ixfmin1=ixomin1+1
1448 ixfmax1=ixomax1+1
1449 ixfmin2=ixomin2+1
1450 ixfmax2=ixomax2-1
1451 if(slab_uniform) then
1452 dx1x2=dxlevel(1)/dxlevel(2)
1453 do ix1=ixfmax1,ixfmin1,-1
1454 w(ix1-1,ixfmin2:ixfmax2,mag(1))=w(ix1+1,ixfmin2:ixfmax2,mag(1)) &
1455 +dx1x2*(w(ix1,ixfmin2+1:ixfmax2+1,mag(2))-&
1456 w(ix1,ixfmin2-1:ixfmax2-1,mag(2)))
1457 enddo
1458 else
1459 do ix1=ixfmax1,ixfmin1,-1
1460 w(ix1-1,ixfmin2:ixfmax2,mag(1))=( (w(ix1+1,ixfmin2:ixfmax2,mag(1))+&
1461 w(ix1,ixfmin2:ixfmax2,mag(1)))*block%surfaceC(ix1,ixfmin2:ixfmax2,1)&
1462 +(w(ix1,ixfmin2+1:ixfmax2+1,mag(2))+w(ix1,ixfmin2:ixfmax2,mag(2)))*&
1463 block%surfaceC(ix1,ixfmin2:ixfmax2,2)&
1464 -(w(ix1,ixfmin2:ixfmax2,mag(2))+w(ix1,ixfmin2-1:ixfmax2-1,mag(2)))*&
1465 block%surfaceC(ix1,ixfmin2-1:ixfmax2-1,2) )&
1466 /block%surfaceC(ix1-1,ixfmin2:ixfmax2,1)-w(ix1,ixfmin2:ixfmax2,mag(1))
1467 end do
1468 end if
1469 }
1470 {^ifthreed
1471 ixfmin1=ixomin1+1
1472 ixfmax1=ixomax1+1
1473 ixfmin2=ixomin2+1
1474 ixfmax2=ixomax2-1
1475 ixfmin3=ixomin3+1
1476 ixfmax3=ixomax3-1
1477 if(slab_uniform) then
1478 dx1x2=dxlevel(1)/dxlevel(2)
1479 dx1x3=dxlevel(1)/dxlevel(3)
1480 do ix1=ixfmax1,ixfmin1,-1
1481 w(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))=&
1482 w(ix1+1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1)) &
1483 +dx1x2*(w(ix1,ixfmin2+1:ixfmax2+1,ixfmin3:ixfmax3,mag(2))-&
1484 w(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,mag(2))) &
1485 +dx1x3*(w(ix1,ixfmin2:ixfmax2,ixfmin3+1:ixfmax3+1,mag(3))-&
1486 w(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,mag(3)))
1487 end do
1488 else
1489 do ix1=ixfmax1,ixfmin1,-1
1490 w(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))=&
1491 ( (w(ix1+1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))+&
1492 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1)))*&
1493 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,1)&
1494 +(w(ix1,ixfmin2+1:ixfmax2+1,ixfmin3:ixfmax3,mag(2))+&
1495 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(2)))*&
1496 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,2)&
1497 -(w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(2))+&
1498 w(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,mag(2)))*&
1499 block%surfaceC(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,2)&
1500 +(w(ix1,ixfmin2:ixfmax2,ixfmin3+1:ixfmax3+1,mag(3))+&
1501 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(3)))*&
1502 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,3)&
1503 -(w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(3))+&
1504 w(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,mag(3)))*&
1505 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,3) )&
1506 /block%surfaceC(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,1)-&
1507 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))
1508 end do
1509 end if
1510 }
1511 case(2)
1512 {^iftwod
1513 ixfmin1=ixomin1-1
1514 ixfmax1=ixomax1-1
1515 ixfmin2=ixomin2+1
1516 ixfmax2=ixomax2-1
1517 if(slab_uniform) then
1518 dx1x2=dxlevel(1)/dxlevel(2)
1519 do ix1=ixfmin1,ixfmax1
1520 w(ix1+1,ixfmin2:ixfmax2,mag(1))=w(ix1-1,ixfmin2:ixfmax2,mag(1)) &
1521 -dx1x2*(w(ix1,ixfmin2+1:ixfmax2+1,mag(2))-&
1522 w(ix1,ixfmin2-1:ixfmax2-1,mag(2)))
1523 enddo
1524 else
1525 do ix1=ixfmin1,ixfmax1
1526 w(ix1+1,ixfmin2:ixfmax2,mag(1))=( (w(ix1-1,ixfmin2:ixfmax2,mag(1))+&
1527 w(ix1,ixfmin2:ixfmax2,mag(1)))*block%surfaceC(ix1-1,ixfmin2:ixfmax2,1)&
1528 -(w(ix1,ixfmin2+1:ixfmax2+1,mag(2))+w(ix1,ixfmin2:ixfmax2,mag(2)))*&
1529 block%surfaceC(ix1,ixfmin2:ixfmax2,2)&
1530 +(w(ix1,ixfmin2:ixfmax2,mag(2))+w(ix1,ixfmin2-1:ixfmax2-1,mag(2)))*&
1531 block%surfaceC(ix1,ixfmin2-1:ixfmax2-1,2) )&
1532 /block%surfaceC(ix1,ixfmin2:ixfmax2,1)-w(ix1,ixfmin2:ixfmax2,mag(1))
1533 end do
1534 end if
1535 }
1536 {^ifthreed
1537 ixfmin1=ixomin1-1
1538 ixfmax1=ixomax1-1
1539 ixfmin2=ixomin2+1
1540 ixfmax2=ixomax2-1
1541 ixfmin3=ixomin3+1
1542 ixfmax3=ixomax3-1
1543 if(slab_uniform) then
1544 dx1x2=dxlevel(1)/dxlevel(2)
1545 dx1x3=dxlevel(1)/dxlevel(3)
1546 do ix1=ixfmin1,ixfmax1
1547 w(ix1+1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))=&
1548 w(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1)) &
1549 -dx1x2*(w(ix1,ixfmin2+1:ixfmax2+1,ixfmin3:ixfmax3,mag(2))-&
1550 w(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,mag(2))) &
1551 -dx1x3*(w(ix1,ixfmin2:ixfmax2,ixfmin3+1:ixfmax3+1,mag(3))-&
1552 w(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,mag(3)))
1553 end do
1554 else
1555 do ix1=ixfmin1,ixfmax1
1556 w(ix1+1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))=&
1557 ( (w(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))+&
1558 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1)))*&
1559 block%surfaceC(ix1-1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,1)&
1560 -(w(ix1,ixfmin2+1:ixfmax2+1,ixfmin3:ixfmax3,mag(2))+&
1561 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(2)))*&
1562 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,2)&
1563 +(w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(2))+&
1564 w(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,mag(2)))*&
1565 block%surfaceC(ix1,ixfmin2-1:ixfmax2-1,ixfmin3:ixfmax3,2)&
1566 -(w(ix1,ixfmin2:ixfmax2,ixfmin3+1:ixfmax3+1,mag(3))+&
1567 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(3)))*&
1568 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,3)&
1569 +(w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(3))+&
1570 w(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,mag(3)))*&
1571 block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3-1:ixfmax3-1,3) )&
1572 /block%surfaceC(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,1)-&
1573 w(ix1,ixfmin2:ixfmax2,ixfmin3:ixfmax3,mag(1))
1574 end do
1575 end if
1576 }
1577 case(3)
1578 {^iftwod
1579 ixfmin1=ixomin1+1
1580 ixfmax1=ixomax1-1
1581 ixfmin2=ixomin2+1
1582 ixfmax2=ixomax2+1
1583 if(slab_uniform) then
1584 dx2x1=dxlevel(2)/dxlevel(1)
1585 do ix2=ixfmax2,ixfmin2,-1
1586 w(ixfmin1:ixfmax1,ix2-1,mag(2))=w(ixfmin1:ixfmax1,ix2+1,mag(2)) &
1587 +dx2x1*(w(ixfmin1+1:ixfmax1+1,ix2,mag(1))-&
1588 w(ixfmin1-1:ixfmax1-1,ix2,mag(1)))
1589 enddo
1590 else
1591 do ix2=ixfmax2,ixfmin2,-1
1592 w(ixfmin1:ixfmax1,ix2-1,mag(2))=( (w(ixfmin1:ixfmax1,ix2+1,mag(2))+&
1593 w(ixfmin1:ixfmax1,ix2,mag(2)))*block%surfaceC(ixfmin1:ixfmax1,ix2,2)&
1594 +(w(ixfmin1+1:ixfmax1+1,ix2,mag(1))+w(ixfmin1:ixfmax1,ix2,mag(1)))*&
1595 block%surfaceC(ixfmin1:ixfmax1,ix2,1)&
1596 -(w(ixfmin1:ixfmax1,ix2,mag(1))+w(ixfmin1-1:ixfmax1-1,ix2,mag(1)))*&
1597 block%surfaceC(ixfmin1-1:ixfmax1-1,ix2,1) )&
1598 /block%surfaceC(ixfmin1:ixfmax1,ix2-1,2)-w(ixfmin1:ixfmax1,ix2,mag(2))
1599 end do
1600 end if
1601 }
1602 {^ifthreed
1603 ixfmin1=ixomin1+1
1604 ixfmax1=ixomax1-1
1605 ixfmin3=ixomin3+1
1606 ixfmax3=ixomax3-1
1607 ixfmin2=ixomin2+1
1608 ixfmax2=ixomax2+1
1609 if(slab_uniform) then
1610 dx2x1=dxlevel(2)/dxlevel(1)
1611 dx2x3=dxlevel(2)/dxlevel(3)
1612 do ix2=ixfmax2,ixfmin2,-1
1613 w(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,mag(2))=w(ixfmin1:ixfmax1,&
1614 ix2+1,ixfmin3:ixfmax3,mag(2)) &
1615 +dx2x1*(w(ixfmin1+1:ixfmax1+1,ix2,ixfmin3:ixfmax3,mag(1))-&
1616 w(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,mag(1))) &
1617 +dx2x3*(w(ixfmin1:ixfmax1,ix2,ixfmin3+1:ixfmax3+1,mag(3))-&
1618 w(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,mag(3)))
1619 end do
1620 else
1621 do ix2=ixfmax2,ixfmin2,-1
1622 w(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,mag(2))=&
1623 ( (w(ixfmin1:ixfmax1,ix2+1,ixfmin3:ixfmax3,mag(2))+&
1624 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(2)))*&
1625 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,2)&
1626 +(w(ixfmin1+1:ixfmax1+1,ix2,ixfmin3:ixfmax3,mag(1))+&
1627 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(1)))*&
1628 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,1)&
1629 -(w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(1))+&
1630 w(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,mag(1)))*&
1631 block%surfaceC(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,1)&
1632 +(w(ixfmin1:ixfmax1,ix2,ixfmin3+1:ixfmax3+1,mag(3))+&
1633 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(3)))*&
1634 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,3)&
1635 -(w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(3))+&
1636 w(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,mag(3)))*&
1637 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,3) )&
1638 /block%surfaceC(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,2)-&
1639 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(2))
1640 end do
1641 end if
1642 }
1643 case(4)
1644 {^iftwod
1645 ixfmin1=ixomin1+1
1646 ixfmax1=ixomax1-1
1647 ixfmin2=ixomin2-1
1648 ixfmax2=ixomax2-1
1649 if(slab_uniform) then
1650 dx2x1=dxlevel(2)/dxlevel(1)
1651 do ix2=ixfmin2,ixfmax2
1652 w(ixfmin1:ixfmax1,ix2+1,mag(2))=w(ixfmin1:ixfmax1,ix2-1,mag(2)) &
1653 -dx2x1*(w(ixfmin1+1:ixfmax1+1,ix2,mag(1))-&
1654 w(ixfmin1-1:ixfmax1-1,ix2,mag(1)))
1655 end do
1656 else
1657 do ix2=ixfmin2,ixfmax2
1658 w(ixfmin1:ixfmax1,ix2+1,mag(2))=( (w(ixfmin1:ixfmax1,ix2-1,mag(2))+&
1659 w(ixfmin1:ixfmax1,ix2,mag(2)))*block%surfaceC(ixfmin1:ixfmax1,ix2-1,2)&
1660 -(w(ixfmin1+1:ixfmax1+1,ix2,mag(1))+w(ixfmin1:ixfmax1,ix2,mag(1)))*&
1661 block%surfaceC(ixfmin1:ixfmax1,ix2,1)&
1662 +(w(ixfmin1:ixfmax1,ix2,mag(1))+w(ixfmin1-1:ixfmax1-1,ix2,mag(1)))*&
1663 block%surfaceC(ixfmin1-1:ixfmax1-1,ix2,1) )&
1664 /block%surfaceC(ixfmin1:ixfmax1,ix2,2)-w(ixfmin1:ixfmax1,ix2,mag(2))
1665 end do
1666 end if
1667 }
1668 {^ifthreed
1669 ixfmin1=ixomin1+1
1670 ixfmax1=ixomax1-1
1671 ixfmin3=ixomin3+1
1672 ixfmax3=ixomax3-1
1673 ixfmin2=ixomin2-1
1674 ixfmax2=ixomax2-1
1675 if(slab_uniform) then
1676 dx2x1=dxlevel(2)/dxlevel(1)
1677 dx2x3=dxlevel(2)/dxlevel(3)
1678 do ix2=ixfmin2,ixfmax2
1679 w(ixfmin1:ixfmax1,ix2+1,ixfmin3:ixfmax3,mag(2))=w(ixfmin1:ixfmax1,&
1680 ix2-1,ixfmin3:ixfmax3,mag(2)) &
1681 -dx2x1*(w(ixfmin1+1:ixfmax1+1,ix2,ixfmin3:ixfmax3,mag(1))-&
1682 w(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,mag(1))) &
1683 -dx2x3*(w(ixfmin1:ixfmax1,ix2,ixfmin3+1:ixfmax3+1,mag(3))-&
1684 w(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,mag(3)))
1685 end do
1686 else
1687 do ix2=ixfmin2,ixfmax2
1688 w(ixfmin1:ixfmax1,ix2+1,ixfmin3:ixfmax3,mag(2))=&
1689 ( (w(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,mag(2))+&
1690 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(2)))*&
1691 block%surfaceC(ixfmin1:ixfmax1,ix2-1,ixfmin3:ixfmax3,2)&
1692 -(w(ixfmin1+1:ixfmax1+1,ix2,ixfmin3:ixfmax3,mag(1))+&
1693 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(1)))*&
1694 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,1)&
1695 +(w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(1))+&
1696 w(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,mag(1)))*&
1697 block%surfaceC(ixfmin1-1:ixfmax1-1,ix2,ixfmin3:ixfmax3,1)&
1698 -(w(ixfmin1:ixfmax1,ix2,ixfmin3+1:ixfmax3+1,mag(3))+&
1699 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(3)))*&
1700 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,3)&
1701 +(w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(3))+&
1702 w(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,mag(3)))*&
1703 block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3-1:ixfmax3-1,3) )&
1704 /block%surfaceC(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,2)-&
1705 w(ixfmin1:ixfmax1,ix2,ixfmin3:ixfmax3,mag(2))
1706 end do
1707 end if
1708 }
1709 {^ifthreed
1710 case(5)
1711 ixfmin1=ixomin1+1
1712 ixfmax1=ixomax1-1
1713 ixfmin2=ixomin2+1
1714 ixfmax2=ixomax2-1
1715 ixfmin3=ixomin3+1
1716 ixfmax3=ixomax3+1
1717 if(slab_uniform) then
1718 dx3x1=dxlevel(3)/dxlevel(1)
1719 dx3x2=dxlevel(3)/dxlevel(2)
1720 do ix3=ixfmax3,ixfmin3,-1
1721 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,mag(3))=w(ixfmin1:ixfmax1,&
1722 ixfmin2:ixfmax2,ix3+1,mag(3)) &
1723 +dx3x1*(w(ixfmin1+1:ixfmax1+1,ixfmin2:ixfmax2,ix3,mag(1))-&
1724 w(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,mag(1))) &
1725 +dx3x2*(w(ixfmin1:ixfmax1,ixfmin2+1:ixfmax2+1,ix3,mag(2))-&
1726 w(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,mag(2)))
1727 end do
1728 else
1729 do ix3=ixfmax3,ixfmin3,-1
1730 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,mag(3))=&
1731 ( (w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3+1,mag(3))+&
1732 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(3)))*&
1733 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,3)&
1734 +(w(ixfmin1+1:ixfmax1+1,ixfmin2:ixfmax2,ix3,mag(1))+&
1735 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(1)))*&
1736 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,1)&
1737 -(w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(1))+&
1738 w(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,mag(1)))*&
1739 block%surfaceC(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,1)&
1740 +(w(ixfmin1:ixfmax1,ixfmin2+1:ixfmax2+1,ix3,mag(2))+&
1741 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(2)))*&
1742 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,2)&
1743 -(w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(2))+&
1744 w(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,mag(2)))*&
1745 block%surfaceC(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,2) )&
1746 /block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,3)-&
1747 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(3))
1748 end do
1749 end if
1750 case(6)
1751 ixfmin1=ixomin1+1
1752 ixfmax1=ixomax1-1
1753 ixfmin2=ixomin2+1
1754 ixfmax2=ixomax2-1
1755 ixfmin3=ixomin3-1
1756 ixfmax3=ixomax3-1
1757 if(slab_uniform) then
1758 dx3x1=dxlevel(3)/dxlevel(1)
1759 dx3x2=dxlevel(3)/dxlevel(2)
1760 do ix3=ixfmin3,ixfmax3
1761 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3+1,mag(3))=w(ixfmin1:ixfmax1,&
1762 ixfmin2:ixfmax2,ix3-1,mag(3)) &
1763 -dx3x1*(w(ixfmin1+1:ixfmax1+1,ixfmin2:ixfmax2,ix3,mag(1))-&
1764 w(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,mag(1))) &
1765 -dx3x2*(w(ixfmin1:ixfmax1,ixfmin2+1:ixfmax2+1,ix3,mag(2))-&
1766 w(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,mag(2)))
1767 end do
1768 else
1769 do ix3=ixfmin3,ixfmax3
1770 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3+1,mag(3))=&
1771 ( (w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,mag(3))+&
1772 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(3)))*&
1773 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3-1,3)&
1774 -(w(ixfmin1+1:ixfmax1+1,ixfmin2:ixfmax2,ix3,mag(1))+&
1775 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(1)))*&
1776 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,1)&
1777 +(w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(1))+&
1778 w(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,mag(1)))*&
1779 block%surfaceC(ixfmin1-1:ixfmax1-1,ixfmin2:ixfmax2,ix3,1)&
1780 -(w(ixfmin1:ixfmax1,ixfmin2+1:ixfmax2+1,ix3,mag(2))+&
1781 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(2)))*&
1782 block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,2)&
1783 +(w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(2))+&
1784 w(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,mag(2)))*&
1785 block%surfaceC(ixfmin1:ixfmax1,ixfmin2-1:ixfmax2-1,ix3,2) )&
1786 /block%surfaceC(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,3)-&
1787 w(ixfmin1:ixfmax1,ixfmin2:ixfmax2,ix3,mag(3))
1788 end do
1789 end if
1790 }
1791 case default
1792 call mpistop("Special boundary is not defined for this region")
1793 end select
1794
1795 end subroutine fixdivb_boundary
1796
1797 {^nooned
1798 subroutine mf_clean_divb_multigrid(qdt, qt, active)
1799 use mod_forest
1802 use mod_geometry
1803
1804 double precision, intent(in) :: qdt !< Current time step
1805 double precision, intent(in) :: qt !< Current time
1806 logical, intent(inout) :: active !< Output if the source is active
1807
1808 double precision :: tmp(ixg^t), grad(ixg^t, ndim)
1809 double precision :: res
1810 double precision, parameter :: max_residual = 1d-3
1811 double precision, parameter :: residual_reduction = 1d-10
1812 integer :: id
1813 integer, parameter :: max_its = 50
1814 double precision :: residual_it(max_its), max_divb
1815 integer :: iigrid, igrid
1816 integer :: n, nc, lvl, ix^l, ixc^l, idim
1817 type(tree_node), pointer :: pnode
1818
1819 mg%operator_type = mg_laplacian
1820
1821 ! Set boundary conditions
1822 do n = 1, 2*ndim
1823 idim = (n+1)/2
1824 select case (typeboundary(mag(idim), n))
1825 case (bc_symm)
1826 ! d/dx B = 0, take phi = 0
1827 mg%bc(n, mg_iphi)%bc_type = mg_bc_dirichlet
1828 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
1829 case (bc_asymm)
1830 ! B = 0, so grad(phi) = 0
1831 mg%bc(n, mg_iphi)%bc_type = mg_bc_neumann
1832 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
1833 case (bc_cont)
1834 mg%bc(n, mg_iphi)%bc_type = mg_bc_dirichlet
1835 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
1836 case (bc_special)
1837 ! Assume Dirichlet boundary conditions, derivative zero
1838 mg%bc(n, mg_iphi)%bc_type = mg_bc_neumann
1839 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
1840 case (bc_periodic)
1841 ! Nothing to do here
1842 case default
1843 write(*,*) "mf_clean_divb_multigrid warning: unknown boundary type"
1844 mg%bc(n, mg_iphi)%bc_type = mg_bc_dirichlet
1845 mg%bc(n, mg_iphi)%bc_value = 0.0_dp
1846 end select
1847 end do
1848
1849 ix^l=ixm^ll^ladd1;
1850 max_divb = 0.0d0
1851
1852 ! Store divergence of B as right-hand side
1853 do iigrid = 1, igridstail
1854 igrid = igrids(iigrid);
1855 pnode => igrid_to_node(igrid, mype)%node
1856 id = pnode%id
1857 lvl = mg%boxes(id)%lvl
1858 nc = mg%box_size_lvl(lvl)
1859
1860 ! Geometry subroutines expect this to be set
1861 block => ps(igrid)
1862 ^d&dxlevel(^d)=rnode(rpdx^d_,igrid);
1863
1864 call get_divb(ps(igrid)%w(ixg^t, 1:nw), ixg^ll, ixm^ll, tmp, &
1866 mg%boxes(id)%cc({1:nc}, mg_irhs) = tmp(ixm^t)
1867 max_divb = max(max_divb, maxval(abs(tmp(ixm^t))))
1868 end do
1869
1870 ! Solve laplacian(phi) = divB
1871 if(stagger_grid) then
1872 call mpi_allreduce(mpi_in_place, max_divb, 1, mpi_double_precision, &
1873 mpi_max, icomm, ierrmpi)
1874
1875 if (mype == 0) print *, "Performing multigrid divB cleaning"
1876 if (mype == 0) print *, "iteration vs residual"
1877 ! Solve laplacian(phi) = divB
1878 do n = 1, max_its
1879 call mg_fas_fmg(mg, n>1, max_res=residual_it(n))
1880 if (mype == 0) write(*, "(I4,E11.3)") n, residual_it(n)
1881 if (residual_it(n) < residual_reduction * max_divb) exit
1882 end do
1883 if (mype == 0 .and. n > max_its) then
1884 print *, "divb_multigrid warning: not fully converged"
1885 print *, "current amplitude of divb: ", residual_it(max_its)
1886 print *, "multigrid smallest grid: ", &
1887 mg%domain_size_lvl(:, mg%lowest_lvl)
1888 print *, "note: smallest grid ideally has <= 8 cells"
1889 print *, "multigrid dx/dy/dz ratio: ", mg%dr(:, 1)/mg%dr(1, 1)
1890 print *, "note: dx/dy/dz should be similar"
1891 end if
1892 else
1893 do n = 1, max_its
1894 call mg_fas_vcycle(mg, max_res=res)
1895 if (res < max_residual) exit
1896 end do
1897 if (res > max_residual) call mpistop("divb_multigrid: no convergence")
1898 end if
1899
1900
1901 ! Correct the magnetic field
1902 do iigrid = 1, igridstail
1903 igrid = igrids(iigrid);
1904 pnode => igrid_to_node(igrid, mype)%node
1905 id = pnode%id
1906
1907 ! Geometry subroutines expect this to be set
1908 block => ps(igrid)
1909 ^d&dxlevel(^d)=rnode(rpdx^d_,igrid);
1910
1911 ! Compute the gradient of phi
1912 tmp(ix^s) = mg%boxes(id)%cc({:,}, mg_iphi)
1913
1914 if(stagger_grid) then
1915 do idim =1, ndim
1916 ixcmin^d=ixmlo^d-kr(idim,^d);
1917 ixcmax^d=ixmhi^d;
1918 call gradientf(tmp,ps(igrid)%x,ixg^ll,ixc^l,idim,grad(ixg^t,idim))
1919 ps(igrid)%ws(ixc^s,idim)=ps(igrid)%ws(ixc^s,idim)-grad(ixc^s,idim)
1920 end do
1921 call mf_face_to_center(ixm^ll,ps(igrid))
1922 else
1923 do idim = 1, ndim
1924 call gradient(tmp,ixg^ll,ixm^ll,idim,grad(ixg^t, idim))
1925 end do
1926 ! Apply the correction B* = B - gradient(phi)
1927 ps(igrid)%w(ixm^t, mag(1:ndim)) = &
1928 ps(igrid)%w(ixm^t, mag(1:ndim)) - grad(ixm^t, :)
1929 end if
1930
1931 end do
1932
1933 active = .true.
1934
1935 end subroutine mf_clean_divb_multigrid
1936 }
1937
1938 !> get electric field though averaging neighors to update faces in CT
1939 subroutine mf_update_faces_average(ixI^L,ixO^L,qt,qdt,wp,fC,fE,sCT,s,vcts)
1941 use mod_usr_methods
1942
1943 integer, intent(in) :: ixi^l, ixo^l
1944 double precision, intent(in) :: qt, qdt
1945 ! cell-center primitive variables
1946 double precision, intent(in) :: wp(ixi^s,1:nw)
1947 type(state) :: sct, s
1948 type(ct_velocity) :: vcts
1949 double precision, intent(in) :: fc(ixi^s,1:nwflux,1:ndim)
1950 double precision, intent(inout) :: fe(ixi^s,sdim:3)
1951
1952 double precision :: circ(ixi^s,1:ndim)
1953 double precision :: e_resi(ixi^s,sdim:3)
1954 integer :: ix^d,ixc^l,ixa^l,i1kr^d,i2kr^d
1955 integer :: idim1,idim2,idir,iwdim1,iwdim2
1956
1957 associate(bfaces=>s%ws,x=>s%x)
1958
1959 ! Calculate contribution to FEM of each edge,
1960 ! that is, estimate value of line integral of
1961 ! electric field in the positive idir direction.
1962
1963 ! if there is resistivity, get eta J
1964 if(mf_eta/=zero) call get_resistive_electric_field(ixi^l,ixo^l,sct,s,e_resi)
1965
1966 do idim1=1,ndim
1967 iwdim1 = mag(idim1)
1968 i1kr^d=kr(idim1,^d);
1969 do idim2=1,ndim
1970 iwdim2 = mag(idim2)
1971 i2kr^d=kr(idim2,^d);
1972 do idir=sdim,3! Direction of line integral
1973 ! Allow only even permutations
1974 if (lvc(idim1,idim2,idir)==1) then
1975 ixcmax^d=ixomax^d;
1976 ixcmin^d=ixomin^d+kr(idir,^d)-1;
1977 ! average cell-face electric field to cell edges
1978 {do ix^db=ixcmin^db,ixcmax^db\}
1979 fe(ix^d,idir)=quarter*&
1980 (fc(ix^d,iwdim1,idim2)+fc({ix^d+i1kr^d},iwdim1,idim2)&
1981 -fc(ix^d,iwdim2,idim1)-fc({ix^d+i2kr^d},iwdim2,idim1))
1982 ! add resistive electric field at cell edges E=-vxB+eta J
1983 if(mf_eta/=zero) fe(ix^d,idir)=fe(ix^d,idir)+e_resi(ix^d,idir)
1984 if(record_electric_field) s%we(ix^d,idir)=fe(ix^d,idir)
1985 ! times time step and edge length
1986 fe(ix^d,idir)=fe(ix^d,idir)*qdt*s%dsC(ix^d,idir)
1987 {end do\}
1988 end if
1989 end do
1990 end do
1991 end do
1992
1993 ! allow user to change inductive electric field, especially for boundary driven applications
1994 if(associated(usr_set_electric_field)) &
1995 call usr_set_electric_field(ixi^l,ixo^l,qt,qdt,fe,sct)
1996
1997 circ(ixi^s,1:ndim)=zero
1998
1999 ! Calculate circulation on each face
2000
2001 do idim1=1,ndim ! Coordinate perpendicular to face
2002 ixcmax^d=ixomax^d;
2003 ixcmin^d=ixomin^d-kr(idim1,^d);
2004 do idim2=1,ndim
2005 ixa^l=ixc^l-kr(idim2,^d);
2006 do idir=sdim,3 ! Direction of line integral
2007 ! Assemble indices
2008 if(lvc(idim1,idim2,idir)==1) then
2009 ! Add line integrals in direction idir
2010 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
2011 +(fe(ixc^s,idir)&
2012 -fe(ixa^s,idir))
2013 else if(lvc(idim1,idim2,idir)==-1) then
2014 ! Add line integrals in direction idir
2015 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
2016 -(fe(ixc^s,idir)&
2017 -fe(ixa^s,idir))
2018 end if
2019 end do
2020 end do
2021 ! Divide by the area of the face to get dB/dt
2022 circ(ixc^s,idim1)=circ(ixc^s,idim1)/s%surfaceC(ixc^s,idim1)
2023 ! Time update
2024 bfaces(ixc^s,idim1)=bfaces(ixc^s,idim1)-circ(ixc^s,idim1)
2025 end do
2026
2027 end associate
2028
2029 end subroutine mf_update_faces_average
2030
2031 !> update faces using UCT contact mode by Gardiner and Stone 2005 JCP 205, 509
2032 subroutine mf_update_faces_contact(ixI^L,ixO^L,qt,qdt,wp,fC,fE,sCT,s,vcts)
2034 use mod_usr_methods
2035
2036 integer, intent(in) :: ixi^l, ixo^l
2037 double precision, intent(in) :: qt, qdt
2038 ! cell-center primitive variables
2039 double precision, intent(in) :: wp(ixi^s,1:nw)
2040 type(state) :: sct, s
2041 type(ct_velocity) :: vcts
2042 double precision, intent(in) :: fc(ixi^s,1:nwflux,1:ndim)
2043 double precision, intent(inout) :: fe(ixi^s,sdim:3)
2044
2045 double precision :: circ(ixi^s,1:ndim)
2046 ! electric field at cell centers
2047 double precision :: ecc(ixi^s,sdim:3)
2048 ! gradient of E at left and right side of a cell face
2049 double precision :: el(ixi^s),er(ixi^s)
2050 ! gradient of E at left and right side of a cell corner
2051 double precision :: elc(ixi^s),erc(ixi^s)
2052 ! current on cell edges
2053 double precision :: jce(ixi^s,sdim:3)
2054 integer :: hxc^l,ixc^l,jxc^l,ixa^l,ixb^l
2055 integer :: idim1,idim2,idir,iwdim1,iwdim2
2056
2057 associate(bfaces=>s%ws,x=>s%x,w=>s%w,vnorm=>vcts%vnorm)
2058
2059 ecc=0.d0
2060 ! Calculate electric field at cell centers
2061 do idim1=1,ndim; do idim2=1,ndim; do idir=sdim,3
2062 if(lvc(idim1,idim2,idir)==1)then
2063 ecc(ixi^s,idir)=ecc(ixi^s,idir)+wp(ixi^s,mag(idim1))*wp(ixi^s,mom(idim2))
2064 else if(lvc(idim1,idim2,idir)==-1) then
2065 ecc(ixi^s,idir)=ecc(ixi^s,idir)-wp(ixi^s,mag(idim1))*wp(ixi^s,mom(idim2))
2066 endif
2067 enddo; enddo; enddo
2068
2069 ! if there is resistivity, get eta J
2070 if(mf_eta/=zero) call get_resistive_electric_field(ixi^l,ixo^l,sct,s,jce)
2071
2072 ! Calculate contribution to FEM of each edge,
2073 ! that is, estimate value of line integral of
2074 ! electric field in the positive idir direction.
2075 ! evaluate electric field along cell edges according to equation (41)
2076 do idim1=1,ndim
2077 iwdim1 = mag(idim1)
2078 do idim2=1,ndim
2079 iwdim2 = mag(idim2)
2080 do idir=sdim,3 ! Direction of line integral
2081 ! Allow only even permutations
2082 if (lvc(idim1,idim2,idir)==1) then
2083 ixcmax^d=ixomax^d;
2084 ixcmin^d=ixomin^d+kr(idir,^d)-1;
2085 ! Assemble indices
2086 jxc^l=ixc^l+kr(idim1,^d);
2087 hxc^l=ixc^l+kr(idim2,^d);
2088 ! average cell-face electric field to cell edges
2089 fe(ixc^s,idir)=quarter*&
2090 (fc(ixc^s,iwdim1,idim2)+fc(jxc^s,iwdim1,idim2)&
2091 -fc(ixc^s,iwdim2,idim1)-fc(hxc^s,iwdim2,idim1))
2092
2093 ! add slope in idim2 direction from equation (50)
2094 ixamin^d=ixcmin^d;
2095 ixamax^d=ixcmax^d+kr(idim1,^d);
2096 el(ixa^s)=fc(ixa^s,iwdim1,idim2)-ecc(ixa^s,idir)
2097 hxc^l=ixa^l+kr(idim2,^d);
2098 er(ixa^s)=fc(ixa^s,iwdim1,idim2)-ecc(hxc^s,idir)
2099 where(vnorm(ixc^s,idim1)>0.d0)
2100 elc(ixc^s)=el(ixc^s)
2101 else where(vnorm(ixc^s,idim1)<0.d0)
2102 elc(ixc^s)=el(jxc^s)
2103 else where
2104 elc(ixc^s)=0.5d0*(el(ixc^s)+el(jxc^s))
2105 end where
2106 hxc^l=ixc^l+kr(idim2,^d);
2107 where(vnorm(hxc^s,idim1)>0.d0)
2108 erc(ixc^s)=er(ixc^s)
2109 else where(vnorm(hxc^s,idim1)<0.d0)
2110 erc(ixc^s)=er(jxc^s)
2111 else where
2112 erc(ixc^s)=0.5d0*(er(ixc^s)+er(jxc^s))
2113 end where
2114 fe(ixc^s,idir)=fe(ixc^s,idir)+0.25d0*(elc(ixc^s)+erc(ixc^s))
2115
2116 ! add slope in idim1 direction from equation (50)
2117 jxc^l=ixc^l+kr(idim2,^d);
2118 ixamin^d=ixcmin^d;
2119 ixamax^d=ixcmax^d+kr(idim2,^d);
2120 el(ixa^s)=-fc(ixa^s,iwdim2,idim1)-ecc(ixa^s,idir)
2121 hxc^l=ixa^l+kr(idim1,^d);
2122 er(ixa^s)=-fc(ixa^s,iwdim2,idim1)-ecc(hxc^s,idir)
2123 where(vnorm(ixc^s,idim2)>0.d0)
2124 elc(ixc^s)=el(ixc^s)
2125 else where(vnorm(ixc^s,idim2)<0.d0)
2126 elc(ixc^s)=el(jxc^s)
2127 else where
2128 elc(ixc^s)=0.5d0*(el(ixc^s)+el(jxc^s))
2129 end where
2130 hxc^l=ixc^l+kr(idim1,^d);
2131 where(vnorm(hxc^s,idim2)>0.d0)
2132 erc(ixc^s)=er(ixc^s)
2133 else where(vnorm(hxc^s,idim2)<0.d0)
2134 erc(ixc^s)=er(jxc^s)
2135 else where
2136 erc(ixc^s)=0.5d0*(er(ixc^s)+er(jxc^s))
2137 end where
2138 fe(ixc^s,idir)=fe(ixc^s,idir)+0.25d0*(elc(ixc^s)+erc(ixc^s))
2139
2140 ! add current component of electric field at cell edges E=-vxB+eta J
2141 if(mf_eta/=zero) fe(ixc^s,idir)=fe(ixc^s,idir)+jce(ixc^s,idir)
2142
2143 if(record_electric_field) s%we(ixc^s,idir)=fe(ixc^s,idir)
2144 ! times time step and edge length
2145 fe(ixc^s,idir)=fe(ixc^s,idir)*qdt*s%dsC(ixc^s,idir)
2146 if (.not.slab) then
2147 where(abs(x(ixc^s,r_)+half*dxlevel(r_))<1.0d-9)
2148 fe(ixc^s,idir)=zero
2149 end where
2150 end if
2151 end if
2152 end do
2153 end do
2154 end do
2155
2156 ! allow user to change inductive electric field, especially for boundary driven applications
2157 if(associated(usr_set_electric_field)) &
2158 call usr_set_electric_field(ixi^l,ixo^l,qt,qdt,fe,sct)
2159
2160 circ(ixi^s,1:ndim)=zero
2161
2162 ! Calculate circulation on each face
2163 do idim1=1,ndim ! Coordinate perpendicular to face
2164 ixcmax^d=ixomax^d;
2165 ixcmin^d=ixomin^d-kr(idim1,^d);
2166 do idim2=1,ndim
2167 do idir=sdim,3 ! Direction of line integral
2168 ! Assemble indices
2169 if(lvc(idim1,idim2,idir)/=0) then
2170 hxc^l=ixc^l-kr(idim2,^d);
2171 ! Add line integrals in direction idir
2172 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
2173 +lvc(idim1,idim2,idir)&
2174 *(fe(ixc^s,idir)&
2175 -fe(hxc^s,idir))
2176 end if
2177 end do
2178 end do
2179 ! Divide by the area of the face to get dB/dt
2180 where(s%surfaceC(ixc^s,idim1) > 1.0d-9*s%dvolume(ixc^s))
2181 circ(ixc^s,idim1)=circ(ixc^s,idim1)/s%surfaceC(ixc^s,idim1)
2182 elsewhere
2183 circ(ixc^s,idim1)=zero
2184 end where
2185 ! Time update cell-face magnetic field component
2186 bfaces(ixc^s,idim1)=bfaces(ixc^s,idim1)-circ(ixc^s,idim1)
2187 end do
2188
2189 end associate
2190
2191 end subroutine mf_update_faces_contact
2192
2193 !> update faces
2194 subroutine mf_update_faces_hll(ixI^L,ixO^L,qt,qdt,wp,fC,fE,sCT,s,vcts)
2196 use mod_usr_methods
2198
2199 integer, intent(in) :: ixi^l, ixo^l
2200 double precision, intent(in) :: qt, qdt
2201 ! cell-center primitive variables
2202 double precision, intent(in) :: wp(ixi^s,1:nw)
2203 type(state) :: sct, s
2204 type(ct_velocity) :: vcts
2205 double precision, intent(in) :: fc(ixi^s,1:nwflux,1:ndim)
2206 double precision, intent(inout) :: fe(ixi^s,sdim:3)
2207
2208 double precision :: vtill(ixi^s,2)
2209 double precision :: vtilr(ixi^s,2)
2210 double precision :: btill(ixi^s,ndim)
2211 double precision :: btilr(ixi^s,ndim)
2212 double precision :: cp(ixi^s,2)
2213 double precision :: cm(ixi^s,2)
2214 double precision :: circ(ixi^s,1:ndim)
2215 ! current on cell edges
2216 double precision :: jce(ixi^s,sdim:3)
2217 integer :: hxc^l,ixc^l,ixcp^l,jxc^l,ixcm^l
2218 integer :: idim1,idim2,idir
2219
2220 associate(bfaces=>s%ws,bfacesct=>sct%ws,x=>s%x,vbarc=>vcts%vbarC,cbarmin=>vcts%cbarmin,&
2221 cbarmax=>vcts%cbarmax)
2222
2223 ! Calculate contribution to FEM of each edge,
2224 ! that is, estimate value of line integral of
2225 ! electric field in the positive idir direction.
2226
2227 ! Loop over components of electric field
2228
2229 ! idir: electric field component we need to calculate
2230 ! idim1: directions in which we already performed the reconstruction
2231 ! idim2: directions in which we perform the reconstruction
2232
2233 ! if there is resistivity, get eta J
2234 if(mf_eta/=zero) call get_resistive_electric_field(ixi^l,ixo^l,sct,s,jce)
2235
2236 do idir=sdim,3
2237 ! Indices
2238 ! idir: electric field component
2239 ! idim1: one surface
2240 ! idim2: the other surface
2241 ! cyclic permutation: idim1,idim2,idir=1,2,3
2242 ! Velocity components on the surface
2243 ! follow cyclic premutations:
2244 ! Sx(1),Sx(2)=y,z ; Sy(1),Sy(2)=z,x ; Sz(1),Sz(2)=x,y
2245
2246 ixcmax^d=ixomax^d;
2247 ixcmin^d=ixomin^d-1+kr(idir,^d);
2248
2249 ! Set indices and directions
2250 idim1=mod(idir,3)+1
2251 idim2=mod(idir+1,3)+1
2252
2253 jxc^l=ixc^l+kr(idim1,^d);
2254 ixcp^l=ixc^l+kr(idim2,^d);
2255
2256 ! Reconstruct transverse transport velocities
2257 call reconstruct(ixi^l,ixc^l,idim2,vbarc(ixi^s,idim1,1),&
2258 vtill(ixi^s,2),vtilr(ixi^s,2))
2259
2260 call reconstruct(ixi^l,ixc^l,idim1,vbarc(ixi^s,idim2,2),&
2261 vtill(ixi^s,1),vtilr(ixi^s,1))
2262
2263 ! Reconstruct magnetic fields
2264 ! Eventhough the arrays are larger, reconstruct works with
2265 ! the limits ixG.
2266 call reconstruct(ixi^l,ixc^l,idim2,bfacesct(ixi^s,idim1),&
2267 btill(ixi^s,idim1),btilr(ixi^s,idim1))
2268
2269 call reconstruct(ixi^l,ixc^l,idim1,bfacesct(ixi^s,idim2),&
2270 btill(ixi^s,idim2),btilr(ixi^s,idim2))
2271
2272 ! Take the maximum characteristic
2273
2274 cm(ixc^s,1)=max(cbarmin(ixcp^s,idim1),cbarmin(ixc^s,idim1))
2275 cp(ixc^s,1)=max(cbarmax(ixcp^s,idim1),cbarmax(ixc^s,idim1))
2276
2277 cm(ixc^s,2)=max(cbarmin(jxc^s,idim2),cbarmin(ixc^s,idim2))
2278 cp(ixc^s,2)=max(cbarmax(jxc^s,idim2),cbarmax(ixc^s,idim2))
2279
2280
2281 ! Calculate eletric field
2282 fe(ixc^s,idir)=-(cp(ixc^s,1)*vtill(ixc^s,1)*btill(ixc^s,idim2) &
2283 + cm(ixc^s,1)*vtilr(ixc^s,1)*btilr(ixc^s,idim2) &
2284 - cp(ixc^s,1)*cm(ixc^s,1)*(btilr(ixc^s,idim2)-btill(ixc^s,idim2)))&
2285 /(cp(ixc^s,1)+cm(ixc^s,1)) &
2286 +(cp(ixc^s,2)*vtill(ixc^s,2)*btill(ixc^s,idim1) &
2287 + cm(ixc^s,2)*vtilr(ixc^s,2)*btilr(ixc^s,idim1) &
2288 - cp(ixc^s,2)*cm(ixc^s,2)*(btilr(ixc^s,idim1)-btill(ixc^s,idim1)))&
2289 /(cp(ixc^s,2)+cm(ixc^s,2))
2290
2291 ! add current component of electric field at cell edges E=-vxB+eta J
2292 if(mf_eta/=zero) fe(ixc^s,idir)=fe(ixc^s,idir)+jce(ixc^s,idir)
2293
2294 if(record_electric_field) s%we(ixc^s,idir)=fe(ixc^s,idir)
2295 ! times time step and edge length
2296 fe(ixc^s,idir)=qdt*s%dsC(ixc^s,idir)*fe(ixc^s,idir)
2297
2298 if (.not.slab) then
2299 where(abs(x(ixc^s,r_)+half*dxlevel(r_)).lt.1.0d-9)
2300 fe(ixc^s,idir)=zero
2301 end where
2302 end if
2303
2304 end do
2305
2306 ! allow user to change inductive electric field, especially for boundary driven applications
2307 if(associated(usr_set_electric_field)) &
2308 call usr_set_electric_field(ixi^l,ixo^l,qt,qdt,fe,sct)
2309
2310 circ(ixi^s,1:ndim)=zero
2311
2312 ! Calculate circulation on each face: interal(fE dot dl)
2313
2314 do idim1=1,ndim ! Coordinate perpendicular to face
2315 ixcmax^d=ixomax^d;
2316 ixcmin^d=ixomin^d-kr(idim1,^d);
2317 do idim2=1,ndim
2318 do idir=sdim,3 ! Direction of line integral
2319 ! Assemble indices
2320 if(lvc(idim1,idim2,idir)/=0) then
2321 hxc^l=ixc^l-kr(idim2,^d);
2322 ! Add line integrals in direction idir
2323 circ(ixc^s,idim1)=circ(ixc^s,idim1)&
2324 +lvc(idim1,idim2,idir)&
2325 *(fe(ixc^s,idir)&
2326 -fe(hxc^s,idir))
2327 end if
2328 end do
2329 end do
2330 ! Divide by the area of the face to get dB/dt
2331 where(s%surfaceC(ixc^s,idim1) > 1.0d-9*s%dvolume(ixc^s))
2332 circ(ixc^s,idim1)=circ(ixc^s,idim1)/s%surfaceC(ixc^s,idim1)
2333 elsewhere
2334 circ(ixc^s,idim1)=zero
2335 end where
2336 ! Time update
2337 bfaces(ixc^s,idim1)=bfaces(ixc^s,idim1)-circ(ixc^s,idim1)
2338 end do
2339
2340 end associate
2341 end subroutine mf_update_faces_hll
2342
2343 !> calculate eta J at cell edges
2344 subroutine get_resistive_electric_field(ixI^L,ixO^L,sCT,s,jce)
2346 use mod_usr_methods
2347 use mod_geometry
2348
2349 integer, intent(in) :: ixi^l, ixo^l
2350 type(state), intent(in) :: sct, s
2351 ! current on cell edges
2352 double precision :: jce(ixi^s,sdim:3)
2353
2354 ! current on cell centers
2355 double precision :: jcc(ixi^s,7-2*ndir:3)
2356 ! location at cell faces
2357 double precision :: xs(ixgs^t,1:ndim)
2358 ! resistivity
2359 double precision :: eta(ixi^s)
2360 double precision :: gradi(ixgs^t)
2361 integer :: ix^d,ixc^l,ixa^l,ixb^l,idir,idirmin,idim1,idim2
2362
2363 associate(x=>s%x,dx=>s%dx,w=>s%w,wct=>sct%w,wcts=>sct%ws)
2364 ! calculate current density at cell edges
2365 jce=0.d0
2366 do idim1=1,ndim
2367 do idim2=1,ndim
2368 do idir=sdim,3
2369 if (lvc(idim1,idim2,idir)==0) cycle
2370 ixcmax^d=ixomax^d+1;
2371 ixcmin^d=ixomin^d+kr(idir,^d)-2;
2372 ixbmax^d=ixcmax^d-kr(idir,^d)+1;
2373 ixbmin^d=ixcmin^d;
2374 ! current at transverse faces
2375 xs(ixb^s,:)=x(ixb^s,:)
2376 xs(ixb^s,idim2)=x(ixb^s,idim2)+half*dx(ixb^s,idim2)
2377 call gradientf(wcts(ixgs^t,idim2),xs,ixgs^ll,ixc^l,idim1,gradi)
2378 if (lvc(idim1,idim2,idir)==1) then
2379 jce(ixc^s,idir)=jce(ixc^s,idir)+gradi(ixc^s)
2380 else
2381 jce(ixc^s,idir)=jce(ixc^s,idir)-gradi(ixc^s)
2382 end if
2383 end do
2384 end do
2385 end do
2386 ! get resistivity
2387 if(mf_eta>zero)then
2388 jce(ixi^s,:)=jce(ixi^s,:)*mf_eta
2389 else
2390 ixa^l=ixo^l^ladd1;
2391 call get_current(wct,ixi^l,ixa^l,idirmin,jcc)
2392 call usr_special_resistivity(wct,ixi^l,ixa^l,idirmin,x,jcc,eta)
2393 ! calcuate eta on cell edges
2394 do idir=sdim,3
2395 ixcmax^d=ixomax^d;
2396 ixcmin^d=ixomin^d+kr(idir,^d)-1;
2397 jcc(ixc^s,idir)=0.d0
2398 {do ix^db=0,1\}
2399 if({ ix^d==1 .and. ^d==idir | .or.}) cycle
2400 ixamin^d=ixcmin^d+ix^d;
2401 ixamax^d=ixcmax^d+ix^d;
2402 jcc(ixc^s,idir)=jcc(ixc^s,idir)+eta(ixa^s)
2403 {end do\}
2404 jcc(ixc^s,idir)=jcc(ixc^s,idir)*0.25d0
2405 jce(ixc^s,idir)=jce(ixc^s,idir)*jcc(ixc^s,idir)
2406 enddo
2407 end if
2408
2409 end associate
2410 end subroutine get_resistive_electric_field
2411
2412 !> calculate cell-center values from face-center values
2413 subroutine mf_face_to_center(ixO^L,s)
2415 ! Non-staggered interpolation range
2416 integer, intent(in) :: ixo^l
2417 type(state) :: s
2418
2419 integer :: ix^d
2420
2421 ! calculate cell-center values from face-center values in 2nd order
2422 {!dir$ ivdep
2423 do ix^db=ixomin^db,ixomax^db\}
2424 {^ifthreed
2425 s%w(ix^d,mag(1))=half/s%surface(ix^d,1)*(s%ws(ix^d,1)*s%surfaceC(ix^d,1)&
2426 +s%ws(ix1-1,ix2,ix3,1)*s%surfaceC(ix1-1,ix2,ix3,1))
2427 s%w(ix^d,mag(2))=half/s%surface(ix^d,2)*(s%ws(ix^d,2)*s%surfaceC(ix^d,2)&
2428 +s%ws(ix1,ix2-1,ix3,2)*s%surfaceC(ix1,ix2-1,ix3,2))
2429 s%w(ix^d,mag(3))=half/s%surface(ix^d,3)*(s%ws(ix^d,3)*s%surfaceC(ix^d,3)&
2430 +s%ws(ix1,ix2,ix3-1,3)*s%surfaceC(ix1,ix2,ix3-1,3))
2431 }
2432 {^iftwod
2433 s%w(ix^d,mag(1))=half/s%surface(ix^d,1)*(s%ws(ix^d,1)*s%surfaceC(ix^d,1)&
2434 +s%ws(ix1-1,ix2,1)*s%surfaceC(ix1-1,ix2,1))
2435 s%w(ix^d,mag(2))=half/s%surface(ix^d,2)*(s%ws(ix^d,2)*s%surfaceC(ix^d,2)&
2436 +s%ws(ix1,ix2-1,2)*s%surfaceC(ix1,ix2-1,2))
2437 }
2438 {end do\}
2439
2440 ! calculate cell-center values from face-center values in 4th order
2441 !do idim=1,ndim
2442 ! gxO^L=ixO^L-2*kr(idim,^D);
2443 ! hxO^L=ixO^L-kr(idim,^D);
2444 ! jxO^L=ixO^L+kr(idim,^D);
2445
2446 ! ! Interpolate to cell barycentre using fourth order central formula
2447 ! w(ixO^S,mag(idim))=(0.0625d0/s%surface(ixO^S,idim))*&
2448 ! ( -ws(gxO^S,idim)*s%surfaceC(gxO^S,idim) &
2449 ! +9.0d0*ws(hxO^S,idim)*s%surfaceC(hxO^S,idim) &
2450 ! +9.0d0*ws(ixO^S,idim)*s%surfaceC(ixO^S,idim) &
2451 ! -ws(jxO^S,idim)*s%surfaceC(jxO^S,idim) )
2452 !end do
2453
2454 ! calculate cell-center values from face-center values in 6th order
2455 !do idim=1,ndim
2456 ! fxO^L=ixO^L-3*kr(idim,^D);
2457 ! gxO^L=ixO^L-2*kr(idim,^D);
2458 ! hxO^L=ixO^L-kr(idim,^D);
2459 ! jxO^L=ixO^L+kr(idim,^D);
2460 ! kxO^L=ixO^L+2*kr(idim,^D);
2461
2462 ! ! Interpolate to cell barycentre using sixth order central formula
2463 ! w(ixO^S,mag(idim))=(0.00390625d0/s%surface(ixO^S,idim))* &
2464 ! ( +3.0d0*ws(fxO^S,idim)*s%surfaceC(fxO^S,idim) &
2465 ! -25.0d0*ws(gxO^S,idim)*s%surfaceC(gxO^S,idim) &
2466 ! +150.0d0*ws(hxO^S,idim)*s%surfaceC(hxO^S,idim) &
2467 ! +150.0d0*ws(ixO^S,idim)*s%surfaceC(ixO^S,idim) &
2468 ! -25.0d0*ws(jxO^S,idim)*s%surfaceC(jxO^S,idim) &
2469 ! +3.0d0*ws(kxO^S,idim)*s%surfaceC(kxO^S,idim) )
2470 !end do
2471
2472 end subroutine mf_face_to_center
2473
2474 !> calculate magnetic field from vector potential
2475 subroutine b_from_vector_potential(ixIs^L, ixI^L, ixO^L, ws, x)
2478
2479 integer, intent(in) :: ixis^l, ixi^l, ixo^l
2480 double precision, intent(inout) :: ws(ixis^s,1:nws)
2481 double precision, intent(in) :: x(ixi^s,1:ndim)
2482
2483 double precision :: adummy(ixis^s,1:3)
2484
2485 call b_from_vector_potentiala(ixis^l, ixi^l, ixo^l, ws, x, adummy)
2486
2487 end subroutine b_from_vector_potential
2488
2491
2492 double precision :: sum_jbb_ipe, sum_j_ipe, sum_l_ipe, f_i_ipe, volume_pe
2493 double precision :: sum_jbb, sum_j, sum_l, f_i, volume, cw_sin_theta
2494 integer :: iigrid, igrid, ix^d
2495 integer :: amode, istatus(mpi_status_size)
2496 integer, save :: fhmf
2497 logical :: patchwi(ixg^t)
2498 logical, save :: logmfopened=.false.
2499 character(len=800) :: filename,filehead
2500 character(len=800) :: line,datastr
2501
2502 sum_jbb_ipe = 0.d0
2503 sum_j_ipe = 0.d0
2504 sum_l_ipe = 0.d0
2505 f_i_ipe = 0.d0
2506 volume_pe=0.d0
2507 do iigrid=1,igridstail; igrid=igrids(iigrid);
2508 block=>ps(igrid)
2509 ^d&dxlevel(^d)=rnode(rpdx^d_,igrid);
2510 call mask_inner(ixg^ll,ixm^ll,ps(igrid)%w,ps(igrid)%x,patchwi,volume_pe)
2511 sum_jbb_ipe = sum_jbb_ipe+integral_grid_mf(ixg^ll,ixm^ll,ps(igrid)%w,&
2512 ps(igrid)%x,1,patchwi)
2513 sum_j_ipe = sum_j_ipe+integral_grid_mf(ixg^ll,ixm^ll,ps(igrid)%w,&
2514 ps(igrid)%x,2,patchwi)
2515 f_i_ipe=f_i_ipe+integral_grid_mf(ixg^ll,ixm^ll,ps(igrid)%w,&
2516 ps(igrid)%x,3,patchwi)
2517 sum_l_ipe = sum_l_ipe+integral_grid_mf(ixg^ll,ixm^ll,ps(igrid)%w,&
2518 ps(igrid)%x,4,patchwi)
2519 end do
2520 call mpi_reduce(sum_jbb_ipe,sum_jbb,1,mpi_double_precision,&
2521 mpi_sum,0,icomm,ierrmpi)
2522 call mpi_reduce(sum_j_ipe,sum_j,1,mpi_double_precision,mpi_sum,0,&
2523 icomm,ierrmpi)
2524 call mpi_reduce(f_i_ipe,f_i,1,mpi_double_precision,mpi_sum,0,&
2525 icomm,ierrmpi)
2526 call mpi_reduce(sum_l_ipe,sum_l,1,mpi_double_precision,mpi_sum,0,&
2527 icomm,ierrmpi)
2528 call mpi_reduce(volume_pe,volume,1,mpi_double_precision,mpi_sum,0,&
2529 icomm,ierrmpi)
2530
2531 if(mype==0) then
2532 ! current- and volume-weighted average of the sine of the angle
2533 ! between the magnetic field B and the current density J
2534 cw_sin_theta = sum_jbb/sum_j
2535 ! volume-weighted average of the absolute value of the fractional
2536 ! magnetic flux change
2537 f_i = f_i/volume
2538 sum_j=sum_j/volume
2539 sum_l=sum_l/volume
2540 if(.not.logmfopened) then
2541 ! generate filename
2542 write(filename,"(a,a)") trim(base_filename), "_mf_metrics.csv"
2543
2544 ! Delete the log when not doing a restart run
2545 if (restart_from_file == undefined) then
2546 open(unit=20,file=trim(filename),status='replace')
2547 close(20, status='delete')
2548 end if
2549
2550 amode=ior(mpi_mode_create,mpi_mode_wronly)
2551 amode=ior(amode,mpi_mode_append)
2552 call mpi_file_open(mpi_comm_self,filename,amode,mpi_info_null,fhmf,ierrmpi)
2553 logmfopened=.true.
2554 filehead=" it, time, CW_sin_theta, current, Lorenz_force, f_i"
2555 if (it == 0) then
2556 call mpi_file_write(fhmf,filehead,len_trim(filehead), &
2557 mpi_character,istatus,ierrmpi)
2558 call mpi_file_write(fhmf,achar(10),1,mpi_character,istatus,ierrmpi)
2559 end if
2560 end if
2561 line=''
2562 write(datastr,'(i8,a)') it,','
2563 line=trim(line)//trim(datastr)
2564 write(datastr,'(es13.6,a)') global_time,','
2565 line=trim(line)//trim(datastr)
2566 write(datastr,'(es13.6,a)') cw_sin_theta,','
2567 line=trim(line)//trim(datastr)
2568 write(datastr,'(es13.6,a)') sum_j,','
2569 line=trim(line)//trim(datastr)
2570 write(datastr,'(es13.6,a)') sum_l,','
2571 line=trim(line)//trim(datastr)
2572 write(datastr,'(es13.6)') f_i
2573 line=trim(line)//trim(datastr)//new_line('A')
2574 call mpi_file_write(fhmf,line,len_trim(line),mpi_character,istatus,ierrmpi)
2575 if(.not.time_advance) then
2576 call mpi_file_close(fhmf,ierrmpi)
2577 logmfopened=.false.
2578 end if
2579 end if
2580
2581 end subroutine record_force_free_metrics
2582
2583 subroutine mask_inner(ixI^L,ixO^L,w,x,patchwi,volume_pe)
2585
2586 integer, intent(in) :: ixi^l,ixo^l
2587 double precision, intent(in):: w(ixi^s,nw),x(ixi^s,1:ndim)
2588 double precision, intent(inout) :: volume_pe
2589 logical, intent(out) :: patchwi(ixi^s)
2590
2591 double precision :: xo^l
2592 integer :: ix^d
2593
2594 {xomin^d = xprobmin^d + 0.05d0*(xprobmax^d-xprobmin^d)\}
2595 {xomax^d = xprobmax^d - 0.05d0*(xprobmax^d-xprobmin^d)\}
2596 if(slab) then
2597 xomin^nd = xprobmin^nd
2598 else
2599 xomin1 = xprobmin1
2600 end if
2601
2602 {do ix^db=ixomin^db,ixomax^db\}
2603 if({ x(ix^dd,^d) > xomin^d .and. x(ix^dd,^d) < xomax^d | .and. }) then
2604 patchwi(ix^d)=.true.
2605 volume_pe=volume_pe+block%dvolume(ix^d)
2606 else
2607 patchwi(ix^d)=.false.
2608 endif
2609 {end do\}
2610
2611 end subroutine mask_inner
2612
2613 function integral_grid_mf(ixI^L,ixO^L,w,x,iw,patchwi)
2615
2616 integer, intent(in) :: ixi^l,ixo^l,iw
2617 double precision, intent(in) :: x(ixi^s,1:ndim)
2618 double precision :: w(ixi^s,nw+nwauxio)
2619 logical, intent(in) :: patchwi(ixi^s)
2620
2621 double precision, dimension(ixI^S,7-2*ndir:3) :: current
2622 double precision, dimension(ixI^S,1:ndir) :: bvec,qvec
2623 double precision :: integral_grid_mf,tmp(ixi^s),bm2
2624 integer :: ix^d,idirmin0,idirmin,idir,jdir,kdir
2625
2626 integral_grid_mf=0.d0
2627 select case(iw)
2628 case(1)
2629 ! Sum(|JxB|/|B|*dvolume)
2630 bvec(ixo^s,:)=w(ixo^s,mag(:))
2631 call get_current(w,ixi^l,ixo^l,idirmin,current)
2632 ! calculate Lorentz force
2633 qvec(ixo^s,1:ndir)=zero
2634 do idir=1,ndir; do jdir=idirmin,3; do kdir=1,ndir
2635 if(lvc(idir,jdir,kdir)/=0)then
2636 if(lvc(idir,jdir,kdir)==1)then
2637 qvec(ixo^s,idir)=qvec(ixo^s,idir)+current(ixo^s,jdir)*bvec(ixo^s,kdir)
2638 else
2639 qvec(ixo^s,idir)=qvec(ixo^s,idir)-current(ixo^s,jdir)*bvec(ixo^s,kdir)
2640 end if
2641 end if
2642 end do; end do; end do
2643
2644 {do ix^db=ixomin^db,ixomax^db\}
2645 if(patchwi(ix^d)) then
2646 bm2=sum(bvec(ix^d,:)**2)
2647 if(bm2/=0.d0) bm2=1.d0/bm2
2648 integral_grid_mf=integral_grid_mf+sqrt(sum(qvec(ix^d,:)**2)*&
2649 bm2)*block%dvolume(ix^d)
2650 end if
2651 {end do\}
2652 case(2)
2653 ! Sum(|J|*dvolume)
2654 call get_current(w,ixi^l,ixo^l,idirmin,current)
2655 {do ix^db=ixomin^db,ixomax^db\}
2656 if(patchwi(ix^d)) integral_grid_mf=integral_grid_mf+sqrt(sum(current(ix^d,:)**2))*&
2657 block%dvolume(ix^d)
2658 {end do\}
2659 case(3)
2660 ! f_i solenoidal property of B: (dvolume |div B|)/(dsurface |B|)
2661 ! Sum(f_i*dvolume)
2662 call get_normalized_divb(w,ixi^l,ixo^l,tmp)
2663 {do ix^db=ixomin^db,ixomax^db\}
2664 if(patchwi(ix^d)) integral_grid_mf=integral_grid_mf+tmp(ix^d)*block%dvolume(ix^d)
2665 {end do\}
2666 case(4)
2667 ! Sum(|JxB|*dvolume)
2668 bvec(ixo^s,:)=w(ixo^s,mag(:))
2669 call get_current(w,ixi^l,ixo^l,idirmin,current)
2670 ! calculate Lorentz force
2671 qvec(ixo^s,1:ndir)=zero
2672 do idir=1,ndir; do jdir=idirmin,3; do kdir=1,ndir
2673 if(lvc(idir,jdir,kdir)/=0)then
2674 if(lvc(idir,jdir,kdir)==1)then
2675 qvec(ixo^s,idir)=qvec(ixo^s,idir)+current(ixo^s,jdir)*bvec(ixo^s,kdir)
2676 else
2677 qvec(ixo^s,idir)=qvec(ixo^s,idir)-current(ixo^s,jdir)*bvec(ixo^s,kdir)
2678 end if
2679 end if
2680 end do; end do; end do
2681
2682 {do ix^db=ixomin^db,ixomax^db\}
2683 if(patchwi(ix^d)) integral_grid_mf=integral_grid_mf+&
2684 sqrt(sum(qvec(ix^d,:)**2))*block%dvolume(ix^d)
2685 {end do\}
2686 end select
2687 return
2688 end function integral_grid_mf
2689
2690end module mod_mf_phys
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
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
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
integer coordinate
Definition mod_geometry.t:7
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)
update ghost cells of all blocks including physical boundaries
integer, dimension(0:3^d &), target type_recv_p_p1
integer, dimension( :^d &), pointer type_recv_r
integer, dimension(-1:1^d &), target type_send_srl_p1
subroutine getbc(time, qdt, psb, nwstart, nwbc)
do update ghost cells of all blocks including physical boundaries
integer, dimension(0:3^d &), target type_send_p_f
integer, dimension( :^d &), pointer type_send_p
integer, dimension( :^d &), pointer type_send_srl
integer, dimension(-1:1^d &), target type_send_r_p1
subroutine create_bc_mpi_datatype(nwstart, nwbc)
integer, dimension(0:3^d &), target type_recv_r_f
integer, dimension(-1:1^d &), target type_recv_srl_f
integer, dimension(-1:1^d &), target type_send_r_f
integer, dimension(-1:1^d &), target type_send_srl_f
integer, dimension(0:3^d &), target type_send_p_p1
integer, dimension( :^d &), pointer type_send_r
integer, dimension(0:3^d &), target type_recv_r_p1
integer, dimension( :^d &), pointer type_recv_p
integer, dimension(-1:1^d &), target type_recv_srl_p1
integer, dimension(0:3^d &), target type_recv_p_f
integer, dimension( :^d &), pointer type_recv_srl
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 ixghi
Upper index of grid block arrays.
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.
integer, parameter unitpar
file handle for IO
double precision global_time
The global simulation time.
double precision unit_mass
Physical scaling factor for mass.
integer, dimension(3, 3) kr
Kronecker delta tensor.
integer it
Number of time steps taken.
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.
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 mype
The rank of the current MPI task.
double precision, dimension(:), allocatable, parameter d
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 slab
Cartesian geometry or not.
integer, parameter bc_periodic
integer, parameter bc_special
boundary condition types
double precision unit_magneticfield
Physical scaling factor for magnetic field.
integer nwauxio
Number of auxiliary variables that are only included in output.
double precision unit_velocity
Physical scaling factor for velocity.
logical time_advance
do time evolving
character(len=std_len) restart_from_file
If not 'unavailable', resume from snapshot with this base file name.
double precision c_norm
Normalised speed of light.
double precision, dimension(:,:), allocatable rnode
Corner coordinates.
double precision unit_temperature
Physical scaling factor for temperature.
logical si_unit
Use SI units (.true.) or use cgs units (.false.)
double precision, dimension(:,:), allocatable dx
spatial steps for all dimensions at all levels
integer nghostcells
Number of ghost cells surrounding a grid.
integer, parameter sdim
starting dimension for electric field
character(len= *), parameter undefined
logical need_global_cmax
need global maximal wave speed
double precision, dimension(^nd) dxlevel
store unstretched cell size of current level
logical use_multigrid
Use multigrid (only available in 2D and 3D)
logical slab_uniform
uniform Cartesian geometry or not (stretched Cartesian)
character(len=std_len) base_filename
Base file name for simulation output, which will be followed by a number.
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:
logical record_electric_field
True for record electric field.
integer, parameter ixglo
Lower index of grid block arrays (always 1)
Magnetofriction module.
Definition mod_mf_phys.t:2
integer, public, protected b
Definition mod_mf_phys.t:47
double precision, public mf_nu
viscosity coefficient in s cm^-2 for solar corona (Cheung 2012 ApJ)
Definition mod_mf_phys.t:13
subroutine, public mf_phys_init()
integer, public, protected mf_divb_nth
Whether divB is computed with a fourth order approximation.
Definition mod_mf_phys.t:80
subroutine, public b_from_vector_potential(ixisl, ixil, ixol, ws, x)
calculate magnetic field from vector potential
logical, public, protected mf_record_electric_field
set to true if need to record electric field on cell edges
Definition mod_mf_phys.t:77
logical, public, protected clean_initial_divb
clean divb in the initial condition
Definition mod_mf_phys.t:92
logical, public divbwave
Add divB wave in Roe solver.
Definition mod_mf_phys.t:89
subroutine, public mf_face_to_center(ixol, s)
calculate cell-center values from face-center values
double precision, public mf_vmax
maximal limit of magnetofrictional velocity in cm s^-1 (Pomoell 2019 A&A)
Definition mod_mf_phys.t:16
logical, public, protected mf_glm
Whether GLM-MHD is used.
Definition mod_mf_phys.t:71
double precision, public mf_eta_hyper
The hyper-resistivity.
Definition mod_mf_phys.t:29
double precision, public mf_glm_alpha
GLM-MHD parameter: ratio of the diffusive and advective time scales for div b taking values within [0...
Definition mod_mf_phys.t:23
integer, public, protected c
Indices of the momentum density for the form of better vectorization.
Definition mod_mf_phys.t:44
integer, public, protected c_
Definition mod_mf_phys.t:44
logical, dimension(2 *^nd), public, protected boundary_divbfix
To control divB=0 fix for boundary.
Definition mod_mf_phys.t:83
character(len=std_len), public, protected type_ct
Method type of constrained transport.
Definition mod_mf_phys.t:98
character(len=std_len), public, protected typedivbfix
Method type to clean divergence of B.
Definition mod_mf_phys.t:95
subroutine, public get_normalized_divb(w, ixil, ixol, divb)
get dimensionless div B = |divB| * volume / area / |B|
integer, dimension(2 *^nd), public, protected boundary_divbfix_skip
To skip * layer of ghost cells during divB=0 fix for boundary.
Definition mod_mf_phys.t:53
double precision, dimension(2 *^nd), public mf_decay_scale
decay scale of frictional velocity near boundaries
Definition mod_mf_phys.t:19
subroutine, public record_force_free_metrics()
subroutine, public mf_to_conserved(ixil, ixol, w, x)
Transform primitive variables into conservative ones.
subroutine, public get_current(w, ixil, ixol, idirmin, current, fourthorder)
Calculate idirmin and the idirmin:3 components of the common current array make sure that dxlevel(^D)...
integer, public, protected psi_
Indices of the GLM psi.
Definition mod_mf_phys.t:50
integer, dimension(:), allocatable, public, protected mom
Indices of the momentum density.
Definition mod_mf_phys.t:41
subroutine, public mf_clean_divb_multigrid(qdt, qt, active)
logical, public, protected mf_particles
Whether particles module is added.
Definition mod_mf_phys.t:68
integer, public, protected m
Definition mod_mf_phys.t:44
subroutine, public mf_to_primitive(ixil, ixol, w, x)
Transform conservative variables into primitive ones.
double precision, public mf_eta
The resistivity.
Definition mod_mf_phys.t:26
logical, public, protected source_split_divb
Whether divB cleaning sources are added splitting from fluid solver.
Definition mod_mf_phys.t:74
double precision, public, protected he_abundance
Helium abundance over Hydrogen.
Definition mod_mf_phys.t:35
Module to couple the octree-mg library to AMRVAC. This file uses the VACPP preprocessor,...
type(mg_t) mg
Data structure containing the multigrid tree.
Module containing all the particle routines.
subroutine particles_init()
Initialize particle data and parameters.
This module defines the procedures of a physics module. It contains function pointers for the various...
Definition mod_physics.t:4
Module with all the methods that users can customize in AMRVAC.
procedure(special_resistivity), pointer usr_special_resistivity
procedure(set_electric_field), pointer usr_set_electric_field
integer nw
Total number of variables.
integer number_species
number of species: each species has different characterictic speeds and should be used accordingly in...
The data structure that contains information about a tree node/grid block.
Definition mod_forest.t:11