Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/KernelWrappers/Common/SoilProfile2DHelperTests.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/KernelWrappers/Common/SoilProfile2DHelperTests.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/KernelWrappers/Common/SoilProfile2DHelperTests.cs (revision 5382) @@ -0,0 +1,1002 @@ +// Copyright (C) Stichting Deltares 2024. All rights reserved. +// +// This file is part of the Dam Engine. +// +// The Dam Engine is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// +// All names, logos, and references to "Deltares" are registered trademarks of +// Stichting Deltares and remain full property of Stichting Deltares at all times. +// All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using Deltares.DamEngine.Calculators.KernelWrappers.Common; +using Deltares.DamEngine.Data.Geometry; +using Deltares.DamEngine.Data.Geotechnics; +using Deltares.DamEngine.TestHelpers.Factories; +using NUnit.Framework; + +namespace Deltares.DamEngine.Calculators.Tests.KernelWrappers.Common; + +public class SoilProfile2DHelperTests +{ + private const double precision5Decimals = 0.0000051; + + private static readonly Random random = new(21); + private static readonly double leftCoordinate = random.NextDouble(); + private static readonly double rightCoordinate = leftCoordinate + 1; + private static readonly double middleXCoordinate = 0.5 * (leftCoordinate + rightCoordinate); + + /// + /// Different soil profiles 2D are tested, see the pictures in method GetSoilProfilesWithContinuousBottomAquiferLayer, + /// with a top clay layer and bottom aquifer layers always forming a continuous bottom layer. + /// As there is only one aquifer, then the BottomAquifer is also the HighestAquifer. + /// + /// + /// + [Test] + [TestCaseSource(nameof(GetSoilProfilesWithContinuousBottomAquiferLayer))] + public void Given2DSoilProfileWithContinuousBottomAquifer_WhenDeterminingLayerBoundaryPointsOfBottomAquifer_ThenExpectedCoordinatesReturned( + SoilProfile2D soilProfile, IEnumerable expectedBottomAquiferCoordinates) + { + // Call + SoilProfile2DHelper.DetermineAquiferLayerBoundaryPoints(SoilProfile2DHelper.LayerType.BottomAquiferCluster, soilProfile, out Point2D[] bottomAquifer, out _); + SoilProfile2DHelper.DetermineAquiferLayerBoundaryPoints(SoilProfile2DHelper.LayerType.HighestAquiferCluster, soilProfile, out Point2D[] highestAquifer, out _); + + // Assert + AssertGeometry(bottomAquifer, expectedBottomAquiferCoordinates); + AssertGeometry(highestAquifer, expectedBottomAquiferCoordinates); + } + + /// + /// |-------------------------------------------------------------| Level 0 m + /// | Aquitard 1 | + /// |-------------------------------------------------------------| Level -10 m + /// | Aquitard 2 B /------------------------\ C | Level -12 m + /// | / Aquifer 1 \ | + /// | / point X \ | + /// |---------------/--------------*---------------\--------------| Level -20 m + /// | A Aquifer 2 D | + /// |------------------------------*------------------------------| Level -30 m + /// point Y + /// + [Test] + [TestCase(-20,-30)] + [TestCase(-20.1,-30)] + [TestCase(-19.9,-30)] + [TestCase(-20,-30.1)] + [TestCase(-20,-29.9)] + public void Given2DSoilProfileWithNotContinuousAquiferConnectedToContinuousAquifer_WhenDeterminingLayerBoundaryPointsOfAquifer_ThenExpectedCoordinatesReturned(double zPointX, double zPointY) + { + var pointA = new Point2D(leftCoordinate + (rightCoordinate - leftCoordinate) / 3, -20); + var pointB = new Point2D(pointA.X + 0.01, -12); + var pointC = new Point2D(leftCoordinate + 2 * (rightCoordinate - leftCoordinate) / 3, -12); + var pointD = new Point2D(pointC.X + 0.01, -20); + var pointX = new Point2D(middleXCoordinate, zPointX); + var pointY = new Point2D(middleXCoordinate, zPointY); + + SoilLayer2D soilUpperLayer = CreateRectangularSoilLayer2D(0, -10, leftCoordinate, rightCoordinate, false); + SoilLayer2D soilLayerAquitard2 = FactoryForSoilProfiles.CreatePolygoneSoilLayer2D([ + ..new[] + { + new Point2D(leftCoordinate, -10), + new Point2D(rightCoordinate, -10), + new Point2D(rightCoordinate, -20), + pointD, + pointC, + pointB, + pointA, + new Point2D(leftCoordinate, -20) + } + ], null); + SoilLayer2D soilLayerAquifer1 = FactoryForSoilProfiles.CreatePentagonSoilLayer2D(pointA, pointB, pointC, pointD, pointX, null, null, true); + SoilLayer2D soilLayerAquifer2 = FactoryForSoilProfiles.CreatePolygoneSoilLayer2D([ + ..new[] + { + new Point2D(leftCoordinate, -20), + pointA, + pointX, + pointD, + new Point2D(rightCoordinate, -20), + new Point2D(rightCoordinate, -30), + pointY, + new Point2D(leftCoordinate, -30) + } + ], null, true); + + var soilProfile = new SoilProfile2D + { + Geometry = new GeometryData + { + Left = leftCoordinate, + Right = rightCoordinate, + Bottom = -30 + } + }; + soilProfile.Surfaces.Add(soilUpperLayer); + soilProfile.Surfaces.Add(soilLayerAquitard2); + soilProfile.Surfaces.Add(soilLayerAquifer1); + soilProfile.Surfaces.Add(soilLayerAquifer2); + soilProfile.Geometry.Surfaces.Add(new GeometrySurface()); + + // Call + SoilProfile2DHelper.DetermineAquiferLayerBoundaryPoints(SoilProfile2DHelper.LayerType.BottomAquiferCluster, soilProfile, out Point2D[] bottomAquifer, out _); + SoilProfile2DHelper.DetermineAquiferLayerBoundaryPoints(SoilProfile2DHelper.LayerType.HighestAquiferCluster, soilProfile, out Point2D[] highestAquifer, out _); + + // Assert + var expectedBottomAquiferCoordinates = new[] + { + new GeometryPoint(leftCoordinate, -20), + new GeometryPoint(pointA.X, pointA.Z), + new GeometryPoint(pointB.X, pointB.Z), + new GeometryPoint(middleXCoordinate, -12), + new GeometryPoint(pointC.X, pointC.Z), + new GeometryPoint(pointD.X, pointD.Z), + new GeometryPoint(rightCoordinate, -20) + }; + AssertGeometry(bottomAquifer, expectedBottomAquiferCoordinates); + AssertGeometry(highestAquifer, expectedBottomAquiferCoordinates); + } + + /// + /// --------------------------------------------------------------- Level 0 m + /// top layer + /// --------------------------------------------------------------- Level -10 m + /// in-between aquifer layer left | in-between aquifer layer right + /// --------------------------------------------------------------- Level -20 m + /// in-between layer + /// --------------------------------------------------------------- Level -25 m + /// bottom aquifer layer + /// --------------------------------------------------------------- Level -30 m + /// + [Test] + public void Given2DSoilProfileWithContinuousInBetweenAquiferLayerConsistingOfTwoParts_WhenDeterminingLayerBoundaryPointsOfInBetweenAquifer_ThenExpectedCoordinatesReturned() + { + // Setup + SoilLayer2D soilLayer = CreateRectangularSoilLayer2D(0, -10, leftCoordinate, rightCoordinate, false); + SoilLayer2D soilLayerAquiferInBetweenLeft = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, middleXCoordinate, true); + SoilLayer2D soilLayerAquiferInBetweenRight = CreateRectangularSoilLayer2D(-10, -20, middleXCoordinate, rightCoordinate, true); + SoilLayer2D soilLayerInBetween = CreateRectangularSoilLayer2D(-20, -25, leftCoordinate, rightCoordinate, false); + SoilLayer2D soilLayerAquiferBottom = CreateRectangularSoilLayer2D(-25, -30, leftCoordinate, rightCoordinate, true); + + var soilProfile = new SoilProfile2D + { + Geometry = new GeometryData + { + Left = leftCoordinate, + Right = rightCoordinate, + Bottom = -30 + } + }; + soilProfile.Surfaces.Add(soilLayer); + soilProfile.Surfaces.Add(soilLayerAquiferInBetweenLeft); + soilProfile.Surfaces.Add(soilLayerAquiferInBetweenRight); + soilProfile.Surfaces.Add(soilLayerInBetween); + soilProfile.Surfaces.Add(soilLayerAquiferBottom); + soilProfile.Geometry.Surfaces.Add(new GeometrySurface()); + + // Call + bool isInBetweenLayerPresent = SoilProfile2DHelper.AreInBetweenAquiferClustersPresent(soilProfile, out int count); + SoilProfile2DHelper.DetermineAquiferLayerBoundaryPoints(SoilProfile2DHelper.LayerType.InBetweenAquiferCluster, soilProfile, out Point2D[] topLevelInBetweenAquifer, out Point2D[] bottomLevelInBetweenAquifer, 0); + + // Assert + Assert.Multiple(() => + { + Assert.That(isInBetweenLayerPresent, Is.True); + Assert.That(count, Is.EqualTo(1)); + }); + GeometryPoint[] expectedTop = + [ + new(leftCoordinate, -10), + new(middleXCoordinate, -10), + new(rightCoordinate, -10) + ]; + GeometryPoint[] expectedBottom = + [ + new(leftCoordinate, -20), + new(middleXCoordinate, -20), + new(rightCoordinate, -20) + ]; + AssertGeometry(topLevelInBetweenAquifer, expectedTop); + AssertGeometry(bottomLevelInBetweenAquifer, expectedBottom); + } + + /// + /// --------------------------------------------------- Level 0 m + /// top layer + /// --------------------------------------------------- Level -10 m + /// in-between aquifer layer + /// --------------------------------------------------- Level -20 m + /// in-between layer + /// --------------------------------------------------- Level -25 m + /// bottom aquifer layer + /// --------------------------------------------------- Level -30 m + /// + [Test] + public void Given2DSoilProfileWithContinuousInBetweenAquiferLayerConsistingOfOnePart_WhenDeterminingLayerBoundaryPointsOfInBetweenAquifer_ThenExpectedCoordinatesReturned() + { + // Setup + SoilLayer2D soilLayer = CreateRectangularSoilLayer2D(0, -10, leftCoordinate, rightCoordinate, false); + SoilLayer2D soilLayerAquiferInBetween = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, rightCoordinate, true); + SoilLayer2D soilLayerInBetween = CreateRectangularSoilLayer2D(-20, -25, leftCoordinate, rightCoordinate, false); + SoilLayer2D soilLayerAquiferBottom = CreateRectangularSoilLayer2D(-25, -30, leftCoordinate, rightCoordinate, true); + + var soilProfile = new SoilProfile2D + { + Geometry = new GeometryData + { + Left = leftCoordinate, + Right = rightCoordinate, + Bottom = -30 + } + }; + soilProfile.Surfaces.Add(soilLayer); + soilProfile.Surfaces.Add(soilLayerAquiferInBetween); + soilProfile.Surfaces.Add(soilLayerInBetween); + soilProfile.Surfaces.Add(soilLayerAquiferBottom); + soilProfile.Geometry.Surfaces.Add(new GeometrySurface()); + + // Call + bool isInBetweenLayerPresent = SoilProfile2DHelper.AreInBetweenAquiferClustersPresent(soilProfile, out int count); + SoilProfile2DHelper.DetermineAquiferLayerBoundaryPoints(SoilProfile2DHelper.LayerType.InBetweenAquiferCluster, soilProfile, out Point2D[] topLevelInBetweenAquifer, out Point2D[] bottomLevelInBetweenAquifer, 0); + + // Assert + Assert.Multiple(() => + { + Assert.That(isInBetweenLayerPresent, Is.True); + Assert.That(count, Is.EqualTo(1)); + }); + GeometryPoint[] expectedTop = + [ + new(leftCoordinate, -10), + new(rightCoordinate, -10) + ]; + GeometryPoint[] expectedBottom = + [ + new(leftCoordinate, -20), + new(rightCoordinate, -20) + ]; + AssertGeometry(topLevelInBetweenAquifer, expectedTop); + AssertGeometry(bottomLevelInBetweenAquifer, expectedBottom); + } + + /// + /// ------------------------------------------------------------- Level 0 m + /// top layer + /// ------------------------------------------------------------- Level -10 m + /// | Gap layer + /// in-between aquifer |------------------------------- Level -15 m + /// layer left | + /// | in-between aquifer + /// -----------------------------| layer right Level -20 m + /// in-between layer | + /// |------------------------------- Level -21 m + /// + /// ------------------------------------------------------------- Level -25 m + /// bottom aquifer layer + /// ------------------------------------------------------------- Level -30 m + /// + [Test] + public void Given2DSoilProfileWithContinuousInBetweenAquiferConsistingOfTwoLayersWithRightLayerTopLevelInRangeLeftLayer_WhenDeterminingLayerBoundaryPointsOfInBetweenAquifer_ThenExpectedCoordinatesReturned() + { + // Setup + SoilLayer2D soilLayer = CreateRectangularSoilLayer2D(0, -10, leftCoordinate, rightCoordinate, false); + SoilLayer2D soilLayerAquiferInBetweenLeft = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, middleXCoordinate, true); + // Add a layer to fill the gap between the right aquifer layer and the upper layer + SoilLayer2D soilLayerGap = CreateRectangularSoilLayer2D(-10, -15, middleXCoordinate, rightCoordinate, false); + SoilLayer2D soilLayerAquiferInBetweenRight = CreateRectangularSoilLayer2D(-15, -21, middleXCoordinate, rightCoordinate, true); + // Add continuous in-between layer + var bottomLeftInBetweenAquiferLayerLeft = new Point2D(leftCoordinate, -20); + var bottomRightInBetweenAquiferLayerLeft = new Point2D(middleXCoordinate, -20); + var bottomLeftInBetweenAquiferLayerRight = new Point2D(middleXCoordinate, -21); + var bottomRightInBetweenAquiferLayerRight = new Point2D(rightCoordinate, -21); + var bottomRightInBetweenLayer = new Point2D(rightCoordinate, -25); + var bottomLeftInBetweenLayer = new Point2D(leftCoordinate, -25); + var soilLayerInBetween = new SoilLayer2D + { + GeometrySurface = new GeometrySurface + { + OuterLoop = new GeometryLoop + { + CurveList = + { + new GeometryCurve(bottomLeftInBetweenAquiferLayerLeft, bottomRightInBetweenAquiferLayerLeft), + new GeometryCurve(bottomRightInBetweenAquiferLayerLeft, bottomLeftInBetweenAquiferLayerRight), + new GeometryCurve(bottomLeftInBetweenAquiferLayerRight, bottomRightInBetweenAquiferLayerRight), + new GeometryCurve(bottomRightInBetweenAquiferLayerRight, bottomRightInBetweenLayer), + new GeometryCurve(bottomRightInBetweenLayer, bottomLeftInBetweenLayer) + } + } + } + }; + SoilLayer2D soilLayerAquiferBottom = CreateRectangularSoilLayer2D(-25, -30, leftCoordinate, rightCoordinate, true); + + var soilProfile = new SoilProfile2D + { + Geometry = new GeometryData + { + Left = leftCoordinate, + Right = rightCoordinate, + Bottom = -30 + } + }; + soilProfile.Surfaces.Add(soilLayer); + soilProfile.Surfaces.Add(soilLayerAquiferInBetweenLeft); + soilProfile.Surfaces.Add(soilLayerGap); + soilProfile.Surfaces.Add(soilLayerAquiferInBetweenRight); + soilProfile.Surfaces.Add(soilLayerInBetween); + soilProfile.Surfaces.Add(soilLayerAquiferBottom); + soilProfile.Geometry.Surfaces.Add(new GeometrySurface()); + + // Call + bool isInBetweenLayerPresent = SoilProfile2DHelper.AreInBetweenAquiferClustersPresent(soilProfile, out int count); + SoilProfile2DHelper.DetermineAquiferLayerBoundaryPoints(SoilProfile2DHelper.LayerType.InBetweenAquiferCluster, soilProfile, out Point2D[] topLevelInBetweenAquifer, out Point2D[] bottomLevelInBetweenAquifer, 0); + + // Assert + Assert.Multiple(() => + { + Assert.That(isInBetweenLayerPresent, Is.True); + Assert.That(count, Is.EqualTo(1)); + }); + GeometryPoint[] expectedTop = + [ + new(leftCoordinate, -10), + new(middleXCoordinate, -10), + new(middleXCoordinate, -15), + new(rightCoordinate, -15) + ]; + GeometryPoint[] expectedBottom = + [ + new(leftCoordinate, -20), + new(middleXCoordinate, -20), + new(middleXCoordinate, -21), + new(rightCoordinate, -21) + ]; + AssertGeometry(topLevelInBetweenAquifer, expectedTop); + AssertGeometry(bottomLevelInBetweenAquifer, expectedBottom); + } + + /// + /// -------------------------------------------------------------- Level 0 m + /// top layer + /// |------------------------------- Level -5 m + /// | + /// | in-between aquifer + /// ------------------------------| layer right Level -10 m + /// in-between aquifer layer left | + /// ------------------------------------------------------------- Level -20 m + /// in-between layer + /// -------------------------------------------------------------- Level -25 m + /// bottom aquifer layer + /// -------------------------------------------------------------- Level -30 m + /// + [Test] + public void Given2DSoilProfileWithContinuousInBetweenAquiferConsistingOfTwoLayersWithRightLayerBottomLevelInRangeLeftLayer_WhenDeterminingLayerBoundaryPointsOfInBetweenAquifer_ThenExpectedCoordinatesReturned() + { + // Setup + var topLeftUpperLayer = new Point2D(leftCoordinate, 0); + var topRightUpperLayer = new Point2D(rightCoordinate, 0); + var bottomRightUpperLayer = new Point2D(rightCoordinate, -5); + var bottomIntermediateUpperUpperLayer = new Point2D(middleXCoordinate, -5); + var bottomIntermediateLowerUpperLayer = new Point2D(middleXCoordinate, -10); + var bottomLeftUpperLayer = new Point2D(leftCoordinate, -10); + var soilLayer = new SoilLayer2D + { + GeometrySurface = new GeometrySurface + { + OuterLoop = new GeometryLoop + { + CurveList = + { + new GeometryCurve(topLeftUpperLayer, topRightUpperLayer), + new GeometryCurve(topRightUpperLayer, bottomRightUpperLayer), + new GeometryCurve(bottomRightUpperLayer, bottomIntermediateUpperUpperLayer), + new GeometryCurve(bottomIntermediateUpperUpperLayer, bottomIntermediateLowerUpperLayer), + new GeometryCurve(bottomIntermediateLowerUpperLayer, bottomLeftUpperLayer), + new GeometryCurve(bottomLeftUpperLayer, topLeftUpperLayer) + } + } + } + }; + SoilLayer2D soilLayerAquiferInBetweenLeft = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, middleXCoordinate, true); + SoilLayer2D soilLayerAquiferInBetweenRight = CreateRectangularSoilLayer2D(-5, -20, middleXCoordinate, rightCoordinate, true); + SoilLayer2D soilLayerInBetween = CreateRectangularSoilLayer2D(-20, -25, leftCoordinate, rightCoordinate, false); + SoilLayer2D soilLayerAquiferBottom = CreateRectangularSoilLayer2D(-25, -30, leftCoordinate, rightCoordinate, true); + soilLayerAquiferBottom.IsAquifer = true; + + var soilProfile = new SoilProfile2D + { + Geometry = new GeometryData + { + Left = leftCoordinate, + Right = rightCoordinate, + Bottom = -30 + } + }; + soilProfile.Surfaces.Add(soilLayer); + soilProfile.Surfaces.Add(soilLayerAquiferInBetweenLeft); + soilProfile.Surfaces.Add(soilLayerAquiferInBetweenRight); + soilProfile.Surfaces.Add(soilLayerInBetween); + soilProfile.Surfaces.Add(soilLayerAquiferBottom); + soilProfile.Geometry.Surfaces.Add(new GeometrySurface()); + + // Call + bool isInBetweenLayerPresent = SoilProfile2DHelper.AreInBetweenAquiferClustersPresent(soilProfile, out int count); + SoilProfile2DHelper.DetermineAquiferLayerBoundaryPoints(SoilProfile2DHelper.LayerType.InBetweenAquiferCluster, soilProfile, out Point2D[] topLevelInBetweenAquifer, out Point2D[] bottomLevelInBetweenAquifer, 0); + + // Assert + Assert.Multiple(() => + { + Assert.That(isInBetweenLayerPresent, Is.True); + Assert.That(count, Is.EqualTo(1)); + }); + GeometryPoint[] expectedTop = + [ + new(leftCoordinate, -10), + new(middleXCoordinate, -10), + new(middleXCoordinate, -5), + new(rightCoordinate, -5) + ]; + GeometryPoint[] expectedBottom = + [ + new(leftCoordinate, -20), + new(middleXCoordinate, -20), + new(middleXCoordinate, -20), + new(rightCoordinate, -20) + ]; + AssertGeometry(topLevelInBetweenAquifer, expectedTop); + AssertGeometry(bottomLevelInBetweenAquifer, expectedBottom); + } + + /// + /// ------------------------------------------------------------- Level 0 m + /// top layer + /// + /// -----------------------------| Level -10 m + /// in-between aquifer | + /// layer left |------------------------------- Level -15 m + /// | in-between aquifer layer right + /// | ------------------------------ Level -17 m + /// | + /// | + /// -----------------------------| Level -20 m + /// in-between layer + /// ------------------------------------------------------------- Level -25 m + /// bottom aquifer layer + /// ------------------------------------------------------------- Level -30 m + /// + [Test] + public void Given2DSoilProfileWithContinuousInBetweenAquiferConsistingOfTwoLayersWithRightLayerEnvelopedByLeftLayer_WhenDeterminingLayerBoundaryPointsOfInBetweenAquifer_ThenExpectedCoordinatesReturned() + { + // Setup + var topLeftUpperLayer = new Point2D(leftCoordinate, 0); + var topRightUpperLayer = new Point2D(rightCoordinate, 0); + var bottomRightUpperLayer = new Point2D(rightCoordinate, -15); + var bottomIntermediateLowerUpperLayer = new Point2D(middleXCoordinate, -15); + var bottomIntermediateUpperUpperLayer = new Point2D(middleXCoordinate, -10); + var bottomLeftUpperLayer = new Point2D(leftCoordinate, -10); + var soilLayer = new SoilLayer2D + { + GeometrySurface = new GeometrySurface + { + OuterLoop = new GeometryLoop + { + CurveList = + { + new GeometryCurve(topLeftUpperLayer, topRightUpperLayer), + new GeometryCurve(topRightUpperLayer, bottomRightUpperLayer), + new GeometryCurve(bottomRightUpperLayer, bottomIntermediateLowerUpperLayer), + new GeometryCurve(bottomIntermediateLowerUpperLayer, bottomIntermediateUpperUpperLayer), + new GeometryCurve(bottomIntermediateUpperUpperLayer, bottomLeftUpperLayer), + new GeometryCurve(bottomLeftUpperLayer, topLeftUpperLayer) + } + } + } + }; + SoilLayer2D soilLayerAquiferInBetweenLeft = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, middleXCoordinate, true); + SoilLayer2D soilLayerAquiferInBetweenRight = CreateRectangularSoilLayer2D(-15, -17, middleXCoordinate, rightCoordinate, true); + + var bottomLeftInBetweenAquiferLayerLeft = new Point2D(leftCoordinate, -20); + var bottomRightInBetweenAquiferLayerLeft = new Point2D(middleXCoordinate, -20); + var bottomLeftInBetweenAquiferLayerRight = new Point2D(middleXCoordinate, -17); + var bottomRightInBetweenAquiferLayerRight = new Point2D(rightCoordinate, -17); + var bottomRightInBetweenLayer = new Point2D(rightCoordinate, -25); + var bottomLeftInBetweenLayer = new Point2D(leftCoordinate, -25); + var soilLayerInBetween = new SoilLayer2D + { + GeometrySurface = new GeometrySurface + { + OuterLoop = new GeometryLoop + { + CurveList = + { + new GeometryCurve(bottomLeftInBetweenAquiferLayerLeft, bottomRightInBetweenAquiferLayerLeft), + new GeometryCurve(bottomRightInBetweenAquiferLayerLeft, bottomLeftInBetweenAquiferLayerRight), + new GeometryCurve(bottomLeftInBetweenAquiferLayerRight, bottomRightInBetweenAquiferLayerRight), + new GeometryCurve(bottomRightInBetweenAquiferLayerRight, bottomRightInBetweenLayer), + new GeometryCurve(bottomRightInBetweenLayer, bottomLeftInBetweenLayer), + new GeometryCurve(bottomLeftInBetweenLayer, bottomLeftInBetweenAquiferLayerLeft) + } + } + } + }; + SoilLayer2D soilLayerAquiferBottom = CreateRectangularSoilLayer2D(-25, -30, leftCoordinate, rightCoordinate, true); + + var soilProfile = new SoilProfile2D + { + Geometry = new GeometryData + { + Left = leftCoordinate, + Right = rightCoordinate, + Bottom = -30 + } + }; + soilProfile.Surfaces.Add(soilLayer); + soilProfile.Surfaces.Add(soilLayerAquiferInBetweenLeft); + soilProfile.Surfaces.Add(soilLayerAquiferInBetweenRight); + soilProfile.Surfaces.Add(soilLayerInBetween); + soilProfile.Surfaces.Add(soilLayerAquiferBottom); + soilProfile.Geometry.Surfaces.Add(new GeometrySurface()); + + // Call + bool isInBetweenLayerPresent = SoilProfile2DHelper.AreInBetweenAquiferClustersPresent(soilProfile, out int count); + SoilProfile2DHelper.DetermineAquiferLayerBoundaryPoints(SoilProfile2DHelper.LayerType.InBetweenAquiferCluster, soilProfile, out Point2D[] topLevelInBetweenAquifer, out Point2D[] bottomLevelInBetweenAquifer, 0); + + // Assert + Assert.Multiple(() => + { + Assert.That(isInBetweenLayerPresent, Is.True); + Assert.That(count, Is.EqualTo(1)); + }); + GeometryPoint[] expectedTop = + [ + new(leftCoordinate, -10), + new(middleXCoordinate, -10), + new(middleXCoordinate, -15), + new(rightCoordinate, -15) + ]; + GeometryPoint[] expectedBottom = + [ + new(leftCoordinate, -20), + new(middleXCoordinate, -20), + new(middleXCoordinate, -17), + new(rightCoordinate, -17) + ]; + AssertGeometry(topLevelInBetweenAquifer, expectedTop); + AssertGeometry(bottomLevelInBetweenAquifer, expectedBottom); + } + + /// + /// ------------------------------------------------------------- Level 0 m + /// top layer + /// |------------------------------ Level -5 m + /// | in-between aquifer + /// ------------------------------| layer right Level -10 m + /// in-between aquifer layer left | + /// | + /// ------------------------------| Level -20 m + /// in-between layer | + /// |------------------------------ Level -21 m + /// + /// ------------------------------------------------------------- Level -25 m + /// bottom aquifer layer + /// ------------------------------------------------------------- Level -30 m + /// + [Test] + public void Given2DSoilProfileWithContinuousInBetweenAquiferConsistingOfTwoLayersWithLeftLayerEnvelopedByRightLayer_WhenDeterminingLayerBoundaryPointsOfInBetweenAquifer_ThenExpectedCoordinatesReturned() + { + // Setup + var topLeftUpperLayer = new Point2D(leftCoordinate, 0); + var topRightUpperLayer = new Point2D(rightCoordinate, 0); + var bottomRightUpperLayer = new Point2D(rightCoordinate, -5); + var bottomIntermediateLowerUpperLayer = new Point2D(middleXCoordinate, -5); + var bottomIntermediateUpperUpperLayer = new Point2D(middleXCoordinate, -10); + var bottomLeftUpperLayer = new Point2D(leftCoordinate, -10); + var soilLayer = new SoilLayer2D + { + GeometrySurface = new GeometrySurface + { + OuterLoop = new GeometryLoop + { + CurveList = + { + new GeometryCurve(topLeftUpperLayer, topRightUpperLayer), + new GeometryCurve(topRightUpperLayer, bottomRightUpperLayer), + new GeometryCurve(bottomRightUpperLayer, bottomIntermediateLowerUpperLayer), + new GeometryCurve(bottomIntermediateLowerUpperLayer, bottomIntermediateUpperUpperLayer), + new GeometryCurve(bottomIntermediateUpperUpperLayer, bottomLeftUpperLayer), + new GeometryCurve(bottomLeftUpperLayer, topLeftUpperLayer) + } + } + } + }; + + SoilLayer2D soilLayerAquiferInBetweenLeft = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, middleXCoordinate, true); + SoilLayer2D soilLayerAquiferInBetweenRight = CreateRectangularSoilLayer2D(-5, -21, middleXCoordinate, rightCoordinate, true); + + var bottomLeftInBetweenAquiferLayerLeft = new Point2D(leftCoordinate, -20); + var bottomRightInBetweenAquiferLayerLeft = new Point2D(middleXCoordinate, -20); + var bottomLeftInBetweenAquiferLayerRight = new Point2D(middleXCoordinate, -21); + var bottomRightInBetweenAquiferLayerRight = new Point2D(rightCoordinate, -21); + var bottomRightInBetweenLayer = new Point2D(rightCoordinate, -25); + var bottomLeftInBetweenLayer = new Point2D(leftCoordinate, -25); + var soilLayerInBetween = new SoilLayer2D + { + GeometrySurface = new GeometrySurface + { + OuterLoop = new GeometryLoop + { + CurveList = + { + new GeometryCurve(bottomLeftInBetweenAquiferLayerLeft, bottomRightInBetweenAquiferLayerLeft), + new GeometryCurve(bottomRightInBetweenAquiferLayerLeft, bottomLeftInBetweenAquiferLayerRight), + new GeometryCurve(bottomLeftInBetweenAquiferLayerRight, bottomRightInBetweenAquiferLayerRight), + new GeometryCurve(bottomRightInBetweenAquiferLayerRight, bottomRightInBetweenLayer), + new GeometryCurve(bottomRightInBetweenLayer, bottomLeftInBetweenLayer), + new GeometryCurve(bottomLeftInBetweenLayer, bottomLeftInBetweenAquiferLayerLeft) + } + } + } + }; + SoilLayer2D soilLayerAquiferBottom = CreateRectangularSoilLayer2D(-25, -30, leftCoordinate, rightCoordinate, true); + + var soilProfile = new SoilProfile2D + { + Geometry = new GeometryData + { + Left = leftCoordinate, + Right = rightCoordinate, + Bottom = -30 + } + }; + soilProfile.Surfaces.Add(soilLayer); + soilProfile.Surfaces.Add(soilLayerAquiferInBetweenLeft); + soilProfile.Surfaces.Add(soilLayerAquiferInBetweenRight); + soilProfile.Surfaces.Add(soilLayerInBetween); + soilProfile.Surfaces.Add(soilLayerAquiferBottom); + soilProfile.Geometry.Surfaces.Add(new GeometrySurface()); + + // Call + bool isInBetweenLayerPresent = SoilProfile2DHelper.AreInBetweenAquiferClustersPresent(soilProfile, out int count); + SoilProfile2DHelper.DetermineAquiferLayerBoundaryPoints(SoilProfile2DHelper.LayerType.InBetweenAquiferCluster, soilProfile, out Point2D[] topLevelInBetweenAquifer, out Point2D[] bottomLevelInBetweenAquifer, 0); + + // Assert + Assert.Multiple(() => + { + Assert.That(isInBetweenLayerPresent, Is.True); + Assert.That(count, Is.EqualTo(1)); + }); + GeometryPoint[] expectedTop = + [ + new(leftCoordinate, -10), + new(middleXCoordinate, -10), + new(middleXCoordinate, -5), + new(rightCoordinate, -5) + ]; + GeometryPoint[] expectedBottom = + [ + new(leftCoordinate, -20), + new(middleXCoordinate, -20), + new(middleXCoordinate, -21), + new(rightCoordinate, -21) + ]; + AssertGeometry(topLevelInBetweenAquifer, expectedTop); + AssertGeometry(bottomLevelInBetweenAquifer, expectedBottom); + } + + /// + /// --------------------------------------------------- Level 0 m + /// top layer + /// -------------------------|------------------------- Level -10 m + /// in-between layer left | in-between layer right + /// -------------------------|------------------------- Level -20 m + /// in-between layer + /// --------------------------------------------------- Level -25 m + /// bottom aquifer layer + /// --------------------------------------------------- Level -30 m + /// + [Test] + [TestCase(true, false)] + [TestCase(false, true)] + public void Given2DSoilProfileWithDiscontinuousInBetweenAquifer_WhenDeterminingInBetweenAquiferCount_ThenReturns0( + bool isLeftInBetweenLayerAquifer, bool isRightInBetweenLayerAquifer) + { + // Setup + SoilLayer2D soilLayer = CreateRectangularSoilLayer2D(0, -10, leftCoordinate, rightCoordinate, false); + SoilLayer2D soilLayerAquiferInBetweenLeft = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, middleXCoordinate, isLeftInBetweenLayerAquifer); + SoilLayer2D soilLayerAquiferInBetweenRight = CreateRectangularSoilLayer2D(-10, -20, middleXCoordinate, rightCoordinate, isRightInBetweenLayerAquifer); + SoilLayer2D soilLayerInBetween = CreateRectangularSoilLayer2D(-20, -25, leftCoordinate, rightCoordinate, false); + SoilLayer2D soilLayerAquiferBottom = CreateRectangularSoilLayer2D(-25, -30, leftCoordinate, rightCoordinate, true); + + var soilProfile = new SoilProfile2D + { + Geometry = new GeometryData + { + Left = leftCoordinate, + Right = rightCoordinate, + Bottom = -30 + } + }; + soilProfile.Surfaces.Add(soilLayer); + soilProfile.Surfaces.Add(soilLayerAquiferInBetweenLeft); + soilProfile.Surfaces.Add(soilLayerAquiferInBetweenRight); + soilProfile.Surfaces.Add(soilLayerInBetween); + soilProfile.Surfaces.Add(soilLayerAquiferBottom); + + // Call + bool isInBetweenLayerPresent = SoilProfile2DHelper.AreInBetweenAquiferClustersPresent(soilProfile, out int count); + + // Assert + Assert.Multiple(() => + { + Assert.That(isInBetweenLayerPresent, Is.False); + Assert.That(count, Is.EqualTo(0)); + }); + } + + /// + /// Many test cases are considered using a different soil profile 2D: + /// Within the method below, pictures of these soil profiles 2D are drawn to get a better idea of the test. + /// + private static IEnumerable GetSoilProfilesWithContinuousBottomAquiferLayer() + { + // TEST CASE 1: "Right aquifer fully adjoins left aquifer" + // + // ------------------------------------------------------------- Level 0 m + // Upper layer (not aquifer) + // -----------------------------|------------------------------- Level -10 m + // Bottom aquifer layer left | Bottom aquifer layer right + // -----------------------------|------------------------------- Level -20 m + SoilLayer2D soilUpperLayer = CreateRectangularSoilLayer2D(0, -10, leftCoordinate, rightCoordinate, false); + SoilLayer2D soilBottomLayerAquiferPartLeft = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, middleXCoordinate, true); + SoilLayer2D soilBottomLayerAquiferPartRight = CreateRectangularSoilLayer2D(-10, -20, middleXCoordinate, rightCoordinate, true); + var soilProfileFullAdjoin = new SoilProfile2D + { + Geometry = new GeometryData + { + Left = leftCoordinate, + Right = rightCoordinate, + Bottom = -20 + } + }; + soilProfileFullAdjoin.Surfaces.Add(soilUpperLayer); + soilProfileFullAdjoin.Surfaces.Add(soilBottomLayerAquiferPartLeft); + soilProfileFullAdjoin.Surfaces.Add(soilBottomLayerAquiferPartRight); + soilProfileFullAdjoin.Geometry.Surfaces.Add(new GeometrySurface()); + + yield return new TestCaseData(soilProfileFullAdjoin, new[] + { + new GeometryPoint(leftCoordinate, -10), + new GeometryPoint(middleXCoordinate, -10), + new GeometryPoint(rightCoordinate, -10) + }).SetName("Right aquifer fully adjoins left aquifer"); + + // TEST CASE 2: "One aquifer bottom layer" + // + // ------------------------------------------------------------- Level 0 m + // Upper layer (not aquifer) + // ------------------------------------------------------------- Level -10 m + // Bottom aquifer layer + // ------------------------------------------------------------- Level -20 m + SoilLayer2D soilBottomLayerAquifer = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, rightCoordinate, true); + var soilProfileOneBottomAquiferLayer = new SoilProfile2D + { + Geometry = new GeometryData + { + Left = leftCoordinate, + Right = rightCoordinate, + Bottom = -20 + } + }; + soilProfileOneBottomAquiferLayer.Surfaces.Add(soilUpperLayer); + soilProfileOneBottomAquiferLayer.Surfaces.Add(soilBottomLayerAquifer); + soilProfileOneBottomAquiferLayer.Geometry.Surfaces.Add(new GeometrySurface()); + + yield return new TestCaseData(soilProfileOneBottomAquiferLayer, new[] + { + new GeometryPoint(leftCoordinate, -10), + new GeometryPoint(rightCoordinate, -10) + }).SetName("One aquifer bottom layer"); + + // TEST CASE 3: "Right aquifer only bottom in range" + // + // ------------------------------------------------------------- Level 0 m + // Upper layer (not aquifer) |------------------------------ Level -5 m + // ------------------------------| Right bottom aquifer layer Level -10 m + // Left bottom aquifer layer |------------------------------ Level -15 m + // ------------------------------| Level -20 m + soilBottomLayerAquiferPartRight = CreateRectangularSoilLayer2D(-5, -15, middleXCoordinate, rightCoordinate, true); + var soilProfileRightSoilLayerBottomInRange = new SoilProfile2D + { + Geometry = new GeometryData + { + Left = leftCoordinate, + Right = rightCoordinate, + Bottom = -20 + } + }; + soilProfileRightSoilLayerBottomInRange.Surfaces.Add(soilUpperLayer); + soilProfileRightSoilLayerBottomInRange.Surfaces.Add(soilBottomLayerAquiferPartLeft); + soilProfileRightSoilLayerBottomInRange.Surfaces.Add(soilBottomLayerAquiferPartRight); + soilProfileRightSoilLayerBottomInRange.Geometry.Surfaces.Add(new GeometrySurface()); + + yield return new TestCaseData(soilProfileRightSoilLayerBottomInRange, new[] + { + new GeometryPoint(leftCoordinate, -10), + new GeometryPoint(middleXCoordinate, -10), + new GeometryPoint(middleXCoordinate, -5), + new GeometryPoint(rightCoordinate, -5) + }).SetName("Right aquifer only bottom in range"); + + // TEST CASE 4: "Right aquifer only top in range" + // + // ------------------------------------------------------------- Level 0 m + // Upper layer (not aquifer) | + // ------------------------------|------------------------------ Level -10 m + // Left bottom aquifer layer |------------------------------ Level -15 m + // ------------------------------| Right bottom aquifer layer | Level -20 m + // |------------------------------ Level -25 m + soilBottomLayerAquiferPartRight = CreateRectangularSoilLayer2D(-15, -25, middleXCoordinate, rightCoordinate, true); + + var soilProfileRightSoilLayerTopInRange = new SoilProfile2D + { + Geometry = new GeometryData + { + Left = leftCoordinate, + Right = rightCoordinate, + Bottom = -25 + } + }; + soilProfileRightSoilLayerTopInRange.Surfaces.Add(soilUpperLayer); + soilProfileRightSoilLayerTopInRange.Surfaces.Add(soilBottomLayerAquiferPartLeft); + soilProfileRightSoilLayerTopInRange.Surfaces.Add(soilBottomLayerAquiferPartRight); + soilProfileRightSoilLayerTopInRange.Geometry.Surfaces.Add(new GeometrySurface()); + + yield return new TestCaseData(soilProfileRightSoilLayerTopInRange, new[] + { + new GeometryPoint(leftCoordinate, -10), + new GeometryPoint(middleXCoordinate, -10), + new GeometryPoint(middleXCoordinate, -15), + new GeometryPoint(rightCoordinate, -15) + }).SetName("Right aquifer only top in range"); + + // TEST CASE 5: "Right aquifer fully envelopes left aquifer" + // + // ------------------------------------------------------------- Level 0 m + // Upper layer (not aquifer) |------------------------------ Level -5 m + // ------------------------------| Level -10 m + // Left bottom aquifer layer | Right bottom aquifer layer Level -15 m + // ------------------------------| Level -20 m + // |------------------------------ Level -25 m + soilBottomLayerAquiferPartRight = CreateRectangularSoilLayer2D(-5, -25, middleXCoordinate, rightCoordinate, true); + var soilProfileRightAquiferLayerFullyEnvelopsLeft = new SoilProfile2D + { + Geometry = new GeometryData + { + Left = leftCoordinate, + Right = rightCoordinate, + Bottom = -25 + } + }; + soilProfileRightAquiferLayerFullyEnvelopsLeft.Surfaces.Add(soilUpperLayer); + soilProfileRightAquiferLayerFullyEnvelopsLeft.Surfaces.Add(soilBottomLayerAquiferPartLeft); + soilProfileRightAquiferLayerFullyEnvelopsLeft.Surfaces.Add(soilBottomLayerAquiferPartRight); + soilProfileRightAquiferLayerFullyEnvelopsLeft.Geometry.Surfaces.Add(new GeometrySurface()); + + yield return new TestCaseData(soilProfileRightAquiferLayerFullyEnvelopsLeft, new[] + { + new GeometryPoint(leftCoordinate, -10), + new GeometryPoint(middleXCoordinate, -10), + new GeometryPoint(middleXCoordinate, -5), + new GeometryPoint(rightCoordinate, -5) + }).SetName("Right aquifer fully envelopes left aquifer"); + + // TEST CASE 6: "Right aquifer fully enveloped by left aquifer" + // + // ------------------------------------------------------------- Level 0 m + // Upper layer (not aquifer) | + // ------------------------------|------------------------------ Level -10 m + // Left bottom aquifer layer |------------------------------ Level -15 m + // | Right bottom aquifer layer | + // |------------------------------ Level -17 m + // ------------------------------| Level -20 m + soilBottomLayerAquiferPartRight = CreateRectangularSoilLayer2D(-15, -17, middleXCoordinate, rightCoordinate, true); + var soilProfileRightAquiferLayerFullyEnvelopedByLeft = new SoilProfile2D + { + Geometry = new GeometryData + { + Left = leftCoordinate, + Right = rightCoordinate, + Bottom = -20 + } + }; + soilProfileRightAquiferLayerFullyEnvelopedByLeft.Surfaces.Add(soilUpperLayer); + soilProfileRightAquiferLayerFullyEnvelopedByLeft.Surfaces.Add(soilBottomLayerAquiferPartLeft); + soilProfileRightAquiferLayerFullyEnvelopedByLeft.Surfaces.Add(soilBottomLayerAquiferPartRight); + soilProfileRightAquiferLayerFullyEnvelopedByLeft.Geometry.Surfaces.Add(new GeometrySurface()); + + yield return new TestCaseData(soilProfileRightAquiferLayerFullyEnvelopedByLeft, new[] + { + new GeometryPoint(leftCoordinate, -10), + new GeometryPoint(middleXCoordinate, -10), + new GeometryPoint(middleXCoordinate, -15), + new GeometryPoint(rightCoordinate, -15) + }).SetName("Right aquifer fully enveloped by left aquifer"); + } + + private static SoilLayer2D CreateSoilLayer2D(Point2D topLeftCoordinate, Point2D topRightCoordinate, + Point2D bottomRightCoordinate, Point2D bottomLeftCoordinate, bool isAquifer) + { + return new SoilLayer2D + { + GeometrySurface = new GeometrySurface + { + OuterLoop = new GeometryLoop + { + CurveList = + { + new GeometryCurve(topLeftCoordinate, topRightCoordinate), + new GeometryCurve(topRightCoordinate, bottomRightCoordinate), + new GeometryCurve(bottomRightCoordinate, bottomLeftCoordinate), + new GeometryCurve(bottomLeftCoordinate, topLeftCoordinate) + } + } + }, + IsAquifer = isAquifer + }; + } + + /// + /// Creates a rectangular . + /// + /// + /// + /// + /// + /// + /// A rectangular . + private static SoilLayer2D CreateRectangularSoilLayer2D(double topCoord, double bottomCoord, double leftCoord, double rightCoord, bool isAquifer) + { + var topLeftPoint = new Point2D(leftCoord, topCoord); + var topRightPoint = new Point2D(rightCoord, topCoord); + var bottomRightPoint = new Point2D(rightCoord, bottomCoord); + var bottomLeftPoint = new Point2D(leftCoord, bottomCoord); + + return CreateSoilLayer2D(topLeftPoint, topRightPoint, bottomRightPoint, bottomLeftPoint, isAquifer); + } + + private static void AssertGeometry(IEnumerable expectedPoints, IEnumerable actualPoints) + { + GeometryPoint[] convertedPoints = expectedPoints.Select(p => new GeometryPoint(p.X, p.Z)).ToArray(); + AssertGeometry(convertedPoints, actualPoints); + } + + private static void AssertGeometry(IEnumerable expectedPoints, IEnumerable actualPoints) + { + int expectedNrOfPoints = expectedPoints.Count(); + Assert.That(actualPoints.Count(), Is.EqualTo(expectedNrOfPoints)); + + for (var i = 0; i < expectedNrOfPoints; i++) + { + GeometryPoint expectedPoint = expectedPoints.ElementAt(i); + GeometryPoint actualPoint = actualPoints.ElementAt(i); + Assert.Multiple(() => + { + Assert.That(actualPoint.X, Is.EqualTo(expectedPoint.X).Within(precision5Decimals)); + Assert.That(actualPoint.Z, Is.EqualTo(expectedPoint.Z).Within(precision5Decimals)); + }); + } + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverterTests.cs =================================================================== diff -u -r5325 -r5382 --- DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverterTests.cs (.../PlLinesToWaternetConverterTests.cs) (revision 5325) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverterTests.cs (.../PlLinesToWaternetConverterTests.cs) (revision 5382) @@ -597,716 +597,9 @@ }); } - /// - /// Different soil profiles 2D are tested, see the pictures in method GetSoilProfilesWithContinuousBottomAquiferLayer, - /// with a top clay layer and bottom aquifer layers always forming a continuous bottom layer. - /// As there is only one aquifer, then the BottomAquifer is also the HighestAquifer. - /// - /// - /// - [Test] - [TestCaseSource(nameof(GetSoilProfilesWithContinuousBottomAquiferLayer))] - public void Given2DSoilProfileWithContinuousBottomAquifer_WhenDeterminingLayerBoundaryPointsOfBottomAquifer_ThenExpectedCoordinatesReturned( - SoilProfile2D soilProfile, IEnumerable expectedBottomAquiferCoordinates) - { - // Call - PlLinesToWaternetConverter.DetermineAquiferLayerBoundaryPoints(PlLinesToWaternetConverter.LayerType.BottomAquiferCluster, soilProfile, out Point2D[] bottomAquifer, out _); - PlLinesToWaternetConverter.DetermineAquiferLayerBoundaryPoints(PlLinesToWaternetConverter.LayerType.HighestAquiferCluster, soilProfile, out Point2D[] highestAquifer, out _); - - // Assert - AssertGeometry(bottomAquifer, expectedBottomAquiferCoordinates); - AssertGeometry(highestAquifer, expectedBottomAquiferCoordinates); - } - /// - /// |-------------------------------------------------------------| Level 0 m - /// | Aquitard 1 | - /// |-------------------------------------------------------------| Level -10 m - /// | Aquitard 2 B /------------------------\ C | Level -12 m - /// | / Aquifer 1 \ | - /// | / point X \ | - /// |---------------/--------------*---------------\--------------| Level -20 m - /// | A Aquifer 2 D | - /// |------------------------------*------------------------------| Level -30 m - /// point Y - /// - [Test] - [TestCase(-20,-30)] - [TestCase(-20.1,-30)] - [TestCase(-19.9,-30)] - [TestCase(-20,-30.1)] - [TestCase(-20,-29.9)] - public void Given2DSoilProfileWithNotContinuousAquiferConnectedToContinuousAquifer_WhenDeterminingLayerBoundaryPointsOfAquifer_ThenExpectedCoordinatesReturned(double zPointX, double zPointY) - { - var pointA = new Point2D(leftCoordinate + (rightCoordinate - leftCoordinate) / 3, -20); - var pointB = new Point2D(pointA.X + 0.01, -12); - var pointC = new Point2D(leftCoordinate + 2 * (rightCoordinate - leftCoordinate) / 3, -12); - var pointD = new Point2D(pointC.X + 0.01, -20); - var pointX = new Point2D(middleXCoordinate, zPointX); - var pointY = new Point2D(middleXCoordinate, zPointY); - - SoilLayer2D soilUpperLayer = CreateRectangularSoilLayer2D(0, -10, leftCoordinate, rightCoordinate, false); - SoilLayer2D soilLayerAquitard2 = FactoryForSoilProfiles.CreatePolygoneSoilLayer2D([ - ..new[] - { - new Point2D(leftCoordinate, -10), - new Point2D(rightCoordinate, -10), - new Point2D(rightCoordinate, -20), - pointD, - pointC, - pointB, - pointA, - new Point2D(leftCoordinate, -20) - } - ], null); - SoilLayer2D soilLayerAquifer1 = FactoryForSoilProfiles.CreatePentagonSoilLayer2D(pointA, pointB, pointC, pointD, pointX, null, null, true); - SoilLayer2D soilLayerAquifer2 = FactoryForSoilProfiles.CreatePolygoneSoilLayer2D([ - ..new[] - { - new Point2D(leftCoordinate, -20), - pointA, - pointX, - pointD, - new Point2D(rightCoordinate, -20), - new Point2D(rightCoordinate, -30), - pointY, - new Point2D(leftCoordinate, -30) - } - ], null, true); - - var soilProfile = new SoilProfile2D - { - Geometry = new GeometryData - { - Left = leftCoordinate, - Right = rightCoordinate, - Bottom = -30 - } - }; - soilProfile.Surfaces.Add(soilUpperLayer); - soilProfile.Surfaces.Add(soilLayerAquitard2); - soilProfile.Surfaces.Add(soilLayerAquifer1); - soilProfile.Surfaces.Add(soilLayerAquifer2); - soilProfile.Geometry.Surfaces.Add(new GeometrySurface()); - - // Call - PlLinesToWaternetConverter.DetermineAquiferLayerBoundaryPoints(PlLinesToWaternetConverter.LayerType.BottomAquiferCluster, soilProfile, out Point2D[] bottomAquifer, out _); - PlLinesToWaternetConverter.DetermineAquiferLayerBoundaryPoints(PlLinesToWaternetConverter.LayerType.HighestAquiferCluster, soilProfile, out Point2D[] highestAquifer, out _); - - // Assert - var expectedBottomAquiferCoordinates = new[] - { - new GeometryPoint(leftCoordinate, -20), - new GeometryPoint(pointA.X, pointA.Z), - new GeometryPoint(pointB.X, pointB.Z), - new GeometryPoint(middleXCoordinate, -12), - new GeometryPoint(pointC.X, pointC.Z), - new GeometryPoint(pointD.X, pointD.Z), - new GeometryPoint(rightCoordinate, -20) - }; - AssertGeometry(bottomAquifer, expectedBottomAquiferCoordinates); - AssertGeometry(highestAquifer, expectedBottomAquiferCoordinates); - } - - /// - /// --------------------------------------------------------------- Level 0 m - /// top layer - /// --------------------------------------------------------------- Level -10 m - /// in-between aquifer layer left | in-between aquifer layer right - /// --------------------------------------------------------------- Level -20 m - /// in-between layer - /// --------------------------------------------------------------- Level -25 m - /// bottom aquifer layer - /// --------------------------------------------------------------- Level -30 m - /// - [Test] - public void Given2DSoilProfileWithContinuousInBetweenAquiferLayerConsistingOfTwoParts_WhenDeterminingLayerBoundaryPointsOfInBetweenAquifer_ThenExpectedCoordinatesReturned() - { - // Setup - SoilLayer2D soilLayer = CreateRectangularSoilLayer2D(0, -10, leftCoordinate, rightCoordinate, false); - SoilLayer2D soilLayerAquiferInBetweenLeft = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, middleXCoordinate, true); - SoilLayer2D soilLayerAquiferInBetweenRight = CreateRectangularSoilLayer2D(-10, -20, middleXCoordinate, rightCoordinate, true); - SoilLayer2D soilLayerInBetween = CreateRectangularSoilLayer2D(-20, -25, leftCoordinate, rightCoordinate, false); - SoilLayer2D soilLayerAquiferBottom = CreateRectangularSoilLayer2D(-25, -30, leftCoordinate, rightCoordinate, true); - - var soilProfile = new SoilProfile2D - { - Geometry = new GeometryData - { - Left = leftCoordinate, - Right = rightCoordinate, - Bottom = -30 - } - }; - soilProfile.Surfaces.Add(soilLayer); - soilProfile.Surfaces.Add(soilLayerAquiferInBetweenLeft); - soilProfile.Surfaces.Add(soilLayerAquiferInBetweenRight); - soilProfile.Surfaces.Add(soilLayerInBetween); - soilProfile.Surfaces.Add(soilLayerAquiferBottom); - soilProfile.Geometry.Surfaces.Add(new GeometrySurface()); - - // Call - bool isInBetweenLayerPresent = PlLinesToWaternetConverter.AreInBetweenAquiferClustersPresent(soilProfile, out int count); - PlLinesToWaternetConverter.DetermineAquiferLayerBoundaryPoints(PlLinesToWaternetConverter.LayerType.InBetweenAquiferCluster, soilProfile, out Point2D[] topLevelInBetweenAquifer, out Point2D[] bottomLevelInBetweenAquifer, 0); - - // Assert - Assert.Multiple(() => - { - Assert.That(isInBetweenLayerPresent, Is.True); - Assert.That(count, Is.EqualTo(1)); - }); - GeometryPoint[] expectedTop = - [ - new(leftCoordinate, -10), - new(middleXCoordinate, -10), - new(rightCoordinate, -10) - ]; - GeometryPoint[] expectedBottom = - [ - new(leftCoordinate, -20), - new(middleXCoordinate, -20), - new(rightCoordinate, -20) - ]; - AssertGeometry(topLevelInBetweenAquifer, expectedTop); - AssertGeometry(bottomLevelInBetweenAquifer, expectedBottom); - } - - /// /// --------------------------------------------------- Level 0 m - /// top layer - /// --------------------------------------------------- Level -10 m - /// in-between aquifer layer - /// --------------------------------------------------- Level -20 m - /// in-between layer - /// --------------------------------------------------- Level -25 m - /// bottom aquifer layer - /// --------------------------------------------------- Level -30 m - /// - [Test] - public void Given2DSoilProfileWithContinuousInBetweenAquiferLayerConsistingOfOnePart_WhenDeterminingLayerBoundaryPointsOfInBetweenAquifer_ThenExpectedCoordinatesReturned() - { - // Setup - SoilLayer2D soilLayer = CreateRectangularSoilLayer2D(0, -10, leftCoordinate, rightCoordinate, false); - SoilLayer2D soilLayerAquiferInBetween = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, rightCoordinate, true); - SoilLayer2D soilLayerInBetween = CreateRectangularSoilLayer2D(-20, -25, leftCoordinate, rightCoordinate, false); - SoilLayer2D soilLayerAquiferBottom = CreateRectangularSoilLayer2D(-25, -30, leftCoordinate, rightCoordinate, true); - - var soilProfile = new SoilProfile2D - { - Geometry = new GeometryData - { - Left = leftCoordinate, - Right = rightCoordinate, - Bottom = -30 - } - }; - soilProfile.Surfaces.Add(soilLayer); - soilProfile.Surfaces.Add(soilLayerAquiferInBetween); - soilProfile.Surfaces.Add(soilLayerInBetween); - soilProfile.Surfaces.Add(soilLayerAquiferBottom); - soilProfile.Geometry.Surfaces.Add(new GeometrySurface()); - - // Call - bool isInBetweenLayerPresent = PlLinesToWaternetConverter.AreInBetweenAquiferClustersPresent(soilProfile, out int count); - PlLinesToWaternetConverter.DetermineAquiferLayerBoundaryPoints(PlLinesToWaternetConverter.LayerType.InBetweenAquiferCluster, soilProfile, out Point2D[] topLevelInBetweenAquifer, out Point2D[] bottomLevelInBetweenAquifer, 0); - - // Assert - Assert.Multiple(() => - { - Assert.That(isInBetweenLayerPresent, Is.True); - Assert.That(count, Is.EqualTo(1)); - }); - GeometryPoint[] expectedTop = - [ - new(leftCoordinate, -10), - new(rightCoordinate, -10) - ]; - GeometryPoint[] expectedBottom = - [ - new(leftCoordinate, -20), - new(rightCoordinate, -20) - ]; - AssertGeometry(topLevelInBetweenAquifer, expectedTop); - AssertGeometry(bottomLevelInBetweenAquifer, expectedBottom); - } - - /// - /// ------------------------------------------------------------- Level 0 m - /// top layer - /// ------------------------------------------------------------- Level -10 m - /// | Gap layer - /// in-between aquifer |------------------------------- Level -15 m - /// layer left | - /// | in-between aquifer - /// -----------------------------| layer right Level -20 m - /// in-between layer | - /// |------------------------------- Level -21 m /// - /// ------------------------------------------------------------- Level -25 m - /// bottom aquifer layer - /// ------------------------------------------------------------- Level -30 m - /// - [Test] - public void Given2DSoilProfileWithContinuousInBetweenAquiferConsistingOfTwoLayersWithRightLayerTopLevelInRangeLeftLayer_WhenDeterminingLayerBoundaryPointsOfInBetweenAquifer_ThenExpectedCoordinatesReturned() - { - // Setup - SoilLayer2D soilLayer = CreateRectangularSoilLayer2D(0, -10, leftCoordinate, rightCoordinate, false); - SoilLayer2D soilLayerAquiferInBetweenLeft = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, middleXCoordinate, true); - // Add a layer to fill the gap between the right aquifer layer and the upper layer - SoilLayer2D soilLayerGap = CreateRectangularSoilLayer2D(-10, -15, middleXCoordinate, rightCoordinate, false); - SoilLayer2D soilLayerAquiferInBetweenRight = CreateRectangularSoilLayer2D(-15, -21, middleXCoordinate, rightCoordinate, true); - // Add continuous in-between layer - var bottomLeftInBetweenAquiferLayerLeft = new Point2D(leftCoordinate, -20); - var bottomRightInBetweenAquiferLayerLeft = new Point2D(middleXCoordinate, -20); - var bottomLeftInBetweenAquiferLayerRight = new Point2D(middleXCoordinate, -21); - var bottomRightInBetweenAquiferLayerRight = new Point2D(rightCoordinate, -21); - var bottomRightInBetweenLayer = new Point2D(rightCoordinate, -25); - var bottomLeftInBetweenLayer = new Point2D(leftCoordinate, -25); - var soilLayerInBetween = new SoilLayer2D - { - GeometrySurface = new GeometrySurface - { - OuterLoop = new GeometryLoop - { - CurveList = - { - new GeometryCurve(bottomLeftInBetweenAquiferLayerLeft, bottomRightInBetweenAquiferLayerLeft), - new GeometryCurve(bottomRightInBetweenAquiferLayerLeft, bottomLeftInBetweenAquiferLayerRight), - new GeometryCurve(bottomLeftInBetweenAquiferLayerRight, bottomRightInBetweenAquiferLayerRight), - new GeometryCurve(bottomRightInBetweenAquiferLayerRight, bottomRightInBetweenLayer), - new GeometryCurve(bottomRightInBetweenLayer, bottomLeftInBetweenLayer) - } - } - } - }; - SoilLayer2D soilLayerAquiferBottom = CreateRectangularSoilLayer2D(-25, -30, leftCoordinate, rightCoordinate, true); - - var soilProfile = new SoilProfile2D - { - Geometry = new GeometryData - { - Left = leftCoordinate, - Right = rightCoordinate, - Bottom = -30 - } - }; - soilProfile.Surfaces.Add(soilLayer); - soilProfile.Surfaces.Add(soilLayerAquiferInBetweenLeft); - soilProfile.Surfaces.Add(soilLayerGap); - soilProfile.Surfaces.Add(soilLayerAquiferInBetweenRight); - soilProfile.Surfaces.Add(soilLayerInBetween); - soilProfile.Surfaces.Add(soilLayerAquiferBottom); - soilProfile.Geometry.Surfaces.Add(new GeometrySurface()); - - // Call - bool isInBetweenLayerPresent = PlLinesToWaternetConverter.AreInBetweenAquiferClustersPresent(soilProfile, out int count); - PlLinesToWaternetConverter.DetermineAquiferLayerBoundaryPoints(PlLinesToWaternetConverter.LayerType.InBetweenAquiferCluster, soilProfile, out Point2D[] topLevelInBetweenAquifer, out Point2D[] bottomLevelInBetweenAquifer, 0); - - // Assert - Assert.Multiple(() => - { - Assert.That(isInBetweenLayerPresent, Is.True); - Assert.That(count, Is.EqualTo(1)); - }); - GeometryPoint[] expectedTop = - [ - new(leftCoordinate, -10), - new(middleXCoordinate, -10), - new(middleXCoordinate, -15), - new(rightCoordinate, -15) - ]; - GeometryPoint[] expectedBottom = - [ - new(leftCoordinate, -20), - new(middleXCoordinate, -20), - new(middleXCoordinate, -21), - new(rightCoordinate, -21) - ]; - AssertGeometry(topLevelInBetweenAquifer, expectedTop); - AssertGeometry(bottomLevelInBetweenAquifer, expectedBottom); - } - - /// - /// -------------------------------------------------------------- Level 0 m - /// top layer - /// |------------------------------- Level -5 m - /// | - /// | in-between aquifer - /// ------------------------------| layer right Level -10 m - /// in-between aquifer layer left | - /// ------------------------------------------------------------- Level -20 m - /// in-between layer - /// -------------------------------------------------------------- Level -25 m - /// bottom aquifer layer - /// -------------------------------------------------------------- Level -30 m - /// - [Test] - public void Given2DSoilProfileWithContinuousInBetweenAquiferConsistingOfTwoLayersWithRightLayerBottomLevelInRangeLeftLayer_WhenDeterminingLayerBoundaryPointsOfInBetweenAquifer_ThenExpectedCoordinatesReturned() - { - // Setup - var topLeftUpperLayer = new Point2D(leftCoordinate, 0); - var topRightUpperLayer = new Point2D(rightCoordinate, 0); - var bottomRightUpperLayer = new Point2D(rightCoordinate, -5); - var bottomIntermediateUpperUpperLayer = new Point2D(middleXCoordinate, -5); - var bottomIntermediateLowerUpperLayer = new Point2D(middleXCoordinate, -10); - var bottomLeftUpperLayer = new Point2D(leftCoordinate, -10); - var soilLayer = new SoilLayer2D - { - GeometrySurface = new GeometrySurface - { - OuterLoop = new GeometryLoop - { - CurveList = - { - new GeometryCurve(topLeftUpperLayer, topRightUpperLayer), - new GeometryCurve(topRightUpperLayer, bottomRightUpperLayer), - new GeometryCurve(bottomRightUpperLayer, bottomIntermediateUpperUpperLayer), - new GeometryCurve(bottomIntermediateUpperUpperLayer, bottomIntermediateLowerUpperLayer), - new GeometryCurve(bottomIntermediateLowerUpperLayer, bottomLeftUpperLayer), - new GeometryCurve(bottomLeftUpperLayer, topLeftUpperLayer) - } - } - } - }; - SoilLayer2D soilLayerAquiferInBetweenLeft = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, middleXCoordinate, true); - SoilLayer2D soilLayerAquiferInBetweenRight = CreateRectangularSoilLayer2D(-5, -20, middleXCoordinate, rightCoordinate, true); - SoilLayer2D soilLayerInBetween = CreateRectangularSoilLayer2D(-20, -25, leftCoordinate, rightCoordinate, false); - SoilLayer2D soilLayerAquiferBottom = CreateRectangularSoilLayer2D(-25, -30, leftCoordinate, rightCoordinate, true); - soilLayerAquiferBottom.IsAquifer = true; - - var soilProfile = new SoilProfile2D - { - Geometry = new GeometryData - { - Left = leftCoordinate, - Right = rightCoordinate, - Bottom = -30 - } - }; - soilProfile.Surfaces.Add(soilLayer); - soilProfile.Surfaces.Add(soilLayerAquiferInBetweenLeft); - soilProfile.Surfaces.Add(soilLayerAquiferInBetweenRight); - soilProfile.Surfaces.Add(soilLayerInBetween); - soilProfile.Surfaces.Add(soilLayerAquiferBottom); - soilProfile.Geometry.Surfaces.Add(new GeometrySurface()); - - // Call - bool isInBetweenLayerPresent = PlLinesToWaternetConverter.AreInBetweenAquiferClustersPresent(soilProfile, out int count); - PlLinesToWaternetConverter.DetermineAquiferLayerBoundaryPoints(PlLinesToWaternetConverter.LayerType.InBetweenAquiferCluster, soilProfile, out Point2D[] topLevelInBetweenAquifer, out Point2D[] bottomLevelInBetweenAquifer, 0); - - // Assert - Assert.Multiple(() => - { - Assert.That(isInBetweenLayerPresent, Is.True); - Assert.That(count, Is.EqualTo(1)); - }); - GeometryPoint[] expectedTop = - [ - new(leftCoordinate, -10), - new(middleXCoordinate, -10), - new(middleXCoordinate, -5), - new(rightCoordinate, -5) - ]; - GeometryPoint[] expectedBottom = - [ - new(leftCoordinate, -20), - new(middleXCoordinate, -20), - new(middleXCoordinate, -20), - new(rightCoordinate, -20) - ]; - AssertGeometry(topLevelInBetweenAquifer, expectedTop); - AssertGeometry(bottomLevelInBetweenAquifer, expectedBottom); - } - - /// - /// ------------------------------------------------------------- Level 0 m - /// top layer - /// - /// -----------------------------| Level -10 m - /// in-between aquifer | - /// layer left |------------------------------- Level -15 m - /// | in-between aquifer layer right - /// | ------------------------------ Level -17 m - /// | - /// | - /// -----------------------------| Level -20 m - /// in-between layer - /// ------------------------------------------------------------- Level -25 m - /// bottom aquifer layer - /// ------------------------------------------------------------- Level -30 m - /// - [Test] - public void Given2DSoilProfileWithContinuousInBetweenAquiferConsistingOfTwoLayersWithRightLayerEnvelopedByLeftLayer_WhenDeterminingLayerBoundaryPointsOfInBetweenAquifer_ThenExpectedCoordinatesReturned() - { - // Setup - var topLeftUpperLayer = new Point2D(leftCoordinate, 0); - var topRightUpperLayer = new Point2D(rightCoordinate, 0); - var bottomRightUpperLayer = new Point2D(rightCoordinate, -15); - var bottomIntermediateLowerUpperLayer = new Point2D(middleXCoordinate, -15); - var bottomIntermediateUpperUpperLayer = new Point2D(middleXCoordinate, -10); - var bottomLeftUpperLayer = new Point2D(leftCoordinate, -10); - var soilLayer = new SoilLayer2D - { - GeometrySurface = new GeometrySurface - { - OuterLoop = new GeometryLoop - { - CurveList = - { - new GeometryCurve(topLeftUpperLayer, topRightUpperLayer), - new GeometryCurve(topRightUpperLayer, bottomRightUpperLayer), - new GeometryCurve(bottomRightUpperLayer, bottomIntermediateLowerUpperLayer), - new GeometryCurve(bottomIntermediateLowerUpperLayer, bottomIntermediateUpperUpperLayer), - new GeometryCurve(bottomIntermediateUpperUpperLayer, bottomLeftUpperLayer), - new GeometryCurve(bottomLeftUpperLayer, topLeftUpperLayer) - } - } - } - }; - SoilLayer2D soilLayerAquiferInBetweenLeft = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, middleXCoordinate, true); - SoilLayer2D soilLayerAquiferInBetweenRight = CreateRectangularSoilLayer2D(-15, -17, middleXCoordinate, rightCoordinate, true); - - var bottomLeftInBetweenAquiferLayerLeft = new Point2D(leftCoordinate, -20); - var bottomRightInBetweenAquiferLayerLeft = new Point2D(middleXCoordinate, -20); - var bottomLeftInBetweenAquiferLayerRight = new Point2D(middleXCoordinate, -17); - var bottomRightInBetweenAquiferLayerRight = new Point2D(rightCoordinate, -17); - var bottomRightInBetweenLayer = new Point2D(rightCoordinate, -25); - var bottomLeftInBetweenLayer = new Point2D(leftCoordinate, -25); - var soilLayerInBetween = new SoilLayer2D - { - GeometrySurface = new GeometrySurface - { - OuterLoop = new GeometryLoop - { - CurveList = - { - new GeometryCurve(bottomLeftInBetweenAquiferLayerLeft, bottomRightInBetweenAquiferLayerLeft), - new GeometryCurve(bottomRightInBetweenAquiferLayerLeft, bottomLeftInBetweenAquiferLayerRight), - new GeometryCurve(bottomLeftInBetweenAquiferLayerRight, bottomRightInBetweenAquiferLayerRight), - new GeometryCurve(bottomRightInBetweenAquiferLayerRight, bottomRightInBetweenLayer), - new GeometryCurve(bottomRightInBetweenLayer, bottomLeftInBetweenLayer), - new GeometryCurve(bottomLeftInBetweenLayer, bottomLeftInBetweenAquiferLayerLeft) - } - } - } - }; - SoilLayer2D soilLayerAquiferBottom = CreateRectangularSoilLayer2D(-25, -30, leftCoordinate, rightCoordinate, true); - - var soilProfile = new SoilProfile2D - { - Geometry = new GeometryData - { - Left = leftCoordinate, - Right = rightCoordinate, - Bottom = -30 - } - }; - soilProfile.Surfaces.Add(soilLayer); - soilProfile.Surfaces.Add(soilLayerAquiferInBetweenLeft); - soilProfile.Surfaces.Add(soilLayerAquiferInBetweenRight); - soilProfile.Surfaces.Add(soilLayerInBetween); - soilProfile.Surfaces.Add(soilLayerAquiferBottom); - soilProfile.Geometry.Surfaces.Add(new GeometrySurface()); - - // Call - bool isInBetweenLayerPresent = PlLinesToWaternetConverter.AreInBetweenAquiferClustersPresent(soilProfile, out int count); - PlLinesToWaternetConverter.DetermineAquiferLayerBoundaryPoints(PlLinesToWaternetConverter.LayerType.InBetweenAquiferCluster, soilProfile, out Point2D[] topLevelInBetweenAquifer, out Point2D[] bottomLevelInBetweenAquifer, 0); - - // Assert - Assert.Multiple(() => - { - Assert.That(isInBetweenLayerPresent, Is.True); - Assert.That(count, Is.EqualTo(1)); - }); - GeometryPoint[] expectedTop = - [ - new(leftCoordinate, -10), - new(middleXCoordinate, -10), - new(middleXCoordinate, -15), - new(rightCoordinate, -15) - ]; - GeometryPoint[] expectedBottom = - [ - new(leftCoordinate, -20), - new(middleXCoordinate, -20), - new(middleXCoordinate, -17), - new(rightCoordinate, -17) - ]; - AssertGeometry(topLevelInBetweenAquifer, expectedTop); - AssertGeometry(bottomLevelInBetweenAquifer, expectedBottom); - } - - /// - /// ------------------------------------------------------------- Level 0 m - /// top layer - /// |------------------------------ Level -5 m - /// | in-between aquifer - /// ------------------------------| layer right Level -10 m - /// in-between aquifer layer left | - /// | - /// ------------------------------| Level -20 m - /// in-between layer | - /// |------------------------------ Level -21 m - /// - /// ------------------------------------------------------------- Level -25 m - /// bottom aquifer layer - /// ------------------------------------------------------------- Level -30 m - /// - [Test] - public void Given2DSoilProfileWithContinuousInBetweenAquiferConsistingOfTwoLayersWithLeftLayerEnvelopedByRightLayer_WhenDeterminingLayerBoundaryPointsOfInBetweenAquifer_ThenExpectedCoordinatesReturned() - { - // Setup - var topLeftUpperLayer = new Point2D(leftCoordinate, 0); - var topRightUpperLayer = new Point2D(rightCoordinate, 0); - var bottomRightUpperLayer = new Point2D(rightCoordinate, -5); - var bottomIntermediateLowerUpperLayer = new Point2D(middleXCoordinate, -5); - var bottomIntermediateUpperUpperLayer = new Point2D(middleXCoordinate, -10); - var bottomLeftUpperLayer = new Point2D(leftCoordinate, -10); - var soilLayer = new SoilLayer2D - { - GeometrySurface = new GeometrySurface - { - OuterLoop = new GeometryLoop - { - CurveList = - { - new GeometryCurve(topLeftUpperLayer, topRightUpperLayer), - new GeometryCurve(topRightUpperLayer, bottomRightUpperLayer), - new GeometryCurve(bottomRightUpperLayer, bottomIntermediateLowerUpperLayer), - new GeometryCurve(bottomIntermediateLowerUpperLayer, bottomIntermediateUpperUpperLayer), - new GeometryCurve(bottomIntermediateUpperUpperLayer, bottomLeftUpperLayer), - new GeometryCurve(bottomLeftUpperLayer, topLeftUpperLayer) - } - } - } - }; - - SoilLayer2D soilLayerAquiferInBetweenLeft = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, middleXCoordinate, true); - SoilLayer2D soilLayerAquiferInBetweenRight = CreateRectangularSoilLayer2D(-5, -21, middleXCoordinate, rightCoordinate, true); - - var bottomLeftInBetweenAquiferLayerLeft = new Point2D(leftCoordinate, -20); - var bottomRightInBetweenAquiferLayerLeft = new Point2D(middleXCoordinate, -20); - var bottomLeftInBetweenAquiferLayerRight = new Point2D(middleXCoordinate, -21); - var bottomRightInBetweenAquiferLayerRight = new Point2D(rightCoordinate, -21); - var bottomRightInBetweenLayer = new Point2D(rightCoordinate, -25); - var bottomLeftInBetweenLayer = new Point2D(leftCoordinate, -25); - var soilLayerInBetween = new SoilLayer2D - { - GeometrySurface = new GeometrySurface - { - OuterLoop = new GeometryLoop - { - CurveList = - { - new GeometryCurve(bottomLeftInBetweenAquiferLayerLeft, bottomRightInBetweenAquiferLayerLeft), - new GeometryCurve(bottomRightInBetweenAquiferLayerLeft, bottomLeftInBetweenAquiferLayerRight), - new GeometryCurve(bottomLeftInBetweenAquiferLayerRight, bottomRightInBetweenAquiferLayerRight), - new GeometryCurve(bottomRightInBetweenAquiferLayerRight, bottomRightInBetweenLayer), - new GeometryCurve(bottomRightInBetweenLayer, bottomLeftInBetweenLayer), - new GeometryCurve(bottomLeftInBetweenLayer, bottomLeftInBetweenAquiferLayerLeft) - } - } - } - }; - SoilLayer2D soilLayerAquiferBottom = CreateRectangularSoilLayer2D(-25, -30, leftCoordinate, rightCoordinate, true); - - var soilProfile = new SoilProfile2D - { - Geometry = new GeometryData - { - Left = leftCoordinate, - Right = rightCoordinate, - Bottom = -30 - } - }; - soilProfile.Surfaces.Add(soilLayer); - soilProfile.Surfaces.Add(soilLayerAquiferInBetweenLeft); - soilProfile.Surfaces.Add(soilLayerAquiferInBetweenRight); - soilProfile.Surfaces.Add(soilLayerInBetween); - soilProfile.Surfaces.Add(soilLayerAquiferBottom); - soilProfile.Geometry.Surfaces.Add(new GeometrySurface()); - - // Call - bool isInBetweenLayerPresent = PlLinesToWaternetConverter.AreInBetweenAquiferClustersPresent(soilProfile, out int count); - PlLinesToWaternetConverter.DetermineAquiferLayerBoundaryPoints(PlLinesToWaternetConverter.LayerType.InBetweenAquiferCluster, soilProfile, out Point2D[] topLevelInBetweenAquifer, out Point2D[] bottomLevelInBetweenAquifer, 0); - - // Assert - Assert.Multiple(() => - { - Assert.That(isInBetweenLayerPresent, Is.True); - Assert.That(count, Is.EqualTo(1)); - }); - GeometryPoint[] expectedTop = - [ - new(leftCoordinate, -10), - new(middleXCoordinate, -10), - new(middleXCoordinate, -5), - new(rightCoordinate, -5) - ]; - GeometryPoint[] expectedBottom = - [ - new(leftCoordinate, -20), - new(middleXCoordinate, -20), - new(middleXCoordinate, -21), - new(rightCoordinate, -21) - ]; - AssertGeometry(topLevelInBetweenAquifer, expectedTop); - AssertGeometry(bottomLevelInBetweenAquifer, expectedBottom); - } - - /// - /// --------------------------------------------------- Level 0 m - /// top layer - /// -------------------------|------------------------- Level -10 m - /// in-between layer left | in-between layer right - /// -------------------------|------------------------- Level -20 m - /// in-between layer - /// --------------------------------------------------- Level -25 m - /// bottom aquifer layer - /// --------------------------------------------------- Level -30 m - /// - [Test] - [TestCase(true, false)] - [TestCase(false, true)] - public void Given2DSoilProfileWithDiscontinuousInBetweenAquifer_WhenDeterminingInBetweenAquiferCount_ThenReturns0( - bool isLeftInBetweenLayerAquifer, bool isRightInBetweenLayerAquifer) - { - // Setup - SoilLayer2D soilLayer = CreateRectangularSoilLayer2D(0, -10, leftCoordinate, rightCoordinate, false); - SoilLayer2D soilLayerAquiferInBetweenLeft = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, middleXCoordinate, isLeftInBetweenLayerAquifer); - SoilLayer2D soilLayerAquiferInBetweenRight = CreateRectangularSoilLayer2D(-10, -20, middleXCoordinate, rightCoordinate, isRightInBetweenLayerAquifer); - SoilLayer2D soilLayerInBetween = CreateRectangularSoilLayer2D(-20, -25, leftCoordinate, rightCoordinate, false); - SoilLayer2D soilLayerAquiferBottom = CreateRectangularSoilLayer2D(-25, -30, leftCoordinate, rightCoordinate, true); - - var soilProfile = new SoilProfile2D - { - Geometry = new GeometryData - { - Left = leftCoordinate, - Right = rightCoordinate, - Bottom = -30 - } - }; - soilProfile.Surfaces.Add(soilLayer); - soilProfile.Surfaces.Add(soilLayerAquiferInBetweenLeft); - soilProfile.Surfaces.Add(soilLayerAquiferInBetweenRight); - soilProfile.Surfaces.Add(soilLayerInBetween); - soilProfile.Surfaces.Add(soilLayerAquiferBottom); - - // Call - bool isInBetweenLayerPresent = PlLinesToWaternetConverter.AreInBetweenAquiferClustersPresent(soilProfile, out int count); - - // Assert - Assert.Multiple(() => - { - Assert.That(isInBetweenLayerPresent, Is.False); - Assert.That(count, Is.EqualTo(0)); - }); - } - - /// - /// --------------------------------------------------- Level 0 m - /// /// |------------------------- Level -5 m /// top layer | right aquifer /// |------------------------- Level -9 m @@ -1814,196 +1107,6 @@ return soilProfile1D; } - /// - /// Many test cases are considered using a different soil profile 2D: - /// Within the method below, pictures of these soil profiles 2D are drawn to get a better idea of the test. - /// - private static IEnumerable GetSoilProfilesWithContinuousBottomAquiferLayer() - { - // TEST CASE 1: "Right aquifer fully adjoins left aquifer" - // - // ------------------------------------------------------------- Level 0 m - // Upper layer (not aquifer) - // -----------------------------|------------------------------- Level -10 m - // Bottom aquifer layer left | Bottom aquifer layer right - // -----------------------------|------------------------------- Level -20 m - SoilLayer2D soilUpperLayer = CreateRectangularSoilLayer2D(0, -10, leftCoordinate, rightCoordinate, false); - SoilLayer2D soilBottomLayerAquiferPartLeft = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, middleXCoordinate, true); - SoilLayer2D soilBottomLayerAquiferPartRight = CreateRectangularSoilLayer2D(-10, -20, middleXCoordinate, rightCoordinate, true); - var soilProfileFullAdjoin = new SoilProfile2D - { - Geometry = new GeometryData - { - Left = leftCoordinate, - Right = rightCoordinate, - Bottom = -20 - } - }; - soilProfileFullAdjoin.Surfaces.Add(soilUpperLayer); - soilProfileFullAdjoin.Surfaces.Add(soilBottomLayerAquiferPartLeft); - soilProfileFullAdjoin.Surfaces.Add(soilBottomLayerAquiferPartRight); - soilProfileFullAdjoin.Geometry.Surfaces.Add(new GeometrySurface()); - - yield return new TestCaseData(soilProfileFullAdjoin, new[] - { - new GeometryPoint(leftCoordinate, -10), - new GeometryPoint(middleXCoordinate, -10), - new GeometryPoint(rightCoordinate, -10) - }).SetName("Right aquifer fully adjoins left aquifer"); - - // TEST CASE 2: "One aquifer bottom layer" - // - // ------------------------------------------------------------- Level 0 m - // Upper layer (not aquifer) - // ------------------------------------------------------------- Level -10 m - // Bottom aquifer layer - // ------------------------------------------------------------- Level -20 m - SoilLayer2D soilBottomLayerAquifer = CreateRectangularSoilLayer2D(-10, -20, leftCoordinate, rightCoordinate, true); - var soilProfileOneBottomAquiferLayer = new SoilProfile2D - { - Geometry = new GeometryData - { - Left = leftCoordinate, - Right = rightCoordinate, - Bottom = -20 - } - }; - soilProfileOneBottomAquiferLayer.Surfaces.Add(soilUpperLayer); - soilProfileOneBottomAquiferLayer.Surfaces.Add(soilBottomLayerAquifer); - soilProfileOneBottomAquiferLayer.Geometry.Surfaces.Add(new GeometrySurface()); - - yield return new TestCaseData(soilProfileOneBottomAquiferLayer, new[] - { - new GeometryPoint(leftCoordinate, -10), - new GeometryPoint(rightCoordinate, -10) - }).SetName("One aquifer bottom layer"); - - // TEST CASE 3: "Right aquifer only bottom in range" - // - // ------------------------------------------------------------- Level 0 m - // Upper layer (not aquifer) |------------------------------ Level -5 m - // ------------------------------| Right bottom aquifer layer Level -10 m - // Left bottom aquifer layer |------------------------------ Level -15 m - // ------------------------------| Level -20 m - soilBottomLayerAquiferPartRight = CreateRectangularSoilLayer2D(-5, -15, middleXCoordinate, rightCoordinate, true); - var soilProfileRightSoilLayerBottomInRange = new SoilProfile2D - { - Geometry = new GeometryData - { - Left = leftCoordinate, - Right = rightCoordinate, - Bottom = -20 - } - }; - soilProfileRightSoilLayerBottomInRange.Surfaces.Add(soilUpperLayer); - soilProfileRightSoilLayerBottomInRange.Surfaces.Add(soilBottomLayerAquiferPartLeft); - soilProfileRightSoilLayerBottomInRange.Surfaces.Add(soilBottomLayerAquiferPartRight); - soilProfileRightSoilLayerBottomInRange.Geometry.Surfaces.Add(new GeometrySurface()); - - yield return new TestCaseData(soilProfileRightSoilLayerBottomInRange, new[] - { - new GeometryPoint(leftCoordinate, -10), - new GeometryPoint(middleXCoordinate, -10), - new GeometryPoint(middleXCoordinate, -5), - new GeometryPoint(rightCoordinate, -5) - }).SetName("Right aquifer only bottom in range"); - - // TEST CASE 4: "Right aquifer only top in range" - // - // ------------------------------------------------------------- Level 0 m - // Upper layer (not aquifer) | - // ------------------------------|------------------------------ Level -10 m - // Left bottom aquifer layer |------------------------------ Level -15 m - // ------------------------------| Right bottom aquifer layer | Level -20 m - // |------------------------------ Level -25 m - soilBottomLayerAquiferPartRight = CreateRectangularSoilLayer2D(-15, -25, middleXCoordinate, rightCoordinate, true); - - var soilProfileRightSoilLayerTopInRange = new SoilProfile2D - { - Geometry = new GeometryData - { - Left = leftCoordinate, - Right = rightCoordinate, - Bottom = -25 - } - }; - soilProfileRightSoilLayerTopInRange.Surfaces.Add(soilUpperLayer); - soilProfileRightSoilLayerTopInRange.Surfaces.Add(soilBottomLayerAquiferPartLeft); - soilProfileRightSoilLayerTopInRange.Surfaces.Add(soilBottomLayerAquiferPartRight); - soilProfileRightSoilLayerTopInRange.Geometry.Surfaces.Add(new GeometrySurface()); - - yield return new TestCaseData(soilProfileRightSoilLayerTopInRange, new[] - { - new GeometryPoint(leftCoordinate, -10), - new GeometryPoint(middleXCoordinate, -10), - new GeometryPoint(middleXCoordinate, -15), - new GeometryPoint(rightCoordinate, -15) - }).SetName("Right aquifer only top in range"); - - // TEST CASE 5: "Right aquifer fully envelopes left aquifer" - // - // ------------------------------------------------------------- Level 0 m - // Upper layer (not aquifer) |------------------------------ Level -5 m - // ------------------------------| Level -10 m - // Left bottom aquifer layer | Right bottom aquifer layer Level -15 m - // ------------------------------| Level -20 m - // |------------------------------ Level -25 m - soilBottomLayerAquiferPartRight = CreateRectangularSoilLayer2D(-5, -25, middleXCoordinate, rightCoordinate, true); - var soilProfileRightAquiferLayerFullyEnvelopsLeft = new SoilProfile2D - { - Geometry = new GeometryData - { - Left = leftCoordinate, - Right = rightCoordinate, - Bottom = -25 - } - }; - soilProfileRightAquiferLayerFullyEnvelopsLeft.Surfaces.Add(soilUpperLayer); - soilProfileRightAquiferLayerFullyEnvelopsLeft.Surfaces.Add(soilBottomLayerAquiferPartLeft); - soilProfileRightAquiferLayerFullyEnvelopsLeft.Surfaces.Add(soilBottomLayerAquiferPartRight); - soilProfileRightAquiferLayerFullyEnvelopsLeft.Geometry.Surfaces.Add(new GeometrySurface()); - - yield return new TestCaseData(soilProfileRightAquiferLayerFullyEnvelopsLeft, new[] - { - new GeometryPoint(leftCoordinate, -10), - new GeometryPoint(middleXCoordinate, -10), - new GeometryPoint(middleXCoordinate, -5), - new GeometryPoint(rightCoordinate, -5) - }).SetName("Right aquifer fully envelopes left aquifer"); - - // TEST CASE 6: "Right aquifer fully enveloped by left aquifer" - // - // ------------------------------------------------------------- Level 0 m - // Upper layer (not aquifer) | - // ------------------------------|------------------------------ Level -10 m - // Left bottom aquifer layer |------------------------------ Level -15 m - // | Right bottom aquifer layer | - // |------------------------------ Level -17 m - // ------------------------------| Level -20 m - soilBottomLayerAquiferPartRight = CreateRectangularSoilLayer2D(-15, -17, middleXCoordinate, rightCoordinate, true); - var soilProfileRightAquiferLayerFullyEnvelopedByLeft = new SoilProfile2D - { - Geometry = new GeometryData - { - Left = leftCoordinate, - Right = rightCoordinate, - Bottom = -20 - } - }; - soilProfileRightAquiferLayerFullyEnvelopedByLeft.Surfaces.Add(soilUpperLayer); - soilProfileRightAquiferLayerFullyEnvelopedByLeft.Surfaces.Add(soilBottomLayerAquiferPartLeft); - soilProfileRightAquiferLayerFullyEnvelopedByLeft.Surfaces.Add(soilBottomLayerAquiferPartRight); - soilProfileRightAquiferLayerFullyEnvelopedByLeft.Geometry.Surfaces.Add(new GeometrySurface()); - - yield return new TestCaseData(soilProfileRightAquiferLayerFullyEnvelopedByLeft, new[] - { - new GeometryPoint(leftCoordinate, -10), - new GeometryPoint(middleXCoordinate, -10), - new GeometryPoint(middleXCoordinate, -15), - new GeometryPoint(rightCoordinate, -15) - }).SetName("Right aquifer fully enveloped by left aquifer"); - } - private static SoilLayer2D CreateSoilLayer2D(Point2D topLeftCoordinate, Point2D topRightCoordinate, Point2D bottomRightCoordinate, Point2D bottomLeftCoordinate, bool isAquifer) { Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverter.cs =================================================================== diff -u -r5329 -r5382 --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverter.cs (.../PlLinesToWaternetConverter.cs) (revision 5329) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverter.cs (.../PlLinesToWaternetConverter.cs) (revision 5382) @@ -20,10 +20,10 @@ // All rights reserved. using System; -using System.Collections; using System.Collections.Generic; using System.Data; using System.Linq; +using Deltares.DamEngine.Calculators.KernelWrappers.Common; using Deltares.DamEngine.Calculators.Properties; using Deltares.DamEngine.Data.General; using Deltares.DamEngine.Data.General.PlLines; @@ -37,21 +37,6 @@ public static class PlLinesToWaternetConverter { - public enum LayerType - { - BottomAquiferCluster, - InBetweenAquiferCluster, - HighestAquiferCluster, - LowestLayer - } - - private enum BoundaryType - { - Top, - Bottom - } - - private const double deviationX = 1e-06; private const double toleranceAlmostEqual = 1e-09; private const string waternetLine1Name = "Waternet line phreatic line"; @@ -152,7 +137,7 @@ ThrowWhenPlLinesIsNull(plLines); ThrowWhenSoilProfileIsNull(soilProfile); - DetermineAquiferLayerBoundaryPoints(LayerType.BottomAquiferCluster, soilProfile, out Point2D[] bottomAquiferCoordinates, out _); + SoilProfile2DHelper.DetermineAquiferLayerBoundaryPoints(SoilProfile2DHelper.LayerType.BottomAquiferCluster, soilProfile, out Point2D[] bottomAquiferCoordinates, out _); if (bottomAquiferCoordinates == null) { throw new NoNullAllowedException(string.Format(Resources.NoBottomAquiferLayer, soilProfile.Name)); @@ -208,19 +193,7 @@ waternetLine.SyncCalcPoints(); return waternetLine; } - - /// - /// Determine all the xCoordinates to make cross-sections. - /// - /// The soil profile 2D. - /// All the xCoordinates of the soil profile 2D. - private static double[] DetermineAllXCoordinatesOfSoilProfile(SoilProfile2D soilProfile) - { - IEnumerable points = soilProfile.Surfaces.SelectMany(surf => surf.GeometrySurface.OuterLoop.CalcPoints); - double[] xCoordinates = points.Select(point => point.X).OrderBy(x => x).Distinct().ToArray(); - return xCoordinates; - } - + private static void CreateWaternetLinesForInBetweenAquifers(Waternet waternet, SoilProfile1D soilProfile1D, double xLeft, double xRight, PlLine plLine4, IntrusionVerticalWaterPressureType? pressureType) { var headLine = CreateLine(plLine4, headLine4Name); @@ -266,7 +239,7 @@ return; } - if (!AreInBetweenAquiferClustersPresent(soilProfile, out int inBetweenAquiferCount)) + if (!SoilProfile2DHelper.AreInBetweenAquiferClustersPresent(soilProfile, out int inBetweenAquiferCount)) { return; } @@ -275,7 +248,7 @@ waternet.HeadLineList.Add(headLine); for (var i = 0; i < inBetweenAquiferCount; i++) { - DetermineAquiferLayerBoundaryPoints(LayerType.InBetweenAquiferCluster, soilProfile, out Point2D[] inBetweenAquiferUpperCoordinates, out Point2D[] inBetweenAquiferLowerCoordinates, i); + SoilProfile2DHelper.DetermineAquiferLayerBoundaryPoints(SoilProfile2DHelper.LayerType.InBetweenAquiferCluster, soilProfile, out Point2D[] inBetweenAquiferUpperCoordinates, out Point2D[] inBetweenAquiferLowerCoordinates, i); if (inBetweenAquiferUpperCoordinates.Any() && inBetweenAquiferLowerCoordinates.Any()) { @@ -296,28 +269,6 @@ } } - internal static bool AreInBetweenAquiferClustersPresent(SoilProfile2D soilProfile, out int count) - { - double[] xCoordinates = DetermineAllXCoordinatesOfSoilProfile(soilProfile); - var currentCount = 0; - var previousCount = 0; - for (var i = 0; i < xCoordinates.Length; i++) - { - SoilProfile1D crossSection = soilProfile.GetSoilProfile1D(xCoordinates[i]); - currentCount = crossSection.GetInBetweenAquiferClusters?.Count ?? 0; - if (i > 0 && currentCount != previousCount) - { - count = 0; - return false; - } - - previousCount = currentCount; - } - - count = currentCount; - return count > 0; - } - private static TLineType CreateLine(PlLine plLine, string name) where TLineType : GeometryPointString, new() { @@ -349,118 +300,7 @@ return line; } - - /// - /// Determines all the points of the layer boundary for the specified layer type: - /// - For LayerType = BottomAquiferCluster, layerBoundaryTop is the top boundary of the bottom aquifer (= waternet line for PL3) - /// - For LayerType = InBetweenAquiferCluster, layerBoundaryTop and layerBoundaryBottom are both boundaries of the in-between aquifer (= waternet line for PL4) - /// - For LayerType = HighestAquiferCluster, layerBoundaryTop is the top of the highest aquifer (= waternet line for PL1 for "Hydrostatic" waternet) - /// - For LayerType = LowestLayer, layerBoundaryBottom is the bottom boundary of the lowest layer (= waternet line for PL1 for "Full hydrostatic" waternet) - /// When the aquifer is not continuous, null top and bottom boundaries are returned. - /// - /// The type of layer boundary. - /// The soil profile 2D. - /// All the points of the layer top boundary. - /// All the points of the layer bottom boundary. - /// In case of several in-between aquifers, the index of the in-between aquifer must be specified. - /// All the points of the layer boundary. - internal static void DetermineAquiferLayerBoundaryPoints(LayerType layerType, SoilProfile2D soilProfile, out Point2D[] layerBoundaryTop, out Point2D[] layerBoundaryBottom, int indexInBetweenAquifer = -1) - { - double[] xCoordinates = DetermineAllXCoordinatesOfSoilProfile(soilProfile); - var xCoordinatesAll = new List(); - foreach (double xCoordinate in xCoordinates) - { - if (HasAquiferAVerticalPartAtGivenX(layerType, xCoordinate, soilProfile, indexInBetweenAquifer)) - { - xCoordinatesAll.Add(xCoordinate - deviationX); - xCoordinatesAll.Add(xCoordinate + deviationX); - } - else - { - xCoordinatesAll.Add(xCoordinate); - } - } - - var layerBoundaryTopPoints = new List(); - var layerBoundaryBottomPoints = new List(); - double previousAquiferTop = double.NaN; - double previousAquiferBottom = double.NaN; - foreach (double xCoordinate in xCoordinatesAll) - { - SoilProfile1D crossSection = soilProfile.GetSoilProfile1D(xCoordinate); - - // Determine if the cluster of layers is in range of the previous cluster of layers. - // If not, return empty coordinates, because the cluster of layers is interrupted. - double currentAquiferTop = GetLevel(layerType, BoundaryType.Top, crossSection, indexInBetweenAquifer); - double currentAquiferBottom = GetLevel(layerType, BoundaryType.Bottom, crossSection, indexInBetweenAquifer); - - if (!double.IsNaN(previousAquiferTop) && !double.IsNaN(previousAquiferBottom) - && !double.IsNaN(currentAquiferTop) - && !double.IsNaN(currentAquiferBottom) - && !AreHorizontallyConnected(previousAquiferTop, previousAquiferBottom, currentAquiferTop, currentAquiferBottom)) - { - layerBoundaryTop = null; - layerBoundaryBottom = null; - return; - } - - if (!double.IsNaN(currentAquiferTop) && !double.IsNaN(currentAquiferBottom)) - { - previousAquiferTop = currentAquiferTop; - previousAquiferBottom = currentAquiferBottom; - layerBoundaryTopPoints.Add(new Point2D(xCoordinate, currentAquiferTop)); - layerBoundaryBottomPoints.Add(new Point2D(xCoordinate, currentAquiferBottom)); - } - } - - // Perform a short validation that the coordinates are fully defined from the beginning to the end - // of the profile. If not, the layer is not continuous. - if (!IsLayerBoundaryContinuous(layerBoundaryTopPoints, xCoordinates.First(), xCoordinates.Last()) - || !IsLayerBoundaryContinuous(layerBoundaryBottomPoints, xCoordinates.First(), xCoordinates.Last())) - { - layerBoundaryTop = null; - layerBoundaryBottom = null; - return; - } - layerBoundaryTop = layerBoundaryTopPoints.ToArray(); - layerBoundaryBottom = layerBoundaryBottomPoints.ToArray(); - } - - private static bool IsLayerBoundaryContinuous(IEnumerable boundaryPoints, double leftGeometryBoundary, double rightGeometryBoundary) - { - IEnumerable point2Ds = boundaryPoints.ToList(); - return point2Ds.Any() && (Math.Abs(point2Ds.First().X - leftGeometryBoundary) < toleranceAlmostEqual) - && Math.Abs(point2Ds.Last().X - rightGeometryBoundary) < toleranceAlmostEqual; - } - - private static double GetLevel(LayerType layerType, BoundaryType boundaryType, SoilProfile1D soilProfile1D, int indexInBetweenAquifer) - { - SoilLayer1D layer = GetLayer(layerType, boundaryType, soilProfile1D, indexInBetweenAquifer); - if (layer != null) - { - return boundaryType == BoundaryType.Top ? layer.TopLevel : layer.BottomLevel; - } - - return double.NaN; - } - private static SoilLayer1D GetLayer(LayerType layerType, BoundaryType boundaryType, SoilProfile1D soilProfile1D, int indexInBetweenAquifer) - { - switch (layerType) - { - case LayerType.BottomAquiferCluster: - return boundaryType == BoundaryType.Top ? soilProfile1D.BottomAquiferLayer : soilProfile1D.DeepestAquiferLayer; - case LayerType.InBetweenAquiferCluster: - return boundaryType == BoundaryType.Top ? soilProfile1D.GetInBetweenAquiferClusters[indexInBetweenAquifer].Item1 : soilProfile1D.GetInBetweenAquiferClusters[indexInBetweenAquifer].Item2; - case LayerType.HighestAquiferCluster: - return boundaryType == BoundaryType.Top ? soilProfile1D.GetHighestAquifer() : soilProfile1D.GetLowestLayerOfHighestAquiferCluster(); - case LayerType.LowestLayer: - return soilProfile1D.Layers.Last(); - } - - return null; - } - /// /// The position of the waternet line associated to the phreatic line depends on the water pressure type: /// - For DAM Standard type, this line lies on the bottom of the soil layers “in which the phreatic plane lies”. @@ -540,7 +380,7 @@ } case IntrusionVerticalWaterPressureType.HydroStatic: { - DetermineAquiferLayerBoundaryPoints(LayerType.HighestAquiferCluster, soilProfile2D, out Point2D[] highestAquiferCoordinates, out _); + SoilProfile2DHelper.DetermineAquiferLayerBoundaryPoints(SoilProfile2DHelper.LayerType.HighestAquiferCluster, soilProfile2D, out Point2D[] highestAquiferCoordinates, out _); return CreateWaternetLine(highestAquiferCoordinates); } case IntrusionVerticalWaterPressureType.Linear: @@ -550,7 +390,7 @@ } case IntrusionVerticalWaterPressureType.FullHydroStatic: { - DetermineAquiferLayerBoundaryPoints(LayerType.LowestLayer, soilProfile2D, out _, out Point2D[] lowestBoundaryCoordinates); + SoilProfile2DHelper.DetermineAquiferLayerBoundaryPoints(SoilProfile2DHelper.LayerType.LowestLayer, soilProfile2D, out _, out Point2D[] lowestBoundaryCoordinates); return CreateWaternetLine(lowestBoundaryCoordinates); } case null: @@ -637,46 +477,7 @@ return waternetLine; } - - private static bool HasAquiferAVerticalPartAtGivenX(LayerType layerType, double xCoordinate, SoilProfile2D soilProfile, int indexInBetweenAquifer) - { - SoilProfile1D crossSectionLeft = soilProfile.GetSoilProfile1D(xCoordinate - deviationX); - SoilProfile1D crossSectionRight = soilProfile.GetSoilProfile1D(xCoordinate + deviationX); - if (crossSectionLeft == null || crossSectionRight == null) - { - return false; - } - - double aquiferLeftTop = GetLevel(layerType, BoundaryType.Top, crossSectionLeft, indexInBetweenAquifer); - double aquiferLefBottom = GetLevel(layerType, BoundaryType.Bottom, crossSectionLeft, indexInBetweenAquifer); - double aquiferRightTop = GetLevel(layerType, BoundaryType.Top, crossSectionRight, indexInBetweenAquifer); - double aquiferRightBottom = GetLevel(layerType, BoundaryType.Bottom, crossSectionRight, indexInBetweenAquifer); - if (!double.IsNaN(aquiferLeftTop) && !double.IsNaN(aquiferLefBottom) && !double.IsNaN(aquiferRightTop) && !double.IsNaN(aquiferRightBottom)) - { - return Math.Abs(aquiferLeftTop - aquiferRightTop) > GeometryConstants.Accuracy || Math.Abs(aquiferLefBottom - aquiferRightBottom) > GeometryConstants.Accuracy; - } - - return false; - } - - private static bool AreHorizontallyConnected(double leftLayerTop, double leftLayerBottom, double rightLayerTop, double rightLayerBottom) - { - // Left soil layer envelopes whole right soil layer - if (leftLayerBottom <= rightLayerBottom && leftLayerTop >= rightLayerTop) - { - return true; - } - - // Right soil layer envelopes whole left soil layer - if (rightLayerBottom <= leftLayerBottom && rightLayerTop >= leftLayerTop) - { - return true; - } - - return (rightLayerTop <= leftLayerTop && rightLayerTop >= leftLayerBottom) // Top level lies in-between the left soil layer - || (rightLayerBottom >= leftLayerBottom && rightLayerBottom <= leftLayerTop); // Bottom level lies in-between the left soil layer - } - + private static bool IsBelowSoilProfile(SoilProfile1D soilProfile, PlLine line) { double bottomSoilProfileLevel = soilProfile.BottomLevel; Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/KernelWrappers/Common/SoilProfile2DHelper.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/KernelWrappers/Common/SoilProfile2DHelper.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/KernelWrappers/Common/SoilProfile2DHelper.cs (revision 5382) @@ -0,0 +1,232 @@ +// Copyright (C) Stichting Deltares 2024. All rights reserved. +// +// This file is part of the Dam Engine. +// +// The Dam Engine is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero General Public License +// along with this program. If not, see . +// +// All names, logos, and references to "Deltares" are registered trademarks of +// Stichting Deltares and remain full property of Stichting Deltares at all times. +// All rights reserved. + +using System; +using System.Collections.Generic; +using System.Linq; +using Deltares.DamEngine.Data.Geometry; +using Deltares.DamEngine.Data.Geotechnics; + +namespace Deltares.DamEngine.Calculators.KernelWrappers.Common; + +public static class SoilProfile2DHelper +{ + public enum LayerType + { + BottomAquiferCluster, + InBetweenAquiferCluster, + HighestAquiferCluster, + LowestLayer + } + + private enum BoundaryType + { + Top, + Bottom + } + + private const double deviationX = 1e-06; + private const double toleranceAlmostEqual = 1e-09; + + /// + /// Determine all the xCoordinates to make cross-sections. + /// + /// The soil profile 2D. + /// All the xCoordinates of the soil profile 2D. + private static double[] DetermineAllXCoordinatesOfSoilProfile(SoilProfile2D soilProfile) + { + IEnumerable points = soilProfile.Surfaces.SelectMany(surf => surf.GeometrySurface.OuterLoop.CalcPoints); + double[] xCoordinates = points.Select(point => point.X).OrderBy(x => x).Distinct().ToArray(); + return xCoordinates; + } + + internal static bool AreInBetweenAquiferClustersPresent(SoilProfile2D soilProfile, out int count) + { + double[] xCoordinates = DetermineAllXCoordinatesOfSoilProfile(soilProfile); + var currentCount = 0; + var previousCount = 0; + for (var i = 0; i < xCoordinates.Length; i++) + { + SoilProfile1D crossSection = soilProfile.GetSoilProfile1D(xCoordinates[i]); + currentCount = crossSection.GetInBetweenAquiferClusters?.Count ?? 0; + if (i > 0 && currentCount != previousCount) + { + count = 0; + return false; + } + + previousCount = currentCount; + } + + count = currentCount; + return count > 0; + } + + /// + /// Determines all the points of the layer boundary for the specified layer type: + /// - For LayerType = BottomAquiferCluster, layerBoundaryTop is the top boundary of the bottom aquifer (= waternet line for PL3) + /// - For LayerType = InBetweenAquiferCluster, layerBoundaryTop and layerBoundaryBottom are both boundaries of the in-between aquifer (= waternet line for PL4) + /// - For LayerType = HighestAquiferCluster, layerBoundaryTop is the top of the highest aquifer (= waternet line for PL1 for "Hydrostatic" waternet) + /// - For LayerType = LowestLayer, layerBoundaryBottom is the bottom boundary of the lowest layer (= waternet line for PL1 for "Full hydrostatic" waternet) + /// When the aquifer is not continuous, null top and bottom boundaries are returned. + /// + /// The type of layer boundary. + /// The soil profile 2D. + /// All the points of the layer top boundary. + /// All the points of the layer bottom boundary. + /// In case of several in-between aquifers, the index of the in-between aquifer must be specified. + /// All the points of the layer boundary. + internal static void DetermineAquiferLayerBoundaryPoints(LayerType layerType, SoilProfile2D soilProfile, out Point2D[] layerBoundaryTop, out Point2D[] layerBoundaryBottom, int indexInBetweenAquifer = -1) + { + double[] xCoordinates = DetermineAllXCoordinatesOfSoilProfile(soilProfile); + var xCoordinatesAll = new List(); + foreach (double xCoordinate in xCoordinates) + { + if (HasAquiferAVerticalPartAtGivenX(layerType, xCoordinate, soilProfile, indexInBetweenAquifer)) + { + xCoordinatesAll.Add(xCoordinate - deviationX); + xCoordinatesAll.Add(xCoordinate + deviationX); + } + else + { + xCoordinatesAll.Add(xCoordinate); + } + } + + var layerBoundaryTopPoints = new List(); + var layerBoundaryBottomPoints = new List(); + double previousAquiferTop = double.NaN; + double previousAquiferBottom = double.NaN; + foreach (double xCoordinate in xCoordinatesAll) + { + SoilProfile1D crossSection = soilProfile.GetSoilProfile1D(xCoordinate); + + // Determine if the cluster of layers is in range of the previous cluster of layers. + // If not, return empty coordinates, because the cluster of layers is interrupted. + double currentAquiferTop = GetLevel(layerType, BoundaryType.Top, crossSection, indexInBetweenAquifer); + double currentAquiferBottom = GetLevel(layerType, BoundaryType.Bottom, crossSection, indexInBetweenAquifer); + + if (!double.IsNaN(previousAquiferTop) && !double.IsNaN(previousAquiferBottom) + && !double.IsNaN(currentAquiferTop) + && !double.IsNaN(currentAquiferBottom) + && !AreHorizontallyConnected(previousAquiferTop, previousAquiferBottom, currentAquiferTop, currentAquiferBottom)) + { + layerBoundaryTop = null; + layerBoundaryBottom = null; + return; + } + + if (!double.IsNaN(currentAquiferTop) && !double.IsNaN(currentAquiferBottom)) + { + previousAquiferTop = currentAquiferTop; + previousAquiferBottom = currentAquiferBottom; + layerBoundaryTopPoints.Add(new Point2D(xCoordinate, currentAquiferTop)); + layerBoundaryBottomPoints.Add(new Point2D(xCoordinate, currentAquiferBottom)); + } + } + + // Perform a short validation that the coordinates are fully defined from the beginning to the end + // of the profile. If not, the layer is not continuous. + if (!IsLayerBoundaryContinuous(layerBoundaryTopPoints, xCoordinates.First(), xCoordinates.Last()) + || !IsLayerBoundaryContinuous(layerBoundaryBottomPoints, xCoordinates.First(), xCoordinates.Last())) + { + layerBoundaryTop = null; + layerBoundaryBottom = null; + return; + } + layerBoundaryTop = layerBoundaryTopPoints.ToArray(); + layerBoundaryBottom = layerBoundaryBottomPoints.ToArray(); + } + + private static bool IsLayerBoundaryContinuous(IEnumerable boundaryPoints, double leftGeometryBoundary, double rightGeometryBoundary) + { + IEnumerable point2Ds = boundaryPoints.ToList(); + return point2Ds.Any() && (Math.Abs(point2Ds.First().X - leftGeometryBoundary) < toleranceAlmostEqual) + && Math.Abs(point2Ds.Last().X - rightGeometryBoundary) < toleranceAlmostEqual; + } + + private static double GetLevel(LayerType layerType, BoundaryType boundaryType, SoilProfile1D soilProfile1D, int indexInBetweenAquifer) + { + SoilLayer1D layer = GetLayer(layerType, boundaryType, soilProfile1D, indexInBetweenAquifer); + if (layer != null) + { + return boundaryType == BoundaryType.Top ? layer.TopLevel : layer.BottomLevel; + } + + return double.NaN; + } + + private static SoilLayer1D GetLayer(LayerType layerType, BoundaryType boundaryType, SoilProfile1D soilProfile1D, int indexInBetweenAquifer) + { + switch (layerType) + { + case LayerType.BottomAquiferCluster: + return boundaryType == BoundaryType.Top ? soilProfile1D.BottomAquiferLayer : soilProfile1D.DeepestAquiferLayer; + case LayerType.InBetweenAquiferCluster: + return boundaryType == BoundaryType.Top ? soilProfile1D.GetInBetweenAquiferClusters[indexInBetweenAquifer].Item1 : soilProfile1D.GetInBetweenAquiferClusters[indexInBetweenAquifer].Item2; + case LayerType.HighestAquiferCluster: + return boundaryType == BoundaryType.Top ? soilProfile1D.GetHighestAquifer() : soilProfile1D.GetLowestLayerOfHighestAquiferCluster(); + case LayerType.LowestLayer: + return soilProfile1D.Layers.Last(); + } + + return null; + } + + private static bool HasAquiferAVerticalPartAtGivenX(LayerType layerType, double xCoordinate, SoilProfile2D soilProfile, int indexInBetweenAquifer) + { + SoilProfile1D crossSectionLeft = soilProfile.GetSoilProfile1D(xCoordinate - deviationX); + SoilProfile1D crossSectionRight = soilProfile.GetSoilProfile1D(xCoordinate + deviationX); + if (crossSectionLeft == null || crossSectionRight == null) + { + return false; + } + + double aquiferLeftTop = GetLevel(layerType, BoundaryType.Top, crossSectionLeft, indexInBetweenAquifer); + double aquiferLefBottom = GetLevel(layerType, BoundaryType.Bottom, crossSectionLeft, indexInBetweenAquifer); + double aquiferRightTop = GetLevel(layerType, BoundaryType.Top, crossSectionRight, indexInBetweenAquifer); + double aquiferRightBottom = GetLevel(layerType, BoundaryType.Bottom, crossSectionRight, indexInBetweenAquifer); + if (!double.IsNaN(aquiferLeftTop) && !double.IsNaN(aquiferLefBottom) && !double.IsNaN(aquiferRightTop) && !double.IsNaN(aquiferRightBottom)) + { + return Math.Abs(aquiferLeftTop - aquiferRightTop) > GeometryConstants.Accuracy || Math.Abs(aquiferLefBottom - aquiferRightBottom) > GeometryConstants.Accuracy; + } + + return false; + } + + private static bool AreHorizontallyConnected(double leftLayerTop, double leftLayerBottom, double rightLayerTop, double rightLayerBottom) + { + // Left soil layer envelopes whole right soil layer + if (leftLayerBottom <= rightLayerBottom && leftLayerTop >= rightLayerTop) + { + return true; + } + + // Right soil layer envelopes whole left soil layer + if (rightLayerBottom <= leftLayerBottom && rightLayerTop >= leftLayerTop) + { + return true; + } + + return (rightLayerTop <= leftLayerTop && rightLayerTop >= leftLayerBottom) // Top level lies in-between the left soil layer + || (rightLayerBottom >= leftLayerBottom && rightLayerBottom <= leftLayerTop); // Bottom level lies in-between the left soil layer + } +} \ No newline at end of file