Bembel
Patch.hpp
1 // This file is part of Bembel, the higher order C++ boundary element library.
2 //
3 // Copyright (C) 2022 see <http://www.bembel.eu>
4 //
5 // It was written as part of a cooperation of J. Doelz, H. Harbrecht, S. Kurz,
6 // M. Multerer, S. Schoeps, and F. Wolf at Technische Universitaet Darmstadt,
7 // Universitaet Basel, and Universita della Svizzera italiana, Lugano. This
8 // source code is subject to the GNU General Public License version 3 and
9 // provided WITHOUT ANY WARRANTY, see <http://www.bembel.eu> for further
10 // information.
11 #ifndef BEMBEL_SRC_GEOMETRY_PATCH_HPP_
12 #define BEMBEL_SRC_GEOMETRY_PATCH_HPP_
13 namespace Bembel {
14 
20 class Patch {
21  public:
25 
28  Patch() {}
42  Patch(const std::vector<Eigen::Matrix<double, -1, -1>> &control_points,
43  const std::vector<double> &knots_x,
44  const std::vector<double> &knots_y) {
45  init_Patch(control_points, knots_x, knots_y);
46  }
50 
63  inline void init_Patch(const std::vector<Eigen::Matrix<double, -1, -1>> &xyzw,
64  const std::vector<double> &x_knots,
65  const std::vector<double> &y_knots) {
66  assert(xyzw.size() == 4);
67  const int xyzw_cols = xyzw[0].cols();
68  const int xyzw_rows = xyzw[0].rows();
69  unique_knots_x_ = Spl::ExtractUniqueKnotVector(x_knots);
70  unique_knots_y_ = Spl::ExtractUniqueKnotVector(y_knots);
71  const int xnumpatch = unique_knots_x_.size() - 1;
72  const int ynumpatch = unique_knots_y_.size() - 1;
73  polynomial_degree_x_ = x_knots.size() - xyzw_cols;
74  polynomial_degree_y_ = y_knots.size() - xyzw_rows;
75  data_.resize(4 * (polynomial_degree_x_ * xnumpatch * polynomial_degree_y_ *
76  ynumpatch));
77  {
78  // Since its only for initialization, I do not care about speed.
79  // Here I look weather the data given is already in bezier form.
80  if (unique_knots_x_.size() == 2 && unique_knots_y_.size() == 2) {
81  for (int i = 0; i < 4; i++) {
82  Eigen::Matrix<double, -1, 1> tmp = Spl::Unroll(xyzw[i]);
83  for (int j = 0; j < tmp.rows(); j++) data_[j * 4 + i] = (tmp[j]);
84  }
85  } else {
86  // If not, I construct the dynamic projection (i.e. solve
87  // systems for
88  // the coeffs) and project to the superspace.
89 
90  Eigen::SparseMatrix<double> phi = Spl::MakeProjection(
91  x_knots, y_knots, unique_knots_x_, unique_knots_y_,
92  polynomial_degree_x_, polynomial_degree_y_);
93 
94  for (int i = 0; i < 4; i++) {
95  Eigen::Matrix<double, -1, 1> tmp =
96  Spl::Unroll(xyzw[i]).transpose() * phi.transpose();
97 
98  for (int j = 0; j < tmp.rows(); j++) data_[j * 4 + i] = (tmp[j]);
99  }
100  }
101  }
102 
103  return;
104  }
115  Eigen::Vector3d eval(const Eigen::Vector2d &reference_point) const {
116  const int x_location =
117  Spl::FindLocationInKnotVector(reference_point(0), unique_knots_x_);
118  const int y_location =
119  Spl::FindLocationInKnotVector(reference_point(1), unique_knots_y_);
120  const int numy = (unique_knots_y_.size() - 1) * polynomial_degree_y_;
121  const double scaledx =
122  Spl::Rescale(reference_point(0), unique_knots_x_[x_location],
123  unique_knots_x_[x_location + 1]);
124  const double scaledy =
125  Spl::Rescale(reference_point(1), unique_knots_y_[y_location],
126  unique_knots_y_[y_location + 1]);
127 
128  double *xbasis = new double[polynomial_degree_x_];
129  double *ybasis = new double[polynomial_degree_y_];
130 
131  Bembel::Basis::ShapeFunctionHandler::evalBasis(polynomial_degree_x_ - 1,
132  xbasis, scaledx);
133  Bembel::Basis::ShapeFunctionHandler::evalBasis(polynomial_degree_y_ - 1,
134  ybasis, scaledy);
135 
136  double tmp[4] = {0., 0., 0., 0.};
137 
138  for (int i = 0; i < polynomial_degree_x_; i++) {
139  for (int j = 0; j < polynomial_degree_y_; j++) {
140  const double tpbasisval = xbasis[i] * ybasis[j];
141  const int accs = 4 * (numy * (polynomial_degree_x_ * x_location + i) +
142  polynomial_degree_y_ * y_location + j);
143 #pragma omp simd
144  for (int k = 0; k < 4; k++) tmp[k] += data_[accs + k] * tpbasisval;
145  }
146  }
147 
148  delete[] xbasis;
149  delete[] ybasis;
150 
151  Eigen::Vector3d out(tmp[0], tmp[1], tmp[2]);
152  // Rescaling by the NRBS weight, i.e. projection to 3D from 4D hom
153 
154  return out / tmp[3];
155  }
156 
163  Eigen::Matrix<double, 3, 2> evalJacobian(
164  const Eigen::Vector2d &reference_point) const {
165  const int x_location =
166  Spl::FindLocationInKnotVector(reference_point(0), unique_knots_x_);
167  const int y_location =
168  Spl::FindLocationInKnotVector(reference_point(1), unique_knots_y_);
169  const int numy = (unique_knots_y_.size() - 1) * polynomial_degree_y_;
170  const double scaledx =
171  Spl::Rescale(reference_point(0), unique_knots_x_[x_location],
172  unique_knots_x_[x_location + 1]);
173  const double scaledy =
174  Spl::Rescale(reference_point(1), unique_knots_y_[y_location],
175  unique_knots_y_[y_location + 1]);
176 
177  double *xbasis = new double[polynomial_degree_x_];
178  double *ybasis = new double[polynomial_degree_y_];
179  double *xbasisD = new double[polynomial_degree_x_];
180  double *ybasisD = new double[polynomial_degree_y_];
181 
182  Bembel::Basis::ShapeFunctionHandler::evalBasis(polynomial_degree_x_ - 1,
183  xbasis, scaledx);
184  Bembel::Basis::ShapeFunctionHandler::evalBasis(polynomial_degree_y_ - 1,
185  ybasis, scaledy);
186  Bembel::Basis::ShapeFunctionHandler::evalDerBasis(polynomial_degree_x_ - 1,
187  xbasisD, scaledx);
188  Bembel::Basis::ShapeFunctionHandler::evalDerBasis(polynomial_degree_y_ - 1,
189  ybasisD, scaledy);
190 
191  double tmp[4] = {0., 0., 0., 0.};
192  double tmpDx[4] = {0., 0., 0., 0.};
193  double tmpDy[4] = {0., 0., 0., 0.};
194 
195  for (int i = 0; i < polynomial_degree_x_; i++) {
196  for (int j = 0; j < polynomial_degree_y_; j++) {
197  const double tpbasisval = xbasis[i] * ybasis[j];
198  const double tpbasisvalDx = xbasisD[i] * ybasis[j];
199  const double tpbasisvalDy = xbasis[i] * ybasisD[j];
200  const int accs = 4 * (numy * (polynomial_degree_x_ * x_location + i) +
201  polynomial_degree_y_ * y_location + j);
202 
203  // Here I add up the values of the basis functions in the dc
204  // basis
205 #pragma omp simd
206  for (int k = 0; k < 4; k++) {
207  tmp[k] += data_[accs + k] * tpbasisval;
208  tmpDx[k] += data_[accs + k] * tpbasisvalDx;
209  tmpDy[k] += data_[accs + k] * tpbasisvalDy;
210  }
211  }
212  }
213 
214  delete[] xbasis;
215  delete[] ybasis;
216  delete[] xbasisD;
217  delete[] ybasisD;
218 
219  Eigen::Matrix<double, 3, 2> out;
220 
221  // Eigen::Vector3d out;
222 
223  double bot = 1. / (tmp[3] * tmp[3]);
224 
225 #pragma omp simd
226  for (int k = 0; k < 3; k++) {
227  out(k, 0) = (tmpDx[k] * tmp[3] - tmp[k] * tmpDx[3]) * bot;
228  out(k, 1) = (tmpDy[k] * tmp[3] - tmp[k] * tmpDy[3]) * bot;
229  }
230 
231  return out;
232  }
233 
240  inline Eigen::Matrix<double, 3, 1> evalNormal(
241  const Eigen::Vector2d &reference_point) const {
242  Eigen::Matrix<double, 3, 2> jac = evalJacobian(reference_point);
243  return jac.col(0).cross(jac.col(1));
244  }
245 
257  inline Eigen::Vector3d eval(double x, double y) const {
258  return eval(Eigen::Vector2d(x, y));
259  }
267  inline Eigen::Matrix<double, 3, 2> evalJacobian(double x, double y) const {
268  return evalJacobian(Eigen::Vector2d(x, y));
269  }
277  inline Eigen::Matrix<double, 3, 1> evalNormal(double x, double y) const {
278  return evalNormal(Eigen::Vector2d(x, y));
279  }
280 
281  //
295  const Eigen::Vector2d &ref_pt, double w,
296  const Eigen::Vector2d &xi) const {
297  const int x_location =
298  Spl::FindLocationInKnotVector(ref_pt(0), unique_knots_x_);
299  const int y_location =
300  Spl::FindLocationInKnotVector(ref_pt(1), unique_knots_y_);
301  const int numy = (unique_knots_y_.size() - 1) * polynomial_degree_y_;
302  const double scaledx = Spl::Rescale(ref_pt(0), unique_knots_x_[x_location],
303  unique_knots_x_[x_location + 1]);
304  const double scaledy = Spl::Rescale(ref_pt(1), unique_knots_y_[y_location],
305  unique_knots_y_[y_location + 1]);
306 
307  // TODO(Felix) Do not use variable-length arrays in accordance to Google
308  // Style guide
309  double *buffer =
310  new double[2 * (polynomial_degree_x_ + polynomial_degree_y_) + 12];
311  for (int i = 0; i < 12; ++i) buffer[i] = 0;
312 
313  double *tmp = buffer;
314  double *tmpDx = tmp + 4;
315  double *tmpDy = tmpDx + 4;
316  double *xbasis = tmpDy + 4;
317  double *ybasis = xbasis + polynomial_degree_x_;
318  double *xbasisD = ybasis + polynomial_degree_y_;
319  double *ybasisD = xbasisD + polynomial_degree_x_;
320 
321  Bembel::Basis::ShapeFunctionHandler::evalBasis(polynomial_degree_x_ - 1,
322  xbasis, scaledx);
323  Bembel::Basis::ShapeFunctionHandler::evalBasis(polynomial_degree_y_ - 1,
324  ybasis, scaledy);
325  Bembel::Basis::ShapeFunctionHandler::evalDerBasis(polynomial_degree_x_ - 1,
326  xbasisD, scaledx);
327  Bembel::Basis::ShapeFunctionHandler::evalDerBasis(polynomial_degree_y_ - 1,
328  ybasisD, scaledy);
329 
330  for (int i = 0; i < polynomial_degree_x_; ++i) {
331  for (int j = 0; j < polynomial_degree_y_; ++j) {
332  const double tpbasisval = xbasis[i] * ybasis[j];
333  const double tpbasisvalDx = xbasisD[i] * ybasis[j];
334  const double tpbasisvalDy = xbasis[i] * ybasisD[j];
335  const int accs = 4 * (numy * (polynomial_degree_x_ * x_location + i) +
336  polynomial_degree_y_ * y_location + j);
337 
338  // Here I add up the values of the basis functions in the dc
339  // basis
340  for (int k = 0; k < 4; ++k) {
341  tmp[k] += data_[accs + k] * tpbasisval;
342  tmpDx[k] += data_[accs + k] * tpbasisvalDx;
343  tmpDy[k] += data_[accs + k] * tpbasisvalDy;
344  }
345  }
346  }
347 
348  const double bot = 1. / tmp[3];
349  const double botsqr = bot * bot;
350 
351  (*srf_pt)(0) = xi(0);
352  (*srf_pt)(1) = xi(1);
353  (*srf_pt)(2) = w;
354  (*srf_pt)(3) = tmp[0] * bot;
355  (*srf_pt)(4) = tmp[1] * bot;
356  (*srf_pt)(5) = tmp[2] * bot;
357  (*srf_pt)(6) = (tmpDx[0] * tmp[3] - tmp[0] * tmpDx[3]) * botsqr;
358  (*srf_pt)(7) = (tmpDx[1] * tmp[3] - tmp[1] * tmpDx[3]) * botsqr;
359  (*srf_pt)(8) = (tmpDx[2] * tmp[3] - tmp[2] * tmpDx[3]) * botsqr;
360  (*srf_pt)(9) = (tmpDy[0] * tmp[3] - tmp[0] * tmpDy[3]) * botsqr;
361  (*srf_pt)(10) = (tmpDy[1] * tmp[3] - tmp[1] * tmpDy[3]) * botsqr;
362  (*srf_pt)(11) = (tmpDy[2] * tmp[3] - tmp[2] * tmpDy[3]) * botsqr;
363  delete[] buffer;
364  return;
365  }
366 
370 
371  std::vector<double> data_; // Controllpoints in Bezier-Extracted Format.
372  int polynomial_degree_x_; // Degree in x
373  int polynomial_degree_y_; // Degree in y
374  std::vector<double>
375  unique_knots_x_; // The knot vectors, where each knot is unique
376  std::vector<double>
377  unique_knots_y_; // The knot vectors, where each knot is unique
378 };
379 
386 inline std::vector<Patch> PatchShredder(const Patch &patch) noexcept {
387  // Already a Bezier patch
388  if (patch.unique_knots_y_.size() == 2 && patch.unique_knots_x_.size() == 2) {
389  return {patch};
390  }
391 
392  // number of subpatches in x and y directions
393  const int xchips = patch.unique_knots_x_.size() - 1;
394  const int ychips = patch.unique_knots_y_.size() - 1;
395 
396  const int xp = patch.polynomial_degree_x_;
397  const int yp = patch.polynomial_degree_y_;
398  const int numy = ychips * yp;
399 
400  std::vector<Patch> out(xchips * ychips);
401 
402  for (int ix = 0; ix < xchips; ix++) {
403  for (int iy = 0; iy < ychips; iy++) {
404  const int index = ix * ychips + iy;
405 
406  out[index].unique_knots_x_ = {0, 1};
407  out[index].unique_knots_y_ = {0, 1};
408  out[index].polynomial_degree_x_ = xp;
409  out[index].polynomial_degree_y_ = yp;
410  out[index].data_.reserve(xp * yp * 4);
411  }
412  }
413 
414  for (int ix = 0; ix < xchips; ix++) {
415  for (int iy = 0; iy < ychips; iy++) {
416  const int index = ix * ychips + iy;
417  for (int jx = 0; jx < xp; jx++) {
418  for (int jy = 0; jy < yp; jy++) {
419  const int accs = 4 * (numy * (xp * ix + jx) + yp * iy + jy);
420  for (int k = 0; k < 4; k++) {
421  out[index].data_.push_back(patch.data_[accs + k]);
422  }
423  }
424  }
425  }
426  }
427 
428  return out;
429 }
430 
437 inline std::vector<Patch> PatchShredder(
438  const std::vector<Patch> &patches) noexcept {
439  std::vector<Patch> out;
440  const int input_size = patches.size();
441 
442  for (int i = 0; i < input_size; i++) {
443  std::vector<Patch> tmp = PatchShredder(patches[i]);
444 
445  const int tmp_size = tmp.size();
446 
447  for (int j = 0; j < tmp_size; j++) out.push_back(tmp[j]);
448  }
449 
450  out.shrink_to_fit();
451 
452  return out;
453 }
454 
455 } // namespace Bembel
456 
457 #endif // BEMBEL_SRC_GEOMETRY_PATCH_HPP_
handles a single patch
Definition: Patch.hpp:20
Eigen::Matrix< double, 3, 1 > evalNormal(double x, double y) const
Evaluate normal vector at given point.
Definition: Patch.hpp:277
Eigen::Matrix< double, 3, 2 > evalJacobian(double x, double y) const
Evaluate Jacobian of the parametrization at given point.
Definition: Patch.hpp:267
Eigen::Matrix< double, 3, 2 > evalJacobian(const Eigen::Vector2d &reference_point) const
Evaluate Jacobian of the parametrization at given point.
Definition: Patch.hpp:163
Patch()
constructors
Definition: Patch.hpp:28
Eigen::Matrix< double, 3, 1 > evalNormal(const Eigen::Vector2d &reference_point) const
Evaluate normal vector at given point.
Definition: Patch.hpp:240
void init_Patch(const std::vector< Eigen::Matrix< double, -1, -1 >> &xyzw, const std::vector< double > &x_knots, const std::vector< double > &y_knots)
init
Definition: Patch.hpp:63
Patch(const std::vector< Eigen::Matrix< double, -1, -1 >> &control_points, const std::vector< double > &knots_x, const std::vector< double > &knots_y)
Constructor of a Patch.
Definition: Patch.hpp:42
Eigen::Vector3d eval(double x, double y) const
Evaluate patch at given point.
Definition: Patch.hpp:257
Eigen::Vector3d eval(const Eigen::Vector2d &reference_point) const
Evaluate patch at given point.
Definition: Patch.hpp:115
std::vector< double > data_
getter
Definition: Patch.hpp:371
void updateSurfacePoint(SurfacePoint *srf_pt, const Eigen::Vector2d &ref_pt, double w, const Eigen::Vector2d &xi) const
Updates the surface point and returns the physical point and the derivatives there.
Definition: Patch.hpp:294
Eigen::Matrix< double, 12, 1 > SurfacePoint
typedef of SurfacePoint
Eigen::SparseMatrix< T > MakeProjection(const std::vector< T > &x_knots, const std::vector< T > &y_knots, const std::vector< T > &x_unique_knots, const std::vector< T > &y_unique_knots, const int polynomial_degree_x, const int polynomial_degree_y) noexcept
implements Bezier extraction via the solution of an interpolation problem.
Eigen::Matrix< T, -1, 1 > Unroll(const Eigen::Matrix< T, -1, -1 > &input_matrix) noexcept
Tiny helper functions.
Definition: Unroll.hpp:23
Routines for the evalutation of pointwise errors.
Definition: AnsatzSpace.hpp:14
std::vector< Patch > PatchShredder(const Patch &patch) noexcept
This function cuts a patch along internal knots, if any.
Definition: Patch.hpp:386