Branch data 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 : : !!***
|