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