(git:b279b6b)
grid_sphere_cache.c
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: BSD-3-Clause */
6 /*----------------------------------------------------------------------------*/
7 
8 #include "grid_sphere_cache.h"
9 #include "grid_common.h"
10 #include "grid_library.h"
11 #include <assert.h>
12 #include <math.h>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include <string.h>
16 
17 /*******************************************************************************
18  * \brief Compute the sphere bounds for a given single radius.
19  * \author Ole Schuett
20  ******************************************************************************/
21 static int single_sphere_bounds(const double disr_radius, const double dh[3][3],
22  const double dh_inv[3][3], int *bounds) {
23 
24  int ibound = 0;
25 
26  // The cube contains an even number of grid points in each direction and
27  // collocation is always performed on a pair of two opposing grid points.
28  // Hence, the points with index 0 and 1 are both assigned distance zero via
29  // the formular distance=(2*index-1)/2.
30  const int kgmin = ceil(-1e-8 - disr_radius * dh_inv[2][2]);
31  if (bounds != NULL) {
32  bounds[ibound] = kgmin;
33  }
34  ibound++;
35  for (int kg = kgmin; kg <= 0; kg++) {
36  const int kd = (2 * kg - 1) / 2; // distance from center in grid points
37  const double kr = kd * dh[2][2]; // distance from center in a.u.
38  const double kremain = disr_radius * disr_radius - kr * kr;
39  const int jgmin = ceil(-1e-8 - sqrt(fmax(0.0, kremain)) * dh_inv[1][1]);
40  if (bounds != NULL) {
41  bounds[ibound] = jgmin;
42  }
43  ibound++;
44  for (int jg = jgmin; jg <= 0; jg++) {
45  const int jd = (2 * jg - 1) / 2; // distance from center in grid points
46  const double jr = jd * dh[1][1]; // distance from center in a.u.
47  const double jremain = kremain - jr * jr;
48  const int igmin = ceil(-1e-8 - sqrt(fmax(0.0, jremain)) * dh_inv[0][0]);
49  if (bounds != NULL) {
50  bounds[ibound] = igmin;
51  }
52  ibound++;
53  }
54  }
55  return ibound; // Number of bounds - needed to allocate array.
56 }
57 
58 /*******************************************************************************
59  * \brief Rebuild a cache entry for a given cell and max radius.
60  * \author Ole Schuett
61  ******************************************************************************/
62 static void rebuild_cache_entry(const int max_imr, const double drmin,
63  const double dh[3][3],
64  const double dh_inv[3][3],
65  grid_sphere_cache_entry *entry) {
66  if (entry->max_imr > 0) {
67  free(entry->offsets);
68  free(entry->storage);
69  }
70  entry->max_imr = max_imr;
71 
72  // Compute required storage size.
73  entry->offsets = malloc(max_imr * sizeof(int));
74  int nbounds_total = 0;
75  for (int imr = 1; imr <= max_imr; imr++) {
76  const double radius = imr * drmin;
77  const int nbounds = single_sphere_bounds(radius, dh, dh_inv, NULL);
78  entry->offsets[imr - 1] = nbounds_total;
79  nbounds_total += nbounds;
80  }
81 
82  // Allocate and fill storage.
83  entry->storage = malloc(nbounds_total * sizeof(int));
84  for (int imr = 1; imr <= max_imr; imr++) {
85  const double radius = imr * drmin;
86  const int offset = entry->offsets[imr - 1];
87  single_sphere_bounds(radius, dh, dh_inv, &entry->storage[offset]);
88  }
89 }
90 
91 /*******************************************************************************
92  * \brief Lookup the sphere bound from cache and compute them as needed.
93  * See grid_sphere_cache.h for details.
94  * \author Ole Schuett
95  ******************************************************************************/
96 void grid_sphere_cache_lookup(const double radius, const double dh[3][3],
97  const double dh_inv[3][3], int **sphere_bounds,
98  double *discr_radius) {
99 
100  // Prepare the cache.
102 
103  // Find or create cache entry for given grid.
104  const double dr0 = dh[0][0], dr1 = dh[1][1], dr2 = dh[2][2];
106  bool found = false;
107 
108  // Fast path: check prev match.
109  if (cache->prev_match < cache->size) {
110  entry = &cache->entries[cache->prev_match];
111  if (entry->dr[0] == dr0 && entry->dr[1] == dr1 && entry->dr[2] == dr2) {
112  found = true;
113  }
114  }
115 
116  // Full search.
117  if (!found) {
118  for (int i = 0; i < cache->size; i++) {
119  entry = &cache->entries[i];
120  if (entry->dr[0] == dr0 && entry->dr[1] == dr1 && entry->dr[2] == dr2) {
121  cache->prev_match = i;
122  found = true;
123  break;
124  }
125  }
126  }
127 
128  // If no existing cache entry was found then create a new one.
129  if (!found) {
130  cache->size++;
131  grid_sphere_cache_entry *old_entries = cache->entries;
132  const size_t entry_size = sizeof(grid_sphere_cache_entry);
133  cache->entries = malloc(cache->size * entry_size);
134  memcpy(cache->entries, old_entries, (cache->size - 1) * entry_size);
135  free(old_entries);
136  cache->prev_match = cache->size - 1;
137  entry = &cache->entries[cache->size - 1];
138  // Initialize new cache entry
139  entry->max_imr = 0;
140  entry->dr[0] = dr0;
141  entry->dr[1] = dr1;
142  entry->dr[2] = dr2;
143  entry->drmin = fmin(dr0, fmin(dr1, dr2));
144  entry->drmin_inv = 1.0 / entry->drmin;
145  }
146 
147  // Discretize the radius.
148  const int imr = imax(1, (int)ceil(radius * entry->drmin_inv));
149  *discr_radius = entry->drmin * imr;
150 
151  // Rebuild cache entry if requested radius is too large.
152  if (entry->max_imr < imr) {
153  rebuild_cache_entry(imr, entry->drmin, dh, dh_inv, entry);
154  }
155  const int offset = entry->offsets[imr - 1];
156  *sphere_bounds = &entry->storage[offset];
157 }
158 
159 /*******************************************************************************
160  * \brief Free the memory of the sphere cache.
161  * \author Ole Schuett
162  ******************************************************************************/
164  for (int i = 0; i < cache->size; i++) {
165  if (cache->entries[i].max_imr > 0) {
166  free(cache->entries[i].offsets);
167  free(cache->entries[i].storage);
168  }
169  }
170  free(cache->entries);
171  cache->size = 0;
172 }
173 
174 // EOF
static int imax(int x, int y)
Returns the larger of two given integer (missing from the C standard)
static void const int const int i
grid_sphere_cache * grid_library_get_sphere_cache(void)
Returns a pointer to the thread local sphere cache.
Definition: grid_library.c:102
void grid_sphere_cache_lookup(const double radius, const double dh[3][3], const double dh_inv[3][3], int **sphere_bounds, double *discr_radius)
Lookup the sphere bound from cache and compute them as needed. See grid_sphere_cache....
static void rebuild_cache_entry(const int max_imr, const double drmin, const double dh[3][3], const double dh_inv[3][3], grid_sphere_cache_entry *entry)
Rebuild a cache entry for a given cell and max radius.
void grid_sphere_cache_free(grid_sphere_cache *cache)
Free the memory of the sphere cache.
static int single_sphere_bounds(const double disr_radius, const double dh[3][3], const double dh_inv[3][3], int *bounds)
Compute the sphere bounds for a given single radius.
Struct holding the sphere cache for one grid as specified by dr[3].
int * storage
double drmin
double dr[3]
int max_imr
double drmin_inv
int * offsets
Struct holding the entire sphere cache, ie. for all grids.
grid_sphere_cache_entry * entries