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