Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverterTests.cs =================================================================== diff -u -r4838 -r4846 --- DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverterTests.cs (.../PlLinesToWaternetConverterTests.cs) (revision 4838) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverterTests.cs (.../PlLinesToWaternetConverterTests.cs) (revision 4846) @@ -35,6 +35,8 @@ public class PlLinesToWaternetConverterTests { private const double precision3Decimals = 0.00051; + private const double precision6Decimals = 0.00000051; + private static readonly Random random = new(21); private static readonly double leftCoordinate = random.NextDouble(); private static readonly double rightCoordinate = leftCoordinate + 1; @@ -537,6 +539,7 @@ AssertGeometry([ new GeometryPoint(leftCoordinate, -10), new GeometryPoint(middleXCoordinate, -10), + new GeometryPoint(middleXCoordinate, -15), new GeometryPoint(rightCoordinate, -15) ], pl4WaternetLine.Points); } @@ -643,6 +646,7 @@ AssertGeometry(new[] { bottomLeftUpperLayer, + bottomIntermediateLowerUpperLayer, bottomIntermediateUpperUpperLayer, bottomRightUpperLayer }, pl4WaternetLine.Points); @@ -775,6 +779,7 @@ { bottomLeftUpperLayer, bottomIntermediateUpperUpperLayer, + bottomIntermediateLowerUpperLayer, bottomRightUpperLayer }, pl4WaternetLine.Points); } @@ -905,6 +910,7 @@ AssertGeometry(new[] { bottomLeftUpperLayer, + bottomIntermediateUpperUpperLayer, bottomIntermediateLowerUpperLayer, bottomRightUpperLayer }, pl4WaternetLine.Points); @@ -1343,6 +1349,7 @@ 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"); @@ -1375,6 +1382,7 @@ { new GeometryPoint(leftCoordinate, -10), new GeometryPoint(middleXCoordinate, -10), + new GeometryPoint(middleXCoordinate, -15), new GeometryPoint(rightCoordinate, -15) }).SetName("Right aquifer only top in range"); @@ -1404,6 +1412,7 @@ 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"); @@ -1436,6 +1445,7 @@ { new GeometryPoint(leftCoordinate, -10), new GeometryPoint(middleXCoordinate, -10), + new GeometryPoint(middleXCoordinate, -15), new GeometryPoint(rightCoordinate, -15) }).SetName("Right aquifer fully enveloped by left aquifer"); } @@ -1498,8 +1508,8 @@ GeometryPoint actualPoint = actualPoints.ElementAt(i); Assert.Multiple(() => { - Assert.That(actualPoint.X, Is.EqualTo(expectedPoint.X)); - Assert.That(actualPoint.Z, Is.EqualTo(expectedPoint.Z)); + Assert.That(actualPoint.X, Is.EqualTo(expectedPoint.X).Within(precision6Decimals)); + Assert.That(actualPoint.Z, Is.EqualTo(expectedPoint.Z).Within(precision6Decimals)); }); } } Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverter.cs =================================================================== diff -u -r4540 -r4846 --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverter.cs (.../PlLinesToWaternetConverter.cs) (revision 4540) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverter.cs (.../PlLinesToWaternetConverter.cs) (revision 4846) @@ -34,6 +34,8 @@ public static class PlLinesToWaternetConverter { + private const double epsilon = 1e-9; + /// /// Converts the pl lines to a waternet. /// @@ -121,7 +123,7 @@ ThrowWhenPlLinesIsNull(plLines); ThrowWhenSoilProfileIsNull(soilProfile); - // Get all the xCoordinates to make cross sections + // Get all the xCoordinates to make cross-sections IEnumerable points = soilProfile.Surfaces.SelectMany(surf => surf.GeometrySurface.OuterLoop.CalcPoints); double[] xCoordinates = points.Select(point => point.X).OrderBy(x => x).Distinct().ToArray(); @@ -219,32 +221,44 @@ return line; } - private static IEnumerable GetAquiferCoordinates(Func getSoilLayerFunc, IEnumerable xCoordinates, + private static IEnumerable GetAquiferCoordinates(Func getSoilLayerFunc, double[] xCoordinates, SoilProfile2D soilProfile) { SoilLayer1D previousAquiferLayer = null; var coordinates = new List(); + var xCoordinatesAll = new List(); foreach (double xCoordinate in xCoordinates) { - SoilProfile1D crossSection = soilProfile.GetSoilProfile1D(xCoordinate); - - // Determine if the bottom aquifer layer is in range of the previous aquifer layer - // If not, return empty coordinates, because the aquifer layer is interrupted - SoilLayer1D currentAquifer = getSoilLayerFunc(crossSection); - if (previousAquiferLayer != null && - currentAquifer != null - && !AreHorizontallyConnected(previousAquiferLayer, currentAquifer)) + if (HasAquiferAVerticalPartAtGivenX(getSoilLayerFunc, xCoordinate, soilProfile)) { - return Enumerable.Empty(); + xCoordinatesAll.Add(xCoordinate - epsilon); + xCoordinatesAll.Add(xCoordinate + epsilon); } - - if (currentAquifer != null) + else { - previousAquiferLayer = currentAquifer; - coordinates.Add(new Point2D(xCoordinate, currentAquifer.TopLevel)); + xCoordinatesAll.Add(xCoordinate); } } + foreach (double xCoordinate in xCoordinatesAll) + { + SoilProfile1D crossSection = soilProfile.GetSoilProfile1D(xCoordinate); + // Determine if the aquifer layer is in range of the previous aquifer layer + // If not, return empty coordinates, because the aquifer layer is interrupted + SoilLayer1D currentAquifer = getSoilLayerFunc(crossSection); + if (previousAquiferLayer != null && currentAquifer != null + && !AreHorizontallyConnected(previousAquiferLayer, currentAquifer)) + { + return Enumerable.Empty(); + } + + if (currentAquifer != null) + { + previousAquiferLayer = currentAquifer; + coordinates.Add(new Point2D(xCoordinate, currentAquifer.TopLevel)); + } + } + // Perform a short validation that the coordinates are fully defined from the beginning to the end // of the profile. If not, the aquifer is not continuous. if (!coordinates.Any() || coordinates.First().X != xCoordinates.First() || coordinates.Last().X != xCoordinates.Last()) @@ -255,6 +269,22 @@ return coordinates; } + private static bool HasAquiferAVerticalPartAtGivenX(Func getSoilLayerFunc, double xCoordinate, SoilProfile2D soilProfile) + { + SoilProfile1D crossSectionLeft = soilProfile.GetSoilProfile1D(xCoordinate - epsilon); + SoilProfile1D crossSectionRight = soilProfile.GetSoilProfile1D(xCoordinate + epsilon); + if (crossSectionLeft != null && crossSectionRight != null) + { + SoilLayer1D currentAquiferLeft = getSoilLayerFunc(crossSectionLeft); + SoilLayer1D currentAquiferRight = getSoilLayerFunc(crossSectionRight); + if (currentAquiferLeft != null && currentAquiferRight != null) + { + return Math.Abs(currentAquiferLeft.TopLevel - currentAquiferRight.TopLevel) > GeometryConstants.Accuracy; + } + } + return false; + } + private static bool AreHorizontallyConnected(SoilLayer1D leftSoilLayer, SoilLayer1D rightSoilLayer) { // The layers are identical