binaryTree.C
Go to the documentation of this file.
1/*---------------------------------------------------------------------------*\
2 ========= |
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4 \\ / O peration |
5 \\ / A nd | www.openfoam.com
6 \\/ M anipulation |
7-------------------------------------------------------------------------------
8 Copyright (C) 2016-2017 OpenFOAM Foundation
9 Copyright (C) 2019-2020 OpenCFD Ltd.
10-------------------------------------------------------------------------------
11License
12 This file is part of OpenFOAM.
13
14 OpenFOAM is free software: you can redistribute it and/or modify it
15 under the terms of the GNU General Public License as published by
16 the Free Software Foundation, either version 3 of the License, or
17 (at your option) any later version.
18
19 OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22 for more details.
23
24 You should have received a copy of the GNU General Public License
25 along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26
27\*---------------------------------------------------------------------------*/
28
29#include "binaryTree.H"
30#include "SortableList.H"
31#include "demandDrivenData.H"
32
33// * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
34
35template<class CompType, class ThermoType>
37(
38 chemPoint*& phi0,
39 node*& newNode
40)
41{
42 if (phi0 == phi0->node()->leafRight())
43 {
44 // phi0 is on the right
45 phi0->node()->leafRight() = nullptr;
46 phi0->node()->nodeRight() = newNode;
47 return;
48 }
49 else if (phi0 == phi0->node()->leafLeft())
50 {
51 // phi0 is on the left
52 phi0->node()->leafLeft() = nullptr;
53 phi0->node()->nodeLeft() = newNode;
54 return;
55
56 }
57
58 // if we reach this point, there is an issue with the addressing
60 << "trying to insert a node with a wrong pointer to a chemPoint"
61 << exit(FatalError);
62}
63
64
65template<class CompType, class ThermoType>
67(
68 const scalarField& phiq,
69 node* y,
70 chemPoint* x
71)
72{
73 if ((n2ndSearch_ < max2ndSearch_) && (y != nullptr))
74 {
75 scalar vPhi = 0;
76 const scalarField& v = y->v();
77 const scalar a = y->a();
78
79 // compute v*phi
80 for (label i=0; i<phiq.size(); ++i)
81 {
82 vPhi += phiq[i]*v[i];
83 }
84 if (vPhi <= a)
85 {
86 // on the left side of the node
87 if (y->nodeLeft() == nullptr)
88 {
89 // left is a chemPoint
90 ++n2ndSearch_;
91 if (y->leafLeft()->inEOA(phiq))
92 {
93 x = y->leafLeft();
94 return true;
95 }
96 }
97 else
98 {
99 // the left side is a node
100 if (inSubTree(phiq, y->nodeLeft(), x))
101 {
102 return true;
103 }
104 }
105
106 // not on the left side, try the right side
107 if ((n2ndSearch_ < max2ndSearch_) && y->nodeRight() == nullptr)
108 {
109 ++n2ndSearch_;
110 // we reach the end of the subTree we can return the result
111 if (y->leafRight()->inEOA(phiq))
112 {
113 x = y->leafRight();
114 return true;
115 }
116 else
117 {
118 x = nullptr;
119 return false;
120 }
121 }
122
123 // Test for n2ndSearch is done in the call of inSubTree
124 return inSubTree(phiq, y->nodeRight(), x);
125 }
126 else
127 {
128 // on right side (symmetric of above)
129
130 if (y->nodeRight() == nullptr)
131 {
132 ++n2ndSearch_;
133 if (y->leafRight()->inEOA(phiq))
134 {
135 return true;
136 }
137 }
138 else // the right side is a node
139 {
140 if (inSubTree(phiq, y->nodeRight(), x))
141 {
142 x = y->leafRight();
143 return true;
144 }
145 }
146
147 // if we reach this point, the retrieve has
148 // failed on the right side, explore the left side
149 if ((n2ndSearch_ < max2ndSearch_) && y->nodeLeft() == nullptr)
150 {
151 ++n2ndSearch_;
152 if (y->leafLeft()->inEOA(phiq))
153 {
154 x = y->leafLeft();
155 return true;
156 }
157 else
158 {
159 x = nullptr;
160 return false;
161 }
162 }
163
164 return inSubTree(phiq, y->nodeLeft(), x);
165 }
166 }
167
168 return false;
169}
170
171
172template<class CompType, class ThermoType>
174{
175 if (subTreeRoot != nullptr)
176 {
177 deleteDemandDrivenData(subTreeRoot->leafLeft());
178 deleteDemandDrivenData(subTreeRoot->leafRight());
179 deleteSubTree(subTreeRoot->nodeLeft());
180 deleteSubTree(subTreeRoot->nodeRight());
181 deleteDemandDrivenData(subTreeRoot);
182 }
183}
184
185
186template<class CompType, class ThermoType>
188{
189 if (v != nullptr)
190 {
191 // u is root_
192 if (u->parent() == nullptr)
193 {
194 root_ = v;
195 }
196 // u is on the left of its parent
197 else if (u == u->parent()->nodeLeft())
198 {
199 u->parent()->nodeLeft() = v;
200 }
201 // u is ont the right of its parent
202 else if (u == u->parent()->nodeRight())
203 {
204 u->parent()->nodeRight() = v;
205 }
206 else
207 {
209 << "wrong addressing of the initial node"
210 << exit(FatalError);
211 }
212 v->parent() = u->parent();
213 }
214 else
215 {
217 << "trying to transplant a nullptr node"
218 << exit(FatalError);
219 }
220}
221
222
223template<class CompType, class ThermoType>
226{
227 if (y->parent() != nullptr)
228 {
229 if (y == y->parent()->nodeLeft())// y is on the left, return right side
230 {
231 // might return nullptr if the right side is a node
232 return y->parent()->leafRight();
233 }
234 else if (y == y->parent()->nodeRight())
235 {
236 return y->parent()->leafLeft();
237 }
238
240 << "wrong addressing of the initial node"
241 << exit(FatalError);
242 }
243
244 // the binaryNode is root_ and has no sibling
245 return nullptr;
246}
247
248
249template<class CompType, class ThermoType>
252{
253 if (size_ > 1)
254 {
255 if (x == x->node()->leafLeft())
256 {
257 // x is on the left, return right side
258 // might return nullptr if the right side is a node
259 return x->node()->leafRight();
260 }
261 else if (x == x->node()->leafRight())
262 {
263 // x is on the right, return left side
264 return x->node()->leafLeft();
265 }
266
268 << "wrong addressing of the initial leaf"
269 << exit(FatalError);
270 }
271
272 // there is only one leaf attached to the root_, no sibling
273 return nullptr;
274}
275
276
277template<class CompType, class ThermoType>
280{
281 if (y->parent() != nullptr)
282 {
283 if (y == y->parent()->nodeLeft())
284 {
285 // y is on the left, return right side
286 return y->parent()->nodeRight();
287 }
288 else if (y == y->parent()->nodeRight())
289 {
290 return y->parent()->nodeLeft();
291 }
292
294 << "wrong addressing of the initial node"
295 << exit(FatalError);
296 }
297
298 return nullptr;
299}
300
301
302template<class CompType, class ThermoType>
305{
306 if (size_ > 1)
307 {
308 if (x == x->node()->leafLeft())
309 {
310 // x is on the left, return right side
311 return x->node()->nodeRight();
312 }
313 else if (x == x->node()->leafRight())
314 {
315 // x is on the right, return left side
316 return x->node()->nodeLeft();
317 }
318
320 << "wrong addressing of the initial leaf"
321 << exit(FatalError);
322 }
323
324 return nullptr;
325}
326
327
328template<class CompType, class ThermoType>
330{
331 if (subTreeRoot != nullptr)
332 {
333 deleteAllNode(subTreeRoot->nodeLeft());
334 deleteAllNode(subTreeRoot->nodeRight());
335 deleteDemandDrivenData(subTreeRoot);
336 }
337}
338
339
340// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
341
342template<class CompType, class ThermoType>
344(
346 dictionary coeffsDict
347)
348:
349 chemistry_(chemistry),
350 root_(nullptr),
351 maxNLeafs_(coeffsDict.get<label>("maxNLeafs")),
352 size_(0),
353 n2ndSearch_(0),
354 max2ndSearch_(coeffsDict.getOrDefault("max2ndSearch", 0)),
355 coeffsDict_(coeffsDict)
356{}
357
358// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
359
360template<class CompType, class ThermoType>
362{
363 // when we reach the leaf, we return 0
364 if (subTreeRoot == nullptr)
365 {
366 return 0;
367 }
368 else
369 {
370 return
371 1
372 + max
373 (
374 depth(subTreeRoot->nodeLeft()),
375 depth(subTreeRoot->nodeRight())
376 );
377 }
378}
379
380
381template<class CompType, class ThermoType>
383(
384 const scalarField& phiq,
385 const scalarField& Rphiq,
386 const scalarSquareMatrix& A,
387 const scalarField& scaleFactor,
388 const scalar& epsTol,
389 const label nCols,
390 chemPoint*& phi0
391)
392{
393 if (size_ == 0) // no points are stored
394 {
395 // create an empty binary node and point root_ to it
396 root_ = new node();
397 // create the new chemPoint which holds the composition point
398 // phiq and the data to initialize the EOA
399 chemPoint* newChemPoint =
400 new chemPoint
401 (
402 chemistry_,
403 phiq,
404 Rphiq,
405 A,
406 scaleFactor,
407 epsTol,
408 nCols,
409 coeffsDict_,
410 root_
411 );
412 root_->leafLeft() = newChemPoint;
413 }
414 else // at least one point stored
415 {
416 // no reference chemPoint, a BT search is required
417 if (phi0 == nullptr)
418 {
419 binaryTreeSearch(phiq, root_, phi0);
420 }
421 // access to the parent node of the chemPoint
422 node* parentNode = phi0->node();
423
424 // create the new chemPoint which holds the composition point
425 // phiq and the data to initialize the EOA
426 chemPoint* newChemPoint =
427 new chemPoint
428 (
429 chemistry_,
430 phiq,
431 Rphiq,
432 A,
433 scaleFactor,
434 epsTol,
435 nCols,
436 coeffsDict_
437 );
438 // insert new node on the parent node in the position of the
439 // previously stored leaf (phi0)
440 // the new node contains phi0 on the left and phiq on the right
441 // the hyper plane is computed in the binaryNode constructor
442 node* newNode;
443 if (size_ > 1)
444 {
445 newNode = new node(phi0, newChemPoint, parentNode);
446 // make the parent of phi0 point to the newly created node
447 insertNode(phi0, newNode);
448 }
449 else // size_ == 1 (because not equal to 0)
450 {
451 // when size is 1, the binaryNode is without hyperplane
453 newNode = new node(phi0, newChemPoint, nullptr);
454 root_ = newNode;
455 }
456
457 phi0->node() = newNode;
458 newChemPoint->node()=newNode;
459 }
460
461 ++size_;
462}
463
464
465template<class CompType, class ThermoType>
467(
468 const scalarField& phiq,
469 node* node,
470 chemPoint*& nearest
471)
472{
473 if (size_ > 1)
474 {
475 scalar vPhi=0.0;
476 const scalarField& v = node->v();
477 const scalar& a = node->a();
478 // compute v*phi
479 for (label i=0; i<phiq.size(); ++i)
480 {
481 vPhi += phiq[i]*v[i];
482 }
483
484
485 if (vPhi > a) // on right side (side of the newly added point)
486 {
487 if (node->nodeRight() != nullptr)
488 {
489 binaryTreeSearch(phiq, node->nodeRight(), nearest);
490 }
491 else // the terminal node is reached, store leaf on the right
492 {
493 nearest = node->leafRight();
494 return;
495 }
496 }
497 else // on left side (side of the previously stored point)
498 {
499 if (node->nodeLeft() != nullptr)
500 {
501 binaryTreeSearch(phiq, node->nodeLeft(), nearest);
502 }
503 else // the terminal node is reached, return element on right
504 {
505 nearest = node->leafLeft();
506 return;
507 }
508 }
509 }
510 // only one point stored (left element of the root)
511 else if (size_ == 1)
512 {
513 nearest = root_->leafLeft();
514 }
515 else // no point stored
516 {
517 nearest = nullptr;
518 }
519}
520
521
522template<class CompType, class ThermoType>
524(
525 const scalarField& phiq,
526 chemPoint*& x
527)
528{
529 // initialize n2ndSearch_
530 n2ndSearch_ = 0;
531 if ((n2ndSearch_ < max2ndSearch_) && (size_ > 1))
532 {
533 chemPoint* xS = chemPSibling(x);
534 if (xS != nullptr)
535 {
536 n2ndSearch_++;
537 if (xS->inEOA(phiq))
538 {
539 x = xS;
540 return true;
541 }
542 }
543 else if (inSubTree(phiq, nodeSibling(x), x))
544 {
545 return true;
546 }
547
548 // if we reach this point, no leafs were found at this depth or lower
549 // we move upward in the tree
550 node* y = x->node();
551 while ((y->parent()!= nullptr) && (n2ndSearch_ < max2ndSearch_))
552 {
553 xS = chemPSibling(y);
554 if (xS != nullptr)
555 {
556 n2ndSearch_++;
557 if (xS->inEOA(phiq))
558 {
559 x=xS;
560 return true;
561 }
562 }
563 else if (inSubTree(phiq, nodeSibling(y),x))
564 {
565 return true;
566 }
567 y = y->parent();
568 }
569
570 // if we reach this point it is either because
571 // we did not find another covering EOA in the entire tree or
572 // we reach the maximum number of secondary search
573 return false;
574 }
575
576 return false;
577}
578
579
580template<class CompType, class ThermoType>
582{
583 if (size_ == 1) // only one point is stored
584 {
587 }
588 else if (size_ > 1)
589 {
590 node* z = phi0->node();
591 node* x;
592 chemPoint* siblingPhi0 = chemPSibling(phi0);
593
594 if (siblingPhi0 != nullptr)// the sibling of phi0 is a chemPoint
595 {
596 // z was root (only two chemPoints in the tree)
597 if (z->parent() == nullptr)
598 {
599 root_ = new node();
600 root_->leafLeft()=siblingPhi0;
601 siblingPhi0->node()=root_;
602 }
603 else if (z == z->parent()->nodeLeft())
604 {
605 z->parent()->leafLeft() = siblingPhi0;
606 z->parent()->nodeLeft() = nullptr;
607 siblingPhi0->node() = z->parent();
608 }
609 else if (z == z->parent()->nodeRight())
610 {
611 z->parent()->leafRight() = siblingPhi0;
612 z->parent()->nodeRight() = nullptr;
613 siblingPhi0->node() = z->parent();
614 }
615 else
616 {
618 << "wrong addressing of the initial leaf"
619 << exit(FatalError);
620 }
621 }
622 else
623 {
624 x = nodeSibling(phi0);
625 if (x !=nullptr)
626 {
627 transplant(z, x);
628 }
629 else
630 {
632 << "inconsistent structure of the tree, no leaf and no node"
633 << exit(FatalError);
634 }
635 }
638 }
639
640 --size_;
641}
642
643
644template<class CompType, class ThermoType>
646{
647 //1) walk through the entire tree by starting with the tree's most left
648 // chemPoint
649 chemPoint* x = treeMin();
650 List<chemPoint*> chemPoints(size_);
651 label chemPointi = 0;
652
653 //2) compute the mean composition
654 label n = x->phi().size();
655 scalarField mean(n, Zero);
656 while (x != nullptr)
657 {
658 const scalarField& phij = x->phi();
659 mean += phij;
660 chemPoints[chemPointi++] = x;
661 x = treeSuccessor(x);
662 }
663 mean /= scalar(size_);
664
665 //3) compute the variance for each space direction
666 List<scalar> variance(n, Zero);
667 forAll(chemPoints, j)
668 {
669 const scalarField& phij = chemPoints[j]->phi();
670 forAll(variance, vi)
671 {
672 variance[vi] += sqr(phij[vi] - mean[vi]);
673 }
674 }
675
676 //4) analyze what is the direction of the maximal variance
677 scalar maxVariance(-1.0);
678 label maxDir(-1);
679 forAll(variance, vi)
680 {
681 if (maxVariance < variance[vi])
682 {
683 maxVariance = variance[vi];
684 maxDir = vi;
685 }
686 }
687 // maxDir indicates the direction of maximum variance
688 // we create the new root node by taking the two extreme points
689 // in this direction if these extreme points were not deleted in the
690 // cleaning that come before the balance function they are still important
691 // and the tree should therefore take them into account
692 SortableList<scalar> phiMaxDir(chemPoints.size(), Zero);
693 forAll(chemPoints, j)
694 {
695 phiMaxDir[j] = chemPoints[j]->phi()[maxDir];
696 }
697
698 phiMaxDir.sort();
699 // delete reference to all node since the tree is reshaped
700 deleteAllNode();
701 root_ = nullptr;
702
703 // add the node for the two extremum
704 node* newNode = new node
705 (
706 chemPoints[phiMaxDir.indices()[0]],
707 chemPoints[phiMaxDir.indices()[phiMaxDir.size()-1]],
708 nullptr
709 );
710 root_ = newNode;
711
712 chemPoints[phiMaxDir.indices()[0]]->node() = newNode;
713 chemPoints[phiMaxDir.indices()[phiMaxDir.size()-1]]->node() = newNode;
714
715 for (label cpi=1; cpi<chemPoints.size()-1; ++cpi)
716 {
717 chemPoint* phi0;
718 binaryTreeSearch
719 (
720 chemPoints[phiMaxDir.indices()[cpi]]->phi(),
721 root_,
722 phi0
723 );
724 // add the chemPoint
725 node* nodeToAdd =
726 new node(phi0, chemPoints[phiMaxDir.indices()[cpi]], phi0->node());
727 // make the parent of phi0 point to the newly created node
728 insertNode(phi0, nodeToAdd);
729 phi0->node() = nodeToAdd;
730 chemPoints[phiMaxDir.indices()[cpi]]->node() = nodeToAdd;
731 }
732}
733
734
735template<class CompType, class ThermoType>
738{
739 if (subTreeRoot != nullptr)
740 {
741 while (subTreeRoot->nodeLeft() != nullptr)
742 {
743 subTreeRoot = subTreeRoot->nodeLeft();
744 }
745 return subTreeRoot->leafLeft();
746 }
747
748 return nullptr;
749}
750
751
752template<class CompType, class ThermoType>
755{
756 if (size_ > 1)
757 {
758 if (x == x->node()->leafLeft())
759 {
760 if (x->node()->nodeRight() == nullptr)
761 {
762 return x->node()->leafRight();
763 }
764 else
765 {
766 return treeMin(x->node()->nodeRight());
767 }
768 }
769 else if (x == x->node()->leafRight())
770 {
771 node* y = x->node();
772 while ((y->parent() != nullptr))
773 {
774 if (y == y->parent()->nodeLeft())
775 {
776 if (y->parent()->nodeRight() == nullptr)
777 {
778 return y->parent()->leafRight();
779 }
780 else
781 {
782 return treeMin(y->parent()->nodeRight());
783 }
784 }
785 y = y->parent();
786 }
787
788 // when we reach this point, y points to the root and
789 // never entered in the if loop (coming from the right)
790 // so we are at the tree maximum and there is no successor
791 return nullptr;
792 }
793
795 << "inconsistent structure of the tree, no leaf and no node"
796 << exit(FatalError);
797 }
798
799 return nullptr;
800}
801
802
803template<class CompType, class ThermoType>
805{
806 // Recursively delete the element in the subTree
807 deleteSubTree();
808
809 // Reset root node (should already be nullptr)
810 root_ = nullptr;
811
812 // Reset size_
813 size_ = 0;
814}
815
816
817template<class CompType, class ThermoType>
819{
820 return size_ >= maxNLeafs_;
821}
822
823
824template<class CompType, class ThermoType>
826{
827 // Has to go along each chemPoint of the tree
828 if (size_ > 0)
829 {
830 // First finds the first leaf
831 chemPoint* chemPoint0 = treeMin();
832 chemPoint0->resetNumRetrieve();
833
834 // Then go to each successor
835 chemPoint* nextchemPoint = treeSuccessor(chemPoint0);
836 while (nextchemPoint != nullptr)
837 {
838 nextchemPoint->resetNumRetrieve();
839 nextchemPoint = treeSuccessor(nextchemPoint);
840 }
841 }
842}
843
844
845// ************************************************************************* //
static const Foam::dimensionedScalar A("", Foam::dimPressure, 611.21)
scalar y
label n
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: List.H:77
A list that is sorted upon construction or when explicitly requested with the sort() method.
Definition: SortableList.H:63
const labelList & indices() const noexcept
Return the list of sorted indices. Updated every sort.
Definition: SortableList.H:108
void sort()
Forward (stable) sort the list (if changed after construction).
Definition: SortableList.C:124
Extends StandardChemistryModel by adding the TDAC method.
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:114
Node of the binary tree.
Definition: binaryNode.H:52
binaryNode< CompType, ThermoType > *& nodeLeft()
Definition: binaryNode.H:154
binaryNode< CompType, ThermoType > *& parent()
Definition: binaryNode.H:164
chemPointISAT< CompType, ThermoType > *& leafRight()
Definition: binaryNode.H:149
binaryNode< CompType, ThermoType > *& nodeRight()
Definition: binaryNode.H:159
const scalarField & v() const
Topology.
Definition: binaryNode.H:171
chemPointISAT< CompType, ThermoType > *& leafLeft()
Access.
Definition: binaryNode.H:144
const scalar & a() const
Definition: binaryNode.H:181
Data storage of the chemistryOnLineLibrary according to a binary tree structure.
Definition: binaryTree.H:60
void resetNumRetrieve()
Definition: binaryTree.C:825
void deleteLeaf(chemPoint *&phi0)
Delete a leaf from the binary tree and reshape the binary tree for.
Definition: binaryTree.C:581
bool isFull()
ListFull.
Definition: binaryTree.C:818
void binaryTreeSearch(const scalarField &phiq, node *node, chemPoint *&nearest)
Definition: binaryTree.C:467
void insertNewLeaf(const scalarField &phiq, const scalarField &Rphiq, const scalarSquareMatrix &A, const scalarField &scaleFactor, const scalar &epsTol, const label nCols, chemPoint *&phi0)
Definition: binaryTree.C:383
void deleteAllNode()
Definition: binaryTree.H:221
chemPoint * treeMin()
Definition: binaryTree.H:228
chemPoint * treeSuccessor(chemPoint *x)
Definition: binaryTree.C:754
void clear()
Removes every entries of the tree and delete the associated objects.
Definition: binaryTree.C:804
void balance()
Cheap balance function.
Definition: binaryTree.C:645
bool secondaryBTSearch(const scalarField &phiq, chemPoint *&x)
Definition: binaryTree.C:524
Leaf of the binary tree. The chemPoint stores the composition 'phi', the mapping of this composition ...
void resetNumRetrieve()
Resets the number of retrieves at each time step.
bool inEOA(const scalarField &phiq)
To RETRIEVE the mapping from the stored chemPoint phi, the query.
binaryNode< CompType, ThermoType > *& node()
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:126
BasicChemistryModel< psiReactionThermo > & chemistry
Template functions to aid in the implementation of demand driven data.
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:453
const dimensionedScalar phi0
Magnetic flux quantum: default SI units: [Wb].
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
dimensionedSymmTensor sqr(const dimensionedVector &dv)
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:131
error FatalError
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
void deleteDemandDrivenData(DataPtr &dataPtr)
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:333