229 alpha,top_boundary,flux_treatment,max_flux_imbalance)
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
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
266 if(
ndim/=3)
call mpistop(
'FFT potential field requires three dimensions')
270 call mpistop(
'FFT potential field v1 requires refine_max_level=1')
272 if(.not.
allocated(
bz0) .or. .not.
allocated(
xa1) .or. .not.
allocated(
xa2)) &
273 call mpistop(
'FFT potential field requires initialized magnetogram data')
276 if(
present(padding_factor)) pad=padding_factor
277 if(pad<1)
call mpistop(
'FFT padding factor must be at least one')
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')
284 if(
present(alpha)) alpha_fft=alpha
286 alpha_nonzero=(alpha_fft/=0.d0)
289 if(
present(top_boundary)) top_mode=trim(adjustl(top_boundary))
290 select case(trim(top_mode))
296 call mpistop(
"FFT top boundary must be 'open' or 'closed'")
300 if(
present(flux_treatment)) flux_mode=trim(adjustl(flux_treatment))
301 select case(trim(flux_mode))
302 case(
'strict',
'subtract_mean')
305 call mpistop(
"LFFF flux treatment must be 'strict' or 'subtract_mean'")
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')
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))
322 if(
nx1>=domain_nx1)
then
323 do starti=1,
nx1-domain_nx1+1
326 maxerr=max(maxerr,dabs(
xa1(starti+ix1-1)-&
327 (xprobmin1+(dble(ix1)-0.5d0)*dx1)))
329 if(maxerr<=tol1)
then
336 if(
nx2>=domain_nx2)
then
337 do starti=1,
nx2-domain_nx2+1
340 maxerr=max(maxerr,dabs(
xa2(starti+ix2-1)-&
341 (xprobmin2+(dble(ix2)-0.5d0)*dx2)))
343 if(maxerr<=tol2)
then
349 if(ix0==0 .or. iy0==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
355 call mpistop(
'FFT magnetogram centers do not match the level-one physical grid')
363 write(message,
'(a,i0,a,a,a,i0,a,a,a,i0,a,i0)') &
366 '); next supported sizes are ',next1,
' x ',next2
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
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'
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))
401 if(alpha_nonzero)
then
403 flux_imbalance_before,flux_imbalance,mean_correction,flux_status)
404 select case(flux_status)
409 write(*,*)
'FFT LFFF alpha:',alpha_fft
410 write(*,*)
'relative bottom flux imbalance:',flux_imbalance_before
413 call mpistop(
'constant-alpha FFT LFFF requires a flux-balanced bottom magnetogram')
416 write(*,*)
'FFT LFFF alpha:',alpha_fft
417 write(*,*)
'relative bottom flux imbalance:',flux_imbalance_before
418 write(*,*)
'maximum automatic-balance imbalance:',max_imbalance
420 call mpistop(
'LFFF magnetogram is too unbalanced for automatic mean subtraction')
422 call mpistop(
'invalid LFFF flux-balance configuration')
425 flux_imbalance=flux_imbalance_before
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
432 allocate(spec_r(npx,npy),spec_i(npx,npy),work_r(npx,npy),work_i(npx,npy))
433 allocate(kx(npx),ky(npy))
437 spec_r(ip0:ip0+domain_nx1-1,jp0:jp0+domain_nx2-1)=bcore
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)
445 if(mode>npx/2) mode=mode-npx
446 kx(ix1)=2.d0*dpi*dble(mode)/(dble(npx)*dx1)
450 if(mode>npy/2) mode=mode-npy
451 ky(ix2)=2.d0*dpi*dble(mode)/(dble(npy)*dx2)
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
457 write(*,*)
'FFT LFFF alpha:',alpha_fft
458 write(*,*)
'smallest nonzero padded horizontal wavenumber:',kmin
460 call mpistop(
'open-top FFT LFFF requires abs(alpha) < kmin')
466 if(alpha_nonzero .and. top_closed)
then
469 k2=kx(ix1)**2+ky(ix2)**2
470 if(k2<=0.d0 .or. alpha2<=k2) cycle
471 beta=dsqrt(alpha2-k2)
473 transfer_b,transfer_d,mode_status)
474 if(mode_status==2)
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
479 call mpistop(
'closed-top FFT LFFF is singular for a horizontal mode')
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))
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
503 write(*,*)
'FFT subtracted core mean Bz:',mean_correction*
bzmax
504 write(*,*)
'FFT corrected relative bottom flux imbalance:',flux_imbalance
506 write(*,*)
'FFT padded zero-mode mean Bz:',bmean,
' net bottom flux:',bflux
510 layer_owner=mod(layer-1,
npe)
517 if(
mype==layer_owner)
then
521 sendcounts(ipe)=sendcounts(ipe)+payload
525 sdispls(ipe)=sdispls(ipe-1)+sendcounts(ipe-1)
531 blockpos(ig1,ig2)=cursor(ipe)+1
532 cursor(ipe)=cursor(ipe)+payload
540 if(
tree_root(ig1,ig2,layer)%node%ipe==
mype) nrecv=nrecv+payload
543 recvcounts(layer_owner)=nrecv
544 nsend=sum(sendcounts)
545 allocate(sendbuf(max(1,nsend)),recvbuf(max(1,nrecv)))
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
558 k2=kx(ix1)**2+ky(ix2)**2
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
568 if(alpha_nonzero)
then
578 top_closed,transfer_b,transfer_d,mode_status)
580 call mpistop(
'invalid FFT LFFF mode reached distributed solve')
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)
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)
605 work_r(ix1,ix2)=transfer_b*spec_r(ix1,ix2)
606 work_i(ix1,ix2)=transfer_b*spec_i(ix1,ix2)
611 bplane(:,:,ic)=
bzmax*work_r(ip0:ip0+domain_nx1-1,&
612 jp0:jp0+domain_nx2-1)
617 base=blockpos(ig1,ig2)+(klocal-1)*slice_size
622 sendbuf(pos)=bplane((ig1-1)*block_nx1+ix1,&
623 (ig2-1)*block_nx2+ix2,ic)
634 call mpi_alltoallv(sendbuf,sendcounts,sdispls,mpi_double_precision,&
635 recvbuf,recvcounts,rdispls,mpi_double_precision,
icomm,
ierrmpi)
641 igrid=
tree_root(ig1,ig2,layer)%node%igrid
642 do klocal=1,block_nx3
646 ps(igrid)%w(ixmlo1+ix1-1,ixmlo2+ix2-1,&
647 ixmlo3+klocal-1,iw_b(ic))=recvbuf(pos)
655 deallocate(sendbuf,recvbuf)
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)
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)
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
748 zk(ixo^s)=x(ixo^s,3)-xprobmin3+zshift
751 if(
present(idir))
then
753 if(idir>=1 .and. idir<=ndir) compute_dir(idir)=.true.
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)))
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)
778 bf(ixo^s,:)=bf(ixo^s,:)*twopiinv
783 cos_az(ixo^s)=dcos(alpha*zk(ixo^s))
784 sin_az(ixo^s)=dsin(alpha*zk(ixo^s))
788 bigr(ixo^s)=dsqrt((x(ixo^s,1)-
xa1(ixp1))**2+&
789 (x(ixo^s,2)-
xa2(ixp2))**2)
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
810 if(
present(idir))
then
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)
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)
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))
827 bf(ixo^s,:)=bf(ixo^s,:)*twopiinv