From 26670523952c1a81a5c5c2bf877d6722f9bbfa97 Mon Sep 17 00:00:00 2001 From: BillSenior Date: Wed, 16 Oct 2024 18:23:05 +0200 Subject: [PATCH 01/14] GRIDEDIT-1336 Passing to other computer --- .../include/MeshKernelApi/State.hpp | 18 +++ .../include/MeshKernelApi/Utils.hpp | 4 +- libs/MeshKernelApi/src/MeshKernel.cpp | 115 +++++++++++++++--- .../tests/src/CurvilinearGridTests.cpp | 81 ++++++++++++ libs/MeshKernelApi/tests/src/Mesh2DTests.cpp | 88 ++++++++++++++ 5 files changed, 287 insertions(+), 19 deletions(-) diff --git a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp index 74024f930..5474c58a0 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp @@ -73,6 +73,24 @@ namespace meshkernelapi // Exclusively owned state meshkernel::Projection m_projection{meshkernel::Projection::cartesian}; ///< Projection used by the meshes + + std::vector m_facePolygonsCoordsX; + std::vector m_facePolygonsCoordsY; + + int m_facePolygonsPropertyValue = 0; + double m_facePolygonsMinValue = -999.0; + double m_facePolygonsMaxValue = -999.0; + bool m_facePolygonsCoordsCached = false; + + std::vector m_boundaryPolygonsCoordsX; + std::vector m_boundaryPolygonsCoordsY; + + int m_lowerLeftNValue = -1; + int m_lowerLeftMValue = -1; + int m_upperRightNValue = -1; + int m_upperRightMValue = -1; + + bool m_boundaryPolygonsCoordsCached = false; }; } // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp b/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp index e42acc13c..e1f7f4301 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp @@ -486,7 +486,9 @@ namespace meshkernelapi const meshkernel::Projection& projection) { meshkernel::CurvilinearGridRectangular grid(projection); - + std::cout << "grid.Compute: " << makeGridParameters.num_columns << " " << makeGridParameters.num_rows << " " + << makeGridParameters.origin_x << " " << makeGridParameters.origin_y << " " + << makeGridParameters.angle << " " << makeGridParameters.block_size_x << " " << makeGridParameters.block_size_y << std::endl; return grid.Compute(makeGridParameters.num_columns, makeGridParameters.num_rows, makeGridParameters.origin_x, diff --git a/libs/MeshKernelApi/src/MeshKernel.cpp b/libs/MeshKernelApi/src/MeshKernel.cpp index 643584742..91fc7fe0a 100644 --- a/libs/MeshKernelApi/src/MeshKernel.cpp +++ b/libs/MeshKernelApi/src/MeshKernel.cpp @@ -945,19 +945,40 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("Invalid curvilinear grid"); } - auto lowerLeftNUnsigned = static_cast(lowerLeftN); - auto lowerLeftMUnsigned = static_cast(lowerLeftM); - auto upperRightNUnsigned = static_cast(upperRightN); - auto upperRightMUnsigned = static_cast(upperRightM); + if (!meshKernelState[meshKernelId].m_boundaryPolygonsCoordsCached) + { + throw meshkernel::MeshKernelError("Polygon data has not been cached"); + } - const auto minN = std::min(lowerLeftNUnsigned, upperRightNUnsigned); - const auto maxN = std::max(lowerLeftNUnsigned, upperRightNUnsigned); - const auto minM = std::min(lowerLeftMUnsigned, upperRightMUnsigned); - const auto maxM = std::max(lowerLeftMUnsigned, upperRightMUnsigned); + if (lowerLeftN != meshKernelState[meshKernelId].m_lowerLeftNValue || + lowerLeftM != meshKernelState[meshKernelId].m_lowerLeftMValue || + upperRightN != meshKernelState[meshKernelId].m_upperRightNValue || + upperRightM != meshKernelState[meshKernelId].m_upperRightMValue) + { + throw meshkernel::ConstraintError("Given polygon ranges are incompatible with the cached values"); + } - const auto boundaryPolygon = meshKernelState[meshKernelId].m_curvilinearGrid->ComputeBoundaryPolygons({minN, minM}, - {maxN, maxM}); - ConvertPointVectorToGeometryList(boundaryPolygon, boundaryPolygons); + if (boundaryPolygons.coordinates_x == nullptr || boundaryPolygons.coordinates_y == nullptr) + { + throw meshkernel::ConstraintError("Boundary polygon array are null"); + } + + if (boundaryPolygons.num_coordinates != static_cast(meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.size())) + { + throw meshkernel::ConstraintError("Incompatible boundary polygon size (user-size /= cached-size): {} /= {}", + boundaryPolygons.num_coordinates, + meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.size()); + } + + size_t valueCount = sizeof(double) * meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.size(); + + std::memcpy(boundaryPolygons.coordinates_x, meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.data(), valueCount); + std::memcpy(boundaryPolygons.coordinates_y, meshKernelState[meshKernelId].m_boundaryPolygonsCoordsY.data(), valueCount); + + // Now reset the vectors + meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.clear(); + meshKernelState[meshKernelId].m_boundaryPolygonsCoordsY.clear(); + meshKernelState[meshKernelId].m_boundaryPolygonsCoordsCached = false; } catch (...) { @@ -981,6 +1002,10 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("Invalid curvilinear grid"); } + meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.clear(); + meshKernelState[meshKernelId].m_boundaryPolygonsCoordsY.clear(); + meshKernelState[meshKernelId].m_boundaryPolygonsCoordsCached = false; + const auto lowerLeftNUnsigned = static_cast(lowerLeftN); const auto lowerLeftMUnsigned = static_cast(lowerLeftM); const auto upperRightNUnsigned = static_cast(upperRightN); @@ -994,6 +1019,21 @@ namespace meshkernelapi const auto boundaryPolygon = meshKernelState[meshKernelId].m_curvilinearGrid->ComputeBoundaryPolygons({minN, minM}, {maxN, maxM}); numberOfPolygonNodes = static_cast(boundaryPolygon.size()); + + meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.resize(boundaryPolygon.size()); + meshKernelState[meshKernelId].m_boundaryPolygonsCoordsY.resize(boundaryPolygon.size()); + meshKernelState[meshKernelId].m_boundaryPolygonsCoordsCached = true; + + meshKernelState[meshKernelId].m_lowerLeftNValue = lowerLeftN; + meshKernelState[meshKernelId].m_lowerLeftMValue = lowerLeftM; + meshKernelState[meshKernelId].m_upperRightNValue = upperRightN; + meshKernelState[meshKernelId].m_upperRightMValue = upperRightM; + + GeometryList boundaryPolygons; + boundaryPolygons.num_coordinates = boundaryPolygon.size(); + boundaryPolygons.coordinates_x = meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.data(); + boundaryPolygons.coordinates_y = meshKernelState[meshKernelId].m_boundaryPolygonsCoordsY.data(); + ConvertPointVectorToGeometryList(boundaryPolygon, boundaryPolygons); } catch (...) { @@ -2488,12 +2528,19 @@ namespace meshkernelapi { throw meshkernel::ConstraintError("The 2d mesh contains no nodes."); } + + meshKernelState[meshKernelId].m_facePolygonsCoordsX.clear(); + meshKernelState[meshKernelId].m_facePolygonsCoordsY.clear(); + meshKernelState[meshKernelId].m_facePolygonsCoordsCached = false; + geometryListDimension = 0; + const auto filterEnum = static_cast(propertyValue); const auto filterMask = meshKernelState[meshKernelId].m_mesh2d->FilterBasedOnMetric(meshkernel::Location::Faces, filterEnum, minValue, maxValue); - geometryListDimension = 0; + + // Now compute the size of the arrays required for (meshkernel::UInt f = 0; f < filterMask.size(); ++f) { if (!filterMask[f]) @@ -2503,9 +2550,23 @@ namespace meshkernelapi const auto faceNumEdges = static_cast(meshKernelState[meshKernelId].m_mesh2d->m_facesNodes[f].size()); geometryListDimension += faceNumEdges + 2; } + if (geometryListDimension > 0) { geometryListDimension -= 1; + meshKernelState[meshKernelId].m_facePolygonsCoordsX.resize(static_cast(geometryListDimension)); + meshKernelState[meshKernelId].m_facePolygonsCoordsY.resize(static_cast(geometryListDimension)); + GeometryList facePolygons; + + facePolygons.coordinates_x = meshKernelState[meshKernelId].m_facePolygonsCoordsX.data(); + facePolygons.coordinates_y = meshKernelState[meshKernelId].m_facePolygonsCoordsY.data(); + + meshKernelState[meshKernelId].m_facePolygonsPropertyValue = propertyValue; + meshKernelState[meshKernelId].m_facePolygonsMinValue = minValue; + meshKernelState[meshKernelId].m_facePolygonsMaxValue = maxValue; + + FillFacePolygons(meshKernelState[meshKernelId].m_mesh2d, filterMask, facePolygons); + meshKernelState[meshKernelId].m_facePolygonsCoordsCached = true; } } catch (...) @@ -2533,12 +2594,30 @@ namespace meshkernelapi throw meshkernel::ConstraintError("The 2d mesh contains no nodes."); } - const auto filterEnum = static_cast(propertyValue); - const auto filterMask = meshKernelState[meshKernelId].m_mesh2d->FilterBasedOnMetric(meshkernel::Location::Faces, - filterEnum, - minValue, - maxValue); - FillFacePolygons(meshKernelState[meshKernelId].m_mesh2d, filterMask, facePolygons); + if (!meshKernelState[meshKernelId].m_facePolygonsCoordsCached) + { + throw meshkernel::ConstraintError("Filtered data has not been calculated"); + } + + if (propertyValue != meshKernelState[meshKernelId].m_facePolygonsPropertyValue || + minValue != meshKernelState[meshKernelId].m_facePolygonsMinValue || + maxValue != meshKernelState[meshKernelId].m_facePolygonsMaxValue) + { + throw meshkernel::ConstraintError("Given filter properties are incompatible with the cached values: property value: {} <=> {}, min value: {} <=> {}, max value {} <=> {}", + propertyValue, meshKernelState[meshKernelId].m_facePolygonsPropertyValue, + minValue, meshKernelState[meshKernelId].m_facePolygonsMinValue, + maxValue, meshKernelState[meshKernelId].m_facePolygonsMaxValue); + } + + size_t valueCount = sizeof(double) * meshKernelState[meshKernelId].m_facePolygonsCoordsX.size(); + + std::memcpy(facePolygons.coordinates_x, meshKernelState[meshKernelId].m_facePolygonsCoordsX.data(), valueCount); + std::memcpy(facePolygons.coordinates_y, meshKernelState[meshKernelId].m_facePolygonsCoordsY.data(), valueCount); + + // Now reset the vectors + meshKernelState[meshKernelId].m_facePolygonsCoordsX.clear(); + meshKernelState[meshKernelId].m_facePolygonsCoordsY.clear(); + meshKernelState[meshKernelId].m_facePolygonsCoordsCached = false; } catch (...) { diff --git a/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp b/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp index 3d8f3bbae..292445807 100644 --- a/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp +++ b/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp @@ -1490,6 +1490,87 @@ TEST_P(CurvilineartBoundariesAsPolygonsTests, GetLocationIndex_OnACurvilinearGri ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); } +TEST(CurvilineartGrid, GetLocationIndex_OnACurvilinearGrid_GetLocationIdexFailures) +{ + const int lowerLeftN = 1; + const int lowerLeftM = 1; + const int upperRightN = 8; + const int upperRightM = 8; + + // Prepare + int meshKernelId; + auto errorCode = meshkernelapi::mkernel_allocate_state(0, meshKernelId); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + MakeGridParameters makeGridParameters; + + makeGridParameters.num_columns = 10; + makeGridParameters.num_rows = 10; + makeGridParameters.angle = 0.0; + makeGridParameters.origin_x = 0.0; + makeGridParameters.origin_y = 0.0; + makeGridParameters.block_size_x = 10.0; + makeGridParameters.block_size_y = 10.0; + + std::cout << "here 0 " << std::endl; + + errorCode = meshkernelapi::mkernel_curvilinear_compute_rectangular_grid(meshKernelId, makeGridParameters); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + int numberOfPolygonNodes; + + meshkernelapi::GeometryList boundaryPolygon; + std::vector coordinates_x(numberOfPolygonNodes); + std::vector coordinates_y(numberOfPolygonNodes); + + boundaryPolygon.coordinates_x = coordinates_x.data(); + boundaryPolygon.coordinates_y = coordinates_y.data(); + boundaryPolygon.num_coordinates = numberOfPolygonNodes; + boundaryPolygon.geometry_separator = constants::missing::doubleValue; + + std::cout << "here 1 " << std::endl; + + // Get before being cached + errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, boundaryPolygon); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + std::cout << "here 2 " << std::endl; + + errorCode = meshkernelapi::mkernel_curvilinear_count_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, numberOfPolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + std::cout << "here 3 " << std::endl; + + // Check working version + errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, boundaryPolygon); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + std::cout << "here 4 " << std::endl; + + // Different lowerLeftN + errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN + 1, lowerLeftM, upperRightN, upperRightM, boundaryPolygon); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + std::cout << "here 5 " << std::endl; + + // Different lowerLeftM + errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM + 1, upperRightN, upperRightM, boundaryPolygon); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + std::cout << "here 6 " << std::endl; + + // Different upperRightN + errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN + 1, upperRightM, boundaryPolygon); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Different upperRightM + errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM + 1, boundaryPolygon); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + errorCode = meshkernelapi::mkernel_expunge_state(meshKernelId); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); +} + INSTANTIATE_TEST_SUITE_P(CurvilineartBoundariesAsPolygonsTests, CurvilineartBoundariesAsPolygonsTests, ::testing::ValuesIn(CurvilineartBoundariesAsPolygonsTests::GetData())); TEST(CurvilinearGrid, MakeCircularGrid_CartesianCoordinate_ShouldMakeCurvilinearGrid) diff --git a/libs/MeshKernelApi/tests/src/Mesh2DTests.cpp b/libs/MeshKernelApi/tests/src/Mesh2DTests.cpp index b65690cc1..5cf9abc79 100644 --- a/libs/MeshKernelApi/tests/src/Mesh2DTests.cpp +++ b/libs/MeshKernelApi/tests/src/Mesh2DTests.cpp @@ -259,3 +259,91 @@ TEST(Mesh2DTests, GetPolygonsOfDeletedFaces_WithPolygon_ShouldGetPolygonOfDelete ASSERT_NEAR(expectedFacePolygonsY[i], yfacePolygons[i], 1e-6); } } + +TEST(Mesh2DTests, GetPolygonsOfDeletedFaces_WithPolygon_FailureTests) +{ + meshkernel::MakeGridParameters makeGridParameters; + + makeGridParameters.origin_x = 0.0; + makeGridParameters.origin_y = 0.0; + makeGridParameters.block_size_x = 1.0; + makeGridParameters.block_size_y = 1.0; + makeGridParameters.num_columns = 20; + makeGridParameters.num_rows = 20; + + int meshKernelId = 0; + const int projectionType = 0; + auto errorCode = meshkernelapi::mkernel_allocate_state(projectionType, meshKernelId); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + errorCode = meshkernelapi::mkernel_curvilinear_compute_rectangular_grid(meshKernelId, makeGridParameters); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + errorCode = mkapi::mkernel_curvilinear_convert_to_mesh2d(meshKernelId); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + // + + int propertyType = -1; + errorCode = meshkernelapi::mkernel_mesh2d_get_orthogonality_property_type(propertyType); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + double minValue = -1.0; + double maxValue = 1.0; + + meshkernelapi::GeometryList facePolygons{}; + + // face data not yet cached + errorCode = mkernel_mesh2d_get_filtered_face_polygons(meshKernelId, + propertyType + 1, + minValue, + maxValue, + facePolygons); + + // Assert + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Execute + int geometryListDimension = -1; + errorCode = meshkernelapi::mkernel_mesh2d_get_filtered_face_polygons_dimension(meshKernelId, + propertyType, + minValue, + maxValue, + geometryListDimension); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + facePolygons.num_coordinates = geometryListDimension; + facePolygons.geometry_separator = meshkernel::constants::missing::doubleValue; + std::vector xfacePolygons(geometryListDimension); + std::vector yfacePolygons(geometryListDimension); + facePolygons.coordinates_x = xfacePolygons.data(); + facePolygons.coordinates_y = yfacePolygons.data(); + + // Check different property type + errorCode = mkernel_mesh2d_get_filtered_face_polygons(meshKernelId, + propertyType + 1, + minValue, + maxValue, + facePolygons); + + // Assert + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Check different minimum value + errorCode = mkernel_mesh2d_get_filtered_face_polygons(meshKernelId, + propertyType, + minValue + 0.5, + maxValue, + facePolygons); + + // Assert + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + // Check different minimum value + errorCode = mkernel_mesh2d_get_filtered_face_polygons(meshKernelId, + propertyType, + minValue, + maxValue + 0.5, + facePolygons); + + // Assert + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); +} From 18e9e8292e32ec18c3426b02ae9da43d4ab245b1 Mon Sep 17 00:00:00 2001 From: BillSenior Date: Thu, 17 Oct 2024 09:37:52 +0200 Subject: [PATCH 02/14] GRIDEDIT-1336 Removed debugging cout statements. Fixed unit test --- .../include/MeshKernelApi/Utils.hpp | 3 --- .../tests/src/CurvilinearGridTests.cpp | 27 ++++++++----------- 2 files changed, 11 insertions(+), 19 deletions(-) diff --git a/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp b/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp index e1f7f4301..132a5d73f 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp @@ -486,9 +486,6 @@ namespace meshkernelapi const meshkernel::Projection& projection) { meshkernel::CurvilinearGridRectangular grid(projection); - std::cout << "grid.Compute: " << makeGridParameters.num_columns << " " << makeGridParameters.num_rows << " " - << makeGridParameters.origin_x << " " << makeGridParameters.origin_y << " " - << makeGridParameters.angle << " " << makeGridParameters.block_size_x << " " << makeGridParameters.block_size_y << std::endl; return grid.Compute(makeGridParameters.num_columns, makeGridParameters.num_rows, makeGridParameters.origin_x, diff --git a/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp b/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp index 292445807..78332c385 100644 --- a/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp +++ b/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp @@ -1490,7 +1490,7 @@ TEST_P(CurvilineartBoundariesAsPolygonsTests, GetLocationIndex_OnACurvilinearGri ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); } -TEST(CurvilineartGrid, GetLocationIndex_OnACurvilinearGrid_GetLocationIdexFailures) +TEST(CurvilineartGrid, GetLocationIndex_OnACurvilinearGrid_GetLocationIndexFailures) { const int lowerLeftN = 1; const int lowerLeftM = 1; @@ -1512,8 +1512,6 @@ TEST(CurvilineartGrid, GetLocationIndex_OnACurvilinearGrid_GetLocationIdexFailur makeGridParameters.block_size_x = 10.0; makeGridParameters.block_size_y = 10.0; - std::cout << "here 0 " << std::endl; - errorCode = meshkernelapi::mkernel_curvilinear_compute_rectangular_grid(meshKernelId, makeGridParameters); ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); @@ -1528,42 +1526,39 @@ TEST(CurvilineartGrid, GetLocationIndex_OnACurvilinearGrid_GetLocationIdexFailur boundaryPolygon.num_coordinates = numberOfPolygonNodes; boundaryPolygon.geometry_separator = constants::missing::doubleValue; - std::cout << "here 1 " << std::endl; - - // Get before being cached + // Try to get before being cached errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, boundaryPolygon); ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); - std::cout << "here 2 " << std::endl; - errorCode = meshkernelapi::mkernel_curvilinear_count_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, numberOfPolygonNodes); ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); - std::cout << "here 3 " << std::endl; - // Check working version + boundaryPolygon.num_coordinates = numberOfPolygonNodes; errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, boundaryPolygon); ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); - std::cout << "here 4 " << std::endl; - // Different lowerLeftN + errorCode = meshkernelapi::mkernel_curvilinear_count_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, numberOfPolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN + 1, lowerLeftM, upperRightN, upperRightM, boundaryPolygon); ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); - std::cout << "here 5 " << std::endl; - // Different lowerLeftM + errorCode = meshkernelapi::mkernel_curvilinear_count_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, numberOfPolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM + 1, upperRightN, upperRightM, boundaryPolygon); ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); - std::cout << "here 6 " << std::endl; - // Different upperRightN + errorCode = meshkernelapi::mkernel_curvilinear_count_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, numberOfPolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN + 1, upperRightM, boundaryPolygon); ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); // Different upperRightM + errorCode = meshkernelapi::mkernel_curvilinear_count_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, numberOfPolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM + 1, boundaryPolygon); ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); From 2c9e3bfdb7e359ecde93b5a7dcb2c5b6720cdf01 Mon Sep 17 00:00:00 2001 From: BillSenior Date: Thu, 17 Oct 2024 16:21:35 +0200 Subject: [PATCH 03/14] GRIDEDIT-1336 Added caching for polygon refinement --- libs/MeshKernelApi/CMakeLists.txt | 8 + .../BoundariesAsPolygonCache.hpp | 66 +++++++ .../MeshKernelApi/CachedPointValues.hpp | 70 +++++++ .../FacePolygonPropertyCache.hpp | 62 +++++++ .../include/MeshKernelApi/MeshKernel.hpp | 8 + .../MeshKernelApi/PolygonRefinementCache.hpp | 61 +++++++ .../include/MeshKernelApi/State.hpp | 27 +-- .../include/MeshKernelApi/Utils.hpp | 16 +- .../src/BoundariesAsPolygonCache.cpp | 52 ++++++ libs/MeshKernelApi/src/CachedPointValues.cpp | 57 ++++++ .../src/FacePolygonPropertyCache.cpp | 63 +++++++ libs/MeshKernelApi/src/MeshKernel.cpp | 172 +++++++++--------- .../src/PolygonRefinementCache.cpp | 24 +++ .../tests/src/Mesh2DRefinmentTests.cpp | 2 +- 14 files changed, 577 insertions(+), 111 deletions(-) create mode 100644 libs/MeshKernelApi/include/MeshKernelApi/BoundariesAsPolygonCache.hpp create mode 100644 libs/MeshKernelApi/include/MeshKernelApi/CachedPointValues.hpp create mode 100644 libs/MeshKernelApi/include/MeshKernelApi/FacePolygonPropertyCache.hpp create mode 100644 libs/MeshKernelApi/include/MeshKernelApi/PolygonRefinementCache.hpp create mode 100644 libs/MeshKernelApi/src/BoundariesAsPolygonCache.cpp create mode 100644 libs/MeshKernelApi/src/CachedPointValues.cpp create mode 100644 libs/MeshKernelApi/src/FacePolygonPropertyCache.cpp create mode 100644 libs/MeshKernelApi/src/PolygonRefinementCache.cpp diff --git a/libs/MeshKernelApi/CMakeLists.txt b/libs/MeshKernelApi/CMakeLists.txt index 1be63d013..0560e930b 100644 --- a/libs/MeshKernelApi/CMakeLists.txt +++ b/libs/MeshKernelApi/CMakeLists.txt @@ -21,22 +21,30 @@ set(VERSION_INC_DIR ${CMAKE_SOURCE_DIR}/tools) # list of target sources set(SRC_LIST + ${SRC_DIR}/BoundariesAsPolygonCache.cpp + ${SRC_DIR}/CachedPointValues.cpp + ${SRC_DIR}/FacePolygonPropertyCache.cpp ${SRC_DIR}/MKStateUndoAction.cpp ${SRC_DIR}/MeshKernel.cpp + ${SRC_DIR}/PolygonRefinementCache.cpp ) # list of target headers set( INC_LIST + ${DOMAIN_INC_DIR}/BoundariesAsPolygonCache.hpp ${DOMAIN_INC_DIR}/BoundingBox.hpp + ${DOMAIN_INC_DIR}/CachedPointValues.hpp ${DOMAIN_INC_DIR}/Contacts.hpp ${DOMAIN_INC_DIR}/CurvilinearGrid.hpp + ${DOMAIN_INC_DIR}/FacePolygonPropertyCache.hpp ${DOMAIN_INC_DIR}/GeometryList.hpp ${DOMAIN_INC_DIR}/GriddedSamples.hpp ${DOMAIN_INC_DIR}/MKStateUndoAction.hpp ${DOMAIN_INC_DIR}/Mesh1D.hpp ${DOMAIN_INC_DIR}/Mesh2D.hpp ${DOMAIN_INC_DIR}/MeshKernel.hpp + ${DOMAIN_INC_DIR}/PolygonRefinementCache.hpp ${DOMAIN_INC_DIR}/State.hpp ${DOMAIN_INC_DIR}/Utils.hpp ${VERSION_INC_DIR}/Version/Version.hpp diff --git a/libs/MeshKernelApi/include/MeshKernelApi/BoundariesAsPolygonCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/BoundariesAsPolygonCache.hpp new file mode 100644 index 000000000..9e0bfc44d --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/BoundariesAsPolygonCache.hpp @@ -0,0 +1,66 @@ +//---- 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 +#include + +#include "MeshKernelApi/CachedPointValues.hpp" +#include "MeshKernelApi/GeometryList.hpp" +#include "MeshKernelApi/Utils.hpp" + +namespace meshkernelapi +{ + + /// @brief Boundary points + class BoundariesAsPolygonCache : public CachedPointValues + { + public: + /// @brief Constructor + BoundariesAsPolygonCache(const int lowerLeftN, + const int lowerLeftM, + const int upperRightN, + const int upperRightM, + const std::vector& boundaryPoints); + + /// @brief Determine if current options match those used to construct the object + bool ValidOptions(const int lowerLeftN, + const int lowerLeftM, + const int upperRightN, + const int upperRightM) const; + + private: + int m_lowerLeftNValue = -1; ///< Initial lower left N value + int m_lowerLeftMValue = -1; ///< Initial lower left M value + int m_upperRightNValue = -1; ///< Initial upper right N value + int m_upperRightMValue = -1; ///< Initial upper right M value + }; + +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/CachedPointValues.hpp b/libs/MeshKernelApi/include/MeshKernelApi/CachedPointValues.hpp new file mode 100644 index 000000000..098245439 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/CachedPointValues.hpp @@ -0,0 +1,70 @@ +//---- 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/GeometryList.hpp" + +namespace meshkernelapi +{ + /// @brief Caches x- and y-coordinate values for various algorithms + class CachedPointValues + { + public: + /// @brief Default constructor + CachedPointValues() {} + + /// @brief Construct with point values + CachedPointValues(const std::vector& coordinates); + + /// @brief Destructor + virtual ~CachedPointValues() = default; + + /// @brief Number of points saved + int Size() const + { + return static_cast(m_coordsX.size()); + } + + /// @brief Copy cached points to geometry + void Copy(const GeometryList& geometry) const; + + protected: + /// @brief Reset the saved coordinate values. + void Reset(std::vector&& xValues, + std::vector&& yValues); + + private: + std::vector m_coordsX; ///< x-coordinate values + std::vector m_coordsY; ///< y-coordinate values + }; +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/FacePolygonPropertyCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/FacePolygonPropertyCache.hpp new file mode 100644 index 000000000..d05a44408 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/FacePolygonPropertyCache.hpp @@ -0,0 +1,62 @@ +//---- 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 "MeshKernel/Mesh2D.hpp" + +#include "MeshKernelApi/CachedPointValues.hpp" + +namespace meshkernelapi +{ + /// @brief + class FacePolygonPropertyCache : public CachedPointValues + { + + public: + /// @brief Constuctor + FacePolygonPropertyCache(const int propertyValue, + const double minValue, + const double maxValue, + const meshkernel::Mesh2D& mesh, + const int validSize, + const std::vector& filterMask); + + /// @brief Determine if current options match those used to construct the object + bool ValidOptions(const int propertyValue, + const double minValue, + const double maxValue) const; + + private: + int m_propertyValue = 0; ///< Initial property value + double m_minimumValue = meshkernel::constants::missing::doubleValue; ///< Initial minimum value + double m_maximumValue = meshkernel::constants::missing::doubleValue; ///< Initial maximum value + }; + +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp b/libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp index 514638850..e8e429f04 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp @@ -351,6 +351,7 @@ namespace meshkernelapi /// @param[in] upperRightM The m index of the upper right corner /// @param[out] boundaryPolygons The geometry list containing the boundary polygons /// @returns Error code + /// @note Values are retrieved from the cache, cached values are deleted after copying MKERNEL_API int mkernel_curvilinear_get_boundaries_as_polygons(int meshKernelId, int lowerLeftN, int lowerLeftM, int upperRightN, int upperRightM, GeometryList& boundaryPolygons); /// @brief Count the number of nodes in curvilinear grid boundary polygons. @@ -362,6 +363,7 @@ namespace meshkernelapi /// @param[in] upperRightM The m index of the upper right corner /// @param[out] numberOfPolygonNodes The number of polygon nodes /// @returns Error code + /// @note Refined boundary polygon values are cached, so that they can be copied MKERNEL_API int mkernel_curvilinear_count_boundaries_as_polygons(int meshKernelId, int lowerLeftN, int lowerLeftM, int upperRightN, int upperRightM, int& numberOfPolygonNodes); /// @brief Gets the curvilinear grid dimensions as a CurvilinearGrid struct (converted as set of edges and nodes). @@ -1261,6 +1263,7 @@ namespace meshkernelapi /// @param[out] geometryListDimension The output parameter that will store the dimension (size) of the geometry list /// containing the polygons that match the filtering criteria. /// @returns An error code indicating the success or failure of the operation. + /// @note property values are cached, so that they can be copied MKERNEL_API int mkernel_mesh2d_get_filtered_face_polygons_dimension(int meshKernelId, int propertyValue, double minValue, @@ -1275,6 +1278,7 @@ namespace meshkernelapi /// @param[in] maxValue The maximum value of the property. /// @param[out] facePolygons The geometry list containing the filtered locations. /// @returns Error code + /// @note Values are retrieved from the cache, cached values are deleted after copying MKERNEL_API int mkernel_mesh2d_get_filtered_face_polygons(int meshKernelId, int propertyValue, double minValue, @@ -1697,6 +1701,7 @@ namespace meshkernelapi /// @param[in] distance The target interval edge length /// @param[out] numberOfPolygonNodes The number of nodes after refinement /// @returns Error code + /// @note Refined polygon values are cached, so that they can be copied in the mkernel_polygon_refine MKERNEL_API int mkernel_polygon_count_refine(int meshKernelId, const GeometryList& polygonToRefine, int firstIndex, @@ -1713,6 +1718,7 @@ namespace meshkernelapi /// @param[in] secondIndex The second index of the refinement interval /// @param[out] numberOfPolygonNodes The number of nodes after refinement /// @returns Error code + /// @note Refined polygon values are cached, so that they can be copied in the mkernel_polygon_linear_refine MKERNEL_API int mkernel_polygon_count_linear_refine(int meshKernelId, const GeometryList& polygonToRefine, int firstIndex, @@ -1756,6 +1762,7 @@ namespace meshkernelapi /// @param[in] targetEdgeLength The target interval edge length /// @param[out] refinedPolygon The refined polygon /// @returns Error code + /// @note Values are retrieved from the cache, cached values are cleared after copying MKERNEL_API int mkernel_polygon_refine(int meshKernelId, const GeometryList& polygonToRefine, int firstNodeIndex, @@ -1771,6 +1778,7 @@ namespace meshkernelapi /// @param[in] secondNodeIndex The second index of the refinement interval /// @param[out] refinedPolygon The refined polygon /// @returns Error code + /// @note Values are retrieved from the cache, cached values are cleared after copying MKERNEL_API int mkernel_polygon_linear_refine(int meshKernelId, const GeometryList& polygonToRefine, int firstNodeIndex, diff --git a/libs/MeshKernelApi/include/MeshKernelApi/PolygonRefinementCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/PolygonRefinementCache.hpp new file mode 100644 index 000000000..4d24ec463 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/PolygonRefinementCache.hpp @@ -0,0 +1,61 @@ +//---- 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 "MeshKernelApi/CachedPointValues.hpp" + +namespace meshkernelapi +{ + /// @brief Refined polygon point values + class PolygonRefinementCache : public CachedPointValues + { + + public: + /// @brief Constructor + PolygonRefinementCache(const std::vector& polyPoints, + const int firstIndex, + const int secondIndex, + const double edgeLength, + const std::vector& refinedPoints); + + /// @brief Determine if current options match those used to construct the object + bool ValidOptions(const std::vector& polyPoints, + const int firstIndex, + const int secondIndex, + const double edgeLength) const; + + private: + std::vector m_polygonPoints; ///< Initial polygon points + int m_firstNodeIndex = meshkernel::constants::missing::intValue; ///< Initial first node index value + int m_secondNodeIndex = meshkernel::constants::missing::intValue; ///< Initial second node index value + double m_targetEdgeLength = meshkernel::constants::missing::doubleValue; ///< Initial edge length, may be missing value. + }; + +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp index 5474c58a0..d8aa22bb8 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp @@ -38,6 +38,12 @@ #include #include +#include "MeshKernelApi/BoundariesAsPolygonCache.hpp" +#include "MeshKernelApi/CachedPointValues.hpp" +#include "MeshKernelApi/FacePolygonPropertyCache.hpp" +#include "MeshKernelApi/PolygonRefinementCache.hpp" + + namespace meshkernelapi { @@ -74,23 +80,10 @@ namespace meshkernelapi // Exclusively owned state meshkernel::Projection m_projection{meshkernel::Projection::cartesian}; ///< Projection used by the meshes - std::vector m_facePolygonsCoordsX; - std::vector m_facePolygonsCoordsY; - - int m_facePolygonsPropertyValue = 0; - double m_facePolygonsMinValue = -999.0; - double m_facePolygonsMaxValue = -999.0; - bool m_facePolygonsCoordsCached = false; - - std::vector m_boundaryPolygonsCoordsX; - std::vector m_boundaryPolygonsCoordsY; - - int m_lowerLeftNValue = -1; - int m_lowerLeftMValue = -1; - int m_upperRightNValue = -1; - int m_upperRightMValue = -1; - - bool m_boundaryPolygonsCoordsCached = false; + // Cached values, used when dimensions are computed first, followed by values beign retrieved in a separate call + std::shared_ptr m_facePropertyCache; ///< Cache for + std::shared_ptr m_boundariesAsPolygonCache; ///< Cache + std::shared_ptr m_polygonRefinementCache; ///< Cache for polygon refinement }; } // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp b/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp index 132a5d73f..89c165fac 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/Utils.hpp @@ -44,6 +44,8 @@ #include "MeshKernel/BilinearInterpolationOnGriddedSamples.hpp" #include +#include "MeshKernelApi/CurvilinearGrid.hpp" + #include #include #include @@ -582,34 +584,34 @@ namespace meshkernelapi } } - static void FillFacePolygons(std::shared_ptr mesh2d, + static void FillFacePolygons(const meshkernel::Mesh2D& mesh2d, const std::vector& facesInPolygon, const GeometryList& facePolygons) { meshkernel::UInt count = 0; - for (meshkernel::UInt f = 0; f < mesh2d->GetNumFaces(); ++f) + for (meshkernel::UInt f = 0; f < mesh2d.GetNumFaces(); ++f) { if (!facesInPolygon[f]) { continue; } - const auto& faceNodes = mesh2d->m_facesNodes[f]; + const auto& faceNodes = mesh2d.m_facesNodes[f]; if (count != 0) { - facePolygons.coordinates_x[count] = missing::doubleValue; - facePolygons.coordinates_y[count] = missing::doubleValue; + facePolygons.coordinates_x[count] = meshkernel::constants::missing::doubleValue; + facePolygons.coordinates_y[count] = meshkernel::constants::missing::doubleValue; count++; } for (meshkernel::UInt n = 0u; n < faceNodes.size(); ++n) { - const auto& currentNode = mesh2d->Node(faceNodes[n]); + const auto& currentNode = mesh2d.Node(faceNodes[n]); facePolygons.coordinates_x[count] = currentNode.x; facePolygons.coordinates_y[count] = currentNode.y; count++; } - const auto& currentNode = mesh2d->Node(faceNodes[0]); + const auto& currentNode = mesh2d.Node(faceNodes[0]); facePolygons.coordinates_x[count] = currentNode.x; facePolygons.coordinates_y[count] = currentNode.y; count++; diff --git a/libs/MeshKernelApi/src/BoundariesAsPolygonCache.cpp b/libs/MeshKernelApi/src/BoundariesAsPolygonCache.cpp new file mode 100644 index 000000000..900a25c64 --- /dev/null +++ b/libs/MeshKernelApi/src/BoundariesAsPolygonCache.cpp @@ -0,0 +1,52 @@ +//---- 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/BoundariesAsPolygonCache.hpp" + +meshkernelapi::BoundariesAsPolygonCache::BoundariesAsPolygonCache(const int lowerLeftN, + const int lowerLeftM, + const int upperRightN, + const int upperRightM, + const std::vector& boundaryPoints) + : CachedPointValues(boundaryPoints), + m_lowerLeftNValue(lowerLeftN), + m_lowerLeftMValue(lowerLeftM), + m_upperRightNValue(upperRightN), + m_upperRightMValue(upperRightM) +{ +} + +bool meshkernelapi::BoundariesAsPolygonCache::ValidOptions(const int lowerLeftN, + const int lowerLeftM, + const int upperRightN, + const int upperRightM) const +{ + return lowerLeftN == m_lowerLeftNValue && + lowerLeftM == m_lowerLeftMValue && + upperRightN == m_upperRightNValue && + upperRightM == m_upperRightMValue; +} diff --git a/libs/MeshKernelApi/src/CachedPointValues.cpp b/libs/MeshKernelApi/src/CachedPointValues.cpp new file mode 100644 index 000000000..186b71d74 --- /dev/null +++ b/libs/MeshKernelApi/src/CachedPointValues.cpp @@ -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. +// +//------------------------------------------------------------------------------ + +#include +#include + +#include "MeshKernelApi/CachedPointValues.hpp" + +meshkernelapi::CachedPointValues::CachedPointValues(const std::vector& coordinates) + : m_coordsX(coordinates.size()), + m_coordsY(coordinates.size()) +{ + for (size_t i = 0; i < coordinates.size(); ++i) + { + m_coordsX[i] = coordinates[i].x; + m_coordsY[i] = coordinates[i].y; + } +} + +void meshkernelapi::CachedPointValues::Copy(const GeometryList& geometry) const +{ + size_t valueCount = sizeof(double) * m_coordsX.size(); + + std::memcpy(geometry.coordinates_x, m_coordsX.data(), valueCount); + std::memcpy(geometry.coordinates_y, m_coordsY.data(), valueCount); +} + +void meshkernelapi::CachedPointValues::Reset(std::vector&& xValues, + std::vector&& yValues) +{ + m_coordsX = std::move(xValues); + m_coordsY = std::move(yValues); +} diff --git a/libs/MeshKernelApi/src/FacePolygonPropertyCache.cpp b/libs/MeshKernelApi/src/FacePolygonPropertyCache.cpp new file mode 100644 index 000000000..dda3ec840 --- /dev/null +++ b/libs/MeshKernelApi/src/FacePolygonPropertyCache.cpp @@ -0,0 +1,63 @@ +//---- 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 + +#include "MeshKernelApi/FacePolygonPropertyCache.hpp" +#include "MeshKernelApi/Utils.hpp" + +meshkernelapi::FacePolygonPropertyCache::FacePolygonPropertyCache(const int propertyValue, + const double minValue, + const double maxValue, + const meshkernel::Mesh2D& mesh, + const int validSize, + const std::vector& filterMask) + : m_propertyValue(propertyValue), + m_minimumValue(minValue), + m_maximumValue(maxValue) +{ + std::vector xCoordinates(validSize); + std::vector yCoordinates(validSize); + + GeometryList facePolygons; + + facePolygons.coordinates_x = xCoordinates.data(); + facePolygons.coordinates_y = yCoordinates.data(); + + FillFacePolygons(mesh, filterMask, facePolygons); + + CachedPointValues::Reset(std::move(xCoordinates), std::move(yCoordinates)); +} + +bool meshkernelapi::FacePolygonPropertyCache::ValidOptions(const int propertyValue, + const double minValue, + const double maxValue) const +{ + return propertyValue == m_propertyValue && + minValue == m_minimumValue && + maxValue == m_maximumValue; +} diff --git a/libs/MeshKernelApi/src/MeshKernel.cpp b/libs/MeshKernelApi/src/MeshKernel.cpp index 91fc7fe0a..ecc122a4f 100644 --- a/libs/MeshKernelApi/src/MeshKernel.cpp +++ b/libs/MeshKernelApi/src/MeshKernel.cpp @@ -935,51 +935,39 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } - if (!meshKernelState.contains(meshKernelId)) - { - throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); - } - if (!meshKernelState[meshKernelId].m_curvilinearGrid->IsValid()) { throw meshkernel::MeshKernelError("Invalid curvilinear grid"); } - if (!meshKernelState[meshKernelId].m_boundaryPolygonsCoordsCached) + if (boundaryPolygons.coordinates_x == nullptr || boundaryPolygons.coordinates_y == nullptr) { - throw meshkernel::MeshKernelError("Polygon data has not been cached"); + throw meshkernel::MeshKernelError("Boundary polygon array are null"); } - if (lowerLeftN != meshKernelState[meshKernelId].m_lowerLeftNValue || - lowerLeftM != meshKernelState[meshKernelId].m_lowerLeftMValue || - upperRightN != meshKernelState[meshKernelId].m_upperRightNValue || - upperRightM != meshKernelState[meshKernelId].m_upperRightMValue) + if (meshKernelState[meshKernelId].m_boundariesAsPolygonCache == nullptr) { - throw meshkernel::ConstraintError("Given polygon ranges are incompatible with the cached values"); + throw meshkernel::MeshKernelError("Polygon data has not been cached"); } - if (boundaryPolygons.coordinates_x == nullptr || boundaryPolygons.coordinates_y == nullptr) + if (!meshKernelState[meshKernelId].m_boundariesAsPolygonCache->ValidOptions(lowerLeftN, lowerLeftM, upperRightN, upperRightM)) { - throw meshkernel::ConstraintError("Boundary polygon array are null"); + meshKernelState[meshKernelId].m_boundariesAsPolygonCache.reset(); + throw meshkernel::ConstraintError("Given polygon ranges are incompatible with the cached values. Cached values will be deleted."); } - if (boundaryPolygons.num_coordinates != static_cast(meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.size())) + if (boundaryPolygons.num_coordinates != meshKernelState[meshKernelId].m_boundariesAsPolygonCache->Size()) { - throw meshkernel::ConstraintError("Incompatible boundary polygon size (user-size /= cached-size): {} /= {}", + meshKernelState[meshKernelId].m_boundariesAsPolygonCache.reset(); + throw meshkernel::ConstraintError("Incompatible boundary polygon size (user-size /= cached-size): {} /= {}. Cached values will be deleted.", boundaryPolygons.num_coordinates, - meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.size()); + meshKernelState[meshKernelId].m_boundariesAsPolygonCache->Size()); } - size_t valueCount = sizeof(double) * meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.size(); - - std::memcpy(boundaryPolygons.coordinates_x, meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.data(), valueCount); - std::memcpy(boundaryPolygons.coordinates_y, meshKernelState[meshKernelId].m_boundaryPolygonsCoordsY.data(), valueCount); - - // Now reset the vectors - meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.clear(); - meshKernelState[meshKernelId].m_boundaryPolygonsCoordsY.clear(); - meshKernelState[meshKernelId].m_boundaryPolygonsCoordsCached = false; + meshKernelState[meshKernelId].m_boundariesAsPolygonCache->Copy(boundaryPolygons); + meshKernelState[meshKernelId].m_boundariesAsPolygonCache.reset(); } + catch (...) { lastExitCode = HandleException(); @@ -1002,9 +990,11 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("Invalid curvilinear grid"); } - meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.clear(); - meshKernelState[meshKernelId].m_boundaryPolygonsCoordsY.clear(); - meshKernelState[meshKernelId].m_boundaryPolygonsCoordsCached = false; + if (meshKernelState[meshKernelId].m_boundariesAsPolygonCache != nullptr) + { + std::cout << "Polygon data has already been cached" << std::endl; + throw meshkernel::MeshKernelError("Polygon data has already been cached"); + } const auto lowerLeftNUnsigned = static_cast(lowerLeftN); const auto lowerLeftMUnsigned = static_cast(lowerLeftM); @@ -1019,21 +1009,7 @@ namespace meshkernelapi const auto boundaryPolygon = meshKernelState[meshKernelId].m_curvilinearGrid->ComputeBoundaryPolygons({minN, minM}, {maxN, maxM}); numberOfPolygonNodes = static_cast(boundaryPolygon.size()); - - meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.resize(boundaryPolygon.size()); - meshKernelState[meshKernelId].m_boundaryPolygonsCoordsY.resize(boundaryPolygon.size()); - meshKernelState[meshKernelId].m_boundaryPolygonsCoordsCached = true; - - meshKernelState[meshKernelId].m_lowerLeftNValue = lowerLeftN; - meshKernelState[meshKernelId].m_lowerLeftMValue = lowerLeftM; - meshKernelState[meshKernelId].m_upperRightNValue = upperRightN; - meshKernelState[meshKernelId].m_upperRightMValue = upperRightM; - - GeometryList boundaryPolygons; - boundaryPolygons.num_coordinates = boundaryPolygon.size(); - boundaryPolygons.coordinates_x = meshKernelState[meshKernelId].m_boundaryPolygonsCoordsX.data(); - boundaryPolygons.coordinates_y = meshKernelState[meshKernelId].m_boundaryPolygonsCoordsY.data(); - ConvertPointVectorToGeometryList(boundaryPolygon, boundaryPolygons); + meshKernelState[meshKernelId].m_boundariesAsPolygonCache = std::make_shared(lowerLeftN, lowerLeftM, upperRightN, upperRightM, boundaryPolygon); } catch (...) { @@ -1976,12 +1952,25 @@ namespace meshkernelapi { throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + + if (meshKernelState[meshKernelId].m_polygonRefinementCache == nullptr) + { + throw meshkernel::MeshKernelError("Polygon data has not been cached"); + } + auto const polygonVector = ConvertGeometryListToPointVector(polygonToRefine); + if (!meshKernelState[meshKernelId].m_polygonRefinementCache->ValidOptions(polygonVector, firstNodeIndex, secondNodeIndex, targetEdgeLength)) + { + meshKernelState[meshKernelId].m_polygonRefinementCache.reset(); + throw meshkernel::ConstraintError("Given refinement properties are incompatible with the cached values. Cached values will be deleted."); + } + const meshkernel::Polygons polygon(polygonVector, meshKernelState[meshKernelId].m_projection); auto const refinementResult = polygon.RefineFirstPolygon(firstNodeIndex, secondNodeIndex, targetEdgeLength); - ConvertPointVectorToGeometryList(refinementResult, refinedPolygon); + meshKernelState[meshKernelId].m_polygonRefinementCache->Copy(refinedPolygon); + meshKernelState[meshKernelId].m_polygonRefinementCache.reset(); } catch (...) { @@ -2000,14 +1989,21 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + if (meshKernelState[meshKernelId].m_polygonRefinementCache == nullptr) + { + throw meshkernel::MeshKernelError("Polygon data has not been cached"); + } + auto const polygonVector = ConvertGeometryListToPointVector(polygonToRefine); - const meshkernel::Polygons polygon(polygonVector, meshKernelState[meshKernelId].m_projection); - const auto firstNodeIndexUnsigned = static_cast(firstNodeIndex); - const auto secondNodeUnsigned = static_cast(secondNodeIndex); - const auto refinementResult = polygon.LinearRefinePolygon(0, firstNodeIndexUnsigned, secondNodeUnsigned); + if (!meshKernelState[meshKernelId].m_polygonRefinementCache->ValidOptions(polygonVector, firstNodeIndex, secondNodeIndex, meshkernel::constants::missing::doubleValue)) + { + meshKernelState[meshKernelId].m_polygonRefinementCache.reset(); + throw meshkernel::ConstraintError("Given refinement properties are incompatible with the cached values. Cached values will be deleted."); + } - ConvertPointVectorToGeometryList(refinementResult, refinedPolygon); + meshKernelState[meshKernelId].m_polygonRefinementCache->Copy(refinedPolygon); + meshKernelState[meshKernelId].m_polygonRefinementCache.reset(); } catch (...) { @@ -2031,12 +2027,21 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + if (meshKernelState[meshKernelId].m_polygonRefinementCache != nullptr) + { + meshKernelState[meshKernelId].m_polygonRefinementCache.reset(); + throw meshkernel::MeshKernelError("Polygon data has already been cached. Cached values will be delelted."); + } + auto const polygonVector = ConvertGeometryListToPointVector(polygonToRefine); const meshkernel::Polygons polygon(polygonVector, meshKernelState[meshKernelId].m_projection); const auto refinedPolygon = polygon.RefineFirstPolygon(firstIndex, secondIndex, distance); + // Cache refinedPolygon + meshKernelState[meshKernelId].m_polygonRefinementCache = std::make_shared(polygonVector, firstIndex, secondIndex, distance, refinedPolygon); + numberOfPolygonNodes = static_cast(refinedPolygon.size()); } catch (...) @@ -2056,13 +2061,23 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } - auto const polygonVector = ConvertGeometryListToPointVector(polygonToRefine); + if (meshKernelState[meshKernelId].m_polygonRefinementCache != nullptr) + { + meshKernelState[meshKernelId].m_polygonRefinementCache.reset(); + throw meshkernel::MeshKernelError("Polygon data has already been cached. Cached values will be delelted."); + } + auto const polygonVector = ConvertGeometryListToPointVector(polygonToRefine); const meshkernel::Polygons polygon(polygonVector, meshKernelState[meshKernelId].m_projection); const auto firstNodeIndexUnsigned = static_cast(firstNodeIndex); const auto secondNodeUnsigned = static_cast(secondNodeIndex); const auto refinementResult = polygon.LinearRefinePolygon(0, firstNodeIndexUnsigned, secondNodeUnsigned); + // Cache refinedPolygon + meshKernelState[meshKernelId].m_polygonRefinementCache = std::make_shared(polygonVector, firstNodeIndex, secondNodeIndex, + meshkernel::constants::missing::doubleValue, + refinementResult); + numberOfPolygonNodes = static_cast(refinementResult.size()); } catch (...) @@ -2469,7 +2484,7 @@ namespace meshkernelapi validFace[f] = true; } } - FillFacePolygons(meshKernelState[meshKernelId].m_mesh2d, validFace, facePolygons); + FillFacePolygons(*meshKernelState[meshKernelId].m_mesh2d, validFace, facePolygons); } catch (...) { @@ -2524,14 +2539,18 @@ namespace meshkernelapi { throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + if (meshKernelState[meshKernelId].m_mesh2d->GetNumNodes() <= 0) { throw meshkernel::ConstraintError("The 2d mesh contains no nodes."); } - meshKernelState[meshKernelId].m_facePolygonsCoordsX.clear(); - meshKernelState[meshKernelId].m_facePolygonsCoordsY.clear(); - meshKernelState[meshKernelId].m_facePolygonsCoordsCached = false; + if (meshKernelState[meshKernelId].m_facePropertyCache != nullptr) + { + meshKernelState[meshKernelId].m_facePropertyCache.reset(); + throw meshkernel::ConstraintError("Filtered data has already been cached. Cached values will be deleted."); + } + geometryListDimension = 0; const auto filterEnum = static_cast(propertyValue); @@ -2547,6 +2566,7 @@ namespace meshkernelapi { continue; } + const auto faceNumEdges = static_cast(meshKernelState[meshKernelId].m_mesh2d->m_facesNodes[f].size()); geometryListDimension += faceNumEdges + 2; } @@ -2554,19 +2574,10 @@ namespace meshkernelapi if (geometryListDimension > 0) { geometryListDimension -= 1; - meshKernelState[meshKernelId].m_facePolygonsCoordsX.resize(static_cast(geometryListDimension)); - meshKernelState[meshKernelId].m_facePolygonsCoordsY.resize(static_cast(geometryListDimension)); - GeometryList facePolygons; - - facePolygons.coordinates_x = meshKernelState[meshKernelId].m_facePolygonsCoordsX.data(); - facePolygons.coordinates_y = meshKernelState[meshKernelId].m_facePolygonsCoordsY.data(); - - meshKernelState[meshKernelId].m_facePolygonsPropertyValue = propertyValue; - meshKernelState[meshKernelId].m_facePolygonsMinValue = minValue; - meshKernelState[meshKernelId].m_facePolygonsMaxValue = maxValue; - - FillFacePolygons(meshKernelState[meshKernelId].m_mesh2d, filterMask, facePolygons); - meshKernelState[meshKernelId].m_facePolygonsCoordsCached = true; + meshKernelState[meshKernelId].m_facePropertyCache = std::make_shared(propertyValue, minValue, maxValue, + *meshKernelState[meshKernelId].m_mesh2d, + geometryListDimension, + filterMask); } } catch (...) @@ -2594,30 +2605,19 @@ namespace meshkernelapi throw meshkernel::ConstraintError("The 2d mesh contains no nodes."); } - if (!meshKernelState[meshKernelId].m_facePolygonsCoordsCached) + if (meshKernelState[meshKernelId].m_facePropertyCache == nullptr) { - throw meshkernel::ConstraintError("Filtered data has not been calculated"); + throw meshkernel::ConstraintError("Filtered data has not been cached"); } - if (propertyValue != meshKernelState[meshKernelId].m_facePolygonsPropertyValue || - minValue != meshKernelState[meshKernelId].m_facePolygonsMinValue || - maxValue != meshKernelState[meshKernelId].m_facePolygonsMaxValue) + if (!meshKernelState[meshKernelId].m_facePropertyCache->ValidOptions(propertyValue, minValue, maxValue)) { - throw meshkernel::ConstraintError("Given filter properties are incompatible with the cached values: property value: {} <=> {}, min value: {} <=> {}, max value {} <=> {}", - propertyValue, meshKernelState[meshKernelId].m_facePolygonsPropertyValue, - minValue, meshKernelState[meshKernelId].m_facePolygonsMinValue, - maxValue, meshKernelState[meshKernelId].m_facePolygonsMaxValue); + meshKernelState[meshKernelId].m_facePropertyCache.reset(); + throw meshkernel::ConstraintError("Given filter properties are incompatible with the cached values. Cached values will be deleted."); } - size_t valueCount = sizeof(double) * meshKernelState[meshKernelId].m_facePolygonsCoordsX.size(); - - std::memcpy(facePolygons.coordinates_x, meshKernelState[meshKernelId].m_facePolygonsCoordsX.data(), valueCount); - std::memcpy(facePolygons.coordinates_y, meshKernelState[meshKernelId].m_facePolygonsCoordsY.data(), valueCount); - - // Now reset the vectors - meshKernelState[meshKernelId].m_facePolygonsCoordsX.clear(); - meshKernelState[meshKernelId].m_facePolygonsCoordsY.clear(); - meshKernelState[meshKernelId].m_facePolygonsCoordsCached = false; + meshKernelState[meshKernelId].m_facePropertyCache->Copy(facePolygons); + meshKernelState[meshKernelId].m_facePropertyCache.reset(); } catch (...) { diff --git a/libs/MeshKernelApi/src/PolygonRefinementCache.cpp b/libs/MeshKernelApi/src/PolygonRefinementCache.cpp new file mode 100644 index 000000000..e61015a6e --- /dev/null +++ b/libs/MeshKernelApi/src/PolygonRefinementCache.cpp @@ -0,0 +1,24 @@ +#include "MeshKernelApi/PolygonRefinementCache.hpp" + +meshkernelapi::PolygonRefinementCache::PolygonRefinementCache(const std::vector& polyPoints, + const int firstIndex, + const int secondIndex, + const double edgeLength, + const std::vector& refinedPoints) + : CachedPointValues(refinedPoints), + m_polygonPoints(polyPoints), + m_firstNodeIndex(firstIndex), + m_secondNodeIndex(secondIndex), + m_targetEdgeLength(edgeLength) {} + +bool meshkernelapi::PolygonRefinementCache::ValidOptions(const std::vector& polyPoints, + const int firstIndex, + const int secondIndex, + const double edgeLength) const +{ + return firstIndex == m_firstNodeIndex && + secondIndex == m_secondNodeIndex && + edgeLength == m_targetEdgeLength && + polyPoints.size() == m_polygonPoints.size() && + std::equal(polyPoints.begin(), polyPoints.end(), m_polygonPoints.begin()); +} diff --git a/libs/MeshKernelApi/tests/src/Mesh2DRefinmentTests.cpp b/libs/MeshKernelApi/tests/src/Mesh2DRefinmentTests.cpp index c8c59fec1..6f73e5fe3 100644 --- a/libs/MeshKernelApi/tests/src/Mesh2DRefinmentTests.cpp +++ b/libs/MeshKernelApi/tests/src/Mesh2DRefinmentTests.cpp @@ -53,7 +53,7 @@ TEST_F(CartesianApiTestFixture, RefineAPolygonThroughApi) geometryListOut.coordinates_x = xCoordinatesOut.data(); geometryListOut.coordinates_y = yCoordinatesOut.data(); geometryListOut.values = valuesOut.data(); - errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, false, 0, 2, geometryListOut); + errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, 0, 2, 40, geometryListOut); ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); // Assert From e3c3c119075d3455766d90531820d1cc68483a27 Mon Sep 17 00:00:00 2001 From: BillSenior Date: Thu, 17 Oct 2024 16:38:06 +0200 Subject: [PATCH 04/14] GRIDEDIT-1336 Fixed clang formatting, doxygen spelling warnings and window build --- .../MeshKernelApi/BoundariesAsPolygonCache.hpp | 2 +- .../MeshKernelApi/FacePolygonPropertyCache.hpp | 4 ++-- libs/MeshKernelApi/include/MeshKernelApi/State.hpp | 1 - .../MeshKernelApi/tests/src/CurvilinearGridTests.cpp | 12 +++++++++++- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/libs/MeshKernelApi/include/MeshKernelApi/BoundariesAsPolygonCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/BoundariesAsPolygonCache.hpp index 9e0bfc44d..8020babd0 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/BoundariesAsPolygonCache.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/BoundariesAsPolygonCache.hpp @@ -39,7 +39,7 @@ namespace meshkernelapi { - /// @brief Boundary points + /// @brief Cache boundary polygon points class BoundariesAsPolygonCache : public CachedPointValues { public: diff --git a/libs/MeshKernelApi/include/MeshKernelApi/FacePolygonPropertyCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/FacePolygonPropertyCache.hpp index d05a44408..699126482 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/FacePolygonPropertyCache.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/FacePolygonPropertyCache.hpp @@ -35,12 +35,12 @@ namespace meshkernelapi { - /// @brief + /// @brief Cache node values of faces class FacePolygonPropertyCache : public CachedPointValues { public: - /// @brief Constuctor + /// @brief Constructor FacePolygonPropertyCache(const int propertyValue, const double minValue, const double maxValue, diff --git a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp index d8aa22bb8..315db357d 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp @@ -43,7 +43,6 @@ #include "MeshKernelApi/FacePolygonPropertyCache.hpp" #include "MeshKernelApi/PolygonRefinementCache.hpp" - namespace meshkernelapi { diff --git a/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp b/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp index 78332c385..0677bebfc 100644 --- a/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp +++ b/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp @@ -1515,9 +1515,11 @@ TEST(CurvilineartGrid, GetLocationIndex_OnACurvilinearGrid_GetLocationIndexFailu errorCode = meshkernelapi::mkernel_curvilinear_compute_rectangular_grid(meshKernelId, makeGridParameters); ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); - int numberOfPolygonNodes; + // Initialise with a temporary size value + int numberOfPolygonNodes = 1000; meshkernelapi::GeometryList boundaryPolygon; + // Set coordinate arrays with temporary size for first test std::vector coordinates_x(numberOfPolygonNodes); std::vector coordinates_y(numberOfPolygonNodes); @@ -1533,6 +1535,14 @@ TEST(CurvilineartGrid, GetLocationIndex_OnACurvilinearGrid_GetLocationIndexFailu errorCode = meshkernelapi::mkernel_curvilinear_count_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, numberOfPolygonNodes); ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + // Resize coordinate array with correct size + coordinates_x.resize(numberOfPolygonNodes); + coordinates_y.resize(numberOfPolygonNodes); + + boundaryPolygon.coordinates_x = coordinates_x.data(); + boundaryPolygon.coordinates_y = coordinates_y.data(); + boundaryPolygon.num_coordinates = numberOfPolygonNodes; + // Check working version boundaryPolygon.num_coordinates = numberOfPolygonNodes; errorCode = mkernel_curvilinear_get_boundaries_as_polygons(meshKernelId, lowerLeftN, lowerLeftM, upperRightN, upperRightM, boundaryPolygon); From 79db071c742bcdf70b632fd0a25da13d2ea3ee6f Mon Sep 17 00:00:00 2001 From: BillSenior Date: Thu, 17 Oct 2024 17:31:42 +0200 Subject: [PATCH 05/14] GRIDEDIT-1336 Added unit tests for failing cases --- .../tests/src/Mesh2DRefinmentTests.cpp | 122 ++++++++++++++++++ 1 file changed, 122 insertions(+) diff --git a/libs/MeshKernelApi/tests/src/Mesh2DRefinmentTests.cpp b/libs/MeshKernelApi/tests/src/Mesh2DRefinmentTests.cpp index 6f73e5fe3..8cb748984 100644 --- a/libs/MeshKernelApi/tests/src/Mesh2DRefinmentTests.cpp +++ b/libs/MeshKernelApi/tests/src/Mesh2DRefinmentTests.cpp @@ -62,6 +62,73 @@ TEST_F(CartesianApiTestFixture, RefineAPolygonThroughApi) ASSERT_NEAR(92.626556, geometryListOut.coordinates_y[0], tolerance); } +TEST_F(CartesianApiTestFixture, RefineAPolygonThroughApiFailures) +{ + // Prepare + MakeMesh(); + auto const meshKernelId = GetMeshKernelId(); + + meshkernelapi::GeometryList geometryListIn; + geometryListIn.geometry_separator = meshkernel::constants::missing::doubleValue; + + std::vector xCoordinatesIn{76.251099, 498.503723, 505.253784, 76.251099}; + std::vector yCoordinatesIn{92.626556, 91.126541, 490.130554, 92.626556}; + std::vector valuesIn{0.0, 0.0, 0.0, 0.0}; + + geometryListIn.coordinates_x = xCoordinatesIn.data(); + geometryListIn.coordinates_y = yCoordinatesIn.data(); + geometryListIn.values = valuesIn.data(); + geometryListIn.num_coordinates = static_cast(xCoordinatesIn.size()); + + // Execute + meshkernelapi::GeometryList geometryListOut; + // Should fail due to the values not yet being cached. + auto errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, 0, 2, 40, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + int numberOfpolygonNodes; + // cache the values + errorCode = mkernel_polygon_count_refine(meshKernelId, geometryListIn, 0, 2, 40, numberOfpolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + geometryListOut.num_coordinates = numberOfpolygonNodes; + geometryListOut.geometry_separator = meshkernel::constants::missing::doubleValue; + std::vector xCoordinatesOut(numberOfpolygonNodes); + std::vector yCoordinatesOut(numberOfpolygonNodes); + std::vector valuesOut(numberOfpolygonNodes); + geometryListOut.coordinates_x = xCoordinatesOut.data(); + geometryListOut.coordinates_y = yCoordinatesOut.data(); + geometryListOut.values = valuesOut.data(); + errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, 0, 2, 40, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Should fail due to the values have been deleted in the last call + errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, 0, 2, 40, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + // re-cache the values + errorCode = mkernel_polygon_count_refine(meshKernelId, geometryListIn, 0, 2, 40, numberOfpolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Should fail due to different parameters + errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, 1, 2, 40, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // re-cache the values + errorCode = mkernel_polygon_count_refine(meshKernelId, geometryListIn, 0, 2, 40, numberOfpolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + // Should fail due to different parameters + errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, 0, 3, 40, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // re-cache the values + errorCode = mkernel_polygon_count_refine(meshKernelId, geometryListIn, 0, 2, 40, numberOfpolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + // Should fail due to different parameters + errorCode = mkernel_polygon_refine(meshKernelId, geometryListIn, 0, 2, 41, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); +} + TEST_F(CartesianApiTestFixture, LinearRefineAPolygonThroughApi) { // Prepare @@ -106,6 +173,61 @@ TEST_F(CartesianApiTestFixture, LinearRefineAPolygonThroughApi) ASSERT_NEAR(8.281492376, geometryListOut.coordinates_y[4], tolerance); } +TEST_F(CartesianApiTestFixture, LinearRefineAPolygonThroughApiFailures) +{ + // Prepare + MakeMesh(); + auto const meshKernelId = GetMeshKernelId(); + + meshkernelapi::GeometryList geometryListIn; + geometryListIn.geometry_separator = meshkernel::constants::missing::doubleValue; + + std::vector xCoordinatesIn{0.0, 1.0, 3.0, 11.0, 11.0, 0.0, 0.0}; + std::vector yCoordinatesIn{10.0, 10.0, 10.0, 10.0, 0.0, 0.0, 10.0}; + + geometryListIn.coordinates_x = xCoordinatesIn.data(); + geometryListIn.coordinates_y = yCoordinatesIn.data(); + geometryListIn.num_coordinates = static_cast(xCoordinatesIn.size()); + + // Execute + meshkernelapi::GeometryList geometryListOut; + auto errorCode = mkernel_polygon_linear_refine(meshKernelId, geometryListIn, 1, 4, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + int expectedNumberOfpolygonNodes; + // cache the values + errorCode = mkernel_polygon_count_linear_refine(meshKernelId, geometryListIn, 1, 4, expectedNumberOfpolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + geometryListOut.num_coordinates = expectedNumberOfpolygonNodes; + geometryListOut.geometry_separator = meshkernel::constants::missing::doubleValue; + std::vector xCoordinatesOut(expectedNumberOfpolygonNodes); + std::vector yCoordinatesOut(expectedNumberOfpolygonNodes); + geometryListOut.coordinates_x = xCoordinatesOut.data(); + geometryListOut.coordinates_y = yCoordinatesOut.data(); + + errorCode = mkernel_polygon_linear_refine(meshKernelId, geometryListIn, 1, 4, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Should fail due to the values have been deleted in the last call + errorCode = mkernel_polygon_linear_refine(meshKernelId, geometryListIn, 1, 4, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + // re-cache the values + errorCode = mkernel_polygon_count_linear_refine(meshKernelId, geometryListIn, 1, 4, expectedNumberOfpolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + // Should fail due to different parameters + errorCode = mkernel_polygon_linear_refine(meshKernelId, geometryListIn, 2, 4, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // re-cache the values + errorCode = mkernel_polygon_count_linear_refine(meshKernelId, geometryListIn, 1, 4, expectedNumberOfpolygonNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + // Should fail due to different parameters + errorCode = mkernel_polygon_linear_refine(meshKernelId, geometryListIn, 1, 5, geometryListOut); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); +} + TEST_F(CartesianApiTestFixture, RefineBasedOnSamplesWaveCourant_OnAUniformMesh_shouldRefineMesh) { // Prepare From d6b33cb3a99a34bf0795ecd1048a755e52eef468 Mon Sep 17 00:00:00 2001 From: BillSenior Date: Thu, 17 Oct 2024 17:54:50 +0200 Subject: [PATCH 06/14] GRIDEDIT-1336 Added some comments --- libs/MeshKernelApi/src/MeshKernel.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/libs/MeshKernelApi/src/MeshKernel.cpp b/libs/MeshKernelApi/src/MeshKernel.cpp index ecc122a4f..4e2aafab2 100644 --- a/libs/MeshKernelApi/src/MeshKernel.cpp +++ b/libs/MeshKernelApi/src/MeshKernel.cpp @@ -964,7 +964,9 @@ namespace meshkernelapi meshKernelState[meshKernelId].m_boundariesAsPolygonCache->Size()); } + // Retrieve cached values meshKernelState[meshKernelId].m_boundariesAsPolygonCache->Copy(boundaryPolygons); + // Clear the cache now that the values have been retrieved meshKernelState[meshKernelId].m_boundariesAsPolygonCache.reset(); } @@ -1969,7 +1971,9 @@ namespace meshkernelapi const meshkernel::Polygons polygon(polygonVector, meshKernelState[meshKernelId].m_projection); auto const refinementResult = polygon.RefineFirstPolygon(firstNodeIndex, secondNodeIndex, targetEdgeLength); + // Retrieve cached values meshKernelState[meshKernelId].m_polygonRefinementCache->Copy(refinedPolygon); + // Clear the cache now that the values have been retrieved meshKernelState[meshKernelId].m_polygonRefinementCache.reset(); } catch (...) @@ -2002,7 +2006,9 @@ namespace meshkernelapi throw meshkernel::ConstraintError("Given refinement properties are incompatible with the cached values. Cached values will be deleted."); } + // Retrieve cached values meshKernelState[meshKernelId].m_polygonRefinementCache->Copy(refinedPolygon); + // Clear the cache now that the values have been retrieved meshKernelState[meshKernelId].m_polygonRefinementCache.reset(); } catch (...) @@ -2616,7 +2622,9 @@ namespace meshkernelapi throw meshkernel::ConstraintError("Given filter properties are incompatible with the cached values. Cached values will be deleted."); } + // Retrieve cached values meshKernelState[meshKernelId].m_facePropertyCache->Copy(facePolygons); + // Clear the cache now that the values have been retrieved meshKernelState[meshKernelId].m_facePropertyCache.reset(); } catch (...) From 5fa166766d4da6f4c9b67a2d32fc5c88236f2e52 Mon Sep 17 00:00:00 2001 From: BillSenior Date: Mon, 21 Oct 2024 12:56:19 +0200 Subject: [PATCH 07/14] GRIDEDIT-1336 Cached values for mesh point in polygon --- libs/MeshKernelApi/CMakeLists.txt | 2 + .../MeshKernelApi/NodeInPolygonCache.hpp | 69 ++++++++++++++++++ .../include/MeshKernelApi/State.hpp | 2 + libs/MeshKernelApi/src/MeshKernel.cpp | 63 ++++++++++------- libs/MeshKernelApi/src/NodeInPolygonCache.cpp | 70 +++++++++++++++++++ libs/MeshKernelApi/tests/src/ApiTest.cpp | 57 +++++++++++++++ .../tests/src/CurvilinearGridTests.cpp | 2 +- 7 files changed, 238 insertions(+), 27 deletions(-) create mode 100644 libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp create mode 100644 libs/MeshKernelApi/src/NodeInPolygonCache.cpp diff --git a/libs/MeshKernelApi/CMakeLists.txt b/libs/MeshKernelApi/CMakeLists.txt index 0560e930b..041a7024e 100644 --- a/libs/MeshKernelApi/CMakeLists.txt +++ b/libs/MeshKernelApi/CMakeLists.txt @@ -26,6 +26,7 @@ set(SRC_LIST ${SRC_DIR}/FacePolygonPropertyCache.cpp ${SRC_DIR}/MKStateUndoAction.cpp ${SRC_DIR}/MeshKernel.cpp + ${SRC_DIR}/NodeInPolygonCache.cpp ${SRC_DIR}/PolygonRefinementCache.cpp ) @@ -44,6 +45,7 @@ set( ${DOMAIN_INC_DIR}/Mesh1D.hpp ${DOMAIN_INC_DIR}/Mesh2D.hpp ${DOMAIN_INC_DIR}/MeshKernel.hpp + ${DOMAIN_INC_DIR}/NodeInPolygonCache.hpp ${DOMAIN_INC_DIR}/PolygonRefinementCache.hpp ${DOMAIN_INC_DIR}/State.hpp ${DOMAIN_INC_DIR}/Utils.hpp diff --git a/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp new file mode 100644 index 000000000..f23ec95f4 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp @@ -0,0 +1,69 @@ +//---- 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/GeometryList.hpp" + +namespace meshkernelapi +{ + + class NodeInPolygonCache + { + public: + NodeInPolygonCache(const std::vector& nodeMask, + const std::vector& polygonPoints, + const int inside); + + bool ValidOptions(const std::vector& polygonPoints, const int inside) const; + + int Size() const; + + void Copy(int* selectedNodes) const; + + private: + /// &brief Points making up the polygon + std::vector m_polygonPoints; + + /// &brief Indicates if the points are inside or outside of the polygon + int m_inside = -1; + + /// &brief Indices of nodes in the polygon + std::vector m_nodeIndices; + }; + +} // namespace meshkernelapi + +inline int meshkernelapi::NodeInPolygonCache::Size() const +{ + return static_cast(m_nodeIndices.size()); +} diff --git a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp index 315db357d..ba44c1b8d 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp @@ -41,6 +41,7 @@ #include "MeshKernelApi/BoundariesAsPolygonCache.hpp" #include "MeshKernelApi/CachedPointValues.hpp" #include "MeshKernelApi/FacePolygonPropertyCache.hpp" +#include "MeshKernelApi/NodeInPolygonCache.hpp" #include "MeshKernelApi/PolygonRefinementCache.hpp" namespace meshkernelapi @@ -83,6 +84,7 @@ namespace meshkernelapi std::shared_ptr m_facePropertyCache; ///< Cache for 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 }; } // namespace meshkernelapi diff --git a/libs/MeshKernelApi/src/MeshKernel.cpp b/libs/MeshKernelApi/src/MeshKernel.cpp index 4e2aafab2..ffd564407 100644 --- a/libs/MeshKernelApi/src/MeshKernel.cpp +++ b/libs/MeshKernelApi/src/MeshKernel.cpp @@ -89,10 +89,15 @@ #include #include -#include -#include -#include -#include +#include "MeshKernelApi/BoundariesAsPolygonCache.hpp" +#include "MeshKernelApi/CachedPointValues.hpp" +#include "MeshKernelApi/FacePolygonPropertyCache.hpp" +#include "MeshKernelApi/MKStateUndoAction.hpp" +#include "MeshKernelApi/MeshKernel.hpp" +#include "MeshKernelApi/NodeInPolygonCache.hpp" +#include "MeshKernelApi/PolygonRefinementCache.hpp" +#include "MeshKernelApi/State.hpp" +#include "MeshKernelApi/Utils.hpp" #include @@ -994,8 +999,8 @@ namespace meshkernelapi if (meshKernelState[meshKernelId].m_boundariesAsPolygonCache != nullptr) { - std::cout << "Polygon data has already been cached" << std::endl; - throw meshkernel::MeshKernelError("Polygon data has already been cached"); + meshKernelState[meshKernelId].m_boundariesAsPolygonCache.reset(); + throw meshkernel::MeshKernelError("Polygon data has already been cached, deleting cached data"); } const auto lowerLeftNUnsigned = static_cast(lowerLeftN); @@ -2174,22 +2179,27 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } - auto const polygonVector = ConvertGeometryListToPointVector(geometryListIn); + if (meshKernelState[meshKernelId].m_nodeInPolygonCache == nullptr) + { + throw meshkernel::MeshKernelError("Node in polygon data has not been cached"); + } - const meshkernel::Polygons polygon(polygonVector, meshKernelState[meshKernelId].m_mesh2d->m_projection); + auto const polygonVector = ConvertGeometryListToPointVector(geometryListIn); - const bool selectInside = inside == 1 ? true : false; - const auto nodeMask = meshKernelState[meshKernelId].m_mesh2d->NodeMaskFromPolygon(polygon, selectInside); + if (!meshKernelState[meshKernelId].m_nodeInPolygonCache->ValidOptions(polygonVector, inside)) + { + meshKernelState[meshKernelId].m_nodeInPolygonCache.reset(); + throw meshkernel::ConstraintError("Given polygon data and inside flag are incompatible with the cached values. Cached values will be deleted."); + } - int index = 0; - for (size_t i = 0; i < meshKernelState[meshKernelId].m_mesh2d->GetNumNodes(); ++i) + if (selectedNodes == nullptr) { - if (nodeMask[i] > 0) - { - selectedNodes[index] = static_cast(i); - index++; - } + meshKernelState[meshKernelId].m_nodeInPolygonCache.reset(); + throw meshkernel::MeshKernelError("Selected node array is null"); } + + meshKernelState[meshKernelId].m_nodeInPolygonCache->Copy(selectedNodes); + meshKernelState[meshKernelId].m_nodeInPolygonCache.reset(); } catch (...) { @@ -2210,21 +2220,22 @@ namespace meshkernelapi { throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + + if (meshKernelState[meshKernelId].m_nodeInPolygonCache != nullptr) + { + meshKernelState[meshKernelId].m_nodeInPolygonCache.reset(); + throw meshkernel::MeshKernelError("Node in polygon data has already been cached, deleting cached data"); + } + auto const polygonVector = ConvertGeometryListToPointVector(geometryListIn); const meshkernel::Polygons polygon(polygonVector, meshKernelState[meshKernelId].m_mesh2d->m_projection); - const bool selectInside = inside == 1 ? true : false; + const bool selectInside = inside == 1; const auto nodeMask = meshKernelState[meshKernelId].m_mesh2d->NodeMaskFromPolygon(polygon, selectInside); - numberOfMeshNodes = 0; - for (size_t i = 0; i < meshKernelState[meshKernelId].m_mesh2d->GetNumNodes(); ++i) - { - if (nodeMask[i] > 0) - { - numberOfMeshNodes++; - } - } + meshKernelState[meshKernelId].m_nodeInPolygonCache = std::make_shared(nodeMask, polygonVector, inside); + numberOfMeshNodes = meshKernelState[meshKernelId].m_nodeInPolygonCache->Size(); } catch (...) { diff --git a/libs/MeshKernelApi/src/NodeInPolygonCache.cpp b/libs/MeshKernelApi/src/NodeInPolygonCache.cpp new file mode 100644 index 000000000..d8e74d70d --- /dev/null +++ b/libs/MeshKernelApi/src/NodeInPolygonCache.cpp @@ -0,0 +1,70 @@ +//---- 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 +#include +#include + +#include "MeshKernel/Exceptions.hpp" + +#include "MeshKernelApi/NodeInPolygonCache.hpp" + +meshkernelapi::NodeInPolygonCache::NodeInPolygonCache(const std::vector& nodeMask, + const std::vector& polygonPoints, + const int inside) + : m_polygonPoints(polygonPoints), m_inside(inside) +{ + + m_nodeIndices.reserve(m_polygonPoints.size()); + + for (size_t i = 0; i < nodeMask.size(); ++i) + { + if (nodeMask[i] > 0) + { + m_nodeIndices.push_back(static_cast(i)); + } + } +} + +bool meshkernelapi::NodeInPolygonCache::ValidOptions(const std::vector& polygonPoints, const int inside) const +{ + return inside == m_inside && + polygonPoints.size() == m_polygonPoints.size() && + std::equal(polygonPoints.begin(), polygonPoints.end(), m_polygonPoints.begin()); +} + +void meshkernelapi::NodeInPolygonCache::Copy(int* selectedNodes) const +{ + if (selectedNodes == nullptr) + { + throw meshkernel::MeshKernelError("Selected nodes array is null."); + } + + size_t valueCount = sizeof(int) * m_nodeIndices.size(); + + std::memcpy(selectedNodes, m_nodeIndices.data(), valueCount); +} diff --git a/libs/MeshKernelApi/tests/src/ApiTest.cpp b/libs/MeshKernelApi/tests/src/ApiTest.cpp index 6166d10ca..c24e7da03 100644 --- a/libs/MeshKernelApi/tests/src/ApiTest.cpp +++ b/libs/MeshKernelApi/tests/src/ApiTest.cpp @@ -1485,8 +1485,18 @@ TEST_F(CartesianApiTestFixture, GetNodesInPolygonMesh2D_OnMesh2D_ShouldGetAllNod auto errorCode = mkernel_mesh2d_get_dimensions(meshKernelId, mesh2d); ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + int numberOfNodes = -1; + + errorCode = mkernel_mesh2d_count_nodes_in_polygons(meshKernelId, + geometryListIn, + 1, + numberOfNodes); + + ASSERT_EQ(numberOfNodes, mesh2d.num_nodes); + // Execute std::vector selectedNodes(mesh2d.num_nodes); + errorCode = mkernel_mesh2d_get_nodes_in_polygons(meshKernelId, geometryListIn, 1, @@ -1522,6 +1532,53 @@ TEST_F(CartesianApiTestFixture, CountNodesInPolygonMesh2D_OnMesh2D_ShouldCountAl ASSERT_EQ(12, numNodes); } +TEST_F(CartesianApiTestFixture, GetNodesInPolygonMesh2D_OnMesh2D_NodeInPolygonFailures) +{ + // Prepare + MakeMesh(); + auto const meshKernelId = GetMeshKernelId(); + + // By using an empty list, all nodes will be selected + const meshkernelapi::GeometryList geometryListIn{}; + + meshkernelapi::Mesh2D mesh2d{}; + auto errorCode = mkernel_mesh2d_get_dimensions(meshKernelId, mesh2d); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + int numberOfNodes = -1; + // Execute + std::vector selectedNodes(mesh2d.num_nodes); + + // No daata has been cached. + errorCode = mkernel_mesh2d_get_nodes_in_polygons(meshKernelId, geometryListIn, 0, selectedNodes.data()); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + errorCode = mkernel_mesh2d_count_nodes_in_polygons(meshKernelId, geometryListIn, 1, numberOfNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + ASSERT_EQ(numberOfNodes, mesh2d.num_nodes); + + // Values have been cached already + errorCode = mkernel_mesh2d_count_nodes_in_polygons(meshKernelId, geometryListIn, 1, numberOfNodes); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + // Re-cache data + errorCode = mkernel_mesh2d_count_nodes_in_polygons(meshKernelId, geometryListIn, 1, numberOfNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Incorrect parameters, should be the same as the call to count (which does the cahcing) + errorCode = mkernel_mesh2d_get_nodes_in_polygons(meshKernelId, geometryListIn, 0, selectedNodes.data()); + ASSERT_EQ(meshkernel::ExitCode::ConstraintErrorCode, errorCode); + + // Re-cache data + errorCode = mkernel_mesh2d_count_nodes_in_polygons(meshKernelId, geometryListIn, 1, numberOfNodes); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Incorrect parameters, should be the same as the call to count (which does the cahcing) + int* nullArray = nullptr; + errorCode = mkernel_mesh2d_get_nodes_in_polygons(meshKernelId, geometryListIn, 1, nullArray); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); +} + TEST_F(CartesianApiTestFixture, InsertNodeAndEdge_OnMesh2D_ShouldInsertNodeAndEdge) { // Prepare diff --git a/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp b/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp index 0677bebfc..349a89b83 100644 --- a/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp +++ b/libs/MeshKernelApi/tests/src/CurvilinearGridTests.cpp @@ -1490,7 +1490,7 @@ TEST_P(CurvilineartBoundariesAsPolygonsTests, GetLocationIndex_OnACurvilinearGri ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); } -TEST(CurvilineartGrid, GetLocationIndex_OnACurvilinearGrid_GetLocationIndexFailures) +TEST(CurvilinearGrid, GetLocationIndex_OnACurvilinearGrid_GetLocationIndexFailures) { const int lowerLeftN = 1; const int lowerLeftM = 1; From f758bade22054886ec5025eecb5b4f64b910b1b1 Mon Sep 17 00:00:00 2001 From: BillSenior Date: Mon, 21 Oct 2024 12:59:22 +0200 Subject: [PATCH 08/14] GRIDEDIT-1336 Added doxygen comments --- .../include/MeshKernelApi/NodeInPolygonCache.hpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp index f23ec95f4..9e8843c3b 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp @@ -37,17 +37,22 @@ namespace meshkernelapi { + /// @brief Cache node indices contained in a polygon class NodeInPolygonCache { public: + /// @brief Constructor NodeInPolygonCache(const std::vector& nodeMask, const std::vector& polygonPoints, const int inside); + /// @brief Determine if current options match those used to construct the object bool ValidOptions(const std::vector& polygonPoints, const int inside) const; + /// @brief Get the number of values being cached. int Size() const; + /// @brief Copy cached values to array void Copy(int* selectedNodes) const; private: From 4275b3b042d8274aade68cbd69aa5ff48860e5c5 Mon Sep 17 00:00:00 2001 From: BillSenior Date: Mon, 21 Oct 2024 12:59:48 +0200 Subject: [PATCH 09/14] GRIDEDIT-1336 Corrected doxygen comments --- .../include/MeshKernelApi/NodeInPolygonCache.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp index 9e8843c3b..bb4bfa185 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp @@ -56,13 +56,13 @@ namespace meshkernelapi void Copy(int* selectedNodes) const; private: - /// &brief Points making up the polygon + /// @brief Points making up the polygon std::vector m_polygonPoints; - /// &brief Indicates if the points are inside or outside of the polygon + /// @brief Indicates if the points are inside or outside of the polygon int m_inside = -1; - /// &brief Indices of nodes in the polygon + /// @brief Indices of nodes in the polygon std::vector m_nodeIndices; }; From 9b61223f81de8cfcecd38d30bc860447ff2dd5ad Mon Sep 17 00:00:00 2001 From: BillSenior Date: Mon, 21 Oct 2024 14:54:33 +0200 Subject: [PATCH 10/14] GRIDEDIT-1336 Added cache for small flow edges --- libs/MeshKernelApi/CMakeLists.txt | 2 + .../SmallFlowEdgeCentreCache.hpp | 57 ++++++++++++++++++ .../include/MeshKernelApi/State.hpp | 2 + libs/MeshKernelApi/src/MeshKernel.cpp | 26 ++++++-- .../src/SmallFlowEdgeCentreCache.cpp | 37 ++++++++++++ libs/MeshKernelApi/tests/src/ApiTest.cpp | 59 +++++++++++++++++++ 6 files changed, 179 insertions(+), 4 deletions(-) create mode 100644 libs/MeshKernelApi/include/MeshKernelApi/SmallFlowEdgeCentreCache.hpp create mode 100644 libs/MeshKernelApi/src/SmallFlowEdgeCentreCache.cpp 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 From 9ff0f3f76b934271ccaaf2fa34b99d9be72cfb29 Mon Sep 17 00:00:00 2001 From: BillSenior Date: Mon, 21 Oct 2024 15:34:12 +0200 Subject: [PATCH 11/14] GRIDEDIT-1336 Added cache for hanging edges --- libs/MeshKernelApi/CMakeLists.txt | 4 +++ .../MeshKernelApi/NodeInPolygonCache.hpp | 17 ++--------- .../include/MeshKernelApi/State.hpp | 2 ++ libs/MeshKernelApi/src/MeshKernel.cpp | 19 +++++++++--- libs/MeshKernelApi/src/NodeInPolygonCache.cpp | 19 ++++-------- libs/MeshKernelApi/tests/src/ApiTest.cpp | 30 +++++++++++++++++++ 6 files changed, 58 insertions(+), 33 deletions(-) diff --git a/libs/MeshKernelApi/CMakeLists.txt b/libs/MeshKernelApi/CMakeLists.txt index c23cab058..78374792f 100644 --- a/libs/MeshKernelApi/CMakeLists.txt +++ b/libs/MeshKernelApi/CMakeLists.txt @@ -22,8 +22,10 @@ set(VERSION_INC_DIR ${CMAKE_SOURCE_DIR}/tools) # list of target sources set(SRC_LIST ${SRC_DIR}/BoundariesAsPolygonCache.cpp + ${SRC_DIR}/CachedIntegerValues.cpp ${SRC_DIR}/CachedPointValues.cpp ${SRC_DIR}/FacePolygonPropertyCache.cpp + ${SRC_DIR}/HangingEdgeCache.cpp ${SRC_DIR}/MKStateUndoAction.cpp ${SRC_DIR}/MeshKernel.cpp ${SRC_DIR}/NodeInPolygonCache.cpp @@ -36,12 +38,14 @@ set( INC_LIST ${DOMAIN_INC_DIR}/BoundariesAsPolygonCache.hpp ${DOMAIN_INC_DIR}/BoundingBox.hpp + ${DOMAIN_INC_DIR}/CachedIntegerValues.hpp ${DOMAIN_INC_DIR}/CachedPointValues.hpp ${DOMAIN_INC_DIR}/Contacts.hpp ${DOMAIN_INC_DIR}/CurvilinearGrid.hpp ${DOMAIN_INC_DIR}/FacePolygonPropertyCache.hpp ${DOMAIN_INC_DIR}/GeometryList.hpp ${DOMAIN_INC_DIR}/GriddedSamples.hpp + ${DOMAIN_INC_DIR}/HangingEdgeCache.hpp ${DOMAIN_INC_DIR}/MKStateUndoAction.hpp ${DOMAIN_INC_DIR}/Mesh1D.hpp ${DOMAIN_INC_DIR}/Mesh2D.hpp diff --git a/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp index bb4bfa185..c0c1fbfa5 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/NodeInPolygonCache.hpp @@ -32,13 +32,14 @@ #include "MeshKernel/Point.hpp" +#include "MeshKernelApi/CachedIntegerValues.hpp" #include "MeshKernelApi/GeometryList.hpp" namespace meshkernelapi { /// @brief Cache node indices contained in a polygon - class NodeInPolygonCache + class NodeInPolygonCache : public CachedIntegerValues { public: /// @brief Constructor @@ -49,26 +50,12 @@ namespace meshkernelapi /// @brief Determine if current options match those used to construct the object bool ValidOptions(const std::vector& polygonPoints, const int inside) const; - /// @brief Get the number of values being cached. - int Size() const; - - /// @brief Copy cached values to array - void Copy(int* selectedNodes) const; - private: /// @brief Points making up the polygon std::vector m_polygonPoints; /// @brief Indicates if the points are inside or outside of the polygon int m_inside = -1; - - /// @brief Indices of nodes in the polygon - std::vector m_nodeIndices; }; } // namespace meshkernelapi - -inline int meshkernelapi::NodeInPolygonCache::Size() const -{ - return static_cast(m_nodeIndices.size()); -} diff --git a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp index 64e470509..55eac4ba0 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp @@ -41,6 +41,7 @@ #include "MeshKernelApi/BoundariesAsPolygonCache.hpp" #include "MeshKernelApi/CachedPointValues.hpp" #include "MeshKernelApi/FacePolygonPropertyCache.hpp" +#include "MeshKernelApi/HangingEdgeCache.hpp" #include "MeshKernelApi/NodeInPolygonCache.hpp" #include "MeshKernelApi/PolygonRefinementCache.hpp" #include "MeshKernelApi/SmallFlowEdgeCentreCache.hpp" @@ -87,6 +88,7 @@ namespace meshkernelapi 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 + std::shared_ptr m_hangingEdgeCache; ///< Cache for hanging edge ids }; } // namespace meshkernelapi diff --git a/libs/MeshKernelApi/src/MeshKernel.cpp b/libs/MeshKernelApi/src/MeshKernel.cpp index 6f1879671..ab98d18b7 100644 --- a/libs/MeshKernelApi/src/MeshKernel.cpp +++ b/libs/MeshKernelApi/src/MeshKernel.cpp @@ -1173,9 +1173,17 @@ namespace meshkernelapi { throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + + if (meshKernelState[meshKernelId].m_hangingEdgeCache != nullptr) + { + meshKernelState[meshKernelId].m_hangingEdgeCache.reset(); + throw meshkernel::MeshKernelError("Polygon Hanging edge has already been cached. Cached values will be delelted."); + } + meshKernelState[meshKernelId].m_mesh2d->Administrate(); const auto hangingEdges = meshKernelState[meshKernelId].m_mesh2d->GetHangingEdges(); - numHangingEdges = static_cast(hangingEdges.size()); + meshKernelState[meshKernelId].m_hangingEdgeCache = std::make_shared(hangingEdges); + numHangingEdges = meshKernelState[meshKernelId].m_hangingEdgeCache->Size(); } catch (...) { @@ -1193,11 +1201,14 @@ namespace meshkernelapi { throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } - const auto hangingEdges = meshKernelState[meshKernelId].m_mesh2d->GetHangingEdges(); - for (size_t i = 0; i < hangingEdges.size(); ++i) + + if (meshKernelState[meshKernelId].m_hangingEdgeCache == nullptr) { - edges[i] = static_cast(hangingEdges[i]); + throw meshkernel::MeshKernelError("Hanging edge data has not been cached"); } + + meshKernelState[meshKernelId].m_hangingEdgeCache->Copy(edges); + meshKernelState[meshKernelId].m_hangingEdgeCache.reset(); } catch (...) { diff --git a/libs/MeshKernelApi/src/NodeInPolygonCache.cpp b/libs/MeshKernelApi/src/NodeInPolygonCache.cpp index d8e74d70d..df5564af4 100644 --- a/libs/MeshKernelApi/src/NodeInPolygonCache.cpp +++ b/libs/MeshKernelApi/src/NodeInPolygonCache.cpp @@ -38,16 +38,19 @@ meshkernelapi::NodeInPolygonCache::NodeInPolygonCache(const std::vector& no const int inside) : m_polygonPoints(polygonPoints), m_inside(inside) { + std::vector nodeIndices; - m_nodeIndices.reserve(m_polygonPoints.size()); + nodeIndices.reserve(m_polygonPoints.size()); for (size_t i = 0; i < nodeMask.size(); ++i) { if (nodeMask[i] > 0) { - m_nodeIndices.push_back(static_cast(i)); + nodeIndices.push_back(static_cast(i)); } } + + Reset(std::move(nodeIndices)); } bool meshkernelapi::NodeInPolygonCache::ValidOptions(const std::vector& polygonPoints, const int inside) const @@ -56,15 +59,3 @@ bool meshkernelapi::NodeInPolygonCache::ValidOptions(const std::vector hangingEdges(numHangingEdges); + errorCode = meshkernelapi::mkernel_mesh2d_get_hanging_edges(meshKernelId, hangingEdges.data()); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + errorCode = meshkernelapi::mkernel_mesh2d_get_hanging_edges(meshKernelId, hangingEdges.data()); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); +} + TEST_F(CartesianApiTestFixture, DeleteHangingEdgesMesh2D_WithOneHangingEdges_ShouldDeleteOneHangingEdges) { // Prepare From 088223c57cae7e2026618cd83f26066abe305b36 Mon Sep 17 00:00:00 2001 From: BillSenior Date: Mon, 21 Oct 2024 16:04:54 +0200 Subject: [PATCH 12/14] GRIDEDIT-1336 Added cache for obtuse triangles and some files missing in last commit --- libs/MeshKernelApi/CMakeLists.txt | 2 + .../MeshKernelApi/CachedIntegerValues.hpp | 64 +++++++++++++++++++ .../MeshKernelApi/HangingEdgeCache.hpp | 48 ++++++++++++++ .../ObtuseTriangleCentreCache.hpp | 48 ++++++++++++++ .../include/MeshKernelApi/State.hpp | 14 ++-- .../MeshKernelApi/src/CachedIntegerValues.cpp | 46 +++++++++++++ libs/MeshKernelApi/src/HangingEdgeCache.cpp | 46 +++++++++++++ libs/MeshKernelApi/src/MeshKernel.cpp | 17 ++++- .../src/ObtuseTriangleCentreCache.cpp | 31 +++++++++ libs/MeshKernelApi/tests/src/ApiTest.cpp | 61 ++++++++++++++++++ 10 files changed, 368 insertions(+), 9 deletions(-) create mode 100644 libs/MeshKernelApi/include/MeshKernelApi/CachedIntegerValues.hpp create mode 100644 libs/MeshKernelApi/include/MeshKernelApi/HangingEdgeCache.hpp create mode 100644 libs/MeshKernelApi/include/MeshKernelApi/ObtuseTriangleCentreCache.hpp create mode 100644 libs/MeshKernelApi/src/CachedIntegerValues.cpp create mode 100644 libs/MeshKernelApi/src/HangingEdgeCache.cpp create mode 100644 libs/MeshKernelApi/src/ObtuseTriangleCentreCache.cpp diff --git a/libs/MeshKernelApi/CMakeLists.txt b/libs/MeshKernelApi/CMakeLists.txt index 78374792f..554d97868 100644 --- a/libs/MeshKernelApi/CMakeLists.txt +++ b/libs/MeshKernelApi/CMakeLists.txt @@ -29,6 +29,7 @@ set(SRC_LIST ${SRC_DIR}/MKStateUndoAction.cpp ${SRC_DIR}/MeshKernel.cpp ${SRC_DIR}/NodeInPolygonCache.cpp + ${SRC_DIR}/ObtuseTriangleCentreCache.cpp ${SRC_DIR}/PolygonRefinementCache.cpp ${SRC_DIR}/SmallFlowEdgeCentreCache.cpp ) @@ -51,6 +52,7 @@ set( ${DOMAIN_INC_DIR}/Mesh2D.hpp ${DOMAIN_INC_DIR}/MeshKernel.hpp ${DOMAIN_INC_DIR}/NodeInPolygonCache.hpp + ${DOMAIN_INC_DIR}/ObtuseTriangleCentreCache.hpp ${DOMAIN_INC_DIR}/PolygonRefinementCache.hpp ${DOMAIN_INC_DIR}/SmallFlowEdgeCentreCache.hpp ${DOMAIN_INC_DIR}/State.hpp diff --git a/libs/MeshKernelApi/include/MeshKernelApi/CachedIntegerValues.hpp b/libs/MeshKernelApi/include/MeshKernelApi/CachedIntegerValues.hpp new file mode 100644 index 000000000..daa887f05 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/CachedIntegerValues.hpp @@ -0,0 +1,64 @@ +//---- 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 + +namespace meshkernelapi +{ + /// @brief Caches x- and y-coordinate values for various algorithms + class CachedIntegerValues + { + public: + /// @brief Default constructor + CachedIntegerValues() {} + + /// @brief Construct with point values + CachedIntegerValues(const std::vector& values); + + /// @brief Destructor + virtual ~CachedIntegerValues() = default; + + /// @brief Number of points saved + int Size() const + { + return static_cast(m_values.size()); + } + + /// @brief Copy cached points to geometry + void Copy(int* buffer) const; + + protected: + /// @brief Reset the saved integer values. + void Reset(std::vector&& values); + + private: + std::vector m_values; ///< integer values + }; +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/HangingEdgeCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/HangingEdgeCache.hpp new file mode 100644 index 000000000..b8090f802 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/HangingEdgeCache.hpp @@ -0,0 +1,48 @@ +//---- 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/CachedIntegerValues.hpp" + +namespace meshkernelapi +{ + + /// @brief Cache edge indices for hanging nodes/edges + class HangingEdgeCache : public CachedIntegerValues + { + public: + /// @brief Constructor + HangingEdgeCache(const std::vector& edgeIds); + }; + +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/ObtuseTriangleCentreCache.hpp b/libs/MeshKernelApi/include/MeshKernelApi/ObtuseTriangleCentreCache.hpp new file mode 100644 index 000000000..6b6c174c8 --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/ObtuseTriangleCentreCache.hpp @@ -0,0 +1,48 @@ +//---- 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" + +namespace meshkernelapi +{ + + /// @brief Cache centre of edges + class ObtuseTriangleCentreCache : public CachedPointValues + { + public: + /// @brief Constructor + ObtuseTriangleCentreCache(const std::vector& triangleCentres); + }; + +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp index 55eac4ba0..b9078f612 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/HangingEdgeCache.hpp" #include "MeshKernelApi/NodeInPolygonCache.hpp" +#include "MeshKernelApi/ObtuseTriangleCentreCache.hpp" #include "MeshKernelApi/PolygonRefinementCache.hpp" #include "MeshKernelApi/SmallFlowEdgeCentreCache.hpp" @@ -83,12 +84,13 @@ namespace meshkernelapi meshkernel::Projection m_projection{meshkernel::Projection::cartesian}; ///< Projection used by the meshes // Cached values, used when dimensions are computed first, followed by values beign retrieved in a separate call - std::shared_ptr m_facePropertyCache; ///< Cache for - 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 - std::shared_ptr m_hangingEdgeCache; ///< Cache for hanging edge ids + std::shared_ptr m_facePropertyCache; ///< Cache for + 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 + std::shared_ptr m_hangingEdgeCache; ///< Cache for hanging edge ids + std::shared_ptr m_obtuseTriangleCentreCache; ///< Cache for the centre of obtuse triangles }; } // namespace meshkernelapi diff --git a/libs/MeshKernelApi/src/CachedIntegerValues.cpp b/libs/MeshKernelApi/src/CachedIntegerValues.cpp new file mode 100644 index 000000000..2a61c9b9a --- /dev/null +++ b/libs/MeshKernelApi/src/CachedIntegerValues.cpp @@ -0,0 +1,46 @@ +//---- 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 +#include + +#include "MeshKernelApi/CachedIntegerValues.hpp" + +meshkernelapi::CachedIntegerValues::CachedIntegerValues(const std::vector& values) + : m_values(values) {} + +void meshkernelapi::CachedIntegerValues::Copy(int* values) const +{ + size_t valueCount = sizeof(int) * m_values.size(); + + std::memcpy(values, m_values.data(), valueCount); +} + +void meshkernelapi::CachedIntegerValues::Reset(std::vector&& values) +{ + m_values = std::move(values); +} diff --git a/libs/MeshKernelApi/src/HangingEdgeCache.cpp b/libs/MeshKernelApi/src/HangingEdgeCache.cpp new file mode 100644 index 000000000..23b88f11c --- /dev/null +++ b/libs/MeshKernelApi/src/HangingEdgeCache.cpp @@ -0,0 +1,46 @@ +//---- 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 +#include +#include + +#include "MeshKernel/Exceptions.hpp" + +#include "MeshKernelApi/HangingEdgeCache.hpp" + +meshkernelapi::HangingEdgeCache::HangingEdgeCache(const std::vector& edgeId) +{ + std::vector edgeIdInt(edgeId.size()); + + for (size_t i = 0; i < edgeId.size(); ++i) + { + edgeIdInt[i] = edgeId[i]; + } + + Reset(std::move(edgeIdInt)); +} diff --git a/libs/MeshKernelApi/src/MeshKernel.cpp b/libs/MeshKernelApi/src/MeshKernel.cpp index ab98d18b7..28a398501 100644 --- a/libs/MeshKernelApi/src/MeshKernel.cpp +++ b/libs/MeshKernelApi/src/MeshKernel.cpp @@ -3591,9 +3591,16 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } + if (meshKernelState[meshKernelId].m_obtuseTriangleCentreCache != nullptr) + { + meshKernelState[meshKernelId].m_obtuseTriangleCentreCache.reset(); + throw meshkernel::MeshKernelError("Obtuse triangle centre data has already been cached, deleting cached data"); + } + const auto obtuseTriangles = meshKernelState[meshKernelId].m_mesh2d->GetObtuseTrianglesCenters(); + meshKernelState[meshKernelId].m_obtuseTriangleCentreCache = std::make_shared(obtuseTriangles); - numObtuseTriangles = static_cast(obtuseTriangles.size()); + numObtuseTriangles = meshKernelState[meshKernelId].m_obtuseTriangleCentreCache->Size(); } catch (...) { @@ -3612,9 +3619,13 @@ namespace meshkernelapi throw meshkernel::MeshKernelError("The selected mesh kernel id does not exist."); } - const auto obtuseTriangles = meshKernelState[meshKernelId].m_mesh2d->GetObtuseTrianglesCenters(); + if (meshKernelState[meshKernelId].m_obtuseTriangleCentreCache == nullptr) + { + throw meshkernel::MeshKernelError("Obtuse triangle centre data has not been cached"); + } - ConvertPointVectorToGeometryList(obtuseTriangles, result); + meshKernelState[meshKernelId].m_obtuseTriangleCentreCache->Copy(result); + meshKernelState[meshKernelId].m_obtuseTriangleCentreCache.reset(); } catch (...) { diff --git a/libs/MeshKernelApi/src/ObtuseTriangleCentreCache.cpp b/libs/MeshKernelApi/src/ObtuseTriangleCentreCache.cpp new file mode 100644 index 000000000..ec065d682 --- /dev/null +++ b/libs/MeshKernelApi/src/ObtuseTriangleCentreCache.cpp @@ -0,0 +1,31 @@ +//---- 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/ObtuseTriangleCentreCache.hpp" + +meshkernelapi::ObtuseTriangleCentreCache::ObtuseTriangleCentreCache(const std::vector& triangleCentres) + : CachedPointValues(triangleCentres) {} diff --git a/libs/MeshKernelApi/tests/src/ApiTest.cpp b/libs/MeshKernelApi/tests/src/ApiTest.cpp index d19c5f882..acf8fbeee 100644 --- a/libs/MeshKernelApi/tests/src/ApiTest.cpp +++ b/libs/MeshKernelApi/tests/src/ApiTest.cpp @@ -1873,6 +1873,67 @@ TEST_F(CartesianApiTestFixture, CountObtuseTriangles_OnMesh2DWithOneObtuseTriang // Assert ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); ASSERT_EQ(1, numObtuseTriangles); + + // Need to clear the obtuse triangle cache for the next tests + meshkernelapi::GeometryList geometryList{}; + + std::vector coordinatesObtuseTrianglesX(numObtuseTriangles); + std::vector coordinatesObtuseTrianglesY(numObtuseTriangles); + geometryList.coordinates_x = coordinatesObtuseTrianglesX.data(); + geometryList.coordinates_y = coordinatesObtuseTrianglesY.data(); + geometryList.num_coordinates = numObtuseTriangles; + errorCode = mkernel_mesh2d_get_obtuse_triangles_mass_centers(meshKernelId, geometryList); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); +} + +TEST_F(CartesianApiTestFixture, CountObtuseTriangles_OnMesh2DWithOneObtuseTriangle_ObtuseTrianglesFailures) +{ + // Prepare a mesh with one obtuse triangle + meshkernelapi::Mesh2D mesh2d; + std::vector coordinatesX{0.0, 3.0, -1.0, 1.5}; + std::vector coordinatesY{0.0, 0.0, 2.0, -2.0}; + std::vector edge_nodes{0, 1, 1, 2, 2, 0, 0, 3, 3, 1}; + mesh2d.node_x = coordinatesX.data(); + mesh2d.node_y = coordinatesY.data(); + mesh2d.edge_nodes = edge_nodes.data(); + mesh2d.num_edges = static_cast(edge_nodes.size() * 0.5); + mesh2d.num_nodes = static_cast(coordinatesX.size()); + auto const meshKernelId = GetMeshKernelId(); + + // Execute + auto errorCode = mkernel_mesh2d_set(meshKernelId, mesh2d); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Need to clear the obtuse triangle cache for the next tests + meshkernelapi::GeometryList geometryList{}; + + // Data has not yet been cached + errorCode = mkernel_mesh2d_get_obtuse_triangles_mass_centers(meshKernelId, geometryList); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + int numObtuseTriangles; + errorCode = meshkernelapi::mkernel_mesh2d_count_obtuse_triangles(meshKernelId, numObtuseTriangles); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Already cached, cached data will be deleted + errorCode = meshkernelapi::mkernel_mesh2d_count_obtuse_triangles(meshKernelId, numObtuseTriangles); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + // Re-cache data. + errorCode = meshkernelapi::mkernel_mesh2d_count_obtuse_triangles(meshKernelId, numObtuseTriangles); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + std::vector coordinatesObtuseTrianglesX(numObtuseTriangles); + std::vector coordinatesObtuseTrianglesY(numObtuseTriangles); + geometryList.coordinates_x = coordinatesObtuseTrianglesX.data(); + geometryList.coordinates_y = coordinatesObtuseTrianglesY.data(); + geometryList.num_coordinates = numObtuseTriangles; + errorCode = mkernel_mesh2d_get_obtuse_triangles_mass_centers(meshKernelId, geometryList); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Cache has been deleted in the last call + errorCode = mkernel_mesh2d_get_obtuse_triangles_mass_centers(meshKernelId, geometryList); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); } TEST_F(CartesianApiTestFixture, Mesh2DCountObtuseTriangles_OnMesh2DWithOneObtuseTriangle_ShouldGetObtuseTriangle) From 0fd48b72c5fd25b65105042f14ecd8590d4628e8 Mon Sep 17 00:00:00 2001 From: BillSenior Date: Mon, 21 Oct 2024 16:38:40 +0200 Subject: [PATCH 13/14] GRIDEDIT-1336 Small change to doxygen comment --- libs/MeshKernelApi/include/MeshKernelApi/State.hpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp index b9078f612..e7c468dbf 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp @@ -84,13 +84,13 @@ namespace meshkernelapi meshkernel::Projection m_projection{meshkernel::Projection::cartesian}; ///< Projection used by the meshes // Cached values, used when dimensions are computed first, followed by values beign retrieved in a separate call - std::shared_ptr m_facePropertyCache; ///< Cache for - 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 - std::shared_ptr m_hangingEdgeCache; ///< Cache for hanging edge ids - std::shared_ptr m_obtuseTriangleCentreCache; ///< Cache for the centre of obtuse triangles + std::shared_ptr m_facePropertyCache; ///< face property cache + std::shared_ptr m_boundariesAsPolygonCache; ///< boundaries as polygon cache + std::shared_ptr m_polygonRefinementCache; ///< polygon refinement cache + std::shared_ptr m_nodeInPolygonCache; ///< node in polygon cache + std::shared_ptr m_smallFlowEdgeCentreCache; ///< small flow edge centres cache + std::shared_ptr m_hangingEdgeCache; ///< hanging edge id cache + std::shared_ptr m_obtuseTriangleCentreCache; ///< centre of obtuse triangles cache }; } // namespace meshkernelapi From 3ed22e297a3d826b5a8a9323f4ed5a56c8a76fda Mon Sep 17 00:00:00 2001 From: BillSenior Date: Mon, 21 Oct 2024 17:03:40 +0200 Subject: [PATCH 14/14] GRIDEDIT-1336 Corrected spelling in doxygen comment --- libs/MeshKernelApi/include/MeshKernelApi/State.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp index e7c468dbf..b10f67aa3 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/State.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/State.hpp @@ -83,7 +83,7 @@ namespace meshkernelapi // Exclusively owned state meshkernel::Projection m_projection{meshkernel::Projection::cartesian}; ///< Projection used by the meshes - // Cached values, used when dimensions are computed first, followed by values beign retrieved in a separate call + // Cached values, used when dimensions are computed first, followed by values being retrieved in a separate call std::shared_ptr m_facePropertyCache; ///< face property cache std::shared_ptr m_boundariesAsPolygonCache; ///< boundaries as polygon cache std::shared_ptr m_polygonRefinementCache; ///< polygon refinement cache