NuTo
Numerics Tool
Nurbs.h
Go to the documentation of this file.
1 #pragma once
2 
4 #include "nuto/base/Exception.h"
5 #include <array>
6 #include <eigen3/Eigen/Dense>
7 #include <iostream>
8 #include <vector>
9 
10 namespace NuTo
11 {
17 template <int TDimParameter>
18 class Nurbs
19 {
20 public:
22  {
25  };
26 
29  Nurbs(const std::array<std::vector<double>, TDimParameter>& knots,
34  const std::vector<std::vector<NodeSimple*>>& controlPoints, const std::vector<std::vector<double>>& weights,
35  const std::array<int, TDimParameter>& degree)
36  : mKnots(knots)
37  , mControlPoints(controlPoints)
38  , mWeights(weights)
39  , mDegree(degree)
40  {
41  }
42 
45  int GetDimension() const
48  {
49  return mControlPoints[0][0]->GetNumValues();
50  }
51 
54  int GetNumControlPointsElement(int dir) const
55  {
56  assert(dir <= TDimParameter && dir >= 0);
57  return mDegree[dir] + 1;
58  }
59 
63  {
64  int numCPs = 1;
65  for (int degree : mDegree)
66  numCPs *= degree + 1;
67 
68  return numCPs;
69  }
70 
73  std::array<Eigen::Vector2d, TDimParameter> GetKnotVectorElement(std::array<int, TDimParameter> knotIDs) const
74  {
75  std::array<Eigen::Vector2d, TDimParameter> knots;
76  Eigen::Vector2d knotCoordinates;
77  for (int i = 0; i < TDimParameter; i++)
78  {
79  knotCoordinates(0) = mKnots[i][knotIDs[i]];
80  knotCoordinates(1) = mKnots[i][knotIDs[i] + 1];
81  knots[i] = knotCoordinates;
82  }
83  return knots;
84  }
85 
86  Eigen::VectorXd GetControlPointsElement(const std::array<int, TDimParameter>& knotID, int instance = 0) const;
89  Eigen::VectorXd Evaluate(const Eigen::Matrix<double, TDimParameter, 1>& parameter, int derivativeOrder = 0) const
90  {
91  const std::array<int, TDimParameter> spanIdx = FindSpan(parameter);
92  Eigen::MatrixXd shapeFunctions = BasisFunctionsAndDerivativesRational(derivativeOrder, parameter);
93 
94  Eigen::VectorXd coordinates(GetDimension());
95  coordinates.setZero(GetDimension());
96 
97  Eigen::VectorXd cpCoords = GetControlPointsElement(spanIdx);
98 
99  int numCPs = GetNumControlPointsElement();
100 
101  assert(numCPs == shapeFunctions.rows());
102 
103  int dim = GetDimension();
104 
105  for (int i = 0; i < dim; i++)
106  for (int j = 0; j < numCPs; j++)
107  coordinates(i) += shapeFunctions(j, 0) * cpCoords(dim * j + i);
108 
109  return coordinates;
110  }
111 
114  static int FindSpan(double parameter, int degree, const std::vector<double>& knots)
115  {
116  int size = knots.size();
117  int iterations = 0;
118 
119  if (parameter < knots[0] || parameter > knots[size - 1])
120  throw NuTo::Exception(__PRETTY_FUNCTION__, "The parameter is out of the range of the knot vector.");
121 
122  int numBasisFuns = size - degree - 1;
123  if (parameter == knots[numBasisFuns])
124  return numBasisFuns - 1;
125 
126  int low = degree;
127  int high = numBasisFuns;
128  int mid = (low + high) / 2;
129 
130  while (parameter < knots[mid] || parameter >= knots[mid + 1])
131  {
132  if (parameter < knots[mid])
133  high = mid;
134  else
135  low = mid;
136 
137  mid = (low + high) / 2;
138  iterations++;
139  if (iterations > size)
140  throw NuTo::Exception(__PRETTY_FUNCTION__,
141  "The maximum number of iterations for finding the span exceeded.");
142  }
143  return mid;
144  }
145 
154  static Eigen::MatrixXd BasisFunctionsAndDerivatives(int derivativeOrder, double parameter, int spanIdx, int degree,
155  const std::vector<double>& knots)
156  {
157  // store shape functions and knot differences
158  Eigen::MatrixXd ndu(degree + 1, degree + 1);
159 
160  ndu(0, 0) = 1.0;
161 
162  Eigen::VectorXd left(degree + 1);
163  Eigen::VectorXd right(degree + 1);
164 
165  for (int j = 1; j <= degree; j++)
166  {
167  left[j] = parameter - knots[spanIdx + 1 - j];
168  right[j] = knots[spanIdx + j] - parameter;
169  double saved = 0.;
170  for (int r = 0; r < j; r++)
171  {
172  ndu(j, r) = right[r + 1] + left[j - r]; // lower triange (knot differences)
173  double temp = ndu(r, j - 1) / ndu(j, r);
174  ndu(r, j) = saved + right[r + 1] * temp; // upper triangle
175  saved = left[j - r] * temp;
176  }
177  ndu(j, j) = saved;
178  }
179 
180  Eigen::MatrixXd basisFctsDerivatives(derivativeOrder + 1, degree + 1);
181 
182  for (int j = 0; j <= degree; j++)
183  basisFctsDerivatives(0, j) = ndu(j, degree);
184 
185  // the shape functions
186  Eigen::MatrixXd a(2, degree + 1);
187  for (int r = 0; r <= degree; r++)
188  {
189  int s1 = 0;
190  int s2 = 1;
191 
192  a(0, 0) = 1.0;
193 
194  int j = 0;
195  for (int k = 1; k <= derivativeOrder; k++)
196  {
197  double d = 0.;
198  int rk = r - k;
199  int pk = degree - k;
200  if (r >= k)
201  {
202  a(s2, 0) = a(s1, 0) / ndu(pk + 1, rk);
203  d = a(s2, 0) * ndu(rk, pk);
204  }
205  int j1 = 0;
206  if (rk >= -1)
207  j1 = 1;
208  else
209  j1 = -rk;
210 
211  int j2 = 0;
212  if (r - 1 <= pk)
213  j2 = k - 1;
214  else
215  j2 = degree - r;
216 
217  for (j = j1; j <= j2; j++)
218  {
219  a(s2, j) = (a(s1, j) - a(s1, j - 1)) / ndu(pk + 1, rk + j);
220  d += a(s2, j) * ndu(rk + j, pk);
221  }
222 
223  if (r <= pk)
224  {
225  a(s2, k) = -a(s1, k - 1) / ndu(pk + 1, r);
226  d += a(s2, k) * ndu(r, pk);
227  }
228 
229  basisFctsDerivatives(k, r) = d;
230  j = s1;
231  s1 = s2;
232  s2 = j;
233  }
234  }
235 
236  int r = degree;
237  for (int k = 1; k <= derivativeOrder; k++)
238  {
239  for (int j = 0; j <= degree; j++)
240  basisFctsDerivatives(k, j) *= r;
241  r *= (degree - k);
242  }
243 
244  return basisFctsDerivatives;
245  }
246 
255  static Eigen::VectorXd BasisFunctionsAndDerivativesRational(int derivativeOrder, double parameter, int spanIdx,
256  int degree, const std::vector<double>& knots,
257  const std::vector<double>& weights)
258  {
259  assert(derivativeOrder >= 0 && derivativeOrder <= 2);
260 
261  Eigen::MatrixXd ders = BasisFunctionsAndDerivatives(derivativeOrder, parameter, spanIdx, degree, knots);
262 
263  // NURBS specific ...
264  Eigen::VectorXd sum(derivativeOrder + 1);
265  sum.setZero(derivativeOrder + 1);
266 
267  for (int i = 0; i <= degree; i++)
268  {
269  double weight = weights[spanIdx - degree + i];
270  if (derivativeOrder == 0)
271  sum(0) += ders(0, i) * weight;
272  else if (derivativeOrder == 1)
273  {
274  sum(0) += ders(0, i) * weight;
275  sum(1) += ders(1, i) * weight;
276  }
277  else
278  {
279  sum(0) += ders(0, i) * weight;
280  sum(1) += ders(1, i) * weight;
281  sum(2) += ders(2, i) * weight;
282  }
283  }
284 
285  Eigen::VectorXd basisFctsDerivativesRational(degree + 1);
286  basisFctsDerivativesRational.setZero(degree + 1);
287 
288  for (int i = 0; i <= degree; i++)
289  {
290  double weight = weights[spanIdx - degree + i];
291  if (derivativeOrder == 0)
292  basisFctsDerivativesRational(i) = ders(0, i) * weight / sum(0);
293  else if (derivativeOrder == 1)
294  basisFctsDerivativesRational(i) =
295  (ders(1, i) * sum(0) - ders(0, i) * sum(1)) * weight / (sum(0) * sum(0));
296  else
297  {
298  double sum2 = sum(0) * sum(0);
299  basisFctsDerivativesRational(i) =
300  weight * (ders(2, i) / sum(0) - 2 * ders(1, i) * sum(1) / (sum2)-ders(0, i) * sum(2) / (sum2) +
301  2 * ders(0, i) * sum(1) * sum(1) / (sum2 * sum(0)));
302  }
303  }
304 
305  return basisFctsDerivativesRational;
306  }
307 
311  const std::array<int, TDimParameter> FindSpan(const Eigen::Matrix<double, TDimParameter, 1>& parameter) const
312  {
313  std::array<int, TDimParameter> parameterIDs;
314  for (int i = 0; i < parameter.rows(); i++)
315  {
316  parameterIDs[i] = FindSpan(parameter[i], mDegree[i], mKnots[i]);
317  }
318  return parameterIDs;
319  }
320 
321  Eigen::MatrixXd
323 
324 private:
327  std::array<std::vector<double>, TDimParameter> mKnots;
328 
330  std::vector<std::vector<NodeSimple*>> mControlPoints;
331 
333  std::vector<std::vector<double>> mWeights;
334 
336  std::array<int, TDimParameter> mDegree;
337 };
338 
339 template <>
340 inline Eigen::VectorXd Nurbs<1>::GetControlPointsElement(const std::array<int, 1>& knotID, int instance) const
341 {
342  assert(knotID[0] >= mDegree[0]);
343  int dim = GetDimension();
344 
345  int numCPs = GetNumControlPointsElement();
346 
347  Eigen::VectorXd nodeValues(numCPs * dim);
348 
349  for (int i = 0; i < numCPs; i++)
350  nodeValues.segment(dim * i, dim) = mControlPoints[0][knotID[0] - mDegree[0] + i]->GetValues(instance);
351 
352  return nodeValues;
353 }
354 
355 template <>
356 inline Eigen::VectorXd Nurbs<2>::GetControlPointsElement(const std::array<int, 2>& knotID, int instance) const
357 {
358  assert(knotID[0] >= mDegree[0] && knotID[1] >= mDegree[1]);
359  int dim = GetDimension();
360 
361  int numCPs = GetNumControlPointsElement();
362 
363  Eigen::VectorXd nodeValues(numCPs * dim);
364 
365  int count = 0;
366  for (int i = 0; i <= mDegree[1]; i++)
367  {
368  for (int j = 0; j <= mDegree[0]; j++)
369  {
370  nodeValues.segment(count, dim) =
371  mControlPoints[knotID[1] - mDegree[1] + i][knotID[0] - mDegree[0] + j]->GetValues(instance);
372  count += dim;
373  }
374  }
375  return nodeValues;
376 }
377 
378 template <>
379 inline Eigen::MatrixXd
381 {
382  assert(der >= 0 && der <= 2);
383 
384  int spanIdx = FindSpan(parameter)[0];
385  Eigen::MatrixXd ders = BasisFunctionsAndDerivatives(der, parameter[0], spanIdx, mDegree[0], mKnots[0]);
386 
387  // NURBS specific ...
388  Eigen::VectorXd sum = Eigen::VectorXd::Zero(der + 1);
389 
390  for (int i = 0; i <= mDegree[0]; i++)
391  {
392  double weight = mWeights[0][spanIdx - mDegree[0] + i];
393  if (der == 0)
394  sum(0) += ders(0, i) * weight;
395  else if (der == 1)
396  {
397  sum(0) += ders(0, i) * weight;
398  sum(1) += ders(1, i) * weight;
399  }
400  else
401  {
402  sum(0) += ders(0, i) * weight;
403  sum(1) += ders(1, i) * weight;
404  sum(2) += ders(2, i) * weight;
405  }
406  }
407 
408  Eigen::VectorXd dersRat(mDegree[0] + 1);
409  dersRat.setZero(mDegree[0] + 1);
410 
411  for (int i = 0; i <= mDegree[0]; i++)
412  {
413  double weight = mWeights[0][spanIdx - mDegree[0] + i];
414  if (der == 0)
415  dersRat(i) = ders(0, i) * weight / sum(0);
416  else if (der == 1)
417  dersRat(i) = (ders(1, i) * sum(0) - ders(0, i) * sum(1)) * weight / (sum(0) * sum(0));
418  else
419  {
420  double sum2 = sum(0) * sum(0);
421  dersRat(i) = weight * (ders(2, i) / sum(0) - 2 * ders(1, i) * sum(1) / (sum2)-ders(0, i) * sum(2) / (sum2) +
422  2 * ders(0, i) * sum(1) * sum(1) / (sum2 * sum(0)));
423  }
424  }
425  return dersRat;
426 }
427 
428 template <>
429 inline Eigen::MatrixXd
431 {
432  assert(der >= 0 && der <= 2);
433 
434  const std::array<int, 2> spanIdx = FindSpan(parameter);
435  std::array<Eigen::MatrixXd, 2> shapeFunctions;
436 
437  int numDers = 1;
438  for (int i = 0; i < 2; i++)
439  {
440  shapeFunctions[i] = BasisFunctionsAndDerivatives(der, parameter[i], spanIdx[i], mDegree[i], mKnots[i]);
441  numDers *= mDegree[i] + 1;
442  }
443 
444  Eigen::MatrixXd ders(numDers, der + 1);
445  ders.setZero(numDers, der + 1);
446 
447  int num = ((der + 1) * (der + 1) + (der + 1)) / 2;
448  Eigen::VectorXd sum(num);
449  sum.setZero(num);
450 
451  for (int i = 0; i <= mDegree[1]; i++)
452  {
453  for (int j = 0; j <= mDegree[0]; j++)
454  {
455  double weight = mWeights[spanIdx[1] - mDegree[1] + i][spanIdx[0] - mDegree[0] + j];
456 
457  if (der == 0)
458  {
459  sum(0) += shapeFunctions[0](0, j) * shapeFunctions[1](0, i) * weight;
460  }
461  else if (der == 1)
462  {
463  sum(0) += shapeFunctions[0](0, j) * shapeFunctions[1](0, i) * weight;
464  sum(1) += shapeFunctions[0](1, j) * shapeFunctions[1](0, i) * weight;
465  sum(2) += shapeFunctions[0](0, j) * shapeFunctions[1](1, i) * weight;
466  }
467  else
468  {
469  sum(0) += shapeFunctions[0](0, j) * shapeFunctions[1](0, i) * weight;
470  sum(1) += shapeFunctions[0](1, j) * shapeFunctions[1](0, i) * weight;
471  sum(2) += shapeFunctions[0](2, j) * shapeFunctions[1](0, i) * weight;
472 
473  sum(3) += shapeFunctions[0](0, j) * shapeFunctions[1](1, i) * weight;
474  sum(4) += shapeFunctions[0](0, j) * shapeFunctions[1](2, i) * weight;
475 
476  sum(5) += shapeFunctions[0](1, j) * shapeFunctions[1](1, i) * weight;
477  }
478  }
479  }
480 
481  int count = 0;
482  for (int i = 0; i <= mDegree[1]; i++)
483  {
484  for (int j = 0; j <= mDegree[0]; j++)
485  {
486  double weight = mWeights[spanIdx[1] - mDegree[1] + i][spanIdx[0] - mDegree[0] + j];
487 
488  if (der == 0)
489  {
490  ders(count, 0) = shapeFunctions[0](0, j) * shapeFunctions[1](0, i) * weight / sum(0);
491  count++;
492  }
493  else if (der == 1)
494  {
495  ders(count, 0) = (shapeFunctions[0](1, j) * shapeFunctions[1](0, i) * sum(0) -
496  shapeFunctions[0](0, j) * shapeFunctions[1](0, i) * sum(1)) *
497  weight / (sum(0) * sum(0));
498  ders(count, 1) = (shapeFunctions[0](0, j) * shapeFunctions[1](1, i) * sum(0) -
499  shapeFunctions[0](0, j) * shapeFunctions[1](0, i) * sum(2)) *
500  weight / (sum(0) * sum(0));
501  count++;
502  }
503  else
504  {
505  ders(count, 0) = (shapeFunctions[0](2, j) * shapeFunctions[1](0, i) / sum(0) -
506  2 * shapeFunctions[0](1, j) * shapeFunctions[1](0, i) * sum(1) / (sum(0) * sum(0)) -
507  shapeFunctions[0](0, j) * shapeFunctions[1](0, i) * sum(2) / (sum(0) * sum(0)) +
508  2 * shapeFunctions[0](0, j) * shapeFunctions[1](0, i) * sum(1) * sum(1) /
509  (sum(0) * sum(0) * sum(0))) *
510  weight;
511 
512  ders(count, 1) = (shapeFunctions[0](0, j) * shapeFunctions[1](2, i) / sum(0) -
513  2 * shapeFunctions[0](0, j) * shapeFunctions[1](1, i) * sum(3) / (sum(0) * sum(0)) -
514  shapeFunctions[0](0, j) * shapeFunctions[1](0, i) * sum(4) / (sum(0) * sum(0)) +
515  2 * shapeFunctions[0](0, j) * shapeFunctions[0](0, i) * sum(3) * sum(3) /
516  (sum(0) * sum(0) * sum(0))) *
517  weight;
518 
519  ders(count, 2) =
520  (shapeFunctions[0](1, j) * shapeFunctions[1](1, i) / sum(0) -
521  shapeFunctions[0](1, j) * shapeFunctions[1](0, i) * sum(3) / (sum(0) * sum(0)) -
522  shapeFunctions[0](0, j) * shapeFunctions[1](1, i) * sum(1) / (sum(0) * sum(0)) -
523  shapeFunctions[0](1, j) * shapeFunctions[1](0, i) * sum(5) * sum(5) / (sum(0) * sum(0)) +
524  2 * shapeFunctions[0](0, j) * shapeFunctions[1](0, i) * sum(1) * sum(3) /
525  (sum(0) * sum(0) * sum(0))) *
526  weight;
527  count++;
528  }
529  }
530  }
531 
532  return ders;
533 }
534 }
std::array< Eigen::Vector2d, TDimParameter > GetKnotVectorElement(std::array< int, TDimParameter > knotIDs) const
get the knots to given knot ids
Definition: Nurbs.h:73
Definition: Nurbs.h:23
coordinates
Definition: DamageBar.py:13
const NuTo::DofType d("...", 1)
int GetNumControlPointsElement() const
get the number of control points for each IGA element (one parametric span)
Definition: Nurbs.h:62
Base class for all exceptions thrown in NuTo.
Definition: Exception.h:9
static Eigen::MatrixXd BasisFunctionsAndDerivatives(int derivativeOrder, double parameter, int spanIdx, int degree, const std::vector< double > &knots)
calculates the basisfunctions and derivatives, see Piegl/Tiller &#39;NURBS Book&#39; 2nd ed., Page 72
Definition: Nurbs.h:154
Eigen::VectorXd GetControlPointsElement(const std::array< int, TDimParameter > &knotID, int instance=0) const
Class for NURBS curves, with IGA specific functions. NURBS specific algorithms taken from Piegl...
Definition: Nurbs.h:18
Nurbs(const std::array< std::vector< double >, TDimParameter > &knots, const std::vector< std::vector< NodeSimple * >> &controlPoints, const std::vector< std::vector< double >> &weights, const std::array< int, TDimParameter > &degree)
Constructors.
Definition: Nurbs.h:33
const std::array< int, TDimParameter > FindSpan(const Eigen::Matrix< double, TDimParameter, 1 > &parameter) const
calculates the knot span to given parameters
Definition: Nurbs.h:311
static int FindSpan(double parameter, int degree, const std::vector< double > &knots)
Basis Functions.
Definition: Nurbs.h:114
int GetDimension() const
Getter.
Definition: Nurbs.h:47
int GetNumControlPointsElement(int dir) const
get the number of control points for each IGA element (one parametric span) in a specific direction ...
Definition: Nurbs.h:54
mParametrization
Definition: Nurbs.h:21
Definition: Exception.h:6
Definition: SerializeStreamOut.h:9
Definition: Nurbs.h:24
static Eigen::VectorXd BasisFunctionsAndDerivativesRational(int derivativeOrder, double parameter, int spanIdx, int degree, const std::vector< double > &knots, const std::vector< double > &weights)
calculates the basisfunctions and derivatives, see Piegl/Tiller &#39;NURBS Book&#39; 2nd ed., Page 72
Definition: Nurbs.h:255
constexpr int dim
Definition: ConstraintNodeToElement2D.cpp:24
Eigen::VectorXd Evaluate(const Eigen::Matrix< double, TDimParameter, 1 > &parameter, int derivativeOrder=0) const
Evaluation.
Definition: Nurbs.h:89