343 alpha,top_boundary,flux_treatment,max_flux_imbalance)
352 integer,
intent(in) :: iw_b(3)
353 integer,
intent(in),
optional :: padding_factor
354 double precision,
intent(in),
optional :: source_plane_depth
355 double precision,
intent(in),
optional :: alpha
356 character(len=*),
intent(in),
optional :: top_boundary
357 character(len=*),
intent(in),
optional :: flux_treatment
358 double precision,
intent(in),
optional :: max_flux_imbalance
360 double precision,
allocatable :: bcore(:,:),spec_r(:,:),spec_i(:,:)
361 double precision,
allocatable :: work_r(:,:),work_i(:,:),bplane(:,:,:)
362 double precision,
allocatable :: sendbuf(:),recvbuf(:),kx(:),ky(:)
363 integer,
allocatable :: sendcounts(:),recvcounts(:),sdispls(:),rdispls(:)
364 integer,
allocatable :: cursor(:),blockpos(:,:)
365 double precision :: dx1,dx2,dx3,tol1,tol2,maxerr,z,fac,source_depth
366 double precision :: bmean,bflux,tstart,memory_mb,alpha_fft,alpha2,k2
367 double precision :: beta,transfer_b,transfer_d,top_height
368 double precision :: kx_der,ky_der,kmin,flux_imbalance,unsigned_flux
369 double precision :: flux_imbalance_before,bmean_before,bflux_before
370 double precision :: mean_correction,max_imbalance
371 double precision,
parameter :: fft_memory_limit_mb=2048.d0
372 integer :: pad,npx,npy,ip0,jp0,ix0,iy0,starti
373 integer :: nb1,nb2,nb3,layer,layer_owner,kg,klocal
374 integer :: ig1,ig2,ipe,igrid,ic,ix1,ix2,pos,base,payload,slice_size
375 integer :: mode,next1,next2,nrecv,nsend,mode_status,flux_status
376 character(len=16) :: top_mode,flux_mode
377 character(len=256) :: message
378 logical :: top_closed,alpha_nonzero
380 if(
ndim/=3)
call mpistop(
'FFT potential field requires three dimensions')
384 call mpistop(
'FFT potential field v1 requires refine_max_level=1')
386 if(.not.
allocated(
bz0) .or. .not.
allocated(
xa1) .or. .not.
allocated(
xa2)) &
387 call mpistop(
'FFT potential field requires initialized magnetogram data')
390 if(
present(padding_factor)) pad=padding_factor
391 if(pad<1)
call mpistop(
'FFT padding factor must be at least one')
394 if(
present(source_plane_depth)) source_depth=source_plane_depth
395 if(source_depth<0.d0)
call mpistop(
'FFT source-plane depth must not be negative')
398 if(
present(alpha)) alpha_fft=alpha
400 alpha_nonzero=(alpha_fft/=0.d0)
403 if(
present(top_boundary)) top_mode=trim(adjustl(top_boundary))
404 select case(trim(top_mode))
410 call mpistop(
"FFT top boundary must be 'open' or 'closed'")
414 if(
present(flux_treatment)) flux_mode=trim(adjustl(flux_treatment))
415 select case(trim(flux_mode))
416 case(
'strict',
'subtract_mean')
419 call mpistop(
"LFFF flux treatment must be 'strict' or 'subtract_mean'")
422 if(
present(max_flux_imbalance)) max_imbalance=max_flux_imbalance
423 if(max_imbalance<0.d0 .or. max_imbalance>1.d0) &
424 call mpistop(
'LFFF maximum flux imbalance must be between zero and one')
429 top_height=source_depth+dble(domain_nx3)*dx3
430 tol1=1.
d-8*max(1.d0,dabs(xprobmin1),dabs(xprobmax1),dabs(dx1))
431 tol2=1.
d-8*max(1.d0,dabs(xprobmin2),dabs(xprobmax2),dabs(dx2))
436 if(
nx1>=domain_nx1)
then
437 do starti=1,
nx1-domain_nx1+1
440 maxerr=max(maxerr,dabs(
xa1(starti+ix1-1)-&
441 (xprobmin1+(dble(ix1)-0.5d0)*dx1)))
443 if(maxerr<=tol1)
then
450 if(
nx2>=domain_nx2)
then
451 do starti=1,
nx2-domain_nx2+1
454 maxerr=max(maxerr,dabs(
xa2(starti+ix2-1)-&
455 (xprobmin2+(dble(ix2)-0.5d0)*dx2)))
457 if(maxerr<=tol2)
then
463 if(ix0==0 .or. iy0==0)
then
465 write(*,*)
'magnetogram size:',
nx1,
nx2
466 write(*,*)
'required physical FFT size:',domain_nx1,domain_nx2
467 write(*,*)
'AMRVAC dx/dy:',dx1,dx2
469 call mpistop(
'FFT magnetogram centers do not match the level-one physical grid')
477 write(message,
'(a,i0,a,a,a,i0,a,a,a,i0,a,i0)') &
480 '); next supported sizes are ',next1,
' x ',next2
484 if(mod(domain_nx1,block_nx1)/=0 .or. mod(domain_nx2,block_nx2)/=0 .or. &
485 mod(domain_nx3,block_nx3)/=0) &
486 call mpistop(
'FFT potential field requires domain_nx divisible by block_nx')
487 nb1=domain_nx1/block_nx1
488 nb2=domain_nx2/block_nx2
489 nb3=domain_nx3/block_nx3
490 slice_size=block_nx1*block_nx2*3
491 payload=slice_size*block_nx3
497 memory_mb=8.d0*(4.d0*dble(npx)*dble(npy)+4.d0*dble(domain_nx1)*&
498 dble(domain_nx2)+6.d0*dble(domain_nx1)*dble(domain_nx2)*&
499 dble(block_nx3))/(1024.d0**2)
500 if(memory_mb>fft_memory_limit_mb)
then
501 write(message,
'(a,f10.1,a,f10.1,a)')
'FFT potential field needs up to ',&
502 memory_mb,
' MiB/rank, above the internal limit of ',&
503 fft_memory_limit_mb,
' MiB; reduce block_nx3 or horizontal size'
507 allocate(bcore(domain_nx1,domain_nx2))
508 bcore=
bz0(ix0:ix0+domain_nx1-1,iy0:iy0+domain_nx2-1)
509 bmean_before=sum(bcore)/dble(
size(bcore))*
bzmax
510 bflux_before=sum(bcore)*
bzmax*dx1*dx2
511 unsigned_flux=sum(dabs(bcore))
512 flux_imbalance_before=dabs(sum(bcore))/max(unsigned_flux,tiny(1.d0))
515 if(alpha_nonzero)
then
517 flux_imbalance_before,flux_imbalance,mean_correction,flux_status)
518 select case(flux_status)
523 write(*,*)
'FFT LFFF alpha:',alpha_fft
524 write(*,*)
'relative bottom flux imbalance:',flux_imbalance_before
527 call mpistop(
'constant-alpha FFT LFFF requires a flux-balanced bottom magnetogram')
530 write(*,*)
'FFT LFFF alpha:',alpha_fft
531 write(*,*)
'relative bottom flux imbalance:',flux_imbalance_before
532 write(*,*)
'maximum automatic-balance imbalance:',max_imbalance
534 call mpistop(
'LFFF magnetogram is too unbalanced for automatic mean subtraction')
536 call mpistop(
'invalid LFFF flux-balance configuration')
539 flux_imbalance=flux_imbalance_before
541 bmean=sum(bcore)/dble(npx*npy)*
bzmax
542 bflux=sum(bcore)*
bzmax*dx1*dx2
543 ip0=(npx-domain_nx1)/2+1
544 jp0=(npy-domain_nx2)/2+1
546 allocate(spec_r(npx,npy),spec_i(npx,npy),work_r(npx,npy),work_i(npx,npy))
547 allocate(kx(npx),ky(npy))
551 spec_r(ip0:ip0+domain_nx1-1,jp0:jp0+domain_nx2-1)=bcore
554 call mpi_bcast(spec_r,npx*npy,mpi_double_precision,0,
icomm,
ierrmpi)
555 call mpi_bcast(spec_i,npx*npy,mpi_double_precision,0,
icomm,
ierrmpi)
559 if(mode>npx/2) mode=mode-npx
560 kx(ix1)=2.d0*dpi*dble(mode)/(dble(npx)*dx1)
564 if(mode>npy/2) mode=mode-npy
565 ky(ix2)=2.d0*dpi*dble(mode)/(dble(npy)*dx2)
568 kmin=min(2.d0*dpi/(dble(npx)*dx1),2.d0*dpi/(dble(npy)*dx2))
569 if(alpha_nonzero .and. .not.top_closed .and. dabs(alpha_fft)>=kmin)
then
571 write(*,*)
'FFT LFFF alpha:',alpha_fft
572 write(*,*)
'smallest nonzero padded horizontal wavenumber:',kmin
574 call mpistop(
'open-top FFT LFFF requires abs(alpha) < kmin')
580 if(alpha_nonzero .and. top_closed)
then
583 k2=kx(ix1)**2+ky(ix2)**2
584 if(k2<=0.d0 .or. alpha2<=k2) cycle
585 beta=dsqrt(alpha2-k2)
587 transfer_b,transfer_d,mode_status)
588 if(mode_status==2)
then
590 write(*,*)
'FFT closed LFFF resonant mode kx,ky:',kx(ix1),ky(ix2)
591 write(*,*)
'alpha, beta, top height:',alpha_fft,beta,top_height
593 call mpistop(
'closed-top FFT LFFF is singular for a horizontal mode')
599 allocate(sendcounts(0:
npe-1),recvcounts(0:
npe-1))
600 allocate(sdispls(0:
npe-1),rdispls(0:
npe-1),cursor(0:
npe-1))
601 allocate(blockpos(nb1,nb2))
605 write(*,*)
'FFT potential field physical size:',domain_nx1,domain_nx2,domain_nx3
606 write(*,*)
'FFT padded size:',npx,npy,
' padding factor:',pad
607 write(*,*)
'FFT conservative memory bound [MiB/rank]:',memory_mb
608 write(*,*)
'FFT source plane below lower face:',source_depth
609 write(*,*)
'FFT alpha and top boundary:',alpha_fft,trim(top_mode)
610 if(top_closed)
write(*,*)
'FFT closed-top height above source plane:',top_height
611 write(*,*)
'FFT magnetogram core starts at:',ix0,iy0
612 write(*,*)
'FFT input core mean Bz:',bmean_before,
' input net bottom flux:',bflux_before
613 if(alpha_nonzero)
then
614 write(*,*)
'FFT LFFF flux treatment:',trim(flux_mode)
615 write(*,*)
'FFT input relative bottom flux imbalance:',flux_imbalance_before
617 write(*,*)
'FFT subtracted core mean Bz:',mean_correction*
bzmax
618 write(*,*)
'FFT corrected relative bottom flux imbalance:',flux_imbalance
620 write(*,*)
'FFT padded zero-mode mean Bz:',bmean,
' net bottom flux:',bflux
624 layer_owner=mod(layer-1,
npe)
631 if(
mype==layer_owner)
then
635 sendcounts(ipe)=sendcounts(ipe)+payload
639 sdispls(ipe)=sdispls(ipe-1)+sendcounts(ipe-1)
645 blockpos(ig1,ig2)=cursor(ipe)+1
646 cursor(ipe)=cursor(ipe)+payload
654 if(
tree_root(ig1,ig2,layer)%node%ipe==
mype) nrecv=nrecv+payload
657 recvcounts(layer_owner)=nrecv
658 nsend=sum(sendcounts)
659 allocate(sendbuf(max(1,nsend)),recvbuf(max(1,nrecv)))
663 if(
mype==layer_owner)
then
664 allocate(bplane(domain_nx1,domain_nx2,3))
665 do klocal=1,block_nx3
666 kg=(layer-1)*block_nx3+klocal
667 z=(dble(kg)-0.5d0)*dx3+source_depth
672 k2=kx(ix1)**2+ky(ix2)**2
678 if(mod(npx,2)==0 .and. ix1==npx/2+1) kx_der=0.d0
679 if(mod(npy,2)==0 .and. ix2==npy/2+1) ky_der=0.d0
682 if(alpha_nonzero)
then
692 top_closed,transfer_b,transfer_d,mode_status)
694 call mpistop(
'invalid FFT LFFF mode reached distributed solve')
700 fac=(kx_der*transfer_d-&
701 ky_der*alpha_fft*transfer_b)/k2
702 work_r(ix1,ix2)= fac*spec_i(ix1,ix2)
703 work_i(ix1,ix2)=-fac*spec_r(ix1,ix2)
710 fac=(ky_der*transfer_d+&
711 kx_der*alpha_fft*transfer_b)/k2
712 work_r(ix1,ix2)= fac*spec_i(ix1,ix2)
713 work_i(ix1,ix2)=-fac*spec_r(ix1,ix2)
719 work_r(ix1,ix2)=transfer_b*spec_r(ix1,ix2)
720 work_i(ix1,ix2)=transfer_b*spec_i(ix1,ix2)
725 bplane(:,:,ic)=
bzmax*work_r(ip0:ip0+domain_nx1-1,&
726 jp0:jp0+domain_nx2-1)
731 base=blockpos(ig1,ig2)+(klocal-1)*slice_size
736 sendbuf(pos)=bplane((ig1-1)*block_nx1+ix1,&
737 (ig2-1)*block_nx2+ix2,ic)
748 call mpi_alltoallv(sendbuf,sendcounts,sdispls,mpi_double_precision,&
749 recvbuf,recvcounts,rdispls,mpi_double_precision,
icomm,
ierrmpi)
755 igrid=
tree_root(ig1,ig2,layer)%node%igrid
756 do klocal=1,block_nx3
760 ps(igrid)%w(ixmlo1+ix1-1,ixmlo2+ix2-1,&
761 ixmlo3+klocal-1,iw_b(ic))=recvbuf(pos)
769 deallocate(sendbuf,recvbuf)
772 if(
mype==0)
write(*,*)
'FFT potential/LFFF extrapolation took:',mpi_wtime()-tstart,
's'
773 deallocate(bcore,spec_r,spec_i,work_r,work_i,kx,ky)
774 deallocate(sendcounts,recvcounts,sdispls,rdispls,cursor,blockpos)
849 integer,
intent(in) :: ixI^L, ixO^L
850 integer,
optional,
intent(in) :: idir
851 double precision,
intent(in) :: x(ixI^S,1:ndim),alpha,zshift
852 double precision,
intent(inout) :: Bf(ixI^S,1:ndir)
854 double precision,
dimension(ixO^S) :: cos_az,sin_az,zk,bigr,r,r2,r3,cos_ar,sin_ar,g,dgdz
855 double precision,
dimension(ixO^S) :: dx1,dx2,invr3
856 double precision :: twopiinv
857 logical :: compute_dir(1:ndir)
858 integer :: idim,ixp1,ixp2
862 zk(ixo^s)=x(ixo^s,3)-xprobmin3+zshift
865 if(
present(idir))
then
867 if(idir>=1 .and. idir<=ndir) compute_dir(idir)=.true.
876 dx1(ixo^s)=x(ixo^s,1)-
xa1(ixp1)
877 dx2(ixo^s)=x(ixo^s,2)-
xa2(ixp2)
878 r2(ixo^s)=dx1(ixo^s)**2+dx2(ixo^s)**2+zk(ixo^s)**2
879 where(r2(ixo^s)>0.d0)
880 invr3(ixo^s)=1.d0/(r2(ixo^s)*dsqrt(r2(ixo^s)))
884 if(compute_dir(1)) bf(ixo^s,1)=bf(ixo^s,1)+&
885 bz0(ixp1,ixp2)*dx1(ixo^s)*invr3(ixo^s)
886 if(compute_dir(2)) bf(ixo^s,2)=bf(ixo^s,2)+&
887 bz0(ixp1,ixp2)*dx2(ixo^s)*invr3(ixo^s)
888 if(compute_dir(3)) bf(ixo^s,3)=bf(ixo^s,3)+&
889 bz0(ixp1,ixp2)*zk(ixo^s)*invr3(ixo^s)
892 bf(ixo^s,:)=bf(ixo^s,:)*twopiinv
897 cos_az(ixo^s)=dcos(alpha*zk(ixo^s))
898 sin_az(ixo^s)=dsin(alpha*zk(ixo^s))
902 bigr(ixo^s)=dsqrt((x(ixo^s,1)-
xa1(ixp1))**2+&
903 (x(ixo^s,2)-
xa2(ixp2))**2)
921 g=(zk*cos_ar*r-cos_az)*bigr
922 dgdz=(cos_ar*(r-zk**2*r3)-alpha*zk**2*sin_ar*r2+alpha*sin_az)*bigr
924 if(
present(idir))
then
929 bf(ixo^s,1)=bf(ixo^s,1)+
bz0(ixp1,ixp2)*((x(ixo^s,1)-
xa1(ixp1))*dgdz(ixo^s)&
930 +alpha*g(ixo^s)*(x(ixo^s,2)-
xa2(ixp2)))*bigr(ixo^s)
932 bf(ixo^s,2)=bf(ixo^s,2)+
bz0(ixp1,ixp2)*((x(ixo^s,2)-
xa2(ixp2))*dgdz(ixo^s)&
933 -alpha*g(ixo^s)*(x(ixo^s,1)-
xa1(ixp1)))*bigr(ixo^s)
935 bf(ixo^s,3)=bf(ixo^s,3)+
bz0(ixp1,ixp2)*(zk(ixo^s)*cos_ar(ixo^s)*r3(ixo^s)+alpha*&
936 zk(ixo^s)*sin_ar(ixo^s)*r2(ixo^s))
941 bf(ixo^s,:)=bf(ixo^s,:)*twopiinv