3#include <Eigen/Cholesky>
6#include <boost/range/adaptor/indexed.hpp>
7#include <boost/range/irange.hpp>
24template <
typename RADIAL_BASIS_FUNCTION_T>
39 template <
typename IndexContainer>
46 void computeCacheData(Eigen::MatrixXd &inputData,
Polynomial polynomial, Eigen::MatrixXd &polyOut, Eigen::MatrixXd &coeffsOut)
const;
63 template <
typename IndexContainer>
65 const RADIAL_BASIS_FUNCTION_T &function,
const IndexContainer &inputIDs,
const mesh::Mesh &inMesh)
const;
67 template <
typename IndexContainer>
69 const RADIAL_BASIS_FUNCTION_T &basisFunction,
const IndexContainer &inputIDs,
const mesh::Mesh &inMesh)
const;
108 for (
unsigned int d = 0; d < v.
size(); ++d) {
109 v[d] = (u[d] - v[d]) *
static_cast<int>(activeAxis[d]);
112 return std::accumulate(v.
begin(), v.
end(),
static_cast<double>(0.), [](
auto &res,
auto &val) { return res + val * val; });
116template <
typename IndexContainer>
125 if (axis[d] ==
false) {
128 auto res =
std::minmax_element(IDs.begin(), IDs.end(), [&](
const auto &a,
const auto &b) { return mesh.vertex(a).coord(d) < mesh.vertex(b).coord(d); });
134 std::sort(differences.
begin(), differences.
end(), [](
const auto &d1,
const auto &d2) { return d1.second < d2.second; });
136 axis[differences[0].first] =
false;
140template <
typename IndexContainer>
144 for (
const auto &i : IDs | boost::adaptors::indexed()) {
147 matrix(i.index(), startIndex) = 1.0;
150 const auto &u =
mesh.vertex(i.value()).rawCoords();
153 for (
unsigned int d = 0; d < activeAxis.
size(); ++d) {
155 PRECICE_ASSERT(matrix.rows() > i.index(), matrix.rows(), i.index());
156 PRECICE_ASSERT(matrix.cols() > startIndex + 1 + k, matrix.cols(), startIndex + 1 + k);
157 matrix(i.index(), startIndex + 1 + k) = u[d];
164template <
typename RADIAL_BASIS_FUNCTION_T,
typename IndexContainer>
169 const unsigned int deadDimensions =
std::count(activeAxis.
begin(), activeAxis.
end(),
false);
170 const unsigned int dimensions = 3;
171 const unsigned int polyparams = polynomial ==
Polynomial::ON ? 1 + dimensions - deadDimensions : 0;
174 const auto inputSize = inputIDs.size();
175 const auto n = inputSize + polyparams;
180 Eigen::MatrixXd matrixCLU(n, n);
188 auto i_iter = inputIDs.begin();
189 Eigen::Index i_index = 0;
190 for (; i_iter != inputIDs.end(); ++i_iter, ++i_index) {
192 auto j_iter = i_iter;
193 auto j_index = i_index;
194 for (; j_iter != inputIDs.end(); ++j_iter, ++j_index) {
197 matrixCLU(i_index, j_index) = basisFunction.evaluate(
std::sqrt(squaredDifference));
205 matrixCLU.triangularView<Eigen::Lower>() = matrixCLU.transpose();
209template <
typename RADIAL_BASIS_FUNCTION_T,
typename IndexContainer>
210Eigen::MatrixXd
buildMatrixA(RADIAL_BASIS_FUNCTION_T basisFunction,
const mesh::Mesh &inputMesh,
const IndexContainer &inputIDs,
214 const unsigned int deadDimensions =
std::count(activeAxis.
begin(), activeAxis.
end(),
false);
215 const unsigned int dimensions = 3;
216 const unsigned int polyparams = polynomial ==
Polynomial::ON ? 1 + dimensions - deadDimensions : 0;
218 const auto inputSize = inputIDs.size();
219 const auto outputSize = outputIDs.size();
220 const auto n = inputSize + polyparams;
225 Eigen::MatrixXd matrixA(outputSize, n);
228 for (
const auto &i : outputIDs | boost::adaptors::indexed()) {
230 for (
const auto &j : inputIDs | boost::adaptors::indexed()) {
233 matrixA(i.index(), j.index()) = basisFunction.evaluate(
std::sqrt(squaredDifference));
273 Eigen::VectorXd inverseDiagonal = (L_inv.array().square().colwise().sum()).transpose();
275 return inverseDiagonal;
300 Eigen::VectorXd inverseDiagonal;
301 const Eigen::Index n = decMatrixC.matrixR().cols();
305 Eigen::MatrixXd R_inv = Eigen::MatrixXd::Identity(n, n);
306 decMatrixC.matrixR().template triangularView<Eigen::Upper>().solveInPlace(R_inv);
307 Eigen::VectorXi P = decMatrixC.colsPermutation().indices();
308 Eigen::MatrixXd Q = decMatrixC.householderQ();
311 inverseDiagonal.resize(n);
312 for (Eigen::Index i = 0; i < n; ++i) {
313 inverseDiagonal(P(i)) = R_inv.row(i) * Q.transpose().col(P(i));
315 return inverseDiagonal;
318template <
typename RADIAL_BASIS_FUNCTION_T>
323 const Eigen::Index n = lambda.size();
340template <
typename RADIAL_BASIS_FUNCTION_T>
341template <
typename IndexContainer>
345 PRECICE_ASSERT(!(RADIAL_BASIS_FUNCTION_T::isStrictlyPositiveDefinite() && polynomial ==
Polynomial::ON),
"The integrated polynomial (polynomial=\"on\") is not supported for the selected radial-basis function. Please select another radial-basis function or change the polynomial configuration.");
351 bool decompositionSuccessful =
false;
352 if constexpr (RADIAL_BASIS_FUNCTION_T::isStrictlyPositiveDefinite()) {
354 decompositionSuccessful =
_decMatrixC.info() == Eigen::ComputationInfo::Success;
357 decompositionSuccessful =
_decMatrixC.isInvertible();
361 "The interpolation matrix of the RBF mapping from mesh \"{}\" to mesh \"{}\" is not invertable. "
362 "This means that the mapping problem is not well-posed. "
363 "Please check if your coupling meshes are correct (e.g. no vertices are duplicated) or reconfigure "
364 "your basis-function (e.g. reduce the support-radius).",
374 _matrixA =
buildMatrixA(basisFunction, inputMesh, inputIDs, outputMesh, outputIDs, activeAxis, polynomial);
385 _matrixQ.resize(inputIDs.size(), polyParams);
389 Eigen::JacobiSVD<Eigen::MatrixXd> svd(
_matrixQ);
391 PRECICE_DEBUG(
"Singular values in polynomial solver: {}", svd.singularValues());
396 if (conditionNumber > 1e5) {
405 _matrixV.resize(outputIDs.size(), polyParams);
413template <
typename RADIAL_BASIS_FUNCTION_T>
420 Eigen::VectorXd Au =
_matrixA.transpose() * inputData;
427 Eigen::VectorXd epsilon =
_matrixV.transpose() * inputData;
431 epsilon -=
_matrixQ.transpose() * out;
435 out -=
static_cast<Eigen::VectorXd
>(
_qrMatrixQ.transpose().solve(-epsilon));
441template <
typename RADIAL_BASIS_FUNCTION_T>
445 Eigen::VectorXd polynomialContribution;
448 polynomialContribution =
_qrMatrixQ.solve(inputData);
449 inputData -= (
_matrixQ * polynomialContribution);
465 out += (
_matrixV * polynomialContribution);
470template <
typename RADIAL_BASIS_FUNCTION_T>
484template <
typename RADIAL_BASIS_FUNCTION_T>
485template <
typename IndexContainer>
487 const RADIAL_BASIS_FUNCTION_T &basisFunction,
const IndexContainer &inputIDs,
const mesh::Mesh &inMesh)
const
494 Eigen::VectorXd result(coeffs.cols());
499 for (
const auto &j : inputIDs | boost::adaptors::indexed()) {
502 double eval = basisFunction.evaluate(
std::sqrt(squaredDifference));
504 result += eval * coeffs.row(j.index()).transpose();
509 if (poly.size() > 0) {
511 result += 1 * poly.row(0).transpose();
516 result += out[d] * poly.row(k).transpose();
524template <
typename RADIAL_BASIS_FUNCTION_T>
525template <
typename IndexContainer>
527 const RADIAL_BASIS_FUNCTION_T &basisFunction,
const IndexContainer &inputIDs,
const mesh::Mesh &inMesh)
const
534 PRECICE_ASSERT(Au.rows() ==
static_cast<Eigen::Index
>(inputIDs.size()));
538 for (
const auto &j : inputIDs | boost::adaptors::indexed()) {
541 double eval = basisFunction.evaluate(
std::sqrt(squaredDifference));
543 Au.row(j.index()) += eval * load.transpose();
548 if (epsilon.size() > 0) {
552 epsilon.row(0) += 1 * load.transpose();
557 epsilon.row(k) += out[d] * load.transpose();
564template <
typename RADIAL_BASIS_FUNCTION_T>
570 if (epsilon.size() > 0) {
571 epsilon -=
_matrixQ.transpose() * out;
573 out -=
static_cast<Eigen::MatrixXd
>(
_qrMatrixQ.transpose().solve(-epsilon));
577template <
typename RADIAL_BASIS_FUNCTION_T>
584template <
typename RADIAL_BASIS_FUNCTION_T>
590template <
typename RADIAL_BASIS_FUNCTION_T>
596template <
typename RADIAL_BASIS_FUNCTION_T>
#define PRECICE_DEBUG(...)
#define PRECICE_TRACE(...)
#define PRECICE_INFO(...)
#define PRECICE_CHECK(check,...)
#define PRECICE_ASSERT(...)
This class provides a lightweight logger.
Eigen::Index getInputSize() const
void computeCacheData(Eigen::MatrixXd &inputData, Polynomial polynomial, Eigen::MatrixXd &polyOut, Eigen::MatrixXd &coeffsOut) const
bool computeCrossValidation
double evaluateRippaLOOCVerror(const Eigen::VectorXd &lambda) const
Eigen::MatrixXd _matrixA
Evaluation matrix (output x input)
RADIAL_BASIS_FUNCTION_T BASIS_FUNCTION_T
Eigen::VectorXd solveConsistent(Eigen::VectorXd &inputData, Polynomial polynomial) const
Maps the given input data.
std::conditional_t< RADIAL_BASIS_FUNCTION_T::isStrictlyPositiveDefinite(), Eigen::LLT< Eigen::MatrixXd >, Eigen::ColPivHouseholderQR< Eigen::MatrixXd > > DecompositionType
Eigen::VectorXd solveConservative(const Eigen::VectorXd &inputData, Polynomial polynomial) const
Maps the given input data.
std::array< bool, 3 > _localActiveAxis
Eigen::Index getNumberOfPolynomials() const
Eigen::VectorXd interpolateAt(const mesh::Vertex &v, const Eigen::MatrixXd &poly, const Eigen::MatrixXd &coeffs, const RADIAL_BASIS_FUNCTION_T &function, const IndexContainer &inputIDs, const mesh::Mesh &inMesh) const
Eigen::ColPivHouseholderQR< Eigen::MatrixXd > _qrMatrixQ
Decomposition of the polynomial (for separate polynomial)
void addWriteDataToCache(const mesh::Vertex &v, const Eigen::VectorXd &load, Eigen::MatrixXd &epsilon, Eigen::MatrixXd &Au, const RADIAL_BASIS_FUNCTION_T &basisFunction, const IndexContainer &inputIDs, const mesh::Mesh &inMesh) const
Eigen::VectorXd _inverseDiagonal
Diagonal entris of the inverse matrix C, requires for the Rippa scheme.
RadialBasisFctSolver()=default
Default constructor.
Eigen::Index getOutputSize() const
Eigen::MatrixXd _matrixV
Polynomial matrix of the output mesh (for separate polynomial)
DecompositionType _decMatrixC
Decomposition of the interpolation matrix.
precice::logging::Logger _log
void evaluateConservativeCache(Eigen::MatrixXd &epsilon, const Eigen::MatrixXd &Au, Eigen::MatrixXd &result) const
Eigen::MatrixXd _matrixQ
Polynomial matrix of the input mesh (for separate polynomial)
Container and creator for meshes.
int getDimensions() const
const std::string & getName() const
Returns the name of the mesh, as set in the config file.
Vertex & vertex(VertexID id)
Mutable access to a vertex by VertexID.
const RawCoords & rawCoords() const
Direct access to the coordinates.
T minmax_element(T... args)
contains data mapping from points to meshes.
double computeSquaredDifference(const std::array< double, 3 > &u, std::array< double, 3 > v, const std::array< bool, 3 > &activeAxis={{true, true, true}})
Deletes all dead directions from fullVector and returns a vector of reduced dimensionality.
Eigen::VectorXd computeInverseDiagonal(Eigen::LLT< Eigen::MatrixXd > decMatrixC)
void fillPolynomialEntries(Eigen::MatrixXd &matrix, const mesh::Mesh &mesh, const IndexContainer &IDs, Eigen::Index startIndex, std::array< bool, 3 > activeAxis)
constexpr void reduceActiveAxis(const mesh::Mesh &mesh, const IndexContainer &IDs, std::array< bool, 3 > &axis)
given the active axis, computes sets the axis with the lowest spatial expansion to dead
Eigen::MatrixXd buildMatrixCLU(RADIAL_BASIS_FUNCTION_T basisFunction, const mesh::Mesh &inputMesh, const IndexContainer &inputIDs, std::array< bool, 3 > activeAxis, Polynomial polynomial)
Eigen::MatrixXd buildMatrixA(RADIAL_BASIS_FUNCTION_T basisFunction, const mesh::Mesh &inputMesh, const IndexContainer &inputIDs, const mesh::Mesh &outputMesh, const IndexContainer outputIDs, std::array< bool, 3 > activeAxis, Polynomial polynomial)
Polynomial
How to handle the polynomial?
constexpr double NUMERICAL_ZERO_DIFFERENCE
provides Mesh, Data and primitives.
Eigen::MatrixXd invertLowerTriangularBlockwise(const Eigen::MatrixXd &L)
Implements an iterative block scheme to determine the inverse of a lower triangular matrix which is m...