// Copyright (C) Stichting Deltares 2017. All rights reserved.
//
// This file is part of Ringtoets.
//
// Ringtoets is free software: you can redistribute it and/or modify
// it under the terms of the GNU 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 General Public License for more details.
//
// You should have received a copy of the GNU 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 Core.Common.Base.Data;
using Ringtoets.Common.Data.Exceptions;
using Ringtoets.Common.Data.Probabilistics;
using Ringtoets.Piping.Data.SoilProfile;
using Ringtoets.Piping.InputParameterCalculation;
using Ringtoets.Piping.Primitives;
namespace Ringtoets.Piping.Data
{
///
/// Class responsible for calculating the derived piping input.
///
public static class DerivedPipingInput
{
///
/// Gets the piezometric head at the exit point.
/// [m]
///
/// The input to calculate the derived piping input for.
/// The assessment level at stake.
/// Thrown when is null.
/// Returns the corresponding derived input value.
public static RoundedDouble GetPiezometricHeadExit(PipingInput input, RoundedDouble assessmentLevel)
{
if (input == null)
{
throw new ArgumentNullException(nameof(input));
}
RoundedDouble dampingFactorExit = PipingSemiProbabilisticDesignVariableFactory.GetDampingFactorExit(input).GetDesignValue();
RoundedDouble phreaticLevelExit = PipingSemiProbabilisticDesignVariableFactory.GetPhreaticLevelExit(input).GetDesignValue();
return new RoundedDouble(2, InputParameterCalculationService.CalculatePiezometricHeadAtExit(assessmentLevel,
dampingFactorExit,
phreaticLevelExit));
}
///
/// Gets the horizontal distance between entry and exit point.
/// [m]
///
/// The input to calculate the derived piping input for.
/// Thrown when is null.
/// Returns the corresponding derived input value.
public static VariationCoefficientLogNormalDistribution GetSeepageLength(PipingInput input)
{
if (input == null)
{
throw new ArgumentNullException(nameof(input));
}
double seepageLengthMean = input.ExitPointL - input.EntryPointL;
return new VariationCoefficientLogNormalDistribution(2)
{
Mean = (RoundedDouble) seepageLengthMean,
CoefficientOfVariation = (RoundedDouble) 0.1
};
}
///
/// Gets the total thickness of the coverage layers at the exit point.
/// [m]
///
/// The input to calculate the derived piping input for.
/// Thrown when is null.
/// Returns the corresponding derived input value.
public static LogNormalDistribution GetThicknessCoverageLayer(PipingInput input)
{
if (input == null)
{
throw new ArgumentNullException(nameof(input));
}
var thicknessCoverageLayer = new LogNormalDistribution(2)
{
Mean = RoundedDouble.NaN,
StandardDeviation = (RoundedDouble) 0.5
};
UpdateThicknessCoverageLayerMean(input, thicknessCoverageLayer);
return thicknessCoverageLayer;
}
///
/// Gets the effective thickness of the coverage layers at the exit point.
/// [m]
///
/// The input to calculate the derived piping input for.
/// Thrown when is null.
/// Returns the corresponding derived input value.
public static LogNormalDistribution GetEffectiveThicknessCoverageLayer(PipingInput input)
{
if (input == null)
{
throw new ArgumentNullException(nameof(input));
}
var thicknessCoverageLayer = new LogNormalDistribution(2)
{
Mean = RoundedDouble.NaN,
StandardDeviation = (RoundedDouble) 0.5
};
UpdateEffectiveThicknessCoverageLayerMean(input, thicknessCoverageLayer);
return thicknessCoverageLayer;
}
///
/// Gets the total thickness of the aquifer layers at the exit point.
/// [m]
///
/// The input to calculate the derived piping input for.
/// Thrown when is null.
/// Returns the corresponding derived input value.
public static LogNormalDistribution GetThicknessAquiferLayer(PipingInput input)
{
if (input == null)
{
throw new ArgumentNullException(nameof(input));
}
var thicknessAquiferLayer = new LogNormalDistribution(2)
{
Mean = RoundedDouble.NaN,
StandardDeviation = (RoundedDouble) 0.5
};
UpdateThicknessAquiferLayerMean(input, thicknessAquiferLayer);
return thicknessAquiferLayer;
}
///
/// Gets the sieve size through which 70% of the grains of the top part of the aquifer pass.
/// [m]
///
/// The input to calculate the derived piping input for.
/// Thrown when is null.
/// Returns the corresponding derived input value.
public static VariationCoefficientLogNormalDistribution GetDiameterD70(PipingInput input)
{
if (input == null)
{
throw new ArgumentNullException(nameof(input));
}
PipingSoilLayer topMostAquiferLayer = GetConsecutiveAquiferLayers(input).FirstOrDefault();
return topMostAquiferLayer != null
? topMostAquiferLayer.DiameterD70
: new VariationCoefficientLogNormalDistribution(6)
{
Mean = RoundedDouble.NaN,
CoefficientOfVariation = RoundedDouble.NaN
};
}
///
/// Gets the Darcy-speed with which water flows through the aquifer layer.
/// [m/s]
///
/// The input to calculate the derived piping input for.
/// Thrown when is null.
/// Returns the corresponding derived input value.
public static VariationCoefficientLogNormalDistribution GetDarcyPermeability(PipingInput input)
{
if (input == null)
{
throw new ArgumentNullException(nameof(input));
}
var distribution = new VariationCoefficientLogNormalDistribution(6)
{
Mean = RoundedDouble.NaN,
CoefficientOfVariation = RoundedDouble.NaN
};
UpdateDarcyPermeabilityParameters(input, distribution);
return distribution;
}
///
/// Gets the volumic weight of the saturated coverage layer.
///
/// The input to calculate the derived piping input for.
/// Thrown when is null.
/// Returns the corresponding derived input value.
public static LogNormalDistribution GetSaturatedVolumicWeightOfCoverageLayer(PipingInput input)
{
if (input == null)
{
throw new ArgumentNullException(nameof(input));
}
var distribution = new LogNormalDistribution(2)
{
Mean = RoundedDouble.NaN,
StandardDeviation = RoundedDouble.NaN,
Shift = RoundedDouble.NaN
};
UpdateSaturatedVolumicWeightOfCoverageLayerParameters(input, distribution);
return distribution;
}
private static void UpdateThicknessAquiferLayerMean(PipingInput input, LogNormalDistribution thicknessAquiferLayer)
{
PipingStochasticSoilProfile stochasticSoilProfile = input.StochasticSoilProfile;
PipingSurfaceLine surfaceLine = input.SurfaceLine;
RoundedDouble exitPointL = input.ExitPointL;
if (stochasticSoilProfile?.SoilProfile != null && surfaceLine != null && !double.IsNaN(exitPointL))
{
var thicknessTopAquiferLayer = new RoundedDouble(GetNumberOfDecimals(thicknessAquiferLayer),
GetThicknessTopAquiferLayer(stochasticSoilProfile.SoilProfile, surfaceLine, exitPointL));
if (thicknessTopAquiferLayer > 0)
{
thicknessAquiferLayer.Mean = thicknessTopAquiferLayer;
}
}
}
private static void UpdateThicknessCoverageLayerMean(PipingInput input, LogNormalDistribution thicknessCoverageLayerDistribution)
{
PipingStochasticSoilProfile stochasticSoilProfile = input.StochasticSoilProfile;
PipingSurfaceLine surfaceLine = input.SurfaceLine;
RoundedDouble exitPointL = input.ExitPointL;
if (stochasticSoilProfile?.SoilProfile != null && surfaceLine != null && !double.IsNaN(exitPointL))
{
var weightedMean = new RoundedDouble(GetNumberOfDecimals(thicknessCoverageLayerDistribution),
GetThicknessCoverageLayers(stochasticSoilProfile.SoilProfile, surfaceLine, exitPointL));
if (weightedMean > 0)
{
thicknessCoverageLayerDistribution.Mean = weightedMean;
}
}
}
private static void UpdateEffectiveThicknessCoverageLayerMean(PipingInput input, LogNormalDistribution effectiveThicknessCoverageLayerDistribution)
{
if (input.SurfaceLine != null && input.StochasticSoilProfile?.SoilProfile != null && !double.IsNaN(input.ExitPointL))
{
var weightedMean = new RoundedDouble(GetNumberOfDecimals(effectiveThicknessCoverageLayerDistribution),
InputParameterCalculationService.CalculateEffectiveThicknessCoverageLayer(
input.WaterVolumetricWeight,
PipingSemiProbabilisticDesignVariableFactory.GetPhreaticLevelExit(input).GetDesignValue(),
input.ExitPointL,
input.SurfaceLine,
input.StochasticSoilProfile.SoilProfile));
if (weightedMean > 0)
{
effectiveThicknessCoverageLayerDistribution.Mean = weightedMean;
}
}
}
private static void UpdateDarcyPermeabilityParameters(PipingInput input, VariationCoefficientLogNormalDistribution darcyPermeabilityDistribution)
{
IEnumerable aquiferLayers = GetConsecutiveAquiferLayers(input);
int numberOfDecimals = GetNumberOfDecimals(darcyPermeabilityDistribution);
if (HasCorrectDarcyPermeabilityWeightDistributionParameterDefinition(aquiferLayers))
{
PipingSoilLayer topMostAquiferLayer = aquiferLayers.First();
var weightedMean = new RoundedDouble(numberOfDecimals,
GetWeightedMeanForDarcyPermeabilityOfAquiferLayer(aquiferLayers,
input.StochasticSoilProfile.SoilProfile,
input.SurfaceLine.GetZAtL(input.ExitPointL)));
darcyPermeabilityDistribution.Mean = weightedMean;
darcyPermeabilityDistribution.CoefficientOfVariation = topMostAquiferLayer.Permeability.CoefficientOfVariation;
}
}
private static void UpdateSaturatedVolumicWeightOfCoverageLayerParameters(PipingInput input, LogNormalDistribution volumicWeightDistribution)
{
IEnumerable coverageLayers = GetConsecutiveCoverageLayers(input);
int numberOfDecimals = GetNumberOfDecimals(volumicWeightDistribution);
if (HasCorrectSaturatedWeightDistributionParameterDefinition(coverageLayers))
{
PipingSoilLayer topMostAquitardLayer = coverageLayers.First();
volumicWeightDistribution.Shift = topMostAquitardLayer.BelowPhreaticLevel.Shift;
volumicWeightDistribution.StandardDeviation = topMostAquitardLayer.BelowPhreaticLevel.StandardDeviation;
var weightedMean = new RoundedDouble(numberOfDecimals,
GetWeightedMeanForVolumicWeightOfCoverageLayer(
coverageLayers,
input.StochasticSoilProfile.SoilProfile,
input.SurfaceLine.GetZAtL(input.ExitPointL)));
if (weightedMean > 0)
{
volumicWeightDistribution.Mean = weightedMean;
}
}
}
private static int GetNumberOfDecimals(IDistribution distribution)
{
return distribution.Mean.NumberOfDecimalPlaces;
}
private static int GetNumberOfDecimals(IVariationCoefficientDistribution distribution)
{
return distribution.Mean.NumberOfDecimalPlaces;
}
private static bool HasCorrectSaturatedWeightDistributionParameterDefinition(IEnumerable consecutiveAquitardLayers)
{
if (!consecutiveAquitardLayers.Any())
{
return false;
}
IEnumerable distributions = consecutiveAquitardLayers.Select(layer => layer.BelowPhreaticLevel);
if (distributions.Count() == 1)
{
return true;
}
return distributions.All(currentLayerDistribution => AreShiftAndDeviationEqual(
currentLayerDistribution,
distributions.First()));
}
private static bool HasCorrectDarcyPermeabilityWeightDistributionParameterDefinition(IEnumerable consecutiveAquitardLayers)
{
if (!consecutiveAquitardLayers.Any())
{
return false;
}
IEnumerable distributions = consecutiveAquitardLayers.Select(layer => layer.Permeability);
if (distributions.Count() == 1)
{
return true;
}
return distributions.All(currentLayerDistribution => AreCoefficientEqual(
currentLayerDistribution,
distributions.First()));
}
private static bool AreShiftAndDeviationEqual(LogNormalDistribution currentLayerDistribution, LogNormalDistribution baseLayerDistribution)
{
return currentLayerDistribution.StandardDeviation == baseLayerDistribution.StandardDeviation &&
currentLayerDistribution.Shift == baseLayerDistribution.Shift;
}
private static bool AreCoefficientEqual(VariationCoefficientLogNormalDistribution currentLayerDistribution,
VariationCoefficientLogNormalDistribution baseLayerDistribution)
{
return Math.Abs(baseLayerDistribution.CoefficientOfVariation - currentLayerDistribution.CoefficientOfVariation) < 1e-6;
}
private static double GetWeightedMeanForVolumicWeightOfCoverageLayer(IEnumerable aquitardLayers,
PipingSoilProfile profile,
double surfaceLevel)
{
var totalThickness = 0.0;
var weighedTotal = 0.0;
foreach (PipingSoilLayer layer in aquitardLayers)
{
double layerThickness = profile.GetLayerThickness(layer);
double bottom = layer.Top - layerThickness;
double thicknessUnderSurface = Math.Min(layer.Top, surfaceLevel) - bottom;
totalThickness += thicknessUnderSurface;
weighedTotal += layer.BelowPhreaticLevel.Mean * thicknessUnderSurface;
}
return weighedTotal / totalThickness;
}
private static double GetWeightedMeanForDarcyPermeabilityOfAquiferLayer(IEnumerable aquitardLayers,
PipingSoilProfile profile,
double surfaceLevel)
{
var totalThickness = 0.0;
var weighedTotal = 0.0;
foreach (PipingSoilLayer layer in aquitardLayers)
{
double layerThickness = profile.GetLayerThickness(layer);
double bottom = layer.Top - layerThickness;
double thicknessUnderSurface = Math.Min(layer.Top, surfaceLevel) - bottom;
totalThickness += thicknessUnderSurface;
weighedTotal += layer.Permeability.Mean * thicknessUnderSurface;
}
return weighedTotal / totalThickness;
}
private static IEnumerable GetConsecutiveAquiferLayers(PipingInput input)
{
PipingSurfaceLine surfaceLine = input.SurfaceLine;
PipingSoilProfile soilProfile = input.StochasticSoilProfile?.SoilProfile;
RoundedDouble exitPointL = input.ExitPointL;
if (surfaceLine != null && soilProfile != null && !double.IsNaN(exitPointL))
{
return soilProfile.GetConsecutiveAquiferLayersBelowLevel(surfaceLine.GetZAtL(exitPointL)).ToArray();
}
return new PipingSoilLayer[0];
}
private static IEnumerable GetConsecutiveCoverageLayers(PipingInput input)
{
PipingSurfaceLine surfaceLine = input.SurfaceLine;
PipingSoilProfile soilProfile = input.StochasticSoilProfile?.SoilProfile;
RoundedDouble exitPointL = input.ExitPointL;
if (surfaceLine != null && soilProfile != null && !double.IsNaN(exitPointL))
{
PipingSoilLayer[] consecutiveAquitardLayersBelowLevel = soilProfile
.GetConsecutiveCoverageLayersBelowLevel(surfaceLine.GetZAtL(exitPointL))
.ToArray();
return consecutiveAquitardLayersBelowLevel;
}
return new PipingSoilLayer[0];
}
private static double GetThicknessTopAquiferLayer(PipingSoilProfile soilProfile, PipingSurfaceLine surfaceLine, RoundedDouble exitPointL)
{
try
{
double zAtL = surfaceLine.GetZAtL(exitPointL);
return soilProfile.GetTopmostConsecutiveAquiferLayerThicknessBelowLevel(zAtL);
}
catch (Exception e)
{
if (e is MechanismSurfaceLineException || e is InvalidOperationException || e is ArgumentException)
{
return double.NaN;
}
throw;
}
}
private static double GetThicknessCoverageLayers(PipingSoilProfile soilProfile, PipingSurfaceLine surfaceLine, RoundedDouble exitPointL)
{
try
{
double zAtL = surfaceLine.GetZAtL(exitPointL);
return soilProfile.GetConsecutiveCoverageLayerThicknessBelowLevel(zAtL);
}
catch (Exception e)
{
if (e is MechanismSurfaceLineException || e is InvalidOperationException || e is ArgumentException)
{
return double.NaN;
}
throw;
}
}
}
}