Branch data Line data Source code
1 : : !!****m* ABINIT/m_ipi
2 : : !! NAME
3 : : !! m_ipi
4 : : !!
5 : : !! FUNCTION
6 : : !! This module implements the client-side parf of the i-pi protocol.
7 : : !! The communication between the driver and the client (Abinit) is implemented as follows:
8 : : !! 1)
9 : : !! 2)
10 : : !!
11 : : !! Only structural relaxations with ionmov 28 are implemented.
12 : : !! Support MD runs requires the proper handling of velocities.
13 : : !! No parallelism over images.
14 : : !!
15 : : !! COPYRIGHT
16 : : !! Copyright (C) 2020-2026 ABINIT group (MG)
17 : : !! This file is distributed under the terms of the
18 : : !! GNU General Public License, see ~abinit/COPYING
19 : : !! or http://www.gnu.org/copyleft/gpl.txt .
20 : : !!
21 : : !! NOTES
22 : : !!
23 : : !! From the i-pi user guide available at: https://github.com/i-pi/i-pi/blob/master/doc/manual.tex
24 : : !!
25 : : !! The server assumes that 4-byte integers, 8-byte floats and 1-byte characters are used.
26 : : !! The typical communication flow is as follows:
27 : : !!
28 : : !! 1. a header string "STATUS" is sent by the server to the client that has connected to it;
29 : : !!
30 : : !! 2. a header string is then returned, giving the status of the client code.
31 : : !! Recognized messages are:
32 : : !!
33 : : !! "NEEDINIT": if the client code needs any initialising data, it can be sent here.
34 : : !! The server code will then send a header string "INIT", followed by an integer
35 : : !! corresponding to the bead index, another integer giving the number of bits
36 : : !! in the initialization string, and finally the initialization string itself.
37 : : !!
38 : : !! "READY": sent if the client code is ready to calculate the forces. The server
39 : : !! socket will then send a string "POSDATA", then nine floats for the cell vector
40 : : !! matrix, then another nine floats for the inverse matrix. The server socket
41 : : !! will then send one integer giving the number of atoms, then the position data
42 : : !! as 3 floats for each atom giving the 3 cartesian components of its position.
43 : : !!
44 : : !! "HAVEDATA": is sent if the client has finished computing the potential and
45 : : !! forces. The server socket then sends a string "GETFORCE", and the client
46 : : !! socket returns "FORCEREADY". The potential is then returned as a float,
47 : : !! the number of atoms as an integer, then the force data as 3 floats per atom
48 : : !! in the same way as the positions, and the virial as 9 floats in the same
49 : : !! way as the cell vector matrix. Finally, the client may return an arbitrary
50 : : !! string containing additional data that have been obtained by the electronic
51 : : !! structure calculation (atomic charges, dipole moment). The client first
52 : : !! returns an integer specifying the number of characters, and then the string,
53 : : !! which will be output verbatim if this "extra" information is requested in the
54 : : !! output section (see 3.2.2).
55 : : !!
56 : : !! 3. The server socket waits until the force data for each replica of the system has
57 : : !! been calculated and returned, then the MD can be propagated for one more time
58 : : !! step, and new force requests will be dispatched.
59 : : !!
60 : : !! SOURCE
61 : :
62 : : #if defined HAVE_CONFIG_H
63 : : #include "config.h"
64 : : #endif
65 : :
66 : : #include "abi_common.h"
67 : :
68 : : module m_ipi
69 : :
70 : : use defs_basis
71 : : use m_abicore
72 : : use m_abimover
73 : : use m_abihist
74 : : use m_xmpi
75 : : use m_errors
76 : : use m_fsockets
77 : :
78 : : use m_fstrings, only : sjoin, itoa
79 : : use m_geometry, only : xcart2xred, det3r, stress_voigt_to_mat
80 : :
81 : : implicit none
82 : :
83 : : private
84 : :
85 : : public :: ipi_setup
86 : : public :: ipi_shutdown
87 : : public :: ipi_pred
88 : : public :: ipi_check_initial_consistency
89 : : !!***
90 : :
91 : : ! =============
92 : : ! private stuff
93 : : ! =============
94 : :
95 : : INTEGER, PARAMETER :: HDRLEN = 12
96 : : integer, save :: socket
97 : : integer, save :: origin_natom = 0
98 : : real(dp), save :: origin_rprimd(3, 3) = zero
99 : : real(dp), save, allocatable :: origin_xred(:,:)
100 : :
101 : : contains
102 : : !!***
103 : :
104 : : !!****f* m_ipi/ipi_setup
105 : : !! NAME
106 : : !! ipi_setup
107 : : !!
108 : : !! FUNCTION
109 : : !! Initialize the socket from a string that is usually passed via the command line interface.
110 : : !! Get the initial configuration from the server and save it in module global variables
111 : : !! for subsequent consistency check.
112 : : !! These values, indeed, must agree with those read from the input file.
113 : : !! See also ipi_check_initial_consistency.
114 : : !!
115 : : !! INPUTS
116 : : !!
117 : : !! OUTPUT
118 : : !!
119 : : !! OUTPUT
120 : : !!
121 : : !! SOURCE
122 : :
123 : 0 : subroutine ipi_setup(string, comm)
124 : :
125 : : !Arguments ------------------------------------
126 : : !scalars
127 : : character(len=*),intent(in) :: string
128 : : integer,intent(in) :: comm
129 : :
130 : : !local variables
131 : : integer,parameter :: master = 0
132 : : integer :: my_rank, ierr
133 : : character(len=HDRLEN) :: header
134 : :
135 : : ! *************************************************************************
136 : :
137 : 0 : my_rank = xmpi_comm_rank(comm)
138 : :
139 : 0 : call wrtout(std_out, "Initializing i-pi protocol...")
140 [ # # ]: 0 : if (my_rank == master) then
141 : 0 : call socket_from_string(string, socket)
142 : 0 : call readbuffer(socket, header, HDRLEN)
143 [ # # # # ]: 0 : ABI_CHECK(trim(header) == "STATUS", sjoin("Expecting STATUS header, got:", trim(header)))
144 : 0 : call writebuffer(socket, "READY ", HDRLEN)
145 : 0 : call readbuffer(socket, header, HDRLEN)
146 [ # # # # ]: 0 : ABI_CHECK(trim(header) == "POSDATA", sjoin("Expecting POSDATA header, got:", trim(header)))
147 [ # # ]: 0 : call handle_posdata(origin_natom, origin_rprimd, origin_xred)
148 : : end if
149 : :
150 : : ! broadcast data to other procs.
151 : 0 : call xmpi_bcast(origin_natom, master, comm, ierr)
152 : 0 : call xmpi_bcast(origin_rprimd, master, comm, ierr)
153 [ # # ]: 0 : if (my_rank /= master) then
154 [ # # # # : 0 : ABI_MALLOC(origin_xred, (3, origin_natom))
# # # # #
# # # #
# ]
155 : : end if
156 : 0 : call xmpi_bcast(origin_xred, master, comm, ierr)
157 : 0 : call wrtout(std_out, "ipi_setup completed")
158 : :
159 : 0 : end subroutine ipi_setup
160 : : !!***
161 : :
162 : : !!****f* m_ipi/ipi_check_initial_consistency
163 : : !! NAME
164 : : !! ipi_check_initial_consistency
165 : : !!
166 : : !! FUNCTION
167 : : !! Compare initial configuration read from input file with the one trasmitted by the server.
168 : : !!
169 : : !! INPUTS
170 : : !!
171 : : !! OUTPUT
172 : : !!
173 : : !! OUTPUT
174 : : !!
175 : : !! SOURCE
176 : :
177 : 0 : subroutine ipi_check_initial_consistency(in_natom, in_rprimd, in_xred, ierr)
178 : :
179 : : integer,intent(in) :: in_natom
180 : : real(dp),intent(in) :: in_rprimd(3,3)
181 : : real(dp),intent(in) :: in_xred(3,in_natom)
182 : : integer,intent(out) :: ierr
183 : :
184 : : integer :: ii
185 : :
186 : 0 : call wrtout(std_out, "ipi mode: Checking whether initial geometry from server agrees with input file")
187 : :
188 : 0 : ierr = 0
189 [ # # ]: 0 : if (in_natom /= origin_natom) then
190 : 0 : ABI_WARNING(sjoin("in_natom:", sjoin(itoa(in_natom), " != origin_natom", itoa(origin_natom))))
191 : 0 : ierr = ierr + 1
192 : : end if
193 : :
194 [ # # # # : 0 : if (any(abs(in_rprimd - origin_rprimd) > tol6)) then
# # # # ]
195 : 0 : ABI_WARNING("Mismatch between input file and data from socket: in_rprimd and origin_rprimd do not agree within 1e-6")
196 : 0 : write(std_out, "(a)")" in_rprind(:,ii), origin_rprimd(:,ii)"
197 [ # # ]: 0 : do ii=1,3
198 : 0 : write(std_out, *)in_rprimd(:,ii), origin_rprimd(:,ii)
199 : : end do
200 : 0 : ierr = ierr + 1
201 : : end if
202 : :
203 [ # # ]: 0 : if (.not. allocated(origin_xred)) then
204 : 0 : ierr = ierr + 1
205 : 0 : ABI_WARNING("origin_xred is not allocated!")
206 : : end if
207 : :
208 [ # # # # ]: 0 : if (in_natom == origin_natom .and. allocated(origin_xred)) then
209 [ # # # # : 0 : if (any(abs(in_xred - origin_xred) > tol6)) then
# # # # ]
210 : 0 : ABI_WARNING("Mismatch between input file and data from socket: in_xred and origin_xred do not agree withing 1e-6")
211 : 0 : ierr = ierr + 1
212 : 0 : write(std_out, "(a)")" in_xred(:,ii), origin_xred(:,ii)"
213 [ # # ]: 0 : do ii=1,3
214 : 0 : write(std_out, *)in_xred(:,ii), origin_xred(:,ii)
215 : : end do
216 : : end if
217 : : end if
218 : :
219 : : !ABI_CHECK(ierr == 0, "Initial structure sent by i-pi server does not match the one read from file! See messages above")
220 : :
221 : 0 : end subroutine ipi_check_initial_consistency
222 : : !!***
223 : :
224 : : !!****f* m_ipi/handle_posdata
225 : : !! NAME
226 : : !! handle_posdata
227 : : !!
228 : : !! FUNCTION
229 : : !! Receive new geometry from the server after POSDATA header.
230 : : !!
231 : : !! INPUTS
232 : : !!
233 : : !! OUTPUT
234 : : !!
235 : : !! OUTPUT
236 : : !!
237 : : !! SOURCE
238 : :
239 : 0 : subroutine handle_posdata(out_natom, out_rprimd, out_xred)
240 : :
241 : : integer,intent(out) :: out_natom
242 : : real(dp),intent(out) :: out_rprimd(3,3)
243 : : real(dp),allocatable,intent(out) :: out_xred(:,:)
244 : :
245 : : real(dp) :: gprimd(3,3), mtxbuffer(9)
246 : 0 : real(dp), allocatable :: combuf(:), xcart(:,:)
247 : :
248 : 0 : call readbuffer(socket, mtxbuffer, 9)
249 [ # # # # ]: 0 : out_rprimd = transpose(reshape(mtxbuffer, [3, 3]))
250 : 0 : call readbuffer(socket, mtxbuffer, 9)
251 : 0 : gprimd = transpose(reshape(mtxbuffer, [3, 3]))
252 : 0 : call readbuffer(socket, out_natom)
253 [ # # # # : 0 : ABI_MALLOC(combuf, (3 * out_natom))
# # # # #
# # # #
# ]
254 : 0 : call readbuffer(socket, combuf, 3 * out_natom)
255 [ # # # # : 0 : ABI_MALLOC(xcart, (3, out_natom))
# # # # #
# # # ]
256 [ # # ]: 0 : xcart = reshape(combuf, [3, out_natom])
257 [ # # # # : 0 : ABI_MALLOC(out_xred, (3, out_natom))
# # # # #
# # # #
# ]
258 : 0 : call xcart2xred(out_natom, out_rprimd, xcart, out_xred)
259 [ # # ]: 0 : ABI_FREE(combuf)
260 [ # # ]: 0 : ABI_FREE(xcart)
261 : :
262 : 0 : end subroutine handle_posdata
263 : : !!***
264 : :
265 : : !!****f* m_ipi/ipi_shutdown
266 : : !! NAME
267 : : !! ipi_shutdown
268 : : !!
269 : : !! FUNCTION
270 : : !!
271 : : !! INPUTS
272 : : !!
273 : : !! OUTPUT
274 : : !!
275 : : !! OUTPUT
276 : : !!
277 : : !! SOURCE
278 : :
279 : 0 : subroutine ipi_shutdown()
280 : :
281 : : ! *************************************************************************
282 : :
283 : 0 : origin_natom = 0
284 : 0 : origin_rprimd = zero
285 [ # # ]: 0 : ABI_SFREE(origin_xred)
286 : : !call close_socket()
287 : :
288 : 0 : end subroutine ipi_shutdown
289 : : !!***
290 : :
291 : : !!****f* m_ipi/ipi_pred
292 : : !! NAME
293 : : !! ipi_pred
294 : : !!
295 : : !! FUNCTION
296 : : !! "predict" new structure using the data sent by the server.
297 : : !!
298 : : !! INPUTS
299 : : !! ab_mover <type(abimover)> : Datatype with all the information needed by the predictor
300 : : !! itime : Index of the present iteration
301 : : !! ntime : Maximal number of iterations
302 : : !! zDEBUG : if true print some debugging information
303 : : !!
304 : : !! OUTPUT
305 : : !!
306 : : !! SIDE EFFECTS
307 : : !! hist <type(abihist)>: History of positions,forces,acell, rprimd, stresses
308 : : !!
309 : : !! OUTPUT
310 : : !!
311 : : !! SOURCE
312 : :
313 : 0 : subroutine ipi_pred(ab_mover, hist, itime, ntime, zDEBUG, iexit, comm_cell)
314 : :
315 : : !Arguments ------------------------------------
316 : : !scalars
317 : : integer,intent(in) :: itime, ntime, iexit, comm_cell
318 : : logical,intent(in) :: zDEBUG
319 : : type(abimover),intent(in) :: ab_mover
320 : : type(abihist),intent(inout) :: hist
321 : :
322 : : !local variables
323 : : integer,parameter :: master = 0
324 : : integer :: my_rank, natom, new_natom, ierr !, ihist_prev
325 : : real(dp) :: etotal, ucvol
326 : : character(len=HDRLEN) :: header
327 : : character(len=500) :: msg
328 : : !arrays
329 : 0 : real(dp) :: acell(3), rprimd(3,3), xred(3,ab_mover%natom), strten(6), sigma(3,3)
330 : : real(dp) :: new_rprimd(3, 3)
331 : 0 : real(dp),allocatable :: new_xred(:,:)
332 : :
333 : : ! *************************************************************************
334 : :
335 : 0 : write(msg, "(3(a,i0))")" ipi_pred: itime: ", itime, ", ntime: ", ntime, ", iexit:", iexit
336 : 0 : call wrtout(std_out, msg)
337 : :
338 : 0 : natom = ab_mover%natom
339 : : ! Obtain the present values from the history
340 : 0 : call hist2var(acell, hist, natom, rprimd, xred, zDEBUG)
341 : 0 : etotal = hist%etot(hist%ihist)
342 [ # # ]: 0 : strten = hist%strten(:,hist%ihist)
343 : 0 : ucvol = det3r(rprimd)
344 : :
345 [ # # ]: 0 : if (my_rank == master) then
346 : 0 : call readbuffer(socket, header, HDRLEN)
347 [ # # # # ]: 0 : ABI_CHECK(trim(header) == "STATUS", sjoin("Expecting STATUS header, got:", trim(header)))
348 : 0 : call writebuffer( socket, "HAVEDATA ", HDRLEN )
349 : 0 : call readbuffer(socket, header, HDRLEN)
350 [ # # # # ]: 0 : ABI_CHECK(trim(header) == "GETFORCE", sjoin("Expecting GETFORCE header, got:", trim(header)))
351 : :
352 : : ! Communicate energy info back to i-pi
353 : 0 : call wrtout(std_out, " i-pi mode: Returning etotal, forces, and stress tensor to server...")
354 : 0 : call writebuffer(socket, "FORCEREADY ", HDRLEN)
355 : 0 : call writebuffer(socket, etotal)
356 : 0 : call writebuffer(socket, natom)
357 : 0 : call writebuffer_dv(socket, hist%fcart(:,:,hist%ihist), 3 * natom)
358 : 0 : call stress_voigt_to_mat(strten, sigma)
359 : : ! Well it's a symmetric tensor.
360 : : ! TODO: Check volume
361 [ # # # # : 0 : sigma = -ucvol * transpose(sigma)
# # # # ]
362 : 0 : call writebuffer_dv(socket, sigma, 9)
363 : :
364 : : ! Note: i-pi can also receive an arbitrary string, that will be printed
365 : : ! out to the "extra" trajectory file. This is useful if you want to
366 : : ! return additional information, e.g. atomic charges, wannier centres,
367 : : ! etc. one must return the number of characters, then the string. Here
368 : : ! we just send back zero characters.
369 : : !
370 : 0 : call writebuffer(socket, 0)
371 : :
372 : : ! ===============================================================
373 : : ! Here master waits for new lattice and positions from the server
374 : : ! ===============================================================
375 : 0 : call readbuffer(socket, header, HDRLEN)
376 [ # # # # ]: 0 : ABI_CHECK(trim(header) == "STATUS", sjoin("Expecting STATUS header, got:", trim(header)))
377 : 0 : call writebuffer(socket, "READY ", HDRLEN)
378 : 0 : call readbuffer(socket, header, HDRLEN)
379 [ # # # # ]: 0 : ABI_CHECK(trim(header) == "POSDATA", sjoin("Expecting POSDATA header, got:", trim(header)))
380 : 0 : call handle_posdata(new_natom, new_rprimd, new_xred)
381 : :
382 : : ! Some basic consistency checks. Obviously there are lot of things that can go wrong here
383 : : ! if the driver changes the space group or the input file is not consistent with
384 : : ! the logic implemented by the server.
385 [ # # ]: 0 : ABI_CHECK(new_natom == ab_mover%natom, "ipi server shall not change the number of atoms!")
386 [ # # # # : 0 : if (ab_mover%optcell == 0 .and. any(abs(new_rprimd - origin_rprimd) > tol6)) then
# # # # #
# ]
387 : 0 : ABI_ERROR("Mismatch between origin_rprimd and data from socket: origin_rprimd and new_rprimd do not agree within 1e-6")
388 : : end if
389 : : end if
390 : :
391 : : ! ====================
392 : : ! All procs block here
393 : : ! ====================
394 : 0 : call xmpi_barrier(comm_cell)
395 : 0 : call xmpi_bcast(new_rprimd, master, comm_cell, ierr)
396 : 0 : call xmpi_bcast(new_xred, master, comm_cell, ierr)
397 : :
398 : : ! Update the history with the prediction
399 : : ! Increase indexes
400 : 0 : hist%ihist = abihist_findIndex(hist, +1)
401 : : !call mkradim(acell,rprim,rprimd_symm)
402 : :
403 : : ! Fill the history with the variables xred, acell, rprimd, vel
404 [ # # ]: 0 : acell = one
405 : 0 : call var2hist(acell, hist, ab_mover%natom, new_rprimd, new_xred, zDEBUG)
406 : : !ihist_prev = abihist_findIndex(hist, -1)
407 : : ! FIXME: I don't know how to handle vel if MD run!
408 : : !hist%vel(:,:,hist%ihist) = hist%vel(:,:,ihist_prev)
409 : :
410 [ # # ]: 0 : end subroutine ipi_pred
411 : : !!***
412 : :
413 : : end module m_ipi
414 : : !!***
|