// Copyright (C) Stichting Deltares 2021. 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: 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; EmbankmentDesignParameters embankmentDesignParameters; // Prepare the kernel for design kernelWrapper.PrepareDesign(kernelDataInput, kernelDataOutput, damKernelInput, iterationIndex, out embankmentDesignParameters); SurfaceLine2 surfaceLine = designScenario.GetMostRecentSurfaceLine(subSoilScenario.SoilProfile1D, subSoilScenario.SoilProfile2D).FullDeepClone(); try { iterationIndex = 1; bool isRedesignRequired; damKernelInput.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); 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, designScenario.PolderLevel); 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.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); var 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 designScenario.Location.AlignBoundaryPointsOfPl1LineWithAdaptedSurfaceLine(surfaceLine); damKernelInput.Location.SurfaceLine = surfaceLine; kernelWrapper.PrepareDesign(kernelDataInput, kernelDataOutput, damKernelInput, iterationIndex, out embankmentDesignParameters); if (damKernelInput.SubSoilScenario.SoilProfileType == SoilProfileType.ProfileType2D) { var embankmentSoil = damKernelInput.Location.SoilList.GetSoilByName(embankmentDesignParameters .EmbankmentMaterialname); damKernelInput.SubSoilScenario.SoilProfile2D = AdaptProfile2DToNewSurfaceLine(damKernelInput.SubSoilScenario.SoilProfile2D, 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); } } private static SoilProfile2D AdaptProfile2DToNewSurfaceLine(SoilProfile2D soilProfile2D, SurfaceLine2 surfaceLine, Soil dikeEmbankmentSoil) { var topX = surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtPolder).X; var soilProfile1D = soilProfile2D.GetSoilProfile1D(topX); var soilSurfaceProfile = new SoilSurfaceProfile { SoilProfile = soilProfile1D, SurfaceLine2 = surfaceLine, Name = soilProfile2D.Name, DikeEmbankmentMaterial = dikeEmbankmentSoil }; // Convert the soilSurfaceProfile to a SoilProfile2D to be able to edit it properly. var soilProfile2DNew = soilSurfaceProfile.ConvertToSoilProfile2D(); var numberOfCommonSurface = Math.Min(soilProfile2DNew.Surfaces.Count, soilProfile2D.Surfaces.Count); for (int i = 0; i < numberOfCommonSurface; i++) { soilProfile2DNew.Surfaces[i].WaterpressureInterpolationModel = soilProfile2D.Surfaces[i].WaterpressureInterpolationModel; } for (int i = numberOfCommonSurface; i < soilProfile2DNew.Surfaces.Count; i++) { soilProfile2DNew.Surfaces[i].WaterpressureInterpolationModel = WaterpressureInterpolationModel.Automatic; } return soilProfile2DNew; } } }