LCOV - code coverage report
Current view: top level - src/45_geomoptim - m_bfgs.F90 (source / functions) Coverage Total Hit
Test: coverage.info Lines: 77.3 % 88 68
Test Date: 2026-09-21 22:40:37 Functions: 100.0 % 3 3

            Line data    Source code
       1              : !!****m* ABINIT/m_bfgs
       2              : !! NAME
       3              : !!  m_bfgs
       4              : !!
       5              : !! FUNCTION
       6              : !!  This module provides several routines for the application of a
       7              : !!  Broyden-Fletcher-Goldfarb-Shanno (BFGS) minimization algorithm.
       8              : !!
       9              : !! COPYRIGHT
      10              : !! Copyright (C) 2012-2026 ABINIT group (XG,JCC)
      11              : !! This file is distributed under the terms of the
      12              : !! GNU General Public License, see ~abinit/COPYING
      13              : !! or http://www.gnu.org/copyleft/gpl.txt .
      14              : !!
      15              : !! SOURCE
      16              : 
      17              : #if defined HAVE_CONFIG_H
      18              : #include "config.h"
      19              : #endif
      20              : 
      21              : #include "abi_common.h"
      22              : 
      23              : MODULE m_bfgs
      24              : 
      25              :  use defs_basis
      26              :  use m_abicore
      27              :  use m_errors
      28              :  use m_abimover
      29              : 
      30              :  use m_io_tools,       only : open_file
      31              :  use m_numeric_tools,  only : findmin
      32              : 
      33              :  implicit none
      34              : 
      35              :  private
      36              : 
      37              : !public procedures
      38              :  public :: hessinit   ! Initialize Hessian matrix
      39              :  public :: hessupdt   ! Update the hessian matrix
      40              :  public :: brdene
      41              : !!***
      42              : 
      43              : CONTAINS  !===========================================================
      44              : !!***
      45              : 
      46              : !----------------------------------------------------------------------
      47              : 
      48              : !!****f* m_bfgs/hessinit
      49              : !! NAME
      50              : !! hessinit
      51              : !!
      52              : !! FUNCTION
      53              : !! Initialize the Hessian matrix, either from disk or using init_matrix.
      54              : !! The size ndim must be greater or equal than 3 * ab_mover%natom.
      55              : !!
      56              : !! INPUTS
      57              : !!  fnameabi_hes=filename for Hessian matrix
      58              : !!  ab_mover = the input variables relevant for moving ions
      59              : !!  init_matrix(3,3)=matrix used for each atom (if iatfix = 0) for initialisation.
      60              : !!  ndim=size of the hessian and vectors
      61              : !!  ucvol=volume of the box (used when ab_mover%optcell is not null).
      62              : !!
      63              : !! OUTPUT
      64              : !!  hessin(ndim,ndim)=hessian matrix, initialised at output.
      65              : !!
      66              : !! SOURCE
      67              : 
      68          286 : subroutine hessinit(ab_mover, hessin, init_matrix, ndim, ucvol)
      69              : 
      70              : !Arguments ------------------------------------
      71              : !scalars
      72              :  integer,intent(in) :: ndim
      73              :  real(dp),intent(in) :: ucvol
      74              :  type(abimover),intent(in) :: ab_mover
      75              : !arrays
      76              :  real(dp),intent(in) :: init_matrix(3,3)
      77              :  real(dp),intent(out) :: hessin(ndim,ndim)
      78              : 
      79              : !Local variables-------------------------------
      80              : !scalars
      81              :  integer :: hess_ok,iatom,idim,idir1,idir2,ii,ios,jj,ndim0,temp_unit
      82              :  real(dp) :: diag
      83              :  logical :: ex
      84              :  character(len=500) :: message
      85              : 
      86              : ! *********************************************************************
      87              : 
      88              : !Initialization of the inverse Hessian to the unit matrix
      89              : !Much better choices are possible--this simply corresponds to
      90              : !taking the first minimization step as the negative of the
      91              : !gradient, with the full length of the gradient vector as
      92              : !the step size.  Any spring type model would probably be a better starting guess.
      93              : 
      94          286 :  if (ndim < 3 * ab_mover%natom) then
      95              :    write(message, '(a,a,a)' )&
      96            0 : &   'the size of the given hessian matrix is too small.', ch10, &
      97            0 : &   'This is an internal error, contact ABINIT developers.'
      98            0 :    ABI_ERROR(message)
      99              :  end if
     100              : 
     101              : !Special arrangement: if input hessian file exists, read data from there
     102          286 :  inquire (file=ab_mover%fnameabi_hes,iostat=ios,exist=ex)
     103          286 :  hess_ok=0
     104              : 
     105          286 :  if (ex) then
     106              :    ! Read inverse hessian data from file; format is
     107            0 :    if (open_file(ab_mover%fnameabi_hes,message,newunit=temp_unit,form='formatted',status='old') /= 0) then
     108            0 :      ABI_ERROR(message)
     109              :    end if
     110            0 :    read (temp_unit,*)
     111            0 :    read (temp_unit,*) ndim0
     112            0 :    if (ndim0/=ndim) then
     113              : !    Cannot read data because data file natom disagrees with current job
     114              :      write(message,'(5a,i10,a,i10,2a)')&
     115            0 : &     'Tried to read inverse hessian from file',trim(ab_mover%fnameabi_hes),' but',ch10,&
     116            0 : &     'ndim of that file =',ndim0,' , is not equal to input ndim =',ndim,ch10,&
     117            0 : &     ' => initialize inverse hessian with identity matrix.'
     118            0 :      ABI_WARNING(message)
     119            0 :      close(unit=temp_unit)
     120              :    else
     121              :      ! Read inverse hessian
     122            0 :      do jj=1,ndim
     123            0 :        read (temp_unit,*)
     124            0 :        read (temp_unit,*) (hessin(ii,jj),ii=1,ndim)
     125              :      end do
     126            0 :      close (unit=temp_unit)
     127            0 :      write(message,*)' Inverse hessian has been input from input hessian file',trim(ab_mover%fnameabi_hes)
     128            0 :      call wrtout(std_out,message,'COLL')
     129              :      hess_ok=1
     130              :    end if
     131              :  end if
     132              : 
     133              : !If hessin was not read, initialize inverse hessian with identity matrix
     134              : !in cartesian coordinates, which makes use of metric tensor gmet in reduced coordinates.
     135              :  if(hess_ok==0)then
     136        29696 :    hessin(:,:)=zero
     137          936 :    do iatom=1,ab_mover%natom
     138         2886 :      do idir1=1,3
     139         8450 :        do idir2=1,3
     140              : !        Warning : implemented in reduced coordinates
     141         7800 :          if ( ab_mover%iatfix(idir1,iatom) ==0 .and. ab_mover%iatfix(idir2,iatom) ==0 )then
     142         5412 :            hessin(idir1+3*(iatom-1),idir2+3*(iatom-1))=init_matrix(idir1,idir2)
     143              :          end if
     144              :        end do
     145              :      end do
     146              :    end do
     147          286 :    if(ab_mover%optcell/=0)then
     148              : !    These values might lead to too large changes in some cases ...
     149           93 :      diag=ab_mover%strprecon*30.0_dp/ucvol
     150           93 :      if(ab_mover%optcell==1)diag=diag/three
     151          296 :      do idim=3*ab_mover%natom+1,ndim
     152          296 :        hessin(idim,idim)=diag
     153              :      end do
     154              :    end if
     155          286 :    call wrtout(std_out,'Inverse hessian has been initialized.','COLL')
     156              :  end if
     157              : 
     158          286 : end subroutine hessinit
     159              : !!***
     160              : 
     161              : !----------------------------------------------------------------------
     162              : 
     163              : !!****f* m_bfgs/hessupdt
     164              : !! NAME
     165              : !! hessupdt
     166              : !!
     167              : !! FUNCTION
     168              : !! Update of the hessian matrix according to the Broyden formula.
     169              : !! Could see Numerical Recipes (Fortran), 1986, page 307.
     170              : !!
     171              : !! INPUTS
     172              : !!  iatfix(3,natom)=1 for each atom fixed along specified direction, else 0
     173              : !!  natom=number of atoms in unit cell
     174              : !!  ndim=size of the hessian and vectors
     175              : !!  nimage= -- optional, default=1 --
     176              : !!         Number of images of the system described in
     177              : !!         vin, vin_prev, vout, vout_prev
     178              : !!  vin(ndim)=new input vector
     179              : !!  vin_prev(ndim)=previous input vector
     180              : !!  vout(ndim)=new output vector
     181              : !!  vout_prev(ndim)=previous output vector
     182              : !!
     183              : !! OUTPUT
     184              : !!  (see side effects)
     185              : !!
     186              : !! SIDE EFFECTS
     187              : !!  hessin(ndim,ndim)=hessian matrix, updated at output.
     188              : !!
     189              : !! SOURCE
     190              : 
     191          652 : subroutine hessupdt(hessin,iatfix,natom,ndim,vin,vin_prev,vout,vout_prev, &
     192              : &                   nimage) ! optional argument
     193              : 
     194              : !Arguments ------------------------------------
     195              : !scalars
     196              :  integer,intent(in) :: natom,ndim
     197              :  integer,intent(in),optional :: nimage
     198              : !arrays
     199              :  integer,intent(in) :: iatfix(3,natom)
     200              :  real(dp),intent(in) :: vin(ndim),vin_prev(ndim),vout(ndim),vout_prev(ndim)
     201              :  real(dp),intent(inout) :: hessin(ndim,ndim)
     202              : 
     203              : !Local variables-------------------------------
     204              : !scalars
     205              :  integer :: iatom,idir,ii,jj,nimage_
     206              :  real(dp) :: den1,den2,den3
     207              :  !character(len=500) :: msg
     208              : !arrays
     209         1304 :  real(dp) :: bfgs(ndim),din(ndim),dout(ndim),hdelta(ndim)
     210              : 
     211              : !***************************************************************************
     212              : 
     213          652 :  nimage_=1;if (present(nimage)) nimage_=nimage
     214              : 
     215              : !write(ab_out,*) 'VECTOR INPUT (vin)'
     216              : !do ii=1,ndim,3
     217              : !if (ii+2<=ndim)then
     218              : !write(ab_out,*) ii,vin(ii:ii+2)
     219              : !else
     220              : !write(ab_out,*) ii,vin(ii:ndim)
     221              : !end if
     222              : !end do
     223              : !write(ab_out,*) 'VECTOR OUTPUT (vout)'
     224              : !do ii=1,ndim,3
     225              : !if (ii+2<=ndim)then
     226              : !write(ab_out,*) ii,vout(ii:ii+2)
     227              : !else
     228              : !write(ab_out,*) ii,vout(ii:ndim)
     229              : !end if
     230              : !end do
     231              : 
     232              : !write(ab_out,*) 'VECTOR INPUT (vin_prev)'
     233              : !do ii=1,ndim,3
     234              : !if (ii+2<=ndim)then
     235              : !write(ab_out,*) ii,vin(ii:ii+2)
     236              : !else
     237              : !write(ab_out,*) ii,vin(ii:ndim)
     238              : !end if
     239              : !end do
     240              : !write(ab_out,*) 'VECTOR OUTPUT (vout_prev)'
     241              : !do ii=1,ndim,3
     242              : !if (ii+2<=ndim)then
     243              : !write(ab_out,*) ii,vout(ii:ii+2)
     244              : !else
     245              : !write(ab_out,*) ii,vout(ii:ndim)
     246              : !end if
     247              : !end do
     248              : 
     249          652 :  if (mod(ndim,nimage_)/=0) then
     250            0 :    ABI_BUG('nimage must be a dividor of ndim !')
     251              :  end if
     252              : 
     253              : !Difference between new and previous vectors
     254         7957 :  din(:) =vin(:) -vin_prev(:)
     255         7957 :  dout(:)=vout(:)-vout_prev(:)
     256              : 
     257              : !Implement fixing of atoms; must discard the change of forces on fixed atoms
     258         1304 :  do ii=1,nimage_
     259          652 :    jj=3*natom*(ii-1)
     260         3465 :    do iatom=1,natom
     261         8644 :      do idir=1,3
     262         8644 :        if (iatfix(idir,iatom)==1) dout(idir+jj)=zero
     263              :      end do
     264         2813 :      jj=jj+3
     265              :    end do
     266              :  end do
     267              : 
     268              : !Compute approximate inverse Hessian times delta fcart
     269              : !hdelta=hessin*deltaf
     270         7957 :  hdelta(:)=zero
     271         7957 :  do ii=1,ndim
     272       224620 :    hdelta(:)=hdelta(:)+hessin(:,ii)*dout(ii)
     273              :  end do
     274              : 
     275              : !Calculation of dot products for the denominators
     276         7957 : den1=zero ; den2=zero
     277         7957 : den1 = sum(dout(1:ndim) * din(1:ndim))
     278         7957 : den2 = sum(dout(1:ndim) * hdelta(1:ndim))
     279              : 
     280              : !DEBUG
     281              : !write(std_out,*)' hessupdt : den1,den2',den1,den2
     282              : !write(std_out,*)' din ',din
     283              : !write(std_out,*)' dout ',dout
     284              : !write(std_out,*)' hdelta ',hdelta
     285              : !ENDDEBUG
     286              : 
     287              : !Denominators are multiplicative
     288          652 :  den1=one/den1
     289          652 :  den3=one/den2
     290              : 
     291              : !Vectors which make a difference between the BROYDEN and
     292              : !the DAVIDON scheme.
     293         7957 :  bfgs(:)=den1*din(:)-den3*hdelta(:)
     294              : 
     295              : !B.F.G.S. updating formula
     296         7957 :  do ii=1,ndim
     297       224620 :    do jj=1,ndim
     298              :      hessin(ii,jj)=hessin(ii,jj) +den1*din(ii)*din(jj) &
     299       223968 : &     -den3*hdelta(ii)*hdelta(jj) +den2*bfgs(ii)*bfgs(jj)
     300              :    end do
     301              :  end do
     302              : 
     303          652 : end subroutine hessupdt
     304              : !!***
     305              : 
     306              : !----------------------------------------------------------------------
     307              : 
     308              : !!****f* m_bfgs/brdene
     309              : !! NAME
     310              : !! brdene
     311              : !!
     312              : !! FUNCTION
     313              : !! Update vin according to the Broyden formula, combined
     314              : !! with a line minimisation that take into account the total energies.
     315              : !! Also transfer vin to vin_prev, vout to vout_prev, and etotal to etotal_prev
     316              : !! Could see Numerical Recipes (Fortran), 1986, page 307,
     317              : !! as well as Schlegel, J. Comp. Chem. 3, 214 (1982) [[cite:Schlegel1982]].
     318              : !!
     319              : !! INPUTS
     320              : !!  etotal=new total energy (no meaning at output)
     321              : !!  hessin(ndim,ndim)=hessian matrix
     322              : !!  ndim=size of the hessian and vectors
     323              : !!  vout(ndim)=new output vector (no meaning at output)
     324              : !!
     325              : !! OUTPUT
     326              : !!  (see side effects)
     327              : !!
     328              : !! SIDE EFFECTS
     329              : !!  etotal_prev=previous total energy; contains input etotal at output
     330              : !!  vin(ndim)=new input vector; updated at output
     331              : !!  vin_prev(ndim)=previous input vector; contains input vin at output
     332              : !!  vout_prev(ndim)=previous output vector; contains input vout at output
     333              : !!
     334              : !! SOURCE
     335              : 
     336          247 : subroutine brdene(etotal,etotal_prev,hessin,ndim,vin,vin_prev,vout,vout_prev)
     337              : 
     338              : !Arguments ------------------------------------
     339              : !scalars
     340              :  integer,intent(in) :: ndim
     341              :  real(dp),intent(in) :: etotal
     342              :  real(dp),intent(inout) :: etotal_prev
     343              : !arrays+
     344              :  real(dp),intent(in) :: hessin(ndim,ndim),vout(ndim)
     345              :  real(dp),intent(inout) :: vin(ndim),vin_prev(ndim),vout_prev(ndim)
     346              : 
     347              : !Local variables-------------------------------
     348              : !scalars
     349              :  integer :: idim,brd_status
     350              :  real(dp) :: d2edv2_1,d2edv2_2,d2edv2_predict,dedv_1,dedv_2,dedv_min
     351              :  real(dp) :: dedv_predict,etotal_1,etotal_2,etotal_predict,lambda_1,lambda_2
     352              :  real(dp) :: lambda_predict
     353              : !arrays
     354          247 :  real(dp),allocatable :: dvin(:),vin_min(:),vout_min(:)
     355              : 
     356              : !***************************************************************************
     357              : 
     358          741 :  ABI_MALLOC(dvin,(ndim))
     359          494 :  ABI_MALLOC(vin_min,(ndim))
     360          494 :  ABI_MALLOC(vout_min,(ndim))
     361              : 
     362          247 :  lambda_1=1.0_dp       ; lambda_2=0.0_dp
     363          247 :  etotal_1=etotal      ; etotal_2=etotal_prev
     364         1750 :  dvin(:)=vin(:)-vin_prev(:)
     365         1750 :  dedv_1=dot_product(vout,dvin)
     366         1750 :  dedv_2=dot_product(vout_prev,dvin)
     367              :  call findmin(dedv_1,dedv_2,dedv_predict,&
     368              : & d2edv2_1,d2edv2_2,d2edv2_predict,&
     369              : & etotal_1,etotal_2,etotal_predict,&
     370          247 : & lambda_1,lambda_2,lambda_predict,brd_status)
     371              : 
     372              : !DEBUG : comes back to usual BFGS !
     373              : !lambda_predict=1.0_dp
     374              : !dedv_predict=dedv_1
     375              : !ENDDEBUG
     376              : 
     377              : !Generates vin at the minimum, and an interpolated vout, modified
     378              : !to have the right value of dedv_predict, from findmin.
     379         1750 :  vin_min(:)=vin_prev(:)+lambda_predict*dvin(:)
     380         1750 :  vout_min(:)=vout_prev(:)+lambda_predict*(vout(:)-vout_prev(:))
     381          247 :  dedv_min=dedv_2+lambda_predict*(dedv_1-dedv_2)
     382              : !Modify vout_min in order to impose dedv_predict
     383         3500 :  vout_min(:)=vout_min(:)+dvin(:)*(dedv_predict-dedv_min)/dot_product(dvin,dvin)
     384              : 
     385              : !Previous cartesian coordinates
     386          247 :  etotal_prev=etotal
     387         1750 :  vin_prev(:)=vin(:)
     388              : 
     389              : !New atomic cartesian coordinates are obtained from vin, hessin and vout
     390         1750 :  vin(:)=vin_min(:)
     391         1750 :  do idim=1,ndim
     392        10987 :    vin(:)=vin(:)-hessin(:,idim)*vout_min(idim)
     393              :  end do
     394              : 
     395              : !Previous atomic forces
     396         1750 :  vout_prev(:)=vout(:)
     397              : 
     398          247 :  ABI_FREE(dvin)
     399          247 :  ABI_FREE(vin_min)
     400          247 :  ABI_FREE(vout_min)
     401              : 
     402          247 : end subroutine brdene
     403              : !!***
     404              : 
     405              : END MODULE m_bfgs
     406              : !!***
        

Generated by: LCOV version 2.3-1