(git:374b731)
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-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! **************************************************************************************************
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 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
370END 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....
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