preCICE v3.2.0
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
SphericalVertexCluster.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <Eigen/Core>
4
5#include <boost/container/flat_set.hpp>
6
10#include "mesh/Filter.hpp"
12#include "profiling/Event.hpp"
13
14namespace precice::mapping {
15
32template <typename RADIAL_BASIS_FUNCTION_T>
34public:
54 double radius,
55 RADIAL_BASIS_FUNCTION_T function,
56 Polynomial polynomial,
57 mesh::PtrMesh inputMesh,
58 mesh::PtrMesh outputMesh);
59
61 void mapConservative(const time::Sample &inData, Eigen::VectorXd &outData) const;
62
64 void computeCacheData(const Eigen::Ref<const Eigen::MatrixXd> &globalIn, Eigen::MatrixXd &polyOut, Eigen::MatrixXd &coefficientsOut) const;
65
67 void mapConsistent(const time::Sample &inData, Eigen::VectorXd &outData) const;
68
70 void setNormalizedWeight(double normalizedWeight, VertexID vertexID);
71
72 void evaluateConservativeCache(Eigen::MatrixXd &epsilon, const Eigen::MatrixXd &Au, Eigen::Ref<Eigen::MatrixXd> out);
73
75 double computeWeight(const mesh::Vertex &v) const;
76
77 Eigen::VectorXd interpolateAt(const mesh::Vertex &v, const Eigen::MatrixXd &poly, const Eigen::MatrixXd &coeffs, const mesh::Mesh &inMesh) const;
79 unsigned int getNumberOfInputVertices() const;
80
83
85 void clear();
86
89 bool empty() const;
90
91 void addWriteDataToCache(const mesh::Vertex &v, const Eigen::VectorXd &load, Eigen::MatrixXd &epsilon, Eigen::MatrixXd &Au,
92 const mesh::Mesh &inMesh);
93
94 void initializeCacheData(Eigen::MatrixXd &polynomial, Eigen::MatrixXd &coeffs, const int nComponents);
95
96private:
98 mutable precice::logging::Logger _log{"mapping::SphericalVertexCluster"};
99
102
104 const double _radius;
105
108
109 // Stores the global IDs of the vertices so that we can apply a binary
110 // search in order to query specific objects. Here we have logarithmic
111 // complexity for setting the weights (only performed once), and constant
112 // complexity when traversing through the IDs (performed in each iteration).
113
115 boost::container::flat_set<VertexID> _inputIDs;
116
118 boost::container::flat_set<VertexID> _outputIDs;
119
122 Eigen::VectorXd _normalizedWeights;
123
126
127 RADIAL_BASIS_FUNCTION_T _function;
128
131
134};
135
136// --------------------------------------------------- HEADER IMPLEMENTATIONS
137
138template <typename RADIAL_BASIS_FUNCTION_T>
140 mesh::Vertex center,
141 double radius,
142 RADIAL_BASIS_FUNCTION_T function,
143 Polynomial polynomial,
144 mesh::PtrMesh inputMesh,
145 mesh::PtrMesh outputMesh)
146 : _center(center), _radius(radius), _polynomial(polynomial), _function(function), _weightingFunction(radius)
147{
149 precice::profiling::Event eq("map.pou.computeMapping.queryVertices");
150 // Disable integrated polynomial, as it might cause locally singular matrices
151 PRECICE_ASSERT(_polynomial != Polynomial::ON, "Integrated polynomial is not supported for partition of unity data mappings.");
152
153 // Get vertices to be mapped
154 // Subtract a safety margin to exclude the vertices at the edge
155 auto outIDs = outputMesh->index().getVerticesInsideBox(center, radius - math::NUMERICAL_ZERO_DIFFERENCE);
156 // Constructing the partition when we don't have evaluation points is pointless
157 auto inIDs = inputMesh->index().getVerticesInsideBox(center, radius);
158
159 // Transform the vector to the appropriate boost data structure
160 // The IDs are sorted in the boost flat_set, hence, the function here has N log(N) complexity
161 _inputIDs.insert(inIDs.begin(), inIDs.end());
162 _outputIDs.insert(outIDs.begin(), outIDs.end());
163 eq.stop();
164 // If the cluster is empty, we return immediately
165 if (empty()) {
166 return;
167 }
168
169 PRECICE_DEBUG("SphericalVertexCluster input size: {}", inIDs.size());
170 PRECICE_DEBUG("SphericalVertexCluster output size: {}", outIDs.size());
171
172 // The polynomial system is underdetermined if inIDs.size() < dimension + 1. However, the dynamic adoption of the axis in the RBF solver
173 // disables axis, if necessary. Hence, we don't disable the complete polynomial here for underdetermined systems. The case should anyway
174 // only occur for an almost unreasonable small vertices-per-cluster configuration.
175
176 // Construct the solver. Here, the constructor of the RadialBasisFctSolver computes already the decompositions etc, such that we can mark the
177 // mapping in this cluster as computed (mostly for debugging purpose)
178 std::vector<bool> deadAxis(inputMesh->getDimensions(), false);
179 precice::profiling::Event e("map.pou.computeMapping.rbfSolver");
180 _rbfSolver = RadialBasisFctSolver<RADIAL_BASIS_FUNCTION_T>{function, *inputMesh.get(), _inputIDs, *outputMesh.get(), _outputIDs, deadAxis, _polynomial};
181 _hasComputedMapping = true;
182}
183
184template <typename RADIAL_BASIS_FUNCTION_T>
186{
187 PRECICE_ASSERT(_outputIDs.size() > 0);
188 PRECICE_ASSERT(_outputIDs.contains(id), id);
189 PRECICE_ASSERT(normalizedWeight > 0);
190
191 if (_normalizedWeights.size() == 0)
192 _normalizedWeights.resize(_outputIDs.size());
193
194 // The find method of boost flat_set comes with O(log(N)) complexity (the more expensive part here)
195 auto localID = _outputIDs.index_of(_outputIDs.find(id));
196
197 PRECICE_ASSERT(static_cast<Eigen::Index>(localID) < _normalizedWeights.size(), localID, _normalizedWeights.size());
198 _normalizedWeights[localID] = normalizedWeight;
199}
200
201template <typename RADIAL_BASIS_FUNCTION_T>
203{
204 // First, a few sanity checks. Empty partitions shouldn't be stored at all
205 PRECICE_ASSERT(!empty());
206 PRECICE_ASSERT(_hasComputedMapping);
207 PRECICE_ASSERT(_normalizedWeights.size() == static_cast<Eigen::Index>(_outputIDs.size()));
208
209 // Define an alias for data dimension in order to avoid ambiguity
210 const unsigned int nComponents = inData.dataDims;
211 const auto &localInData = inData.values;
212
213 // TODO: We can probably reduce the temporary allocations here
214 Eigen::VectorXd in(_rbfSolver.getOutputSize());
215
216 // Now we perform the data mapping component-wise
217 for (unsigned int c = 0; c < nComponents; ++c) {
218 // Step 1: extract the relevant input data from the global input data and store
219 // it in a contiguous array, which is required for the RBF solver
220 for (unsigned int i = 0; i < _outputIDs.size(); ++i) {
221 const auto dataIndex = *(_outputIDs.nth(i));
222 PRECICE_ASSERT(dataIndex * nComponents + c < localInData.size(), dataIndex * nComponents + c, localInData.size());
223 PRECICE_ASSERT(_normalizedWeights[i] > 0, _normalizedWeights[i], i);
224 // here, we also directly apply the weighting, i.e., we split the input data
225 in[i] = localInData[dataIndex * nComponents + c] * _normalizedWeights[i];
226 }
227
228 // Step 2: solve the system using a conservative constraint
229 auto result = _rbfSolver.solveConservative(in, _polynomial);
230 PRECICE_ASSERT(result.size() == static_cast<Eigen::Index>(_inputIDs.size()));
231
232 // Step 3: now accumulate the result into our global output data
233 for (unsigned int i = 0; i < _inputIDs.size(); ++i) {
234 const auto dataIndex = *(_inputIDs.nth(i));
235 PRECICE_ASSERT(dataIndex * nComponents + c < outData.size(), dataIndex * nComponents + c, outData.size());
236 outData[dataIndex * nComponents + c] += result(i);
237 }
238 }
239}
240
241template <typename RADIAL_BASIS_FUNCTION_T>
242void SphericalVertexCluster<RADIAL_BASIS_FUNCTION_T>::evaluateConservativeCache(Eigen::MatrixXd &epsilon, const Eigen::MatrixXd &Au, Eigen::Ref<Eigen::MatrixXd> out)
243{
244 Eigen::MatrixXd localIn(_inputIDs.size(), Au.cols());
245 _rbfSolver.evaluateConservativeCache(epsilon, Au, localIn);
246 // Step 3: now accumulate the result into our global output data
247 for (std::size_t i = 0; i < _inputIDs.size(); ++i) {
248 const auto dataIndex = *(_inputIDs.nth(i));
249 PRECICE_ASSERT(dataIndex < out.cols(), out.cols());
250 out.col(dataIndex) += localIn.row(i);
251 }
252}
253
254template <typename RADIAL_BASIS_FUNCTION_T>
255void SphericalVertexCluster<RADIAL_BASIS_FUNCTION_T>::computeCacheData(const Eigen::Ref<const Eigen::MatrixXd> &globalIn, Eigen::MatrixXd &polyOut, Eigen::MatrixXd &coeffOut) const
256{
258 Eigen::MatrixXd in(_rbfSolver.getInputSize(), globalIn.rows());
259 // Step 1: extract the relevant input data from the global input data and store
260 // it in a contiguous array, which is required for the RBF solver (last polyparams entries remain zero)
261 for (std::size_t i = 0; i < _inputIDs.size(); i++) {
262 const auto dataIndex = *(_inputIDs.nth(i));
263 PRECICE_ASSERT(dataIndex < globalIn.cols(), globalIn.cols());
264 in.row(i) = globalIn.col(dataIndex);
265 }
266 // Step 2: solve the system using a consistent constraint
267 _rbfSolver.computeCacheData(in, _polynomial, polyOut, coeffOut);
268}
269
270template <typename RADIAL_BASIS_FUNCTION_T>
271Eigen::VectorXd SphericalVertexCluster<RADIAL_BASIS_FUNCTION_T>::interpolateAt(const mesh::Vertex &v, const Eigen::MatrixXd &poly, const Eigen::MatrixXd &coeffs, const mesh::Mesh &inMesh) const
272{
274 return _rbfSolver.interpolateAt(v, poly, coeffs, _function, _inputIDs, inMesh);
275}
276
277template <typename RADIAL_BASIS_FUNCTION_T>
279 Eigen::MatrixXd &epsilon, Eigen::MatrixXd &Au,
280 const mesh::Mesh &inMesh)
281{
283 _rbfSolver.addWriteDataToCache(v, load, epsilon, Au, _function, _inputIDs, inMesh);
284}
285
286template <typename RADIAL_BASIS_FUNCTION_T>
287void SphericalVertexCluster<RADIAL_BASIS_FUNCTION_T>::initializeCacheData(Eigen::MatrixXd &poly, Eigen::MatrixXd &coeffs, const int nComponents)
288{
289 if (Polynomial::SEPARATE == _polynomial) {
290 poly.resize(_rbfSolver.getNumberOfPolynomials(), nComponents);
291 }
292 coeffs.resize(_inputIDs.size(), nComponents);
293}
294
295template <typename RADIAL_BASIS_FUNCTION_T>
296void SphericalVertexCluster<RADIAL_BASIS_FUNCTION_T>::mapConsistent(const time::Sample &inData, Eigen::VectorXd &outData) const
297{
298 // First, a few sanity checks. Empty partitions shouldn't be stored at all
299 PRECICE_ASSERT(!empty());
300 PRECICE_ASSERT(_hasComputedMapping);
301 PRECICE_ASSERT(_normalizedWeights.size() == static_cast<Eigen::Index>(_outputIDs.size()));
302
303 // Define an alias for data dimension in order to avoid ambiguity
304 const unsigned int nComponents = inData.dataDims;
305 const auto &localInData = inData.values;
306
307 Eigen::VectorXd in(_rbfSolver.getInputSize());
308
309 // Now we perform the data mapping component-wise
310 for (unsigned int c = 0; c < nComponents; ++c) {
311 // Step 1: extract the relevant input data from the global input data and store
312 // it in a contiguous array, which is required for the RBF solver (last polyparams entries remain zero)
313 for (unsigned int i = 0; i < _inputIDs.size(); i++) {
314 const auto dataIndex = *(_inputIDs.nth(i));
315 PRECICE_ASSERT(dataIndex * nComponents + c < localInData.size(), dataIndex * nComponents + c, localInData.size());
316 in[i] = localInData[dataIndex * nComponents + c];
317 }
318
319 // Step 2: solve the system using a consistent constraint
320 auto result = _rbfSolver.solveConsistent(in, _polynomial);
321 PRECICE_ASSERT(static_cast<Eigen::Index>(_outputIDs.size()) == result.size());
322
323 // Step 3: now accumulate the result into our global output data
324 for (unsigned int i = 0; i < _outputIDs.size(); ++i) {
325 const auto dataIndex = *(_outputIDs.nth(i));
326 PRECICE_ASSERT(dataIndex * nComponents + c < outData.size(), dataIndex * nComponents + c, outData.size());
327 PRECICE_ASSERT(_normalizedWeights[i] > 0);
328 // here, we also directly apply the weighting, i.e., split the result data
329 outData[dataIndex * nComponents + c] += result(i) * _normalizedWeights[i];
330 }
331 }
332}
333
334template <typename RADIAL_BASIS_FUNCTION_T>
336{
337 // Assume that the local interpolant and the weighting function are the same
338 // TODO: We don't need to reduce the dead coordinates here as the values should reduce anyway
339 auto res = computeSquaredDifference(_center.rawCoords(), v.rawCoords(), {{true, true, true}});
340 return _weightingFunction.evaluate(std::sqrt(res));
341}
342
343template <typename RADIAL_BASIS_FUNCTION_T>
345{
346 return _inputIDs.size();
347}
348
349template <typename RADIAL_BASIS_FUNCTION_T>
354
355template <typename RADIAL_BASIS_FUNCTION_T>
357{
358 return _inputIDs.size() == 0;
359}
360
361template <typename RADIAL_BASIS_FUNCTION_T>
363{
365 _inputIDs.clear();
366 _outputIDs.clear();
367 _rbfSolver.clear();
368 _hasComputedMapping = false;
369}
370} // namespace precice::mapping
std::ostream & out
#define PRECICE_DEBUG(...)
Definition LogMacros.hpp:61
#define PRECICE_TRACE(...)
Definition LogMacros.hpp:92
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
This class provides a lightweight logger.
Definition Logger.hpp:17
Wendland radial basis function with compact support.
boost::container::flat_set< VertexID > _outputIDs
Global VertexIDs associated to the output mesh (see constructor)
void computeCacheData(const Eigen::Ref< const Eigen::MatrixXd > &globalIn, Eigen::MatrixXd &polyOut, Eigen::MatrixXd &coefficientsOut) const
Computes and saves the RBF coefficients.
unsigned int getNumberOfInputVertices() const
Number of input vertices this partition operates on.
mesh::Vertex _center
center vertex of the cluster
const double _radius
radius of the vertex cluster
std::array< double, 3 > getCenterCoords() const
The center coordinate of this cluster.
double computeWeight(const mesh::Vertex &v) const
Compute the weight for a given vertex.
void initializeCacheData(Eigen::MatrixXd &polynomial, Eigen::MatrixXd &coeffs, const int nComponents)
bool _hasComputedMapping
Boolean switch in order to indicate that a mapping was computed.
CompactPolynomialC2 _weightingFunction
The weighting function.
Eigen::VectorXd interpolateAt(const mesh::Vertex &v, const Eigen::MatrixXd &poly, const Eigen::MatrixXd &coeffs, const mesh::Mesh &inMesh) const
void setNormalizedWeight(double normalizedWeight, VertexID vertexID)
Set the normalized weight for the given vertexID in the outputMesh.
Polynomial _polynomial
Polynomial treatment in the RBF solver.
RadialBasisFctSolver< RADIAL_BASIS_FUNCTION_T > _rbfSolver
The RBF solver.
void mapConsistent(const time::Sample &inData, Eigen::VectorXd &outData) const
Evaluates a consistent mapping and agglomerates the result in the given output data.
void clear()
Invalidates and erases data structures the cluster holds.
void evaluateConservativeCache(Eigen::MatrixXd &epsilon, const Eigen::MatrixXd &Au, Eigen::Ref< Eigen::MatrixXd > out)
SphericalVertexCluster(mesh::Vertex center, double radius, RADIAL_BASIS_FUNCTION_T function, Polynomial polynomial, mesh::PtrMesh inputMesh, mesh::PtrMesh outputMesh)
boost::container::flat_set< VertexID > _inputIDs
Global VertexIDs associated to the input mesh (see constructor)
void mapConservative(const time::Sample &inData, Eigen::VectorXd &outData) const
Evaluates a conservative mapping and agglomerates the result in the given output data.
precice::logging::Logger _log
logger, as usual
void addWriteDataToCache(const mesh::Vertex &v, const Eigen::VectorXd &load, Eigen::MatrixXd &epsilon, Eigen::MatrixXd &Au, const mesh::Mesh &inMesh)
Container and creator for meshes.
Definition Mesh.hpp:38
Vertex of a mesh.
Definition Vertex.hpp:16
const RawCoords & rawCoords() const
Direct access to the coordinates.
Definition Vertex.hpp:121
Eigen::VectorXd getCoords() const
Returns the coordinates of the vertex.
Definition Vertex.hpp:114
void stop()
Stops a running event.
Definition Event.cpp:51
T get(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.
Polynomial
How to handle the polynomial?
constexpr double NUMERICAL_ZERO_DIFFERENCE
int VertexID
Definition Types.hpp:13
T sqrt(T... args)
int dataDims
The dimensionality of the data.
Definition Sample.hpp:60
Eigen::VectorXd values
Definition Sample.hpp:64