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