// 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); } } } }