MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_magnetic_helicity.t
Go to the documentation of this file.
1!> Finite-volume relative magnetic helicity on a uniform Cartesian mesh.
2!>
3!> The implementation uses a DeVore gauge and streams one plane at a time.
4!> Plane data are reduced to a deterministic owner; consecutive plane owners
5!> exchange only the O(N_perp**2) integration state. No magnetic-field-line
6!> tracing or replicated three-dimensional field is used.
11 implicit none
12 private
13
14 integer, parameter :: mh_name_len=256
15 integer, parameter :: mh_nstate=24
16 integer, parameter :: mh_state_tag=28431
17 integer, parameter :: mh_debug_tag=28432
18 integer, parameter :: mh_debug_nvalue=12
19 integer, parameter :: mh_debug_narray=6
20 ! Plane-pipeline channel starts: two integration levels for B and Bp,
21 ! two magnetic planes for centered derivatives, one Bp plane, and top b.
22 integer, parameter :: mh_q1_b=1,mh_q2_b=4
23 integer, parameter :: mh_q1_bp=7,mh_q2_bp=10
24 integer, parameter :: mh_b1=13,mh_b2=16,mh_bp1=19,mh_btop=22
25
27 integer :: gauge_axis=3
28 double precision :: ratio_tolerance=1.d-12
29 logical :: write_debug_vti=.false.
30 logical :: write_mg_timing=.false.
31 character(len=mh_name_len) :: debug_vti_file=''
32 type(magnetic_reference_config) :: reference
34
36 double precision :: hm=0.d0,hj=0.d0,hpj=0.d0
37 double precision :: abs_hj_over_abs_hm=0.d0
38 double precision :: energy=0.d0,potential_energy=0.d0,free_energy=0.d0
39 double precision :: hm_physical=0.d0,hj_physical=0.d0
40 double precision :: hpj_physical=0.d0
41 double precision :: energy_physical=0.d0
42 double precision :: potential_energy_physical=0.d0
43 double precision :: free_energy_physical=0.d0
44 double precision :: helicity_unit=1.d0,energy_unit=1.d0
45 double precision :: curl_a_error=0.d0,curl_ap_error=0.d0
46 double precision :: decomposition_error=0.d0
47 double precision :: epsilon_div_b=0.d0
48 double precision :: net_flux_imbalance=0.d0
49 double precision :: boundary_normal_error=0.d0
50 double precision :: mg_residual=0.d0
51 integer :: mg_cycles=0
52 logical :: ratio_is_valid=.false.
54
55 type, private :: mh_debug_vti_state
56 logical :: enabled=.false.
57 integer :: unit=0
58 integer :: nxyz(3)=0
59 integer(kind=8) :: data_pos=0_8
60 integer(kind=8) :: offsets(mh_debug_narray)=0_8
61 integer(kind=8) :: nbytes(mh_debug_narray)=0_8
62 end type mh_debug_vti_state
63
64 character(len=mh_name_len), public :: mh_output_file=''
65 integer, public :: mh_gauge_axis=3
66 double precision, public :: mh_mg_tolerance=1.d-4
67 integer, public :: mh_mg_max_cycles=50
68 double precision, public :: mh_max_flux_imbalance=1.d-6
69 logical, public :: mh_write_debug_vti=.false.
70 logical, public :: mh_write_mg_timing=.false.
71 character(len=mh_name_len), public :: mh_debug_vti_file=''
73
75
76contains
77
78 subroutine mh_params_read(files)
80 character(len=*), intent(in) :: files(:)
81 integer :: n
82
83 namelist /magnetic_helicity_list/ mh_output_file,&
87
93 mh_write_debug_vti=.false.
94 mh_write_mg_timing=.false.
96 do n=1,size(files)
97 open(unitpar,file=trim(files(n)),status='old')
98 read(unitpar,magnetic_helicity_list,end=111)
99111 close(unitpar)
100 end do
101 end subroutine mh_params_read
102
103 subroutine mh_run_task()
104 use mod_comm_lib, only: mpistop
106 ps,iwstart,nwgc
108 type(magnetic_helicity_config) :: config
109
110 if(.not.convert) call mpistop(&
111 'magnetic-helicity conversion requires convert=.true.')
112 if(level_io<1) call mpistop(&
113 'magnetic-helicity conversion requires level_io > 0')
115 call getbc(global_time,0.d0,ps,iwstart,nwgc)
116 config%gauge_axis=mh_gauge_axis
117 config%reference%residual_tolerance=mh_mg_tolerance
118 config%reference%max_cycles=mh_mg_max_cycles
119 config%reference%max_flux_imbalance=mh_max_flux_imbalance
120 config%reference%write_timing=mh_write_mg_timing
121 config%write_debug_vti=mh_write_debug_vti
122 config%debug_vti_file=mh_debug_vti_file
124 call mh_write_result_csv(mh_last_result)
125 end subroutine mh_run_task
126
127 subroutine mh_write_result_csv(result)
128 use mod_comm_lib, only: mpistop
131 type(magnetic_helicity_result), intent(in) :: result
132 character(len=mh_name_len) :: filename
133 character(len=5) :: helicity_label
134 character(len=3) :: energy_label
135 character(len=5) :: ratio_label
136 logical :: exists
137 integer :: csv_unit,io_status
138 integer(kind=8) :: file_size
139
140 if(mype/=0) return
141 if(len_trim(mh_output_file)>0) then
142 filename=trim(mh_output_file)
143 else
144 filename=trim(base_filename)//'_helicity.csv'
145 end if
146 if(si_unit) then
147 helicity_label='Wb2'
148 energy_label='J'
149 else
150 helicity_label='Mx2'
151 energy_label='erg'
152 end if
153 ratio_label=merge('true ','false',result%ratio_is_valid)
154
155 file_size=0_8
156 inquire(file=trim(filename),exist=exists)
157 if(exists) inquire(file=trim(filename),size=file_size)
158 open(newunit=csv_unit,file=trim(filename),status='unknown',&
159 action='write',position='append',iostat=io_status)
160 if(io_status/=0) call mpistop('could not open magnetic-helicity CSV')
161 if(.not.exists .or. file_size==0) then
162 write(csv_unit,'(a)') 'snapshot,code_time,physical_time_s,'//&
163 'Hm_code,HJ_code,HPJ_code,abs_HJ_over_abs_Hm,'//&
164 'Hm_'//trim(helicity_label)//',HJ_'//trim(helicity_label)//&
165 ',HPJ_'//trim(helicity_label)//',E_code,Ep_code,Efree_code,'//&
166 'E_'//trim(energy_label)//',Ep_'//trim(energy_label)//&
167 ',Efree_'//trim(energy_label)//',net_flux_imbalance,'//&
168 'epsilon_div_B,boundary_normal_error,curl_A_error,'//&
169 'curl_Ap_error,decomposition_error,mg_residual,mg_cycles,'//&
170 'ratio_is_valid'
171 end if
172
173 write(csv_unit,'(i0)',advance='no') snapshotini
174 call mh_csv_real(csv_unit,global_time)
175 call mh_csv_real(csv_unit,global_time*time_convert_factor)
176 call mh_csv_real(csv_unit,result%Hm)
177 call mh_csv_real(csv_unit,result%HJ)
178 call mh_csv_real(csv_unit,result%HPJ)
179 if(result%ratio_is_valid) then
180 call mh_csv_real(csv_unit,result%abs_HJ_over_abs_Hm)
181 else
182 write(csv_unit,'(",")',advance='no')
183 end if
184 call mh_csv_real(csv_unit,result%Hm_physical)
185 call mh_csv_real(csv_unit,result%HJ_physical)
186 call mh_csv_real(csv_unit,result%HPJ_physical)
187 call mh_csv_real(csv_unit,result%energy)
188 call mh_csv_real(csv_unit,result%potential_energy)
189 call mh_csv_real(csv_unit,result%free_energy)
190 call mh_csv_real(csv_unit,result%energy_physical)
191 call mh_csv_real(csv_unit,result%potential_energy_physical)
192 call mh_csv_real(csv_unit,result%free_energy_physical)
193 call mh_csv_real(csv_unit,result%net_flux_imbalance)
194 call mh_csv_real(csv_unit,result%epsilon_div_B)
195 call mh_csv_real(csv_unit,result%boundary_normal_error)
196 call mh_csv_real(csv_unit,result%curl_A_error)
197 call mh_csv_real(csv_unit,result%curl_Ap_error)
198 call mh_csv_real(csv_unit,result%decomposition_error)
199 call mh_csv_real(csv_unit,result%mg_residual)
200 write(csv_unit,'(",",i0,",",a)') result%mg_cycles,trim(ratio_label)
201 close(csv_unit)
202 end subroutine mh_write_result_csv
203
204 subroutine mh_csv_real(csv_unit,value)
205 integer, intent(in) :: csv_unit
206 double precision, intent(in) :: value
207 write(csv_unit,'(",",es24.16)',advance='no') value
208 end subroutine mh_csv_real
209
210 subroutine compute_magnetic_helicity_fv(config,result)
211 use mod_comm_lib, only: mpistop
214 type(magnetic_helicity_config), intent(in) :: config
215 type(magnetic_helicity_result), intent(out) :: result
216
218 if(ndim/=3) call mpistop('finite-volume magnetic helicity requires three dimensions')
219 if(coordinate/=cartesian) &
220 call mpistop('finite-volume magnetic helicity requires Cartesian coordinates')
221 if(any(stretched_dim)) &
222 call mpistop('finite-volume magnetic helicity requires an unstretched mesh')
223 if(config%gauge_axis<1 .or. config%gauge_axis>3) &
224 call mpistop('mh_gauge_axis must be 1, 2, or 3')
225 if(config%ratio_tolerance<=0.d0) &
226 call mpistop('magnetic-helicity ratio tolerance must be positive')
227
228 {^ifthreed
229 call mh_compute_3d(config,result)
230 }
231 end subroutine compute_magnetic_helicity_fv
232
233{^ifthreed
234 subroutine mh_compute_3d(config,result)
235 use mod_comm_lib, only: mpistop
237 type(magnetic_helicity_config), intent(in) :: config
238 type(magnetic_helicity_result), intent(inout) :: result
239
240 type(magnetic_reference_result) :: ref_result
241 type(magnetic_reference_field) :: bp
242 type(mh_debug_vti_state) :: debug_vti
243 double precision, allocatable :: send_b(:,:,:),recv_b(:,:,:)
244 double precision, allocatable :: send_bp(:,:,:),recv_bp(:,:,:)
245 double precision, allocatable :: avec(:,:,:),apvec(:,:,:)
246 double precision, allocatable :: q_b(:,:,:),q_bp(:,:,:)
247 double precision, allocatable :: bdiff(:,:,:)
248 double precision, allocatable :: plane_state(:,:,:)
249 double precision, allocatable :: debug_plane(:,:)
250 double precision, allocatable :: face_send(:,:),face_recv(:,:)
251 integer, allocatable :: count_send(:,:),count_recv(:,:)
252 integer, allocatable :: face_count_send(:,:),face_count_recv(:,:)
253 double precision :: local_values(11),global_values(11)
254 double precision :: cell_dx(3),du,dv,da,dvolume,denom
255 integer :: nxyz(3),axis,uaxis,vaxis,orientation
256 integer :: nu,nv,na,k,owner,previous_owner,next_owner
257 integer :: request,status(mpi_status_size),state_count
258
259 call mh_uniform_grid(config%gauge_axis,nxyz,cell_dx,uaxis,vaxis,&
260 orientation)
261 axis=config%gauge_axis
262 nu=nxyz(uaxis)
263 nv=nxyz(vaxis)
264 na=nxyz(axis)
265 du=cell_dx(uaxis)
266 dv=cell_dx(vaxis)
267 da=cell_dx(axis)
268 dvolume=cell_dx(1)*cell_dx(2)*cell_dx(3)
269 if(min(nu,nv,na)<3) &
270 call mpistop('magnetic helicity requires at least three cells per direction')
271
272 call mh_debug_vti_begin(debug_vti,config,nxyz,cell_dx)
273
274 call solve_magnetic_reference_fv(config%reference,bp,ref_result)
275
276 allocate(send_b(nu,nv,3),recv_b(nu,nv,3))
277 allocate(send_bp(nu,nv,3),recv_bp(nu,nv,3))
278 allocate(avec(nu,nv,3),apvec(nu,nv,3))
279 allocate(q_b(nu,nv,3),q_bp(nu,nv,3))
280 allocate(bdiff(nu,nv,3))
281 allocate(plane_state(nu,nv,mh_nstate))
282 allocate(face_send(nu,nv),face_recv(nu,nv))
283 allocate(count_send(nu,nv),count_recv(nu,nv))
284 allocate(face_count_send(nu,nv),face_count_recv(nu,nv))
285 if(debug_vti%enabled) allocate(debug_plane(mh_debug_nvalue,nu*nv))
286
287 plane_state=0.d0
288 local_values=0.d0
289 state_count=nu*nv*mh_nstate
290 do k=na,1,-1
291 owner=mh_plane_owner(k)
292 request=mpi_request_null
293 if(k<na .and. mype==owner) then
294 previous_owner=mh_plane_owner(k+1)
295 if(previous_owner/=owner) call mpi_irecv(plane_state,state_count,&
296 mpi_double_precision,previous_owner,mh_state_tag,icomm,request,&
297 ierrmpi)
298 end if
299
300 call mh_assemble_plane(axis,uaxis,vaxis,k,nxyz,cell_dx,bp,send_b,&
301 send_bp,count_send,face_send,face_count_send)
302 call mpi_reduce(send_b,recv_b,nu*nv*3,mpi_double_precision,&
303 mpi_sum,owner,icomm,ierrmpi)
304 call mpi_reduce(send_bp,recv_bp,nu*nv*3,mpi_double_precision,&
305 mpi_sum,owner,icomm,ierrmpi)
306 call mpi_reduce(count_send,count_recv,nu*nv,mpi_integer,&
307 mpi_sum,owner,icomm,ierrmpi)
308 if(k==na) then
309 call mpi_reduce(face_send,face_recv,nu*nv,mpi_double_precision,&
310 mpi_sum,owner,icomm,ierrmpi)
311 call mpi_reduce(face_count_send,face_count_recv,nu*nv,mpi_integer,&
312 mpi_sum,owner,icomm,ierrmpi)
313 end if
314
315 if(mype==owner) then
316 if(request/=mpi_request_null) call mpi_wait(request,status,ierrmpi)
317 if(any(count_recv/=1)) &
318 call mpistop('magnetic-helicity plane assembly is not one-to-one')
319 if(k==na) then
320 if(any(face_count_recv/=1)) call mpistop(&
321 'magnetic-helicity top boundary assembly is not one-to-one')
322 plane_state=0.d0
323 call mh_build_top_b(face_recv,du,dv,uaxis,vaxis,axis,&
324 orientation,plane_state(:,:,mh_btop:mh_btop+2))
325 q_b=0.5d0*da*recv_b
326 q_bp=0.5d0*da*recv_bp
327 else if(k==na-1) then
328 ! Seed the second plane by a trapezoid between cell centers.
329 q_b=plane_state(:,:,mh_q1_b:mh_q1_b+2)+0.5d0*da*(&
330 plane_state(:,:,mh_b1:mh_b1+2)+recv_b)
331 q_bp=plane_state(:,:,mh_q1_bp:mh_q1_bp+2)+0.5d0*da*(&
332 plane_state(:,:,mh_bp1:mh_bp1+2)+recv_bp)
333 else
334 ! I(k)=I(k+2)+2*ds*B(k+1), so the centered derivative of I
335 ! is exactly -B at every interior plane.
336 q_b=plane_state(:,:,mh_q2_b:mh_q2_b+2)+2.d0*da*&
337 plane_state(:,:,mh_b1:mh_b1+2)
338 q_bp=plane_state(:,:,mh_q2_bp:mh_q2_bp+2)+2.d0*da*&
339 plane_state(:,:,mh_bp1:mh_bp1+2)
340 end if
341
342 call mh_make_vector_potential(&
343 plane_state(:,:,mh_btop:mh_btop+2),q_b,uaxis,&
344 vaxis,axis,orientation,avec)
345 call mh_make_vector_potential(&
346 plane_state(:,:,mh_btop:mh_btop+2),q_bp,uaxis,&
347 vaxis,axis,orientation,apvec)
348 if(k<=na-2) call mh_plane_quality(plane_state,q_b,q_bp,recv_b,&
349 uaxis,vaxis,axis,orientation,du,dv,da,dvolume,local_values)
350 call mh_plane_integrals(avec,apvec,recv_b,recv_bp,bdiff,&
351 dvolume,local_values)
352 if(debug_vti%enabled) call mh_debug_pack_plane(recv_bp,avec,apvec,&
353 bdiff,debug_plane)
354
355 plane_state(:,:,mh_q2_b:mh_q2_b+2)=&
356 plane_state(:,:,mh_q1_b:mh_q1_b+2)
357 plane_state(:,:,mh_q1_b:mh_q1_b+2)=q_b
358 plane_state(:,:,mh_q2_bp:mh_q2_bp+2)=&
359 plane_state(:,:,mh_q1_bp:mh_q1_bp+2)
360 plane_state(:,:,mh_q1_bp:mh_q1_bp+2)=q_bp
361 plane_state(:,:,mh_b2:mh_b2+2)=plane_state(:,:,mh_b1:mh_b1+2)
362 plane_state(:,:,mh_b1:mh_b1+2)=recv_b
363 plane_state(:,:,mh_bp1:mh_bp1+2)=recv_bp
364
365 end if
366 if(debug_vti%enabled) call mh_debug_vti_write_plane(debug_vti,owner,&
367 k,uaxis,vaxis,axis,debug_plane)
368 if(mype==owner .and. k>1) then
369 next_owner=mh_plane_owner(k-1)
370 if(next_owner/=owner) call mpi_send(plane_state,state_count,&
371 mpi_double_precision,next_owner,mh_state_tag,icomm,ierrmpi)
372 end if
373 end do
374 call mh_debug_vti_end(debug_vti)
375
376 call mpi_allreduce(local_values,global_values,size(local_values),&
377 mpi_double_precision,mpi_sum,icomm,ierrmpi)
378 result%Hm=global_values(1)
379 result%HJ=global_values(2)
380 result%HPJ=global_values(3)
381 result%energy=global_values(4)
382 result%potential_energy=ref_result%magnetic_energy
383 result%free_energy=result%energy-result%potential_energy
384 result%curl_A_error=dsqrt(global_values(6)/&
385 max(global_values(7),tiny(1.d0)))
386 result%curl_Ap_error=dsqrt(global_values(8)/&
387 max(global_values(9),tiny(1.d0)))
388 result%epsilon_div_B=dsqrt(global_values(10)/&
389 max(global_values(11),tiny(1.d0)))
390 denom=max(abs(result%Hm),abs(result%HJ)+abs(result%HPJ),tiny(1.d0))
391 result%decomposition_error=abs(result%Hm-result%HJ-result%HPJ)/denom
392 result%ratio_is_valid=abs(result%Hm)>&
393 config%ratio_tolerance*max(global_values(5),tiny(1.d0))
394 if(result%ratio_is_valid) &
395 result%abs_HJ_over_abs_Hm=abs(result%HJ)/abs(result%Hm)
396
397 result%net_flux_imbalance=ref_result%flux_imbalance
398 result%boundary_normal_error=ref_result%boundary_normal_error
399 result%mg_residual=ref_result%residual
400 result%mg_cycles=ref_result%cycles
401 result%helicity_unit=unit_magneticfield**2*unit_length**4
402 result%energy_unit=unit_pressure*unit_length**3
403 result%Hm_physical=result%Hm*result%helicity_unit
404 result%HJ_physical=result%HJ*result%helicity_unit
405 result%HPJ_physical=result%HPJ*result%helicity_unit
406 result%energy_physical=result%energy*result%energy_unit
407 result%potential_energy_physical=result%potential_energy*result%energy_unit
408 result%free_energy_physical=result%free_energy*result%energy_unit
409
410 call free_magnetic_reference_field(bp)
411 deallocate(send_b,recv_b,send_bp,recv_bp,avec,apvec,q_b,q_bp,&
412 bdiff,plane_state)
413 deallocate(face_send,face_recv,count_send,count_recv)
414 deallocate(face_count_send,face_count_recv)
415 if(allocated(debug_plane)) deallocate(debug_plane)
416 end subroutine mh_compute_3d
417
418 integer function mh_plane_owner(k)
419 use mod_global_parameters, only: npe
420 integer, intent(in) :: k
421 mh_plane_owner=mod(k-1,npe)
422 end function mh_plane_owner
423
424 subroutine mh_uniform_grid(axis,nxyz,cell_dx,uaxis,vaxis,orientation)
425 use mod_comm_lib, only: mpistop
427 integer, intent(in) :: axis
428 integer, intent(out) :: nxyz(3),uaxis,vaxis,orientation
429 double precision, intent(out) :: cell_dx(3)
430 integer :: iigrid,igrid,local_min,local_max,grid_min,grid_max
431 double precision :: local_error,global_error
432
433 local_min=huge(1)
434 local_max=-huge(1)
435 do iigrid=1,igridstail
436 igrid=igrids(iigrid)
437 local_min=min(local_min,node(plevel_,igrid))
438 local_max=max(local_max,node(plevel_,igrid))
439 end do
440 call mpi_allreduce(local_min,grid_min,1,mpi_integer,mpi_min,icomm,ierrmpi)
441 call mpi_allreduce(local_max,grid_max,1,mpi_integer,mpi_max,icomm,ierrmpi)
442 if(grid_min/=grid_max) call mpistop(&
443 'magnetic helicity requires one uniform AMR level; set level_io')
444 nxyz=(/domain_nx1,domain_nx2,domain_nx3/)*2**(grid_min-1)
445 cell_dx(1)=(xprobmax1-xprobmin1)/dble(nxyz(1))
446 cell_dx(2)=(xprobmax2-xprobmin2)/dble(nxyz(2))
447 cell_dx(3)=(xprobmax3-xprobmin3)/dble(nxyz(3))
448 local_error=0.d0
449 do iigrid=1,igridstail
450 igrid=igrids(iigrid)
451 local_error=max(local_error,abs(rnode(rpdx1_,igrid)-cell_dx(1))/&
452 cell_dx(1))
453 local_error=max(local_error,abs(rnode(rpdx2_,igrid)-cell_dx(2))/&
454 cell_dx(2))
455 local_error=max(local_error,abs(rnode(rpdx3_,igrid)-cell_dx(3))/&
456 cell_dx(3))
457 end do
458 call mpi_allreduce(local_error,global_error,1,mpi_double_precision,&
459 mpi_max,icomm,ierrmpi)
460 if(global_error>1.d-12) call mpistop(&
461 'magnetic helicity requires a uniform Cartesian cell size')
462
463 select case(axis)
464 case(1)
465 uaxis=2; vaxis=3; orientation=1
466 case(2)
467 uaxis=1; vaxis=3; orientation=-1
468 case(3)
469 uaxis=1; vaxis=2; orientation=1
470 end select
471 end subroutine mh_uniform_grid
472
473 subroutine mh_assemble_plane(axis,uaxis,vaxis,kplane,nxyz,cell_dx,bp,&
474 bsend,bpsend,counts,face_send,face_counts)
476 integer, intent(in) :: axis,uaxis,vaxis,kplane,nxyz(3)
477 double precision, intent(in) :: cell_dx(3)
478 type(magnetic_reference_field), intent(in) :: bp
479 double precision, intent(out) :: bsend(:,:,:),bpsend(:,:,:)
480 integer, intent(out) :: counts(:,:)
481 double precision, intent(out) :: face_send(:,:)
482 integer, intent(out) :: face_counts(:,:)
483 integer :: iigrid,igrid,i,j,l,idir,ig,jg,lg,iloc,jloc,lloc
484 integer :: start(3),block_extent(3),gu,gv
485
486 bsend=0.d0
487 bpsend=0.d0
488 counts=0
489 face_send=0.d0
490 face_counts=0
491 do iigrid=1,igridstail
492 igrid=igrids(iigrid)
493 start(1)=nint((ps(igrid)%x(ixmlo1,ixmlo2,ixmlo3,1)-&
494 xprobmin1)/cell_dx(1)+0.5d0)
495 start(2)=nint((ps(igrid)%x(ixmlo1,ixmlo2,ixmlo3,2)-&
496 xprobmin2)/cell_dx(2)+0.5d0)
497 start(3)=nint((ps(igrid)%x(ixmlo1,ixmlo2,ixmlo3,3)-&
498 xprobmin3)/cell_dx(3)+0.5d0)
499 block_extent=(/ixmhi1-ixmlo1,ixmhi2-ixmlo2,ixmhi3-ixmlo3/)
500 if(kplane<start(axis) .or. &
501 kplane>start(axis)+block_extent(axis)) cycle
502 select case(axis)
503 case(1)
504 iloc=ixmlo1+kplane-start(1)
505 do l=ixmlo3,ixmhi3; lg=start(3)+l-ixmlo3
506 do j=ixmlo2,ixmhi2; jg=start(2)+j-ixmlo2
507 gu=jg; gv=lg
508 do idir=1,3
509 bsend(gu,gv,idir)=mh_total_b(igrid,iloc,j,l,idir)
510 bpsend(gu,gv,idir)=bp%blocks(igrid)%b(iloc,j,l,idir)
511 end do
512 counts(gu,gv)=1
513 if(kplane==nxyz(axis) .and.&
514 ps(igrid)%is_physical_boundary(2*axis)) then
515 face_send(gu,gv)=mh_boundary_normal(igrid,iloc,j,l,axis)
516 face_counts(gu,gv)=1
517 end if
518 end do
519 end do
520 case(2)
521 jloc=ixmlo2+kplane-start(2)
522 do l=ixmlo3,ixmhi3; lg=start(3)+l-ixmlo3
523 do i=ixmlo1,ixmhi1; ig=start(1)+i-ixmlo1
524 gu=ig; gv=lg
525 do idir=1,3
526 bsend(gu,gv,idir)=mh_total_b(igrid,i,jloc,l,idir)
527 bpsend(gu,gv,idir)=bp%blocks(igrid)%b(i,jloc,l,idir)
528 end do
529 counts(gu,gv)=1
530 if(kplane==nxyz(axis) .and.&
531 ps(igrid)%is_physical_boundary(2*axis)) then
532 face_send(gu,gv)=mh_boundary_normal(igrid,i,jloc,l,axis)
533 face_counts(gu,gv)=1
534 end if
535 end do
536 end do
537 case(3)
538 lloc=ixmlo3+kplane-start(3)
539 do j=ixmlo2,ixmhi2; jg=start(2)+j-ixmlo2
540 do i=ixmlo1,ixmhi1; ig=start(1)+i-ixmlo1
541 gu=ig; gv=jg
542 do idir=1,3
543 bsend(gu,gv,idir)=mh_total_b(igrid,i,j,lloc,idir)
544 bpsend(gu,gv,idir)=bp%blocks(igrid)%b(i,j,lloc,idir)
545 end do
546 counts(gu,gv)=1
547 if(kplane==nxyz(axis) .and.&
548 ps(igrid)%is_physical_boundary(2*axis)) then
549 face_send(gu,gv)=mh_boundary_normal(igrid,i,j,lloc,axis)
550 face_counts(gu,gv)=1
551 end if
552 end do
553 end do
554 end select
555 end do
556 end subroutine mh_assemble_plane
557
558 double precision function mh_total_b(igrid,i,j,k,idir)
560 integer, intent(in) :: igrid,i,j,k,idir
561 mh_total_b=ps(igrid)%w(i,j,k,iw_mag(idir))
562 if(b0field) mh_total_b=mh_total_b+ps(igrid)%B0(i,j,k,idir,0)
563 end function mh_total_b
564
565 double precision function mh_boundary_normal(igrid,i,j,k,axis)
567 integer, intent(in) :: igrid,i,j,k,axis
568 integer :: ip,jp,kp
569
570 if(stagger_grid) then
571 mh_boundary_normal=ps(igrid)%ws(i,j,k,axis)
572 if(b0field) mh_boundary_normal=mh_boundary_normal+&
573 ps(igrid)%B0(i,j,k,axis,axis)
574 else
575 ip=i; jp=j; kp=k
576 select case(axis)
577 case(1); ip=i+1
578 case(2); jp=j+1
579 case(3); kp=k+1
580 end select
581 mh_boundary_normal=0.5d0*(ps(igrid)%w(i,j,k,iw_mag(axis))+&
582 ps(igrid)%w(ip,jp,kp,iw_mag(axis)))
583 if(b0field) mh_boundary_normal=mh_boundary_normal+0.5d0*(&
584 ps(igrid)%B0(i,j,k,axis,0)+ps(igrid)%B0(ip,jp,kp,axis,0))
585 end if
586 end function mh_boundary_normal
587
588 subroutine mh_build_top_b(bnormal,du,dv,uaxis,vaxis,axis,&
589 orientation,btop)
590 double precision, intent(in) :: bnormal(:,:),du,dv
591 integer, intent(in) :: uaxis,vaxis,axis,orientation
592 double precision, intent(out) :: btop(:,:,:)
593 integer :: i,j,nu,nv
594
595 nu=size(bnormal,1); nv=size(bnormal,2)
596 btop=0.d0
597 ! The same odd/even recurrence makes the centered transverse curl of b
598 ! equal to the supplied top-face normal field at interior points.
599 if(nu>=2) then
600 do j=1,nv
601 btop(2,j,vaxis)=0.25d0*dble(orientation)*du*&
602 (bnormal(1,j)+bnormal(2,j))
603 do i=3,nu
604 btop(i,j,vaxis)=btop(i-2,j,vaxis)+&
605 dble(orientation)*du*bnormal(i-1,j)
606 end do
607 end do
608 end if
609 if(nv>=2) then
610 do i=1,nu
611 btop(i,2,uaxis)=-0.25d0*dble(orientation)*dv*&
612 (bnormal(i,1)+bnormal(i,2))
613 do j=3,nv
614 btop(i,j,uaxis)=btop(i,j-2,uaxis)-&
615 dble(orientation)*dv*bnormal(i,j-1)
616 end do
617 end do
618 end if
619 btop(:,:,axis)=0.d0
620 end subroutine mh_build_top_b
621
622 subroutine mh_make_vector_potential(btop,q,uaxis,vaxis,axis,&
623 orientation,avec)
624 double precision, intent(in) :: btop(:,:,:),q(:,:,:)
625 integer, intent(in) :: uaxis,vaxis,axis,orientation
626 double precision, intent(out) :: avec(:,:,:)
627
628 avec=btop
629 avec(:,:,uaxis)=avec(:,:,uaxis)-dble(orientation)*q(:,:,vaxis)
630 avec(:,:,vaxis)=avec(:,:,vaxis)+dble(orientation)*q(:,:,uaxis)
631 avec(:,:,axis)=0.d0
632 end subroutine mh_make_vector_potential
633
634 subroutine mh_plane_integrals(avec,apvec,b,bp,bdiff,dvolume,values)
635 double precision, intent(in) :: avec(:,:,:),apvec(:,:,:)
636 double precision, intent(in) :: b(:,:,:),bp(:,:,:),dvolume
637 double precision, intent(out) :: bdiff(:,:,:)
638 double precision, intent(inout) :: values(11)
639
640 bdiff=b-bp
641 values(1)=values(1)+sum((avec+apvec)*bdiff)*dvolume
642 values(2)=values(2)+sum((avec-apvec)*bdiff)*dvolume
643 values(3)=values(3)+2.d0*sum(apvec*bdiff)*dvolume
644 values(4)=values(4)+0.5d0*sum(b**2)*dvolume
645 ! Use the full-field A*B scale for the near-zero Hm decision. A scale
646 ! based only on B-Bp collapses together with a potential field and would
647 ! make a roundoff-level Hm look like a valid denominator.
648 values(5)=values(5)+sum((dsqrt(sum(avec**2,dim=3))+&
649 dsqrt(sum(apvec**2,dim=3)))*(dsqrt(sum(b**2,dim=3))+&
650 dsqrt(sum(bp**2,dim=3))))*dvolume
651 end subroutine mh_plane_integrals
652
653 subroutine mh_plane_quality(state,qcur,qcurp,bcur,uaxis,vaxis,axis,&
654 orientation,du,dv,da,dvolume,values)
655 double precision, intent(in) :: state(:,:,:),qcur(:,:,:),qcurp(:,:,:)
656 double precision, intent(in) :: bcur(:,:,:),du,dv,da,dvolume
657 integer, intent(in) :: uaxis,vaxis,axis,orientation
658 double precision, intent(inout) :: values(11)
659 double precision :: curl_a(3),curl_ap(3),btarget(3),bptarget(3),divb
660 double precision :: s,du_av,dv_au,du_apv,dv_apu
661 integer :: i,j
662
663 s=dble(orientation)
664 do j=2,size(state,2)-1
665 do i=2,size(state,1)-1
666 curl_a=0.d0
667 curl_ap=0.d0
668 curl_a(uaxis)=-(state(i,j,mh_q2_b-1+uaxis)-&
669 qcur(i,j,uaxis))/(2.d0*da)
670 curl_a(vaxis)=-(state(i,j,mh_q2_b-1+vaxis)-&
671 qcur(i,j,vaxis))/(2.d0*da)
672 curl_ap(uaxis)=-(state(i,j,mh_q2_bp-1+uaxis)-&
673 qcurp(i,j,uaxis))/(2.d0*da)
674 curl_ap(vaxis)=-(state(i,j,mh_q2_bp-1+vaxis)-&
675 qcurp(i,j,vaxis))/(2.d0*da)
676 du_av=(state(i+1,j,mh_btop-1+vaxis)+&
677 s*state(i+1,j,mh_q1_b-1+uaxis)-&
678 state(i-1,j,mh_btop-1+vaxis)-&
679 s*state(i-1,j,mh_q1_b-1+uaxis))/(2.d0*du)
680 dv_au=(state(i,j+1,mh_btop-1+uaxis)-&
681 s*state(i,j+1,mh_q1_b-1+vaxis)-&
682 state(i,j-1,mh_btop-1+uaxis)+&
683 s*state(i,j-1,mh_q1_b-1+vaxis))/(2.d0*dv)
684 du_apv=(state(i+1,j,mh_btop-1+vaxis)+&
685 s*state(i+1,j,mh_q1_bp-1+uaxis)-&
686 state(i-1,j,mh_btop-1+vaxis)-&
687 s*state(i-1,j,mh_q1_bp-1+uaxis))/(2.d0*du)
688 dv_apu=(state(i,j+1,mh_btop-1+uaxis)-&
689 s*state(i,j+1,mh_q1_bp-1+vaxis)-&
690 state(i,j-1,mh_btop-1+uaxis)+&
691 s*state(i,j-1,mh_q1_bp-1+vaxis))/(2.d0*dv)
692 curl_a(axis)=s*(du_av-dv_au)
693 curl_ap(axis)=s*(du_apv-dv_apu)
694 btarget=state(i,j,mh_b1:mh_b1+2)
695 bptarget=state(i,j,mh_bp1:mh_bp1+2)
696 values(6)=values(6)+sum((curl_a-btarget)**2)*dvolume
697 values(7)=values(7)+sum(btarget**2)*dvolume
698 values(8)=values(8)+sum((curl_ap-bptarget)**2)*dvolume
699 values(9)=values(9)+sum(bptarget**2)*dvolume
700 divb=(state(i+1,j,mh_b1-1+uaxis)-&
701 state(i-1,j,mh_b1-1+uaxis))/(2.d0*du)+&
702 (state(i,j+1,mh_b1-1+vaxis)-&
703 state(i,j-1,mh_b1-1+vaxis))/(2.d0*dv)+&
704 (state(i,j,mh_b2-1+axis)-bcur(i,j,axis))/(2.d0*da)
705 values(10)=values(10)+divb**2*dvolume
706 values(11)=values(11)+sum(btarget**2)*dvolume
707 end do
708 end do
709 end subroutine mh_plane_quality
710
711 subroutine mh_debug_pack_plane(bp,avec,apvec,bdiff,packed)
712 double precision, intent(in) :: bp(:,:,:),avec(:,:,:),apvec(:,:,:)
713 double precision, intent(in) :: bdiff(:,:,:)
714 double precision, intent(out) :: packed(:,:)
715 integer :: i,j,ip
716
717 ip=0
718 do j=1,size(bp,2)
719 do i=1,size(bp,1)
720 ip=ip+1
721 packed(1:3,ip)=bp(i,j,1:3)
722 packed(4:6,ip)=avec(i,j,1:3)
723 packed(7:9,ip)=apvec(i,j,1:3)
724 packed(10,ip)=sum((avec(i,j,1:3)+apvec(i,j,1:3))*&
725 bdiff(i,j,1:3))
726 packed(11,ip)=sum((avec(i,j,1:3)-apvec(i,j,1:3))*&
727 bdiff(i,j,1:3))
728 packed(12,ip)=2.d0*sum(apvec(i,j,1:3)*bdiff(i,j,1:3))
729 end do
730 end do
731 end subroutine mh_debug_pack_plane
732
733 subroutine mh_debug_vti_begin(writer,config,nxyz,cell_dx)
734 use mod_comm_lib, only: mpistop
736 xprobmin1,xprobmin2,xprobmin3,type_endian
737 type(mh_debug_vti_state), intent(out) :: writer
738 type(magnetic_helicity_config), intent(in) :: config
739 integer, intent(in) :: nxyz(3)
740 double precision, intent(in) :: cell_dx(3)
741 character(len=mh_name_len) :: filename
742 character(len=1024) :: line
743 character(len=12) :: byte_order
744 integer :: io_status,i
745 integer(kind=8) :: ncell
746
747 writer=mh_debug_vti_state()
748 writer%enabled=config%write_debug_vti
749 writer%nxyz=nxyz
750 if(.not.writer%enabled) return
751 if(len_trim(config%debug_vti_file)>0) then
752 filename=trim(config%debug_vti_file)
753 else
754 write(filename,'(a,"_helicity_debug",i4.4,".vti")') &
756 end if
757
758 ncell=int(nxyz(1),8)*int(nxyz(2),8)*int(nxyz(3),8)
759 writer%nbytes(1:3)=3_8*ncell*8_8
760 writer%nbytes(4:6)=ncell*8_8
761 writer%offsets(1)=0_8
762 do i=2,mh_debug_narray
763 writer%offsets(i)=writer%offsets(i-1)+8_8+writer%nbytes(i-1)
764 end do
765
766 if(mype/=0) return
767 byte_order=merge('LittleEndian','BigEndian ',type_endian==1)
768 open(newunit=writer%unit,file=trim(filename),status='replace',&
769 action='write',access='stream',form='unformatted',iostat=io_status)
770 if(io_status/=0) call mpistop('could not open magnetic-helicity debug VTI')
771 call mh_stream_line(writer%unit,'<?xml version="1.0"?>')
772 call mh_stream_line(writer%unit,'<VTKFile type="ImageData" version="1.0" '//&
773 'byte_order="'//trim(byte_order)//'" header_type="UInt64">')
774 write(line,'(a,6(i0,1x),a,3(es24.16,1x),a,3(es24.16,1x),a)') &
775 ' <ImageData WholeExtent="',0,nxyz(1),0,nxyz(2),0,nxyz(3),&
776 '" Origin="',xprobmin1,xprobmin2,xprobmin3,'" Spacing="',&
777 cell_dx,'">'
778 call mh_stream_line(writer%unit,trim(line))
779 write(line,'(a,6(i0,1x),a)') ' <Piece Extent="',0,nxyz(1),&
780 0,nxyz(2),0,nxyz(3),'">'
781 call mh_stream_line(writer%unit,trim(line))
782 call mh_stream_line(writer%unit,' <CellData>')
783 call mh_debug_vti_array_line(writer%unit,'Bp',3,writer%offsets(1))
784 call mh_debug_vti_array_line(writer%unit,'A',3,writer%offsets(2))
785 call mh_debug_vti_array_line(writer%unit,'Ap',3,writer%offsets(3))
786 call mh_debug_vti_array_line(writer%unit,'h_m',1,writer%offsets(4))
787 call mh_debug_vti_array_line(writer%unit,'h_J',1,writer%offsets(5))
788 call mh_debug_vti_array_line(writer%unit,'h_PJ',1,writer%offsets(6))
789 call mh_stream_line(writer%unit,' </CellData>')
790 call mh_stream_line(writer%unit,' </Piece>')
791 call mh_stream_line(writer%unit,' </ImageData>')
792 write(writer%unit) '<AppendedData encoding="raw">_'
793 inquire(unit=writer%unit,pos=writer%data_pos)
794 do i=1,mh_debug_narray
795 write(writer%unit,pos=writer%data_pos+writer%offsets(i),&
796 iostat=io_status) writer%nbytes(i)
797 if(io_status/=0) call mpistop('could not initialize debug VTI payload')
798 end do
799 end subroutine mh_debug_vti_begin
800
801 subroutine mh_debug_vti_array_line(vti_unit,name,ncomponent,offset)
802 integer, intent(in) :: vti_unit,ncomponent
803 character(len=*), intent(in) :: name
804 integer(kind=8), intent(in) :: offset
805 character(len=512) :: line
806
807 write(line,'(a,a,a,i0,a,i0,a)') &
808 ' <DataArray type="Float64" Name="',trim(name),&
809 '" NumberOfComponents="',ncomponent,&
810 '" format="appended" offset="',offset,'"/>'
811 call mh_stream_line(vti_unit,trim(line))
812 end subroutine mh_debug_vti_array_line
813
814 subroutine mh_stream_line(vti_unit,line)
815 integer, intent(in) :: vti_unit
816 character(len=*), intent(in) :: line
817 write(vti_unit) trim(line)//new_line('a')
818 end subroutine mh_stream_line
819
820 subroutine mh_debug_vti_write_plane(writer,owner,kplane,uaxis,vaxis,&
821 axis,packed)
822 use mod_comm_lib, only: mpistop
824 type(mh_debug_vti_state), intent(inout) :: writer
825 integer, intent(in) :: owner,kplane,uaxis,vaxis,axis
826 double precision, intent(inout) :: packed(:,:)
827 integer :: status(mpi_status_size),i,j,ip,ixyz(3),io_status
828 integer(kind=8) :: global_index,position
829
830 if(.not.writer%enabled) return
831 if(owner/=0) then
832 if(mype==owner) call mpi_send(packed,size(packed),&
833 mpi_double_precision,0,mh_debug_tag,icomm,ierrmpi)
834 if(mype==0) call mpi_recv(packed,size(packed),mpi_double_precision,&
835 owner,mh_debug_tag,icomm,status,ierrmpi)
836 end if
837 if(mype/=0) return
838
839 ip=0
840 do j=1,writer%nxyz(vaxis)
841 do i=1,writer%nxyz(uaxis)
842 ip=ip+1
843 ixyz=1
844 ixyz(axis)=kplane
845 ixyz(uaxis)=i
846 ixyz(vaxis)=j
847 global_index=int(ixyz(1),8)+int(writer%nxyz(1),8)*(&
848 int(ixyz(2)-1,8)+int(writer%nxyz(2),8)*int(ixyz(3)-1,8))
849 position=writer%data_pos+writer%offsets(1)+8_8+&
850 (global_index-1_8)*24_8
851 write(writer%unit,pos=position,iostat=io_status) packed(1:3,ip)
852 if(io_status/=0) call mpistop('could not write Bp debug VTI payload')
853 position=writer%data_pos+writer%offsets(2)+8_8+&
854 (global_index-1_8)*24_8
855 write(writer%unit,pos=position,iostat=io_status) packed(4:6,ip)
856 if(io_status/=0) call mpistop('could not write A debug VTI payload')
857 position=writer%data_pos+writer%offsets(3)+8_8+&
858 (global_index-1_8)*24_8
859 write(writer%unit,pos=position,iostat=io_status) packed(7:9,ip)
860 if(io_status/=0) call mpistop('could not write Ap debug VTI payload')
861 position=writer%data_pos+writer%offsets(4)+8_8+&
862 (global_index-1_8)*8_8
863 write(writer%unit,pos=position,iostat=io_status) packed(10,ip)
864 if(io_status/=0) call mpistop('could not write hm debug VTI payload')
865 position=writer%data_pos+writer%offsets(5)+8_8+&
866 (global_index-1_8)*8_8
867 write(writer%unit,pos=position,iostat=io_status) packed(11,ip)
868 if(io_status/=0) call mpistop('could not write hJ debug VTI payload')
869 position=writer%data_pos+writer%offsets(6)+8_8+&
870 (global_index-1_8)*8_8
871 write(writer%unit,pos=position,iostat=io_status) packed(12,ip)
872 if(io_status/=0) call mpistop('could not write hPJ debug VTI payload')
873 end do
874 end do
875 end subroutine mh_debug_vti_write_plane
876
877 subroutine mh_debug_vti_end(writer)
879 type(mh_debug_vti_state), intent(inout) :: writer
880 integer(kind=8) :: footer_pos
881
882 if(.not.writer%enabled) return
883 if(mype==0) then
884 footer_pos=writer%data_pos+writer%offsets(mh_debug_narray)+8_8+&
885 writer%nbytes(mh_debug_narray)
886 write(writer%unit,pos=footer_pos) new_line('a')//&
887 '</AppendedData>'//new_line('a')//'</VTKFile>'//new_line('a')
888 close(writer%unit)
889 end if
890 call mpi_barrier(icomm,ierrmpi)
891 end subroutine mh_debug_vti_end
892}
893
894end module mod_magnetic_helicity
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
Module with geometry-related routines (e.g., divergence, curl)
Definition mod_geometry.t:2
integer coordinate
Definition mod_geometry.t:7
integer, parameter cartesian
Definition mod_geometry.t:8
update ghost cells of all blocks including physical boundaries
subroutine getbc(time, qdt, psb, nwstart, nwbc)
do update ghost cells of all blocks including physical boundaries
This module contains definitions of global parameters and variables and some generic functions/subrou...
integer, parameter unitpar
file handle for IO
double precision global_time
The global simulation time.
integer snapshotini
Resume from the snapshot with this index.
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 time_convert_factor
Conversion factor for time unit.
character(len=std_len), dimension(:), allocatable par_files
Which par files are used as input.
integer icomm
The MPI communicator.
integer mype
The rank of the current MPI task.
integer ierrmpi
A global MPI error return code.
double precision, dimension(:), allocatable, parameter d
double precision unit_magneticfield
Physical scaling factor for magnetic field.
logical, dimension(ndim) stretched_dim
True if a dimension is stretched.
integer npe
The number of MPI tasks.
logical b0field
split magnetic field as background B0 field
double precision, dimension(:,:), allocatable rnode
Corner coordinates.
logical si_unit
Use SI units (.true.) or use cgs units (.false.)
logical convert
If true and restart_from_file is given, convert snapshots to other file formats.
character(len=std_len) base_filename
Base file name for simulation output, which will be followed by a number.
integer, dimension(:,:), allocatable node
Finite-volume relative magnetic helicity on a uniform Cartesian mesh.
type(magnetic_helicity_result), save, public mh_last_result
character(len=mh_name_len), public mh_debug_vti_file
subroutine, public mh_params_read(files)
character(len=mh_name_len), public mh_output_file
logical, public mh_write_debug_vti
logical, public mh_write_mg_timing
double precision, public mh_max_flux_imbalance
subroutine, public mh_run_task()
subroutine, public compute_magnetic_helicity_fv(config, result)
double precision, public mh_mg_tolerance
Finite-volume potential reference field for magnetic diagnostics.
subroutine, public solve_magnetic_reference_fv(config, bp, result)
subroutine, public free_magnetic_reference_field(bp)