// Copyright (C) Stichting Deltares 2023. 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.Data; using Deltares.DamEngine.Calculators.DikesDesign; using Deltares.DamEngine.Calculators.KernelWrappers.Common; using Deltares.DamEngine.Calculators.KernelWrappers.Interfaces; using Deltares.DamEngine.Calculators.Properties; using Deltares.DamEngine.Calculators.Uplift; using Deltares.DamEngine.Data.Design; using Deltares.DamEngine.Data.General; using Deltares.DamEngine.Data.General.PlLines; using Deltares.DamEngine.Data.General.Results; using Deltares.DamEngine.Data.Geometry; using Deltares.DamEngine.Data.Geotechnics; using Deltares.DamEngine.Data.Standard; using Deltares.DamEngine.Data.Standard.Calculation; using Deltares.DamEngine.Data.Standard.Logging; using Deltares.DamPiping.Sellmeijer4ForcesCalculator; namespace Deltares.DamEngine.Calculators.KernelWrappers.DamPipingSellmeijer4Forces; public class DamPipingSellmeijer4ForcesKernelWrapper : IKernelWrapper { private const double defaultFluidisationGradient = 0.3; private const double defaultMaxReturnValue = 90.0; /// /// Prepares the specified dam kernel input. /// /// The dam kernel input. /// The number of the currect iteration /// The kernel data input. /// The krenel data output /// /// Result of the prepare /// public PrepareResult Prepare(DamKernelInput damKernelInput, int iterationIndex, out IKernelDataInput kernelDataInput, out IKernelDataOutput kernelDataOutput) { var damPipingOutput = new DamPipingSellmeijer4ForcesOutput { FoSp = defaultMaxReturnValue }; kernelDataOutput = damPipingOutput; if (damKernelInput.SubSoilScenario.SegmentFailureMechanismType.Value.In(SegmentFailureMechanismType.Piping, SegmentFailureMechanismType.All)) { SoilProfile1D soilProfile1D = damKernelInput.SubSoilScenario.SoilProfile1D; Location location = damKernelInput.Location; double waterLevel = damKernelInput.RiverLevelHigh; PlLines plLines = PlLinesHelper.CreatePlLinesForPiping(damKernelInput.TimeStepDateTime, location, soilProfile1D, waterLevel); if (EvaluateUpliftSituation(damKernelInput, out kernelDataInput, plLines, waterLevel, damPipingOutput)) { return PrepareResult.Successful; } } kernelDataInput = null; return PrepareResult.NotRelevant; } /// /// Validates the specified kernel data input. /// /// The kernel data input. /// The kernel data output. /// The return messages. /// /// Number of errors that prevent a calculation /// public int Validate(IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, out List messages) { var damPipingOutput = (DamPipingSellmeijer4ForcesOutput) kernelDataOutput; PipingCalculatorSellmeijer4Forces calculator = CreatePipingCalculatorSellmeijer4Forces(kernelDataInput); List kernelMessages = calculator.Validate(); messages = new List(); foreach (string stringMessage in kernelMessages) { messages.Add(new LogMessage { Message = stringMessage, MessageType = LogMessageType.Error }); } if (messages.Count > 0) { damPipingOutput.CalculationResult = CalculationResult.InvalidInputData; } return messages.Count; } /// /// Executes the kernel. /// /// The kernel data input. /// The kernel data input. /// The return messages. /// No input object defined public void Execute(IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, out List messages) { var damPipingInput = kernelDataInput as DamPipingSellmeijer4ForcesInput; var damPipingOutput = (DamPipingSellmeijer4ForcesOutput) kernelDataOutput; ThrowWhenKernelInputNull(damPipingInput); ThrowWhenKernelOutputNull(damPipingOutput); PerformSingleCalculationSellmeijer4Forces(out messages, damPipingOutput, damPipingInput); } /// /// Fills the design results with the kernel output. /// /// The dam kernel input. /// The kernel data output. /// The design scenario. /// The result message. /// The design results. /// No input or output object defined public void PostProcess(DamKernelInput damKernelInput, IKernelDataOutput kernelDataOutput, DesignScenario designScenario, string resultMessage, out List designResults) { var damPipingOutput = kernelDataOutput as DamPipingSellmeijer4ForcesOutput; ThrowWhenDamKernelInputNull(damKernelInput); ThrowWhenKernelOutputNull(damPipingOutput); designResults = new List(); var designResult = new DesignResult(damKernelInput.DamFailureMechanismeCalculationSpecification, designScenario, damKernelInput.SubSoilScenario.SoilProfile1D, null) { CalculationResult = damPipingOutput.CalculationResult }; var pipingDesignResults = new PipingDesignResults(PipingModelType.Sellmeijer4Forces); designResult.PipingDesignResults = pipingDesignResults; pipingDesignResults.ResultMessage = resultMessage; pipingDesignResults.RedesignedSurfaceLine = damKernelInput.Location.SurfaceLine; pipingDesignResults.Sellmeijer4ForcesFactor = damPipingOutput.FoSp; pipingDesignResults.Sellmeijer4ForcesHcritical = damPipingOutput.Hc; pipingDesignResults.LocalExitPointX = damPipingOutput.ExitPointX; pipingDesignResults.UpliftFactor = damPipingOutput.UpliftFactor; pipingDesignResults.UpliftSituation = damPipingOutput.UpliftSituation; designResults.Add(designResult); } /// /// Calculates the design at point. /// /// The dam kernel input. /// The kernel data input. /// The kernel data output. /// The point. /// The messages. /// public ShoulderDesign CalculateDesignAtPoint(DamKernelInput damKernelInput, IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, GeometryPoint point, out List messages) { messages = new List(); var damPipingInput = kernelDataInput as DamPipingSellmeijer4ForcesInput; var damPipingOutput = (DamPipingSellmeijer4ForcesOutput) kernelDataOutput; ThrowWhenDamKernelInputNull(damKernelInput); ThrowWhenKernelOutputNull(damPipingOutput); Location location = damKernelInput.Location; SoilProfile1D soilProfile = damKernelInput.SubSoilScenario.SoilProfile1D; SurfaceLine2 surfaceLine = damKernelInput.Location.SurfaceLine; PlLines plLines; UpliftLocationAndResult upliftLocationAndResult; DamPipingHelper.DeterminePlLinesAndUpliftLocation(damKernelInput, point, out plLines, out upliftLocationAndResult); double requiredFoS = location.ModelFactors.RequiredSafetyFactorPiping; double upliftCriterion = location.UpliftCriterionPiping; // if there is no uplift, then there is no piping so return null if (upliftLocationAndResult != null) { double xEntry = surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtRiver).X; double xExit = upliftLocationAndResult.X; damPipingInput.SeepageLength = xExit - xEntry; double topLevelAquifer = soilProfile.GetLayerWithName(upliftLocationAndResult.LayerWhereUpliftOccuresId).TopLevel; // The following 2 parameters are dependent on the position of the point and have to be recalculated for the current point double dCoverLayer = DamPipingHelper.DetermineHeightCoverLayer(topLevelAquifer, point.Z); // point.Z is surfacelevel damPipingInput.DTotal = dCoverLayer; double referenceLevel = Math.Max(location.Scenarios[0].PolderLevel, point.Z); // point.Z is surfacelevel damPipingInput.HExit = referenceLevel; // Calculate the piping safety factor using the level of the given point PerformSingleCalculationSellmeijer4Forces(out messages, damPipingOutput, damPipingInput); // If too low, then determine required height and length (from uplift) if (damPipingOutput.FoSp < requiredFoS) { // Finally, determine the required shoulderheight double currentShoulderHeight = upliftLocationAndResult.Z - surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtPolder).Z; var shoulderDesign = new ShoulderDesign( upliftLocationAndResult.X - surfaceLine.GetDikeToeInward().X, currentShoulderHeight + ShoulderDesignHelper.CalculateExtraShoulderHeight(soilProfile, plLines, upliftLocationAndResult, upliftCriterion)); return shoulderDesign; } } return null; } /// /// Evaluates the design (current factor greater than desired factor) /// /// The dam kernel input. /// The kernel data input. /// The kernel data output. /// The design advise. /// The evaluation message. /// /// if the design was succesful /// public bool EvaluateDesign(DamKernelInput damKernelInput, IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, out DesignAdvise designAdvise, out string evaluationMessage) { var damPipingInput = kernelDataInput as DamPipingSellmeijer4ForcesInput; var damPipingOutput = (DamPipingSellmeijer4ForcesOutput) kernelDataOutput; ThrowWhenKernelInputNull(damPipingInput); ThrowWhenDamKernelInputNull(damKernelInput); ThrowWhenKernelOutputNull(damPipingOutput); double fosRequiered = damKernelInput.Location.ModelFactors.RequiredSafetyFactorPiping; double fosAchieved = damPipingOutput.FoSp; evaluationMessage = String.Format(Resources.FactorAchievedVsFactorRequired, fosAchieved, fosRequiered); designAdvise = DesignAdvise.None; return (fosAchieved >= fosRequiered); } public void PrepareDesign(IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, DamKernelInput damKernelInput, int iterationIndex, out EmbankmentDesignParameters embankmentDesignParameters) { throw new NotImplementedException(); } /// /// Gets the design strategy /// /// /// public DesignStrategy GetDesignStrategy(DamKernelInput damKernelInput) { return DesignStrategy.ShoulderPerPoint; } private bool EvaluateUpliftSituation(DamKernelInput damKernelInput, out IKernelDataInput kernelDataInput, PlLines plLines, double waterLevel, DamPipingSellmeijer4ForcesOutput damPipingOutput) { const double upliftCriterionTolerance = 0.000000001; SoilProfile1D soilProfile1D = damKernelInput.SubSoilScenario.SoilProfile1D; SurfaceLine2 surfaceLine = damKernelInput.Location.SurfaceLine; Location location = damKernelInput.Location; var upliftSituation = new UpliftSituation(); var upliftLocationDeterminator = new UpliftLocationDeterminator { PlLines = plLines, SoilProfile = soilProfile1D, SurfaceLine = surfaceLine, DikeEmbankmentMaterial = location.GetDikeEmbankmentSoil(), XSoilGeometry2DOrigin = location.XSoilGeometry2DOrigin }; // The tolerance is built in because after design it could be that the value that is designed to, is not reached by this margin double upliftCriterion = location.UpliftCriterionPiping - upliftCriterionTolerance; UpliftLocationAndResult upliftLocationAndResult = upliftLocationDeterminator.GetLocationAndResult(upliftCriterion); upliftSituation.IsUplift = (upliftLocationAndResult != null); double xEntry = surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtRiver).X; if (upliftLocationAndResult != null) { double xExit = upliftLocationAndResult.X; double surfaceLevel = surfaceLine.Geometry.GetZatX(upliftLocationAndResult.X); double topLevelAquifer = soilProfile1D.GetLayerWithName(upliftLocationAndResult.LayerWhereUpliftOccuresId).TopLevel; double dCoverLayer = DamPipingHelper.DetermineHeightCoverLayer(topLevelAquifer, surfaceLevel); SoilProfile1DAquiferLayerCombiner.AquiferLayerProperties aquiferLayer = SoilProfile1DAquiferLayerCombiner.CombineLayers(soilProfile1D, upliftLocationAndResult.LayerWhereUpliftOccuresId); double d70 = aquiferLayer.D70 * Physics.FactorMeterToMicroMeter; double aquiferHeight = aquiferLayer.Height; double permeabilityKx = aquiferLayer.PermeabilityKx; double? upliftFactor = upliftLocationAndResult.UpliftFactor; double seepageLength = xExit - xEntry; // Reference level is highest value of surfaceLevel or PolderLevel // Uit TR Zandmeevoerende wellen (1999): "Het verval dH is gelijk aan het verschil tussen buitenwaterstand (het ontwerppeil(OP)) // bij zeedijken en de maatgevende hoogwaterstand (MHW bij rivierdijken) en de waterstand binnendijks ter plaatse van het uittredepunt, // rekening houdend met zeespiegelrijzing etc.(zie paragraaf 3.7.2). In dien ter plaatse van het uittreepunt of de opbarstlocatie // geen vrije waterstand heerst kan gerekend worden met het maaiveldniveau, rekening houdend met eventuele maaiveld daling (zie paragraaf 3.7.2)." double referenceLevel = Math.Max(location.Scenarios[0].PolderLevel, surfaceLevel); Soil inBetweenAquiferlayerSoil = soilProfile1D.BottomAquiferLayer.Soil; if (soilProfile1D.InBetweenAquiferLayer != null) { inBetweenAquiferlayerSoil = soilProfile1D.InBetweenAquiferLayer.Soil; } kernelDataInput = new DamPipingSellmeijer4ForcesInput { HRiver = waterLevel, HExit = referenceLevel, Rc = defaultFluidisationGradient, DTotal = dCoverLayer, SeepageLength = seepageLength, // specific Sellmeijer 4 Forces WaterViscosity = Physics.WaterViscosity, WhitesConstant = inBetweenAquiferlayerSoil.WhitesConstant, BeddingAngle = inBetweenAquiferlayerSoil.BeddingAngle, D70 = d70, AquiferHeight = aquiferHeight, PermeabilityKx = permeabilityKx }; damPipingOutput.ExitPointX = xExit; damPipingOutput.UpliftFactor = upliftFactor; damPipingOutput.UpliftSituation = upliftSituation; return true; } kernelDataInput = new DamPipingSellmeijer4ForcesInput(); return false; } private void PerformSingleCalculationSellmeijer4Forces(out List messages, DamPipingSellmeijer4ForcesOutput damPipingOutput, DamPipingSellmeijer4ForcesInput damPipingInput) { damPipingOutput.CalculationResult = CalculationResult.NoRun; damPipingOutput.FoSp = defaultMaxReturnValue; messages = new List(); try { if (damPipingOutput.UpliftSituation.IsUplift) { PipingCalculatorSellmeijer4Forces calculator = CreatePipingCalculatorSellmeijer4Forces(damPipingInput); calculator.Calculate(); damPipingOutput.FoSp = calculator.FoSp; damPipingOutput.Hc = calculator.Hc; damPipingOutput.CalculationResult = CalculationResult.Succeeded; } } catch (Exception e) { damPipingOutput.CalculationResult = CalculationResult.UnexpectedError; messages.Add(new LogMessage(LogMessageType.Error, null, e.Message)); } } private PipingCalculatorSellmeijer4Forces CreatePipingCalculatorSellmeijer4Forces(IKernelDataInput kernelDataInput) { var damPipingInput = kernelDataInput as DamPipingSellmeijer4ForcesInput; ThrowWhenKernelInputNull(damPipingInput); var calculator = new PipingCalculatorSellmeijer4Forces { HRiver = damPipingInput.HRiver, HExit = damPipingInput.HExit, Rc = damPipingInput.Rc, DTotal = damPipingInput.DTotal, AquiferHeight = damPipingInput.AquiferHeight, SeepageLength = damPipingInput.SeepageLength, D70 = damPipingInput.D70, WhitesConstant = damPipingInput.WhitesConstant, BeddingAngle = damPipingInput.BeddingAngle, PermeabilityKx = damPipingInput.PermeabilityKx, WaterViscosity = damPipingInput.WaterViscosity }; return calculator; } private void ThrowWhenKernelInputNull(DamPipingSellmeijer4ForcesInput damPipingInput) { if (damPipingInput == null) { throw new NoNullAllowedException(Resources.DamPipingSellmeijer4ForcesKernelWrapper_NoInputObjectDefinedForSellmeijer4Forces); } } private void ThrowWhenKernelOutputNull(DamPipingSellmeijer4ForcesOutput damPipingOutput) { if (damPipingOutput == null) { throw new NoNullAllowedException(Resources.DamPipingSellmeijer4ForcesKernelWrapper_NoOutputObjectDefinedForSellmeijer4Forces); } } private void ThrowWhenDamKernelInputNull(DamKernelInput damKernelInput) { if (damKernelInput == null) { throw new NoNullAllowedException(Resources.DamPipingSellmeijer4ForcesKernelWrapper_NoInputObjectDefinedForSellmeijer4Forces); } } }