Index: DamEngine/trunk/src/Deltares.DamEngine.IntegrationTests/TestFiles/Operational/Profile2DTests/CalculateStabilityInside2DInput.xml =================================================================== diff -u -r5865 -r6063 --- DamEngine/trunk/src/Deltares.DamEngine.IntegrationTests/TestFiles/Operational/Profile2DTests/CalculateStabilityInside2DInput.xml (.../CalculateStabilityInside2DInput.xml) (revision 5865) +++ DamEngine/trunk/src/Deltares.DamEngine.IntegrationTests/TestFiles/Operational/Profile2DTests/CalculateStabilityInside2DInput.xml (.../CalculateStabilityInside2DInput.xml) (revision 6063) @@ -33,22 +33,6 @@ - - - - - - - - - - - - - - - - @@ -93,21 +77,21 @@ - - - - + + + + - - - - + + + + @@ -168,19 +152,17 @@ + - - - @@ -231,14 +213,15 @@ + + - @@ -257,6 +240,7 @@ + @@ -279,6 +263,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -304,67 +314,17 @@ + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -391,30 +351,54 @@ + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - + @@ -439,39 +423,25 @@ - + - - - - - - - - + - + - - - - - - - - + - + @@ -481,34 +451,20 @@ - + - + - + - - + + - - - - - - - - - - - - - - @@ -523,18 +479,18 @@ - + - - + + - + - - + + @@ -551,18 +507,18 @@ - + - - + + - + - - + + @@ -579,25 +535,25 @@ - + - + - - + + - - + + @@ -608,14 +564,10 @@ - - - - @@ -636,70 +588,58 @@ - + - + - + - - - - - - - + - + - + - + - - - - - - - + - + - + - + - + - + - + - + Index: DamEngine/trunk/src/Deltares.DamEngine.IntegrationTests/TestFiles/Operational/Profile2DTests/CalculateStabilityInside2DOutput.xml =================================================================== diff -u -r5870 -r6063 Binary files differ Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/SensorEnumerations.cs =================================================================== diff -u -r4540 -r6063 --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/SensorEnumerations.cs (.../SensorEnumerations.cs) (revision 4540) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/Sensors/SensorEnumerations.cs (.../SensorEnumerations.cs) (revision 6063) @@ -21,14 +21,14 @@ namespace Deltares.DamEngine.Data.General.Sensors; -public enum DataSourceTypeSensors //Sensors +public enum DataSourceTypeSensors { Ignore, LocationData, Sensor } -public enum SensorType //Sensors +public enum SensorType { PiezometricHead, WaterLevel, Index: DamEngine/trunk/src/Deltares.DamEngine.IntegrationTests/IntegrationTests/OperationalStabilityProfile2DTests.cs =================================================================== diff -u -r5911 -r6063 --- DamEngine/trunk/src/Deltares.DamEngine.IntegrationTests/IntegrationTests/OperationalStabilityProfile2DTests.cs (.../OperationalStabilityProfile2DTests.cs) (revision 5911) +++ DamEngine/trunk/src/Deltares.DamEngine.IntegrationTests/IntegrationTests/OperationalStabilityProfile2DTests.cs (.../OperationalStabilityProfile2DTests.cs) (revision 6063) @@ -21,55 +21,232 @@ using System.IO; using System.Linq; +using Deltares.DamEngine.Data.General.PlLines; +using Deltares.DamEngine.Interface; using Deltares.DamEngine.Io; +using Deltares.DamEngine.Io.XmlInput; using Deltares.DamEngine.Io.XmlOutput; using Deltares.DamEngine.TestHelpers; +using Deltares.MacroStability.Io; +using Deltares.MacroStability.Io.XmlInput; using KellermanSoftware.CompareNetObjects; using NUnit.Framework; +using CharacteristicPointType = Deltares.DamEngine.Data.Geotechnics.CharacteristicPointType; +using SurfaceLinePoint = Deltares.DamEngine.Io.XmlInput.SurfaceLinePoint; namespace Deltares.DamEngine.IntegrationTests.IntegrationTests; [TestFixture] public class OperationalStabilityProfile2DTests { + private const double tolerance = 0.0000001; + + private const uint sourceTypeLocationData = 1; + private const uint sourceTypeSensor = 2; + + private const string projectPath = "Profile2DTests"; + private const string testFilesLocation = @".\TestFiles\Operational\Profile2DTests\"; + private const string inputFilename = testFilesLocation + "CalculateStabilityInside2DInput.xml"; + private const string outputFilename = testFilesLocation + "CalculateStabilityInside2DOutput.xml"; + + // Characteristic points + private SurfaceLinePoint outsideLimit; + private SurfaceLinePoint dikeTopAtRiver; + private SurfaceLinePoint dikeTopAtPolder; + private SurfaceLinePoint shoulderInside; + private SurfaceLinePoint dikeToeAtPolder; + private SurfaceLinePoint insideLimit; + /// - /// The following test is based on the DamLive test Deltares.DamLive.Tests.StabilityInside2DTest + /// The following test is based on the DamLive test Deltares.DamLive.Tests.StabilityInside2DTest. + /// Two soil profiles (with and without in-between aquifer) are tested for 2 time steps, so 4 locations in total. + /// Two types of source data are tested: location data or sensor. + /// The expected Pl-lines are re-calculated using the input. /// - [Test, Category(Categories.WorkInProgress)] - public void GivenStabilityInsideProfile2DProject_WhenCalculating_ThenExpectedResultIsGenerated() + [Test] + [TestCase(false)] + [TestCase(true)] + public void GivenStabilityProjectWithTwoSoilProfiles2DAndTwoTimeSteps_WhenCalculating_ThenExpectedWaternetIsGeneratedPerProfileAndPerTimeStep(bool isUseLocationData) { - const string calcDir = "DAMLive.Calc"; - const string testFilesLocation = @".\TestFiles\Operational\Profile2DTests\"; - const string baseOutputName = "CalculateStabilityInside2D"; - const string inputFilename = baseOutputName + "Input.xml"; - const string outputFilename = baseOutputName + "Output.xml"; + // Setup + const string calcDir = $"DAMLive.Calc."; if (Directory.Exists(calcDir)) { Directory.Delete(calcDir, true); // delete previous results } - - const string expectedOutputFileName = testFilesLocation + outputFilename; - Output expectedOutput = DamXmlSerialization.LoadOutputFromXmlFile(expectedOutputFileName); - // Given - string inputString = File.ReadAllText(testFilesLocation + inputFilename); - inputString = XmlAdapter.ChangeValueInXml(inputString, "ProjectPath", baseOutputName); // Current directory will be used - inputString = XmlAdapter.ChangeValueInXml(inputString, "CalculationMap", calcDir); // Current directory will be used - - // When - Output actualOutput = GeneralHelper.RunAfterInputValidation(inputString, true, outputFilename); - - // Then - Assert.Multiple(() => + Input input = DamXmlSerialization.LoadInputFromXmlFile(inputFilename); + input.CalculationMap = calcDir; + input.ProjectPath = projectPath; + FillSensorData(input, isUseLocationData); + string inputString = DamXmlSerialization.SaveInputAsXmlString(input); + DamXmlSerialization.SaveInputAsXmlFile(inputFilename, input); + Assert.That(input.Locations, Has.Length.EqualTo(4)); + + // Run + Output output = GeneralHelper.RunAfterInputValidation(inputString, true, outputFilename); + Assert.That(output, Is.Not.Null); + + outsideLimit = input.SurfaceLines[0].Points.First(p => p.PointType == ConversionHelper.ConvertToInputPointType(CharacteristicPointType.SurfaceLevelOutside)); + dikeTopAtRiver = input.SurfaceLines[0].Points.First(p => p.PointType == ConversionHelper.ConvertToInputPointType(CharacteristicPointType.DikeTopAtRiver)); + dikeTopAtPolder = input.SurfaceLines[0].Points.First(p => p.PointType == ConversionHelper.ConvertToInputPointType(CharacteristicPointType.DikeTopAtPolder)); + dikeToeAtPolder = input.SurfaceLines[0].Points.First(p => p.PointType == ConversionHelper.ConvertToInputPointType(CharacteristicPointType.DikeToeAtPolder)); + shoulderInside = input.SurfaceLines[0].Points.First(p => p.PointType == ConversionHelper.ConvertToInputPointType(CharacteristicPointType.ShoulderBaseInside)); + insideLimit = input.SurfaceLines[0].Points.First(p => p.PointType == ConversionHelper.ConvertToInputPointType(CharacteristicPointType.SurfaceLevelInside)); + + // Check the created PL lines for the 4 locations + for (var indexLocation = 0; indexLocation < 4; indexLocation++) + { + int indexTimeStep = indexLocation is 0 or 1 ? 0 : 1; + WaternetType waternet = ReadWaternetFromCreatedSkxFile(calcDir, indexTimeStep, indexLocation); + CheckPhreaticLine(waternet, input, indexLocation, indexTimeStep, isUseLocationData); + CheckPl3AndPl4(waternet, input, indexLocation, indexTimeStep, isUseLocationData); + } + + // Check safety factors + Output expectedOutput = DamXmlSerialization.LoadOutputFromXmlFile(outputFilename); + Assert.That(expectedOutput, Is.Not.Null); + CompareOutput(expectedOutput, output); + } + + private static void FillSensorData(Input input, bool isUseLocationData) + { + for (var indexSensorLocation = 0; indexSensorLocation < 2; indexSensorLocation++) + { + input.SensorData.SensorLocations[indexSensorLocation].SourceTypePl1WaterLevelAtRiver = isUseLocationData ? sourceTypeLocationData : sourceTypeSensor; + input.SensorData.SensorLocations[indexSensorLocation].SourceTypePl1PlLineOffsetBelowDikeTopAtRiver = isUseLocationData ? sourceTypeLocationData : sourceTypeSensor; + input.SensorData.SensorLocations[indexSensorLocation].SourceTypePl1PlLineOffsetBelowDikeTopAtPolder = isUseLocationData ? sourceTypeLocationData : sourceTypeSensor; + input.SensorData.SensorLocations[indexSensorLocation].SourceTypePl1PlLineOffsetBelowShoulderBaseInside = isUseLocationData ? sourceTypeLocationData : sourceTypeSensor; + input.SensorData.SensorLocations[indexSensorLocation].SourceTypePl1PlLineOffsetBelowDikeToeAtPolder = isUseLocationData ? sourceTypeLocationData : sourceTypeSensor; + input.SensorData.SensorLocations[indexSensorLocation].SourceTypePl1WaterLevelAtPolder = isUseLocationData ? sourceTypeLocationData : sourceTypeSensor; + input.SensorData.SensorLocations[indexSensorLocation].SourceTypePl3 = isUseLocationData ? sourceTypeLocationData : sourceTypeSensor; + input.SensorData.SensorLocations[indexSensorLocation].SourceTypePl4 = isUseLocationData ? sourceTypeLocationData : sourceTypeSensor; + } + + for (var indexLocation = 0; indexLocation < 4; indexLocation++) + { + double difference = indexLocation is 0 or 2 ? 0 : 0.11; + input.Locations[indexLocation].DesignScenarios[0].RiverLevel = isUseLocationData ? -2.0 + difference : double.NaN; + input.Locations[indexLocation].DesignScenarios[0].PlLineOffsetBelowDikeTopAtRiver = isUseLocationData ? 0.6 + difference : double.NaN; + input.Locations[indexLocation].DesignScenarios[0].PlLineOffsetBelowDikeTopAtPolder = isUseLocationData ? 1.6 + difference : double.NaN; + input.Locations[indexLocation].DesignScenarios[0].PlLineOffsetBelowShoulderBaseInside = isUseLocationData ? 0.2 + difference : double.NaN; + input.Locations[indexLocation].DesignScenarios[0].PlLineOffsetBelowDikeToeAtPolder = isUseLocationData ? 0.3 + difference : double.NaN; + input.Locations[indexLocation].DesignScenarios[0].PolderLevel = isUseLocationData ? -4.5 + difference : double.NaN; + input.Locations[indexLocation].DesignScenarios[0].HeadPl3 = isUseLocationData ? -1.8 + difference : double.NaN; + input.Locations[indexLocation].DesignScenarios[0].HeadPl3Specified = isUseLocationData; + input.Locations[indexLocation].DesignScenarios[0].HeadPl4 = isUseLocationData ? -1.6 + difference : double.NaN; + input.Locations[indexLocation].DesignScenarios[0].HeadPl4Specified = isUseLocationData; + } + } + + private static WaternetType ReadWaternetFromCreatedSkxFile(string calcDir, int indexTimeStep, int indexLocation) + { + string kernelInputFilename = $"{projectPath}\\{calcDir}\\Stability\\Bishop\\Dik(dike)_Loc(LocationOnlyPL3)_Stp(" + + indexTimeStep + ")_Mdl(Bishop)_2016-03-02T03_" + (indexTimeStep + 1) + "0_00_Pro(SoilProfile_NoInbetweenAquifer_stix).skx"; + if (indexLocation is 1 or 3) + { + kernelInputFilename = $"{projectPath}\\{calcDir}\\Stability\\Bishop\\Dik(dike)_Loc(LocationPL3AndPL4)_Stp(" + + indexTimeStep + ")_Mdl(Bishop)_2016-03-02T03_" + (indexTimeStep + 1) + "0_00_Pro(SoilProfile_OneInbetweenAquifer_stix).skx"; + } + + FullInputModelType macrostabilityInput = MacroStabilityXmlSerialization.LoadInputFromXmlFile(kernelInputFilename); + return macrostabilityInput.StabilityModel.ConstructionStages[0].Waternet; + } + + private void CheckPhreaticLine(WaternetType waternet, Input input, int indexLocation, int indexTimeStep, bool isUseLocationData) + { + WaternetLineType phreaticLine = waternet.PhreaticLine.WaternetLine; + + string sensor = indexLocation is 0 or 2 ? "SensorLoc1" : "SensorLoc2"; + LocationDesignScenario locData = input.Locations[indexLocation].DesignScenarios[0]; + double riverLevelUsed = isUseLocationData ? locData.RiverLevel : FetchValue(input, sensor + "_PL1_River", indexTimeStep).Z; + // Note that the polder level is not the sensor value of "SensorLoc1_PL1_Polder" or the locData.PolderLevel, + // but it is the sensor value of the sensor "_PL1_Ditch" because it is situated within the ditch width. + double polderLevelUsed = FetchValue(input, sensor + "_PL1_Ditch", indexTimeStep).Z; + + // Expected phreatic line + var expectedPhreaticLine = new PlLine(); + expectedPhreaticLine.Points.Add(new PlLinePoint(outsideLimit.X, riverLevelUsed)); + // Point 2 is the intersection between dike and River level (X coord not calculated here) + expectedPhreaticLine.Points.Add(new PlLinePoint(double.NaN, riverLevelUsed)); + expectedPhreaticLine.Points.Add(FetchValue(input, sensor + "_PL1_DikeMiddleCrest", indexTimeStep)); + // Point 4 is the intersection of the polder level with the (X coord not calculated here). + expectedPhreaticLine.Points.Add(new PlLinePoint(double.NaN, polderLevelUsed)); + expectedPhreaticLine.Points.Add(new PlLinePoint(FetchValue(input, sensor + "_PL1_Ditch", indexTimeStep).X, polderLevelUsed)); + // Point 6 is the intersection between the ditch and the polder level (X coord not calculated here). + expectedPhreaticLine.Points.Add(new PlLinePoint(double.NaN, polderLevelUsed)); + expectedPhreaticLine.Points.Add(new PlLinePoint(insideLimit.X, polderLevelUsed)); + + if (isUseLocationData) + { + expectedPhreaticLine.Points.Insert(2, new PlLinePoint(dikeTopAtRiver.X, riverLevelUsed - locData.PlLineOffsetBelowDikeTopAtRiver)); + expectedPhreaticLine.Points.Insert(4, new PlLinePoint(dikeTopAtPolder.X, riverLevelUsed - locData.PlLineOffsetBelowDikeTopAtPolder)); + expectedPhreaticLine.Points.Insert(5, new PlLinePoint(shoulderInside.X, shoulderInside.Z - locData.PlLineOffsetBelowShoulderBaseInside)); + expectedPhreaticLine.Points.Insert(6, new PlLinePoint(dikeToeAtPolder.X, dikeToeAtPolder.Z - locData.PlLineOffsetBelowDikeToeAtPolder)); + } + + CheckPoints(phreaticLine, expectedPhreaticLine, "Incorrect point in location index " + indexLocation + " for phreatic line"); + } + + private void CheckPl3AndPl4(WaternetType waternet, Input input, int indexLocation, int indexTimeStep, bool isUseLocationData) + { + for (var indexHeadLine = 0; indexHeadLine < 2; indexHeadLine++) + { + // No PL 4 available for locations without in-between aquifer present + if (indexHeadLine == 1 && indexLocation is 0 or 2) { - Assert.That(actualOutput, Is.Not.Null); - Assert.That(expectedOutput, Is.Not.Null); + continue; } - ); - Assert.That(actualOutput.Results.CalculationMessages.Any(message => message.MessageType == MessageMessageType.Error), Is.False, "No errors should occur during the calculation"); - CompareOutput(expectedOutput, actualOutput); + + WaternetLineType actualPlLine = waternet.HeadLines[indexHeadLine].WaternetLine; + + // Expected PL-line + string location = indexLocation is 0 or 2 ? "SensorLoc1_" : "SensorLoc2_"; + string plType = indexHeadLine == 0 ? "PL3" : "PL4"; + string sensorPrefix = location + plType; + LocationDesignScenario locData = input.Locations[indexLocation].DesignScenarios[0]; + double headPl3Used = isUseLocationData ? (indexHeadLine == 0 ? locData.HeadPl3 : locData.HeadPl4) : FetchValue(input, sensorPrefix + "_River", indexTimeStep).Z; + + var expectedPlLine = new PlLine(); + expectedPlLine.Points.Add(new PlLinePoint(outsideLimit.X, headPl3Used)); + expectedPlLine.Points.Add(FetchValue(input, sensorPrefix + "_River", indexTimeStep)); + // Point 3 is the intersection between dike and River level (X coord not calculated here) + expectedPlLine.Points.Add(new PlLinePoint(double.NaN, headPl3Used)); + expectedPlLine.Points.Add(FetchValue(input, sensorPrefix + "_DikeMiddleCrest", indexTimeStep)); + expectedPlLine.Points.Add(FetchValue(input, sensorPrefix + "_Ditch", indexTimeStep)); + expectedPlLine.Points.Add(FetchValue(input, sensorPrefix + "_Polder", indexTimeStep)); + expectedPlLine.Points.Add(new PlLinePoint(insideLimit.X, FetchValue(input, sensorPrefix + "_Polder", indexTimeStep).Z)); + + CheckPoints(actualPlLine, expectedPlLine, "Incorrect point in location index " + indexLocation + " for " + plType); + } } + private static void CheckPoints(WaternetLineType actualPlLine, PlLine expectedPlLine, string message) + { + Assert.That(actualPlLine.Points, Has.Length.EqualTo(expectedPlLine.Points.Count)); + for (var i = 0; i < actualPlLine.Points.Length; i++) + { + if (!double.IsNaN(expectedPlLine.Points[i].X)) + { + Assert.That(actualPlLine.Points[i].X, Is.EqualTo(expectedPlLine.Points[i].X).Within(tolerance), message); + } + + Assert.That(actualPlLine.Points[i].Z, Is.EqualTo(expectedPlLine.Points[i].Z).Within(tolerance), message); + } + } + + private static PlLinePoint FetchValue(Input input, string sensorName, int indexTimeStep) + { + double z = input.OperationalInputTimeSeries.First(t => t.LocationId == sensorName).Entries.TimeSerieEntry[indexTimeStep].Value; + double x = input.SensorData.Sensors.First(t => t.Name == sensorName).RelativeLocation; + + return new PlLinePoint + { + X = x, + Z = z + }; + } + private static void CompareOutput(Output expected, Output actual) { var compare = new CompareLogic Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine1Creator.cs =================================================================== diff -u -r5658 -r6063 --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine1Creator.cs (.../SensorPlLine1Creator.cs) (revision 5658) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/PlLinesCreator/SensorPlLine1Creator.cs (.../SensorPlLine1Creator.cs) (revision 6063) @@ -346,7 +346,7 @@ lineConstructor.Insert(new PlLinePoint(xLocation, depth)); - // always continue horiontal to end + // always continue horizontal to end lineConstructor.Insert(new PlLinePoint(XEndBoundary, depth)); /*