3#include <Eigen/Cholesky>
6#include <boost/range/adaptor/indexed.hpp>
7#include <boost/range/irange.hpp>
22template <
typename RADIAL_BASIS_FUNCTION_T>
37 template <
typename IndexContainer>
84 for (
unsigned int d = 0; d < v.
size(); ++d) {
85 v[d] = (u[d] - v[d]) *
static_cast<int>(activeAxis[d]);
88 return std::accumulate(v.
begin(), v.
end(),
static_cast<double>(0.), [](
auto &res,
auto &val) { return res + val * val; });
92template <
typename IndexContainer>
101 if (axis[d] ==
false) {
104 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); });
106 differences[d] = std::make_pair<int, double>(d, std::abs(mesh.
vertex(*res.second).
coord(d) - mesh.
vertex(*res.first).
coord(d)));
110 std::sort(differences.
begin(), differences.
end(), [](
const auto &d1,
const auto &d2) { return d1.second < d2.second; });
112 axis[differences[0].first] =
false;
116template <
typename IndexContainer>
120 for (
const auto &i : IDs | boost::adaptors::indexed()) {
123 matrix(i.index(), startIndex) = 1.0;
129 for (
unsigned int d = 0; d < activeAxis.
size(); ++d) {
131 PRECICE_ASSERT(matrix.rows() > i.index(), matrix.rows(), i.index());
132 PRECICE_ASSERT(matrix.cols() > startIndex + 1 + k, matrix.cols(), startIndex + 1 + k);
133 matrix(i.index(), startIndex + 1 + k) = u[d];
140template <
typename RADIAL_BASIS_FUNCTION_T,
typename IndexContainer>
145 const unsigned int deadDimensions =
std::count(activeAxis.
begin(), activeAxis.
end(),
false);
146 const unsigned int dimensions = 3;
147 const unsigned int polyparams = polynomial ==
Polynomial::ON ? 1 + dimensions - deadDimensions : 0;
150 const auto inputSize = inputIDs.size();
151 const auto n = inputSize + polyparams;
156 Eigen::MatrixXd matrixCLU(n, n);
164 auto i_iter = inputIDs.begin();
165 Eigen::Index i_index = 0;
166 for (; i_iter != inputIDs.end(); ++i_iter, ++i_index) {
168 auto j_iter = i_iter;
169 auto j_index = i_index;
170 for (; j_iter != inputIDs.end(); ++j_iter, ++j_index) {
173 matrixCLU(i_index, j_index) = basisFunction.evaluate(
std::sqrt(squaredDifference));
181 matrixCLU.triangularView<Eigen::Lower>() = matrixCLU.transpose();
185template <
typename RADIAL_BASIS_FUNCTION_T,
typename IndexContainer>
186Eigen::MatrixXd
buildMatrixA(RADIAL_BASIS_FUNCTION_T basisFunction,
const mesh::Mesh &inputMesh,
const IndexContainer &inputIDs,
190 const unsigned int deadDimensions =
std::count(activeAxis.
begin(), activeAxis.
end(),
false);
191 const unsigned int dimensions = 3;
192 const unsigned int polyparams = polynomial ==
Polynomial::ON ? 1 + dimensions - deadDimensions : 0;
194 const auto inputSize = inputIDs.size();
195 const auto outputSize = outputIDs.size();
196 const auto n = inputSize + polyparams;
201 Eigen::MatrixXd matrixA(outputSize, n);
204 for (
const auto &i : outputIDs | boost::adaptors::indexed()) {
205 const auto &u = outputMesh.
vertex(i.value()).rawCoords();
206 for (
const auto &j : inputIDs | boost::adaptors::indexed()) {
207 const auto &v = inputMesh.
vertex(j.value()).rawCoords();
209 matrixA(i.index(), j.index()) = basisFunction.evaluate(
std::sqrt(squaredDifference));
220template <
typename RADIAL_BASIS_FUNCTION_T>
221template <
typename IndexContainer>
225 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.");
231 bool decompositionSuccessful =
false;
232 if constexpr (RADIAL_BASIS_FUNCTION_T::isStrictlyPositiveDefinite()) {
233 _decMatrixC =
buildMatrixCLU(basisFunction, inputMesh, inputIDs, activeAxis, polynomial).llt();
234 decompositionSuccessful = _decMatrixC.info() == Eigen::ComputationInfo::Success;
236 _decMatrixC =
buildMatrixCLU(basisFunction, inputMesh, inputIDs, activeAxis, polynomial).colPivHouseholderQr();
237 decompositionSuccessful = _decMatrixC.isInvertible();
241 "The interpolation matrix of the RBF mapping from mesh \"{}\" to mesh \"{}\" is not invertable. "
242 "This means that the mapping problem is not well-posed. "
243 "Please check if your coupling meshes are correct (e.g. no vertices are duplicated) or reconfigure "
244 "your basis-function (e.g. reduce the support-radius).",
248 _matrixA =
buildMatrixA(basisFunction, inputMesh, inputIDs, outputMesh, outputIDs, activeAxis, polynomial);
254 auto localActiveAxis = activeAxis;
255 unsigned int polyParams = 4 -
std::count(localActiveAxis.begin(), localActiveAxis.end(),
false);
259 _matrixQ.resize(inputIDs.size(), polyParams);
263 Eigen::JacobiSVD<Eigen::MatrixXd> svd(_matrixQ);
265 PRECICE_DEBUG(
"Singular values in polynomial solver: {}", svd.singularValues());
270 if (conditionNumber > 1e5) {
272 polyParams = 4 -
std::count(localActiveAxis.begin(), localActiveAxis.end(),
false);
279 _matrixV.resize(outputIDs.size(), polyParams);
283 _qrMatrixQ = _matrixQ.colPivHouseholderQr();
287template <
typename RADIAL_BASIS_FUNCTION_T>
294 Eigen::VectorXd Au = _matrixA.transpose() * inputData;
298 Eigen::VectorXd
out = _decMatrixC.solve(Au);
301 Eigen::VectorXd epsilon = _matrixV.transpose() * inputData;
305 epsilon -= _matrixQ.transpose() *
out;
310#if EIGEN_VERSION_AT_LEAST(3, 4, 0)
311 out -=
static_cast<Eigen::VectorXd
>(_qrMatrixQ.transpose().solve(-epsilon));
314 Eigen::VectorXd sigma(_matrixQ.rows());
315 const Eigen::Index nonzero_pivots = _qrMatrixQ.nonzeroPivots();
317 if (nonzero_pivots == 0) {
320 Eigen::VectorXd c(_qrMatrixQ.colsPermutation().transpose() * (-epsilon));
322 _qrMatrixQ.matrixQR().topLeftCorner(nonzero_pivots, nonzero_pivots).template triangularView<Eigen::Upper>().transpose().conjugate().solveInPlace(c.topRows(nonzero_pivots));
324 sigma.topRows(nonzero_pivots) = c.topRows(nonzero_pivots);
325 sigma.bottomRows(_qrMatrixQ.rows() - nonzero_pivots).setZero();
327 sigma.applyOnTheLeft(_qrMatrixQ.householderQ().setLength(nonzero_pivots));
335template <
typename RADIAL_BASIS_FUNCTION_T>
339 Eigen::VectorXd polynomialContribution;
342 polynomialContribution = _qrMatrixQ.solve(inputData);
343 inputData -= (_matrixQ * polynomialContribution);
348 Eigen::VectorXd p = _decMatrixC.solve(inputData);
350 Eigen::VectorXd
out = _matrixA * p;
354 out += (_matrixV * polynomialContribution);
359template <
typename RADIAL_BASIS_FUNCTION_T>
362 _matrixA = Eigen::MatrixXd();
366template <
typename RADIAL_BASIS_FUNCTION_T>
369 return _matrixA.cols();
372template <
typename RADIAL_BASIS_FUNCTION_T>
375 return _matrixA.rows();
#define PRECICE_DEBUG(...)
#define PRECICE_CHECK(check,...)
#define PRECICE_ASSERT(...)
This class provides a lightweight logger.
Eigen::Index getInputSize() 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.
Eigen::VectorXd solveConservative(const Eigen::VectorXd &inputData, Polynomial polynomial) const
Maps the given input data.
Eigen::ColPivHouseholderQR< Eigen::MatrixXd > _qrMatrixQ
Decomposition of the polynomial (for separate polynomial)
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
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.
double coord(int index) const
Returns a coordinate of a vertex.
const RawCoords & rawCoords() const
Direct access to the coordinates.
T minmax_element(T... args)
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.
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
Main namespace of the precice library.