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