Line data Source code
1 : !--------------------------------------------------------------------------------------------------!
2 : ! CP2K: A general program to perform molecular dynamics simulations !
3 : ! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4 : ! !
5 : ! SPDX-License-Identifier: GPL-2.0-or-later !
6 : !--------------------------------------------------------------------------------------------------!
7 :
8 : ! **************************************************************************************************
9 : !> \brief Driver mode - To communicate with i-PI Python wrapper
10 : !> \par History
11 : !> none
12 : !> \author Michele Ceriotti 03.2012
13 : ! **************************************************************************************************
14 : MODULE ipi_driver
15 : USE, INTRINSIC :: IEEE_ARITHMETIC, ONLY: ieee_is_finite
16 : USE ISO_C_BINDING, ONLY: C_CHAR, &
17 : C_DOUBLE, &
18 : C_INT, &
19 : C_LOC, &
20 : C_NULL_CHAR, &
21 : C_PTR
22 : USE bibliography, ONLY: Ceriotti2014, &
23 : Kapil2016, &
24 : cite_reference
25 : USE cell_methods, ONLY: cell_create, &
26 : init_cell
27 : USE cell_types, ONLY: cell_release, &
28 : cell_type
29 : USE cp_external_control, ONLY: external_control
30 : USE cp_log_handling, ONLY: cp_logger_get_default_io_unit
31 : USE cp_subsys_types, ONLY: cp_subsys_get, &
32 : cp_subsys_set, &
33 : cp_subsys_type
34 : USE force_env_methods, ONLY: force_env_calc_energy_force
35 : USE force_env_types, ONLY: force_env_get, &
36 : force_env_type
37 : USE global_types, ONLY: global_environment_type
38 : USE input_section_types, ONLY: section_vals_get_subs_vals, &
39 : section_vals_type, &
40 : section_vals_val_get
41 : USE kinds, ONLY: default_path_length, &
42 : default_string_length, &
43 : dp, &
44 : int_4
45 : USE message_passing, ONLY: mp_para_env_type, &
46 : mp_request_type, &
47 : mp_testany
48 : USE mathlib, ONLY: det_3x3
49 : #ifndef __NO_SOCKETS
50 : USE sockets_interface, ONLY: writebuffer, &
51 : readbuffer, &
52 : open_connect_socket, &
53 : close_socket, &
54 : uwait
55 : #endif
56 : USE virial_types, ONLY: virial_type
57 : #include "./base/base_uses.f90"
58 :
59 : IMPLICIT NONE
60 :
61 : PRIVATE
62 :
63 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'ipi_driver'
64 :
65 : PUBLIC :: run_driver
66 :
67 : CONTAINS
68 :
69 : ! **************************************************************************************************
70 : !> \brief ...
71 : !> \param force_env ...
72 : !> \param globenv ...
73 : !> \par History
74 : !> 12.2013 included in repository
75 : !> \author Ceriotti
76 : ! **************************************************************************************************
77 :
78 0 : SUBROUTINE run_driver(force_env, globenv)
79 : TYPE(force_env_type), POINTER :: force_env
80 : TYPE(global_environment_type), POINTER :: globenv
81 :
82 : CHARACTER(len=*), PARAMETER :: routineN = 'run_driver'
83 :
84 : #ifdef __NO_SOCKETS
85 : INTEGER :: handle
86 : CALL timeset(routineN, handle)
87 : CPABORT("CP2K was compiled with the __NO_SOCKETS option!")
88 : MARK_USED(globenv)
89 : MARK_USED(force_env)
90 : #else
91 : INTEGER, PARAMETER :: MSGLEN = 12
92 :
93 : CHARACTER(len=default_path_length) :: c_hostname, drv_hostname, drv_prefix
94 : CHARACTER(LEN=default_string_length) :: header
95 : INTEGER :: drv_port, handle, i_drv_unix, &
96 : idir, ii, inet, ip, iwait, &
97 : nat, output_unit, socket
98 0 : TYPE(mp_request_type), DIMENSION(2) :: wait_req
99 0 : INTEGER(KIND=int_4), POINTER :: wait_msg(:)
100 : LOGICAL :: drv_unix, fwait, hasdata, &
101 : ionode, should_stop
102 : REAL(KIND=dp) :: cellh(3, 3), cellih(3, 3), &
103 : mxmat(9), pot, vir(3, 3)
104 0 : REAL(KIND=dp), ALLOCATABLE :: combuf(:)
105 : TYPE(cell_type), POINTER :: cpcell
106 : TYPE(mp_para_env_type), POINTER :: para_env
107 : TYPE(cp_subsys_type), POINTER :: subsys
108 : TYPE(section_vals_type), POINTER :: drv_section, motion_section
109 : TYPE(virial_type), POINTER :: virial
110 : REAL(KIND=dp) :: sleeptime
111 : INTEGER, DIMENSION(3) :: perd
112 : TYPE(cell_type), POINTER :: oldcell
113 :
114 0 : CALL timeset(routineN, handle)
115 :
116 0 : CALL cite_reference(Ceriotti2014)
117 0 : CALL cite_reference(Kapil2016)
118 :
119 : ! server address parsing
120 : ! buffers and temporaries for communication
121 : ! access cp2k structures
122 :
123 0 : CPASSERT(ASSOCIATED(force_env))
124 0 : CALL force_env_get(force_env, para_env=para_env)
125 :
126 0 : hasdata = .FALSE.
127 0 : ionode = para_env%is_source()
128 :
129 0 : output_unit = cp_logger_get_default_io_unit()
130 :
131 : ! reads driver parameters from input
132 0 : motion_section => section_vals_get_subs_vals(force_env%root_section, "MOTION")
133 0 : drv_section => section_vals_get_subs_vals(motion_section, "DRIVER")
134 :
135 0 : CALL section_vals_val_get(drv_section, "HOST", c_val=drv_hostname)
136 0 : CALL section_vals_val_get(drv_section, "PORT", i_val=drv_port)
137 0 : CALL section_vals_val_get(drv_section, "UNIX", l_val=drv_unix)
138 0 : CALL section_vals_val_get(drv_section, "PREFIX", c_val=drv_prefix)
139 0 : CALL section_vals_val_get(drv_section, "SLEEP_TIME", r_val=sleeptime)
140 0 : CPASSERT(sleeptime >= 0)
141 :
142 : ! opens the socket
143 0 : socket = 0
144 0 : inet = 1
145 0 : i_drv_unix = 1 ! a bit convoluted. socket.c uses a different convention...
146 0 : IF (drv_unix) i_drv_unix = 0
147 0 : IF (output_unit > 0) THEN
148 0 : WRITE (output_unit, *) "@ i-PI DRIVER BEING LOADED"
149 0 : WRITE (output_unit, *) "@ INPUT DATA: ", TRIM(drv_hostname), drv_port, drv_unix
150 : END IF
151 :
152 0 : IF (drv_unix) THEN
153 : ! for UNIX sockets, HOST names the socket file which lives at
154 : ! /tmp/<PREFIX>_<HOST> (PREFIX must match the peer's convention,
155 : ! e.g. "ipi" for i-PI)
156 0 : c_hostname = "/tmp/"//TRIM(drv_prefix)//"_"//TRIM(drv_hostname)//C_NULL_CHAR
157 : ELSE
158 0 : c_hostname = TRIM(drv_hostname)//C_NULL_CHAR
159 : END IF
160 0 : IF (ionode) CALL open_connect_socket(socket, i_drv_unix, drv_port, c_hostname)
161 :
162 : NULLIFY (wait_msg)
163 0 : ALLOCATE (wait_msg(1))
164 : !now we have a socket, so we can initialize the CP2K environments.
165 0 : NULLIFY (cpcell)
166 0 : CALL cell_create(cpcell)
167 : driver_loop: DO
168 : ! do communication on master node only...
169 0 : header = ""
170 :
171 0 : CALL para_env%sync()
172 :
173 : ! non-blocking sync to avoid useless CPU consumption
174 0 : IF (ionode) THEN
175 0 : CALL readbuffer(socket, header, MSGLEN)
176 0 : wait_msg = 0
177 0 : DO iwait = 0, para_env%num_pe - 1
178 0 : IF (iwait /= para_env%source) THEN
179 0 : CALL para_env%send(msg=wait_msg, dest=iwait, tag=666)
180 : END IF
181 : END DO
182 : ELSE
183 : CALL para_env%irecv(msgout=wait_msg, source=para_env%source, &
184 0 : tag=666, request=wait_req(2))
185 0 : CALL mp_testany(wait_req(2:), flag=fwait)
186 0 : DO WHILE (.NOT. fwait)
187 0 : CALL mp_testany(wait_req(2:), flag=fwait)
188 0 : CALL uwait(sleeptime)
189 : END DO
190 : END IF
191 :
192 0 : CALL para_env%sync()
193 :
194 0 : CALL para_env%bcast(header)
195 :
196 0 : IF (output_unit > 0) WRITE (output_unit, *) " @ DRIVER MODE: Message from server: ", TRIM(header)
197 0 : IF (TRIM(header) == "STATUS") THEN
198 :
199 0 : CALL para_env%sync()
200 0 : IF (ionode) THEN ! does not need init (well, maybe it should, just to check atom numbers and the like... )
201 0 : IF (hasdata) THEN
202 0 : CALL writebuffer(socket, "HAVEDATA ", MSGLEN)
203 : ELSE
204 0 : CALL writebuffer(socket, "READY ", MSGLEN)
205 : END IF
206 : END IF
207 0 : CALL para_env%sync()
208 0 : ELSE IF (TRIM(header) == "POSDATA") THEN
209 0 : IF (hasdata) CPABORT("@DRIVER MODE: POSDATA received before GETFORCE")
210 0 : IF (ionode) THEN
211 0 : CALL readbuffer(socket, mxmat, 9)
212 0 : cellh = RESHAPE(mxmat, [3, 3])
213 0 : CALL readbuffer(socket, mxmat, 9)
214 0 : cellih = RESHAPE(mxmat, [3, 3])
215 0 : CALL readbuffer(socket, nat)
216 0 : cellh = TRANSPOSE(cellh)
217 0 : cellih = TRANSPOSE(cellih)
218 : END IF
219 0 : CALL para_env%bcast(cellh)
220 0 : CALL para_env%bcast(cellih)
221 0 : CALL para_env%bcast(nat)
222 0 : CALL force_env_get(force_env, subsys=subsys)
223 : ! Check before allocating/reading: nat is supplied by the socket peer.
224 0 : IF (nat <= 0 .OR. nat /= subsys%particles%n_els .OR. nat > HUGE(nat)/3) THEN
225 0 : CPABORT("@DRIVER MODE: Particle number mismatch between i-PI and CP2K input")
226 : END IF
227 0 : IF (.NOT. ALL(ieee_is_finite(cellh)) .OR. .NOT. ALL(ieee_is_finite(cellih))) THEN
228 0 : CPABORT("@DRIVER MODE: Non-finite cell received from i-PI")
229 : END IF
230 0 : IF (.NOT. ieee_is_finite(det_3x3(cellh)) .OR. det_3x3(cellh) <= 0.0_dp) THEN
231 0 : CPABORT("@DRIVER MODE: i-PI cell must be nonsingular and right-handed")
232 : END IF
233 0 : IF (.NOT. ALLOCATED(combuf)) ALLOCATE (combuf(3*nat))
234 0 : IF (ionode) CALL readbuffer(socket, combuf, nat*3)
235 0 : CALL para_env%bcast(combuf)
236 :
237 0 : IF (.NOT. ALL(ieee_is_finite(combuf))) THEN
238 0 : CPABORT("@DRIVER MODE: Non-finite positions received from i-PI")
239 : END IF
240 0 : ii = 0
241 0 : DO ip = 1, subsys%particles%n_els
242 0 : DO idir = 1, 3
243 0 : ii = ii + 1
244 0 : subsys%particles%els(ip)%r(idir) = combuf(ii)
245 : END DO
246 : END DO
247 0 : NULLIFY (oldcell)
248 0 : CALL cp_subsys_get(subsys, cell=oldcell)
249 0 : perd = oldcell%perd
250 :
251 0 : CALL init_cell(cpcell, hmat=cellh, periodic=perd)
252 0 : CALL cp_subsys_set(subsys, cell=cpcell)
253 :
254 0 : CALL force_env_calc_energy_force(force_env, calc_force=.TRUE.)
255 :
256 0 : IF (output_unit > 0) WRITE (output_unit, *) " @ DRIVER MODE: Received positions "
257 :
258 0 : combuf = 0
259 0 : ii = 0
260 0 : DO ip = 1, subsys%particles%n_els
261 0 : DO idir = 1, 3
262 0 : ii = ii + 1
263 0 : combuf(ii) = subsys%particles%els(ip)%f(idir)
264 : END DO
265 : END DO
266 0 : CALL force_env_get(force_env, potential_energy=pot)
267 0 : CALL force_env_get(force_env, cell=cpcell)
268 0 : CALL cp_subsys_get(subsys, virial=virial)
269 0 : vir = TRANSPOSE(virial%pv_virial)
270 :
271 0 : CALL external_control(should_stop, "IPI", globenv=globenv)
272 0 : IF (should_stop) EXIT driver_loop
273 :
274 0 : hasdata = .TRUE.
275 0 : ELSE IF (TRIM(header) == "GETFORCE") THEN
276 0 : IF (.NOT. hasdata) CPABORT("@DRIVER MODE: GETFORCE received without POSDATA")
277 0 : IF (output_unit > 0) WRITE (output_unit, *) " @ DRIVER MODE: Returning v,forces,stress "
278 0 : IF (ionode) THEN
279 0 : CALL writebuffer(socket, "FORCEREADY ", MSGLEN)
280 0 : CALL writebuffer(socket, pot)
281 0 : CALL writebuffer(socket, nat)
282 0 : CALL writebuffer(socket, combuf, 3*nat)
283 0 : CALL writebuffer(socket, RESHAPE(vir, [9]), 9)
284 :
285 : ! i-pi can also receive an arbitrary string, that will be printed out to the "extra"
286 : ! trajectory file. this is useful if you want to return additional information, e.g.
287 : ! atomic charges, wannier centres, etc. one must return the number of characters, then
288 : ! the string. here we just send back zero characters.
289 0 : nat = 0
290 0 : CALL writebuffer(socket, nat) ! writes out zero for the length of the "extra" field (not implemented yet!)
291 : END IF
292 : hasdata = .FALSE.
293 0 : ELSE IF (TRIM(header) == "EXIT") THEN
294 0 : IF (output_unit > 0) WRITE (output_unit, *) " @DRIVER MODE: Socket disconnected, time to exit."
295 : EXIT driver_loop
296 : ELSE
297 0 : CPABORT("@DRIVER MODE: Unknown i-PI message: "//TRIM(header))
298 : END IF
299 : END DO driver_loop
300 :
301 : ! clean up
302 0 : IF (ionode) CALL close_socket(socket)
303 0 : CALL cell_release(cpcell)
304 0 : DEALLOCATE (wait_msg)
305 : #endif
306 :
307 0 : CALL timestop(handle)
308 :
309 0 : END SUBROUTINE run_driver
310 : END MODULE ipi_driver
|