Bembel
 
Loading...
Searching...
No Matches
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
14namespace Bembel {
15namespace ProjectorRoutines {
19template <typename Derived, unsigned int DF>
21 static Eigen::SparseMatrix<double> makeMatrix(
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
39template <typename Derived>
40class Projector {
41 public:
45
64
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(
115 Derived,
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_;
127 Projector& operator=(const Projector<Derived>& other);
128 Projector& operator=(Projector<Derived>&& other);
130};
131
132namespace ProjectorRoutines {
133
139struct _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
147template <typename Derived>
165inline _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);
175 ? minp
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;
194
195 _proj_info out;
196
198 maximal_polynomial_degree * patch_number);
200 maximal_polynomial_degree * patch_number);
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
210 "projector.cpp: System needs to be square");
211 Eigen::Matrix<double, -1, -1> system(
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(
223 for (int ix = 0; ix < maximal_polynomial_degree; ++ix) {
224 Bembel::Basis::ShapeFunctionHandler::evalBasis(
226 for (int jy = 0; jy < maximal_polynomial_degree; ++jy) {
227 for (int jx = 0; jx < 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 =
297 std::vector<double> vals_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;
310 if (std::abs(sol(i)) > Constants::projector_tolerance) {
311 out.cols.push_back(element->patch_ * (c_space_dim) +
315 i);
316 out.vals.push_back(sol(i));
317 }
318 }
319 }
320 }
322 }
323
324 out.num_c_dof = c_space_dim_x * c_space_dim_y * patch_number;
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
344template <typename Derived>
345struct projector_matrixmaker_<Derived, DifferentialForm::Continuous> {
346 static Eigen::SparseMatrix<double> makeMatrix(
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");
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,
357
358 return local_matrix;
359 }
360};
361
370template <typename Derived>
371struct projector_matrixmaker_<Derived, DifferentialForm::DivConforming> {
372 static Eigen::SparseMatrix<double> makeMatrix(
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
412template <typename Derived>
413struct projector_matrixmaker_<Derived, DifferentialForm::Discontinuous> {
414 static Eigen::SparseMatrix<double> makeMatrix(
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_
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
int get_knot_repetition() const
getter
Definition Projector.hpp:88
int get_dofs_after_projector() const
Return number of smooth B-splines.
Projector(const SuperSpace< Derived > &super_space, const int knot_repetition)
Constructor for the Projector.
Definition Projector.hpp:58
const Eigen::SparseMatrix< double > & get_projection_matrix()
This function returns the transformation matrix to assemble smooth tensor product B-splines.
Definition Projector.hpp:99
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.
constexpr int getFunctionSpaceOutputDimension()
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...