Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions libs/MeshKernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ set(
${SRC_DIR}/MeshEdgeLength.cpp
${SRC_DIR}/Mesh1D.cpp
${SRC_DIR}/Mesh2D.cpp
${SRC_DIR}/Mesh2DFaceBounds.cpp
${SRC_DIR}/Mesh2DGenerateGlobal.cpp
${SRC_DIR}/Mesh2DIntersections.cpp
${SRC_DIR}/Mesh2DToCurvilinear.cpp
Expand Down Expand Up @@ -182,6 +183,7 @@ set(
${DOMAIN_INC_DIR}/MeshEdgeLength.hpp
${DOMAIN_INC_DIR}/Mesh1D.hpp
${DOMAIN_INC_DIR}/Mesh2D.hpp
${DOMAIN_INC_DIR}/Mesh2DFaceBounds.hpp
${DOMAIN_INC_DIR}/Mesh2DGenerateGlobal.hpp
${DOMAIN_INC_DIR}/Mesh2DIntersections.hpp
${DOMAIN_INC_DIR}/Mesh2DToCurvilinear.hpp
Expand Down
3 changes: 2 additions & 1 deletion libs/MeshKernel/include/MeshKernel/Definitions.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,8 @@ namespace meshkernel
Orthogonality = 0,
EdgeLength = 1,
FaceCircumcenter = 2,
NetlinkContourPolygon = 3
NetlinkContourPolygon = 3,
FaceBounds = 4
};

/// \brief Describe how the circumcentre should be computed.
Expand Down
3 changes: 3 additions & 0 deletions libs/MeshKernel/include/MeshKernel/Mesh.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,9 @@ namespace meshkernel
/// is false then the edge is in-valid.
bool IsValidEdge(const UInt edgeId) const;

/// @brief Indicate if the face-id is a valid face
bool IsValidFace(const UInt faceId) const;

/// @brief Apply the reset node action
void CommitAction(const ResetNodeAction& undoAction);

Expand Down
53 changes: 53 additions & 0 deletions libs/MeshKernel/include/MeshKernel/Mesh2DFaceBounds.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
//---- GPL ---------------------------------------------------------------------
//
// Copyright (C) Stichting Deltares, 2011-2026.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// contact: delft3d.support@deltares.nl
// Stichting Deltares
// P.O. Box 177
// 2600 MH Delft, The Netherlands
//
// All indications and logos of, and references to, "Delft3D" and "Deltares"
// are registered trademarks of Stichting Deltares, and remain the property of
// Stichting Deltares. All rights reserved.
//
//------------------------------------------------------------------------------

#pragma once

#include <span>
#include <vector>

#include "MeshKernel/Constants.hpp"
#include "MeshKernel/Definitions.hpp"
#include "MeshKernel/Mesh.hpp"
#include "MeshKernel/Point.hpp"

namespace meshkernel::algo
{

/// @brief Compute the nodes for each face in the grid, will be saved as facebounds
class Mesh2DFaceBounds
{
public:
/// @brief Compute the nodes for each face in the grid
static std::vector<Point> Compute(const Mesh& mesh);

private:
/// @brief Compute the face bounds for a single face
static void ComputeBoundsForFace(const Mesh& mesh, UInt faceId, std::span<Point> faceBounds);
};

} // namespace meshkernel::algo
19 changes: 19 additions & 0 deletions libs/MeshKernel/src/Mesh.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1190,6 +1190,25 @@ bool Mesh::IsValidEdge(const UInt edgeId) const
m_nodes[m_edges[edgeId].first].IsValid() && m_nodes[m_edges[edgeId].second].IsValid();
}

bool Mesh::IsValidFace(const UInt faceId) const
{
if (faceId >= GetNumFaces())
{
throw ConstraintError("The face index is out of bounds. {} >= {}.", faceId, GetNumFaces());
}

for (UInt edgeId : m_facesEdges[faceId])
{

if (!IsValidEdge(edgeId))
{
return false;
}
}

return true;
}

void Mesh::BuildTree(Location location, const BoundingBox& boundingBox)
{
switch (location)
Expand Down
72 changes: 72 additions & 0 deletions libs/MeshKernel/src/Mesh2DFaceBounds.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
//---- GPL ---------------------------------------------------------------------
//
// Copyright (C) Stichting Deltares, 2011-2026.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// contact: delft3d.support@deltares.nl
// Stichting Deltares
// P.O. Box 177
// 2600 MH Delft, The Netherlands
//
// All indications and logos of, and references to, "Delft3D" and "Deltares"
// are registered trademarks of Stichting Deltares, and remain the property of
// Stichting Deltares. All rights reserved.
//
//------------------------------------------------------------------------------

#include "MeshKernel/Mesh2DFaceBounds.hpp"

#include <algorithm>

#include "MeshKernel/Constants.hpp"
#include "MeshKernel/MeshFaceCenters.hpp"
#include "MeshKernel/Operations.hpp"

std::vector<meshkernel::Point> meshkernel::algo::Mesh2DFaceBounds::Compute(const Mesh& mesh)
{
std::vector<Point> meshFaceBounds(constants::geometric::maximumNumberOfNodesPerFace * mesh.GetNumFaces());

#pragma omp parallel for
for (int face = 0; face < static_cast<int>(mesh.GetNumFaces()); ++face)
{
const UInt pointCount = constants::geometric::maximumNumberOfNodesPerFace * static_cast<UInt>(face);

std::span<Point> faceBounds(meshFaceBounds.data() + pointCount, meshFaceBounds.data() + pointCount + constants::geometric::maximumNumberOfNodesPerFace);
ComputeBoundsForFace(mesh, static_cast<UInt>(face), faceBounds);
}

return meshFaceBounds;
}

void meshkernel::algo::Mesh2DFaceBounds::ComputeBoundsForFace(const Mesh& mesh, UInt faceId, std::span<Point> faceBounds)
{
if (mesh.IsValidFace(faceId))
{
UInt numNodes = static_cast<UInt>(mesh.m_numFacesNodes[faceId]);

for (UInt i = 0; i < numNodes; ++i)
{
faceBounds[i] = mesh.Node(mesh.m_facesNodes[faceId][i]);
}

for (UInt i = numNodes; i < constants::geometric::maximumNumberOfNodesPerFace; ++i)
{
faceBounds[i] = Point{constants::missing::doubleValue, constants::missing::doubleValue};
}
}
else
{
std::ranges::fill(faceBounds, Point{constants::missing::doubleValue, constants::missing::doubleValue});
}
}
27 changes: 27 additions & 0 deletions libs/MeshKernel/tests/src/Mesh2DTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
#include "MeshKernel/Entities.hpp"
#include "MeshKernel/Mesh.hpp"
#include "MeshKernel/Mesh2D.hpp"
#include "MeshKernel/Mesh2DFaceBounds.hpp"
#include "MeshKernel/Mesh2DIntersections.hpp"
#include "MeshKernel/Mesh2DToCurvilinear.hpp"
#include "MeshKernel/MeshEdgeLength.hpp"
Expand Down Expand Up @@ -1862,3 +1863,29 @@ TEST(Mesh2D, ComputeEdgeNetlinkContourPolygonsSpherical)
++expectedNodeCount;
}
}

TEST(Mesh2D, ComputeMesh2DFaceBounds)
{
std::vector<meshkernel::Point> meshPoints{{10.0, 0.0}, {20.0, 10.0}, {10.0, 20.0}, {0.0, 10.0}, {30.0, 20.0}, {0.0, 30.0}, {20.0, 30.0}, {30.0, 30.0}};
std::vector<meshkernel::Edge> meshEdges{{0, 1}, {1, 2}, {2, 3}, {3, 0}, {1, 4}, {3, 5}, {5, 6}, {6, 7}, {2, 5}, {2, 6}, {4, 7}};

meshkernel::Mesh2D mesh(meshEdges, meshPoints, meshkernel::Projection::cartesian);
mesh.Administrate();

meshkernel::algo::Mesh2DFaceBounds mesh2dFaceBounds;

auto faceBoundPoints(mesh2dFaceBounds.Compute(mesh));

std::vector<double> expectedXs{10.0, 20.0, 0.0, -999.0, -999.0, -999.0, 10.0, 0.0, 0.0, -999.0, -999.0, -999.0, 10.0, 20.0, 10.0, 0.0, -999.0, -999.0, 20.0, 30.0, 30.0, 20.0, 10.0, -999.0};
std::vector<double> expectedYs{20.0, 30.0, 30.0, -999.0, -999.0, -999.0, 20.0, 30.0, 10.0, -999.0, -999.0, -999.0, 0.0, 10.0, 20.0, 10.0, -999.0, -999.0, 10.0, 20.0, 30.0, 30.0, 20.0, -999.0};

constexpr double tolerance = 1.0e-10;

ASSERT_EQ(faceBoundPoints.size(), expectedXs.size());

for (size_t i = 0; i < faceBoundPoints.size(); ++i)
{
EXPECT_NEAR(faceBoundPoints[i].x, expectedXs[i], tolerance);
EXPECT_NEAR(faceBoundPoints[i].y, expectedYs[i], tolerance);
}
}
2 changes: 2 additions & 0 deletions libs/MeshKernelApi/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ set(SRC_LIST
${SRC_DIR}/PropertyCalculator.cpp
${SRC_DIR}/EdgeLengthPropertyCalculator.cpp
${SRC_DIR}/FaceCircumcenterPropertyCalculator.cpp
${SRC_DIR}/Mesh2DFaceBoundsPropertyCalculator.cpp
${SRC_DIR}/NetlinkContourPolygonPropertyCalculator.cpp
${SRC_DIR}/OrthogonalityPropertyCalculator.cpp
${SRC_DIR}/InterpolatedSamplePropertyCalculator.cpp
Expand Down Expand Up @@ -65,6 +66,7 @@ set(
${DOMAIN_INC_DIR}/PropertyCalculator.hpp
${DOMAIN_INC_DIR}/EdgeLengthPropertyCalculator.hpp
${DOMAIN_INC_DIR}/FaceCircumcenterPropertyCalculator.hpp
${DOMAIN_INC_DIR}/Mesh2DFaceBoundsPropertyCalculator.hpp
${DOMAIN_INC_DIR}/NetlinkContourPolygonPropertyCalculator.hpp
${DOMAIN_INC_DIR}/OrthogonalityPropertyCalculator.hpp
${DOMAIN_INC_DIR}/InterpolatedSamplePropertyCalculator.hpp
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//---- GPL ---------------------------------------------------------------------
//
// Copyright (C) Stichting Deltares, 2011-2026.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// contact: delft3d.support@deltares.nl
// Stichting Deltares
// P.O. Box 177
// 2600 MH Delft, The Netherlands
//
// All indications and logos of, and references to, "Delft3D" and "Deltares"
// are registered trademarks of Stichting Deltares, and remain the property of
// Stichting Deltares. All rights reserved.
//
//------------------------------------------------------------------------------

#pragma once

#include "MeshKernel/Definitions.hpp"
#include "MeshKernel/Parameters.hpp"
#include "MeshKernel/SampleInterpolator.hpp"

#include "MeshKernelApi/GeometryList.hpp"
#include "MeshKernelApi/PropertyCalculator.hpp"

namespace meshkernelapi
{

/// @brief Calculator for the face bounds values for a mesh
class Mesh2DFaceBoundsPropertyCalculator : public PropertyCalculator
{
public:
/// @brief Determine is the calculator can compute the desired results correctly.
///
/// This has a default of checking that the mesh2d is valid and the location is at faces
bool IsValid(const MeshKernelState& state, const meshkernel::Location location) const override;

/// @brief Calculate the face bounds values for a mesh
void Calculate(const MeshKernelState& state, const meshkernel::Location location, const GeometryList& geometryList) const override;

/// @brief Determine the size of the face bounds array (for all faces) required
int Size(const MeshKernelState& state, const meshkernel::Location location) const override;
};

} // namespace meshkernelapi
5 changes: 5 additions & 0 deletions libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1250,6 +1250,11 @@ namespace meshkernelapi
/// @returns Error code
MKERNEL_API int mkernel_mesh2d_get_netlink_contour_polygon_property_type(int& type);

/// @brief Gets an int indicating the face bounds property type for mesh2d
/// @param[out] type The int indicating the face bounds property type
/// @returns Error code
MKERNEL_API int mkernel_mesh2d_get_face_bounds_property_type(int& type);

/// @brief Gets the Mesh2D inner boundary polygons data
///
/// @param[in] meshKernelId The id of the mesh state
Expand Down
70 changes: 70 additions & 0 deletions libs/MeshKernelApi/src/Mesh2DFaceBoundsPropertyCalculator.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
//---- GPL ---------------------------------------------------------------------
//
// Copyright (C) Stichting Deltares, 2011-2026.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation version 3.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// contact: delft3d.support@deltares.nl
// Stichting Deltares
// P.O. Box 177
// 2600 MH Delft, The Netherlands
//
// All indications and logos of, and references to, "Delft3D" and "Deltares"
// are registered trademarks of Stichting Deltares, and remain the property of
// Stichting Deltares. All rights reserved.
//
//------------------------------------------------------------------------------

#include "MeshKernelApi/Mesh2DFaceBoundsPropertyCalculator.hpp"
#include "MeshKernelApi/PropertyCalculator.hpp"
#include "MeshKernelApi/State.hpp"

#include "MeshKernel/Mesh2DFaceBounds.hpp"

bool meshkernelapi::Mesh2DFaceBoundsPropertyCalculator::IsValid(const MeshKernelState& state, const meshkernel::Location location) const
{
return state.m_mesh2d != nullptr && state.m_mesh2d->GetNumNodes() > 0 && location == meshkernel::Location::Faces;
}

void meshkernelapi::Mesh2DFaceBoundsPropertyCalculator::Calculate(const MeshKernelState& state, const meshkernel::Location location, const GeometryList& geometryList) const
{
if (geometryList.num_coordinates < static_cast<int>(meshkernel::constants::geometric::maximumNumberOfNodesPerFace * state.m_mesh2d->GetNumFaces()))
{
throw meshkernel::ConstraintError("GeometryList with wrong dimensions, {} must be greater than or equal to {}",
geometryList.num_coordinates, Size(state, location));
}

std::vector<meshkernel::Point> faceBounds(meshkernel::algo::Mesh2DFaceBounds::Compute(*state.m_mesh2d));

size_t size = static_cast<size_t>(Size(state, location));
std::span<double> xCoord(geometryList.coordinates_x, size);
std::span<double> yCoord(geometryList.coordinates_y, size);

for (size_t i = 0; i < faceBounds.size(); ++i)
{
xCoord[i] = faceBounds[i].x;
yCoord[i] = faceBounds[i].y;
}
}

int meshkernelapi::Mesh2DFaceBoundsPropertyCalculator::Size(const MeshKernelState& state, const meshkernel::Location location) const
{
int size = -1;

if (location == meshkernel::Location::Faces)
{
size = meshkernel::constants::geometric::maximumNumberOfNodesPerFace * static_cast<int>(state.m_mesh2d->GetNumFaces());
}

return size;
}
Loading
Loading