(git:98357aa)
Loading...
Searching...
No Matches
swarm_mpi.F
Go to the documentation of this file.
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 Handles the MPI communication of the swarm framework.
10!> \author Ole Schuett
11! **************************************************************************************************
13 USE cp_files, ONLY: close_file,&
26 USE kinds, ONLY: default_path_length,&
38#include "../base/base_uses.f90"
39
40 IMPLICIT NONE
41 PRIVATE
42
43 CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'swarm_mpi'
44
48
50 TYPE(mp_para_env_type), POINTER :: world => null()
51 TYPE(mp_para_env_type), POINTER :: worker => null()
52 TYPE(mp_para_env_type), POINTER :: master => null()
53 INTEGER, DIMENSION(:), ALLOCATABLE :: wid2group
54 CHARACTER(LEN=default_path_length) :: master_output_path = ""
55 END TYPE swarm_mpi_type
56
57CONTAINS
58
59! **************************************************************************************************
60!> \brief Initialize MPI communicators for a swarm run.
61!> \param swarm_mpi ...
62!> \param world_para_env ...
63!> \param root_section ...
64!> \param n_workers ...
65!> \param worker_id ...
66!> \param iw ...
67!> \author Ole Schuett
68! **************************************************************************************************
69 SUBROUTINE swarm_mpi_init(swarm_mpi, world_para_env, root_section, n_workers, worker_id, iw)
71 TYPE(mp_para_env_type), POINTER :: world_para_env
72 TYPE(section_vals_type), POINTER :: root_section
73 INTEGER, INTENT(IN) :: n_workers
74 INTEGER, INTENT(OUT) :: worker_id
75 INTEGER, INTENT(IN) :: iw
76
77 INTEGER :: n_groups_created, pe_per_worker, &
78 subgroup_rank, subgroup_size
79 TYPE(mp_comm_type) :: subgroup
80 LOGICAL :: im_the_master
81 INTEGER, DIMENSION(:), POINTER :: group_distribution_p
82 INTEGER, DIMENSION(0:world_para_env%num_pe-2), &
83 TARGET :: group_distribution
84
85! ====== Setup of MPI-Groups ======
86
87 worker_id = -1
88 swarm_mpi%world => world_para_env
89
90 IF (mod(swarm_mpi%world%num_pe - 1, n_workers) /= 0) THEN
91 cpabort("number of processors-1 is not divisible by n_workers.")
92 END IF
93 IF (swarm_mpi%world%num_pe < n_workers + 1) THEN
94 cpabort("There are not enough processes for n_workers + 1. Aborting.")
95 END IF
96
97 pe_per_worker = (swarm_mpi%world%num_pe - 1)/n_workers
98
99 IF (iw > 0) THEN
100 WRITE (iw, '(A,45X,I8)') " SWARM| Number of mpi ranks", swarm_mpi%world%num_pe
101 WRITE (iw, '(A,47X,I8)') " SWARM| Number of workers", n_workers
102 END IF
103
104 ! the last task becomes the master. Preseves node-alignment of other tasks.
105 im_the_master = (swarm_mpi%world%mepos == swarm_mpi%world%num_pe - 1)
106
107 ! First split split para_env into a master- and a workers-groups...
108 IF (im_the_master) THEN
109 ALLOCATE (swarm_mpi%master)
110 CALL swarm_mpi%master%from_split(swarm_mpi%world, 1)
111 subgroup_size = swarm_mpi%master%num_pe
112 subgroup_rank = swarm_mpi%master%mepos
113 IF (swarm_mpi%master%num_pe /= 1) cpabort("mp_comm_split_direct failed (master)")
114 ELSE
115 CALL subgroup%from_split(swarm_mpi%world, 2)
116 subgroup_size = subgroup%num_pe
117 subgroup_rank = subgroup%mepos
118 IF (subgroup_size /= swarm_mpi%world%num_pe - 1) cpabort("mp_comm_split_direct failed (worker)")
119 END IF
120
121 ALLOCATE (swarm_mpi%wid2group(n_workers))
122 swarm_mpi%wid2group = 0
123
124 IF (.NOT. im_the_master) THEN
125 ! ...then split workers-group into n_workers groups - one for each worker.
126 group_distribution_p => group_distribution
127 ALLOCATE (swarm_mpi%worker)
128 CALL swarm_mpi%worker%from_split(subgroup, n_groups_created, group_distribution_p, n_subgroups=n_workers)
129 worker_id = group_distribution(subgroup_rank) + 1 ! shall start by 1
130 IF (n_groups_created /= n_workers) cpabort("mp_comm_split failed.")
131 CALL subgroup%free()
132
133 !WRITE (*,*) "this is worker ", worker_id, swarm_mpi%worker%mepos, swarm_mpi%worker%num_pe
134
135 ! collect world-ranks of each worker groups rank-0 node
136 IF (swarm_mpi%worker%mepos == 0) THEN
137 swarm_mpi%wid2group(worker_id) = swarm_mpi%world%mepos
138 END IF
139
140 END IF
141
142 CALL swarm_mpi%world%sum(swarm_mpi%wid2group)
143 !WRITE (*,*), "wid2group table: ",swarm_mpi%wid2group
144
145 CALL logger_init_master(swarm_mpi)
146 CALL logger_init_worker(swarm_mpi, root_section, worker_id)
147 END SUBROUTINE swarm_mpi_init
148
149! **************************************************************************************************
150!> \brief Helper routine for swarm_mpi_init, configures the master's logger.
151!> \param swarm_mpi ...
152!> \author Ole Schuett
153! **************************************************************************************************
154 SUBROUTINE logger_init_master(swarm_mpi)
156
157 INTEGER :: output_unit
158 TYPE(cp_logger_type), POINTER :: logger
159
160! broadcast master_output_path to all ranks
161
162 IF (swarm_mpi%world%is_source()) THEN
163 logger => cp_get_default_logger()
164 output_unit = logger%default_local_unit_nr
165 swarm_mpi%master_output_path = output_unit2path(output_unit)
166 IF (output_unit /= default_output_unit) THEN
167 CLOSE (output_unit)
168 END IF
169 END IF
170
171 CALL swarm_mpi%world%bcast(swarm_mpi%master_output_path)
172
173 IF (ASSOCIATED(swarm_mpi%master)) THEN
174 CALL error_add_new_logger(swarm_mpi%master, swarm_mpi%master_output_path)
175 END IF
176 END SUBROUTINE logger_init_master
177
178! **************************************************************************************************
179!> \brief Helper routine for logger_init_master, inquires filename for given unit.
180!> \param output_unit ...
181!> \return ...
182!> \author Ole Schuett
183! **************************************************************************************************
184 FUNCTION output_unit2path(output_unit) RESULT(output_path)
185 INTEGER, INTENT(IN) :: output_unit
186 CHARACTER(LEN=default_path_length) :: output_path
187
188 output_path = "__STD_OUT__"
189 IF (output_unit /= default_output_unit) THEN
190 INQUIRE (unit=output_unit, name=output_path)
191 END IF
192 END FUNCTION output_unit2path
193
194! **************************************************************************************************
195!> \brief Helper routine for swarm_mpi_init, configures the workers's logger.
196!> \param swarm_mpi ...
197!> \param root_section ...
198!> \param worker_id ...
199!> \author Ole Schuett
200! **************************************************************************************************
201 SUBROUTINE logger_init_worker(swarm_mpi, root_section, worker_id)
203 TYPE(section_vals_type), POINTER :: root_section
204 INTEGER :: worker_id
205
206 CHARACTER(LEN=default_path_length) :: output_path
207 CHARACTER(len=default_string_length) :: new_project_name, project_name, &
208 worker_name
209 TYPE(cp_iteration_info_type), POINTER :: new_iter_info
210 TYPE(cp_logger_type), POINTER :: old_logger
211
212 NULLIFY (old_logger, new_iter_info)
213 IF (ASSOCIATED(swarm_mpi%worker)) THEN
214 old_logger => cp_get_default_logger()
215 project_name = old_logger%iter_info%project_name
216 IF (worker_id > 99999) THEN
217 cpabort("Did not expect so many workers.")
218 END IF
219 WRITE (worker_name, "(A,I5.5)") 'WORKER', worker_id
220 IF (len_trim(project_name) + 1 + len_trim(worker_name) > default_string_length) THEN
221 cpabort("project name too long")
222 END IF
223 output_path = trim(project_name)//"-"//trim(worker_name)//".out"
224 new_project_name = trim(project_name)//"-"//trim(worker_name)
225 CALL section_vals_val_set(root_section, "GLOBAL%PROJECT_NAME", c_val=new_project_name)
226 CALL cp_iteration_info_create(new_iter_info, new_project_name)
227 CALL error_add_new_logger(swarm_mpi%worker, output_path, new_iter_info)
228 CALL cp_iteration_info_release(new_iter_info)
229 END IF
230 END SUBROUTINE logger_init_worker
231
232! **************************************************************************************************
233!> \brief Helper routine for logger_init_master and logger_init_worker
234!> \param para_env ...
235!> \param output_path ...
236!> \param iter_info ...
237!> \author Ole Schuett
238! **************************************************************************************************
239 SUBROUTINE error_add_new_logger(para_env, output_path, iter_info)
240 TYPE(mp_para_env_type), POINTER :: para_env
241 CHARACTER(LEN=default_path_length) :: output_path
242 TYPE(cp_iteration_info_type), OPTIONAL, POINTER :: iter_info
243
244 INTEGER :: output_unit
245 TYPE(cp_logger_type), POINTER :: new_logger, old_logger
246
247 NULLIFY (new_logger, old_logger)
248 output_unit = -1
249 IF (para_env%is_source()) THEN
250 ! open output_unit according to output_path
251 output_unit = default_output_unit
252 IF (output_path /= "__STD_OUT__") THEN
253 CALL open_file(file_name=output_path, file_status="UNKNOWN", &
254 file_action="WRITE", file_position="APPEND", unit_number=output_unit)
255 END IF
256 END IF
257
258 old_logger => cp_get_default_logger()
259 CALL cp_logger_create(new_logger, para_env=para_env, &
260 default_global_unit_nr=output_unit, close_global_unit_on_dealloc=.false., &
261 template_logger=old_logger, iter_info=iter_info)
262
263 CALL cp_add_default_logger(new_logger)
264 CALL cp_logger_release(new_logger)
265 END SUBROUTINE error_add_new_logger
266
267! **************************************************************************************************
268!> \brief Finalizes the MPI communicators of a swarm run.
269!> \param swarm_mpi ...
270!> \param root_section ...
271!> \author Ole Schuett
272! **************************************************************************************************
273 SUBROUTINE swarm_mpi_finalize(swarm_mpi, root_section)
275 TYPE(section_vals_type), POINTER :: root_section
276
277 CALL swarm_mpi%world%sync()
278 CALL logger_finalize(swarm_mpi, root_section)
279
280 IF (ASSOCIATED(swarm_mpi%worker)) CALL mp_para_env_release(swarm_mpi%worker)
281 IF (ASSOCIATED(swarm_mpi%master)) CALL mp_para_env_release(swarm_mpi%master)
282 NULLIFY (swarm_mpi%worker, swarm_mpi%master)
283 DEALLOCATE (swarm_mpi%wid2group)
284 END SUBROUTINE swarm_mpi_finalize
285
286! **************************************************************************************************
287!> \brief Helper routine for swarm_mpi_finalize, restores the original loggers
288!> \param swarm_mpi ...
289!> \param root_section ...
290!> \author Ole Schuett
291! **************************************************************************************************
292 SUBROUTINE logger_finalize(swarm_mpi, root_section)
294 TYPE(section_vals_type), POINTER :: root_section
295
296 INTEGER :: output_unit
297 TYPE(cp_logger_type), POINTER :: logger, old_logger
298
299 NULLIFY (logger, old_logger)
300 logger => cp_get_default_logger()
301 output_unit = logger%default_local_unit_nr
302 IF (output_unit > 0 .AND. output_unit /= default_output_unit) THEN
303 CALL close_file(output_unit)
304 END IF
305
306 CALL cp_rm_default_logger() !pops the top-most logger
307 old_logger => cp_get_default_logger()
308
309 ! restore GLOBAL%PROJECT_NAME
310 CALL section_vals_val_set(root_section, "GLOBAL%PROJECT_NAME", &
311 c_val=old_logger%iter_info%project_name)
312
313 CALL swarm_mpi%world%sync()
314
315 ! do this only on master's rank 0
316 IF (swarm_mpi%world%is_source() .AND. output_unit /= default_output_unit) THEN
317 output_unit = old_logger%default_local_unit_nr
318 OPEN (unit=output_unit, file=swarm_mpi%master_output_path, &
319 status="UNKNOWN", action="WRITE", position="APPEND")
320 END IF
321 END SUBROUTINE logger_finalize
322
323! **************************************************************************************************
324!> \brief Sends a report via MPI
325!> \param swarm_mpi ...
326!> \param report ...
327!> \author Ole Schuett
328! **************************************************************************************************
329 SUBROUTINE swarm_mpi_send_report(swarm_mpi, report)
331 TYPE(swarm_message_type) :: report
332
333 INTEGER :: dest, tag
334
335! Only rank-0 of worker group sends its report
336
337 IF (swarm_mpi%worker%is_source()) THEN
338 dest = swarm_mpi%world%num_pe - 1
339 tag = 42
340 CALL swarm_message_mpi_send(report, group=swarm_mpi%world, dest=dest, tag=tag)
341 END IF
342
343 END SUBROUTINE swarm_mpi_send_report
344
345! **************************************************************************************************
346!> \brief Receives a report via MPI
347!> \param swarm_mpi ...
348!> \param report ...
349!> \author Ole Schuett
350! **************************************************************************************************
351 SUBROUTINE swarm_mpi_recv_report(swarm_mpi, report)
353 TYPE(swarm_message_type), INTENT(OUT) :: report
354
355 INTEGER :: src, tag
356
357 tag = 42
358 src = mp_any_source
359
360 CALL swarm_message_mpi_recv(report, group=swarm_mpi%world, src=src, tag=tag)
361
362 END SUBROUTINE swarm_mpi_recv_report
363
364! **************************************************************************************************
365!> \brief Sends a command via MPI
366!> \param swarm_mpi ...
367!> \param cmd ...
368!> \author Ole Schuett
369! **************************************************************************************************
370 SUBROUTINE swarm_mpi_send_command(swarm_mpi, cmd)
372 TYPE(swarm_message_type) :: cmd
373
374 INTEGER :: dest, tag, worker_id
375
376 CALL swarm_message_get(cmd, "worker_id", worker_id)
377 tag = 42
378 dest = swarm_mpi%wid2group(worker_id)
379
380 CALL swarm_message_mpi_send(cmd, group=swarm_mpi%world, dest=dest, tag=tag)
381
382 END SUBROUTINE swarm_mpi_send_command
383
384! **************************************************************************************************
385!> \brief Receives a command via MPI and broadcasts it within a worker.
386!> \param swarm_mpi ...
387!> \param cmd ...
388!> \author Ole Schuett
389! **************************************************************************************************
390 SUBROUTINE swarm_mpi_recv_command(swarm_mpi, cmd)
392 TYPE(swarm_message_type), INTENT(OUT) :: cmd
393
394 INTEGER :: src, tag
395
396! This is a two step communication schema.
397! First: The rank-0 of the worker groups receives the command from the master.
398
399 IF (swarm_mpi%worker%is_source()) THEN
400 src = swarm_mpi%world%num_pe - 1 !
401 tag = 42
402 CALL swarm_message_mpi_recv(cmd, group=swarm_mpi%world, src=src, tag=tag)
403
404 END IF
405
406! ! Second: The command is broadcasted within the worker group.
407 CALL swarm_message_mpi_bcast(cmd, src=swarm_mpi%worker%source, group=swarm_mpi%worker)
408
409 END SUBROUTINE swarm_mpi_recv_command
410
411END MODULE swarm_mpi
412
Returns an entry from a swarm-message.
Utility routines to open and close files. Tracking of preconnections.
Definition cp_files.F:16
subroutine, public open_file(file_name, file_status, file_form, file_action, file_position, file_pad, unit_number, debug, skip_get_unit_number, file_access)
Opens the requested file using a free unit number.
Definition cp_files.F:311
subroutine, public close_file(unit_number, file_status, keep_preconnection)
Close an open file given by its logical unit number. Optionally, keep the file and unit preconnected.
Definition cp_files.F:122
Collection of routines to handle the iteration info.
pure subroutine, public cp_iteration_info_create(iteration_info, project_name)
creates an output info object
subroutine, public cp_iteration_info_release(iteration_info)
releases the iteration_info (see doc/ReferenceCounting.html)
various routines to log and control the output. The idea is that decisions about where to log should ...
subroutine, public cp_rm_default_logger()
the cousin of cp_add_default_logger, decrements the stack, so that the default logger is what it has ...
subroutine, public cp_logger_release(logger)
releases this logger
subroutine, public cp_logger_create(logger, para_env, print_level, default_global_unit_nr, default_local_unit_nr, global_filename, local_filename, close_global_unit_on_dealloc, iter_info, close_local_unit_on_dealloc, suffix, template_logger)
initializes a logger
subroutine, public cp_add_default_logger(logger)
adds a default logger. MUST be called before logging occours
type(cp_logger_type) function, pointer, public cp_get_default_logger()
returns the default logger
objects that represent the structure of input sections and the data contained in an input section
subroutine, public section_vals_val_set(section_vals, keyword_name, i_rep_section, i_rep_val, val, l_val, i_val, r_val, c_val, l_vals_ptr, i_vals_ptr, r_vals_ptr, c_vals_ptr)
sets the requested value
Defines the basic variable types.
Definition kinds.F:23
integer, parameter, public default_string_length
Definition kinds.F:57
integer, parameter, public default_path_length
Definition kinds.F:58
Machine interface based on Fortran 2003 and POSIX.
Definition machine.F:17
integer, parameter, public default_output_unit
Definition machine.F:46
Interface to the message passing library MPI.
subroutine, public mp_para_env_release(para_env)
releases the para object (to be called when you don't want anymore the shared copy of this object)
integer, parameter, public mp_any_source
Swarm-message, a convenient data-container for with build-in serialization.
subroutine, public swarm_message_mpi_send(msg, group, dest, tag)
Sends a swarm message via MPI.
subroutine, public swarm_message_mpi_bcast(msg, src, group)
Broadcasts a swarm message via MPI.
subroutine, public swarm_message_mpi_recv(msg, group, src, tag)
Receives a swarm message via MPI.
Handles the MPI communication of the swarm framework.
Definition swarm_mpi.F:12
subroutine, public swarm_mpi_init(swarm_mpi, world_para_env, root_section, n_workers, worker_id, iw)
Initialize MPI communicators for a swarm run.
Definition swarm_mpi.F:70
subroutine, public swarm_mpi_send_command(swarm_mpi, cmd)
Sends a command via MPI.
Definition swarm_mpi.F:371
subroutine, public swarm_mpi_recv_report(swarm_mpi, report)
Receives a report via MPI.
Definition swarm_mpi.F:352
subroutine, public swarm_mpi_recv_command(swarm_mpi, cmd)
Receives a command via MPI and broadcasts it within a worker.
Definition swarm_mpi.F:391
subroutine, public swarm_mpi_send_report(swarm_mpi, report)
Sends a report via MPI.
Definition swarm_mpi.F:330
subroutine, public swarm_mpi_finalize(swarm_mpi, root_section)
Finalizes the MPI communicators of a swarm run.
Definition swarm_mpi.F:274
contains the information about the current state of the program to be able to decide if output is nec...
type of a logger, at the moment it contains just a print level starting at which level it should be l...
stores all the informations relevant to an mpi environment