// Copyright (C) Stichting Deltares 2016. 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.Probabilistics;
using Ringtoets.HydraRing.Data;
using Ringtoets.Piping.InputParameterCalculation;
using Ringtoets.Piping.Primitives;
using Ringtoets.Piping.Primitives.Exceptions;
namespace Ringtoets.Piping.Data
{
///
/// Class responsible for calculating the derived piping input.
///
public class DerivedPipingInput
{
private const double seepageLengthStandardDeviationFraction = 0.1;
private readonly PipingInput input;
///
/// Creates a new instance of .
///
/// The input to calculate the derived piping input.
/// Thrown when is null.
public DerivedPipingInput(PipingInput input)
{
if (input == null)
{
throw new ArgumentNullException("input", @"Cannot create DerivedPipingInput without PipingInput.");
}
this.input = input;
}
///
/// Gets the assessment level from the .
///
public RoundedDouble AssessmentLevel
{
get
{
return input.HydraulicBoundaryLocation == null ?
new RoundedDouble(2, double.NaN) :
input.HydraulicBoundaryLocation.DesignWaterLevel;
}
}
///
/// Gets the piezometric head at the exit point.
/// [m]
///
public RoundedDouble PiezometricHeadExit
{
get
{
var dampingFactorExit = PipingSemiProbabilisticDesignValueFactory.GetDampingFactorExit(input).GetDesignValue();
var phreaticLevelExit = PipingSemiProbabilisticDesignValueFactory.GetPhreaticLevelExit(input).GetDesignValue();
return new RoundedDouble(2, InputParameterCalculationService.CalculatePiezometricHeadAtExit(AssessmentLevel, dampingFactorExit, phreaticLevelExit));
}
}
///
/// Gets the horizontal distance between entry and exit point.
/// [m]
///
public LogNormalDistribution SeepageLength
{
get
{
LogNormalDistribution seepageLength = new LogNormalDistribution(2);
double seepageLengthMean = input.ExitPointL - input.EntryPointL;
seepageLength.Mean = (RoundedDouble) seepageLengthMean;
seepageLength.StandardDeviation = (RoundedDouble) seepageLengthMean*seepageLengthStandardDeviationFraction;
return seepageLength;
}
}
///
/// Gets the total thickness of the coverage layers at the exit point.
/// [m]
///
public LogNormalDistribution ThicknessCoverageLayer
{
get
{
LogNormalDistribution thicknessCoverageLayer = new LogNormalDistribution(2)
{
Mean = (RoundedDouble) double.NaN,
StandardDeviation = (RoundedDouble) 0.5
};
UpdateThicknessCoverageLayerMean(thicknessCoverageLayer);
return thicknessCoverageLayer;
}
}
///
/// Gets the total thickness of the aquifer layers at the exit point.
/// [m]
///
public LogNormalDistribution ThicknessAquiferLayer
{
get
{
LogNormalDistribution thicknessAquiferLayer = new LogNormalDistribution(2)
{
Mean = (RoundedDouble) double.NaN,
StandardDeviation = (RoundedDouble) 0.5
};
UpdateThicknessAquiferLayerMean(thicknessAquiferLayer);
return thicknessAquiferLayer;
}
}
///
/// Gets the sieve size through which 70% of the grains of the top part of the aquifer pass.
/// [m]
///
public LogNormalDistribution DiameterD70
{
get
{
var distribution = new LogNormalDistribution(6)
{
Mean = (RoundedDouble) double.NaN,
StandardDeviation = (RoundedDouble) double.NaN
};
UpdateDiameterD70Parameters(distribution);
return distribution;
}
}
///
/// Gets the Darcy-speed with which water flows through the aquifer layer.
/// [m/s]
///
public LogNormalDistribution DarcyPermeability
{
get
{
var distribution = new LogNormalDistribution(6)
{
Mean = (RoundedDouble) double.NaN,
StandardDeviation = (RoundedDouble) double.NaN
};
UpdateDarcyPermeabilityParameters(distribution);
return distribution;
}
}
///
/// Gets the volumic weight of the saturated coverage layer.
///
public LogNormalDistribution SaturatedVolumicWeightOfCoverageLayer
{
get
{
var distribution = new LogNormalDistribution(2)
{
Mean = (RoundedDouble) double.NaN,
StandardDeviation = (RoundedDouble) double.NaN,
Shift = (RoundedDouble) double.NaN
};
UpdateSaturatedVolumicWeightOfCoverageLayerParameters(distribution);
return distribution;
}
}
private void UpdateThicknessAquiferLayerMean(LogNormalDistribution thicknessAquiferLayer)
{
StochasticSoilProfile stochasticSoilProfile = input.StochasticSoilProfile;
RingtoetsPipingSurfaceLine surfaceLine = input.SurfaceLine;
RoundedDouble exitPointL = input.ExitPointL;
if (stochasticSoilProfile != null && stochasticSoilProfile.SoilProfile != null && surfaceLine != null && !double.IsNaN(exitPointL))
{
var thicknessTopAquiferLayer = new RoundedDouble(thicknessAquiferLayer.Mean.NumberOfDecimalPlaces,
GetThicknessTopAquiferLayer(stochasticSoilProfile.SoilProfile, surfaceLine, exitPointL));
if (thicknessTopAquiferLayer > 0)
{
thicknessAquiferLayer.Mean = thicknessTopAquiferLayer;
}
}
}
private void UpdateThicknessCoverageLayerMean(LogNormalDistribution thicknessCoverageLayerDistribution)
{
if (input.SurfaceLine != null && input.StochasticSoilProfile != null && input.StochasticSoilProfile.SoilProfile != null && !double.IsNaN(input.ExitPointL))
{
var weightedMean = new RoundedDouble(thicknessCoverageLayerDistribution.Mean.NumberOfDecimalPlaces,
InputParameterCalculationService.CalculateThicknessCoverageLayer(
input.WaterVolumetricWeight,
PipingSemiProbabilisticDesignValueFactory.GetPhreaticLevelExit(input).GetDesignValue(),
input.ExitPointL,
input.SurfaceLine,
input.StochasticSoilProfile.SoilProfile));
if (weightedMean > 0)
{
thicknessCoverageLayerDistribution.Mean = weightedMean;
}
}
}
private void UpdateDiameterD70Parameters(LogNormalDistribution diameterD70Distribution)
{
PipingSoilLayer topMostAquiferLayer = GetConsecutiveAquiferLayers().FirstOrDefault();
if (topMostAquiferLayer != null)
{
var diameterD70Mean = new RoundedDouble(diameterD70Distribution.Mean.NumberOfDecimalPlaces, topMostAquiferLayer.DiameterD70Mean);
if (diameterD70Mean > 0)
{
diameterD70Distribution.Mean = diameterD70Mean;
}
diameterD70Distribution.StandardDeviation = (RoundedDouble) topMostAquiferLayer.DiameterD70Deviation;
}
}
private void UpdateDarcyPermeabilityParameters(LogNormalDistribution darcyPermeabilityDistribution)
{
PipingSoilLayer topMostAquiferLayer = GetConsecutiveAquiferLayers().FirstOrDefault();
if (topMostAquiferLayer != null)
{
var darcyPermeabilityMean = new RoundedDouble(darcyPermeabilityDistribution.Mean.NumberOfDecimalPlaces, topMostAquiferLayer.PermeabilityMean);
if (darcyPermeabilityMean > 0)
{
darcyPermeabilityDistribution.Mean = darcyPermeabilityMean;
}
darcyPermeabilityDistribution.StandardDeviation = (RoundedDouble) topMostAquiferLayer.PermeabilityDeviation;
}
}
private void UpdateSaturatedVolumicWeightOfCoverageLayerParameters(LogNormalDistribution volumicWeightDistribution)
{
PipingSoilLayer[] coverageLayers = GetConsecutiveCoverageLayers();
var numberOfDecimals = GetNumberOfDecimals(volumicWeightDistribution);
if (HasCorrectSaturatedWeightDistributionParameterDefinition(
coverageLayers,
numberOfDecimals))
{
PipingSoilLayer topMostAquitardLayer = coverageLayers.First();
volumicWeightDistribution.Shift = (RoundedDouble) topMostAquitardLayer.BelowPhreaticLevelShift;
volumicWeightDistribution.StandardDeviation = (RoundedDouble) topMostAquitardLayer.BelowPhreaticLevelDeviation;
var weightedMean = new RoundedDouble(volumicWeightDistribution.Mean.NumberOfDecimalPlaces,
GetWeightedMeanForVolumicWeightOfCoverageLayer(
coverageLayers,
input.StochasticSoilProfile.SoilProfile,
input.SurfaceLine.GetZAtL(input.ExitPointL)));
if (weightedMean > 0)
{
volumicWeightDistribution.Mean = weightedMean;
}
}
}
private int GetNumberOfDecimals(LogNormalDistribution volumicWeightDistribution)
{
return volumicWeightDistribution.Mean.NumberOfDecimalPlaces;
}
private static bool HasCorrectSaturatedWeightDistributionParameterDefinition(IList consecutiveAquitardLayers, int numberOfDecimals)
{
if (!consecutiveAquitardLayers.Any())
{
return false;
}
var distributions = GetLayerDistributionDefinitions(consecutiveAquitardLayers, numberOfDecimals);
if (distributions == null)
{
return false;
}
if (distributions.Length == 1)
{
return true;
}
return distributions.All(currentLayerDistribution => AreShiftAndDeviationEqual(
currentLayerDistribution,
distributions[0]));
}
private static LogNormalDistribution[] GetLayerDistributionDefinitions(IList consecutiveAquitardLayers, int numberOfDecimals)
{
try
{
return consecutiveAquitardLayers.Select(layer => new LogNormalDistribution(numberOfDecimals)
{
Mean = (RoundedDouble) layer.BelowPhreaticLevelMean,
StandardDeviation = (RoundedDouble) layer.BelowPhreaticLevelDeviation,
Shift = (RoundedDouble) layer.BelowPhreaticLevelShift
}).ToArray();
}
catch (ArgumentOutOfRangeException)
{
return null;
}
}
private static bool AreShiftAndDeviationEqual(LogNormalDistribution currentLayerDistribution, LogNormalDistribution baseLayerDistribution)
{
return currentLayerDistribution.StandardDeviation == baseLayerDistribution.StandardDeviation &&
currentLayerDistribution.Shift == baseLayerDistribution.Shift;
}
private static double GetWeightedMeanForVolumicWeightOfCoverageLayer(PipingSoilLayer[] aquitardLayers, PipingSoilProfile profile, double surfaceLevel)
{
double totalThickness = 0.0;
double weighedTotal = 0.0;
foreach (var 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.BelowPhreaticLevelMean*thicknessUnderSurface;
}
return weighedTotal/totalThickness;
}
private PipingSoilLayer[] GetConsecutiveAquiferLayers()
{
RingtoetsPipingSurfaceLine surfaceLine = input.SurfaceLine;
PipingSoilProfile soilProfile = input.StochasticSoilProfile != null ? input.StochasticSoilProfile.SoilProfile : null;
RoundedDouble exitPointL = input.ExitPointL;
if (surfaceLine != null && soilProfile != null && !double.IsNaN(exitPointL))
{
return soilProfile.GetConsecutiveAquiferLayersBelowLevel(surfaceLine.GetZAtL(exitPointL)).ToArray();
}
return new PipingSoilLayer[0];
}
private PipingSoilLayer[] GetConsecutiveCoverageLayers()
{
RingtoetsPipingSurfaceLine surfaceLine = input.SurfaceLine;
PipingSoilProfile soilProfile = input.StochasticSoilProfile != null ? input.StochasticSoilProfile.SoilProfile : null;
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, RingtoetsPipingSurfaceLine surfaceLine, RoundedDouble exitPointL)
{
try
{
double zAtL = surfaceLine.GetZAtL(exitPointL);
return soilProfile.GetTopmostConsecutiveAquiferLayerThicknessBelowLevel(zAtL);
}
catch (Exception e)
{
if (e is RingtoetsPipingSurfaceLineException || e is InvalidOperationException || e is ArgumentException)
{
return double.NaN;
}
throw;
}
}
}
}