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 Routines for optimizing load balance between processes in HFX calculations
10 : !> \par History
11 : !> 04.2008 created [Manuel Guidon]
12 : !> \author Manuel Guidon
13 : ! **************************************************************************************************
14 : MODULE hfx_load_balance_methods
15 : USE cell_types, ONLY: cell_type
16 : USE cp_files, ONLY: close_file, &
17 : open_file
18 : USE message_passing, ONLY: mp_para_env_type
19 : USE hfx_pair_list_methods, ONLY: build_atomic_pair_list, &
20 : build_pair_list
21 : USE hfx_types, ONLY: &
22 : hfx_basis_type, hfx_block_range_type, hfx_distribution, hfx_load_balance_type, hfx_p_kind, &
23 : hfx_screen_coeff_type, hfx_set_distr_energy, hfx_set_distr_forces, hfx_type, &
24 : pair_list_type, pair_set_list_type
25 : USE input_constants, ONLY: hfx_do_eval_energy, &
26 : hfx_do_eval_forces
27 : USE kinds, ONLY: dp, &
28 : int_8
29 : USE message_passing, ONLY: mp_waitall, mp_request_type
30 : USE parallel_rng_types, ONLY: UNIFORM, &
31 : rng_stream_type
32 : USE particle_types, ONLY: particle_type
33 : USE util, ONLY: sort
34 : #include "./base/base_uses.f90"
35 :
36 : IMPLICIT NONE
37 : PRIVATE
38 :
39 : PUBLIC :: hfx_load_balance, &
40 : hfx_update_load_balance, &
41 : collect_load_balance_info, cost_model, p1_energy, p2_energy, p3_energy
42 :
43 : CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'hfx_load_balance_methods'
44 :
45 : REAL(KIND=dp), PARAMETER :: p1_energy(12) = [2.9461408209700424_dp, 1.0624718662999657_dp, &
46 : -1.91570128356921242E-002_dp, -1.6668495454436603_dp, &
47 : 1.7512639006523709_dp, -9.76074323945336081E-002_dp, &
48 : 2.6230786127311889_dp, -0.31870737623014189_dp, &
49 : 7.9588203912690973_dp, 1.8331423413134813_dp, &
50 : -0.15427618665346299_dp, 0.19749436090711650_dp]
51 : REAL(KIND=dp), PARAMETER :: p2_energy(12) = [2.3104682960662593_dp, 1.8744052737304417_dp, &
52 : -9.36564055598656797E-002_dp, 0.64284973765086939_dp, &
53 : 1.0137565430060556_dp, -6.80088178288954567E-003_dp, &
54 : 1.1692629207374552_dp, -2.6314710080507573_dp, &
55 : 19.237814781880786_dp, 1.0505934173661349_dp, &
56 : 0.80382371955699250_dp, 0.49903401991818103_dp]
57 : REAL(KIND=dp), PARAMETER :: p3_energy(2) = [7.82336287670072350E-002_dp, 0.38073304105744837_dp]
58 : REAL(KIND=dp), PARAMETER :: p1_forces(12) = [2.5746279948798874_dp, 1.3420575378609276_dp, &
59 : -9.41673106447732111E-002_dp, 0.94568006899317825_dp, &
60 : -1.4511897117448544_dp, 0.59178934677316952_dp, &
61 : 2.7291149361757236_dp, -0.50555512044800210_dp, &
62 : 8.3508180969609871_dp, 1.6829982496141809_dp, &
63 : -0.74895370472152600_dp, 0.43801726744197500_dp]
64 : REAL(KIND=dp), PARAMETER :: p2_forces(12) = [2.6398568961569020_dp, 2.3024918834564101_dp, &
65 : 5.33216585432061581E-003_dp, 0.45572145697283628_dp, &
66 : 1.8119743851500618_dp, -0.12533918548421166_dp, &
67 : -1.4040312084552751_dp, -4.5331650463917859_dp, &
68 : 12.593431549069477_dp, 1.1311978374487595_dp, &
69 : 1.4245996087624646_dp, 1.1425350529853495_dp]
70 : REAL(KIND=dp), PARAMETER :: p3_forces(2) = [0.12051930516830946_dp, 1.3828051586144336_dp]
71 :
72 : !***
73 :
74 : CONTAINS
75 :
76 : ! **************************************************************************************************
77 : !> \brief Distributes the computation of eri's to all available processes.
78 : !> \param x_data Object that stores the indices array
79 : !> \param eps_schwarz screening parameter
80 : !> \param particle_set , atomic_kind_set, para_env ...
81 : !> \param max_set Maximum number of set to be considered
82 : !> \param para_env para_env
83 : !> \param coeffs_set screening functions
84 : !> \param coeffs_kind screening functions
85 : !> \param is_assoc_atomic_block_global KS-matrix sparsity
86 : !> \param do_periodic flag for periodicity
87 : !> \param load_balance_parameter Parameters for Monte-Carlo routines
88 : !> \param kind_of helper array for mapping
89 : !> \param basis_parameter Basis set parameters
90 : !> \param pmax_set Initial screening matrix
91 : !> \param pmax_atom ...
92 : !> \param i_thread Process ID of current Thread
93 : !> \param n_threads Total Number of Threads
94 : !> \param cell cell
95 : !> \param do_p_screening Flag for initial p screening
96 : !> \param map_atom_to_kind_atom ...
97 : !> \param nkind ...
98 : !> \param eval_type ...
99 : !> \param pmax_block ...
100 : !> \param use_virial ...
101 : !> \par History
102 : !> 06.2007 created [Manuel Guidon]
103 : !> 08.2007 new parallel scheme [Manuel Guidon]
104 : !> 09.2007 new 'modulo' parellel scheme and Monte Carlo step [Manuel Guidon]
105 : !> 11.2007 parallelize load balance on box_idx1 [Manuel Guidon]
106 : !> 02.2009 completely refactored [Manuel Guidon]
107 : !> \author Manuel Guidon
108 : !> \note
109 : !> The optimization is done via a binning procedure followed by simple
110 : !> Monte Carlo procedure:
111 : !> In a first step the total amount of integrals in the system is calculated,
112 : !> taking into account the sparsity of the KS-matrix , the screening based
113 : !> on near/farfield approximations and if desired the screening on an initial
114 : !> density matrix.
115 : !> In a second step, bins are generate that contain approximately the same number
116 : !> of integrals, and a cost for these bins is estimated (currently the number of integrals)
117 : !> In a third step, a Monte Carlo procedure optimizes the assignment
118 : !> of the different loads to each process
119 : !> At the end each process owns an unique array of *atomic* indices-ranges
120 : !> that are used to decide whether a process has to calculate a certain
121 : !> bunch of integrals or not
122 : ! **************************************************************************************************
123 2128 : SUBROUTINE hfx_load_balance(x_data, eps_schwarz, particle_set, max_set, para_env, &
124 : coeffs_set, coeffs_kind, &
125 2128 : is_assoc_atomic_block_global, do_periodic, &
126 : load_balance_parameter, kind_of, basis_parameter, pmax_set, &
127 : pmax_atom, i_thread, n_threads, cell, &
128 : do_p_screening, map_atom_to_kind_atom, nkind, eval_type, &
129 : pmax_block, use_virial)
130 : TYPE(hfx_type), POINTER :: x_data
131 : REAL(dp), INTENT(IN) :: eps_schwarz
132 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
133 : INTEGER, INTENT(IN) :: max_set
134 : TYPE(mp_para_env_type), POINTER :: para_env
135 : TYPE(hfx_screen_coeff_type), &
136 : DIMENSION(:, :, :, :), POINTER :: coeffs_set
137 : TYPE(hfx_screen_coeff_type), DIMENSION(:, :), &
138 : POINTER :: coeffs_kind
139 : INTEGER, DIMENSION(:, :) :: is_assoc_atomic_block_global
140 : LOGICAL :: do_periodic
141 : TYPE(hfx_load_balance_type), POINTER :: load_balance_parameter
142 : INTEGER :: kind_of(*)
143 : TYPE(hfx_basis_type), DIMENSION(:), POINTER :: basis_parameter
144 : TYPE(hfx_p_kind), DIMENSION(:), POINTER :: pmax_set
145 : REAL(dp), DIMENSION(:, :), POINTER :: pmax_atom
146 : INTEGER, INTENT(IN) :: i_thread, n_threads
147 : TYPE(cell_type), POINTER :: cell
148 : LOGICAL, INTENT(IN) :: do_p_screening
149 : INTEGER, DIMENSION(:), POINTER :: map_atom_to_kind_atom
150 : INTEGER, INTENT(IN) :: nkind, eval_type
151 : REAL(dp), DIMENSION(:, :), POINTER :: pmax_block
152 : LOGICAL, INTENT(IN) :: use_virial
153 :
154 : CHARACTER(LEN=*), PARAMETER :: routineN = 'hfx_load_balance'
155 : ! Cap per-thread cost cache at ~16 MB (16 MB * n_threads per rank);
156 : ! larger systems recompute estimate_block_cost on the second pass.
157 : INTEGER(int_8), PARAMETER :: max_cached_quartet_costs = 2000000_int_8
158 :
159 : CHARACTER(LEN=512) :: error_msg
160 : INTEGER :: block_size, current_block_id, data_from, dest, handle, handle_inner, &
161 : handle_range, i, iatom_block, iatom_end, iatom_start, ibin, icpu, j, jatom_block, &
162 : jatom_end, jatom_start, katom_block, katom_end, katom_start, latom_block, latom_end, &
163 : latom_start, mepos, my_process_id, n_processes, natom, nbins, nblocks, ncpu, &
164 : new_iatom_end, new_iatom_start, new_jatom_end, new_jatom_start, non_empty_blocks, &
165 : objective_block_size, objective_nblocks, source, total_blocks
166 6384 : TYPE(mp_request_type), DIMENSION(2) :: req
167 : INTEGER(int_8) :: atom_block, block_cost_val, cost_per_bin, cost_per_core, current_cost, &
168 : distribution_counter_end, distribution_counter_start, global_quartet_counter, &
169 : local_quartet_counter, n_my_quartets, q_idx, &
170 : self_cost_per_block, tmp_block, total_block_self_cost
171 2128 : INTEGER(int_8), ALLOCATABLE, DIMENSION(:) :: buffer_in, buffer_out, cached_block_cost
172 2128 : INTEGER(int_8), DIMENSION(:), POINTER :: local_cost_matrix, recbuffer, &
173 2128 : sendbuffer, swapbuffer
174 : INTEGER(int_8), DIMENSION(:), POINTER, SAVE :: cost_matrix
175 : INTEGER(int_8), SAVE :: shm_global_quartet_counter, &
176 : shm_local_quartet_counter
177 4256 : INTEGER, ALLOCATABLE, DIMENSION(:) :: rcount, rdispl, tmp_index, tmp_pos, &
178 2128 : to_be_sorted
179 : INTEGER, DIMENSION(:), POINTER, SAVE :: shm_distribution_vector
180 : INTEGER, SAVE :: shm_nblocks
181 : LOGICAL :: changed, last_bin_needs_to_be_filled, &
182 : optimized
183 : LOGICAL, DIMENSION(:, :), POINTER, SAVE :: atomic_pair_list
184 : REAL(dp) :: coeffs_kind_max0, log10_eps_schwarz, &
185 : log_2, pmax_blocks
186 4256 : TYPE(hfx_block_range_type), DIMENSION(:), POINTER :: blocks_guess, tmp_blocks, tmp_blocks2
187 : TYPE(hfx_block_range_type), DIMENSION(:), &
188 : POINTER, SAVE :: shm_blocks
189 6384 : TYPE(hfx_distribution), DIMENSION(:), POINTER :: binned_dist, ptr_to_tmp_dist, tmp_dist
190 : TYPE(hfx_distribution), DIMENSION(:, :), POINTER, &
191 : SAVE :: full_dist
192 : TYPE(pair_list_type) :: list_ij, list_kl
193 : TYPE(pair_set_list_type), ALLOCATABLE, &
194 2128 : DIMENSION(:) :: set_list_ij, set_list_kl
195 :
196 2128 : !$OMP BARRIER
197 4256 : !$OMP MASTER
198 2128 : CALL timeset(routineN, handle)
199 : !$OMP END MASTER
200 2128 : !$OMP BARRIER
201 :
202 2128 : log10_eps_schwarz = LOG10(eps_schwarz)
203 2128 : log_2 = LOG10(2.0_dp)
204 14372 : coeffs_kind_max0 = MAXVAL(coeffs_kind(:, :)%x(2))
205 2128 : ncpu = para_env%num_pe
206 2128 : n_processes = ncpu*n_threads
207 2128 : natom = SIZE(particle_set)
208 :
209 2128 : block_size = load_balance_parameter%block_size
210 41068 : ALLOCATE (set_list_ij((max_set*block_size)**2))
211 38940 : ALLOCATE (set_list_kl((max_set*block_size)**2))
212 :
213 2128 : IF (.NOT. load_balance_parameter%blocks_initialized) THEN
214 1490 : !$OMP BARRIER
215 1490 : !$OMP MASTER
216 1490 : CALL timeset(routineN//"_range", handle_range)
217 :
218 1490 : nblocks = MAX((natom + block_size - 1)/block_size, 1)
219 9186 : ALLOCATE (blocks_guess(nblocks))
220 9186 : ALLOCATE (tmp_blocks(natom))
221 7696 : ALLOCATE (tmp_blocks2(natom))
222 :
223 1490 : pmax_blocks = 0.0_dp
224 2980 : SELECT CASE (eval_type)
225 : CASE (hfx_do_eval_energy)
226 1490 : atomic_pair_list => x_data%atomic_pair_list
227 : CASE (hfx_do_eval_forces)
228 1490 : atomic_pair_list => x_data%atomic_pair_list_forces
229 : END SELECT
230 25846 : atomic_pair_list = .TRUE.
231 : CALL init_blocks(nkind, para_env, natom, block_size, nblocks, blocks_guess, &
232 : list_ij, list_kl, set_list_ij, set_list_kl, &
233 : particle_set, &
234 : coeffs_set, coeffs_kind, &
235 : is_assoc_atomic_block_global, do_periodic, &
236 : kind_of, basis_parameter, pmax_set, pmax_atom, &
237 : pmax_blocks, cell, &
238 : do_p_screening, map_atom_to_kind_atom, eval_type, &
239 1490 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, atomic_pair_list)
240 :
241 1490 : total_block_self_cost = 0
242 :
243 6206 : DO i = 1, nblocks
244 6206 : total_block_self_cost = total_block_self_cost + blocks_guess(i)%cost
245 : END DO
246 :
247 1490 : CALL para_env%sum(total_block_self_cost)
248 :
249 1490 : objective_block_size = load_balance_parameter%block_size
250 1490 : objective_nblocks = MAX((natom + objective_block_size - 1)/objective_block_size, 1)
251 :
252 1490 : self_cost_per_block = (total_block_self_cost + objective_nblocks - 1)/(objective_nblocks)
253 :
254 6206 : DO i = 1, nblocks
255 6206 : tmp_blocks2(i) = blocks_guess(i)
256 : END DO
257 :
258 : optimized = .FALSE.
259 : i = 0
260 2980 : DO WHILE (.NOT. optimized)
261 1490 : i = i + 1
262 1490 : current_block_id = 0
263 1490 : changed = .FALSE.
264 6206 : DO atom_block = 1, nblocks
265 4716 : current_block_id = current_block_id + 1
266 4716 : iatom_start = tmp_blocks2(atom_block)%istart
267 4716 : iatom_end = tmp_blocks2(atom_block)%iend
268 6206 : IF (tmp_blocks2(atom_block)%cost > 1.5_dp*self_cost_per_block .AND. iatom_end - iatom_start > 0) THEN
269 0 : changed = .TRUE.
270 0 : new_iatom_start = iatom_start
271 0 : new_iatom_end = (iatom_end - iatom_start + 1)/2 + iatom_start - 1
272 0 : new_jatom_start = new_iatom_end + 1
273 0 : new_jatom_end = iatom_end
274 0 : tmp_blocks(current_block_id)%istart = new_iatom_start
275 0 : tmp_blocks(current_block_id)%iend = new_iatom_end
276 : tmp_blocks(current_block_id)%cost = estimate_block_cost( &
277 : natom, nkind, list_ij, list_kl, set_list_ij, set_list_kl, &
278 : new_iatom_start, new_iatom_end, new_iatom_start, new_iatom_end, &
279 : new_iatom_start, new_iatom_end, new_iatom_start, new_iatom_end, &
280 : particle_set, &
281 : coeffs_set, coeffs_kind, &
282 : is_assoc_atomic_block_global, do_periodic, &
283 : kind_of, basis_parameter, pmax_set, pmax_atom, pmax_blocks, &
284 : cell, &
285 : do_p_screening, map_atom_to_kind_atom, eval_type, &
286 0 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, atomic_pair_list)
287 0 : current_block_id = current_block_id + 1
288 0 : tmp_blocks(current_block_id)%istart = new_jatom_start
289 0 : tmp_blocks(current_block_id)%iend = new_jatom_end
290 : tmp_blocks(current_block_id)%cost = estimate_block_cost( &
291 : natom, nkind, list_ij, list_kl, set_list_ij, set_list_kl, &
292 : new_jatom_start, new_jatom_end, new_jatom_start, new_jatom_end, &
293 : new_jatom_start, new_jatom_end, new_jatom_start, new_jatom_end, &
294 : particle_set, &
295 : coeffs_set, coeffs_kind, &
296 : is_assoc_atomic_block_global, do_periodic, &
297 : kind_of, basis_parameter, pmax_set, pmax_atom, pmax_blocks, &
298 : cell, &
299 : do_p_screening, map_atom_to_kind_atom, eval_type, &
300 0 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, atomic_pair_list)
301 : ELSE
302 4716 : tmp_blocks(current_block_id)%istart = iatom_start
303 4716 : tmp_blocks(current_block_id)%iend = iatom_end
304 4716 : tmp_blocks(current_block_id)%cost = tmp_blocks2(atom_block)%cost
305 : END IF
306 : END DO
307 1490 : IF (.NOT. changed) optimized = .TRUE.
308 1490 : IF (i > 20) optimized = .TRUE.
309 1490 : nblocks = current_block_id
310 7696 : DO atom_block = 1, nblocks
311 6206 : tmp_blocks2(atom_block) = tmp_blocks(atom_block)
312 : END DO
313 : END DO
314 :
315 1490 : DEALLOCATE (tmp_blocks2)
316 :
317 : ! ** count number of non empty blocks on each node
318 1490 : non_empty_blocks = 0
319 6206 : DO atom_block = 1, nblocks
320 4716 : IF (tmp_blocks(atom_block)%istart == 0) CYCLE
321 6206 : non_empty_blocks = non_empty_blocks + 1
322 : END DO
323 :
324 4470 : ALLOCATE (rcount(ncpu))
325 1490 : rcount = 0
326 1490 : rcount(para_env%mepos + 1) = non_empty_blocks
327 1490 : CALL para_env%sum(rcount)
328 :
329 : ! ** sum all non_empty_blocks
330 1490 : total_blocks = 0
331 4468 : DO i = 1, ncpu
332 4468 : total_blocks = total_blocks + rcount(i)
333 : END DO
334 :
335 : ! ** calculate offsets
336 2980 : ALLOCATE (rdispl(ncpu))
337 4468 : rcount(:) = rcount(:)*3
338 1490 : rdispl(1) = 0
339 2978 : DO i = 2, ncpu
340 2978 : rdispl(i) = rdispl(i - 1) + rcount(i - 1)
341 : END DO
342 :
343 4440 : ALLOCATE (buffer_in(3*non_empty_blocks))
344 :
345 1490 : non_empty_blocks = 0
346 6206 : DO atom_block = 1, nblocks
347 4716 : IF (tmp_blocks(atom_block)%istart == 0) CYCLE
348 2361 : buffer_in(non_empty_blocks*3 + 1) = tmp_blocks(atom_block)%istart
349 2361 : buffer_in(non_empty_blocks*3 + 2) = tmp_blocks(atom_block)%iend
350 2361 : buffer_in(non_empty_blocks*3 + 3) = tmp_blocks(atom_block)%cost
351 6206 : non_empty_blocks = non_empty_blocks + 1
352 : END DO
353 :
354 1490 : nblocks = total_blocks
355 :
356 9186 : ALLOCATE (tmp_blocks2(nblocks))
357 :
358 4470 : ALLOCATE (buffer_out(3*nblocks))
359 :
360 : ! ** Gather all three arrays
361 1490 : CALL para_env%allgatherv(buffer_in, buffer_out, rcount, rdispl)
362 :
363 6206 : DO i = 1, nblocks
364 4716 : tmp_blocks2(i)%istart = INT(buffer_out((i - 1)*3 + 1))
365 4716 : tmp_blocks2(i)%iend = INT(buffer_out((i - 1)*3 + 2))
366 6206 : tmp_blocks2(i)%cost = buffer_out((i - 1)*3 + 3)
367 : END DO
368 :
369 : ! ** Now we sort the blocks
370 4470 : ALLOCATE (to_be_sorted(nblocks))
371 2980 : ALLOCATE (tmp_index(nblocks))
372 :
373 6206 : DO atom_block = 1, nblocks
374 6206 : to_be_sorted(atom_block) = tmp_blocks2(atom_block)%istart
375 : END DO
376 :
377 1490 : CALL sort(to_be_sorted, nblocks, tmp_index)
378 :
379 7696 : ALLOCATE (x_data%blocks(nblocks))
380 :
381 6206 : DO atom_block = 1, nblocks
382 6206 : x_data%blocks(atom_block) = tmp_blocks2(tmp_index(atom_block))
383 : END DO
384 :
385 1490 : shm_blocks => x_data%blocks
386 1490 : shm_nblocks = nblocks
387 :
388 : ! ** Set nblocks in structure
389 1490 : load_balance_parameter%nblocks = nblocks
390 :
391 1490 : DEALLOCATE (blocks_guess, tmp_blocks, tmp_blocks2)
392 :
393 1490 : DEALLOCATE (rcount, rdispl, buffer_in, buffer_out, to_be_sorted, tmp_index)
394 :
395 1490 : load_balance_parameter%blocks_initialized = .TRUE.
396 :
397 10922 : x_data%blocks = shm_blocks
398 1490 : load_balance_parameter%nblocks = shm_nblocks
399 1490 : load_balance_parameter%blocks_initialized = .TRUE.
400 :
401 5960 : ALLOCATE (x_data%pmax_block(shm_nblocks, shm_nblocks))
402 25846 : x_data%pmax_block = 0.0_dp
403 1490 : pmax_block => x_data%pmax_block
404 2980 : CALL timestop(handle_range)
405 : !$OMP END MASTER
406 1490 : !$OMP BARRIER
407 :
408 1490 : IF (.NOT. load_balance_parameter%blocks_initialized) THEN
409 0 : ALLOCATE (x_data%blocks(shm_nblocks))
410 0 : x_data%blocks = shm_blocks
411 0 : load_balance_parameter%nblocks = shm_nblocks
412 0 : load_balance_parameter%blocks_initialized = .TRUE.
413 : END IF
414 : !! ** precalculate maximum density matrix elements in blocks
415 1490 : !$OMP BARRIER
416 : END IF
417 :
418 2128 : !$OMP BARRIER
419 2128 : !$OMP MASTER
420 2128 : pmax_block => x_data%pmax_block
421 34544 : pmax_block = 0.0_dp
422 2128 : IF (do_p_screening) THEN
423 1736 : DO iatom_block = 1, shm_nblocks
424 1324 : iatom_start = x_data%blocks(iatom_block)%istart
425 1324 : iatom_end = x_data%blocks(iatom_block)%iend
426 6512 : DO jatom_block = 1, shm_nblocks
427 4776 : jatom_start = x_data%blocks(jatom_block)%istart
428 4776 : jatom_end = x_data%blocks(jatom_block)%iend
429 15652 : pmax_block(iatom_block, jatom_block) = MAXVAL(pmax_atom(iatom_start:iatom_end, jatom_start:jatom_end))
430 : END DO
431 : END DO
432 : END IF
433 :
434 3618 : SELECT CASE (eval_type)
435 : CASE (hfx_do_eval_energy)
436 1490 : atomic_pair_list => x_data%atomic_pair_list
437 : CASE (hfx_do_eval_forces)
438 2128 : atomic_pair_list => x_data%atomic_pair_list_forces
439 : END SELECT
440 : CALL build_atomic_pair_list(natom, atomic_pair_list, kind_of, basis_parameter, particle_set, &
441 : do_periodic, coeffs_kind, coeffs_kind_max0, log10_eps_schwarz, cell, &
442 2128 : x_data%blocks)
443 :
444 : !$OMP END MASTER
445 2128 : !$OMP BARRIER
446 :
447 : !! If there is only 1 cpu skip the binning
448 2128 : IF (n_processes == 1) THEN
449 4 : ALLOCATE (tmp_dist(1))
450 2 : tmp_dist(1)%number_of_atom_quartets = HUGE(tmp_dist(1)%number_of_atom_quartets)
451 2 : tmp_dist(1)%istart = 0_int_8
452 2 : ptr_to_tmp_dist => tmp_dist(:)
453 4 : SELECT CASE (eval_type)
454 : CASE (hfx_do_eval_energy)
455 2 : CALL hfx_set_distr_energy(ptr_to_tmp_dist, x_data)
456 : CASE (hfx_do_eval_forces)
457 2 : CALL hfx_set_distr_forces(ptr_to_tmp_dist, x_data)
458 : END SELECT
459 2 : DEALLOCATE (tmp_dist)
460 : ELSE
461 : !! Calculate total numbers of integrals that have to be calculated (wrt screening and symmetry)
462 2126 : !$OMP BARRIER
463 2126 : !$OMP MASTER
464 2126 : CALL timeset(routineN//"_count", handle_inner)
465 : !$OMP END MASTER
466 2126 : !$OMP BARRIER
467 :
468 2126 : cost_per_core = 0_int_8
469 2126 : my_process_id = para_env%mepos*n_threads + i_thread
470 2126 : nblocks = load_balance_parameter%nblocks
471 2126 : n_my_quartets = 0_int_8
472 595542 : DO atom_block = my_process_id, INT(nblocks, KIND=int_8)**4 - 1_int_8, n_processes
473 593416 : latom_block = INT(MODULO(atom_block, INT(nblocks, KIND=int_8))) + 1
474 593416 : tmp_block = atom_block/nblocks
475 593416 : katom_block = INT(MODULO(tmp_block, INT(nblocks, KIND=int_8))) + 1
476 593416 : IF (latom_block < katom_block) CYCLE
477 332701 : tmp_block = tmp_block/nblocks
478 332701 : jatom_block = INT(MODULO(tmp_block, INT(nblocks, KIND=int_8))) + 1
479 332701 : tmp_block = tmp_block/nblocks
480 332701 : iatom_block = INT(MODULO(tmp_block, INT(nblocks, KIND=int_8))) + 1
481 332701 : IF (jatom_block < iatom_block) CYCLE
482 187570 : n_my_quartets = n_my_quartets + 1_int_8
483 189696 : IF (n_my_quartets == max_cached_quartet_costs) EXIT
484 : END DO
485 2096 : IF (n_my_quartets > 0_int_8) THEN
486 6288 : ALLOCATE (cached_block_cost(n_my_quartets))
487 : END IF
488 2126 : q_idx = 0_int_8
489 :
490 595542 : DO atom_block = my_process_id, INT(nblocks, KIND=int_8)**4 - 1, n_processes
491 :
492 593416 : latom_block = INT(MODULO(atom_block, INT(nblocks, KIND=int_8))) + 1
493 593416 : tmp_block = atom_block/nblocks
494 593416 : katom_block = INT(MODULO(tmp_block, INT(nblocks, KIND=int_8))) + 1
495 593416 : IF (latom_block < katom_block) CYCLE
496 332701 : tmp_block = tmp_block/nblocks
497 332701 : jatom_block = INT(MODULO(tmp_block, INT(nblocks, KIND=int_8))) + 1
498 332701 : tmp_block = tmp_block/nblocks
499 332701 : iatom_block = INT(MODULO(tmp_block, INT(nblocks, KIND=int_8))) + 1
500 332701 : IF (jatom_block < iatom_block) CYCLE
501 :
502 187570 : q_idx = q_idx + 1_int_8
503 :
504 187570 : iatom_start = x_data%blocks(iatom_block)%istart
505 187570 : iatom_end = x_data%blocks(iatom_block)%iend
506 187570 : jatom_start = x_data%blocks(jatom_block)%istart
507 187570 : jatom_end = x_data%blocks(jatom_block)%iend
508 187570 : katom_start = x_data%blocks(katom_block)%istart
509 187570 : katom_end = x_data%blocks(katom_block)%iend
510 187570 : latom_start = x_data%blocks(latom_block)%istart
511 187570 : latom_end = x_data%blocks(latom_block)%iend
512 :
513 357153 : SELECT CASE (eval_type)
514 : CASE (hfx_do_eval_energy)
515 : pmax_blocks = MAX(pmax_block(katom_block, iatom_block), &
516 : pmax_block(latom_block, jatom_block), &
517 : pmax_block(latom_block, iatom_block), &
518 169583 : pmax_block(katom_block, jatom_block))
519 : CASE (hfx_do_eval_forces)
520 : pmax_blocks = MAX(pmax_block(katom_block, iatom_block) + &
521 : pmax_block(latom_block, jatom_block), &
522 : pmax_block(latom_block, iatom_block) + &
523 187570 : pmax_block(katom_block, jatom_block))
524 : END SELECT
525 :
526 187570 : IF (2.0_dp*coeffs_kind_max0 + pmax_blocks < log10_eps_schwarz) THEN
527 652 : IF (q_idx <= n_my_quartets) cached_block_cost(q_idx) = -1_int_8
528 : CYCLE
529 : END IF
530 :
531 : block_cost_val = estimate_block_cost(natom, nkind, list_ij, list_kl, set_list_ij, set_list_kl, &
532 : iatom_start, iatom_end, jatom_start, jatom_end, &
533 : katom_start, katom_end, latom_start, latom_end, &
534 : particle_set, &
535 : coeffs_set, coeffs_kind, &
536 : is_assoc_atomic_block_global, do_periodic, &
537 : kind_of, basis_parameter, pmax_set, pmax_atom, pmax_blocks, &
538 : cell, &
539 : do_p_screening, map_atom_to_kind_atom, eval_type, &
540 186918 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, atomic_pair_list)
541 186918 : IF (q_idx <= n_my_quartets) cached_block_cost(q_idx) = block_cost_val
542 594890 : cost_per_core = cost_per_core + block_cost_val
543 :
544 : END DO ! atom_block
545 :
546 2126 : nbins = load_balance_parameter%nbins
547 2126 : cost_per_bin = (cost_per_core + nbins - 1)/(nbins)
548 :
549 2126 : !$OMP BARRIER
550 2126 : !$OMP MASTER
551 2126 : CALL timestop(handle_inner)
552 : !$OMP END MASTER
553 2126 : !$OMP BARRIER
554 :
555 : ! new load balancing test
556 : IF (.FALSE.) THEN
557 : CALL hfx_recursive_load_balance(n_processes, my_process_id, nblocks, &
558 : natom, nkind, list_ij, list_kl, set_list_ij, set_list_kl, &
559 : particle_set, &
560 : coeffs_set, coeffs_kind, &
561 : is_assoc_atomic_block_global, do_periodic, &
562 : kind_of, basis_parameter, pmax_set, pmax_atom, pmax_blocks, &
563 : cell, x_data, para_env, pmax_block, &
564 : do_p_screening, map_atom_to_kind_atom, eval_type, &
565 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, atomic_pair_list)
566 : END IF
567 :
568 2126 : !$OMP BARRIER
569 2126 : !$OMP MASTER
570 2126 : CALL timeset(routineN//"_bin", handle_inner)
571 : !$OMP END MASTER
572 2126 : !$OMP BARRIER
573 :
574 142442 : ALLOCATE (binned_dist(nbins))
575 138190 : binned_dist(:)%istart = -1_int_8
576 138190 : binned_dist(:)%number_of_atom_quartets = 0_int_8
577 138190 : binned_dist(:)%cost = 0_int_8
578 138190 : binned_dist(:)%time_first_scf = 0.0_dp
579 138190 : binned_dist(:)%time_other_scf = 0.0_dp
580 138190 : binned_dist(:)%time_forces = 0.0_dp
581 :
582 2126 : current_cost = 0
583 2126 : mepos = 1
584 2126 : distribution_counter_start = 1
585 2126 : distribution_counter_end = 0
586 2126 : ibin = 1
587 :
588 2126 : global_quartet_counter = 0
589 2126 : local_quartet_counter = 0
590 2126 : last_bin_needs_to_be_filled = .FALSE.
591 595542 : DO atom_block = my_process_id, INT(nblocks, KIND=int_8)**4 - 1, n_processes
592 593416 : latom_block = INT(MODULO(atom_block, INT(nblocks, KIND=int_8))) + 1
593 593416 : tmp_block = atom_block/nblocks
594 593416 : katom_block = INT(MODULO(tmp_block, INT(nblocks, KIND=int_8))) + 1
595 593416 : IF (latom_block < katom_block) CYCLE
596 332701 : tmp_block = tmp_block/nblocks
597 332701 : jatom_block = INT(MODULO(tmp_block, INT(nblocks, KIND=int_8))) + 1
598 332701 : tmp_block = tmp_block/nblocks
599 332701 : iatom_block = INT(MODULO(tmp_block, INT(nblocks, KIND=int_8))) + 1
600 332701 : IF (jatom_block < iatom_block) CYCLE
601 :
602 187570 : distribution_counter_end = distribution_counter_end + 1
603 187570 : global_quartet_counter = global_quartet_counter + 1
604 187570 : last_bin_needs_to_be_filled = .TRUE.
605 :
606 187570 : IF (binned_dist(ibin)%istart == -1_int_8) binned_dist(ibin)%istart = atom_block
607 :
608 187570 : IF (distribution_counter_end <= n_my_quartets) THEN
609 187570 : block_cost_val = cached_block_cost(distribution_counter_end)
610 187570 : IF (block_cost_val < 0_int_8) CYCLE
611 186918 : current_cost = current_cost + block_cost_val
612 : ELSE
613 0 : iatom_start = x_data%blocks(iatom_block)%istart
614 0 : iatom_end = x_data%blocks(iatom_block)%iend
615 0 : jatom_start = x_data%blocks(jatom_block)%istart
616 0 : jatom_end = x_data%blocks(jatom_block)%iend
617 0 : katom_start = x_data%blocks(katom_block)%istart
618 0 : katom_end = x_data%blocks(katom_block)%iend
619 0 : latom_start = x_data%blocks(latom_block)%istart
620 0 : latom_end = x_data%blocks(latom_block)%iend
621 :
622 0 : SELECT CASE (eval_type)
623 : CASE (hfx_do_eval_energy)
624 : pmax_blocks = MAX(pmax_block(katom_block, iatom_block), &
625 : pmax_block(latom_block, jatom_block), &
626 : pmax_block(latom_block, iatom_block), &
627 0 : pmax_block(katom_block, jatom_block))
628 : CASE (hfx_do_eval_forces)
629 : pmax_blocks = MAX(pmax_block(katom_block, iatom_block) + &
630 : pmax_block(latom_block, jatom_block), &
631 : pmax_block(latom_block, iatom_block) + &
632 0 : pmax_block(katom_block, jatom_block))
633 : END SELECT
634 :
635 0 : IF (2.0_dp*coeffs_kind_max0 + pmax_blocks < log10_eps_schwarz) CYCLE
636 :
637 : current_cost = current_cost &
638 : + estimate_block_cost(natom, nkind, list_ij, list_kl, set_list_ij, set_list_kl, &
639 : iatom_start, iatom_end, jatom_start, jatom_end, &
640 : katom_start, katom_end, latom_start, latom_end, &
641 : particle_set, &
642 : coeffs_set, coeffs_kind, &
643 : is_assoc_atomic_block_global, do_periodic, &
644 : kind_of, basis_parameter, pmax_set, pmax_atom, pmax_blocks, &
645 : cell, &
646 : do_p_screening, map_atom_to_kind_atom, eval_type, &
647 0 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, atomic_pair_list)
648 : END IF
649 :
650 189044 : IF (current_cost >= cost_per_bin) THEN
651 23009 : IF (ibin == nbins) THEN
652 : binned_dist(ibin)%number_of_atom_quartets = binned_dist(ibin)%number_of_atom_quartets + &
653 0 : distribution_counter_end - distribution_counter_start + 1
654 : ELSE
655 23009 : binned_dist(ibin)%number_of_atom_quartets = distribution_counter_end - distribution_counter_start + 1
656 : END IF
657 23009 : binned_dist(ibin)%cost = binned_dist(ibin)%cost + current_cost
658 23009 : ibin = MIN(ibin + 1, nbins)
659 23009 : distribution_counter_start = distribution_counter_end + 1
660 23009 : current_cost = 0
661 23009 : last_bin_needs_to_be_filled = .FALSE.
662 : END IF
663 : END DO
664 2126 : IF (ALLOCATED(cached_block_cost)) DEALLOCATE (cached_block_cost)
665 :
666 2126 : !$OMP BARRIER
667 2126 : !$OMP MASTER
668 2126 : CALL timestop(handle_inner)
669 2126 : CALL timeset(routineN//"_dist", handle_inner)
670 : !$OMP END MASTER
671 2126 : !$OMP BARRIER
672 : !! Fill the last bin if necessary
673 2126 : IF (last_bin_needs_to_be_filled) THEN
674 618 : binned_dist(ibin)%cost = binned_dist(ibin)%cost + current_cost
675 618 : IF (ibin == nbins) THEN
676 : binned_dist(ibin)%number_of_atom_quartets = binned_dist(ibin)%number_of_atom_quartets + &
677 0 : distribution_counter_end - distribution_counter_start + 1
678 : ELSE
679 618 : binned_dist(ibin)%number_of_atom_quartets = distribution_counter_end - distribution_counter_start + 1
680 : END IF
681 : END IF
682 :
683 : !! Sanity-Check
684 138190 : DO ibin = 1, nbins
685 138190 : local_quartet_counter = local_quartet_counter + binned_dist(ibin)%number_of_atom_quartets
686 : END DO
687 2126 : !$OMP BARRIER
688 2126 : !$OMP MASTER
689 2126 : shm_local_quartet_counter = 0
690 2126 : shm_global_quartet_counter = 0
691 : !$OMP END MASTER
692 2126 : !$OMP BARRIER
693 2126 : !$OMP ATOMIC
694 : shm_local_quartet_counter = shm_local_quartet_counter + local_quartet_counter
695 2126 : !$OMP ATOMIC
696 : shm_global_quartet_counter = shm_global_quartet_counter + global_quartet_counter
697 :
698 2126 : !$OMP BARRIER
699 2126 : !$OMP MASTER
700 2126 : CALL para_env%sum(shm_local_quartet_counter)
701 2126 : CALL para_env%sum(shm_global_quartet_counter)
702 2126 : IF (para_env%is_source()) THEN
703 1063 : IF (shm_local_quartet_counter /= shm_global_quartet_counter) THEN
704 : WRITE (error_msg, '(A,I0,A,I0,A)') "HFX Sanity check for parallel distribution failed. "// &
705 0 : "Number of local quartets (", shm_local_quartet_counter, &
706 0 : ") and number of global quartets (", shm_global_quartet_counter, &
707 0 : ") are different. Please send in a bug report."
708 0 : CPABORT(error_msg)
709 : END IF
710 : END IF
711 : !$OMP END MASTER
712 :
713 2126 : !$OMP BARRIER
714 2126 : !$OMP MASTER
715 6378 : ALLOCATE (cost_matrix(ncpu*nbins*n_threads))
716 274254 : cost_matrix = 0
717 : !$OMP END MASTER
718 2126 : !$OMP BARRIER
719 2126 : icpu = para_env%mepos + 1
720 138190 : DO i = 1, nbins
721 138190 : cost_matrix((icpu - 1)*nbins*n_threads + i_thread*nbins + i) = binned_dist(i)%cost
722 : END DO
723 2126 : mepos = para_env%mepos
724 2126 : !$OMP BARRIER
725 :
726 2126 : !$OMP MASTER
727 : ! sync before/after ring of isendrecv
728 2126 : CALL para_env%sync()
729 :
730 6378 : ALLOCATE (sendbuffer(nbins*n_threads))
731 4252 : ALLOCATE (recbuffer(nbins*n_threads))
732 :
733 274254 : sendbuffer = cost_matrix(mepos*nbins*n_threads + 1:mepos*nbins*n_threads + nbins*n_threads)
734 :
735 2126 : dest = MODULO(mepos + 1, ncpu)
736 2126 : source = MODULO(mepos - 1, ncpu)
737 6378 : DO icpu = 0, ncpu - 1
738 4252 : IF (icpu /= ncpu - 1) THEN
739 : CALL para_env%isendrecv(sendbuffer, dest, recbuffer, source, &
740 2126 : req(1), req(2), 13)
741 : END IF
742 4252 : data_from = MODULO(mepos - icpu, ncpu)
743 548508 : cost_matrix(data_from*nbins*n_threads + 1:data_from*nbins*n_threads + nbins*n_threads) = sendbuffer
744 4252 : IF (icpu /= ncpu - 1) THEN
745 2126 : CALL mp_waitall(req)
746 : END IF
747 4252 : swapbuffer => sendbuffer
748 4252 : sendbuffer => recbuffer
749 6378 : recbuffer => swapbuffer
750 : END DO
751 2126 : DEALLOCATE (recbuffer, sendbuffer)
752 : !$OMP END MASTER
753 2126 : !$OMP BARRIER
754 :
755 2126 : !$OMP BARRIER
756 2126 : !$OMP MASTER
757 2126 : CALL timestop(handle_inner)
758 2126 : CALL timeset(routineN//"_opt", handle_inner)
759 : !$OMP END MASTER
760 2126 : !$OMP BARRIER
761 :
762 : !! Find an optimal distribution i.e. assign each element of the cost matrix to a certain process
763 2126 : !$OMP BARRIER
764 6378 : ALLOCATE (local_cost_matrix(SIZE(cost_matrix, 1)))
765 546382 : local_cost_matrix = cost_matrix
766 2126 : !$OMP MASTER
767 6378 : ALLOCATE (shm_distribution_vector(ncpu*nbins*n_threads))
768 :
769 : CALL optimize_distribution(ncpu*nbins*n_threads, ncpu*n_threads, local_cost_matrix, &
770 2126 : shm_distribution_vector, x_data%load_balance_parameter%do_randomize)
771 :
772 2126 : CALL timestop(handle_inner)
773 2126 : CALL timeset(routineN//"_redist", handle_inner)
774 : !! Collect local data to global array
775 416696 : ALLOCATE (full_dist(ncpu*n_threads, nbins))
776 :
777 410318 : full_dist(:, :)%istart = 0_int_8
778 410318 : full_dist(:, :)%number_of_atom_quartets = 0_int_8
779 410318 : full_dist(:, :)%cost = 0_int_8
780 410318 : full_dist(:, :)%time_first_scf = 0.0_dp
781 410318 : full_dist(:, :)%time_other_scf = 0.0_dp
782 412444 : full_dist(:, :)%time_forces = 0.0_dp
783 : !$OMP END MASTER
784 2126 : !$OMP BARRIER
785 2126 : mepos = para_env%mepos + 1
786 274254 : full_dist((mepos - 1)*n_threads + i_thread + 1, :) = binned_dist(:)
787 :
788 2126 : !$OMP BARRIER
789 2126 : !$OMP MASTER
790 6378 : ALLOCATE (sendbuffer(3*nbins*n_threads))
791 4252 : ALLOCATE (recbuffer(3*nbins*n_threads))
792 2126 : mepos = para_env%mepos
793 4252 : DO j = 1, n_threads
794 140316 : DO i = 1, nbins
795 136064 : sendbuffer((j - 1)*3*nbins + (i - 1)*3 + 1) = full_dist(mepos*n_threads + j, i)%istart
796 136064 : sendbuffer((j - 1)*3*nbins + (i - 1)*3 + 2) = full_dist(mepos*n_threads + j, i)%number_of_atom_quartets
797 138190 : sendbuffer((j - 1)*3*nbins + (i - 1)*3 + 3) = full_dist(mepos*n_threads + j, i)%cost
798 : END DO
799 : END DO
800 :
801 : ! sync before/after ring of isendrecv
802 2126 : CALL para_env%sync()
803 2126 : dest = MODULO(mepos + 1, ncpu)
804 2126 : source = MODULO(mepos - 1, ncpu)
805 6378 : DO icpu = 0, ncpu - 1
806 4252 : IF (icpu /= ncpu - 1) THEN
807 : CALL para_env%isendrecv(sendbuffer, dest, recbuffer, source, &
808 2126 : req(1), req(2), 13)
809 : END IF
810 4252 : data_from = MODULO(mepos - icpu, ncpu)
811 8504 : DO j = 1, n_threads
812 280632 : DO i = 1, nbins
813 272128 : full_dist(data_from*n_threads + j, i)%istart = sendbuffer((j - 1)*3*nbins + (i - 1)*3 + 1)
814 272128 : full_dist(data_from*n_threads + j, i)%number_of_atom_quartets = sendbuffer((j - 1)*3*nbins + (i - 1)*3 + 2)
815 276380 : full_dist(data_from*n_threads + j, i)%cost = sendbuffer((j - 1)*3*nbins + (i - 1)*3 + 3)
816 : END DO
817 : END DO
818 :
819 4252 : IF (icpu /= ncpu - 1) THEN
820 2126 : CALL mp_waitall(req)
821 : END IF
822 4252 : swapbuffer => sendbuffer
823 4252 : sendbuffer => recbuffer
824 6378 : recbuffer => swapbuffer
825 : END DO
826 2126 : DEALLOCATE (recbuffer, sendbuffer)
827 :
828 : ! sync before/after ring of isendrecv
829 2126 : CALL para_env%sync()
830 : !$OMP END MASTER
831 2126 : !$OMP BARRIER
832 : !! reorder the distribution according to the distribution vector
833 6378 : ALLOCATE (tmp_pos(ncpu*n_threads))
834 6378 : tmp_pos = 1
835 278506 : ALLOCATE (tmp_dist(nbins*ncpu*n_threads))
836 :
837 274254 : tmp_dist(:)%istart = 0_int_8
838 274254 : tmp_dist(:)%number_of_atom_quartets = 0_int_8
839 274254 : tmp_dist(:)%cost = 0_int_8
840 274254 : tmp_dist(:)%time_first_scf = 0.0_dp
841 274254 : tmp_dist(:)%time_other_scf = 0.0_dp
842 274254 : tmp_dist(:)%time_forces = 0.0_dp
843 :
844 6378 : DO icpu = 1, n_processes
845 278506 : DO i = 1, nbins
846 272128 : mepos = my_process_id + 1
847 276380 : IF (shm_distribution_vector((icpu - 1)*nbins + i) == mepos) THEN
848 136064 : tmp_dist(tmp_pos(mepos)) = full_dist(icpu, i)
849 136064 : tmp_pos(mepos) = tmp_pos(mepos) + 1
850 : END IF
851 : END DO
852 : END DO
853 :
854 : !! Assign the load to each process
855 : NULLIFY (ptr_to_tmp_dist)
856 2126 : mepos = my_process_id + 1
857 2126 : ptr_to_tmp_dist => tmp_dist(1:tmp_pos(mepos) - 1)
858 3614 : SELECT CASE (eval_type)
859 : CASE (hfx_do_eval_energy)
860 1488 : CALL hfx_set_distr_energy(ptr_to_tmp_dist, x_data)
861 : CASE (hfx_do_eval_forces)
862 2126 : CALL hfx_set_distr_forces(ptr_to_tmp_dist, x_data)
863 : END SELECT
864 :
865 2126 : !$OMP BARRIER
866 2126 : !$OMP MASTER
867 2126 : DEALLOCATE (full_dist, cost_matrix, shm_distribution_vector)
868 : !$OMP END MASTER
869 2126 : !$OMP BARRIER
870 2126 : DEALLOCATE (tmp_dist, tmp_pos)
871 2126 : DEALLOCATE (binned_dist, local_cost_matrix)
872 2126 : DEALLOCATE (set_list_ij, set_list_kl)
873 :
874 2126 : !$OMP BARRIER
875 2126 : !$OMP MASTER
876 2126 : CALL timestop(handle_inner)
877 : !$OMP END MASTER
878 2126 : !$OMP BARRIER
879 : END IF
880 2128 : !$OMP BARRIER
881 2128 : !$OMP MASTER
882 2128 : CALL timestop(handle)
883 : !$OMP END MASTER
884 2128 : !$OMP BARRIER
885 4415600 : END SUBROUTINE hfx_load_balance
886 :
887 : ! **************************************************************************************************
888 : !> \brief Reference implementation of new recursive load balancing routine
889 : !> Computes a local list of atom_blocks (p_atom_blocks,q_atom_blocks) for
890 : !> each process in a P-Q grid such that every process has more or less the
891 : !> same amount of work. Has no output at the moment (not used) but writes
892 : !> its computed load balance values into a file. Possible output is ready
893 : !> to use in the two arrays p_atom_blocks & q_atom_blocks
894 : !> \param n_processes ...
895 : !> \param my_process_id ...
896 : !> \param nblocks ...
897 : !> \param natom ...
898 : !> \param nkind ...
899 : !> \param list_ij ...
900 : !> \param list_kl ...
901 : !> \param set_list_ij ...
902 : !> \param set_list_kl ...
903 : !> \param particle_set ...
904 : !> \param coeffs_set ...
905 : !> \param coeffs_kind ...
906 : !> \param is_assoc_atomic_block_global ...
907 : !> \param do_periodic ...
908 : !> \param kind_of ...
909 : !> \param basis_parameter ...
910 : !> \param pmax_set ...
911 : !> \param pmax_atom ...
912 : !> \param pmax_blocks ...
913 : !> \param cell ...
914 : !> \param x_data ...
915 : !> \param para_env ...
916 : !> \param pmax_block ...
917 : !> \param do_p_screening ...
918 : !> \param map_atom_to_kind_atom ...
919 : !> \param eval_type ...
920 : !> \param log10_eps_schwarz ...
921 : !> \param log_2 ...
922 : !> \param coeffs_kind_max0 ...
923 : !> \param use_virial ...
924 : !> \param atomic_pair_list ...
925 : !> \par History
926 : !> 03.2011 created [Michael Steinlechner]
927 : !> \author Michael Steinlechner
928 : ! **************************************************************************************************
929 :
930 0 : SUBROUTINE hfx_recursive_load_balance(n_processes, my_process_id, nblocks, &
931 : natom, nkind, list_ij, list_kl, set_list_ij, set_list_kl, &
932 : particle_set, &
933 : coeffs_set, coeffs_kind, &
934 0 : is_assoc_atomic_block_global, do_periodic, &
935 : kind_of, basis_parameter, pmax_set, pmax_atom, pmax_blocks, &
936 : cell, x_data, para_env, pmax_block, &
937 : do_p_screening, map_atom_to_kind_atom, eval_type, &
938 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, atomic_pair_list)
939 :
940 : ! input variables:
941 : INTEGER, INTENT(IN) :: n_processes, my_process_id, nblocks, &
942 : natom, nkind
943 : TYPE(pair_list_type), INTENT(IN) :: list_ij, list_kl
944 : TYPE(pair_set_list_type), ALLOCATABLE, &
945 : DIMENSION(:), INTENT(IN) :: set_list_ij, set_list_kl
946 : TYPE(particle_type), DIMENSION(:), INTENT(IN), &
947 : POINTER :: particle_set
948 : TYPE(hfx_screen_coeff_type), &
949 : DIMENSION(:, :, :, :), INTENT(IN), POINTER :: coeffs_set
950 : TYPE(hfx_screen_coeff_type), DIMENSION(:, :), &
951 : INTENT(IN), POINTER :: coeffs_kind
952 : INTEGER, DIMENSION(:, :), INTENT(IN) :: is_assoc_atomic_block_global
953 : LOGICAL, INTENT(IN) :: do_periodic
954 : INTEGER, INTENT(IN) :: kind_of(*)
955 : TYPE(hfx_basis_type), DIMENSION(:), INTENT(IN), &
956 : POINTER :: basis_parameter
957 : TYPE(hfx_p_kind), DIMENSION(:), INTENT(IN), &
958 : POINTER :: pmax_set
959 : REAL(dp), DIMENSION(:, :), INTENT(IN), POINTER :: pmax_atom
960 : REAL(dp) :: pmax_blocks
961 : TYPE(cell_type), INTENT(IN), POINTER :: cell
962 : TYPE(hfx_type), INTENT(IN), POINTER :: x_data
963 : TYPE(mp_para_env_type), INTENT(IN) :: para_env
964 : REAL(dp), DIMENSION(:, :), INTENT(IN), POINTER :: pmax_block
965 : LOGICAL, INTENT(IN) :: do_p_screening
966 : INTEGER, DIMENSION(:), INTENT(IN), POINTER :: map_atom_to_kind_atom
967 : INTEGER, INTENT(IN) :: eval_type
968 : REAL(dp), INTENT(IN) :: log10_eps_schwarz, log_2, &
969 : coeffs_kind_max0
970 : LOGICAL, INTENT(IN) :: use_virial
971 : LOGICAL, DIMENSION(:, :), INTENT(IN), POINTER :: atomic_pair_list
972 :
973 : CHARACTER(LEN=*), PARAMETER :: routineN = 'hfx_recursive_load_balance'
974 :
975 : INTEGER :: handle, i, iatom_block, iatom_end, iatom_start, j, jatom_block, jatom_end, &
976 : jatom_start, katom_block, katom_end, katom_start, latom_block, latom_end, latom_start, &
977 : nP, nQ, numBins, p, q, sizeP, sizeQ, unit_nr
978 : INTEGER(int_8) :: local_cost, pidx, qidx, sumP, sumQ
979 0 : INTEGER(int_8), ALLOCATABLE, DIMENSION(:) :: local_cost_vector
980 0 : INTEGER, ALLOCATABLE, DIMENSION(:) :: blocksize, p_atom_blocks, permute, &
981 0 : q_atom_blocks
982 : REAL(dp) :: maximum, mean
983 :
984 : ! internal variables:
985 :
986 0 : !$OMP BARRIER
987 0 : !$OMP MASTER
988 0 : CALL timeset(routineN, handle)
989 : !$OMP END MASTER
990 0 : !$OMP BARRIER
991 :
992 : ! calculate best p/q distribution grid for the n_processes
993 0 : CALL hfx_calculate_PQ(p, q, numBins, n_processes)
994 :
995 0 : ALLOCATE (blocksize(numBins))
996 0 : ALLOCATE (permute(nblocks**2))
997 0 : DO i = 1, nblocks**2
998 0 : permute(i) = i
999 : END DO
1000 :
1001 : ! call the main recursive permutation routine.
1002 : ! Output:
1003 : ! blocksize :: vector (size numBins) with the sizes for each column/row block
1004 : ! permute :: permutation vector
1005 : CALL hfx_recursive_permute(blocksize, 1, nblocks**2, numBins, &
1006 : permute, 1, &
1007 : my_process_id, n_processes, nblocks, &
1008 : natom, nkind, list_ij, list_kl, set_list_ij, set_list_kl, &
1009 : particle_set, &
1010 : coeffs_set, coeffs_kind, &
1011 : is_assoc_atomic_block_global, do_periodic, &
1012 : kind_of, basis_parameter, pmax_set, pmax_atom, pmax_blocks, &
1013 : cell, x_data, para_env, pmax_block, &
1014 : do_p_screening, map_atom_to_kind_atom, eval_type, &
1015 0 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, atomic_pair_list)
1016 :
1017 : ! number of blocks per processor in p-direction (vertical)
1018 0 : nP = numBins/p
1019 : ! number of blocks per processor in q-direction (horizontal)
1020 0 : nQ = numBins/q
1021 :
1022 : ! calc own position in P-Q-processor grid (PQ-grid is column-major)
1023 0 : pidx = MODULO(INT(my_process_id), INT(p)) + 1
1024 0 : qidx = my_process_id/p + 1
1025 :
1026 0 : sizeP = SUM(blocksize((nP*(pidx - 1) + 1):(nP*pidx)))
1027 0 : sizeQ = SUM(blocksize((nQ*(qidx - 1) + 1):(nQ*qidx)))
1028 :
1029 0 : sumP = SUM(blocksize(1:(nP*(pidx - 1))))
1030 0 : sumQ = SUM(blocksize(1:(nQ*(qidx - 1))))
1031 :
1032 0 : ALLOCATE (p_atom_blocks(sizeP))
1033 0 : ALLOCATE (q_atom_blocks(sizeQ))
1034 :
1035 0 : p_atom_blocks(:) = permute((sumP + 1):(sumP + sizeP))
1036 0 : q_atom_blocks(:) = permute((sumQ + 1):(sumQ + sizeQ))
1037 :
1038 : ! from here on, we are actually finished, each process has been
1039 : ! assigned a (p_atom_blocks,q_atom_blocks) pair list.
1040 : ! what follows is just a small routine to calculate the local cost
1041 : ! for each processor which is then written to a file.
1042 :
1043 : ! calculate local cost for each processor!
1044 : ! ****************************************
1045 : local_cost = 0
1046 0 : DO i = 1, sizeP
1047 0 : DO j = 1, sizeQ
1048 :
1049 : ! get corresponding 4D block indices out of our own P-Q-block
1050 0 : latom_block = MODULO(q_atom_blocks(j), nblocks)
1051 0 : iatom_block = q_atom_blocks(j)/nblocks + 1
1052 0 : jatom_block = MODULO(p_atom_blocks(i), nblocks)
1053 0 : katom_block = p_atom_blocks(i)/nblocks + 1
1054 :
1055 : ! symmetry checks.
1056 0 : IF (latom_block < katom_block) CYCLE
1057 0 : IF (jatom_block < iatom_block) CYCLE
1058 :
1059 0 : iatom_start = x_data%blocks(iatom_block)%istart
1060 0 : iatom_end = x_data%blocks(iatom_block)%iend
1061 0 : jatom_start = x_data%blocks(jatom_block)%istart
1062 0 : jatom_end = x_data%blocks(jatom_block)%iend
1063 0 : katom_start = x_data%blocks(katom_block)%istart
1064 0 : katom_end = x_data%blocks(katom_block)%iend
1065 0 : latom_start = x_data%blocks(latom_block)%istart
1066 0 : latom_end = x_data%blocks(latom_block)%iend
1067 :
1068 : ! whatever.
1069 0 : SELECT CASE (eval_type)
1070 : CASE (hfx_do_eval_energy)
1071 : pmax_blocks = MAX(pmax_block(katom_block, iatom_block), &
1072 : pmax_block(latom_block, jatom_block), &
1073 : pmax_block(latom_block, iatom_block), &
1074 0 : pmax_block(katom_block, jatom_block))
1075 : CASE (hfx_do_eval_forces)
1076 : pmax_blocks = MAX(pmax_block(katom_block, iatom_block) + &
1077 : pmax_block(latom_block, jatom_block), &
1078 : pmax_block(latom_block, iatom_block) + &
1079 0 : pmax_block(katom_block, jatom_block))
1080 : END SELECT
1081 :
1082 : ! screening.
1083 0 : IF (2.0_dp*coeffs_kind_max0 + pmax_blocks < log10_eps_schwarz) CYCLE
1084 :
1085 : ! estimate the cost of this atom_block.
1086 : local_cost = local_cost + estimate_block_cost(natom, nkind, list_ij, list_kl, set_list_ij, &
1087 : set_list_kl, &
1088 : iatom_start, iatom_end, jatom_start, jatom_end, &
1089 : katom_start, katom_end, latom_start, latom_end, &
1090 : particle_set, &
1091 : coeffs_set, coeffs_kind, &
1092 : is_assoc_atomic_block_global, do_periodic, &
1093 : kind_of, basis_parameter, pmax_set, pmax_atom, pmax_blocks, &
1094 : cell, &
1095 : do_p_screening, map_atom_to_kind_atom, eval_type, &
1096 0 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, atomic_pair_list)
1097 : END DO
1098 : END DO
1099 :
1100 0 : ALLOCATE (local_cost_vector(n_processes))
1101 0 : local_cost_vector = 0
1102 0 : local_cost_vector(my_process_id + 1) = local_cost
1103 0 : CALL para_env%sum(local_cost_vector)
1104 :
1105 0 : mean = SUM(local_cost_vector)/n_processes
1106 0 : maximum = MAXVAL(local_cost_vector)
1107 :
1108 0 : !$OMP BARRIER
1109 0 : !$OMP MASTER
1110 : ! only output once
1111 0 : IF (my_process_id == 0) THEN
1112 0 : CALL open_file(unit_number=unit_nr, file_name="loads.dat")
1113 0 : WRITE (unit_nr, *) 'maximum cost:', maximum
1114 0 : WRITE (unit_nr, *) 'mean cost:', mean
1115 0 : WRITE (unit_nr, *) 'load balance ratio max/mean: ', maximum/mean
1116 0 : WRITE (unit_nr, *) '-------- detailed per-process costs ---------'
1117 0 : DO i = 1, n_processes
1118 0 : WRITE (unit_nr, *) local_cost_vector(i)
1119 : END DO
1120 0 : CALL close_file(unit_nr)
1121 : END IF
1122 : !$OMP END MASTER
1123 0 : !$OMP BARRIER
1124 :
1125 0 : DEALLOCATE (local_cost_vector)
1126 0 : DEALLOCATE (p_atom_blocks, q_atom_blocks)
1127 0 : DEALLOCATE (blocksize, permute)
1128 :
1129 0 : !$OMP BARRIER
1130 0 : !$OMP MASTER
1131 0 : CALL timestop(handle)
1132 : !$OMP END MASTER
1133 0 : !$OMP BARRIER
1134 :
1135 0 : END SUBROUTINE hfx_recursive_load_balance
1136 :
1137 : ! **************************************************************************************************
1138 : !> \brief Small routine to calculate the optimal P-Q-processor grid distribution
1139 : !> for a given number of processors N
1140 : !> and the corresponding number of Bins for the load balancing routine
1141 : !> \param p number of rows on P-Q process grid (output)
1142 : !> \param q number of columns on P-Q process grid (output)
1143 : !> \param nBins number of Bins (output)
1144 : !> \param N number of processes (input)
1145 : !> \par History
1146 : !> 03.2011 created [Michael Steinlechner]
1147 : !> \author Michael Steinlechner
1148 : ! **************************************************************************************************
1149 0 : SUBROUTINE hfx_calculate_PQ(p, q, nBins, N)
1150 :
1151 : INTEGER, INTENT(OUT) :: p, q, nBins
1152 : INTEGER, INTENT(IN) :: N
1153 :
1154 : INTEGER :: a, b, k
1155 : REAL(dp) :: sqN
1156 :
1157 0 : k = 2
1158 0 : sqN = SQRT(REAL(N, KIND=dp))
1159 0 : p = 1
1160 :
1161 0 : DO WHILE (REAL(k, KIND=dp) <= sqN)
1162 0 : IF (MODULO(N, k) == 0) THEN
1163 0 : p = k
1164 : END IF
1165 0 : k = k + 1
1166 : END DO
1167 0 : q = N/p
1168 :
1169 : ! now compute the least common multiple of p & q to get the number of necessary bins
1170 : ! compute using the relation LCM(p,q) = abs(p*q) / GCD(p,q)
1171 : ! and use euclid's algorithm for GCD computation.
1172 0 : a = p
1173 0 : b = q
1174 :
1175 0 : DO WHILE (b /= 0)
1176 0 : IF (a > b) THEN
1177 0 : a = a - b
1178 : ELSE
1179 0 : b = b - a
1180 : END IF
1181 : END DO
1182 : ! gcd(p,q) is now saved in a
1183 :
1184 0 : nBins = p*q/a
1185 :
1186 0 : END SUBROUTINE hfx_calculate_PQ
1187 :
1188 : ! **************************************************************************************************
1189 : !> \brief Recursive permutation routine for the load balancing of the integral
1190 : !> computation
1191 : !> \param blocksize vector of blocksizes, size(nProc), which contains for
1192 : !> each process the local blocksize (OUTPUT)
1193 : !> \param blockstart starting row/column idx of the block which is to be examined
1194 : !> at this point (INPUT)
1195 : !> \param blockend ending row/column idx of the block which is to be examined
1196 : !> (INPUT)
1197 : !> \param nProc_in number of bins into which the current block has to be divided
1198 : !> (INPUT)
1199 : !> \param permute permutation vector which balances column/row cost
1200 : !> size(nblocks^2). (OUTPUT)
1201 : !> \param step ...
1202 : !> \param my_process_id ...
1203 : !> \param n_processes ...
1204 : !> \param nblocks ...
1205 : !> \param natom ...
1206 : !> \param nkind ...
1207 : !> \param list_ij ...
1208 : !> \param list_kl ...
1209 : !> \param set_list_ij ...
1210 : !> \param set_list_kl ...
1211 : !> \param particle_set ...
1212 : !> \param coeffs_set ...
1213 : !> \param coeffs_kind ...
1214 : !> \param is_assoc_atomic_block_global ...
1215 : !> \param do_periodic ...
1216 : !> \param kind_of ...
1217 : !> \param basis_parameter ...
1218 : !> \param pmax_set ...
1219 : !> \param pmax_atom ...
1220 : !> \param pmax_blocks ...
1221 : !> \param cell ...
1222 : !> \param x_data ...
1223 : !> \param para_env ...
1224 : !> \param pmax_block ...
1225 : !> \param do_p_screening ...
1226 : !> \param map_atom_to_kind_atom ...
1227 : !> \param eval_type ...
1228 : !> \param log10_eps_schwarz ...
1229 : !> \param log_2 ...
1230 : !> \param coeffs_kind_max0 ...
1231 : !> \param use_virial ...
1232 : !> \param atomic_pair_list ...
1233 : !> \par History
1234 : !> 03.2011 created [Michael Steinlechner]
1235 : !> \author Michael Steinlechner
1236 : ! **************************************************************************************************
1237 0 : RECURSIVE SUBROUTINE hfx_recursive_permute(blocksize, blockstart, blockend, nProc_in, &
1238 0 : permute, step, &
1239 : my_process_id, n_processes, nblocks, &
1240 : natom, nkind, list_ij, list_kl, set_list_ij, set_list_kl, &
1241 : particle_set, &
1242 : coeffs_set, coeffs_kind, &
1243 0 : is_assoc_atomic_block_global, do_periodic, &
1244 : kind_of, basis_parameter, pmax_set, pmax_atom, pmax_blocks, &
1245 : cell, x_data, para_env, pmax_block, &
1246 : do_p_screening, map_atom_to_kind_atom, eval_type, &
1247 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, atomic_pair_list)
1248 :
1249 : INTEGER :: nProc_in, blockend, blockstart
1250 : INTEGER, DIMENSION(nProc_in) :: blocksize
1251 : INTEGER :: nblocks, n_processes, my_process_id
1252 : INTEGER, INTENT(IN) :: step
1253 : INTEGER, DIMENSION(nblocks*nblocks) :: permute
1254 : INTEGER :: natom
1255 : INTEGER, INTENT(IN) :: nkind
1256 : TYPE(pair_list_type) :: list_ij, list_kl
1257 : TYPE(pair_set_list_type), ALLOCATABLE, &
1258 : DIMENSION(:) :: set_list_ij, set_list_kl
1259 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
1260 : TYPE(hfx_screen_coeff_type), &
1261 : DIMENSION(:, :, :, :), POINTER :: coeffs_set
1262 : TYPE(hfx_screen_coeff_type), DIMENSION(:, :), &
1263 : POINTER :: coeffs_kind
1264 : INTEGER, DIMENSION(:, :) :: is_assoc_atomic_block_global
1265 : LOGICAL :: do_periodic
1266 : INTEGER :: kind_of(*)
1267 : TYPE(hfx_basis_type), DIMENSION(:), POINTER :: basis_parameter
1268 : TYPE(hfx_p_kind), DIMENSION(:), POINTER :: pmax_set
1269 : REAL(dp), DIMENSION(:, :), POINTER :: pmax_atom
1270 : REAL(dp) :: pmax_blocks
1271 : TYPE(cell_type), POINTER :: cell
1272 : TYPE(hfx_type), POINTER :: x_data
1273 : TYPE(mp_para_env_type), INTENT(IN) :: para_env
1274 : REAL(dp), DIMENSION(:, :), POINTER :: pmax_block
1275 : LOGICAL, INTENT(IN) :: do_p_screening
1276 : INTEGER, DIMENSION(:), POINTER :: map_atom_to_kind_atom
1277 : INTEGER, INTENT(IN) :: eval_type
1278 : REAL(dp) :: log10_eps_schwarz, log_2, &
1279 : coeffs_kind_max0
1280 : LOGICAL, INTENT(IN) :: use_virial
1281 : LOGICAL, DIMENSION(:, :), POINTER :: atomic_pair_list
1282 :
1283 : INTEGER :: col, endoffset, i, iatom_block, iatom_end, iatom_start, idx, inv_perm, &
1284 : jatom_block, jatom_end, jatom_start, katom_block, katom_end, katom_start, latom_block, &
1285 : latom_end, latom_start, nbins, nProc, row, startoffset
1286 : INTEGER(int_8) :: atom_block, tmp_block
1287 0 : INTEGER, ALLOCATABLE, DIMENSION(:) :: ithblocksize, localblocksize
1288 0 : INTEGER, DIMENSION(blockend - blockstart + 1) :: bin_perm, tmp_perm
1289 : REAL(dp) :: partialcost
1290 0 : REAL(dp), DIMENSION(nblocks*nblocks) :: cost_vector
1291 :
1292 : nProc = nProc_in
1293 0 : cost_vector = 0.0_dp
1294 :
1295 : ! loop over local atom_blocks.
1296 0 : DO atom_block = my_process_id, INT(nblocks, KIND=int_8)**4 - 1, n_processes
1297 :
1298 : ! get corresponding 4D block indices
1299 0 : latom_block = INT(MODULO(atom_block, INT(nblocks, KIND=int_8))) + 1
1300 0 : tmp_block = atom_block/nblocks
1301 0 : katom_block = INT(MODULO(tmp_block, INT(nblocks, KIND=int_8))) + 1
1302 0 : IF (latom_block < katom_block) CYCLE
1303 0 : tmp_block = tmp_block/nblocks
1304 0 : jatom_block = INT(MODULO(tmp_block, INT(nblocks, KIND=int_8))) + 1
1305 0 : tmp_block = tmp_block/nblocks
1306 0 : iatom_block = INT(MODULO(tmp_block, INT(nblocks, KIND=int_8))) + 1
1307 0 : IF (jatom_block < iatom_block) CYCLE
1308 :
1309 : ! get 2D indices of this atom_block (with permutation applied)
1310 : ! for this, we need to invert the permutation, this means
1311 : ! find position in permutation vector where value==idx
1312 :
1313 0 : row = (katom_block - 1)*nblocks + jatom_block
1314 0 : inv_perm = 1
1315 0 : DO WHILE (permute(inv_perm) /= row)
1316 0 : inv_perm = inv_perm + 1
1317 : END DO
1318 0 : row = inv_perm
1319 :
1320 0 : col = (iatom_block - 1)*nblocks + latom_block
1321 0 : inv_perm = 1
1322 0 : DO WHILE (permute(inv_perm) /= col)
1323 0 : inv_perm = inv_perm + 1
1324 : END DO
1325 0 : col = inv_perm
1326 :
1327 : ! if row/col outside our current diagonal block, skip calculation.
1328 0 : IF (col < blockstart .OR. col > blockend) CYCLE
1329 0 : IF (row < blockstart .OR. row > blockend) CYCLE
1330 :
1331 0 : iatom_start = x_data%blocks(iatom_block)%istart
1332 0 : iatom_end = x_data%blocks(iatom_block)%iend
1333 0 : jatom_start = x_data%blocks(jatom_block)%istart
1334 0 : jatom_end = x_data%blocks(jatom_block)%iend
1335 0 : katom_start = x_data%blocks(katom_block)%istart
1336 0 : katom_end = x_data%blocks(katom_block)%iend
1337 0 : latom_start = x_data%blocks(latom_block)%istart
1338 0 : latom_end = x_data%blocks(latom_block)%iend
1339 :
1340 : ! whatever.
1341 0 : SELECT CASE (eval_type)
1342 : CASE (hfx_do_eval_energy)
1343 : pmax_blocks = MAX(pmax_block(katom_block, iatom_block), &
1344 : pmax_block(latom_block, jatom_block), &
1345 : pmax_block(latom_block, iatom_block), &
1346 0 : pmax_block(katom_block, jatom_block))
1347 : CASE (hfx_do_eval_forces)
1348 : pmax_blocks = MAX(pmax_block(katom_block, iatom_block) + &
1349 : pmax_block(latom_block, jatom_block), &
1350 : pmax_block(latom_block, iatom_block) + &
1351 0 : pmax_block(katom_block, jatom_block))
1352 : END SELECT
1353 :
1354 : ! screening.
1355 0 : IF (2.0_dp*coeffs_kind_max0 + pmax_blocks < log10_eps_schwarz) CYCLE
1356 :
1357 : ! every second recursion step, compute row sum instead of column sum
1358 :
1359 0 : IF (MODULO(step, 2) == 0) THEN
1360 : idx = row
1361 : ELSE
1362 0 : idx = col
1363 : END IF
1364 :
1365 : ! estimate the cost of this atom_block.
1366 : partialcost = estimate_block_cost(natom, nkind, list_ij, list_kl, set_list_ij, &
1367 : set_list_kl, &
1368 : iatom_start, iatom_end, jatom_start, jatom_end, &
1369 : katom_start, katom_end, latom_start, latom_end, &
1370 : particle_set, &
1371 : coeffs_set, coeffs_kind, &
1372 : is_assoc_atomic_block_global, do_periodic, &
1373 : kind_of, basis_parameter, pmax_set, pmax_atom, pmax_blocks, &
1374 : cell, &
1375 : do_p_screening, map_atom_to_kind_atom, eval_type, &
1376 0 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, atomic_pair_list)
1377 :
1378 0 : cost_vector(idx) = cost_vector(idx) + partialcost
1379 : END DO ! atom_block
1380 :
1381 : ! sum costvector over all processes
1382 0 : CALL para_env%sum(cost_vector)
1383 :
1384 : ! calculate next prime factor of nProc
1385 0 : nBins = 2
1386 0 : DO WHILE (MODULO(INT(nProc), INT(nBins)) /= 0)
1387 0 : nBins = nBins + 1
1388 : END DO
1389 :
1390 0 : nProc = nProc/nBins
1391 :
1392 : ! ... do the binning...
1393 :
1394 0 : ALLOCATE (localblocksize(nBins))
1395 0 : CALL hfx_permute_binning(nBins, cost_vector(blockstart:blockend), blockend - blockstart + 1, bin_perm, localblocksize)
1396 :
1397 : !... and update the permutation vector
1398 :
1399 0 : tmp_perm = permute(blockstart:blockend)
1400 0 : permute(blockstart:blockend) = tmp_perm(bin_perm)
1401 :
1402 : ! split recursion into the nBins Bins
1403 0 : IF (nProc > 1) THEN
1404 0 : ALLOCATE (ithblocksize(nProc))
1405 0 : DO i = 1, nBins
1406 0 : startoffset = SUM(localblocksize(1:(i - 1)))
1407 0 : endoffset = SUM(localblocksize(1:i)) - 1
1408 :
1409 : CALL hfx_recursive_permute(ithblocksize, blockstart + startoffset, blockstart + endoffset, nProc, &
1410 : permute, step + 1, &
1411 : my_process_id, n_processes, nblocks, &
1412 : natom, nkind, list_ij, list_kl, set_list_ij, set_list_kl, &
1413 : particle_set, &
1414 : coeffs_set, coeffs_kind, &
1415 : is_assoc_atomic_block_global, do_periodic, &
1416 : kind_of, basis_parameter, pmax_set, pmax_atom, pmax_blocks, &
1417 : cell, x_data, para_env, pmax_block, &
1418 : do_p_screening, map_atom_to_kind_atom, eval_type, &
1419 0 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, atomic_pair_list)
1420 0 : blocksize(((i - 1)*nProc + 1):(i*nProc)) = ithblocksize
1421 : END DO
1422 0 : DEALLOCATE (ithblocksize)
1423 : ELSE
1424 0 : DO i = 1, nBins
1425 0 : blocksize(i) = localblocksize(i)
1426 : END DO
1427 : END IF
1428 :
1429 0 : DEALLOCATE (localblocksize)
1430 :
1431 0 : END SUBROUTINE hfx_recursive_permute
1432 :
1433 : ! **************************************************************************************************
1434 : !> \brief small binning routine for the recursive load balancing
1435 : !>
1436 : !> \param nBins number of Bins (INPUT)
1437 : !> \param costvector vector of current row/column costs which have to be binned (INPUT)
1438 : !> \param maxbinsize upper bound for bin size (INPUT)
1439 : !> \param perm resulting permutation due to be binning routine (OUTPUT)
1440 : !> \param block_count vector of size(nbins) which contains the size of each bin (OUTPUT)
1441 : !> \par History
1442 : !> 03.2011 created [Michael Steinlechner]
1443 : !> \author Michael Steinlechner
1444 : ! **************************************************************************************************
1445 0 : SUBROUTINE hfx_permute_binning(nBins, costvector, maxbinsize, perm, block_count)
1446 :
1447 : INTEGER, INTENT(IN) :: nBins, maxbinsize
1448 : REAL(dp), DIMENSION(maxbinsize), INTENT(IN) :: costvector
1449 : INTEGER, DIMENSION(maxbinsize), INTENT(OUT) :: perm
1450 : INTEGER, DIMENSION(nBins), INTENT(OUT) :: block_count
1451 :
1452 : INTEGER :: i, j, mod_idx, offset
1453 0 : INTEGER, DIMENSION(nBins, maxbinsize) :: bin
1454 0 : INTEGER, DIMENSION(nBins) :: bin_idx
1455 0 : INTEGER, DIMENSION(maxbinsize) :: idx
1456 0 : REAL(dp), DIMENSION(maxbinsize) :: vec
1457 0 : REAL(dp), DIMENSION(nBins) :: bincosts
1458 :
1459 : ! be careful not to change costvector (copy it!)
1460 :
1461 0 : vec = costvector
1462 0 : block_count = 0
1463 0 : bincosts = 0
1464 :
1465 : !sort the array (ascending)
1466 0 : CALL sort(vec, maxbinsize, idx)
1467 :
1468 : ! count the loop down to distribute the largest cols/rows first
1469 0 : DO i = maxbinsize, 1, -1
1470 0 : IF (vec(i) == 0) THEN
1471 : ! spread zero-cost col/rows evenly among procs
1472 0 : mod_idx = MODULO(i, nBins) + 1 !(note the fortran offset by one!)
1473 0 : block_count(mod_idx) = block_count(mod_idx) + 1
1474 0 : bin(mod_idx, block_count(mod_idx)) = idx(i)
1475 : ELSE
1476 : ! sort the bins so that the one with the lowest cost is at the
1477 : ! first place, where we then assign the current col/row
1478 0 : CALL sort(bincosts, nBins, bin_idx)
1479 0 : block_count = block_count(bin_idx)
1480 0 : bin = bin(bin_idx, :)
1481 :
1482 0 : bincosts(1) = bincosts(1) + vec(i)
1483 0 : block_count(1) = block_count(1) + 1
1484 0 : bin(1, block_count(1)) = idx(i)
1485 : END IF
1486 : END DO
1487 :
1488 : ! construct permutation vector from the binning
1489 : offset = 0
1490 0 : DO i = 1, nBins
1491 0 : DO j = 1, block_count(i)
1492 0 : perm(offset + j) = bin(i, j)
1493 : END DO
1494 0 : offset = offset + block_count(i)
1495 : END DO
1496 :
1497 0 : END SUBROUTINE hfx_permute_binning
1498 :
1499 : ! **************************************************************************************************
1500 : !> \brief Cheap way of redistributing the eri's
1501 : !> \param x_data Object that stores the indices array
1502 : !> \param para_env para_env
1503 : !> \param load_balance_parameter contains parmameter for Monte-Carlo routines
1504 : !> \param i_thread current thread ID
1505 : !> \param n_threads Total Number of threads
1506 : !> \param eval_type ...
1507 : !> \par History
1508 : !> 12.2007 created [Manuel Guidon]
1509 : !> 02.2009 optimize Memory Usage [Manuel Guidon]
1510 : !> \author Manuel Guidon
1511 : !> \note
1512 : !> The cost matrix is given by the walltime for each bin that is measured
1513 : !> during the calculation
1514 : ! **************************************************************************************************
1515 1982 : SUBROUTINE hfx_update_load_balance(x_data, para_env, &
1516 : load_balance_parameter, &
1517 : i_thread, n_threads, eval_type)
1518 :
1519 : TYPE(hfx_type), POINTER :: x_data
1520 : TYPE(mp_para_env_type), INTENT(IN) :: para_env
1521 : TYPE(hfx_load_balance_type) :: load_balance_parameter
1522 : INTEGER, INTENT(IN) :: i_thread, n_threads, eval_type
1523 :
1524 : CHARACTER(LEN=*), PARAMETER :: routineN = 'hfx_update_load_balance'
1525 :
1526 : INTEGER :: data_from, dest, end_idx, handle, i, ibin, icpu, iprocess, j, mepos, my_bin_size, &
1527 : my_global_start_idx, my_process_id, n_processes, nbins, ncpu, source, start_idx
1528 5946 : TYPE(mp_request_type), DIMENSION(2) :: req
1529 1982 : INTEGER(int_8), DIMENSION(:), POINTER :: local_cost_matrix, recbuffer, &
1530 1982 : sendbuffer, swapbuffer
1531 : INTEGER(int_8), DIMENSION(:), POINTER, SAVE :: cost_matrix
1532 1982 : INTEGER, ALLOCATABLE, DIMENSION(:) :: tmp_pos
1533 : INTEGER, ALLOCATABLE, DIMENSION(:), SAVE :: bins_per_rank
1534 : INTEGER, ALLOCATABLE, DIMENSION(:, :), SAVE :: bin_histogram
1535 : INTEGER, DIMENSION(:), POINTER, SAVE :: shm_distribution_vector
1536 : INTEGER, SAVE :: max_bin_size
1537 3964 : TYPE(hfx_distribution), DIMENSION(:), POINTER :: binned_dist, ptr_to_tmp_dist, tmp_dist
1538 : TYPE(hfx_distribution), DIMENSION(:, :), POINTER, &
1539 : SAVE :: full_dist
1540 :
1541 1982 : !$OMP BARRIER
1542 3964 : !$OMP MASTER
1543 1982 : CALL timeset(routineN, handle)
1544 : !$OMP END MASTER
1545 1982 : !$OMP BARRIER
1546 :
1547 1982 : ncpu = para_env%num_pe
1548 1982 : n_processes = ncpu*n_threads
1549 : !! If there is only 1 cpu skip the binning
1550 1982 : IF (n_processes == 1) THEN
1551 0 : ALLOCATE (tmp_dist(1))
1552 0 : tmp_dist(1)%number_of_atom_quartets = HUGE(tmp_dist(1)%number_of_atom_quartets)
1553 0 : tmp_dist(1)%istart = 0_int_8
1554 0 : ptr_to_tmp_dist => tmp_dist(:)
1555 0 : SELECT CASE (eval_type)
1556 : CASE (hfx_do_eval_energy)
1557 0 : CALL hfx_set_distr_energy(ptr_to_tmp_dist, x_data)
1558 : CASE (hfx_do_eval_forces)
1559 0 : CALL hfx_set_distr_forces(ptr_to_tmp_dist, x_data)
1560 : END SELECT
1561 0 : DEALLOCATE (tmp_dist)
1562 : ELSE
1563 1982 : mepos = para_env%mepos
1564 1982 : my_process_id = para_env%mepos*n_threads + i_thread
1565 1982 : nbins = load_balance_parameter%nbins
1566 1982 : !$OMP MASTER
1567 5946 : ALLOCATE (bin_histogram(n_processes, 2))
1568 1982 : bin_histogram = 0
1569 : !$OMP END MASTER
1570 1982 : !$OMP BARRIER
1571 3088 : SELECT CASE (eval_type)
1572 : CASE (hfx_do_eval_energy)
1573 1106 : my_bin_size = SIZE(x_data%distribution_energy)
1574 : CASE (hfx_do_eval_forces)
1575 1982 : my_bin_size = SIZE(x_data%distribution_forces)
1576 : END SELECT
1577 1982 : bin_histogram(my_process_id + 1, 1) = my_bin_size
1578 1982 : !$OMP BARRIER
1579 1982 : !$OMP MASTER
1580 1982 : CALL para_env%sum(bin_histogram(:, 1))
1581 1982 : bin_histogram(1, 2) = bin_histogram(1, 1)
1582 3964 : DO iprocess = 2, n_processes
1583 3964 : bin_histogram(iprocess, 2) = bin_histogram(iprocess - 1, 2) + bin_histogram(iprocess, 1)
1584 : END DO
1585 :
1586 3964 : max_bin_size = MAXVAL(bin_histogram(para_env%mepos*n_threads + 1:para_env%mepos*n_threads + n_threads, 1))
1587 1982 : CALL para_env%max(max_bin_size)
1588 : !$OMP END MASTER
1589 1982 : !$OMP BARRIER
1590 132788 : ALLOCATE (binned_dist(my_bin_size))
1591 : !! Use old binned_dist, but with timings cost
1592 1106 : SELECT CASE (eval_type)
1593 : CASE (hfx_do_eval_energy)
1594 142674 : binned_dist = x_data%distribution_energy
1595 : CASE (hfx_do_eval_forces)
1596 114110 : binned_dist = x_data%distribution_forces
1597 : END SELECT
1598 :
1599 128830 : DO ibin = 1, my_bin_size
1600 128830 : IF (binned_dist(ibin)%number_of_atom_quartets == 0) THEN
1601 109449 : binned_dist(ibin)%cost = 0
1602 : ELSE
1603 10191 : SELECT CASE (eval_type)
1604 : CASE (hfx_do_eval_energy)
1605 10191 : IF (.NOT. load_balance_parameter%rtp_redistribute) THEN
1606 : binned_dist(ibin)%cost = INT((binned_dist(ibin)%time_first_scf + &
1607 10191 : binned_dist(ibin)%time_other_scf)*10000.0_dp, int_8)
1608 : ELSE
1609 0 : binned_dist(ibin)%cost = INT((binned_dist(ibin)%time_other_scf)*10000.0_dp, int_8)
1610 : END IF
1611 : CASE (hfx_do_eval_forces)
1612 17399 : binned_dist(ibin)%cost = INT((binned_dist(ibin)%time_forces)*10000.0_dp, int_8)
1613 : END SELECT
1614 : END IF
1615 : END DO
1616 1982 : !$OMP BARRIER
1617 1982 : !$OMP MASTER
1618 : !! store all local results in a big cost matrix
1619 5946 : ALLOCATE (cost_matrix(ncpu*nbins*n_threads))
1620 255678 : cost_matrix = 0
1621 5946 : ALLOCATE (sendbuffer(max_bin_size*n_threads))
1622 3964 : ALLOCATE (recbuffer(max_bin_size*n_threads))
1623 : !$OMP END MASTER
1624 1982 : !$OMP BARRIER
1625 1982 : my_global_start_idx = bin_histogram(my_process_id + 1, 2) - my_bin_size
1626 1982 : icpu = para_env%mepos + 1
1627 128830 : DO i = 1, my_bin_size
1628 128830 : cost_matrix(my_global_start_idx + i) = binned_dist(i)%cost
1629 : END DO
1630 :
1631 1982 : mepos = para_env%mepos
1632 1982 : !$OMP BARRIER
1633 1982 : !$OMP MASTER
1634 5946 : ALLOCATE (bins_per_rank(ncpu))
1635 1982 : bins_per_rank = 0
1636 5946 : DO icpu = 1, ncpu
1637 9910 : bins_per_rank(icpu) = SUM(bin_histogram((icpu - 1)*n_threads + 1:(icpu - 1)*n_threads + n_threads, 1))
1638 : END DO
1639 : sendbuffer(1:bins_per_rank(para_env%mepos + 1)) = &
1640 255678 : cost_matrix(my_global_start_idx + 1:my_global_start_idx + bins_per_rank(para_env%mepos + 1))
1641 :
1642 1982 : dest = MODULO(mepos + 1, ncpu)
1643 1982 : source = MODULO(mepos - 1, ncpu)
1644 : ! sync before/after ring of isendrecv
1645 1982 : CALL para_env%sync()
1646 5946 : DO icpu = 0, ncpu - 1
1647 3964 : IF (icpu /= ncpu - 1) THEN
1648 : CALL para_env%isendrecv(sendbuffer, dest, recbuffer, source, &
1649 1982 : req(1), req(2), 13)
1650 : END IF
1651 3964 : data_from = MODULO(mepos - icpu, ncpu)
1652 9910 : start_idx = SUM(bins_per_rank(1:data_from + 1)) - bins_per_rank(data_from + 1) + 1
1653 3964 : end_idx = start_idx + bins_per_rank(data_from + 1) - 1
1654 511356 : cost_matrix(start_idx:end_idx) = sendbuffer(1:end_idx - start_idx + 1)
1655 :
1656 3964 : IF (icpu /= ncpu - 1) THEN
1657 1982 : CALL mp_waitall(req)
1658 : END IF
1659 3964 : swapbuffer => sendbuffer
1660 3964 : sendbuffer => recbuffer
1661 5946 : recbuffer => swapbuffer
1662 : END DO
1663 1982 : DEALLOCATE (recbuffer, sendbuffer)
1664 : ! sync before/after ring of isendrecv
1665 1982 : CALL para_env%sync()
1666 : !$OMP END MASTER
1667 1982 : !$OMP BARRIER
1668 5946 : ALLOCATE (local_cost_matrix(SIZE(cost_matrix, 1)))
1669 509374 : local_cost_matrix = cost_matrix
1670 1982 : !$OMP MASTER
1671 5946 : ALLOCATE (shm_distribution_vector(ncpu*nbins*n_threads))
1672 : CALL optimize_distribution(ncpu*nbins*n_threads, ncpu*n_threads, local_cost_matrix, &
1673 1982 : shm_distribution_vector, x_data%load_balance_parameter%do_randomize)
1674 :
1675 718484 : ALLOCATE (full_dist(ncpu*n_threads, max_bin_size))
1676 :
1677 712538 : full_dist(:, :)%istart = 0_int_8
1678 712538 : full_dist(:, :)%number_of_atom_quartets = 0_int_8
1679 712538 : full_dist(:, :)%cost = 0_int_8
1680 712538 : full_dist(:, :)%time_first_scf = 0.0_dp
1681 712538 : full_dist(:, :)%time_other_scf = 0.0_dp
1682 712538 : full_dist(:, :)%time_forces = 0.0_dp
1683 : !$OMP END MASTER
1684 :
1685 1982 : !$OMP BARRIER
1686 1982 : mepos = para_env%mepos + 1
1687 255678 : full_dist((mepos - 1)*n_threads + i_thread + 1, 1:my_bin_size) = binned_dist(1:my_bin_size)
1688 1982 : !$OMP BARRIER
1689 1982 : !$OMP MASTER
1690 5946 : ALLOCATE (sendbuffer(3*max_bin_size*n_threads))
1691 3964 : ALLOCATE (recbuffer(3*max_bin_size*n_threads))
1692 1982 : mepos = para_env%mepos
1693 3964 : DO j = 1, n_threads
1694 240816 : DO i = 1, max_bin_size
1695 236852 : sendbuffer((j - 1)*3*max_bin_size + (i - 1)*3 + 1) = full_dist(mepos*n_threads + j, i)%istart
1696 236852 : sendbuffer((j - 1)*3*max_bin_size + (i - 1)*3 + 2) = full_dist(mepos*n_threads + j, i)%number_of_atom_quartets
1697 238834 : sendbuffer((j - 1)*3*max_bin_size + (i - 1)*3 + 3) = full_dist(mepos*n_threads + j, i)%cost
1698 : END DO
1699 : END DO
1700 1982 : dest = MODULO(mepos + 1, ncpu)
1701 1982 : source = MODULO(mepos - 1, ncpu)
1702 : ! sync before/after ring of isendrecv
1703 1982 : CALL para_env%sync()
1704 5946 : DO icpu = 0, ncpu - 1
1705 3964 : IF (icpu /= ncpu - 1) THEN
1706 : CALL para_env%isendrecv(sendbuffer, dest, recbuffer, source, &
1707 1982 : req(1), req(2), 13)
1708 : END IF
1709 3964 : data_from = MODULO(mepos - icpu, ncpu)
1710 7928 : DO j = 1, n_threads
1711 481632 : DO i = 1, max_bin_size
1712 473704 : full_dist(data_from*n_threads + j, i)%istart = sendbuffer((j - 1)*3*max_bin_size + (i - 1)*3 + 1)
1713 473704 : full_dist(data_from*n_threads + j, i)%number_of_atom_quartets = sendbuffer((j - 1)*3*max_bin_size + (i - 1)*3 + 2)
1714 477668 : full_dist(data_from*n_threads + j, i)%cost = sendbuffer((j - 1)*3*max_bin_size + (i - 1)*3 + 3)
1715 : END DO
1716 : END DO
1717 :
1718 3964 : IF (icpu /= ncpu - 1) THEN
1719 1982 : CALL mp_waitall(req)
1720 : END IF
1721 3964 : swapbuffer => sendbuffer
1722 3964 : sendbuffer => recbuffer
1723 5946 : recbuffer => swapbuffer
1724 : END DO
1725 : ! sync before/after ring of isendrecv
1726 1982 : DEALLOCATE (recbuffer, sendbuffer)
1727 1982 : CALL para_env%sync()
1728 : !$OMP END MASTER
1729 1982 : !$OMP BARRIER
1730 : !! reorder the distribution according to the distribution vector
1731 5946 : ALLOCATE (tmp_pos(ncpu*n_threads))
1732 5946 : tmp_pos = 1
1733 259642 : ALLOCATE (tmp_dist(nbins*ncpu*n_threads))
1734 :
1735 255678 : tmp_dist(:)%istart = 0_int_8
1736 255678 : tmp_dist(:)%number_of_atom_quartets = 0_int_8
1737 255678 : tmp_dist(:)%cost = 0_int_8
1738 255678 : tmp_dist(:)%time_first_scf = 0.0_dp
1739 255678 : tmp_dist(:)%time_other_scf = 0.0_dp
1740 255678 : tmp_dist(:)%time_forces = 0.0_dp
1741 :
1742 5946 : mepos = my_process_id + 1
1743 5946 : DO icpu = 1, n_processes
1744 259642 : DO i = 1, bin_histogram(icpu, 1)
1745 257660 : IF (shm_distribution_vector(bin_histogram(icpu, 2) - bin_histogram(icpu, 1) + i) == mepos) THEN
1746 126848 : tmp_dist(tmp_pos(mepos)) = full_dist(icpu, i)
1747 126848 : tmp_pos(mepos) = tmp_pos(mepos) + 1
1748 : END IF
1749 : END DO
1750 : END DO
1751 :
1752 : !! Assign the load to each process
1753 : NULLIFY (ptr_to_tmp_dist)
1754 1982 : mepos = my_process_id + 1
1755 1982 : ptr_to_tmp_dist => tmp_dist(1:tmp_pos(mepos) - 1)
1756 1106 : SELECT CASE (eval_type)
1757 : CASE (hfx_do_eval_energy)
1758 1106 : CALL hfx_set_distr_energy(ptr_to_tmp_dist, x_data)
1759 : CASE (hfx_do_eval_forces)
1760 1982 : CALL hfx_set_distr_forces(ptr_to_tmp_dist, x_data)
1761 : END SELECT
1762 :
1763 1982 : !$OMP BARRIER
1764 1982 : !$OMP MASTER
1765 1982 : DEALLOCATE (full_dist, cost_matrix, shm_distribution_vector)
1766 1982 : DEALLOCATE (bins_per_rank, bin_histogram)
1767 : !$OMP END MASTER
1768 1982 : !$OMP BARRIER
1769 1982 : DEALLOCATE (tmp_dist, tmp_pos)
1770 1982 : DEALLOCATE (binned_dist, local_cost_matrix)
1771 : END IF
1772 1982 : !$OMP BARRIER
1773 1982 : !$OMP MASTER
1774 1982 : CALL timestop(handle)
1775 : !$OMP END MASTER
1776 1982 : !$OMP BARRIER
1777 :
1778 3964 : END SUBROUTINE hfx_update_load_balance
1779 :
1780 : ! **************************************************************************************************
1781 : !> \brief estimates the cost of a set quartet with info available at load balance time
1782 : !> i.e. without much info on the primitives primitives
1783 : !> \param nsa ...
1784 : !> \param nsb ...
1785 : !> \param nsc ...
1786 : !> \param nsd ...
1787 : !> \param npgfa ...
1788 : !> \param npgfb ...
1789 : !> \param npgfc ...
1790 : !> \param npgfd ...
1791 : !> \param ratio ...
1792 : !> \param p1 ...
1793 : !> \param p2 ...
1794 : !> \param p3 ...
1795 : !> \return ...
1796 : !> \par History
1797 : !> 08.2009 created Joost VandeVondele
1798 : !> \author Joost VandeVondele
1799 : ! **************************************************************************************************
1800 5591994 : FUNCTION cost_model(nsa, nsb, nsc, nsd, npgfa, npgfb, npgfc, npgfd, ratio, p1, p2, p3) RESULT(res)
1801 : REAL(KIND=dp) :: estimate1, estimate2, estimate, ratio, switch, mu, sigma
1802 : INTEGER(KIND=int_8) :: res
1803 : REAL(KIND=dp), INTENT(IN) :: p1(12), p2(12), p3(2)
1804 :
1805 : INTEGER :: nsa, nsb, nsc, nsd, npgfa, npgfb, npgfc, npgfd
1806 :
1807 5591994 : estimate1 = estimate_basic(p1)
1808 5591994 : estimate2 = estimate_basic(p2)
1809 5591994 : mu = LOG(ABS(1.0E6_dp*p3(1)) + 1)
1810 5591994 : sigma = p3(2)*0.1_dp*mu
1811 5591994 : switch = 1.0_dp/(1.0_dp + EXP((LOG(estimate1) - mu)/sigma))
1812 5591994 : estimate = estimate1*(1.0_dp - switch) + estimate2*switch
1813 5591994 : res = INT(estimate*0.001_dp, KIND=int_8) + 1
1814 :
1815 : CONTAINS
1816 :
1817 : ! **************************************************************************************************
1818 : !> \brief ...
1819 : !> \param p ...
1820 : !> \return ...
1821 : ! **************************************************************************************************
1822 11183988 : REAL(KIND=dp) FUNCTION estimate_basic(p) RESULT(res)
1823 : REAL(KIND=dp) :: p(12)
1824 :
1825 : REAL(KIND=dp) :: p1, p10, p11, p12, p2, p3, p4, p5, p6, &
1826 : p7, p8, p9
1827 :
1828 11183988 : p1 = p(1); p2 = p(2); p3 = p(3); p4 = p(4)
1829 11183988 : p5 = p(5); p6 = p(6); p7 = p(7); p8 = p(8)
1830 11183988 : p9 = p(9); p10 = p(10); p11 = p(11); p12 = p(12)
1831 : res = poly2(nsa, p1, p2, p3)*poly2(nsb, p1, p2, p3)*poly2(nsc, p1, p2, p3)*poly2(nsd, p1, p2, p3)* &
1832 : poly2(npgfa, p4, p5, p6)*poly2(npgfb, p4, p5, p6)*poly2(npgfc, p4, p5, p6)* &
1833 : poly2(npgfd, p4, p5, p6)*EXP(-p7*ratio + p8*ratio**2) + &
1834 11183988 : 1000.0_dp*p9 + poly2(nsa, p10, p11, p12)*poly2(nsb, p10, p11, p12)*poly2(nsc, p10, p11, p12)*poly2(nsd, p10, p11, p12)
1835 11183988 : res = 1 + ABS(res)
1836 11183988 : END FUNCTION estimate_basic
1837 :
1838 : ! **************************************************************************************************
1839 : !> \brief ...
1840 : !> \param x ...
1841 : !> \param a0 ...
1842 : !> \param a1 ...
1843 : !> \param a2 ...
1844 : !> \return ...
1845 : ! **************************************************************************************************
1846 134207856 : REAL(KIND=dp) FUNCTION poly2(x, a0, a1, a2)
1847 : INTEGER, INTENT(IN) :: x
1848 : REAL(KIND=dp), INTENT(IN) :: a0, a1, a2
1849 : REAL(KIND=dp) :: r
1850 :
1851 134207856 : r = REAL(x, KIND=dp)
1852 134207856 : poly2 = a0 + (a1 + a2*r)*r
1853 134207856 : END FUNCTION poly2
1854 :
1855 : END FUNCTION cost_model
1856 : ! **************************************************************************************************
1857 : !> \brief Minimizes the maximum cost per cpu by shuffling around all bins
1858 : !> \param total_number_of_bins ...
1859 : !> \param number_of_processes ...
1860 : !> \param bin_costs costs per bin
1861 : !> \param distribution_vector will contain the final distribution
1862 : !> \param do_randomize ...
1863 : !> \par History
1864 : !> 03.2009 created from a hack by Joost [Manuel Guidon]
1865 : !> \author Manuel Guidon
1866 : ! **************************************************************************************************
1867 4108 : SUBROUTINE optimize_distribution(total_number_of_bins, number_of_processes, bin_costs, &
1868 : distribution_vector, do_randomize)
1869 : INTEGER :: total_number_of_bins, number_of_processes
1870 : INTEGER(int_8), DIMENSION(:), POINTER :: bin_costs
1871 : INTEGER, DIMENSION(:), POINTER :: distribution_vector
1872 : LOGICAL, INTENT(IN) :: do_randomize
1873 :
1874 : INTEGER :: i, itmp, j, nstep
1875 4108 : INTEGER(int_8), DIMENSION(:), POINTER :: my_cost_cpu, tmp_cost, tmp_cpu_cost
1876 : INTEGER, DIMENSION(:), POINTER :: tmp_cpu_index, tmp_index
1877 4108 : TYPE(rng_stream_type), ALLOCATABLE :: rng_stream
1878 :
1879 4108 : nstep = MAX(1, INT(number_of_processes)/2)
1880 :
1881 12324 : ALLOCATE (tmp_cost(total_number_of_bins))
1882 12324 : ALLOCATE (tmp_index(total_number_of_bins))
1883 12324 : ALLOCATE (tmp_cpu_cost(number_of_processes))
1884 12324 : ALLOCATE (tmp_cpu_index(number_of_processes))
1885 8216 : ALLOCATE (my_cost_cpu(number_of_processes))
1886 1059864 : tmp_cost = bin_costs
1887 :
1888 4108 : CALL sort(tmp_cost, total_number_of_bins, tmp_index)
1889 12324 : my_cost_cpu = 0
1890 : !
1891 : ! assign the largest remaining bin to the CPU with the smallest load
1892 : ! gives near perfect distributions for a sufficient number of bins ...
1893 : ! doing this in chunks of nstep (where nstep ~ number_of_processes) makes this n log n and gives
1894 : ! each cpu a similar number of tasks.
1895 : ! it also avoids degenerate cases where thousands of zero sized tasks
1896 : ! are assigned to the same (least loaded) cpu
1897 : !
1898 4108 : IF (do_randomize) THEN
1899 : rng_stream = rng_stream_type(name="uniform_rng", &
1900 0 : distribution_type=UNIFORM)
1901 : END IF
1902 :
1903 529932 : DO i = total_number_of_bins, 1, -nstep
1904 3154944 : tmp_cpu_cost = my_cost_cpu
1905 525824 : CALL sort(tmp_cpu_cost, INT(number_of_processes), tmp_cpu_index)
1906 525824 : IF (do_randomize) THEN
1907 0 : CALL rng_stream%shuffle(tmp_cpu_index(1:MIN(i, nstep)))
1908 : END IF
1909 1055756 : DO j = 1, MIN(i, nstep)
1910 525824 : itmp = tmp_cpu_index(j)
1911 525824 : distribution_vector(tmp_index(i - j + 1)) = itmp
1912 1051648 : my_cost_cpu(itmp) = my_cost_cpu(itmp) + bin_costs(tmp_index(i - j + 1))
1913 : END DO
1914 : END DO
1915 :
1916 4108 : DEALLOCATE (tmp_cost, tmp_index, tmp_cpu_cost)
1917 4108 : DEALLOCATE (tmp_cpu_index, my_cost_cpu)
1918 8216 : END SUBROUTINE optimize_distribution
1919 :
1920 : ! **************************************************************************************************
1921 : !> \brief Given a 2d index pair, this function returns a 1d index pair for
1922 : !> a symmetric upper triangle NxN matrix
1923 : !> The compiler should inline this function, therefore it appears in
1924 : !> several modules
1925 : !> \param i 2d index
1926 : !> \param j 2d index
1927 : !> \param N matrix size
1928 : !> \return ...
1929 : !> \par History
1930 : !> 03.2009 created [Manuel Guidon]
1931 : !> \author Manuel Guidon
1932 : ! **************************************************************************************************
1933 34660 : PURE FUNCTION get_1D_idx(i, j, N)
1934 : INTEGER, INTENT(IN) :: i, j
1935 : INTEGER(int_8), INTENT(IN) :: N
1936 : INTEGER(int_8) :: get_1D_idx
1937 :
1938 : INTEGER(int_8) :: min_ij
1939 :
1940 34660 : min_ij = MIN(i, j)
1941 34660 : get_1D_idx = min_ij*N + MAX(i, j) - (min_ij - 1)*min_ij/2 - N
1942 :
1943 34660 : END FUNCTION get_1D_idx
1944 :
1945 : ! **************************************************************************************************
1946 : !> \brief ...
1947 : !> \param natom ...
1948 : !> \param nkind ...
1949 : !> \param list_ij ...
1950 : !> \param list_kl ...
1951 : !> \param set_list_ij ...
1952 : !> \param set_list_kl ...
1953 : !> \param iatom_start ...
1954 : !> \param iatom_end ...
1955 : !> \param jatom_start ...
1956 : !> \param jatom_end ...
1957 : !> \param katom_start ...
1958 : !> \param katom_end ...
1959 : !> \param latom_start ...
1960 : !> \param latom_end ...
1961 : !> \param particle_set ...
1962 : !> \param coeffs_set ...
1963 : !> \param coeffs_kind ...
1964 : !> \param is_assoc_atomic_block_global ...
1965 : !> \param do_periodic ...
1966 : !> \param kind_of ...
1967 : !> \param basis_parameter ...
1968 : !> \param pmax_set ...
1969 : !> \param pmax_atom ...
1970 : !> \param pmax_blocks ...
1971 : !> \param cell ...
1972 : !> \param do_p_screening ...
1973 : !> \param map_atom_to_kind_atom ...
1974 : !> \param eval_type ...
1975 : !> \param log10_eps_schwarz ...
1976 : !> \param log_2 ...
1977 : !> \param coeffs_kind_max0 ...
1978 : !> \param use_virial ...
1979 : !> \param atomic_pair_list ...
1980 : !> \return ...
1981 : ! **************************************************************************************************
1982 189279 : FUNCTION estimate_block_cost(natom, nkind, list_ij, list_kl, set_list_ij, set_list_kl, &
1983 : iatom_start, iatom_end, jatom_start, jatom_end, &
1984 : katom_start, katom_end, latom_start, latom_end, &
1985 : particle_set, &
1986 189279 : coeffs_set, coeffs_kind, &
1987 189279 : is_assoc_atomic_block_global, do_periodic, &
1988 : kind_of, basis_parameter, pmax_set, pmax_atom, pmax_blocks, &
1989 : cell, &
1990 : do_p_screening, map_atom_to_kind_atom, eval_type, &
1991 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, &
1992 189279 : atomic_pair_list)
1993 :
1994 : INTEGER, INTENT(IN) :: natom, nkind
1995 : TYPE(pair_list_type) :: list_ij, list_kl
1996 : TYPE(pair_set_list_type), DIMENSION(:) :: set_list_ij, set_list_kl
1997 : INTEGER, INTENT(IN) :: iatom_start, iatom_end, jatom_start, &
1998 : jatom_end, katom_start, katom_end, &
1999 : latom_start, latom_end
2000 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
2001 : TYPE(hfx_screen_coeff_type), &
2002 : DIMENSION(:, :, :, :), POINTER :: coeffs_set
2003 : TYPE(hfx_screen_coeff_type), &
2004 : DIMENSION(nkind, nkind) :: coeffs_kind
2005 : INTEGER, DIMENSION(:, :) :: is_assoc_atomic_block_global
2006 : LOGICAL :: do_periodic
2007 : INTEGER :: kind_of(*)
2008 : TYPE(hfx_basis_type), DIMENSION(:), POINTER :: basis_parameter
2009 : TYPE(hfx_p_kind), DIMENSION(:), POINTER :: pmax_set
2010 : REAL(dp), DIMENSION(:, :), POINTER :: pmax_atom
2011 : REAL(dp) :: pmax_blocks
2012 : TYPE(cell_type), POINTER :: cell
2013 : LOGICAL, INTENT(IN) :: do_p_screening
2014 : INTEGER, DIMENSION(:), POINTER :: map_atom_to_kind_atom
2015 : INTEGER, INTENT(IN) :: eval_type
2016 : REAL(dp) :: log10_eps_schwarz, log_2, &
2017 : coeffs_kind_max0
2018 : LOGICAL, INTENT(IN) :: use_virial
2019 : LOGICAL, DIMENSION(natom, natom) :: atomic_pair_list
2020 : INTEGER(int_8) :: estimate_block_cost
2021 :
2022 : INTEGER :: i_list_ij, i_list_kl, i_set_list_ij, i_set_list_ij_start, i_set_list_ij_stop, &
2023 : i_set_list_kl, i_set_list_kl_start, i_set_list_kl_stop, iatom, ikind, iset, jatom, jkind, &
2024 : jset, katom, kind_kind_idx, kkind, kset, latom, lkind, lset, swap_id
2025 189279 : INTEGER, DIMENSION(:), POINTER :: npgfa, npgfb, npgfc, npgfd, nsgfa, &
2026 189279 : nsgfb, nsgfc, nsgfd
2027 : REAL(dp) :: actual_pmax_atom, cost_tmp, max_val1, &
2028 : max_val2, pmax_entry, rab2, rcd2, &
2029 : screen_kind_ij, screen_kind_kl
2030 189279 : REAL(dp), DIMENSION(:, :), POINTER :: ptr_p_1, ptr_p_2, ptr_p_3, ptr_p_4
2031 :
2032 189279 : estimate_block_cost = 0_int_8
2033 :
2034 : CALL build_pair_list(natom, list_ij, set_list_ij, iatom_start, iatom_end, jatom_start, jatom_end, &
2035 : kind_of, basis_parameter, particle_set, &
2036 : do_periodic, coeffs_set, coeffs_kind, coeffs_kind_max0, &
2037 189279 : log10_eps_schwarz, cell, pmax_blocks, atomic_pair_list)
2038 :
2039 : CALL build_pair_list(natom, list_kl, set_list_kl, katom_start, katom_end, latom_start, latom_end, &
2040 : kind_of, basis_parameter, particle_set, &
2041 : do_periodic, coeffs_set, coeffs_kind, coeffs_kind_max0, &
2042 189279 : log10_eps_schwarz, cell, pmax_blocks, atomic_pair_list)
2043 :
2044 363427 : DO i_list_ij = 1, list_ij%n_element
2045 174148 : iatom = list_ij%elements(i_list_ij)%pair(1)
2046 174148 : jatom = list_ij%elements(i_list_ij)%pair(2)
2047 174148 : i_set_list_ij_start = list_ij%elements(i_list_ij)%set_bounds(1)
2048 174148 : i_set_list_ij_stop = list_ij%elements(i_list_ij)%set_bounds(2)
2049 174148 : ikind = list_ij%elements(i_list_ij)%kind_pair(1)
2050 174148 : jkind = list_ij%elements(i_list_ij)%kind_pair(2)
2051 174148 : rab2 = list_ij%elements(i_list_ij)%dist2
2052 :
2053 174148 : nsgfa => basis_parameter(ikind)%nsgf
2054 174148 : nsgfb => basis_parameter(jkind)%nsgf
2055 174148 : npgfa => basis_parameter(ikind)%npgf
2056 174148 : npgfb => basis_parameter(jkind)%npgf
2057 :
2058 528416 : DO i_list_kl = 1, list_kl%n_element
2059 :
2060 164989 : katom = list_kl%elements(i_list_kl)%pair(1)
2061 164989 : latom = list_kl%elements(i_list_kl)%pair(2)
2062 :
2063 164989 : IF (.NOT. (katom + latom <= iatom + jatom)) CYCLE
2064 91295 : IF (((iatom + jatom) == (katom + latom)) .AND. (katom < iatom)) CYCLE
2065 :
2066 87629 : IF (eval_type == hfx_do_eval_forces) THEN
2067 8303 : IF (.NOT. use_virial) THEN
2068 7543 : IF ((iatom == jatom .AND. iatom == katom .AND. iatom == latom)) CYCLE
2069 : END IF
2070 : END IF
2071 :
2072 86836 : i_set_list_kl_start = list_kl%elements(i_list_kl)%set_bounds(1)
2073 86836 : i_set_list_kl_stop = list_kl%elements(i_list_kl)%set_bounds(2)
2074 86836 : kkind = list_kl%elements(i_list_kl)%kind_pair(1)
2075 86836 : lkind = list_kl%elements(i_list_kl)%kind_pair(2)
2076 86836 : rcd2 = list_kl%elements(i_list_kl)%dist2
2077 :
2078 86836 : nsgfc => basis_parameter(kkind)%nsgf
2079 86836 : nsgfd => basis_parameter(lkind)%nsgf
2080 86836 : npgfc => basis_parameter(kkind)%npgf
2081 86836 : npgfd => basis_parameter(lkind)%npgf
2082 :
2083 86836 : IF (do_p_screening) THEN
2084 : actual_pmax_atom = MAX(pmax_atom(katom, iatom), &
2085 : pmax_atom(latom, jatom), &
2086 : pmax_atom(latom, iatom), &
2087 8845 : pmax_atom(katom, jatom))
2088 : ELSE
2089 : actual_pmax_atom = 0.0_dp
2090 : END IF
2091 :
2092 : screen_kind_ij = coeffs_kind(jkind, ikind)%x(1)*rab2 + &
2093 86836 : coeffs_kind(jkind, ikind)%x(2)
2094 : screen_kind_kl = coeffs_kind(lkind, kkind)%x(1)*rcd2 + &
2095 86836 : coeffs_kind(lkind, kkind)%x(2)
2096 86836 : IF (screen_kind_ij + screen_kind_kl + actual_pmax_atom < log10_eps_schwarz) CYCLE
2097 :
2098 83247 : IF (.NOT. (is_assoc_atomic_block_global(latom, iatom) >= 1 .AND. &
2099 : is_assoc_atomic_block_global(katom, iatom) >= 1 .AND. &
2100 : is_assoc_atomic_block_global(katom, jatom) >= 1 .AND. &
2101 : is_assoc_atomic_block_global(latom, jatom) >= 1)) CYCLE
2102 :
2103 81655 : IF (do_p_screening) THEN
2104 3131 : SELECT CASE (eval_type)
2105 : CASE (hfx_do_eval_energy)
2106 3131 : swap_id = 0
2107 3131 : kind_kind_idx = INT(get_1D_idx(kkind, ikind, INT(nkind, int_8)))
2108 3131 : IF (ikind >= kkind) THEN
2109 : ptr_p_1 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2110 : map_atom_to_kind_atom(katom), &
2111 2824 : map_atom_to_kind_atom(iatom))
2112 : ELSE
2113 : ptr_p_1 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2114 : map_atom_to_kind_atom(iatom), &
2115 307 : map_atom_to_kind_atom(katom))
2116 307 : swap_id = swap_id + 1
2117 : END IF
2118 3131 : kind_kind_idx = INT(get_1D_idx(lkind, jkind, INT(nkind, int_8)))
2119 3131 : IF (jkind >= lkind) THEN
2120 : ptr_p_2 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2121 : map_atom_to_kind_atom(latom), &
2122 2949 : map_atom_to_kind_atom(jatom))
2123 : ELSE
2124 : ptr_p_2 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2125 : map_atom_to_kind_atom(jatom), &
2126 182 : map_atom_to_kind_atom(latom))
2127 182 : swap_id = swap_id + 2
2128 : END IF
2129 3131 : kind_kind_idx = INT(get_1D_idx(lkind, ikind, INT(nkind, int_8)))
2130 3131 : IF (ikind >= lkind) THEN
2131 : ptr_p_3 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2132 : map_atom_to_kind_atom(latom), &
2133 1754 : map_atom_to_kind_atom(iatom))
2134 : ELSE
2135 : ptr_p_3 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2136 : map_atom_to_kind_atom(iatom), &
2137 1377 : map_atom_to_kind_atom(latom))
2138 1377 : swap_id = swap_id + 4
2139 : END IF
2140 3131 : kind_kind_idx = INT(get_1D_idx(kkind, jkind, INT(nkind, int_8)))
2141 3131 : IF (jkind >= kkind) THEN
2142 : ptr_p_4 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2143 : map_atom_to_kind_atom(katom), &
2144 3131 : map_atom_to_kind_atom(jatom))
2145 : ELSE
2146 : ptr_p_4 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2147 : map_atom_to_kind_atom(jatom), &
2148 0 : map_atom_to_kind_atom(katom))
2149 0 : swap_id = swap_id + 8
2150 : END IF
2151 : CASE (hfx_do_eval_forces)
2152 5534 : swap_id = 16
2153 5534 : kind_kind_idx = INT(get_1D_idx(kkind, ikind, INT(nkind, int_8)))
2154 5534 : IF (ikind >= kkind) THEN
2155 : ptr_p_1 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2156 : map_atom_to_kind_atom(katom), &
2157 5233 : map_atom_to_kind_atom(iatom))
2158 : ELSE
2159 : ptr_p_1 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2160 : map_atom_to_kind_atom(iatom), &
2161 301 : map_atom_to_kind_atom(katom))
2162 301 : swap_id = swap_id + 1
2163 : END IF
2164 5534 : kind_kind_idx = INT(get_1D_idx(lkind, jkind, INT(nkind, int_8)))
2165 5534 : IF (jkind >= lkind) THEN
2166 : ptr_p_2 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2167 : map_atom_to_kind_atom(latom), &
2168 5534 : map_atom_to_kind_atom(jatom))
2169 : ELSE
2170 : ptr_p_2 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2171 : map_atom_to_kind_atom(jatom), &
2172 0 : map_atom_to_kind_atom(latom))
2173 0 : swap_id = swap_id + 2
2174 : END IF
2175 5534 : kind_kind_idx = INT(get_1D_idx(lkind, ikind, INT(nkind, int_8)))
2176 5534 : IF (ikind >= lkind) THEN
2177 : ptr_p_3 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2178 : map_atom_to_kind_atom(latom), &
2179 4517 : map_atom_to_kind_atom(iatom))
2180 : ELSE
2181 : ptr_p_3 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2182 : map_atom_to_kind_atom(iatom), &
2183 1017 : map_atom_to_kind_atom(latom))
2184 1017 : swap_id = swap_id + 4
2185 : END IF
2186 5534 : kind_kind_idx = INT(get_1D_idx(kkind, jkind, INT(nkind, int_8)))
2187 14199 : IF (jkind >= kkind) THEN
2188 : ptr_p_4 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2189 : map_atom_to_kind_atom(katom), &
2190 5534 : map_atom_to_kind_atom(jatom))
2191 : ELSE
2192 : ptr_p_4 => pmax_set(kind_kind_idx)%p_kind(:, :, &
2193 : map_atom_to_kind_atom(jatom), &
2194 0 : map_atom_to_kind_atom(katom))
2195 0 : swap_id = swap_id + 8
2196 : END IF
2197 : END SELECT
2198 : END IF
2199 :
2200 652451 : DO i_set_list_ij = i_set_list_ij_start, i_set_list_ij_stop
2201 396648 : iset = set_list_ij(i_set_list_ij)%pair(1)
2202 396648 : jset = set_list_ij(i_set_list_ij)%pair(2)
2203 :
2204 : max_val1 = coeffs_set(jset, iset, jkind, ikind)%x(1)*rab2 + &
2205 396648 : coeffs_set(jset, iset, jkind, ikind)%x(2)
2206 :
2207 396648 : IF (max_val1 + screen_kind_kl + actual_pmax_atom < log10_eps_schwarz) CYCLE
2208 5734196 : DO i_set_list_kl = i_set_list_kl_start, i_set_list_kl_stop
2209 5180743 : kset = set_list_kl(i_set_list_kl)%pair(1)
2210 5180743 : lset = set_list_kl(i_set_list_kl)%pair(2)
2211 :
2212 : max_val2 = max_val1 + (coeffs_set(lset, kset, lkind, kkind)%x(1)*rcd2 + &
2213 5180743 : coeffs_set(lset, kset, lkind, kkind)%x(2))
2214 :
2215 5180743 : IF (max_val2 + actual_pmax_atom < log10_eps_schwarz) CYCLE
2216 4678349 : IF (do_p_screening) THEN
2217 : CALL get_pmax_val(ptr_p_1, ptr_p_2, ptr_p_3, ptr_p_4, &
2218 : iset, jset, kset, lset, &
2219 555612 : pmax_entry, swap_id)
2220 555612 : IF (eval_type == hfx_do_eval_forces) THEN
2221 349692 : pmax_entry = log_2 + pmax_entry
2222 : END IF
2223 : ELSE
2224 4122737 : pmax_entry = 0.0_dp
2225 : END IF
2226 4678349 : max_val2 = max_val2 + pmax_entry
2227 4678349 : IF (max_val2 < log10_eps_schwarz) CYCLE
2228 396648 : SELECT CASE (eval_type)
2229 : CASE (hfx_do_eval_energy)
2230 : cost_tmp = cost_model(nsgfa(iset), nsgfb(jset), nsgfc(kset), nsgfd(lset), &
2231 : npgfa(iset), npgfb(jset), npgfc(kset), npgfd(lset), &
2232 : max_val2/log10_eps_schwarz, &
2233 4037248 : p1_energy, p2_energy, p3_energy)
2234 4037248 : estimate_block_cost = estimate_block_cost + INT(cost_tmp, KIND=int_8)
2235 : CASE (hfx_do_eval_forces)
2236 : cost_tmp = cost_model(nsgfa(iset), nsgfb(jset), nsgfc(kset), nsgfd(lset), &
2237 : npgfa(iset), npgfb(jset), npgfc(kset), npgfd(lset), &
2238 : max_val2/log10_eps_schwarz, &
2239 542929 : p1_forces, p2_forces, p3_forces)
2240 5123106 : estimate_block_cost = estimate_block_cost + INT(cost_tmp, KIND=int_8)
2241 : END SELECT
2242 : END DO ! i_set_list_kl
2243 : END DO ! i_set_list_ij
2244 : END DO ! i_list_kl
2245 : END DO ! i_list_ij
2246 :
2247 189279 : END FUNCTION estimate_block_cost
2248 :
2249 : ! **************************************************************************************************
2250 : !> \brief ...
2251 : !> \param nkind ...
2252 : !> \param para_env ...
2253 : !> \param natom ...
2254 : !> \param block_size ...
2255 : !> \param nblock ...
2256 : !> \param blocks ...
2257 : !> \param list_ij ...
2258 : !> \param list_kl ...
2259 : !> \param set_list_ij ...
2260 : !> \param set_list_kl ...
2261 : !> \param particle_set ...
2262 : !> \param coeffs_set ...
2263 : !> \param coeffs_kind ...
2264 : !> \param is_assoc_atomic_block_global ...
2265 : !> \param do_periodic ...
2266 : !> \param kind_of ...
2267 : !> \param basis_parameter ...
2268 : !> \param pmax_set ...
2269 : !> \param pmax_atom ...
2270 : !> \param pmax_blocks ...
2271 : !> \param cell ...
2272 : !> \param do_p_screening ...
2273 : !> \param map_atom_to_kind_atom ...
2274 : !> \param eval_type ...
2275 : !> \param log10_eps_schwarz ...
2276 : !> \param log_2 ...
2277 : !> \param coeffs_kind_max0 ...
2278 : !> \param use_virial ...
2279 : !> \param atomic_pair_list ...
2280 : ! **************************************************************************************************
2281 1490 : SUBROUTINE init_blocks(nkind, para_env, natom, block_size, nblock, blocks, &
2282 1490 : list_ij, list_kl, set_list_ij, set_list_kl, &
2283 : particle_set, &
2284 : coeffs_set, coeffs_kind, &
2285 1490 : is_assoc_atomic_block_global, do_periodic, &
2286 : kind_of, basis_parameter, pmax_set, pmax_atom, &
2287 : pmax_blocks, cell, &
2288 : do_p_screening, map_atom_to_kind_atom, eval_type, &
2289 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, &
2290 1490 : atomic_pair_list)
2291 :
2292 : INTEGER, INTENT(IN) :: nkind
2293 : TYPE(mp_para_env_type), INTENT(IN) :: para_env
2294 : INTEGER :: natom, block_size, nblock
2295 : TYPE(hfx_block_range_type), DIMENSION(1:nblock) :: blocks
2296 : TYPE(pair_list_type) :: list_ij, list_kl
2297 : TYPE(pair_set_list_type), DIMENSION(:) :: set_list_ij, set_list_kl
2298 : TYPE(particle_type), DIMENSION(:), POINTER :: particle_set
2299 : TYPE(hfx_screen_coeff_type), &
2300 : DIMENSION(:, :, :, :), POINTER :: coeffs_set
2301 : TYPE(hfx_screen_coeff_type), DIMENSION(:, :), &
2302 : POINTER :: coeffs_kind
2303 : INTEGER, DIMENSION(:, :) :: is_assoc_atomic_block_global
2304 : LOGICAL :: do_periodic
2305 : INTEGER :: kind_of(*)
2306 : TYPE(hfx_basis_type), DIMENSION(:), POINTER :: basis_parameter
2307 : TYPE(hfx_p_kind), DIMENSION(:), POINTER :: pmax_set
2308 : REAL(dp), DIMENSION(:, :), POINTER :: pmax_atom
2309 : REAL(dp) :: pmax_blocks
2310 : TYPE(cell_type), POINTER :: cell
2311 : LOGICAL, INTENT(IN) :: do_p_screening
2312 : INTEGER, DIMENSION(:), POINTER :: map_atom_to_kind_atom
2313 : INTEGER, INTENT(IN) :: eval_type
2314 : REAL(dp) :: log10_eps_schwarz, log_2, &
2315 : coeffs_kind_max0
2316 : LOGICAL, INTENT(IN) :: use_virial
2317 : LOGICAL, DIMENSION(natom, natom) :: atomic_pair_list
2318 :
2319 : INTEGER :: atom_block, i, iatom_block, iatom_end, &
2320 : iatom_start, my_cpu_rank, ncpus
2321 :
2322 6206 : DO atom_block = 0, nblock - 1
2323 4716 : iatom_block = MODULO(atom_block, nblock) + 1
2324 4716 : iatom_start = (iatom_block - 1)*block_size + 1
2325 4716 : iatom_end = MIN(iatom_block*block_size, natom)
2326 4716 : blocks(atom_block + 1)%istart = iatom_start
2327 4716 : blocks(atom_block + 1)%iend = iatom_end
2328 6206 : blocks(atom_block + 1)%cost = 0_int_8
2329 : END DO
2330 :
2331 1490 : ncpus = para_env%num_pe
2332 1490 : my_cpu_rank = para_env%mepos
2333 6206 : DO i = 1, nblock
2334 4716 : IF (MODULO(i, ncpus) /= my_cpu_rank) THEN
2335 2355 : blocks(i)%istart = 0
2336 2355 : blocks(i)%iend = 0
2337 2355 : CYCLE
2338 : END IF
2339 2361 : iatom_start = blocks(i)%istart
2340 2361 : iatom_end = blocks(i)%iend
2341 : blocks(i)%cost = estimate_block_cost(natom, nkind, list_ij, list_kl, set_list_ij, set_list_kl, &
2342 : iatom_start, iatom_end, iatom_start, iatom_end, &
2343 : iatom_start, iatom_end, iatom_start, iatom_end, &
2344 : particle_set, &
2345 : coeffs_set, coeffs_kind, &
2346 : is_assoc_atomic_block_global, do_periodic, &
2347 : kind_of, basis_parameter, pmax_set, pmax_atom, pmax_blocks, &
2348 : cell, &
2349 : do_p_screening, map_atom_to_kind_atom, eval_type, &
2350 3851 : log10_eps_schwarz, log_2, coeffs_kind_max0, use_virial, atomic_pair_list)
2351 :
2352 : END DO
2353 1490 : END SUBROUTINE init_blocks
2354 :
2355 : ! **************************************************************************************************
2356 : !> \brief ...
2357 : !> \param para_env ...
2358 : !> \param x_data ...
2359 : !> \param iw ...
2360 : !> \param n_threads ...
2361 : !> \param i_thread ...
2362 : !> \param eval_type ...
2363 : ! **************************************************************************************************
2364 0 : SUBROUTINE collect_load_balance_info(para_env, x_data, iw, n_threads, i_thread, &
2365 : eval_type)
2366 :
2367 : TYPE(mp_para_env_type), INTENT(IN) :: para_env
2368 : TYPE(hfx_type), POINTER :: x_data
2369 : INTEGER, INTENT(IN) :: iw, n_threads, i_thread, eval_type
2370 :
2371 : INTEGER :: i, j, k, my_rank, nbins, nranks, &
2372 : total_bins
2373 : INTEGER(int_8) :: avg_bin, avg_rank, max_bin, max_rank, &
2374 : min_bin, min_rank, sum_bin, sum_rank
2375 0 : INTEGER(int_8), ALLOCATABLE, DIMENSION(:) :: buffer, buffer_in, buffer_out, summary
2376 : INTEGER(int_8), ALLOCATABLE, DIMENSION(:), SAVE :: shm_cost_vector
2377 0 : INTEGER, ALLOCATABLE, DIMENSION(:) :: bins_per_rank, rdispl, sort_idx
2378 : INTEGER, ALLOCATABLE, DIMENSION(:), SAVE :: shm_bins_per_rank, shm_displ
2379 :
2380 0 : SELECT CASE (eval_type)
2381 : CASE (hfx_do_eval_energy)
2382 0 : nbins = SIZE(x_data%distribution_energy)
2383 : CASE (hfx_do_eval_forces)
2384 0 : nbins = SIZE(x_data%distribution_forces)
2385 : END SELECT
2386 :
2387 0 : !$OMP MASTER
2388 0 : ALLOCATE (shm_bins_per_rank(n_threads))
2389 0 : ALLOCATE (shm_displ(n_threads + 1))
2390 : !$OMP END MASTER
2391 0 : !$OMP BARRIER
2392 :
2393 0 : shm_bins_per_rank(i_thread + 1) = nbins
2394 0 : !$OMP BARRIER
2395 0 : nbins = 0
2396 0 : DO i = 1, n_threads
2397 0 : nbins = nbins + shm_bins_per_rank(i)
2398 : END DO
2399 0 : my_rank = para_env%mepos
2400 0 : nranks = para_env%num_pe
2401 :
2402 0 : !$OMP BARRIER
2403 0 : !$OMP MASTER
2404 0 : ALLOCATE (bins_per_rank(nranks))
2405 0 : bins_per_rank = 0
2406 :
2407 0 : bins_per_rank(my_rank + 1) = nbins
2408 :
2409 0 : CALL para_env%sum(bins_per_rank)
2410 :
2411 0 : total_bins = 0
2412 0 : DO i = 1, nranks
2413 0 : total_bins = total_bins + bins_per_rank(i)
2414 : END DO
2415 :
2416 0 : ALLOCATE (shm_cost_vector(2*total_bins))
2417 0 : shm_cost_vector = -1_int_8
2418 0 : shm_displ(1) = 1
2419 0 : DO i = 2, n_threads
2420 0 : shm_displ(i) = shm_displ(i - 1) + shm_bins_per_rank(i - 1)
2421 : END DO
2422 0 : shm_displ(n_threads + 1) = nbins + 1
2423 : !$OMP END MASTER
2424 0 : !$OMP BARRIER
2425 0 : j = 0
2426 0 : SELECT CASE (eval_type)
2427 : CASE (hfx_do_eval_energy)
2428 0 : DO i = shm_displ(i_thread + 1), shm_displ(i_thread + 2) - 1
2429 0 : j = j + 1
2430 0 : shm_cost_vector(2*(i - 1) + 1) = x_data%distribution_energy(j)%cost
2431 0 : shm_cost_vector(2*i) = INT(x_data%distribution_energy(j)%time_first_scf*10000.0_dp, KIND=int_8)
2432 : END DO
2433 : CASE (hfx_do_eval_forces)
2434 0 : DO i = shm_displ(i_thread + 1), shm_displ(i_thread + 2) - 1
2435 0 : j = j + 1
2436 0 : shm_cost_vector(2*(i - 1) + 1) = x_data%distribution_forces(j)%cost
2437 0 : shm_cost_vector(2*i) = INT(x_data%distribution_forces(j)%time_forces*10000.0_dp, KIND=int_8)
2438 : END DO
2439 : END SELECT
2440 0 : !$OMP BARRIER
2441 0 : !$OMP MASTER
2442 : ! ** calculate offsets
2443 0 : ALLOCATE (rdispl(nranks))
2444 0 : bins_per_rank(:) = bins_per_rank(:)*2
2445 0 : rdispl(1) = 0
2446 0 : DO i = 2, nranks
2447 0 : rdispl(i) = rdispl(i - 1) + bins_per_rank(i - 1)
2448 : END DO
2449 :
2450 0 : ALLOCATE (buffer_in(2*nbins))
2451 0 : ALLOCATE (buffer_out(2*total_bins))
2452 :
2453 0 : DO i = 1, nbins
2454 0 : buffer_in(2*(i - 1) + 1) = shm_cost_vector(2*(i - 1) + 1)
2455 0 : buffer_in(2*i) = shm_cost_vector(2*i)
2456 : END DO
2457 :
2458 0 : CALL para_env%gatherv(buffer_in, buffer_out, bins_per_rank, rdispl)
2459 :
2460 0 : IF (iw > 0) THEN
2461 :
2462 0 : ALLOCATE (summary(2*nranks))
2463 0 : summary = 0_int_8
2464 :
2465 0 : WRITE (iw, '( /, 1X, 79("-") )')
2466 0 : WRITE (iw, '( " -", 77X, "-" )')
2467 0 : SELECT CASE (eval_type)
2468 : CASE (hfx_do_eval_energy)
2469 0 : WRITE (iw, '( " -", 20X, A, 19X, "-" )') ' HFX LOAD BALANCE INFORMATION - ENERGY '
2470 : CASE (hfx_do_eval_forces)
2471 0 : WRITE (iw, '( " -", 20X, A, 19X, "-" )') ' HFX LOAD BALANCE INFORMATION - FORCES '
2472 : END SELECT
2473 0 : WRITE (iw, '( " -", 77X, "-" )')
2474 0 : WRITE (iw, '( 1X, 79("-") )')
2475 :
2476 0 : WRITE (iw, FMT="(T3,A,T15,A,T35,A,T55,A)") "MPI RANK", "BIN #", "EST cost", "Processing time [s]"
2477 0 : WRITE (iw, '( 1X, 79("-"), / )')
2478 0 : k = 0
2479 0 : DO i = 1, nranks
2480 0 : DO j = 1, bins_per_rank(i)/2
2481 0 : k = k + 1
2482 : WRITE (iw, FMT="(T6,I5,T15,I5,T27,I16,T55,F19.8)") &
2483 0 : i - 1, j, buffer_out(2*(k - 1) + 1), REAL(buffer_out(2*k), dp)/10000.0_dp
2484 0 : summary(2*(i - 1) + 1) = summary(2*(i - 1) + 1) + buffer_out(2*(k - 1) + 1)
2485 0 : summary(2*i) = summary(2*i) + buffer_out(2*k)
2486 : END DO
2487 : END DO
2488 :
2489 : !** Summary
2490 : max_bin = 0_int_8
2491 : min_bin = HUGE(min_bin)
2492 : sum_bin = 0_int_8
2493 0 : DO i = 1, total_bins
2494 0 : sum_bin = sum_bin + buffer_out(2*i)
2495 0 : max_bin = MAX(max_bin, buffer_out(2*i))
2496 0 : min_bin = MIN(min_bin, buffer_out(2*i))
2497 : END DO
2498 0 : avg_bin = sum_bin/total_bins
2499 :
2500 0 : max_rank = 0_int_8
2501 0 : min_rank = HUGE(min_rank)
2502 0 : sum_rank = 0_int_8
2503 0 : DO i = 1, nranks
2504 0 : sum_rank = sum_rank + summary(2*i)
2505 0 : max_rank = MAX(max_rank, summary(2*i))
2506 0 : min_rank = MIN(min_rank, summary(2*i))
2507 : END DO
2508 0 : avg_rank = sum_rank/nranks
2509 :
2510 0 : WRITE (iw, FMT='(/,T3,A,/)') "SUMMARY:"
2511 0 : WRITE (iw, FMT="(T3,A,T35,F19.8)") "Max bin", REAL(max_bin, dp)/10000.0_dp
2512 0 : WRITE (iw, FMT="(T3,A,T35,F19.8)") "Min bin", REAL(min_bin, dp)/10000.0_dp
2513 0 : WRITE (iw, FMT="(T3,A,T35,F19.8)") "Sum bin", REAL(sum_bin, dp)/10000.0_dp
2514 0 : WRITE (iw, FMT="(T3,A,T35,F19.8,/)") "Avg bin", REAL(avg_bin, dp)/10000.0_dp
2515 0 : WRITE (iw, FMT="(T3,A,T35,F19.8)") "Max rank", REAL(max_rank, dp)/10000.0_dp
2516 0 : WRITE (iw, FMT="(T3,A,T35,F19.8)") "Min rank", REAL(min_rank, dp)/10000.0_dp
2517 0 : WRITE (iw, FMT="(T3,A,T35,F19.8)") "Sum rank", REAL(sum_rank, dp)/10000.0_dp
2518 0 : WRITE (iw, FMT="(T3,A,T35,F19.8,/)") "Avg rank", REAL(avg_rank, dp)/10000.0_dp
2519 :
2520 0 : ALLOCATE (buffer(nranks))
2521 0 : ALLOCATE (sort_idx(nranks))
2522 :
2523 0 : DO i = 1, nranks
2524 0 : buffer(i) = summary(2*i)
2525 : END DO
2526 :
2527 0 : CALL sort(buffer, nranks, sort_idx)
2528 :
2529 0 : WRITE (iw, FMT="(T3,A,T35,A,T55,A,/)") "MPI RANK", "EST cost", "Processing time [s]"
2530 0 : DO i = nranks, 1, -1
2531 0 : WRITE (iw, FMT="(T6,I5,T27,I16,T55,F19.8)") sort_idx(i) - 1, summary(2*(sort_idx(i) - 1) + 1), REAL(buffer(i), dp)/10000.0_dp
2532 : END DO
2533 :
2534 0 : DEALLOCATE (summary, buffer, sort_idx)
2535 :
2536 : END IF
2537 :
2538 0 : DEALLOCATE (buffer_in, buffer_out, rdispl)
2539 :
2540 0 : CALL para_env%sync()
2541 :
2542 0 : DEALLOCATE (shm_bins_per_rank, shm_displ, shm_cost_vector)
2543 : !$OMP END MASTER
2544 0 : !$OMP BARRIER
2545 :
2546 0 : END SUBROUTINE collect_load_balance_info
2547 :
2548 : #:include 'hfx_get_pmax_val.fypp'
2549 :
2550 : END MODULE hfx_load_balance_methods
|