// 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.Linq;
using Deltares.DamEngine.Calculators.KernelWrappers.Common;
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.DamEngine.Data.Standard.Validation;
namespace Deltares.DamEngine.Calculators.DikesDesign
{
///
/// Design strategy: first slope adaption, then shoulder adaption
///
public class DesignCalculatorFirstSlopeAdaptionThenShoulderAdaption
{
private const double defaultMaxFractionOfDikeHeightForShoulderHeight = 0.67;
///
/// Performs the design calculation with strategy: first slope adaption then 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 PerformDesignCalculationFirstSlopeAdaptionThenShoulderAdaption(IKernelWrapper kernelWrapper,
IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, DamKernelInput damKernelInput,
DesignScenario designScenario, List calculationMessages, List designCalculations)
{
Location location = damKernelInput.Location;
SoilGeometryProbability subSoilScenario = damKernelInput.SubSoilScenario;
const int maxRedesignIterations = 200;
string resultMessage = "";
List designResults;
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.StiFileName).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 = designScenario.Location.UseNewMaxHeightShoulderAsFraction ?
designScenario.Location.NewMaxHeightShoulderAsFraction : defaultMaxFractionOfDikeHeightForShoulderHeight;
double maxShoulderLevel = DesignCalculatorUtils.CalculateMaximumShoulderLevel(surfaceLine, maxFractionOfDikeHeightForShoulderHeight);
// First slope adaption
double startCoTangent = location.SlopeAdaptionStartCotangent;
double endCoTangent = location.SlopeAdaptionEndCotangent;
double stepCoTangent = location.SlopeAdaptionStepCotangent;
var orgCotangent = surfaceLine.GetCotangentOfInnerSlope();
double coTangent = startCoTangent;
double currentCoTangent = orgCotangent;
// Find out for which cotangent we want to start designing
while (coTangent <= orgCotangent)
{
coTangent += stepCoTangent;
}
// Design for slope adaption
while (isRedesignRequired && (coTangent < endCoTangent))
{
iterationIndex++;
DesignCalculatorUtils.ThrowWhenMaxIterationsExceeded(iterationIndex, maxRedesignIterations);
var surfaceLineSlopeAdapter = new SurfaceLineSlopeAdapter(surfaceLine, location);
// The parameter for ConstructNewSurfaceLineBySlope is the tangent of the slope, so use reciproce value
surfaceLine = surfaceLineSlopeAdapter.ConstructNewSurfaceLineBySlope(1 / coTangent);
currentCoTangent = coTangent;
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);
// Recalculate new surfaceline
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);
coTangent += stepCoTangent;
}
// Then shoulder adaption
while (isRedesignRequired && surfaceLine != null)
{
iterationIndex++;
DesignCalculatorUtils.ThrowWhenMaxIterationsExceeded(iterationIndex, maxRedesignIterations);
// Determine new width and height for shoulder
double shoulderHeight;
double shoulderLength;
GeometryPoint limitPointForShoulderDesign = surfaceLine.GetLimitPointForShoulderDesign();
DesignCalculatorUtils.DetermineNewShoulderLengthAndHeight(designScenario.Location.StabilityShoulderGrowDeltaX,
designScenario.Location.StabilityShoulderGrowSlope, surfaceLine, limitPointForShoulderDesign,
out shoulderHeight, out shoulderLength);
// Create new shoulder
var surfaceLineShoulderAdapter = new SurfaceLineShoulderAdapter(surfaceLine, designScenario.Location);
surfaceLineShoulderAdapter.MaxShoulderLevel = maxShoulderLevel;
surfaceLineShoulderAdapter.SlopeOfNewShoulder = currentCoTangent;
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);
// Recalculate new surfaceline
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);
}
}
designScenario.SetResultMessage(subSoilScenario.SoilProfile1D, subSoilScenario.StiFileName, "Succes");
designScenario.CalculationResult = CalculationResult.Succeeded;
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.StiFileName, 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);
designCalculations.AddRange(designResults);
throw new DesignCalculatorException(Resources.DesignUnsuccessful + " " + errorMessage);
}
}
}
}