1 /* Ergo, version 3.8, a program for linear scaling electronic structure
2 * calculations.
3 * Copyright (C) 2019 Elias Rudberg, Emanuel H. Rubensson, Pawel Salek,
4 * and Anastasia Kruchinina.
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 * Primary academic reference:
20 * Ergo: An open-source program for linear-scaling electronic structure
21 * calculations,
22 * Elias Rudberg, Emanuel H. Rubensson, Pawel Salek, and Anastasia
23 * Kruchinina,
24 * SoftwareX 7, 107 (2018),
25 * <http://dx.doi.org/10.1016/j.softx.2018.03.005>
26 *
27 * For further information about Ergo, see <http://www.ergoscf.org>.
28 */
29
30 /** @file box_system.cc
31
32 @brief BoxSystem class representing a hierarchical data structure
33 of boxes in 3D space (an oct-tree).
34
35 The idea is that you have a list of items at different points in space,
36 and you want a hierarchical system of boxes containing those items.
37
38 You give a list of items, and the function create_box_system will
39 create a system of boxes for you.
40
41 @author: Elias Rudberg <em>responsible</em>
42 */
43
44 #include <cmath>
45 #include <stdlib.h>
46 #include <vector>
47 #include "box_system.h"
48 #include "output.h"
49 #include "memorymanag.h"
50 #include "utilities.h"
51 #include "mat_gblas.h"
52
53
BoxSystem()54 BoxSystem::BoxSystem()
55 {
56 boxList = NULL;
57 }
58
~BoxSystem()59 BoxSystem::~BoxSystem()
60 {
61 if(boxList)
62 delete [] boxList;
63 }
64
65
66 /** Creates the box system.
67
68 @param itemList list of items to create the box structure for.
69 @param noOfItems their number.
70 @param toplevelBoxSize
71 */
72
73 int
create_box_system(box_item_struct * itemList,int noOfItems,ergo_real toplevelBoxSize)74 BoxSystem::create_box_system(box_item_struct* itemList,
75 int noOfItems,
76 ergo_real toplevelBoxSize)
77 {
78 // Allocate resultBoxList with just one item to begin with,
79 // It will be expanded later as needed.
80 int maxNoOfBoxes = 1;
81 boxList = new box_struct_basic[maxNoOfBoxes];
82
83 // create "mother box" containing all distrs.
84 int currBoxIndex = 0;
85 if(currBoxIndex >= maxNoOfBoxes)
86 {
87 do_output(LOG_CAT_ERROR, LOG_AREA_INTEGRALS, "error in create_box_system: (currBoxIndex >= maxNoOfBoxes)");
88 return -1;
89 }
90 box_struct_basic* motherBox = &boxList[currBoxIndex];
91 currBoxIndex++;
92
93 // first get dimensions of box
94 const ergo_real HUGE_NUMBER = 888888888;
95 ergo_real xminList[3];
96 ergo_real xmaxList[3];
97 ergo_real xdiffList[3];
98 for(int kk = 0; kk < 3; kk++)
99 {
100 xminList[kk] = HUGE_NUMBER;
101 xmaxList[kk] = -HUGE_NUMBER;
102 }
103 for(int i = 0; i < noOfItems; i++)
104 {
105 for(int kk = 0; kk < 3; kk++)
106 {
107 ergo_real x = itemList[i].centerCoords[kk];
108 if(x < xminList[kk])
109 xminList[kk] = x;
110 if(x > xmaxList[kk])
111 xmaxList[kk] = x;
112 }
113 } // END FOR i
114 int bestCoordIndex = 0;
115 for(int kk = 0; kk < 3; kk++)
116 {
117 xdiffList[kk] = xmaxList[kk] - xminList[kk];
118 if(xdiffList[kk] > xdiffList[bestCoordIndex])
119 bestCoordIndex = kk;
120 }
121 for(int kk = 0; kk < 3; kk++) {
122 if(xdiffList[kk] < 0) {
123 do_output(LOG_CAT_ERROR, LOG_AREA_INTEGRALS, "error in create_box_system: (xdiffList[kk] < 0).");
124 do_output(LOG_CAT_ERROR, LOG_AREA_INTEGRALS, "error in create_box_system: kk = %d, xdiffList[kk] = %9.4g < 0.\n", kk, xdiffList[kk]);
125 return -1;
126 }
127 }
128
129 ergo_real largestDiff = xdiffList[bestCoordIndex];
130
131 // compute number of levels and size of mother box
132 int numberOfLevels = 1;
133 ergo_real width = toplevelBoxSize;
134 while(width < largestDiff)
135 {
136 width *= 2;
137 numberOfLevels++;
138 }
139
140 if(numberOfLevels >= MAX_NO_OF_BOX_LEVELS)
141 {
142 do_output(LOG_CAT_ERROR, LOG_AREA_INTEGRALS, "error in create_box_system: (numberOfLevels >= MAX_NO_OF_BOX_LEVELS)");
143 do_output(LOG_CAT_ERROR, LOG_AREA_INTEGRALS, "error in create_box_system: numberOfLevels = %d, MAX_NO_OF_BOX_LEVELS = %d.", numberOfLevels, (int)MAX_NO_OF_BOX_LEVELS);
144 do_output(LOG_CAT_ERROR, LOG_AREA_INTEGRALS, "error in create_box_system: toplevelBoxSize = %7.3g, largestDiff = %7.3g.", (double)toplevelBoxSize, (double)largestDiff);
145 return -1;
146 }
147
148 motherBox->width = width;
149 // Set center of mother box.
150 for(int kk = 0; kk < 3; kk++)
151 motherBox->centerCoords[kk] = xminList[kk] + motherBox->width / 2;
152 motherBox->firstItemIndex = 0;
153 motherBox->noOfItems = noOfItems;
154
155 // OK, mother box done.
156 // Now create boxes on other levels, one level at a time.
157
158 this->levelList[0].noOfBoxes = 1;
159 this->levelList[0].startIndexInBoxList = 0;
160
161 int* itemIndexBucketList[2][2][2];
162 for(int ix = 0; ix < 2; ix++)
163 for(int iy = 0; iy < 2; iy++)
164 for(int iz = 0; iz < 2; iz++)
165 itemIndexBucketList[ix][iy][iz] = new int[noOfItems];
166 int itemCounterList[2][2][2];
167 // Allocate temporary itemList for use when reordering items.
168 std::vector<box_item_struct> itemListTemp(noOfItems);
169
170 for(int levelNumber = 1; levelNumber < numberOfLevels; levelNumber++)
171 {
172 levelList[levelNumber].startIndexInBoxList = currBoxIndex;
173 int currLevelNoOfBoxes_1 = 0;
174 // go through boxes of previous level, and create new boxes at this level if needed.
175 // We go through boxes of previous level twice, the first time to check how many
176 // new boxes must be allocated.
177 int startIndex = levelList[levelNumber-1].startIndexInBoxList;
178 for(int i = startIndex; i < startIndex + levelList[levelNumber-1].noOfBoxes; i++)
179 {
180 // now resultBoxList[i] is the box whose children we are creating.
181 boxList[i].firstChildBoxIndex = currBoxIndex;
182 int noOfChildren = 0;
183 if(boxList[i].noOfItems == 0)
184 {
185 do_output(LOG_CAT_ERROR, LOG_AREA_INTEGRALS, "ERROR: (boxList[i].noOfItems == 0)");
186 return -1;
187 }
188 // create 2*2*2 = 8 new boxes
189 for(int ix = 0; ix < 2; ix++)
190 for(int iy = 0; iy < 2; iy++)
191 for(int iz = 0; iz < 2; iz++)
192 {
193 itemCounterList[ix][iy][iz] = 0;
194 } // END FOR ix iy iz
195 // now go through the points of the parent box to decide which of the 8 children each point belongs to.
196 for(int j = boxList[i].firstItemIndex; j < boxList[i].firstItemIndex + boxList[i].noOfItems; j++)
197 {
198 int ix = 0;
199 int iy = 0;
200 int iz = 0;
201 if(itemList[j].centerCoords[0] > boxList[i].centerCoords[0])
202 ix = 1;
203 if(itemList[j].centerCoords[1] > boxList[i].centerCoords[1])
204 iy = 1;
205 if(itemList[j].centerCoords[2] > boxList[i].centerCoords[2])
206 iz = 1;
207 itemCounterList[ix][iy][iz]++;
208 } // END FOR j
209 // OK, all items counted, counters updated to indicate how many items
210 // belong to each of the 8 candidate child boxes.
211 // Now count new boxes.
212 for(int ix = 0; ix < 2; ix++)
213 for(int iy = 0; iy < 2; iy++)
214 for(int iz = 0; iz < 2; iz++)
215 {
216 if(itemCounterList[ix][iy][iz] > 0)
217 {
218 currLevelNoOfBoxes_1++;
219 noOfChildren++;
220 } // END IF (itemCounterList[ix][iy][iz] > 0)
221 } // END FOR ix iy iz
222 } // END FOR i
223
224 // OK, now we know how many new boxes are needed.
225 int maxNoOfBoxesNew = maxNoOfBoxes + currLevelNoOfBoxes_1;
226 box_struct_basic* boxListNew = new box_struct_basic[maxNoOfBoxesNew];
227 // copy previous contents of resultBoxList
228 memcpy(boxListNew, boxList, maxNoOfBoxes*sizeof(box_struct_basic));
229 // free old list, and set pointer to new list instead.
230 delete [] boxList;
231 boxList = boxListNew;
232 maxNoOfBoxes = maxNoOfBoxesNew;
233
234 // Now go through level again, this time creating new boxes and reordering items.
235 int currLevelNoOfBoxes_2 = 0;
236 for(int i = startIndex; i < startIndex + levelList[levelNumber-1].noOfBoxes; i++)
237 {
238 // now resultBoxList[i] is the box whose children we are creating.
239 boxList[i].firstChildBoxIndex = currBoxIndex;
240 int noOfChildren = 0;
241 if(boxList[i].noOfItems == 0)
242 {
243 do_output(LOG_CAT_ERROR, LOG_AREA_INTEGRALS, "ERROR: (resultBoxList[i].noOfItems == 0)");
244 return -1;
245 }
246 // create 2*2*2 = 8 new boxes
247 box_struct_basic tempBoxList[2][2][2];
248 for(int ix = 0; ix < 2; ix++)
249 for(int iy = 0; iy < 2; iy++)
250 for(int iz = 0; iz < 2; iz++)
251 {
252 ergo_real newWidth = boxList[i].width / 2;
253 ergo_real x = boxList[i].centerCoords[0] + (ix*2-1)*0.5*newWidth;
254 ergo_real y = boxList[i].centerCoords[1] + (iy*2-1)*0.5*newWidth;
255 ergo_real z = boxList[i].centerCoords[2] + (iz*2-1)*0.5*newWidth;
256 tempBoxList[ix][iy][iz].centerCoords[0] = x;
257 tempBoxList[ix][iy][iz].centerCoords[1] = y;
258 tempBoxList[ix][iy][iz].centerCoords[2] = z;
259 tempBoxList[ix][iy][iz].width = newWidth;
260 itemCounterList[ix][iy][iz] = 0;
261 } // END FOR ix iy iz
262 // now go through the points of the parent box to decide which of the 8 children each point belongs to.
263 for(int j = boxList[i].firstItemIndex; j < boxList[i].firstItemIndex + boxList[i].noOfItems; j++)
264 {
265 int ix = 0;
266 int iy = 0;
267 int iz = 0;
268 if(itemList[j].centerCoords[0] > boxList[i].centerCoords[0])
269 ix = 1;
270 if(itemList[j].centerCoords[1] > boxList[i].centerCoords[1])
271 iy = 1;
272 if(itemList[j].centerCoords[2] > boxList[i].centerCoords[2])
273 iz = 1;
274 itemIndexBucketList[ix][iy][iz][itemCounterList[ix][iy][iz]] = j;
275 itemCounterList[ix][iy][iz]++;
276 } // END FOR j
277 // OK, all items copied to itemBucketList.
278 // Now add new boxes, and order the items accordingly.
279 int currStartIndex = boxList[i].firstItemIndex;
280 for(int ix = 0; ix < 2; ix++)
281 for(int iy = 0; iy < 2; iy++)
282 for(int iz = 0; iz < 2; iz++)
283 {
284 if(itemCounterList[ix][iy][iz] > 0)
285 {
286 if(currBoxIndex >= maxNoOfBoxes)
287 {
288 do_output(LOG_CAT_ERROR, LOG_AREA_INTEGRALS, "error in create_box_system: (currBoxIndex >= maxNoOfBoxes)");
289 return -1;
290 }
291 boxList[currBoxIndex] = tempBoxList[ix][iy][iz];
292 boxList[currBoxIndex].firstItemIndex = currStartIndex;
293 boxList[currBoxIndex].noOfItems = itemCounterList[ix][iy][iz];
294 for(int k = 0; k < itemCounterList[ix][iy][iz]; k++)
295 itemListTemp[currStartIndex+k] = itemList[itemIndexBucketList[ix][iy][iz][k]];
296 currStartIndex += itemCounterList[ix][iy][iz];
297 currBoxIndex++;
298 currLevelNoOfBoxes_2++;
299 noOfChildren++;
300 } // END IF (itemCounterList[ix][iy][iz] > 0)
301 } // END FOR ix iy iz
302 boxList[i].noOfChildBoxes = noOfChildren;
303 } // END FOR i
304 if(currLevelNoOfBoxes_2 != currLevelNoOfBoxes_1)
305 {
306 do_output(LOG_CAT_ERROR, LOG_AREA_INTEGRALS, "error in create_box_system: (currLevelNoOfBoxes_2 != currLevelNoOfBoxes_1)");
307 return -1;
308 }
309 levelList[levelNumber].noOfBoxes = currLevelNoOfBoxes_2;
310 memcpy(itemList, &itemListTemp[0], noOfItems*sizeof(box_item_struct));
311 } // END FOR levelNumber
312
313 // OK, boxes created.
314
315 totNoOfBoxes = currBoxIndex;
316 noOfLevels = numberOfLevels;
317
318 for(int ix = 0; ix < 2; ix++)
319 for(int iy = 0; iy < 2; iy++)
320 for(int iz = 0; iz < 2; iz++)
321 delete [] itemIndexBucketList[ix][iy][iz];
322
323 do_output(LOG_CAT_INFO, LOG_AREA_INTEGRALS,
324 "create_box_system end OK, toplevelBoxSize: %5.1f, #levels: %2i, #boxes at top level: %6i",
325 (double)toplevelBoxSize, numberOfLevels, levelList[noOfLevels-1].noOfBoxes);
326
327 return 0;
328 }
329
330
331
332 ergo_real
get_min_distance_from_point_to_box(const ergo_real * boxCenterCoords,ergo_real halfwidth,const ergo_real * point)333 get_min_distance_from_point_to_box(const ergo_real* boxCenterCoords,
334 ergo_real halfwidth,
335 const ergo_real* point)
336 {
337 ergo_real dxList[3];
338 for(int k = 0; k < 3; k++)
339 {
340 ergo_real dx = template_blas_fabs(boxCenterCoords[k] - point[k]);
341 if(dx > halfwidth)
342 dxList[k] = dx - halfwidth;
343 else
344 dxList[k] = 0;
345 }
346 ergo_real sum = 0;
347 for(int k = 0; k < 3; k++)
348 sum += dxList[k] * dxList[k];
349 return template_blas_sqrt(sum);
350 }
351
get_items_near_point_recursive(const box_item_struct * itemList,const ergo_real * coords,ergo_real distance,int * resultOrgIndexList,int level,int boxIndex) const352 int BoxSystem::get_items_near_point_recursive(const box_item_struct* itemList,
353 const ergo_real* coords,
354 ergo_real distance,
355 int* resultOrgIndexList,
356 int level,
357 int boxIndex) const
358 {
359 const box_struct_basic* box = &boxList[boxIndex];
360 // Check if this entire box is so far away that it can be skipped.
361 ergo_real min_distance_from_point_to_box = get_min_distance_from_point_to_box(box->centerCoords, box->width/2, coords);
362 if(min_distance_from_point_to_box > distance)
363 return 0;
364 // No, we could not skip. Take care of box contents.
365 if(level == noOfLevels-1)
366 {
367 // We are at top level.
368 // Go through all points in box and add the relevant ones to result list.
369 int count = 0;
370 for(int i = 0; i < box->noOfItems; i++)
371 {
372 const box_item_struct* currItem = &itemList[box->firstItemIndex+i];
373 ergo_real sum = 0;
374 for(int coordNo = 0; coordNo < 3; coordNo++)
375 {
376 ergo_real d = coords[coordNo] - currItem->centerCoords[coordNo];
377 sum += d*d;
378 }
379 ergo_real currDist = template_blas_sqrt(sum);
380 if(currDist < distance)
381 {
382 // Add to result.
383 resultOrgIndexList[count] = currItem->originalIndex;
384 count++;
385 }
386 } // END FOR i
387 return count;
388 }
389 // Not top level. Go to next level.
390 int count = 0;
391 for(int i = 0; i < box->noOfChildBoxes; i++)
392 {
393 int childBoxIndex = box->firstChildBoxIndex + i;
394 int nNew = get_items_near_point_recursive(itemList,
395 coords,
396 distance,
397 &resultOrgIndexList[count],
398 level+1,
399 childBoxIndex);
400 count += nNew;
401 }
402 return count;
403 }
404
405
406
407 static int
compare_ints(const void * p1,const void * p2)408 compare_ints(const void* p1, const void* p2)
409 {
410 int i1 = *((int*)p1);
411 int i2 = *((int*)p2);
412 if(i1 > i2)
413 return 1;
414 if(i1 < i2)
415 return -1;
416 return 0;
417 }
418
419
420
421 /** Goes through existning box system to find all items within
422 specified distance from given reference point.
423
424 @param itemList the list of items for which the box system was created.
425 @param coords list of 3 coordinates for reference point.
426 @param distance the distance to find items within.
427 @param resultOrgIndexList preallocated list of resulting org indexes.
428 */
429
get_items_near_point(const box_item_struct * itemList,const ergo_real * coords,ergo_real distance,int * resultOrgIndexList) const430 int BoxSystem::get_items_near_point(const box_item_struct* itemList,
431 const ergo_real* coords,
432 ergo_real distance,
433 int* resultOrgIndexList) const
434 {
435 if(!boxList)
436 {
437 do_output(LOG_CAT_ERROR, LOG_AREA_INTEGRALS, "error in BoxSystem::get_items_near_point: (!boxList). Must create box system first!");
438 return -1;
439 }
440 int noOfItems = get_items_near_point_recursive(itemList, coords, distance, resultOrgIndexList, 0, levelList[0].startIndexInBoxList);
441 // sort resultOrgIndexList
442 qsort(resultOrgIndexList, noOfItems, sizeof(int), compare_ints);
443 return noOfItems;
444 }
445
446
447