OOFEM 3.0
Loading...
Searching...
No Matches
octreelocalizer.C
Go to the documentation of this file.
1/*
2 *
3 * ##### ##### ###### ###### ### ###
4 * ## ## ## ## ## ## ## ### ##
5 * ## ## ## ## #### #### ## # ##
6 * ## ## ## ## ## ## ## ##
7 * ## ## ## ## ## ## ## ##
8 * ##### ##### ## ###### ## ##
9 *
10 *
11 * OOFEM : Object Oriented Finite Element Code
12 *
13 * Copyright (C) 1993 - 2025 Borek Patzak
14 *
15 *
16 *
17 * Czech Technical University, Faculty of Civil Engineering,
18 * Department of Structural Mechanics, 166 29 Prague, Czech Republic
19 *
20 * This library is free software; you can redistribute it and/or
21 * modify it under the terms of the GNU Lesser General Public
22 * License as published by the Free Software Foundation; either
23 * version 2.1 of the License, or (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
28 * Lesser General Public License for more details.
29 *
30 * You should have received a copy of the GNU Lesser General Public
31 * License along with this library; if not, write to the Free Software
32 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
33 */
34
35#include "octreelocalizer.h"
36#include "element.h"
37#include "domain.h"
38#include "integrationrule.h"
39#include "gausspoint.h"
40#include "dofmanager.h"
41#include "node.h"
42#include "connectivitytable.h"
43#include "mathfem.h"
44#include "timer.h"
45#include "error.h"
47
48#include <iostream>
49
50namespace oofem {
51OctantRec :: OctantRec(OctantRec *parent, FloatArray origin, double halfWidth) :
53 origin(std::move(origin)),
55{
56 this->depth = parent ? parent->giveCellDepth() + 1 : 0;
57}
58
59std :: list< int > &
60OctantRec :: giveNodeList()
61{
62 return nodeList;
63}
64
66OctantRec :: giveIPElementList()
67{
68 return elementIPList;
69}
70
71std :: list< int > &
72OctantRec :: giveElementList(int region)
73{
74 if ( (int)elementList.size() < region + 1 ) {
75 elementList.resize(region + 1);
76 }
77 return elementList[region];
78}
79
80
82OctantRec :: giveChild(int xi, int yi, int zi)
83{
84 if ( ( xi >= 0 ) && ( xi < 2 ) && ( yi >= 0 ) && ( yi < 2 ) && ( zi >= 0 ) && ( zi < 2 ) ) {
85 return this->child [ xi ] [ yi ] [ zi ].get();
87 //return &(*child) [ xi ] [ yi ] [ zi ];
88 } else {
89 OOFEM_ERROR("invalid child index (%d,%d,%d)", xi, yi, zi);
90 }
91
92 // return nullptr;
93}
94
95
96OctantRec :: ChildStatus
97OctantRec :: giveChildContainingPoint(OctantRec *&child, const FloatArray &coords, const IntArray &mask)
98{
99 if ( this->isTerminalOctant() ) {
100 child = nullptr;
101 return CS_NoChild;
102 }
103
104 IntArray ind(3);
105 for ( int i = 0; i < coords.giveSize(); ++i ) {
106 ind[i] = mask[i] && coords[i] > this->origin[i];
107 }
108
109 child = this->child [ ind[0] ] [ ind[1] ] [ ind[2] ].get();
110 return CS_ChildFound;
111}
112
113
114bool
115OctantRec :: isTerminalOctant()
116{
117 // This implementation is relying on fact, that child [0][0][0]
118 // is created even for all degenerated trees
119 return ! this->child [ 0 ] [ 0 ] [ 0 ];
120}
121
122
123void
124OctantRec :: divideLocally(int level, const IntArray &mask)
125{
126 if ( this->isTerminalOctant() ) {
127 // create corresponding child octants
128 for ( int i = 0; i <= mask.at(1); i++ ) {
129 for ( int j = 0; j <= mask.at(2); j++ ) {
130 for ( int k = 0; k <= mask.at(3); k++ ) {
131 FloatArray childOrigin = Vec3(
132 this->origin.at(1) + ( i - 0.5 ) * this->halfWidth * mask.at(1),
133 this->origin.at(2) + ( j - 0.5 ) * this->halfWidth * mask.at(2),
134 this->origin.at(3) + ( k - 0.5 ) * this->halfWidth * mask.at(3)
135 );
136 this->child [ i ] [ j ] [ k ] = std::make_unique<OctantRec>(this, std::move(childOrigin), this->halfWidth * 0.5);
137 }
138 }
139 }
140 }
141
142 int newLevel = level - 1;
143 if ( newLevel > 0 ) {
144 // propagate message to children recursively with level param decreased
145 for ( int i = 0; i <= mask.at(1); i++ ) {
146 for ( int j = 0; j <= mask.at(2); j++ ) {
147 for ( int k = 0; k <= mask.at(3); k++ ) {
148 if ( this->child [ i ] [ j ] [ k ] ) {
149 this->child [ i ] [ j ] [ k ]->divideLocally(newLevel, mask);
150 }
151 }
152 }
153 }
154 }
155}
156
157
158OctantRec :: BoundingBoxStatus
159OctantRec :: testBoundingBox(const FloatArray &coords, double radius, const IntArray &mask)
160{
161 bool bbInside = true;
162
163 for ( int i = 1; i <= coords.giveSize(); i++ ) {
164 if ( mask.at(i) ) {
165 double bb0 = coords.at(i) - radius;
166 double bb1 = coords.at(i) + radius;
167 double oct0 = this->origin.at(i) - this->halfWidth;
168 double oct1 = this->origin.at(i) + this->halfWidth;
169
170 if ( oct1 < bb0 || oct0 > bb1 ) { // Then its definitely outside, no need to go on
171 return BBS_OutsideCell;
172 }
173 // Other cases; We first check if bb is completely inside the cell, in every spatial dimension
174 bbInside = bbInside && oct0 < bb0 && oct1 > bb1;
175 }
176 }
177 return bbInside ? BBS_InsideCell : BBS_ContainsCell;
178}
179
180
181void OctantRec :: printYourself()
182{
183 if ( this->isTerminalOctant() ) {
184 std :: cout << " center = {" << this->origin.at(1) << "," << this->origin.at(2) << "," << this->origin.at(3)
185 << "} size = " << ( this->halfWidth * 2. ) << " nodes = " << this->nodeList.size() << " elem_ips = " << this->elementIPList.giveSize() << "\n";
186 } else {
187 if ( this->depth == 0 ) {
188 printf("*ROOTCELL*");
189 }
190 printf("\n");
191 for ( int i = 0; i <= 1; i++ ) {
192 for ( int j = 0; j <= 1; j++ ) {
193 for ( int k = 0; k <= 1; k++ ) {
194 if ( this->child [ i ] [ j ] [ k ] ) {
195 for ( int q = 0; q < this->depth - 1; q++ ) {
196 printf(" ");
197 }
198 printf("+");
199 this->child [ i ] [ j ] [ k ]->printYourself();
200 }
201 }
202 }
203 }
204 }
205}
206
207
208OctreeSpatialLocalizer :: OctreeSpatialLocalizer(Domain* d) : SpatialLocalizer(d),
209 octreeMask(3),
211{
212 this->initialized = false;
213 this->rootCell=nullptr;
214#ifdef _OPENMP
215 omp_init_lock(&ElementIPDataStructureLock);
216 omp_init_lock(&buildOctreeDataStructureLock);
217 omp_init_lock(&initLock);
218#endif
219}
220
221
222OctantRec *
223OctreeSpatialLocalizer :: findTerminalContaining(OctantRec &startCell, const FloatArray &coords)
224{
225 OctantRec *currCell = &startCell;
226 // found terminal octant containing node
227 while ( !currCell->isTerminalOctant() ) {
228 auto result = currCell->giveChildContainingPoint(currCell, coords, octreeMask);
229 if ( result == OctantRec :: CS_NoChild ) {
230 OOFEM_ERROR("internal error - octree inconsistency");
231 }
232 }
233 return currCell;
234}
235
236
237bool
238OctreeSpatialLocalizer :: buildOctreeDataStructure()
239{
240// OOFEM_LOG_INFO("Initializing Octree structure\n");
241 int init = 1, nnode = this->domain->giveNumberOfDofManagers();
242 double rootSize, resolutionLimit;
243 FloatArray minc(3), maxc(3);
244
245 // test if tree already built
246 if ( rootCell ) {
247 return true;
248 }
249#ifdef _OPENMP
250 omp_set_lock(&buildOctreeDataStructureLock); // if not initialized yet; one thread can proceed with init; others have to wait until init completed
251 if ( rootCell ) {
252 omp_unset_lock(&buildOctreeDataStructureLock);
253 return true;
254 }
255#endif
256
257 this->elementListsInitialized.resize(this->domain->giveNumberOfRegions() + 1);
258 this->elementListsInitialized.zero();
259 this->elementIPListsInitialized = false;
260
261 // measure time consumed by octree build phase
262 Timer timer;
263 timer.startTimer();
264
265 // first determine domain extends (bounding box), and check for degenerated domain type
266 for ( int i = 1; i <= nnode; i++ ) {
267 Node *node = domain->giveNode(i);
268 if ( node ) {
269 const auto &coords = node->giveCoordinates();
270 if ( init ) {
271 init = 0;
272 for ( int j = 1; j <= coords.giveSize(); j++ ) {
273 minc.at(j) = maxc.at(j) = coords.at(j);
274 }
275 } else {
276 for ( int j = 1; j <= coords.giveSize(); j++ ) {
277 if ( coords.at(j) < minc.at(j) ) {
278 minc.at(j) = coords.at(j);
279 }
280
281 if ( coords.at(j) > maxc.at(j) ) {
282 maxc.at(j) = coords.at(j);
283 }
284 }
285 }
286 }
287 } // end loop over nodes
288
289 // determine root size
290 rootSize = 0.0;
291 for ( int i = 1; i <= 3; i++ ) {
292 rootSize = max( rootSize, 1.000001 * (maxc.at(i) - minc.at(i)) );
293 }
294
295 // check for degenerated domain
296 resolutionLimit = min(1.e-3, rootSize / 1.e6);
297 for ( int i = 1; i <= 3; i++ ) {
298 if ( ( maxc.at(i) - minc.at(i) ) > resolutionLimit ) {
299 this->octreeMask.at(i) = 1;
300 } else {
301 this->octreeMask.at(i) = 0;
302 }
303 }
304
305 // Create root Octant
306 FloatArray center = minc;
307 center.add(maxc);
308 center.times(0.5);
309 this->rootCell = std::make_unique<OctantRec>(nullptr, center, rootSize * 0.5);
310
311 // Build octree tree
312 if ( nnode > OCTREE_MAX_NODES_LIMIT ) {
313 this->rootCell->divideLocally(1, this->octreeMask);
314 }
315
316 // insert domain nodes into tree
317 for ( int i = 1; i <= nnode; i++ ) {
318 Node *node = domain->giveNode(i);
319 if ( node ) {
320 const auto &coords = node->giveCoordinates();
321 this->insertNodeIntoOctree(*this->rootCell, i, coords);
322 }
323 }
324
325 timer.stopTimer();
326
327 // compute max. tree depth
328 int treeDepth = this->giveMaxTreeDepthFrom(*this->rootCell);
329 OOFEM_LOG_DEBUG( "Octree init [depth %d in %.2fs]\n", treeDepth, timer.getUtime() );
330#ifdef _OPENMP
331 omp_unset_lock(&buildOctreeDataStructureLock);
332#endif
333 return true;
334}
335
336
337void
338OctreeSpatialLocalizer :: initElementIPDataStructure()
339{
340 // Original implementation
341 //
342 int nelems = this->domain->giveNumberOfElements();
343 FloatArray jGpCoords;
344 if ( this->elementIPListsInitialized ) {
345 return;
346 }
347#ifdef _OPENMP
348 omp_set_lock(&ElementIPDataStructureLock); // if not initialized yet; one thread can proceed with init; others have to wait until init completed
349 if ( this->elementIPListsInitialized ) {
350 omp_unset_lock(&ElementIPDataStructureLock);
351 return;
352 }
353#endif
354 // insert IP records into tree (the tree topology is determined by nodes)
355 for ( int i = 1; i <= nelems; i++ ) {
356 // only default IP are taken into account
357 Element *ielem = this->giveDomain()->giveElement(i);
358 if ( ielem->giveNumberOfIntegrationRules() > 0 ) {
359 for ( GaussPoint *jGp: *ielem->giveDefaultIntegrationRulePtr() ) {
360 if ( ielem->computeGlobalCoordinates( jGpCoords, jGp->giveNaturalCoordinates() ) ) {
361 this->insertIPElementIntoOctree(*this->rootCell, i, jGpCoords);
362 } else {
363 OOFEM_ERROR("computeGlobalCoordinates failed");
364 }
365 }
366 }
367 // there are no IP (belonging to default integration rule of an element)
368 // but the element should be present in octree data structure
369 // this is needed by some services (giveElementContainingPoint, for example)
370 for ( int j = 1; j <= ielem->giveNumberOfNodes(); j++ ) {
371 const auto &nc = ielem->giveNode(j)->giveCoordinates();
372 this->insertIPElementIntoOctree(*this->rootCell, i, nc);
373 }
374 }
375
376 // Initializes the element lists in octree data structure.
377 // This implementation requires that the list of nodes in terminate cells exists
378 // simply all shared elements to nodes in terminal cell are added.
379 // If this is added to existing implementation based on adding elements only if integration point is in the cell
380 // This leads to more complete element list in terminal cell.
381 // Can improve the searching for background element, but will deteriorate
382 // the performance of closest IP search and search of elements in given volume.
383
384 // Note: since in general, the integration point of an element may fall into
385 // an octant, where are not the element nodes, the original algorithm is
386 // necessary.
387
388 //this->insertElementsUsingNodalConnectivitiesIntoOctree (this->rootCell);
389 this->elementIPListsInitialized = true;
390#ifdef _OPENMP
391 omp_unset_lock(&ElementIPDataStructureLock);
392#endif
393
394}
395
396
397void
398OctreeSpatialLocalizer :: insertIPElementIntoOctree(OctantRec &rootCell, int elemNum, const FloatArray &coords)
399{
400 // found terminal octant containing IP
401 OctantRec *currCell = this->findTerminalContaining(rootCell, coords);
402 currCell->addElementIP(elemNum);
403}
404
405
406void
407OctreeSpatialLocalizer :: initElementDataStructure(int region)
408{
409 FloatArray b0, b1;
410
411 this->init();
412 if ( this->elementListsInitialized.giveSize() >= region + 1 && this->elementListsInitialized[region] ) {
413 return;
414 }
415
416 for ( int i = 1; i <= this->domain->giveNumberOfElements(); i++ ) {
417 Element *ielem = this->giveDomain()->giveElement(i);
418 if ( ielem->giveRegionNumber() == region || region == 0 ) {
420 if ( interface ) {
421 interface->SpatialLocalizerI_giveBBox(b0, b1);
422 this->insertElementIntoOctree(*this->rootCell, region, i, b0, b1);
423 }
424 }
425 }
426 this->elementListsInitialized[region] = true;
427}
428
429
430void
431OctreeSpatialLocalizer :: insertElementIntoOctree(OctantRec &rootCell, int region, int elemNum, const FloatArray &b0, const FloatArray &b1)
432{
433 // Compare the bounding box corners to the center to determine which region is overlaps
434 // Checks: b0 <= center, b1 >= center for each entry.
435 // This is bundled into an array for more convenient code in the loop.
436 IntArray bbc [ 2 ] = {
437 IntArray(3), IntArray(3)
438 };
439 FloatArray origin = rootCell.giveOrigin();
440 for ( int i = 1; i <= b0.giveSize(); i++ ) {
441 if ( this->octreeMask.at(i) ) {
442 bbc [ 0 ].at(i) = b0.at(i) <= origin.at(i);
443 bbc [ 1 ].at(i) = b1.at(i) >= origin.at(i);
444 } else {
445 bbc [ 0 ].at(i) = bbc [ 1 ].at(i) = true;
446 }
447 }
448 for ( int i = b0.giveSize() + 1; i <= 3; i++ ) {
449 bbc [ 0 ].at(i) = bbc [ 1 ].at(i) = true;
450 }
451 // Check terminal or recurse
452 if ( rootCell.isTerminalOctant() ) {
453 rootCell.addElement(region, elemNum);
454 } else {
455 // The for loops below check if the bounding box overlaps the region before or after the cell center
456 // e.g. when i = 0, then we check for the children that have x-coordinate <= than root center
457 for ( int i = 0; i <= octreeMask.at(1); i++ ) {
458 if ( bbc [ i ].at(1) ) {
459 for ( int j = 0; j <= octreeMask.at(2); j++ ) {
460 if ( bbc [ j ].at(2) ) {
461 for ( int k = 0; k <= octreeMask.at(3); k++ ) {
462 if ( bbc [ k ].at(3) && rootCell.giveChild(i, j, k) ) {
463 this->insertElementIntoOctree(*rootCell.giveChild(i, j, k), region, elemNum, b0, b1);
464 }
465 }
466 }
467 }
468 }
469 }
470 }
471}
472
473
474void
475OctreeSpatialLocalizer :: insertElementsUsingNodalConnectivitiesIntoOctree(OctantRec &currCell)
476{
477 if ( currCell.isTerminalOctant() ) {
478 for ( int inod: currCell.giveNodeList() ) {
479 // loop over cell nodes and ask connectivity table for shared elements
480 const IntArray *dofmanConnectivity = domain->giveConnectivityTable()->giveDofManConnectivityArray(inod);
481 if ( dofmanConnectivity ) {
482 for ( int d: *dofmanConnectivity ) {
483 currCell.addElementIP( d );
484 }
485 }
486 }
487 } else {
488 for ( int i = 0; i <= octreeMask.at(1); i++ ) {
489 for ( int j = 0; j <= octreeMask.at(2); j++ ) {
490 for ( int k = 0; k <= octreeMask.at(3); k++ ) {
491 auto child = currCell.giveChild(i, j, k);
492 if ( child ) {
494 }
495 }
496 }
497 }
498 }
499}
500
501
502void
503OctreeSpatialLocalizer :: insertNodeIntoOctree(OctantRec &rootCell, int nodeNum, const FloatArray &coords)
504{
505 // found terminal octant containing node
506 OctantRec *currCell = this->findTerminalContaining(rootCell, coords);
507 // request cell node list
508 nodeContainerType &cellNodeList = currCell->giveNodeList();
509 int nCellItems = (int) cellNodeList.size();
510 int cellDepth = currCell->giveCellDepth();
511 // check for refinement criteria
512 // should also include max refinement level criteria
513 //
514 if ( nCellItems > OCTREE_MAX_NODES_LIMIT && cellDepth <= OCTREE_MAX_DEPTH ) {
515 // refine tree one level
516 currCell->divideLocally(1, this->octreeMask);
517 // propagate all nodes already assigned to currCell to children
518 for ( int inod: cellNodeList ) {
519 Node *node = domain->giveNode(inod);
520 const auto &nodeCoords = node->giveCoordinates();
521 this->insertNodeIntoOctree(*currCell, inod, nodeCoords);
522 }
523
524 // remove node list at relative root cell
525 currCell->deleteNodeList();
526 // now the new node record is saved simply to one of created children.
527 // Generally, one has to check if child is candidate for further refinement
528 // (case, when all parent nodes are belonging to particular child), refine it
529 // and check children again. This can be implemented by recursive procedure, but
530 // probability of this case is relatively small.
531 // Current implementation simply insert node to child created in first refinement,
532 // which can lead ne node violation of refinement criteria.
533 // But implementation is simple and effective.
534 // Please note, that the next insertion in particular cell cause the refinement.
535
536 // find child containing new node
537 auto result = currCell->giveChildContainingPoint(currCell, coords, this->octreeMask);
538 if ( result != OctantRec :: CS_ChildFound ) {
539 OOFEM_ERROR("internal error - octree inconsistency");
540 }
541 this->insertNodeIntoOctree(*currCell, nodeNum, coords);
542 } else {
543 currCell->addNode(nodeNum);
544 }
545}
546
547
548Element *
549OctreeSpatialLocalizer :: giveElementContainingPoint(const FloatArray &coords, const IntArray *regionList)
550{
551 OctantRec *currCell, *childCell = nullptr;
552
553 this->init();
555
556 // found terminal octant containing point
557 currCell = this->findTerminalContaining(*rootCell, coords);
558
559 while ( currCell ) {
560 Element *answer = this->giveElementContainingPoint(*currCell, coords, childCell, regionList);
561 if ( answer ) {
562 return answer;
563 }
564
565 childCell = currCell;
566 // terminal cell does not contain node and its connected elements containing the given point
567 // search at parent level
568 currCell = currCell->giveParent();
569 }
570
571 // there isn't any element containing point.
572 return nullptr;
573}
574
575Element *
576OctreeSpatialLocalizer :: giveElementContainingPoint(const FloatArray &coords, const Set &eset)
577{
578 OctantRec *currCell, *childCell = nullptr;
579
580 this->init();
582
583 // found terminal octant containing point
584 currCell = this->findTerminalContaining(*rootCell, coords);
585
586 while ( currCell ) {
587 // loop over all elements in currCell, skip search on child cell already scanned
588 Element *answer = this->giveElementContainingPoint(*currCell, coords, childCell, & eset);
589 if ( answer ) {
590 return answer;
591 }
592
593 childCell = currCell;
594 // terminal cell does not contain node and its connected elements containing the given point
595 // search at parent level
596 currCell = currCell->giveParent();
597 }
598
599 // there isn't any element containing point.
600 return nullptr;
601}
602
603
604Element *
605OctreeSpatialLocalizer :: giveElementContainingPoint(OctantRec &cell, const FloatArray &coords,
606 OctantRec *scannedChild, const IntArray *regionList)
607{
608 // recursive implementation
609 if ( cell.isTerminalOctant() ) {
610
611 for ( int iel: cell.giveIPElementList() ) {
612 Element *ielemptr = this->giveDomain()->giveElement(iel);
613
614 if ( ielemptr->giveParallelMode() == Element_remote ) {
615 continue;
616 }
617
619 if ( interface ) {
620 if ( regionList && ( regionList->findFirstIndexOf( ielemptr->giveRegionNumber() ) == 0 ) ) {
621 continue;
622 }
623
624 if ( interface->SpatialLocalizerI_BBoxContainsPoint(coords) == 0 ) {
625 continue;
626 }
627
628 if ( interface->SpatialLocalizerI_containsPoint(coords) ) {
629 return ielemptr;
630 }
631 }
632 }
633
634 return nullptr;
635 } else {
636 // receiver is not terminal octant -> call same service on childs
637 for ( int i = 0; i <= octreeMask.at(1); i++ ) {
638 for ( int j = 0; j <= octreeMask.at(2); j++ ) {
639 for ( int k = 0; k <= octreeMask.at(3); k++ ) {
640 auto child = cell.giveChild(i, j, k);
641 if ( child ) {
642 if ( scannedChild == child ) {
643 continue;
644 }
645
646 Element *answer = this->giveElementContainingPoint(*child, coords, nullptr, regionList);
647 if ( answer ) {
648 return answer;
649 }
650 }
651 }
652 }
653 }
654 }
655
656 return nullptr;
657}
658
659Element *
660OctreeSpatialLocalizer :: giveElementContainingPoint(OctantRec &cell, const FloatArray &coords,
661 OctantRec *scannedChild, const Set *eset)
662{
663 // recursive implementation
664 if ( cell.isTerminalOctant() ) {
665
666 for ( int iel: cell.giveIPElementList() ) {
667 Element *ielemptr = this->giveDomain()->giveElement(iel);
668
669 if ( ielemptr->giveParallelMode() == Element_remote ) {
670 continue;
671 }
672
674 if ( interface ) {
675 if ( eset && ( !eset->hasElement( ielemptr->giveNumber() ) ) ) {
676 continue;
677 }
678
679 if ( interface->SpatialLocalizerI_BBoxContainsPoint(coords) == 0 ) {
680 continue;
681 }
682
683 if ( interface->SpatialLocalizerI_containsPoint(coords) ) {
684 return ielemptr;
685 }
686 }
687 }
688
689 return nullptr;
690 } else {
691 // receiver is not terminal octant -> call same service on childs
692 for ( int i = 0; i <= octreeMask.at(1); i++ ) {
693 for ( int j = 0; j <= octreeMask.at(2); j++ ) {
694 for ( int k = 0; k <= octreeMask.at(3); k++ ) {
695 auto child = cell.giveChild(i, j, k);
696 if ( child ) {
697 if ( scannedChild == child ) {
698 continue;
699 }
700
701 Element *answer = this->giveElementContainingPoint(*child, coords, nullptr, eset);
702 if ( answer ) {
703 return answer;
704 }
705 }
706 }
707 }
708 }
709 }
710
711 return nullptr;
712}
713
714
715Element *
716OctreeSpatialLocalizer :: giveElementClosestToPoint(FloatArray &lcoords, FloatArray &closest,
717 const FloatArray &gcoords, int region)
718{
719 Element *answer = nullptr;
720 std :: list< OctantRec * >cellList;
721 OctantRec *currCell;
722 double radius, prevRadius;
723 const FloatArray &c = this->rootCell->giveOrigin();
724
725 this->initElementDataStructure(region);
726
727 // Maximum distance given coordinate and furthest terminal cell ( center_distance + width/2*sqrt(3) )
728 double minDist = distance(c, gcoords) + this->rootCell->giveWidth() * 0.87;
729
730 // found terminal octant containing point
731 currCell = this->findTerminalContaining(*rootCell, gcoords);
732
733 // Look in center, then expand.
734 this->giveElementClosestToPointWithinOctant(*currCell, gcoords, minDist, lcoords, closest, answer, region);
735 prevRadius = 0.;
736 radius = currCell->giveWidth();
737 while ( radius < minDist ) {
738 this->giveListOfTerminalCellsInBoundingBox(cellList, gcoords, radius, prevRadius, *this->rootCell);
739 for ( OctantRec *icell: cellList ) {
740 this->giveElementClosestToPointWithinOctant(*icell, gcoords, minDist, lcoords, closest, answer, region);
741 }
742 prevRadius = radius;
743 radius *= 2.; // Keep expanding the scope until the radius is larger than the entire root cell, then give up (because we have checked every possible cell)
744 }
745 return answer;
746}
747
748
749void
750OctreeSpatialLocalizer :: giveElementClosestToPointWithinOctant(OctantRec &currCell, const FloatArray &gcoords,
751 double &minDist, FloatArray &lcoords, FloatArray &closest, Element * &answer, int region)
752{
753 FloatArray currLcoords;
754 FloatArray currClosest;
755
756 auto &elementList = currCell.giveElementList(region);
757 for ( int iel: elementList ) {
758 Element *ielemptr = this->giveDomain()->giveElement(iel);
759
760 if ( ielemptr->giveParallelMode() == Element_remote ) {
761 continue;
762 }
763
765 if ( region > 0 && ielemptr->giveRegionNumber() != region ) {
766 continue;
767 }
768 double currDist = interface->SpatialLocalizerI_giveClosestPoint(currLcoords, currClosest, gcoords);
769 if ( currDist < minDist ) {
770 lcoords = currLcoords;
771 closest = currClosest;
772 answer = ielemptr;
773 minDist = currDist;
774 if ( minDist == 0.0 ) {
775 return;
776 }
777 }
778 }
779}
780
781
783OctreeSpatialLocalizer :: giveClosestIP(const FloatArray &coords, int region, bool iCohesiveZoneGP)
784{
785 GaussPoint *nearestGp = nullptr;
786 FloatArray jGpCoords;
787
788 this->init();
790
791 // list of already tested elements
792 // elementContainerType visitedElems;
793 double minDist = 1.1 * rootCell->giveWidth();
794 // found terminal octant containing point
795 OctantRec *currCell = this->findTerminalContaining(*rootCell, coords);
796 auto &elementList = currCell->giveIPElementList();
797 // find nearest ip in this terminal cell
798
799 for ( int iel: elementList ) {
800 Element *ielem = domain->giveElement(iel);
801
802 if ( ielem->giveParallelMode() == Element_remote ) {
803 continue;
804 }
805
806 if ( region > 0 && region != ielem->giveRegionNumber() ) {
807 continue;
808 }
809
810 if ( !iCohesiveZoneGP ) {
811 // test if element already visited
812 // if (!visitedElems.insert(*pos).second) continue;
813 for ( auto &jGp: *ielem->giveDefaultIntegrationRulePtr() ) {
814 if ( ielem->computeGlobalCoordinates( jGpCoords, jGp->giveNaturalCoordinates() ) ) {
815 // compute distance
816 double dist = distance(coords, jGpCoords);
817 if ( dist < minDist ) {
818 minDist = dist;
819 nearestGp = jGp;
820 }
821 } else {
822 OOFEM_ERROR("computeGlobalCoordinates failed");
823 }
824 }
825 } else {
827 // Check for cohesive zone Gauss points
828 XfemElementInterface *xFemEl = dynamic_cast< XfemElementInterface * >(ielem);
829
830 if ( xFemEl ) {
831 size_t numCZRules = xFemEl->mpCZIntegrationRules.size();
832 for ( size_t czRuleIndex = 0; czRuleIndex < numCZRules; czRuleIndex++ ) {
833 std :: unique_ptr< IntegrationRule > &iRule = xFemEl->mpCZIntegrationRules [ czRuleIndex ];
834 if ( iRule ) {
835 for ( auto &jGp: *iRule ) {
836 if ( ielem->computeGlobalCoordinates( jGpCoords, jGp->giveNaturalCoordinates() ) ) {
837 // compute distance
838 double dist = distance(coords, jGpCoords);
839 //printf("czRuleIndex: %d j: %d dist: %e\n", czRuleIndex, j, dist);
840 if ( dist < minDist ) {
841 minDist = dist;
842 nearestGp = jGp;
843 }
844 } else {
845 OOFEM_ERROR("computeGlobalCoordinates failed");
846 }
847 }
848 } else {
849 OOFEM_ERROR("iRule is null");
850 }
851 }
852 }
853 }
854 }
855
856 FloatArray bborigin = coords;
857 // all cell element ip's scanned
858 // construct bounding box and test its position within currCell
859 auto BBStatus = currCell->testBoundingBox(bborigin, minDist, this->octreeMask);
860 if ( BBStatus == OctantRec :: BBS_InsideCell ) {
861 return nearestGp;
862 } else if ( BBStatus == OctantRec :: BBS_ContainsCell ) {
863 std :: list< OctantRec * >cellList;
864
865 // found terminal octant containing point
866 OctantRec *startCell = this->findTerminalContaining(*rootCell, coords);
867 // go up, until cell containing bbox is found
868 if ( startCell != rootCell.get() ) {
869 while ( startCell->testBoundingBox(coords, minDist, this->octreeMask) != OctantRec :: BBS_InsideCell ) {
870 startCell = startCell->giveParent();
871 if ( startCell == rootCell.get() ) {
872 break;
873 }
874 }
875 }
876
877 this->giveListOfTerminalCellsInBoundingBox(cellList, bborigin, minDist, 0, *startCell);
878
879 for ( OctantRec *icell: cellList ) {
880 if ( currCell == icell ) {
881 continue;
882 }
883
884 this->giveClosestIPWithinOctant(*icell, coords, region, minDist, nearestGp, iCohesiveZoneGP);
885 }
886
887 return nearestGp;
888
889#if 0
890 // found terminal octant containing point
891 OctantRec *startCell = this->findTerminalContaining(rootCell, coords);
892 // go up, until cell containing bbox is found
893 if ( startCell != rootCell ) {
894 while ( startCell->testBoundingBox(coords, minDist, this->octreeMask) != OctantRec :: BBS_InsideCell ) {
895 startCell = startCell->giveParent();
896 if ( startCell == rootCell ) {
897 break;
898 }
899 }
900 }
901 // loop over all child (if any) and found all nodes meeting the criteria
902 // this->giveClosestIPWithinOctant (startCell, visitedElems, coords, region, minDist, &nearestGp);
903 this->giveClosestIPWithinOctant(startCell, coords, region, minDist, & nearestGp);
904 return nearestGp;
905
906#endif
907
908
909#if 0
910 set< int >nearElementList;
911 // find all elements within bbox and find nearest ip
912 this->giveAllElementsWithIpWithinBox(nearElementList, bborigin, minDist);
913 // loop over those elements and find nearest ip and return it
914 // check for region also
915 if ( !nearElementList.empty() ) {
916 for ( int elnum: nearElementList ) {
917 ielem = domain->giveElement(elnum);
918 //igp = ipContainer[i].giveGp();
919 if ( region == ielem->giveRegionNumber() ) {
920 iRule = ielem->giveDefaultIntegrationRulePtr();
921 for ( auto &jGp: *iRule ) {
922 if ( ielem->computeGlobalCoordinates( jGpCoords, * ( jGp->giveCoordinates() ) ) ) {
923 // compute distance
924 double dist = distance(coords, jGpCoords);
925 if ( dist < minDist ) {
926 minDist = dist;
927 nearestGp = jGp;
928 }
929 } else {
930 OOFEM_ERROR("computeGlobalCoordinates failed");
931 }
932 }
933 }
934 }
935 }
936#endif
937 } else {
938 coords.printYourself("coords");
939 OOFEM_ERROR("octree inconsistency found");
940 }
941
942 return nullptr;
943}
944
945
946void
947OctreeSpatialLocalizer :: giveClosestIPWithinOctant(OctantRec &currentCell, //elementContainerType& visitedElems,
948 const FloatArray &coords,
949 int region, double &dist, GaussPoint *&answer, bool iCohesiveZoneGP)
950{
951 if ( currentCell.isTerminalOctant() ) {
952 FloatArray jGpCoords;
953
954 // loop over cell elements and check if they meet the criteria
955 for ( int iel: currentCell.giveIPElementList() ) {
956 // ask for element
957 Element *ielem = domain->giveElement(iel);
958
959 if ( ielem->giveParallelMode() == Element_remote ) {
960 continue;
961 }
962
963 if ( ( region > 0 ) && ( region != ielem->giveRegionNumber() ) ) {
964 continue;
965 }
966
967 if ( !iCohesiveZoneGP ) {
968 // test if element already visited
969 // if (!visitedElems.insert(*pos).second) continue;
970 // is one of his ip's within given bbox -> inset it into elemSet
971 for ( auto &gp: *ielem->giveDefaultIntegrationRulePtr() ) {
972 if ( ielem->computeGlobalCoordinates( jGpCoords, gp->giveNaturalCoordinates() ) ) {
973 double currDist = distance(coords, jGpCoords);
974 // multiple insertion are handled by STL set implementation
975 if ( currDist <= dist ) {
976 dist = currDist;
977 answer = gp;
978 }
979 } else {
980 OOFEM_ERROR("computeGlobalCoordinates failed");
981 }
982 }
983 } else {
985 // Check for cohesive zone Gauss points
986 XfemElementInterface *xFemEl = dynamic_cast< XfemElementInterface * >(ielem);
987
988 if ( xFemEl ) {
989 size_t numCZRules = xFemEl->mpCZIntegrationRules.size();
990 for ( size_t czRuleIndex = 0; czRuleIndex < numCZRules; czRuleIndex++ ) {
991 std :: unique_ptr< IntegrationRule > &iRule = xFemEl->mpCZIntegrationRules [ czRuleIndex ];
992 if ( iRule ) {
993 for ( auto &gp: *iRule ) {
994 if ( ielem->computeGlobalCoordinates( jGpCoords, gp->giveNaturalCoordinates() ) ) {
995 double currDist = distance(coords, jGpCoords);
996 // multiple insertion are handled by STL set implementation
997 if ( currDist <= dist ) {
998 dist = currDist;
999 answer = gp;
1000 }
1001 } else {
1002 OOFEM_ERROR("computeGlobalCoordinates failed");
1003 }
1004 }
1005 }
1006 }
1007 }
1008 }
1009 }
1010 } else {
1011 for ( int i = 0; i <= octreeMask.at(1); i++ ) {
1012 for ( int j = 0; j <= octreeMask.at(2); j++ ) {
1013 for ( int k = 0; k <= octreeMask.at(3); k++ ) {
1014 if ( currentCell.giveChild(i, j, k) ) {
1015 // test if box hits the cell
1016 auto BBStatus = currentCell.giveChild(i, j, k)->testBoundingBox(coords, dist, this->octreeMask);
1017 if ( BBStatus == OctantRec :: BBS_InsideCell || BBStatus == OctantRec :: BBS_ContainsCell ) {
1018 // if yes call this method for such cell
1019 //this->giveClosestIPWithinOctant(*currentCell.giveChild(i,j,k), visitedElems, coords, region, dist, answer);
1020 this->giveClosestIPWithinOctant(*currentCell.giveChild(i, j, k), coords, region, dist, answer, iCohesiveZoneGP);
1021 }
1022 }
1023 }
1024 }
1025 }
1026 }
1027}
1028
1029
1030GaussPoint *
1031OctreeSpatialLocalizer :: giveClosestIP(const FloatArray &coords, Set &elementSet, bool iCohesiveZoneGP)
1032{
1033 GaussPoint *nearestGp = nullptr;
1034 FloatArray jGpCoords;
1035
1036 this->init();
1038
1039 // list of already tested elements
1040 // elementContainerType visitedElems;
1041 double minDist = 1.1 * rootCell->giveWidth();
1042 // found terminal octant containing point
1043 OctantRec *currCell = this->findTerminalContaining(*rootCell, coords);
1044 // find nearest ip in this terminal cell
1045 for ( int iel: currCell->giveIPElementList()) {
1046 Element *ielem = domain->giveElement(iel);
1047
1048 if ( ielem->giveParallelMode() == Element_remote ) {
1049 continue;
1050 }
1051
1052 //igp = ipContainer[i].giveGp();
1053 if ( !elementSet.hasElement( ielem->giveNumber() ) ) {
1054 continue;
1055 }
1056
1057 if ( !iCohesiveZoneGP ) {
1058 // test if element already visited
1059 // if (!visitedElems.insert(ielem).second) continue;
1060 for ( auto &jGp: *ielem->giveDefaultIntegrationRulePtr() ) {
1061 if ( ielem->computeGlobalCoordinates( jGpCoords, jGp->giveNaturalCoordinates() ) ) {
1062 // compute distance
1063 double dist = distance(coords, jGpCoords);
1064 if ( dist < minDist ) {
1065 minDist = dist;
1066 nearestGp = jGp;
1067 }
1068 } else {
1069 OOFEM_ERROR("computeGlobalCoordinates failed");
1070 }
1071 }
1072 } else {
1074 // Check for cohesive zone Gauss points
1075 XfemElementInterface *xFemEl = dynamic_cast< XfemElementInterface * >(ielem);
1076
1077 if ( xFemEl ) {
1078 size_t numCZRules = xFemEl->mpCZIntegrationRules.size();
1079 for ( size_t czRuleIndex = 0; czRuleIndex < numCZRules; czRuleIndex++ ) {
1080 std :: unique_ptr< IntegrationRule > &iRule = xFemEl->mpCZIntegrationRules [ czRuleIndex ];
1081 if ( iRule ) {
1082 for ( auto &jGp: *iRule ) {
1083 if ( ielem->computeGlobalCoordinates( jGpCoords, jGp->giveNaturalCoordinates() ) ) {
1084 // compute distance
1085 double dist = distance(coords, jGpCoords);
1086 //printf("czRuleIndex: %d j: %d dist: %e\n", czRuleIndex, j, dist);
1087 if ( dist < minDist ) {
1088 minDist = dist;
1089 nearestGp = jGp;
1090 }
1091 } else {
1092 OOFEM_ERROR("computeGlobalCoordinates failed");
1093 }
1094 }
1095 } else {
1096 OOFEM_ERROR("iRule is null");
1097 }
1098 }
1099 }
1100 }
1101 }
1102
1103 FloatArray bborigin = coords;
1104 // all cell element ip's scanned
1105 // construct bounding box and test its position within currCell
1106 auto BBStatus = currCell->testBoundingBox(bborigin, minDist, this->octreeMask);
1107 if ( BBStatus == OctantRec :: BBS_InsideCell ) {
1108 return nearestGp;
1109 } else if ( BBStatus == OctantRec :: BBS_ContainsCell ) {
1110 std :: list< OctantRec * >cellList;
1111
1112 // found terminal octant containing point
1113 OctantRec *startCell = this->findTerminalContaining(*rootCell, coords);
1114 // go up, until cell containing bbox is found
1115 if ( startCell != rootCell.get() ) {
1116 while ( startCell->testBoundingBox(coords, minDist, this->octreeMask) != OctantRec :: BBS_InsideCell ) {
1117 startCell = startCell->giveParent();
1118 if ( startCell == rootCell.get() ) {
1119 break;
1120 }
1121 }
1122 }
1123
1124 this->giveListOfTerminalCellsInBoundingBox(cellList, bborigin, minDist, 0, *startCell);
1125
1126 for ( OctantRec *icell: cellList ) {
1127 if ( currCell == icell ) {
1128 continue;
1129 }
1130
1131 this->giveClosestIPWithinOctant(*icell, coords, elementSet, minDist, nearestGp, iCohesiveZoneGP );
1132 }
1133
1134 return nearestGp;
1135
1136#if 0
1137 // found terminal octant containing point
1138 OctantRec *startCell = this->findTerminalContaining(rootCell, coords);
1139 // go up, until cell containing bbox is found
1140 if ( startCell != rootCell ) {
1141 while ( startCell->testBoundingBox(coords, minDist, this->octreeMask) != OctantRec :: BBS_InsideCell ) {
1142 startCell = startCell->giveParent();
1143 if ( startCell == rootCell ) {
1144 break;
1145 }
1146 }
1147 }
1148 // loop over all child (if any) and found all nodes meeting the criteria
1149 // this->giveClosestIPWithinOctant(startCell, visitedElems, coords, region, minDist, &nearestGp);
1150 this->giveClosestIPWithinOctant(startCell, coords, region, minDist, & nearestGp);
1151 return nearestGp;
1152
1153#endif
1154
1155
1156#if 0
1157 set< int >nearElementList;
1158 // find all elements within bbox and find nearest ip
1159 this->giveAllElementsWithIpWithinBox(nearElementList, bborigin, minDist);
1160 // loop over those elements and find nearest ip and return it
1161 // check for region also
1162 if ( !nearElementList.empty() ) {
1163 for ( int iel: nearElementList ) {
1164 Element *ielem = domain->giveElement(iel);
1165 //igp = ipContainer[i].giveGp();
1166 if ( region == ielem->giveRegionNumber() ) {
1167 for ( auto &jGp: *ielem->giveDefaultIntegrationRulePtr() ) {
1168 if ( ielem->computeGlobalCoordinates( jGpCoords, * ( jGp->giveCoordinates() ) ) ) {
1169 // compute distance
1170 double dist = distance(coords, jGpCoords);
1171 if ( dist < minDist ) {
1172 minDist = dist;
1173 nearestGp = jGp;
1174 }
1175 } else {
1176 OOFEM_ERROR("computeGlobalCoordinates failed");
1177 }
1178 }
1179 }
1180 }
1181 }
1182#endif
1183 } else {
1184 printf("coords: ");
1185 coords.printYourself();
1186 OOFEM_ERROR("octree inconsistency found");
1187 }
1188
1189 return nullptr;
1190}
1191
1192
1193void
1194OctreeSpatialLocalizer :: giveClosestIPWithinOctant(OctantRec &currentCell, //elementContainerType& visitedElems,
1195 const FloatArray &coords,
1196 Set &elementSet, double &dist, GaussPoint *&answer, bool iCohesiveZoneGP)
1197{
1198 if ( currentCell.isTerminalOctant() ) {
1199 double currDist;
1200 FloatArray jGpCoords;
1201
1202 // loop over cell elements and check if they meet the criteria
1203 for ( int iel: currentCell.giveIPElementList() ) {
1204 // ask for element
1205 Element *ielem = domain->giveElement(iel);
1206
1207 if ( ielem->giveParallelMode() == Element_remote ) {
1208 continue;
1209 }
1210
1211 if ( !elementSet.hasElement( ielem->giveNumber() ) ) {
1212 continue;
1213 }
1214
1215 if ( !iCohesiveZoneGP ) {
1216 // test if element already visited
1217 // if (!visitedElems.insert(iel).second) continue;
1218 // is one of his ip's within given bbox -> inset it into elemSet
1219 for ( auto &gp: *ielem->giveDefaultIntegrationRulePtr() ) {
1220 if ( ielem->computeGlobalCoordinates( jGpCoords, gp->giveNaturalCoordinates() ) ) {
1221 currDist = distance(coords, jGpCoords);
1222 // multiple insertion are handled by STL set implementation
1223 if ( currDist <= dist ) {
1224 dist = currDist;
1225 answer = gp;
1226 }
1227 } else {
1228 OOFEM_ERROR("computeGlobalCoordinates failed");
1229 }
1230 }
1231 } else {
1233 // Check for cohesive zone Gauss points
1234 XfemElementInterface *xFemEl = dynamic_cast< XfemElementInterface * >(ielem);
1235
1236 if ( xFemEl ) {
1237 size_t numCZRules = xFemEl->mpCZIntegrationRules.size();
1238 for ( size_t czRuleIndex = 0; czRuleIndex < numCZRules; czRuleIndex++ ) {
1239 std :: unique_ptr< IntegrationRule > &iRule = xFemEl->mpCZIntegrationRules [ czRuleIndex ];
1240 if ( iRule ) {
1241 for ( auto &gp: *iRule ) {
1242 if ( ielem->computeGlobalCoordinates( jGpCoords, gp->giveNaturalCoordinates() ) ) {
1243 currDist = distance(coords, jGpCoords);
1244 // multiple insertion are handled by STL set implementation
1245 if ( currDist <= dist ) {
1246 dist = currDist;
1247 answer = gp;
1248 }
1249 } else {
1250 OOFEM_ERROR("computeGlobalCoordinates failed");
1251 }
1252 }
1253 }
1254 }
1255 }
1256 }
1257 }
1258 } else {
1259 for ( int i = 0; i <= octreeMask.at(1); i++ ) {
1260 for ( int j = 0; j <= octreeMask.at(2); j++ ) {
1261 for ( int k = 0; k <= octreeMask.at(3); k++ ) {
1262 if ( currentCell.giveChild(i, j, k) ) {
1263 // test if box hits the cell
1264 auto BBStatus = currentCell.giveChild(i, j, k)->testBoundingBox(coords, dist, this->octreeMask);
1265 if ( BBStatus == OctantRec :: BBS_InsideCell || BBStatus == OctantRec :: BBS_ContainsCell ) {
1266 // if yes call this method for such cell
1267 //this->giveClosestIPWithinOctant (currentCell->giveChild(i,j,k), visitedElems, coords, region, dist, answer);
1268 this->giveClosestIPWithinOctant(*currentCell.giveChild(i, j, k), coords, elementSet, dist, answer, iCohesiveZoneGP);
1269 }
1270 }
1271 }
1272 }
1273 }
1274 }
1275}
1276
1277
1278void
1279OctreeSpatialLocalizer :: giveAllElementsWithIpWithinBox_EvenIfEmpty(elementContainerType &elemSet, const FloatArray &coords,
1280 const double radius, bool iCohesiveZoneGP)
1281{
1282 this->init();
1284 // found terminal octant containing point
1285 OctantRec *currCell = this->findTerminalContaining(*rootCell, coords);
1286 // go up, until cell containing bbox is found
1287 if ( currCell != rootCell.get() ) {
1288 while ( currCell->testBoundingBox(coords, radius, this->octreeMask) != OctantRec :: BBS_InsideCell ) {
1289 currCell = currCell->giveParent();
1290 if ( currCell == rootCell.get() ) {
1291 break;
1292 }
1293 }
1294 }
1295
1296 // loop over all child (if any) and found all nodes meeting the criteria
1297 this->giveElementsWithIPWithinBox(elemSet, *currCell, coords, radius, iCohesiveZoneGP);
1298}
1299
1300
1301void
1302OctreeSpatialLocalizer :: giveAllElementsWithIpWithinBox(elementContainerType &elemSet, const FloatArray &coords,
1303 const double radius, bool iCohesiveZoneGP)
1304{
1305 this->giveAllElementsWithIpWithinBox_EvenIfEmpty(elemSet, coords, radius, iCohesiveZoneGP);
1306 if ( elemSet.isEmpty() ) {
1307 OOFEM_ERROR("empty set found");
1308 }
1309}
1310
1311
1312void
1313OctreeSpatialLocalizer :: giveElementsWithIPWithinBox(elementContainerType &elemSet, OctantRec &currentCell,
1314 const FloatArray &coords, const double radius, bool iCohesiveZoneGP)
1315{
1316 if ( currentCell.isTerminalOctant() ) {
1317 double currDist;
1318 FloatArray jGpCoords;
1319
1320 // loop over cell elements and check if they meet the criteria
1321 for ( int iel: currentCell.giveIPElementList() ) {
1322 // test if element is already present
1323 if ( elemSet.findSorted(iel) ) {
1324 continue;
1325 }
1326
1327 // ask for element
1328 Element *ielem = domain->giveElement(iel);
1329
1330 //if(ielem -> giveParallelMode() == Element_remote)continue;
1331 if ( !iCohesiveZoneGP ) {
1332 // is one of his ip's within given bbox -> inset it into elemSet
1333 for ( auto &gp: *ielem->giveDefaultIntegrationRulePtr() ) {
1334 if ( ielem->computeGlobalCoordinates( jGpCoords, gp->giveNaturalCoordinates() ) ) {
1335 currDist = distance(coords, jGpCoords);
1336 // multiple insertion are handled by STL set implementation
1337 if ( currDist <= radius ) {
1338 elemSet.insertSortedOnce(iel);
1339 break;
1340 }
1341 } else {
1342 OOFEM_ERROR("computeGlobalCoordinates failed");
1343 }
1344 }
1345 } else {
1347
1348 XfemElementInterface *xFemEl = dynamic_cast< XfemElementInterface * >(ielem);
1349
1350 if ( xFemEl ) {
1351 size_t numCZRules = xFemEl->mpCZIntegrationRules.size();
1352 for ( size_t czRuleIndex = 0; czRuleIndex < numCZRules; czRuleIndex++ ) {
1353 std :: unique_ptr< IntegrationRule > &iRule = xFemEl->mpCZIntegrationRules [ czRuleIndex ];
1354 if ( iRule ) {
1355 for ( auto &gp: *iRule ) {
1356 if ( ielem->computeGlobalCoordinates( jGpCoords, gp->giveNaturalCoordinates() ) ) {
1357 currDist = distance(coords, jGpCoords);
1358 // multiple insertion are handled by STL set implementation
1359 if ( currDist <= radius ) {
1360 elemSet.insertSortedOnce(iel);
1361 czRuleIndex = numCZRules;
1362 break;
1363 }
1364 } else {
1365 OOFEM_ERROR("computeGlobalCoordinates failed");
1366 }
1367 }
1368 }
1369 }
1370 }
1371 }
1372 }
1373 } else {
1374 for ( int i = 0; i <= octreeMask.at(1); i++ ) {
1375 for ( int j = 0; j <= octreeMask.at(2); j++ ) {
1376 for ( int k = 0; k <= octreeMask.at(3); k++ ) {
1377 auto child = currentCell.giveChild(i, j, k);
1378 if ( child ) {
1379 // test if box hits the cell
1380 auto BBStatus = child->testBoundingBox(coords, radius, this->octreeMask);
1381 if ( BBStatus == OctantRec :: BBS_InsideCell || BBStatus == OctantRec :: BBS_ContainsCell ) {
1382 // if yes call this method for such cell
1383 this->giveElementsWithIPWithinBox(elemSet, *child, coords, radius, iCohesiveZoneGP);
1384 }
1385 }
1386 }
1387 }
1388 }
1389 }
1390}
1391
1392
1393void
1394OctreeSpatialLocalizer :: giveAllNodesWithinBox(nodeContainerType &nodeSet, const FloatArray &coords, const double radius)
1395{
1396 this->init();
1397 // found terminal octant containing point
1398 OctantRec *currCell = this->findTerminalContaining(*rootCell, coords);
1399 // go up, until cell containing bbox is found
1400 if ( currCell != rootCell.get() ) {
1401 while ( currCell->testBoundingBox(coords, radius, this->octreeMask) != OctantRec :: BBS_InsideCell ) {
1402 currCell = currCell->giveParent();
1403 if ( currCell == rootCell.get() ) {
1404 break;
1405 }
1406 }
1407 }
1408
1409 // loop over all child (if any) and found all nodes meeting the criteria
1410 this->giveNodesWithinBox(nodeSet, *currCell, coords, radius);
1411}
1412
1413
1414Node *
1415OctreeSpatialLocalizer :: giveNodeClosestToPoint(const FloatArray &gcoords, double maxDist)
1416{
1417 Node *answer = nullptr;
1418 std :: list< OctantRec * > cellList;
1419
1420 // Maximum distance given coordinate and furthest terminal cell ( center_distance + width/2*sqrt(3) )
1421 double minDist = maxDist;
1422
1423 // found terminal octant containing point
1424 OctantRec *currCell = this->findTerminalContaining(*rootCell, gcoords);
1425
1426 // Look in center, then expand.
1427 this->giveNodeClosestToPointWithinOctant(*currCell, gcoords, minDist, answer);
1428 double prevRadius = 0.;
1429 double radius = min(currCell->giveWidth(), minDist);
1430 do {
1431 this->giveListOfTerminalCellsInBoundingBox(cellList, gcoords, radius, prevRadius, *this->rootCell);
1432 for ( OctantRec *cell: cellList ) {
1433 this->giveNodeClosestToPointWithinOctant(*cell, gcoords, minDist, answer);
1434 }
1435 prevRadius = radius;
1436 radius *= 2.; // Keep expanding the scope until the radius is larger than the entire root cell, then give up (because we have checked every possible cell)
1437 } while ( prevRadius < minDist );
1438 return answer;
1439}
1440
1441
1442void
1443OctreeSpatialLocalizer :: giveNodeClosestToPointWithinOctant(OctantRec &currCell, const FloatArray &gcoords,
1444 double &minDist, Node * &answer)
1445{
1446 double minDist2 = minDist*minDist;
1447 for ( auto &inod : currCell.giveNodeList() ) {
1448 Node *node = domain->giveNode(inod);
1449
1450 double currDist2 = distance_square(gcoords, node->giveCoordinates());
1451
1452 if ( currDist2 < minDist2 ) {
1453 answer = node;
1454 minDist2 = currDist2;
1455 }
1456 }
1457 minDist = sqrt(minDist2);
1458}
1459
1460
1461void
1462OctreeSpatialLocalizer :: giveNodesWithinBox(nodeContainerType &nodeList, OctantRec &currentCell,
1463 const FloatArray &coords, const double radius)
1464{
1465 if ( currentCell.isTerminalOctant() ) {
1466 nodeContainerType &cellNodes = currentCell.giveNodeList();
1467 if ( !cellNodes.empty() ) {
1468 for ( int inod: cellNodes ) {
1469 // loop over cell nodes and check if they meet the criteria
1470 const auto &nodeCoords = domain->giveNode(inod)->giveCoordinates();
1471 // is node within bbox
1472 if ( distance(nodeCoords, coords) <= radius ) {
1473 // if yes, append them into set
1474 nodeList.push_back(inod);
1475 }
1476 }
1477 }
1478 } else {
1479 for ( int i = 0; i <= octreeMask.at(1); i++ ) {
1480 for ( int j = 0; j <= octreeMask.at(2); j++ ) {
1481 for ( int k = 0; k <= octreeMask.at(3); k++ ) {
1482 auto child = currentCell.giveChild(i, j, k);
1483 if ( child ) {
1484 // test if box hits the cell
1485 auto BBStatus = child->testBoundingBox(coords, radius, this->octreeMask);
1486 if ( BBStatus == OctantRec :: BBS_InsideCell || BBStatus == OctantRec :: BBS_ContainsCell ) {
1487 // if yes call this method for such cell
1488 this->giveNodesWithinBox(nodeList, *child, coords, radius);
1489 }
1490 }
1491 }
1492 }
1493 }
1494 }
1495}
1496
1497
1498int
1499OctreeSpatialLocalizer :: giveMaxTreeDepthFrom(OctantRec &root)
1500{
1501 int maxDepth = 0;
1502 for ( int i = 0; i <= octreeMask.at(1); i++ ) {
1503 for ( int j = 0; j <= octreeMask.at(2); j++ ) {
1504 for ( int k = 0; k <= octreeMask.at(3); k++ ) {
1505 auto child = root.giveChild(i, j, k);
1506 if ( child ) {
1507 maxDepth = std::max(maxDepth, this->giveMaxTreeDepthFrom(*child));
1508 }
1509 }
1510 }
1511 }
1512 return maxDepth + 1;
1513}
1514
1515
1516void
1517OctreeSpatialLocalizer :: giveListOfTerminalCellsInBoundingBox(std :: list< OctantRec * > &cellList, const FloatArray &coords,
1518 double radius, double innerRadius, OctantRec &currentCell)
1519{
1520 auto BBStatus = currentCell.testBoundingBox(coords, radius, this->octreeMask);
1521 if ( BBStatus != OctantRec :: BBS_OutsideCell ) {
1522 if ( currentCell.isTerminalOctant() ) {
1523 //if ( currentCell.testBoundingBox(coords, innerRadius, this->octreeMask) == OctantRec :: BBS_OutsideCell ) {
1524 cellList.push_back(&currentCell);
1525 //}
1526 } else {
1527 for ( int i = 0; i <= octreeMask.at(1); i++ ) {
1528 for ( int j = 0; j <= octreeMask.at(2); j++ ) {
1529 for ( int k = 0; k <= octreeMask.at(3); k++ ) {
1530 auto child = currentCell.giveChild(i, j, k);
1531 if ( child ) {
1532 this->giveListOfTerminalCellsInBoundingBox( cellList, coords, radius, innerRadius, *child );
1533 }
1534 }
1535 }
1536 }
1537 }
1538 }
1539}
1540
1541
1542int
1543OctreeSpatialLocalizer :: init(bool force)
1544{
1545 if ( force ) {
1546 int ans;
1547 initialized = false;
1548#ifdef _OPENMP
1549 omp_set_lock(&initLock); // if not initialized yet; one thread can proceed with init; others have to wait until init completed
1550 if ( initialized) {
1551 omp_unset_lock(&initLock);
1552 return 0;
1553 }
1554#endif
1555 rootCell = nullptr;
1558 ans = this->buildOctreeDataStructure();
1559 //this->initElementIPDataStructure();
1560 this->initialized=true;
1561#ifdef _OPENMP
1562 omp_unset_lock(&initLock);
1563#endif
1564 return ans;
1565 } else {
1566 if ( !this->initialized) {
1567 int ans;
1568#ifdef _OPENMP
1569 omp_set_lock(&initLock); // if not initialized yet; one thread can proceed with init; others have to wait until init completed
1570 if ( initialized) {
1571 omp_unset_lock(&initLock);
1572 return 0;
1573 }
1574#endif
1575 OOFEM_LOG_INFO("OctreeLocalizer: init\n");
1576 ans = this->buildOctreeDataStructure();
1577 //this->initElementIPDataStructure();
1578 this->initialized = true;
1579#ifdef _OPENMP
1580 omp_unset_lock(&initLock);
1581#endif
1582 return ans;
1583 } else {
1584 return 0;
1585 }
1586 }
1587}
1588} // end namespace oofem
const FloatArray & giveCoordinates() const
Definition dofmanager.h:390
Node * giveNode(int i) const
Definition element.h:629
virtual int computeGlobalCoordinates(FloatArray &answer, const FloatArray &lcoords)
Definition element.C:1225
virtual int giveNumberOfNodes() const
Definition element.h:703
int giveNumberOfIntegrationRules()
Definition element.h:894
elementParallelMode giveParallelMode() const
Definition element.h:1139
virtual IntegrationRule * giveDefaultIntegrationRulePtr()
Definition element.h:886
int giveRegionNumber()
Definition element.C:546
virtual Interface * giveInterface(InterfaceType t)
Definition femcmpnn.h:181
int giveNumber() const
Definition femcmpnn.h:104
double & at(Index i)
Definition floatarray.h:202
Index giveSize() const
Returns the size of receiver.
Definition floatarray.h:261
virtual void printYourself() const
Definition floatarray.C:762
void add(const FloatArray &src)
Definition floatarray.C:218
void times(double s)
Definition floatarray.C:834
bool insertSortedOnce(int value, int allocChunk=0)
Definition intarray.C:309
int findSorted(int value) const
Definition intarray.C:293
bool isEmpty() const
Definition intarray.h:217
int findFirstIndexOf(int value) const
Definition intarray.C:280
int & at(std::size_t i)
Definition intarray.h:104
std ::list< int > nodeList
Octant node list.
std ::list< int > & giveNodeList()
OctantRec * giveChild(int xi, int yi, int zi)
void addNode(int nodeNum)
FloatArray origin
Octant origin coordinates (lower corner).
std ::vector< std ::list< int > > elementList
Element list of all elements close to the cell.
std::unique_ptr< OctantRec > child[2][2][2]
Link to octant children.
OctantRec * giveParent()
void divideLocally(int level, const IntArray &octantMask)
std ::list< int > & giveElementList(int region)
double halfWidth
Octant size.
OctantRec * parent
Link to parent cell record.
OctantRec(OctantRec *parent, FloatArray origin, double halfWidth)
Constructor.
int depth
Tree depth.
BoundingBoxStatus testBoundingBox(const FloatArray &coords, double radius, const IntArray &mask)
void addElementIP(int elementNum)
IntArray elementIPList
Element list, containing all elements having IP in cell.
IntArray & giveIPElementList()
ChildStatus giveChildContainingPoint(OctantRec *&child, const FloatArray &coords, const IntArray &mask)
int init(bool force=false) override
OctantRec * findTerminalContaining(OctantRec &startCell, const FloatArray &coords)
bool elementIPListsInitialized
Flag indicating elementIP tables are initialized.
int giveMaxTreeDepthFrom(OctantRec &root)
void insertIPElementIntoOctree(OctantRec &rootCell, int elemNum, const FloatArray &coords)
void giveElementClosestToPointWithinOctant(OctantRec &currCell, const FloatArray &gcoords, double &minDist, FloatArray &lcoords, FloatArray &closest, Element *&answer, int region)
void giveAllElementsWithIpWithinBox(elementContainerType &elemSet, const FloatArray &coords, const double radius) override
void giveNodesWithinBox(nodeContainerType &nodeList, OctantRec &currentCell, const FloatArray &coords, const double radius)
std::unique_ptr< OctantRec > rootCell
Root cell of octree.
Element * giveElementContainingPoint(const FloatArray &coords, const IntArray *regionList=nullptr) override
void insertNodeIntoOctree(OctantRec &rootCell, int nodeNum, const FloatArray &coords)
IntArray octreeMask
Octree degenerate mask.
void giveClosestIPWithinOctant(OctantRec &currentCell, const FloatArray &coords, int region, double &dist, GaussPoint *&answer, bool iCohesiveZoneGP)
void initElementDataStructure(int region=0)
void insertElementIntoOctree(OctantRec &rootCell, int region, int elemNum, const FloatArray &b0, const FloatArray &b1)
void giveAllElementsWithIpWithinBox_EvenIfEmpty(elementContainerType &elemSet, const FloatArray &coords, const double radius) override
void giveElementsWithIPWithinBox(elementContainerType &elemSet, OctantRec &currentCell, const FloatArray &coords, const double radius, bool iCohesiveZoneGP=false)
void giveNodeClosestToPointWithinOctant(OctantRec &cell, const FloatArray &gcoords, double &minDist, Node *&answer)
void insertElementsUsingNodalConnectivitiesIntoOctree(OctantRec &rootCell)
void giveListOfTerminalCellsInBoundingBox(std ::list< OctantRec * > &cellList, const FloatArray &coords, const double radius, double innerRadius, OctantRec &currentCell)
bool hasElement(int elem) const
Return True if given element is contained.
Definition set.C:245
virtual double SpatialLocalizerI_giveClosestPoint(FloatArray &lcoords, FloatArray &closest, const FloatArray &gcoords)
SpatialLocalizer(Domain *d)
Constructor.
Domain * domain
Link to domain object.
Domain * giveDomain()
Returns the domain that localizer acts on.
std ::list< int > nodeContainerType
Typedefs to introduce the container type for nodal numbers, returned by some services.
IntArray elementContainerType
Typedefs to introduce the container type for element numbers, returned by some services.
double getUtime()
Returns total user time elapsed in seconds.
Definition timer.C:105
void startTimer()
Definition timer.C:69
void stopTimer()
Definition timer.C:77
std ::vector< std ::unique_ptr< IntegrationRule > > mpCZIntegrationRules
#define OOFEM_ERROR(...)
Definition error.h:79
#define OOFEM_LOG_INFO(...)
Definition logger.h:143
#define OOFEM_LOG_DEBUG(...)
Definition logger.h:144
FloatArrayF< N > min(const FloatArrayF< N > &a, const FloatArrayF< N > &b)
@ Element_remote
Element in active domain is only mirror of some remote element.
Definition element.h:89
FloatArrayF< N > max(const FloatArrayF< N > &a, const FloatArrayF< N > &b)
@ SpatialLocalizerInterfaceType
double distance(const FloatArray &x, const FloatArray &y)
double distance_square(const FloatArray &x, const FloatArray &y)
static FloatArray Vec3(const double &a, const double &b, const double &c)
Definition floatarray.h:607
#define OCTREE_MAX_DEPTH
Max octree depth.
#define OCTREE_MAX_NODES_LIMIT
Max desired number of nodes per octant.

This page is part of the OOFEM-3.0 documentation. Copyright Copyright (C) 1994-2025 Borek Patzak Bořek Patzák
Project e-mail: oofem@fsv.cvut.cz
Generated at for OOFEM by doxygen 1.15.0 written by Dimitri van Heesch, © 1997-2011