// Copyright (C) Stichting Deltares 2019. 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.Linq;
using Deltares.DamEngine.Calculators.DikesDesign;
using Deltares.DamEngine.Calculators.KernelWrappers.Common;
using Deltares.DamEngine.Calculators.KernelWrappers.DamMacroStabilityCommon;
using Deltares.DamEngine.Calculators.KernelWrappers.Interfaces;
using Deltares.DamEngine.Calculators.Properties;
using Deltares.DamEngine.Data.Design;
using Deltares.DamEngine.Data.General;
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.WTIStability.Calculation.Wrapper;
namespace Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityInwards
{
public class MacroStabilityInwardsKernelWrapper : IKernelWrapper
{
private WTIStabilityCalculation stabilityCalculator = null;
private int lastIterationIndex = 0;
///
/// Gets or sets the failure mechanisme paramaters for mstab.
///
///
/// The failure mechanisme paramaters mstab.
///
public FailureMechanismParametersMStab FailureMechanismParametersMStab { get; set; } //ToDo MWDAM-? Use same as for DGeoStability or create new one?
public bool tmpPresumePrepareSucceeds; //ToDo MWDAM-1356
public bool tmpPresumeInputValid; //ToDo MWDAM-1367
public bool tmpPresumeRunSucceeds; //ToDo MWDAM-1356, MWDAM-1367, MWDAM-1357
///
/// 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)
{
var macroStabilityInput = new MacroStabilityInput();
kernelDataInput = macroStabilityInput;
var macroStabilityOutput = new MacroStabilityOutput
{
CalculationResult = CalculationResult.NoRun
};
kernelDataOutput = macroStabilityOutput;
if (damKernelInput.SubSoilScenario.SegmentFailureMechanismType == FailureMechanismSystemType.WtiStabilityInside)
{
try
{
// Determine whether there is uplift
var upliftSituation = UpliftHelper.DetermineStabilityUplift(damKernelInput, false);
macroStabilityOutput.UpliftSituation = upliftSituation;
if (upliftSituation.IsUplift)
{
// ToDo MWDAM-1356: Not clear yet what parts are required for input.
var xmlInput = CreateXmlInput(macroStabilityInput, FailureMechanismParametersMStab.EmbankmentDesignParameters);
return PrepareKernel(xmlInput);
}
return PrepareResult.NotRelevant;
}
catch
{
kernelDataOutput = macroStabilityOutput;
return PrepareResult.Failed;
}
}
kernelDataInput = null;
return PrepareResult.NotRelevant;
}
private PrepareResult PrepareKernel(string xmlInput)
{
ThrowWhenMacroStabilityCalculatorNull(stabilityCalculator);
try
{
// ToDo MWDAM-1356: Not clear yet what parts are required for input. Temporary test code added.
//macroStabilityCalculator.InitializeForDeterministic(xmlInput); //ToDo: uncomment when input is set
//return PrepareResult.Successful; //ToDo: uncomment when input is set
if (tmpPresumePrepareSucceeds) //ToDo: remove the temporary test code when input is set
{
return PrepareResult.Successful;
}
else
{
return PrepareResult.Failed;
}
}
catch
{
return PrepareResult.Failed;
}
}
private string CreateXmlInput(MacroStabilityInput macroStabilityInput, EmbankmentDesignParameters embankmentDesignParameters)
{
string xmlInput = "";
//ToDo in MWDAM-1356
return xmlInput;
}
///
/// 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)
{
ThrowWhenMacroStabilityCalculatorNull(stabilityCalculator);
if (tmpPresumeInputValid)
{
messages = new List();
return 0;
}
else
{
messages = new List();
messages.Add(new LogMessage(LogMessageType.Error, null, "Not implemented yet"));
// Todo MWDAM-1356 : make valid XML, only then following lines can be enabled
// string kernelMessage = macroStabilityCalculator.Validate();
// ParseValidationResult(kernelMessage, out messages);
MacroStabilityOutput macroStabilityOutput = (MacroStabilityOutput)kernelDataOutput;
var numberOfErrors = messages.Count(mt => mt.MessageType == LogMessageType.Error);
if (numberOfErrors > 0)
{
macroStabilityOutput.CalculationResult = CalculationResult.InvalidInputData;
}
return numberOfErrors;
}
}
internal void ParseValidationResult(string xmlValidationResult, out List messages)
{
messages = new List();
// ToDo MWDAM-1367: Parse the xml from the kernel and add message with type Error, Info or Warning
// start of temporary test code
if (tmpPresumeInputValid)
{
// only an info/warning message, presumed input valid for test
messages.Add(new LogMessage()
{
Message = "test info message",
MessageType = LogMessageType.Info
});
}
else
{
// add an error message, presumed input not valid for test
messages.Add(new LogMessage()
{
Message = "test error message",
MessageType = LogMessageType.Error
});
}
// end of temporary test code. next code must be enough, presumed that the kernel does all the validation
}
///
/// Executes the kernel.
///
/// The kernel data input.
/// The kernel data output.
/// The return messages.
public void Execute(IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, out List messages)
{
MacroStabilityInput macroStabilityInput = (MacroStabilityInput)kernelDataInput;
MacroStabilityOutput macroStabilityOutput = (MacroStabilityOutput)kernelDataOutput;
ThrowWhenMacroStabilityKernelInputNull(macroStabilityInput);
ThrowWhenMacroStabilityKernelOutputNull(macroStabilityOutput);
messages = new List();
PerformStabilityCalculation(out messages, macroStabilityOutput, macroStabilityInput);
}
private void PerformStabilityCalculation(out List messages, MacroStabilityOutput macroStabilityOutput,
MacroStabilityInput macroStabilityInput)
{
ThrowWhenMacroStabilityCalculatorNull(stabilityCalculator);
macroStabilityOutput.CalculationResult = CalculationResult.NoRun;
macroStabilityOutput.StabilityOutputItems = new List();
//this must be code when prepare is done
// try
// {
// string kernelMessage = macroStabilityCalculator.Run();
// ParseRunResult(kernelMessage, macroStabilityOutput, messages);
// macroStabilityOutput.CalculationResult = CalculationResult.Succeeded;
// }
// catch (Exception e)
// {
// macroStabilityOutput.CalculationResult = CalculationResult.UnexpectedError;
// messages.Add(new LogMessage(LogMessageType.Error, null, e.Message));
// }
// ToDo MWDAM-1356: setting input is not done yet. Temporary test code added.
// start of temporary test code
if (tmpPresumeRunSucceeds)
{
//presume run succeeded
MacroStabilityOutputItem macroStabilityOutputItem;
ParseRunResult("", out macroStabilityOutputItem, out messages);
macroStabilityOutput.CalculationResult = macroStabilityOutputItem.CalculationResult;
macroStabilityOutput.StabilityOutputItems.Add(macroStabilityOutputItem);
}
else
{
//presume run failed
macroStabilityOutput.CalculationResult = CalculationResult.UnexpectedError;
messages = new List();
messages.Add(new LogMessage(LogMessageType.Error, null, "run failed"));
}
// end of temporary test code
}
internal void ParseRunResult(string xmlRunResult, out MacroStabilityOutputItem macroStabilityOutputItem, out List messages)
{
messages = new List();
// ToDo MWDAM-1357: Parse the xml from the kernel, fill results and add message with type Error, Info or Warning
macroStabilityOutputItem = new MacroStabilityOutputItem();
// start of temporary test code
if (tmpPresumeRunSucceeds)
{
//presume run succeeded
macroStabilityOutputItem.StabilityModelType = MStabModelType.UpliftVan;
macroStabilityOutputItem.SafetyFactor = 1.357; // presume SafetyFactor in xml output is 1.357
macroStabilityOutputItem.CalculationResult = CalculationResult.Succeeded; // presume Succeeded in xml output is true
//presume there are no messages in xml output, but there might be info or warning messages
}
else
{
// ToDo MWDAM-1357: get an example of output that has a failed run with error messages
//presume run failed
macroStabilityOutputItem.StabilityModelType = MStabModelType.UpliftVan;
macroStabilityOutputItem.SafetyFactor = -1; // presume SafetyFactor in xml output is NaN. Set to -1?
macroStabilityOutputItem.CalculationResult = CalculationResult.RunFailed; // presume Succeeded in xml output is false
//when run failed there are probably also error messages in the xml output
messages.Add(new LogMessage(LogMessageType.Error, null, "Index was out of range. Must be non-negative and less than the size of the collection.\r\nParameter name: index"));
messages.Add(new LogMessage(LogMessageType.Error, null, "Fatale fout in Uplift-Van berekening"));
}
// end of temporary test code
}
///
/// Creates the calculator for stability based on kernel input.
///
public void CreateStabilityCalculator()
{
stabilityCalculator = new WTIStabilityCalculation();
}
///
/// 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)
{
ThrowWhenMacroStabilityDamKernelInputNull(damKernelInput);
MacroStabilityOutput macroStabilityOutput = kernelDataOutput as MacroStabilityOutput;
ThrowWhenMacroStabilityKernelOutputNull(macroStabilityOutput);
designResults = new List();
if (macroStabilityOutput.StabilityOutputItems.Count > 0)
{
var macroStabilityOutputItem = macroStabilityOutput.StabilityOutputItems[0];
if (macroStabilityOutputItem != null)
{
var designResult = NewDesignResult(damKernelInput, designScenario, macroStabilityOutputItem);
FillDesignResult(macroStabilityOutputItem, designResult);
designResult.StabilityDesignResults.NumberOfIterations = lastIterationIndex;
designResult.StabilityDesignResults.UpliftSituation = macroStabilityOutput.UpliftSituation;
designResults.Add(designResult);
}
}
}
private DesignResult NewDesignResult(DamKernelInput damKernelInput, DesignScenario designScenario,
MacroStabilityOutputItem macroStabilityOutputItem)
{
string soilProfile2DName = damKernelInput.SubSoilScenario.ToString();
var designResult = new DesignResult(damKernelInput.DamFailureMechanismeCalculationSpecification,
designScenario, damKernelInput.SubSoilScenario.SoilProfile1D, soilProfile2DName)
{
// initialize as failed
CalculationResult = CalculationResult.RunFailed
};
designResult.StabilityDesignResults = new StabilityDesignResults();
var stabilityDesignResults = new StabilityDesignResults();
stabilityDesignResults.RedesignedSurfaceLine = damKernelInput.Location.SurfaceLine;
designResult.ProfileName = soilProfile2DName;
designResult.StabilityDesignResults = stabilityDesignResults;
return designResult;
}
private static void FillDesignResult(MacroStabilityOutputItem macroStabilityOutputItem, DesignResult designResult)
{
designResult.CalculationResult = macroStabilityOutputItem.CalculationResult;
designResult.StabilityDesignResults.StabilityModelType = macroStabilityOutputItem.StabilityModelType;
if (designResult.CalculationResult == CalculationResult.Succeeded)
{
designResult.StabilityDesignResults.SafetyFactor = macroStabilityOutputItem.SafetyFactor;
}
}
///
/// Throws the when macro stability kernel input is not assigned.
///
/// The dam macro stability input.
///
public static void ThrowWhenMacroStabilityKernelInputNull(MacroStabilityInput macroStabilityInput)
{
if (macroStabilityInput == 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);
}
}
///
/// Throws the when macro stability calculator is not assigned.
///
/// The macro stability calculator.
///
public static void ThrowWhenMacroStabilityCalculatorNull(WTIStabilityCalculation macroStabilityCalculator)
{
if (macroStabilityCalculator == null)
{
throw new NoNullAllowedException(Resources.MacroStabilityKernelWrapper_NoDamKernelObjectDefinedForMacroStability);
}
}
///
/// 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 succesful
///
///
public bool EvaluateDesign(DamKernelInput damKernelInput, IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput,
out DesignAdvise designAdvise, out string evaluationMessage)
{
MacroStabilityInput macroStabilityInput = kernelDataInput as MacroStabilityInput;
MacroStabilityOutput macroStabilityOutput = kernelDataOutput as MacroStabilityOutput;
ThrowWhenMacroStabilityKernelInputNull(macroStabilityInput);
ThrowWhenMacroStabilityKernelOutputNull(macroStabilityOutput);
ThrowWhenMacroStabilityDamKernelInputNull(damKernelInput);
double fosRequired = damKernelInput.Location.ModelFactors.RequiredSafetyFactorStabilityInnerSlope.Value;
double fosAchieved = macroStabilityOutput.StabilityOutputItems[0].SafetyFactor;
//ToDo MWDAM-1357: not clear where the exitPointXCoordinate must come from
//double exitPointXCoordinate = macroStabilityOutput.StabilityOutputItems[0].CircleSurfacePointRightXCoordinate;
double exitPointXCoordinate = 1.123; // ToDo replace by value above. set a value for test.
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;
}
///
/// 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)
{
MacroStabilityInput macroStabilityInput = kernelDataInput as MacroStabilityInput;
ThrowWhenMacroStabilityKernelInputNull(macroStabilityInput);
lastIterationIndex = iterationIndex;
var location = damKernelInput.Location;
//ToDo MWDAM-1356: Not clear yet what parts are required for input. subSoilScenario, riverLevel and others might be necessary for CreateXmlInput
//MStabModelType model = MStabModelType.UpliftVan;
//var subSoilScenario = damKernelInput.SubSoilScenario;
//double riverLevel = damKernelInput.RiverLevelHigh;
EmbankmentDesignParameters embankmentDesignParametersForKernelInput;
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,
};
//ToDo MWDAM-? Use FailureMechanismParametersMStab or create new one?
FailureMechanismParametersMStab.EmbankmentDesignParameters = embankmentDesignParameters;
embankmentDesignParametersForKernelInput = null;
}
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;
embankmentDesignParametersForKernelInput = embankmentDesignParameters;
}
var xmlInput = CreateXmlInput(macroStabilityInput, embankmentDesignParametersForKernelInput);
var prepareResult = PrepareKernel(xmlInput);
if (prepareResult != PrepareResult.Successful)
{
throw new MacroStabilityException(Resources.MacroStabilityKernelWrapper_PrepareForMacroStabilityDidNotSucceed);
}
}
///
/// 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;
}
}
}
}