NURBS3DVolume.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) 2007-2023 PCOpt/NTUA
9  Copyright (C) 2013-2023 FOSS GP
10  Copyright (C) 2019-2021 OpenCFD Ltd.
11 -------------------------------------------------------------------------------
12 License
13  This file is part of OpenFOAM.
14 
15  OpenFOAM is free software: you can redistribute it and/or modify it
16  under the terms of the GNU General Public License as published by
17  the Free Software Foundation, either version 3 of the License, or
18  (at your option) any later version.
19 
20  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
21  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
22  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
23  for more details.
24 
25  You should have received a copy of the GNU General Public License
26  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
27 
28 \*---------------------------------------------------------------------------*/
29 
31 #include "NURBS3DVolume.H"
32 
33 #include "OFstream.H"
34 #include "Time.H"
35 #include "deltaBoundary.H"
36 #include "coupledFvPatch.H"
38 
39 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
40 
41 namespace Foam
42 {
45 }
46 
47 
48 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
49 
51 {
52  // It is considered an error to recompute points in the control boxes
53  if (mapPtr_ || reverseMapPtr_)
54  {
56  << "Attempting to recompute points residing within control boxes"
57  << exit(FatalError);
58  }
59 
60  mapPtr_.reset(new labelList(meshPoints.size(), -1));
61  reverseMapPtr_.reset(new labelList(meshPoints.size(), -1));
62  labelList& map = mapPtr_();
63  labelList& reverseMap = reverseMapPtr_();
64 
65  // Identify points inside morphing boxes
66  scalar lowerX = min(cps_.component(0));
67  scalar upperX = max(cps_.component(0));
68  scalar lowerY = min(cps_.component(1));
69  scalar upperY = max(cps_.component(1));
70  scalar lowerZ = min(cps_.component(2));
71  scalar upperZ = max(cps_.component(2));
72 
73  Info<< "Control Points bounds \n"
74  << "\tX1 : (" << lowerX << " " << upperX << ")\n"
75  << "\tX2 : (" << lowerY << " " << upperY << ")\n"
76  << "\tX3 : (" << lowerZ << " " << upperZ << ")\n" << endl;
77 
78  label count(0);
79  forAll(meshPoints, pI)
80  {
81  const vector& pointI = meshPoints[pI];
82  if
83  (
84  pointI.x() >= lowerX && pointI.x() <= upperX
85  && pointI.y() >= lowerY && pointI.y() <= upperY
86  && pointI.z() >= lowerZ && pointI.z() <= upperZ
87  )
88  {
89  map[count] = pI;
90  reverseMap[pI] = count;
91  ++count;
92  }
93  }
94 
95  // Resize lists
96  map.setSize(count);
97 
99  Info<< "Initially found " << count << " points inside control boxes"
100  << endl;
101 }
102 
103 
105 (
106  const vectorField& points
107 )
108 {
109  scalar timeBef = mesh_.time().elapsedCpuTime();
110 
111  if (parametricCoordinatesPtr_)
112  {
114  << "Attempting to recompute parametric coordinates"
115  << exit(FatalError);
116  }
117 
118  labelList& map = mapPtr_();
119  labelList& reverseMap = reverseMapPtr_();
120 
121  parametricCoordinatesPtr_.reset
122  (
123  new pointVectorField
124  (
125  IOobject
126  (
127  "parametricCoordinates" + name_,
128  mesh_.time().timeName(),
129  mesh_,
132  ),
133  pointMesh::New(mesh_),
135  )
136  );
137  vectorField& paramCoors = parametricCoordinatesPtr_().primitiveFieldRef();
138 
139  // If already present, read from file
140  if
141  (
142  parametricCoordinatesPtr_().typeHeaderOk<pointVectorField>(true)
143  && readStoredData_
144  )
145  {
146  // These are the parametric coordinates that have been computed
147  // correctly (hopefully!) in a previous run.
148  // findPointsInBox has possibly found more points and lists need
149  // resizing
150  Info<< "Reading parametric coordinates from file" << endl;
151  IOobject& header = parametricCoordinatesPtr_().ref();
152  parametricCoordinatesPtr_() =
154  (
155  header,
156  pointMesh::New(mesh_)
157  );
158 
159  // Initialize intermediate fields with sizes from findPointInBox
160  labelList actualMap(map.size());
161 
162  // Read and store
163  label curIndex(0);
164  forAll(map, pI)
165  {
166  const label globalPointIndex = map[pI];
167  if (paramCoors[globalPointIndex] != vector::zero)
168  {
169  actualMap[curIndex] = map[pI];
170  reverseMap[globalPointIndex] = curIndex;
171  ++curIndex;
172  }
173  else
174  {
175  reverseMap[globalPointIndex] = -1;
176  }
177  }
178 
179  // Resize intermediate
180  actualMap.setSize(curIndex);
181 
182  reduce(curIndex, sumOp<label>());
183  Info<< "Read non-zero parametric coordinates for " << curIndex
184  << " points" << endl;
185 
186  // Update lists with the appropriate entries
187  map = actualMap;
188  }
189  // Else, compute parametric coordinates iteratively
190  else
191  {
192  // Initialize parametric coordinates based on min/max of control points
193  scalar minX1 = min(cps_.component(0));
194  scalar maxX1 = max(cps_.component(0));
195  scalar minX2 = min(cps_.component(1));
196  scalar maxX2 = max(cps_.component(1));
197  scalar minX3 = min(cps_.component(2));
198  scalar maxX3 = max(cps_.component(2));
199 
200  scalar oneOverDenomX(1./(maxX1 - minX1));
201  scalar oneOverDenomY(1./(maxX2 - minX2));
202  scalar oneOverDenomZ(1./(maxX3 - minX3));
203 
204  forAll(map, pI)
205  {
206  const label globalPI = map[pI];
207  paramCoors[globalPI].x() = (points[pI].x() - minX1)*oneOverDenomX;
208  paramCoors[globalPI].y() = (points[pI].y() - minX2)*oneOverDenomY;
209  paramCoors[globalPI].z() = (points[pI].z() - minX3)*oneOverDenomZ;
210  }
211 
212  // Indices of points that failed to converge
213  // (i.e. are bounded for nMaxBound iters)
214  boolList dropOffPoints(map.size(), false);
215  label nDropedPoints(0);
216 
217  // Initial cartesian coordinates
218  tmp<vectorField> tsplinesBasedCoors(coordinates(paramCoors));
219  vectorField& splinesBasedCoors = tsplinesBasedCoors.ref();
220 
221  // Newton-Raphson loop to compute parametric coordinates
222  // based on cartesian coordinates and the known control points
223  Info<< "Mapping of mesh points to parametric space for box " << name_
224  << " ..." << endl;
225  // Do loop on a point-basis and check residual of each point equation
226  label maxIterNeeded(0);
227  forAll(points, pI)
228  {
229  label iter(0);
230  label nBoundIters(0);
231  vector res(GREAT, GREAT, GREAT);
232  do
233  {
234  const label globalPI = map[pI];
235  vector& uVec = paramCoors[globalPI];
236  vector& coorPointI = splinesBasedCoors[pI];
237  uVec += ((inv(JacobianUVW(uVec))) & (points[pI] - coorPointI));
238  // Bounding might be needed for the first iterations
239  // If multiple bounds happen, point is outside of the control
240  // boxes and should be discarded
241  if (bound(uVec))
242  {
243  ++nBoundIters;
244  }
245  if (nBoundIters > nMaxBound_)
246  {
247  dropOffPoints[pI] = true;
248  ++nDropedPoints;
249  break;
250  }
251  // Update current cartesian coordinates based on parametric ones
252  coorPointI = coordinates(uVec);
253  // Compute residual
254  res = cmptMag(points[pI] - coorPointI);
255  }
256  while
257  (
258  (iter++ < maxIter_)
259  && (
260  res.component(0) > tolerance_
261  || res.component(1) > tolerance_
262  || res.component(2) > tolerance_
263  )
264  );
265  if (iter > maxIter_)
266  {
268  << "Mapping to parametric space for point " << pI
269  << " failed." << endl
270  << "Residual after " << maxIter_ + 1 << " iterations : "
271  << res << endl
272  << "parametric coordinates " << paramCoors[map[pI]]
273  << endl
274  << "Local system coordinates " << points[pI] << endl
275  << "Threshold residual per direction : " << tolerance_
276  << endl;
277  }
278  maxIterNeeded = max(maxIterNeeded, iter);
279  }
280  reduce(maxIterNeeded, maxOp<label>());
281 
282  label nParameterizedPoints = map.size() - nDropedPoints;
283 
284  // Resize mapping lists and parametric coordinates after dropping some
285  // points
286  labelList mapOld(map);
287 
288  map.setSize(nParameterizedPoints);
289 
290  label curIndex(0);
291  forAll(dropOffPoints, pI)
292  {
293  if (!dropOffPoints[pI])
294  {
295  map[curIndex] = mapOld[pI];
296  reverseMap[mapOld[pI]] = curIndex;
297  ++curIndex;
298  }
299  else
300  {
301  paramCoors[mapOld[pI]] = vector::zero;
302  reverseMap[mapOld[pI]] = -1;
303  }
304  }
305 
306  reduce(nDropedPoints, sumOp<label>());
307  reduce(nParameterizedPoints, sumOp<label>());
308  Info<< "Found " << nDropedPoints
309  << " to discard from morphing boxes" << endl;
310  Info<< "Keeping " << nParameterizedPoints
311  << " parameterized points in boxes" << endl;
312 
313  splinesBasedCoors = coordinates(paramCoors)();
314  scalar maxDiff(-GREAT);
315  forAll(splinesBasedCoors, pI)
316  {
317  scalar diff =
318  mag(splinesBasedCoors[pI] - localSystemCoordinates_[map[pI]]);
319  if (diff > maxDiff)
320  {
321  maxDiff = diff;
322  }
323  }
324  reduce(maxDiff, maxOp<scalar>());
325  scalar timeAft = mesh_.time().elapsedCpuTime();
326  Info<< "\tMapping completed in " << timeAft - timeBef << " seconds"
327  << endl;
328  Info<< "\tMax iterations per point needed to compute parametric "
329  << "coordinates : "
330  << maxIterNeeded << endl;
331  Info<< "\tMax difference between original mesh points and "
332  << "parameterized ones "
333  << maxDiff << endl;
334  }
335 }
336 
337 
339 (
340  tmp<vectorField> tPoints
341 )
342 {
343  const vectorField& points = tPoints();
344  computeParametricCoordinates(points);
345 }
346 
347 
349 (
350  vector& vec,
351  scalar minValue,
352  scalar maxValue
353 ) const
354 {
355  bool boundPoint(false);
356  // Lower value bounding
357  if (vec.x() < scalar(0))
358  {
359  vec.x() = minValue;
360  boundPoint = true;
361  }
362  if (vec.y() < scalar(0))
363  {
364  vec.y() = minValue;
365  boundPoint = true;
366  }
367  if (vec.z() < scalar(0))
368  {
369  vec.z() = minValue;
370  boundPoint = true;
371  }
372  // Upper value bounding
373  if (vec.x() > 1)
374  {
375  vec.x() = maxValue;
376  boundPoint = true;
377  }
378  if (vec.y() > 1)
379  {
380  vec.y() = maxValue;
381  boundPoint = true;
382  }
383  if (vec.z() > 1)
384  {
385  vec.z() = maxValue;
386  boundPoint = true;
387  }
388  return boundPoint;
389 }
390 
391 
393 {
395  {
396  mkDir(mesh_.time().globalPath()/"optimisation"/cpsFolder_);
397  }
398 }
399 
400 
402 {
403  label nCPs = cps_.size();
404  activeControlPoints_ = boolList(nCPs, true);
405  activeDesignVariables_ = boolList(3*nCPs, true);
406 
407  // Check whether all boundary control points should be confined
408  confineBoundaryControlPoints();
409 
410  // Apply confinement to maintain continuity
411  continuityRealatedConfinement();
412 
413  // Confine user-specified directions
414  confineControlPointsDirections();
415 
416  // Determine active control points. A control point is considered active
417  // if at least one of its components is free to move
418  forAll(activeControlPoints_, cpI)
419  {
420  if
421  (
422  !activeDesignVariables_[3*cpI]
423  && !activeDesignVariables_[3*cpI + 1]
424  && !activeDesignVariables_[3*cpI + 2]
425  )
426  {
427  activeControlPoints_[cpI] = false;
428  }
429  }
430 
431  // Deactivate design varibles if they affect no mesh point
432  confineInertControlPoints();
433 }
434 
435 
437 {
438  // Loop over all active control points and check whether they parameterize
439  // at least one mesh point of the ones laying within the morphing box
440  const labelList& map = getMap();
441  const pointVectorField& parametricCoors = getParametricCoordinates();
442 
443  const scalarField& knotsU = basisU_.knots();
444  const scalarField& knotsV = basisV_.knots();
445  const scalarField& knotsW = basisW_.knots();
446 
447  const label pU = basisU_.degree();
448  const label pV = basisV_.degree();
449  const label pW = basisW_.degree();
450  forAll(activeControlPoints_, cpI)
451  {
452  if (activeControlPoints_[cpI])
453  {
454  // Get i, j, k corresponding to this control point
455  label i(-1), j(-1), k(-1);
456  getIJK(i, j, k, cpI);
457 
458  const scalar uMin(knotsU[i]);
459  const scalar uMax(knotsU[i + pU + 1]);
460  const scalar vMin(knotsV[j]);
461  const scalar vMax(knotsV[j + pV + 1]);
462  const scalar wMin(knotsW[k]);
463  const scalar wMax(knotsW[k + pW + 1]);
464  bool foundParamPt(false);
465  for (const label paramI : map)
466  {
467  const vector& paramCoors = parametricCoors()[paramI];
468  if
469  (
470  paramCoors.x() >= uMin && paramCoors.x() < uMax
471  && paramCoors.y() >= vMin && paramCoors.y() < vMax
472  && paramCoors.z() >= wMin && paramCoors.z() < wMax
473  )
474  {
475  foundParamPt = true;
476  break;
477  }
478  }
479  reduce(foundParamPt, orOp<bool>());
480  if (!foundParamPt)
481  {
482  activeControlPoints_[cpI] = false;
483  activeDesignVariables_[3*cpI] = false;
484  activeDesignVariables_[3*cpI + 1] = false;
485  activeDesignVariables_[3*cpI + 2] = false;
486  Info<< "Disabling control " << cpI
487  << " and variables "
488  << "(" << 3*cpI << ", " << 3*cpI+1 << ", " << 3*cpI+2 << ")"
489  << " since they does not parameterize any mesh point"
490  << endl;
491  }
492  }
493  }
494 }
495 
496 
498 {
499  const label nCPsU = basisU_.nCPs();
500  const label nCPsV = basisV_.nCPs();
501  const label nCPsW = basisW_.nCPs();
502 
503  // Zero movement of the boundary control points. Active by default
504  if (confineBoundaryControlPoints_)
505  {
506  // Side patches
507  for (label iCPw = 0; iCPw < nCPsW; iCPw += nCPsW - 1)
508  {
509  for (label iCPv = 0; iCPv < nCPsV; iCPv++)
510  {
511  for (label iCPu = 0; iCPu < nCPsU; iCPu++)
512  {
513  confineControlPoint(getCPID(iCPu, iCPv, iCPw));
514  }
515  }
516  }
517  // Front-back patches
518  for (label iCPw = 0; iCPw < nCPsW; iCPw++)
519  {
520  for (label iCPv = 0; iCPv < nCPsV; iCPv++)
521  {
522  for (label iCPu = 0; iCPu < nCPsU; iCPu += nCPsU - 1)
523  {
524  confineControlPoint(getCPID(iCPu, iCPv, iCPw));
525  }
526  }
527  }
528  // Top-bottom patches
529  for (label iCPw = 0; iCPw < nCPsW; iCPw++)
530  {
531  for (label iCPv = 0; iCPv < nCPsV; iCPv += nCPsV - 1)
532  {
533  for (label iCPu = 0; iCPu < nCPsU; iCPu++)
534  {
535  confineControlPoint(getCPID(iCPu, iCPv, iCPw));
536  }
537  }
538  }
539  }
540 }
541 
542 
544 {
545  const label nCPsU = basisU_.nCPs();
546  const label nCPsV = basisV_.nCPs();
547  const label nCPsW = basisW_.nCPs();
548 
549  // Zero movement to a number of x-constant slices of cps in order to
550  // preserve continuity at the boundary of the parameterized space
551  forAll(confineUMinCPs_, iCPu)
552  {
553  const boolVector& confineSlice = confineUMinCPs_[iCPu];
554  // Control points at the start of the parameterized space
555  for (label iCPw = 0; iCPw < nCPsW; iCPw++)
556  {
557  for (label iCPv = 0; iCPv < nCPsV; iCPv++)
558  {
559  confineControlPoint(getCPID(iCPu, iCPv, iCPw), confineSlice);
560  }
561  }
562  }
563 
564  forAll(confineUMaxCPs_, sliceI)
565  {
566  const boolVector& confineSlice = confineUMaxCPs_[sliceI];
567  label iCPu = nCPsU - 1 - sliceI;
568  // Control points at the end of the parameterized space
569  for (label iCPw = 0; iCPw < nCPsW; iCPw++)
570  {
571  for (label iCPv = 0; iCPv < nCPsV; iCPv++)
572  {
573  confineControlPoint(getCPID(iCPu, iCPv, iCPw), confineSlice);
574  }
575  }
576  }
577 
578  // Zero movement to a number of y-constant slices of cps in order to
579  // preserve continuity at the boundary of the parameterized space
580  forAll(confineVMinCPs_, iCPv)
581  {
582  const boolVector& confineSlice = confineVMinCPs_[iCPv];
583  // Control points at the start of the parameterized space
584  for (label iCPw = 0; iCPw < nCPsW; iCPw++)
585  {
586  for (label iCPu = 0; iCPu < nCPsU; iCPu++)
587  {
588  confineControlPoint(getCPID(iCPu, iCPv, iCPw), confineSlice);
589  }
590  }
591  }
592 
593  forAll(confineVMaxCPs_, sliceI)
594  {
595  const boolVector& confineSlice = confineVMaxCPs_[sliceI];
596  label iCPv = nCPsV - 1 - sliceI;
597  // Control points at the end of the parameterized space
598  for (label iCPw = 0; iCPw < nCPsW; iCPw++)
599  {
600  for (label iCPu = 0; iCPu < nCPsU; iCPu++)
601  {
602  confineControlPoint(getCPID(iCPu, iCPv, iCPw), confineSlice);
603  }
604  }
605  }
606 
607  // Zero movement to a number of w-constant slices of cps in order to
608  // preserve continuity at the boundary of the parameterized space
609  forAll(confineWMinCPs_, iCPw)
610  {
611  const boolVector& confineSlice = confineWMinCPs_[iCPw];
612  // Control points at the start of the parameterized space
613  for (label iCPv = 0; iCPv < nCPsV; iCPv++)
614  {
615  for (label iCPu = 0; iCPu < nCPsU; iCPu++)
616  {
617  confineControlPoint(getCPID(iCPu, iCPv, iCPw), confineSlice);
618  }
619  }
620  }
621 
622  forAll(confineWMaxCPs_, sliceI)
623  {
624  const boolVector& confineSlice = confineWMaxCPs_[sliceI];
625  label iCPw = nCPsW - 1 - sliceI;
626  // Control points at the end of the parameterized space
627  for (label iCPv = 0; iCPv < nCPsV; iCPv++)
628  {
629  for (label iCPu = 0; iCPu < nCPsU; iCPu++)
630  {
631  confineControlPoint(getCPID(iCPu, iCPv, iCPw), confineSlice);
632  }
633  }
634  }
635 }
636 
637 
639 {
640  for (label cpI = 0; cpI < cps_.size(); ++cpI)
641  {
642  if (confineUMovement_) activeDesignVariables_[3*cpI] = false;
643  if (confineVMovement_) activeDesignVariables_[3*cpI + 1] = false;
644  if (confineWMovement_) activeDesignVariables_[3*cpI + 2] = false;
645  }
646 }
647 
648 
649 void Foam::NURBS3DVolume::confineControlPoint(const label cpI)
650 {
651  if (cpI < 0 || cpI > cps_.size() -1)
652  {
654  << "Attempted to confine control point movement for a control point "
655  << " ID which is out of bounds"
656  << exit(FatalError);
657  }
658  else
659  {
660  activeDesignVariables_[3*cpI] = false;
661  activeDesignVariables_[3*cpI + 1] = false;
662  activeDesignVariables_[3*cpI + 2] = false;
663  }
664 }
665 
666 
668 (
669  const label cpI,
670  const boolVector& confineDirections
671 )
672 {
673  if (cpI < 0 || cpI > cps_.size() -1)
674  {
676  << "Attempted to confine control point movement for a control point "
677  << " ID which is out of bounds"
678  << exit(FatalError);
679  }
680  else
681  {
682  if (confineDirections.x()) activeDesignVariables_[3*cpI] = false;
683  if (confineDirections.y()) activeDesignVariables_[3*cpI + 1] = false;
684  if (confineDirections.z()) activeDesignVariables_[3*cpI + 2] = false;
685  }
686 }
687 
688 
689 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
690 
692 (
693  const dictionary& dict,
694  const fvMesh& mesh,
695  bool computeParamCoors
696 )
697 :
698  localIOdictionary
699  (
700  IOobject
701  (
702  dict.dictName() + "cpsBsplines",
703  mesh.time().timeName(),
704  fileName("uniform")/fileName("volumetricBSplines"),
705  mesh,
706  IOobject::READ_IF_PRESENT,
707  IOobject::AUTO_WRITE
708  ),
709  word::null
710  ),
711  mesh_(mesh),
712  dict_(dict),
713  name_(dict.dictName()),
714  basisU_(dict.get<label>("nCPsU"), dict.get<label>("degreeU")),
715  basisV_(dict.get<label>("nCPsV"), dict.get<label>("degreeV")),
716  basisW_(dict.get<label>("nCPsW"), dict.get<label>("degreeW")),
717  maxIter_(dict.getOrDefault<label>("maxIterations", 10)),
718  tolerance_(dict.getOrDefault<scalar>("tolerance", 1.e-10)),
719  nMaxBound_(dict.getOrDefault<scalar>("nMaxBoundIterations", 4)),
720  cps_(),
721  mapPtr_(nullptr),
722  reverseMapPtr_(nullptr),
723  parametricCoordinatesPtr_(nullptr),
724  localSystemCoordinates_(mesh_.nPoints(), Zero),
725  confineUMovement_
726  (
727  dict.getOrDefaultCompat<bool>
728  (
729  "confineUMovement", {{"confineX1movement", 1912}}, false
730  )
731  ),
732  confineVMovement_
733  (
735  (
736  "confineVMovement", {{"confineX2movement", 1912}}, false
737  )
738  ),
739  confineWMovement_
740  (
742  (
743  "confineWMovement", {{"confineX3movement", 1912}}, false
744  )
745  ),
746  confineBoundaryControlPoints_
747  (
748  dict.getOrDefault<bool>("confineBoundaryControlPoints", true)
749  ),
750  confineUMinCPs_
751  (
752  dict.getOrDefaultCompat<boolVectorList>
753  (
754  "confineUMinCPs", {{"boundUMinCPs", 1912}}, boolVectorList()
755  )
756  ),
757  confineUMaxCPs_
758  (
759  dict.getOrDefaultCompat<boolVectorList>
760  (
761  "confineUMaxCPs", {{"boundUMaxCPs", 1912}}, boolVectorList()
762  )
763  ),
764  confineVMinCPs_
765  (
766  dict.getOrDefaultCompat<boolVectorList>
767  (
768  "confineVMinCPs", {{"boundVMinCPs", 1912}}, boolVectorList()
769  )
770  ),
771  confineVMaxCPs_
772  (
773  dict.getOrDefaultCompat<boolVectorList>
774  (
775  "confineVMaxCPs", {{"boundVMaxCPs", 1912}}, boolVectorList()
776  )
777  ),
778  confineWMinCPs_
779  (
780  dict.getOrDefaultCompat<boolVectorList>
781  (
782  "confineWMinCPs", {{"boundWMinCPs", 1912}}, boolVectorList()
783  )
784  ),
785  confineWMaxCPs_
786  (
787  dict.getOrDefaultCompat<boolVectorList>
788  (
789  "confineWMaxCPs", {{"boundWMaxCPs", 1912}}, boolVectorList()
790  )
791  ),
792  activeControlPoints_(0), //zero here, execute sanity checks first
793  activeDesignVariables_(0), //zero here, execute sanity checks first
794  cpsFolder_("controlPoints"),
795  readStoredData_(dict.getOrDefault<bool>("readStoredData", true))
796 {
797  // Create folders
798  makeFolders();
799 
800  // Sanity checks
801  if
802  (
803  (confineUMinCPs_.size() + confineUMaxCPs_.size() >= basisU_.nCPs())
804  || (confineVMinCPs_.size() + confineVMaxCPs_.size() >= basisV_.nCPs())
805  || (confineWMinCPs_.size() + confineWMaxCPs_.size() >= basisW_.nCPs())
806  )
807  {
809  << "Number of control point slices to be kept frozen at "
810  << "the boundaries is invalid \n"
811  << "Number of control points in u " << basisU_.nCPs() << "\n"
812  << "Number of control points in v " << basisV_.nCPs() << "\n"
813  << "Number of control points in w " << basisW_.nCPs() << "\n"
814  << exit(FatalError);
815  }
816 
817  // Construct control points, if not already read from file
818  if (found("controlPoints"))
819  {
820  cps_ =
822  (
823  "controlPoints",
824  *this,
825  basisU_.nCPs()*basisV_.nCPs()*basisW_.nCPs()
826  );
827  }
828  else
829  {
831  }
832 }
833 
834 
835 // * * * * * * * * * * * * * * * * * Selectors * * * * * * * * * * * * * * * //
836 
838 (
839  const dictionary& dict,
840  const fvMesh& mesh,
841  bool computeParamCoors
842 )
843 {
844  const word modelType(dict.get<word>("type"));
845 
846  Info<< "NURBS3DVolume type : " << modelType << endl;
847 
848  auto* ctorPtr = dictionaryConstructorTable(modelType);
849 
850  if (!ctorPtr)
851  {
853  (
854  dict,
855  "type",
856  modelType,
857  *dictionaryConstructorTablePtr_
858  ) << exit(FatalIOError);
859  }
860 
861  return autoPtr<NURBS3DVolume>(ctorPtr(dict, mesh, computeParamCoors));
862 }
863 
864 
865 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
866 
867 
869 (
870  const vectorField& points
871 ) const
872 {
874  vectorField& paramCoors = tparamCoors.ref();
875  // Initialize parametric coordinates based on min/max of control points
876  scalar minX1 = min(cps_.component(0));
877  scalar maxX1 = max(cps_.component(0));
878  scalar minX2 = min(cps_.component(1));
879  scalar maxX2 = max(cps_.component(1));
880  scalar minX3 = min(cps_.component(2));
881  scalar maxX3 = max(cps_.component(2));
882 
883  scalar oneOverDenomX(1./(maxX1 - minX1));
884  scalar oneOverDenomY(1./(maxX2 - minX2));
885  scalar oneOverDenomZ(1./(maxX3 - minX3));
886 
887  forAll(points, pI)
888  {
889  paramCoors[pI].x() = (points[pI].x() - minX1)*oneOverDenomX;
890  paramCoors[pI].y() = (points[pI].y() - minX2)*oneOverDenomY;
891  paramCoors[pI].z() = (points[pI].z() - minX3)*oneOverDenomZ;
892  }
893 
894  // Indices of points that failed to converge
895  // (i.e. are bounded for nMaxBound iters)
896  boolList dropOffPoints(points.size(), false);
897  label nDropedPoints(0);
898 
899  // Initial cartesian coordinates
900  tmp<vectorField> tsplinesBasedCoors(coordinates(paramCoors));
901  vectorField& splinesBasedCoors = tsplinesBasedCoors.ref();
902 
903  // Newton-Raphson loop to compute parametric coordinates
904  // based on cartesian coordinates and the known control points
905  Info<< "Mapping of mesh points to parametric space for box " << name_
906  << " ..." << endl;
907  // Do loop on a point-basis and check residual of each point equation
908  label maxIterNeeded(0);
909  forAll(points, pI)
910  {
911  label iter(0);
912  label nBoundIters(0);
913  vector res(GREAT, GREAT, GREAT);
914  do
915  {
916  vector& uVec = paramCoors[pI];
917  vector& coorPointI = splinesBasedCoors[pI];
918  uVec += ((inv(JacobianUVW(uVec))) & (points[pI] - coorPointI));
919  // Bounding might be needed for the first iterations
920  // If multiple bounds happen, point is outside of the control
921  // boxes and should be discarded
922  if (bound(uVec))
923  {
924  ++nBoundIters;
925  }
926  if (nBoundIters > nMaxBound_)
927  {
928  dropOffPoints[pI] = true;
929  ++nDropedPoints;
930  break;
931  }
932  // Update current cartesian coordinates based on parametric ones
933  coorPointI = coordinates(uVec);
934  // Compute residual
935  res = cmptMag(points[pI] - coorPointI);
936  }
937  while
938  (
939  (iter++ < maxIter_)
940  && (
941  res.component(0) > tolerance_
942  || res.component(1) > tolerance_
943  || res.component(2) > tolerance_
944  )
945  );
946  if (iter > maxIter_)
947  {
949  << "Mapping to parametric space for point " << pI
950  << " failed." << endl
951  << "Residual after " << maxIter_ + 1 << " iterations : "
952  << res << endl
953  << "parametric coordinates " << paramCoors[pI]
954  << endl
955  << "Local system coordinates " << points[pI] << endl
956  << "Threshold residual per direction : " << tolerance_
957  << endl;
958  }
959  maxIterNeeded = max(maxIterNeeded, iter);
960  }
961  reduce(maxIterNeeded, maxOp<label>());
962 
963  label nParameterizedPoints = points.size() - nDropedPoints;
964 
965  reduce(nDropedPoints, sumOp<label>());
966  reduce(nParameterizedPoints, sumOp<label>());
967  Info<< "Found " << nDropedPoints
968  << " to discard from morphing boxes" << endl;
969  Info<< "Keeping " << nParameterizedPoints
970  << " parameterized points in boxes" << endl;
971  return tparamCoors;
972 }
973 
974 
976 (
977  const scalar u,
978  const scalar v,
979  const scalar w
980 ) const
981 {
982  const label degreeU = basisU_.degree();
983  const label degreeV = basisV_.degree();
984  const label degreeW = basisW_.degree();
985 
986  const label nCPsU = basisU_.nCPs();
987  const label nCPsV = basisV_.nCPs();
988  const label nCPsW = basisW_.nCPs();
989 
990  vector derivative(Zero);
991 
992  for (label iCPw = 0; iCPw < nCPsW; ++iCPw)
993  {
994  const scalar basisW(basisW_.basisValue(iCPw, degreeW, w));
995  for (label iCPv = 0; iCPv < nCPsV; ++iCPv)
996  {
997  const scalar basisVW = basisW*basisV_.basisValue(iCPv, degreeV, v);
998  for (label iCPu = 0; iCPu < nCPsU; ++iCPu)
999  {
1000  derivative +=
1001  cps_[getCPID(iCPu, iCPv, iCPw)]
1002  *basisU_.basisDerivativeU(iCPu, degreeU, u)
1003  *basisVW;
1004  }
1005  }
1006  }
1007 
1008  return derivative;
1009 }
1010 
1011 
1013 (
1014  const scalar u,
1015  const scalar v,
1016  const scalar w
1017 ) const
1018 {
1019  const label degreeU = basisU_.degree();
1020  const label degreeV = basisV_.degree();
1021  const label degreeW = basisW_.degree();
1022 
1023  const label nCPsU = basisU_.nCPs();
1024  const label nCPsV = basisV_.nCPs();
1025  const label nCPsW = basisW_.nCPs();
1026 
1027  vector derivative(Zero);
1028 
1029  for (label iCPw = 0; iCPw < nCPsW; ++iCPw)
1030  {
1031  const scalar basisW(basisW_.basisValue(iCPw, degreeW, w));
1032  for (label iCPv = 0; iCPv < nCPsV; ++iCPv)
1033  {
1034  const scalar basisWDeriV =
1035  basisW*basisV_.basisDerivativeU(iCPv, degreeV, v);
1036  for (label iCPu = 0; iCPu < nCPsU; ++iCPu)
1037  {
1038  derivative +=
1039  cps_[getCPID(iCPu, iCPv, iCPw)]
1040  *basisU_.basisValue(iCPu, degreeU, u)
1041  *basisWDeriV;
1042  }
1043  }
1044  }
1045 
1046  return derivative;
1047 }
1048 
1049 
1051 (
1052  const scalar u,
1053  const scalar v,
1054  const scalar w
1055 ) const
1056 {
1057  const label degreeU = basisU_.degree();
1058  const label degreeV = basisV_.degree();
1059  const label degreeW = basisW_.degree();
1060 
1061  const label nCPsU = basisU_.nCPs();
1062  const label nCPsV = basisV_.nCPs();
1063  const label nCPsW = basisW_.nCPs();
1064 
1065  vector derivative(Zero);
1066 
1067  for (label iCPw = 0; iCPw < nCPsW; iCPw++)
1068  {
1069  const scalar derivW(basisW_.basisDerivativeU(iCPw, degreeW, w));
1070  for (label iCPv = 0; iCPv < nCPsV; iCPv++)
1071  {
1072  const scalar derivWBasisV =
1073  derivW*basisV_.basisValue(iCPv, degreeV, v);
1074  for (label iCPu = 0; iCPu < nCPsU; iCPu++)
1075  {
1076  derivative +=
1077  cps_[getCPID(iCPu, iCPv, iCPw)]
1078  *basisU_.basisValue(iCPu, degreeU, u)
1079  *derivWBasisV;
1080  }
1081  }
1082  }
1083 
1084  return derivative;
1085 }
1086 
1087 
1089 (
1090  const vector& uVector
1091 ) const
1092 {
1093  const scalar u = uVector.x();
1094  const scalar v = uVector.y();
1095  const scalar w = uVector.z();
1096 
1097  vector uDeriv = volumeDerivativeU(u, v, w);
1098  vector vDeriv = volumeDerivativeV(u, v, w);
1099  vector wDeriv = volumeDerivativeW(u, v, w);
1100 
1101  tensor Jacobian(uDeriv, vDeriv, wDeriv, true);
1102 
1103  return Jacobian;
1104 }
1105 
1106 
1108 (
1109  const vector& uVector,
1110  const label cpI
1111 ) const
1112 {
1113  const scalar u = uVector.x();
1114  const scalar v = uVector.y();
1115  const scalar w = uVector.z();
1116 
1117  const label nCPsU = basisU_.nCPs();
1118  const label nCPsV = basisV_.nCPs();
1119 
1120  const label degreeU = basisU_.degree();
1121  const label degreeV = basisV_.degree();
1122  const label degreeW = basisW_.degree();
1123 
1124  label iCPw = cpI/label(nCPsU*nCPsV);
1125  label iCPv = (cpI - iCPw*nCPsU*nCPsV)/nCPsU;
1126  label iCPu = (cpI - iCPw*nCPsU*nCPsV - iCPv*nCPsU);
1127 
1128  // Normally, this should be a tensor, however the parameterization is
1129  // isotropic. Hence the tensor degenerates to a diagonal tensor with all
1130  // diagonal elements being equal. This returns the (unique) diag element
1131  scalar derivative =
1132  basisU_.basisValue(iCPu, degreeU, u)
1133  *basisV_.basisValue(iCPv, degreeV, v)
1134  *basisW_.basisValue(iCPw, degreeW, w);
1135 
1136  return derivative;
1137 }
1138 
1139 
1141 (
1142  const pointVectorField& pointSens,
1143  const labelList& sensitivityPatchIDs
1144 )
1145 {
1146  vectorField controlPointDerivs(cps_.size(), Zero);
1147 
1148  // Get parametric coordinates
1149  const vectorField& parametricCoordinates = getParametricCoordinates();
1150 
1151  forAll(controlPointDerivs, cpI)
1152  {
1153  forAll(sensitivityPatchIDs, pI)
1154  {
1155  const label patchI = sensitivityPatchIDs[pI];
1156  const polyPatch& patch = mesh_.boundaryMesh()[patchI];
1157  const labelList& meshPoints = patch.meshPoints();
1158 
1159  forAll(meshPoints, mpI)
1160  {
1161  const label globalIndex = meshPoints[mpI];
1162  const label whichPointInBox = reverseMapPtr_()[globalIndex];
1163 
1164  // If point resides within control points box,
1165  // add contribution to cp derivative
1166  if (whichPointInBox != -1)
1167  {
1168  controlPointDerivs[cpI] +=
1169  (
1170  pointSens[globalIndex]
1171  & transformationTensorDxDb(globalIndex)
1172  )
1173  *volumeDerivativeCP
1174  (
1175  parametricCoordinates[globalIndex],
1176  cpI
1177  );
1178  }
1179  }
1180  }
1181  }
1182 
1183  // Sum contributions from all processors
1185 
1186  return controlPointDerivs;
1187 }
1188 
1189 
1191 (
1192  const volVectorField& faceSens,
1193  const labelList& sensitivityPatchIDs
1194 )
1195 {
1196  return
1197  computeControlPointSensitivities
1198  (
1199  faceSens.boundaryField(),
1200  sensitivityPatchIDs
1201  );
1202 }
1203 
1204 
1206 (
1207  const boundaryVectorField& faceSens,
1208  const labelList& sensitivityPatchIDs
1209 )
1210 {
1211  // Return field
1212  vectorField controlPointDerivs(cps_.size(), Zero);
1213 
1214  // Get parametric coordinates
1215  const vectorField& parametricCoordinates = getParametricCoordinates();
1216 
1217  // Auxiliary quantities
1219  const labelList& reverseMap = reverseMapPtr_();
1220 
1221  forAll(controlPointDerivs, cpI)
1222  {
1223  forAll(sensitivityPatchIDs, pI)
1224  {
1225  const label patchI = sensitivityPatchIDs[pI];
1226  const polyPatch& patch = mesh_.boundaryMesh()[patchI];
1227  const label patchStart = patch.start();
1228  const fvPatchVectorField& patchSens = faceSens[patchI];
1229 
1230  // loop over patch faces
1231  forAll(patch, fI)
1232  {
1233  const face& fGlobal = mesh_.faces()[fI + patchStart];
1234  const pointField facePoints = fGlobal.points(mesh_.points());
1235  // loop over face points
1236  tensorField facePointDerivs(facePoints.size(), Zero);
1237  forAll(fGlobal, pI)
1238  {
1239  const label globalIndex = fGlobal[pI]; //global point index
1240  const label whichPointInBox = reverseMap[globalIndex];
1241  // if point resides within control points box,
1242  // add contribution to d( facePoints )/db
1243  if (whichPointInBox != -1)
1244  {
1245  // TENSOR-BASED
1246  //~~~~~~~~~~~~~
1247  facePointDerivs[pI] =
1248  transformationTensorDxDb(globalIndex)
1249  * volumeDerivativeCP
1250  (
1251  parametricCoordinates[globalIndex],
1252  cpI
1253  );
1254 
1255  }
1256  }
1257 
1258  tensor fCtrs_d =
1260  (
1261  facePoints,
1262  facePointDerivs
1263  )[0];
1264  controlPointDerivs[cpI] += patchSens[fI] & fCtrs_d;
1265  }
1266  }
1267  }
1268  // Sum contributions from all processors
1270 
1271  return controlPointDerivs;
1272 }
1273 
1274 
1276 (
1277  const vectorField& faceSens,
1278  const label patchI,
1279  const label cpI
1280 )
1281 {
1282  // Return vector
1283  vector cpSens(Zero);
1284  // Get parametric coordinates
1285  const vectorField& parametricCoordinates = getParametricCoordinates();
1286 
1287  // Auxiliary quantities
1289  const labelList& reverseMap = reverseMapPtr_();
1290 
1291  const polyPatch& patch = mesh_.boundaryMesh()[patchI];
1292  const label patchStart = patch.start();
1293  // Loop over patch faces
1294  forAll(patch, fI)
1295  {
1296  const face& fGlobal = mesh_.faces()[fI + patchStart];
1297  const pointField facePoints = fGlobal.points(mesh_.points());
1298  // Loop over face points
1299  tensorField facePointDerivs(facePoints.size(), Zero);
1300  forAll(fGlobal, pI)
1301  {
1302  const label globalIndex = fGlobal[pI]; //global point index
1303  const label whichPointInBox = reverseMap[globalIndex];
1304  // If point resides within control points box,
1305  // add contribution to d( facePoints )/db
1306  if (whichPointInBox != -1)
1307  {
1308  // TENSOR-BASED
1309  //~~~~~~~~~~~~~
1310  facePointDerivs[pI] =
1311  transformationTensorDxDb(globalIndex)
1312  *volumeDerivativeCP
1313  (
1314  parametricCoordinates[globalIndex],
1315  cpI
1316  );
1317  }
1318  }
1319 
1320  tensor fCtrs_d =
1321  deltaBoundary.makeFaceCentresAndAreas_d
1322  (
1323  facePoints,
1324  facePointDerivs
1325  )[0];
1326  cpSens += faceSens[fI] & fCtrs_d;
1327  }
1328  // Sum contributions from all processors
1329  reduce(cpSens, sumOp<vector>());
1330 
1331  return cpSens;
1332 }
1333 
1334 
1336 (
1337  const label patchI,
1338  const label cpI,
1339  bool DimensionedNormalSens
1340 )
1341 {
1342  const fvPatch& patch = mesh_.boundary()[patchI];
1343  const polyPatch& ppatch = patch.patch();
1344  // Return field
1345  tmp<tensorField> tdndbSens(new tensorField(patch.size(), Zero));
1346  tensorField& dndbSens = tdndbSens.ref();
1347  // Auxiliary quantities
1349  const label patchStart = ppatch.start();
1350  const labelList& reverseMap = reverseMapPtr_();
1351 
1352  // Get parametric coordinates
1353  const vectorField& parametricCoordinates = getParametricCoordinates();
1354 
1355  // Loop over patch faces
1356  forAll(patch, fI)
1357  {
1358  const face& fGlobal = mesh_.faces()[fI + patchStart];
1359  const pointField facePoints = fGlobal.points(mesh_.points());
1360  // Loop over face points
1361  tensorField facePointDerivs(facePoints.size(), Zero);
1362  forAll(fGlobal, pI)
1363  {
1364  const label globalIndex = fGlobal[pI]; //global point index
1365  const label whichPointInBox = reverseMap[globalIndex];
1366  // If point resides within control points box,
1367  // add contribution to d( facePoints )/db
1368  if (whichPointInBox != -1)
1369  {
1370  // TENSOR-BASED
1371  //~~~~~~~~~~~~~
1372  facePointDerivs[pI] =
1373  transformationTensorDxDb(globalIndex)
1374  *volumeDerivativeCP
1375  (
1376  parametricCoordinates[globalIndex],
1377  cpI
1378  );
1379  }
1380  }
1381 
1382  // Determine whether to return variance of dimensioned or unit normal
1383  tensorField dNdbSens =
1384  deltaBoundary.makeFaceCentresAndAreas_d
1385  (
1386  facePoints,
1387  facePointDerivs
1388  );
1389 
1390  if (DimensionedNormalSens)
1391  {
1392  dndbSens[fI] = dNdbSens[1];
1393  }
1394  else
1395  {
1396  dndbSens[fI] = dNdbSens[2];
1397  }
1398  }
1399 
1400  return tdndbSens;
1401 }
1402 
1403 
1405 (
1406  const label patchI,
1407  const label cpI
1408 )
1409 {
1410  // Get parametric coordinates
1411  const vectorField& parametricCoordinates = getParametricCoordinates();
1412 
1413  // Patch data
1414  const polyPatch& patch = mesh_.boundaryMesh()[patchI];
1415  const labelList& meshPoints = patch.meshPoints();
1416 
1417  // Return field
1418  auto tdxdb = tmp<tensorField>::New(patch.nPoints(), Zero);
1419  auto& dxdb = tdxdb.ref();
1420 
1421  forAll(meshPoints, pI)
1422  {
1423  const label globalIndex = meshPoints[pI]; //global point index
1424  const label whichPointInBox = reverseMapPtr_()[globalIndex];
1425 
1426  // If point resides within control points box, find dxdb
1427  if (whichPointInBox != -1)
1428  {
1429  dxdb[pI] =
1430  transformationTensorDxDb(globalIndex)
1431  *volumeDerivativeCP
1432  (
1433  parametricCoordinates[globalIndex],
1434  cpI
1435  );
1436  }
1437  }
1438 
1439  return tdxdb;
1440 }
1441 
1442 
1444 (
1445  const label patchI,
1446  const label cpI
1447 )
1448 {
1449  // get parametric coordinates
1450  const vectorField& parametricCoordinates = getParametricCoordinates();
1451 
1452  // Patch data
1453  const polyPatch& patch = mesh_.boundaryMesh()[patchI];
1454  const label patchStart = patch.start();
1455 
1456  // Return field
1457  auto tdxdb = tmp<tensorField>::New(patch.size(), Zero);
1458  auto& dxdb = tdxdb.ref();
1459 
1460  // Mesh differentiation engine
1461  deltaBoundary deltaBound(mesh_);
1462 
1463  forAll(patch, fI)
1464  {
1465  const face& fGlobal = mesh_.faces()[fI + patchStart];
1466  const pointField facePoints = fGlobal.points(mesh_.points());
1467  // Loop over face points
1468  tensorField facePointDerivs(facePoints.size(), Zero);
1469  forAll(fGlobal, pI)
1470  {
1471  const label globalIndex = fGlobal[pI]; //global point index
1472  const label whichPointInBox = reverseMapPtr_()[globalIndex];
1473  // If point resides within control points box,
1474  // add contribution to d( facePoints )/db
1475  if (whichPointInBox != -1)
1476  {
1477  // TENSOR-BASED
1478  //~~~~~~~~~~~~~
1479  facePointDerivs[pI] =
1480  transformationTensorDxDb(globalIndex)
1481  *volumeDerivativeCP
1482  (
1483  parametricCoordinates[globalIndex],
1484  cpI
1485  );
1486 
1487  }
1488  }
1489  dxdb[fI] =
1490  deltaBound.makeFaceCentresAndAreas_d
1491  (
1492  facePoints,
1493  facePointDerivs
1494  )[0];
1495  }
1496 
1497  return tdxdb;
1498 }
1499 
1500 
1502 (
1503  const vectorField& uVector
1504 ) const
1505 {
1506  const label nPoints = mapPtr_().size();
1507  auto tpoints = tmp<vectorField>::New(nPoints, Zero);
1508  auto& points = tpoints.ref();
1509 
1510  forAll(points, pI)
1511  {
1512  const label globalPI = mapPtr_()[pI];
1513  points[pI] = coordinates(uVector[globalPI]);
1514  }
1515 
1516  return tpoints;
1517 }
1518 
1519 
1521 (
1522  const vector& uVector
1523 ) const
1524 {
1525  const label degreeU = basisU_.degree();
1526  const label degreeV = basisV_.degree();
1527  const label degreeW = basisW_.degree();
1528 
1529  const label nCPsU = basisU_.nCPs();
1530  const label nCPsV = basisV_.nCPs();
1531  const label nCPsW = basisW_.nCPs();
1532 
1533  const scalar u = uVector.x();
1534  const scalar v = uVector.y();
1535  const scalar w = uVector.z();
1536 
1537  vector point(Zero);
1538  for (label iCPw = 0; iCPw < nCPsW; iCPw++)
1539  {
1540  const scalar basisW(basisW_.basisValue(iCPw, degreeW, w));
1541  for (label iCPv = 0; iCPv < nCPsV; iCPv++)
1542  {
1543  const scalar basisVW =
1544  basisW*basisV_.basisValue(iCPv, degreeV, v);
1545  for (label iCPu = 0; iCPu < nCPsU; iCPu++)
1546  {
1547  point +=
1548  cps_[getCPID(iCPu, iCPv, iCPw)]
1549  *basisU_.basisValue(iCPu, degreeU, u)
1550  *basisVW;
1551  }
1552  }
1553  }
1554 
1555  return point;
1556 }
1557 
1558 
1560 (
1561  const vectorField& controlPointsMovement
1562 )
1563 {
1564  // Get parametric coordinates and map
1565  const vectorField& paramCoors = getParametricCoordinates();
1566  const labelList& map = mapPtr_();
1567 
1568  // Update control points position
1569  cps_ += controlPointsMovement;
1570  writeCps("cpsBsplines"+mesh_.time().timeName());
1571 
1572  // Compute new mesh points based on updated control points
1573  tmp<vectorField> tparameterizedPoints = coordinates(paramCoors);
1574  const vectorField& parameterizedPoints = tparameterizedPoints();
1575 
1576  // Return field. Initialized with current mesh points
1577  tmp<vectorField> tnewPoints(new vectorField(mesh_.points()));
1578  vectorField& newPoints = tnewPoints.ref();
1579 
1580  // Update position of parameterized points
1581  forAll(parameterizedPoints, pI)
1582  {
1583  newPoints[map[pI]] = transformPointToCartesian(parameterizedPoints[pI]);
1584  }
1585 
1586  // Update coordinates in the local system based on the cartesian points
1587  updateLocalCoordinateSystem(newPoints);
1588  DebugInfo
1589  << "Max mesh movement equal to "
1590  << gMax(mag(newPoints - mesh_.points())) << endl;
1591 
1592  return tnewPoints;
1593 }
1594 
1595 
1597 (
1598  const vectorField& controlPointsMovement,
1599  const labelList& patchesToBeMoved,
1600  const bool updateCPs
1601 )
1602 {
1603  // Get parametric coordinates
1604  const vectorField& paramCoors = getParametricCoordinates();
1605 
1606  // Update control points position
1607  cps_ += controlPointsMovement;
1608 
1609  if (updateCPs)
1610  {
1611  writeCps("cpsBsplines"+mesh_.time().timeName());
1612  }
1613 
1614  // Return field. Initialized with current mesh points
1615  tmp<vectorField> tnewPoints(new vectorField(mesh_.points()));
1616  vectorField& newPoints = tnewPoints.ref();
1617 
1618  // Update position of parameterized boundary points
1619  for (const label patchI : patchesToBeMoved)
1620  {
1621  const polyPatch& patch = mesh_.boundaryMesh()[patchI];
1622  const labelList& meshPoints = patch.meshPoints();
1623 
1624  for (const label globalIndex : meshPoints)
1625  {
1626  const label whichPointInBox = reverseMapPtr_()[globalIndex];
1627  // If point resides within control points box,
1628  // compute new cartesian coordinates
1629  if (whichPointInBox != -1)
1630  {
1631  newPoints[globalIndex] =
1632  transformPointToCartesian
1633  (
1634  coordinates
1635  (
1636  paramCoors[globalIndex]
1637  )
1638  );
1639  }
1640  }
1641  }
1642 
1643  if (updateCPs)
1644  {
1645  // Update coordinates in the local system based on the cartesian points
1646  updateLocalCoordinateSystem(newPoints);
1647  }
1648  else
1649  {
1650  // Move control points to their initial position
1651  cps_ -= controlPointsMovement;
1652  }
1653 
1654  DebugInfo
1655  << "Max mesh movement equal to "
1656  << gMax(mag(newPoints - mesh_.points())) << endl;
1657 
1658  return tnewPoints;
1659 }
1660 
1661 
1662 Foam::label Foam::NURBS3DVolume::getCPID
1663 (
1664  const label i,
1665  const label j,
1666  const label k
1667 ) const
1668 {
1669  const label nCPsU = basisU_.nCPs();
1670  const label nCPsV = basisV_.nCPs();
1671 
1672  return k*nCPsU*nCPsV + j*nCPsU + i;
1673 }
1674 
1675 
1677 (
1678  label& i,
1679  label& j,
1680  label& k,
1681  const label cpID
1682 ) const
1683 {
1684  const label nCPsU = basisU_.nCPs();
1685  const label nCPsV = basisV_.nCPs();
1686  k = cpID/(nCPsU*nCPsV);
1687  const label inKplaneID = (cpID - k*(nCPsU*nCPsV));
1688  j = inKplaneID/nCPsU;
1689  i = inKplaneID - j*nCPsU;
1690 }
1691 
1692 
1694 {
1695  if (cps_.size() != newCps.size())
1696  {
1698  << "Attempting to replace control points with a set of "
1699  << "different size"
1701  }
1702  cps_ = newCps;
1703 }
1704 
1705 
1707 (
1708  vectorField& controlPointsMovement
1709 ) const
1710 {
1711  forAll(controlPointsMovement, cpI)
1712  {
1713  if (!activeDesignVariables_[3*cpI])
1714  {
1715  controlPointsMovement[cpI].x() = Zero;
1716  }
1717  if (!activeDesignVariables_[3*cpI + 1])
1718  {
1719  controlPointsMovement[cpI].y() = Zero;
1720  }
1721  if (!activeDesignVariables_[3*cpI + 2])
1722  {
1723  controlPointsMovement[cpI].z() = Zero;
1724  }
1725  }
1726 }
1727 
1728 
1730 (
1731  const vectorField& controlPointsMovement,
1732  const labelList& patchesToBeMoved
1733 )
1734 {
1735  // Backup old cps
1736  vectorField oldCPs = cps_;
1737  // Get parametric coordinates
1738  const vectorField& paramCoors = getParametricCoordinates();
1739  // Update control points position
1740  cps_ += controlPointsMovement;
1741  // Update position of parameterized boundary points
1742  scalar maxDisplacement(Zero);
1743  for (const label patchI : patchesToBeMoved)
1744  {
1745  const polyPatch& patch = mesh_.boundaryMesh()[patchI];
1746  const labelList& meshPoints = patch.meshPoints();
1747 
1748  for (const label globalIndex : meshPoints)
1749  {
1750  const label whichPointInBox = reverseMapPtr_()[globalIndex];
1751  // If point resides within control points box,
1752  // compute new cartesian coordinates
1753  if (whichPointInBox != -1)
1754  {
1755  vector newPoint =
1756  transformPointToCartesian
1757  (
1758  coordinates
1759  (
1760  paramCoors[globalIndex]
1761  )
1762  );
1763  maxDisplacement =
1764  max
1765  (
1766  maxDisplacement,
1767  mag(newPoint - mesh_.points()[globalIndex])
1768  );
1769  }
1770  }
1771  }
1772  reduce(maxDisplacement, maxOp<scalar>());
1773  cps_ = oldCPs;
1774 
1775  return maxDisplacement;
1776 }
1777 
1778 
1780 {
1781  if (!mapPtr_)
1782  {
1783  findPointsInBox(localSystemCoordinates_);
1784  }
1785  tmp<vectorField> pointsInBox
1786  (
1787  new vectorField(localSystemCoordinates_, mapPtr_())
1788  );
1789 
1790  return pointsInBox;
1791 }
1792 
1793 
1795 {
1796  if (!mapPtr_)
1797  {
1798  findPointsInBox(localSystemCoordinates_);
1799  }
1800 
1801  return mapPtr_();
1802 }
1803 
1804 
1806 {
1807  if (!reverseMapPtr_)
1808  {
1809  findPointsInBox(localSystemCoordinates_);
1810  }
1811 
1812  return reverseMapPtr_();
1813 }
1814 
1815 
1817 {
1818  // If not computed yet, compute parametric coordinates
1819  if (!parametricCoordinatesPtr_)
1820  {
1821  // Find mesh points inside control points box
1822  // if they have been identified yet
1823  if (!mapPtr_)
1824  {
1825  findPointsInBox(localSystemCoordinates_);
1826  }
1827  computeParametricCoordinates(getPointsInBox()());
1828  }
1829 
1830  return parametricCoordinatesPtr_();
1831 }
1832 
1833 
1835 {
1836  // Get parametric coordinates
1837  const vectorField& parametricCoordinates = getParametricCoordinates();
1838 
1839  // Set return field to zero
1840  tmp<pointTensorField> tDxDb
1841  (
1842  new pointTensorField
1843  (
1844  IOobject
1845  (
1846  "DxDb",
1847  mesh_.time().timeName(),
1848  mesh_,
1851  ),
1852  pointMesh::New(mesh_),
1854  )
1855  );
1856 
1857  pointTensorField& DxDb = tDxDb.ref();
1858 
1859  // All points outside the control box remain unmoved.
1860  // Loop over only points within the control box
1861  const labelList& map = mapPtr_();
1862  for (const label globalIndex : map)
1863  {
1864  DxDb[globalIndex] =
1865  transformationTensorDxDb(globalIndex)
1866  *volumeDerivativeCP
1867  (
1868  parametricCoordinates[globalIndex],
1869  cpI
1870  );
1871  }
1872 
1873  return tDxDb;
1874 }
1875 
1876 
1878 (
1879  const label cpI
1880 )
1881 {
1882  // Get parametric coordinates
1883  const vectorField& parametricCoordinates = getParametricCoordinates();
1884 
1885  // Set return field to zero
1886  tmp<volTensorField> tDxDb
1887  (
1888  new volTensorField
1889  (
1890  IOobject
1891  (
1892  "DxDb",
1893  mesh_.time().timeName(),
1894  mesh_,
1897  ),
1898  mesh_,
1900  )
1901  );
1902 
1903  volTensorField& DxDb = tDxDb.ref();
1904  deltaBoundary deltaBound(mesh_);
1905  const labelListList& pointCells = mesh_.pointCells();
1906 
1907  // All points outside the control box remain unmoved.
1908  // Loop over only points within the control box
1909  const labelList& map = mapPtr_();
1910  for (const label globalIndex : map)
1911  {
1912  tensor pointDxDb =
1913  transformationTensorDxDb(globalIndex)
1914  *volumeDerivativeCP
1915  (
1916  parametricCoordinates[globalIndex],
1917  cpI
1918  );
1919  const labelList& pointCellsI = pointCells[globalIndex];
1920  tmp<tensorField> tC_d = deltaBound.cellCenters_d(globalIndex);
1921  const tensorField& C_d = tC_d();
1922  forAll(pointCellsI, cI)
1923  {
1924  const label cellI = pointCellsI[cI];
1925  DxDb[cellI] += C_d[cI] & pointDxDb;
1926  }
1927  }
1928 
1929  // Assign boundary values since the grad of this field is often needed
1930  forAll(mesh_.boundary(), pI)
1931  {
1932  const fvPatch& patch = mesh_.boundary()[pI];
1933  if (!isA<coupledFvPatch>(patch))
1934  {
1935  DxDb.boundaryFieldRef()[pI] = patchDxDbFace(pI, cpI);
1936  }
1937  }
1938 
1939  // Correct coupled boundaries
1940  DxDb.correctBoundaryConditions();
1941 
1942  return tDxDb;
1943 }
1944 
1945 
1947 {
1948  label nU(basisU_.nCPs());
1949  return label(nU % 2 == 0 ? 0.5*nU : 0.5*(nU - 1) + 1);
1950 }
1951 
1952 
1954 {
1955  label nV(basisV_.nCPs());
1956  return label(nV % 2 == 0 ? 0.5*nV : 0.5*(nV - 1) + 1);
1957 }
1958 
1959 
1961 {
1962  label nW(basisW_.nCPs());
1963  return label(nW % 2 == 0 ? 0.5*nW : 0.5*(nW - 1) + 1);
1964 }
1965 
1966 
1968 {
1969  return Vector<label>(nUSymmetry(), nVSymmetry(), nWSymmetry());
1970 }
1971 
1972 
1974 (
1975  const fileName& baseName,
1976  const bool transform
1977 ) const
1978 {
1979  const label nCPsU = basisU_.nCPs();
1980  const label nCPsV = basisV_.nCPs();
1981 
1982  vectorField cpsInCartesian(cps_);
1983  if (transform)
1984  {
1985  forAll(cpsInCartesian, cpI)
1986  {
1987  cpsInCartesian[cpI] = transformPointToCartesian(cps_[cpI]);
1988  }
1989  }
1990 
1991  Info<< "Writing control point positions to file" << endl;
1992 
1993  if (Pstream::master())
1994  {
1995  OFstream cpsFile("optimisation"/cpsFolder_/name_ + baseName + ".csv");
1996  // Write header
1997  cpsFile
1998  << "\"Points : 0\", \"Points : 1\", \"Points : 2\","
1999  << "\"i\", \"j\", \"k\","
2000  << "\"active : 0\", \"active : 1\", \"active : 2\"" << endl;
2001 
2002  forAll(cpsInCartesian, cpI)
2003  {
2004  const label iCPw = cpI/label(nCPsU*nCPsV);
2005  const label iCPv = (cpI - iCPw*nCPsU*nCPsV)/nCPsU;
2006  const label iCPu = (cpI - iCPw*nCPsU*nCPsV - iCPv*nCPsU);
2007 
2008  cpsFile
2009  << cpsInCartesian[cpI].x() << ", "
2010  << cpsInCartesian[cpI].y() << ", "
2011  << cpsInCartesian[cpI].z() << ", "
2012  << iCPu << ", "
2013  << iCPv << ", "
2014  << iCPw << ", "
2015  << activeDesignVariables_[3*cpI] << ", "
2016  << activeDesignVariables_[3*cpI + 1] << ", "
2017  << activeDesignVariables_[3*cpI + 2] << endl;
2018  }
2019  }
2020 }
2021 
2024 {
2025  parametricCoordinatesPtr_().write();
2026 }
2027 
2028 
2030 {
2031  cps_.writeEntry("controlPoints", os);
2032  return true;
2033 }
2034 
2035 
2036 // ************************************************************************* //
List< ReturnType > get(const UPtrList< T > &list, const AccessOp &aop)
List of values generated by applying the access operation to each list item.
void continuityRealatedConfinement()
Confine control point movement to maintain user-defined continuity.
vector volumeDerivativeU(const scalar u, const scalar v, const scalar w) const
Volume point derivative wrt u at point u,v,w.
label nWSymmetry() const
Get number of variables if CPs are moved symmetrically in W.
scalar diff(const triad &A, const triad &B)
Return a quantity of the difference between two triads.
Definition: triad.C:373
dictionary dict
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
tmp< vectorField > getPointsInBox()
Get mesh points that reside within the control points box.
A class for handling file names.
Definition: fileName.H:72
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
A face is a list of labels corresponding to mesh vertices.
Definition: face.H:68
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
autoPtr< labelList > reverseMapPtr_
Map of mesh points to points-in-box.
tmp< tensorField > patchDxDb(const label patchI, const label cpI)
Get patch dx/db.
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
void confineControlPoint(const label cpI)
Confine all three movements for a prescribed control point.
label start() const noexcept
Return start label of this patch in the polyMesh face list.
Definition: polyPatch.H:441
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
static const pointMesh & New(const polyMesh &mesh, Args &&... args)
Get existing or create a new MeshObject. Registered with typeName.
Definition: MeshObject.C:53
scalar minValue
T & ref() const
Return non-const reference to the contents of a non-null managed pointer.
Definition: tmpI.H:235
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
GeometricField< tensor, fvPatchField, volMesh > volTensorField
Definition: volFieldsFwd.H:86
dimensionedSphericalTensor inv(const dimensionedSphericalTensor &dt)
vectorField cps_
The volumetric B-Splines control points.
bool bound(vector &vec, scalar minValue=1e-7, scalar maxValue=0.999999) const
Bound components to certain limits.
const word dictName("faMeshDefinition")
dimensioned< vector > dimensionedVector
Dimensioned vector obtained from generic dimensioned type.
Tensor< scalar > tensor
Definition: symmTensor.H:57
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
void getIJK(label &i, label &j, label &k, const label cpID) const
Get I-J-K coordinates from control point ID.
A finiteVolume patch using a polyPatch and a fvBoundaryMesh.
Definition: fvPatch.H:70
tmp< tensorField > dndbBasedSensitivities(const label patchI, const label cpI, bool DimensionedNormalSens=true)
Part of control point sensitivities related to the face normal variations.
autoPtr< labelList > mapPtr_
Map of points-in-box to mesh points.
tmp< tensorField > patchDxDbFace(const label patchI, const label cpI)
Get patch dx/db.
Differentiation of the mesh data structure.
Definition: deltaBoundary.H:54
GeometricField< vector, pointPatchField, pointMesh > pointVectorField
vectorField makeFaceCentresAndAreas_d(const pointField &p, const pointField &p_d)
Given a face and the points to be moved in the normal direction, find faceArea, faceCentre and unitVe...
Definition: deltaBoundary.C:65
label k
Boltzmann constant.
Ignore writing from objectRegistry::writeObject()
const dimensionSet dimless
Dimensionless.
T get(const word &keyword, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a T. FatalIOError if not found, or if the number of tokens is incorrect.
label nUSymmetry() const
Get number of variables if CPs are moved symmetrically in U.
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
const pointVectorField & getParametricCoordinates()
Get parametric coordinates.
NURBS3DVolume morpher. Includes support functions for gradient computations Base class providing supp...
Definition: NURBS3DVolume.H:69
Macros for easy insertion into run-time selection tables.
GeometricField< tensor, pointPatchField, pointMesh > pointTensorField
static autoPtr< NURBS3DVolume > New(const dictionary &dict, const fvMesh &mesh, bool computeParamCoors=true)
Return a reference to the selected NURBS model.
void confineInertControlPoints()
Deactivate control points if they affect no mesh point.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
vectorField computeControlPointSensitivities(const pointVectorField &pointSens, const labelList &sensitivityPatchIDs)
Control point sensitivities computed using point-based surface sensitivities.
void determineActiveDesignVariablesAndPoints()
Create lists with active design variables and control points.
void makeFolders()
Create folders to store cps and derivatives.
word timeName
Definition: getTimeIndex.H:3
scalar maxValue
pointField points(const UList< point > &pts) const
Return the points corresponding to this face.
Definition: faceI.H:80
unsigned int count(const UList< bool > &bools, const bool val=true)
Count number of &#39;true&#39; entries.
Definition: BitOps.H:73
Calculates a unique integer (label so might not have enough room - 2G max) for processor + local inde...
Definition: globalIndex.H:61
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:38
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
tensor JacobianUVW(const vector &u) const
Jacobian matrix wrt to the volume parametric coordinates.
dynamicFvMesh & mesh
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition: POSIX.C:614
const pointField & points
void writeParamCoordinates() const
Write parametric coordinates.
label nPoints
virtual bool writeData(Ostream &os) const
Write the control points to support restart.
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
void setControlPoints(const vectorField &newCps)
Set new control points.
tmp< vectorField > computeNewBoundaryPoints(const vectorField &controlPointsMovement, const labelList &patchesToBeMoved, const bool moveCPs=true)
Boundary mesh movement based on given control point movement.
void confineControlPointsDirections()
Confine movement in all control points for user-defined directions.
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition: tmp.H:206
vector volumeDerivativeV(const scalar u, const scalar v, const scalar w) const
Volume point derivative wrt v at point u,v,w.
void findPointsInBox(const vectorField &meshPoints)
Find points within control points box.
Definition: NURBS3DVolume.C:43
Vector< scalar > vector
Definition: vector.H:57
label getCPID(const label i, const label j, const label k) const
Get control point ID from its I-J-K coordinates.
dimensioned< tensor > dimensionedTensor
Dimensioned tensor obtained from generic dimensioned type.
void boundControlPointMovement(vectorField &controlPointsMovement) const
Bound control points movement in the boundary control points and in certain directions if needed...
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
#define DebugInfo
Report an information message using Foam::Info.
tmp< Field< cmptType > > component(const direction) const
Return a component field of the field.
Definition: Field.C:608
Templated 3D Vector derived from VectorSpace adding construction from 3 components, element access using x(), y() and z() member functions and the inner-product (dot-product) and cross-product operators.
Definition: Vector.H:58
void cmptMag(FieldField< Field, Type > &cf, const FieldField< Field, Type > &f)
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:56
label nVSymmetry() const
Get number of variables if CPs are moved symmetrically in V.
void writeCps(const fileName &="cpsFile", const bool transform=true) const
Write control points on a cartesian coordinates system for visualization.
Vector< label > nSymmetry() const
Get number of variables per direction, if CPs are moved symmetrically.
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
defineRunTimeSelectionTable(reactionRateFlameArea, dictionary)
Type gMax(const FieldField< Field, Type > &f)
OBJstream os(runTime.globalPath()/outputName)
tmp< vectorField > coordinates(const vectorField &uVector) const
Compute cartesian coordinates based on control points and parametric coordinates. ...
defineTypeNameAndDebug(combustionModel, 0)
void computeParametricCoordinates(const vectorField &points)
Compute parametric coordinates in order to match a given set of coordinates, based on the cps of the ...
Definition: NURBS3DVolume.C:98
tmp< vectorField > computeNewPoints(const vectorField &controlPointsMovement)
Mesh movement based on given control point movement.
const labelList & getMap()
Get map of points in box to mesh points.
Field< tensor > tensorField
Specialisation of Field<T> for tensor.
void confineBoundaryControlPoints()
Confine movement in boundary control points if necessary.
PtrList< coordinateSystem > coordinates(solidRegions.size())
vector point
Point is a vector.
Definition: point.H:37
volScalarField & bound(volScalarField &, const dimensionedScalar &lowerBound)
Bound the given scalar field if it has gone unbounded.
Definition: bound.C:29
#define WarningInFunction
Report a warning using Foam::Warning.
vector volumeDerivativeW(const scalar u, const scalar v, const scalar w) const
Volume point derivative wrt w at point u,v,w.
NURBS3DVolume(const dictionary &dict, const fvMesh &mesh, bool computeParamCoors=true)
Construct from dictionary.
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1082
Nothing to be read.
Automatically write from objectRegistry::writeObject()
const std::string patch
OpenFOAM patch number as a std::string.
scalar computeMaxBoundaryDisplacement(const vectorField &controlPointsMovement, const labelList &patchesToBeMoved)
Compute max. displacement at the boundary.
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
Reduce inplace (cf. MPI Allreduce) using specified communication schedule.
messageStream Info
Information stream (stdout output on master, null elsewhere)
Internal & ref(const bool updateAccessTime=true)
Same as internalFieldRef()
const labelList & getReverseMap()
Get map of mesh points to points in box. Return -1 if point is outside the box.
Field< vector > vectorField
Specialisation of Field<T> for vector.
Pointer management similar to std::unique_ptr, with some additional methods and type checking...
Definition: HashPtrTable.H:48
T getOrDefault(const word &keyword, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a T, or return the given default value. FatalIOError if it is found and the number of...
tmp< pointTensorField > getDxDb(const label cpI)
Get dxCartesiandb for a certain control point.
List< label > labelList
A List of labels.
Definition: List.H:62
tmp< volTensorField > getDxCellsDb(const label cpI)
Get dxCartesiandb for a certain control point on cells.
A class for managing temporary objects.
Definition: HashPtrTable.H:50
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:69
dimensionSet transform(const dimensionSet &ds)
Return the argument; transformations do not change the dimensions.
Definition: dimensionSet.C:521
Tensor of scalars, i.e. Tensor<scalar>.
#define FatalIOErrorInLookup(ios, lookupTag, lookupName, lookupTable)
Report an error message using Foam::FatalIOError.
Definition: error.H:635
List< bool > boolList
A List of bools.
Definition: List.H:60
bool found
const Boundary & boundaryField() const noexcept
Return const-reference to the boundary field.
static autoPtr< controlPointsDefinition > New(NURBS3DVolume &box)
Return a reference to the selected controlPointsDefinition model.
Namespace for OpenFOAM.
static void listCombineReduce(List< T > &values, const CombineOp &cop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
After completion all processors have the same data.
T getOrDefaultCompat(const word &keyword, std::initializer_list< std::pair< const char *, int >> compat, const T &deflt, enum keyType::option matchOpt=keyType::REGEX) const
Find and return a T, or return the given default value using any compatibility names if needed...
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127
scalar volumeDerivativeCP(const vector &u, const label cpI) const
Volume point derivative wrt to control point cpI at point u,v,w.