// Copyright (C) Stichting Deltares 2018. 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.KernelWrappers.Common;
using Deltares.DamEngine.Calculators.KernelWrappers.Interfaces;
using Deltares.DamEngine.Calculators.Properties;
using Deltares.DamEngine.Calculators.Uplift;
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.Calculation;
using Deltares.DamEngine.Data.Standard.Logging;
using Deltares.DamPiping.BlighCalculator;
namespace Deltares.DamEngine.Calculators.KernelWrappers.DamPipingBligh
{
///
/// Wrapper around Bligh piping kernel
///
///
public class DamPipingBlighKernelWrapper : IKernelWrapper
{
private const double defaultFluidisationGradient = 0.3;
private const double defaultMaxReturnValue = 90.0;
///
/// Create the kernel input.
///
/// The dam kernel input.
/// The number of the current iteration.
/// The kernel data input.
/// The kernel data output.
///
/// Result of the prepare
///
public PrepareResult Prepare(DamKernelInput damKernelInput, int iterationIndex, out IKernelDataInput kernelDataInput, out IKernelDataOutput kernelDataOutput)
{
var damPipingBlighOutput = new DamPipingBlighOutput()
{
CalculationResult = CalculationResult.NoRun,
FoSp = defaultMaxReturnValue
};
kernelDataOutput = damPipingBlighOutput;
if (damKernelInput.SubSoilScenario.SegmentFailureMechanismType == FailureMechanismSystemType.Piping)
{
var damPipingBlighInput = new DamPipingBlighInput();
var soilProfile1D = damKernelInput.SubSoilScenario.SoilProfile1D;
var location = damKernelInput.Location;
double waterLevel = damKernelInput.RiverLevelHigh;
UpliftSituation upliftSituation;
PlLines plLines = PlLinesHelper.CreatePlLinesForPiping(location, soilProfile1D, waterLevel, out upliftSituation);
EvaluateUpliftSituation(damKernelInput, out kernelDataInput, plLines, damPipingBlighInput, waterLevel, damPipingBlighOutput);
return PrepareResult.Successful;
}
kernelDataInput = null;
return PrepareResult.NotRelevant;
}
private static void EvaluateUpliftSituation(DamKernelInput damKernelInput, out IKernelDataInput kernelDataInput, PlLines plLines, DamPipingBlighInput damPipingBlighInput, double waterLevel, DamPipingBlighOutput damPipingBlighOutput)
{
const double upliftCriterionTolerance = 0.000000001;
SoilProfile1D soilProfile1D = damKernelInput.SubSoilScenario.SoilProfile1D;
SurfaceLine2 surfaceLine = damKernelInput.Location.SurfaceLine;
Location location = damKernelInput.Location;
UpliftSituation upliftSituation = new UpliftSituation();
UpliftLocationDeterminator 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.Value - upliftCriterionTolerance;
var upliftLocationAndResult = upliftLocationDeterminator.GetLocationAndResult(upliftCriterion);
upliftSituation.IsUplift = (upliftLocationAndResult != null);
double xEntry = surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtRiver).X;
double xExit = 0.0;
double surfaceLevel = 0.0;
double d70 = 0.0;
double dCoverLayer = 0.0;
double? upliftFactor = null;
if (upliftLocationAndResult != null)
{
xExit = upliftLocationAndResult.X;
surfaceLevel = surfaceLine.Geometry.GetZatX(upliftLocationAndResult.X);
SoilLayer1D heaveLayer = soilProfile1D.GetLayerWithName(upliftLocationAndResult.LayerWhereUpliftOccuresId);
d70 = Physics.FactorMeterToMicroMeter * heaveLayer.Soil.DiameterD70;
var topLevelAquifer = soilProfile1D.GetLayerWithName(upliftLocationAndResult.LayerWhereUpliftOccuresId).TopLevel;
dCoverLayer = DamPipingHelper.DetermineHeightCoverLayer(topLevelAquifer, surfaceLevel);
upliftFactor = upliftLocationAndResult.UpliftFactor;
}
double seepageLength = xExit - xEntry;
damPipingBlighInput.HRiver = waterLevel;
// 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)."
var referenceLevel = Math.Max(location.PolderLevel, surfaceLevel);
kernelDataInput = new DamPipingBlighInput()
{
HRiver = waterLevel,
HExit = referenceLevel,
Rc = defaultFluidisationGradient,
DTotal = dCoverLayer,
SeepageLength = seepageLength,
D70 = d70,
};
damPipingBlighOutput.ExitPointX = xExit;
damPipingBlighOutput.UpliftFactor = upliftFactor;
damPipingBlighOutput.UpliftSituation = upliftSituation;
}
///
/// Validates the kernel data input.
///
/// The kernel data input.
/// The kernel data output.
/// The messages.
///
/// Number of errors that prevent a calculation
///
public int Validate(IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, out List messages)
{
DamPipingBlighOutput damPipingBlighOutput = (DamPipingBlighOutput) kernelDataOutput;
var calculatorBligh = CreatePipingCalculatorBligh(kernelDataInput);
List kernelMessages = calculatorBligh.Validate();
messages = new List();
foreach (string stringMessage in kernelMessages)
{
messages.Add(new LogMessage()
{
Message = stringMessage,
MessageType = LogMessageType.Error
});
}
if (messages.Count > 0)
{
damPipingBlighOutput.CalculationResult = CalculationResult.InvalidInputData;
}
return messages.Count;
}
///
/// Executes the kernel.
///
/// The kernel data input.
/// The kernel data output.
/// The messages.
/// No input object defined for Bligh
public void Execute(IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, out List messages)
{
DamPipingBlighInput damPipingBlighInput = kernelDataInput as DamPipingBlighInput;
DamPipingBlighOutput damPipingBlighOutput = (DamPipingBlighOutput)kernelDataOutput;
ThrowWhenKernelInputNull(damPipingBlighInput);
ThrowWhenKernelOutputNull(damPipingBlighOutput);
PerformSingleCalculationBligh(out messages, damPipingBlighOutput, damPipingBlighInput);
}
private static void PerformSingleCalculationBligh(out List messages, DamPipingBlighOutput damPipingBlighOutput, DamPipingBlighInput damPipingBlighInput)
{
damPipingBlighOutput.CalculationResult = CalculationResult.NoRun;
damPipingBlighOutput.FoSp = defaultMaxReturnValue;
messages = new List();
try
{
if (damPipingBlighOutput.UpliftSituation.IsUplift)
{
var calculatorBligh = CreatePipingCalculatorBligh(damPipingBlighInput);
calculatorBligh.Calculate();
damPipingBlighOutput.FoSp = calculatorBligh.FoSp;
damPipingBlighOutput.Hc = calculatorBligh.Hc;
damPipingBlighOutput.CalculationResult = CalculationResult.Succeeded;
}
}
catch (Exception e)
{
damPipingBlighOutput.CalculationResult = CalculationResult.UnexpectedError;
messages.Add(new LogMessage(LogMessageType.Error, null, e.Message));
}
}
///
/// Creates the piping calculator bligh based on kernel input.
///
/// The kernel data input.
///
/// No input object defined for Bligh
private static PipingCalculatorBligh CreatePipingCalculatorBligh(IKernelDataInput kernelDataInput)
{
DamPipingBlighInput damPipingBlighInput = kernelDataInput as DamPipingBlighInput;
ThrowWhenKernelInputNull(damPipingBlighInput);
var calculator = new PipingCalculatorBligh
{
HRiver = damPipingBlighInput.HRiver,
HExit = damPipingBlighInput.HExit,
Rc = damPipingBlighInput.Rc,
DTotal = damPipingBlighInput.DTotal,
SeepageLength = damPipingBlighInput.SeepageLength,
D70 = damPipingBlighInput.D70
};
return calculator;
}
///
/// Fills the design results from the kernel output.
///
/// The dam kernel input.
/// The kernel data output.
/// The result message.
/// The design results.
/// No output object defined for Bligh
public void PostProcess(DamKernelInput damKernelInput, IKernelDataOutput kernelDataOutput, string resultMessage, out List designResults)
{
DamPipingBlighOutput damPipingBlighOutput = kernelDataOutput as DamPipingBlighOutput;
ThrowWhenDamKernelInputNull(damKernelInput);
ThrowWhenKernelOutputNull(damPipingBlighOutput);
designResults = new List();
var designResult = new DesignResult(damKernelInput.DamFailureMechanismeCalculationSpecification,
damKernelInput.DesignScenario, damKernelInput.SubSoilScenario.SoilProfile1D, null,
DamProjectCalculationSpecification.SelectedAnalysisType)
{
CalculationResult = damPipingBlighOutput.CalculationResult
};
var pipingDesignResults = new PipingDesignResults(PipingModelType.Bligh);
designResult.PipingDesignResults = pipingDesignResults;
pipingDesignResults.ResultMessage = resultMessage;
pipingDesignResults.BlighFactor = damPipingBlighOutput.FoSp;
pipingDesignResults.BlighHcritical = damPipingBlighOutput.Hc;
// TODO: for now this only works for NoAdaption of geometry; if adaption is enabled, the real redesigned surfaceline has to be assigned
pipingDesignResults.RedesignedSurfaceLine = damKernelInput.Location.SurfaceLine;
pipingDesignResults.UpliftSituation = damPipingBlighOutput.UpliftSituation;
pipingDesignResults.LocalExitPointX = damPipingBlighOutput.ExitPointX;
pipingDesignResults.UpliftFactor = damPipingBlighOutput.UpliftFactor;
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();
DamPipingBlighInput damPipingBlighInput = kernelDataInput as DamPipingBlighInput;
DamPipingBlighOutput damPipingBlighOutput = (DamPipingBlighOutput)kernelDataOutput;
ThrowWhenDamKernelInputNull(damKernelInput);
ThrowWhenKernelOutputNull(damPipingBlighOutput);
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.Value;
double upliftCriterion = location.UpliftCriterionPiping.Value;
// 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;
damPipingBlighInput.SeepageLength = xExit - xEntry;
var 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
damPipingBlighInput.DTotal = dCoverLayer;
double referenceLevel = Math.Max(location.PolderLevel, point.Z); // point.Z is surfacelevel
damPipingBlighInput.HExit = referenceLevel;
// Calculate the piping safety factor using the level of the given point
PerformSingleCalculationBligh(out messages, damPipingBlighOutput, damPipingBlighInput);
// If too low, then determine required height and length (from uplift)
if (damPipingBlighOutput.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 evaluation message.
///
/// if the design was succesful
///
///
public bool EvaluateDesign(DamKernelInput damKernelInput, IKernelDataInput kernelDataInput, IKernelDataOutput kernelDataOutput, out string evaluationMessage)
{
DamPipingBlighInput damPipingBlighInput = kernelDataInput as DamPipingBlighInput;
DamPipingBlighOutput damPipingBlighOutput = (DamPipingBlighOutput)kernelDataOutput;
ThrowWhenKernelInputNull(damPipingBlighInput);
ThrowWhenDamKernelInputNull(damKernelInput);
ThrowWhenKernelOutputNull(damPipingBlighOutput);
double fosRequiered = damKernelInput.Location.ModelFactors.RequiredSafetyFactorPiping.Value;
double fosAchieved = damPipingBlighOutput.FoSp;
evaluationMessage = String.Format(Resources.FactorAchievedVsFactorRequired, fosAchieved, fosRequiered);
return (fosAchieved >= fosRequiered);
}
private static void ThrowWhenKernelInputNull(DamPipingBlighInput damPipingBlighInput)
{
if (damPipingBlighInput == null)
{
throw new NoNullAllowedException(Resources.DamPipingBlighKernelWrapper_NoInputObjectDefinedForBligh);
}
}
private static void ThrowWhenKernelOutputNull(DamPipingBlighOutput damPipingBlighOutput)
{
if (damPipingBlighOutput == null)
{
throw new NoNullAllowedException(Resources.DamPipingBlighKernelWrapper_NoOutputObjectDefinedForBligh);
}
}
private static void ThrowWhenDamKernelInputNull(DamKernelInput damKernelInput)
{
if (damKernelInput == null)
{
throw new NoNullAllowedException(Resources.DamPipingBlighKernelWrapper_NoDamInputObjectDefinedForBligh);
}
}
}
}