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 : MODULE farming_methods
10 : USE cp_files, ONLY: close_file,&
11 : open_file
12 : USE cp_log_handling, ONLY: cp_get_default_logger,&
13 : cp_logger_type
14 : USE cp_output_handling, ONLY: cp_print_key_finished_output,&
15 : cp_print_key_generate_filename,&
16 : cp_print_key_unit_nr
17 : USE farming_types, ONLY: farming_env_type,&
18 : init_job_type,&
19 : job_finished,&
20 : job_pending,&
21 : job_running
22 : USE input_section_types, ONLY: section_vals_get,&
23 : section_vals_get_subs_vals,&
24 : section_vals_type,&
25 : section_vals_val_get
26 : USE message_passing, ONLY: mp_para_env_type
27 : #include "./base/base_uses.f90"
28 :
29 : IMPLICIT NONE
30 : PRIVATE
31 : PUBLIC :: farming_parse_input, get_next_job
32 :
33 : ! must be negative in order to avoid confusion with job numbers
34 : INTEGER, PARAMETER, PUBLIC :: do_nothing = -1, &
35 : do_wait = -2, &
36 : do_deadlock = -3
37 :
38 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'farming_methods'
39 :
40 : CONTAINS
41 :
42 : ! **************************************************************************************************
43 : !> \brief ...
44 : !> \param farming_env ...
45 : !> \param start ...
46 : !> \param END ...
47 : !> \param current ...
48 : !> \param todo ...
49 : ! **************************************************************************************************
50 20 : SUBROUTINE get_next_job(farming_env, start, END, current, todo)
51 : TYPE(farming_env_type), POINTER :: farming_env
52 : INTEGER, INTENT(IN) :: start, END
53 : INTEGER, INTENT(INOUT) :: current
54 : INTEGER, INTENT(OUT) :: todo
55 :
56 : INTEGER :: icheck, idep, itry, ndep
57 : LOGICAL :: dep_ok
58 :
59 20 : IF (farming_env%cycle) THEN
60 0 : IF (current < start) THEN
61 0 : current = start
62 : ELSE
63 0 : current = current + 1
64 : END IF
65 0 : IF (current > END) THEN
66 0 : todo = do_nothing
67 : ELSE
68 0 : todo = MODULO(current - 1, farming_env%njobs) + 1
69 : END IF
70 : ELSE
71 : ! find a pending job
72 20 : itry = start
73 20 : todo = do_nothing
74 110 : DO itry = start, END
75 110 : IF (farming_env%job(itry)%status == job_pending) THEN
76 :
77 : ! see if all dependencies are OK
78 22 : ndep = SIZE(farming_env%job(itry)%dependencies)
79 22 : dep_ok = .TRUE.
80 30 : dep: DO idep = 1, ndep
81 128 : DO icheck = start, END
82 110 : IF (farming_env%job(icheck)%status /= job_finished) THEN
83 42 : IF (farming_env%job(icheck)%id == farming_env%job(itry)%dependencies(idep)) THEN
84 : dep_ok = .FALSE.
85 : EXIT dep
86 : END IF
87 : END IF
88 : END DO
89 : END DO dep
90 :
91 : ! if there are pending jobs, the minion can not be told to stop
92 : ! at least wait if there are unresolved dependencies
93 22 : IF (dep_OK) THEN
94 18 : todo = itry
95 18 : EXIT
96 : ELSE
97 4 : todo = do_wait
98 : END IF
99 : END IF
100 : END DO
101 : ! If we have to wait, but there are no running jobs we are deadlocked
102 : ! which we signal
103 20 : IF (todo == do_wait) THEN
104 : dep_OK = .FALSE.
105 0 : DO itry = start, END
106 0 : IF (farming_env%job(itry)%status == job_running) dep_OK = .TRUE.
107 : END DO
108 0 : IF (.NOT. dep_OK) todo = do_deadlock
109 : END IF
110 : END IF
111 20 : END SUBROUTINE get_next_job
112 :
113 : ! **************************************************************************************************
114 : !> \brief ...
115 : !> \param farming_env ...
116 : !> \param root_section ...
117 : !> \param para_env ...
118 : ! **************************************************************************************************
119 24 : SUBROUTINE farming_parse_input(farming_env, root_section, para_env)
120 : TYPE(farming_env_type), POINTER :: farming_env
121 : TYPE(section_vals_type), POINTER :: root_section
122 : TYPE(mp_para_env_type), POINTER :: para_env
123 :
124 : CHARACTER(LEN=3) :: text
125 : INTEGER :: i, iunit, n_rep_val, num_minions, &
126 : output_unit, stat
127 24 : INTEGER, DIMENSION(:), POINTER :: dependencies, i_vals
128 : LOGICAL :: exists, explicit, has_dep
129 : TYPE(cp_logger_type), POINTER :: logger
130 : TYPE(section_vals_type), POINTER :: farming_section, jobs_section, print_key
131 :
132 24 : NULLIFY (farming_section, jobs_section, print_key, logger, dependencies, i_vals)
133 48 : logger => cp_get_default_logger()
134 24 : farming_env%group_size_wish_set = .FALSE.
135 24 : farming_env%ngroup_wish_set = .FALSE.
136 24 : farming_section => section_vals_get_subs_vals(root_section, "FARMING")
137 :
138 24 : IF (ASSOCIATED(farming_env%group_partition)) THEN
139 0 : DEALLOCATE (farming_env%group_partition)
140 : END IF
141 :
142 : ! The following input order is used
143 : ! 1) GROUP_PARTITION
144 : ! 2) NGROUP
145 : ! 3) GROUP_SIZE (default 8)
146 : CALL section_vals_val_get(farming_section, "GROUP_PARTITION", &
147 24 : n_rep_val=n_rep_val)
148 24 : IF (n_rep_val > 0) THEN
149 : CALL section_vals_val_get(farming_section, "GROUP_PARTITION", &
150 0 : i_vals=i_vals)
151 0 : ALLOCATE (farming_env%group_partition(0:SIZE(i_vals) - 1))
152 0 : farming_env%group_partition(:) = i_vals
153 0 : farming_env%ngroup_wish_set = .TRUE.
154 0 : farming_env%ngroup_wish = SIZE(i_vals)
155 : ELSE
156 : CALL section_vals_val_get(farming_section, "NGROUP", &
157 24 : n_rep_val=n_rep_val)
158 24 : IF (n_rep_val > 0) THEN
159 : CALL section_vals_val_get(farming_section, "NGROUP", &
160 20 : i_val=farming_env%ngroup_wish)
161 20 : farming_env%ngroup_wish_set = .TRUE.
162 : ELSE
163 : CALL section_vals_val_get(farming_section, "GROUP_SIZE", &
164 4 : i_val=farming_env%group_size_wish)
165 4 : farming_env%group_size_wish_set = .TRUE.
166 : END IF
167 : END IF
168 : CALL section_vals_val_get(farming_section, "STRIDE", &
169 24 : i_val=farming_env%stride)
170 :
171 : CALL section_vals_val_get(farming_section, "RESTART_FILE_NAME", &
172 24 : explicit=explicit)
173 24 : IF (explicit) THEN
174 : CALL section_vals_val_get(farming_section, "RESTART_FILE_NAME", &
175 2 : c_val=farming_env%restart_file_name)
176 : ELSE
177 22 : print_key => section_vals_get_subs_vals(farming_section, "RESTART")
178 : farming_env%restart_file_name = cp_print_key_generate_filename(logger, print_key, extension=".restart", &
179 22 : my_local=.FALSE.)
180 : END IF
181 :
182 : CALL section_vals_val_get(farming_section, "DO_RESTART", &
183 24 : l_val=farming_env%restart)
184 : CALL section_vals_val_get(farming_section, "MAX_JOBS_PER_GROUP", &
185 24 : i_val=farming_env%max_steps)
186 : CALL section_vals_val_get(farming_section, "CYCLE", &
187 24 : l_val=farming_env%cycle)
188 : CALL section_vals_val_get(farming_section, "WAIT_TIME", &
189 24 : r_val=farming_env%wait_time)
190 :
191 : CALL section_vals_val_get(farming_section, "CAPTAIN_MINION", &
192 24 : l_val=farming_env%captain_minion)
193 :
194 24 : jobs_section => section_vals_get_subs_vals(farming_section, "JOB")
195 24 : CALL section_vals_get(jobs_section, n_repetition=farming_env%njobs)
196 :
197 232 : ALLOCATE (farming_env%Job(farming_env%njobs))
198 184 : CALL init_job_type(farming_env%job)
199 :
200 24 : has_dep = .FALSE.
201 184 : DO i = 1, farming_env%njobs
202 : CALL section_vals_val_get(jobs_section, i_rep_section=i, &
203 160 : keyword_name="DIRECTORY", c_val=farming_env%Job(i)%cwd)
204 : CALL section_vals_val_get(jobs_section, i_rep_section=i, &
205 160 : keyword_name="INPUT_FILE_NAME", c_val=farming_env%Job(i)%input)
206 : CALL section_vals_val_get(jobs_section, i_rep_section=i, &
207 160 : keyword_name="OUTPUT_FILE_NAME", c_val=farming_env%Job(i)%output)
208 :
209 : ! if job id is not specified the job id is the index
210 : CALL section_vals_val_get(jobs_section, i_rep_section=i, &
211 160 : keyword_name="JOB_ID", n_rep_val=n_rep_val)
212 160 : IF (n_rep_val == 0) THEN
213 142 : farming_env%Job(i)%id = i
214 : ELSE
215 : CALL section_vals_val_get(jobs_section, i_rep_section=i, &
216 18 : keyword_name="JOB_ID", i_val=farming_env%Job(i)%id)
217 : END IF
218 :
219 : ! get dependencies
220 : CALL section_vals_val_get(jobs_section, i_rep_section=i, &
221 160 : keyword_name="DEPENDENCIES", n_rep_val=n_rep_val)
222 344 : IF (n_rep_val == 0) THEN
223 146 : ALLOCATE (farming_env%Job(i)%dependencies(0))
224 : ELSE
225 : CALL section_vals_val_get(jobs_section, i_rep_section=i, &
226 14 : keyword_name="DEPENDENCIES", i_vals=dependencies)
227 42 : ALLOCATE (farming_env%Job(i)%dependencies(SIZE(dependencies, 1)))
228 46 : farming_env%Job(i)%dependencies = dependencies
229 14 : IF (SIZE(dependencies, 1) /= 0) has_dep = .TRUE.
230 : END IF
231 : END DO
232 :
233 24 : IF (has_dep) THEN
234 2 : CPASSERT(farming_env%captain_minion)
235 2 : CPASSERT(.NOT. farming_env%cycle)
236 : END IF
237 :
238 : output_unit = cp_print_key_unit_nr(logger, farming_section, "PROGRAM_RUN_INFO", &
239 24 : extension=".log")
240 :
241 : ! Captain/Minion not supported
242 24 : IF (para_env%num_pe == 1) THEN
243 0 : farming_env%captain_minion = .FALSE.
244 0 : WRITE (output_unit, FMT="(T2,A)") "FARMING| Captain-Minion setup not supported for serial runs"
245 : END IF
246 24 : IF (farming_env%captain_minion) THEN
247 4 : num_minions = para_env%num_pe - 1
248 : ELSE
249 20 : num_minions = para_env%num_pe
250 : END IF
251 :
252 24 : IF (output_unit > 0) THEN
253 12 : WRITE (output_unit, FMT="(T2,A,T71,I10)") "FARMING| Number of jobs found", farming_env%njobs
254 12 : IF (farming_env%ngroup_wish_set) THEN
255 10 : WRITE (output_unit, FMT="(T2,A,T71,I10)") "FARMING| Ngroup wish:", farming_env%ngroup_wish
256 10 : IF (ASSOCIATED(farming_env%group_partition)) THEN
257 0 : WRITE (output_unit, FMT="(T2,A)", ADVANCE="NO") "FARMING| User partition:"
258 0 : DO i = 0, SIZE(farming_env%group_partition) - 1
259 0 : IF (MODULO(i, 4) == 0) WRITE (output_unit, *)
260 0 : WRITE (output_unit, FMT='(I4)', ADVANCE="NO") farming_env%group_partition(i)
261 : END DO
262 0 : WRITE (output_unit, *)
263 0 : IF (SUM(farming_env%group_partition) /= num_minions) THEN
264 : WRITE (output_unit, FMT="(T2,A,T61,I10,T71,I10)") &
265 0 : "FARMING| WARNING : group partition CPUs not equal to the available number (ignoring Captain) ", &
266 0 : num_minions, SUM(farming_env%group_partition)
267 0 : WRITE (output_unit, FMT="(T2,A)") "FARMING| partition data ignored" ! any better idea ??
268 0 : DEALLOCATE (farming_env%group_partition)
269 : END IF
270 : END IF
271 : END IF
272 12 : IF (farming_env%group_size_wish_set) THEN
273 2 : WRITE (output_unit, FMT="(T2,A,T71,I10)") "FARMING| Group size wish:", &
274 4 : farming_env%group_size_wish
275 : END IF
276 12 : WRITE (output_unit, FMT="(T2,A,T71,I10)") "FARMING| Max steps :", farming_env%max_steps
277 12 : IF (farming_env%cycle) THEN
278 1 : text = "YES"
279 : ELSE
280 11 : text = " NO"
281 : END IF
282 12 : WRITE (output_unit, FMT="(T2,A,T78,A3)") "FARMING| Cyclic jobs execution:", text
283 12 : IF (farming_env%restart) THEN
284 1 : text = "YES"
285 : ELSE
286 11 : text = " NO"
287 : END IF
288 12 : WRITE (output_unit, FMT="(T2,A,T78,A3)") "FARMING| Restarting farm:", text
289 12 : farming_env%restart_n = 1
290 12 : IF (farming_env%restart) THEN
291 : ! A missing restart file is not an error: the farm then starts with the first job
292 1 : INQUIRE (FILE=farming_env%restart_file_name, EXIST=exists)
293 1 : IF (exists) THEN
294 : CALL open_file(unit_number=iunit, &
295 1 : file_name=farming_env%restart_file_name)
296 1 : READ (UNIT=iunit, FMT=*, IOSTAT=stat) farming_env%restart_n
297 1 : IF (stat /= 0) THEN
298 0 : farming_env%restart_n = 1
299 : WRITE (UNIT=output_unit, FMT="(T2,A)") &
300 : "FARMING| ---- WARNING ---- failed to read from ("// &
301 0 : TRIM(farming_env%restart_file_name)//") starting at 1"
302 : ELSE
303 : WRITE (UNIT=output_unit, FMT="(T2,A)") &
304 1 : "FARMING| restarting from ("//TRIM(farming_env%restart_file_name)//")"
305 : WRITE (UNIT=output_unit, FMT="(T2,A,T71,I10)") &
306 1 : "FARMING| restarting at ", farming_env%restart_n
307 : END IF
308 1 : CALL close_file(unit_number=iunit)
309 : ELSE
310 : WRITE (UNIT=output_unit, FMT="(T2,A)") &
311 : "FARMING| ---- WARNING ---- restart file ("// &
312 0 : TRIM(farming_env%restart_file_name)//") not found, starting at 1"
313 : END IF
314 : END IF
315 :
316 : CALL cp_print_key_finished_output(output_unit, logger, farming_section, &
317 12 : "PROGRAM_RUN_INFO")
318 : END IF
319 24 : CALL para_env%bcast(farming_env%restart_n)
320 :
321 72 : END SUBROUTINE farming_parse_input
322 :
323 : END MODULE farming_methods
|