Bembel
H2Multipole.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_H2MATRIX_H2MULTIPOLE_HPP_
12 #define BEMBEL_SRC_H2MATRIX_H2MULTIPOLE_HPP_
13 
14 namespace Bembel {
15 namespace H2Multipole {
21  ChebychevRoots() {}
22  explicit ChebychevRoots(int number_of_points) {
23  init_ChebyshevRoots(number_of_points);
24  }
25  void init_ChebyshevRoots(int n_pts) {
26  auto grid_pts = Eigen::ArrayXd::LinSpaced(n_pts, 1, n_pts).reverse();
27  double alpha = BEMBEL_PI / (2. * double(n_pts));
28  points_ = 0.5 * (alpha * (2 * grid_pts - 1)).cos() + 0.5;
29  return;
30  }
31  Eigen::VectorXd points_;
32 };
39 template <typename InterpolationPoints>
40 Eigen::MatrixXd computeLagrangePolynomials(int number_of_points) {
41  Eigen::MatrixXd retval =
42  Eigen::MatrixXd::Identity(number_of_points, number_of_points);
43  Eigen::VectorXd x = InterpolationPoints(number_of_points).points_;
44  for (auto i = 0; i < number_of_points; ++i)
45  for (auto j = 1; j < number_of_points; ++j)
46  for (auto k = number_of_points - 1; k >= j; --k)
47  retval(k, i) = (retval(k, i) - retval(k - 1, i)) / (x(k) - x(k - j));
48  return retval;
49 }
55 template <typename InterpolationPoints>
56 double evaluatePolynomial(const Eigen::VectorXd &L, double xi) {
57  int number_of_points = L.size();
58  Eigen::VectorXd x = InterpolationPoints(number_of_points).points_;
59  double retval = 0;
60 
61  retval = L(number_of_points - 1);
62 
63  for (auto i = number_of_points - 2; i >= 0; --i)
64  retval = retval * (xi - x(i)) + L(i);
65 
66  return retval;
67 }
76 template <typename InterpolationPoints>
77 Eigen::MatrixXd computeTransferMatrices(int number_of_points) {
78  int np2 = number_of_points * number_of_points;
79  Eigen::MatrixXd E(number_of_points, 2 * number_of_points);
80  Eigen::MatrixXd T(np2, 4 * np2);
82  Eigen::VectorXd x = InterpolationPoints(number_of_points).points_;
83  Eigen::MatrixXd L =
84  computeLagrangePolynomials<InterpolationPoints>(number_of_points);
94  for (auto j = 0; j < number_of_points; ++j)
95  for (auto i = 0; i < number_of_points; ++i) {
96  E(i, j) = evaluatePolynomial<InterpolationPoints>(L.col(j), 0.5 * x(i));
97  E(i, j + number_of_points) =
98  evaluatePolynomial<InterpolationPoints>(L.col(j), 0.5 * x(i) + 0.5);
99  }
100  // This construction, regard permuation vector, results in the
101  // order T0 T2 T3 T1 to apply to the moments
102  Eigen::Vector4i permutation;
103  permutation << 0, 3, 1, 2;
104  for (auto k = 0; k < 4; ++k)
105  for (auto i = 0; i < number_of_points; ++i)
106  for (auto ii = 0; ii < number_of_points; ++ii)
107  for (auto j = 0; j < number_of_points; ++j)
108  for (auto jj = 0; jj < number_of_points; ++jj)
109  T(j * number_of_points + jj,
110  i * number_of_points + ii + np2 * permutation(k)) =
111  E(i, j + (k / 2) * number_of_points) *
112  E(ii, jj + (k % 2) * number_of_points);
113 
114  return T;
115 }
120 template <typename InterpolationPoints, typename LinOp>
121 struct Moment1D {
122  static Eigen::MatrixXd computeMoment1D(const SuperSpace<LinOp> &super_space,
123  const int cluster_level,
124  const int cluster_refinements,
125  const int number_of_points) {
126  int n = 1 << cluster_refinements;
127  double h = 1. / n;
128  int N = 1 << cluster_level;
129  double H = 1. / N;
130  int polynomial_degree = super_space.get_polynomial_degree();
131  int polynomial_degree_plus_one = polynomial_degree + 1;
132  int polynomial_degree_plus_one_squared =
133  polynomial_degree_plus_one * polynomial_degree_plus_one;
135  auto Q =
136  GL[(int)std::ceil(0.5 * (number_of_points + polynomial_degree - 2))];
137 
138  Eigen::VectorXd x = InterpolationPoints(number_of_points).points_;
139  Eigen::MatrixXd L =
140  computeLagrangePolynomials<InterpolationPoints>(number_of_points);
141 
142  Eigen::MatrixXd moment(number_of_points, n * polynomial_degree_plus_one);
143 
144  for (auto i = 0; i < number_of_points; ++i) {
145  Eigen::Matrix<typename LinearOperatorTraits<LinOp>::Scalar,
146  Eigen::Dynamic, 1>
147  intval(polynomial_degree_plus_one, 1);
148  for (auto j = 0; j < n; ++j) {
149  intval.setZero();
150  for (auto k = 0; k < Q.w_.size(); ++k) {
151  super_space.addScaledBasis1D(
152  &intval,
153  Q.w_(k) * std::sqrt(h * H) *
154  evaluatePolynomial<InterpolationPoints>(L.col(i),
155  h * (j + Q.xi_(k))),
156  Q.xi_(k));
157  }
158  // we are integrating real stuff, so take the real part
159  moment.block(i, j * polynomial_degree_plus_one, 1,
160  polynomial_degree_plus_one) = intval.real().transpose();
161  }
162  }
163 
164  return moment;
165  }
166 };
172 template <typename InterpolationPoints, typename LinOp>
174  static Eigen::MatrixXd computeMoment1D(const SuperSpace<LinOp> &super_space,
175  const int cluster_level,
176  const int cluster_refinements,
177  const int number_of_points) {
178  int n = 1 << cluster_refinements;
179  double h = 1. / n;
180  int N = 1 << cluster_level;
181  double H = 1. / N;
182  int polynomial_degree = super_space.get_polynomial_degree();
183  int polynomial_degree_plus_one = polynomial_degree + 1;
184  int polynomial_degree_plus_one_squared =
185  polynomial_degree_plus_one * polynomial_degree_plus_one;
187  auto Q =
188  GL[(int)std::ceil(0.5 * (number_of_points + polynomial_degree - 2))];
189 
190  Eigen::VectorXd x = InterpolationPoints(number_of_points).points_;
191  Eigen::MatrixXd L =
192  computeLagrangePolynomials<InterpolationPoints>(number_of_points);
193 
194  Eigen::MatrixXd moment(number_of_points, n * polynomial_degree_plus_one);
195 
196  for (auto i = 0; i < number_of_points; ++i) {
197  Eigen::Matrix<typename LinearOperatorTraits<LinOp>::Scalar,
198  Eigen::Dynamic, 1>
199  intval(polynomial_degree_plus_one, 1);
200  for (auto j = 0; j < n; ++j) {
201  intval.setZero();
202  for (auto k = 0; k < Q.w_.size(); ++k) {
203  super_space.addScaledBasis1DDx(
204  &intval,
205  Q.w_(k) / std::sqrt(h * H) *
206  evaluatePolynomial<InterpolationPoints>(L.col(i),
207  h * (j + Q.xi_(k))),
208  Q.xi_(k));
209  }
210  // we are integrating real stuff, so take the real part
211  moment.block(i, j * polynomial_degree_plus_one, 1,
212  polynomial_degree_plus_one) = intval.real().transpose();
213  }
214  }
215 
216  return moment;
217  }
218 };
224 template <typename Mom1D_1, typename Mom1D_2, typename LinOp>
225 Eigen::MatrixXd moment2DComputer(const SuperSpace<LinOp> &super_space,
226  const int cluster_level,
227  const int cluster_refinements,
228  const int number_of_points) {
229  int n = 1 << cluster_refinements;
230  auto n2 = n * n;
231  int polynomial_degree = super_space.get_polynomial_degree();
232  int polynomial_degree_plus_one = polynomial_degree + 1;
233  int polynomial_degree_plus_one_squared =
234  polynomial_degree_plus_one * polynomial_degree_plus_one;
235 
236  // compute 1D moments
237  Eigen::MatrixXd moment1D_1 = Mom1D_1::computeMoment1D(
238  super_space, cluster_level, cluster_refinements, number_of_points);
239  Eigen::MatrixXd moment1D_2 = Mom1D_2::computeMoment1D(
240  super_space, cluster_level, cluster_refinements, number_of_points);
260  Eigen::VectorXi index_s(n2);
261  Eigen::VectorXi index_t(n2);
262  index_s.setZero();
263  index_t.setZero();
264  for (auto j = 0; j < cluster_refinements; ++j) {
265  auto inc = 1 << 2 * j;
266  for (auto i = 0; i < inc; ++i) {
267  index_s(4 * i) = 2 * index_s(i);
268  index_s(4 * i + 1) = 2 * index_s(i) + 1;
269  index_s(4 * i + 2) = 2 * index_s(i) + 1;
270  index_s(4 * i + 3) = 2 * index_s(i);
271  index_t(4 * i) = 2 * index_t(i);
272  index_t(4 * i + 1) = 2 * index_t(i);
273  index_t(4 * i + 2) = 2 * index_t(i) + 1;
274  index_t(4 * i + 3) = 2 * index_t(i) + 1;
275  }
276  }
277 
278  // assemble 2D tensor-product moments
279  Eigen::MatrixXd moment2D(number_of_points * number_of_points,
280  moment1D_1.cols() * moment1D_2.cols());
281  for (auto i = 0; i < number_of_points; ++i)
282  for (auto j = 0; j < number_of_points; ++j)
283  for (auto k = 0; k < n2; ++k)
284  for (auto m1 = 0; m1 < polynomial_degree_plus_one; ++m1)
285  for (auto m2 = 0; m2 < polynomial_degree_plus_one; ++m2)
286  moment2D(i * number_of_points + j,
287  polynomial_degree_plus_one_squared * k +
288  m1 * polynomial_degree_plus_one + m2) =
289  moment1D_1(i, index_s(k) * polynomial_degree_plus_one + m2) *
290  moment1D_2(j, index_t(k) * polynomial_degree_plus_one + m1);
291 
292  return moment2D;
293 }
301 template <typename InterpolationPoints, typename LinOp>
302 struct Moment2D {
303  static std::vector<Eigen::MatrixXd> compute2DMoment(
304  const SuperSpace<LinOp> &super_space, const int cluster_level,
305  const int cluster_refinements, const int number_of_points) {
306  std::vector<Eigen::MatrixXd> vector_of_moments;
307  for (int i = 0;
308  i <
309  getFunctionSpaceVectorDimension<LinearOperatorTraits<LinOp>::Form>();
310  ++i)
311  vector_of_moments.push_back(
314  super_space, cluster_level, cluster_refinements,
315  number_of_points));
316  return vector_of_moments;
317  }
318 };
324 Eigen::MatrixXd interpolationPoints2D(const Eigen::VectorXd &x) {
325  Eigen::MatrixXd x2(x.size() * x.size(), 2);
326  for (auto i = 0; i < x.size(); ++i)
327  for (auto j = 0; j < x.size(); ++j) {
328  x2.row(i * x.size() + j) = Eigen::Vector2d(x(i), x(j));
329  }
330  return x2;
331 }
336 template <typename LinOp>
337 Eigen::Matrix<typename LinearOperatorTraits<LinOp>::Scalar, Eigen::Dynamic,
338  Eigen::Dynamic>
339 interpolateKernel(const LinOp &linOp, const SuperSpace<LinOp> &super_space,
340  const Eigen::MatrixXd &x,
341  const Bembel::ElementTreeNode &cluster1,
342  const Bembel::ElementTreeNode &cluster2) {
343  SurfacePoint qp1, qp2;
344  Eigen::Matrix<
346  getFunctionSpaceVectorDimension<LinearOperatorTraits<LinOp>::Form>() *
350  interpval;
351  const int vector_dimension = Bembel::getFunctionSpaceVectorDimension<
353  const int number_of_FMM_components =
355  Eigen::Matrix<typename LinearOperatorTraits<LinOp>::Scalar, Eigen::Dynamic,
356  Eigen::Dynamic>
357  F(vector_dimension * number_of_FMM_components * x.rows(),
358  vector_dimension * number_of_FMM_components * x.rows());
359  for (int i = 0; i < x.rows(); ++i) {
360  super_space.map2surface(cluster1, x.row(i), 1., &qp1);
361  for (int j = 0; j < x.rows(); ++j) {
362  super_space.map2surface(cluster2, x.row(j), 1., &qp2);
363  auto FMM_output = linOp.evaluateFMMInterpolation(qp1, qp2);
364  for (int ii = 0; ii < vector_dimension; ++ii)
365  for (int jj = 0; jj < vector_dimension; ++jj)
366  for (int iii = 0; iii < number_of_FMM_components; ++iii)
367  for (int jjj = 0; jjj < number_of_FMM_components; ++jjj)
368  F(i + (ii * number_of_FMM_components + iii) * x.rows(),
369  j + (jj * number_of_FMM_components + jjj) * x.rows()) =
370  FMM_output(iii + ii * number_of_FMM_components,
371  jjj + jj * number_of_FMM_components);
372  }
373  }
374  return F;
375 }
380 template <typename Scalar>
381 std::vector<Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>>
382 forwardTransformation(const Eigen::MatrixXd &moment_matrices,
383  const Eigen::MatrixXd &transfer_matrices, const int steps,
384  const Eigen::Matrix<Scalar, Eigen::Dynamic,
385  Eigen::Dynamic> &long_rhs_matrix) {
386  // get numbers
387  int number_of_points = transfer_matrices.rows();
388  int number_of_FMM_components = moment_matrices.rows() / number_of_points;
389  // apply moment matrices
390  std::vector<Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>>
391  long_rhs_forward;
392  long_rhs_forward.push_back(moment_matrices * long_rhs_matrix);
393  // apply transfer matrices
394  for (int i = 0; i < steps; ++i) {
395  Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> reshaped =
396  Eigen::Map<Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>>(
397  long_rhs_forward.back().data(), 4 * long_rhs_forward.back().rows(),
398  long_rhs_forward.back().cols() / 4);
399  Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> transferred(
400  reshaped.rows() / 4, reshaped.cols());
401 
402  // iterate over FMM components
403  for (int j = 0; j < number_of_FMM_components; ++j) {
404  Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> to_transfer(
405  4 * number_of_points, reshaped.cols());
406  to_transfer << reshaped.block(
407  (j + 0 * number_of_FMM_components) * number_of_points, 0,
408  number_of_points, reshaped.cols()),
409  reshaped.block((j + 1 * number_of_FMM_components) * number_of_points,
410  0, number_of_points, reshaped.cols()),
411  reshaped.block((j + 2 * number_of_FMM_components) * number_of_points,
412  0, number_of_points, reshaped.cols()),
413  reshaped.block((j + 3 * number_of_FMM_components) * number_of_points,
414  0, number_of_points, reshaped.cols());
415  transferred.block(j * number_of_points, 0, number_of_points,
416  reshaped.cols()) = transfer_matrices * to_transfer;
417  }
418 
419  long_rhs_forward.push_back(transferred);
420  }
421  return long_rhs_forward;
422 }
428 template <typename Scalar>
429 Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> backwardTransformation(
430  const Eigen::MatrixXd &moment_matrices,
431  const Eigen::MatrixXd &transfer_matrices, const int steps,
432  std::vector<Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>>
433  &long_dst_backward) {
434  // get numbers
435  int number_of_points = transfer_matrices.rows();
436  int number_of_FMM_components = moment_matrices.rows() / number_of_points;
437  // apply transfer matrices
438  for (int i = steps; i > 0; --i) {
439  Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> transferred(
440  4 * long_dst_backward[i].rows(), long_dst_backward[i].cols());
441  for (int j = 0; j < number_of_FMM_components; ++j) {
442  Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> prod =
443  transfer_matrices.transpose() *
444  long_dst_backward[i].block(j * number_of_points, 0, number_of_points,
445  long_dst_backward[i].cols());
446  transferred.block((j + 0 * number_of_FMM_components) * number_of_points,
447  0, number_of_points, prod.cols()) =
448  prod.block(0 * number_of_points, 0, number_of_points, prod.cols());
449  transferred.block((j + 1 * number_of_FMM_components) * number_of_points,
450  0, number_of_points, prod.cols()) =
451  prod.block(1 * number_of_points, 0, number_of_points, prod.cols());
452  transferred.block((j + 2 * number_of_FMM_components) * number_of_points,
453  0, number_of_points, prod.cols()) =
454  prod.block(2 * number_of_points, 0, number_of_points, prod.cols());
455  transferred.block((j + 3 * number_of_FMM_components) * number_of_points,
456  0, number_of_points, prod.cols()) =
457  prod.block(3 * number_of_points, 0, number_of_points, prod.cols());
458  }
459  long_dst_backward[i - 1] +=
460  Eigen::Map<Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic>>(
461  transferred.data(), long_dst_backward[i - 1].rows(),
462  long_dst_backward[i - 1].cols());
463  }
464  // apply moment matrices
465  Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> long_dst_matrix =
466  moment_matrices.transpose() * long_dst_backward[0];
467  return Eigen::Map<Eigen::Matrix<Scalar, Eigen::Dynamic, 1>>(
468  long_dst_matrix.data(), long_dst_matrix.size());
469 }
470 
471 } // namespace H2Multipole
472 } // namespace Bembel
473 #endif // BEMBEL_SRC_H2MATRIX_H2MULTIPOLE_HPP_
The ElementTreeNode corresponds to an element in the element tree.
Eigen::Matrix< double, 12, 1 > SurfacePoint
typedef of SurfacePoint
Eigen::MatrixXd computeLagrangePolynomials(int number_of_points)
computes the Lagrange polynomials wrt the interpolation points given by the InterpolationPoints struc...
Definition: H2Multipole.hpp:40
Eigen::MatrixXd interpolationPoints2D(const Eigen::VectorXd &x)
Compute tensor interpolation points on unit square from 1D interpolation points.
Eigen::Matrix< typename LinearOperatorTraits< LinOp >::Scalar, Eigen::Dynamic, Eigen::Dynamic > interpolateKernel(const LinOp &linOp, const SuperSpace< LinOp > &super_space, const Eigen::MatrixXd &x, const Bembel::ElementTreeNode &cluster1, const Bembel::ElementTreeNode &cluster2)
Interpolate kernel function on reference domain for FMM.
Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > backwardTransformation(const Eigen::MatrixXd &moment_matrices, const Eigen::MatrixXd &transfer_matrices, const int steps, std::vector< Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic >> &long_dst_backward)
Backward transformation for FMM. The content of long_dst_backward is destroyed.
std::vector< Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > > forwardTransformation(const Eigen::MatrixXd &moment_matrices, const Eigen::MatrixXd &transfer_matrices, const int steps, const Eigen::Matrix< Scalar, Eigen::Dynamic, Eigen::Dynamic > &long_rhs_matrix)
Forward transformation for FMM.
double evaluatePolynomial(const Eigen::VectorXd &L, double xi)
evaluates a given polynomial in the Newton basis wrt the interpolation points at a given location
Definition: H2Multipole.hpp:56
Eigen::MatrixXd moment2DComputer(const SuperSpace< LinOp > &super_space, const int cluster_level, const int cluster_refinements, const int number_of_points)
Computes a single 2D moment for the FMM by tensorisation of the 1D moments.
Eigen::MatrixXd computeTransferMatrices(int number_of_points)
Computes transfer matrices in required order to apply them all-at-once in a matrix-vector-product,...
Definition: H2Multipole.hpp:77
Routines for the evalutation of pointwise errors.
Definition: AnsatzSpace.hpp:14
constexpr int getFunctionSpaceVectorDimension()
Computes the number_of_points Chebychev points.
Definition: H2Multipole.hpp:20
Computes 1D moment for FMM using derivatives of the basis functions. All calculations ar performed on...
Computes 1D moment for FMM. All calculations ar performed on [0,1].
Computes all 2D moment for the FMM by tensorisation of the 1D moments. Specialice this for your linea...
struct containing specifications on the linear operator has to be specialized or derived for any part...
this struct wraps all the defined quadrature Rules in a nice structure overloading the [] operator su...
The superspace manages local polynomial bases on each element of the mesh and provides an interface t...
Definition: SuperSpace.hpp:22
void addScaledBasis1DDx(Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > *intval, Scalar w, double s) const
Evaluate derivatives of local shape functions on the unit interval at coordinate s,...
Definition: SuperSpace.hpp:380
void addScaledBasis1D(Eigen::Matrix< Scalar, Eigen::Dynamic, 1 > *intval, Scalar w, double s) const
Evaluate local shape functions on the unit interval at coordinate s, scale by w and add to intval.
Definition: SuperSpace.hpp:363
void map2surface(const ElementTreeNode &e, const Eigen::Vector2d &xi, double w, SurfacePoint *surf_pt) const
Evaluation of a point in the element and its Jacobian matrix.
Definition: SuperSpace.hpp:160