// 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.");
}
}
}