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