// 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.Data; using System.IO; using Deltares.DamEngine.Calculators.DikesDesign; using Deltares.DamEngine.Calculators.KernelWrappers.Common; using Deltares.DamEngine.Calculators.KernelWrappers.Interfaces; using Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityCommon; using Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityCommon.MacroStabilityIo; 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.CSharpWrapper; using Deltares.MacroStability.CSharpWrapper.Output; using UpliftVanCalculationGrid = Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityCommon.UpliftVanCalculationGrid; namespace Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityInwards; public class MacroStabilityInwardsKernelWrapper : IKernelWrapper { readonly MacroStabilityCommonHelper macroStabilityCommonHelper; private Calculator stabilityCalculator; private Calculator stabilityCalculatorForSecondCalculation; private string fileNameForCalculation; private string fileNameForSecondCalculation; private int lastIterationIndex; /// /// Initializes a new instance of the class. /// public MacroStabilityInwardsKernelWrapper() { macroStabilityCommonHelper = new MacroStabilityCommonHelper(); } /// /// Gets or sets the failure mechanism parameters for MStab. /// /// /// The failure mechanism parameters MStab. /// public FailureMechanismParametersMStab FailureMechanismParametersMStab { get; set; } /// /// Prepares the specified dam kernel input. /// /// The dam kernel input. /// The number of the current iteration. /// The kernel data input. /// The kernel data output /// /// Result of the preparation /// public PrepareResult Prepare(DamKernelInput damKernelInput, int iterationIndex, out IKernelDataInput kernelDataInput, out IKernelDataOutput kernelDataOutput) { fileNameForCalculation = ""; fileNameForSecondCalculation = ""; var macroStabilityInput = new MacroStabilityKernelDataInput(); kernelDataInput = macroStabilityInput; var macroStabilityOutput = new MacroStabilityOutput { CalculationResult = CalculationResult.NoRun }; kernelDataOutput = macroStabilityOutput; if (damKernelInput.SubSoilScenario.SegmentFailureMechanismType != null && damKernelInput.SubSoilScenario.SegmentFailureMechanismType.Value == SegmentFailureMechanismType.Stability) { try { bool isBishopUpliftVan = FailureMechanismParametersMStab.MStabParameters.Model == MStabModelType.BishopUpliftVan; MStabModelType model = FailureMechanismParametersMStab.MStabParameters.Model; if (isBishopUpliftVan) { // if current model is BishopUpliftVan then set to Bishop for proper name/path for input file model = MStabModelType.Bishop; } MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(damKernelInput.SubSoilScenario, damKernelInput.Location.SurfaceLine, damKernelInput.Location.GetDikeEmbankmentSoil()); // Check if the surface-line is within the SoilProfile2D boundaries (left/right) if (!SoilProfile2DSurfaceLineHelper.IsSurfaceLineInsideSoilProfile2D(damKernelInput.Location.SurfaceLine, damKernelInput.SubSoilScenario.SoilProfile2D)) { macroStabilityOutput.Message = new LogMessage { MessageType = LogMessageType.FatalError, Message = $"SurfaceLine {damKernelInput.Location.SurfaceLine.Name} does not fit within the boundaries of soil profile {damKernelInput.SubSoilScenario.SoilProfile2D.Name}" }; return PrepareResult.Failed; } const bool useRiverLevelLow = false; // Determine whether there is uplift var upliftHelper = new UpliftHelper(); PlLines plLines = upliftHelper.DeterminePlLinesForStability(damKernelInput, useRiverLevelLow, out UpliftSituation upliftSituation); upliftSituation.IsUplift = upliftHelper.DetermineIsUplift(plLines, damKernelInput.Location, damKernelInput.SubSoilScenario); macroStabilityOutput.UpliftSituation = upliftSituation; var fillMacroStabilityWrapperFromEngine = new FillMacroStabilityWrapperInputFromEngine { TrafficLoad = MacroStabilityCommonHelper.FillTrafficLoad(damKernelInput) }; if (FailureMechanismParametersMStab.MStabParameters.Model == MStabModelType.Bishop || FailureMechanismParametersMStab.MStabParameters.Model == MStabModelType.BishopUpliftVan) { fillMacroStabilityWrapperFromEngine.BishopCalculationGrid = MacroStabilityCommonHelper.FillBishopCalculationGrid(damKernelInput); } if (FailureMechanismParametersMStab.MStabParameters.Model == MStabModelType.UpliftVan || FailureMechanismParametersMStab.MStabParameters.Model == MStabModelType.BishopUpliftVan) { if (!upliftSituation.IsUplift && !isBishopUpliftVan) { return PrepareResult.NotRelevant; } fillMacroStabilityWrapperFromEngine.UpliftVanCalculationGrid = FillUpliftVanCalculationGrid(damKernelInput); } FailureMechanismParametersMStab.MStabParameters.Model = model; double penetrationLength = damKernelInput.Location.ModelParametersForPlLines.PenetrationLength; SoilProfile2D soilProfile2D = damKernelInput.SubSoilScenario.SoilProfile2D; IntrusionVerticalWaterPressureType? pressureType = damKernelInput.Location.IntrusionVerticalWaterPressure; Waternet waterNet = PlLinesToWaternetConverter.CreateWaternetBasedOnPlLines(plLines, soilProfile2D, penetrationLength, pressureType); macroStabilityInput.Input = fillMacroStabilityWrapperFromEngine.CreateMacroStabilityInput(damKernelInput, FailureMechanismParametersMStab.MStabParameters, waterNet); fileNameForCalculation = MacroStabilityCommonHelper.GetStabilityInputFileName(damKernelInput, iterationIndex, FailureMechanismParametersMStab.MStabParameters.Model); stabilityCalculator = new Calculator(macroStabilityInput.Input); PrepareResult firstPrepareResult = MacroStabilityCommonHelper.PrepareKernel(stabilityCalculator, fileNameForCalculation); if (isBishopUpliftVan && upliftSituation.IsUplift && firstPrepareResult == PrepareResult.Successful) { model = MStabModelType.UpliftVan; FailureMechanismParametersMStab.MStabParameters.Model = model; macroStabilityInput.Input = fillMacroStabilityWrapperFromEngine.CreateMacroStabilityInput(damKernelInput, FailureMechanismParametersMStab.MStabParameters, waterNet); fileNameForSecondCalculation = MacroStabilityCommonHelper.GetStabilityInputFileName(damKernelInput, iterationIndex, FailureMechanismParametersMStab.MStabParameters.Model); // reset model FailureMechanismParametersMStab.MStabParameters.Model = MStabModelType.BishopUpliftVan; stabilityCalculatorForSecondCalculation = new Calculator(macroStabilityInput.Input); return MacroStabilityCommonHelper.PrepareKernel(stabilityCalculatorForSecondCalculation, fileNameForSecondCalculation); } if (isBishopUpliftVan) { // reset model FailureMechanismParametersMStab.MStabParameters.Model = MStabModelType.BishopUpliftVan; } return firstPrepareResult; } catch (Exception e) { macroStabilityOutput.Message = new LogMessage { MessageType = LogMessageType.FatalError, Message = e.Message }; kernelDataOutput = macroStabilityOutput; return PrepareResult.Failed; } } kernelDataInput = null; return PrepareResult.NotRelevant; } /// /// 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 int Validate(IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, out List messages) { return MacroStabilityCommonHelper.Validate(kernelDataInput, kernelDataOutput, out messages); } /// /// Executes the kernel. /// /// The kernel data input. /// The kernel data output. /// The return messages. public void Execute(IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, out List messages) { var macroStabilityKernelDataInput = (MacroStabilityKernelDataInput) kernelDataInput; var macroStabilityOutput = (MacroStabilityOutput) kernelDataOutput; MacroStabilityCommonHelper.ThrowWhenMacroStabilityKernelInputNull(macroStabilityKernelDataInput); MacroStabilityCommonHelper.ThrowWhenMacroStabilityKernelOutputNull(macroStabilityOutput); bool isBishopUpliftVan = FailureMechanismParametersMStab.MStabParameters.Model == MStabModelType.BishopUpliftVan; if (isBishopUpliftVan) { macroStabilityKernelDataInput.Input.StabilityModel.ModelOption = StabilityModelOptionType.Bishop; } macroStabilityCommonHelper.PerformStabilityCalculation(macroStabilityKernelDataInput.Input, macroStabilityOutput, fileNameForCalculation, stabilityCalculator, out messages); string fileName = Path.GetFileNameWithoutExtension(fileNameForCalculation); foreach (LogMessage logMessage in messages) { logMessage.Message = fileName + ": " + logMessage.Message; } if (isBishopUpliftVan && macroStabilityOutput.UpliftSituation.IsUplift) { macroStabilityKernelDataInput.Input.StabilityModel.ModelOption = StabilityModelOptionType.UpliftVan; macroStabilityCommonHelper.PerformStabilityCalculation(macroStabilityKernelDataInput.Input, macroStabilityOutput, fileNameForSecondCalculation, stabilityCalculatorForSecondCalculation, out messages); fileName = Path.GetFileNameWithoutExtension(fileNameForSecondCalculation); foreach (LogMessage logMessage in messages) { logMessage.Message = fileName + ": " + logMessage.Message; } } } /// /// Fills the design results with the kernel output. /// /// The dam kernel input. /// The kernel data output. /// The design scenario. /// The result message. /// The design results. /// public void PostProcess(DamKernelInput damKernelInput, IKernelDataOutput kernelDataOutput, DesignScenario designScenario, string resultMessage, out List designResults) { MacroStabilityCommonHelper.ThrowWhenMacroStabilityDamKernelInputNull(damKernelInput); var macroStabilityOutput = kernelDataOutput as MacroStabilityOutput; MacroStabilityCommonHelper.ThrowWhenMacroStabilityKernelOutputNull(macroStabilityOutput); designResults = new List(); if (macroStabilityOutput == null) { return; } MacroStabilityOutputItem macroStabilityOutputItem = ProperMacroStabilityResultsItem(macroStabilityOutput, 0); if (macroStabilityOutputItem != null) { DesignResult designResult = MacroStabilityCommonHelper.NewDesignResult(damKernelInput, designScenario); MacroStabilityCommonHelper.FillDesignResult(macroStabilityOutputItem, designResult); designResult.StabilityDesignResults.NumberOfIterations = lastIterationIndex; designResult.StabilityDesignResults.UpliftSituation = macroStabilityOutput.UpliftSituation; designResults.Add(designResult); } bool isBishopUpliftVan = FailureMechanismParametersMStab.MStabParameters.Model == MStabModelType.BishopUpliftVan; if (isBishopUpliftVan) { macroStabilityOutputItem = ProperMacroStabilityResultsItem(macroStabilityOutput, 1); if (macroStabilityOutputItem != null) { macroStabilityOutputItem = macroStabilityOutput.StabilityOutputItems[1]; if (macroStabilityOutputItem != null) { DesignResult designResult = MacroStabilityCommonHelper.NewDesignResult(damKernelInput, designScenario); MacroStabilityCommonHelper.FillDesignResult(macroStabilityOutputItem, designResult); designResult.StabilityDesignResults.NumberOfIterations = lastIterationIndex; designResult.StabilityDesignResults.UpliftSituation = macroStabilityOutput.UpliftSituation; designResults.Add(designResult); } } int index; // add worst result from Bishop/UpliftVan, but only if both succeeded. if (designResults[0].CalculationResult == CalculationResult.Succeeded && designResults[1].CalculationResult == CalculationResult.Succeeded) { if (designResults[0].StabilityDesignResults.SafetyFactor <= designResults[1].StabilityDesignResults.SafetyFactor) { index = 0; } else { index = 1; } } else if (designResults[0].CalculationResult != CalculationResult.Succeeded) { // There is no reason why Bishop should not have succeeded therefore no end results can be given index = 0; } else if (designResults[1].CalculationResult == CalculationResult.NoRun) { // No uplift therefore no UpliftVan calculation was made. Present Bishop result. index = 0; } else { // UpliftVan calculation failed. No end results can be given index = 1; } macroStabilityOutputItem = ProperMacroStabilityResultsItem(macroStabilityOutput, index); if (macroStabilityOutputItem != null) { DesignResult overallResult = MacroStabilityCommonHelper.NewDesignResult(damKernelInput, designScenario); MacroStabilityCommonHelper.FillDesignResult(macroStabilityOutputItem, overallResult); overallResult.StabilityDesignResults.UpliftSituation = macroStabilityOutput.UpliftSituation; overallResult.StabilityDesignResults.StabilityModelType = MStabModelType.BishopUpliftVan; designResults.Add(overallResult); } } } /// /// Calculates the design at point. /// /// The dam kernel input. /// The kernel data input. /// The kernel data output. /// The point. /// The messages. /// /// public ShoulderDesign CalculateDesignAtPoint(DamKernelInput damKernelInput, IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, GeometryPoint point, out List messages) { // ToDo: Not clear yet if this must be done or how throw new NotImplementedException(); } /// /// Evaluates the design (current factor greater than desired factor) /// /// The dam kernel input. /// The kernel data input. /// The kernel data output. /// The design advise. /// The evaluation message. /// /// if the design was successful /// /// public bool EvaluateDesign(DamKernelInput damKernelInput, IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, out DesignAdvise designAdvise, out string evaluationMessage) { var macroStabilityKernelDataInput = kernelDataInput as MacroStabilityKernelDataInput; var macroStabilityOutput = kernelDataOutput as MacroStabilityOutput; MacroStabilityCommonHelper.ThrowWhenMacroStabilityKernelInputNull(macroStabilityKernelDataInput); MacroStabilityCommonHelper.ThrowWhenMacroStabilityKernelOutputNull(macroStabilityOutput); MacroStabilityCommonHelper.ThrowWhenMacroStabilityDamKernelInputNull(damKernelInput); double fosRequired = damKernelInput.Location.ModelFactors.RequiredSafetyFactorStabilityInnerSlope; if (macroStabilityOutput != null) { double fosAchieved = macroStabilityOutput.StabilityOutputItems[0].SafetyFactor; double exitPointXCoordinate = macroStabilityOutput.StabilityOutputItems[0].CircleSurfacePointRightXCoordinate; GeometryPoint limitPointForShoulderDesign = damKernelInput.Location.SurfaceLine.GetLimitPointForShoulderDesign(); evaluationMessage = string.Format(Resources.FactorAchievedVsFactorRequired, fosAchieved, fosRequired); if (exitPointXCoordinate > limitPointForShoulderDesign.X) { designAdvise = DesignAdvise.ShoulderInwards; } else { designAdvise = DesignAdvise.SlopeInwards; } bool isDesignReady = fosAchieved >= fosRequired; if (isDesignReady) { designAdvise = DesignAdvise.None; } return isDesignReady; } designAdvise = DesignAdvise.None; evaluationMessage = "No Output"; return false; } /// /// Prepares the design. /// /// The kernel data input. /// The kernel data output. /// The dam kernel input. /// Index of the iteration. /// The embankment design parameters. public void PrepareDesign(IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, DamKernelInput damKernelInput, int iterationIndex, out EmbankmentDesignParameters embankmentDesignParameters) { var macroStabilityKernelDataInput = kernelDataInput as MacroStabilityKernelDataInput; MacroStabilityCommonHelper.ThrowWhenMacroStabilityKernelInputNull(macroStabilityKernelDataInput); lastIterationIndex = iterationIndex; Location location = damKernelInput.Location; if (iterationIndex < 1) { // This is the first (initial) call to prepareDesign. // The embankment material is set to DikeEmbankmentMaterial, because the next iteration (Index = 1) will be height adaption embankmentDesignParameters = new EmbankmentDesignParameters { EmbankmentMaterialname = location.DikeEmbankmentMaterial }; FailureMechanismParametersMStab.EmbankmentDesignParameters = embankmentDesignParameters; } else { // Calculation iterations start with IterationIndex = 1. // When IterationIndex = 1: height adaption. // When Iteration > 1: Slope/Shoulder adaption. // Starting from IterationIndex 2 the following parameters should be used: // - The embankment material is set to ShoulderEmbankmentMaterial. // - The previous geometry is set to the height adapted geometry (name is constructed with iteration index 1). if (iterationIndex == 2) { FailureMechanismParametersMStab.EmbankmentDesignParameters.EmbankmentMaterialname = location.ShoulderEmbankmentMaterial; } // In the following prepareDesign calls just return the stored embankmentDesignParameters embankmentDesignParameters = FailureMechanismParametersMStab.EmbankmentDesignParameters; } } /// /// Gets the design strategy /// /// /// public DesignStrategy GetDesignStrategy(DamKernelInput damKernelInput) { switch (damKernelInput.Location.StabilityDesignMethod) { case StabilityDesignMethod.OptimizedSlopeAndShoulderAdaption: return DesignStrategy.OptimizedSlopeAndShoulderAdaption; case StabilityDesignMethod.SlopeAdaptionBeforeShoulderAdaption: return DesignStrategy.SlopeAdaptionBeforeShoulderAdaption; default: return DesignStrategy.NoDesignPossible; } } private UpliftVanCalculationGrid FillUpliftVanCalculationGrid(DamKernelInput damKernelInput) { SlipCircleDefinition slipCircleDefinition = damKernelInput.DamFailureMechanismeCalculationSpecification .FailureMechanismParametersMStab.MStabParameters.SlipCircleDefinition; 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; } private MacroStabilityOutputItem ProperMacroStabilityResultsItem(MacroStabilityOutput macroStabilityOutput, int requestedIndex) { MacroStabilityOutputItem macroStabilityOutputItem = null; if (macroStabilityOutput?.StabilityOutputItems != null && macroStabilityOutput.StabilityOutputItems.Count > requestedIndex && macroStabilityOutput.StabilityOutputItems[requestedIndex] != null) { macroStabilityOutputItem = macroStabilityOutput.StabilityOutputItems[requestedIndex]; } return macroStabilityOutputItem; } }