From 57f68d4a942017aa6ab44705d3899c3f627003cc Mon Sep 17 00:00:00 2001 From: BillSenior Date: Mon, 9 Oct 2023 12:31:38 +0200 Subject: [PATCH] GRIDEDIT-704 Added setting of nodes outside a box to invalid. --- .../CurvilinearGrid/CurvilinearGrid.hpp | 4 + .../src/CurvilinearGrid/CurvilinearGrid.cpp | 63 ++++++++++ .../tests/src/CurvilinearUniformTests.cpp | 110 ++++++++++++++++++ 3 files changed, 177 insertions(+) diff --git a/libs/MeshKernel/include/MeshKernel/CurvilinearGrid/CurvilinearGrid.hpp b/libs/MeshKernel/include/MeshKernel/CurvilinearGrid/CurvilinearGrid.hpp index 9af109748..48db57e72 100644 --- a/libs/MeshKernel/include/MeshKernel/CurvilinearGrid/CurvilinearGrid.hpp +++ b/libs/MeshKernel/include/MeshKernel/CurvilinearGrid/CurvilinearGrid.hpp @@ -210,6 +210,10 @@ namespace meshkernel /// @note If any index is the null value or is out of range a ConstraintError will be thrown void DeleteInterior(const CurvilinearGridNodeIndices& firstNode, const CurvilinearGridNodeIndices& secondNode); + /// @brief Set all the nodes oustide of the block to be invalid. + /// @note If any index is the null value or is out of range a ConstraintError will be thrown + void DeleteExterior(const CurvilinearGridNodeIndices& firstNode, const CurvilinearGridNodeIndices& secondNode); + /// @brief Moves a node from one position to another /// @param[in] fromPoint The input position, the closest node will be used /// @param[in] toPoint The coordinates of the new position diff --git a/libs/MeshKernel/src/CurvilinearGrid/CurvilinearGrid.cpp b/libs/MeshKernel/src/CurvilinearGrid/CurvilinearGrid.cpp index ecb77b6c8..659503e73 100644 --- a/libs/MeshKernel/src/CurvilinearGrid/CurvilinearGrid.cpp +++ b/libs/MeshKernel/src/CurvilinearGrid/CurvilinearGrid.cpp @@ -905,6 +905,69 @@ void CurvilinearGrid::DeleteInterior(const CurvilinearGridNodeIndices& firstNode } } +void CurvilinearGrid::DeleteExterior(const CurvilinearGridNodeIndices& firstNode, const CurvilinearGridNodeIndices& secondNode) +{ + + if (!firstNode.IsValid() || !secondNode.IsValid()) + { + throw ConstraintError("Invalid index: first index - {{{}, {}}}, second index - {{{}, {}}}", firstNode.m_m, firstNode.m_n, secondNode.m_m, secondNode.m_n); + } + + if (firstNode.m_m >= m_numM || firstNode.m_n >= m_numN) + { + throw ConstraintError("Invalid index: first index {{{}, {}}} not in mesh limits {{{}, {}}}", firstNode.m_m, firstNode.m_n, m_numM, m_numN); + } + + if (secondNode.m_m >= m_numM || secondNode.m_n >= m_numN) + { + throw ConstraintError("Invalid index: second index {{{}, {}}} not in mesh limits {{{}, {}}}", secondNode.m_m, secondNode.m_n, m_numM, m_numN); + } + + UInt lowerLimitI = std::min(firstNode.m_n, secondNode.m_n); + UInt upperLimitI = std::max(firstNode.m_n, secondNode.m_n); + + UInt lowerLimitJ = std::min(firstNode.m_m, secondNode.m_m); + UInt upperLimitJ = std::max(firstNode.m_m, secondNode.m_m); + + // Split into 4 regions, setting the nodes in each region to invalid + // + // First region: all nodes "south" the designated box + for (UInt n = 0; n < m_numN; ++n) + { + for (UInt m = 0; m < lowerLimitJ; ++m) + { + m_gridNodes(n, m).SetInvalid(); + } + } + + // Second region: all nodes "directly west of" the designated box + for (UInt n = 0; n < lowerLimitI; ++n) + { + for (UInt m = lowerLimitJ; m <= upperLimitJ; ++m) + { + m_gridNodes(n, m).SetInvalid(); + } + } + + // Third region: all nodes "directly east of" the designated box + for (UInt n = upperLimitI + 1; n < m_numN; ++n) + { + for (UInt m = lowerLimitJ; m <= upperLimitJ; ++m) + { + m_gridNodes(n, m).SetInvalid(); + } + } + + // Fourth region: all nodes "north" the designated box + for (UInt n = 0; n < m_numN; ++n) + { + for (UInt m = upperLimitJ + 1; m < m_numM; ++m) + { + m_gridNodes(n, m).SetInvalid(); + } + } +} + void CurvilinearGrid::MoveNode(Point const& fromPoint, Point const& toPoint) { // Get the node indices of fromPoint diff --git a/libs/MeshKernel/tests/src/CurvilinearUniformTests.cpp b/libs/MeshKernel/tests/src/CurvilinearUniformTests.cpp index f77674579..0581dea1f 100644 --- a/libs/MeshKernel/tests/src/CurvilinearUniformTests.cpp +++ b/libs/MeshKernel/tests/src/CurvilinearUniformTests.cpp @@ -348,3 +348,113 @@ TEST(CurvilinearGridUniform, DeleteInteriorNodesFailureTest) EXPECT_THROW(curvilinearGrid->DeleteInterior({1, 1}, {nx, ny}), meshkernel::ConstraintError); EXPECT_THROW(curvilinearGrid->DeleteInterior({nx, 1}, {4, 4}), meshkernel::ConstraintError); } + +void TestDeleteExteriorNodes(std::shared_ptr curvilinearGrid, + const meshkernel::CurvilinearGridNodeIndices first, + const meshkernel::CurvilinearGridNodeIndices second) +{ + // Check first, that all nodes are valid + for (meshkernel::UInt i = 0; i < curvilinearGrid->m_numN; ++i) + { + for (meshkernel::UInt j = 0; j < curvilinearGrid->m_numM; ++j) + { + EXPECT_TRUE(curvilinearGrid->GetNode(i, j).IsValid()); + } + } + + meshkernel::UInt lowerLimitI = std::min(first.m_n, second.m_n); + meshkernel::UInt upperLimitI = std::max(first.m_n, second.m_n); + + meshkernel::UInt lowerLimitJ = std::min(first.m_m, second.m_m); + meshkernel::UInt upperLimitJ = std::max(first.m_m, second.m_m); + + meshkernel::UInt expectedValid = (upperLimitI - lowerLimitI + 1) * (upperLimitJ - lowerLimitJ + 1); + + // Delete the nodes outside of a block + curvilinearGrid->DeleteExterior(first, second); + + auto inRange = [](const meshkernel::UInt v, const meshkernel::UInt l, const meshkernel::UInt u) + { return l <= v && v <= u; }; + + EXPECT_EQ(expectedValid, CurvilinearGridCountValidNodes(curvilinearGrid)); + + // Check that these exterior nodes have been set to invalid. + for (meshkernel::UInt i = 0; i < curvilinearGrid->m_numN; ++i) + { + for (meshkernel::UInt j = 0; j < curvilinearGrid->m_numM; ++j) + { + if (inRange(i, lowerLimitI, upperLimitI) && inRange(j, lowerLimitJ, upperLimitJ)) + { + EXPECT_TRUE(curvilinearGrid->GetNode(i, j).IsValid()) << "node should be true: " << i << " " << j; + } + else + { + EXPECT_FALSE(curvilinearGrid->GetNode(i, j).IsValid()) << "node should be false: " << i << " " << j; + } + } + } +} + +TEST(CurvilinearGridUniform, DeleteExteriorNodesTest) +{ + // Basic, testing of setting nodes inside a box to invalid + meshkernel::UInt nx = 10; + meshkernel::UInt ny = 10; + std::shared_ptr curvilinearGrid = MakeCurvilinearGrid(0.0, 0.0, 1.0, 1.0, nx, ny); + TestDeleteExteriorNodes(curvilinearGrid, {1, 1}, {4, 4}); + + // Reset the mesh + curvilinearGrid = MakeCurvilinearGrid(0.0, 0.0, 1.0, 1.0, nx, ny); + TestDeleteExteriorNodes(curvilinearGrid, {2, 1}, {5, 4}); + + // Reset the mesh + curvilinearGrid = MakeCurvilinearGrid(0.0, 0.0, 1.0, 1.0, nx, ny); + TestDeleteExteriorNodes(curvilinearGrid, {4, 3}, {7, 8}); +} + +TEST(CurvilinearGridUniform, DeleteExteriorNodesReverseTest) +{ + // testing of setting nodes inside a box to invalid, with lower and upper reversed + + meshkernel::UInt nx = 10; + meshkernel::UInt ny = 10; + + // Prepare + std::shared_ptr curvilinearGrid = MakeCurvilinearGrid(0.0, 0.0, 1.0, 1.0, nx, ny); + TestDeleteExteriorNodes(curvilinearGrid, {5, 6}, {1, 2}); + + // Reset the mesh + curvilinearGrid = MakeCurvilinearGrid(0.0, 0.0, 1.0, 1.0, nx, ny); + TestDeleteExteriorNodes(curvilinearGrid, {5, 6}, {0, 4}); +} + +TEST(CurvilinearGridUniform, DeleteExteriorNodesMixedTest) +{ + // testing of setting nodes inside a box to invalid, with lower and upper reversed for any of i and j index + + meshkernel::UInt nx = 100; + meshkernel::UInt ny = 100; + + std::shared_ptr curvilinearGrid = MakeCurvilinearGrid(0.0, 0.0, 1.0, 1.0, nx, ny); + TestDeleteExteriorNodes(curvilinearGrid, {5, 1}, {1, 6}); + + // Reset grid + curvilinearGrid = MakeCurvilinearGrid(0.0, 0.0, 1.0, 1.0, nx, ny); + TestDeleteExteriorNodes(curvilinearGrid, {1, 6}, {5, 2}); +} + +TEST(CurvilinearGridUniform, DeleteExteriorNodesFailureTest) +{ + // testing of setting nodes inside a box to invalid with invalid or out of range indices. + + meshkernel::UInt nx = 10; + meshkernel::UInt ny = 10; + + // Prepare + std::shared_ptr curvilinearGrid = MakeCurvilinearGrid(0.0, 0.0, 1.0, 1.0, nx, ny); + + EXPECT_THROW(curvilinearGrid->DeleteExterior({1, meshkernel::constants::missing::uintValue}, {nx, ny}), meshkernel::ConstraintError); + EXPECT_THROW(curvilinearGrid->DeleteExterior({1, 1}, {meshkernel::constants::missing::uintValue, ny}), meshkernel::ConstraintError); + EXPECT_THROW(curvilinearGrid->DeleteExterior({1, 1}, {nx, ny}), meshkernel::ConstraintError); + EXPECT_THROW(curvilinearGrid->DeleteExterior({nx, 1}, {4, 4}), meshkernel::ConstraintError); +}