PolyFEM
Loading...
Searching...
No Matches
RBFWithQuadratic.cpp
Go to the documentation of this file.
1
3
7
8#include <igl/Timer.h>
9
10#include <Eigen/Dense>
11
12#include <iostream>
13#include <fstream>
14#include <array>
16
17// #define VERBOSE
18
19using namespace polyfem;
20using namespace polyfem::assembler;
21using namespace polyfem::basis;
22using namespace polyfem::quadrature;
23using namespace polyfem::utils;
24
25namespace
26{
27
28 // Harmonic kernel
29 double kernel(const bool is_volume, const double r)
30 {
31 if (r < 1e-8)
32 {
33 return 0;
34 }
35
36 if (is_volume)
37 {
38 return 1 / r;
39 }
40 else
41 {
42 return log(r);
43 }
44 }
45
46 double kernel_prime(const bool is_volume, const double r)
47 {
48 if (r < 1e-8)
49 {
50 return 0;
51 }
52
53 if (is_volume)
54 {
55 return -1 / (r * r);
56 }
57 else
58 {
59 return 1 / r;
60 }
61 }
62
63 // Biharmonic kernel (2d only)
64 // double kernel(const bool is_volume, const double r) {
65 // assert(!is_volume);
66 // if (r < 1e-8) { return 0; }
67
68 // return r * r * (log(r)-1);
69 // }
70
71 // double kernel_prime(const bool is_volume, const double r) {
72 // assert(!is_volume);
73 // if (r < 1e-8) { return 0; }
74
75 // return r * ( 2 * log(r) - 1);
76 // }
77
78} // anonymous namespace
79
81
82// output is std::array<Eigen::MatrixXd, 5> &strong rhs(q(x_i) er)
83void RBFWithQuadratic::setup_monomials_strong_2d(const int dim, const LinearAssembler &assembler, const Eigen::MatrixXd &pts, const QuadratureVector &da, std::array<Eigen::MatrixXd, 5> &strong)
84{
85 // a(u,v) = a(q er, phi_j es) = <rhs(q(x_i) er) , phi_j(x_i) es >
86 // (not a(phi_j es, q er))
87
89 AutodiffHessianPt pt(dim);
90 for (int i = 0; i < 5; ++i)
91 {
92 strong[i].resize(dim * dim, pts.rows());
93 strong[i].setZero();
94 }
95
96 Eigen::MatrixXd tmp;
97
98 for (int i = 0; i < pts.rows(); ++i)
99 {
100 // loop for er
101 for (int d = 0; d < dim; ++d)
102 {
103 pt((d + 1) % dim) = AutodiffScalarHessian(0);
104 // for d = 0 pt(q, 0), for d = 1 pt=(0, q)
105
106 // x
107 pt(d) = AutodiffScalarHessian(0, pts(i, 0)); // pt=(x, 0) or pt=(0, x)
108 tmp = assembler.compute_rhs(pt); // in R^dim
109 for (int d1 = 0; d1 < dim; ++d1)
110 strong[0](d * dim + d1, i) = tmp(d1) * da(i);
111
112 // y
113 pt(d) = AutodiffScalarHessian(1, pts(i, 1));
114 tmp = assembler.compute_rhs(pt);
115 for (int d1 = 0; d1 < dim; ++d1)
116 strong[1](d * dim + d1, i) = tmp(d1) * da(i);
117
118 // xy
119 pt(d) = AutodiffScalarHessian(0, pts(i, 0)) * AutodiffScalarHessian(1, pts(i, 1));
120 tmp = assembler.compute_rhs(pt);
121 for (int d1 = 0; d1 < dim; ++d1)
122 strong[2](d * dim + d1, i) = tmp(d1) * da(i);
123
124 // x^2
125 pt(d) = AutodiffScalarHessian(0, pts(i, 0)) * AutodiffScalarHessian(0, pts(i, 0));
126 tmp = assembler.compute_rhs(pt);
127 for (int d1 = 0; d1 < dim; ++d1)
128 strong[3](d * dim + d1, i) = tmp(d1) * da(i);
129
130 // y^2
131 pt(d) = AutodiffScalarHessian(1, pts(i, 1)) * AutodiffScalarHessian(1, pts(i, 1));
132 tmp = assembler.compute_rhs(pt);
133 for (int d1 = 0; d1 < dim; ++d1)
134 strong[4](d * dim + d1, i) = tmp(d1) * da(i);
135 }
136 }
137}
138
139void RBFWithQuadratic::setup_monomials_vals_2d(const int star_index, const Eigen::MatrixXd &pts, ElementAssemblyValues &vals)
140{
141 assert(star_index + 5 <= vals.basis_values.size());
142 // x
143 vals.basis_values[star_index + 0].val = pts.col(0);
144 vals.basis_values[star_index + 0].grad = Eigen::MatrixXd(pts.rows(), pts.cols());
145 vals.basis_values[star_index + 0].grad.col(0).setOnes();
146 vals.basis_values[star_index + 0].grad.col(1).setZero();
147
148 // y
149 vals.basis_values[star_index + 1].val = pts.col(1);
150 vals.basis_values[star_index + 1].grad = Eigen::MatrixXd(pts.rows(), pts.cols());
151 vals.basis_values[star_index + 1].grad.col(0).setZero();
152 vals.basis_values[star_index + 1].grad.col(1).setOnes();
153
154 // xy
155 vals.basis_values[star_index + 2].val = pts.col(0).array() * pts.col(1).array();
156 vals.basis_values[star_index + 2].grad = Eigen::MatrixXd(pts.rows(), pts.cols());
157 vals.basis_values[star_index + 2].grad.col(0) = pts.col(1);
158 vals.basis_values[star_index + 2].grad.col(1) = pts.col(0);
159
160 // x^2
161 vals.basis_values[star_index + 3].val = pts.col(0).array() * pts.col(0).array();
162 vals.basis_values[star_index + 3].grad = Eigen::MatrixXd(pts.rows(), pts.cols());
163 vals.basis_values[star_index + 3].grad.col(0) = 2 * pts.col(0);
164 vals.basis_values[star_index + 3].grad.col(1).setZero();
165
166 // y^2
167 vals.basis_values[star_index + 4].val = pts.col(1).array() * pts.col(1).array();
168 vals.basis_values[star_index + 4].grad = Eigen::MatrixXd(pts.rows(), pts.cols());
169 vals.basis_values[star_index + 4].grad.col(0).setZero();
170 vals.basis_values[star_index + 4].grad.col(1) = 2 * pts.col(1);
171
172 for (size_t i = star_index; i < star_index + 5; ++i)
173 {
174 vals.basis_values[i].grad_t_m = vals.basis_values[i].grad;
175 }
176
177 // for(size_t i = star_index; i < star_index + 5; ++i)
178 // {
179 // vals.basis_values[i].grad_t_m = Eigen::MatrixXd(pts.rows(), pts.cols());
180 // for(int k = 0; k < vals.jac_it.size(); ++k)
181 // vals.basis_values[i].grad_t_m.row(k) = vals.basis_values[i].grad.row(k) * vals.jac_it[k];
182 // }
183}
184
186 const LinearAssembler &assembler,
187 const Eigen::MatrixXd &centers,
188 const Eigen::MatrixXd &collocation_points,
189 const Eigen::MatrixXd &local_basis_integral,
190 const Quadrature &quadr,
191 Eigen::MatrixXd &rhs,
192 bool with_constraints)
193 : centers_(centers)
194{
195 // centers_.resize(0, centers.cols());
196 compute_weights(assembler, collocation_points, local_basis_integral, quadr, rhs, with_constraints);
197}
198
199// -----------------------------------------------------------------------------
200
201void RBFWithQuadratic::basis(const int local_index, const Eigen::MatrixXd &samples, Eigen::MatrixXd &val) const
202{
203 Eigen::MatrixXd tmp;
204 bases_values(samples, tmp);
205 val = tmp.col(local_index);
206}
207
208// -----------------------------------------------------------------------------
209
210void RBFWithQuadratic::grad(const int local_index, const Eigen::MatrixXd &samples, Eigen::MatrixXd &val) const
211{
212 Eigen::MatrixXd tmp;
213 const int dim = centers_.cols();
214 val.resize(samples.rows(), dim);
215 for (int d = 0; d < dim; ++d)
216 {
217 bases_grads(d, samples, tmp);
218 val.col(d) = tmp.col(local_index);
219 }
220}
221
223
224void RBFWithQuadratic::bases_values(const Eigen::MatrixXd &samples, Eigen::MatrixXd &val) const
225{
226 // Compute A
227 Eigen::MatrixXd A;
228 compute_kernels_matrix(samples, A);
229
230 // Multiply by the weights
231 val = A * weights_;
232}
233
234// -----------------------------------------------------------------------------
235
236void RBFWithQuadratic::bases_grads(const int axis, const Eigen::MatrixXd &samples, Eigen::MatrixXd &val) const
237{
238 const int num_kernels = centers_.rows();
239 const int dim = (is_volume() ? 3 : 2);
240
241 // Compute ∇xA
242 Eigen::MatrixXd A_prime(samples.rows(), num_kernels + 1 + dim + dim * (dim + 1) / 2);
243 A_prime.setZero();
244
245 for (int j = 0; j < num_kernels; ++j)
246 {
247 A_prime.col(j) = (samples.rowwise() - centers_.row(j)).rowwise().norm().unaryExpr([this](double x) { return kernel_prime(is_volume(), x) / x; });
248 A_prime.col(j) = (samples.col(axis).array() - centers_(j, axis)) * A_prime.col(j).array();
249 }
250 // Linear terms
251 A_prime.middleCols(num_kernels + 1 + axis, 1).setOnes();
252 // Mixed terms
253 if (dim == 2)
254 {
255 A_prime.col(num_kernels + 1 + dim) = samples.col(1 - axis);
256 }
257 else
258 {
259 A_prime.col(num_kernels + 1 + dim + axis) = samples.col((axis + 1) % dim);
260 A_prime.col(num_kernels + 1 + dim + (axis + 2) % dim) = samples.col((axis + 2) % dim);
261 }
262 // Quadratic terms
263 A_prime.rightCols(dim).col(axis) = 2.0 * samples.col(axis);
264
265 // Apply weights
266 val = A_prime * weights_;
267}
268
270//
271// For each FEM basis φ that is nonzero on the element E, we want to
272// solve the least square system A w = rhs, where:
273// ┏ ┓
274// ┃ ψ_k(pi) ... 1 xi yi xi*yi xi^2 yi^2 ┃
275// A = ┃ ┊ ┊ ┊ ┊ ┊ ┊ ┊ ┃ ∊ ℝ^{#S x (#K+1+dim+dim*(dim+1)/2)}
276// ┃ ┊ ┊ ┊ ┊ ┊ ┊ ┊ ┃
277// ┗ ┛
278// ┏ ┓^⊤
279// w = ┃ w_k ... a00 a10 a01 a11 a20 a02 ┃ ∊ ℝ^{#K+1+dim+dim*(dim+1)/2}
280// ┗ ┛
281// - A is the RBF kernels evaluated over the collocation points (#S)
282// - b is the expected value of the basis sampled on the boundary (#S)
283// - w is the weight of the kernels defining the basis
284// - pi = (xi, yi) is the i-th collocation point
285//
286// Moreover, we want to impose a constraint on the weight vector w so that each
287// monomial Q(x,y) = x^α*y^β with α+β <= 2 is in the span of the FEM bases {φ_j}_j.
288//
289// In the case of Laplace's equation, we recall the weak form of the PDE as:
290//
291// Find u such that: ∫_Ω Δu v = - ∫_Ω ∇u·∇v ∀ v
292//
293// For our bases to exactly represent a monomial Q(x,y), it means that its
294// approximation by the finite element bases {φ_j}_j must be equal to Q(x,y).
295// In particular, for any φ_j that is nonzero on the polyhedral element E, we must have:
296//
297// ∫_{𝘅 in Ω} ΔQ(𝘅) φ_j(𝘅) d𝘅 = - ∫_{𝘅 \in Ω} ∇Q(𝘅)·∇φ_j(𝘅) d𝘅 (1)
298//
299// Now, for each of the 5 non-constant monomials (9 in 3D), we need to compute
300// Δ(x^α*y^β). For (α,β) ∊ {(1,0), (0,1), (1,1), (2,0), (0,2)}, this yields
301// the following equalities:
302//
303// Δx = 0 (2a)
304// Δy = 0 (2b)
305// Δxy = 0 (2c)
306// Δx² = 1 (2d)
307// Δy² = 1 (2e)
308//
309// If we plug these back into (1), and split the integral between the polyhedral
310// element E and Ω\E, we obtain the following constraints:
311//
312// ∫_E ∇Q·∇φ_j + ∫_E ΔQ φ_j = - ∫_{Ω\E} ∇Q·∇φ_j - ∫_{Ω\E} ΔQ φ_j (3)
313//
314// Note that the right-hand side of (3) is already known, since no two polyhedral
315// cells are adjacent to each other, and the bases overlapping a polyhedron vanish
316// on the boundary of the domain ∂Ω. This right-hand side is computed in advance
317// and passed to our functions as in argument `local_basis_integral`.
318//
319// The left-hand side of equation (3) reduces to the following (in 2D):
320//
321// ∫_E ∇x(φ_j) = c10 (4a)
322// ∫_E ∇y(φ_j) = c01 (4b)
323// ∫_E (y·∇x(φ_j) + y·∇x(φ_jj)) = c11 (4c)
324// ∫_E 2x·∇x(φ_j) + ∫_E 2 φ_j = c20 (4d)
325// ∫_E 2y·∇y(φ_j) + ∫_E 2 φ_j = c02 (4e)
326//
327// The next step is to express the basis φ_j in terms of the harmonic kernels and
328// quadratic polynomials:
329//
330// φ_j(x,y) = Σ_k w_k ψ_k(x,y) + a00 + a10*x + a01*y + a11*x*y + a20*x² + a02*y²
331//
332// The five equations in (4) become:
333//
334// Σ_j w_k ∫∇x(ψ_k) = ∫ Δ q10 (Σ_j w_k (ψ_k) + a00) + Σ_j w_k ∫∇q10 . ∇(ψ_k + a00)
335// Σ_j w_k ∫∇x(ψ_k) + a10 |E| + a11 ∫y + a20 ∫2x = c10
336// Σ_j w_k ∫∇y(ψ_k) + a01 |E| + a11 ∫x + a02 ∫2y = c01
337// Σ_j w_k (∫y·∇x(ψ_k) + ∫x·∇y(ψ_k)) + a10 ∫y + a01 ∫x + a11 (∫x²+∫y²) + a20 2∫xy + a02 2∫xy = c11
338// Σ_j w_k (2∫x·∇x(ψ_k) + 2ψ_k) + a10 4∫x + a01 2∫y + a11 4∫xy + a20 6∫x² + a02 2∫y² = c20
339// Σ_j w_k (2∫y·∇y(ψ_k) + 2ψ_k) + a10 2∫x + a01 4∫y + a11 4∫xy + a20 2∫x² + a02 6∫y² = c02
340// Σ_j w_k (2∫y·∇y(ψ_k) + 2ψ_k) = ∫ Δ q20 (Σ_j w_k (ψ_k) + a00) + Σ_j w_k ∫∇q20 . ∇(ψ_k + a00) = ∫ -2 (Σ_j w_k (ψ_k) + a00) + Σ_j w_k ∫2x ∇x(ψ_k)
341//
342// This system gives us a relationship between the fives a10, a01, a11, a20, a02
343// and the rest of the w_k + a constant translation term. We can write down the
344// corresponding system:
345//
346// a10 a01 a11 a20 a02
347// ┏ ┓ ┏ ┓
348// ┃ |E| ∫y 2∫x ┃ ┃ w_k ┃
349// ┃ ┃ ┃ ┊ ┃
350// ┃ |E| ∫x 2∫y ┃ ┃ ┊ ┃
351// ┃ ┃ ┃ ┊ ┃
352// M = ┃ ∫y ∫x ∫x²+∫y² 2∫xy 2∫xy ┃ = \tilde{L} ┃ ┊ ┃ + \tilde{t}
353// ┃ ┃ ┃ ┊ ┃
354// ┃ 4∫x 2∫y 4∫xy 6∫x² 2∫y² ┃ ┃ ┊ ┃
355// ┃ ┃ ┃w_#K ┃
356// ┃ 2∫x 4∫y 4∫xy 2∫x² 6∫y² ┃ ┃ a00 ┃
357// ┗ ┛ ┗ ┛
358//
359// Now, if we want to express w as w = Lv + t, and solve our least-square
360// system as before, we need to invert M and compute L and t in terms of
361// \tilde{L} and \tilde{t}
362//
363// ┏ ┓
364// ┃ 1 ┃
365// ┃ 1 ┃
366// ┃ · ┃
367// L = ┃ · ┃ ∊ ℝ^{ (#K+1+dim+dim*(dim+1)/2) x (#K+1}) }
368// ┃ 1 ┃
369// ┃ M^{-1} \tilde{L} ┃
370// ┗ ┛
371// ┏ ┓
372// ┃ 0 ┃
373// ┃ ┊ ┃
374// t = ┃ ┊ ┃ ∊ ℝ^{#K+1+dim+dim*(dim+1)/2}
375// ┃ 0 ┃
376// ┃ M^{-1} \tilde{t} ┃
377// ┗ ┛
378// After solving the new least square system A L v = rhs - A t, we can retrieve
379// w = L v
380//
382
383void RBFWithQuadratic::compute_kernels_matrix(const Eigen::MatrixXd &samples, Eigen::MatrixXd &A) const
384{
385 // Compute A
386 const int num_kernels = centers_.rows();
387 const int dim = (is_volume() ? 3 : 2);
388
389 A.resize(samples.rows(), num_kernels + 1 + dim + dim * (dim + 1) / 2);
390 for (int j = 0; j < num_kernels; ++j)
391 {
392 A.col(j) = (samples.rowwise() - centers_.row(j)).rowwise().norm().unaryExpr([this](double x) { return kernel(is_volume(), x); });
393 }
394 A.col(num_kernels).setOnes(); // constant term
395 A.middleCols(num_kernels + 1, dim) = samples; // linear terms
396 if (dim == 2)
397 {
398 A.middleCols(num_kernels + dim + 1, 1) = samples.rowwise().prod(); // mixed terms
399 }
400 else if (dim == 3)
401 {
402 A.middleCols(num_kernels + dim + 1, 3) = samples;
403 A.middleCols(num_kernels + dim + 1 + 0, 1).array() *= samples.col(1).array();
404 A.middleCols(num_kernels + dim + 1 + 1, 1).array() *= samples.col(2).array();
405 A.middleCols(num_kernels + dim + 1 + 2, 1).array() *= samples.col(0).array();
406 }
407 A.rightCols(dim) = samples.array().square(); // quadratic terms
408}
409
410// -----------------------------------------------------------------------------
411
413 const int num_bases,
414 const Quadrature &quadr,
415 const Eigen::MatrixXd &local_basis_integral,
416 Eigen::MatrixXd &L,
417 Eigen::MatrixXd &t) const
418{
419 const int num_kernels = centers_.rows();
420 const int dim = centers_.cols();
421 assert(dim == 2);
422
423 // K_cst = ∫ψ_k
424 // K_lin = ∫∇x(ψ_k), ∫∇y(ψ_k)
425 // K_mix = ∫y·∇x(ψ_k), ∫x·∇y(ψ_k)
426 // K_sqr = ∫x·∇x(ψ_k), ∫y·∇y(ψ_k)
427 Eigen::VectorXd K_cst = Eigen::VectorXd::Zero(num_kernels);
428 Eigen::MatrixXd K_lin = Eigen::MatrixXd::Zero(num_kernels, dim);
429 Eigen::MatrixXd K_mix = Eigen::MatrixXd::Zero(num_kernels, dim);
430 Eigen::MatrixXd K_sqr = Eigen::MatrixXd::Zero(num_kernels, dim);
431 for (int j = 0; j < num_kernels; ++j)
432 {
433 // ∫∇x(ψ_k)(p) = Σ_q (xq - xk) * 1/r * h'(r) * wq
434 // - xq is the x coordinate of the q-th quadrature point
435 // - wq is the q-th quadrature weight
436 // - r is the distance from pq to the kernel center
437 // - h is the RBF kernel (scalar function)
438 for (int q = 0; q < quadr.points.rows(); ++q)
439 {
440 const RowVectorNd p = quadr.points.row(q) - centers_.row(j);
441 const double r = p.norm();
442 const RowVectorNd gradPhi = p * kernel_prime(is_volume(), r) / r * quadr.weights(q);
443 K_cst(j) += kernel(is_volume(), r) * quadr.weights(q);
444 K_lin.row(j) += gradPhi;
445 K_mix(j, 0) += quadr.points(q, 1) * gradPhi(0);
446 K_mix(j, 1) += quadr.points(q, 0) * gradPhi(1);
447 K_sqr.row(j) += (quadr.points.row(q).array() * gradPhi.array()).matrix();
448 }
449 }
450
451 // I_lin = ∫x, ∫y
452 // I_mix = ∫xy
453 // I_sqr = ∫x², ∫y²
454 Eigen::RowVectorXd I_lin = (quadr.points.array().colwise() * quadr.weights.array()).colwise().sum();
455 Eigen::RowVectorXd I_mix = (quadr.points.rowwise().prod().array() * quadr.weights.array()).colwise().sum();
456 Eigen::RowVectorXd I_sqr = (quadr.points.array().square().colwise() * quadr.weights.array()).colwise().sum();
457 double volume = quadr.weights.sum();
458
459 // std::cout << I_lin << std::endl;
460 // std::cout << I_mix << std::endl;
461 // std::cout << I_sqr << std::endl;
462
463 // Compute M
464 Eigen::Matrix<double, 5, 5> M;
465 M << volume, 0, I_lin(1), 2 * I_lin(0), 0,
466 0, volume, I_lin(0), 0, 2 * I_lin(1),
467 I_lin(1), I_lin(0), I_sqr(0) + I_sqr(1), 2 * I_mix(0), 2 * I_mix(0),
468 4 * I_lin(0), 2 * I_lin(1), 4 * I_mix(0), 6 * I_sqr(0), 2 * I_sqr(1),
469 2 * I_lin(0), 4 * I_lin(1), 4 * I_mix(0), 2 * I_sqr(0), 6 * I_sqr(1);
470 Eigen::FullPivLU<Eigen::Matrix<double, 5, 5>> lu(M);
471 assert(lu.isInvertible());
472
473 // show_matrix_stats(M);
474
475 // Compute L
476 L.resize(num_kernels + 1 + dim + dim * (dim + 1) / 2, num_kernels + 1);
477 L.setZero();
478 L.diagonal().setOnes();
479
480 L.block(num_kernels + 1, 0, dim, num_kernels) = -K_lin.transpose();
481 L.block(num_kernels + 1 + dim, 0, 1, num_kernels) = -K_mix.transpose().colwise().sum();
482 L.block(num_kernels + 1 + dim + 1, 0, dim, num_kernels) = -2.0 * (K_sqr.colwise() + K_cst).transpose();
483 L.bottomRightCorner(dim, 1).setConstant(-2.0 * volume);
484 // j \in [0, 4]
485 // i \in [0, num_kernels]
486 // ass_val = [q_10, q_01, q_11, q_20, q_02, psi_0, ..., psi_k]
487
488 // strong rows is the evaluation at quadrature points
489 // strong.col(0) = pde(q_10) (probably 0)
490 // strong.col(4) = pde(q_02) (it is 2 for laplacian)
491 // L.block(num_kernels + 1 + i, j) = +/- assembler.assemble(ass_val, j, 5 + i) +/- (strong.col(j).array() * ass_val.basis_values[5+i].val.array() * quadr.weights.array()).sum();
492
493 L.block(num_kernels + 1, 0, 5, num_kernels + 1) = lu.solve(L.block(num_kernels + 1, 0, 5, num_kernels + 1));
494 // std::cout << L.bottomRightCorner(10, 10) << std::endl;
495
496 // Compute t
497 t.resize(L.rows(), num_bases);
498 t.setZero();
499 t.bottomRows(5) = local_basis_integral.transpose();
500 t.bottomRows(5) = lu.solve(weights_.bottomRows(5));
501}
502
504 const LinearAssembler &assembler,
505 const int num_bases,
506 const Quadrature &quadr,
507 const Eigen::MatrixXd &local_basis_integral,
508 Eigen::MatrixXd &L,
509 Eigen::MatrixXd &t) const
510{
511 // TODO
512 const double time = 0;
513 const int num_kernels = centers_.rows();
514 const int space_dim = centers_.cols();
515 const int assembler_dim = assembler.is_tensor() ? 2 : 1;
516 assert(space_dim == 2);
517
518 std::array<Eigen::MatrixXd, 5> strong;
519
520 // ass_val = [q_10, q_01, q_11, q_20, q_02, psi_0, ..., psi_k]
521 ElementAssemblyValues ass_val;
522 ass_val.has_parameterization = false;
523 ass_val.basis_values.resize(5 + num_kernels);
524
525 // evaluating monomial and grad of monomials at quad points
526 setup_monomials_vals_2d(0, quadr.points, ass_val);
527 setup_monomials_strong_2d(assembler_dim, assembler, quadr.points, quadr.weights.array(), strong);
528
529 // evaluating psi and grad psi at quadr points
530 for (int j = 0; j < num_kernels; ++j)
531 {
532 ass_val.basis_values[5 + j].val = Eigen::MatrixXd(quadr.points.rows(), 1);
533 ass_val.basis_values[5 + j].grad = Eigen::MatrixXd(quadr.points.rows(), quadr.points.cols());
534
535 for (int q = 0; q < quadr.points.rows(); ++q)
536 {
537 const RowVectorNd p = quadr.points.row(q) - centers_.row(j);
538 const double r = p.norm();
539
540 ass_val.basis_values[5 + j].val(q) = kernel(is_volume(), r);
541 ass_val.basis_values[5 + j].grad.row(q) = p * kernel_prime(is_volume(), r) / r;
542 }
543 }
544
545 for (size_t i = 5; i < ass_val.basis_values.size(); ++i)
546 {
547 ass_val.basis_values[i].grad_t_m = ass_val.basis_values[i].grad;
548 }
549
550 Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, 0, 10, 10> M(5 * assembler_dim, 5 * assembler_dim);
551 for (int i = 0; i < 5; ++i)
552 {
553 for (int j = 0; j < 5; ++j)
554 {
555 const auto tmp = assembler.assemble(LinearAssemblerData(ass_val, time, i, j, quadr.weights));
556
557 for (int d1 = 0; d1 < assembler_dim; ++d1)
558 {
559 for (int d2 = 0; d2 < assembler_dim; ++d2)
560 {
561 const int loc_index = d1 * assembler_dim + d2;
562 M(i * assembler_dim + d1, j * assembler_dim + d2) = tmp(loc_index) + (strong[i].row(loc_index).transpose().array() * ass_val.basis_values[j].val.array()).sum();
563 }
564 }
565 }
566 }
567
568 Eigen::FullPivLU<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic, 0, 10, 10>> lu(M);
569 assert(lu.isInvertible());
570
571 // Compute L
572 L.resize((num_kernels + 1 + space_dim + space_dim * (space_dim + 1) / 2) * assembler_dim, (num_kernels + 1) * assembler_dim);
573 L.setZero();
574 L.diagonal().setOnes();
575
576 for (int i = 0; i < 5; ++i)
577 {
578 for (int j = 0; j < num_kernels; ++j)
579 {
580 const auto tmp = assembler.assemble(LinearAssemblerData(ass_val, time, i, 5 + j, quadr.weights));
581 for (int d1 = 0; d1 < assembler_dim; ++d1)
582 {
583 for (int d2 = 0; d2 < assembler_dim; ++d2)
584 {
585 const int loc_index = d1 * assembler_dim + d2;
586 L((num_kernels + 1 + i) * assembler_dim + d1, j * assembler_dim + d2) = -tmp(loc_index) - (strong[i].row(loc_index).transpose().array() * ass_val.basis_values[5 + j].val.array()).sum();
587 // L(num_kernels + 1 + i*assembler_dim + d1, j*assembler_dim + d2) = -assembler.local_assemble(ass_val, i, 5 + j, quadr.weights)(0) - (strong[i].transpose().array() * ass_val.basis_values[5+j].val.array()).sum();
588 }
589 }
590 }
591 for (int d1 = 0; d1 < assembler_dim; ++d1)
592 {
593 for (int d2 = 0; d2 < assembler_dim; ++d2)
594 {
595 const int loc_index = d1 * assembler_dim + d2;
596 L(num_kernels + 1 + i * assembler_dim + d1, assembler_dim * num_kernels + d2) = -strong[i].row(loc_index).sum();
597 }
598 }
599
600 // L(num_kernels + 1 + i*assembler_dim + d1, assembler_dim*num_kernels) = - strong[i].sum();
601 }
602
603 L.block((num_kernels + 1) * assembler_dim, 0, 5 * assembler_dim, (num_kernels + 1) * assembler_dim) = lu.solve(L.block((num_kernels + 1) * assembler_dim, 0, 5 * assembler_dim, (num_kernels + 1) * assembler_dim));
604
605 // Compute t
606 // t == weights_
607 t.resize(L.rows(), num_bases * assembler_dim);
608 t.setZero();
609 t.bottomRows(5 * assembler_dim) = lu.solve(local_basis_integral.transpose());
610}
611
612// -----------------------------------------------------------------------------
613
615 const LinearAssembler &assembler,
616 const int num_bases,
617 const Quadrature &quadr,
618 const Eigen::MatrixXd &local_basis_integral,
619 Eigen::MatrixXd &L,
620 Eigen::MatrixXd &t) const
621{
622 const int num_kernels = centers_.rows();
623 const int dim = centers_.cols();
624 assert(dim == 3);
625 assert(local_basis_integral.cols() == 9);
626
627 // K_cst = ∫ψ_k
628 // K_lin = ∫∇x(ψ_k), ∫∇y(ψ_k), ∫∇z(ψ_k)
629 // K_mix = ∫(y·∇x(ψ_k)+x·∇y(ψ_k)), ∫(z·∇y(ψ_k)+y·∇z(ψ_k)), ∫(x·∇z(ψ_k)+z·∇x(ψ_k))
630 // K_sqr = ∫x·∇x(ψ_k), ∫y·∇y(ψ_k), ∫z·∇z(ψ_k)
631 Eigen::VectorXd K_cst = Eigen::VectorXd::Zero(num_kernels);
632 Eigen::MatrixXd K_lin = Eigen::MatrixXd::Zero(num_kernels, dim);
633 Eigen::MatrixXd K_mix = Eigen::MatrixXd::Zero(num_kernels, dim);
634 Eigen::MatrixXd K_sqr = Eigen::MatrixXd::Zero(num_kernels, dim);
635 for (int j = 0; j < num_kernels; ++j)
636 {
637 // ∫∇x(ψ_k)(p) = Σ_q (xq - xk) * 1/r * h'(r) * wq
638 // - xq is the x coordinate of the q-th quadrature point
639 // - wq is the q-th quadrature weight
640 // - r is the distance from pq to the kernel center
641 // - h is the RBF kernel (scalar function)
642 for (int q = 0; q < quadr.points.rows(); ++q)
643 {
644 const RowVectorNd p = quadr.points.row(q) - centers_.row(j);
645 const double r = p.norm();
646 const RowVectorNd gradPhi = p * kernel_prime(is_volume(), r) / r * quadr.weights(q);
647 K_cst(j) += kernel(is_volume(), r) * quadr.weights(q);
648 K_lin.row(j) += gradPhi;
649 for (int d = 0; d < dim; ++d)
650 {
651 K_mix(j, d) += quadr.points(q, (d + 1) % dim) * gradPhi(d) + quadr.points(q, d) * gradPhi((d + 1) % dim);
652 }
653 K_sqr.row(j) += (quadr.points.row(q).array() * gradPhi.array()).matrix();
654 }
655 }
656
657 // I_lin = ∫x, ∫y, ∫z
658 // I_sqr = ∫x², ∫y², ∫z²
659 // I_mix = ∫xy, ∫yz, ∫zx
660 Eigen::RowVectorXd I_lin = (quadr.points.array().colwise() * quadr.weights.array()).colwise().sum();
661 Eigen::RowVectorXd I_sqr = (quadr.points.array().square().colwise() * quadr.weights.array()).colwise().sum();
662 Eigen::RowVectorXd I_mix(3);
663 I_mix(0) = (quadr.points.col(0).array() * quadr.points.col(1).array() * quadr.weights.array()).sum();
664 I_mix(1) = (quadr.points.col(1).array() * quadr.points.col(2).array() * quadr.weights.array()).sum();
665 I_mix(2) = (quadr.points.col(2).array() * quadr.points.col(0).array() * quadr.weights.array()).sum();
666 double volume = quadr.weights.sum();
667
668 // std::cout << I_lin << std::endl;
669 // std::cout << I_mix << std::endl;
670 // std::cout << I_sqr << std::endl;
671
672 // Compute M
673 Eigen::Matrix<double, 9, 9> M;
674 M << volume, 0, 0, I_lin(1), 0, I_lin(2), 2 * I_lin(0), 0, 0,
675 0, volume, 0, I_lin(0), I_lin(2), 0, 0, 2 * I_lin(1), 0,
676 0, 0, volume, 0, I_lin(1), I_lin(0), 0, 0, 2 * I_lin(2),
677 I_lin(1), I_lin(0), 0, I_sqr(0) + I_sqr(1), I_mix(2), I_mix(1), 2 * I_mix(0), 2 * I_mix(0), 0,
678 0, I_lin(2), I_lin(1), I_mix(2), I_sqr(1) + I_sqr(2), I_mix(0), 0, 2 * I_mix(1), 2 * I_mix(1),
679 I_lin(2), 0, I_lin(0), I_mix(1), I_mix(0), I_sqr(2) + I_sqr(0), 2 * I_mix(2), 0, 2 * I_mix(2),
680 2 * I_lin(0), 0, 0, 2 * I_mix(0), 0, 2 * I_mix(2), 4 * I_sqr(0), 0, 0,
681 0, 2 * I_lin(1), 0, 2 * I_mix(0), 2 * I_mix(1), 0, 0, 4 * I_sqr(1), 0,
682 0, 0, 2 * I_lin(2), 0, 2 * I_mix(1), 2 * I_mix(2), 0, 0, 4 * I_sqr(2);
683 Eigen::Matrix<double, 1, 9> M_rhs;
684 M_rhs.segment<3>(0) = I_lin;
685 M_rhs.segment<3>(3) = I_mix;
686 M_rhs.segment<3>(6) = I_sqr;
687 // M_rhs << I_lin, I_mix, I_sqr;
688 M.bottomRows(dim).rowwise() += 2.0 * M_rhs;
689 Eigen::FullPivLU<Eigen::Matrix<double, 9, 9>> lu(M);
690 assert(lu.isInvertible());
691
692 // show_matrix_stats(M);
693
694 // Compute L
695 L.resize(num_kernels + 1 + dim + dim * (dim + 1) / 2, num_kernels + 1);
696 L.setZero();
697 L.diagonal().setOnes();
698 L.block(num_kernels + 1, 0, dim, num_kernels) = -K_lin.transpose();
699 L.block(num_kernels + 1 + dim, 0, dim, num_kernels) = -K_mix.transpose();
700 L.block(num_kernels + 1 + dim + dim, 0, dim, num_kernels) = -2.0 * (K_sqr.colwise() + K_cst).transpose();
701 L.bottomRightCorner(dim, 1).setConstant(-2.0 * volume);
702 L.block(num_kernels + 1, 0, 9, num_kernels + 1) = lu.solve(L.block(num_kernels + 1, 0, 9, num_kernels + 1));
703 // std::cout << L.bottomRightCorner(10, 10) << std::endl;
704
705 // Compute t
706 t.resize(L.rows(), num_bases);
707 t.setZero();
708 t.bottomRows(9) = local_basis_integral.transpose();
709 t.bottomRows(9) = lu.solve(weights_.bottomRows(9));
710}
711
712// -----------------------------------------------------------------------------
713
714void RBFWithQuadratic::compute_weights(const LinearAssembler &assembler, const Eigen::MatrixXd &samples,
715 const Eigen::MatrixXd &local_basis_integral, const Quadrature &quadr,
716 Eigen::MatrixXd &rhs, bool with_constraints)
717{
718#ifdef VERBOSE
719 logger().trace("#kernel centers: {}", centers_.rows());
720 logger().trace("#collocation points: {}", samples.rows());
721 logger().trace("#quadrature points: {}", quadr.weights.size());
722 logger().trace("#non-vanishing bases: {}", rhs.cols());
723#endif
724
725 if (!with_constraints)
726 {
727 // Compute A
728 Eigen::MatrixXd A;
729 compute_kernels_matrix(samples, A);
730
731 // Solve the system
732 const int num_kernels = centers_.rows();
733 logger().trace("-- Solving system of size {}x{}", num_kernels, num_kernels);
734 weights_ = (A.transpose() * A).ldlt().solve(A.transpose() * rhs);
735 logger().trace("-- Solved!");
736
737 return;
738 }
739
740 const int num_bases = rhs.cols();
741
742 // Compute A
743 Eigen::MatrixXd A;
744 compute_kernels_matrix(samples, A);
745
746 // Compute L and t
747 // Note that t is stored into `weights_` for memory efficiency reasons
748 Eigen::MatrixXd L;
749 if (is_volume())
750 {
751 compute_constraints_matrix_3d(assembler, num_bases, quadr, local_basis_integral, L, weights_);
752 }
753 else
754 {
755 compute_constraints_matrix_2d(assembler, num_bases, quadr, local_basis_integral, L, weights_);
756 }
757
758 // Compute b = rhs - A t
759 Eigen::MatrixXd b = rhs - A * weights_;
760
761// Solve the system
762#ifdef VERBOSE
763 logger().trace("-- Solving system of size {}x{}", L.cols(), L.cols());
764#endif
765 auto ldlt = (L.transpose() * A.transpose() * A * L).ldlt();
766 if (ldlt.info() == Eigen::NumericalIssue)
767 {
768 logger().error("-- WARNING: Numerical issues when solving the harmonic least square.");
769 }
770 weights_ += L * ldlt.solve(L.transpose() * A.transpose() * b);
771#ifdef VERBOSE
772 logger().trace("-- Solved!");
773#endif
774
775#ifdef VERBOSE
776 logger().trace("-- Mean residual: {}", (A * weights_ - rhs).array().abs().colwise().maxCoeff().mean());
777#endif
778
779#if 0
780 Eigen::MatrixXd MM, x, dx, val;
781 basis(0, quadr.points, val);
782 grad(0, quadr.points, MM);
783 int dim = (is_volume() ? 3 : 2);
784 for (int d = 0; d < dim; ++d) {
785 // basis(0, quadr.points, x);
786 // auto asd = quadr.points;
787 // asd.col(d).array() += 1e-7;
788 // basis(0, asd, dx);
789 // std::cout << (dx - x) / 1e-7 - MM.col(d) << std::endl;
790 std::cout << (MM.col(d).array() * quadr.weights.array()).sum() - local_basis_integral(0, d) << std::endl;
791 std::cout << ((
792 MM.col((d+1)%dim).array() * quadr.points.col(d).array()
793 + MM.col(d).array() * quadr.points.col((d+1)%dim).array()
794 ) * quadr.weights.array()).sum() - local_basis_integral(0, (dim == 2 ? 2 : (dim+d) )) << std::endl;
795 std::cout << 2.0 * (
796 (quadr.points.col(d).array() * MM.col(d).array()
797 + val.array())
798 * quadr.weights.array()
799 ).sum() - local_basis_integral(0, (dim == 2 ? (3 + d) : (dim+dim+d))) << std::endl;
800 }
801#endif
802}
double val
Definition Assembler.cpp:86
QuadratureVector da
Definition Assembler.cpp:23
ElementAssemblyValues vals
Definition Assembler.cpp:22
int x
virtual bool is_tensor() const
virtual VectorNd compute_rhs(const AutodiffHessianPt &pt) const
stores per element basis values at given quadrature points and geometric mapping
assemble matrix based on the local assembler local assembler is eg Laplace, LinearElasticity etc
void assemble(const bool is_volume, const int n_basis, const std::vector< basis::ElementBases > &bases, const std::vector< basis::ElementBases > &gbases, const AssemblyValsCache &cache, const double t, StiffnessMatrix &stiffness, const bool is_mass=false) const override
assembles the stiffness matrix for the given basis the bilinear form (local assembler) is encoded by ...
static void setup_monomials_strong_2d(const int dim, const assembler::LinearAssembler &assembler, const Eigen::MatrixXd &pts, const QuadratureVector &da, std::array< Eigen::MatrixXd, 5 > &strong)
void basis(const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) const
Evaluates one RBF function over a list of coordinates.
void compute_constraints_matrix_3d(const assembler::LinearAssembler &assembler, const int num_bases, const quadrature::Quadrature &quadr, const Eigen::MatrixXd &local_basis_integral, Eigen::MatrixXd &L, Eigen::MatrixXd &t) const
void bases_values(const Eigen::MatrixXd &samples, Eigen::MatrixXd &val) const
Batch evaluates the RBF + polynomials on a set of sample points.
void bases_grads(const int axis, const Eigen::MatrixXd &samples, Eigen::MatrixXd &val) const
Batch evaluates the gradient of the RBF + polynomials on a set of sample points.
void compute_constraints_matrix_2d_old(const int num_bases, const quadrature::Quadrature &quadr, const Eigen::MatrixXd &local_basis_integral, Eigen::MatrixXd &L, Eigen::MatrixXd &t) const
void compute_weights(const assembler::LinearAssembler &assembler, const Eigen::MatrixXd &collocation_points, const Eigen::MatrixXd &local_basis_integral, const quadrature::Quadrature &quadr, Eigen::MatrixXd &rhs, bool with_constraints)
static void setup_monomials_vals_2d(const int star_index, const Eigen::MatrixXd &pts, assembler::ElementAssemblyValues &vals)
void compute_constraints_matrix_2d(const assembler::LinearAssembler &assembler, const int num_bases, const quadrature::Quadrature &quadr, const Eigen::MatrixXd &local_basis_integral, Eigen::MatrixXd &L, Eigen::MatrixXd &t) const
RBFWithQuadratic(const assembler::LinearAssembler &assembler, const Eigen::MatrixXd &centers, const Eigen::MatrixXd &collocation_points, const Eigen::MatrixXd &local_basis_integral, const quadrature::Quadrature &quadr, Eigen::MatrixXd &rhs, bool with_constraints=true)
Initialize RBF functions over a polytope element.
void grad(const int local_index, const Eigen::MatrixXd &uv, Eigen::MatrixXd &val) const
Evaluates the gradient of one RBF function over a list of coordinates.
void compute_kernels_matrix(const Eigen::MatrixXd &samples, Eigen::MatrixXd &A) const
Used for test only.
spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:42
Eigen::Matrix< AutodiffScalarHessian, Eigen::Dynamic, 1, 0, 3, 1 > AutodiffHessianPt
DScalar2< double, Eigen::Matrix< double, Eigen::Dynamic, 1, 0, 3, 1 >, Eigen::Matrix< double, Eigen::Dynamic, Eigen::Dynamic, 0, 3, 3 > > AutodiffScalarHessian
Eigen::Matrix< double, Eigen::Dynamic, 1, 0, MAX_QUAD_POINTS, 1 > QuadratureVector
Definition Types.hpp:17
Eigen::Matrix< double, 1, Eigen::Dynamic, Eigen::RowMajor, 1, 3 > RowVectorNd
Definition Types.hpp:13
static void setVariableCount(size_t value)
Set the independent variable count used by the automatic differentiation layer.
Definition autodiff.h:54