// 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.Collections.Generic; using System.ComponentModel; using System.Data; using System.IO; using System.Linq; using System.Text.RegularExpressions; using Deltares.DamEngine.Calculators.KernelWrappers.Common; using Deltares.DamEngine.Calculators.KernelWrappers.Interfaces; using Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityCommon.MacroStabilityIo; using Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityInwards; using Deltares.DamEngine.Calculators.Properties; using Deltares.DamEngine.Data.Design; using Deltares.DamEngine.Data.General; using Deltares.DamEngine.Data.General.PlLines; using Deltares.DamEngine.Data.General.Results; using Deltares.DamEngine.Data.Geometry; using Deltares.DamEngine.Data.Geotechnics; using Deltares.DamEngine.Data.Standard.Calculation; using Deltares.DamEngine.Data.Standard.Logging; using Deltares.MacroStability.Data; using Deltares.MacroStability.Interface; using Deltares.MacroStability.Io; using Deltares.MacroStability.Io.XmlOutput; using Deltares.MacroStability.Io.XmlValidationOutput; using CharacteristicPointType = Deltares.DamEngine.Data.Geotechnics.CharacteristicPointType; using ConversionHelper = Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityCommon.MacroStabilityIo.ConversionHelper; using MacroStabilityOutput = Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityInwards.MacroStabilityOutput; using MessagesTypeMessage = Deltares.MacroStability.Io.XmlValidationOutput.MessagesTypeMessage; using MessageType = Deltares.MacroStability.Io.XmlValidationOutput.MessageType; using Point2D = Deltares.DamEngine.Data.Geometry.Point2D; using Soil = Deltares.DamEngine.Data.Geotechnics.Soil; using SoilProfileType = Deltares.DamEngine.Data.General.SoilProfileType; namespace Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityCommon; /// /// Class with helper methods for the Macro Stability Wrappers. /// public static class MacroStabilityCommonHelper { /// /// Combines the surface line with the SoilProfile1D or the SoilProfile2D. /// /// The sub soil scenario. /// The surface line. /// The dike embankment soil. /// /// The combined profile as full SoilProfile2D public static SoilProfile2D CombineSoilProfileWithSurfaceLine(SoilGeometryProbability subSoilScenario, SurfaceLine2 surfaceLine2, Soil dikeEmbankmentSoil) { switch (subSoilScenario.SoilProfileType) { case SoilProfileType.ProfileType1D: CombineSoilProfile1DWithSurfaceLine(subSoilScenario, surfaceLine2, dikeEmbankmentSoil); break; case SoilProfileType.ProfileType2D: CombineSoilProfile2DWithSurfaceLine(subSoilScenario, surfaceLine2, dikeEmbankmentSoil); break; default: throw new NotImplementedException(); } return subSoilScenario.SoilProfile2D; } /// /// Fills the traffic load. /// /// The dam kernel input. /// Filled traffic load public static TrafficLoad FillTrafficLoad(DamKernelInput damKernelInput) { TrafficLoad trafficLoad = null; if (damKernelInput.Location.StabilityOptions != null && damKernelInput.Location.StabilityOptions.TrafficLoad.HasValue && !(Math.Abs(damKernelInput.Location.StabilityOptions.TrafficLoad.Value) < 1e-6)) { trafficLoad = new TrafficLoad { Pressure = damKernelInput.Location.StabilityOptions.TrafficLoad.Value, XEnd = damKernelInput.Location.SurfaceLine .CharacteristicPoints.GetPoint2D(CharacteristicPointType.TrafficLoadInside).X, XStart = damKernelInput.Location.SurfaceLine .CharacteristicPoints.GetPoint2D(CharacteristicPointType.TrafficLoadOutside).X }; } return trafficLoad; } /// /// Fills the bishop calculation grid and tangent lines. /// /// The dam kernel input. /// Filled grid and tangent lines public static BishopCalculationGrid FillBishopCalculationGrid(DamKernelInput damKernelInput) { SlipCircleDefinition slipCircleDefinition = damKernelInput.DamFailureMechanismeCalculationSpecification .FailureMechanismParametersMStab.MStabParameters.SlipCircleDefinition; StabilityGridPosition gridPosition = damKernelInput.DamFailureMechanismeCalculationSpecification .FailureMechanismParametersMStab.MStabParameters.GridPosition; BishopCalculationGrid bishopCalculationGrid = BishopGridCreator.DetermineGridsFromSettings(gridPosition, slipCircleDefinition, damKernelInput.Location.SurfaceLine); double centerOfLeftGridXCoordinate = (bishopCalculationGrid.GridXLeft + bishopCalculationGrid.GridXRight) * 0.5; if (bishopCalculationGrid.IsSearchAreaAutomatic) { if (damKernelInput.DamFailureMechanismeCalculationSpecification.FailureMechanismSystemType == FailureMechanismSystemType.StabilityInside) { centerOfLeftGridXCoordinate = damKernelInput.Location.SurfaceLine.CharacteristicPoints.GetPoint2D( CharacteristicPointType.DikeToeAtPolder).X; } else { centerOfLeftGridXCoordinate = damKernelInput.Location.SurfaceLine.CharacteristicPoints.GetPoint2D( CharacteristicPointType.DikeToeAtRiver).X; } } SoilProfile1D soilProfile1DAtCenterOfLeftGridXCoordinate = damKernelInput.SubSoilScenario.DetermineSoilProfile1DAtX(centerOfLeftGridXCoordinate, damKernelInput.Location.SurfaceLine, damKernelInput.Location.GetDikeEmbankmentSoil()); BishopGridCreator.DetermineTangentLines(bishopCalculationGrid, slipCircleDefinition, soilProfile1DAtCenterOfLeftGridXCoordinate); return bishopCalculationGrid; } /// /// Creates the actual calculation grids and tangent lines for the uplift model specific for the calculation /// /// /// /// Filled UpliftVan calculation grids (left and right) and tangent lines public static UpliftVanCalculationGrid FillUpliftVanCalculationGrid(DamKernelInput damKernelInput, double xCoordinateLastUpliftPoint) { SlipCircleDefinition slipCircleDefinition = damKernelInput.DamFailureMechanismeCalculationSpecification .FailureMechanismParametersMStab.MStabParameters.SlipCircleDefinition; slipCircleDefinition.XCoordinateLowestUpliftFactorPoint = xCoordinateLastUpliftPoint; UpliftVanCalculationGrid upliftVanCalculationGrid = UpliftVanGridCreator.DetermineGridsFromSettings( slipCircleDefinition, damKernelInput.Location.SurfaceLine); double centerOfLeftGridXCoordinate = (upliftVanCalculationGrid.LeftGridXLeft + upliftVanCalculationGrid.LeftGridXRight) * 0.5; SoilProfile1D soilProfile1DAtCenterOfLeftGridXCoordinate = damKernelInput.SubSoilScenario.DetermineSoilProfile1DAtX(centerOfLeftGridXCoordinate, damKernelInput.Location.SurfaceLine, damKernelInput.Location.GetDikeEmbankmentSoil()); UpliftVanGridCreator.DetermineTangentLines(upliftVanCalculationGrid, slipCircleDefinition, soilProfile1DAtCenterOfLeftGridXCoordinate); return upliftVanCalculationGrid; } /// /// Gets the name of the stability input file. /// /// The dam kernel input. /// Index of the iteration. /// The model. /// public static string GetStabilityInputFileName(DamKernelInput damKernelInput, int iterationIndex, StabilityModelType model) { // Assume 2D sti-file, then check on type being 1D string soilGeometryName = damKernelInput.SubSoilScenario.SoilProfile2DName; string calculationName = DetermineCalculationFilename(damKernelInput.FilenamePrefix, soilGeometryName, iterationIndex); const string filenameExtension = ".skx"; string fileName = calculationName + filenameExtension; string stabilityDirectory = GetStabilityCalculationDirectory(model, damKernelInput.CalculationDir); return Path.Combine(stabilityDirectory, fileName); } /// /// Throws the when macro stability kernel input is not assigned. /// /// The dam macro stability input. /// public static void ThrowWhenMacroStabilityKernelInputNull(MacroStabilityKernelDataInput macroStabilityKernelDataInput) { if (macroStabilityKernelDataInput == null) { throw new NoNullAllowedException(Resources.MacroStabilityKernelWrapper_NoMacroStabilityInputObjectDefined); } } /// /// Throws the when macro stability kernel output is not assigned. /// /// The dam macro stability output. /// public static void ThrowWhenMacroStabilityKernelOutputNull(MacroStabilityOutput macroStabilityOutput) { if (macroStabilityOutput == null) { throw new NoNullAllowedException(Resources.MacroStabilityKernelWrapper_NoMacroStabilityOutputObjectDefined); } } /// /// Throws the when macro stability dam kernel input is not assigned. /// /// The dam kernel input. /// public static void ThrowWhenMacroStabilityDamKernelInputNull(DamKernelInput damKernelInput) { if (damKernelInput == null) { throw new NoNullAllowedException(Resources.MacroStabilityKernelWrapper_NoDamInputObjectDefinedForMacroStability); } } /// /// Creates new designresult. /// /// The dam kernel input. /// The design scenario. /// public static DesignResult NewDesignResult(DamKernelInput damKernelInput, DesignScenario designScenario) { var soilProfile2DName = damKernelInput.SubSoilScenario.ToString(); var designResult = new DesignResult(damKernelInput.DamFailureMechanismeCalculationSpecification, designScenario, damKernelInput.SubSoilScenario.SoilProfile1D, soilProfile2DName) { // Initialize to the result of the designScenario CalculationResult = designScenario != null ? designScenario.CalculationResult : CalculationResult.NoRun, StabilityDesignResults = new StabilityDesignResults() }; var stabilityDesignResults = new StabilityDesignResults { RedesignedSurfaceLine = damKernelInput.Location.SurfaceLine }; designResult.ProfileName = soilProfile2DName; designResult.StabilityDesignResults = stabilityDesignResults; designResult.CalculationSubDir = damKernelInput.CalculationDir; return designResult; } /// /// Fills the design result. /// /// The macro stability output item. /// The design result. /// The last iteration index, to determine the total number of iterations public static void FillDesignResult(MacroStabilityOutputItem macroStabilityOutputItem, DesignResult designResult, int lastIterationIndex = 0) { designResult.BaseFileName = Path.GetFileNameWithoutExtension(macroStabilityOutputItem.ProjectName); designResult.CalculationSubDir = macroStabilityOutputItem.CalculationPath; designResult.StabilityDesignResults.StabilityModelType = macroStabilityOutputItem.StabilityModelType; if (macroStabilityOutputItem.CalculationResult == CalculationResult.Succeeded) { designResult.StabilityDesignResults.SafetyFactor = macroStabilityOutputItem.SafetyFactor; designResult.StabilityDesignResults.NumberOfIterations = lastIterationIndex; designResult.StabilityDesignResults.ActiveCenterPoint = macroStabilityOutputItem.ActiveCenterPoint; designResult.StabilityDesignResults.ActiveCenterPointRadius = macroStabilityOutputItem.ActiveCenterPointRadius; designResult.StabilityDesignResults.ResultSlices = macroStabilityOutputItem.ResultSlices; if (macroStabilityOutputItem.StabilityModelType == StabilityModelType.UpliftVan || macroStabilityOutputItem.StabilityModelType == StabilityModelType.BishopUpliftVan) { designResult.StabilityDesignResults.PassiveCenterPoint = macroStabilityOutputItem.PassiveCenterPoint; designResult.StabilityDesignResults.PassiveCenterPointRadius = macroStabilityOutputItem.PassiveCenterPointRadius; } } if (designResult.CalculationResult == CalculationResult.Succeeded) { designResult.CalculationResult = macroStabilityOutputItem.CalculationResult; } } /// /// Prepares the kernel. /// /// The calculator. /// Name of the file. /// public static PrepareResult PrepareKernel(MacroStabilityInterface calculator, string fileName) { try { // For now a simple check to see if any data has been past at all. var inputAsXml = calculator.KernelModel.ToString(); if (inputAsXml is { Length: > 10 }) { return PrepareResult.Successful; } return PrepareResult.Failed; } catch { return PrepareResult.Failed; } } /// /// Validates the specified kernel data input. /// /// The kernel data input. /// The kernel data output. /// The return messages. /// /// Zero when there are no errors, one when there are errors that prevent a calculation /// public static int Validate(IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, out List messages) { var macroStabilityKernelDataInput = (MacroStabilityKernelDataInput) kernelDataInput; messages = []; if (macroStabilityKernelDataInput.Input.StabilityModel == null || macroStabilityKernelDataInput.Input.PreprocessingInput == null) { var message = new LogMessage { Message = "De invoer voor de berekening met de stabiliteits kernel is incompleet.", MessageType = LogMessageType.Error }; messages.Add(message); if (kernelDataOutput != null) { ((MacroStabilityOutput) kernelDataOutput).CalculationResult = CalculationResult.InvalidInputData; } return 1; } var macroStabilityInterface = new MacroStabilityInterface(macroStabilityKernelDataInput.Input); try { bool result = macroStabilityInterface.Validate(out ValidationOutputType validationOutput); if (result) { return 0; } if (kernelDataOutput != null) { ((MacroStabilityOutput) kernelDataOutput).CalculationResult = CalculationResult.InvalidInputData; } foreach (MessagesTypeMessage resultMessage in validationOutput.Messages) { var message = new LogMessage { Message = resultMessage.Message }; switch (resultMessage.MessageType) { case MessageType.Error: { message.MessageType = LogMessageType.Error; break; } case MessageType.Info: { message.MessageType = LogMessageType.Info; break; } case MessageType.Warning: { message.MessageType = LogMessageType.Warning; break; } } messages.Add(message); } return 1; } catch (Exception e) { var message = new LogMessage { MessageType = LogMessageType.FatalError, Message = e.Message }; messages.Add(message); if (kernelDataOutput != null) { ((MacroStabilityOutput) kernelDataOutput).CalculationResult = CalculationResult.InvalidInputData; } return 1; } } /// /// Performs the stability calculation. /// /// /// The macro stability output. /// Name of the file. /// The calculator. /// The error messages. public static void PerformStabilityCalculation(MacroStabilityKernelDataInput macroStabilityKernelDataInput, MacroStabilityOutput macroStabilityOutput, string fileName, MacroStabilityInterface calculator, out List errorMessages) { macroStabilityOutput.CalculationResult = CalculationResult.NoRun; macroStabilityOutput.StabilityOutputItems ??= []; errorMessages = new List(); try { FullOutputModelType output; Result result = calculator.Calculate(out output); string outputFileName = fileName.Replace(".skx", ".out.xml"); MacroStabilityXmlSerialization.SaveOutputAsXmlFile(outputFileName, output); FillEngineFromMacroStabilityKernelOutput.FillDamProjectDataFromKernelModel(output, macroStabilityOutput, out List allMessages); CreateCalculationGridInOutputItem(calculator.KernelModel.StabilityModel, macroStabilityOutput.StabilityOutputItems[^1]); macroStabilityOutput.CalculationResult = ConversionHelper.ConvertToDamResultType(result); macroStabilityOutput.StabilityOutputItems[^1].CalculationPath = Path.GetDirectoryName(fileName); macroStabilityOutput.StabilityOutputItems[^1].ProjectName = Path.GetFileName(fileName); errorMessages = allMessages.Where(logMessage => logMessage.MessageType == LogMessageType.Error).ToList(); } catch (Exception exceptionDuringCalculation) { macroStabilityOutput.CalculationResult = CalculationResult.UnexpectedError; errorMessages.Add(new LogMessage(LogMessageType.Error, null, exceptionDuringCalculation.Message)); } if (macroStabilityOutput.CalculationResult != CalculationResult.UnexpectedError) { StixFileWriterConnector.WriteStixFileWithInput(macroStabilityKernelDataInput.DamKernelInput, macroStabilityKernelDataInput.MStabParameters, macroStabilityKernelDataInput.WaterNet, macroStabilityKernelDataInput.XCoordinateLowestUpliftFactorPoint, macroStabilityOutput, fileName, new List()); StixFileWriterConnector.WriteStixFileWithInputAndResults(macroStabilityKernelDataInput.DamKernelInput, macroStabilityKernelDataInput.MStabParameters, macroStabilityKernelDataInput.WaterNet, macroStabilityKernelDataInput.XCoordinateLowestUpliftFactorPoint, macroStabilityOutput, fileName, errorMessages); } } /// /// Create the waternet using the 1D soil profile if available, otherwise the 2D soil profile. /// /// /// /// The waternet. public static Waternet CreateWaternet(DamKernelInput damKernelInput, PlLines plLines) { double penetrationLength = damKernelInput.Location.ModelParametersForPlLines.PenetrationLength; IntrusionVerticalWaterPressureType? pressureType = damKernelInput.Location.IntrusionVerticalWaterPressure; ThrowWhenSoilProfile2DIsNull(damKernelInput.SubSoilScenario); SurfaceLine2 surfaceLine = damKernelInput.Location.SurfaceLine; if (damKernelInput.SubSoilScenario.SoilProfile1D != null) { return PlLinesToWaternetConverter.CreateWaternetBasedOnPlLines(plLines, damKernelInput.SubSoilScenario.SoilProfile1D, surfaceLine, penetrationLength, pressureType); } SoilProfile2D soilProfile2D = damKernelInput.SubSoilScenario.SoilProfile2D; return PlLinesToWaternetConverter.CreateWaternetBasedOnPlLines(plLines, soilProfile2D, surfaceLine, penetrationLength, pressureType); } private static void CreateCalculationGridInOutputItem(StabilityModel kernelModelStabilityModel, MacroStabilityOutputItem stabilityOutputItem) { switch (kernelModelStabilityModel.ModelOption) { case ModelOptions.Bishop: CreateCalculationGridBishop(kernelModelStabilityModel, stabilityOutputItem); break; case ModelOptions.UpliftVan: CreateCalculationGridUpliftVan(kernelModelStabilityModel, stabilityOutputItem); break; default: throw new InvalidEnumArgumentException(nameof(kernelModelStabilityModel.ModelOption), (int) kernelModelStabilityModel.ModelOption, typeof(ModelOptions)); } } private static void CreateCalculationGridBishop(StabilityModel kernelModelStabilityModel, MacroStabilityOutputItem stabilityOutputItem) { stabilityOutputItem.InputBishopCalculationGrid = new BishopCalculationGrid { GridXLeft = kernelModelStabilityModel.SlipCircle.SlipCircleGrid.GridXLeft, GridXRight = kernelModelStabilityModel.SlipCircle.SlipCircleGrid.GridXRight, GridZTop = kernelModelStabilityModel.SlipCircle.SlipCircleGrid.GridZTop, GridZBottom = kernelModelStabilityModel.SlipCircle.SlipCircleGrid.GridZBottom, GridXCount = kernelModelStabilityModel.SlipCircle.SlipCircleGrid.GridXNumber, GridZCount = kernelModelStabilityModel.SlipCircle.SlipCircleGrid.GridZNumber, TangentLineLevels = [] }; foreach (TangentLine tangentLine in kernelModelStabilityModel.SlipCircle.SlipCircleTangentLine.BoundaryHeights) { stabilityOutputItem.InputBishopCalculationGrid.TangentLineLevels.Add(tangentLine.Height); } } private static void CreateCalculationGridUpliftVan(StabilityModel kernelModelStabilityModel, MacroStabilityOutputItem stabilityOutputItem) { stabilityOutputItem.InputUpliftVanCalculationGrid = new UpliftVanCalculationGrid { LeftGridXLeft = kernelModelStabilityModel.SlipPlaneUpliftVan.SlipPlaneLeftGrid.GridXLeft, LeftGridXRight = kernelModelStabilityModel.SlipPlaneUpliftVan.SlipPlaneLeftGrid.GridXRight, LeftGridZTop = kernelModelStabilityModel.SlipPlaneUpliftVan.SlipPlaneLeftGrid.GridZTop, LeftGridZBottom = kernelModelStabilityModel.SlipPlaneUpliftVan.SlipPlaneLeftGrid.GridZBottom, LeftGridXCount = kernelModelStabilityModel.SlipPlaneUpliftVan.SlipPlaneLeftGrid.GridXNumber, LeftGridZCount = kernelModelStabilityModel.SlipPlaneUpliftVan.SlipPlaneLeftGrid.GridZNumber, RightGridXLeft = kernelModelStabilityModel.SlipPlaneUpliftVan.SlipPlaneRightGrid.GridXLeft, RightGridXRight = kernelModelStabilityModel.SlipPlaneUpliftVan.SlipPlaneRightGrid.GridXRight, RightGridZTop = kernelModelStabilityModel.SlipPlaneUpliftVan.SlipPlaneRightGrid.GridZTop, RightGridZBottom = kernelModelStabilityModel.SlipPlaneUpliftVan.SlipPlaneRightGrid.GridZBottom, RightGridXCount = kernelModelStabilityModel.SlipPlaneUpliftVan.SlipPlaneRightGrid.GridXNumber, RightGridZCount = kernelModelStabilityModel.SlipPlaneUpliftVan.SlipPlaneRightGrid.GridZNumber, TangentLineLevels = [] }; foreach (TangentLine tangentLine in kernelModelStabilityModel.SlipPlaneUpliftVan.SlipCircleTangentLine.BoundaryHeights) { stabilityOutputItem.InputUpliftVanCalculationGrid.TangentLineLevels.Add(tangentLine.Height); } } /// /// Combines the surface line with the SoilProfile2D. /// /// The sub soil scenario. /// The surface line. /// The dike embankment soil. /// Thrown when no SoilProfile2D is defined private static void CombineSoilProfile2DWithSurfaceLine(SoilGeometryProbability subSoilScenario, SurfaceLine2 surfaceLine2, Soil dikeEmbankmentSoil) { ValidateForCombineSoilProfile2DWithSurfaceLine(subSoilScenario, surfaceLine2, dikeEmbankmentSoil); subSoilScenario.SoilProfile2D = SoilProfile2DSurfaceLineHelper.CombineSurfaceLineWithSoilProfile2D(surfaceLine2.Geometry, subSoilScenario.SoilProfile2D, dikeEmbankmentSoil); CorrectSurfacelinePointsForGeometryCoordinates(subSoilScenario.SoilProfile2D.Geometry, surfaceLine2); } private static void CorrectSurfacelinePointsForGeometryCoordinates(GeometryData geometry, SurfaceLine2 surfaceLine) { foreach (Point2D surfaceLinePoint in surfaceLine.Geometry.Points) { Point2D point = geometry.GetPointAtLocation(surfaceLinePoint, GeometryConstants.Accuracy * 1.5); if (point != null) { surfaceLinePoint.X = point.X; surfaceLinePoint.Z = point.Z; } } RemovePointsAtSameXLocation(surfaceLine); } /// /// Removes points with same X coordinates and different Z /// private static void RemovePointsAtSameXLocation(SurfaceLine2 surfaceLine) { var pointsToDelete = new List(); CharacteristicPoint[] pointsAsArray = surfaceLine.CharacteristicPoints.ToArray(); for (var i = 0; i < pointsAsArray.Length; i++) { for (int j = i; j < pointsAsArray.Length; j++) { if (i != j && Math.Abs(pointsAsArray[i].Point.X - pointsAsArray[j].Point.X) < GeometryConstants.Accuracy && Math.Abs(pointsAsArray[i].Point.Z - pointsAsArray[j].Point.Z) > GeometryConstants.Accuracy && !pointsToDelete.Contains(pointsAsArray[j])) { if (pointsAsArray[j].CharacteristicPointType == CharacteristicPointType.None) { pointsToDelete.Add(pointsAsArray[j]); } else { if (pointsAsArray[i].CharacteristicPointType == CharacteristicPointType.None) { pointsToDelete.Add(pointsAsArray[i]); } } } } } foreach (CharacteristicPoint point in pointsToDelete) { surfaceLine.CharacteristicPoints.Remove(point); } } private static void ValidateForCombineSoilProfile2DWithSurfaceLine(SoilGeometryProbability subSoilScenario, SurfaceLine2 surfaceLine2, Soil dikeEmbankmentSoil) { ThrowWhenSoilProfile2DIsNull(subSoilScenario); if (surfaceLine2 == null || surfaceLine2.Geometry == null || surfaceLine2.Geometry.Count < 2) { throw new ArgumentNullException(nameof(surfaceLine2), @"SurfaceLine cannot be null and must have a valid geometry."); } if (dikeEmbankmentSoil == null) { throw new ArgumentNullException(nameof(dikeEmbankmentSoil), @"DikeEmbankmentSoil cannot be null."); } if (!SoilProfile2DSurfaceLineHelper.IsSurfaceLineAboveBottomSoilProfile2D(surfaceLine2, subSoilScenario.SoilProfile2D)) { throw new InvalidOperationException(@"SurfaceLine is (partly) below the bottom of the SoilProfile2D."); } if (!SurfaceLine2Validator.AreAllPointsXCoordinatesAscending(surfaceLine2)) { throw new InvalidOperationException(@"SurfaceLine points are not strictly ascending"); } } /// /// Combines the surfaceline with the SoilProfile1D. /// /// The sub soil scenario. /// The surfaceline. /// The dike embankment soil. /// Thrown when no SoilProfile1D is defined private static void CombineSoilProfile1DWithSurfaceLine(SoilGeometryProbability subSoilScenario, SurfaceLine2 surfaceLine2, Soil dikeEmbankmentSoil) { if (subSoilScenario.SoilProfile1D == null) { throw new ArgumentNullException(nameof(subSoilScenario), @"SoilProfile1D cannot be null."); } SoilProfile2D soilProfile2D = subSoilScenario.SoilProfile2D; if (soilProfile2D == null) { var soilSurfaceProfile = new SoilSurfaceProfile { SoilProfile = subSoilScenario.SoilProfile1D, SurfaceLine2 = surfaceLine2, Name = subSoilScenario.SoilProfile1D.Name, DikeEmbankmentMaterial = dikeEmbankmentSoil }; // Convert the soilSurfaceProfile to a SoilProfile2D to be able to edit it properly. SoilProfile2D soilProfile2DNew = soilSurfaceProfile.ConvertToSoilProfile2D(); subSoilScenario.SoilProfile2D = soilProfile2DNew; subSoilScenario.SoilProfile2DName = soilProfile2DNew.Name; subSoilScenario.SoilProfileType = SoilProfileType.ProfileType2D; subSoilScenario.SoilProfile2D.Geometry.Rebox(); } } /// /// Determines the calculation filename. /// /// The filename prefix. /// Name of the soil geometry. /// Index of the iteration. /// private static string DetermineCalculationFilename(string filenamePrefix, string soilGeometryName, int iterationIndex) { string calculationName; if (iterationIndex <= 0) { calculationName = $"{filenamePrefix}_Pro({soilGeometryName})"; } else { calculationName = $"{filenamePrefix}_Pro({soilGeometryName})_Ite({iterationIndex})"; } return Regex.Replace(calculationName, @"[\\\/:\*\?""'<>|.]", "_"); } /// /// Gets the stability calculation directory. /// /// The model. /// The project working path. /// private static string GetStabilityCalculationDirectory(StabilityModelType model, string projectWorkingPath) { string calculationBaseDirectory = projectWorkingPath; string stabilitySubDir = GetCalculationSubDir(model); string stabilityDirectory = Path.Combine(calculationBaseDirectory, stabilitySubDir); if (!Directory.Exists(stabilityDirectory)) { Directory.CreateDirectory(stabilityDirectory); } return stabilityDirectory; } /// /// Gets the calculation sub dir. /// /// The model. /// private static string GetCalculationSubDir(StabilityModelType model) { const string stabilitySubDir = @"Stability\"; var modelSubDirectory = model.ToString(); string dir = Path.Combine(stabilitySubDir, modelSubDirectory); return dir; } private static void ThrowWhenSoilProfile2DIsNull(SoilGeometryProbability subSoilScenario) { if (subSoilScenario.SoilProfile2D == null) { throw new ArgumentNullException(nameof(subSoilScenario), @"SoilProfile2D cannot be null."); } } }