(git:08825b9)
Loading...
Searching...
No Matches
cube_utils.F
Go to the documentation of this file.
1!--------------------------------------------------------------------------------------------------!
2! CP2K: A general program to perform molecular dynamics simulations !
3! Copyright 2000-2026 CP2K developers group <https://cp2k.org> !
4! !
5! SPDX-License-Identifier: GPL-2.0-or-later !
6!--------------------------------------------------------------------------------------------------!
7
8! **************************************************************************************************
9!> \brief 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! **************************************************************************************************
19
20 USE kinds, ONLY: dp
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
33
34 TYPE :: cube_ptr
35 INTEGER, POINTER, DIMENSION(:) :: p => null()
36 END TYPE cube_ptr
37
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
51CONTAINS
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 CALL cp_abort(__location__, &
109 "The required radius is too large to match the maxradius "// &
110 "in cube info when calling return_cube_nonortho")
111 END IF
112
113 ! get the min/max indices of a cube that contains a sphere of the given radius around rp
114 ! if the cell is very non-orthogonal this implies that many useless points are included
115 ! this estimate can be improved (i.e. not box but sphere should be used)
116 lb = huge(lb)
117 ub = -huge(ub)
118 DO i = -1, 1
119 DO j = -1, 1
120 DO k = -1, 1
121 point(1) = rp(1) + i*radius
122 point(2) = rp(2) + j*radius
123 point(3) = rp(3) + k*radius
124 res = matmul(info%dh_inv, point)
125 lb = min(lb, floor(res))
126 ub = max(ub, ceiling(res))
127 END DO
128 END DO
129 END DO
130
131 END SUBROUTINE return_cube_nonortho
132
133! **************************************************************************************************
134!> \brief ...
135!> \param info ...
136!> \param radius ...
137!> \param lb_cube ...
138!> \param ub_cube ...
139!> \param sphere_bounds ...
140! **************************************************************************************************
141 SUBROUTINE return_cube(info, radius, lb_cube, ub_cube, sphere_bounds)
142
143 TYPE(cube_info_type) :: info
144 REAL(kind=dp) :: radius
145 INTEGER :: lb_cube(3), ub_cube(3)
146 INTEGER, DIMENSION(:), POINTER :: sphere_bounds
147
148 INTEGER :: imr
149
150 IF (info%orthorhombic) THEN
151 imr = max(1, ceiling(radius/info%drmin))
152 IF (imr > info%max_radius) THEN
153 !
154 ! This is an important check. If the required radius for mapping the density is different
155 ! from the actual computed one, (significant) errors can occur.
156 ! This error can invariably be fixed by improving the computation of maxradius
157 ! in the call to init_cube_info
158 !
159 CALL cp_abort(__location__, &
160 "The required radius is too large to match the maxradius "// &
161 "in cube info when calling return_cube")
162 END IF
163 lb_cube(:) = info%lb_cube(:, imr)
164 ub_cube(:) = info%ub_cube(:, imr)
165 sphere_bounds => info%sphere_bounds(imr)%p
166 ELSE
167 ! nothing yet, we should check the radius
168 END IF
169
170 END SUBROUTINE return_cube
171
172 ! this is the integer max radius of the cube
173! **************************************************************************************************
174!> \brief ...
175!> \param info ...
176!> \return ...
177! **************************************************************************************************
178 INTEGER FUNCTION return_cube_max_iradius(info)
179 TYPE(cube_info_type) :: info
180
181 return_cube_max_iradius = info%max_radius
182 END FUNCTION return_cube_max_iradius
183
184! **************************************************************************************************
185!> \brief ...
186!> \param info ...
187! **************************************************************************************************
188 SUBROUTINE destroy_cube_info(info)
189 TYPE(cube_info_type) :: info
190
191 INTEGER :: i
192
193 IF (info%orthorhombic) THEN
194 DEALLOCATE (info%lb_cube)
195 DEALLOCATE (info%ub_cube)
196 DEALLOCATE (info%sphere_bounds_count)
197 DO i = 1, info%max_radius
198 DEALLOCATE (info%sphere_bounds(i)%p)
199 END DO
200 DEALLOCATE (info%sphere_bounds)
201 ELSE
202 ! no info to be deallocated
203 END IF
204 END SUBROUTINE destroy_cube_info
205
206! **************************************************************************************************
207!> \brief ...
208!> \param info ...
209!> \param dr ...
210!> \param dh ...
211!> \param dh_inv ...
212!> \param ortho ...
213!> \param max_radius ...
214! **************************************************************************************************
215 SUBROUTINE init_cube_info(info, dr, dh, dh_inv, ortho, max_radius)
216 TYPE(cube_info_type), INTENT(OUT) :: info
217 REAL(kind=dp), INTENT(IN) :: dr(3), dh(3, 3), dh_inv(3, 3)
218 LOGICAL, INTENT(IN) :: ortho
219 REAL(kind=dp), INTENT(IN) :: max_radius
220
221 CHARACTER(LEN=*), PARAMETER :: routinen = 'init_cube_info'
222
223 INTEGER :: check_1, check_2, handle, i, igmin, imr, &
224 jg, jg2, jgmin, k, kg, kg2, kgmin, &
225 lb(3), ub(3)
226 REAL(kind=dp) :: drmin, dxi, dy2, dyi, dz2, dzi, radius, &
227 radius2, rp(3)
228
229 CALL timeset(routinen, handle)
230 info%dr = dr
231 info%dh = dh
232 info%dh_inv = dh_inv
233 info%orthorhombic = ortho
234 info%max_rad_ga = max_radius
235 drmin = minval(dr)
236 info%drmin = drmin
237
238 NULLIFY (info%lb_cube, info%ub_cube, &
239 info%sphere_bounds_count, info%sphere_bounds)
240
241 IF (.NOT. info%orthorhombic) THEN
242
243 rp = 0.0_dp
244 !
245 ! could still be wrong (maybe needs an additional +1 to account for off-gridpoint rp's)
246 !
247 CALL return_cube_nonortho(info, max_radius, lb, ub, rp)
248 info%max_radius = max(maxval(abs(lb)), maxval(abs(ub)))
249
250 ELSE
251
252 ! this info is specialized to orthogonal grids
253 imr = ceiling((max_radius)/drmin)
254 info%max_radius = imr
255 dzi = 1.0_dp/dr(3)
256 dyi = 1.0_dp/dr(2)
257 dxi = 1.0_dp/dr(1)
258 dz2 = (dr(3))**2
259 dy2 = (dr(2))**2
260
261 ALLOCATE (info%lb_cube(3, imr), info%ub_cube(3, imr), &
262 info%sphere_bounds_count(imr), info%sphere_bounds(imr))
263 check_1 = 0
264 check_2 = 0
265! count and allocate
266
267 DO i = 1, imr
268 k = 1
269 radius = i*drmin
270 radius2 = radius**2
271 kgmin = do_and_hide_it_1(dzi, i, drmin, 0.0_dp, 0.0_dp, 0, 0)
272 k = k + 1
273 DO kg = kgmin, 0
274 kg2 = kg*kg
275 jgmin = do_and_hide_it_1(dyi, i, drmin, dz2, 0.0_dp, kg2, 0)
276 k = k + 1
277 DO jg = jgmin, 0
278 jg2 = jg*jg
279 igmin = do_and_hide_it_1(dxi, i, drmin, dz2, dy2, kg2, jg2)
280 check_1 = modulo((kgmin*97 + jgmin*37 + igmin*113)*check_1 + 1277, 9343)
281 k = k + 1
282 END DO
283 END DO
284 info%sphere_bounds_count(i) = k - 1
285 ALLOCATE (info%sphere_bounds(i)%p(info%sphere_bounds_count(i)))
286 END DO
287
288! init sphere_bounds array
289 ! notice : as many points in lb_cube..0 as 1..ub_cube
290 DO i = 1, imr
291 k = 1
292 radius = i*drmin
293 info%lb_cube(:, i) = -1
294 radius2 = radius**2
295 kgmin = do_and_hide_it_1(dzi, i, drmin, 0.0_dp, 0.0_dp, 0, 0)
296 info%lb_cube(3, i) = min(kgmin, info%lb_cube(3, i))
297 info%sphere_bounds(i)%p(k) = kgmin
298 k = k + 1
299 DO kg = kgmin, 0
300 kg2 = kg*kg
301 jgmin = do_and_hide_it_1(dyi, i, drmin, dz2, 0.0_dp, kg2, 0)
302 info%lb_cube(2, i) = min(jgmin, info%lb_cube(2, i))
303 info%sphere_bounds(i)%p(k) = jgmin
304 k = k + 1
305 DO jg = jgmin, 0
306 jg2 = jg*jg
307 igmin = do_and_hide_it_1(dxi, i, drmin, dz2, dy2, kg2, jg2)
308 check_2 = modulo((kgmin*97 + jgmin*37 + igmin*113)*check_2 + 1277, 9343)
309 info%lb_cube(1, i) = min(igmin, info%lb_cube(1, i))
310 info%sphere_bounds(i)%p(k) = igmin
311 k = k + 1
312 END DO
313 END DO
314 info%ub_cube(:, i) = 1 - info%lb_cube(:, i)
315 END DO
316 IF (check_1 /= check_2) THEN
317 cpabort("Irreproducible fp math caused memory corruption")
318 END IF
319
320 END IF
321
322 CALL timestop(handle)
323
324 END SUBROUTINE init_cube_info
325
326 ! try to hide things from the optimizer, so that we get the same numbers,
327 ! always (this solves the optimisation problems with the intel and nag compiler
328 ! in which the counting loops and execution loops above are executed a different
329 ! number of times, even at -O1
330! **************************************************************************************************
331!> \brief ...
332!> \param prefactor ...
333!> \param i ...
334!> \param drmin ...
335!> \param dz2 ...
336!> \param dy2 ...
337!> \param kg2 ...
338!> \param jg2 ...
339!> \return ...
340! **************************************************************************************************
341 FUNCTION do_and_hide_it_1(prefactor, i, drmin, dz2, dy2, kg2, jg2) RESULT(res)
342 REAL(kind=dp) :: prefactor
343 INTEGER :: i
344 REAL(kind=dp) :: drmin, dz2, dy2
345 INTEGER :: kg2, jg2, res
346
347 REAL(kind=dp), DIMENSION(:), POINTER :: buf
348
349 ALLOCATE (buf(4))
350 buf(1) = prefactor
351 buf(2) = drmin
352 buf(3) = dz2
353 buf(4) = dy2
354 res = do_and_hide_it_2(buf, i, jg2, kg2)
355 DEALLOCATE (buf)
356 END FUNCTION do_and_hide_it_1
357
358! **************************************************************************************************
359!> \brief ...
360!> \param buf ...
361!> \param i ...
362!> \param jg2 ...
363!> \param kg2 ...
364!> \return ...
365! **************************************************************************************************
366 FUNCTION do_and_hide_it_2(buf, i, jg2, kg2) RESULT(res)
367 REAL(kind=dp), DIMENSION(:), POINTER :: buf
368 INTEGER :: i, jg2, kg2, res
369
370 buf(2) = (i*buf(2))**2
371 res = ceiling(-0.1e-7_dp - buf(1)*sqrt(max(buf(2) - kg2*buf(3) - jg2*buf(4), 0.0_dp)))
372 END FUNCTION do_and_hide_it_2
373
374END MODULE cube_utils
375
static GRID_HOST_DEVICE int modulo(int a, int m)
Equivalent of Fortran's MODULO, which always return a positive number. https://gcc....
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:142
integer function, public return_cube_max_iradius(info)
...
Definition cube_utils.F:179
subroutine, public destroy_cube_info(info)
...
Definition cube_utils.F:189
subroutine, public init_cube_info(info, dr, dh, dh_inv, ortho, max_radius)
...
Definition cube_utils.F:216
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