// Copyright (C) Stichting Deltares 2025. 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; using Deltares.DamEngine.Data.Standard; namespace Deltares.DamEngine.Calculators.Uplift; /// /// Determie uplift location and uplift factor /// public class UpliftLocationDeterminator { public PlLines PlLines { get; set; } public SurfaceLine2 SurfaceLine { get; set; } public SoilProfile1D SoilProfile { get; set; } public SoilProfile2D SoilProfile2D { get; set; } public Soil DikeEmbankmentMaterial { get; set; } // public SoilbaseDB SoilBaseDB { get; set; } public SoilList SoilList { get; set; } /// /// 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(); Point2D startSurfacePoint = SurfaceLine.GetDikeToeInward(); IEnumerable relevantSurfacePointsList = from Point2D point in SurfaceLine.Geometry.Points where point.X >= startSurfacePoint.X orderby point.X select point; var foundUpliftFactor = false; UpliftLocationAndResult upliftLocationAndResult = null; foreach (Point2D 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; } /// /// Calculate upliftfactor for given point /// /// /// location and upliftfactor public UpliftLocationAndResult GetUpliftFactorAtPoint(Point2D point) { const double toleranceAlmostEqual = 1e-09; SoilProfile1D soilProfileInCurrentPoint = GetSoilProfileBelowPoint(point.X); var 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)); } } } } var 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.IsNearEqual(double.MaxValue, toleranceAlmostEqual) && upliftFactorForInBetweenSandLayer.IsNearEqual(double.MaxValue, toleranceAlmostEqual)) { return null; } if (upliftFactorForBottomSandLayer < upliftFactorForInBetweenSandLayer) { return new UpliftLocationAndResult(point, upliftFactorForBottomSandLayer, soilProfileInCurrentPoint.BottomAquiferLayer.Name); } return new UpliftLocationAndResult(point, upliftFactorForInBetweenSandLayer, soilProfileInCurrentPoint.InBetweenAquiferLayer.Name); } /// /// Determine location with the lowest uplift factor /// /// location and uplift factor public UpliftLocationAndResult GetLocationWithLowestUpliftFactor() { double? lowestUpliftFactor = null; ThrowIfNoPlLinesDefined(); ThrowIfNoSurfaceLinDefined(); ThrowIfNoSoilProfileDefined(); Point2D startSurfacePoint = SurfaceLine.GetDikeToeInward(); IEnumerable relevantSurfacePointsList = from Point2D point in SurfaceLine.Geometry.Points where point.X >= startSurfacePoint.X orderby point.X select point; UpliftLocationAndResult lowestUpliftLocationAndResult = null; foreach (Point2D surfacePoint in relevantSurfacePointsList) { UpliftLocationAndResult upliftLocationAndResult = GetUpliftFactorAtPoint(surfacePoint); if (upliftLocationAndResult != null) { if (!lowestUpliftFactor.HasValue || upliftLocationAndResult.UpliftFactor < lowestUpliftFactor) { lowestUpliftFactor = upliftLocationAndResult.UpliftFactor; lowestUpliftLocationAndResult = upliftLocationAndResult; } } } return lowestUpliftLocationAndResult; } /// /// Create upliftcalculator at given point /// /// Point2D for which to calculate upliftfactor /// Top of layer where uplift occurs /// location and upliftfactor private UpliftCalculator CreateUpliftCalculator(Point2D 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 = (DikeEmbankmentMaterial == null) ? null : DikeEmbankmentMaterial.AbovePhreaticLevel }; } /// /// /// /// /// private SoilProfile1D GetSoilProfileBelowPoint(double xCoordinate) { if (SoilProfile != null) { SoilProfile1D 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 && SoilProfile2D == null) { throw new UpliftLocationDeterminatorException("Required soilProfile not found"); } } }