MPI-AMRVAC 3.2
The MPI - Adaptive Mesh Refinement - Versatile Advection Code (development version)
Loading...
Searching...
No Matches
mod_lfff.t
Go to the documentation of this file.
1!> Program to extrapolate linear force-free fields in 3D Cartesian coordinates,
2!> based on exact Green function method (Chiu & Hilton 1977 ApJ 212,873).
3!>
4!> Usage:
5!> 1 In the subroutine usr_set_parameters of mod_usr.t:
6!> To extrapolate a linear force free field from a observed magnetogram
7!> prepared in a data file, e.g., 'hmiM720sxxxx.dat' replace
8!> call init_bc_fff_data('hmiM720sxxxx.dat',unit_length,unit_magneticfield)
9!> 'hmiM720sxxxx.dat' must be a binary file containing nx1,nx2,xc1,xc2,dxm1,
10!> dxm2, Bz0(nx1,nx2). Integers nx1 and nx2 give the resolution of the
11!> uniform-grid magentogram. Others are double-precision floats. xc1 and xc2
12!> are coordinates of the central point of the magnetogram. dxm1 and dxm2
13!> are the cell sizes for each direction, Bz0 is the vertical conponent
14!> of magetic field on the solar surface from observations.
15!>2 In the subroutine usr_init_one_grid of mod_usr.t,
16!> add lines like:
17!>
18!> double precision :: Bf(ixG^S,1:ndir), alpha, zshift
19!>
20!> alpha=0.d0 ! potential field
21!> !alpha=0.08d0 ! non-potential linear force-free field
22!> zshift=0.05d0 ! lift your box zshift heigher to the bottom magnetogram
23!> call calc_lin_fff(ixG^L,ix^L,Bf,x,alpha,zshift)
24!>
25!>3 Notice that the resolution of input magnetogram must be better than the best
26!> resolution of your AMR grid to have a good behavior close to the bottom layer
28 implicit none
29
30 double precision, save :: bzmax,darea
31 double precision, allocatable, save :: bz0(:,:)
32 double precision, allocatable, save :: xa1(:),xa2(:)
33 integer, save :: nx1,nx2
34 double precision, parameter :: lfff_mode_tolerance=1.d-12
35 double precision, parameter :: lfff_resonance_tolerance=1.d-10
36 double precision, parameter :: lfff_flux_balance_tolerance=1.d-8
37{^iftwod
38 double precision, allocatable, save :: l2d_br(:),l2d_bi(:),l2d_k(:)
39 double precision, save :: l2d_b0,l2d_alpha,l2d_y0,l2d_ytop
40 integer, save :: l2d_nm
41 logical, save :: l2d_closed,l2d_ready=.false.
42}
43
44contains
45
46 subroutine init_b_fff_data(magnetogramname,qLunit,qBunit)
48 use mod_comm_lib, only: mpistop
49
50 double precision, intent(in) :: qLunit,qBunit
51 double precision :: xc1,xc2,dxm1,dxm2
52 integer, dimension(MPI_STATUS_SIZE) :: statuss
53 integer :: file_handle,i
54 logical :: aexist
55 character(len=*), intent(in) :: magnetogramname
56 ! nx1,nx2 are numbers of cells for each direction
57 ! xc1,xc2 are coordinates of the central point of the magnetogram
58 ! dxm1,dxm2 are cell sizes for each direction
59 ! Bz0 is the 2D Bz magnetogram
60 inquire(file=magnetogramname,exist=aexist)
61 if(.not. aexist) then
62 if(mype==0) write(*,'(2a)') "can not find file:",magnetogramname
63 call mpistop("no input magnetogram----init_b_fff_data")
64 end if
65 call mpi_file_open(icomm,magnetogramname,mpi_mode_rdonly,mpi_info_null,&
66 file_handle,ierrmpi)
67 call mpi_file_read_all(file_handle,nx1,1,mpi_integer,statuss,ierrmpi)
68 call mpi_file_read_all(file_handle,nx2,1,mpi_integer,statuss,ierrmpi)
69 allocate(bz0(nx1,nx2))
70 call mpi_file_read_all(file_handle,xc1,1,mpi_double_precision,statuss,ierrmpi)
71 call mpi_file_read_all(file_handle,xc2,1,mpi_double_precision,statuss,ierrmpi)
72 call mpi_file_read_all(file_handle,dxm1,1,mpi_double_precision,statuss,ierrmpi)
73 call mpi_file_read_all(file_handle,dxm2,1,mpi_double_precision,statuss,ierrmpi)
74 call mpi_file_read_all(file_handle,bz0,nx1*nx2,mpi_double_precision,&
75 statuss,ierrmpi)
76 call mpi_file_close(file_handle,ierrmpi)
77 allocate(xa1(nx1))
78 allocate(xa2(nx2))
79 do i=1,nx1
80 xa1(i) = xc1 + (dble(i) - dble(nx1)/2.d0 - 0.5d0)*dxm1
81 enddo
82 do i=1,nx2
83 xa2(i) = xc2 + (dble(i) - dble(nx2)/2.d0 - 0.5d0)*dxm2
84 enddo
85 ! declare and define global variables Lunit and Bunit to be your length unit in
86 ! cm and magnetic strength unit in Gauss first
87 dxm1=dxm1/qlunit
88 dxm2=dxm2/qlunit
89 xa1=xa1/qlunit
90 xa2=xa2/qlunit
91 darea=dxm1*dxm2
92 bz0=bz0/qbunit
93 bzmax=maxval(dabs(bz0(:,:)))
94
95 ! normalize b
97 if(mype==0) then
98 print*,'magnetogram xrange:',minval(xa1),maxval(xa1)
99 print*,'magnetogram yrange:',minval(xa2),maxval(xa2)
100 end if
101
102 if(mype==0) then
103 print*,'extrapolating 3D force-free field from an observed Bz '
104 print*,'magnetogram of',nx1,'by',nx2,'pixels. Bzmax=',bzmax
105 endif
106
107 end subroutine init_b_fff_data
108
109{^ifthreed
110 subroutine init_b_fff_data_driven_boundary(boundaryname,qLunit,qBunit,qxc1,qxc2)
112 use mod_comm_lib, only: mpistop
114
115 character(len=*), intent(in) :: boundaryname
116 double precision, intent(in) :: qLunit,qBunit
117 double precision, intent(in), optional :: qxc1,qxc2
118
119 double precision :: snapshot_time,dxm1,dxm2,xc1,xc2
120 double precision, allocatable :: bframe(:,:,:)
121 integer :: i,bnx,bny
122
123 call read_data_driven_boundary_frame(boundaryname,snapshot_time,bnx,bny,dxm1,dxm2,bframe)
124
125 nx1 = bnx
126 nx2 = bny
127 if(allocated(bz0)) deallocate(bz0)
128 if(allocated(xa1)) deallocate(xa1)
129 if(allocated(xa2)) deallocate(xa2)
130 allocate(bz0(nx1,nx2))
131 allocate(xa1(nx1))
132 allocate(xa2(nx2))
133
134 ! Python V1 stores dx/dy in km and B in Gauss.
135 dxm1 = dxm1*1.d5
136 dxm2 = dxm2*1.d5
137 if(present(qxc1)) then
138 xc1 = qxc1
139 else
140 xc1 = 0.5d0*(xprobmin1+xprobmax1)*qlunit
141 end if
142 if(present(qxc2)) then
143 xc2 = qxc2
144 else
145 xc2 = 0.5d0*(xprobmin2+xprobmax2)*qlunit
146 end if
147
148 bz0(:,:) = bframe(:,:,3)
149 deallocate(bframe)
150
151 do i=1,nx1
152 xa1(i) = xc1 + (dble(i) - dble(nx1)/2.d0 - 0.5d0)*dxm1
153 end do
154 do i=1,nx2
155 xa2(i) = xc2 + (dble(i) - dble(nx2)/2.d0 - 0.5d0)*dxm2
156 end do
157
158 dxm1 = dxm1/qlunit
159 dxm2 = dxm2/qlunit
160 xa1 = xa1/qlunit
161 xa2 = xa2/qlunit
162 darea = dxm1*dxm2
163 bz0 = bz0/qbunit
164 bzmax = maxval(dabs(bz0(:,:)))
165 if(bzmax<=0.d0) call mpistop('zero Bz in data-driven boundary frame')
166 bz0 = bz0/bzmax
167
168 if(mype==0) then
169 print*,'data-driven boundary frame:',trim(boundaryname)
170 print*,'snapshot_time [s]:',snapshot_time
171 print*,'magnetogram xrange:',minval(xa1),maxval(xa1)
172 print*,'magnetogram yrange:',minval(xa2),maxval(xa2)
173 print*,'extrapolating potential field from Bz of',nx1,'by',nx2,'pixels. Bzmax=',bzmax
174 end if
176}
177
178 !> Check a constant-alpha magnetogram and optionally remove its core mean.
179 !> Status: 0 accepted unchanged, 1 mean removed, 2 invalid treatment,
180 !> 3 invalid threshold, 4 strict-mode imbalance, 5 above auto-balance limit.
181 subroutine lfff_balance_bottom_flux(bz,treatment,max_imbalance,&
182 imbalance_before,imbalance_after,mean_correction,status)
183 double precision, intent(inout) :: bz(:,:)
184 character(len=*), intent(in) :: treatment
185 double precision, intent(in) :: max_imbalance
186 double precision, intent(out) :: imbalance_before,imbalance_after
187 double precision, intent(out) :: mean_correction
188 integer, intent(out) :: status
189
190 double precision :: unsigned_flux
191
192 mean_correction=0.d0
193 unsigned_flux=sum(dabs(bz))
194 imbalance_before=dabs(sum(bz))/max(unsigned_flux,tiny(1.d0))
195 imbalance_after=imbalance_before
196 status=0
197
198 if(max_imbalance<0.d0 .or. max_imbalance>1.d0) then
199 status=3
200 return
201 end if
202
203 select case(trim(adjustl(treatment)))
204 case('strict')
205 if(imbalance_before>lfff_flux_balance_tolerance) status=4
206 case('subtract_mean')
207 if(imbalance_before>max_imbalance) then
208 status=5
209 return
210 end if
211 if(imbalance_before>lfff_flux_balance_tolerance) then
212 mean_correction=sum(bz)/dble(size(bz))
213 bz=bz-mean_correction
214 unsigned_flux=sum(dabs(bz))
215 imbalance_after=dabs(sum(bz))/max(unsigned_flux,tiny(1.d0))
216 status=1
217 end if
218 case default
219 status=2
220 end select
221 end subroutine lfff_balance_bottom_flux
222
223{^ifthreed
224 !> Extrapolate a Cartesian potential field with horizontal Fourier modes.
225 !> The bottom magnetogram must match the level-one physical cell centers.
226 !> Results are streamed one AMRVAC block layer at a time and written directly
227 !> to the distributed cell-centered magnetic variables.
228 subroutine extrapolate_potential_fft(iw_b,padding_factor,source_plane_depth,&
229 alpha,top_boundary,flux_treatment,max_flux_imbalance)
230 use mpi
232 use mod_comm_lib, only: mpistop
235 use mod_forest, only: tree_root
236 use mod_geometry
237
238 integer, intent(in) :: iw_b(3)
239 integer, intent(in), optional :: padding_factor
240 double precision, intent(in), optional :: source_plane_depth
241 double precision, intent(in), optional :: alpha
242 character(len=*), intent(in), optional :: top_boundary
243 character(len=*), intent(in), optional :: flux_treatment
244 double precision, intent(in), optional :: max_flux_imbalance
245
246 double precision, allocatable :: bcore(:,:),spec_r(:,:),spec_i(:,:)
247 double precision, allocatable :: work_r(:,:),work_i(:,:),bplane(:,:,:)
248 double precision, allocatable :: sendbuf(:),recvbuf(:),kx(:),ky(:)
249 integer, allocatable :: sendcounts(:),recvcounts(:),sdispls(:),rdispls(:)
250 integer, allocatable :: cursor(:),blockpos(:,:)
251 double precision :: dx1,dx2,dx3,tol1,tol2,maxerr,z,fac,source_depth
252 double precision :: bmean,bflux,tstart,memory_mb,alpha_fft,alpha2,k2
253 double precision :: beta,transfer_b,transfer_d,top_height
254 double precision :: kx_der,ky_der,kmin,flux_imbalance,unsigned_flux
255 double precision :: flux_imbalance_before,bmean_before,bflux_before
256 double precision :: mean_correction,max_imbalance
257 double precision, parameter :: fft_memory_limit_mb=2048.d0
258 integer :: pad,npx,npy,ip0,jp0,ix0,iy0,starti
259 integer :: nb1,nb2,nb3,layer,layer_owner,kg,klocal
260 integer :: ig1,ig2,ipe,igrid,ic,ix1,ix2,pos,base,payload,slice_size
261 integer :: mode,next1,next2,nrecv,nsend,mode_status,flux_status
262 character(len=16) :: top_mode,flux_mode
263 character(len=256) :: message
264 logical :: top_closed,alpha_nonzero
265
266 if(ndim/=3) call mpistop('FFT potential field requires three dimensions')
267 if(coordinate/=cartesian) call mpistop('FFT potential field requires Cartesian coordinates')
268 if(any(stretched_dim)) call mpistop('FFT potential field requires a uniform mesh')
269 if(refine_max_level/=1 .or. levmax/=1) &
270 call mpistop('FFT potential field v1 requires refine_max_level=1')
271 if(stagger_grid) call mpistop('FFT potential field v1 does not support stagger_grid')
272 if(.not.allocated(bz0) .or. .not.allocated(xa1) .or. .not.allocated(xa2)) &
273 call mpistop('FFT potential field requires initialized magnetogram data')
274
275 pad=2
276 if(present(padding_factor)) pad=padding_factor
277 if(pad<1) call mpistop('FFT padding factor must be at least one')
278
279 source_depth=0.d0
280 if(present(source_plane_depth)) source_depth=source_plane_depth
281 if(source_depth<0.d0) call mpistop('FFT source-plane depth must not be negative')
282
283 alpha_fft=0.d0
284 if(present(alpha)) alpha_fft=alpha
285 alpha2=alpha_fft**2
286 alpha_nonzero=(alpha_fft/=0.d0)
287
288 top_mode='open'
289 if(present(top_boundary)) top_mode=trim(adjustl(top_boundary))
290 select case(trim(top_mode))
291 case('open')
292 top_closed=.false.
293 case('closed')
294 top_closed=.true.
295 case default
296 call mpistop("FFT top boundary must be 'open' or 'closed'")
297 end select
298
299 flux_mode='strict'
300 if(present(flux_treatment)) flux_mode=trim(adjustl(flux_treatment))
301 select case(trim(flux_mode))
302 case('strict','subtract_mean')
303 continue
304 case default
305 call mpistop("LFFF flux treatment must be 'strict' or 'subtract_mean'")
306 end select
307 max_imbalance=0.1d0
308 if(present(max_flux_imbalance)) max_imbalance=max_flux_imbalance
309 if(max_imbalance<0.d0 .or. max_imbalance>1.d0) &
310 call mpistop('LFFF maximum flux imbalance must be between zero and one')
311
312 dx1=dx(1,1)
313 dx2=dx(2,1)
314 dx3=dx(3,1)
315 top_height=source_depth+dble(domain_nx3)*dx3
316 tol1=1.d-8*max(1.d0,dabs(xprobmin1),dabs(xprobmax1),dabs(dx1))
317 tol2=1.d-8*max(1.d0,dabs(xprobmin2),dabs(xprobmax2),dabs(dx2))
318
319 ! Locate, by coordinates, the physical magnetogram core. This naturally
320 ! removes any symmetric ghost layers without assuming their width.
321 ix0=0
322 if(nx1>=domain_nx1) then
323 do starti=1,nx1-domain_nx1+1
324 maxerr=0.d0
325 do ix1=1,domain_nx1
326 maxerr=max(maxerr,dabs(xa1(starti+ix1-1)-&
327 (xprobmin1+(dble(ix1)-0.5d0)*dx1)))
328 end do
329 if(maxerr<=tol1) then
330 ix0=starti
331 exit
332 end if
333 end do
334 end if
335 iy0=0
336 if(nx2>=domain_nx2) then
337 do starti=1,nx2-domain_nx2+1
338 maxerr=0.d0
339 do ix2=1,domain_nx2
340 maxerr=max(maxerr,dabs(xa2(starti+ix2-1)-&
341 (xprobmin2+(dble(ix2)-0.5d0)*dx2)))
342 end do
343 if(maxerr<=tol2) then
344 iy0=starti
345 exit
346 end if
347 end do
348 end if
349 if(ix0==0 .or. iy0==0) then
350 if(mype==0) then
351 write(*,*) 'magnetogram size:',nx1,nx2
352 write(*,*) 'required physical FFT size:',domain_nx1,domain_nx2
353 write(*,*) 'AMRVAC dx/dy:',dx1,dx2
354 end if
355 call mpistop('FFT magnetogram centers do not match the level-one physical grid')
356 end if
357
358 npx=pad*domain_nx1
359 npy=pad*domain_nx2
360 if(.not.fft_size_supported(npx) .or. .not.fft_size_supported(npy)) then
361 next1=fft_next_supported(npx)
362 next2=fft_next_supported(npy)
363 write(message,'(a,i0,a,a,a,i0,a,a,a,i0,a,i0)') &
364 'unsupported padded FFT size ',npx,' (',trim(fft_factorization(npx)),&
365 ') x ',npy,' (',trim(fft_factorization(npy)),&
366 '); next supported sizes are ',next1,' x ',next2
367 call mpistop(trim(message))
368 end if
369
370 if(mod(domain_nx1,block_nx1)/=0 .or. mod(domain_nx2,block_nx2)/=0 .or. &
371 mod(domain_nx3,block_nx3)/=0) &
372 call mpistop('FFT potential field requires domain_nx divisible by block_nx')
373 nb1=domain_nx1/block_nx1
374 nb2=domain_nx2/block_nx2
375 nb3=domain_nx3/block_nx3
376 slice_size=block_nx1*block_nx2*3
377 payload=slice_size*block_nx3
378
379 ! Conservative per-rank upper bound: four padded spectral/work planes,
380 ! core plus three-component physical plane, and one full block layer in
381 ! each MPI send/receive buffer. Refuse an unexpectedly large allocation
382 ! instead of relying on the operating system to terminate a rank.
383 memory_mb=8.d0*(4.d0*dble(npx)*dble(npy)+4.d0*dble(domain_nx1)*&
384 dble(domain_nx2)+6.d0*dble(domain_nx1)*dble(domain_nx2)*&
385 dble(block_nx3))/(1024.d0**2)
386 if(memory_mb>fft_memory_limit_mb) then
387 write(message,'(a,f10.1,a,f10.1,a)') 'FFT potential field needs up to ',&
388 memory_mb,' MiB/rank, above the internal limit of ',&
389 fft_memory_limit_mb,' MiB; reduce block_nx3 or horizontal size'
390 call mpistop(trim(message))
391 end if
392
393 allocate(bcore(domain_nx1,domain_nx2))
394 bcore=bz0(ix0:ix0+domain_nx1-1,iy0:iy0+domain_nx2-1)
395 bmean_before=sum(bcore)/dble(size(bcore))*bzmax
396 bflux_before=sum(bcore)*bzmax*dx1*dx2
397 unsigned_flux=sum(dabs(bcore))
398 flux_imbalance_before=dabs(sum(bcore))/max(unsigned_flux,tiny(1.d0))
399 mean_correction=0.d0
400 flux_status=0
401 if(alpha_nonzero) then
402 call lfff_balance_bottom_flux(bcore,flux_mode,max_imbalance,&
403 flux_imbalance_before,flux_imbalance,mean_correction,flux_status)
404 select case(flux_status)
405 case(0,1)
406 continue
407 case(4)
408 if(mype==0) then
409 write(*,*) 'FFT LFFF alpha:',alpha_fft
410 write(*,*) 'relative bottom flux imbalance:',flux_imbalance_before
411 write(*,*) 'strict balance tolerance:',lfff_flux_balance_tolerance
412 end if
413 call mpistop('constant-alpha FFT LFFF requires a flux-balanced bottom magnetogram')
414 case(5)
415 if(mype==0) then
416 write(*,*) 'FFT LFFF alpha:',alpha_fft
417 write(*,*) 'relative bottom flux imbalance:',flux_imbalance_before
418 write(*,*) 'maximum automatic-balance imbalance:',max_imbalance
419 end if
420 call mpistop('LFFF magnetogram is too unbalanced for automatic mean subtraction')
421 case default
422 call mpistop('invalid LFFF flux-balance configuration')
423 end select
424 else
425 flux_imbalance=flux_imbalance_before
426 end if
427 bmean=sum(bcore)/dble(npx*npy)*bzmax
428 bflux=sum(bcore)*bzmax*dx1*dx2
429 ip0=(npx-domain_nx1)/2+1
430 jp0=(npy-domain_nx2)/2+1
431
432 allocate(spec_r(npx,npy),spec_i(npx,npy),work_r(npx,npy),work_i(npx,npy))
433 allocate(kx(npx),ky(npy))
434 spec_r=0.d0
435 spec_i=0.d0
436 if(mype==0) then
437 spec_r(ip0:ip0+domain_nx1-1,jp0:jp0+domain_nx2-1)=bcore
438 call fft_2d_real_imag(spec_r,spec_i,.false.)
439 end if
440 call mpi_bcast(spec_r,npx*npy,mpi_double_precision,0,icomm,ierrmpi)
441 call mpi_bcast(spec_i,npx*npy,mpi_double_precision,0,icomm,ierrmpi)
442
443 do ix1=1,npx
444 mode=ix1-1
445 if(mode>npx/2) mode=mode-npx
446 kx(ix1)=2.d0*dpi*dble(mode)/(dble(npx)*dx1)
447 end do
448 do ix2=1,npy
449 mode=ix2-1
450 if(mode>npy/2) mode=mode-npy
451 ky(ix2)=2.d0*dpi*dble(mode)/(dble(npy)*dx2)
452 end do
453
454 kmin=min(2.d0*dpi/(dble(npx)*dx1),2.d0*dpi/(dble(npy)*dx2))
455 if(alpha_nonzero .and. .not.top_closed .and. dabs(alpha_fft)>=kmin) then
456 if(mype==0) then
457 write(*,*) 'FFT LFFF alpha:',alpha_fft
458 write(*,*) 'smallest nonzero padded horizontal wavenumber:',kmin
459 end if
460 call mpistop('open-top FFT LFFF requires abs(alpha) < kmin')
461 end if
462
463 ! A finite-height closed LFFF permits oscillatory low-wavenumber modes,
464 ! except at the eigenvalues sin(beta*L)=0 where the two Bz boundary
465 ! conditions are singular. Validate these modes before distributed work.
466 if(alpha_nonzero .and. top_closed) then
467 do ix2=1,npy
468 do ix1=1,npx
469 k2=kx(ix1)**2+ky(ix2)**2
470 if(k2<=0.d0 .or. alpha2<=k2) cycle
471 beta=dsqrt(alpha2-k2)
472 call lfff_fft_transfer(k2,alpha_fft,0.d0,top_height,.true.,&
473 transfer_b,transfer_d,mode_status)
474 if(mode_status==2) then
475 if(mype==0) then
476 write(*,*) 'FFT closed LFFF resonant mode kx,ky:',kx(ix1),ky(ix2)
477 write(*,*) 'alpha, beta, top height:',alpha_fft,beta,top_height
478 end if
479 call mpistop('closed-top FFT LFFF is singular for a horizontal mode')
480 end if
481 end do
482 end do
483 end if
484
485 allocate(sendcounts(0:npe-1),recvcounts(0:npe-1))
486 allocate(sdispls(0:npe-1),rdispls(0:npe-1),cursor(0:npe-1))
487 allocate(blockpos(nb1,nb2))
488 tstart=mpi_wtime()
489
490 if(mype==0) then
491 write(*,*) 'FFT potential field physical size:',domain_nx1,domain_nx2,domain_nx3
492 write(*,*) 'FFT padded size:',npx,npy,' padding factor:',pad
493 write(*,*) 'FFT conservative memory bound [MiB/rank]:',memory_mb
494 write(*,*) 'FFT source plane below lower face:',source_depth
495 write(*,*) 'FFT alpha and top boundary:',alpha_fft,trim(top_mode)
496 if(top_closed) write(*,*) 'FFT closed-top height above source plane:',top_height
497 write(*,*) 'FFT magnetogram core starts at:',ix0,iy0
498 write(*,*) 'FFT input core mean Bz:',bmean_before,' input net bottom flux:',bflux_before
499 if(alpha_nonzero) then
500 write(*,*) 'FFT LFFF flux treatment:',trim(flux_mode)
501 write(*,*) 'FFT input relative bottom flux imbalance:',flux_imbalance_before
502 if(flux_status==1) &
503 write(*,*) 'FFT subtracted core mean Bz:',mean_correction*bzmax
504 write(*,*) 'FFT corrected relative bottom flux imbalance:',flux_imbalance
505 end if
506 write(*,*) 'FFT padded zero-mode mean Bz:',bmean,' net bottom flux:',bflux
507 end if
508
509 do layer=1,nb3
510 layer_owner=mod(layer-1,npe)
511 sendcounts=0
512 recvcounts=0
513 sdispls=0
514 rdispls=0
515 blockpos=0
516
517 if(mype==layer_owner) then
518 do ig2=1,nb2
519 do ig1=1,nb1
520 ipe=tree_root(ig1,ig2,layer)%node%ipe
521 sendcounts(ipe)=sendcounts(ipe)+payload
522 end do
523 end do
524 do ipe=1,npe-1
525 sdispls(ipe)=sdispls(ipe-1)+sendcounts(ipe-1)
526 end do
527 cursor=sdispls
528 do ig2=1,nb2
529 do ig1=1,nb1
530 ipe=tree_root(ig1,ig2,layer)%node%ipe
531 blockpos(ig1,ig2)=cursor(ipe)+1
532 cursor(ipe)=cursor(ipe)+payload
533 end do
534 end do
535 end if
536
537 nrecv=0
538 do ig2=1,nb2
539 do ig1=1,nb1
540 if(tree_root(ig1,ig2,layer)%node%ipe==mype) nrecv=nrecv+payload
541 end do
542 end do
543 recvcounts(layer_owner)=nrecv
544 nsend=sum(sendcounts)
545 allocate(sendbuf(max(1,nsend)),recvbuf(max(1,nrecv)))
546 sendbuf=0.d0
547 recvbuf=0.d0
548
549 if(mype==layer_owner) then
550 allocate(bplane(domain_nx1,domain_nx2,3))
551 do klocal=1,block_nx3
552 kg=(layer-1)*block_nx3+klocal
553 z=(dble(kg)-0.5d0)*dx3+source_depth
554
555 do ic=1,3
556 do ix2=1,npy
557 do ix1=1,npx
558 k2=kx(ix1)**2+ky(ix2)**2
559 kx_der=kx(ix1)
560 ky_der=ky(ix2)
561 ! A first derivative of the self-conjugate Nyquist mode cannot
562 ! be represented by a real grid function. Use the standard
563 ! zero derivative symbol for that one mode in each direction.
564 if(mod(npx,2)==0 .and. ix1==npx/2+1) kx_der=0.d0
565 if(mod(npy,2)==0 .and. ix2==npy/2+1) ky_der=0.d0
566
567 if(k2<=0.d0) then
568 if(alpha_nonzero) then
569 transfer_b=0.d0
570 else
571 ! A potential field retains the padded-domain mean flux.
572 ! For a closed top only the fluctuating modes are closed.
573 transfer_b=1.d0
574 end if
575 transfer_d=0.d0
576 else
577 call lfff_fft_transfer(k2,alpha_fft,z,top_height,&
578 top_closed,transfer_b,transfer_d,mode_status)
579 if(mode_status/=0) &
580 call mpistop('invalid FFT LFFF mode reached distributed solve')
581 end if
582
583 select case(ic)
584 case(1)
585 if(k2>0.d0) then
586 fac=(kx_der*transfer_d-&
587 ky_der*alpha_fft*transfer_b)/k2
588 work_r(ix1,ix2)= fac*spec_i(ix1,ix2)
589 work_i(ix1,ix2)=-fac*spec_r(ix1,ix2)
590 else
591 work_r(ix1,ix2)=0.d0
592 work_i(ix1,ix2)=0.d0
593 end if
594 case(2)
595 if(k2>0.d0) then
596 fac=(ky_der*transfer_d+&
597 kx_der*alpha_fft*transfer_b)/k2
598 work_r(ix1,ix2)= fac*spec_i(ix1,ix2)
599 work_i(ix1,ix2)=-fac*spec_r(ix1,ix2)
600 else
601 work_r(ix1,ix2)=0.d0
602 work_i(ix1,ix2)=0.d0
603 end if
604 case(3)
605 work_r(ix1,ix2)=transfer_b*spec_r(ix1,ix2)
606 work_i(ix1,ix2)=transfer_b*spec_i(ix1,ix2)
607 end select
608 end do
609 end do
610 call fft_2d_real_imag(work_r,work_i,.true.)
611 bplane(:,:,ic)=bzmax*work_r(ip0:ip0+domain_nx1-1,&
612 jp0:jp0+domain_nx2-1)
613 end do
614
615 do ig2=1,nb2
616 do ig1=1,nb1
617 base=blockpos(ig1,ig2)+(klocal-1)*slice_size
618 pos=base
619 do ic=1,3
620 do ix2=1,block_nx2
621 do ix1=1,block_nx1
622 sendbuf(pos)=bplane((ig1-1)*block_nx1+ix1,&
623 (ig2-1)*block_nx2+ix2,ic)
624 pos=pos+1
625 end do
626 end do
627 end do
628 end do
629 end do
630 end do
631 deallocate(bplane)
632 end if
633
634 call mpi_alltoallv(sendbuf,sendcounts,sdispls,mpi_double_precision,&
635 recvbuf,recvcounts,rdispls,mpi_double_precision,icomm,ierrmpi)
636
637 pos=1
638 do ig2=1,nb2
639 do ig1=1,nb1
640 if(tree_root(ig1,ig2,layer)%node%ipe/=mype) cycle
641 igrid=tree_root(ig1,ig2,layer)%node%igrid
642 do klocal=1,block_nx3
643 do ic=1,3
644 do ix2=1,block_nx2
645 do ix1=1,block_nx1
646 ps(igrid)%w(ixmlo1+ix1-1,ixmlo2+ix2-1,&
647 ixmlo3+klocal-1,iw_b(ic))=recvbuf(pos)
648 pos=pos+1
649 end do
650 end do
651 end do
652 end do
653 end do
654 end do
655 deallocate(sendbuf,recvbuf)
656 end do
657
658 if(mype==0) write(*,*) 'FFT potential/LFFF extrapolation took:',mpi_wtime()-tstart,'s'
659 deallocate(bcore,spec_r,spec_i,work_r,work_i,kx,ky)
660 deallocate(sendcounts,recvcounts,sdispls,rdispls,cursor,blockpos)
661 end subroutine extrapolate_potential_fft
662}
663
664 !> Vertical transfer functions for one nonzero horizontal constant-alpha
665 !> Fourier mode. transfer_b multiplies Bz0, while transfer_d=-d(transfer_b)/dz
666 !> enters the horizontal field. status is zero on success, one for an
667 !> oscillatory mode in an open half-space, two for a closed-box resonance,
668 !> and three for invalid geometry.
669 subroutine lfff_fft_transfer(k2,alpha,z,top_height,top_closed,&
670 transfer_b,transfer_d,status)
671 double precision, intent(in) :: k2,alpha,z,top_height
672 logical, intent(in) :: top_closed
673 double precision, intent(out) :: transfer_b,transfer_d
674 integer, intent(out) :: status
675
676 double precision :: q2,q,beta,denominator,scale
677
678 transfer_b=0.d0
679 transfer_d=0.d0
680 status=0
681 if(k2<=0.d0 .or. top_height<=0.d0 .or. z<0.d0 .or. z>top_height) then
682 status=3
683 return
684 end if
685
686 q2=k2-alpha**2
687 scale=max(1.d0,k2,alpha**2)
688 if(.not.top_closed) then
689 if(q2< -lfff_mode_tolerance*scale) then
690 status=1
691 return
692 end if
693 q=dsqrt(max(0.d0,q2))
694 transfer_b=dexp(-q*z)
695 transfer_d=q*transfer_b
696 else if(q2>lfff_mode_tolerance*scale) then
697 q=dsqrt(q2)
698 if(q*top_height<50.d0) then
699 denominator=dsinh(q*top_height)
700 transfer_b=dsinh(q*(top_height-z))/denominator
701 transfer_d=q*dcosh(q*(top_height-z))/denominator
702 else
703 ! Algebraically identical exponential form that cannot overflow for
704 ! large q*L when 0 <= z <= L.
705 denominator=1.d0-dexp(-2.d0*q*top_height)
706 transfer_b=(dexp(-q*z)-dexp(-q*(2.d0*top_height-z)))/denominator
707 transfer_d=q*(dexp(-q*z)+dexp(-q*(2.d0*top_height-z)))/denominator
708 end if
709 else if(q2< -lfff_mode_tolerance*scale) then
710 beta=dsqrt(-q2)
711 denominator=dsin(beta*top_height)
712 if(dabs(denominator)<lfff_resonance_tolerance) then
713 status=2
714 return
715 end if
716 transfer_b=dsin(beta*(top_height-z))/denominator
717 transfer_d=beta*dcos(beta*(top_height-z))/denominator
718 else
719 ! Critical q=0 limit of sinh(q*(L-z))/sinh(q*L).
720 transfer_b=1.d0-z/top_height
721 transfer_d=1.d0/top_height
722 end if
723 end subroutine lfff_fft_transfer
724
725{^ifthreed
726 subroutine calc_lin_fff(ixI^L,ixO^L,Bf,x,alpha,zshift,idir)
727 ! PURPOSE:
728 ! Calculation to determine linear FFF from the field on
729 ! the lower boundary (Chiu and Hilton 1977 ApJ 212,873).
730 ! NOTE: Only works for Cartesian coordinates
731 ! INPUT: Bf,x
732 ! OUTPUT: updated b in w
734
735 integer, intent(in) :: ixI^L, ixO^L
736 integer, optional, intent(in) :: idir
737 double precision, intent(in) :: x(ixI^S,1:ndim),alpha,zshift
738 double precision, intent(inout) :: Bf(ixI^S,1:ndir)
739
740 double precision, dimension(ixO^S) :: cos_az,sin_az,zk,bigr,r,r2,r3,cos_ar,sin_ar,g,dgdz
741 double precision, dimension(ixO^S) :: dx1,dx2,invr3
742 double precision :: twopiinv
743 logical :: compute_dir(1:ndir)
744 integer :: idim,ixp1,ixp2
745
746 bf=0.d0
747 twopiinv = 0.5d0/dpi*bzmax*darea
748 zk(ixo^s)=x(ixo^s,3)-xprobmin3+zshift
749
750 compute_dir=.true.
751 if(present(idir)) then
752 compute_dir=.false.
753 if(idir>=1 .and. idir<=ndir) compute_dir(idir)=.true.
754 end if
755
756 ! For a potential field, the Green-function kernel simplifies exactly to
757 ! (dx,dy,z)/r**3. Avoid the trigonometric functions, singular 1/bigr
758 ! factors, and intermediate arrays needed by the general linear FFF form.
759 if(alpha==0.d0) then
760 do ixp2=1,nx2
761 do ixp1=1,nx1
762 dx1(ixo^s)=x(ixo^s,1)-xa1(ixp1)
763 dx2(ixo^s)=x(ixo^s,2)-xa2(ixp2)
764 r2(ixo^s)=dx1(ixo^s)**2+dx2(ixo^s)**2+zk(ixo^s)**2
765 where(r2(ixo^s)>0.d0)
766 invr3(ixo^s)=1.d0/(r2(ixo^s)*dsqrt(r2(ixo^s)))
767 elsewhere
768 invr3(ixo^s)=0.d0
769 end where
770 if(compute_dir(1)) bf(ixo^s,1)=bf(ixo^s,1)+&
771 bz0(ixp1,ixp2)*dx1(ixo^s)*invr3(ixo^s)
772 if(compute_dir(2)) bf(ixo^s,2)=bf(ixo^s,2)+&
773 bz0(ixp1,ixp2)*dx2(ixo^s)*invr3(ixo^s)
774 if(compute_dir(3)) bf(ixo^s,3)=bf(ixo^s,3)+&
775 bz0(ixp1,ixp2)*zk(ixo^s)*invr3(ixo^s)
776 end do
777 end do
778 bf(ixo^s,:)=bf(ixo^s,:)*twopiinv
779 return
780 end if
781
782 ! get cos and sin arrays for a non-zero linear force-free alpha
783 cos_az(ixo^s)=dcos(alpha*zk(ixo^s))
784 sin_az(ixo^s)=dsin(alpha*zk(ixo^s))
785 ! looping Bz0 pixels
786 do ixp2=1,nx2
787 do ixp1=1,nx1
788 bigr(ixo^s)=dsqrt((x(ixo^s,1)-xa1(ixp1))**2+&
789 (x(ixo^s,2)-xa2(ixp2))**2)
790 r2=bigr**2+zk**2
791 r=dsqrt(r2)
792 r3=r**3
793 cos_ar=dcos(alpha*r)
794 sin_ar=dsin(alpha*r)
795 where(bigr/=0.d0)
796 bigr=1.d0/bigr
797 end where
798 where(r/=0.d0)
799 r=1.d0/r
800 end where
801 where(r2/=0.d0)
802 r2=1.d0/r2
803 end where
804 where(r3/=0.d0)
805 r3=1.d0/r3
806 end where
807 g=(zk*cos_ar*r-cos_az)*bigr
808 dgdz=(cos_ar*(r-zk**2*r3)-alpha*zk**2*sin_ar*r2+alpha*sin_az)*bigr
809 do idim=1,ndim
810 if(present(idir)) then
811 if(idim/=idir) cycle
812 end if
813 select case(idim)
814 case(1)
815 bf(ixo^s,1)=bf(ixo^s,1)+bz0(ixp1,ixp2)*((x(ixo^s,1)-xa1(ixp1))*dgdz(ixo^s)&
816 +alpha*g(ixo^s)*(x(ixo^s,2)-xa2(ixp2)))*bigr(ixo^s)
817 case(2)
818 bf(ixo^s,2)=bf(ixo^s,2)+bz0(ixp1,ixp2)*((x(ixo^s,2)-xa2(ixp2))*dgdz(ixo^s)&
819 -alpha*g(ixo^s)*(x(ixo^s,1)-xa1(ixp1)))*bigr(ixo^s)
820 case(3)
821 bf(ixo^s,3)=bf(ixo^s,3)+bz0(ixp1,ixp2)*(zk(ixo^s)*cos_ar(ixo^s)*r3(ixo^s)+alpha*&
822 zk(ixo^s)*sin_ar(ixo^s)*r2(ixo^s))
823 end select
824 end do
825 end do
826 end do
827 bf(ixo^s,:)=bf(ixo^s,:)*twopiinv
828
829 end subroutine calc_lin_fff
830
831 subroutine get_potential_field_potential(ixI^L,ixO^L,potential,x,zshift)
832 ! PURPOSE:
833 ! Calculation scalar potential of potential field given
834 ! Bz at photosphere (Schmidt 1964 NASSP).
835 ! NOTE: Only works for Cartesian coordinates
836 ! INPUT: x,zshift
837 ! OUTPUT: potential
839
840 integer, intent(in) :: ixI^L, ixO^L
841 double precision, intent(in) :: x(ixI^S,1:ndim),zshift
842 double precision, intent(inout) :: potential(ixI^S)
843
844 double precision :: zk
845 integer :: ixp1,ixp2,ix^D
846
847 potential=0.d0
848 ! looping Bz0 pixels see equation (2)
849 !$OMP PARALLEL DO
850 do ix3=ixomin3,ixomax3
851 zk=x(ixomin1,ixomin2,ix3,3)-xprobmin3+zshift
852 do ix2=ixomin2,ixomax2
853 do ix1=ixomin1,ixomax1
854 do ixp2=1,nx2
855 do ixp1=1,nx1
856 potential(ix^d)=potential(ix^d)+0.5d0*bz0(ixp1,ixp2)*darea/&
857 (dpi*dsqrt((x(ix^d,1)-xa1(ixp1))**2+(x(ix^d,2)-xa2(ixp2))**2+zk**2))
858 end do
859 end do
860 end do
861 end do
862 end do
863 !$OMP END PARALLEL DO
864 end subroutine get_potential_field_potential
865
866 subroutine get_potential_field_potential_sphere(ixI^L,x,potential,nth,nph,magnetogram,theta,phi,r_sphere)
867 ! PURPOSE:
868 ! Calculation scalar potential of potential field given
869 ! Bz at photosphere (Schmidt 1964 NASSP).
870 ! NOTE: Only works for spherical coordinates
871 ! OUTPUT: potential
873 integer, intent(in) :: ixI^L,nth,nph
874 real*8, intent(in) :: x(ixi^s,1:ndim)
875 ! magnetogram Br on photosphere
876 real*8, intent(in) :: magnetogram(nth,nph)
877 ! theta and phi grid of the photospheric magnetogram
878 real*8, intent(in) :: theta(nth),phi(nph)
879 ! radius of photosphere
880 real*8, intent(in) :: r_sphere
881 real*8, intent(out) :: potential(ixi^s)
882
883 real*8 :: area(nth),dtheta_half,dphi,inv2pi
884 integer :: ixp1,ixp2,ix^D
885
886 potential=0.d0
887 ! assume uniformly discretized theta and phi
888 dtheta_half=0.5d0*(theta(2)-theta(1))
889 dphi=phi(2)-phi(1)
890 area(1:nth)=2.d0*r_sphere**2*sin(theta(1:nth))*sin(dtheta_half)*sin(dphi)
891 inv2pi=1.d0/(2.d0*dpi)
892
893 !$OMP PARALLEL DO
894 do ix3=iximin3,iximax3
895 do ix2=iximin2,iximax2
896 do ix1=iximin1,iximax1
897 do ixp2=1,nph
898 do ixp1=1,nth
899 potential(ix^d)=potential(ix^d)+inv2pi*magnetogram(ixp1,ixp2)*area(ixp1)/&
900 dsqrt(x(ix^d,1)**2+r_sphere**2-2.d0*x(ix^d,1)*r_sphere*&
901 (dsin(x(ix^d,2))*dsin(theta(ixp1))*dcos(phi(ixp2)-x(ix^d,3))+dcos(x(ix^d,2))*&
902 dcos(theta(ixp1))))
903 end do
904 end do
905 end do
906 end do
907 end do
908 !$OMP END PARALLEL DO
909
911
912 !> get potential magnetic field energy given normal B on all boundaries
913 subroutine potential_field_energy_mg(benergy)
917
918 real*8, intent(out) :: benergy
919 type(magnetic_reference_config) :: config
920 type(magnetic_reference_result) :: result
921 type(magnetic_reference_field) :: bp
922
923 call solve_magnetic_reference_fv(config,bp,result)
924 benergy=result%magnetic_energy
926
927 end subroutine potential_field_energy_mg
928
929 !> Solve Poisson equation of scalar potential using multigrid solver
943
944 !> To set boundary condition on physical boundaries for mg Poisson solver
945 subroutine multigrid_bc(box, nc, iv, nb, bc_type, bc)
948 type(mg_box_t), intent(in) :: box
949 integer, intent(in) :: nc
950 integer, intent(in) :: iv !< Index of variable
951 integer, intent(in) :: nb !< number of boundary from 1 to 6 for 3D
952 integer, intent(out) :: bc_type !< Type of b.c.
953 ! mg boundary values
954 double precision, intent(out) :: bc(nc, nc)
955 call magnetic_reference_bc(box,nc,iv,nb,bc_type,bc)
956
957 end subroutine multigrid_bc
958}
959
960{^iftwod
961 subroutine init_lfff_2d(bn_bottom,nx,qalpha,qy0,qtop_closed,qy_top,flux_treatment,nmodes_keep)
963 use mod_comm_lib, only: mpistop
964 integer, intent(in) :: nx
965 double precision, intent(in) :: bn_bottom(nx),qalpha,qy0,qy_top
966 logical, intent(in) :: qtop_closed
967 character(len=*), intent(in), optional :: flux_treatment
968 integer, intent(in), optional :: nmodes_keep
969
970 double precision :: Lx,xj,arg
971 integer :: m,j,nm
972
973 lx=xprobmax1-xprobmin1
974 nm=nx/2-1
975 if(present(nmodes_keep)) nm=min(nm,nmodes_keep)
976 if(allocated(l2d_br)) deallocate(l2d_br,l2d_bi,l2d_k)
977 allocate(l2d_br(nm),l2d_bi(nm),l2d_k(nm))
978 l2d_b0=sum(bn_bottom)/dble(nx)
979 if(present(flux_treatment)) then
980 if(flux_treatment=='subtract') then
981 if(mype==0.and.dabs(l2d_b0)>lfff_flux_balance_tolerance) &
982 print*,'init_lfff_2d: subtracting net flux ',l2d_b0
983 l2d_b0=0.d0
984 end if
985 end if
986 do m=1,nm
987 l2d_k(m)=2.d0*dpi*dble(m)/lx
988 l2d_br(m)=0.d0; l2d_bi(m)=0.d0
989 do j=1,nx
990 xj=xprobmin1+(dble(j)-0.5d0)*lx/dble(nx)
991 arg=l2d_k(m)*xj
992 l2d_br(m)=l2d_br(m)+bn_bottom(j)*dcos(arg)
993 l2d_bi(m)=l2d_bi(m)-bn_bottom(j)*dsin(arg)
994 end do
995 l2d_br(m)=2.d0*l2d_br(m)/dble(nx)
996 l2d_bi(m)=2.d0*l2d_bi(m)/dble(nx)
997 end do
998 l2d_nm=nm; l2d_alpha=qalpha; l2d_y0=qy0; l2d_ytop=qy_top; l2d_closed=qtop_closed
999 if(.not.qtop_closed .and. qalpha**2>=l2d_k(1)**2) &
1000 call mpistop('init_lfff_2d: |alpha| >= k_1, oscillatory open modes')
1001 l2d_ready=.true.
1002 end subroutine init_lfff_2d
1003
1004 !> B from the stored modes at arbitrary points; Bf(:,:,1:3)=(Bx,By,Bz=alpha*A)
1005 subroutine calc_lfff_2d(ixI^L,ixO^L,x,Bf)
1007 use mod_comm_lib, only: mpistop
1008 integer, intent(in) :: ixI^L,ixO^L
1009 double precision, intent(in) :: x(ixI^S,1:ndim)
1010 double precision, intent(out) :: Bf(ixI^S,1:3)
1011
1012 double precision :: tb,td,ca,sa,cme,sme,zz
1013 integer :: ix^D,m,istat
1014
1015 if(.not.l2d_ready) call mpistop('calc_lfff_2d: call init_lfff_2d first')
1016 bf(ixo^s,1)=0.d0
1017 bf(ixo^s,2)=l2d_b0
1018 bf(ixo^s,3)=0.d0
1019 {do ix^db=ixomin^db,ixomax^db\}
1020 zz=min(max(x(ix^d,2)-l2d_y0,0.d0),l2d_ytop-l2d_y0)
1021 do m=1,l2d_nm
1023 l2d_closed,tb,td,istat)
1024 if(istat==2) call mpistop('calc_lfff_2d: closed-box resonance')
1025 cme=dcos(l2d_k(m)*x(ix^d,1)); sme=dsin(l2d_k(m)*x(ix^d,1))
1026 ca=l2d_br(m)*cme-l2d_bi(m)*sme
1027 sa=l2d_bi(m)*cme+l2d_br(m)*sme
1028 bf(ix^d,2)=bf(ix^d,2)+ca*tb
1029 bf(ix^d,1)=bf(ix^d,1)+sa*td/l2d_k(m)
1030 bf(ix^d,3)=bf(ix^d,3)+l2d_alpha*sa*tb/l2d_k(m)
1031 end do
1032 {end do\}
1033 end subroutine calc_lfff_2d
1034}
1035
1036end module mod_lfff
subroutine, public mpistop(message)
Exit MPI-AMRVAC with an error message.
Utilities for reading AMRVAC data-driven magnetic boundary frames.
subroutine read_data_driven_boundary_frame(filename, snapshot_time, nx, ny, dx_km, dy_km, bframe)
Self-contained complex FFT utilities based on the mixed-radix Singleton transform historically embedd...
Definition mod_fft.t:3
subroutine, public fft_2d_real_imag(ar, ai, inverse)
In-place two-dimensional FFT on split real/imaginary storage. This avoids allocation and copying in r...
Definition mod_fft.t:181
character(len=128) function, public fft_factorization(n)
Human-readable prime factorization used in unsupported-size diagnostics.
Definition mod_fft.t:83
integer function, public fft_next_supported(n)
Definition mod_fft.t:73
logical function, public fft_size_supported(n)
Return whether the legacy mixed-radix workspace can handle a transform.
Definition mod_fft.t:19
Module with basic grid data structures.
Definition mod_forest.t:2
type(tree_node_ptr), dimension(:^d &), allocatable, save tree_root
Pointers to the coarse grid.
Definition mod_forest.t:29
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
This module contains definitions of global parameters and variables and some generic functions/subrou...
integer, parameter ndim
Number of spatial dimensions for grid variables.
logical stagger_grid
True for using stagger grid.
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
logical, dimension(ndim) stretched_dim
True if a dimension is stretched.
integer npe
The number of MPI tasks.
double precision, dimension(:,:), allocatable dx
spatial steps for all dimensions at all levels
integer refine_max_level
Maximal number of AMR levels.
Program to extrapolate linear force-free fields in 3D Cartesian coordinates, based on exact Green fun...
Definition mod_lfff.t:27
double precision, save darea
Definition mod_lfff.t:30
double precision, dimension(:,:), allocatable, save bz0
Definition mod_lfff.t:31
subroutine multigrid_bc(box, nc, iv, nb, bc_type, bc)
To set boundary condition on physical boundaries for mg Poisson solver.
Definition mod_lfff.t:946
logical, save l2d_ready
Definition mod_lfff.t:41
double precision, save l2d_b0
Definition mod_lfff.t:39
integer, save nx1
Definition mod_lfff.t:33
subroutine get_potential_field_potential_sphere(ixil, x, potential, nth, nph, magnetogram, theta, phi, r_sphere)
Definition mod_lfff.t:867
double precision, parameter lfff_mode_tolerance
Definition mod_lfff.t:34
subroutine lfff_fft_transfer(k2, alpha, z, top_height, top_closed, transfer_b, transfer_d, status)
Vertical transfer functions for one nonzero horizontal constant-alpha Fourier mode....
Definition mod_lfff.t:671
double precision, parameter lfff_resonance_tolerance
Definition mod_lfff.t:35
subroutine lfff_balance_bottom_flux(bz, treatment, max_imbalance, imbalance_before, imbalance_after, mean_correction, status)
Check a constant-alpha magnetogram and optionally remove its core mean. Status: 0 accepted unchanged,...
Definition mod_lfff.t:183
subroutine init_lfff_2d(bn_bottom, nx, qalpha, qy0, qtop_closed, qy_top, flux_treatment, nmodes_keep)
Definition mod_lfff.t:962
subroutine init_b_fff_data(magnetogramname, qlunit, qbunit)
Definition mod_lfff.t:47
subroutine get_potential_field_potential_mg()
Solve Poisson equation of scalar potential using multigrid solver.
Definition mod_lfff.t:931
subroutine calc_lin_fff(ixil, ixol, bf, x, alpha, zshift, idir)
Definition mod_lfff.t:727
subroutine init_b_fff_data_driven_boundary(boundaryname, qlunit, qbunit, qxc1, qxc2)
Definition mod_lfff.t:111
double precision, save l2d_y0
Definition mod_lfff.t:39
subroutine potential_field_energy_mg(benergy)
get potential magnetic field energy given normal B on all boundaries
Definition mod_lfff.t:914
double precision, parameter lfff_flux_balance_tolerance
Definition mod_lfff.t:36
integer, save nx2
Definition mod_lfff.t:33
integer, save l2d_nm
Definition mod_lfff.t:40
double precision, dimension(:), allocatable, save l2d_k
Definition mod_lfff.t:38
double precision, dimension(:), allocatable, save xa2
Definition mod_lfff.t:32
double precision, dimension(:), allocatable, save l2d_br
Definition mod_lfff.t:38
double precision, save bzmax
Definition mod_lfff.t:30
double precision, save l2d_alpha
Definition mod_lfff.t:39
logical, save l2d_closed
Definition mod_lfff.t:41
subroutine calc_lfff_2d(ixil, ixol, x, bf)
B from the stored modes at arbitrary points; Bf(:,:,1:3)=(Bx,By,Bz=alpha*A)
Definition mod_lfff.t:1006
subroutine extrapolate_potential_fft(iw_b, padding_factor, source_plane_depth, alpha, top_boundary, flux_treatment, max_flux_imbalance)
Extrapolate a Cartesian potential field with horizontal Fourier modes. The bottom magnetogram must ma...
Definition mod_lfff.t:230
subroutine get_potential_field_potential(ixil, ixol, potential, x, zshift)
Definition mod_lfff.t:832
double precision, save l2d_ytop
Definition mod_lfff.t:39
double precision, dimension(:), allocatable, save xa1
Definition mod_lfff.t:32
double precision, dimension(:), allocatable, save l2d_bi
Definition mod_lfff.t:38
Finite-volume potential reference field for magnetic diagnostics.
subroutine, public magnetic_reference_bc(box, nc, iv, nb, bc_type, bc)
Supply d(phi)/dx_i = B_i on a physical multigrid boundary.
subroutine, public solve_magnetic_reference_fv(config, bp, result)
subroutine, public free_magnetic_reference_field(bp)
Module to couple the octree-mg library to AMRVAC. This file uses the VACPP preprocessor,...