// Copyright (C) Stichting Deltares 2024. All rights reserved. // // This file is part of the Dam Engine. // // The Dam Engine is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . // // All names, logos, and references to "Deltares" are registered trademarks of // Stichting Deltares and remain full property of Stichting Deltares at all times. // All rights reserved. using System; using System.Collections.Generic; using System.IO; using System.Xml.Serialization; using Deltares.DamEngine.Data.General; using Deltares.DamEngine.Data.General.Results; using Deltares.DamEngine.Data.General.TimeSeries; using Deltares.DamEngine.Data.Geometry; using Deltares.DamEngine.Data.Geotechnics; using Deltares.DamEngine.Data.Standard.Calculation; using Deltares.DamEngine.Data.Standard.Logging; using Deltares.DamEngine.Io; using Deltares.DamEngine.Io.XmlOutput; using KellermanSoftware.CompareNetObjects; using NUnit.Framework; using DesignResult = Deltares.DamEngine.Data.General.Results.DesignResult; using TimeSerie = Deltares.DamEngine.Data.General.TimeSeries.TimeSerie; using UpliftSituation = Deltares.DamEngine.Data.General.UpliftSituation; namespace Deltares.DamEngine.Interface.Tests; [TestFixture] public class FillXmlOutputFromDamTests { [Test] public void CanWriteAndReadDamProjectDataToXml() { const string outputFilename = "OutputFile.xml"; DamProjectData expectedDamProjectData = CreateExampleDamProjectData(); Output output = FillXmlOutputFromDam.CreateOutput(expectedDamProjectData); DamXmlSerialization.SaveOutputAsXmlFile(outputFilename, output); output = DamXmlSerialization.LoadOutputFromXmlFile(outputFilename); DamProjectData inputData = CreateExampleDamProjectData(); DamProjectData actualDamProjectData = FillDamFromXmlOutput.CreateDamProjectData(inputData, output); CompareDamProjectData(actualDamProjectData, expectedDamProjectData); } [Test] public void CanWriteAndReadDamProjectDataToXmlString() { DamProjectData expectedDamProjectData = CreateExampleDamProjectData(); Output output = FillXmlOutputFromDam.CreateOutput(expectedDamProjectData); string xmlString = DamXmlSerialization.SaveOutputAsXmlString(output); output = DamXmlSerialization.LoadOutputFromXmlString(xmlString); DamProjectData inputData = CreateExampleDamProjectData(); DamProjectData actualDamProjectData = FillDamFromXmlOutput.CreateDamProjectData(inputData, output); CompareDamProjectData(actualDamProjectData, expectedDamProjectData); } [Test] public void CreateOutput_DamProjectDataWithStabilityDesignResults_CreatesOutputWithStabilityResults() { // Setup var random = new Random(21); var project = new DamProjectData { CalculationMessages = new List(), DesignCalculations = new List { CreateStabilityDesignResults(random.Next()), CreateStabilityDesignResults(random.Next()), CreateStabilityDesignResults(random.Next()), CreateStabilityDesignResults(random.Next()) } }; // Call Output output = FillXmlOutputFromDam.CreateOutput(project); // Assert Io.XmlOutput.DesignResult[] calculationResults = output.Results.CalculationResults; int expectedNrOfCalculationResults = project.DesignCalculations.Count; Assert.That(calculationResults.Length, Is.EqualTo(expectedNrOfCalculationResults)); for (var i = 0; i < expectedNrOfCalculationResults; i++) { AssertStabilityDesignResults(project.DesignCalculations[i].StabilityDesignResults, calculationResults[i].StabilityDesignResults); } } private DamProjectData CreateExampleDamProjectData() { const int designResultsCount = 3; const int locationResultsCount = 2; var damProjectData = new DamProjectData { DesignCalculations = new List() }; damProjectData.DamProjectCalculationSpecification = new DamProjectCalculationSpecification(); damProjectData.DamProjectCalculationSpecification.DamCalculationSpecifications.Add(new DamFailureMechanismeCalculationSpecification()); damProjectData.DamProjectCalculationSpecification.CurrentSpecification.FailureMechanismSystemType = FailureMechanismSystemType.StabilityOutside; damProjectData.DamProjectCalculationSpecification.CurrentSpecification.StabilityModelType = MStabModelType.Bishop; for (var i = 0; i < designResultsCount; i++) { var result = new DesignResult("location " + i, "Scenario " + (i * 2)) { BaseFileName = "my basefilename " + i, CalculationSubDir = "CalcSubDir", ProfileName = "profile" + i }; result.CalculationResult = CalculationResult.RunFailed; // Note : as Wti2017BackwardErosionHcritical is in use in this test, the modeltype MUST be Wti2017. result.PipingDesignResults = new PipingDesignResults(PipingModelType.Wti2017) { ResultMessage = "no run made", UpliftFactor = 1.3 * i, HeaveFactor = 1.1 * i, BlighFactor = 1.03 * i, BlighHcritical = 0.4, LocalExitPointX = 34.21, Wti2017UpliftDeltaPhiC = 1.09 * i, EffectiveStress = 13.23 * i, Wti2017UpliftHcritical = 1.19 * i, Wti2017BackwardErosionHcritical = 1.29 * i, Wti2017BackwardErosionDeltaPhiC = 1.34 * i, Wti2017BackwardErosionDeltaPhiReduced = 1.27 * i, Wti2017HeaveHcritical = 1.24 * i, Wti2017BackwardErosionSafetyFactor = 1.39 * i, Wti2017UpliftSafetyFactor = 1.49 * i, Wti2017HeaveSafetyFactor = 1.59 * i, Wti2017Gradient = 1.69 * i, Wti2017HcriticalOverall = 1.79 * i, Wti2017SafetyFactorOverall = 3.21 * i, CCreep = 234.1 * i }; var situation = new UpliftSituation { IsUplift = true, Pl3MinUplift = 0.1, Pl3HeadAdjusted = 0.2, Pl3LocationXMinUplift = 0.3, Pl4MinUplift = 0.1 * i, Pl4HeadAdjusted = 0.2 * i, Pl4LocationXMinUplift = 0.3 * i }; var surfaceline = new SurfaceLine2(); surfaceline.Name = "Redesigned Surfaceline"; surfaceline.CharacteristicPoints.Geometry = surfaceline.Geometry; var p1 = new CharacteristicPoint { CharacteristicPointType = CharacteristicPointType.SurfaceLevelOutside, Point = new Point2D(0, 0) }; surfaceline.CharacteristicPoints.Add(p1); var p2 = new CharacteristicPoint { CharacteristicPointType = CharacteristicPointType.DikeToeAtRiver, Point = new Point2D(10, 0) }; surfaceline.CharacteristicPoints.Add(p2); var p3 = new CharacteristicPoint { CharacteristicPointType = CharacteristicPointType.DikeTopAtRiver, Point = new Point2D(15, 2) }; surfaceline.CharacteristicPoints.Add(p3); var p4 = new CharacteristicPoint { CharacteristicPointType = CharacteristicPointType.DikeTopAtPolder, Point = new Point2D(18, 2) }; surfaceline.CharacteristicPoints.Add(p4); var p5 = new CharacteristicPoint { CharacteristicPointType = CharacteristicPointType.DikeToeAtPolder, Point = new Point2D(23, 0) }; surfaceline.CharacteristicPoints.Add(p5); var p6 = new CharacteristicPoint { CharacteristicPointType = CharacteristicPointType.SurfaceLevelInside, Point = new Point2D(100, 0) }; surfaceline.CharacteristicPoints.Add(p6); result.PipingDesignResults.RedesignedSurfaceLine = surfaceline; result.PipingDesignResults.UpliftSituation = situation; var stabilityMStabModelTypes = new[] { MStabModelType.Bishop, MStabModelType.UpliftVan, MStabModelType.BishopUpliftVan }; result.StabilityDesignResults = new StabilityDesignResults { ResultMessage = "no problemo", SafetyFactor = (i + 1) * 0.66, NumberOfIterations = (i + 1) * 3, UpliftSituation = situation, RedesignedSurfaceLine = surfaceline, StabilityModelType = stabilityMStabModelTypes[i] }; CreateSlipCircleResults(result.StabilityDesignResults); damProjectData.DesignCalculations.Add(result); } damProjectData.Dike = new Dike(); for (var i = 0; i < locationResultsCount; i++) { var location = new Location(); location.Name = "A" + (i + 1); location.DistanceToEntryPoint = 0.2 * i + 0.56; location.Segment = new Segment(); var soilGeometryProbability = new SoilGeometryProbability(); soilGeometryProbability.SoilProfile1D = new SoilProfile1D(); soilGeometryProbability.SoilProfile1DName = soilGeometryProbability.SoilProfile1D.Name; location.Segment.SoilProfileProbabilities.Add(soilGeometryProbability); damProjectData.Dike.Locations.Add(location); } damProjectData.CalculationMessages = new List(); damProjectData.CalculationMessages.Add(new LogMessage(LogMessageType.Error, null, "Error 1")); damProjectData.CalculationMessages.Add(new LogMessage(LogMessageType.Error, null, "Error 2")); damProjectData.CalculationMessages.Add(new LogMessage(LogMessageType.Warning, null, "Warning 1")); FillOutputTimeSeries(damProjectData); return damProjectData; } private void CreateSlipCircleResults(StabilityDesignResults stabilityDesignResults) { switch (stabilityDesignResults.StabilityModelType) { case MStabModelType.Bishop: stabilityDesignResults.ActiveCenterPoint = new Point2D { X = 100, Z = -10 }; stabilityDesignResults.ActiveCenterPointRadius = 50; break; case MStabModelType.UpliftVan: case MStabModelType.BishopUpliftVan: stabilityDesignResults.ActiveCenterPoint = new Point2D { X = 100, Z = -10 }; stabilityDesignResults.ActiveCenterPointRadius = 50; stabilityDesignResults.PassiveCenterPoint = new Point2D { X = 101, Z = -11 }; stabilityDesignResults.PassiveCenterPointRadius = 51; break; } if (stabilityDesignResults.StabilityModelType is MStabModelType.Bishop or MStabModelType.UpliftVan or MStabModelType.BishopUpliftVan) { stabilityDesignResults.ResultSlices = new List(); for (var i = 0; i < 9; i++) { var slice = new StabilityResultSlice(); slice.TopLeftPoint = new Point2D(i + 1.3, i + 2.3); slice.TopRightPoint = new Point2D(i + 12.3, i + 22.3); slice.BottomLeftPoint = new Point2D(i - 1.3, i - 2.3); slice.BottomRightPoint = new Point2D(i - 12.3, i - 22.3); stabilityDesignResults.ResultSlices.Add(slice); } } } private void FillOutputTimeSeries(DamProjectData damProjectData) { const int timeSeriesCount = 2; const int timeEntriesCount = 3; const string idPipingBligh = "PipingFactorBligh"; const string idStabilityInsideFactor = "StabilityInsideFactor"; damProjectData.OutputTimeSerieCollection = new TimeSerieCollection(); for (var i = 0; i < timeSeriesCount; i++) { var locationId = $"location{i}"; TimeSerie timeSerie = damProjectData.OutputTimeSerieCollection.AddNewSeries(locationId); timeSerie.ParameterId = (i % 2 == 0) ? idPipingBligh : idStabilityInsideFactor; timeSerie.ForecastDateTime = DateTime.Now; timeSerie.Type = "instantaneous"; timeSerie.StartDateTime = new DateTime(2012, 12, 31); timeSerie.EndDateTime = new DateTime(2012, 12, 31, 1, 0, 0); timeSerie.MissVal = -9999.0; timeSerie.LongName = timeSerie.LocationId + "long"; timeSerie.StationName = $"station{i}"; timeSerie.Units = "m"; timeSerie.SourceOrganisation = $"organisation{i}"; timeSerie.SourceSystem = $"system{i}"; timeSerie.FileDescription = $"filedescription{i}"; timeSerie.Region = $"region{i}"; timeSerie.TimeStep.Multiplier = 3600; timeSerie.TimeStep.Unit = TimeStepUnit.Second; for (var j = 0; j < timeEntriesCount; j++) { timeSerie.Entries.Add(new TimeSerieEntry { DateTime = new DateTime(2012, 12, 31, 1, j * 10, 0), Value = 1 + j * 0.1, BishopCircleX = 2 + j * 0.2, BishopCircleZ = 3 + j * 0.3, BishopRadius = 4 + j * 0.4 // Flag = 1, // BasisFileName = $"BasisFileName{i}" }); } } } private static DesignResult CreateStabilityDesignResults(int seed) { var random = new Random(seed); var supportedModelTypeValues = new[] { MStabModelType.Bishop, MStabModelType.UpliftVan, MStabModelType.BishopUpliftVan }; var stabilityDesignResults = new StabilityDesignResults { ResultMessage = "ResultMessage", SafetyFactor = random.Next(0, 2) == 1 ? random.NextDouble() : null, NumberOfIterations = random.Next(0, 2) == 1 ? random.Next() : null, UpliftSituation = random.Next(0, 2) == 1 ? CreateUpliftSituation(seed) : null, StabilityModelType = random.Next(0, 2) == 1 ? supportedModelTypeValues[random.Next(supportedModelTypeValues.Length - 1)] : null, RedesignedSurfaceLine = random.Next(0, 2) == 1 ? CreateSurfaceLine(seed) : null, ActiveCenterPoint = CreatePoint2D(seed), ActiveCenterPointRadius = random.NextDouble(), PassiveCenterPoint = CreatePoint2D(seed), PassiveCenterPointRadius = random.NextDouble() }; return new DesignResult { StabilityDesignResults = stabilityDesignResults }; } private static UpliftSituation CreateUpliftSituation(int seed) { var random = new Random(seed); return new UpliftSituation { IsUplift = true, Pl3MinUplift = random.NextDouble(), Pl3HeadAdjusted = random.NextDouble(), Pl3LocationXMinUplift = random.NextDouble(), Pl4MinUplift = random.NextDouble(), Pl4HeadAdjusted = random.NextDouble(), Pl4LocationXMinUplift = random.NextDouble() }; } private static SurfaceLine2 CreateSurfaceLine(int seed) { var random = new Random(seed); var surfaceLine = new SurfaceLine2 { Name = "Redesigned Surfaceline" }; surfaceLine.CharacteristicPoints.Add(new CharacteristicPoint { CharacteristicPointType = CharacteristicPointType.SurfaceLevelOutside, Point = new Point2D(random.NextDouble(), random.NextDouble()) }); surfaceLine.CharacteristicPoints.Add(new CharacteristicPoint { CharacteristicPointType = CharacteristicPointType.DikeToeAtRiver, Point = new Point2D(random.NextDouble(), random.NextDouble()) }); surfaceLine.CharacteristicPoints.Add(new CharacteristicPoint { CharacteristicPointType = CharacteristicPointType.DikeTopAtRiver, Point = new Point2D(random.NextDouble(), random.NextDouble()) }); surfaceLine.CharacteristicPoints.Add(new CharacteristicPoint { CharacteristicPointType = CharacteristicPointType.DikeTopAtPolder, Point = new Point2D(random.NextDouble(), random.NextDouble()), }); surfaceLine.CharacteristicPoints.Add(new CharacteristicPoint { CharacteristicPointType = CharacteristicPointType.DikeToeAtPolder, Point = new Point2D(random.NextDouble(), random.NextDouble()) }); surfaceLine.CharacteristicPoints.Add(new CharacteristicPoint { CharacteristicPointType = CharacteristicPointType.SurfaceLevelInside, Point = new Point2D(random.NextDouble(), random.NextDouble()) }); return surfaceLine; } private static Point2D CreatePoint2D(int seed) { var random = new Random(seed); return new Point2D { X = random.NextDouble(), Z = random.NextDouble() }; } private static DesignResultStabilityDesignResultsStabilityModelType? Convert(MStabModelType? stabilityModelType) { if (stabilityModelType.HasValue) { return ConversionHelper.ConvertToOutputStabilityModelType(stabilityModelType.Value); } throw new NotSupportedException(); } private static void AssertStabilityDesignResults(StabilityDesignResults expected, DesignResultStabilityDesignResults actual) { Assert.That(actual.ResultMessage, Is.EqualTo(expected.ResultMessage)); AssertUpliftSituation(expected.UpliftSituation, actual.UpliftSituation); bool safetyFactorHasValue = expected.SafetyFactor.HasValue; Assert.That(actual.SafetyFactorSpecified, Is.EqualTo(safetyFactorHasValue)); Assert.That(actual.SafetyFactor, Is.EqualTo(safetyFactorHasValue ? expected.SafetyFactor : 0)); bool numberOfIterationsHasValue = expected.NumberOfIterations.HasValue; Assert.That(actual.NumberOfIterationsSpecified, Is.EqualTo(numberOfIterationsHasValue)); Assert.That(actual.NumberOfIterations, Is.EqualTo(numberOfIterationsHasValue ? expected.NumberOfIterations : 0)); bool mStabModelTypeHasValue = expected.StabilityModelType.HasValue; Assert.That(actual.StabilityModelTypeSpecified, Is.EqualTo(mStabModelTypeHasValue)); DesignResultStabilityDesignResultsStabilityModelType? expectedModelType = mStabModelTypeHasValue ? Convert(expected.StabilityModelType) : DesignResultStabilityDesignResultsStabilityModelType.Bishop; Assert.That(actual.StabilityModelType, Is.EqualTo(expectedModelType)); AssertSurfaceLine(expected.RedesignedSurfaceLine, actual.RedesignedSurfaceLine); } private static void AssertUpliftSituation(UpliftSituation? expected, Io.XmlOutput.UpliftSituation actual) { if (!expected.HasValue) { Assert.That(actual, Is.Null); return; } Assert.That(actual.IsUplift, Is.EqualTo(expected.Value.IsUplift)); Assert.That(actual.Pl3MinUplift, Is.EqualTo(expected.Value.Pl3MinUplift)); Assert.That(actual.Pl3HeadAdjusted, Is.EqualTo(expected.Value.Pl3HeadAdjusted)); Assert.That(actual.Pl3LocationXMinUplift, Is.EqualTo(expected.Value.Pl3LocationXMinUplift)); Assert.That(actual.Pl4MinUplift, Is.EqualTo(expected.Value.Pl4MinUplift)); Assert.That(actual.Pl4HeadAdjusted, Is.EqualTo(expected.Value.Pl4HeadAdjusted)); Assert.That(actual.Pl4LocationXMinUplift, Is.EqualTo(expected.Value.Pl4LocationXMinUplift)); } private static void AssertSurfaceLine(SurfaceLine2 expected, SurfaceLine actual) { if (expected == null) { Assert.That(actual, Is.Null); return; } Assert.That(actual.Name, Is.EqualTo(expected.Name)); int expectedNrOfCharacteristicPoints = expected.CharacteristicPoints.Count; Assert.That(actual.Points.Length, Is.EqualTo(expectedNrOfCharacteristicPoints)); for (var i = 0; i < expectedNrOfCharacteristicPoints; i++) { CharacteristicPoint expectedCharacteristicPoint = expected.CharacteristicPoints[i]; SurfaceLinePoint actualCharacteristicPoint = actual.Points[i]; AssertCharacteristicPoint(expectedCharacteristicPoint, actualCharacteristicPoint); } } private static void AssertCharacteristicPoint(CharacteristicPoint expected, SurfaceLinePoint actual) { Assert.That(actual.X, Is.EqualTo(expected.Point.X)); Assert.That(actual.Z, Is.EqualTo(expected.Point.Z)); Assert.That(actual.PointType, Is.EqualTo(ConversionHelper.ConvertToInputPointType(expected.CharacteristicPointType))); } private void CompareDamProjectData(DamProjectData actual, DamProjectData expected) { var compare = new CompareLogic { Config = { MaxDifferences = 100 } }; ComparisonResult result = compare.Compare(expected, actual); var fakeDiffCount = 0; foreach (Difference resultDifference in result.Differences) { if (resultDifference.PropertyName.Contains("ForecastDateTime")) { fakeDiffCount++; } } Assert.That(result.Differences.Count, Is.EqualTo(fakeDiffCount), "Differences found read/write Input object"); } }