// 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 prepare
///
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;
IntrusionVerticalWaterPressureType? pressureType = damKernelInput.Location.IntrusionVerticalWaterPressure;
Waternet waterNet = PlLinesToWaternetConverter.CreateWaternetBasedOnPlLines(plLines, damKernelInput.SubSoilScenario.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;
double minimumCircleDepth = damKernelInput.DamFailureMechanismeCalculationSpecification
.FailureMechanismParametersMStab.MStabParameters.CalculationOptions.MinimalCircleDepth;
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, minimumCircleDepth);
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;
}
}