2146 lines
84 KiB
Fortran
2146 lines
84 KiB
Fortran
! -*- mode: F90; mode: font-lock -*-
|
|
! ------------------------------------------------------------------------------
|
|
! $Id$
|
|
! ------------------------------------------------------------------------------
|
|
! Module DMMin
|
|
! ------------------------------------------------------------------------------
|
|
! Code area 4: Density matrix
|
|
! ------------------------------------------------------------------------------
|
|
|
|
!!****h* Conquest/DMMin
|
|
!! NAME
|
|
!! DMMin
|
|
!! PURPOSE
|
|
!! Contains all routines related to minimisation of energy with
|
|
!! respect to elements of density matrix
|
|
!!
|
|
!! Contains initialisation, early stage (steepest descents) and late
|
|
!! stage (GR-Pulay - D.R.Bowler and M.J.Gillan, Chem. Phys.
|
|
!! Lett. 325, 473 (2000)) techniques as well as a call to a
|
|
!! diagonalisation routine for exact solution
|
|
!! USES
|
|
!! datatypes, DiagModule, GenBlas, GenComms, global_module,
|
|
!! logicals, matrix_data, maxima_module, McWeeny, multiply_module,
|
|
!! mult_module, numbers, PosTan, primary_module, Pulay
|
|
!! AUTHOR
|
|
!! D.R.Bowler
|
|
!! CREATION DATE
|
|
!! 11/12/00
|
|
!! MODIFICATION HISTORY
|
|
!! 05/06/2001 dave
|
|
!! ROBODoc header, RCS Id and Log tags and changed all stops
|
|
!! to cq_abort calls
|
|
!! 06/06/2001 dave
|
|
!! Bug fixes - added temp_M to use matrix_data and closed
|
|
!! brackets on cq_abort call
|
|
!! 08/06/2001 dave
|
|
!! Changed all occurences of dgsum to gsum
|
|
!! 29/05/2002 dave
|
|
!! Minor changes to shift H back after DM minimisation, and to
|
|
!! switch between O(N) and diagonalisation
|
|
!! 14:23, 26/02/2003 drb
|
|
!! Added electron number check to linear part of
|
|
!! correct_electron_number
|
|
!! 16:43, 10/05/2005 dave
|
|
!! Various small changes throughout code: mainly bug fixes
|
|
!! 2008/02/01 17:46 dave
|
|
!! Changes to write output to file not stdout
|
|
!! 2008/05/22 ast
|
|
!! Added timers
|
|
!! 2012/03/01 L.Tong
|
|
!! Added interface for lineMinL
|
|
!! 2013/03/06 L.Tong
|
|
!! Added module globals maxpulaystepDMM and minpulaystepDMM
|
|
!! to allow user to control the pulay mixing stepsize bracket
|
|
!! in lateDM
|
|
!! 2014/09/15 18:30 lat
|
|
!! fixed call start/stop_timer to timer_module (not timer_stdlocks_module !)
|
|
!! 2019/10/24 08:30 dave
|
|
!! Added inode and ionode as use-associated from GenComms in module header
|
|
!! for efficiency and best practice; also iprint_DM
|
|
!! 2021/08/02 14:46 dave
|
|
!! Added dE_DMM for comparison to structural optimisation
|
|
!! SOURCE
|
|
!!
|
|
module DMMin
|
|
|
|
use datatypes
|
|
use global_module, only: io_lun, area_DM, iprint_DM
|
|
use timer_module, only: start_timer, stop_timer, cq_timer
|
|
use timer_module, only: start_backtrace, stop_backtrace
|
|
use timer_stdclocks_module, only: tmr_std_densitymat
|
|
use GenComms, ONLY: inode, ionode
|
|
|
|
! Area identification
|
|
integer, parameter, private :: area = 4
|
|
integer :: maxpulayDMM
|
|
integer, save :: n_dumpL = 0 ! Redefined: 2019Dec30 tsuyoshi
|
|
real(double) :: LinTol_DMM
|
|
real(double) :: minpulaystepDMM ! min step size for pulay minimisation
|
|
real(double) :: maxpulaystepDMM ! max step size for pulay minimisation
|
|
real(double), save:: dE_DMM
|
|
!integer, parameter :: mx_pulay = 5
|
|
!real(double), parameter :: LinTol = 0.1_double
|
|
|
|
!!***
|
|
|
|
contains
|
|
|
|
! -----------------------------------------------------------
|
|
! Subroutine FindMinDM
|
|
! -----------------------------------------------------------
|
|
|
|
!!****f* DMMin/FindMinDM *
|
|
!!
|
|
!! NAME
|
|
!! FindMinDM - control routine for energy minimisation wrt DM
|
|
!! USAGE
|
|
!!
|
|
!! PURPOSE
|
|
!! Controls the minimisation of energy with respect to the
|
|
!! density matrix. Calls McWeeny for initialisation, and
|
|
!! early and late stage techniques for minimisation.
|
|
!!
|
|
!! If the early stage finds an inflexion during exact line
|
|
!! minimisation, then the code immediately bounces back to
|
|
!! McWeeny initialisation. If the residual increases during
|
|
!! GRP minimisation (which it shouldn't !) early stage is then
|
|
!! used.
|
|
!! INPUTS
|
|
!! logical :: resetL - determines whether it's necessary to use McWeeny
|
|
!! logical :: record - flag for recording E vs residual
|
|
!! USES
|
|
!! datatypes, numbers, global_module, maxima_module, matrix_data,
|
|
!! mult_module, multiply_module, McWeeny, PosTan
|
|
!! AUTHOR
|
|
!! D.R.Bowler
|
|
!! CREATION DATE
|
|
!! 11/12/00
|
|
!! MODIFICATION HISTORY
|
|
!! 05/06/2001 dave
|
|
!! Added ROBODoc header
|
|
!! 20/06/2001 dave
|
|
!! Moved fitting of C and beta into PosTan and called fit_coeff
|
|
!! 29/05/2002 dave
|
|
!! Added option to use diagonalisation to find DM
|
|
!! 2004/10/28 drb
|
|
!! Added dump_matrix call at the end of minimisation
|
|
!! 16:43, 10/05/2005 dave
|
|
!! Added local dataM3
|
|
!! 2008/05/22 ast
|
|
!! Added timer
|
|
!! 2011/07/22 L.Tong
|
|
!! Added spin polarisation
|
|
!! 2011/09/13 L.Tong
|
|
!! Removed obsolete dependence on number_of_bands, and instead use
|
|
!! the global module variable ne_in_cell for the total number of
|
|
!! electrons
|
|
!! 2012/03/08 L.Tong
|
|
!! - Major rewrite of spin implementation
|
|
!! - Removed redundant input parameter real(double) mu
|
|
!! 2012/06/24 L.Tong
|
|
!! - Added control of whether to dump L using flag_dump_L
|
|
!! 2013/08/20 M.Arita
|
|
!! - Changed calls for dumping L-matrix
|
|
!! - Added flag to start from EarlyDM
|
|
!! 2013/12/03 M.Arita
|
|
!! - Added calls for writing out matrices for XL-BOMD
|
|
!! - Added call for writing out InfoGlobal.dat
|
|
!! (These used to be called at get_E_and_F)
|
|
!! 2014/01/21 lat
|
|
!! - Added call to exx
|
|
!! 2014/09/15 18:30 lat
|
|
!! - Fixed call start/stop_timer to timer_module (not timer_stdlocks_module !)
|
|
!! 2015/06/08 lat
|
|
!! - Added experimental backtrace
|
|
!! 2017/02/23 dave
|
|
!! - Changing location of diagon flag from DiagModule to global and name to flag_diagonalisation
|
|
!! 2017/05/09 dave
|
|
!! Added code to dump L matrix for both spin channels
|
|
!! 2017/05/11 dave
|
|
!! Added code to dump X matrix (and associated) for both spin channels
|
|
!! 2017/11/06 dave
|
|
!! Added dump of K matrix
|
|
!! 2018/01/22 tsuyoshi (with dave)
|
|
!! Initial changes for atom updates
|
|
!! 2018/11/13 17:30 nakata
|
|
!! Changed matS, matT and matTtran to be spin_SF dependent
|
|
!! 2019/05/27 tsuyoshi
|
|
!! Debug for spin=2, propagateL (not propagateX)
|
|
!! SOURCE
|
|
!!
|
|
subroutine FindMinDM(n_L_iterations, vary_mu, tolerance, &
|
|
resetL, record, level)
|
|
|
|
use datatypes
|
|
use numbers
|
|
use global_module, only: IPRINT_TIME_THRES1, &
|
|
nspin, nspin_SF, flag_fix_spin_population, &
|
|
ne_in_cell, ne_spin_in_cell, flag_dump_L, &
|
|
flag_SkipEarlyDM, flag_XLBOMD, &
|
|
flag_propagateX, flag_dissipation, &
|
|
integratorXL, runtype, flag_exx, &
|
|
flag_diagonalisation, min_layer, flag_DM_converged
|
|
use mult_module, only: matrix_transpose, matT, matTtran, matL, &
|
|
matS, matrix_sum, matK
|
|
use McWeeny, only: InitMcW, McWMin
|
|
use PosTan, only: max_iters, cscale, PulayE, PulayR, PulayC, &
|
|
PulayBeta, pos_tan, fit_coeff
|
|
use DiagModule, only: FindEvals
|
|
use io_module, only: dump_matrix, return_prefix
|
|
use energy, only: entropy
|
|
use timer_module, only: cq_timer, start_timer, stop_print_timer, &
|
|
WITH_LEVEL
|
|
use matrix_data, only: Lrange, Srange, LSrange, Hrange
|
|
!use XLBOMD_module, only: matX, matXvel, dump_XL
|
|
use store_matrix, only: dump_XL
|
|
use mult_module, only: matXL, matXLvel
|
|
|
|
use exx_kernel_default, only: get_X_matrix
|
|
|
|
implicit none
|
|
|
|
! Passed variables
|
|
integer, optional :: level
|
|
integer :: n_L_iterations
|
|
real(double) :: tolerance
|
|
logical :: resetL, vary_mu, record
|
|
|
|
! Local variables
|
|
logical :: done, inflex, problem, early
|
|
integer :: ndone, nkeep, ndelta, i
|
|
real(double) :: LastE
|
|
real(double), parameter :: eprec = 1.0e-12_double
|
|
real(double), save :: delta_e
|
|
type(cq_timer) :: tmr_l_iter
|
|
type(cq_timer) :: backtrace_timer
|
|
integer :: backtrace_level
|
|
!TM 2010.Nov.06
|
|
integer :: niter = 0
|
|
integer, parameter :: niter_max = 10
|
|
character(len=12) :: subname = "FindDM: "
|
|
character(len=120) :: prefix
|
|
|
|
!****lat<$
|
|
if ( present(level) ) backtrace_level = level+1
|
|
if ( .not. present(level) ) backtrace_level = -10
|
|
call start_backtrace(t=backtrace_timer,who='FindMinDM',&
|
|
where=area,level=backtrace_level,echo=.true.)
|
|
!****lat>$
|
|
|
|
call start_timer(tmr_std_densitymat)
|
|
prefix = return_prefix(subname, min_layer)
|
|
|
|
entropy = zero
|
|
|
|
if (flag_diagonalisation) then ! Use exact diagonalisation to get K
|
|
call FindEvals(ne_spin_in_cell)
|
|
else
|
|
if (inode == ionode .and. iprint_DM + min_layer >0) then
|
|
write(io_lun,'(/4x,a,f15.10,2x,a,i5)') &
|
|
trim(prefix)//' tol: ', tolerance, &
|
|
'number of L iterations: ', n_L_iterations
|
|
end if
|
|
|
|
problem = .false.
|
|
inflex = .false.
|
|
done = .false.
|
|
early = .true. ! Do early DM iterations by default
|
|
if (flag_SkipEarlyDM) then
|
|
early = .false.
|
|
end if
|
|
|
|
! Start with the transpose of S^-1
|
|
call matrix_transpose(matT(1), matTtran(1))
|
|
if (nspin_SF==2) call matrix_transpose(matT(2), matTtran(2))
|
|
|
|
! Now minimise the energy
|
|
ndone = 0
|
|
niter = 0
|
|
do while (.not. done)
|
|
call start_timer(tmr_l_iter, WITH_LEVEL)
|
|
if (resetL .or. inflex) then ! Reset L to McW
|
|
call InitMcW
|
|
call McWMin(n_L_iterations, delta_e)
|
|
early = .true.
|
|
resetL = .false. !2010.Nov.06 TM
|
|
end if
|
|
if (early .or. problem) then
|
|
inflex = .false.
|
|
call earlyDM(ndone, n_L_iterations, delta_e, done, vary_mu, &
|
|
tolerance, inflex, record)
|
|
end if
|
|
if ((.not. done) .and. (.not. inflex)) then ! Continue on to late stage
|
|
call lateDM(ndone, n_L_iterations, done, delta_e, vary_mu, &
|
|
tolerance, record)
|
|
end if
|
|
niter = niter + 1
|
|
if (problem) resetL = .true.
|
|
!ORI problem = .true. ! If we're not done, then this kicks us back to early
|
|
if (niter > niter_max) then
|
|
problem = .true. ! If we're not done, then this kicks us back to early
|
|
niter = 0
|
|
end if
|
|
call stop_print_timer(tmr_l_iter, "FindMinDM iteration", &
|
|
IPRINT_TIME_THRES1)
|
|
end do ! end of do while (.not. done)
|
|
end if
|
|
flag_DM_converged = .true.
|
|
if (record) then
|
|
if (inode == ionode .and. iprint_DM + min_layer > 2) then
|
|
write(io_lun,*) ' List of residuals and energies'
|
|
do i = 1, ndone
|
|
write(io_lun, 7) i, PulayR(i), PulayE(i)
|
|
end do
|
|
end if
|
|
call fit_coeff(PulayC, PulayBeta, PulayE, PulayR, ndone)
|
|
if (inode == ionode) write (io_lun, 6) PulayC, PulayBeta
|
|
end if
|
|
|
|
call stop_timer(tmr_std_densitymat)
|
|
|
|
!****lat<$
|
|
call stop_backtrace(t=backtrace_timer,who='FindMinDM',echo=.true.)
|
|
!****lat>$
|
|
|
|
return
|
|
|
|
6 format(2x,'dE to dR parameters - C: ',f15.8,' beta: ',f15.8)
|
|
7 format(2x,i4,2f15.8)
|
|
8 format(2x,'Welcome to minimise_energy. Tolerance is ',f15.8)
|
|
|
|
end subroutine FindMinDM
|
|
!!***
|
|
|
|
! -----------------------------------------------------------
|
|
! Subroutine earlyDM
|
|
! -----------------------------------------------------------
|
|
|
|
!!****f* DMMin/earlyDM *
|
|
!!
|
|
!! NAME
|
|
!! earlyDM - early stage DM min
|
|
!! USAGE
|
|
!!
|
|
!! PURPOSE
|
|
!! Performs steepest descent minimisation of energy wrt density
|
|
!! matrix elements, until the change in gradient is linear in
|
|
!! density matrix (at which point CG or GR-Pulay - late stage in
|
|
!! other words) can be used
|
|
!! INPUTS
|
|
!!
|
|
!!
|
|
!! USES
|
|
!!
|
|
!! AUTHOR
|
|
!! D.R.Bowler
|
|
!! CREATION DATE
|
|
!! 11/12/00
|
|
!! MODIFICATION HISTORY
|
|
!! 05/06/2001 dave
|
|
!! Added ROBODoc header, removed calls to stop
|
|
!! 08/06/2001 dave
|
|
!! Changed dgsum to gsum from GenComms
|
|
!! 29/05/2002 dave
|
|
!! Added call to move H back (i.e. undo the H-mu.S shift) after
|
|
!! minimisation
|
|
!! 2004/10/28 drb
|
|
!! Removed shift of H
|
|
!! 10:09, 13/02/2006 drb
|
|
!! Removed all explicit references to data_ variables and rewrote
|
|
!! in terms of new matrix routines
|
|
!! 2009/07/08 16:41 dave
|
|
!! Introduced atom-based tolerance (just divide by ni_in_cell)
|
|
!! 2011/08/14 L.Tong
|
|
!! Added spin polarisation
|
|
!! Added matphi_dn
|
|
!! 2011/08/25 L.Tong
|
|
!! Removed the redundant parameter number_of_bands
|
|
!! 2012/03/12 L.Tong
|
|
!! - Major rewrite of spin implementation
|
|
!! - Removed redundant input parameter real(double) mu
|
|
!! 2018/11/13 17:30 nakata
|
|
!! Changed matT to be spin_SF dependent
|
|
!! SOURCE
|
|
!!
|
|
subroutine earlyDM(ndone, n_L_iterations, delta_e, done, vary_mu, &
|
|
tolerance, inflex, record)
|
|
|
|
use datatypes
|
|
use logicals
|
|
use numbers
|
|
use matrix_data, only: Lrange, TLrange
|
|
use mult_module, only: matT, matphi, mult, T_L_TL, TL_T_L, &
|
|
matrix_product_trace, &
|
|
LNV_matrix_multiply, &
|
|
allocate_temp_matrix, free_temp_matrix, &
|
|
matrix_sum, matrix_product
|
|
use primary_module, only: bundle
|
|
use PosTan, only: PulayR, PulayE
|
|
use GenComms, only: cq_abort, gsum, cq_warn
|
|
use global_module, only: IPRINT_TIME_THRES1, min_layer, &
|
|
ni_in_cell, flag_global_tolerance, &
|
|
nspin, spin_factor, &
|
|
flag_fix_spin_population, flag_SpinDependentSF
|
|
use timer_module, only: cq_timer, start_timer, stop_print_timer, &
|
|
WITH_LEVEL
|
|
use io_module, only: return_prefix
|
|
|
|
implicit none
|
|
|
|
! Passed variables
|
|
real(double) :: tolerance, delta_e
|
|
integer :: n_L_iterations, ndone
|
|
logical :: inflex, vary_mu, done, record
|
|
|
|
! Local variables
|
|
integer :: n_iter, length, spin, spin_SF
|
|
integer, dimension(nspin) :: matM3, matSM3, matSphi, mat_temp, mat_search
|
|
real(double), dimension(nspin) :: energy0, energy1, electrons
|
|
real(double) :: energy0_tot, energy1_tot, electrons_tot
|
|
real(double), dimension(nspin) :: e_dot_n, n_dot_n
|
|
real(double) :: e_dot_n_tot, n_dot_n_tot
|
|
real(double) :: g0, g1
|
|
real(double) :: interpG, zeta, direct_sum_factor
|
|
type(cq_timer) :: tmr_l_tmp1, tmr_l_iter
|
|
character(len=12) :: subname = "earlyDM: "
|
|
character(len=120) :: prefix
|
|
|
|
! integer :: matM3, matSM3, matSphi, mat_temp, mat_search
|
|
! integer :: matM3_dn, matSM3_dn, mat_search_dn, matSphi_dn
|
|
! real(double) :: energy0, energy0_up, energy0_dn
|
|
! real(double) :: energy1, energy1_up, energy1_dn
|
|
! real(double) :: electrons, electrons_up, electrons_dn
|
|
! real(double) :: e_dot_n, e_dot_n_up, e_dot_n_dn
|
|
! real(double) :: n_dot_n, n_dot_n_up, n_dot_n_dn
|
|
! real(double) :: g0, g1
|
|
! real(double) :: interpG, zeta, direct_sum_factor
|
|
! type(cq_timer) :: tmr_l_tmp1, tmr_l_iter
|
|
|
|
call start_timer(tmr_l_tmp1, WITH_LEVEL)
|
|
prefix = return_prefix(subname, min_layer)
|
|
spin_SF = 1
|
|
if(inode==ionode .and. iprint_DM + min_layer >= 2) write(io_lun,fmt='(/4x,a)') trim(prefix)//" starting"
|
|
! allocate temp matrices
|
|
do spin = 1, nspin
|
|
matM3(spin) = allocate_temp_matrix(Lrange,0)
|
|
matSM3(spin) = allocate_temp_matrix(Lrange,0)
|
|
matSphi(spin) = allocate_temp_matrix(Lrange,0)
|
|
mat_temp(spin) = allocate_temp_matrix(TLrange,0)
|
|
mat_search(spin) = allocate_temp_matrix(Lrange,0)
|
|
end do
|
|
|
|
if (ndone > n_L_iterations) &
|
|
call cq_abort('earlyDM: too many L iterations', &
|
|
ndone, n_L_iterations)
|
|
|
|
min_layer = min_layer - 1
|
|
if (vary_mu) call correct_electron_number
|
|
min_layer = min_layer + 1
|
|
|
|
call LNV_matrix_multiply(electrons, energy0, dontK, dontM1, &
|
|
dontM2, doM3, dontM4, dophi, doE, &
|
|
mat_M3=matM3, mat_phi=matphi)
|
|
electrons_tot = spin_factor * sum(electrons(:))
|
|
energy0_tot = spin_factor * sum(energy0(:))
|
|
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
! Pre- and post-multiply M3 by S^-1 so that it is contravariant
|
|
call matrix_product(matT(spin_SF), matM3(spin), mat_temp(spin), mult(T_L_TL))
|
|
call matrix_product(mat_temp(spin), matT(spin_SF), matSM3(spin), mult(TL_T_L))
|
|
! Project gradient perpendicular to electron gradient
|
|
call matrix_sum(zero, mat_search(spin), -one, matSM3(spin))
|
|
end do
|
|
|
|
if (vary_mu) then
|
|
e_dot_n_tot = zero
|
|
n_dot_n_tot = zero
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
! Pre- and post-multiply phi by S^-1 so that it is contravariant
|
|
call matrix_product(matT(spin_SF), matphi(spin), mat_temp(spin), mult(T_L_TL))
|
|
call matrix_product(mat_temp(spin), matT(spin_SF), matSphi(spin), mult(TL_T_L))
|
|
! Only one is pre- and post-multiplied because (A,B) = Tr(ASBS)
|
|
e_dot_n(spin) = matrix_product_trace(matSM3(spin), matphi(spin))
|
|
e_dot_n_tot = e_dot_n_tot + spin_factor * e_dot_n(spin)
|
|
n_dot_n(spin) = matrix_product_trace(matSphi(spin), matphi(spin))
|
|
n_dot_n_tot = n_dot_n_tot + spin_factor * n_dot_n(spin)
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 3) then
|
|
write(io_lun, '(4x,a,i1,") ",f16.6)') &
|
|
trim(prefix)//" e.n (spin=", spin, e_dot_n(spin)
|
|
write(io_lun, '(4x,a,i1,") ",f16.6)') &
|
|
trim(prefix)//" n.n (spin=", spin, n_dot_n(spin)
|
|
end if
|
|
end do
|
|
! Correct search direction so that it is tangent to
|
|
! iso-surface of electron numbers. This is different for if
|
|
! one fixes spin populations or not
|
|
! If spin population is fixed then (x denote spin)
|
|
!
|
|
! tr(dE/dL^x * dN/dL^x)
|
|
! G^x = - dE/dL^x + --------------------- dN/dL^x
|
|
! tr(dN/dL^x * dN/dL^x)
|
|
!
|
|
! If only the total electron number is fixed then
|
|
!
|
|
! sum_x tr(dE/dL^x * dN/dL^x)
|
|
! direct_sum_factor = ---------------------------
|
|
! sum_x tr(dN/dL_x * dN/dL^x)
|
|
!
|
|
! G^x = - dE/dL^x + direct_sum_factor * dN/dL^x
|
|
!
|
|
if (nspin == 1 .or. flag_fix_spin_population) then
|
|
do spin = 1, nspin
|
|
! if gradient of N w.r.t. L is zero then no correction needed
|
|
if (abs(n_dot_n(spin)) > RD_ERR) then
|
|
call matrix_sum(one, mat_search(spin), &
|
|
e_dot_n(spin) / n_dot_n(spin), &
|
|
matSphi(spin))
|
|
end if
|
|
end do
|
|
else ! variable spin
|
|
! if gradient of N w.r.t. L is zero then no correction needed
|
|
if (abs(n_dot_n_tot) > RD_ERR) then
|
|
direct_sum_factor = e_dot_n_tot / n_dot_n_tot
|
|
do spin = 1, nspin
|
|
call matrix_sum(one, mat_search(spin), direct_sum_factor,&
|
|
matSphi(spin))
|
|
end do
|
|
end if
|
|
end if ! (nspin == 1 .or. flag_fix_spin_population)
|
|
end if ! (vary_mu)
|
|
|
|
call stop_print_timer(tmr_l_tmp1, "earlyDM - Preliminaries", &
|
|
IPRINT_TIME_THRES1)
|
|
|
|
!-----------!
|
|
! MAIN LOOP !
|
|
!-----------!
|
|
! delta_e is used for spin polarised calculations too
|
|
delta_e = zero
|
|
do n_iter = 1, n_L_iterations
|
|
call start_timer(tmr_l_iter, WITH_LEVEL)
|
|
if (flag_global_tolerance) then
|
|
g0 = zero
|
|
do spin = 1, nspin
|
|
g0 = g0 + spin_factor * &
|
|
matrix_product_trace(matM3(spin), matSM3(spin))
|
|
end do
|
|
else
|
|
g0 = zero
|
|
do spin = 1, nspin
|
|
g0 = g0 + spin_factor * &
|
|
matrix_product_trace(matM3(spin), matSM3(spin)) / &
|
|
real(ni_in_cell, double)
|
|
end do
|
|
end if
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 1) &
|
|
write(io_lun, fmt='(4x,a,i3," Energy: ",f16.6," Ha Residual: ",e16.6)') &
|
|
trim(prefix)//" iteration: ",n_iter, energy0_tot, g0
|
|
|
|
! minimise total E along search direction, updates energy1 =
|
|
! E(L_n_iter+1), delta_e = energy1_tot - energy0_tot,
|
|
! matM3(L_n_iter+1), matM3_dn(L_n_iter+1), inflex and interpG
|
|
min_layer = min_layer - 1
|
|
call lineMinL(matM3, mat_search, mat_temp, matSM3, &
|
|
energy0_tot, energy1_tot, delta_e, &
|
|
inflex, interpG)
|
|
min_layer = min_layer + 1
|
|
! panic if found inflexion point
|
|
if (inflex) then
|
|
call cq_warn(subname,"Inflexion point found in DM search; resetting")
|
|
ndone = n_iter
|
|
! deallocate matrices
|
|
do spin = nspin, 1, -1
|
|
call free_temp_matrix(mat_search(spin))
|
|
call free_temp_matrix(mat_temp(spin))
|
|
call free_temp_matrix(matSphi(spin))
|
|
call free_temp_matrix(matSM3(spin))
|
|
call free_temp_matrix(matM3(spin))
|
|
end do
|
|
call stop_print_timer(tmr_l_iter, 'an earlyDM iteration', &
|
|
IPRINT_TIME_THRES1)
|
|
return !This is a panic sign !
|
|
end if ! inflex
|
|
|
|
! Gradient after line min - assumes LVN_matrix_mult at end
|
|
! of lineMinL
|
|
! Pre- and post-multiply M3 by S^-1 so that it is
|
|
! contravariant, matSM3 = matSM3(L_n_iter+1)
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
call matrix_product (matT(spin_SF), matM3(spin), mat_temp(spin), mult(T_L_TL))
|
|
call matrix_product (mat_temp(spin), matT(spin_SF), matSM3(spin), mult(TL_T_L))
|
|
end do
|
|
|
|
! g1 = tr(matM3(L_n_iter+1) * matSM3(L_n_iter+1))
|
|
if(flag_global_tolerance) then
|
|
g1 = zero
|
|
do spin = 1, nspin
|
|
g1 = g1 + spin_factor * &
|
|
matrix_product_trace(matM3(spin), matSM3(spin))
|
|
end do
|
|
else
|
|
g1 = zero
|
|
do spin = 1, nspin
|
|
g1 = g1 + spin_factor * &
|
|
matrix_product_trace(matM3(spin), matSM3(spin)) / &
|
|
real(ni_in_cell, double)
|
|
end do
|
|
end if
|
|
|
|
! Test for linearity or convergence
|
|
! zeta is returned by lineMinL
|
|
zeta = interpG
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun,fmt='(4x,a,f16.6,a,f16.6)') trim(prefix)//" dE ", delta_e, " Ha linearity: ",zeta
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 3) &
|
|
write (io_lun, '(4x,a,3f16.6)') trim(prefix)//" zeta...: ", g0, g1, interpG
|
|
|
|
! Correct L_n_iter+1 again to get electron numbers correct
|
|
min_layer = min_layer - 1
|
|
if (vary_mu) call correct_electron_number
|
|
min_layer = min_layer + 1
|
|
|
|
! recalculate the quantities after L_n_iter+1 is updated
|
|
! 2011/08/24 L.Tong:
|
|
! The orignal implementation uses energy0 as input for
|
|
! LNV_matrix_multiply, but I think this is incorrect and
|
|
! should be energy1
|
|
call LNV_matrix_multiply(electrons, energy1, dontK, dontM1, &
|
|
dontM2, doM3, dontM4, dophi, doE, &
|
|
mat_M3=matM3, mat_phi=matphi)
|
|
electrons_tot = spin_factor * sum(electrons(:))
|
|
energy1_tot = spin_factor * sum(energy1(:))
|
|
|
|
! Pre- and post-multiply M3 by S^-1 so that it is contravariant
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
call matrix_product(matT(spin_SF), matM3(spin), mat_temp(spin), mult(T_L_TL))
|
|
call matrix_product(mat_temp(spin), matT(spin_SF), matSM3(spin), mult(TL_T_L))
|
|
end do
|
|
|
|
! prepare for the next iterative step
|
|
energy0_tot = energy1_tot
|
|
! update the search direction
|
|
do spin = 1, nspin
|
|
call matrix_sum(zero, mat_search(spin), -one, matSM3(spin))
|
|
end do
|
|
|
|
! Project search direction perpendicular to electron gradient
|
|
if (vary_mu) then
|
|
! Wrap electron gradient for tensorial correctness
|
|
e_dot_n_tot = zero
|
|
n_dot_n_tot = zero
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
call matrix_product(matT(spin_SF), matphi(spin), mat_temp(spin), &
|
|
mult(T_L_TL))
|
|
call matrix_product(mat_temp(spin), matT(spin_SF), matSphi(spin), &
|
|
mult(TL_T_L))
|
|
e_dot_n(spin) = matrix_product_trace(matSM3(spin), matphi(spin))
|
|
e_dot_n_tot = e_dot_n_tot + spin_factor * e_dot_n(spin)
|
|
n_dot_n(spin) = matrix_product_trace(matSphi(spin), matphi(spin))
|
|
n_dot_n_tot = n_dot_n_tot + spin_factor * n_dot_n(spin)
|
|
end do
|
|
if (nspin == 1 .or. flag_fix_spin_population) then
|
|
do spin = 1, nspin
|
|
! if gradient of N w.r.t. L is zero then no correction needed
|
|
if (abs(n_dot_n(spin)) > RD_ERR) then
|
|
call matrix_sum(one, mat_search(spin), &
|
|
e_dot_n(spin) / n_dot_n(spin), &
|
|
matSphi(spin))
|
|
end if
|
|
end do
|
|
else
|
|
! if gradient of N w.r.t. L is zero then no correction needed
|
|
if (abs(n_dot_n_tot) > RD_ERR) then
|
|
direct_sum_factor = e_dot_n_tot / n_dot_n_tot
|
|
do spin = 1, nspin
|
|
call matrix_sum(one, mat_search(spin), &
|
|
direct_sum_factor, matSphi(spin))
|
|
end do
|
|
end if
|
|
end if
|
|
! Here, we can't alter M3 directly: lineMinL expects REAL gradient
|
|
end if
|
|
|
|
if (flag_global_tolerance) then
|
|
g0 = zero
|
|
do spin = 1, nspin
|
|
g0 = g0 + spin_factor * &
|
|
matrix_product_trace(matM3(spin), matSM3(spin))
|
|
end do
|
|
else
|
|
g0 = zero
|
|
do spin = 1, nspin
|
|
g0 = g0 + spin_factor * &
|
|
matrix_product_trace(matM3(spin), matSM3(spin)) / &
|
|
real(ni_in_cell, double)
|
|
end do
|
|
end if
|
|
|
|
! store the Pulay histories
|
|
PulayR(ndone + n_iter) = g0
|
|
PulayE(ndone + n_iter) = energy0_tot
|
|
|
|
! test if we are linear
|
|
if (abs(zeta) < LinTol_DMM) then
|
|
! We're linear Added +1 to n_iter for cosmetic reasons (this
|
|
! is true since at this stage g0 and energy0_tot etc are already
|
|
! prepared for n_iter+1 step)
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 1) &
|
|
write(io_lun, fmt='(4x,a,i3," Energy: ",f16.6," Ha Residual: ",e16.6)') &
|
|
trim(prefix)//" iteration: ",n_iter+1, energy0_tot, g0
|
|
if ((inode == ionode).and. (iprint_DM + min_layer >= 1)) &
|
|
write (io_lun, '(4x,a,i3,a)') &
|
|
trim(prefix)//" transition to lateDM after ",n_iter+1," iterations"
|
|
ndone = n_iter
|
|
do spin = nspin, 1, -1
|
|
call free_temp_matrix(mat_search(spin))
|
|
call free_temp_matrix(mat_temp(spin))
|
|
call free_temp_matrix(matSphi(spin))
|
|
call free_temp_matrix(matSM3(spin))
|
|
call free_temp_matrix(matM3(spin))
|
|
end do
|
|
call stop_print_timer(tmr_l_iter, "an earlyDM iteration", &
|
|
IPRINT_TIME_THRES1)
|
|
return
|
|
end if
|
|
call stop_print_timer(tmr_l_iter, "an earlyDM iteration", &
|
|
IPRINT_TIME_THRES1)
|
|
end do ! do n_iter = 1, n_L_iterations
|
|
|
|
do spin = nspin, 1, -1
|
|
call free_temp_matrix(mat_search(spin))
|
|
call free_temp_matrix(mat_temp(spin))
|
|
call free_temp_matrix(matSphi(spin))
|
|
call free_temp_matrix(matSM3(spin))
|
|
call free_temp_matrix(matM3(spin))
|
|
end do
|
|
return
|
|
end subroutine earlyDM
|
|
!!***
|
|
|
|
|
|
! -----------------------------------------------------------
|
|
! Subroutine lateDM
|
|
! -----------------------------------------------------------
|
|
|
|
!!****f* DMMin/lateDM *
|
|
!!
|
|
!! NAME
|
|
!! lateDM
|
|
!! USAGE
|
|
!!
|
|
!! PURPOSE
|
|
!! Performs late-stage (linear) minimisation of energy wrt density
|
|
!! matrix elements. Uses the GR-Pulay scheme (described in Chem Phys Lett
|
|
!! 325, 796 (2000)). If the residual (given by the squared norm of the
|
|
!! gradient) increases, this is taken as a sign that linearity has been
|
|
!! broken, and the scheme exits to earlyDM
|
|
!! INPUTS
|
|
!!
|
|
!!
|
|
!! USES
|
|
!!
|
|
!! AUTHOR
|
|
!! D.R.Bowler
|
|
!! CREATION DATE
|
|
!! 11/12/00
|
|
!! MODIFICATION HISTORY
|
|
!! 05/06/2001 dave
|
|
!! Added ROBODoc header and removed stop commands (for cq_abort)
|
|
!! 08/06/2001 dave
|
|
!! Added my_barrier to GenComms use list
|
|
!! Removed my_barrier call (unnecessary) and changed dgsum to gsum
|
|
!! 2004/10/28 drb
|
|
!! Small tidying
|
|
!! 10:09, 13/02/2006 drb
|
|
!! Removed all explicit references to data_ variables and rewrote
|
|
!! in terms of new matrix routines
|
|
!! 2008/03/03 18:32 dave
|
|
!! Removed dsqrt
|
|
!! 2009/07/08 16:41 dave
|
|
!! Introduced atom-based tolerance (just divide by ni_in_cell)
|
|
!! 2010/03/18 14:08 dave
|
|
!! Added code for mixed L and SCF minimisation
|
|
!! 2011/08/25 L.Tong
|
|
!! - Added spin polarisation
|
|
!! - NOTE:
|
|
!! There is a change to the size of residual. The new
|
|
!! residuals are now a factor 2 less than the original
|
|
!! implementation. The actual method for calculating the
|
|
!! residual has not changed. However since the definition of
|
|
!! mat_M3 has changed, now mat_M3(nspin) is an array and
|
|
!! mat_M3(1) always stores the values for spin up component,
|
|
!! this has resulted a changed in the calculated residual.
|
|
!!
|
|
!! For spin non-polarised calculations:
|
|
!! The original implementation has mat_M3 = 2.0 * mat_M3(1)
|
|
!! Hence: g1 = tr(mat_M3*T*mat_M3*T)
|
|
!! = 4.0 * tr(mat_M3(1)*T*mat_M3(1)*T)
|
|
!! Now this has becomes
|
|
!! g1 = sum_{is} tr(mat_M3(is)*T*mat_M3(is)*T)
|
|
!! = 2.0 * tr(mat_M3(1)*T*mat_M3(1)*T)
|
|
!! Hence the factor two difference.
|
|
!! - Removed redundant parameter number_of_bands
|
|
!! 2012/03/12 L.Tong
|
|
!! - Major rewrite for change in spin implementation
|
|
!! - Removed redundant input paramter mu (real, double)
|
|
!! 2012/06/24 L.Tong
|
|
!! - Added control of whether to dump L using flag_dump_L
|
|
!! 2013/03/06 L.Tong
|
|
!! - Changed the min|max step size to minpulaystepDMM and maxpulaystepDMM
|
|
!! instead of hardwired values of 0.001 and 0.1
|
|
!! 2013/08/20 M.Arita
|
|
!! - Changed calls for dumping L-matrix
|
|
!! 2016/07/13 18:30 nakata
|
|
!! Renamed H_on_supportfns -> H_on_atomfns
|
|
!! 2016/08/08 15:30 nakata
|
|
!! Renamed supportfns -> atomfns
|
|
!! 2017/05/09 dave
|
|
!! Added output of L matrix for both spin channels
|
|
!! 2018/11/13 17:30 nakata
|
|
!! Changed matT to be spin_SF dependent
|
|
!! SOURCE
|
|
!!
|
|
subroutine lateDM(ndone, n_L_iterations, done, deltaE, vary_mu, &
|
|
tolerance, record)
|
|
|
|
use datatypes
|
|
use numbers
|
|
use logicals
|
|
use matrix_data, only: Lrange, TLrange
|
|
use mult_module, only: matrix_product, allocate_temp_matrix, &
|
|
free_temp_matrix, &
|
|
LNV_matrix_multiply, matphi, matT, &
|
|
matL, mult, T_L_TL, TL_T_L, &
|
|
matrix_sum, matrix_product_trace, &
|
|
matrix_scale
|
|
use Pulay
|
|
use PosTan, only: PulayR, PulayE, max_iters
|
|
use GenComms, only: cq_abort, gsum
|
|
use global_module, only: IPRINT_TIME_THRES1, &
|
|
ni_in_cell, flag_global_tolerance, &
|
|
flag_mix_L_SC_min, &
|
|
flag_fix_spin_population, nspin, &
|
|
spin_factor, flag_dump_L, &
|
|
flag_SpinDependentSF, min_layer
|
|
use timer_module, only: cq_timer,start_timer, &
|
|
stop_print_timer, WITH_LEVEL
|
|
use io_module, only: dump_matrix, return_prefix
|
|
use functions_on_grid, only: atomfns, H_on_atomfns
|
|
use H_matrix_module, only: get_H_matrix
|
|
use density_module, only: density, get_electronic_density, flag_DumpChargeDensity
|
|
use maxima_module, only: maxngrid
|
|
use memory_module, only: reg_alloc_mem, reg_dealloc_mem, type_dbl
|
|
!Prints out charge density -- 2010.Nov.06 TM
|
|
use io_module, only: dump_charge
|
|
use dimens, only: n_my_grid_points
|
|
use store_matrix, only: dump_pos_and_matrices, unit_DM_save
|
|
|
|
implicit none
|
|
|
|
! Passed variables
|
|
integer :: n_L_iterations, length, ndone
|
|
logical :: vary_mu, done, record
|
|
real(double) :: tolerance, deltaE
|
|
|
|
! Local variables
|
|
integer, dimension(maxpulayDMM,nspin) :: mat_Lstore, mat_Gstore, mat_SGstore
|
|
integer, dimension(nspin) :: matM3, matSM3, matSphi, mat_temp
|
|
real(double), dimension(nspin) :: e_dot_n, n_dot_n, electrons, &
|
|
energy0, energy1
|
|
real(double), dimension(maxpulayDMM) :: alph
|
|
real(double), dimension(maxpulayDMM,maxpulayDMM) :: Aij
|
|
! real(double), dimension(maxpulayDMM*maxpulayDMM) :: Aij1
|
|
real(double) :: e_dot_n_tot, n_dot_n_tot, electrons_tot, &
|
|
energy0_tot, energy1_tot
|
|
real(double) :: g0, g1, gg, step, dsum_factor
|
|
integer :: n_iter, i, j, pul_mx, npmod, spin, spin_SF, stat
|
|
type(cq_timer) :: tmr_l_tmp1,tmr_l_iter
|
|
real(double), dimension(:), allocatable :: density_tot
|
|
!TM
|
|
integer :: iter_stuck = 0
|
|
integer, parameter :: mx_stuck = 5
|
|
character(len=12) :: subname = "lateDM: "
|
|
character(len=120) :: prefix
|
|
|
|
call start_timer(tmr_l_tmp1, WITH_LEVEL)
|
|
prefix = return_prefix(subname, min_layer)
|
|
iter_stuck = 0
|
|
|
|
spin_SF = 1
|
|
|
|
if (ndone > n_L_iterations) &
|
|
call cq_abort('lateDM: too many L iterations', ndone, n_L_iterations)
|
|
|
|
do spin = 1, nspin
|
|
do i = 1, maxpulayDMM
|
|
mat_Lstore(i,spin) = allocate_temp_matrix(Lrange,0)
|
|
mat_Gstore(i,spin) = allocate_temp_matrix(Lrange,0)
|
|
mat_SGstore(i,spin) = allocate_temp_matrix(Lrange,0)
|
|
end do
|
|
matM3(spin) = allocate_temp_matrix(Lrange,0)
|
|
matSM3(spin) = allocate_temp_matrix(Lrange,0)
|
|
matSphi(spin) = allocate_temp_matrix(Lrange,0)
|
|
mat_temp(spin) = allocate_temp_matrix(TLrange,0)
|
|
end do
|
|
|
|
! Update the charge density if flag is set
|
|
min_layer = min_layer - 1
|
|
if (flag_mix_L_SC_min) then
|
|
! 2011/08/29 L.Tong:
|
|
! original also calculates matphi, (dophi), but I think
|
|
! this is redundant. So set dontphi. Only doK.
|
|
call LNV_matrix_multiply(electrons, energy1, doK, dontM1, &
|
|
dontM2, dontM3, dontM4, dontphi, doE)
|
|
energy1_tot = spin_factor * sum(energy1(:))
|
|
|
|
! note H_on_atomfns(1) is used just as a temp working array
|
|
call get_electronic_density(density, electrons, atomfns, &
|
|
H_on_atomfns(1), inode, ionode, &
|
|
maxngrid)
|
|
call get_H_matrix(.true., .false., electrons, density, maxngrid)
|
|
end if
|
|
min_layer = min_layer + 1
|
|
|
|
! Get the gradient at the starting point (?) this updates matM3
|
|
! and matphi
|
|
call LNV_matrix_multiply(electrons, energy0, dontK, dontM1, &
|
|
dontM2, doM3, dontM4, dophi, doE, &
|
|
mat_M3=matM3, mat_phi=matphi)
|
|
electrons_tot = spin_factor * sum(electrons)
|
|
energy0_tot = spin_factor * sum(energy0)
|
|
|
|
! Covariant gradient in SM3
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
call matrix_product(matT(spin_SF), matM3(spin), mat_temp(spin), mult(T_L_TL))
|
|
call matrix_product(mat_temp(spin), matT(spin_SF), matSM3(spin), mult(TL_T_L))
|
|
end do
|
|
|
|
! Project electron gradient out
|
|
if (vary_mu) then
|
|
! update matSphi from matphi
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
call matrix_product(matT(spin_SF), matphi(spin), mat_temp(spin), mult(T_L_TL))
|
|
call matrix_product(mat_temp(spin), matT(spin_SF), matSphi(spin), mult(TL_T_L))
|
|
e_dot_n(spin) = matrix_product_trace(matSM3(spin), matphi(spin))
|
|
n_dot_n(spin) = matrix_product_trace(matSphi(spin), matphi(spin))
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 3) then
|
|
write(io_lun, '(4x,a,i1,") ",f16.6)') &
|
|
trim(prefix)//" e.n (spin=", spin, e_dot_n(spin)
|
|
write(io_lun, '(4x,a,i1,") ",f16.6)') &
|
|
trim(prefix)//" n.n (spin=", spin, n_dot_n(spin)
|
|
end if
|
|
end do
|
|
|
|
! matSM3 mat matM3 are used as search direction (-SG and -G)
|
|
! These should be positive because we SUBTRACT M3 off below
|
|
! Here, we can alter M3 directly because it's not expected
|
|
! to be an exact gradient
|
|
if (nspin == 1 .or. flag_fix_spin_population) then
|
|
do spin = 1, nspin
|
|
! if gradient of N w.r.t. L is zero then no correction needed
|
|
if (abs(n_dot_n(spin)) > RD_ERR) then
|
|
dsum_factor = e_dot_n(spin) / n_dot_n(spin)
|
|
call matrix_sum(one, matSM3(spin), -dsum_factor, matSphi(spin))
|
|
call matrix_sum(one, matM3(spin), -dsum_factor, matphi(spin))
|
|
end if
|
|
end do
|
|
else
|
|
e_dot_n_tot = spin_factor * sum(e_dot_n)
|
|
n_dot_n_tot = spin_factor * sum(n_dot_n)
|
|
! if gradient of N w.r.t. L is zero then no correction needed
|
|
if (abs(n_dot_n_tot) > RD_ERR) then
|
|
dsum_factor = e_dot_n_tot / n_dot_n_tot
|
|
do spin = 1, nspin
|
|
call matrix_sum(one, matSM3(spin), -dsum_factor, matSphi(spin))
|
|
call matrix_sum(one, matM3(spin), -dsum_factor, matphi(spin))
|
|
end do
|
|
end if
|
|
end if
|
|
end if ! (vary_mu)
|
|
! Store initial gradient (these are search directions G and L)
|
|
do spin = 1, nspin
|
|
call matrix_sum(zero, mat_SGstore(1,spin), -one, matSM3(spin))
|
|
call matrix_sum(zero, mat_Gstore(1,spin), -one, matM3(spin))
|
|
call matrix_sum (zero, mat_Lstore(1,spin), one, matL(spin))
|
|
end do
|
|
|
|
! timer
|
|
call stop_print_timer (tmr_l_tmp1, "lateDM - Preliminaries", &
|
|
IPRINT_TIME_THRES1)
|
|
|
|
!-----------!
|
|
! MAIN LOOP !
|
|
!-----------!
|
|
|
|
if (flag_global_tolerance) then
|
|
g0 = zero
|
|
do spin = 1, nspin
|
|
g0 = g0 + spin_factor * &
|
|
matrix_product_trace(matM3(spin), matSM3(spin))
|
|
end do
|
|
else
|
|
g0 = zero
|
|
do spin = 1, nspin
|
|
g0 = g0 + spin_factor * &
|
|
matrix_product_trace(matM3(spin), matSM3(spin)) / &
|
|
real(ni_in_cell, double)
|
|
end do
|
|
end if
|
|
if(n_L_iterations==0) done = .true. ! Otherwise the calling routine will cycle
|
|
do n_iter = 1, n_L_iterations
|
|
call start_timer(tmr_l_iter, WITH_LEVEL)
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 1) &
|
|
write(io_lun, fmt='(4x,a,i3," Energy: ",f16.6," Ha Residual: ",e16.6)') &
|
|
trim(prefix)//" iteration: ",n_iter, energy0_tot, g0
|
|
! Storage for pulay DMs/residuals
|
|
npmod = mod(n_iter, maxpulayDMM) + 1
|
|
pul_mx = min(n_iter + 1, maxpulayDMM)
|
|
! Take a step - maybe correct electron number after
|
|
if (flag_global_tolerance) then
|
|
! Base step on present gradient and expected dE
|
|
step = deltaE / (g0 + RD_ERR)
|
|
else
|
|
! Base step on present gradient and expected dE
|
|
step = deltaE / (real(ni_in_cell, double) * g0 + RD_ERR)
|
|
end if
|
|
step = abs(step)
|
|
! We don't want the step to be too small or too big
|
|
if (abs(step) < minpulaystepDMM) step = minpulaystepDMM
|
|
if (abs(step) > maxpulaystepDMM) step = maxpulaystepDMM
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 3) &
|
|
write(io_lun, '(4x,a,i3,i3,f16.6)') &
|
|
trim(prefix)//" npmod, pul_mx and step: ", npmod, pul_mx, step
|
|
! take L_n+1 = L_n + step * G_n
|
|
if (npmod > 1) then
|
|
do spin = 1, nspin
|
|
call matrix_sum(zero, matL(spin), one, mat_Lstore(npmod-1,spin))
|
|
call matrix_sum(one, matL(spin), step, mat_SGstore(npmod-1,spin))
|
|
end do
|
|
else
|
|
do spin = 1, nspin
|
|
call matrix_sum(zero, matL(spin), one, mat_Lstore(pul_mx,spin))
|
|
call matrix_sum(one, matL(spin), step, mat_SGstore(pul_mx,spin))
|
|
end do
|
|
endif
|
|
! after the step, correct the electron number
|
|
min_layer = min_layer - 1
|
|
if (vary_mu) call correct_electron_number
|
|
min_layer = min_layer + 1
|
|
! Re-evaluate gradient and energy
|
|
call LNV_matrix_multiply(electrons, energy1, dontK, dontM1, &
|
|
dontM2, doM3, dontM4, dophi, doE, &
|
|
mat_M3=matM3, mat_phi=matphi)
|
|
electrons_tot = spin_factor * sum(electrons)
|
|
energy1_tot = spin_factor * sum(energy1)
|
|
! Covariant gradient in SM3
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
call matrix_product(matT(spin_SF), matM3(spin), mat_temp(spin), mult(T_L_TL))
|
|
call matrix_product(mat_temp(spin), matT(spin_SF), matSM3(spin), mult(TL_T_L))
|
|
end do
|
|
! Project out electron variation
|
|
if (vary_mu) then
|
|
! update matSphi
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
call matrix_product(matT(spin_SF), matphi(spin), mat_temp(spin), &
|
|
mult(T_L_TL))
|
|
call matrix_product(mat_temp(spin), matT(spin_SF), matSphi(spin), &
|
|
mult(TL_T_L))
|
|
e_dot_n(spin) = matrix_product_trace(matSM3(spin), matphi(spin))
|
|
n_dot_n(spin) = matrix_product_trace(matSphi(spin), matphi(spin))
|
|
end do
|
|
if (flag_fix_spin_population) then
|
|
do spin = 1, nspin
|
|
! if gradient of N w.r.t. L is zero then no correction needed
|
|
if (abs(n_dot_n(spin)) > RD_ERR) then
|
|
dsum_factor = e_dot_n(spin) / n_dot_n(spin)
|
|
call matrix_sum(one, matSM3(spin), -dsum_factor, &
|
|
matSphi(spin))
|
|
call matrix_sum(one, matM3(spin), -dsum_factor, &
|
|
matphi(spin))
|
|
end if
|
|
end do
|
|
else
|
|
e_dot_n_tot = spin_factor * sum(e_dot_n)
|
|
n_dot_n_tot = spin_factor * sum(n_dot_n)
|
|
! if gradient of N w.r.t. L is zero then no correction needed
|
|
if (abs(n_dot_n_tot) > RD_ERR) then
|
|
dsum_factor = e_dot_n_tot / n_dot_n_tot
|
|
do spin = 1, nspin
|
|
call matrix_sum (one, matSM3(spin), -dsum_factor, &
|
|
matSphi(spin))
|
|
call matrix_sum (one, matM3(spin), -dsum_factor, &
|
|
matphi(spin))
|
|
end do
|
|
end if
|
|
end if
|
|
end if
|
|
! 2022/10/28 16:01 dave
|
|
! I'm not sure that we need this calculation of residual
|
|
! Find the residual (i.e. the gradient)
|
|
if (flag_global_tolerance) then
|
|
gg = zero
|
|
do spin = 1, nspin
|
|
gg = gg + spin_factor * &
|
|
matrix_product_trace(matM3(spin), matSM3(spin))
|
|
end do
|
|
else
|
|
gg = zero
|
|
do spin = 1, nspin
|
|
gg = gg + spin_factor * &
|
|
matrix_product_trace(matM3(spin), matSM3(spin)) / &
|
|
real(ni_in_cell, double)
|
|
end do
|
|
end if
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a,e16.6)') trim(prefix)//" R2 is ", sqrt(gg)
|
|
! record Pulay histories
|
|
do spin = 1, nspin
|
|
call matrix_sum(zero, mat_SGstore(npmod,spin), -one, matSM3(spin))
|
|
call matrix_sum(zero, mat_Gstore(npmod,spin), -one, matM3(spin))
|
|
call matrix_sum(zero, mat_Lstore(npmod,spin), one, matL(spin))
|
|
end do
|
|
! calculate A_ij
|
|
Aij = zero
|
|
do i = 1, pul_mx
|
|
do j = 1, pul_mx
|
|
gg = zero
|
|
do spin = 1, nspin
|
|
gg = gg + spin_factor * &
|
|
matrix_product_trace(mat_Gstore(j,spin),&
|
|
mat_SGstore(i,spin))
|
|
end do
|
|
Aij(j,i) = gg
|
|
! Aij1(j + (i-1)*pul_mx) = gg
|
|
end do ! j
|
|
end do ! i
|
|
! Solve to get alphas
|
|
call DoPulay(npmod, Aij, alph, pul_mx, maxpulayDMM)
|
|
! Make new L matrix from Pulay sum
|
|
do spin = 1, nspin
|
|
call matrix_scale(zero, matL(spin))
|
|
do i = 1, pul_mx
|
|
call matrix_sum (one, matL(spin), alph(i), mat_Lstore(i,spin))
|
|
end do
|
|
end do
|
|
! after the step, correct the electron number
|
|
min_layer = min_layer - 1
|
|
if (vary_mu) call correct_electron_number
|
|
if (flag_mix_L_SC_min) then
|
|
! 2011/08/29 L.Tong
|
|
! the original also has dophi, but I think this is redundant
|
|
call LNV_matrix_multiply (electrons, energy1, doK, dontM1, &
|
|
dontM2, dontM3, dontM4, dontphi, doE)
|
|
! H_on_atomfns(1) is used just as a temp working array
|
|
call get_electronic_density(density, electrons, &
|
|
atomfns, &
|
|
H_on_atomfns(1), inode, &
|
|
ionode, maxngrid)
|
|
call get_H_matrix(.true., .false., electrons, density, maxngrid)
|
|
end if
|
|
min_layer = min_layer + 1
|
|
! re-evaluate the gradient and energy at new position
|
|
call LNV_matrix_multiply(electrons, energy1, dontK, dontM1, &
|
|
dontM2, doM3, dontM4, dophi, doE, &
|
|
mat_M3=matM3, mat_phi=matphi)
|
|
energy1_tot = spin_factor * sum(energy1)
|
|
electrons_tot = spin_factor * sum(electrons)
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
call matrix_product(matT(spin_SF), matM3(spin), mat_temp(spin), mult(T_L_TL))
|
|
call matrix_product(mat_temp(spin), matT(spin_SF), matSM3(spin), mult(TL_T_L))
|
|
end do
|
|
if (flag_global_tolerance) then
|
|
g1 = zero
|
|
do spin = 1, nspin
|
|
g1 = g1 + spin_factor * &
|
|
matrix_product_trace(matM3(spin), matSM3(spin))
|
|
end do
|
|
else
|
|
g1 = zero
|
|
do spin = 1, nspin
|
|
g1 = g1 + spin_factor * &
|
|
matrix_product_trace(matM3(spin), matSM3(spin)) / &
|
|
real(ni_in_cell, double)
|
|
end do
|
|
end if
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a,e16.6)') &
|
|
trim(prefix)//" Residual before electron gradient correction: ", g1
|
|
if (vary_mu) then
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
call matrix_product(matT(spin_SF), matphi(spin), &
|
|
mat_temp(spin), mult(T_L_TL))
|
|
call matrix_product(mat_temp(spin), matT(spin_SF), &
|
|
matSphi(spin), mult(TL_T_L))
|
|
e_dot_n(spin) = matrix_product_trace(matSM3(spin), matphi(spin))
|
|
n_dot_n(spin) = matrix_product_trace(matSphi(spin), matphi(spin))
|
|
end do
|
|
if (flag_fix_spin_population) then
|
|
do spin = 1, nspin
|
|
! if gradient of N w.r.t. L is zero then no correction needed
|
|
if (abs(n_dot_n(spin)) > RD_ERR) then
|
|
dsum_factor = e_dot_n(spin) / n_dot_n(spin)
|
|
call matrix_sum(one, matSM3(spin), -dsum_factor, &
|
|
matSphi(spin))
|
|
call matrix_sum(one, matM3(spin), -dsum_factor, &
|
|
matphi(spin))
|
|
end if
|
|
end do
|
|
else
|
|
e_dot_n_tot = spin_factor * sum(e_dot_n)
|
|
n_dot_n_tot = spin_factor * sum(n_dot_n)
|
|
! if gradient of N w.r.t. L is zero then no correction needed
|
|
if (abs(n_dot_n_tot) > RD_ERR) then
|
|
dsum_factor = e_dot_n_tot / n_dot_n_tot
|
|
do spin = 1, nspin
|
|
call matrix_sum(one, matSM3(spin), -dsum_factor, &
|
|
matSphi(spin))
|
|
call matrix_sum(one, matM3(spin), -dsum_factor, &
|
|
matphi(spin))
|
|
end do
|
|
end if
|
|
end if
|
|
end if
|
|
! get deltaE
|
|
deltaE = energy1_tot - energy0_tot
|
|
dE_DMM = deltaE
|
|
! Find the residual
|
|
if (flag_global_tolerance) then
|
|
g1 = zero
|
|
do spin = 1, nspin
|
|
g1 = g1 + spin_factor * &
|
|
matrix_product_trace(matM3(spin), matSM3(spin))
|
|
end do
|
|
else
|
|
g1 = zero
|
|
do spin = 1, nspin
|
|
g1 = g1 + spin_factor * &
|
|
matrix_product_trace(matM3(spin), matSM3(spin)) / &
|
|
real(ni_in_cell, double)
|
|
end do
|
|
end if
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a,e16.6)') trim(prefix)//" New residual: ", g1
|
|
if ((ndone + n_iter) < max_iters) then
|
|
PulayR(ndone + n_iter) = g1
|
|
PulayE(ndone + n_iter) = energy1_tot
|
|
end if
|
|
|
|
! dump the L matrix if required
|
|
if (n_dumpL>0) then
|
|
if(mod (n_iter, n_dumpL) == 0) call dump_pos_and_matrices(index=unit_DM_save)
|
|
endif
|
|
!if (flag_dump_L) then
|
|
! if (mod (n_iter, n_dumpL) == 0) then
|
|
! call dump_pos_and_matrices
|
|
! end if
|
|
!end if
|
|
|
|
! check if tolerance is reached
|
|
if (g1 < tolerance) then
|
|
done = .true.
|
|
ndone = n_iter
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 0) &
|
|
write(io_lun, '(/4x,a,e16.6,a,i3,a/)') &
|
|
trim(prefix)//" reached residual of ", g1, " after ",n_iter, " iterations"
|
|
call dealloc_lateDM
|
|
call stop_print_timer(tmr_l_iter, "a lateDM iteration", &
|
|
IPRINT_TIME_THRES1)
|
|
return
|
|
else if ((.not. flag_mix_L_SC_min) .and. (g1 > two * g0)) then
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 0) &
|
|
write(io_lun, '(/4x,a,i3,a,f16.6,a,f16.6)') &
|
|
trim(prefix)//" residual rise after ", n_iter, &
|
|
" iterations with energy and residual: ", energy1_tot, " Ha ", g1
|
|
ndone = n_iter
|
|
call dealloc_lateDM
|
|
call stop_print_timer(tmr_l_iter, "a lateDM iteration", &
|
|
IPRINT_TIME_THRES1)
|
|
return
|
|
else if (g1 > 0.99_double * g0) then
|
|
iter_stuck = iter_stuck + 1
|
|
if (iter_stuck > mx_stuck) then
|
|
done = .false.
|
|
ndone = n_iter
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 0) &
|
|
write(io_lun, '(/4x,a,i3,a,f16.6,a,e16.6)') &
|
|
trim(prefix)//" reset since failed to reduce residual after ",n_iter, &
|
|
" iterations with energy and residual: ", energy1_tot, " Ha ", g1
|
|
call dealloc_lateDM
|
|
call stop_print_timer(tmr_l_iter, "a lateDM iteration", &
|
|
IPRINT_TIME_THRES1)
|
|
return
|
|
end if ! (iter_stuck > mx_stuck)
|
|
end if
|
|
! Replace step with that calculated from real L, and prepare
|
|
! for next step
|
|
do spin = 1, nspin
|
|
call matrix_sum(zero, mat_SGstore(npmod,spin), -one, matSM3(spin))
|
|
call matrix_sum(zero, mat_Gstore(npmod,spin), -one, matM3(spin))
|
|
call matrix_sum(zero, mat_Lstore(npmod,spin), one, matL(spin))
|
|
end do
|
|
g0 = g1
|
|
energy0_tot = energy1_tot
|
|
call stop_print_timer(tmr_l_iter, "a lateDM iteration", &
|
|
IPRINT_TIME_THRES1)
|
|
end do
|
|
|
|
!Prints out charge density when selected
|
|
if (flag_DumpChargeDensity .and. flag_mix_L_SC_min) then
|
|
if (nspin == 1) then
|
|
allocate(density_tot(maxngrid), STAT=stat)
|
|
if (stat /= 0) call cq_abort("Error allocating density_tot: ", maxngrid)
|
|
call reg_alloc_mem(area_DM, maxngrid, type_dbl)
|
|
density_tot = zero
|
|
density_tot = spin_factor * density(:,1)
|
|
call dump_charge(density_tot, n_my_grid_points, inode)
|
|
deallocate(density_tot, STAT=stat)
|
|
if (stat /= 0) call cq_abort("Error deallocating density_tot")
|
|
call reg_dealloc_mem(area_DM, maxngrid, type_dbl)
|
|
|
|
else
|
|
call dump_charge(density(:,1), n_my_grid_points, inode, spin=1)
|
|
call dump_charge(density(:,2), n_my_grid_points, inode, spin=2)
|
|
end if
|
|
endif ! (flag_mix_L_SC_min) then
|
|
|
|
call dealloc_lateDM
|
|
|
|
return
|
|
|
|
contains
|
|
|
|
subroutine dealloc_lateDM
|
|
do spin = nspin, 1, -1
|
|
call free_temp_matrix(mat_temp(spin))
|
|
call free_temp_matrix(matSphi(spin))
|
|
call free_temp_matrix(matSM3(spin))
|
|
call free_temp_matrix(matM3(spin))
|
|
do i = maxpulayDMM, 1, -1
|
|
call free_temp_matrix(mat_SGstore(i,spin))
|
|
call free_temp_matrix(mat_Gstore(i,spin))
|
|
call free_temp_matrix(mat_Lstore(i,spin))
|
|
end do
|
|
end do
|
|
return
|
|
end subroutine dealloc_lateDM
|
|
|
|
end subroutine lateDM
|
|
!!***
|
|
|
|
|
|
|
|
! -----------------------------------------------------------
|
|
! Subroutine lineMinL
|
|
! -----------------------------------------------------------
|
|
|
|
!!****f* DMMin/lineMinL_nospin *
|
|
!!
|
|
!! NAME
|
|
!! lineMinL_nospin
|
|
!! USAGE
|
|
!!
|
|
!! PURPOSE
|
|
!! Performs an analytical line minimisation in the direction given by
|
|
!! mat_D. Uses energy and gradient at 0 and step (where step is given by
|
|
!! the previous energy change) as the four pieces of information needed to
|
|
!! get the cubic coefficients analytically. If the cubic has imaginary
|
|
!! maxima and minima, the solution is a point of inflexion, and we panic
|
|
!! INPUTS
|
|
!!
|
|
!!
|
|
!! USES
|
|
!!
|
|
!! AUTHOR
|
|
!! D.R.Bowler (based on original by CMG and EHH)
|
|
!! CREATION DATE
|
|
!! 11/12/00
|
|
!! MODIFICATION HISTORY
|
|
!! 05/06/2001 dave
|
|
!! Added ROBODoc header
|
|
!! 08/06/2001 dave
|
|
!! Changed dgsum to gsum
|
|
!! 2004/10/28 drb
|
|
!! Fixed (partly ?) definition of linearity and changed to return
|
|
!! zeta in interpG
|
|
!! 10:09, 13/02/2006 drb
|
|
!! Removed all explicit references to data_ variables and
|
|
!! rewrote in terms of new matrix routines
|
|
!! 2011/08/23 L.Tong
|
|
!! Fixed a possible bug, if A=0, then the solution for true step should be
|
|
!! truestep = -C/2B instead of C/2B. Because we are solving the equation
|
|
!! 3Ax^2 + 2Bx + C = 0, and if A = 0, x = -C/2B.
|
|
!! 2012/03/01 L.Tong
|
|
!! renamed to lineMinL_nospin
|
|
!! 2012/03/12 L.Tong
|
|
!! - Major rewrite to include spin implmentation. Now spin
|
|
!! polarised and non-polarised calculations share the same
|
|
!! subroutine.
|
|
!! - Renamed to lineMinL
|
|
!! 2018/11/13 17:30 nakata
|
|
!! Changed matT to be spin_SF dependent
|
|
!! SOURCE
|
|
!!
|
|
subroutine lineMinL( matM3, mat_D, mat_temp, matSM3, &
|
|
energy_in, energy_out, delta_e, &
|
|
inflex, interpG)
|
|
|
|
use datatypes
|
|
use numbers
|
|
use logicals
|
|
use matrix_data, only: Lrange
|
|
use mult_module, only: matrix_sum, matrix_product_trace, &
|
|
allocate_temp_matrix, free_temp_matrix, &
|
|
matrix_product, matT, matL, &
|
|
LNV_matrix_multiply, mult, T_L_TL, &
|
|
TL_T_L, symmetrise_L
|
|
use global_module, only: flag_fix_spin_population, nspin, &
|
|
spin_factor, flag_SpinDependentSF, min_layer
|
|
use io_module, only: return_prefix
|
|
|
|
implicit none
|
|
|
|
! Passed variables
|
|
integer, dimension(:) :: matM3, matSM3, mat_D, mat_temp
|
|
logical :: inflex
|
|
real(double) :: delta_e, energy_in, energy_out, interpG
|
|
|
|
! Local variables
|
|
integer, dimension(nspin) :: matM3old
|
|
real(double), dimension(nspin) :: electrons_spin, energy_1_spin, energy_2_spin
|
|
real(double) :: electrons, energy_0, energy_1
|
|
real(double) :: step, truestep, directsum_step
|
|
real(double) :: A, B, C, D, SQ
|
|
real(double) :: g0, g1, ig0, ig1, ig1_step, igcross, igcross2
|
|
real(double) :: lamG, zeta
|
|
integer :: i, j, k, length, spin, spin_SF
|
|
character(len=12) :: subname = "lineMinL: "
|
|
character(len=120) :: prefix
|
|
|
|
prefix = return_prefix(subname, min_layer)
|
|
spin_SF = 1
|
|
g0 = zero
|
|
ig0 = zero
|
|
do spin = 1, nspin
|
|
matM3old(spin) = allocate_temp_matrix(Lrange, 0)
|
|
! copy matM3(L_old)
|
|
call matrix_sum(zero, matM3old(spin), one, matM3(spin))
|
|
! calculate g0 = tr(matD * matM3), trace of direct sum is sum of
|
|
! traces of spin components
|
|
g0 = g0 + spin_factor * &
|
|
matrix_product_trace(mat_D(spin), matM3(spin))
|
|
|
|
! calculate ig0
|
|
! L.Tong: WARNING! I just copied the approach from
|
|
! lineMinL_nospin, but I think this may be a mistake, and should
|
|
! be: ig0 = tr(matSM3 * matM3), not tr(mat_D, matM3)
|
|
|
|
ig0 = ig0 + spin_factor * &
|
|
matrix_product_trace(mat_D(spin), matM3(spin))
|
|
! ig0 = ig0 + spin_factor * &
|
|
! matrix_product_trace(matSM3(spin), matM3(spin))
|
|
end do
|
|
|
|
! We have a cubic in energy with L_out = L + x.D
|
|
! energy at x = 0 is energy_in
|
|
energy_0 = energy_in
|
|
|
|
! The intermediate step size should depend on expected energy reduction
|
|
step = delta_e / g0
|
|
if (abs(step) < 1.0e-2_double) step = 0.01_double
|
|
if (abs(step) > 0.1_double) step = 0.1_double
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a,f16.6)') trim(prefix)//" trial step is ",step
|
|
|
|
! now we take the step in the direction D
|
|
do spin = 1, nspin
|
|
call matrix_sum(one, matL(spin), step, mat_D(spin))
|
|
end do
|
|
|
|
! calcuate matM3, energy and numbers of electrons again for L_step
|
|
call LNV_matrix_multiply(electrons_spin, energy_1_spin, doK, &
|
|
dontM1, dontM2, doM3, dontM4, dontphi, &
|
|
doE, mat_M3=matM3)
|
|
energy_1 = spin_factor * sum(energy_1_spin(:))
|
|
|
|
! g1 = tr(mat_D(L_old) * matM3(L_step))
|
|
g1 = zero
|
|
do spin = 1, nspin
|
|
g1 = g1 + spin_factor * &
|
|
matrix_product_trace(mat_D(spin), matM3(spin))
|
|
end do
|
|
|
|
! Evaluate old and new gradient cross and new gradient
|
|
! magnitude. This is for linear interpolation.
|
|
! igcross = tr(matSM3(L_old) * matM3(L_step))
|
|
igcross = zero
|
|
do spin = 1, nspin
|
|
igcross = igcross + spin_factor * &
|
|
matrix_product_trace(matSM3(spin), matM3(spin))
|
|
end do
|
|
|
|
! get the matSM3(L_step)
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
call matrix_product(matT(spin_SF), matM3(spin), mat_temp(spin), mult(T_L_TL))
|
|
call matrix_product(mat_temp(spin), matT(spin_SF), matSM3(spin), mult(TL_T_L))
|
|
end do
|
|
|
|
! get ig1 = tr(matSM3(L_step) * matM3(L_step)),
|
|
! this is for linear interpolation
|
|
ig1 = zero
|
|
do spin = 1, nspin
|
|
ig1 = ig1 + spin_factor * &
|
|
matrix_product_trace(matSM3(spin), matM3(spin))
|
|
end do
|
|
! remember the content of ig1 as it will be used to store other values
|
|
ig1_step = ig1
|
|
|
|
! the cubic is then given by Ax^3+Bx^2+Cx+D with
|
|
D = energy_0
|
|
C = g0
|
|
B = three * (energy_1 - D) / (step * step) - (g1 - two * C) / step
|
|
A = (g1 - two * B * step - C) / (three * step * step)
|
|
! check for -ve square root
|
|
SQ = four * B * B - 12.0_double * A * C
|
|
|
|
if (SQ < 0) then
|
|
! point of inflexion - problem !
|
|
if (inode == ionode .and. iprint_DM + min_layer > 0) then
|
|
write(io_lun, fmt='(4x,a)') trim(prefix)//' inflexion approximation:'
|
|
write(io_lun, fmt='(4x,a,4f16.6)') trim(prefix)//' A, B, C, D: ', A, B, C, D
|
|
end if
|
|
inflex = .true.
|
|
do spin = nspin, 1, -1
|
|
call free_temp_matrix(matM3old(spin))
|
|
end do
|
|
return
|
|
else ! Solve cubic
|
|
if (abs(A) > RD_ERR) then
|
|
truestep = (-two * B + SQRT(SQ)) / (six * A)
|
|
else
|
|
if (inode == ionode .and. iprint_DM + min_layer >2) &
|
|
write(io_lun, fmt='(4x,a)') trim(prefix)//' local quadratic approximation'
|
|
! L.Tong shouldn this be -C/(two*B) instead of the orginal
|
|
! C/(two * B) ?
|
|
! truestep = C / (two * B)
|
|
truestep = - C / (two * B)
|
|
end if
|
|
end if ! if (SQ < 0) then
|
|
if (inode == ionode .and. iprint_DM + min_layer>= 2) &
|
|
write(io_lun, '(4x,a,f16.6)') trim(prefix)//" actual step is ", truestep
|
|
|
|
!TM 09/09/2003
|
|
if (truestep < 1.e-04_double) truestep = 1.e-04_double
|
|
!TM 09/09/2003
|
|
|
|
! update value of matL
|
|
directsum_step = truestep - step
|
|
do spin = 1, nspin
|
|
call matrix_sum(one, matL(spin), directsum_step, mat_D(spin))
|
|
end do
|
|
|
|
! matrix symmetric: this avoids the creeping in of asymmetries due
|
|
! to accumulation of errors
|
|
call symmetrise_L()
|
|
|
|
! Get K(L_truestep), E(L_truestep) and M3(L_truestep) before we return
|
|
call LNV_matrix_multiply(electrons_spin, energy_2_spin, doK, &
|
|
dontM1, dontM2, doM3, dontM4, dontphi, &
|
|
doE, mat_M3=matM3)
|
|
energy_out = spin_factor * sum(energy_2_spin(:))
|
|
delta_e = energy_out - energy_in
|
|
|
|
!-----------------------------------------------------------------------
|
|
! finished calculating energy_out, delta_e, matM3, inflex
|
|
!-----------------------------------------------------------------------
|
|
|
|
! Find interpolated gradient for linearity test (find interpG/zeta)
|
|
if (truestep < step) then
|
|
! We need to compare interpG with g at truestep
|
|
lamG = truestep / step
|
|
interpG = ig0 * (one - lamG) * (one - lamG) + ig1 * lamG * lamG + &
|
|
two * igcross * lamG * (one - lamG)
|
|
zeta = (interpG - ig1) / (ig0 - ig1)
|
|
else ! truestep is GREATER than step
|
|
! We need to compare interpG with g at step
|
|
! find matSM3(L_truestep)
|
|
ig1 = zero
|
|
igcross = zero
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
call matrix_product(matT(spin_SF), matM3(spin), mat_temp(spin), mult(T_L_TL))
|
|
call matrix_product(mat_temp(spin), matT(spin_SF), matSM3(spin), mult(TL_T_L))
|
|
! ig1 is now used to store tr(matSM3(L_truestep) * matM3(L_truestep))
|
|
! ig1_step stores tr(matSM3(L_step) * matM3(L_step))
|
|
ig1 = ig1 + spin_factor * &
|
|
matrix_product_trace(matSM3(spin), matM3(spin))
|
|
igcross = igcross + spin_factor * &
|
|
matrix_product_trace(matSM3(spin), matM3old(spin))
|
|
end do
|
|
lamG = step / truestep
|
|
interpG = ig0 * (one - lamG) * (one - lamG) + ig1 * lamG * lamG + &
|
|
two * igcross * lamG * (one - lamG)
|
|
zeta = (interpG - ig1_step) / (ig0 - ig1_step)
|
|
end if
|
|
! returns interpG as zeta
|
|
interpG = zeta
|
|
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) then
|
|
do spin = 1, nspin
|
|
write(io_lun, '(4x,a,i1," is: ",f16.6)') &
|
|
trim(prefix)//" energy_1 for spin = ", spin, energy_1_spin(spin)
|
|
end do
|
|
write(io_lun, '(4x,a,f16.6)') trim(prefix)//" energy_1 overall is: ", energy_1
|
|
end if
|
|
|
|
! deallocate matrix
|
|
do spin = nspin, 1, -1
|
|
call free_temp_matrix(matM3old(spin))
|
|
end do
|
|
|
|
return
|
|
end subroutine lineMinL
|
|
!!***
|
|
|
|
|
|
!!****f* DMMin/correct_electron_number
|
|
!! PURPOSE
|
|
!! Selector subroutine for correct_electron_number
|
|
!! INPUTS
|
|
!! OUTPUT
|
|
!! RETURN VALUE
|
|
!! AUTHOR
|
|
!! L.Tong
|
|
!! CREATION DATE
|
|
!! 2011/08/11
|
|
!! MODIFICATION HISTORY
|
|
!! 2012/03/12 L.Tong
|
|
!! - rewrite for changed spin implementation
|
|
!! 2015/06/08 lat
|
|
!! - Added experimental backtrace
|
|
!! SOURCE
|
|
!!
|
|
subroutine correct_electron_number
|
|
|
|
use mult_module, only: matL, matphi
|
|
use global_module, only: nspin, flag_fix_spin_population
|
|
|
|
implicit none
|
|
|
|
! Local variables
|
|
type(cq_timer) :: backtrace_timer
|
|
integer :: backtrace_level
|
|
|
|
if (nspin == 1 .or. flag_fix_spin_population) then
|
|
call correct_electron_number_fixspin
|
|
else
|
|
call correct_electron_number_varspin
|
|
end if
|
|
|
|
return
|
|
end subroutine correct_electron_number
|
|
!!*****
|
|
|
|
|
|
!!****f* DMMin/correct_electron_number_fixspin
|
|
!! PURPOSE
|
|
!! correct_electron_numbers for each spin component, treating
|
|
!! the spin populations fixed
|
|
!! INPUTS
|
|
!! OUTPUT
|
|
!! RETURN VALUE
|
|
!! AUTHOR
|
|
!! L.Tong
|
|
!! CREATION DATE
|
|
!! 2011/08/11
|
|
!! MODIFICATION HISTORY
|
|
!! 2012/03/11 L.Tong
|
|
!! - Major rewrite, the subroutine corrects electron number for
|
|
!! both spin channels.
|
|
!! 2018/11/13 17:30 nakata
|
|
!! Changed matT and matTtran to be spin_SF dependent
|
|
!! SOURCE
|
|
!!
|
|
subroutine correct_electron_number_fixspin
|
|
|
|
use datatypes
|
|
use logicals
|
|
use numbers
|
|
use matrix_data, only: TLrange, Lrange
|
|
use mult_module, only: matT, matTtran, matL, matphi, &
|
|
allocate_temp_matrix, free_temp_matrix, &
|
|
matrix_product_trace, matrix_product, &
|
|
matrix_sum, mult, T_L_TL, TL_T_L, &
|
|
LNV_matrix_multiply, matrix_transpose
|
|
use primary_module, only: bundle
|
|
use GenComms, only: gsum, cq_abort
|
|
use global_module, only: ne_spin_in_cell, nspin, spin_factor, &
|
|
nspin_SF, flag_SpinDependentSF, min_layer
|
|
use io_module, only: return_prefix
|
|
|
|
implicit none
|
|
|
|
! Local variables
|
|
real(double) :: step, step1, g0, g1, recA, B, C, D, truestep, dne
|
|
integer :: iter, spin, spin_SF
|
|
logical :: done
|
|
real(double), dimension(nspin) :: electrons_0, electrons_2, &
|
|
electrons, energy
|
|
integer, dimension(nspin) :: matTL, matphi2, matSphi, matSphi2
|
|
character(len=20) :: subname = "correct_Ne: "
|
|
character(len=120) :: prefix
|
|
|
|
prefix = return_prefix(subname, min_layer)
|
|
spin_SF = 1
|
|
|
|
! set electrons_0 to the correct electron number
|
|
electrons_0(1:nspin) = ne_spin_in_cell(1:nspin)
|
|
|
|
call matrix_transpose(matT(1), matTtran(1))
|
|
if (nspin_SF==2) call matrix_transpose(matT(2), matTtran(2))
|
|
do spin = 1, nspin
|
|
done = .false.
|
|
iter = 0
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
! allocate temporary matrices
|
|
matTL(spin) = allocate_temp_matrix(TLrange,0)
|
|
matphi2(spin) = allocate_temp_matrix(Lrange,0)
|
|
matSphi(spin) = allocate_temp_matrix(Lrange,0)
|
|
matSphi2(spin) = allocate_temp_matrix(Lrange,0)
|
|
do while (.not. done .and. (iter < 20))
|
|
! get electron number and gradient
|
|
call LNV_matrix_multiply(electrons, energy, dontK, dontM1, &
|
|
dontM2, dontM3, dontM4, dophi, dontE, &
|
|
mat_phi=matphi, spin=spin)
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a,i1,") before correction: ",f16.6)') &
|
|
trim(prefix)//" electron number (spin=", spin, electrons(spin)
|
|
call matrix_product(matT(spin_SF), matphi(spin), matTL(spin), mult(T_L_TL))
|
|
call matrix_product(matTL(spin), matTtran(spin_SF), matSphi(spin), mult(TL_T_L))
|
|
g0 = matrix_product_trace(matSphi(spin), matphi(spin))
|
|
! initial guess is linear correction...
|
|
step1 = (electrons_0(spin) - electrons(spin)) / g0
|
|
step = 0.1_double
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a,i1,") are ",f16.6,e16.6)') &
|
|
trim(prefix)//" g0, step1 (spin=", spin, g0, step1
|
|
if (abs(electrons_0(spin) - electrons(spin)) < 1.0e-9_double) then
|
|
call matrix_sum(one, matL(spin), step1, matSphi(spin))
|
|
! check that electron number is correct
|
|
if (iprint_DM + min_layer >= 2) then
|
|
call LNV_matrix_multiply(electrons_2, energy, dontK, &
|
|
dontM1, dontM2, dontM3, dontM4, &
|
|
dophi, dontE, mat_phi=matphi, &
|
|
spin=spin)
|
|
if (inode == ionode) &
|
|
write(io_lun, '(4x,a,i1,") after correction: ",f15.6)') &
|
|
trim(prefix)//" electron number (spin=", spin, electrons_2(spin)
|
|
end if
|
|
done = .true.
|
|
else
|
|
step = step1
|
|
! Take a step along the electron gradient
|
|
call matrix_sum(one, matL(spin), step1, matSphi(spin))
|
|
call LNV_matrix_multiply(electrons_2, energy, dontK, &
|
|
dontM1, dontM2, dontM3, dontM4, &
|
|
dophi, dontE, mat_phi=matphi2, &
|
|
spin=spin)
|
|
call matrix_product(matT(spin_SF), matphi2(spin), matTL(spin), &
|
|
mult(T_L_TL))
|
|
call matrix_product(matTL(spin), matTtran(spin_SF), &
|
|
matSphi2(spin), mult(TL_T_L))
|
|
g1 = matrix_product_trace(matphi(spin), matSphi2(spin))
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a,i1,") are ",2f16.6)') &
|
|
trim(prefix)//" g1, elec2 (spin=", spin, g1, electrons_2(spin)
|
|
! get coefficients of polynomial
|
|
D = electrons(spin) - electrons_0(spin)
|
|
C = g0
|
|
B = three * (electrons_2(spin) - electrons(spin)) / (step * step) - &
|
|
(g1 + two * g0) / step
|
|
recA = (step * step * step) / &
|
|
(two * (electrons(spin) - electrons_2(spin)) + &
|
|
(g0 + g1) * step)
|
|
B = B * recA
|
|
C = C * recA
|
|
D = D * recA
|
|
truestep = SolveCubic(B, C, D, step, inode, ionode)
|
|
if (inode .eq. ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a,i1,") are ",2e16.6)') &
|
|
trim(prefix)//" step, truestep (spin=", spin, step, truestep
|
|
call matrix_sum(one, matL(spin), truestep - step, matSphi(spin))
|
|
if (truestep == step) then
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a)') trim(prefix)//" still in linear loop"
|
|
! check that electron number is correct
|
|
call LNV_matrix_multiply(electrons_2, energy, dontK, &
|
|
dontM1, dontM2, dontM3, &
|
|
dontM4, dophi, dontE, &
|
|
mat_phi=matphi, spin=spin)
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun,fmt='(4x,a,i1,") after correction",f16.6)') &
|
|
trim(prefix)//" electron number (spin=", spin, electrons_2(spin)
|
|
dne = abs(electrons_2(spin) - electrons_0(spin))
|
|
if (dne < 1e-4_double) done = .true.
|
|
iter = iter + 1
|
|
else
|
|
done = .true.
|
|
call LNV_matrix_multiply(electrons_2, energy, dontK, &
|
|
dontM1, dontM2, dontM3, &
|
|
dontM4, dophi, dontE, &
|
|
mat_phi=matphi, spin=spin)
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a,i1,") after correction: ",f16.6)') &
|
|
trim(prefix)//" electron number (spin=", spin, electrons_2(spin)
|
|
end if ! (truestep == step)
|
|
end if ! (abs(electrons_0(spin) - electrons(spin)) < 1.0e-9_double)
|
|
end do ! while ( .not. done .and. (iter < 20))
|
|
call free_temp_matrix(matSphi2(spin))
|
|
call free_temp_matrix(matSphi(spin))
|
|
call free_temp_matrix(matphi2(spin))
|
|
call free_temp_matrix(matTL(spin))
|
|
end do ! spin
|
|
|
|
return
|
|
end subroutine correct_electron_number_fixspin
|
|
!!*****
|
|
|
|
|
|
!!****f* DMMin/correct_electron_number_varspin *
|
|
!!
|
|
!! NAME
|
|
!! correct_electron_number_varspin
|
|
!! USAGE
|
|
!!
|
|
!! PURPOSE
|
|
!! For spin polarised calculations:
|
|
!! Corrects the L matrices so that the electron number is correct
|
|
!! treating the overall L matrix as direct sum of the spin channels.
|
|
!!
|
|
!! That means both spin channels share the same search steps, and
|
|
!! the steps and directions are calculated from the overall L
|
|
!! (direct sum of both spin components)
|
|
!!
|
|
!! Note that the original number_of_bands in correct_electron_number
|
|
!! subroutine is an obsolete historical artifact. Use ne_in_cell
|
|
!! from the global module instead for the correct number of total
|
|
!! electrons.
|
|
!! INPUTS
|
|
!! USES
|
|
!! AUTHOR
|
|
!! L.Tong
|
|
!! CREATION DATE
|
|
!! 2011/07/27
|
|
!! MODIFICATION HISTORY
|
|
!! Thursday, 2011/08/11 L.Tong
|
|
!! Fixed a bug, forgot to use matphi_dn from mult_module Switched
|
|
!! to LNV_matrix_multiply, as LNV_matrix_multiply_spin is now
|
|
!! obsolete
|
|
!! 2018/11/13 17:30 nakata
|
|
!! Changed matT and matTtran to be spin_SF dependent
|
|
!! SOURCE
|
|
!!
|
|
subroutine correct_electron_number_varspin
|
|
|
|
use datatypes
|
|
use logicals
|
|
use numbers
|
|
use matrix_data, only: TLrange, Lrange
|
|
use mult_module, only: matT, matTtran, matphi, matL, &
|
|
allocate_temp_matrix, free_temp_matrix, &
|
|
matrix_product_trace, matrix_product, &
|
|
matrix_sum, mult, T_L_TL, TL_T_L, &
|
|
LNV_matrix_multiply, matrix_transpose
|
|
use primary_module, only: bundle
|
|
use GenComms, only: gsum
|
|
use global_module, only: ne_in_cell, nspin, spin_factor, &
|
|
nspin_SF, flag_SpinDependentSF, min_layer
|
|
use io_module, only: return_prefix
|
|
|
|
implicit none
|
|
|
|
! Local variables
|
|
real(double), dimension(nspin) :: electrons_spin, energy_spin
|
|
integer, dimension(nspin) :: matTL, matphi2, matSphi, matSphi2
|
|
real(double) :: electrons_0, electrons, energy, step, step1, &
|
|
electrons2, g0, g1, recA, B, C, D, truestep, dne
|
|
integer :: iter, spin, spin_SF
|
|
logical :: done
|
|
character(len=20) :: subname = "correct_Ne: "
|
|
character(len=120) :: prefix
|
|
|
|
prefix = return_prefix(subname, min_layer)
|
|
done = .false.
|
|
iter = 0
|
|
spin_SF = 1
|
|
|
|
! set the correct electron number
|
|
electrons_0 = ne_in_cell
|
|
|
|
do spin = 1, nspin
|
|
matTL(spin) = allocate_temp_matrix(TLrange,0)
|
|
matphi2(spin) = allocate_temp_matrix(Lrange,0)
|
|
matSphi(spin) = allocate_temp_matrix(Lrange,0)
|
|
matSphi2(spin) = allocate_temp_matrix(Lrange,0)
|
|
end do
|
|
|
|
! get electron number and gradient
|
|
call matrix_transpose(matT(1), matTtran(1))
|
|
if (nspin_SF==2) call matrix_transpose(matT(2), matTtran(2))
|
|
|
|
do while (.not. done .and. (iter < 20)) ! Was 20 !
|
|
|
|
call LNV_matrix_multiply(electrons_spin, energy_spin, dontK, &
|
|
dontM1, dontM2, dontM3, dontM4, dophi,&
|
|
dontE, mat_phi=matphi)
|
|
electrons = spin_factor * sum(electrons_spin)
|
|
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a,3f16.6)') &
|
|
trim(prefix)//" electron numbers before correction (up, down, total): ", &
|
|
electrons_spin(1), electrons_spin(nspin), electrons
|
|
g0 = zero
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
call matrix_product(matT(spin_SF), matphi(spin), matTL(spin), mult(T_L_TL))
|
|
call matrix_product(matTL(spin), matTtran(spin_SF), matSphi(spin), mult(TL_T_L))
|
|
g0 = g0 + spin_factor * &
|
|
matrix_product_trace(matSphi(spin), matphi(spin))
|
|
end do
|
|
|
|
! initial guess is linear correction...
|
|
step1 = (electrons_0 - electrons) / (g0 + RD_ERR)
|
|
step = 0.1_double
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a,f16.6,e16.6)') trim(prefix)//" g0, step1 are", g0, step1
|
|
|
|
! if we are within 0.1% of the correct number, linear will do.
|
|
if (abs(electrons_0 - electrons) < 1.0e-9_double) then
|
|
do spin = 1, nspin
|
|
call matrix_sum(one, matL(spin), step1, matSphi(spin))
|
|
end do
|
|
done = .true.
|
|
else
|
|
step = step1
|
|
! Take a step along the electron gradient
|
|
do spin = 1, nspin
|
|
call matrix_sum (one, matL(spin), step1, matSphi(spin))
|
|
end do
|
|
|
|
call LNV_matrix_multiply(electrons_spin, energy_spin, dontK,&
|
|
dontM1, dontM2, dontM3, dontM4, &
|
|
dophi, dontE, mat_phi=matphi2)
|
|
electrons2 = spin_factor * sum(electrons_spin)
|
|
|
|
g1 = zero
|
|
do spin = 1, nspin
|
|
if (flag_SpinDependentSF) spin_SF = spin
|
|
call matrix_product(matT(spin_SF), matphi2(spin), matTL(spin), &
|
|
mult(T_L_TL))
|
|
call matrix_product(matTL(spin), matTtran(spin_SF), &
|
|
matSphi2(spin), mult(TL_T_L))
|
|
g1 = g1 + spin_factor * &
|
|
matrix_product_trace(matphi(spin), matSphi2(spin))
|
|
end do
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a,4f16.6)') &
|
|
trim(prefix)//" g1, elec (up, down, total): ", &
|
|
g1, electrons_spin(1), electrons_spin(nspin), &
|
|
electrons2
|
|
|
|
! get coefficients of polynomial
|
|
D = electrons - electrons_0
|
|
C = g0
|
|
!ORI B = (3.0_double*electrons2 - g1*step - &
|
|
!ORI 2.0_double*g0*step - 3.0_double*electrons) / &
|
|
!ORI (step*step)
|
|
B = three * (electrons2 - electrons) / (step * step) - &
|
|
(g1 + two * g0) / step
|
|
!ORI recA = (step*step*step) / &
|
|
!ORI (2.0_double*electrons - 2.0_double*electrons2 + &
|
|
!ORI g0*step + g1*step)
|
|
recA = (step * step * step) / &
|
|
(two * (electrons - electrons2) + (g0 + g1) * step)
|
|
B = B * recA
|
|
C = C * recA
|
|
D = D * recA
|
|
|
|
truestep = SolveCubic(B, C, D, step, inode, ionode)
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a,2e16.6)') trim(prefix)//" step, truestep ", step, truestep
|
|
|
|
do spin = 1, nspin
|
|
call matrix_sum(one, matL(spin), truestep - step, matSphi(spin))
|
|
end do
|
|
|
|
if (truestep == step) then
|
|
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun, '(4x,a)') trim(prefix)//" still in linear loop"
|
|
|
|
! check that electron number is correct
|
|
call LNV_matrix_multiply(electrons_spin, energy_spin, &
|
|
dontK, dontM1, dontM2, dontM3, &
|
|
dontM4, dophi, dontE, mat_phi=matphi)
|
|
electrons2 = spin_factor * sum(electrons_spin)
|
|
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun,fmt='(4x,a,3f16.6)') &
|
|
trim(prefix)//" electron numbers after correction (up, down, total): ", &
|
|
electrons_spin(1), electrons_spin(nspin), electrons2
|
|
dne = abs(electrons2 - electrons_0)
|
|
if ((dne / electrons_0) < 1.0e-6_double) done = .true.
|
|
iter = iter + 1
|
|
else
|
|
done = .true.
|
|
call LNV_matrix_multiply(electrons_spin, energy_spin, &
|
|
dontK, dontM1, dontM2, dontM3, &
|
|
dontM4, dophi, dontE, mat_phi=matphi)
|
|
electrons2 = spin_factor * sum(electrons_spin)
|
|
if (inode == ionode .and. iprint_DM + min_layer >= 2) &
|
|
write(io_lun,fmt='(4x,a,3f16.6)') &
|
|
trim(prefix)//" electron numbers after correction (up, down, total): ", &
|
|
electrons_spin(1), electrons_spin(nspin), electrons2
|
|
end if
|
|
end if ! (abs(electrons_0 - electrons) < 1.0e-9_double)
|
|
end do ! while (.not. done .and. iter < 20)
|
|
|
|
! free temp matrices
|
|
do spin = nspin, 1, -1
|
|
call free_temp_matrix (matSphi2(spin))
|
|
call free_temp_matrix (matSphi(spin))
|
|
call free_temp_matrix (matphi2(spin))
|
|
call free_temp_matrix (matTL(spin))
|
|
end do
|
|
|
|
return
|
|
|
|
1 format(2x,'Electron numbers before correction (Nup, Ndn, Ntotal) :', &
|
|
f25.15, f25.15, f25.15)
|
|
2 format(2x,'Electron number after correction (Nup, Ndn, Ntotal) :', &
|
|
f25.15, f25.15, f25.15)
|
|
|
|
end subroutine correct_electron_number_varspin
|
|
!!****
|
|
|
|
! -----------------------------------------------------------
|
|
! Function SolveCubic
|
|
! -----------------------------------------------------------
|
|
|
|
!!****f* DMMin/SolveCubic *
|
|
!!
|
|
!! NAME
|
|
!! SolveCubic
|
|
!! USAGE
|
|
!!
|
|
!! PURPOSE
|
|
!! Solves a cubic: x^3+Ax^2+Bx+C
|
|
!! This is a general solution first worked out in the
|
|
!! sixteenth century (published by Gerolamo Cardano)
|
|
!!
|
|
!! See, for example, Abramowitz & Stegun p. 17
|
|
!! INPUTS
|
|
!!
|
|
!!
|
|
!! USES
|
|
!!
|
|
!! AUTHOR
|
|
!! E.H.Hernandez
|
|
!! CREATION DATE
|
|
!! 10/04/95 ?
|
|
!! MODIFICATION HISTORY
|
|
!! 30/05/2001 dave
|
|
!! Added ROBODoc header
|
|
!! 20/06/2001 dave
|
|
!! Included in DMMinModule
|
|
!! 2019/10/23 16:57 dave
|
|
!! Updated and polished
|
|
!! SOURCE
|
|
!!
|
|
function SolveCubic(a,b,c,guess,inode,ionode)
|
|
|
|
use datatypes
|
|
use numbers
|
|
use global_module, only: min_layer
|
|
|
|
implicit none
|
|
|
|
integer inode,ionode
|
|
real(double) :: SolveCubic
|
|
real(double) :: a,b,c,guess
|
|
|
|
real(double) :: Q, R, S, T, theta, z1, z2, z3
|
|
|
|
Q = (a*a - three*b) / nine
|
|
R = (two*a*a*a - nine*a*b + 27.0_double*c)/54.0_double
|
|
|
|
if ((R*R)<(Q*Q*Q)) then ! Guarantees Q is positive
|
|
! three roots...
|
|
theta = acos(R/(sqrt(Q*Q*Q)))
|
|
T = -two * sqrt(Q)
|
|
! Note that solutions are given relative to guess (step taken to find parameters)
|
|
z1 = T * cos(theta/three) - (a/three)
|
|
z2 = T * cos((theta+twopi)/three) - (a/three)
|
|
z3 = T * cos((theta-twopi)/three) - (a/three)
|
|
|
|
! Take solution with smallest magnitude
|
|
if (abs(z1)<=abs(z2).and.abs(z1)<=abs(Z3)) then
|
|
SolveCubic = z1
|
|
else if (abs(z2)<=abs(z3)) then
|
|
SolveCubic = z2
|
|
else
|
|
SolveCubic = z3
|
|
end if
|
|
else
|
|
|
|
! the solution for the single real root
|
|
Q = -Q
|
|
R = -R
|
|
|
|
S = R + sqrt(Q*Q*Q + R*R)
|
|
T = R - sqrt(Q*Q*Q + R*R)
|
|
|
|
if ( S >= zero ) then
|
|
S = S**third
|
|
else
|
|
S = -abs(S)**third
|
|
end if
|
|
|
|
if ( T >= zero ) then
|
|
T = T**third
|
|
else
|
|
T = -abs(T)**third
|
|
end if
|
|
|
|
SolveCubic = S + T - a/three
|
|
! Experience suggests that, while this is a correct solution to the cubic,
|
|
! a large step is completely wrong for electron number correction - test
|
|
! for this, and if necessary return only the initial guess
|
|
!
|
|
! The prefactor of 10 is arbitrary but seems reasonable
|
|
if(abs(SolveCubic)>10.0_double*abs(guess)) then
|
|
if(inode==ionode .and. iprint_DM + min_layer > 2) &
|
|
write (io_lun, '(8x,"Step too large for Ne correction: linear guess was: ",2e16.6)') &
|
|
SolveCubic, guess
|
|
SolveCubic = guess
|
|
end if
|
|
|
|
end if
|
|
|
|
return
|
|
end function SolveCubic
|
|
!!***
|
|
end module DMMin
|