binaryTree.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2016-2024 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "binaryTree.H"
27 #include "SortableList.H"
28 
29 // * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * * //
30 
31 bool Foam::binaryTree::inSubTree
32 (
33  const scalarField& phiq,
34  binaryNode* y,
35  chemPointISAT* x
36 )
37 {
38  if ((n2ndSearch_ < max2ndSearch_) && (y!=nullptr))
39  {
40  scalar vPhi = 0;
41  const scalarField& v = y->v();
42  const scalar a = y->a();
43  // compute v*phi
44  for (label i=0; i<phiq.size(); i++)
45  {
46  vPhi += phiq[i]*v[i];
47  }
48  if (vPhi <= a)// on the left side of the node
49  {
50  if (y->nodeLeft() == nullptr)// left is a chemPoint
51  {
52  n2ndSearch_++;
53  if (y->leafLeft()->inEOA(phiq))
54  {
55  x = y->leafLeft();
56  return true;
57  }
58  }
59  else // the left side is a node
60  {
61  if (inSubTree(phiq, y->nodeLeft(),x))
62  {
63  return true;
64  }
65  }
66 
67  // not on the left side, try the right side
68  if ((n2ndSearch_ < max2ndSearch_) && y->nodeRight() == nullptr)
69  {
70  n2ndSearch_++;
71  // we reach the end of the subTree we can return the result
72  if (y->leafRight()->inEOA(phiq))
73  {
74  x = y->leafRight();
75  return true;
76  }
77  else
78  {
79  x = nullptr;
80  return false;
81  }
82  }
83  else // test for n2ndSearch is done in the call of inSubTree
84  {
85  return inSubTree(phiq, y->nodeRight(),x);
86  }
87  }
88  else // on right side (symmetric of above)
89  {
90  if (y->nodeRight() == nullptr)
91  {
92  n2ndSearch_++;
93  if (y->leafRight()->inEOA(phiq))
94  {
95  return true;
96  }
97  }
98  else // the right side is a node
99  {
100  if (inSubTree(phiq, y->nodeRight(),x))
101  {
102  x = y->leafRight();
103  return true;
104  }
105  }
106  // if we reach this point, the retrieve has
107  // failed on the right side, explore the left side
108  if ((n2ndSearch_ < max2ndSearch_) && y->nodeLeft() == nullptr)
109  {
110  n2ndSearch_++;
111  if (y->leafLeft()->inEOA(phiq))
112  {
113  x = y->leafLeft();
114  return true;
115  }
116  else
117  {
118  x = nullptr;
119  return false;
120  }
121  }
122  else
123  {
124  return inSubTree(phiq, y->nodeLeft(),x);
125  }
126  }
127  }
128  else
129  {
130  return false;
131  }
132 }
133 
134 
135 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
136 
138 (
140  const dictionary& coeffDict
141 )
142 :
143  table_(table),
144  root_(nullptr),
145  maxNLeafs_(coeffDict.lookup<label>("maxNLeafs")),
146  size_(0),
147  n2ndSearch_(0),
148  max2ndSearch_(coeffDict.lookupOrDefault("max2ndSearch",0)),
149  maxNumNewDim_(coeffDict.lookupOrDefault("maxNumNewDim",0)),
150  printProportion_(coeffDict.lookupOrDefault("printProportion",false))
151 {}
152 
153 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
154 
156 (
157  const scalarField& phiq,
158  const scalarField& Rphiq,
159  const scalarSquareMatrix& A,
160  const scalarField& scaleFactor,
161  const scalar& epsTol,
162  const label nCols,
163  const label nActive,
165 )
166 {
167  if (size_ == 0) // no points are stored
168  {
169  // create an empty binary node and point root_ to it
170  root_ = new binaryNode();
171  // create the new chemPoint which holds the composition point
172  // phiq and the data to initialise the EOA
173  chemPointISAT* newChemPoint =
174  new chemPointISAT
175  (
176  table_,
177  phiq,
178  Rphiq,
179  A,
180  scaleFactor,
181  epsTol,
182  nCols,
183  nActive,
184  maxNumNewDim_,
185  printProportion_,
186  root_
187  );
188  root_->leafLeft() = newChemPoint;
189  }
190  else // at least one point stored
191  {
192  // no reference chemPoint, a BT search is required
193  if (phi0 == nullptr)
194  {
195  binaryTreeSearch(phiq, root_,phi0);
196  }
197  // access to the parent node of the chemPoint
198  binaryNode* parentNode = phi0->node();
199 
200  // create the new chemPoint which holds the composition point
201  // phiq and the data to initialise the EOA
202  chemPointISAT* newChemPoint =
203  new chemPointISAT
204  (
205  table_,
206  phiq,
207  Rphiq,
208  A,
209  scaleFactor,
210  epsTol,
211  nCols,
212  nActive,
213  maxNumNewDim_,
214  printProportion_
215  );
216  // insert new node on the parent node in the position of the
217  // previously stored leaf (phi0)
218  // the new node contains phi0 on the left and phiq on the right
219  // the hyper plane is computed in the binaryNode constructor
220  binaryNode* newNode;
221  if (size_>1)
222  {
223  newNode = new binaryNode(phi0, newChemPoint, parentNode);
224  // make the parent of phi0 point to the newly created node
225  insertNode(phi0, newNode);
226  }
227  else // size_ == 1 (because not equal to 0)
228  {
229  // when size is 1, the binaryNode is without hyperplane
230  deleteDemandDrivenData(root_);
231  newNode = new binaryNode(phi0, newChemPoint, nullptr);
232  root_ = newNode;
233  }
234 
235  phi0->node() = newNode;
236  newChemPoint->node()=newNode;
237  }
238  size_++;
239 }
240 
241 
243 (
244  const scalarField& phiq,
245  chemPointISAT*& x
246 )
247 {
248  // initialise n2ndSearch_
249  n2ndSearch_ = 0;
250  if ((n2ndSearch_ < max2ndSearch_) && (size_ > 1))
251  {
252  chemPointISAT* xS = chemPSibling(x);
253  if (xS != nullptr)
254  {
255  n2ndSearch_++;
256  if (xS->inEOA(phiq))
257  {
258  x = xS;
259  return true;
260  }
261  }
262  else if (inSubTree(phiq, nodeSibling(x),x))
263  {
264  return true;
265  }
266  // if we reach this point, no leafs were found at this depth or lower
267  // we move upward in the tree
268  binaryNode* y = x->node();
269  while((y->parent()!= nullptr) && (n2ndSearch_ < max2ndSearch_))
270  {
271  xS = chemPSibling(y);
272  if (xS != nullptr)
273  {
274  n2ndSearch_++;
275  if (xS->inEOA(phiq))
276  {
277  x=xS;
278  return true;
279  }
280  }
281  else if (inSubTree(phiq, nodeSibling(y),x))
282  {
283  return true;
284  }
285  y = y->parent();
286  }
287  // if we reach this point it is either because
288  // we did not find another covering EOA in the entire tree or
289  // we reach the maximum number of secondary search
290  return false;
291  }
292  else
293  {
294  return false;
295  }
296 }
297 
298 
300 {
301  if (size_ == 1) // only one point is stored
302  {
304  deleteDemandDrivenData(root_);
305  }
306  else if (size_ > 1)
307  {
308  binaryNode* z = phi0->node();
309  binaryNode* x;
310  chemPointISAT* siblingPhi0 = chemPSibling(phi0);
311 
312  if (siblingPhi0 != nullptr)// the sibling of phi0 is a chemPoint
313  {
314  // z was root (only two chemPoints in the tree)
315  if (z->parent() == nullptr)
316  {
317  root_ = new binaryNode();
318  root_->leafLeft()=siblingPhi0;
319  siblingPhi0->node()=root_;
320  }
321  else if (z == z->parent()->nodeLeft())
322  {
323  z->parent()->leafLeft() = siblingPhi0;
324  z->parent()->nodeLeft() = nullptr;
325  siblingPhi0->node() = z->parent();
326  }
327  else if (z == z->parent()->nodeRight())
328  {
329  z->parent()->leafRight() = siblingPhi0;
330  z->parent()->nodeRight() = nullptr;
331  siblingPhi0->node() = z->parent();
332  }
333  else
334  {
336  << "wrong addressing of the initial leaf"
337  << exit(FatalError);
338  }
339  }
340  else
341  {
342  x = nodeSibling(phi0);
343  if (x !=nullptr)
344  {
345  transplant(z, x);
346  }
347  else
348  {
350  << "inconsistent structure of the tree, no leaf and no node"
351  << exit(FatalError);
352  }
353  }
356  }
357  size_--;
358 }
359 
360 
362 {
363  //1) walk through the entire tree by starting with the tree's most left
364  // chemPoint
365  chemPointISAT* x = treeMin();
366  List<chemPointISAT*> chemPoints(size_);
367  label chemPointISATi=0;
368  while (x != nullptr)
369  {
370  chemPoints[chemPointISATi++] = x;
371  x = treeSuccessor(x);
372  }
373 
374  //2) compute the mean composition
375  scalarField mean(treeMin()->phi().size(), Zero);
376  forAll(chemPoints, j)
377  {
378  const scalarField& phij = chemPoints[j]->phi();
379  mean += phij;
380  }
381  mean /= size_;
382 
383  //3) compute the variance for each space direction
384  List<scalar> variance(treeMin()->phi().size(), Zero);
385  forAll(chemPoints, j)
386  {
387  const scalarField& phij = chemPoints[j]->phi();
388  forAll(variance, vi)
389  {
390  variance[vi] += sqr(phij[vi]-mean[vi]);
391  }
392  }
393 
394  //4) analyze what is the direction of the maximal variance
395  scalar maxVariance(-1.0);
396  label maxDir(-1);
397  forAll(variance, vi)
398  {
399  if (maxVariance < variance[vi])
400  {
401  maxVariance = variance[vi];
402  maxDir = vi;
403  }
404  }
405 
406  // maxDir indicates the direction of maximum variance
407  // we create the new root node by taking the two extreme points
408  // in this direction if these extreme points were not deleted in the
409  // cleaning that come before the balance function they are still important
410  // and the tree should therefore take them into account
411  SortableList<scalar> phiMaxDir(chemPoints.size(),0.0);
412  forAll(chemPoints, j)
413  {
414  phiMaxDir[j] = chemPoints[j]->phi()[maxDir];
415  }
416 
417  phiMaxDir.sort();
418  // delete reference to all node since the tree is reshaped
419  deleteAllNode();
420  root_ = nullptr;
421 
422  // add the node for the two extremum
423  binaryNode* newNode = new binaryNode
424  (
425  chemPoints[phiMaxDir.indices()[0]],
426  chemPoints[phiMaxDir.indices()[phiMaxDir.size()-1]],
427  nullptr
428  );
429  root_ = newNode;
430 
431  chemPoints[phiMaxDir.indices()[0]]->node() = newNode;
432  chemPoints[phiMaxDir.indices()[phiMaxDir.size()-1]]->node() = newNode;
433 
434  for (label cpi=1; cpi<chemPoints.size()-1; cpi++)
435  {
437  binaryTreeSearch
438  (
439  chemPoints[phiMaxDir.indices()[cpi]]->phi(),
440  root_,
441  phi0
442  );
443  // add the chemPoint
444  binaryNode* nodeToAdd = new binaryNode
445  (
446  phi0,
447  chemPoints[phiMaxDir.indices()[cpi]],
448  phi0->node()
449  );
450 
451  // make the parent of phi0 point to the newly created node
452  insertNode(phi0, nodeToAdd);
453  phi0->node() = nodeToAdd;
454  chemPoints[phiMaxDir.indices()[cpi]]->node() = nodeToAdd;
455  }
456 }
457 
458 
460 {
461  if (size_>1)
462  {
463  if (x == x->node()->leafLeft())
464  {
465  if (x->node()->nodeRight() == nullptr)
466  {
467  return x->node()->leafRight();
468  }
469  else
470  {
471  return treeMin(x->node()->nodeRight());
472  }
473  }
474  else if (x == x->node()->leafRight())
475  {
476  binaryNode* y = x->node();
477  while((y->parent() !=nullptr))
478  {
479  if (y == y->parent()->nodeLeft())
480  {
481  if (y->parent()->nodeRight() == nullptr)
482  {
483  return y->parent()->leafRight();
484  }
485  else
486  {
487  return treeMin(y->parent()->nodeRight());
488  }
489  }
490  y=y->parent();
491  }
492  // when we reach this point, y points to the root and
493  // never entered in the if loop (coming from the right)
494  // so we are at the tree maximum and there is no successor
495  return nullptr;
496  }
497  else
498  {
500  << "inconsistent structure of the tree, no leaf and no node"
501  << exit(FatalError);
502  return nullptr;
503  }
504  }
505 
506  return nullptr;
507 }
508 
509 
510 // ************************************************************************* //
scalar y
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: List.H:91
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
A list that is sorted upon construction or when explicitly requested with the sort() method.
Definition: SortableList.H:55
const labelList & indices() const
Return the list of sorted indices. Updated every sort.
Definition: SortableList.H:96
void sort()
(stable) sort the list (if changed after construction time)
Definition: SortableList.C:112
Node of the binary tree.
Definition: binaryNode.H:49
binaryNode *& parent()
Definition: binaryNode.H:158
chemPointISAT *& leafLeft()
Access.
Definition: binaryNode.H:138
binaryNode *& nodeRight()
Definition: binaryNode.H:153
chemPointISAT *& leafRight()
Definition: binaryNode.H:143
binaryNode *& nodeLeft()
Definition: binaryNode.H:148
void deleteLeaf(chemPointISAT *&phi0)
Delete a leaf from the binary tree and reshape the binary tree for.
Definition: binaryTree.C:299
bool secondaryBTSearch(const scalarField &phiq, chemPointISAT *&x)
Definition: binaryTree.C:243
chemPointISAT * treeSuccessor(chemPointISAT *x)
Definition: binaryTree.C:459
void insertNewLeaf(const scalarField &phiq, const scalarField &Rphiq, const scalarSquareMatrix &A, const scalarField &scaleFactor, const scalar &epsTol, const label nCols, const label nActive, chemPointISAT *&phi0)
Definition: binaryTree.C:156
void balance()
Cheap balance function.
Definition: binaryTree.C:361
binaryTree(chemistryTabulationMethods::ISAT &table, const dictionary &coeffDict)
Constructors.
Definition: binaryTree.C:138
Leaf of the binary tree. The chemPoint stores the composition 'phi', the mapping of this composition ...
bool inEOA(const scalarField &phiq)
To RETRIEVE the mapping from the stored chemPoint phi, the query.
binaryNode *& node()
Implementation of the ISAT (In-situ adaptive tabulation), for chemistry calculation.
Definition: ISAT.H:63
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
volScalarField scalarField(fieldObject, mesh)
const dimensionedScalar phi0
Magnetic flux quantum: default SI units: [Wb].
static const coefficient A("A", dimPressure, 611.21)
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
static const zero Zero
Definition: zero.H:97
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
void deleteDemandDrivenData(DataType *&dataPtr)
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
void sqr(LagrangianPatchField< typename outerProduct< Type, Type >::type > &f, const LagrangianPatchField< Type > &f1)
error FatalError