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