// Copyright (C) Stichting Deltares 2025. 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.Linq; using Deltares.DamEngine.Calculators.KernelWrappers.Common; using Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityCommon; using Deltares.DamEngine.Data.General; using Deltares.DamEngine.Data.Geometry; using Deltares.DamEngine.Data.Geotechnics; using Deltares.DamEngine.TestHelpers.Factories; using NUnit.Framework; using Soil = Deltares.DamEngine.Data.Geotechnics.Soil; namespace Deltares.DamEngine.Calculators.Tests.KernelWrappers.MacroStabilityCommon; [TestFixture] public class MacroStabilityCommonHelperTests { private readonly double cTolerance = 0.001; [Test] public void GivenSoilGeometryProbabilityWithSoilProfile1DAndSurfaceLineWhenCombiningThenSoilProfile2DIsCreated() { SoilGeometryProbability soilGeometryProbability = FactoryForSoilGeometryProbabilities.CreateSoilGeometryProbabilityWithSoilProfile1D(); SurfaceLine2 surfaceLine = FactoryForSurfaceLines.CreateSurfaceLineWithDikeAndDitch(); SoilProfile2D combinedProfile = MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(soilGeometryProbability, surfaceLine, new Soil()); GeometryData geometry = combinedProfile.Geometry; Assert.Multiple(() => { Assert.That(soilGeometryProbability.SoilProfileType, Is.EqualTo(SoilProfileType.ProfileType2D)); Assert.That(geometry.Surfaces, Has.Count.EqualTo(1)); Assert.That(geometry.Left, Is.EqualTo(0.0).Within(cTolerance)); Assert.That(geometry.Right, Is.EqualTo(75.0).Within(cTolerance)); Assert.That(geometry.Bottom, Is.EqualTo(-20.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.First().X, Is.EqualTo(0.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.First().Z, Is.EqualTo(0.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.Last().X, Is.EqualTo(75.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.Last().Z, Is.EqualTo(0.0).Within(cTolerance)); }); } [Test] public void GivenSoilGeometryProbabilityWithSoilProfile2DAndMatchingSurfaceLineWhenCombiningThenSoilProfile2DIsUnchanged() { SoilGeometryProbability soilGeometryProbability = CreateSoilGeometryProbabilityWithSoilProfile2DWithSurfaceLineDitchDike(); // Create the same surfaceline as is used for creating the SoilProfile2D in the SoilGeometryProbability SurfaceLine2 surfaceLine = FactoryForSurfaceLines.CreateSurfaceLineWithDikeAndDitch(); // Now combine the soilGeometryProbability with the same SurfaceLine2 which should result in the same SoilProfile2D MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(soilGeometryProbability, surfaceLine, new Soil()); GeometryData geometry = soilGeometryProbability.SoilProfile2D.Geometry; Assert.Multiple(() => { Assert.That(soilGeometryProbability.SoilProfileType, Is.EqualTo(SoilProfileType.ProfileType2D)); Assert.That(geometry.Surfaces, Has.Count.EqualTo(1)); Assert.That(geometry.Left, Is.EqualTo(0.0).Within(cTolerance)); Assert.That(geometry.Right, Is.EqualTo(75.0).Within(cTolerance)); Assert.That(geometry.Bottom, Is.EqualTo(-20.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.First().X, Is.EqualTo(0.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.First().Z, Is.EqualTo(0.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.Last().X, Is.EqualTo(75.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.Last().Z, Is.EqualTo(0.0).Within(cTolerance)); }); } [Test] public void GivenSoilGeometryProbabilityWithSoilProfile2DAndMatchingSurfaceLineShortOnLeftWhenCombiningThenSoilProfile2DIsCutOnTheLeft() { SoilGeometryProbability soilGeometryProbability = CreateSoilGeometryProbabilityWithSoilProfile2DWithSurfaceLineDitchDike(); // Create the same surfaceline as is used for creating the SoilProfile2D in the SoilGeometryProbability // But now the SurfaceLine2 is shorter on the left side (5.0 instead of 0.0) SurfaceLine2 surfaceLine = FactoryForSurfaceLines.CreateSurfaceLineWithDikeAndDitch(); surfaceLine.CharacteristicPoints.GetPoint2D(CharacteristicPointType.SurfaceLevelOutside).X = surfaceLine.CharacteristicPoints.GetPoint2D(CharacteristicPointType.SurfaceLevelOutside).X + 5.0; // Now combine the soilGeometryProbability with the same SurfaceLine2 which should result the SoilProfile2D // being cut on the left with 5.0 meters SoilProfile2D combinedProfile = MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(soilGeometryProbability, surfaceLine, new Soil()); GeometryData geometry = combinedProfile.Geometry; Assert.Multiple(() => { Assert.That(soilGeometryProbability.SoilProfileType, Is.EqualTo(SoilProfileType.ProfileType2D)); Assert.That(geometry.Surfaces, Has.Count.EqualTo(1)); Assert.That(geometry.Left, Is.EqualTo(5.0).Within(cTolerance)); Assert.That(geometry.Right, Is.EqualTo(75.0).Within(cTolerance)); Assert.That(geometry.Bottom, Is.EqualTo(-20.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.First().X, Is.EqualTo(5.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.First().Z, Is.EqualTo(0.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.Last().X, Is.EqualTo(75.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.Last().Z, Is.EqualTo(0.0).Within(cTolerance)); }); } [Test] public void GivenSoilGeometryProbabilityWithSoilProfile2DAndMatchingSurfaceLineLongerOnLeftWhenCombiningThenSoilProfile2DIsExtendedToTheLeft() { SoilGeometryProbability soilGeometryProbability = CreateSoilGeometryProbabilityWithSoilProfile2DWithSurfaceLineDitchDike(); // Create the same surfaceline as is used for creating the SoilProfile2D in the SoilGeometryProbability // But now the SurfaceLine2 is longer on the left side (-5.0 instead of 0.0) SurfaceLine2 surfaceLine = FactoryForSurfaceLines.CreateSurfaceLineWithDikeAndDitch(); surfaceLine.CharacteristicPoints.GetPoint2D(CharacteristicPointType.SurfaceLevelOutside).X = surfaceLine.CharacteristicPoints.GetPoint2D(CharacteristicPointType.SurfaceLevelOutside).X - 5.0; // Now combine the soilGeometryProbability with the same SurfaceLine2 which should result the SoilProfile2D // being extended to the left with 5.0 meters SoilProfile2D combinedProfile = MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(soilGeometryProbability, surfaceLine, new Soil()); GeometryData geometry = combinedProfile.Geometry; Assert.Multiple(() => { Assert.That(soilGeometryProbability.SoilProfileType, Is.EqualTo(SoilProfileType.ProfileType2D)); Assert.That(geometry.Surfaces, Has.Count.EqualTo(2)); Assert.That(geometry.Left, Is.EqualTo(-5.0).Within(cTolerance)); Assert.That(geometry.Right, Is.EqualTo(75.0).Within(cTolerance)); Assert.That(geometry.Bottom, Is.EqualTo(-20.0).Within(cTolerance)); }); } [Test] public void GivenSoilGeometryProbabilityWithSoilProfile2DAndMatchingSurfaceLineShortOnRightWhenCombiningThenSoilProfile2DIsCutOnTheRight() { SoilGeometryProbability soilGeometryProbability = CreateSoilGeometryProbabilityWithSoilProfile2DWithSurfaceLineDitchDike(); // Create the same surfaceline as is used for creating the SoilProfile2D in the SoilGeometryProbability // But now the SurfaceLine2 is shorter on the right side (70.0 instead of 75.0) SurfaceLine2 surfaceLine = FactoryForSurfaceLines.CreateSurfaceLineWithDikeAndDitch(); surfaceLine.CharacteristicPoints.GetPoint2D(CharacteristicPointType.SurfaceLevelInside).X = surfaceLine.CharacteristicPoints.GetPoint2D(CharacteristicPointType.SurfaceLevelInside).X - 5.0; // Now combine the soilGeometryProbability with the same SurfaceLine2 which should result the SoilProfile2D // being cut on the right with 5.0 meters SoilProfile2D combinedProfile = MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(soilGeometryProbability, surfaceLine, new Soil()); GeometryData geometry = combinedProfile.Geometry; Assert.Multiple(() => { Assert.That(soilGeometryProbability.SoilProfileType, Is.EqualTo(SoilProfileType.ProfileType2D)); Assert.That(geometry.Surfaces, Has.Count.EqualTo(1)); Assert.That(geometry.Left, Is.EqualTo(0.0).Within(cTolerance)); Assert.That(geometry.Right, Is.EqualTo(70.0).Within(cTolerance)); Assert.That(geometry.Bottom, Is.EqualTo(-20.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.First().X, Is.EqualTo(0.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.First().Z, Is.EqualTo(0.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.Last().X, Is.EqualTo(70.0).Within(cTolerance)); Assert.That(geometry.SurfaceLine.Points.Last().Z, Is.EqualTo(0.0).Within(cTolerance)); }); } [Test] public void GivenSoilGeometryProbabilityWithSoilProfile2DAndMatchingSurfaceLineLongerOnRightWhenCombiningThenSoilProfile2DIsExtendedToTheRight() { SoilGeometryProbability soilGeometryProbability = CreateSoilGeometryProbabilityWithSoilProfile2DWithSurfaceLineDitchDike(); // Create the same surfaceline as is used for creating the SoilProfile2D in the SoilGeometryProbability // But now the SurfaceLine2 is longer on the right side (80.0 instead of 75.0) SurfaceLine2 surfaceLine = FactoryForSurfaceLines.CreateSurfaceLineWithDikeAndDitch(); surfaceLine.CharacteristicPoints.GetPoint2D(CharacteristicPointType.SurfaceLevelInside).X = surfaceLine.CharacteristicPoints.GetPoint2D(CharacteristicPointType.SurfaceLevelInside).X + 5.0; // Now combine the soilGeometryProbability with the same SurfaceLine2 which should result the SoilProfile2D // being extended to the right with 5.0 meters SoilProfile2D combinedProfile = MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(soilGeometryProbability, surfaceLine, new Soil()); GeometryData geometry = combinedProfile.Geometry; Assert.Multiple(() => { Assert.That(soilGeometryProbability.SoilProfileType, Is.EqualTo(SoilProfileType.ProfileType2D)); Assert.That(geometry.Surfaces, Has.Count.EqualTo(2)); Assert.That(geometry.Left, Is.EqualTo(0.0).Within(cTolerance)); Assert.That(geometry.Right, Is.EqualTo(80.0).Within(cTolerance)); Assert.That(geometry.Bottom, Is.EqualTo(-20.0).Within(cTolerance)); }); } [Test] public void GivenSoilGeometryProbabilityWithSoilProfile2DAndSurfaceLineFullyAboveTopBoundarySoilProfile2DWhenCombiningThenLayerIsAddedOnTopOfSoilProfile2D() { SoilGeometryProbability soilGeometryProbability = CreateSoilGeometryProbabilityWithSoilProfile2DWithSurfaceLineDitchDike(); SurfaceLine2 surfaceLine = FactoryForSurfaceLines.CreateSurfaceLineAboveSurfaceLineDikeAndDitch(); // Now combine the soilGeometryProbability with the surfaceLine which should result that a layer is added on // top of the SoilProfile2D SoilProfile2D combinedProfile = MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(soilGeometryProbability, surfaceLine, new Soil()); GeometryData geometry = combinedProfile.Geometry; // For debugging purposes // GeometryExporter.ExportToFile(geometry, GeometryExporter.VisualizationFolder + "Geometry.txt"); // GeometryExporter.ExportWithSurfaceLineToJsonFile(GeometryExporter.VisualizationFolder + // GeometryExporter.ExportJasonFile, geometry, surfaceLine.Geometry); Assert.Multiple(() => { Assert.That(soilGeometryProbability.SoilProfileType, Is.EqualTo(SoilProfileType.ProfileType2D)); Assert.That(geometry.Surfaces, Has.Count.EqualTo(2)); Assert.That(geometry.Left, Is.EqualTo(0.0).Within(cTolerance)); Assert.That(geometry.Right, Is.EqualTo(75.0).Within(cTolerance)); Assert.That(geometry.Bottom, Is.EqualTo(-20.0).Within(cTolerance)); Assert.That(geometry.MaxGeometryPointsZ, Is.EqualTo(10.0).Within(cTolerance)); }); } [Test] public void GivenSoilGeometryProbabilityWithSoilProfile2DAndSurfaceLineFullyBelowTopBoundarySoilProfile2DWhenCombiningThenMaterialAboveIsRemoved() { SoilGeometryProbability soilGeometryProbability = CreateSoilGeometryProbabilityWithSoilProfile2DWithSurfaceLineDitchDike(); SurfaceLine2 surfaceLine = FactoryForSurfaceLines.CreateSurfaceLineBelowSurfaceLineDikeAndDitch(); // Now combine the soilGeometryProbability with the surfaceLine which should result that the top part of the // SoilProfile2D is removed (between top boundary of the SoilProfile2D and the surfaceLine) SoilProfile2D combinedProfile = MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(soilGeometryProbability, surfaceLine, new Soil()); GeometryData geometry = combinedProfile.Geometry; Assert.Multiple(() => { Assert.That(soilGeometryProbability.SoilProfileType, Is.EqualTo(SoilProfileType.ProfileType2D)); Assert.That(geometry.Surfaces, Has.Count.EqualTo(1)); Assert.That(geometry.Left, Is.EqualTo(0.0).Within(cTolerance)); Assert.That(geometry.Right, Is.EqualTo(75.0).Within(cTolerance)); Assert.That(geometry.Bottom, Is.EqualTo(-20.0).Within(cTolerance)); Assert.That(geometry.MaxGeometryPointsZ, Is.EqualTo(-5.0).Within(cTolerance)); }); } [Test] public void GivenSoilGeometryProbabilityWithSoilProfile2DAndSurfaceLineCuttingTopBoundarySoilProfile2DWhenCombiningThenCorrectSoilProfile2DIsCreated() { SoilGeometryProbability soilGeometryProbability = CreateSoilGeometryProbabilityWithSoilProfile2DWithSurfaceLineDitchDike(); SurfaceLine2 surfaceLine = FactoryForSurfaceLines.CreateSurfaceLineCuttingSurfaceLineDikeAndDitch(); // Now combine the soilGeometryProbability with the surfaceLine which should result that the top part of the // SoilProfile2D is removed (between top boundary of the SoilProfile2D and the surfaceLine) SoilProfile2D combinedProfile = MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(soilGeometryProbability, surfaceLine, new Soil()); GeometryData geometry = combinedProfile.Geometry; Assert.Multiple(() => { Assert.That(soilGeometryProbability.SoilProfileType, Is.EqualTo(SoilProfileType.ProfileType2D)); Assert.That(geometry.Surfaces, Has.Count.EqualTo(1)); Assert.That(geometry.Left, Is.EqualTo(0.0).Within(cTolerance)); Assert.That(geometry.Right, Is.EqualTo(75.0).Within(cTolerance)); Assert.That(geometry.Bottom, Is.EqualTo(-20.0).Within(cTolerance)); Assert.That(geometry.MaxGeometryPointsZ, Is.EqualTo(3.0).Within(cTolerance)); }); } [Test] public void GivenSoilGeometryProbabilityWithSoilProfile2DAndSurfaceLinePartlyBelowSoilProfile2DBottomWhenCombiningThenExceptionIsRaised() { SoilGeometryProbability soilGeometryProbability = CreateSoilGeometryProbabilityWithSoilProfile2DWithSurfaceLineDitchDike(); SurfaceLine2 surfaceLine = FactoryForSurfaceLines.CreateSurfaceLineDike(-22.0, "PartlyBelowBottom"); // Now combine the soilGeometryProbability with the surfaceLine which should in an exception, because the surfaceLine // is partly below the bottom of the SoilProfile2D Assert.Throws(() => { MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(soilGeometryProbability, surfaceLine, new Soil()); }); } [Test] public void GivenSoilGeometryProbabilityWithSoilProfile2DAndSurfaceLineNonAscendingWhenCombiningThenExceptionIsRaised() { SoilGeometryProbability soilGeometryProbability = CreateSoilGeometryProbabilityWithSoilProfile2DWithSurfaceLineDitchDike(); SurfaceLine2 surfaceLine = FactoryForSurfaceLines.CreateSurfaceLineDikeNonStrictAscending(); // Now combine the soilGeometryProbability with the surfaceLine which should in an exception, because the surfaceLine // is not strictly ascending Assert.Throws(() => { MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(soilGeometryProbability, surfaceLine, new Soil()); }); } [Test] public void GivenDamEngineDataModelWhenFillingUpliftVanCalculationGridThenCorrectUpliftVanCalculationGridIsCreated() { // Given DamEngine data (DamProjectData) DamProjectData expectedDamProjectData = FactoryForDamProjectData.CreateExampleDamProjectData(); expectedDamProjectData.DamProjectCalculationSpecification.DamCalculationSpecifications[0] .StabilityModelType = StabilityModelType.UpliftVan; Location expectedLocation = expectedDamProjectData.Dike.Locations[0]; SurfaceLine2 expectedSurfaceLine2D = expectedLocation.SurfaceLine; var damKernelInput = new DamKernelInput { SubSoilScenario = expectedLocation.Segment.SoilProfileProbabilities[0], Location = expectedLocation, DamFailureMechanismeCalculationSpecification = expectedDamProjectData.DamProjectCalculationSpecification.CurrentSpecification }; // Determine the proper values for grid and traffic load using the helper methods double xCoordinateLowestUpliftFactorPoint = (expectedSurfaceLine2D.CharacteristicPoints.GetPoint2D(CharacteristicPointType.DikeTopAtPolder).X + expectedSurfaceLine2D.CharacteristicPoints.GetPoint2D(CharacteristicPointType.DikeToeAtPolder).X) * 0.5; UpliftVanCalculationGrid expectedUpliftVanCalculationGrid = FillExpectedUpliftVanCalculationGrid(); UpliftVanCalculationGrid createdUpliftVanCalculationGrid = MacroStabilityCommonHelper.FillUpliftVanCalculationGrid(damKernelInput, xCoordinateLowestUpliftFactorPoint); CompareHelper.CompareUpliftVanCalculationGrid(expectedUpliftVanCalculationGrid, createdUpliftVanCalculationGrid, true); } private static UpliftVanCalculationGrid FillExpectedUpliftVanCalculationGrid() { UpliftVanCalculationGrid upliftVanCalculationGrid = new UpliftVanCalculationGrid { IsGridsAutomatic = false, LeftGridXCount = 6, LeftGridXLeft = 11, LeftGridXRight = 14.5, LeftGridZBottom = 5.2667, LeftGridZCount = 3, LeftGridZTop = 7.8666, RightGridXCount = 8, RightGridXLeft = 18, RightGridXRight = 24, RightGridZBottom = 1.2, RightGridZCount = 11, RightGridZTop = 2.6, TangentLineCount = 0, TangentLineZBottom = 0, TangentLineZTop = 0, TangentLinesCreationMethod = TangentLinesDefinition.OnBoundaryLines, TangentLineLevels = [] }; return upliftVanCalculationGrid; } private static SoilGeometryProbability CreateSoilGeometryProbabilityWithSoilProfile2DWithSurfaceLineDitchDike() { SoilGeometryProbability soilGeometryProbability = FactoryForSoilGeometryProbabilities.CreateSoilGeometryProbabilityWithSoilProfile1D(); SurfaceLine2 surfaceLine = FactoryForSurfaceLines.CreateSurfaceLineWithDikeAndDitch(); MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(soilGeometryProbability, surfaceLine, new Soil()); return soilGeometryProbability; } }