Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
static.f90 17.51 KiB
MODULE Static
  USE Params
  USE Densities
  USE Meanfield, ONLY: skyrme, hpsi, upot, bmass
  USE Levels
  USE Grids
  USE Moment
  USE Energies
  USE Inout, ONLY: write_wavefunctions, write_densities, plot_density, &
       sp_properties,start_protocol
  USE Pairs, ONLY: pair,epair
  IMPLICIT NONE
  LOGICAL :: tdiag=.FALSE.
  LOGICAL :: tlarge=.FALSE.
  LOGICAL :: tvaryx_0=.FALSE.
  INTEGER :: maxiter
  REAL(db) :: radinx,radiny,radinz, &
       serr,delesum,x0dmp=0.2D0,e0dmp=100.D0,x0dmpmin=0.2d0
CONTAINS
  !*************************************************************************
  SUBROUTINE getin_static
    NAMELIST/static/ tdiag,tlarge,maxiter, &
         radinx,radiny,radinz,serr,x0dmp,e0dmp,nneut,nprot,npsi,tvaryx_0
    npsi=0
    READ(5,static)
    IF(nof<=0) THEN
       IF(npsi(1)==0) THEN  
          IF(ipair==0.OR.nof<0) THEN  
             npsi(1)=nneut  
          ELSE  
             npsi(1)=NINT(nneut+1.65*REAL(nneut,db)**0.666667D0)  
             IF(MOD(npsi(1),2)/=0) npsi(1)=npsi(1)+1
          ENDIF
       ENDIF
       IF(npsi(2)==0) THEN  
          IF(ipair==0.OR.nof<0) THEN  
             npsi(2)=nprot  
          ELSE  
             npsi(2)=NINT(nprot+1.65*REAL(nprot,db)**0.666667D0)  
             IF(MOD(npsi(2),2)/=0) npsi(2)=npsi(2)+1
          ENDIF
       ENDIF
       IF(nneut>npsi(1).OR.nprot>npsi(2)) & 
            STOP 'Particle & state numbers in conflict'
       nstmax=npsi(1)+npsi(2)
       charge_number=nprot  
       mass_number=nneut+nprot  
       x0dmpmin=x0dmp
       WRITE(*,*) "x0dmpmin=", x0dmpmin
    END IF
  END SUBROUTINE getin_static
  !*************************************************************************
  SUBROUTINE init_static
    IF(wflag) THEN
       WRITE(*,*) '***** Parameters for static calculation *****'
       WRITE(*,"(3(A,I4))") ' Neutrons:',nneut,' in ',npsi(1),' levels'
       WRITE(*,"(3(A,I4))") ' Protons :',nprot,' in ',npsi(2)-npsi(1),' levels'
       WRITE(*,"(A,I6)") " Maximum number of iterations: ",maxiter  
       WRITE(*,"(2(A,G12.5))") ' Damping coefficient:',x0dmp, &
            " Damping energy scale: ",e0dmp
       WRITE(*,"(A,1PE12.4)") " Convergence limit: ",serr  
       ! initialize *.res files
       CALL start_protocol(converfile, &
            '# Iter   Energy  d_Energy    h**2        h*h        rms    &
            &beta2  gamma')
       CALL start_protocol(dipolesfile, &
            '# Iter    c.m. x-y-z                                  Isovector&
            &dipoles x-y-z')
       CALL start_protocol(spinfile, &
            '# Iter      Lx        Ly        Lz        Sx        Sy        &
            &Sz        Jx        Jy        Jz')
    ENDIF
    ! calculate damping matrices
    IF(e0dmp>0.0D0) CALL setup_damping(e0dmp)
    ! c.m. fixing term
    IF(f%zpe==0) THEN
       f%h2m=f%h2m*(mass_number-1.0D0)/mass_number
       WRITE(*,*) '***** Nucleon mass modified for z.p.e. correction'
    END IF
  END SUBROUTINE init_static
  !*************************************************************************
  SUBROUTINE statichf
    LOGICAL, PARAMETER :: taddnew=.TRUE. ! mix old and new densities
    INTEGER :: iq,nst,firstiter
    REAL(db) :: sumflu,denerg
    REAL(db),PARAMETER :: addnew=0.2D0,addco=1.0D0-addnew      
    ! Step 1: initialization
    IF(tdiag) ALLOCATE(hmatr(nstmax,nstmax))
    IF(trestart) THEN
       firstiter=iter+1
    ELSE
       iter=0
       firstiter=1
       sp_energy=0.0D0  
       sp_efluct1=0.0D0  
       sp_efluct2=0.D0
       sp_norm=0.0D0  
       sumflu=0.D0
       CALL schmid
    END IF
    ! Step 2: calculate densities and mean field
    rho=0.0D0
    tau=0.0D0
    current=0.0D0
    sdens=0.0D0
    sodens=0.0D0
    DO nst=1,nstmax
       CALL add_density(isospin(nst),wocc(nst),psi(:,:,:,:,nst), &
            rho,tau,current,sdens,sodens)  
    ENDDO
    CALL skyrme
    ! Step 3: initial gradient step
    delesum=0.0D0  
    sumflu=0.0D0  
    !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(nst,denerg) &
    !$OMP    SCHEDULE(STATIC) REDUCTION(+: sumflu , delesum)
    DO nst=1,nstmax
       CALL grstep(nst,isospin(nst),sp_energy(nst),denerg, &
            psi(:,:,:,:,nst))
       sumflu=sumflu+wocc(nst)*sp_efluct1(nst)  
       delesum=delesum+wocc(nst)*denerg  
    ENDDO
    !$OMP END PARALLEL DO
    ! pairing and orthogonalization
    IF(ipair/=0) CALL pair
    CALL schmid
    ! produce and print detailed information
    CALL sp_properties
    CALL sinfo
    !set x0dmp to 3* its value to get faster convergence
    IF(tvaryx_0) x0dmp=3.0d0*x0dmp
    ! step 4: start static iteration loop
    Iteration: DO iter=firstiter,maxiter  
       WRITE(*,'(a,i6,a,F12.4)') ' Static Iteration No.',iter,'  x0dmp= ',x0dmp
       ! Step 5: gradient step
       delesum=0.0D0  
       sumflu=0.0D0
       !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(nst,denerg) &
       !$OMP    SCHEDULE(STATIC) REDUCTION(+: sumflu , delesum)
       DO nst=1,nstmax
          CALL grstep(nst,isospin(nst),sp_energy(nst),denerg, &
               psi(:,:,:,:,nst))
          sumflu=sumflu+wocc(nst)*sp_efluct1(nst)  
          delesum=delesum+wocc(nst)*denerg  
       ENDDO
       !$OMP END PARALLEL DO
       ! Step 6: diagonalize if desired
       IF(tdiag.AND.iter>20) THEN
          !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(iq) SCHEDULE(STATIC) 
          DO iq=1,2
             CALL diagstep(iq,npsi(iq)-npmin(iq)+1)
          ENDDO
          !$OMP END PARALLEL DO
       ENDIF
       ! Step 7: do pairing and orthogonalization
       IF(ipair/=0) CALL pair
       CALL schmid
       ! Step 8: get new densities and fields with relaxation
       IF(taddnew) THEN
          upot=rho
          bmass=tau
       ENDIF
       rho=0.0D0
       tau=0.0D0
       current=0.0D0
       sdens=0.0D0
       sodens=0.0D0
       !$OMP PARALLEL DO DEFAULT(SHARED) PRIVATE(nst) SCHEDULE(STATIC) &
       !$OMP REDUCTION(+:rho, tau, current, sdens, sodens)
       DO nst=1,nstmax
          CALL add_density(isospin(nst),wocc(nst),psi(:,:,:,:,nst), &
               rho,tau,current,sdens,sodens)  
       ENDDO
       !$OMP END PARALLEL DO
       IF(taddnew) THEN
          rho=addnew*rho+addco*upot
          tau=addnew*tau+addco*bmass
       ENDIF
       CALL skyrme
       ! calculate and print information
       IF(mprint>0.AND.MOD(iter,mprint)==0) THEN
          CALL sp_properties
          CALL sinfo
       ELSEIF(tvaryx_0) THEN
          CALL sp_properties
          CALL sum_energy
       ENDIF
       ! Step 9: check for convergence, saving wave functions
       IF(sumflu/nstmax<serr.AND.iter>1) THEN
          CALL write_wavefunctions
          EXIT Iteration  
       END IF
       IF(MOD(iter,mrest)==0) THEN  
          CALL write_wavefunctions
       ENDIF
       ! Step 10: update step size for the next iteration
       IF(tvaryx_0) THEN
          IF(ehf<ehfprev .OR. efluct1<(efluct1prev*(1.0d0-1.0d-5)) &
               .OR. efluct2<(efluct2prev*(1.0d0-1.0d-5))) THEN
             x0dmp=x0dmp*1.005
          ELSE
             x0dmp=x0dmp*0.8
          END IF
          IF(x0dmp<x0dmpmin) x0dmp=x0dmpmin
          efluct1prev=efluct1
          efluct2prev=efluct2
          ehfprev=ehf
       END IF
    END DO Iteration
    IF(tdiag) DEALLOCATE(hmatr)
  END SUBROUTINE statichf
  !*************************************************************************
  SUBROUTINE grstep(nst,iq,spe,denerg,psin)
    USE Trivial, ONLY: cmulx,cmuly,cmulz,rpsnorm,overlap
    !***********************************************************************
    !                                                               
    !     grstep=one damped gradient iteration step for a given  
    !                 wave function psin with isospin iq.          
    !        psi=o[ psi - x0*damp*[(h-spe)psi] ]                
    !                                                              
    !***********************************************************************
    INTEGER,INTENT(IN) :: nst,iq
    REAL(db) :: spe,denerg
    COMPLEX(db) :: psin(:,:,:,:)  
    INTENT(OUT) :: denerg
    INTENT(INOUT) :: spe,psin
    REAL(db) :: x0act,esf,enrold,xnorm,xnormb,exph2,varh2
    COMPLEX(db) :: ps1(nx,ny,nz,2),ps2(nx,ny,nz,2)
    INTEGER :: nst2
    ! Step 1:(h-esf) on psin yields ps1.                        *
    esf=spe 
    CALL hpsi(iq,esf,psin,ps1)
    ! Step 2: calculate matrix elements
    xnorm=rpsnorm(psin)
    xnormb=overlap(psin,ps1)
    DO nst2=1,nstmax
       IF(tdiag.AND.isospin(nst2)==isospin(nst))   &
            hmatr(nst2,nst)=overlap(psi(:,:,:,:,nst2),ps1)
    ENDDO
    IF(tdiag) hmatr(nst,nst)=hmatr(nst,nst)+spe
    ! Step 3: calculate fluctuation, i.e. <h*h> and |h|**2
    IF((mprint>0.AND.MOD(iter,mprint)==0).OR.tvaryx_0) THEN  
       CALL hpsi(iq,esf,ps1,ps2)
       exph2=overlap(psin,ps2)
       varh2=rpsnorm(ps1)
       sp_efluct1(nst)=SQRT(ABS(exph2/xnorm-(xnormb/xnorm)**2))  
       sp_efluct2(nst)=SQRT(ABS(varh2/xnorm-(xnormb/xnorm)**2))  
    ENDIF
    ! Step 4: the damping step
    IF(e0dmp>0.0D0) THEN  
       ps1=ps1 - xnormb*psin
       x0act=x0dmp
       IF(TFFT) THEN
          CALL laplace(ps1,ps2,e0inv=e0dmp)  
       ELSE
          CALL cmulz(cdmpz,ps1,ps2,0)  
          CALL cmuly(cdmpy,ps2,ps1,0)  
          CALL cmulx(cdmpx,ps1,ps2,0)  
          x0act=x0act/e0dmp
       ENDIF
       psin=psin - x0act*ps2
    ELSE  
       psin=(1.0+x0dmp*xnormb)*psin-x0dmp*ps1
    ENDIF
    ! Step 5: energy convergence criterion
    enrold=spe  
    spe=xnormb+esf  
    denerg=(enrold-spe)/ABS(spe)  
  END SUBROUTINE grstep
  !*************************************************************************
  SUBROUTINE diagstep(iq,nlin)
    !***********************************************************************
    !                                                                      *
    !     diagstep= diagonalize Hamiltonian matrix of active shells      *
    !                                                                      *
    !***********************************************************************
    INTEGER,INTENT(IN) :: iq,nlin
    INTEGER :: nst,nst2,noffset
    INTEGER :: infoconv
    REAL(db) :: eigen(nstmax)
    COMPLEX(db) :: unitary(nstmax,nstmax)
    COMPLEX(db), ALLOCATABLE :: psiw(:,:,:,:)          ! work space
    COMPLEX(db), ALLOCATABLE :: ps1(:,:,:,:,:)
    COMPLEX(db) :: hmatr_lin(nlin+1,nlin)
    COMPLEX(db) :: cwork(2*nlin*nlin)
    REAL(db)    :: rwork(2*nlin*nlin+5*nlin+1)
    INTEGER     :: iwork(5*nlin+3)
    INTERFACE
       SUBROUTINE zhbevd( jobz, uplo, n, kd, ab, ldab, w, z, ldz, work, &
            lwork, rwork, lrwork, iwork, liwork, info )
         USE Params, ONLY: db
         CHARACTER(1) :: jobz, uplo
         INTEGER :: info, kd, ldab, ldz, liwork, lrwork, lwork, n, iwork(*)
         DOUBLE PRECISION ::  rwork( * ), w( * )
         COMPLEX(8) :: ab( ldab, * ), work( * ), z( ldz, * )
         INTENT(IN) :: jobz,uplo,n,kd,ldab,ldz,lwork,lrwork,liwork
         INTENT(INOUT) :: ab
         INTENT(OUT) :: w,z,work,rwork,iwork,info
       END SUBROUTINE zhbevd
    END INTERFACE
    ! Step 1: copy matrix into symmetric storage mode, then diagonalize
    noffset=npmin(iq)-1
    DO nst=npmin(iq),npsi(iq)
       DO nst2=nst,npsi(iq)
          hmatr_lin(1+nst2-nst,nst-noffset)=&
               0.5D0*(CONJG(hmatr(nst,nst2))+hmatr(nst2,nst))
       ENDDO
    ENDDO
    CALL ZHBEVD('V','L',nlin,nlin,hmatr_lin,nlin+1,eigen,unitary,nstmax, &
         cwork,nlin*nlin*2,rwork,2*nlin*nlin+5*nlin+1,       &
         iwork,5*nlin+3,infoconv)
    !  Step 2: transform states, replace original ones
    IF(tlarge) THEN
       OPEN(scratch,status='scratch',form='unformatted')
       ALLOCATE(psiw(nx,ny,nz,2))
       noffset=npmin(iq)-1
       DO nst=npmin(iq),npsi(iq)
          psiw=CMPLX(0.0D0,0.0D0,db)
          DO nst2=npmin(iq),npsi(iq)
             psiw(:,:,:,:)=psiw(:,:,:,:) &
                  + unitary(nst2-noffset,nst-noffset)*psi(:,:,:,:,nst2)
          ENDDO
          WRITE(scratch) psiw
       ENDDO
       REWIND(scratch)
       DO nst=npmin(iq),npsi(iq)
          READ(scratch) psi(:,:,:,:,nst)
       ENDDO
       DEALLOCATE(psiw)
       CLOSE(scratch)
    ELSE
       ALLOCATE(ps1(nx,ny,nz,2,nlin))
       noffset=npmin(iq)-1
       ps1=0.0D0
       DO nst=npmin(iq),npsi(iq)
          DO nst2=npmin(iq),npsi(iq)
             ps1(:,:,:,:,nst-noffset)=ps1(:,:,:,:,nst-noffset) &
                  + unitary(nst2-noffset,nst-noffset)*psi(:,:,:,:,nst2)
          ENDDO
       ENDDO
       psi(:,:,:,:,npmin(iq):npsi(iq))=ps1
       DEALLOCATE(ps1)
    ENDIF
  END SUBROUTINE diagstep
  !*************************************************************************
  SUBROUTINE sinfo
    INTEGER :: il
    CHARACTER(*),PARAMETER :: &
         header='  #  Par   v**2   var_h1   var_h2    Norm     Ekin    Energy &
         &    Lx      Ly      Lz     Sx     Sy     Sz  '   
    ! calculate static observables for printout                       *
    CALL moments
    CALL integ_energy
    CALL sum_energy
    ! add information to summary files
    OPEN(unit=scratch,file=converfile,POSITION='APPEND')  
    WRITE(scratch,'(1x,i5,f9.2,3(1pg11.3),2(0pf8.3),f6.1)') &
         iter,ehf,delesum/pnrtot,efluct1,efluct2,rmstot,beta,gamma
    CLOSE(scratch)
    OPEN(unit=scratch,file=dipolesfile, POSITION='APPEND')  
    WRITE(scratch,'(1x,i5,6E14.4)') iter,cmtot,cm(:,2)-cm(:,1)
    CLOSE(unit=scratch)
    OPEN(unit=scratch,file=spinfile, POSITION='APPEND')  
    WRITE(scratch,'(1x,i5,9F10.4)') iter,orbital,spin,total_angmom 
    CLOSE(unit=scratch)
    WRITE(*,'(/,A,I7,A/2(A,F12.4),A/(3(A,E12.5),A),3(A,E12.5))') &
         ' ***** Iteration ',iter,' *****',' Total energy: ',ehf,' MeV  Total kinetic energy: ', &
         tke,' MeV',' de/e:      ',delesum,'      h**2  fluct.:    ',efluct1, &
         ' MeV, h*hfluc.:    ',efluct2,' MeV', &
         ' MeV. Rearrangement E: ',e3corr,' MeV. Coul.Rearr.: ', &
         ecorc,' MeV x0dmp: ',x0dmp
    ! detail printout
    WRITE(*,'(/A)') ' Energies integrated from density functional:'
    WRITE(*,'(4(A,1PE14.6),A/26X,3(A,1PE14.6),A)') &
         ' Total:',ehfint,' MeV. t0 part:',ehf0,' MeV. t1 part:',ehf1, &
         ' MeV. t2 part:',ehf2,' MeV',' t3 part:',ehf3,' MeV. t4 part:',ehfls, &
         ' MeV. Coulomb:',ehfc,' MeV.'
    IF(ipair/=0) WRITE(*,'(2(A,1PE14.6))') ' Pairing energy neutrons: ', &
         epair(1),' protons: ',epair(2)
    ! output densities
    IF(mplot/=0) THEN  
       IF(MOD(iter,mplot)==0) THEN
          CALL plot_density
          CALL write_densities
       ENDIF
    ENDIF
    IF(.NOT.wflag) RETURN
    ! print details of s.p. levels
    WRITE(*,'(A)') ' Neutron Single Particle States:',header
    DO il=1,nstmax
       IF(il==npmin(2)) THEN
          WRITE(*,'(A)') ' Proton Single Particle States:',header  
       END IF
       WRITE(*,'(1X,I3,F4.0,F8.5,2F9.5,F9.6,F8.3,F10.3,3F8.3,3F7.3)') &
            il,sp_parity(il),wocc(il),sp_efluct1(il),sp_efluct2(il), &
            sp_norm(il),sp_kinetic(il),sp_energy(il), &
            sp_orbital(:,il),sp_spin(:,il)
    ENDDO
    CALL moment_print
  END SUBROUTINE sinfo
  !*************************************************************************
  SUBROUTINE harmosc
    USE Trivial, ONLY: rpsnorm
    REAL(db) :: xx,yy,zz,xx2,zz2,y2,anorm,temp
    INTEGER  :: nst,iq,is,ix,iy,iz,nps,i,j,k,ka,nshell(3,nstmax)
    wocc=0.D0
    wocc(1:nneut)=1.D0
    wocc(npmin(2):npmin(2)+nprot-1)=1.D0
    !*************************************************************************
    ! Lowest state: Gaussian
    !*************************************************************************
    nst=0  
    DO iq=1,2  
       IF(iq==1) THEN
          nps=npsi(1)
       ELSE
          nps=npsi(2)
       ENDIF
       ka_loop: DO ka=0,nps  
          DO k=0,nps  
             DO j=0,nps  
                DO i=0,nps  
                   IF(ka==i+j+k) THEN  
                      DO is=1,2
                         nst=nst+1  
                         IF(nst>nps) EXIT ka_loop
                         nshell(1,nst)=i  
                         nshell(2,nst)=j  
                         nshell(3,nst)=k  
                      ENDDO
                   ENDIF
                ENDDO
             ENDDO
          ENDDO
       ENDDO ka_loop
       nst=nst-1
    END DO
    DO iq=1,2
       nst=npmin(iq)
       DO iz=1,nz  
          zz2=(z(iz)/radinz)**2
          DO ix=1,nx  
             xx2=(x(ix)/radinx)**2  
             DO iy=1,ny  
                y2=(y(iy)/radiny)**2  
                temp=xx2+y2+zz2
                psi(ix,iy,iz,1,nst)=EXP(-(temp))  
             ENDDO
          ENDDO
       ENDDO
       anorm=rpsnorm(psi(:,:,:,:,nst))
       psi(:,:,:,:,nst)=psi(:,:,:,:,nst)/SQRT(anorm)
       !*************************************************************************
       ! Higher states: lowest * polynomial
       !*************************************************************************
       DO nst=npmin(iq)+1,npsi(iq)  
          is=MOD(nst-npmin(iq),2)+1  
          DO iz=1,nz  
             IF(nshell(3,nst)/=0) THEN  
                zz=z(iz)**nshell(3,nst)  
             ELSE  
                zz=1.0D0  
             ENDIF
             DO iy=1,ny  
                IF(nshell(2,nst)/=0) THEN  
                   yy=y(iy)**nshell(2,nst)  
                ELSE  
                   yy=1.0D0  
                ENDIF
                DO ix=1,nx  
                   IF(nshell(1,nst)/=0) THEN  
                      xx=x(ix)**nshell(1,nst)  
                   ELSE  
                      xx=1.0D0  
                   ENDIF
                   psi(ix,iy,iz,is,nst)=psi(ix,iy,iz,1,npmin(iq))*xx*yy*zz
                ENDDO
             ENDDO
          ENDDO
       END DO
    END DO
    WRITE(*,*) "Harmonic oscillators widths (x-y-z):"
    WRITE(*,"(3F12.4)") radinx,radiny,radinz
    WRITE(*,*) '***** Harmonic oscillator initialization complete *****'
  END SUBROUTINE harmosc
END MODULE Static