Spline.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) 2025 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 "Spline.H"
27 
28 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
29 
30 template<class Derived>
32 (
33  const pointField& knots,
34  const scalar tol,
35  const label nIter
36 )
37 :
38  knots_(knots),
39  tol_(tol),
40  nIter_(nIter)
41 {}
42 
43 
44 template<class Derived>
46 :
47  knots_(knots),
48  tol_(rootSmall),
49  nIter_(16)
50 {}
51 
52 
53 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
54 
55 template<class Derived>
57 {
58  const Derived& derived = static_cast<const Derived&>(*this);
59 
60  // Quick return if the parameter is out of range
61  if (lambda <= 0) return start();
62  if (lambda >= 1) return end();
63 
64  // Evaluate the positions for the given local parameters
65  auto evaluatePoint = [&derived](const scalar& lambda)
66  {
67  const scalar l = lambda*derived.nSegments();
68  const label segmenti = floor(l);
69  const scalar segmentLambda = l - segmenti;
70  return derived.position(segmenti, segmentLambda);
71  };
72 
73  // Algorithm for iteratively improving adherence to the desired grading
74  scalar lambdaStar = lambda;
75  scalar error = vGreat;
76  for (label iter = 0; error > tol_ && iter < nIter_; ++ iter)
77  {
78  // Evaluate the position at the current local parameter
79  const point p = evaluatePoint(lambdaStar);
80 
81  // Calculate the actual local parameter based on cumulative distance
82  const scalar dist0 = mag(p - start());
83  const scalar dist1 = mag(p - end());
84  const scalar lambdaDist = dist0/(dist0 + dist1);
85 
86  // Update the error
87  error = mag(lambda - lambdaDist);
88 
89  // Interpolate a new parameter to try and space the cumulative distance
90  // as required by the original parameter
91  const scalar lambdaStar0 = lambda < lambdaDist ? 0 : lambdaStar;
92  const scalar lambdaStar1 = lambda < lambdaDist ? lambdaStar : 1;
93  const scalar lambdaDist0 = lambda < lambdaDist ? 0 : lambdaDist;
94  const scalar lambdaDist1 = lambda < lambdaDist ? lambdaDist : 1;
95  const scalar x =
96  (lambda - lambdaDist0)/max(lambdaDist1 - lambdaDist0, vSmall);
97  lambdaStar = (1 - x)*lambdaStar0 + x*lambdaStar1;
98  }
99 
100  // Evaluate the position at the final local parameter and return
101  return evaluatePoint(lambdaStar);
102 }
103 
104 
105 template<class Derived>
107 (
108  const scalarList& lambdasArg
109 ) const
110 {
111  const Derived& derived = static_cast<const Derived&>(*this);
112 
113  const label n = lambdasArg.size();
114 
115  // Clip the parameters to the bounds
116  const scalarField lambdas
117  (
118  min(max(scalarField(lambdasArg), scalar(0)), scalar(1))
119  );
120 
121  // Workspace
122  tmp<scalarField> tlambdasStar(new scalarField(lambdas));
123  tmp<scalarField> tlambdasStarNew(new scalarField(n));
124  scalarField lambdasDist(n);
125 
126  // Allocate the result
127  tmp<pointField> tpoints(new pointField(n));
128  pointField& points = tpoints.ref();
129 
130  // Evaluate the positions for the given local parameters
131  auto evaluatePoints = [&derived,&points](const scalarField& lambdas)
132  {
133  forAll(lambdas, i)
134  {
135  const scalar l = lambdas[i]*derived.nSegments();
136  const label segmenti = floor(l);
137  const scalar segmentLambda = l - segmenti;
138  points[i] = derived.position(segmenti, segmentLambda);
139  }
140  };
141 
142  // Algorithm for iteratively improving adherence to the desired grading
143  scalar error = vGreat;
144  for (label iter = 0; error > tol_ && iter < nIter_; ++ iter)
145  {
146  scalarField& lambdasStar = tlambdasStar.ref();
147  scalarField& lambdasStarNew = tlambdasStarNew.ref();
148 
149  // Evaluate the positions at the current local parameters
150  evaluatePoints(lambdasStar);
151 
152  // Calculate the actual local parameters based on cumulative distance
153  scalarField lambdasDist(n);
154  lambdasDist.first() = mag(points.first() - start());
155  for (label i = 1; i < n; ++ i)
156  {
157  lambdasDist[i] =
158  lambdasDist[i - 1] + mag(points[i] - points[i - 1]);
159  }
160  lambdasDist /= lambdasDist.last() + mag(end() - points.last());
161 
162  // Update the error
163  error = -vGreat;
164  forAll(lambdas, i)
165  {
166  error = max(error, mag(lambdas[i] - lambdasDist[i]));
167  }
168 
169  // Interpolate new parameters to try and space the cumulative distance
170  // as required by the original parameters
171  label iNew = 0;
172  for (label i = 0; i <= n; ++ i)
173  {
174  const scalar lambdaStar0 = i == 0 ? 0 : lambdasStar[i - 1];
175  const scalar lambdaStar1 = i < n ? lambdasStar[i] : 1;
176 
177  const scalar lambdaDist0 = i == 0 ? 0 : lambdasDist[i - 1];
178  const scalar lambdaDist1 = i < n ? lambdasDist[i] : 1;
179 
180  while (iNew < n && lambdas[iNew] <= lambdaDist1)
181  {
182  const scalar x =
183  (lambdas[iNew] - lambdaDist0)
184  /max(lambdaDist1 - lambdaDist0, vSmall);
185 
186  lambdasStarNew[iNew] = (1 - x)*lambdaStar0 + x*lambdaStar1;
187 
188  iNew ++;
189  }
190  }
191 
192  // Set the old parameters to the new ones and iterate
193  Swap(tlambdasStar, tlambdasStarNew);
194  }
195 
196  // Evaluate the positions at the final local parameters
197  evaluatePoints(tlambdasStar());
198 
199  return tpoints;
200 }
201 
202 
203 // ************************************************************************* //
label n
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
point position(const scalar lambda) const
Return the point position corresponding to the curve parameter.
Definition: Spline.C:56
Spline(const pointField &knots, const scalar tol, const label nIter)
Construct from components.
Definition: Spline.C:32
T & first()
Return the first element of the list.
Definition: UListI.H:114
T & last()
Return the last element of the list.
Definition: UListI.H:128
Class to handle errors and exceptions in a simple, consistent stream-based manner.
Definition: error.H:71
A class for managing temporary objects.
Definition: tmp.H:55
T & ref() const
Return non-const reference or generate a fatal error.
Definition: tmpI.H:197
const pointField & points
dimensionedScalar lambda(viscosity->lookup("lambda"))
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
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
void mag(LagrangianPatchField< scalar > &f, const LagrangianPatchField< Type > &f1)
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
void Swap(T &a, T &b)
Definition: Swap.H:43
volScalarField & p