36constexpr VertexID zCurve(std::array<unsigned int, 3> ids, std::array<unsigned int, 3> nCells)
38 return (ids[0] * nCells[1] + ids[1]) * nCells[2] + ids[2];
42constexpr VertexID zCurve(std::array<int, 3> ids, std::array<unsigned int, 3> nCells)
44 return (ids[0] * nCells[1] + ids[1]) * nCells[2] + ids[2];
55std::array<int, math::pow_int<dim>(3) - 1> zCurveNeighborOffsets(std::array<unsigned int, 3> nCells);
59std::array<int, 8> zCurveNeighborOffsets<2>(std::array<unsigned int, 3> nCells)
62 const std::array<std::array<int, 3>, 8> neighbors2DIndex = {{{-1, -1, 0}, {-1, 0, 0}, {-1, 1, 0}, {0, -1, 0}, {0, 1, 0}, {1, -1, 0}, {1, 0, 0}, {1, 1, 0}}};
63 std::array<int, 8> neighbors2D{{}};
66 std::transform(neighbors2DIndex.begin(), neighbors2DIndex.end(), neighbors2D.begin(), [&nCells](
auto &v) { return zCurve(v, nCells); });
73std::array<int, 26> zCurveNeighborOffsets<3>(std::array<unsigned int, 3> nCells)
76 const std::array<std::array<int, 3>, 26> neighbors3DIndex = {{{-1, -1, -1}, {-1, -1, 0}, {-1, -1, 1}, {-1, 0, 0}, {-1, 0, 1}, {-1, 0, -1}, {-1, 1, 0}, {-1, 1, 1}, {-1, 1, -1}, {0, -1, -1}, {0, -1, 0}, {0, -1, 1}, {0, 0, 1}, {0, 0, -1}, {0, 1, 0}, {0, 1, 1}, {0, 1, -1}, {1, -1, -1}, {1, -1, 0}, {1, -1, 1}, {1, 0, 0}, {1, 0, 1}, {1, 0, -1}, {1, 1, 0}, {1, 1, 1}, {1, 1, -1}}};
77 std::array<int, 26> neighbors3D{{}};
79 std::transform(neighbors3DIndex.begin(), neighbors3DIndex.end(), neighbors3D.begin(), [&nCells](
auto &v) { return zCurve(v, nCells); });
96template <
typename ArrayType>
97std::vector<VertexID> getNeighborsWithinSphere(
const Vertices &vertices,
VertexID centerID,
double radius,
const ArrayType neighborOffsets)
100 static_assert((neighborOffsets.size() == 26) || (neighborOffsets.size() == 8));
101 std::vector<VertexID> result;
102 for (
auto off : neighborOffsets) {
111 VertexID neighborID = centerID + off;
113 if (neighborID >= 0 && neighborID <
static_cast<int>(vertices.size())) {
114 auto &neighborVertex = vertices[neighborID];
115 auto ¢er = vertices[centerID];
117 result.emplace_back(neighborID);
135 std::for_each(clusterCenters.begin(), clusterCenters.end(), [&](
auto &v) {
136 if (!v.isTagged() && !mesh->index().isAnyVertexInsideBox(v, clusterRadius)) {
152 std::transform(clusterCenters.begin(), clusterCenters.end(), clusterCenters.begin(), [&](
auto &v) {
154 auto closestCenter = mesh->index().getClosestVertex(v.getCoords()).index;
155 return mesh::Vertex{mesh->vertex(closestCenter).getCoords(), v.getID()};
174void tagDuplicateCenters(Vertices ¢ers, std::array<unsigned int, 3> nCells,
double threshold)
181 auto neighborOffsets = zCurveNeighborOffsets<dim>(nCells);
182 static_assert((neighborOffsets.size() == 8 && dim == 2) || (neighborOffsets.size() == 26 && dim == 3));
185 PRECICE_ASSERT(std::all_of(centers.begin(), centers.end(), [idx = 0](
auto &v)
mutable { return v.getID() == idx++; }));
187 for (
auto &v : centers) {
189 auto ids = getNeighborsWithinSphere(centers, v.getID(), threshold, neighborOffsets);
205void removeTaggedVertices(Vertices &container)
207 container.erase(std::remove_if(container.begin(), container.end(), [](
auto &v) { return v.isTagged(); }), container.end());
226 std::vector<VertexID> randomSamples;
232 const auto bbCenter = bb.
center();
233 randomSamples.emplace_back(inMesh->
index().getClosestVertex(bbCenter).index);
246 auto sample = bbCenter;
249 randomSamples.emplace_back(inMesh->
index().getClosestVertex(sample).index);
252 randomSamples.emplace_back(inMesh->
index().getClosestVertex(sample).index);
256 std::vector<double> sampledClusterRadii;
257 for (
auto s : randomSamples) {
259 auto kNearestVertexIDs = inMesh->
index().getClosestVertices(inMesh->
vertex(s).
getCoords(), verticesPerCluster);
261 std::vector<double> squaredRadius(kNearestVertexIDs.size());
262 std::transform(kNearestVertexIDs.begin(), kNearestVertexIDs.end(), squaredRadius.begin(), [&inMesh, s](
auto i) {
263 return computeSquaredDifference(inMesh->vertex(i).rawCoords(), inMesh->vertex(s).rawCoords());
266 auto maxRadius = std::max_element(squaredRadius.begin(), squaredRadius.end());
267 sampledClusterRadii.emplace_back(std::sqrt(*maxRadius));
271 PRECICE_ASSERT(sampledClusterRadii.size() % 2 != 0,
"Median calculation is only valid for odd number of elements.");
273 unsigned int middle = sampledClusterRadii.size() / 2;
274 std::nth_element(sampledClusterRadii.begin(), sampledClusterRadii.begin() + middle, sampledClusterRadii.end());
275 double clusterRadius = sampledClusterRadii[middle];
277 return clusterRadius;
303 double relativeOverlap,
unsigned int verticesPerCluster,
304 bool projectClustersToInput)
322 const bool startGridAtEdge = projectClustersToInput;
325 PRECICE_DEBUG(
"Vertices per cluster: {}", verticesPerCluster);
352 if (inMesh->
nVertices() < verticesPerCluster * 2) {
355 PRECICE_WARN(
"Determining a cluster radius failed. This is most likely a result of a coupling mesh consisting of just a single vertex. Setting the cluster radius to 1.");
362 auto globalBB = localBB;
373 const double maximumCenterDistance = std::sqrt(4. / inDim) * clusterRadius * (1 - relativeOverlap);
377 std::array<unsigned int, 3> nClustersGlobal{1, 1, 1};
378 for (
int d = 0; d < globalBB.getDimension(); ++d)
379 nClustersGlobal[d] = std::ceil(std::max(1., globalBB.getEdgeLength(d) / maximumCenterDistance));
384 std::vector<double> centerCoords(inDim);
386 std::vector<double> distances(inDim);
388 std::vector<double> start(inDim);
390 for (
unsigned int d = 0; d < distances.size(); ++d) {
393 distances[d] = globalBB.getEdgeLength(d) / (nClustersGlobal[d]);
394 start[d] = globalBB.minCorner()(d);
399 if (startGridAtEdge) {
400 for (
int d = 0; d < inDim; ++d) {
402 nClustersGlobal[d] += 1;
407 std::transform(start.begin(), start.end(), distances.begin(), start.begin(), [](
auto s,
auto d) { return s + 0.5 * d; });
411 PRECICE_DEBUG(
"Global cluster distribution: {}", nClustersGlobal);
412 unsigned int nTotalClustersGlobal = std::accumulate(nClustersGlobal.begin(), nClustersGlobal.end(), 1U, std::multiplies<unsigned int>());
413 PRECICE_DEBUG(
"Global number of total clusters (tentative): {}", nTotalClustersGlobal);
419 std::array<unsigned int, 3> nClustersLocal{1, 1, 1};
420 for (
unsigned int d = 0; d < start.size(); ++d) {
422 if (distances[d] > 0) {
429 unsigned int nTotalClustersLocal = std::accumulate(nClustersLocal.begin(), nClustersLocal.end(), 1U, std::multiplies<unsigned int>());
430 centers.resize(nTotalClustersLocal,
mesh::Vertex({centerCoords, -1}));
433 PRECICE_DEBUG(
"Local cluster distribution: {}", nClustersLocal);
434 PRECICE_DEBUG(
"Local number of total clusters (tentative): {}", nTotalClustersLocal);
441 centerCoords[0] = start[0];
442 for (
unsigned int x = 0; x < nClustersLocal[0]; ++x, centerCoords[0] += distances[0]) {
443 centerCoords[1] = start[1];
444 for (
unsigned int y = 0; y < nClustersLocal[1]; ++y, centerCoords[1] += distances[1]) {
445 auto id = zCurve(std::array<unsigned int, 3>{{x, y, 0}}, nClustersLocal);
451 centerCoords[0] = start[0];
452 for (
unsigned int x = 0; x < nClustersLocal[0]; ++x, centerCoords[0] += distances[0]) {
453 centerCoords[1] = start[1];
454 for (
unsigned int y = 0; y < nClustersLocal[1]; ++y, centerCoords[1] += distances[1]) {
455 centerCoords[2] = start[2];
456 for (
unsigned int z = 0; z < nClustersLocal[2]; ++z, centerCoords[2] += distances[2]) {
457 auto id = zCurve(std::array<unsigned int, 3>{{x, y, z}}, nClustersLocal);
468 tagEmptyClusters(centers, clusterRadius, inMesh);
470 tagEmptyClusters(centers, clusterRadius, outMesh);
472 PRECICE_DEBUG(
"Number of non-tagged centers after empty clusters were filtered: {}", std::count_if(centers.begin(), centers.end(), [](
auto &v) { return !v.isTagged(); }));
475 if (projectClustersToInput) {
476 projectClusterCentersToinputMesh(centers, inMesh);
478 const auto duplicateThreshold = 0.4 * (*std::min_element(distances.begin(), distances.end()));
479 PRECICE_DEBUG(
"Tagging duplicates using the threshold value {} for the regular distances {}", duplicateThreshold, distances);
484 if (nClustersLocal[2] == 1) {
485 tagDuplicateCenters<2>(centers, nClustersLocal, duplicateThreshold);
487 tagDuplicateCenters<3>(centers, nClustersLocal, duplicateThreshold);
493 tagEmptyClusters(centers, clusterRadius, outMesh);
495 PRECICE_DEBUG(
"Number of non-tagged centers after duplicate tagging : {}", std::count_if(centers.begin(), centers.end(), [](
auto &v) { return !v.isTagged(); }));
497 PRECICE_ASSERT(std::all_of(centers.begin(), centers.end(), [idx = 0](
auto &v)
mutable { return v.getID() == idx++; }));
500 removeTaggedVertices(centers);
501 PRECICE_ASSERT(std::none_of(centers.begin(), centers.end(), [](
auto &v) { return v.isTagged(); }));
502 PRECICE_CHECK(centers.size() > 0,
"Too many vertices have been filtered out.");
504 return {clusterRadius, centers};
#define PRECICE_WARN(...)
#define PRECICE_DEBUG(...)
#define PRECICE_TRACE(...)
#define PRECICE_CHECK(check,...)
#define PRECICE_ASSERT(...)
int getDimensions() const
VertexContainer & vertices()
Returns modifieable container holding all vertices.
std::size_t nVertices() const
Returns the number of vertices.
Vertex & vertex(VertexID id)
Mutable access to a vertex by VertexID.
bool isJustInTime() const
const query::Index & index() const
Call preprocess() before index() to ensure correct projection handling.
bool empty() const
Does the mesh contain any vertices?
This class provides a lightweight logger.
An axis-aligned bounding box around a (partition of a) mesh.
Eigen::VectorXd maxCorner() const
the max corner of the bounding box
Eigen::VectorXd minCorner() const
the min corner of the bounding box
double longestEdgeLength() const
returns the maximum length of the bounding box in any dimension
void expandBy(const BoundingBox &otherBB)
Expand bounding box using another bounding box.
Eigen::VectorXd center() const
Returns the Center Of Gravity of the mesh.
double getEdgeLength(int axis) const
returns the edge length of a specific axis
Eigen::VectorXd getCoords() const
Returns the coordinates of the vertex.
std::tuple< double, Vertices > createClustering(mesh::PtrMesh inMesh, mesh::PtrMesh outMesh, double relativeOverlap, unsigned int verticesPerCluster, bool projectClustersToInput)
Creates a clustering as a collection of Vertices (representing the cluster centers) and a cluster rad...
double estimateClusterRadius(unsigned int verticesPerCluster, mesh::PtrMesh inMesh, const mesh::BoundingBox &bb)
Computes an estimate for the cluster radius, which results in approximately verticesPerCluster vertic...
std::vector< mesh::Vertex > Vertices
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.
constexpr T pow_int(const T base)
Computes the power of a given number by an integral exponent given at compile time,...
constexpr double NUMERICAL_ZERO_DIFFERENCE
provides Mesh, Data and primitives.
std::shared_ptr< Mesh > PtrMesh
static precice::logging::Logger _log("precicec")