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