diff --git a/libs/MeshKernelApi/CMakeLists.txt b/libs/MeshKernelApi/CMakeLists.txt index 041a7024e..c23cab058 100644 --- a/libs/MeshKernelApi/CMakeLists.txt +++ b/libs/MeshKernelApi/CMakeLists.txt @@ -28,6 +28,7 @@ set(SRC_LIST ${SRC_DIR}/MeshKernel.cpp ${SRC_DIR}/NodeInPolygonCache.cpp ${SRC_DIR}/PolygonRefinementCache.cpp + ${SRC_DIR}/SmallFlowEdgeCentreCache.cpp ) # list of target headers @@ -47,6 +48,7 @@ set( ${DOMAIN_INC_DIR}/MeshKernel.hpp ${DOMAIN_INC_DIR}/NodeInPolygonCache.hpp ${DOMAIN_INC_DIR}/PolygonRefinementCache.hpp + ${DOMAIN_INC_DIR}/SmallFlowEdgeCentreCache.hpp ${DOMAIN_INC_DIR}/State.hpp ${DOMAIN_INC_DIR}/Utils.hpp ${VERSION_INC_DIR}/Version/Version.hpp diff --git a/libs/MeshKernelApi/include/MeshKernelApi/SmallFlowEdgeCentreCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/SmallFlowEdgeCentreCache.hpp new file mode 100644 index 000000000..f5e9b9fcf --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/SmallFlowEdgeCentreCache.hpp @@ -0,0 +1,57 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// 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 . +// +// 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 +#include + +#include "MeshKernel/Point.hpp" + +#include "MeshKernelApi/CachedPointValues.hpp" +#include "MeshKernelApi/GeometryList.hpp" + +namespace meshkernelapi +{ + + /// @brief Cache centre of edges + class SmallFlowEdgeCentreCache : public CachedPointValues + { + public: + /// @brief Constructor + SmallFlowEdgeCentreCache(const double lengthThreshold, + const std::vector& edgeCentres); + + /// @brief Determine if current options match those used to construct the object + bool ValidOptions(const double lengthThreshold) const; + + private: + /// @brief Threshold for small edge length + double m_lengthThreshold; + }; + +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp index ba44c1b8d..64e470509 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp @@ -43,6 +43,7 @@ #include "MeshKernelApi/FacePolygonPropertyCache.hpp" #include "MeshKernelApi/NodeInPolygonCache.hpp" #include "MeshKernelApi/PolygonRefinementCache.hpp" +#include "MeshKernelApi/SmallFlowEdgeCentreCache.hpp" namespace meshkernelapi { @@ -85,6 +86,7 @@ namespace meshkernelapi std::shared_ptr m_boundariesAsPolygonCache; ///< Cache std::shared_ptr m_polygonRefinementCache; ///< Cache for polygon refinement std::shared_ptr m_nodeInPolygonCache; ///< Cache for node in polygon + std::shared_ptr m_smallFlowEdgeCentreCache; ///< Cache for small flow edge centres }; } // namespace meshkernelapi diff --git a/libs/MeshKernelApi/src/MeshKernel.cpp b/libs/MeshKernelApi/src/MeshKernel.cpp index ffd564407..6f1879671 100644 --- a/libs/MeshKernelApi/src/MeshKernel.cpp +++ b/libs/MeshKernelApi/src/MeshKernel.cpp @@ -3426,10 +3426,19 @@ namespace meshkernelapi { throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + + if (meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache != nullptr) + { + meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache.reset(); + throw meshkernel::ConstraintError("Small flow edge data has already been cached. Cached values will be deleted."); + } + const auto edgesCrossingSmallFlowEdges = meshKernelState[meshKernelId].m_mesh2d->GetEdgesCrossingSmallFlowEdges(smallFlowEdgesLengthThreshold); const auto smallFlowEdgeCenters = meshKernelState[meshKernelId].m_mesh2d->GetFlowEdgesCenters(edgesCrossingSmallFlowEdges); - numSmallFlowEdges = static_cast(smallFlowEdgeCenters.size()); + meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache = std::make_shared(smallFlowEdgesLengthThreshold, smallFlowEdgeCenters); + + numSmallFlowEdges = meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache->Size(); } catch (...) { @@ -3448,10 +3457,19 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } - const auto edgesCrossingSmallFlowEdges = meshKernelState[meshKernelId].m_mesh2d->GetEdgesCrossingSmallFlowEdges(smallFlowEdgesThreshold); - const auto smallFlowEdgeCenters = meshKernelState[meshKernelId].m_mesh2d->GetFlowEdgesCenters(edgesCrossingSmallFlowEdges); + if (meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache == nullptr) + { + throw meshkernel::ConstraintError("Small flow edge data has not been cached"); + } + + if (!meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache->ValidOptions(smallFlowEdgesThreshold)) + { + meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache.reset(); + throw meshkernel::ConstraintError("Given small flow edge options are incompatible with the cached values. Cached values will be deleted."); + } - ConvertPointVectorToGeometryList(smallFlowEdgeCenters, result); + meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache->Copy(result); + meshKernelState[meshKernelId].m_smallFlowEdgeCentreCache.reset(); } catch (...) { diff --git a/libs/MeshKernelApi/src/SmallFlowEdgeCentreCache.cpp b/libs/MeshKernelApi/src/SmallFlowEdgeCentreCache.cpp new file mode 100644 index 000000000..4ee8fc9fa --- /dev/null +++ b/libs/MeshKernelApi/src/SmallFlowEdgeCentreCache.cpp @@ -0,0 +1,37 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2024. +// +// 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 . +// +// 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/SmallFlowEdgeCentreCache.hpp" + +meshkernelapi::SmallFlowEdgeCentreCache::SmallFlowEdgeCentreCache(const double lengthThreshold, + const std::vector& edgeCentres) + : CachedPointValues(edgeCentres), m_lengthThreshold(lengthThreshold) {} + +bool meshkernelapi::SmallFlowEdgeCentreCache::ValidOptions(const double lengthThreshold) const +{ + return lengthThreshold == m_lengthThreshold; +} diff --git a/libs/MeshKernelApi/tests/src/ApiTest.cpp b/libs/MeshKernelApi/tests/src/ApiTest.cpp index c24e7da03..9ef18bde7 100644 --- a/libs/MeshKernelApi/tests/src/ApiTest.cpp +++ b/libs/MeshKernelApi/tests/src/ApiTest.cpp @@ -1761,6 +1761,65 @@ TEST_F(CartesianApiTestFixture, GetSmallFlowEdges_OnMesh2D_ShouldGetSmallFlowEdg ASSERT_NEAR(result.coordinates_y[0], 0.0, tolerance); } +TEST_F(CartesianApiTestFixture, GetSmallFlowEdges_OnMesh2D_GetSmallFlowEdgesFailures) +{ + // Prepare a mesh with two triangles + meshkernelapi::Mesh2D mesh2d; + + std::vector node_x{0.0, 1.0, 1.0, 1.0}; + std::vector node_y{0.0, 0.0, 0.3, -0.3}; + std::vector edge_nodes{0, 3, 3, 1, 1, 0, 1, 2, 2, 0}; + + mesh2d.node_x = node_x.data(); + mesh2d.node_y = node_y.data(); + mesh2d.edge_nodes = edge_nodes.data(); + mesh2d.num_edges = static_cast(edge_nodes.size() * 0.5); + mesh2d.num_nodes = static_cast(node_x.size()); + + // Get the meshkernel id + auto const meshKernelId = GetMeshKernelId(); + double const smallFlowEdgesThreshold = 100.0; + meshkernelapi::GeometryList result{}; + + auto errorCode = mkernel_mesh2d_get_small_flow_edge_centers(meshKernelId, smallFlowEdgesThreshold, result); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Execute + errorCode = mkernel_mesh2d_set(meshKernelId, mesh2d); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + int numSmallFlowEdges; + errorCode = meshkernelapi::mkernel_mesh2d_count_small_flow_edge_centers(meshKernelId, smallFlowEdgesThreshold, numSmallFlowEdges); + + // Assert + std::vector coordinates_x(numSmallFlowEdges); + std::vector coordinates_y(numSmallFlowEdges); + result.coordinates_x = coordinates_x.data(); + result.coordinates_y = coordinates_y.data(); + result.num_coordinates = numSmallFlowEdges; + + errorCode = mkernel_mesh2d_get_small_flow_edge_centers(meshKernelId, smallFlowEdgesThreshold, result); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Cache has been deleted + errorCode = mkernel_mesh2d_get_small_flow_edge_centers(meshKernelId, smallFlowEdgesThreshold, result); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Re-cache values + errorCode = meshkernelapi::mkernel_mesh2d_count_small_flow_edge_centers(meshKernelId, smallFlowEdgesThreshold, numSmallFlowEdges); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Get values with different set of options + errorCode = mkernel_mesh2d_get_small_flow_edge_centers(meshKernelId, 2.0 * smallFlowEdgesThreshold, result); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Re-cache values + errorCode = meshkernelapi::mkernel_mesh2d_count_small_flow_edge_centers(meshKernelId, smallFlowEdgesThreshold, numSmallFlowEdges); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + // Attempt at getting the size again will cause an error + errorCode = meshkernelapi::mkernel_mesh2d_count_small_flow_edge_centers(meshKernelId, smallFlowEdgesThreshold, numSmallFlowEdges); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); +} + TEST_F(CartesianApiTestFixture, CountObtuseTriangles_OnMesh2DWithOneObtuseTriangle_ShouldCountObtuseTriangles) { // Prepare a mesh with one obtuse triangle