Line data Source code
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 1612156 : 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 1612156 : 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 1612156 : const int kgmin = ceil(-1e-8 - disr_radius * dh_inv[2][2]); 31 1612156 : if (bounds != NULL) { 32 806078 : bounds[ibound] = kgmin; 33 : } 34 : ibound++; 35 15886308 : for (int kg = kgmin; kg <= 0; kg++) { 36 14274152 : const int kd = (2 * kg - 1) / 2; // distance from center in grid points 37 14274152 : const double kr = kd * dh[2][2]; // distance from center in a.u. 38 14274152 : const double kremain = disr_radius * disr_radius - kr * kr; 39 14274152 : const int jgmin = ceil(-1e-8 - sqrt(fmax(0.0, kremain)) * dh_inv[1][1]); 40 14274152 : if (bounds != NULL) { 41 7137076 : bounds[ibound] = jgmin; 42 : } 43 14274152 : ibound++; 44 149369774 : for (int jg = jgmin; jg <= 0; jg++) { 45 135095622 : const int jd = (2 * jg - 1) / 2; // distance from center in grid points 46 135095622 : const double jr = jd * dh[1][1]; // distance from center in a.u. 47 135095622 : const double jremain = kremain - jr * jr; 48 135095622 : const int igmin = ceil(-1e-8 - sqrt(fmax(0.0, jremain)) * dh_inv[0][0]); 49 135095622 : if (bounds != NULL) { 50 67547811 : bounds[ibound] = igmin; 51 : } 52 135095622 : ibound++; 53 : } 54 : } 55 1612156 : 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 64224 : 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 64224 : if (entry->max_imr > 0) { 67 50369 : free(entry->offsets); 68 50369 : free(entry->storage); 69 : } 70 64224 : entry->max_imr = max_imr; 71 : 72 : // Compute required storage size. 73 64224 : entry->offsets = malloc(max_imr * sizeof(int)); 74 64224 : assert(entry->offsets != NULL); 75 : int nbounds_total = 0; 76 870302 : for (int imr = 1; imr <= max_imr; imr++) { 77 806078 : const double radius = imr * drmin; 78 806078 : const int nbounds = single_sphere_bounds(radius, dh, dh_inv, NULL); 79 806078 : entry->offsets[imr - 1] = nbounds_total; 80 806078 : nbounds_total += nbounds; 81 : } 82 : 83 : // Allocate and fill storage. 84 64224 : entry->storage = malloc(nbounds_total * sizeof(int)); 85 64224 : assert(entry->storage != NULL); 86 870302 : for (int imr = 1; imr <= max_imr; imr++) { 87 806078 : const double radius = imr * drmin; 88 806078 : const int offset = entry->offsets[imr - 1]; 89 806078 : single_sphere_bounds(radius, dh, dh_inv, &entry->storage[offset]); 90 : } 91 64224 : } 92 : 93 : /******************************************************************************* 94 : * \brief Lookup the sphere bound from cache and compute them as needed. 95 : * See grid_sphere_cache.h for details. 96 : * \author Ole Schuett 97 : ******************************************************************************/ 98 122177984 : void grid_sphere_cache_lookup(const double radius, const double dh[3][3], 99 : const double dh_inv[3][3], int **sphere_bounds, 100 : double *discr_radius) { 101 : 102 : // Prepare the cache. 103 122177984 : grid_sphere_cache *cache = grid_library_get_sphere_cache(); 104 : 105 : // Find or create cache entry for given grid. 106 122177984 : const double dr0 = dh[0][0], dr1 = dh[1][1], dr2 = dh[2][2]; 107 122177984 : grid_sphere_cache_entry *entry = NULL; 108 122177984 : bool found = false; 109 : 110 : // Fast path: check prev match. 111 122177984 : if (cache->prev_match < cache->size) { 112 122173419 : entry = &cache->entries[cache->prev_match]; 113 122173419 : if (entry->dr[0] == dr0 && entry->dr[1] == dr1 && entry->dr[2] == dr2) { 114 120588328 : found = true; 115 : } 116 : } 117 : 118 : // Full search. 119 122177984 : if (!found) { 120 4159262 : for (int i = 0; i < cache->size; i++) { 121 4145407 : entry = &cache->entries[i]; 122 4145407 : if (entry->dr[0] == dr0 && entry->dr[1] == dr1 && entry->dr[2] == dr2) { 123 1575801 : cache->prev_match = i; 124 1575801 : found = true; 125 1575801 : break; 126 : } 127 : } 128 : } 129 : 130 : // If no existing cache entry was found then create a new one. 131 122177984 : if (!found) { 132 13855 : cache->size++; 133 13855 : grid_sphere_cache_entry *old_entries = cache->entries; 134 13855 : const size_t entry_size = sizeof(grid_sphere_cache_entry); 135 13855 : cache->entries = malloc(cache->size * entry_size); 136 13855 : assert(cache->entries != NULL); 137 13855 : memcpy(cache->entries, old_entries, (cache->size - 1) * entry_size); 138 13855 : free(old_entries); 139 13855 : cache->prev_match = cache->size - 1; 140 13855 : entry = &cache->entries[cache->size - 1]; 141 : // Initialize new cache entry 142 13855 : entry->max_imr = 0; 143 13855 : entry->dr[0] = dr0; 144 13855 : entry->dr[1] = dr1; 145 13855 : entry->dr[2] = dr2; 146 13855 : entry->drmin = fmin(dr0, fmin(dr1, dr2)); 147 13855 : entry->drmin_inv = 1.0 / entry->drmin; 148 : } 149 : 150 : // Discretize the radius. 151 122177984 : const int imr = imax(1, (int)ceil(radius * entry->drmin_inv)); 152 122177984 : *discr_radius = entry->drmin * imr; 153 : 154 : // Rebuild cache entry if requested radius is too large. 155 122177984 : if (entry->max_imr < imr) { 156 64224 : rebuild_cache_entry(imr, entry->drmin, dh, dh_inv, entry); 157 : } 158 122177984 : const int offset = entry->offsets[imr - 1]; 159 122177984 : *sphere_bounds = &entry->storage[offset]; 160 122177984 : } 161 : 162 : /******************************************************************************* 163 : * \brief Free the memory of the sphere cache. 164 : * \author Ole Schuett 165 : ******************************************************************************/ 166 8550 : void grid_sphere_cache_free(grid_sphere_cache *cache) { 167 22405 : for (int i = 0; i < cache->size; i++) { 168 13855 : if (cache->entries[i].max_imr > 0) { 169 13855 : free(cache->entries[i].offsets); 170 13855 : free(cache->entries[i].storage); 171 : } 172 : } 173 8550 : free(cache->entries); 174 8550 : cache->size = 0; 175 8550 : } 176 : 177 : // EOF