// 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.Linq;
using Deltares.DamEngine.Data.General;
using Deltares.DamEngine.Data.General.PlLines;
using Deltares.DamEngine.Data.Geometry;
using Deltares.DamEngine.Data.Geotechnics;
namespace Deltares.DamEngine.Calculators.Uplift
{
///
/// Determie uplift location and uplift factor
///
public class UpliftLocationDeterminator
{
public bool IsUseOvenDryUnitWeight { get; set; }
public PlLines PlLines { get; set; }
public SurfaceLine2 SurfaceLine { get; set; }
public SoilProfile1D SoilProfile { get; set; }
public SoilProfile2D SoilProfile2D { get; set; }
public string SoilGeometry2DName { get; set; }
public Soil DikeEmbankmentMaterial { get; set; }
// public SoilbaseDB SoilBaseDB { get; set; }
public SoilList SoilList { get; set; }
public double XSoilGeometry2DOrigin { get; set; }
///
/// Constructor
///
public UpliftLocationDeterminator()
{
IsUseOvenDryUnitWeight = false;
}
///
/// Get location nearest dike with and upliftfactor lower than required
///
/// location and upliftfactor
public UpliftLocationAndResult GetLocationAndResult(double upliftCriterion)
{
ThrowIfNoPlLinesDefined();
ThrowIfNoSurfaceLinDefined();
ThrowIfNoSoilProfileDefined();
return GetLocationInPolderNearestDikeWithUpliftFactorLowerThanRequired(upliftCriterion);
}
///
/// Get location nearest dike with and upliftfactor lower than required
///
/// location and upliftfactor
public UpliftLocationAndResult GetLocationInPolderNearestDikeWithUpliftFactorLowerThanRequired(double upliftCriterion)
{
ThrowIfNoPlLinesDefined();
ThrowIfNoSurfaceLinDefined();
ThrowIfNoSoilProfileDefined();
GeometryPoint startSurfacePoint = SurfaceLine.GetDikeToeInward();
IEnumerable relevantSurfacePointsList = from GeometryPoint point in SurfaceLine.Geometry.Points
where point.X >= startSurfacePoint.X
orderby point.X ascending
select point;
bool foundUpliftFactor = false;
UpliftLocationAndResult upliftLocationAndResult = null;
foreach (GeometryPoint surfacePoint in relevantSurfacePointsList)
{
upliftLocationAndResult = GetUpliftFactorAtPoint(surfacePoint);
if ((upliftLocationAndResult != null) && (upliftLocationAndResult.UpliftFactor < upliftCriterion))
{
foundUpliftFactor = true;
upliftLocationAndResult.X = surfacePoint.X;
upliftLocationAndResult.Z = surfacePoint.Z;
break;
}
}
return foundUpliftFactor ? upliftLocationAndResult : null;
}
///
/// Create upliftcalculator at given point
///
/// GeometryPoint for which to calculate upliftfactor
/// Top of layer where uplift occurs
/// location and upliftfactor
private UpliftCalculator CreateUpliftCalculator(GeometryPoint point, double topOfLayer, SoilProfile1D soilProfile)
{
PlLine phreaticLine = PlLines.Lines[PlLineType.Pl1];
return new UpliftCalculator
{
PhreaticLevel = phreaticLine.ZFromX(point.X),
SoilProfile = soilProfile,
TopOfLayerToBeEvaluated = topOfLayer,
SurfaceLevel = point.Z,
UnitWeightSoilEmbankment = (this.DikeEmbankmentMaterial == null) ? (double?) null : this.DikeEmbankmentMaterial.AbovePhreaticLevel,
IsUseOvenDryUnitWeight = this.IsUseOvenDryUnitWeight
};
}
///
/// Calculate upliftfactor for given point
///
///
/// location and upliftfactor
public UpliftLocationAndResult GetUpliftFactorAtPoint(GeometryPoint point)
{
SoilProfile1D soilProfileInCurrentPoint = GetSoilProfileBelowPoint(point.X);
double upliftFactorForInBetweenSandLayer = double.MaxValue;
if (soilProfileInCurrentPoint.InBetweenAquiferLayer != null)
{
// Check if inbetween sandlayer below surface
double topInBetweenSandLayer = soilProfileInCurrentPoint.InBetweenAquiferLayer.TopLevel;
if (topInBetweenSandLayer < point.Z)
{
// There is an aquitard above the aquifer, for which we can determine the uplift factor
UpliftCalculator upliftCalculatorForInBetweenSandLayer = CreateUpliftCalculator(point, topInBetweenSandLayer, soilProfileInCurrentPoint);
if ( (PlLines.Lines[PlLineType.Pl4] != null) && (PlLines.Lines[PlLineType.Pl4].Points.Count > 0 ))
upliftFactorForInBetweenSandLayer = upliftCalculatorForInBetweenSandLayer.CalculateUpliftFactor(PlLines.Lines[PlLineType.Pl4].ZFromX(point.X));
}
else
{
if (soilProfileInCurrentPoint.GetBottomLevel(soilProfileInCurrentPoint.InBetweenAquiferLayer) < point.Z)
{
// The surface cuts into the aquifer so the level to be evaluated is at surfacelevel
UpliftCalculator upliftCalculatorForInBetweenSandLayer = CreateUpliftCalculator(point, point.Z, soilProfileInCurrentPoint);
if ((PlLines.Lines[PlLineType.Pl4] != null) && (PlLines.Lines[PlLineType.Pl4].Points.Count > 0))
upliftFactorForInBetweenSandLayer = upliftCalculatorForInBetweenSandLayer.CalculateUpliftFactor(PlLines.Lines[PlLineType.Pl4].ZFromX(point.X));
}
}
}
double upliftFactorForBottomSandLayer = double.MaxValue;
if (soilProfileInCurrentPoint.BottomAquiferLayer != null)
{
// Check if bottom sandlayer below surface
double topBottomSandLayer = soilProfileInCurrentPoint.BottomAquiferLayer.TopLevel;
if (topBottomSandLayer < point.Z)
{
UpliftCalculator upliftCalculatorForBottomSandLayer = CreateUpliftCalculator(point, soilProfileInCurrentPoint.BottomAquiferLayer.TopLevel, soilProfileInCurrentPoint);
if ((PlLines.Lines[PlLineType.Pl3] != null) && (PlLines.Lines[PlLineType.Pl3].Points.Count > 0))
upliftFactorForBottomSandLayer = upliftCalculatorForBottomSandLayer.CalculateUpliftFactor(PlLines.Lines[PlLineType.Pl3].ZFromX(point.X));
}
else
{
if (soilProfileInCurrentPoint.GetBottomLevel(soilProfileInCurrentPoint.BottomAquiferLayer) < point.Z)
{
// The surface cuts into the aquifer so the level to be evaluated is at surfacelevel
UpliftCalculator upliftCalculatorForInBetweenSandLayer = CreateUpliftCalculator(point, point.Z, soilProfileInCurrentPoint);
if ((PlLines.Lines[PlLineType.Pl3] != null) && (PlLines.Lines[PlLineType.Pl3].Points.Count > 0))
upliftFactorForBottomSandLayer = upliftCalculatorForInBetweenSandLayer.CalculateUpliftFactor(PlLines.Lines[PlLineType.Pl3].ZFromX(point.X));
}
}
}
if((upliftFactorForBottomSandLayer == double.MaxValue) && (upliftFactorForInBetweenSandLayer == double.MaxValue))
return null;
if (upliftFactorForBottomSandLayer < upliftFactorForInBetweenSandLayer)
{
return new UpliftLocationAndResult(point, upliftFactorForBottomSandLayer, soilProfileInCurrentPoint.BottomAquiferLayer.Name);
}
else
{
return new UpliftLocationAndResult(point, upliftFactorForInBetweenSandLayer, soilProfileInCurrentPoint.InBetweenAquiferLayer.Name);
}
}
///
/// Determine location with lowest upliftfactor
///
/// location and upliftfactor
public UpliftLocationAndResult GetLocationAtWithLowestUpliftFactor()
{
double? lowestUpliftFactor = null;
ThrowIfNoPlLinesDefined();
ThrowIfNoSurfaceLinDefined();
ThrowIfNoSoilProfileDefined();
GeometryPoint startSurfacePoint = SurfaceLine.GetDikeToeInward();
IEnumerable relevantSurfacePointsList = from GeometryPoint point in SurfaceLine.Geometry.Points
where point.X >= startSurfacePoint.X
orderby point.X ascending
select point;
UpliftLocationAndResult upliftLocationAndResult = null;
UpliftLocationAndResult lowestUpliftLocationAndResult = null;
foreach (GeometryPoint surfacePoint in relevantSurfacePointsList)
{
upliftLocationAndResult = GetUpliftFactorAtPoint(surfacePoint);
if (upliftLocationAndResult != null)
{
if (!lowestUpliftFactor.HasValue || upliftLocationAndResult.UpliftFactor < lowestUpliftFactor)
{
lowestUpliftFactor = upliftLocationAndResult.UpliftFactor;
lowestUpliftLocationAndResult = upliftLocationAndResult;
}
}
}
return lowestUpliftLocationAndResult;
}
///
///
///
///
///
private SoilProfile1D GetSoilProfileBelowPoint(double xCoordinate)
{
if (this.SoilProfile != null)
{
var soilProfile1D = SoilProfileHelper.DetermineForSurfaceLineCorrected1DProfileAtX(SoilProfile, SurfaceLine, xCoordinate, DikeEmbankmentMaterial);
return soilProfile1D;
}
if (SoilProfile2D != null)
{
return SoilProfile2D.GetSoilProfile1D(xCoordinate);
}
// This possibly should become conversion from StixFile
throw new NotImplementedException(@"Using full 2D geometry (based on stix file) not yet available.");
}
///
/// Check on precondition
///
private void ThrowIfNoPlLinesDefined()
{
if (PlLines == null)
throw new UpliftLocationDeterminatorException("Required pllines not found");
}
///
/// Check on precondition
///
private void ThrowIfNoSurfaceLinDefined()
{
if (SurfaceLine == null)
throw new UpliftLocationDeterminatorException("Required surfaceLine line not found");
}
///
/// Check on precondition
///
private void ThrowIfNoSoilProfileDefined()
{
if (SoilProfile == null && (SoilGeometry2DName == null || SoilGeometry2DName == "") && SoilProfile2D == null)
throw new UpliftLocationDeterminatorException("Required soilProfile not found");
}
}
}