// 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;
using Deltares.DamEngine.Data.Standard.Calculation;
using Deltares.DamEngine.Data.Standard.Logging;
using Deltares.MacroStability.CSharpWrapper;
using Deltares.MacroStability.CSharpWrapper.Output;
using Point2D = Deltares.DamEngine.Data.Geometry.Point2D;
using UpliftVanCalculationGrid = Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityCommon.UpliftVanCalculationGrid;
namespace Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityInwards;
public class MacroStabilityInwardsKernelWrapper : IKernelWrapper
{
private 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.In(SegmentFailureMechanismType.Stability,
SegmentFailureMechanismType.All))
{
try
{
bool isBishopUpliftVan = FailureMechanismParametersMStab.MStabParameters.Model == StabilityModelType.BishopUpliftVan;
StabilityModelType model = FailureMechanismParametersMStab.MStabParameters.Model;
if (isBishopUpliftVan)
{
// if current model is BishopUpliftVan then set to Bishop for proper name/path for input file
model = StabilityModelType.Bishop;
}
if (damKernelInput.OriginalSoilProfile2D != null)
{
// Clone the original soil profile for design iterations
damKernelInput.SubSoilScenario.SoilProfile2D = damKernelInput.OriginalSoilProfile2D.Clone();
}
MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(damKernelInput.SubSoilScenario, damKernelInput.Location.SurfaceLine,
damKernelInput.CurrentEmbankmentSoil);
if ((damKernelInput.OriginalSoilProfile2D == null) && (damKernelInput.SubSoilScenario.SoilProfileType == SoilProfileType.ProfileType2D))
{
// Clone the newly created 2D profile the original soil profile for design iterations
damKernelInput.OriginalSoilProfile2D = damKernelInput.SubSoilScenario.SoilProfile2D.Clone();
}
const bool useRiverLevelLow = false;
// Determine whether there is uplift
var upliftHelper = new UpliftHelper();
PlLines plLines = upliftHelper.DeterminePlLinesForStability(damKernelInput, useRiverLevelLow, out UpliftSituation upliftSituation);
macroStabilityOutput.UpliftSituation = upliftSituation;
Waternet waterNet = MacroStabilityCommonHelper.CreateWaternet(damKernelInput, plLines);
var fillMacroStabilityWrapperFromEngine = new FillMacroStabilityWrapperInputFromEngine
{
TrafficLoad = MacroStabilityCommonHelper.FillTrafficLoad(damKernelInput)
};
if (FailureMechanismParametersMStab.MStabParameters.Model == StabilityModelType.Bishop ||
FailureMechanismParametersMStab.MStabParameters.Model == StabilityModelType.BishopUpliftVan)
{
fillMacroStabilityWrapperFromEngine.BishopCalculationGrid = MacroStabilityCommonHelper.FillBishopCalculationGrid(damKernelInput);
}
if (FailureMechanismParametersMStab.MStabParameters.Model == StabilityModelType.UpliftVan ||
FailureMechanismParametersMStab.MStabParameters.Model == StabilityModelType.BishopUpliftVan)
{
if (!upliftSituation.IsUplift && !isBishopUpliftVan)
{
return PrepareResult.NotRelevant;
}
fillMacroStabilityWrapperFromEngine.UpliftVanCalculationGrid = FillUpliftVanCalculationGrid(damKernelInput, upliftSituation.XCoordinateLowestUpliftFactorPoint);
}
FailureMechanismParametersMStab.MStabParameters.Model = model;
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 = StabilityModelType.UpliftVan;
FailureMechanismParametersMStab.MStabParameters.Model = model; //C#wrapper hier vervangen door MAC direct
macroStabilityInput.Input = fillMacroStabilityWrapperFromEngine.CreateMacroStabilityInput(damKernelInput, FailureMechanismParametersMStab.MStabParameters, waterNet);
fileNameForSecondCalculation = MacroStabilityCommonHelper.GetStabilityInputFileName(damKernelInput, iterationIndex, FailureMechanismParametersMStab.MStabParameters.Model);
// reset model
FailureMechanismParametersMStab.MStabParameters.Model = StabilityModelType.BishopUpliftVan;
stabilityCalculatorForSecondCalculation = new Calculator(macroStabilityInput.Input);
return MacroStabilityCommonHelper.PrepareKernel(stabilityCalculatorForSecondCalculation, fileNameForSecondCalculation);
}
if (isBishopUpliftVan)
{
// reset model
FailureMechanismParametersMStab.MStabParameters.Model = StabilityModelType.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 == StabilityModelType.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);
if (designScenario.CalculationResult == CalculationResult.Succeeded)
{
designScenario.CalculationResult = macroStabilityOutput!.CalculationResult;
}
designResults = new List();
MacroStabilityOutputItem macroStabilityOutputItem = ProperMacroStabilityResultsItem(macroStabilityOutput, 0);
AddDesignResult(damKernelInput, designScenario, designResults, macroStabilityOutputItem, macroStabilityOutput);
bool isBishopUpliftVan = FailureMechanismParametersMStab.MStabParameters.Model == StabilityModelType.BishopUpliftVan;
if (isBishopUpliftVan)
{
macroStabilityOutputItem = ProperMacroStabilityResultsItem(macroStabilityOutput, 1);
AddDesignResult(damKernelInput, designScenario, designResults, macroStabilityOutputItem, macroStabilityOutput);
// If no uplift therefore no UpliftVan calculation was made. So add a fictive (empty) design result.
if (macroStabilityOutputItem == null && !macroStabilityOutput!.UpliftSituation.IsUplift)
{
AddDesignResultNoRun(damKernelInput, designScenario, designResults, macroStabilityOutput);
}
// Now add the overall result for Bishop/UpliftVan only if both results are available.
if (designResults.Count == 2)
{
int index;
// add worst result from Bishop/UpliftVan, but only if both succeeded.
if (designResults[0].CalculationResult == CalculationResult.Succeeded &&
designResults[1].CalculationResult == CalculationResult.Succeeded)
{
index = designResults[0].StabilityDesignResults.SafetyFactor <= designResults[1].StabilityDesignResults.SafetyFactor ? 0 : 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);
DesignResult overallResult = MacroStabilityCommonHelper.NewDesignResult(damKernelInput, designScenario);
MacroStabilityCommonHelper.FillDesignResult(macroStabilityOutputItem, overallResult, lastIterationIndex);
overallResult.StabilityDesignResults.UpliftSituation = macroStabilityOutput!.UpliftSituation;
overallResult.StabilityDesignResults.StabilityModelType = StabilityModelType.BishopUpliftVan;
if (macroStabilityOutputItem.CalculationResult != CalculationResult.Succeeded)
{
SoilGeometryProbability subSoilScenario = damKernelInput.SubSoilScenario;
overallResult.StabilityDesignResults.ResultMessage = designScenario.GetResultMessage(subSoilScenario.SoilProfile1D, subSoilScenario.SoilProfile2D);
}
designResults.Add(overallResult);
}
// If not both Bishop and Uplift-Van results are available, this is unexpected.
else
{
DesignResult overallResult = MacroStabilityCommonHelper.NewDesignResult(damKernelInput, designScenario);
overallResult.StabilityDesignResults.StabilityModelType = StabilityModelType.BishopUpliftVan;
overallResult.CalculationResult = CalculationResult.UnexpectedError;
SoilGeometryProbability subSoilScenario = damKernelInput.SubSoilScenario;
overallResult.StabilityDesignResults.ResultMessage = designScenario.GetResultMessage(subSoilScenario.SoilProfile1D, subSoilScenario.SoilProfile2D);
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, Point2D 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.CurrentScenario.RequiredSafetyFactorStabilityInnerSlope;
if (macroStabilityOutput != null)
{
MacroStabilityOutputItem leadingStabilityOutputItem = DetemineLeadingOutputItem(macroStabilityOutput.StabilityOutputItems);
// Abort if calculation failed
if ((leadingStabilityOutputItem == null) || (leadingStabilityOutputItem.CalculationResult != CalculationResult.Succeeded))
{
designAdvise = DesignAdvise.Abort;
evaluationMessage = "Stability calculation failed.";
return true;
}
// Check if the factor of safety is achieved
double fosAchieved = leadingStabilityOutputItem.SafetyFactor;
double exitPointXCoordinate = leadingStabilityOutputItem.CircleSurfacePointRightXCoordinate;
Point2D 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 static void AddDesignResultNoRun(DamKernelInput damKernelInput, DesignScenario designScenario, List designResults, MacroStabilityOutput macroStabilityOutput)
{
DesignResult designResultUpliftNoRun = MacroStabilityCommonHelper.NewDesignResult(damKernelInput, designScenario);
designResultUpliftNoRun.StabilityDesignResults.StabilityModelType = StabilityModelType.UpliftVan;
designResultUpliftNoRun.CalculationResult = CalculationResult.NoRun;
designResultUpliftNoRun.SafetyFactor = null;
designResultUpliftNoRun.StabilityDesignResults.UpliftSituation = macroStabilityOutput.UpliftSituation;
designResultUpliftNoRun.CalculationSubDir = designResults.Count == 1 ? designResults[0].CalculationSubDir.Replace(StabilityModelType.Bishop.ToString(), StabilityModelType.UpliftVan.ToString()) : "";
designResults.Add(designResultUpliftNoRun);
}
private void AddDesignResult(DamKernelInput damKernelInput, DesignScenario designScenario, List designResults, MacroStabilityOutputItem macroStabilityOutputItem, MacroStabilityOutput macroStabilityOutput)
{
if (macroStabilityOutputItem != null)
{
DesignResult designResult = MacroStabilityCommonHelper.NewDesignResult(damKernelInput, designScenario);
MacroStabilityCommonHelper.FillDesignResult(macroStabilityOutputItem, designResult, lastIterationIndex);
designResult.StabilityDesignResults.NumberOfIterations = lastIterationIndex;
designResult.StabilityDesignResults.UpliftSituation = macroStabilityOutput.UpliftSituation;
if ((macroStabilityOutputItem.CalculationResult != CalculationResult.Succeeded) || (designResult.CalculationResult != CalculationResult.Succeeded))
{
SoilGeometryProbability subSoilScenario = damKernelInput.SubSoilScenario;
designResult.StabilityDesignResults.ResultMessage = designScenario.GetResultMessage(subSoilScenario.SoilProfile1D, subSoilScenario.SoilProfile2D);
}
designResults.Add(designResult);
}
}
private static MacroStabilityOutputItem DetemineLeadingOutputItem(List stabilityOutputItems)
{
if (stabilityOutputItems.Count == 1)
{
return stabilityOutputItems[0];
}
if (stabilityOutputItems[0].CalculationResult == CalculationResult.Succeeded && stabilityOutputItems[1].CalculationResult == CalculationResult.Succeeded)
{
return stabilityOutputItems[0].SafetyFactor < stabilityOutputItems[1].SafetyFactor ? stabilityOutputItems[0] : stabilityOutputItems[1];
}
return null;
}
private static UpliftVanCalculationGrid FillUpliftVanCalculationGrid(DamKernelInput damKernelInput, double xCoordinateLastUpliftPoint)
{
SlipCircleDefinition slipCircleDefinition = damKernelInput.DamFailureMechanismeCalculationSpecification
.FailureMechanismParametersMStab.MStabParameters.SlipCircleDefinition;
slipCircleDefinition.XCoordinateLowestUpliftFactorPoint = xCoordinateLastUpliftPoint;
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;
}
}