preCICE
Loading...
Searching...
No Matches
AxialGeoMultiscaleMapping.cpp
Go to the documentation of this file.
2#include <Eigen/Core>
3#include "mesh/Mesh.hpp"
4
5namespace precice::mapping {
6
8 Constraint constraint,
9 int dimensions,
10 MultiscaleDimension dimension,
11 MultiscaleType type,
12 MultiscaleAxis axis,
13 double radius,
14 MultiscaleProfile profile,
15 MultiscaleCrossSection crossSection)
16 : Mapping(constraint, dimensions, false, Mapping::InitialGuessRequirement::None),
17 _dimension(dimension),
18 _type(type),
19 _axis(axis),
20 _radius(radius),
21 _profile(profile),
22 _crossSection(crossSection)
23{
26}
27
29{
30 PRECICE_TRACE(output()->nVertices());
31
32 PRECICE_ASSERT(input().get() != nullptr);
33 PRECICE_ASSERT(output().get() != nullptr);
34
35 if (getConstraint() == CONSISTENT) {
36 PRECICE_DEBUG("Compute consistent mapping");
38 const int outDataDimensions = 3;
39 int effectiveCoordinate = static_cast<std::underlying_type_t<MultiscaleType>>(_axis); // Convert enum struct to int
40 PRECICE_ASSERT(effectiveCoordinate == static_cast<std::underlying_type_t<MultiscaleType>>(MultiscaleAxis::X) ||
41 effectiveCoordinate == static_cast<std::underlying_type_t<MultiscaleType>>(MultiscaleAxis::Y) ||
42 effectiveCoordinate == static_cast<std::underlying_type_t<MultiscaleType>>(MultiscaleAxis::Z),
43 "Unknown multiscale axis type.");
44 size_t const inSize = input()->nVertices();
45 size_t const outSize = output()->nVertices();
47 PRECICE_CHECK(input()->nVertices() == 1, "You can only define an axial geometric multiscale 1D-{} mapping of type spread from a mesh with exactly one vertex.");
48
49 /* When we add support for 1D meshes (https://github.com/precice/precice/issues/1669),
50 we should check for valid dimensions combination.
51
52 const int inDataDimensions = input()->getDimensions();
53 const int outDataDimensions = output()->getDimensions();
54
55 PRECICE_CHECK(input()->getDimensions() == 1, "The input mesh on an axial geometric multiscale mapping can only be 1D at the moment, but it was defined to be {}.", input()->getDimensions())
56 PRECICE_CHECK(output()->getDimensions() == 3, "The output mesh on an axial geometric multiscale mapping can only be 3D at the moment, but it was defined to be {}.", input()->getDimensions())
57 */
58
59 // compute distances between 1D vertex and 3D vertices
60 mesh::Vertex &v0 = input()->vertex(0);
61 constexpr double distance_to_radius_threshold = 1.05;
63 _vertexDistances.clear();
64 _vertexDistances.reserve(output()->nVertices());
65
66 for (size_t i = 0; i < outSize; i++) {
67 Eigen::VectorXd difference(outDataDimensions);
68 difference = v0.getCoords();
69 difference -= output()->vertex(i).getCoords();
70 double distance_to_radius = difference.norm() / _radius;
71 PRECICE_CHECK(distance_to_radius <= distance_to_radius_threshold, "Output mesh has vertices that do not coincide with the geometric multiscale interface defined by the input mesh. Ratio of vertex distance to radius is {} (which is larger than the assumed threshold of distance_to_radius_threshold).", distance_to_radius);
72 _vertexDistances.push_back(distance_to_radius);
73 }
74 } else {
77 _vertexTransverseCoords.reserve(output()->nVertices());
78
79 const int t1 = (effectiveCoordinate + 1) % 3;
80 const int t2 = (effectiveCoordinate + 2) % 3;
81
82 for (size_t i = 0; i < outSize; i++) {
83 Eigen::VectorXd difference(outDataDimensions);
84 difference = v0.getCoords();
85 difference -= output()->vertex(i).getCoords();
86
87 const double s1 = difference[t1] / _radius;
88 const double s2 = difference[t2] / _radius;
89
90 const double squareNorm = std::max(std::abs(s1), std::abs(s2));
91 PRECICE_CHECK(squareNorm <= distance_to_radius_threshold,
92 "Output mesh has vertices that do not coincide with the square multiscale interface defined by the input mesh. "
93 "max(|xn|,|yn|) is {} (threshold {}).",
94 squareNorm, distance_to_radius_threshold);
95 _vertexTransverseCoords.push_back({s1, s2});
96 }
97 }
98
99 } else {
101 PRECICE_CHECK(input()->nVertices() > 1, "You can only define an axial geometric multiscale 2D-3D mapping of type spread from a mesh with more than 1 vertex.");
102 Eigen::Vector3d minC = input()->vertex(0).getCoords().head<3>();
103 Eigen::Vector3d maxC = minC;
104 for (size_t i = 1; i < input()->nVertices(); ++i) {
105 const Eigen::Vector3d c = input()->vertex(i).getCoords().head<3>();
106 minC = minC.cwiseMin(c);
107 maxC = maxC.cwiseMax(c);
108 }
109 const Eigen::Vector3d span = maxC - minC;
110 _lineCoord = 0;
111 if (span[1] > span[_lineCoord])
112 _lineCoord = 1;
113 if (span[2] > span[_lineCoord])
114 _lineCoord = 2;
115 _nearestVertex.clear();
116 _nearestVertex.reserve(output()->nVertices());
117 _vertexDistances.clear();
118 _vertexDistances.reserve(output()->nVertices());
119 _maxDistancePerInput.clear();
120 _maxDistancePerInput.resize(input()->nVertices(), 0.0);
121 for (size_t j = 0; j < outSize; j++) {
122 const Eigen::VectorXd &xOut = output()->vertex(j).getCoords();
123 double bestDistance2 = std::numeric_limits<double>::max();
124 int bestIdx = -1;
125 for (size_t i = 0; i < inSize; i++) {
126 const Eigen::VectorXd &xIn = input()->vertex(i).getCoords();
127 Eigen::VectorXd difference = xOut - xIn;
128 double distance2 = difference.squaredNorm();
129 if (distance2 < bestDistance2) {
130 bestDistance2 = distance2;
131 bestIdx = static_cast<int>(i);
132 }
133 }
134 PRECICE_ASSERT(bestIdx >= 0, "Could not find nearest input vertex for output vertex {}", j);
135 _nearestVertex.push_back(bestIdx);
136 double distance = std::sqrt(bestDistance2);
137 _vertexDistances.push_back(distance);
138 if (distance > _maxDistancePerInput[static_cast<size_t>(bestIdx)]) {
139 _maxDistancePerInput[static_cast<size_t>(bestIdx)] = distance;
140 }
141 }
142 }
143 } else {
145 size_t const inSize = input()->nVertices();
146 size_t const outSize = output()->nVertices();
148 PRECICE_CHECK(output()->nVertices() == 1, "You can only define an axial geometric multiscale 1D-{} mapping of type collect to a mesh with exactly one vertex.");
150 PRECICE_CHECK(output()->nVertices() == 1,
151 "You can only define an axial geometric multiscale 1D-2D mapping of type collect to a mesh with exactly one vertex.");
152 } else {
154 PRECICE_CHECK(outSize > 1, "You can only define an axial geometric multiscale 2D-3D mapping of type collect to a mesh with more than 1 vertex.");
155 Eigen::Vector3d minC = output()->vertex(0).getCoords().head<3>();
156 Eigen::Vector3d maxC = minC;
157 for (size_t i = 1; i < output()->nVertices(); ++i) {
158 const Eigen::Vector3d c = output()->vertex(i).getCoords().head<3>();
159 minC = minC.cwiseMin(c);
160 maxC = maxC.cwiseMax(c);
161 }
162 const Eigen::Vector3d span = maxC - minC;
163 _lineCoord = 0;
164 if (span[1] > span[_lineCoord])
165 _lineCoord = 1;
166 if (span[2] > span[_lineCoord])
167 _lineCoord = 2;
168 _collectBands.clear();
169 _collectBands.resize(output()->nVertices());
170 for (size_t i = 0; i < inSize; i++) {
171 const Eigen::VectorXd &xIn = input()->vertex(i).getCoords();
172 double bestDistance = std::numeric_limits<double>::max();
173 int bestIdx = -1;
174 for (size_t j = 0; j < outSize; j++) {
175 const Eigen::VectorXd &xOut = output()->vertex(j).getCoords();
176 Eigen::VectorXd difference = xIn - xOut;
177 double distance = difference.squaredNorm();
178 if (distance < bestDistance) {
179 bestDistance = distance;
180 bestIdx = static_cast<int>(j);
181 }
182 }
183 PRECICE_ASSERT(bestIdx >= 0, "Could not find nearest output vertex for input vertex {}", i);
184 _collectBands[bestIdx].push_back(static_cast<int>(i));
185 }
186 }
187 }
188
189 } else {
191 PRECICE_UNREACHABLE("Axial conservative geometric multiscale mapping is not implemented");
192 PRECICE_DEBUG("Compute conservative mapping");
193 }
194 _hasComputedMapping = true;
195}
196
203
204void AxialGeoMultiscaleMapping::mapConservative(const time::Sample &inData, Eigen::VectorXd &outData)
205{
207 PRECICE_UNREACHABLE("Axial conservative geometric multiscale mapping is not implemented");
208 PRECICE_DEBUG("Map conservative");
209}
210
211void AxialGeoMultiscaleMapping::mapConsistent(const time::Sample &inData, Eigen::VectorXd &outData)
212{
214
215 const int inDataDimensions = inData.dataDims;
216 const Eigen::VectorXd &inputValues = inData.values;
217 Eigen::VectorXd &outputValues = outData;
218 const int outDataDimensions = outData.size() / output()->nVertices();
219
220 PRECICE_ASSERT((inputValues.size() / static_cast<std::size_t>(inDataDimensions) == input()->nVertices()),
221 inputValues.size(), inDataDimensions, input()->nVertices());
222 PRECICE_ASSERT((outputValues.size() / static_cast<std::size_t>(outDataDimensions) == output()->nVertices()),
223 outputValues.size(), outDataDimensions, output()->nVertices());
224
225 // We currently don't support 1D data, so we need that the user specifies data of the same dimensions on both sides
226 PRECICE_ASSERT(inDataDimensions == outDataDimensions);
227
228 int effectiveCoordinate;
229 if (inDataDimensions == 1) {
230 effectiveCoordinate = 0;
231 } else {
232 effectiveCoordinate = static_cast<std::underlying_type_t<MultiscaleType>>(_axis);
233 PRECICE_ASSERT(effectiveCoordinate == static_cast<std::underlying_type_t<MultiscaleType>>(MultiscaleAxis::X) ||
234 effectiveCoordinate == static_cast<std::underlying_type_t<MultiscaleType>>(MultiscaleAxis::Y) ||
235 effectiveCoordinate == static_cast<std::underlying_type_t<MultiscaleType>>(MultiscaleAxis::Z),
236 "Unknown multiscale axis type.");
237 }
238
239 PRECICE_DEBUG("Map consistent");
240
242 size_t const inSize = input()->nVertices();
243 size_t const outSize = output()->nVertices();
245 PRECICE_ASSERT(input()->nVertices() == 1);
246 for (size_t i = 0; i < outSize; i++) {
247 PRECICE_ASSERT(static_cast<size_t>((i * outDataDimensions) + effectiveCoordinate) < static_cast<size_t>(outputValues.size()), ((i * outDataDimensions) + effectiveCoordinate), outputValues.size());
250 constexpr double factor = 1.5;
251 outputValues((i * outDataDimensions) + effectiveCoordinate) = factor * inputValues(effectiveCoordinate) * (1 - (_vertexDistances[i] * _vertexDistances[i]));
252 } else {
254 constexpr double factor = 2.0;
255 outputValues((i * outDataDimensions) + effectiveCoordinate) = factor * inputValues(effectiveCoordinate) * (1 - (_vertexDistances[i] * _vertexDistances[i]));
257 const double s1 = _vertexTransverseCoords[i][0];
258 const double s2 = _vertexTransverseCoords[i][1];
259
260 constexpr double factor = 2.096;
261 constexpr double m = 0.879;
262 const double b1raw = 1.0 - s1 * s1;
263 const double b2raw = 1.0 - s2 * s2;
264 const double b1 = std::max(0.0, b1raw);
265
266 const double b2 = std::max(0.0, b2raw);
267 outputValues((i * outDataDimensions) + effectiveCoordinate) = factor * inputValues(effectiveCoordinate) * std::pow(b1, m) * std::pow(b2, m);
268 }
269 }
270 } else if (_profile == MultiscaleProfile::UNIFORM) {
271 outputValues((i * outDataDimensions) + effectiveCoordinate) = inputValues(effectiveCoordinate);
272 }
273 }
274 } else {
276 PRECICE_ASSERT(_nearestVertex.size() == outSize);
277 PRECICE_ASSERT(_vertexDistances.size() == outSize);
278 PRECICE_ASSERT(_maxDistancePerInput.size() == inSize);
279 for (size_t i = 0; i < outSize; i++) {
280 PRECICE_ASSERT(_nearestVertex[i] >= 0 && static_cast<size_t>(_nearestVertex[i]) < inSize, _nearestVertex[i], inSize);
281 PRECICE_ASSERT(static_cast<size_t>((i * outDataDimensions) + effectiveCoordinate) < static_cast<size_t>(outputValues.size()), (i * outDataDimensions) + effectiveCoordinate, outputValues.size());
282 PRECICE_ASSERT(static_cast<size_t>((static_cast<size_t>(_nearestVertex[i]) * inDataDimensions) + effectiveCoordinate) < static_cast<size_t>(inputValues.size()), (static_cast<size_t>(_nearestVertex[i]) * inDataDimensions) + effectiveCoordinate, inputValues.size());
283 double R = _maxDistancePerInput[static_cast<size_t>(_nearestVertex[i])];
285 outputValues((i * outDataDimensions) + effectiveCoordinate) = inputValues((static_cast<size_t>(_nearestVertex[i]) * inDataDimensions) + effectiveCoordinate);
288 double r_hat = _vertexDistances[i] / R;
289 outputValues((i * outDataDimensions) + effectiveCoordinate) = (4.0 / 3.0) * inputValues((static_cast<size_t>(_nearestVertex[i]) * inDataDimensions) + effectiveCoordinate) * (1.0 - r_hat * r_hat);
291 constexpr double umax_over_umean = 2.096;
292 constexpr double line_factor = 1.5;
293 constexpr double m = 0.879;
294 constexpr double eps = 1e-12;
295 const size_t inIdx = static_cast<size_t>(_nearestVertex[i]);
296 const double s1 = input()->vertex(inIdx).getCoords()[_lineCoord] / _radius;
297 const double s2 = _vertexDistances[i] / _radius;
298 const double b1raw = 1.0 - s1 * s1;
299 const double b2raw = 1.0 - s2 * s2;
300 const double b1 = std::max(eps, b1raw);
301
302 const double b2 = std::max(0.0, b2raw);
303 outputValues((i * outDataDimensions) + effectiveCoordinate) = inputValues((inIdx * inDataDimensions) + effectiveCoordinate) * (umax_over_umean / line_factor) * std::pow(b1, m - 1.0) * std::pow(b2, m);
304 }
305 }
306 }
307 }
308 } else {
310 size_t const inSize = input()->nVertices();
311 size_t const outSize = output()->nVertices();
313 PRECICE_ASSERT(output()->nVertices() == 1);
314 outputValues(effectiveCoordinate) = 0.0;
315 for (size_t i = 0; i < inSize; i++) {
316 PRECICE_ASSERT(static_cast<size_t>((i * inDataDimensions) + effectiveCoordinate) < static_cast<size_t>(inputValues.size()),
317 ((i * inDataDimensions) + effectiveCoordinate), inputValues.size());
318 outputValues(effectiveCoordinate) +=
319 inputValues((i * inDataDimensions) + effectiveCoordinate);
320 }
321 outputValues(effectiveCoordinate) = outputValues(effectiveCoordinate) / inSize;
322
324 PRECICE_ASSERT(output()->nVertices() == 1);
325
326 outputValues(effectiveCoordinate) = 0.0;
327 for (size_t i = 0; i < inSize; ++i) {
328 PRECICE_ASSERT(static_cast<size_t>((i * inDataDimensions) + effectiveCoordinate) < static_cast<size_t>(inputValues.size()),
329 ((i * inDataDimensions) + effectiveCoordinate), inputValues.size());
330 outputValues(effectiveCoordinate) +=
331 inputValues((i * inDataDimensions) + effectiveCoordinate);
332 }
333 outputValues(effectiveCoordinate) = outputValues(effectiveCoordinate) / inSize;
334
335 } else {
337 PRECICE_ASSERT(_collectBands.size() == output()->nVertices());
338 for (size_t j = 0; j < outSize; j++) {
339 if (_collectBands[j].empty()) {
340 continue;
341 }
342 PRECICE_ASSERT(static_cast<size_t>((j * outDataDimensions) + effectiveCoordinate) < static_cast<size_t>(outputValues.size()), (j * outDataDimensions) + effectiveCoordinate, outputValues.size());
343 outputValues((j * outDataDimensions) + effectiveCoordinate) = 0.0;
344 for (size_t k = 0; k < _collectBands[j].size(); k++) {
345 int i = _collectBands[j][k];
346 PRECICE_ASSERT(i >= 0 && static_cast<size_t>(i) < inSize, i, inSize);
347 PRECICE_ASSERT(static_cast<size_t>((static_cast<size_t>(i) * inDataDimensions) + effectiveCoordinate) < static_cast<size_t>(inputValues.size()), (static_cast<size_t>(i) * inDataDimensions) + effectiveCoordinate, inputValues.size());
348 outputValues((j * outDataDimensions) + effectiveCoordinate) += inputValues((static_cast<size_t>(i) * inDataDimensions) + effectiveCoordinate);
349 }
350 outputValues((j * outDataDimensions) + effectiveCoordinate) = outputValues((j * outDataDimensions) + effectiveCoordinate) / static_cast<double>(_collectBands[j].size());
351
354 outputValues((j * outDataDimensions) + effectiveCoordinate) *= 9.0 / 8.0;
356 const double s1 = output()->vertex(j).getCoords()[_lineCoord] / _radius;
357 const double b1raw = 1.0 - s1 * s1;
358 const double b1 = std::max(0.0, b1raw);
359 outputValues((j * outDataDimensions) + effectiveCoordinate) *= 1.03745 * std::pow(b1, 0.121);
360 }
361 }
362 }
363 }
364 }
365}
366
368{
370
372
373 if (getConstraint() == CONSISTENT) {
374 PRECICE_ASSERT(_type == MultiscaleType::SPREAD, "Not yet implemented");
375 PRECICE_ASSERT(input()->nVertices() == 1);
376
377 input()->tagAll();
378
379 } else {
381 PRECICE_UNREACHABLE("Axial conservative geometric multiscale mapping is not implemented");
382 }
383
384 clear();
385}
386
388{
390 // no operation needed here for the moment
391}
392
394{
395 return "axial-geomultiscale";
396}
397
398} // namespace precice::mapping
#define PRECICE_DEBUG(...)
Definition LogMacros.hpp:61
#define PRECICE_TRACE(...)
Definition LogMacros.hpp:92
#define PRECICE_CHECK(check,...)
Definition LogMacros.hpp:32
#define PRECICE_ASSERT(...)
Definition assertion.hpp:85
#define PRECICE_UNREACHABLE(...)
Definition assertion.hpp:93
std::size_t nVertices() const
Returns the number of vertices.
Definition Mesh.cpp:64
Vertex & vertex(VertexID id)
Mutable access to a vertex by VertexID.
Definition Mesh.cpp:42
void tagAll()
Definition Mesh.cpp:347
void mapConservative(const time::Sample &inData, Eigen::VectorXd &outData) override
Maps data using a conservative constraint.
double _radius
radius of the "tube" from or to which the data is mapped, i.e., radius of the circular interface betw...
MultiscaleType
Geometric multiscale nature of the mapping (spread or collect).
std::string getName() const final override
Returns name of the mapping.
MultiscaleAxis _axis
main axis along which axial geometric multiscale coupling happens
void tagMeshSecondRound() override
Method used by partition. Tags vertices that can be filtered out.
void tagMeshFirstRound() override
Method used by partition. Tags vertices that could be owned by this rank.
void computeMapping() override
Takes care of compute-heavy operations needed only once to set up the mapping.
void clear() override
Removes a computed mapping.
MultiscaleType _type
type of mapping, namely spread or collect
MultiscaleProfile _profile
Cross-sectional profile of the exchanged quantity.
std::vector< Eigen::Vector2d > _vertexTransverseCoords
computed normalized transverse coordinates of oputput vertices for squared cross-section
AxialGeoMultiscaleMapping(Constraint constraint, int dimensions, MultiscaleDimension dimension, MultiscaleType type, MultiscaleAxis axis, double radius, MultiscaleProfile profile=MultiscaleProfile::UNIFORM, MultiscaleCrossSection crossSection=MultiscaleCrossSection::CIRCLE)
Constructor.
std::vector< double > _vertexDistances
computed vertex distances to map data from input vertex to output vertices
void mapConsistent(const time::Sample &inData, Eigen::VectorXd &outData) override
Maps data using a consistent constraint.
MultiscaleProfile
Profile to use when type == SPREAD.
mesh::PtrMesh output() const
Returns pointer to output mesh.
Definition Mapping.cpp:92
Constraint
Specifies additional constraints for a mapping.
Definition Mapping.hpp:30
Mapping(Constraint constraint, int dimensions, bool requiresGradientData, InitialGuessRequirement initialGuessRequirement)
Constructor, takes mapping constraint.
Definition Mapping.cpp:12
mesh::PtrMesh input() const
Returns pointer to input mesh.
Definition Mapping.cpp:87
bool _hasComputedMapping
Flag to indicate whether computeMapping() has been called.
Definition Mapping.hpp:307
Constraint getConstraint() const
Returns the constraint (consistent/conservative) of the mapping.
Definition Mapping.cpp:46
void setInputRequirement(MeshRequirement requirement)
Sets the mesh requirement for the input mesh.
Definition Mapping.cpp:97
void setOutputRequirement(MeshRequirement requirement)
Sets the mesh requirement for the output mesh.
Definition Mapping.cpp:103
InitialGuessRequirement
Specifies whether the mapping requires an initial guess.
Definition Mapping.hpp:64
Vertex of a mesh.
Definition Vertex.hpp:16
Eigen::VectorXd getCoords() const
Returns the coordinates of the vertex.
Definition Vertex.hpp:114
A C++ 11 implementation of the non-owning C++20 std::span type.
Definition span.hpp:284
contains data mapping from points to meshes.
constexpr auto get(span< E, S > s) -> decltype(s[N])
Definition span.hpp:602
int dataDims
The dimensionality of the data.
Definition Sample.hpp:60
Eigen::VectorXd values
Definition Sample.hpp:64