// 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.Linq;
using Deltares.DamEngine.Calculators.KernelWrappers.Common;
using Deltares.DamEngine.Data.General;
using Deltares.DamEngine.Data.Geometry;
using Deltares.DamEngine.Data.Geotechnics;
using Deltares.DamEngine.Data.Standard;
using Deltares.MacroStability.CSharpWrapper;
using Deltares.MacroStability.CSharpWrapper.Input;
using Deltares.MacroStability.CSharpWrapper.Water;
using CharacteristicPointType = Deltares.DamEngine.Data.Geotechnics.CharacteristicPointType;
using HeadLine = Deltares.DamEngine.Data.Geometry.HeadLine;
using KernelUpliftVanCalculationGrid = Deltares.MacroStability.CSharpWrapper.UpliftVanCalculationGrid;
using KernelWaternet = Deltares.MacroStability.CSharpWrapper.Water.Waternet;
using KernelMacroStabilityInput = Deltares.MacroStability.CSharpWrapper.Input.MacroStabilityInput;
using KernelPoint2D = Deltares.MacroStability.CSharpWrapper.Point2D;
using KernelWaternetLine = Deltares.MacroStability.CSharpWrapper.Water.WaternetLine;
using KernelHeadLine = Deltares.MacroStability.CSharpWrapper.Water.HeadLine;
using Point2D = Deltares.DamEngine.Data.Geometry.Point2D;
using Soil = Deltares.MacroStability.CSharpWrapper.Input.Soil;
using SoilProfile = Deltares.MacroStability.CSharpWrapper.Input.SoilProfile;
using Waternet = Deltares.DamEngine.Data.Geometry.Waternet;
using WaternetLine = Deltares.DamEngine.Data.Geometry.WaternetLine;
namespace Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityCommon.MacroStabilityIo;
///
/// Fills the Macro Stability Wrapper from the DAM Engine data
///
public class FillMacroStabilityWrapperInputFromEngine
{
private readonly Dictionary soilsDictionary = new();
///
/// Creates the macro stability input.
///
/// The dam kernel input.
/// The parameters for MStab.
/// The WaterNet.
///
///
public KernelMacroStabilityInput CreateMacroStabilityInput(DamKernelInput damKernelInput,
MStabParameters mStabParameters, Waternet waterNet,
double xCoordinateLowestUpliftFactorPoint = 0.0)
{
soilsDictionary.Clear();
UpliftVanCalculationGrid upliftVanCalculationGrid = null;
BishopCalculationGrid bishopCalculationGrid = null;
if (mStabParameters.Model == StabilityModelType.Bishop)
{
bishopCalculationGrid = MacroStabilityCommonHelper.DetermineBishopCalculationGrid(damKernelInput);
}
if (mStabParameters.Model == StabilityModelType.UpliftVan)
{
upliftVanCalculationGrid = MacroStabilityCommonHelper.DetermineUpliftVanCalculationGrid(damKernelInput, xCoordinateLowestUpliftFactorPoint);
}
var macroStabilityInput = new KernelMacroStabilityInput
{
StabilityModel = new StabilityInput(),
PreprocessingInput = new PreprocessingInput()
};
macroStabilityInput.StabilityModel.UpliftVanCalculationGrid = new KernelUpliftVanCalculationGrid();
macroStabilityInput.StabilityModel.ConstructionStages.Add(new ConstructionStage());
ConstructionStage lastStage = macroStabilityInput.StabilityModel.ConstructionStages.Last();
TransferStabilityModelProperties(mStabParameters, macroStabilityInput.StabilityModel);
TransferSlipPlaneConstraints(damKernelInput.Location, macroStabilityInput.StabilityModel.SlipPlaneConstraints);
TransferSoils(damKernelInput.Location.SoilList, macroStabilityInput.StabilityModel.Soils, lastStage.FixedSoilStresses);
lastStage.SoilProfile = new SoilProfile();
lastStage.PreconsolidationStresses = new List();
lastStage.WaterDefinition = WaterDefinition.WaterNet;
TransferSoilProfile(damKernelInput.SubSoilScenario.SoilProfile2D, lastStage.SoilProfile, lastStage.PreconsolidationStresses);
macroStabilityInput.PreprocessingInput.PreConstructionStages.Add(new PreConstructionStage());
PreConstructionStage preConstructionLastStage = macroStabilityInput.PreprocessingInput.PreConstructionStages.Last();
preConstructionLastStage.SurfaceLine = new SurfaceLine();
TransferSurfaceLine(damKernelInput.Location.SurfaceLine, preConstructionLastStage.SurfaceLine);
lastStage.Waternet = new KernelWaternet();
TransferWaternet(waterNet, lastStage.Waternet);
SearchAreaConditions preprocessingSearchAreaConditions = macroStabilityInput.PreprocessingInput.SearchAreaConditions;
switch (mStabParameters.Model)
{
case StabilityModelType.Bishop:
TransferBishopSearchAreaSettings(preprocessingSearchAreaConditions, bishopCalculationGrid);
TransferBishopCalculationGrid(macroStabilityInput.StabilityModel.BishopCalculationCircle, bishopCalculationGrid);
break;
case StabilityModelType.UpliftVan:
TransferUpliftVanSearchAreaSettings(preprocessingSearchAreaConditions, upliftVanCalculationGrid);
TransferUpliftVanCalculationGrid(macroStabilityInput.StabilityModel.UpliftVanCalculationGrid, upliftVanCalculationGrid);
break;
default:
throw new NotImplementedException(nameof(mStabParameters.Model));
}
lastStage.UniformLoads = new List();
TrafficLoad trafficLoad = MacroStabilityCommonHelper.FillTrafficLoad(damKernelInput);
TransferUniformLoads(trafficLoad, lastStage.UniformLoads);
TransferTrafficLoadDegreeOfConsolidation(damKernelInput.Location.TrafficLoadDegreeOfConsolidations, lastStage);
return macroStabilityInput;
}
protected internal static void TransferSlipPlaneConstraints(Location location, SlipPlaneConstraints slipPlaneConstraints)
{
if (location.StabilityOptions == null)
{
return;
}
slipPlaneConstraints.SlipPlaneMinDepth = location.StabilityOptions.MinimalCircleDepth ?? 0.0;
if ((location.StabilityOptions.StabilityZoneType == StabilityZoneType.ForbiddenZone) &&
location.StabilityOptions.ForbiddenZoneFactor.HasValue)
{
CharacteristicPointSet characteristicPoints = location.SurfaceLine.CharacteristicPoints;
slipPlaneConstraints.XEntryMin = characteristicPoints.GetPoint2D(CharacteristicPointType.DikeTopAtRiver).X;
double xDikeTopAtPolder = characteristicPoints.GetPoint2D(CharacteristicPointType.DikeTopAtPolder).X;
double xDikeToeAtPolder = characteristicPoints.GetPoint2D(CharacteristicPointType.DikeToeAtPolder).X;
double factor = location.StabilityOptions.ForbiddenZoneFactor.Value;
slipPlaneConstraints.XEntryMax = (xDikeToeAtPolder * factor) + ((1 - factor) * xDikeTopAtPolder);
}
}
private void TransferStabilityModelProperties(MStabParameters mStabParameters, StabilityInput kernelStabilityInput)
{
kernelStabilityInput.MoveGrid = true; // is not in DamEngine but MUST be true as we use the brute force approach.
kernelStabilityInput.MaximumSliceWidth = 1.0; // is not in DamEngine datamodel
// For Bishop, only Grid is possible however if Bishop/UpliftVan was selected, then the SearchAlgorithm concerns only Uplift-Van
kernelStabilityInput.SearchAlgorithm = mStabParameters.Model == StabilityModelType.Bishop ? SearchAlgorithm.Grid : ConversionHelper.ConvertToMacroStabilitySearchMethod(mStabParameters.SearchMethod);
if (kernelStabilityInput.SearchAlgorithm == SearchAlgorithm.BeeswarmAndLevenbergMarquardt)
{
CreateDefaultBeeSwarmOptions(kernelStabilityInput);
}
kernelStabilityInput.ModelOption = ConversionHelper.ConvertToModelOptions(mStabParameters.Model);
kernelStabilityInput.Orientation = ConversionHelper.ConvertToGridOrientation(mStabParameters.GridPosition);
kernelStabilityInput.NumberOfRefinementsGrid = 2;
kernelStabilityInput.NumberOfRefinementsTangentLines = 2;
}
private void CreateDefaultBeeSwarmOptions(StabilityInput kernelStabilityInput)
{
kernelStabilityInput.BeeswarmAlgorithmOptions = new BeeSwarmAlgorithmOptions
{
EliteCount = 2,
Seed = 1,
CrossOver = 0.3,
Beta = 0.4,
Delta = 0.7,
DifferentialWeight = 0.3,
MaximumNonImprovingGenerations = 10,
PopulationCount = 200,
GenerationCount = 40
};
}
private void TransferSoils(SoilList damSoilList, ICollection kernelSoils, ICollection kernelFixedSoilStresses)
{
// Transfer all soils
if (damSoilList != null)
{
foreach (Data.Geotechnics.Soil damSoil in damSoilList.Soils)
{
Soil kernelSoil = ConversionHelper.ConvertToMacroStabilitySoil(damSoil);
kernelSoils.Add(kernelSoil);
soilsDictionary.Add(damSoil.Name, kernelSoil);
kernelFixedSoilStresses.Add(new FixedSoilStress
{
POP = damSoil.PoP,
Soil = kernelSoil
});
}
}
}
private static void TransferTrafficLoadDegreeOfConsolidation(IList damDegreeOfConsolidations, ConstructionStage stage)
{
if (damDegreeOfConsolidations != null && stage.UniformLoads.Count == 1)
{
stage.ConsolidationValues = new List();
foreach (SoilProfileSurface surface in stage.SoilProfile.SoilSurfaces)
{
stage.ConsolidationValues.Add(new ConsolidationValue
{
Consolidator = stage.UniformLoads.FirstOrDefault(),
Consolidated = surface,
Value = damDegreeOfConsolidations.Find(t => t.SoilName == surface.Soil.Name).DegreeOfConsolidation
});
}
}
}
private void TransferSoilProfile(SoilProfile2D damSoilProfile2D, SoilProfile kernelSoilProfile,
ICollection preconsolidationStresses)
{
// Add points
var pointsDictionary = new Dictionary();
kernelSoilProfile.Geometry = new Geometry
{
Points = new List()
};
foreach (Point2D damPoint in damSoilProfile2D.Geometry.Points)
{
var kernelPoint = new KernelPoint2D(damPoint.X, damPoint.Z);
kernelSoilProfile.Geometry.Points.Add(kernelPoint);
pointsDictionary.Add(damPoint, kernelPoint);
}
// Add curves
var curvesDictionary = new Dictionary();
kernelSoilProfile.Geometry.Curves = new List();
foreach (GeometryCurve damCurve in damSoilProfile2D.Geometry.Curves)
{
var kernelCurve = new Curve
{
HeadPoint = pointsDictionary[damCurve.HeadPoint],
EndPoint = pointsDictionary[damCurve.EndPoint]
};
kernelSoilProfile.Geometry.Curves.Add(kernelCurve);
curvesDictionary.Add(damCurve, kernelCurve);
}
// Add loops
var loopsDictionary = new Dictionary();
kernelSoilProfile.Geometry.Loops = new List();
foreach (GeometryLoop damLoop in damSoilProfile2D.Geometry.Loops)
{
var kernelLoop = new Loop
{
Curves = new List()
};
foreach (GeometryCurve geometryCurve in damLoop.CurveList)
{
if (!curvesDictionary.ContainsKey(geometryCurve))
{
var kernelCurve = new Curve
{
HeadPoint = pointsDictionary[geometryCurve.HeadPoint],
EndPoint = pointsDictionary[geometryCurve.EndPoint]
};
curvesDictionary.Add(geometryCurve, kernelCurve);
}
kernelLoop.Curves.Add(curvesDictionary[geometryCurve]);
}
kernelSoilProfile.Geometry.Loops.Add(kernelLoop);
loopsDictionary.Add(damLoop, kernelLoop);
}
// Add geometry surfaces
var geometrySurfacesDictionary = new Dictionary();
kernelSoilProfile.Geometry.Surfaces = new List();
foreach (SoilLayer2D damSurface in damSoilProfile2D.Surfaces)
{
var kernelGeometrySurface = new Surface();
GeometrySurface damGeometrySurface = damSurface.GeometrySurface;
kernelGeometrySurface.OuterLoop = loopsDictionary[damGeometrySurface.OuterLoop];
geometrySurfacesDictionary.Add(damGeometrySurface, kernelGeometrySurface);
kernelGeometrySurface.InnerLoops = new List(damGeometrySurface.InnerLoops.Count);
foreach (GeometryLoop damSurfaceInnerLoop in damGeometrySurface.InnerLoops)
{
kernelGeometrySurface.InnerLoops.Add(loopsDictionary[damSurfaceInnerLoop]);
}
kernelSoilProfile.Geometry.Surfaces.Add(kernelGeometrySurface);
}
// Add soil surfaces
kernelSoilProfile.SoilSurfaces = new List();
foreach (SoilLayer2D damSoilLayer2D in damSoilProfile2D.Surfaces)
{
var kernelSoilLayer2D = new SoilProfileSurface
{
Surface = geometrySurfacesDictionary[damSoilLayer2D.GeometrySurface],
Soil = soilsDictionary[damSoilLayer2D.Soil.Name],
IsAquifer = damSoilLayer2D.IsAquifer,
WaterPressureInterpolationModel = ConversionHelper.ConvertToMacroStabilityWaterpressureInterpolationModel(damSoilLayer2D
.WaterpressureInterpolationModel)
};
kernelSoilProfile.SoilSurfaces.Add(kernelSoilLayer2D);
}
// Add the preconsolidation stresses
foreach (PreConsolidationStress preconsolidationStress in damSoilProfile2D.PreconsolidationStresses)
{
var kernelPrecon = new PreconsolidationStress
{
Point = new KernelPoint2D(preconsolidationStress.X, preconsolidationStress.Z),
StressValue = preconsolidationStress.StressValue
};
preconsolidationStresses.Add(kernelPrecon);
}
}
private void TransferSurfaceLine(SurfaceLine2 damSurfaceLine, SurfaceLine kernelSurfaceLine)
{
kernelSurfaceLine.CharacteristicPoints = new List();
foreach (CharacteristicPoint damCharPoint in damSurfaceLine.CharacteristicPoints)
{
var kernelCharPoint = new SurfaceLineCharacteristicPoint
{
CharacteristicPointType = ConversionHelper.ConvertToMacroStabilityCharacteristicPointType(damCharPoint.CharacteristicPointType),
GeometryPoint = new KernelPoint2D(damCharPoint.Point.X, damCharPoint.Point.Z)
};
kernelSurfaceLine.CharacteristicPoints.Add(kernelCharPoint);
}
}
private static void TransferWaternet(Waternet damWaternet, KernelWaternet kernelWaternet)
{
var headLineMapping = new Dictionary();
kernelWaternet.Name = damWaternet.Name;
// Phreatic Line
PhreaticLine phreaticLine = damWaternet.PhreaticLine;
kernelWaternet.PhreaticLine = CreateLine(phreaticLine);
headLineMapping.Add(damWaternet.PhreaticLine, kernelWaternet.PhreaticLine);
// Head Lines
foreach (HeadLine damHeadLine in damWaternet.HeadLineList)
{
var kernelHeadLine = CreateLine(damHeadLine);
kernelWaternet.HeadLines.Add(kernelHeadLine);
headLineMapping.Add(damHeadLine, kernelHeadLine);
}
// Waternet Lines
foreach (WaternetLine damWaternetLine in damWaternet.WaternetLineList)
{
var kernelWaternetLine = CreateLine(damWaternetLine);
kernelWaternetLine.AssociatedHeadLine = damWaternetLine.HeadLine.Name.Contains("PL 1") ? headLineMapping[damWaternet.PhreaticLine] : headLineMapping[damWaternetLine.HeadLine];
kernelWaternet.ReferenceLines.Add(kernelWaternetLine);
}
}
private static TKernelLineType CreateLine(GeometryPointString geometry)
where TKernelLineType : KernelWaternetLine, new()
{
var line = new TKernelLineType
{
Name = geometry.Name,
Points = geometry.Points.Select(p => new KernelPoint2D(p.X, p.Z)).ToList()
};
return line;
}
private static void TransferBishopSearchAreaSettings(SearchAreaConditions kernelSearchAreaConditions, BishopCalculationGrid bishopCalculationGrid)
{
// In the Macrostability kernel, for Bishop, the automatic tangent lines are set by AutoSearchArea = True.
// Setting AutoTangentLines to true will create the automatic tangent lines of Uplift-Van which is not desired.
kernelSearchAreaConditions.AutoTangentLines = false;
kernelSearchAreaConditions.AutoSearchArea = bishopCalculationGrid.IsSearchAreaAutomatic;
kernelSearchAreaConditions.TangentLineNumber = bishopCalculationGrid.TangentLineCount;
kernelSearchAreaConditions.TangentLineZTop = bishopCalculationGrid.TangentLineZTop;
kernelSearchAreaConditions.TangentLineZBottom = bishopCalculationGrid.TangentLineZBottom;
}
private static void TransferUpliftVanSearchAreaSettings(SearchAreaConditions kernelSearchAreaConditions, UpliftVanCalculationGrid upliftVanCalculationGrid)
{
kernelSearchAreaConditions.AutoTangentLines = upliftVanCalculationGrid.TangentLinesCreationMethod is TangentLinesDefinition.Automatic or TangentLinesDefinition.OnBoundaryLines;
kernelSearchAreaConditions.AutoSearchArea = upliftVanCalculationGrid.IsGridsAutomatic;
kernelSearchAreaConditions.OnlyAbovePleistoceen = upliftVanCalculationGrid.TangentLinesCreationMethod == TangentLinesDefinition.Automatic;
kernelSearchAreaConditions.MaxSpacingBetweenBoundaries = 0.8;
kernelSearchAreaConditions.TangentLineNumber = upliftVanCalculationGrid.TangentLineCount;
kernelSearchAreaConditions.TangentLineZTop = upliftVanCalculationGrid.TangentLineZTop;
kernelSearchAreaConditions.TangentLineZBottom = upliftVanCalculationGrid.TangentLineZBottom;
}
private static void TransferBishopCalculationGrid(BishopCalculationCircle kernelBishopCalculationCircle, BishopCalculationGrid bishopCalculationGrid)
{
if (kernelBishopCalculationCircle == null)
{
throw new ArgumentNullException(nameof(kernelBishopCalculationCircle));
}
kernelBishopCalculationCircle.Grid = new CalculationGrid
{
GridXNumber = bishopCalculationGrid.GridXCount,
GridXLeft = bishopCalculationGrid.GridXLeft,
GridXRight = bishopCalculationGrid.GridXRight,
GridZNumber = bishopCalculationGrid.GridZCount,
GridZTop = bishopCalculationGrid.GridZTop,
GridZBottom = bishopCalculationGrid.GridZBottom
};
if (!bishopCalculationGrid.IsSearchAreaAutomatic)
{
kernelBishopCalculationCircle.TangentLines = bishopCalculationGrid.TangentLineLevels.ToArray();
}
else
{
kernelBishopCalculationCircle.TangentLines = new List();
}
}
private static void TransferUpliftVanCalculationGrid(KernelUpliftVanCalculationGrid kernelUpliftVanCalculationGrid, UpliftVanCalculationGrid upliftVanCalculationGrid)
{
if (kernelUpliftVanCalculationGrid == null)
{
throw new ArgumentNullException(nameof(kernelUpliftVanCalculationGrid));
}
kernelUpliftVanCalculationGrid.LeftGrid = new CalculationGrid
{
GridXNumber = upliftVanCalculationGrid.LeftGridXCount,
GridXLeft = upliftVanCalculationGrid.LeftGridXLeft,
GridXRight = upliftVanCalculationGrid.LeftGridXRight,
GridZNumber = upliftVanCalculationGrid.LeftGridZCount,
GridZTop = upliftVanCalculationGrid.LeftGridZTop,
GridZBottom = upliftVanCalculationGrid.LeftGridZBottom
};
kernelUpliftVanCalculationGrid.RightGrid = new CalculationGrid
{
GridXNumber = upliftVanCalculationGrid.RightGridXCount,
GridXLeft = upliftVanCalculationGrid.RightGridXLeft,
GridXRight = upliftVanCalculationGrid.RightGridXRight,
GridZNumber = upliftVanCalculationGrid.RightGridZCount,
GridZTop = upliftVanCalculationGrid.RightGridZTop,
GridZBottom = upliftVanCalculationGrid.RightGridZBottom
};
if (upliftVanCalculationGrid.TangentLinesCreationMethod == TangentLinesDefinition.Specified)
{
kernelUpliftVanCalculationGrid.TangentLines = upliftVanCalculationGrid.TangentLineLevels.ToArray();
}
else
{
kernelUpliftVanCalculationGrid.TangentLines = new List();
}
}
private static void TransferUniformLoads(TrafficLoad damTrafficLoad, ICollection kernelUniformLoads)
{
if (damTrafficLoad != null)
{
kernelUniformLoads.Add(new UniformLoad
{
XStart = damTrafficLoad.XStart,
XEnd = damTrafficLoad.XEnd,
Pressure = damTrafficLoad.Pressure,
DistributionAngle = damTrafficLoad.DistributionAngle
});
}
}
}