Bembel
Projector.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_ANSATZSPACE_PROJECTOR_HPP_
12 #define BEMBEL_SRC_ANSATZSPACE_PROJECTOR_HPP_
13 
14 namespace Bembel {
15 namespace ProjectorRoutines {
19 template <typename Derived, unsigned int DF>
21  static Eigen::SparseMatrix<double> makeMatrix(
22  const Bembel::SuperSpace<Derived>& super_space,
23  const int knotrepetition) {
24  assert(false && "This needs to be specialized");
25  return Eigen::SparseMatrix<double>(1, 1);
26  }
27 };
28 } // namespace ProjectorRoutines
29 
39 template <typename Derived>
40 class Projector {
41  public:
45 
48  Projector() {}
58  Projector(const SuperSpace<Derived>& super_space, const int knot_repetition) {
59  init_Projector(super_space, knot_repetition);
60  }
64 
72  void init_Projector(const SuperSpace<Derived>& super_space,
73  const int knot_repetition) {
74  knot_repetition_ = knot_repetition;
75  projector_ = makeProjectionMatrix(super_space, knot_repetition);
76  dofs_before_projector_ = projector_.rows();
77  dofs_after_projector_ = projector_.cols();
78  return;
79  }
83 
88  int get_knot_repetition() const { return knot_repetition_; }
99  const Eigen::SparseMatrix<double>& get_projection_matrix() {
100  return projector_;
101  }
107  int get_dofs_after_projector() const { return dofs_after_projector_; }
111  private:
112  Eigen::SparseMatrix<double> makeProjectionMatrix(
113  const SuperSpace<Derived>& super_space, const int knotrepetition) {
115  Derived,
116  LinearOperatorTraits<Derived>::Form>::makeMatrix(super_space,
117  knotrepetition);
118  }
119  // we declare functionality which has not been implemented (yet)
120  // to be private
121  int knot_repetition_;
122  int dofs_before_projector_;
123  int dofs_after_projector_;
124  Eigen::SparseMatrix<double> projector_;
125  Projector(const Projector<Derived>& other);
126  Projector(Projector<Derived>&& other);
127  Projector& operator=(const Projector<Derived>& other);
128  Projector& operator=(Projector<Derived>&& other);
130 };
131 
132 namespace ProjectorRoutines {
133 
139 struct _proj_info {
140  std::vector<double> vals;
141  std::vector<int> rows;
142  std::vector<int> cols;
143  int num_c_dof;
144  int num_dc_dof;
145 };
146 
147 template <typename Derived>
165 inline _proj_info makeLocalProjectionTriplets(
166  const SuperSpace<Derived>& super_space, const int pp1x, const int pp1y,
167  const int knotrepetition_in) {
168  const int M = super_space.get_refinement_level();
169  const int maximal_polynomial_degree = std::max(pp1x, pp1y);
170  assert(maximal_polynomial_degree == super_space.get_polynomial_degree() + 1 &&
171  "superSpace not suitable for desired ansatz space -> polynomial "
172  "degree mismatch");
173  const int minp = std::min(pp1x, pp1y);
174  const int knotrepetition = (maximal_polynomial_degree <= knotrepetition_in)
175  ? minp
176  : knotrepetition_in;
177  const int n = (1 << M);
178  const int patch_number = super_space.get_number_of_patches();
179 
180  // At the moment, we allow a difference of one only, i.e., we start our de
181  // Rham sequence with a space of the same degree in every TP-direction.
182  assert(std::abs(maximal_polynomial_degree - minp) <= 1);
183 
184  // Now, we construct the continuous spaces which will be the preimage of the
185  // projector. n-1 is passed, since n = number_elements but n-1 = number of
186  // interior knots.
187  std::vector<double> c_space_knot_x =
188  Spl::MakeUniformKnotVector(pp1x, n - 1, knotrepetition);
189  std::vector<double> c_space_knot_y =
190  Spl::MakeUniformKnotVector(pp1y, n - 1, knotrepetition);
191  const int c_space_dim_x = c_space_knot_x.size() - pp1x;
192  const int c_space_dim_y = c_space_knot_y.size() - pp1y;
193  const int c_space_dim = c_space_dim_x * c_space_dim_y;
194 
195  _proj_info out;
196 
197  out.cols.reserve(c_space_dim * maximal_polynomial_degree *
198  maximal_polynomial_degree * patch_number);
199  out.vals.reserve(c_space_dim * maximal_polynomial_degree *
200  maximal_polynomial_degree * patch_number);
201  out.rows.reserve(c_space_dim * maximal_polynomial_degree *
202  maximal_polynomial_degree * patch_number);
203 
204  std::vector<double> mask =
205  Spl::MakeInterpolationMask(maximal_polynomial_degree);
206  const int masksize = mask.size();
207 
208  // Now we assemble our system which we use for the interpolation
209  assert(masksize == maximal_polynomial_degree &&
210  "projector.cpp: System needs to be square");
211  Eigen::Matrix<double, -1, -1> system(
212  masksize * masksize,
213  maximal_polynomial_degree * maximal_polynomial_degree);
214 
215  // Here, we suddenly use the degree for basisevaluation, i.e.,
216  // maximal_polynomial_degree-1. This is confusing, but correct and tested.
217  {
218  double vals_y[Constants::MaxP + 1];
219  double vals_x[Constants::MaxP + 1];
220  for (int iy = 0; iy < maximal_polynomial_degree; ++iy) {
221  Bembel::Basis::ShapeFunctionHandler::evalBasis(
222  maximal_polynomial_degree - 1, vals_y, mask[iy]);
223  for (int ix = 0; ix < maximal_polynomial_degree; ++ix) {
224  Bembel::Basis::ShapeFunctionHandler::evalBasis(
225  maximal_polynomial_degree - 1, vals_x, mask[ix]);
226  for (int jy = 0; jy < maximal_polynomial_degree; ++jy) {
227  for (int jx = 0; jx < maximal_polynomial_degree; ++jx) {
228  system(iy * masksize + ix, jy * maximal_polynomial_degree + jx) =
229  vals_x[jx] * vals_y[jy];
230  }
231  }
232  }
233  }
234  }
235 
236  // Do LU-Decomposition of
237  // systemsuper_space.get_mesh().get_element_tree().begin()
238  const Eigen::PartialPivLU<Eigen::Matrix<double, -1, -1>> pplu(system);
239 
240  unsigned int element_number = 0;
241 
242  for (auto element = super_space.get_mesh().get_element_tree().cpbegin();
243  element != super_space.get_mesh().get_element_tree().cpend();
244  ++element) {
245  // s = x
246  const double pos_x = element->llc_(0);
247  const double pos_y = element->llc_(1);
248 
249  const double h = element->get_h();
250  const double mid_x = pos_x + (.5 * h);
251  const double mid_y = pos_y + (.5 * h);
252 
253  // The following block of code computes the indeces of all continuous basis
254  // functions which have a support on the element of index 'element'
255  std::vector<double> nonzero_dofs_x;
256  std::vector<double> nonzero_dofs_y;
257  nonzero_dofs_x.reserve(pp1x);
258  nonzero_dofs_y.reserve(pp1y);
259  for (int dof_y = 0; dof_y < c_space_dim_y; ++dof_y) {
260  if ((c_space_knot_y[dof_y] <= mid_y) &&
261  (c_space_knot_y[dof_y + pp1y] >= mid_y)) {
262  nonzero_dofs_y.push_back(dof_y);
263  }
264  }
265  for (int dof_x = 0; dof_x < c_space_dim_x; ++dof_x) {
266  if ((c_space_knot_x[dof_x] <= mid_x) &&
267  (c_space_knot_x[dof_x + pp1x] >= mid_x)) {
268  nonzero_dofs_x.push_back(dof_x);
269  }
270  }
271  nonzero_dofs_x.shrink_to_fit();
272  nonzero_dofs_y.shrink_to_fit();
273 
274  // We now loop over all basis functions wich are non-zero on the element
275  for (auto dof_y : nonzero_dofs_y) {
276  for (auto dof_x : nonzero_dofs_x) {
277  // The next few lines are to check if the element is within the support
278  // of the basis function given by dof_x and dof_y.
279  std::vector<double> local_mask_x(masksize);
280  std::vector<double> local_mask_y(masksize);
281  for (int i = 0; i < masksize; ++i) {
282  local_mask_x[i] = pos_x + (1.0 / n) * mask[i];
283  local_mask_y[i] = pos_y + (1.0 / n) * mask[i];
284  assert(local_mask_x[i] < 1 && local_mask_x[i] > 0);
285  assert(local_mask_y[i] < 1 && local_mask_y[i] > 0);
286  }
287 
288  // build unit vectors
289  std::vector<double> c_coefs_x(c_space_dim_x, 0.0);
290  std::vector<double> c_coefs_y(c_space_dim_y, 0.0);
291  c_coefs_x[dof_x] = 1.;
292  c_coefs_y[dof_y] = 1.;
293 
294  // evaluate rhs for interpolation problem
295  std::vector<double> vals_x =
296  Spl::DeBoor(c_coefs_x, c_space_knot_x, local_mask_x);
297  std::vector<double> vals_y =
298  Spl::DeBoor(c_coefs_y, c_space_knot_y, local_mask_y);
299 
300  // assemble and solve interpolation problem
301  Eigen::VectorXd rhs(masksize * masksize);
302  for (int iy = 0; iy < masksize; ++iy)
303  for (int ix = 0; ix < masksize; ++ix)
304  rhs(iy * masksize + ix) = vals_x[ix] * vals_y[iy];
305  Eigen::VectorXd sol = pplu.solve(rhs);
306 
307  // insert entries into helperstruct
308  for (int i = 0;
309  i < maximal_polynomial_degree * maximal_polynomial_degree; ++i) {
310  if (std::abs(sol(i)) > Constants::projector_tolerance) {
311  out.cols.push_back(element->patch_ * (c_space_dim) +
312  (c_space_dim_x * dof_y) + dof_x);
313  out.rows.push_back((element_number * maximal_polynomial_degree *
314  maximal_polynomial_degree) +
315  i);
316  out.vals.push_back(sol(i));
317  }
318  }
319  }
320  }
321  ++element_number;
322  }
323 
324  out.num_c_dof = c_space_dim_x * c_space_dim_y * patch_number;
325  out.num_dc_dof = maximal_polynomial_degree * maximal_polynomial_degree * n *
326  n * patch_number;
327 
328  out.cols.shrink_to_fit();
329  out.rows.shrink_to_fit();
330  out.vals.shrink_to_fit();
331 
332  assert((out.vals.size() == out.rows.size()) &&
333  (out.rows.size() == out.cols.size()) &&
334  "projector.cpp: you made a mistake, try again.");
335 
336  return out;
337 }
338 
344 template <typename Derived>
345 struct projector_matrixmaker_<Derived, DifferentialForm::Continuous> {
346  static Eigen::SparseMatrix<double> makeMatrix(
347  const SuperSpace<Derived>& super_space, const int knotrepetition) {
348  const int P = super_space.get_polynomial_degree();
349  assert(P > 0 && "P must be 1 or larger for this type of discrete space");
350  assert(knotrepetition <= P &&
351  "Knot repetition must be smaller than P for this type of discrete "
352  "space");
353  // The matrices coincide with the 2 case, up to the limitations above
354  Eigen::SparseMatrix<double> local_matrix = projector_matrixmaker_<
355  Derived, DifferentialForm::Discontinuous>::makeMatrix(super_space,
356  knotrepetition);
357 
358  return local_matrix;
359  }
360 };
361 
370 template <typename Derived>
371 struct projector_matrixmaker_<Derived, DifferentialForm::DivConforming> {
372  static Eigen::SparseMatrix<double> makeMatrix(
373  const SuperSpace<Derived>& super_space, const int knotrepetition) {
374  assert(super_space.get_polynomial_degree() > 0 &&
375  "P must be 1 or larger for this type of discrete space");
376  assert(knotrepetition <= super_space.get_polynomial_degree() &&
377  "Knot repetition must be smaller than P for this type of discrete "
378  "space");
379  const auto info1 = ProjectorRoutines::makeLocalProjectionTriplets<Derived>(
380  super_space, super_space.get_polynomial_degree() + 1,
381  super_space.get_polynomial_degree(), knotrepetition);
382  const auto info2 = ProjectorRoutines::makeLocalProjectionTriplets<Derived>(
383  super_space, super_space.get_polynomial_degree(),
384  super_space.get_polynomial_degree() + 1, knotrepetition);
385  const int size1 = info1.vals.size();
386  const int size2 = info2.vals.size();
387  assert(size1 == size2);
388  std::vector<Eigen::Triplet<double>> trips;
389  for (int k = 0; k < size1; ++k) {
390  trips.push_back(
391  Eigen::Triplet<double>(info1.rows[k], info1.cols[k], info1.vals[k]));
392  }
393  for (int k = 0; k < size2; ++k) {
394  trips.push_back(Eigen::Triplet<double>(info2.rows[k] + info1.num_dc_dof,
395  info2.cols[k] + info1.num_c_dof,
396  info2.vals[k]));
397  }
398  Eigen::SparseMatrix<double> local_matrix(
399  info1.num_dc_dof + info2.num_dc_dof, info1.num_c_dof + info2.num_c_dof);
400  local_matrix.setFromTriplets(trips.begin(), trips.end());
401 
402  return local_matrix;
403  }
404 };
405 
412 template <typename Derived>
413 struct projector_matrixmaker_<Derived, DifferentialForm::Discontinuous> {
414  static Eigen::SparseMatrix<double> makeMatrix(
415  const SuperSpace<Derived>& super_space, const int knotrepetition) {
416  const auto info = ProjectorRoutines::makeLocalProjectionTriplets<Derived>(
417  super_space, super_space.get_polynomial_degree() + 1,
418  super_space.get_polynomial_degree() + 1, knotrepetition);
419  const int size = info.vals.size();
420  std::vector<Eigen::Triplet<double>> trips;
421  for (int k = 0; k < size; ++k) {
422  trips.push_back(
423  Eigen::Triplet<double>(info.rows[k], info.cols[k], info.vals[k]));
424  }
425  Eigen::SparseMatrix<double> local_matrix(info.num_dc_dof, info.num_c_dof);
426  local_matrix.setFromTriplets(trips.begin(), trips.end());
427 
428  return local_matrix;
429  }
430 };
431 } // namespace ProjectorRoutines
432 
433 } // namespace Bembel
434 
435 #endif // BEMBEL_SRC_ANSATZSPACE_PROJECTOR_HPP_
ElementTree & get_element_tree()
getter
Definition: ClusterTree.hpp:95
ElementTreeNode::const_iterator cpbegin() const
Returns an iterator to the beginning of the sequence represented by the leafs as ElementTreeNodes of ...
ElementTreeNode::const_iterator cpend() const
Returns an iterator one past the end of the sequence represented by the leafs as ElementTreeNodes of ...
The Projector provides routines to assemble smooth B-Splines on each patch.
Definition: Projector.hpp:40
Projector()
constructors
Definition: Projector.hpp:48
void init_Projector(const SuperSpace< Derived > &super_space, const int knot_repetition)
init
Definition: Projector.hpp:72
const Eigen::SparseMatrix< double > & get_projection_matrix()
This function returns the transformation matrix to assemble smooth tensor product B-splines.
Definition: Projector.hpp:99
int get_knot_repetition() const
getter
Definition: Projector.hpp:88
int get_dofs_after_projector() const
Return number of smooth B-splines.
Definition: Projector.hpp:107
Projector(const SuperSpace< Derived > &super_space, const int knot_repetition)
Constructor for the Projector.
Definition: Projector.hpp:58
Eigen::Matrix< T, -1, -1 > DeBoor(Eigen::Matrix< T, -1, -1 > const &control_points, const std::vector< double > &knot_vector, const std::vector< double > &evaluation_points) noexcept
"By the book" implementations of the Cox-DeBoor formula. Inefficient, do not use at bottlenecks.
Definition: DeBoor.hpp:22
Routines for the evalutation of pointwise errors.
Definition: AnsatzSpace.hpp:14
Provides information about the discrete space required for the discretisation of a specific operator.
struct containing specifications on the linear operator has to be specialized or derived for any part...
Helper struct for assembling the Projector for the different cases.
Definition: Projector.hpp:20
The superspace manages local polynomial bases on each element of the mesh and provides an interface t...
Definition: SuperSpace.hpp:22