Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverterTests.cs =================================================================== diff -u -r4898 -r4906 --- DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverterTests.cs (.../PlLinesToWaternetConverterTests.cs) (revision 4898) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators.Tests/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverterTests.cs (.../PlLinesToWaternetConverterTests.cs) (revision 4906) @@ -81,14 +81,18 @@ } [Test] - public void TestConvertPlLinesToWaternet() + public void TestConvertPlLinesToWaternet_1DSoilProfile() { PlLines plLines = CreatePlLinesForTest(); SoilProfile1D soilProfile1D = CreateSoilProfile1DForTest(); Assert.Multiple(() => { - Assert.That(soilProfile1D.InBetweenAquiferLayer.TopLevel, Is.EqualTo(-2.111).Within(precision3Decimals)); + Assert.That(soilProfile1D.InBetweenAquiferClusters.Count, Is.EqualTo(2).Within(precision3Decimals)); + Assert.That(soilProfile1D.InBetweenAquiferClusters[0].Item1.TopLevel, Is.EqualTo(-2.111).Within(precision3Decimals)); + Assert.That(soilProfile1D.InBetweenAquiferClusters[0].Item2.BottomLevel, Is.EqualTo(-3.373).Within(precision3Decimals)); + Assert.That(soilProfile1D.InBetweenAquiferClusters[1].Item1.TopLevel, Is.EqualTo(-4.151).Within(precision3Decimals)); + Assert.That(soilProfile1D.InBetweenAquiferClusters[1].Item2.BottomLevel, Is.EqualTo(-5.373).Within(precision3Decimals)); Assert.That(soilProfile1D.BottomAquiferLayer.TopLevel, Is.EqualTo(-6.111).Within(precision3Decimals)); }); @@ -97,10 +101,10 @@ const double right = 100; Waternet waternet = PlLinesToWaternetConverter.CreateWaternetBasedOnPlLines(plLines, soilProfile1D, penetrateLength, left, right); - // Pl 1 is only the phreatic line, a headline or waternet line does not have to be created + // Pl 1 is the phreatic line and gets waternet line to bottom level of the deepest layer where PL 1 lies // Pl 2 gets waternet line to level of BottomAquiferLayer.TopLevel + penetrationLength // Pl 3 gets waternet line to level of InBetweenAquiferLayer.TopLevel - // Pl 4 gets waternet line to level of BottomAquiferLayer.TopLevel + // Pl 4 gets waternet line around the BottomAquiferLayer // expected phreatic line from pl 1 Assert.That(waternet.PhreaticLine.Points, Has.Count.EqualTo(3)); @@ -112,40 +116,83 @@ Assert.That(waternet.PhreaticLine.Points[1].Z, Is.EqualTo(1).Within(precision3Decimals)); Assert.That(waternet.PhreaticLine.Points[2].X, Is.EqualTo(100).Within(precision3Decimals)); Assert.That(waternet.PhreaticLine.Points[2].Z, Is.EqualTo(1).Within(precision3Decimals)); - // expected head lines from pl 2, 3 and 4 - Assert.That(waternet.HeadLineList, Has.Count.EqualTo(3)); }); + // expected headlines from pl 2, 3 and 4 + Assert.That(waternet.HeadLineList, Has.Count.EqualTo(3)); Assert.Multiple(() => { - Assert.That(waternet.HeadLineList[0].Points.Count, Is.EqualTo(2)); - Assert.That(waternet.HeadLineList[1].Points.Count, Is.EqualTo(3)); - Assert.That(waternet.HeadLineList[2].Points.Count, Is.EqualTo(2)); - // check the points of first head line that represents pl 2 + Assert.That(waternet.HeadLineList[0].Points, Has.Count.EqualTo(2)); + Assert.That(waternet.HeadLineList[0].Name, Is.EqualTo("Head line 2 (PL 2)")); + Assert.That(waternet.HeadLineList[1].Points, Has.Count.EqualTo(3)); + Assert.That(waternet.HeadLineList[1].Name, Is.EqualTo("Head line 3 (PL 3)")); + Assert.That(waternet.HeadLineList[2].Points, Has.Count.EqualTo(2)); + Assert.That(waternet.HeadLineList[2].Name, Is.EqualTo("Head line 4 (PL 4)")); + // check the points of first headline that represents pl 2 Assert.That(waternet.HeadLineList[0].Points[0].X, Is.EqualTo(0).Within(precision3Decimals)); Assert.That(waternet.HeadLineList[0].Points[0].Z, Is.EqualTo(0).Within(precision3Decimals)); Assert.That(waternet.HeadLineList[0].Points[1].X, Is.EqualTo(100).Within(precision3Decimals)); Assert.That(waternet.HeadLineList[0].Points[1].Z, Is.EqualTo(-1).Within(precision3Decimals)); - // expected waternet lines from pl 2, 3 and 4 - Assert.That(waternet.WaternetLineList.Count, Is.EqualTo(3)); + // expected: 1 waternet line from pl 1, 2, 3 and 2 waternet lines for 4 + Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(5)); + Assert.That(waternet.WaternetLineList[0].Name, Is.EqualTo("Waternet line phreatic line")); + Assert.That(waternet.WaternetLineList[1].Name, Is.EqualTo("Penetration zone lower aquifer")); + Assert.That(waternet.WaternetLineList[2].Name, Is.EqualTo("Waternet line lower aquifer")); + Assert.That(waternet.WaternetLineList[3].Name, Is.EqualTo("Waternet area in-between aquifer")); + Assert.That(waternet.WaternetLineList[4].Name, Is.EqualTo("Waternet area in-between aquifer")); }); Assert.Multiple(() => { - // expected waternet line 0 is connected to pl2 with level -6.111 + 2.1 = -4.011 - Assert.That(waternet.WaternetLineList[0].HeadLine, Is.EqualTo(waternet.HeadLineList[0])); + // expected waternet line 0 is connected to pl1 with level -2.111 m + 1 mm + Assert.That(waternet.WaternetLineList[0].HeadLine.Points, Is.EqualTo(waternet.PhreaticLine.Points)); + Assert.That(waternet.WaternetLineList[0].HeadLine.Name, Is.EqualTo(waternet.PhreaticLine.Name)); + Assert.That(waternet.WaternetLineList[0].Points, Has.Count.EqualTo(2)); Assert.That(waternet.WaternetLineList[0].Points[0].X, Is.EqualTo(0).Within(precision3Decimals)); - Assert.That(waternet.WaternetLineList[0].Points[0].Z, Is.EqualTo(-4.011).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[0].Points[0].Z, Is.EqualTo(-2.110).Within(precision3Decimals)); Assert.That(waternet.WaternetLineList[0].Points[1].X, Is.EqualTo(100).Within(precision3Decimals)); - Assert.That(waternet.WaternetLineList[0].Points[1].Z, Is.EqualTo(-4.011).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[0].Points[1].Z, Is.EqualTo(-2.110).Within(precision3Decimals)); - // expected waternet line 1 is connected to pl3 with level -6.111 - Assert.That(waternet.WaternetLineList[1].HeadLine, Is.EqualTo(waternet.HeadLineList[1])); - Assert.That(waternet.WaternetLineList[1].Points[0].Z, Is.EqualTo(-6.111).Within(precision3Decimals)); + // expected waternet line 1 is connected to pl2 with level -6.111 + 2.1 = -4.011 + Assert.That(waternet.WaternetLineList[1].HeadLine, Is.EqualTo(waternet.HeadLineList[0])); + Assert.That(waternet.WaternetLineList[1].Points, Has.Count.EqualTo(2)); + Assert.That(waternet.WaternetLineList[1].Points[0].X, Is.EqualTo(0).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[1].Points[0].Z, Is.EqualTo(-4.011).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[1].Points[1].X, Is.EqualTo(100).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[1].Points[1].Z, Is.EqualTo(-4.011).Within(precision3Decimals)); - // expected waternet line 2 is connected to pl4 with level -2.111 - Assert.That(waternet.WaternetLineList[2].HeadLine, Is.EqualTo(waternet.HeadLineList[2])); - Assert.That(waternet.WaternetLineList[2].Points[0].Z, Is.EqualTo(-2.111).Within(precision3Decimals)); + // expected waternet line 2 is connected to pl3 with level -6.111 + Assert.That(waternet.WaternetLineList[2].HeadLine, Is.EqualTo(waternet.HeadLineList[1])); + Assert.That(waternet.WaternetLineList[2].Points, Has.Count.EqualTo(2)); + Assert.That(waternet.WaternetLineList[2].Points[0].Z, Is.EqualTo(-6.111).Within(precision3Decimals)); + + // expected waternet line 3 is connected to pl4 with level between -2.111 and -3.373 + Assert.That(waternet.WaternetLineList[3].HeadLine, Is.EqualTo(waternet.HeadLineList[2])); + Assert.That(waternet.WaternetLineList[3].Points, Has.Count.EqualTo(5)); + Assert.That(waternet.WaternetLineList[3].Points[0].X, Is.EqualTo(0).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[3].Points[0].Z, Is.EqualTo(-2.111).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[3].Points[1].X, Is.EqualTo(100).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[3].Points[1].Z, Is.EqualTo(-2.111).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[3].Points[2].X, Is.EqualTo(100).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[3].Points[2].Z, Is.EqualTo(-3.373).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[3].Points[3].X, Is.EqualTo(0).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[3].Points[3].Z, Is.EqualTo(-3.373).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[3].Points[4].X, Is.EqualTo(0).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[3].Points[4].Z, Is.EqualTo(-2.111).Within(precision3Decimals)); + + // expected waternet line 4 is connected to pl4 with level between -4.151 and -5.373 + Assert.That(waternet.WaternetLineList[4].HeadLine, Is.EqualTo(waternet.HeadLineList[2])); + Assert.That(waternet.WaternetLineList[4].Points, Has.Count.EqualTo(5)); + Assert.That(waternet.WaternetLineList[4].Points[0].X, Is.EqualTo(0).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[4].Points[0].Z, Is.EqualTo(-4.151).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[4].Points[1].X, Is.EqualTo(100).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[4].Points[1].Z, Is.EqualTo(-4.151).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[4].Points[2].X, Is.EqualTo(100).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[4].Points[2].Z, Is.EqualTo(-5.373).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[4].Points[3].X, Is.EqualTo(0).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[4].Points[3].Z, Is.EqualTo(-5.373).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[4].Points[4].X, Is.EqualTo(0).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[4].Points[4].Z, Is.EqualTo(-4.151).Within(precision3Decimals)); }); } @@ -190,7 +237,7 @@ Assert.Multiple(() => { Assert.That(waternet.HeadLineList, Has.Count.EqualTo(3)); - Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(3)); + Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(5)); }); // check that no waternetlines are added for pl3 when InBetweenAquiferLayer is null @@ -199,16 +246,16 @@ Assert.Multiple(() => { Assert.That(waternet.HeadLineList, Has.Count.EqualTo(3)); - Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(2)); + Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(3)); }); Assert.Multiple(() => { // expected waternet line 0 is connected to pl2 with level -6.111 + 2.1 = -4.011 - Assert.That(waternet.WaternetLineList[0].HeadLine, Is.EqualTo(waternet.HeadLineList[0])); - Assert.That(waternet.WaternetLineList[0].Points[0].Z, Is.EqualTo(-4.011).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[1].HeadLine, Is.EqualTo(waternet.HeadLineList[0])); + Assert.That(waternet.WaternetLineList[1].Points[0].Z, Is.EqualTo(-4.011).Within(precision3Decimals)); // expected waternet line 1 is connected to pl3 with level -6.111 - Assert.That(waternet.WaternetLineList[1].HeadLine, Is.EqualTo(waternet.HeadLineList[1])); - Assert.That(waternet.WaternetLineList[1].Points[0].Z, Is.EqualTo(-6.111).Within(precision3Decimals)); + Assert.That(waternet.WaternetLineList[2].HeadLine, Is.EqualTo(waternet.HeadLineList[1])); + Assert.That(waternet.WaternetLineList[2].Points[0].Z, Is.EqualTo(-6.111).Within(precision3Decimals)); }); // check that no waternetlines are added for pl2, pl3 and pl4 when BottomAquiferLayer is null @@ -217,7 +264,8 @@ Assert.Multiple(() => { Assert.That(waternet.HeadLineList, Has.Count.EqualTo(3)); - Assert.That(waternet.WaternetLineList, Is.Empty); + Assert.That(waternet.WaternetLineList.Count, Is.EqualTo(1)); + Assert.That(waternet.WaternetLineList[0].HeadLine.Name, Is.SameAs(waternet.PhreaticLine.Name)); }); } @@ -255,7 +303,7 @@ SoilProfile2D soilProfile, IEnumerable expectedBottomAquiferCoordinates) { // Setup - PlLines plLines = CreateAllPlLines(); + PlLines plLines = CreateAllPlLines(5); // Call Waternet waternet = PlLinesToWaternetConverter.CreateWaternetBasedOnPlLines(plLines, soilProfile, penetrationLength); @@ -268,15 +316,25 @@ AssertGeometry(plLines.Lines[PlLineType.Pl3].Points, waternet.HeadLineList[1].Points); AssertGeometry(plLines.Lines[PlLineType.Pl4].Points, waternet.HeadLineList[2].Points); - Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(2)); + Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(3)); - WaternetLine pl2WaternetLine = waternet.WaternetLineList[0]; + WaternetLine pl1WaternetLine = waternet.WaternetLineList[0]; + Assert.That(pl1WaternetLine.HeadLine.Name, Is.SameAs(waternet.PhreaticLine.Name)); + // As PL1 is above surface line, the waternet line associated to PL1 is the surface line + var expectedWaternetLine1 = new List + { + new(leftCoordinate, 0), + new(rightCoordinate, 0) + }; + AssertGeometry(expectedWaternetLine1, pl1WaternetLine.Points); + + WaternetLine pl2WaternetLine = waternet.WaternetLineList[1]; Assert.That(pl2WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[0])); GeometryPoint[] offSetAquiferCoordinates = expectedBottomAquiferCoordinates.Select(aquiferCoordinate => new GeometryPoint(aquiferCoordinate.X, aquiferCoordinate.Z + penetrationLength)) .ToArray(); AssertGeometry(offSetAquiferCoordinates, pl2WaternetLine.Points); - WaternetLine pl3WaternetLine = waternet.WaternetLineList[1]; + WaternetLine pl3WaternetLine = waternet.WaternetLineList[2]; Assert.That(pl3WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[1])); AssertGeometry(expectedBottomAquiferCoordinates, pl3WaternetLine.Points); } @@ -330,31 +388,41 @@ AssertGeometry(plLines.Lines[PlLineType.Pl3].Points, waternet.HeadLineList[1].Points); AssertGeometry(plLines.Lines[PlLineType.Pl4].Points, waternet.HeadLineList[2].Points); - Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(3)); + Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(4)); + WaternetLine pl1WaternetLine = waternet.WaternetLineList[0]; + Assert.That(pl1WaternetLine.HeadLine.Name, Is.SameAs(waternet.PhreaticLine.Name)); + Point2D[] expectedWaternetLine1 = + [ + new Point2D(leftCoordinate, -9.999), + new Point2D(rightCoordinate, -9.999) + ]; + AssertGeometry(expectedWaternetLine1, pl1WaternetLine.Points); + Point2D[] expectedBottomAquiferCoordinates = [ new Point2D(leftCoordinate, -25), - new Point2D(middleXCoordinate, -25), new Point2D(rightCoordinate, -25) ]; - WaternetLine pl2WaternetLine = waternet.WaternetLineList[0]; + WaternetLine pl2WaternetLine = waternet.WaternetLineList[1]; Assert.That(pl2WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[0])); GeometryPoint[] offSetAquiferCoordinates = expectedBottomAquiferCoordinates.Select(aquiferCoordinate => new GeometryPoint(aquiferCoordinate.X, aquiferCoordinate.Z + penetrationLength)) .ToArray(); AssertGeometry(offSetAquiferCoordinates, pl2WaternetLine.Points); - WaternetLine pl3WaternetLine = waternet.WaternetLineList[1]; + WaternetLine pl3WaternetLine = waternet.WaternetLineList[2]; Assert.That(pl3WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[1])); AssertGeometry(expectedBottomAquiferCoordinates, pl3WaternetLine.Points); - WaternetLine pl4WaternetLine = waternet.WaternetLineList[2]; + WaternetLine pl4WaternetLine = waternet.WaternetLineList[3]; Assert.That(pl4WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[2])); AssertGeometry([ new Point2D(leftCoordinate, -10), - new Point2D(middleXCoordinate, -10), - new Point2D(rightCoordinate, -10) + new Point2D(rightCoordinate, -10), + new Point2D(rightCoordinate, -20), + new Point2D(leftCoordinate, -20), + new Point2D(leftCoordinate, -10) ], pl4WaternetLine.Points); } @@ -405,8 +473,17 @@ AssertGeometry(plLines.Lines[PlLineType.Pl3].Points, waternet.HeadLineList[1].Points); AssertGeometry(plLines.Lines[PlLineType.Pl4].Points, waternet.HeadLineList[2].Points); - Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(3)); + Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(4)); + WaternetLine pl1WaternetLine = waternet.WaternetLineList[0]; + Assert.That(pl1WaternetLine.HeadLine.Name, Is.SameAs(waternet.PhreaticLine.Name)); + Point2D[] expectedWaternetLine1 = + [ + new Point2D(leftCoordinate, -9.999), + new Point2D(rightCoordinate, -9.999) + ]; + AssertGeometry(expectedWaternetLine1, pl1WaternetLine.Points); + var bottomRightInBetweenLayer = new Point2D(rightCoordinate, -25); var bottomLeftInBetweenLayer = new Point2D(leftCoordinate, -25); Point2D[] expectedBottomAquiferCoordinates = @@ -415,23 +492,24 @@ bottomRightInBetweenLayer ]; - WaternetLine pl2WaternetLine = waternet.WaternetLineList[0]; + WaternetLine pl2WaternetLine = waternet.WaternetLineList[1]; Assert.That(pl2WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[0])); GeometryPoint[] offSetAquiferCoordinates = expectedBottomAquiferCoordinates.Select(aquiferCoordinate => new GeometryPoint(aquiferCoordinate.X, aquiferCoordinate.Z + penetrationLength)) .ToArray(); AssertGeometry(offSetAquiferCoordinates, pl2WaternetLine.Points); - WaternetLine pl3WaternetLine = waternet.WaternetLineList[1]; + WaternetLine pl3WaternetLine = waternet.WaternetLineList[2]; Assert.That(pl3WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[1])); AssertGeometry(expectedBottomAquiferCoordinates, pl3WaternetLine.Points); - WaternetLine pl4WaternetLine = waternet.WaternetLineList[2]; + WaternetLine pl4WaternetLine = waternet.WaternetLineList[3]; Assert.That(pl4WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[2])); - var bottomRightUpperLayer = new Point2D(rightCoordinate, -10); - var bottomLeftUpperLayer = new Point2D(leftCoordinate, -10); AssertGeometry([ - bottomLeftUpperLayer, - bottomRightUpperLayer + new Point2D(leftCoordinate, -10), + new Point2D(rightCoordinate, -10), + new Point2D(rightCoordinate, -20), + new Point2D(leftCoordinate, -20), + new Point2D(leftCoordinate, -10) ], pl4WaternetLine.Points); } @@ -515,32 +593,45 @@ AssertGeometry(plLines.Lines[PlLineType.Pl3].Points, waternet.HeadLineList[1].Points); AssertGeometry(plLines.Lines[PlLineType.Pl4].Points, waternet.HeadLineList[2].Points); - Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(3)); + Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(4)); + WaternetLine pl1WaternetLine = waternet.WaternetLineList[0]; + Assert.That(pl1WaternetLine.HeadLine.Name, Is.SameAs(waternet.PhreaticLine.Name)); + Point2D[] expectedWaternetLine1 = + [ + new Point2D(leftCoordinate, -9.999), + new Point2D(rightCoordinate, -9.999) + ]; + AssertGeometry(expectedWaternetLine1, pl1WaternetLine.Points); + Point2D[] expectedBottomAquiferCoordinates = { bottomLeftInBetweenLayer, - new(middleXCoordinate, -25), bottomRightInBetweenLayer }; - WaternetLine pl2WaternetLine = waternet.WaternetLineList[0]; + WaternetLine pl2WaternetLine = waternet.WaternetLineList[1]; Assert.That(pl2WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[0])); GeometryPoint[] offSetAquiferCoordinates = expectedBottomAquiferCoordinates.Select(aquiferCoordinate => new GeometryPoint(aquiferCoordinate.X, aquiferCoordinate.Z + penetrationLength)) .ToArray(); AssertGeometry(offSetAquiferCoordinates, pl2WaternetLine.Points); - WaternetLine pl3WaternetLine = waternet.WaternetLineList[1]; + WaternetLine pl3WaternetLine = waternet.WaternetLineList[2]; Assert.That(pl3WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[1])); AssertGeometry(expectedBottomAquiferCoordinates, pl3WaternetLine.Points); - WaternetLine pl4WaternetLine = waternet.WaternetLineList[2]; + WaternetLine pl4WaternetLine = waternet.WaternetLineList[3]; Assert.That(pl4WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[2])); AssertGeometry([ new GeometryPoint(leftCoordinate, -10), new GeometryPoint(middleXCoordinate, -10), new GeometryPoint(middleXCoordinate, -15), - new GeometryPoint(rightCoordinate, -15) + new GeometryPoint(rightCoordinate, -15), + new GeometryPoint(rightCoordinate, -21), + new GeometryPoint(middleXCoordinate, -21), + new GeometryPoint(middleXCoordinate, -20), + new GeometryPoint(leftCoordinate, -20), + new GeometryPoint(leftCoordinate, -10) ], pl4WaternetLine.Points); } @@ -620,35 +711,48 @@ AssertGeometry(plLines.Lines[PlLineType.Pl3].Points, waternet.HeadLineList[1].Points); AssertGeometry(plLines.Lines[PlLineType.Pl4].Points, waternet.HeadLineList[2].Points); - Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(3)); + Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(4)); + WaternetLine pl1WaternetLine = waternet.WaternetLineList[0]; + Assert.That(pl1WaternetLine.HeadLine.Name, Is.SameAs(waternet.PhreaticLine.Name)); + Point2D[] expectedWaternetLine1 = + [ + new Point2D(leftCoordinate, -9.999), + new Point2D(middleXCoordinate, -9.999), + new Point2D(middleXCoordinate, -19.999), + new Point2D(rightCoordinate, -19.999) + ]; + AssertGeometry(expectedWaternetLine1, pl1WaternetLine.Points); + var bottomRightInBetweenLayer = new Point2D(rightCoordinate, -25); var bottomLeftInBetweenLayer = new Point2D(leftCoordinate, -25); Point2D[] expectedBottomAquiferCoordinates = - { + [ bottomLeftInBetweenLayer, - new Point2D(middleXCoordinate, -25), bottomRightInBetweenLayer - }; + ]; - WaternetLine pl2WaternetLine = waternet.WaternetLineList[0]; + WaternetLine pl2WaternetLine = waternet.WaternetLineList[1]; Assert.That(pl2WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[0])); GeometryPoint[] offSetAquiferCoordinates = expectedBottomAquiferCoordinates.Select(aquiferCoordinate => new GeometryPoint(aquiferCoordinate.X, aquiferCoordinate.Z + penetrationLength)) .ToArray(); AssertGeometry(offSetAquiferCoordinates, pl2WaternetLine.Points); - WaternetLine pl3WaternetLine = waternet.WaternetLineList[1]; + WaternetLine pl3WaternetLine = waternet.WaternetLineList[2]; Assert.That(pl3WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[1])); AssertGeometry(expectedBottomAquiferCoordinates, pl3WaternetLine.Points); - WaternetLine pl4WaternetLine = waternet.WaternetLineList[2]; + WaternetLine pl4WaternetLine = waternet.WaternetLineList[3]; Assert.That(pl4WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[2])); AssertGeometry(new[] { bottomLeftUpperLayer, bottomIntermediateLowerUpperLayer, bottomIntermediateUpperUpperLayer, - bottomRightUpperLayer + bottomRightUpperLayer, + new Point2D(rightCoordinate, -20), + new Point2D(leftCoordinate, -20), + bottomLeftUpperLayer }, pl4WaternetLine.Points); } @@ -754,33 +858,48 @@ AssertGeometry(plLines.Lines[PlLineType.Pl3].Points, waternet.HeadLineList[1].Points); AssertGeometry(plLines.Lines[PlLineType.Pl4].Points, waternet.HeadLineList[2].Points); - Assert.That(waternet.WaternetLineList.Count, Is.EqualTo(3)); + Assert.That(waternet.WaternetLineList.Count, Is.EqualTo(4)); + WaternetLine pl1WaternetLine = waternet.WaternetLineList[0]; + Assert.That(pl1WaternetLine.HeadLine.Name, Is.SameAs(waternet.PhreaticLine.Name)); + Point2D[] expectedWaternetLine1 = + [ + new Point2D(leftCoordinate, -9.999), + new Point2D(middleXCoordinate, -9.999), + new Point2D(middleXCoordinate, -14.999), + new Point2D(rightCoordinate, -14.999) + ]; + AssertGeometry(expectedWaternetLine1, pl1WaternetLine.Points); + Point2D[] expectedBottomAquiferCoordinates = - { + [ bottomLeftInBetweenLayer, - new Point2D(middleXCoordinate, -25), bottomRightInBetweenLayer - }; + ]; - WaternetLine pl2WaternetLine = waternet.WaternetLineList[0]; + WaternetLine pl2WaternetLine = waternet.WaternetLineList[1]; Assert.That(pl2WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[0])); GeometryPoint[] offSetAquiferCoordinates = expectedBottomAquiferCoordinates.Select(aquiferCoordinate => new GeometryPoint(aquiferCoordinate.X, aquiferCoordinate.Z + penetrationLength)) .ToArray(); AssertGeometry(offSetAquiferCoordinates, pl2WaternetLine.Points); - WaternetLine pl3WaternetLine = waternet.WaternetLineList[1]; + WaternetLine pl3WaternetLine = waternet.WaternetLineList[2]; Assert.That(pl3WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[1])); AssertGeometry(expectedBottomAquiferCoordinates, pl3WaternetLine.Points); - WaternetLine pl4WaternetLine = waternet.WaternetLineList[2]; + WaternetLine pl4WaternetLine = waternet.WaternetLineList[3]; Assert.That(pl4WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[2])); AssertGeometry(new[] { bottomLeftUpperLayer, bottomIntermediateUpperUpperLayer, bottomIntermediateLowerUpperLayer, - bottomRightUpperLayer + bottomRightUpperLayer, + new Point2D(rightCoordinate, -17), + new Point2D(middleXCoordinate, -17), + new Point2D(middleXCoordinate, -20), + new Point2D(leftCoordinate, -20), + bottomLeftUpperLayer }, pl4WaternetLine.Points); } @@ -886,33 +1005,48 @@ AssertGeometry(plLines.Lines[PlLineType.Pl3].Points, waternet.HeadLineList[1].Points); AssertGeometry(plLines.Lines[PlLineType.Pl4].Points, waternet.HeadLineList[2].Points); - Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(3)); + Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(4)); + WaternetLine pl1WaternetLine = waternet.WaternetLineList[0]; + Assert.That(pl1WaternetLine.HeadLine.Name, Is.SameAs(waternet.PhreaticLine.Name)); + Point2D[] expectedWaternetLine1 = + [ + new Point2D(leftCoordinate, -9.999), + new Point2D(middleXCoordinate, -9.999), + new Point2D(middleXCoordinate, -20.999), + new Point2D(rightCoordinate, -20.999) + ]; + AssertGeometry(expectedWaternetLine1, pl1WaternetLine.Points); + Point2D[] expectedBottomAquiferCoordinates = { bottomLeftInBetweenLayer, - new Point2D(middleXCoordinate, -25), bottomRightInBetweenLayer }; - WaternetLine pl2WaternetLine = waternet.WaternetLineList[0]; + WaternetLine pl2WaternetLine = waternet.WaternetLineList[1]; Assert.That(pl2WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[0])); GeometryPoint[] offSetAquiferCoordinates = expectedBottomAquiferCoordinates.Select(aquiferCoordinate => new GeometryPoint(aquiferCoordinate.X, aquiferCoordinate.Z + penetrationLength)) .ToArray(); AssertGeometry(offSetAquiferCoordinates, pl2WaternetLine.Points); - WaternetLine pl3WaternetLine = waternet.WaternetLineList[1]; + WaternetLine pl3WaternetLine = waternet.WaternetLineList[2]; Assert.That(pl3WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[1])); AssertGeometry(expectedBottomAquiferCoordinates, pl3WaternetLine.Points); - WaternetLine pl4WaternetLine = waternet.WaternetLineList[2]; + WaternetLine pl4WaternetLine = waternet.WaternetLineList[3]; Assert.That(pl4WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[2])); AssertGeometry(new[] { bottomLeftUpperLayer, bottomIntermediateUpperUpperLayer, bottomIntermediateLowerUpperLayer, - bottomRightUpperLayer + bottomRightUpperLayer, + new Point2D(rightCoordinate, -21), + new Point2D(middleXCoordinate, -21), + new Point2D(middleXCoordinate, -20), + new Point2D(leftCoordinate, -20), + bottomLeftUpperLayer }, pl4WaternetLine.Points); } @@ -968,24 +1102,32 @@ AssertGeometry(plLines.Lines[PlLineType.Pl3].Points, waternet.HeadLineList[1].Points); AssertGeometry(plLines.Lines[PlLineType.Pl4].Points, waternet.HeadLineList[2].Points); - Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(2)); + Assert.That(waternet.WaternetLineList, Has.Count.EqualTo(3)); + WaternetLine pl1WaternetLine = waternet.WaternetLineList[0]; + Assert.That(pl1WaternetLine.HeadLine.Name, Is.SameAs(waternet.PhreaticLine.Name)); + Point2D[] expectedWaternetLine1 = + [ + new Point2D(leftCoordinate, -10.000), + new Point2D(rightCoordinate, -10.000) + ]; + AssertGeometry(expectedWaternetLine1, pl1WaternetLine.Points); + var bottomRightInBetweenLayer = new Point2D(rightCoordinate, -25); var bottomLeftInBetweenLayer = new Point2D(leftCoordinate, -25); Point2D[] expectedBottomAquiferCoordinates = - { + [ bottomLeftInBetweenLayer, - new Point2D(middleXCoordinate, -25), bottomRightInBetweenLayer - }; + ]; - WaternetLine pl2WaternetLine = waternet.WaternetLineList[0]; + WaternetLine pl2WaternetLine = waternet.WaternetLineList[1]; Assert.That(pl2WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[0])); GeometryPoint[] offSetAquiferCoordinates = expectedBottomAquiferCoordinates.Select(aquiferCoordinate => new GeometryPoint(aquiferCoordinate.X, aquiferCoordinate.Z + penetrationLength)) .ToArray(); AssertGeometry(offSetAquiferCoordinates, pl2WaternetLine.Points); - WaternetLine pl3WaternetLine = waternet.WaternetLineList[1]; + WaternetLine pl3WaternetLine = waternet.WaternetLineList[2]; Assert.That(pl3WaternetLine.HeadLine, Is.SameAs(waternet.HeadLineList[1])); AssertGeometry(expectedBottomAquiferCoordinates, pl3WaternetLine.Points); } @@ -1002,7 +1144,7 @@ /// -------------------------| Level -20 m /// [Test] - public void CreateWaternetBasedOnPlLines_ValidPLLinesAndSoilProfile2DWithDiscontinuousBottomAquiferLayer_ReturnsEmptyWaternet() + public void CreateWaternetBasedOnPlLines_ValidPLLinesAndSoilProfile2DWithDiscontinuousBottomAquiferLayer_ReturnsOneWaternetLineAssignedToPl1() { // Setup SoilLayer2D soilLayer = CreateRectangularSoilLayer2D(0, -10, leftCoordinate, rightCoordinate, false); @@ -1032,7 +1174,8 @@ AssertGeometry(plLines.Lines[PlLineType.Pl3].Points, waternet.HeadLineList[1].Points); AssertGeometry(plLines.Lines[PlLineType.Pl4].Points, waternet.HeadLineList[2].Points); - Assert.That(waternet.WaternetLineList, Is.Empty); + Assert.That(waternet.WaternetLineList.Count, Is.EqualTo(1)); + Assert.That(waternet.WaternetLineList[0].HeadLine.Name, Is.SameAs(waternet.PhreaticLine.Name)); } /// @@ -1045,7 +1188,7 @@ [Test] [TestCase(true, false)] [TestCase(false, true)] - public void CreateWaternetBasedOnPlLines_ValidPLLinesAndSoilProfile2DWithBottomAquiferLayerNotSpanningWholeInterval_ReturnsEmptyWaternet( + public void CreateWaternetBasedOnPlLines_ValidPLLinesAndSoilProfile2DWithBottomAquiferLayerNotSpanningWholeInterval_ReturnsOneWaternetLineAssignedToPl1( bool isLeftBottomLayerAquifer, bool isRightBottomLayerAquifer) { // Setup @@ -1077,7 +1220,8 @@ AssertGeometry(plLines.Lines[PlLineType.Pl3].Points, waternet.HeadLineList[1].Points); AssertGeometry(plLines.Lines[PlLineType.Pl4].Points, waternet.HeadLineList[2].Points); - Assert.That(waternet.WaternetLineList, Is.Empty); + Assert.That(waternet.WaternetLineList.Count, Is.EqualTo(1)); + Assert.That(waternet.WaternetLineList[0].HeadLine.Name, Is.SameAs(waternet.PhreaticLine.Name)); } [Test] @@ -1155,14 +1299,14 @@ layer = new SoilLayer1D { Name = "L1b", - TopLevel = 1.212, + TopLevel = 0, IsAquifer = false }; soilProfile1D.Layers.Add(layer); layer = new SoilLayer1D { Name = "L2a", - TopLevel = -2.111, // top of in-between aquifer + TopLevel = -2.111, // top of highest in-between aquifer IsAquifer = true }; soilProfile1D.Layers.Add(layer); @@ -1183,7 +1327,7 @@ layer = new SoilLayer1D { Name = "L4", - TopLevel = -4.151, + TopLevel = -4.151, // top of lowest in-between aquifer IsAquifer = true }; soilProfile1D.Layers.Add(layer); @@ -1294,7 +1438,6 @@ 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"); @@ -1514,13 +1657,13 @@ } } - private static PlLines CreateAllPlLines() + private static PlLines CreateAllPlLines(double pl1Level = -5) { var plLines = new PlLines(); PlLine plLine = plLines.Lines[PlLineType.Pl1]; plLine.Name = "Phreatic line"; - plLine.Points.Add(new PlLinePoint(leftCoordinate, -5)); - plLine.Points.Add(new PlLinePoint(rightCoordinate, -5)); + plLine.Points.Add(new PlLinePoint(leftCoordinate, pl1Level)); + plLine.Points.Add(new PlLinePoint(rightCoordinate, pl1Level)); plLine = plLines.Lines[PlLineType.Pl2]; plLine.Name = "PL 2"; Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverter.cs =================================================================== diff -u -r4898 -r4906 --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverter.cs (.../PlLinesToWaternetConverter.cs) (revision 4898) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/KernelWrappers/MacroStabilityCommon/PlLinesToWaternetConverter.cs (.../PlLinesToWaternetConverter.cs) (revision 4906) @@ -34,10 +34,25 @@ public static class PlLinesToWaternetConverter { + private enum LayerType + { + BottomAquifer, + TopLayerInBetweenAquiferCluster, + BottomLayerInBetweenAquiferCluster + } + private const double epsilon = 1e-9; + private const string waternetLine1Name = "Waternet line phreatic line"; + private const string waternetLine2Name = "Penetration zone lower aquifer"; + private const string waternetLine3Name = "Waternet line lower aquifer"; + private const string waternetLine4Name = "Waternet area in-between aquifer"; + private const string headLine1Name = "Phreatic line (PL 1)"; + private const string headLine2Name = "Head line 2 (PL 2)"; + private const string headLine3Name = "Head line 3 (PL 3)"; + private const string headLine4Name = "Head line 4 (PL 4)"; /// - /// Converts the pl lines to a waternet. + /// Creates a based on a 1D profile by assigning the to waternet lines. /// /// The pl lines. /// The 1D soil profile. @@ -49,68 +64,79 @@ { ThrowWhenPlLinesIsNull(plLines); ThrowWhenSoilProfileIsNull(soilProfile1D); - var waternet = new Waternet - { - IsGenerated = false - }; + var waternet = new Waternet(); PlLine plLine = plLines.Lines[PlLineType.Pl1]; + var headLine = CreateLine(plLine, headLine1Name); if (plLine != null && !IsBelowSoilProfile(soilProfile1D, plLine)) { - waternet.PhreaticLine = CreateLine(plLine); - waternet.PhreaticLine.SyncCalcPoints(); + waternet.PhreaticLine = CreateLine(plLine, headLine1Name); + double level = soilProfile1D.GetLayerAt(headLine.GetMinZ()).BottomLevel; + WaternetLine waternetLine = CreateWaternetLine(level, xLeft, xRight); + waternetLine.Name = waternetLine1Name; + waternetLine.HeadLine = headLine; + waternetLine.HeadLine.SyncCalcPoints(); + waternet.WaternetLineList.Add(waternetLine); } plLine = plLines.Lines[PlLineType.Pl2]; - var headLine = CreateLine(plLine); + headLine = CreateLine(plLine, headLine2Name); if (headLine != null && !IsBelowSoilProfile(soilProfile1D, plLine)) { waternet.HeadLineList.Add(headLine); if (soilProfile1D.BottomAquiferLayer != null) { double level = soilProfile1D.BottomAquiferLayer.TopLevel + penetrationLength; WaternetLine waternetLine = CreateWaternetLine(level, xLeft, xRight); + waternetLine.Name = waternetLine2Name; waternetLine.HeadLine = headLine; - waternetLine.HeadLine.SyncCalcPoints(); waternet.WaternetLineList.Add(waternetLine); } } plLine = plLines.Lines[PlLineType.Pl3]; - headLine = CreateLine(plLine); + headLine = CreateLine(plLine, headLine3Name); if (headLine != null && !IsBelowSoilProfile(soilProfile1D, plLine)) { waternet.HeadLineList.Add(headLine); if (soilProfile1D.BottomAquiferLayer != null) { double level = soilProfile1D.BottomAquiferLayer.TopLevel; WaternetLine waternetLine = CreateWaternetLine(level, xLeft, xRight); + waternetLine.Name = waternetLine3Name; waternetLine.HeadLine = headLine; - waternetLine.HeadLine.SyncCalcPoints(); waternet.WaternetLineList.Add(waternetLine); } } plLine = plLines.Lines[PlLineType.Pl4]; - headLine = CreateLine(plLine); + headLine = CreateLine(plLine, headLine4Name); if (headLine != null && !IsBelowSoilProfile(soilProfile1D, plLine)) { waternet.HeadLineList.Add(headLine); - if (soilProfile1D.InBetweenAquiferLayer != null) + if (soilProfile1D.InBetweenAquiferClusters != null) { - double level = soilProfile1D.InBetweenAquiferLayer.TopLevel; - WaternetLine waternetLine = CreateWaternetLine(level, xLeft, xRight); - waternetLine.HeadLine = headLine; - waternetLine.HeadLine.SyncCalcPoints(); - waternet.WaternetLineList.Add(waternetLine); + foreach ((SoilLayer1D, SoilLayer1D) inBetweenAquiferCluster in soilProfile1D.InBetweenAquiferClusters) + { + if (inBetweenAquiferCluster is { Item1: not null, Item2: not null }) + { + double levelTop = inBetweenAquiferCluster.Item1.TopLevel; + double levelBottom = inBetweenAquiferCluster.Item2.BottomLevel; + WaternetLine waternetLine = CreateWaternetLine(levelTop, levelBottom, xLeft, xRight); + waternetLine.Name = waternetLine4Name; + waternetLine.HeadLine = headLine; + waternet.WaternetLineList.Add(waternetLine); + } + } } } + AdjustWaternetLineOfPhreaticLineWhenCoincidingWithOtherWaternetLines(waternet); return waternet; } /// - /// Converts the to a based on a 2D profile. + /// Creates a based on a 2D profile by assigning the to waternet lines. /// /// The to convert. /// The to convert the with. @@ -123,29 +149,31 @@ ThrowWhenPlLinesIsNull(plLines); ThrowWhenSoilProfileIsNull(soilProfile); - // 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(); + double[] xCoordinates = DetermineAllXCoordinatesOfSoilProfile(soilProfile); + Point2D[] bottomAquiferCoordinates = DetermineLayerBoundaryCoordinates(LayerType.BottomAquifer, xCoordinates, soilProfile).ToArray(); - Point2D[] bottomAquiferCoordinates = GetAquiferCoordinates(sp => sp.BottomAquiferLayer, xCoordinates, soilProfile).ToArray(); - Point2D[] inBetweenAquiferCoordinates = GetAquiferCoordinates(sp => sp.InBetweenAquiferLayer, xCoordinates, soilProfile).ToArray(); - var waternet = new Waternet(); PlLine plLine = plLines.Lines[PlLineType.Pl1]; if (plLine != null) { - waternet.PhreaticLine = CreateLine(plLine); + var headLine = CreateLine(plLine, headLine1Name); + waternet.PhreaticLine = CreateLine(plLine, headLine1Name); + WaternetLine waternetLine = CreateWaternetLineForPhreaticLine(soilProfile, plLine); + waternetLine.Name = waternetLine1Name; + waternetLine.HeadLine = headLine; + waternet.WaternetLineList.Add(waternetLine); } plLine = plLines.Lines[PlLineType.Pl2]; if (plLine != null) { - var headLine = CreateLine(plLine); + var headLine = CreateLine(plLine, headLine2Name); waternet.HeadLineList.Add(headLine); if (bottomAquiferCoordinates.Any()) { WaternetLine waternetLine = CreateWaternetLine(bottomAquiferCoordinates, penetrationLength); + waternetLine.Name = waternetLine2Name; waternetLine.HeadLine = headLine; waternet.WaternetLineList.Add(waternetLine); } @@ -154,30 +182,22 @@ plLine = plLines.Lines[PlLineType.Pl3]; if (plLine != null) { - var headLine = CreateLine(plLine); + var headLine = CreateLine(plLine, headLine3Name); waternet.HeadLineList.Add(headLine); if (bottomAquiferCoordinates.Any()) { WaternetLine waternetLine = CreateWaternetLine(bottomAquiferCoordinates); waternetLine.HeadLine = headLine; + waternetLine.Name = waternetLine3Name; waternet.WaternetLineList.Add(waternetLine); } } - plLine = plLines.Lines[PlLineType.Pl4]; - if (plLine != null) - { - var headLine = CreateLine(plLine); - waternet.HeadLineList.Add(headLine); - if (inBetweenAquiferCoordinates.Any()) - { - WaternetLine waternetLine = CreateWaternetLine(inBetweenAquiferCoordinates); - waternetLine.HeadLine = headLine; - waternet.WaternetLineList.Add(waternetLine); - } - } + CreateWaternetLinesForInBetweenAquifers(waternet, soilProfile, plLines.Lines[PlLineType.Pl4]); + AdjustWaternetLineOfPhreaticLineWhenCoincidingWithOtherWaternetLines(waternet); + return waternet; } @@ -190,7 +210,82 @@ return waternetLine; } - private static TLineType CreateLine(PlLine plLine) + internal static WaternetLine CreateWaternetLine(double levelTop, double levelBottom, double xLeft, double xRight) + { + var waternetLine = new WaternetLine(); + waternetLine.Points.Add(new GeometryPoint(xLeft, levelTop)); + waternetLine.Points.Add(new GeometryPoint(xRight, levelTop)); + waternetLine.Points.Add(new GeometryPoint(xRight, levelBottom)); + waternetLine.Points.Add(new GeometryPoint(xLeft, levelBottom)); + waternetLine.Points.Add(new GeometryPoint(xLeft, levelTop)); + 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, SoilProfile2D soilProfile, PlLine plLine) + { + if (plLine == null) + { + return; + } + + var headLine = CreateLine(plLine, headLine4Name); + waternet.HeadLineList.Add(headLine); + + double[] xCoordinates = DetermineAllXCoordinatesOfSoilProfile(soilProfile); + int inBetweenAquiferCount = DetermineInBetweenAquiferClusterCount(soilProfile, xCoordinates); + if (inBetweenAquiferCount == 0) + { + return; + } + + for (var i = 0; i < inBetweenAquiferCount; i++) + { + Point2D[] inBetweenAquiferUpperCoordinates = DetermineLayerBoundaryCoordinates(LayerType.TopLayerInBetweenAquiferCluster, xCoordinates, soilProfile, i).ToArray(); + Point2D[] inBetweenAquiferLowerCoordinates = DetermineLayerBoundaryCoordinates(LayerType.BottomLayerInBetweenAquiferCluster, xCoordinates, soilProfile, i).ToArray(); + + if (inBetweenAquiferUpperCoordinates.Any() && inBetweenAquiferLowerCoordinates.Any()) + { + WaternetLine waternetLine = CreateWaternetLine(inBetweenAquiferUpperCoordinates, inBetweenAquiferLowerCoordinates); + waternetLine.HeadLine = headLine; + waternetLine.Name = waternetLine4Name; + waternet.WaternetLineList.Add(waternetLine); + } + } + } + + private static int DetermineInBetweenAquiferClusterCount(SoilProfile2D soilProfile, double[] xCoordinates) + { + var currentCount = 0; + var previousCount = 0; + for (var i = 0; i < xCoordinates.Length; i++) + { + SoilProfile1D crossSection = soilProfile.GetSoilProfile1D(xCoordinates[i]); + currentCount = crossSection.InBetweenAquiferClusters?.Count ?? 0; + if (i > 0 && currentCount != previousCount) + { + return 0; + } + + previousCount = currentCount; + } + + return currentCount; + } + + private static TLineType CreateLine(PlLine plLine, string name) where TLineType : GeometryPointString, new() { if (plLine == null || !plLine.Points.Any()) @@ -200,6 +295,8 @@ var line = new TLineType(); line.Points.AddRange(plLine.Points); + line.SyncCalcPoints(); + line.Name = name; return line; } @@ -218,18 +315,51 @@ } line.SyncCalcPoints(); + LineHelper.RemoveDuplicatedPoints(line.CalcPoints, epsilon); + line.RemoveUnnecessaryPoints(); + line.SyncPoints(); + return line; } - private static IEnumerable GetAquiferCoordinates(Func getSoilLayerFunc, double[] xCoordinates, - SoilProfile2D soilProfile) + private static WaternetLine CreateWaternetLine(IEnumerable upperCoordinates, IEnumerable lowerCoordinates) { + var line = new WaternetLine(); + List upperCoordList = upperCoordinates.ToList(); + upperCoordList.Sort(); + GeometryPoint point; + foreach (Point2D upperCoord in upperCoordList) + { + point = new GeometryPoint(upperCoord.X, upperCoord.Z); + line.Points.Add(point); + } + + List lowerCoordList = lowerCoordinates.ToList(); + lowerCoordList.Sort(); + for (int i = lowerCoordList.Count - 1; i >= 0; i--) + { + point = new GeometryPoint(lowerCoordList[i].X, lowerCoordList[i].Z); + line.Points.Add(point); + } + + point = new GeometryPoint(upperCoordList[0].X, upperCoordList[0].Z); + line.Points.Add(point); + + line.SyncCalcPoints(); + line.RemoveUnnecessaryPoints(); + line.SyncPoints(); + + return line; + } + + private static IEnumerable DetermineLayerBoundaryCoordinates(LayerType layerType, double[] xCoordinates, SoilProfile2D soilProfile, int indexInBetweenAquifer = -1) + { SoilLayer1D previousAquiferLayer = null; var coordinates = new List(); var xCoordinatesAll = new List(); foreach (double xCoordinate in xCoordinates) { - if (HasAquiferAVerticalPartAtGivenX(getSoilLayerFunc, xCoordinate, soilProfile)) + if (HasAquiferAVerticalPartAtGivenX(layerType, xCoordinate, soilProfile, indexInBetweenAquifer)) { xCoordinatesAll.Add(xCoordinate - epsilon); xCoordinatesAll.Add(xCoordinate + epsilon); @@ -243,9 +373,10 @@ 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); + // Determine if the layer is in range of the previous layer + // If not, return empty coordinates, because the layer is interrupted + + SoilLayer1D currentAquifer = GetSoilLayer1D(layerType, crossSection, indexInBetweenAquifer); if (previousAquiferLayer != null && currentAquifer != null && !AreHorizontallyConnected(previousAquiferLayer, currentAquifer)) { @@ -255,12 +386,12 @@ if (currentAquifer != null) { previousAquiferLayer = currentAquifer; - coordinates.Add(new Point2D(xCoordinate, currentAquifer.TopLevel)); + coordinates.Add(layerType is LayerType.BottomAquifer or LayerType.TopLayerInBetweenAquiferCluster ? new Point2D(xCoordinate, currentAquifer.TopLevel) : new Point2D(xCoordinate, currentAquifer.BottomLevel)); } } // 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. + // of the profile. If not, the layer is not continuous. if (!coordinates.Any() || coordinates.First().X != xCoordinates.First() || coordinates.Last().X != xCoordinates.Last()) { return Enumerable.Empty(); @@ -269,17 +400,97 @@ return coordinates; } - private static bool HasAquiferAVerticalPartAtGivenX(Func getSoilLayerFunc, double xCoordinate, SoilProfile2D soilProfile) + private static SoilLayer1D GetSoilLayer1D(LayerType layerType, SoilProfile1D soilProfile1D, int indexInBetweenAquifer) { + switch (layerType) + { + case LayerType.BottomAquifer: + return soilProfile1D.BottomAquiferLayer; + case LayerType.TopLayerInBetweenAquiferCluster: + return soilProfile1D.InBetweenAquiferClusters[indexInBetweenAquifer].Item1; + case LayerType.BottomLayerInBetweenAquiferCluster: + return soilProfile1D.InBetweenAquiferClusters[indexInBetweenAquifer].Item2; + } + + return null; + } + + /// + /// Create the waternet line associated to the phreatic line (PL1). + /// For the waternet type "Dam Standard", this line lies on the bottom of the soil layers “in which the phreatic plane lies”. + /// Where PL1 lies above the ground level, the line lies on the ground level. + /// As a consequence, the line can contain vertical "jumps" when layers have vertical parts. + /// + /// + /// + /// + private static WaternetLine CreateWaternetLineForPhreaticLine(SoilProfile2D soilProfile2D, PlLine plLine) + { + var waternetLine = new WaternetLine(); + var curves = new List(); + foreach (SoilLayer2D surface in soilProfile2D.Surfaces) + { + GeometryLoop outerLoop = surface.GeometrySurface.OuterLoop; + // Add the first point to get a closed GeometryPointString + outerLoop.CalcPoints.Add(new Point2D(outerLoop.CalcPoints[0].X, outerLoop.CalcPoints[0].Z)); + + var plLineInGeometryPoint = new GeometryPointString(); + foreach (PlLinePoint point in plLine.Points) + { + plLineInGeometryPoint.Points.Add(new GeometryPoint(point.X, point.Z)); + plLineInGeometryPoint.SyncCalcPoints(); + } + + List intersectionPoints = outerLoop.IntersectionXzPointsWithGeometryString(plLineInGeometryPoint); + // Only the layers that are below and do not intersect the phreatic line are kept. + if ((intersectionPoints.Count == 0) && (outerLoop[0].Z < plLineInGeometryPoint.GetZatX(outerLoop[0].X))) + { + var geometrySurface = new GeometrySurface(outerLoop); + GeometryPointString top = geometrySurface.DetermineTopGeometrySurface(); + top.SyncPoints(); + top.SortPointsByXAscending(); + curves.Add(top); + } + } + + // The waternet line is the polyline formed with the highest lines connected to each other. + List lines = DivideCurvesIntoLines(curves); + IEnumerable allPoints = curves.SelectMany(curve => curve.CalcPoints); + double[] xCoordinates = allPoints.Select(point => point.X).OrderBy(x => x).Distinct().ToArray(); + List splitLines = SplitLinesAtXCoordinates(xCoordinates, lines); + for (var i = 0; i < xCoordinates.Length - 1; i++) + { + List relevantLines = splitLines.FindAll(line => Math.Abs(line.BeginPoint.X - xCoordinates[i]) < epsilon && Math.Abs(line.BeginPoint.X - line.EndPoint.X) > 0); + double max = relevantLines.Select(line => line.BeginPoint.Z).Max(); + Line relevantLine = relevantLines.Find(line => Math.Abs(line.BeginPoint.Z - max) < epsilon); + waternetLine.Points.Add(new GeometryPoint(relevantLine.BeginPoint.X, relevantLine.BeginPoint.Z)); + waternetLine.Points.Add(new GeometryPoint(relevantLine.EndPoint.X, relevantLine.EndPoint.Z)); + } + + waternetLine.SyncCalcPoints(); + LineHelper.RemoveDuplicatedPoints(waternetLine.CalcPoints, epsilon); + waternetLine.RemoveUnnecessaryPoints(); + waternetLine.SyncPoints(); + + return waternetLine; + } + + private static bool HasAquiferAVerticalPartAtGivenX(LayerType layerType, double xCoordinate, SoilProfile2D soilProfile, int indexInBetweenAquifer) + { 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); + SoilLayer1D currentAquiferLeft = GetSoilLayer1D(layerType, crossSectionLeft, indexInBetweenAquifer); + SoilLayer1D currentAquiferRight = GetSoilLayer1D(layerType, crossSectionRight, indexInBetweenAquifer); if (currentAquiferLeft != null && currentAquiferRight != null) { - return Math.Abs(currentAquiferLeft.TopLevel - currentAquiferRight.TopLevel) > GeometryConstants.Accuracy; + if (layerType is LayerType.BottomAquifer or LayerType.TopLayerInBetweenAquiferCluster) + { + return Math.Abs(currentAquiferLeft.TopLevel - currentAquiferRight.TopLevel) > GeometryConstants.Accuracy; + } + + return Math.Abs(currentAquiferLeft.BottomLevel - currentAquiferRight.BottomLevel) > GeometryConstants.Accuracy; } } @@ -343,4 +554,77 @@ throw new NoNullAllowedException(Resources.PlLinesToWaternetConverter_NoPlLinesDefined); } } + + /// + /// Waternet lines can't coincide otherwise the program does not know which headline to use. + /// That's why when the waternet line of the phreatic line coincides with the waternet line of either PL2, PL3 or PL4, + /// an adjustment of the waternet line of the phreatic line is performed by moving it 1 mm upwards. + /// + /// The waternet. + private static void AdjustWaternetLineOfPhreaticLineWhenCoincidingWithOtherWaternetLines(Waternet waternet) + { + const double minimumDistance = 0.001; + WaternetLine waternetLine1 = waternet.WaternetLineList.Find(w => w.Name == waternetLine1Name); + foreach (WaternetLine waternetLineOther in waternet.WaternetLineList.FindAll(w => w.Name != waternetLine1Name)) + { + foreach (GeometryPoint waternetLine1Point in waternetLine1.Points) + { + if (waternetLineOther.Points.Any(point => Math.Abs(point.Z - waternetLine1Point.Z) < epsilon)) + { + waternetLine1Point.Z += minimumDistance; + } + } + } + } + + private static List SplitLinesAtXCoordinates(double[] xCoordinates, List lines) + { + var splitLines = new List(); + foreach (Line line in lines) + { + List xCoordinatesToBeAdded = xCoordinates.Where(xCoordinate => line.BeginPoint.X.IsLessThan(xCoordinate) && xCoordinate.IsLessThan(line.EndPoint.X)).ToList(); + + if (xCoordinatesToBeAdded.Count > 0) + { + for (var i = 0; i < xCoordinatesToBeAdded.Count; i++) + { + var point1 = new Point2D(xCoordinatesToBeAdded[i], Math.Max(line.BeginPoint.Z, line.EndPoint.Z) + 1); + var point2 = new Point2D(xCoordinatesToBeAdded[i], Math.Min(line.BeginPoint.Z, line.EndPoint.Z) - 1); + double zCoordinate = line.GetIntersectPointXz(new Line(point1, point2)).Z; + + var middlePoint = new Point2D(xCoordinatesToBeAdded[i], zCoordinate); + Point2D beginPoint = i == 0 ? line.BeginPoint : middlePoint; + Point2D endPoint = i == xCoordinatesToBeAdded.Count - 1 ? line.EndPoint : middlePoint; + + splitLines.Add(new Line(beginPoint, middlePoint)); + splitLines.Add(new Line(middlePoint, endPoint)); + } + } + else + { + splitLines.Add(line); + } + } + + return splitLines; + } + + private static List DivideCurvesIntoLines(List curves) + { + var lines = new List(); + foreach (GeometryPointString curve in curves) + { + for (var i = 0; i < curve.Points.Count - 1; i++) + { + var line = new Line + { + BeginPoint = new Point2D(curve.Points[i].X, curve.Points[i].Z), + EndPoint = new Point2D(curve.Points[i + 1].X, curve.Points[i + 1].Z) + }; + lines.Add(line); + } + } + + return lines; + } } \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data.Tests/Geotechnics/SoilProfile1DTests.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Data.Tests/Geotechnics/SoilProfile1DTests.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Data.Tests/Geotechnics/SoilProfile1DTests.cs (revision 4906) @@ -0,0 +1,223 @@ +// 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 Deltares.DamEngine.Data.Geotechnics; +using NUnit.Framework; + +namespace Deltares.DamEngine.Data.Tests.Geotechnics; + +[TestFixture] +public class SoilProfile1DTests +{ + private const double topLevelLayer1 = 17; + private const double topLevelLayer2 = 12; + private const double topLevelLayer3 = 4; + private const double topLevelLayer4 = 0; + private const double topLevelLayer5 = -5; + private const double topLevelLayer6 = -9; + private const double bottomLevelLayer6 = -20; + + [Test] + public void GivenASoilProfile1D_WhenGettingTheAquifersPosition_ThenExpectedPositionsAreReturned() + { + // 3 layers with aquifer, aquitard, aquifer => no in-between aquifer and bottom aquifer is layer 3 + SoilProfile1D soilProfile1D = CreateProfileWith3Layers(true, false, true); + Assert.Multiple(() => + { + Assert.That(soilProfile1D.InBetweenAquiferClusters, Is.Null); + Assert.That(soilProfile1D.InBetweenAquiferLayer, Is.Null); + Assert.That(soilProfile1D.BottomAquiferLayer.TopLevel, Is.EqualTo(topLevelLayer3)); + Assert.That(soilProfile1D.BottomAquiferLayer.BottomLevel, Is.EqualTo(topLevelLayer4)); + }); + + // 3 clusters with aquifer, aquitard, aquifer => no in-between aquifer and bottom aquifer is the highest layer of cluster 5-6 + soilProfile1D = CreateProfileWith6Layers(true, true, false, false, true, true); + Assert.Multiple(() => + { + Assert.That(soilProfile1D.InBetweenAquiferClusters, Is.Null); + Assert.That(soilProfile1D.InBetweenAquiferLayer, Is.Null); + Assert.That(soilProfile1D.BottomAquiferLayer.TopLevel, Is.EqualTo(topLevelLayer5)); + Assert.That(soilProfile1D.BottomAquiferLayer.BottomLevel, Is.EqualTo(topLevelLayer6)); + }); + + // All layers are aquifers => no in-between aquifer and bottom aquifer is the highest aquifer (layer 1) + soilProfile1D = CreateProfileWith6Layers(true, true, true, true, true, true); + Assert.Multiple(() => + { + Assert.That(soilProfile1D.InBetweenAquiferClusters, Is.Null); + Assert.That(soilProfile1D.InBetweenAquiferLayer, Is.Null); + Assert.That(soilProfile1D.BottomAquiferLayer.TopLevel, Is.EqualTo(topLevelLayer1)); + Assert.That(soilProfile1D.BottomAquiferLayer.BottomLevel, Is.EqualTo(topLevelLayer2)); + }); + + // All layers are aquitard => no in-between aquifer and no bottom aquifer + soilProfile1D = CreateProfileWith6Layers(false, false, false, false, false, false); + Assert.Multiple(() => + { + Assert.That(soilProfile1D.InBetweenAquiferClusters, Is.Null); + Assert.That(soilProfile1D.InBetweenAquiferLayer, Is.Null); + Assert.That(soilProfile1D.BottomAquiferLayer, Is.Null); + }); + + // Aquifers are at top and bottom of the profile => no in-between aquifer and bottom aquifer is the highest layer of cluster 3-6 + soilProfile1D = CreateProfileWith6Layers(true, false, true, true, true, true); + Assert.Multiple(() => + { + Assert.That(soilProfile1D.InBetweenAquiferClusters, Is.Null); + Assert.That(soilProfile1D.InBetweenAquiferLayer, Is.Null); + Assert.That(soilProfile1D.BottomAquiferLayer.TopLevel, Is.EqualTo(topLevelLayer3)); + Assert.That(soilProfile1D.BottomAquiferLayer.BottomLevel, Is.EqualTo(topLevelLayer4)); + }); + + // Top aquitard cluster and bottom aquifer cluster => no in-between aquifer and bottom aquifer is the highest layer of cluster 4-6 + soilProfile1D = CreateProfileWith6Layers(false, false, false, true, true, true); + Assert.Multiple(() => + { + Assert.That(soilProfile1D.InBetweenAquiferClusters, Is.Null); + Assert.That(soilProfile1D.InBetweenAquiferLayer, Is.Null); + Assert.That(soilProfile1D.BottomAquiferLayer.TopLevel, Is.EqualTo(topLevelLayer4)); + Assert.That(soilProfile1D.BottomAquiferLayer.BottomLevel, Is.EqualTo(topLevelLayer5)); + }); + + // 2 in-between aquifers present (layers 2 and 4) and bottom aquifer is the lowest layer + soilProfile1D = CreateProfileWith6Layers(false, true, false, true, false, true); + Assert.That(soilProfile1D.InBetweenAquiferClusters, Has.Count.EqualTo(2)); + Assert.Multiple(() => + { + Assert.That(soilProfile1D.InBetweenAquiferClusters[0].Item1.TopLevel, Is.EqualTo(topLevelLayer2)); + Assert.That(soilProfile1D.InBetweenAquiferClusters[0].Item2.BottomLevel, Is.EqualTo(topLevelLayer3)); + Assert.That(soilProfile1D.InBetweenAquiferClusters[1].Item1.TopLevel, Is.EqualTo(topLevelLayer4)); + Assert.That(soilProfile1D.InBetweenAquiferClusters[1].Item2.BottomLevel, Is.EqualTo(topLevelLayer5)); + Assert.That(soilProfile1D.InBetweenAquiferLayer.TopLevel, Is.EqualTo(topLevelLayer2)); + Assert.That(soilProfile1D.InBetweenAquiferLayer.BottomLevel, Is.EqualTo(topLevelLayer3)); + Assert.That(soilProfile1D.BottomAquiferLayer.TopLevel, Is.EqualTo(topLevelLayer6)); + Assert.That(soilProfile1D.BottomAquiferLayer.BottomLevel, Is.EqualTo(bottomLevelLayer6)); + }); + + // 1 in-between aquifers cluster present (layers 2-4) and bottom aquifer is the lowest layer + soilProfile1D = CreateProfileWith6Layers(false, true, true, true, false, true); + Assert.That(soilProfile1D.InBetweenAquiferClusters, Has.Count.EqualTo(1)); + Assert.Multiple(() => + { + Assert.That(soilProfile1D.InBetweenAquiferClusters[0].Item1.TopLevel, Is.EqualTo(topLevelLayer2)); + Assert.That(soilProfile1D.InBetweenAquiferClusters[0].Item2.BottomLevel, Is.EqualTo(topLevelLayer5)); + Assert.That(soilProfile1D.InBetweenAquiferLayer.TopLevel, Is.EqualTo(topLevelLayer2)); + Assert.That(soilProfile1D.InBetweenAquiferLayer.BottomLevel, Is.EqualTo(topLevelLayer3)); + Assert.That(soilProfile1D.BottomAquiferLayer.TopLevel, Is.EqualTo(topLevelLayer6)); + Assert.That(soilProfile1D.BottomAquiferLayer.BottomLevel, Is.EqualTo(bottomLevelLayer6)); + }); + + // 1 in-between aquifer present (layer 2) and bottom aquifer is the highest layer of cluster 4-6 + soilProfile1D = CreateProfileWith6Layers(false, true, false, true, true, true); + Assert.That(soilProfile1D.InBetweenAquiferClusters, Has.Count.EqualTo(1)); + Assert.Multiple(() => + { + Assert.That(soilProfile1D.InBetweenAquiferClusters[0].Item1.TopLevel, Is.EqualTo(topLevelLayer2)); + Assert.That(soilProfile1D.InBetweenAquiferClusters[0].Item2.BottomLevel, Is.EqualTo(topLevelLayer3)); + Assert.That(soilProfile1D.InBetweenAquiferLayer.TopLevel, Is.EqualTo(topLevelLayer2)); + Assert.That(soilProfile1D.InBetweenAquiferLayer.BottomLevel, Is.EqualTo(topLevelLayer3)); + Assert.That(soilProfile1D.BottomAquiferLayer.TopLevel, Is.EqualTo(topLevelLayer4)); + Assert.That(soilProfile1D.BottomAquiferLayer.BottomLevel, Is.EqualTo(topLevelLayer5)); + }); + } + + private static SoilProfile1D CreateProfileWith6Layers(bool isLayer1Aquifer, bool isLayer2Aquifer, bool isLayer3Aquifer, + bool isLayer4Aquifer, bool isLayer5Aquifer, bool isLayer6Aquifer) + { + var soilProfile1D = new SoilProfile1D(); + + var soilLayer1 = new SoilLayer1D + { + TopLevel = topLevelLayer1, + IsAquifer = isLayer1Aquifer + }; + soilProfile1D.Layers.Add(soilLayer1); + + var soilLayer2 = new SoilLayer1D + { + TopLevel = topLevelLayer2, + IsAquifer = isLayer2Aquifer + }; + soilProfile1D.Layers.Add(soilLayer2); + + var soilLayer3 = new SoilLayer1D + { + TopLevel = topLevelLayer3, + IsAquifer = isLayer3Aquifer + }; + soilProfile1D.Layers.Add(soilLayer3); + + var soilLayer4 = new SoilLayer1D + { + TopLevel = topLevelLayer4, + IsAquifer = isLayer4Aquifer + }; + soilProfile1D.Layers.Add(soilLayer4); + + var soilLayer5 = new SoilLayer1D + { + TopLevel = topLevelLayer5, + IsAquifer = isLayer5Aquifer + }; + soilProfile1D.Layers.Add(soilLayer5); + + var soilLayer6 = new SoilLayer1D + { + TopLevel = topLevelLayer6, + IsAquifer = isLayer6Aquifer + }; + soilProfile1D.Layers.Add(soilLayer6); + + soilProfile1D.BottomLevel = bottomLevelLayer6; + + return soilProfile1D; + } + + private static SoilProfile1D CreateProfileWith3Layers(bool isLayer1Aquifer, bool isLayer2Aquifer, bool isLayer3Aquifer) + { + var soilProfile1D = new SoilProfile1D(); + + var soilLayer1 = new SoilLayer1D + { + TopLevel = topLevelLayer1, + IsAquifer = isLayer1Aquifer + }; + soilProfile1D.Layers.Add(soilLayer1); + + var soilLayer2 = new SoilLayer1D + { + TopLevel = topLevelLayer2, + IsAquifer = isLayer2Aquifer + }; + soilProfile1D.Layers.Add(soilLayer2); + + var soilLayer3 = new SoilLayer1D + { + TopLevel = topLevelLayer3, + IsAquifer = isLayer3Aquifer + }; + soilProfile1D.Layers.Add(soilLayer3); + + soilProfile1D.BottomLevel = topLevelLayer4; + + return soilProfile1D; + } +} \ No newline at end of file Index: DamEngine/trunk/src/Deltares.DamEngine.Data/Geotechnics/SoilProfile1D.cs =================================================================== diff -u -r4540 -r4906 --- DamEngine/trunk/src/Deltares.DamEngine.Data/Geotechnics/SoilProfile1D.cs (.../SoilProfile1D.cs) (revision 4540) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/Geotechnics/SoilProfile1D.cs (.../SoilProfile1D.cs) (revision 4906) @@ -149,10 +149,10 @@ public SoilLayer1D InfiltrationLayer { get; private set; } /// - /// Gets the toppest aquifer layer of the deepest cluster of aquifers + /// Gets the highest aquifer layer of the deepest cluster of aquifers /// /// - /// The toppest aquifer layer in the deepest cluster of aquifers + /// The highest aquifer layer in the deepest cluster of aquifers /// public SoilLayer1D BottomAquiferLayer { @@ -174,7 +174,7 @@ } } - // aquifer may consists of more then 1 connected (aquifer) layers + // aquifer may consist of more than 1 connected (aquifer) layers // Search all layers above the first found aquifer to find top aquifer layer if (aquiferIndex >= 0) { @@ -197,40 +197,84 @@ } /// - /// Gets the highest aquifer in the highest cluster of in-between aquifers. + /// Gets the list of all clusters of in-between aquifers: + /// Item1 is the top aquifer and Item2 the bottom aquifer of the cluster. + /// For a cluster of 1 layer, Item1 and Item2 are equal. /// The top layer of a cluster of in-between aquifer can't be the highest layer of the soil profile. /// /// - /// The highest aquifer in the highest in-between cluster of aquifers + /// The list of all clusters of in-between aquifers. /// - public SoilLayer1D InBetweenAquiferLayer + public IList<(SoilLayer1D, SoilLayer1D)> InBetweenAquiferClusters { get { + // At least 4 layers are needed (aquitard, aquifer, aquitard, aquifer) to get one in-between aquifer + if (LayerCount <= 3) + { + return null; + } + IList sortedLayers = Layers.OrderByDescending(l => l.TopLevel).ToList(); - SoilLayer1D aquiferLayer = null; + IList<(SoilLayer1D, SoilLayer1D)> aquiferLayers = new List<(SoilLayer1D, SoilLayer1D)>(); - // Search the highest aquifer layer with an aquitard above - for (var layerIndex = 1; layerIndex < sortedLayers.Count; layerIndex++) + SoilLayer1D highestAquiferSoFar = null; + // Search the aquifer layers with an aquitard above + for (var layerIndex = 1; layerIndex < sortedLayers.Count - 1; layerIndex++) { SoilLayer1D previousLayer = sortedLayers[layerIndex - 1]; SoilLayer1D layer = sortedLayers[layerIndex]; - if (IsAquiferLayer(layer) && !IsAquiferLayer(previousLayer)) + SoilLayer1D nextLayer = sortedLayers[layerIndex + 1]; + + if (IsAquiferLayer(layer)) { - aquiferLayer = layer; + if (!IsAquiferLayer(previousLayer)) + { + + if (!IsAquiferLayer(nextLayer)) + { + aquiferLayers.Add(new ValueTuple(layer, layer)); + } + else + { + highestAquiferSoFar = layer; + } + } + else + { + if (!IsAquiferLayer(nextLayer) && highestAquiferSoFar != null) + { + aquiferLayers.Add(new ValueTuple(highestAquiferSoFar, layer)); + highestAquiferSoFar = null; + } + } + } + + if (layerIndex >= sortedLayers.IndexOf(BottomAquiferLayer)) + { break; } } - // If highest aquifer layer is bottom aquifer layer, there is no in between aquiferlayer - if (aquiferLayer == BottomAquiferLayer) - { - aquiferLayer = null; - } + return aquiferLayers.Count == 0 ? null : aquiferLayers; - return aquiferLayer; } } + + /// + /// Gets the highest aquifer in the highest cluster of in-between aquifers. + /// The top layer of a cluster of in-between aquifer can't be the highest layer of the soil profile. + /// + /// + /// The highest aquifer in the highest in-between cluster of aquifers + /// + public SoilLayer1D InBetweenAquiferLayer + { + get + { + return InBetweenAquiferClusters?.First().Item1; + } + } /// /// Assigns the specified profile.