// Copyright (C) Stichting Deltares 2018. 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.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 design scenario.
/// The calculation messages.
/// The design calculations.
///
///
///
public static void PerformDesignCalculationCombinedSlopeAdaptionAndShoulderAdaption(
IKernelWrapper kernelWrapper, IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput,
DamKernelInput damKernelInput, DesignScenario designScenario,
List calculationMessages, List designCalculations)
{
List designResults;
Location location = damKernelInput.Location;
SoilGeometryProbability subSoilScenario = damKernelInput.SubSoilScenario;
const int maxRedesignIterations = 200;
string resultMessage = "";
int iterationIndex = -1;
designScenario.CalculationResult = CalculationResult.NoRun;
// Create the file with the initial geometry to be used to determine which layers have to be defined as dike embankment material
EmbankmentDesignParameters embankmentDesignParameters;
kernelWrapper.PrepareDesign(kernelDataInput, kernelDataOutput, damKernelInput, iterationIndex, out embankmentDesignParameters);
SurfaceLine2 surfaceLine = designScenario.GetMostRecentSurfaceLine(subSoilScenario.SoilProfile1D, subSoilScenario.StiFileName).FullDeepClone();
// During the calculation the location.SurfaceLine is adapted; so store a copy to restore it after the calculation
SurfaceLine2 orgLocationSurfaceLine = location.SurfaceLine.FullDeepClone();
try
{
iterationIndex = 1;
bool isRedesignRequired;
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.StiFileName, surfaceLine);
}
else
{
double maxFractionOfDikeHeightForShoulderHeight = location.UseNewMaxHeightShoulderAsFraction ?
location.NewMaxHeightShoulderAsFraction : defaultMaxFractionOfDikeHeightForShoulderHeight;
double maxShoulderLevel = DesignCalculatorUtils.CalculateMaximumShoulderLevel(surfaceLine,
maxFractionOfDikeHeightForShoulderHeight);
while (isRedesignRequired && surfaceLine != null)
{
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);
surfaceLineShoulderAdapter.MaxShoulderLevel = maxShoulderLevel;
surfaceLine = surfaceLineShoulderAdapter.ConstructNewSurfaceLine(shoulderLength, shoulderHeight, false);
var validationError = surfaceLine.Validate().FirstOrDefault(vr => vr.MessageType == ValidationResultType.Error);
if (validationError != null)
{
throw new SurfaceLineException(validationError.Text);
}
designScenario.SetRedesignedSurfaceLine(subSoilScenario.SoilProfile1D, subSoilScenario.StiFileName, 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);
surfaceLine = surfaceLineSlopeAdapter.ConstructNewSurfaceLine(location.StabilitySlopeAdaptionDeltaX);
var validationError = surfaceLine.Validate().FirstOrDefault(vr => vr.MessageType == ValidationResultType.Error);
if (validationError != null)
{
throw new SurfaceLineException(validationError.Text);
}
designScenario.SetRedesignedSurfaceLine(subSoilScenario.SoilProfile1D, subSoilScenario.StiFileName, surfaceLine);
}
else
{
throw new NotImplementedException();
}
// Calculate again
designScenario.Location.AlignBoundaryPointsOfPl1LineWithAdaptedSurfaceLine(surfaceLine);
damKernelInput.Location.SurfaceLine = surfaceLine;
kernelWrapper.PrepareDesign(kernelDataInput, kernelDataOutput, damKernelInput, iterationIndex, out embankmentDesignParameters);
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.StiFileName, "Succes");
kernelWrapper.PostProcess(damKernelInput, kernelDataOutput, designScenario, resultMessage, out designResults);
}
catch (Exception exception)
{
string errorMessage = "FAIL: " + exception.Message;
Exception innerException = exception.InnerException;
while (innerException != null)
{
errorMessage = errorMessage + ";" + innerException.Message;
innerException = innerException.InnerException;
}
designScenario.SetResultMessage(subSoilScenario.SoilProfile1D, subSoilScenario.StiFileName, errorMessage);
// Add message to log too for output tab.
errorMessage = location.Name + ", scenario " + designScenario.LocationScenarioID + ", " + subSoilScenario.ToString() + " " + errorMessage;
// stabilityCalculator.ErrorMessages.Add(new LogMessage(LogMessageType.Error, this, errorMessage));
designScenario.CalculationResult = CalculationResult.RunFailed;
// Redesign not succesful, so no redesigned surfaceline will be returned
designScenario.SetRedesignedSurfaceLine(subSoilScenario.SoilProfile1D, subSoilScenario.StiFileName, null);
kernelWrapper.PostProcess(damKernelInput, kernelDataOutput, designScenario, errorMessage, out designResults);
}
finally
{
// Restore the original surfaceline
location.SurfaceLine = orgLocationSurfaceLine.FullDeepClone();
}
designCalculations.AddRange(designResults);
}
}
}