// 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.Linq;
using Deltares.DamEngine.Calculators.KernelWrappers.Common;
using Deltares.DamEngine.Calculators.KernelWrappers.Interfaces;
using Deltares.DamEngine.Calculators.KernelWrappers.MacroStabilityCommon;
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.DamEngine.Data.Standard.Validation;
namespace Deltares.DamEngine.Calculators.DikesDesign;
///
/// Design strategy: combined slope adaption and shoulder adaption
///
public class DesignCalculatorCombinedSlopeAndShoulderAdaption
{
private const double defaultMaxFractionOfDikeHeightForShoulderHeight = 0.67;
///
/// Performs the design calculation combined slope adaption and shoulder adaption.
///
/// The kernel wrapper.
/// The kernel data input.
/// The kernel data output.
/// The dam kernel input.
/// The calculation messages.
/// The design calculations.
///
///
///
public static void PerformDesignCalculationCombinedSlopeAdaptionAndShoulderAdaption(
IKernelWrapper kernelWrapper, IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput,
DamKernelInput damKernelInput,
List calculationMessages, List designCalculations)
{
List designResults;
Location location = damKernelInput.Location;
DesignScenario designScenario = location.CurrentScenario;
SoilGeometryProbability subSoilScenario = damKernelInput.SubSoilScenario;
const int maxRedesignIterations = 200;
var resultMessage = "";
int iterationIndex = -1;
designScenario.CalculationResult = CalculationResult.NoRun;
EmbankmentDesignParameters embankmentDesignParameters;
// Prepare the kernel for design
kernelWrapper.PrepareDesign(kernelDataInput, kernelDataOutput, damKernelInput, iterationIndex,
out embankmentDesignParameters);
SurfaceLine2 surfaceLine = designScenario.GetMostRecentSurfaceLine(subSoilScenario.SoilProfile1D,
subSoilScenario.SoilProfile2D);
if (surfaceLine == null)
{
surfaceLine = location.SurfaceLine;
}
surfaceLine = surfaceLine.FullDeepClone();
try
{
iterationIndex = 1;
bool isRedesignRequired;
location.SurfaceLine = surfaceLine;
location.AlignBoundaryPointsOfPl1LineWithAdaptedSurfaceLine(surfaceLine);
List locationCalculationMessages;
DesignCalculatorUtils.KernelCalculate(out kernelDataInput, kernelWrapper, out kernelDataOutput, damKernelInput,
iterationIndex, out locationCalculationMessages);
calculationMessages.AddRange(locationCalculationMessages);
DesignAdvise designAdvise;
string evaluationMessage;
isRedesignRequired = !kernelWrapper.EvaluateDesign(damKernelInput, kernelDataInput, kernelDataOutput,
out designAdvise, out evaluationMessage);
if (!isRedesignRequired && surfaceLine != null)
{
// Set redesigned surfaceline to original, so in case no redesign is needed, the original surfaceline will be returned
designScenario.SetRedesignedSurfaceLine(subSoilScenario.SoilProfile1D, subSoilScenario.SoilProfile2D, surfaceLine);
}
else
{
double maxFractionOfDikeHeightForShoulderHeight = location.UseNewMaxHeightShoulderAsFraction ? location.NewMaxHeightShoulderAsFraction : defaultMaxFractionOfDikeHeightForShoulderHeight;
double maxShoulderLevel = DesignCalculatorUtils.CalculateMaximumShoulderLevel(surfaceLine,
maxFractionOfDikeHeightForShoulderHeight);
damKernelInput.CurrentEmbankmentSoil = location.GetShoulderEmbankmentSoil();
while (isRedesignRequired && surfaceLine != null)
{
// Always start with the original soil profile
damKernelInput.SubSoilScenario.SoilProfile2D = damKernelInput.OriginalSoilProfile2D;
iterationIndex++;
DesignCalculatorUtils.ThrowWhenMaxIterationsExceeded(iterationIndex, maxRedesignIterations);
GeometryPoint limitPointForShoulderDesign = surfaceLine.GetLimitPointForShoulderDesign();
if (designAdvise == DesignAdvise.ShoulderInwards)
{
// If exit point of circle is after the limitPointForShoulderDesign then enlarge the shoulder
// Determine new width and height for shoulder
double shoulderHeight;
double shoulderLength;
DesignCalculatorUtils.DetermineNewShoulderLengthAndHeight(location.StabilityShoulderGrowDeltaX,
location.StabilityShoulderGrowSlope,
surfaceLine, limitPointForShoulderDesign,
out shoulderHeight, out shoulderLength);
// Create new shoulder
var surfaceLineShoulderAdapter = new SurfaceLineShoulderAdapter(surfaceLine, location,
designScenario.PolderLevel);
surfaceLineShoulderAdapter.MaxShoulderLevel = maxShoulderLevel;
surfaceLine = surfaceLineShoulderAdapter.ConstructNewSurfaceLine(shoulderLength, shoulderHeight, false);
ValidationResult validationError = surfaceLine.Validate().FirstOrDefault(vr => vr.MessageType == ValidationResultType.Error);
if (validationError != null)
{
throw new SurfaceLineException(validationError.Text);
}
designScenario.SetRedesignedSurfaceLine(subSoilScenario.SoilProfile1D, subSoilScenario.SoilProfile2D,
surfaceLine);
}
else if (designAdvise == DesignAdvise.SlopeInwards)
{
// If exit point of circle is in the slope (inward) of the dike or the top of the shoulder then adapt slope
var surfaceLineSlopeAdapter = new SurfaceLineSlopeAdapter(surfaceLine, location,
designScenario.PolderLevel);
surfaceLine = surfaceLineSlopeAdapter.ConstructNewSurfaceLine(location.StabilitySlopeAdaptionDeltaX);
ValidationResult validationError = surfaceLine.Validate().FirstOrDefault(vr => vr.MessageType == ValidationResultType.Error);
if (validationError != null)
{
throw new SurfaceLineException(validationError.Text);
}
designScenario.SetRedesignedSurfaceLine(subSoilScenario.SoilProfile1D, subSoilScenario.SoilProfile2D,
surfaceLine);
}
else
{
throw new NotImplementedException();
}
// Calculate again
location.AlignBoundaryPointsOfPl1LineWithAdaptedSurfaceLine(surfaceLine);
location.SurfaceLine = surfaceLine;
kernelWrapper.PrepareDesign(kernelDataInput, kernelDataOutput, damKernelInput, iterationIndex, out embankmentDesignParameters);
if (subSoilScenario.SoilProfileType == SoilProfileType.ProfileType2D)
{
Soil embankmentSoil =
location.SoilList.GetSoilByName(embankmentDesignParameters.EmbankmentMaterialname);
subSoilScenario.SoilProfile2D =
MacroStabilityCommonHelper.CombineSoilProfileWithSurfaceLine(subSoilScenario, surfaceLine, embankmentSoil);
}
DesignCalculatorUtils.KernelCalculate(out kernelDataInput, kernelWrapper, out kernelDataOutput, damKernelInput,
iterationIndex, out locationCalculationMessages);
calculationMessages.AddRange(locationCalculationMessages);
isRedesignRequired = !kernelWrapper.EvaluateDesign(damKernelInput, kernelDataInput, kernelDataOutput,
out designAdvise, out evaluationMessage);
}
}
calculationMessages.AddRange(locationCalculationMessages);
designScenario.CalculationResult = CalculationResult.Succeeded;
designScenario.SetResultMessage(subSoilScenario.SoilProfile1D, subSoilScenario.SoilProfile2D, "Succes");
kernelWrapper.PostProcess(damKernelInput, kernelDataOutput, designScenario, resultMessage, out designResults);
designCalculations.AddRange(designResults);
}
catch (Exception exception)
{
string errorMessage = exception.Message;
Exception innerException = exception.InnerException;
while (innerException != null)
{
errorMessage = errorMessage + ";" + innerException.Message;
innerException = innerException.InnerException;
}
designScenario.SetResultMessage(subSoilScenario.SoilProfile1D, subSoilScenario.SoilProfile2D, errorMessage);
designScenario.CalculationResult = CalculationResult.RunFailed;
// Redesign not succesful, so no redesigned surfaceline will be returned
designScenario.SetRedesignedSurfaceLine(subSoilScenario.SoilProfile1D, subSoilScenario.SoilProfile2D, null);
kernelWrapper.PostProcess(damKernelInput, kernelDataOutput, designScenario, errorMessage, out designResults);
designCalculations.AddRange(designResults);
throw new DesignCalculatorException(Resources.DesignUnsuccessful + " " + errorMessage);
}
}
}