(git:34ef472)
cube_utils.F
Go to the documentation of this file.
1 !--------------------------------------------------------------------------------------------------!
2 ! CP2K: A general program to perform molecular dynamics simulations !
3 ! Copyright 2000-2024 CP2K developers group <https://cp2k.org> !
4 ! !
5 ! SPDX-License-Identifier: GPL-2.0-or-later !
6 !--------------------------------------------------------------------------------------------------!
7 
8 ! **************************************************************************************************
9 !> \brief for a given dr()/dh(r) this will provide the bounds to be used if
10 !> one wants to go over a sphere-subregion of given radius
11 !> \note
12 !> the computation of the exact sphere radius is sensitive to roundoff (e.g.
13 !> compiler optimization level) and hence this small roundoff can result in
14 !> energy difference of about EPS_DEFAULT in QS energies (one gridpoint more or
15 !> less in the density mapping)
16 !> \author Joost VandeVondele
17 ! **************************************************************************************************
18 MODULE cube_utils
19 
20  USE kinds, ONLY: dp
21  USE realspace_grid_types, ONLY: realspace_grid_desc_type
22 #include "../base/base_uses.f90"
23 
24  IMPLICIT NONE
25  PRIVATE
26  CHARACTER(len=*), PARAMETER, PRIVATE :: moduleN = 'cube_utils'
27 
28  PUBLIC :: cube_info_type
29 
30  PUBLIC :: init_cube_info, destroy_cube_info, &
33 
34  TYPE :: cube_ptr
35  INTEGER, POINTER, DIMENSION(:) :: p => null()
36  END TYPE cube_ptr
37 
38  TYPE :: cube_info_type
39  INTEGER :: max_radius = 0.0_dp
40  REAL(KIND=dp) :: dr(3) = 0.0_dp, drmin = 0.0_dp
41  REAL(KIND=dp) :: dh(3, 3) = 0.0_dp
42  REAL(KIND=dp) :: dh_inv(3, 3) = 0.0_dp
43  LOGICAL :: orthorhombic = .true.
44  INTEGER, POINTER :: lb_cube(:, :) => null()
45  INTEGER, POINTER :: ub_cube(:, :) => null()
46  TYPE(cube_ptr), POINTER, DIMENSION(:) :: sphere_bounds => null()
47  INTEGER, POINTER :: sphere_bounds_count(:) => null()
48  REAL(KIND=dp) :: max_rad_ga = 0.0_dp
49  END TYPE cube_info_type
50 
51 CONTAINS
52 ! **************************************************************************************************
53 !> \brief unifies the computation of the cube center, so that differences in
54 !> implementation, and thus roundoff and numerics can not lead to
55 !> off-by-one errors (which can lead to out-of-bounds access with distributed grids).
56 !> in principle, something similar would be needed for the computation of the cube bounds
57 !>
58 !> \param cube_center ...
59 !> \param rs_desc ...
60 !> \param zeta ...
61 !> \param zetb ...
62 !> \param ra ...
63 !> \param rab ...
64 !> \par History
65 !> 11.2008 created [Joost VandeVondele]
66 ! **************************************************************************************************
67  SUBROUTINE compute_cube_center(cube_center, rs_desc, zeta, zetb, ra, rab)
68 
69  INTEGER, DIMENSION(3), INTENT(OUT) :: cube_center
70  TYPE(realspace_grid_desc_type), POINTER :: rs_desc
71  REAL(kind=dp), INTENT(IN) :: zeta, zetb, ra(3), rab(3)
72 
73  REAL(kind=dp) :: zetp
74  REAL(kind=dp), DIMENSION(3) :: rp
75 
76  zetp = zeta + zetb
77  rp(:) = ra(:) + zetb/zetp*rab(:)
78  cube_center(:) = floor(matmul(rs_desc%dh_inv, rp))
79 
80  END SUBROUTINE compute_cube_center
81 
82 ! **************************************************************************************************
83 !> \brief ...
84 !> \param info ...
85 !> \param radius ...
86 !> \param lb ...
87 !> \param ub ...
88 !> \param rp ...
89 ! **************************************************************************************************
90  SUBROUTINE return_cube_nonortho(info, radius, lb, ub, rp)
91 
92  TYPE(cube_info_type), INTENT(IN) :: info
93  REAL(kind=dp), INTENT(IN) :: radius
94  INTEGER, INTENT(OUT) :: lb(3), ub(3)
95  REAL(kind=dp), INTENT(IN) :: rp(3)
96 
97  INTEGER :: i, j, k
98  REAL(kind=dp) :: point(3), res(3)
99 
100  IF (radius > info%max_rad_ga) THEN
101  !
102  ! This is an important check. If the required radius for mapping the density is different
103  ! from the actual computed one, (significant) errors can occur.
104  ! This error can invariably be fixed by improving the computation of maxradius
105  ! in the call to init_cube_info
106  !
107  WRITE (*, *) info%max_rad_ga, radius
108  cpabort("Called with too large radius.")
109  END IF
110 
111  ! get the min/max indices of a cube that contains a sphere of the given radius around rp
112  ! if the cell is very non-orthogonal this implies that many useless points are included
113  ! this estimate can be improved (i.e. not box but sphere should be used)
114  lb = huge(lb)
115  ub = -huge(ub)
116  DO i = -1, 1
117  DO j = -1, 1
118  DO k = -1, 1
119  point(1) = rp(1) + i*radius
120  point(2) = rp(2) + j*radius
121  point(3) = rp(3) + k*radius
122  res = matmul(info%dh_inv, point)
123  lb = min(lb, floor(res))
124  ub = max(ub, ceiling(res))
125  END DO
126  END DO
127  END DO
128 
129  END SUBROUTINE return_cube_nonortho
130 
131 ! **************************************************************************************************
132 !> \brief ...
133 !> \param info ...
134 !> \param radius ...
135 !> \param lb_cube ...
136 !> \param ub_cube ...
137 !> \param sphere_bounds ...
138 ! **************************************************************************************************
139  SUBROUTINE return_cube(info, radius, lb_cube, ub_cube, sphere_bounds)
140 
141  TYPE(cube_info_type) :: info
142  REAL(kind=dp) :: radius
143  INTEGER :: lb_cube(3), ub_cube(3)
144  INTEGER, DIMENSION(:), POINTER :: sphere_bounds
145 
146  INTEGER :: imr
147 
148  IF (info%orthorhombic) THEN
149  imr = max(1, ceiling(radius/info%drmin))
150  IF (imr .GT. info%max_radius) THEN
151  !
152  ! This is an important check. If the required radius for mapping the density is different
153  ! from the actual computed one, (significant) errors can occur.
154  ! This error can invariably be fixed by improving the computation of maxradius
155  ! in the call to init_cube_info
156  !
157  cpabort("Called with too large radius.")
158  END IF
159  lb_cube(:) = info%lb_cube(:, imr)
160  ub_cube(:) = info%ub_cube(:, imr)
161  sphere_bounds => info%sphere_bounds(imr)%p
162  ELSE
163  ! nothing yet, we should check the radius
164  END IF
165 
166  END SUBROUTINE return_cube
167 
168  ! this is the integer max radius of the cube
169 ! **************************************************************************************************
170 !> \brief ...
171 !> \param info ...
172 !> \return ...
173 ! **************************************************************************************************
174  INTEGER FUNCTION return_cube_max_iradius(info)
175  TYPE(cube_info_type) :: info
176 
177  return_cube_max_iradius = info%max_radius
178  END FUNCTION return_cube_max_iradius
179 
180 ! **************************************************************************************************
181 !> \brief ...
182 !> \param info ...
183 ! **************************************************************************************************
184  SUBROUTINE destroy_cube_info(info)
185  TYPE(cube_info_type) :: info
186 
187  INTEGER :: i
188 
189  IF (info%orthorhombic) THEN
190  DEALLOCATE (info%lb_cube)
191  DEALLOCATE (info%ub_cube)
192  DEALLOCATE (info%sphere_bounds_count)
193  DO i = 1, info%max_radius
194  DEALLOCATE (info%sphere_bounds(i)%p)
195  END DO
196  DEALLOCATE (info%sphere_bounds)
197  ELSE
198  ! no info to be deallocated
199  END IF
200  END SUBROUTINE
201 
202 ! **************************************************************************************************
203 !> \brief ...
204 !> \param info ...
205 !> \param dr ...
206 !> \param dh ...
207 !> \param dh_inv ...
208 !> \param ortho ...
209 !> \param max_radius ...
210 ! **************************************************************************************************
211  SUBROUTINE init_cube_info(info, dr, dh, dh_inv, ortho, max_radius)
212  TYPE(cube_info_type), INTENT(OUT) :: info
213  REAL(kind=dp), INTENT(IN) :: dr(3), dh(3, 3), dh_inv(3, 3)
214  LOGICAL, INTENT(IN) :: ortho
215  REAL(kind=dp), INTENT(IN) :: max_radius
216 
217  CHARACTER(LEN=*), PARAMETER :: routinen = 'init_cube_info'
218 
219  INTEGER :: check_1, check_2, handle, i, igmin, imr, &
220  jg, jg2, jgmin, k, kg, kg2, kgmin, &
221  lb(3), ub(3)
222  REAL(kind=dp) :: drmin, dxi, dy2, dyi, dz2, dzi, radius, &
223  radius2, rp(3)
224 
225  CALL timeset(routinen, handle)
226  info%dr = dr
227  info%dh = dh
228  info%dh_inv = dh_inv
229  info%orthorhombic = ortho
230  info%max_rad_ga = max_radius
231  drmin = minval(dr)
232  info%drmin = drmin
233 
234  NULLIFY (info%lb_cube, info%ub_cube, &
235  info%sphere_bounds_count, info%sphere_bounds)
236 
237  IF (.NOT. info%orthorhombic) THEN
238 
239  rp = 0.0_dp
240  !
241  ! could still be wrong (maybe needs an additional +1 to account for off-gridpoint rp's)
242  !
243  CALL return_cube_nonortho(info, max_radius, lb, ub, rp)
244  info%max_radius = max(maxval(abs(lb)), maxval(abs(ub)))
245 
246  ELSE
247 
248  ! this info is specialized to orthogonal grids
249  imr = ceiling((max_radius)/drmin)
250  info%max_radius = imr
251  dzi = 1.0_dp/dr(3)
252  dyi = 1.0_dp/dr(2)
253  dxi = 1.0_dp/dr(1)
254  dz2 = (dr(3))**2
255  dy2 = (dr(2))**2
256 
257  ALLOCATE (info%lb_cube(3, imr), info%ub_cube(3, imr), &
258  info%sphere_bounds_count(imr), info%sphere_bounds(imr))
259  check_1 = 0
260  check_2 = 0
261 ! count and allocate
262 
263  DO i = 1, imr
264  k = 1
265  radius = i*drmin
266  radius2 = radius**2
267  kgmin = do_and_hide_it_1(dzi, i, drmin, 0.0_dp, 0.0_dp, 0, 0)
268  k = k + 1
269  DO kg = kgmin, 0
270  kg2 = kg*kg
271  jgmin = do_and_hide_it_1(dyi, i, drmin, dz2, 0.0_dp, kg2, 0)
272  k = k + 1
273  DO jg = jgmin, 0
274  jg2 = jg*jg
275  igmin = do_and_hide_it_1(dxi, i, drmin, dz2, dy2, kg2, jg2)
276  check_1 = modulo((kgmin*97 + jgmin*37 + igmin*113)*check_1 + 1277, 9343)
277  k = k + 1
278  END DO
279  END DO
280  info%sphere_bounds_count(i) = k - 1
281  ALLOCATE (info%sphere_bounds(i)%p(info%sphere_bounds_count(i)))
282  END DO
283 
284 ! init sphere_bounds array
285  ! notice : as many points in lb_cube..0 as 1..ub_cube
286  DO i = 1, imr
287  k = 1
288  radius = i*drmin
289  info%lb_cube(:, i) = -1
290  radius2 = radius**2
291  kgmin = do_and_hide_it_1(dzi, i, drmin, 0.0_dp, 0.0_dp, 0, 0)
292  info%lb_cube(3, i) = min(kgmin, info%lb_cube(3, i))
293  info%sphere_bounds(i)%p(k) = kgmin
294  k = k + 1
295  DO kg = kgmin, 0
296  kg2 = kg*kg
297  jgmin = do_and_hide_it_1(dyi, i, drmin, dz2, 0.0_dp, kg2, 0)
298  info%lb_cube(2, i) = min(jgmin, info%lb_cube(2, i))
299  info%sphere_bounds(i)%p(k) = jgmin
300  k = k + 1
301  DO jg = jgmin, 0
302  jg2 = jg*jg
303  igmin = do_and_hide_it_1(dxi, i, drmin, dz2, dy2, kg2, jg2)
304  check_2 = modulo((kgmin*97 + jgmin*37 + igmin*113)*check_2 + 1277, 9343)
305  info%lb_cube(1, i) = min(igmin, info%lb_cube(1, i))
306  info%sphere_bounds(i)%p(k) = igmin
307  k = k + 1
308  END DO
309  END DO
310  info%ub_cube(:, i) = 1 - info%lb_cube(:, i)
311  END DO
312  IF (check_1 .NE. check_2) THEN
313  cpabort("Irreproducible fp math caused memory corruption")
314  END IF
315 
316  END IF
317 
318  CALL timestop(handle)
319 
320  END SUBROUTINE
321 
322  ! try to hide things from the optimizer, so that we get the same numbers,
323  ! always (this solves the optimisation problems with the intel and nag compiler
324  ! in which the counting loops and execution loops above are executed a different
325  ! number of times, even at -O1
326 ! **************************************************************************************************
327 !> \brief ...
328 !> \param prefactor ...
329 !> \param i ...
330 !> \param drmin ...
331 !> \param dz2 ...
332 !> \param dy2 ...
333 !> \param kg2 ...
334 !> \param jg2 ...
335 !> \return ...
336 ! **************************************************************************************************
337  FUNCTION do_and_hide_it_1(prefactor, i, drmin, dz2, dy2, kg2, jg2) RESULT(res)
338  REAL(kind=dp) :: prefactor
339  INTEGER :: i
340  REAL(kind=dp) :: drmin, dz2, dy2
341  INTEGER :: kg2, jg2, res
342 
343  REAL(kind=dp), DIMENSION(:), POINTER :: buf
344 
345  ALLOCATE (buf(4))
346  buf(1) = prefactor
347  buf(2) = drmin
348  buf(3) = dz2
349  buf(4) = dy2
350  res = do_and_hide_it_2(buf, i, jg2, kg2)
351  DEALLOCATE (buf)
352  END FUNCTION do_and_hide_it_1
353 
354 ! **************************************************************************************************
355 !> \brief ...
356 !> \param buf ...
357 !> \param i ...
358 !> \param jg2 ...
359 !> \param kg2 ...
360 !> \return ...
361 ! **************************************************************************************************
362  FUNCTION do_and_hide_it_2(buf, i, jg2, kg2) RESULT(res)
363  REAL(kind=dp), DIMENSION(:), POINTER :: buf
364  INTEGER :: i, jg2, kg2, res
365 
366  buf(2) = (i*buf(2))**2
367  res = ceiling(-0.1e-7_dp - buf(1)*sqrt(max(buf(2) - kg2*buf(3) - jg2*buf(4), 0.0_dp)))
368  END FUNCTION do_and_hide_it_2
369 
370 END MODULE cube_utils
371 
static GRID_HOST_DEVICE int modulo(int a, int m)
Equivalent of Fortran's MODULO, which always return a positive number. https://gcc....
Definition: grid_common.h:117
for a given dr()/dh(r) this will provide the bounds to be used if one wants to go over a sphere-subre...
Definition: cube_utils.F:18
subroutine, public compute_cube_center(cube_center, rs_desc, zeta, zetb, ra, rab)
unifies the computation of the cube center, so that differences in implementation,...
Definition: cube_utils.F:68
subroutine, public return_cube(info, radius, lb_cube, ub_cube, sphere_bounds)
...
Definition: cube_utils.F:140
integer function, public return_cube_max_iradius(info)
...
Definition: cube_utils.F:175
subroutine, public destroy_cube_info(info)
...
Definition: cube_utils.F:185
subroutine, public init_cube_info(info, dr, dh, dh_inv, ortho, max_radius)
...
Definition: cube_utils.F:212
subroutine, public return_cube_nonortho(info, radius, lb, ub, rp)
...
Definition: cube_utils.F:91
Defines the basic variable types.
Definition: kinds.F:23
integer, parameter, public dp
Definition: kinds.F:34