Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/gridedit 1566 fast merge meshes #392

Merged
merged 11 commits into from
Jan 8, 2025
3 changes: 2 additions & 1 deletion cmake/compiler_config.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
if (UNIX)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
add_compile_options("-fvisibility=hidden;-Werror;-Wall;-Wextra;-pedantic;-Wno-unused-function")

if(APPLE AND (CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "arm64"))
# CMake automatically sets -Xarch_arm64 (for clang) but gcc doesn't support it
unset(_CMAKE_APPLE_ARCHS_DEFAULT)
Expand Down Expand Up @@ -51,7 +52,7 @@ set(USE_LIBFMT 0)
if(
(CMAKE_CXX_COMPILER_ID STREQUAL "GNU"
AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.1)
OR
OR
(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC"
AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 16.11.14)
)
Expand Down
16 changes: 13 additions & 3 deletions libs/MeshKernel/include/MeshKernel/ConnectMeshes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include "MeshKernel/Definitions.hpp"
#include "MeshKernel/Mesh2D.hpp"
#include "MeshKernel/Point.hpp"
#include "MeshKernel/Utilities/RTreeBase.hpp"

namespace meshkernel
{
Expand Down Expand Up @@ -62,6 +63,9 @@ namespace meshkernel
[[nodiscard]] static std::unique_ptr<meshkernel::UndoAction> Compute(Mesh2D& mesh, const double separationFraction = DefaultMaximumSeparationFraction);

private:
/// @brief The edge length tolerance, minimum size for which an edge will be considered.
static constexpr double EdgeLengthTolerance = 1.0e-10;

/// @brief The maximum number of hanging nodes along a single element edge
static constexpr UInt m_maximumNumberOfIrregularElementsAlongEdge = 5;

Expand Down Expand Up @@ -108,22 +112,26 @@ namespace meshkernel
/// @param [out] areAdjacent Indicates is edge1 and edge2 are adjacent
/// @param [out] startNode End point nodes, if not nullvalue then node is hanging node
/// @param [out] endNode End point nodes, if not nullvalue then node is hanging node
/// @param [in] edgeLengths The length of each edge that has been added, indexed by the edge id
static void AreEdgesAdjacent(const Mesh2D& mesh,
const double separationFraction,
const UInt edge1,
const UInt edge2,
bool& areAdjacent,
UInt& startNode,
UInt& endNode);
UInt& endNode,
const std::vector<double>& edgeLengths);

/// @brief Find all quadrilateral elements that do no have a neighbour across any of edges.
///
/// @param [in] mesh The mesh
/// @param [in,out] elementsOnDomainBoundary List of elements that do not have neighbour
/// @param [in,out] edgesOnDomainBoundary List of edges that do have elements on one side only
/// @param [out] edgesOnDomainBoundary List of edges that do have elements on one side only
/// @param [out] edgeLengths The length of each edge that has been added, indexed by the edge id
static void GetQuadrilateralElementsOnDomainBoundary(const Mesh2D& mesh,
std::vector<UInt>& elementsOnDomainBoundary,
std::vector<UInt>& edgesOnDomainBoundary);
std::vector<UInt>& edgesOnDomainBoundary,
std::vector<double>& edgeLengths);

/// @brief Get list of node id's ordered with distance from given point.
///
Expand Down Expand Up @@ -223,10 +231,12 @@ namespace meshkernel
/// @param [in] mesh The mesh
/// @param [in] separationFraction The fraction of the shortest edge to use when determining neighbour edge closeness
/// @param [in] edgesOnDomainBoundary List of edges along domain boundary, more specifically edges with only a single element attached
/// @param [in] edgeLengths The length of each edge that has been added, indexed by the edge id
/// @param [in,out] irregularEdges List of irregular edges with hanging nodes
static void GatherHangingNodeIds(const Mesh2D& mesh,
const double separationFraction,
const std::vector<UInt>& edgesOnDomainBoundary,
const std::vector<double>& edgeLengths,
IrregularEdgeInfoArray& irregularEdges);

/// @brief Gather all the nodes that need to be merged.
Expand Down
7 changes: 7 additions & 0 deletions libs/MeshKernel/include/MeshKernel/Mesh2D.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,13 @@ namespace meshkernel
/// it may be required to call Administrate after merging
static std::unique_ptr<Mesh2D> Merge(const Mesh2D& mesh1, const Mesh2D& mesh2);

/// @brief Merges mesh node and edge connectivity into a single mesh.
static std::unique_ptr<Mesh2D> Merge(const std::span<const Point>& mesh1Nodes,
const std::span<const Edge>& mesh1Edges,
const std::span<const Point>& mesh2Nodes,
const std::span<const Edge>& mesh2Edges,
const Projection projection);

/// @brief Get the mesh bounding box
///
/// @return The mesh bounding box
Expand Down
3 changes: 1 addition & 2 deletions libs/MeshKernel/include/MeshKernel/Point.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -115,8 +115,7 @@ namespace meshkernel
/// @brief Determines if one of the point coordinates equals to \p missingValue
[[nodiscard]] bool IsValid(const double missingValue = constants::missing::doubleValue) const
{
bool isInvalid = IsEqual(x, missingValue) ||
IsEqual(y, missingValue);
bool isInvalid = (x == missingValue || y == missingValue);

return !isInvalid;
}
Expand Down
67 changes: 44 additions & 23 deletions libs/MeshKernel/src/ConnectMeshes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
#include "MeshKernel/Operations.hpp"
#include "MeshKernel/RangeCheck.hpp"
#include "MeshKernel/UndoActions/CompoundUndoAction.hpp"
#include "MeshKernel/Utilities/RTreeFactory.hpp"

#include <ranges>

Expand All @@ -39,7 +40,8 @@ void meshkernel::ConnectMeshes::AreEdgesAdjacent(const Mesh2D& mesh,
const UInt edge2,
bool& areAdjacent,
UInt& startNode,
UInt& endNode)
UInt& endNode,
const std::vector<double>& edgeLengths)
{
const Point edge1Start = mesh.Node(mesh.GetEdge(edge1).first);
const Point edge1End = mesh.Node(mesh.GetEdge(edge1).second);
Expand All @@ -50,28 +52,32 @@ void meshkernel::ConnectMeshes::AreEdgesAdjacent(const Mesh2D& mesh,
startNode = constants::missing::uintValue;
endNode = constants::missing::uintValue;

if (edge1Start == edge1End || edge2Start == edge2End)
{
return;
}

const double edge1Length = ComputeDistance(edge1Start, edge1End, mesh.m_projection);
const double edge2Length = ComputeDistance(edge2Start, edge2End, mesh.m_projection);
const double edge1Length = edgeLengths[edge1];
const double edge2Length = edgeLengths[edge2];
const double minimumLength = separationFraction * std::min(edge1Length, edge2Length);

const Point midPoint1 = 0.5 * (edge1Start + edge1End);
const Point midPoint2 = 0.5 * (edge2Start + edge2End);

if (edge1Length <= edge2Length)
{
const Point midPoint = 0.5 * (edge1Start + edge1End);

const auto [distance, intersection, parameterisedDistance] = DistanceFromLine(midPoint, edge2Start, edge2End, mesh.m_projection);
areAdjacent = distance != constants::missing::doubleValue && distance < minimumLength;
// Check that the mid point of edge 1 is inside the box centred at the mid point of the edge 2, with size 2 * edge-length.
if (midPoint1.x > midPoint2.x - edge2Length && midPoint1.x < midPoint2.x + edge2Length &&
BillSenior marked this conversation as resolved.
Show resolved Hide resolved
midPoint1.y > midPoint2.y - edge2Length && midPoint1.y < midPoint2.y + edge2Length)
{
const auto [distance, intersection, parameterisedDistance] = DistanceFromLine(midPoint1, edge2Start, edge2End, mesh.m_projection);
areAdjacent = distance != constants::missing::doubleValue && distance < minimumLength;
}
}
else
{
const Point midPoint = 0.5 * (edge2Start + edge2End);

const auto [distance, intersection, parameterisedDistance] = DistanceFromLine(midPoint, edge1Start, edge1End, mesh.m_projection);
areAdjacent = distance != constants::missing::doubleValue && distance < minimumLength;
// Check that the mid point of edge 2 is inside the box centred at the mid point of the edge 1, with size 2 * edge-length.
if (midPoint2.x > midPoint1.x - edge1Length && midPoint2.x < midPoint1.x + edge1Length &&
midPoint2.y > midPoint1.y - edge1Length && midPoint2.y < midPoint1.y + edge1Length)
{
const auto [distance, intersection, parameterisedDistance] = DistanceFromLine(midPoint2, edge1Start, edge1End, mesh.m_projection);
areAdjacent = distance != constants::missing::doubleValue && distance < minimumLength;
}
}

if (areAdjacent)
Expand Down Expand Up @@ -101,7 +107,8 @@ void meshkernel::ConnectMeshes::AreEdgesAdjacent(const Mesh2D& mesh,

void meshkernel::ConnectMeshes::GetQuadrilateralElementsOnDomainBoundary(const Mesh2D& mesh,
std::vector<UInt>& elementsOnDomainBoundary,
std::vector<UInt>& edgesOnDomainBoundary)
std::vector<UInt>& edgesOnDomainBoundary,
std::vector<double>& edgeLengths)
{
for (UInt i = 0; i < mesh.GetNumEdges(); ++i)
{
Expand All @@ -112,8 +119,17 @@ void meshkernel::ConnectMeshes::GetQuadrilateralElementsOnDomainBoundary(const M
// Only store quadrilateral elements
if (mesh.m_numFacesNodes[faceId] == 4)
BillSenior marked this conversation as resolved.
Show resolved Hide resolved
{
elementsOnDomainBoundary.push_back(faceId);
edgesOnDomainBoundary.push_back(i);
double edgeLength = ComputeDistance(mesh.Node(mesh.GetEdge(i).first),
mesh.Node(mesh.GetEdge(i).second),
mesh.m_projection);

// Only store edge info for edges that have a size strictly greater than EdgeLengthTolerance
if (edgeLength != constants::missing::doubleValue && edgeLength > EdgeLengthTolerance) [[likely]]
{
elementsOnDomainBoundary.push_back(faceId);
edgesOnDomainBoundary.push_back(i);
edgeLengths[i] = edgeLength;
}
}
}
}
Expand All @@ -122,8 +138,10 @@ void meshkernel::ConnectMeshes::GetQuadrilateralElementsOnDomainBoundary(const M
void meshkernel::ConnectMeshes::GatherHangingNodeIds(const Mesh2D& mesh,
const double separationFraction,
const std::vector<UInt>& edgesOnDomainBoundary,
const std::vector<double>& edgeLengths,
IrregularEdgeInfoArray& irregularEdges)
{

for (UInt i = 0; i < edgesOnDomainBoundary.size(); ++i)
{
const UInt edgeI = edgesOnDomainBoundary[i];
Expand All @@ -142,7 +160,7 @@ void meshkernel::ConnectMeshes::GatherHangingNodeIds(const Mesh2D& mesh,
bool areAdjacent = false;
IrregularEdgeInfo& edgeInfo = irregularEdges[i];

AreEdgesAdjacent(mesh, separationFraction, edgeI, edgeJ, areAdjacent, startNode, endNode);
AreEdgesAdjacent(mesh, separationFraction, edgeI, edgeJ, areAdjacent, startNode, endNode, edgeLengths);

if (!areAdjacent)
{
Expand Down Expand Up @@ -225,14 +243,17 @@ std::unique_ptr<meshkernel::UndoAction> meshkernel::ConnectMeshes::Compute(Mesh2
// Edges with no neighbour
std::vector<UInt> edgesOnDomainBoundary;
edgesOnDomainBoundary.reserve(numberOfEdges);
std::vector<double> edgeLengths(numberOfEdges, constants::missing::doubleValue);

GetQuadrilateralElementsOnDomainBoundary(mesh, elementsOnDomainBoundary, edgesOnDomainBoundary);
GetQuadrilateralElementsOnDomainBoundary(mesh, elementsOnDomainBoundary, edgesOnDomainBoundary, edgeLengths);

std::vector<NodesToMerge> nodesToMerge;

IrregularEdgeInfoArray irregularEdges(numberOfEdges);
GatherHangingNodeIds(mesh, separationFraction, edgesOnDomainBoundary, irregularEdges);

GatherHangingNodeIds(mesh, separationFraction, edgesOnDomainBoundary, edgeLengths, irregularEdges);

std::vector<bool> adjacentEdgeIndicator(numberOfEdges, true);
std::vector<NodesToMerge> nodesToMerge;

// Size of this array needs to be greater than the number of edges
// The safety margin here is the maximum number of irregular elements along an edge.
Expand Down
43 changes: 43 additions & 0 deletions libs/MeshKernel/src/Mesh2D.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2340,6 +2340,7 @@ meshkernel::UInt Mesh2D::NextFace(const UInt faceId, const UInt edgeId) const

std::unique_ptr<Mesh2D> Mesh2D::Merge(const Mesh2D& mesh1, const Mesh2D& mesh2)
{

if (mesh1.m_projection != mesh2.m_projection)
{
throw MeshKernelError("The two meshes cannot be merged: they have different projections");
Expand Down Expand Up @@ -2475,6 +2476,48 @@ std::unique_ptr<Mesh2D> Mesh2D::Merge(const Mesh2D& mesh1, const Mesh2D& mesh2)
mergedMesh.m_projection);
}

std::unique_ptr<meshkernel::Mesh2D> Mesh2D::Merge(const std::span<const Point>& mesh1Nodes,
const std::span<const Edge>& mesh1Edges,
const std::span<const Point>& mesh2Nodes,
const std::span<const Edge>& mesh2Edges,
const Projection projection)
{
std::vector<Point> mergedNodes(mesh1Nodes.size() + mesh2Nodes.size());
std::vector<Edge> mergedEdges(mesh1Edges.size() + mesh2Edges.size());

if (!mesh1Nodes.empty())
{
// Merge node array from mesh1 nodes
std::ranges::copy(mesh1Nodes, mergedNodes.begin());

// Merge edge array from mesh1 edges
std::ranges::copy(mesh1Edges, mergedEdges.begin());
}

if (!mesh2Nodes.empty())
{
// Merge node array from mesh2 nodes
std::ranges::copy(mesh2Nodes, mergedNodes.begin() + mesh1Nodes.size());

// Merge edge array from mesh2 edges
std::ranges::copy(mesh2Edges, mergedEdges.begin() + mesh1Edges.size());

if (!mesh1Nodes.empty())
{
const UInt nodeOffset = static_cast<UInt>(mesh1Nodes.size());

// Re-assign the node ids for the second mesh data set
for (size_t i = mesh1Edges.size(); i < mergedEdges.size(); ++i)
{
IncrementValidValue(mergedEdges[i].first, nodeOffset);
IncrementValidValue(mergedEdges[i].second, nodeOffset);
}
}
}

return std::make_unique<Mesh2D>(mergedEdges, mergedNodes, projection);
}

meshkernel::BoundingBox Mesh2D::GetBoundingBox() const
{
Point lowerLeft(std::numeric_limits<double>::max(), std::numeric_limits<double>::max());
Expand Down
Loading
Loading