// 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.Collections.ObjectModel;
using System.Linq;
using Core.Common.Base.Geometry;
using Ringtoets.Common.IO.Exceptions;
using Ringtoets.Common.IO.SoilProfile;
using Ringtoets.Piping.IO.Properties;
using Ringtoets.Piping.Primitives;
using RingtoetsCommonResources = Ringtoets.Common.IO.Properties.Resources;
namespace Ringtoets.Piping.IO.SoilProfiles
{
///
/// Transforms generic into .
///
internal static class PipingSoilLayerTransformer
{
///
/// Transforms the generic into a .
///
/// The soil layer to use in the transformation.
/// A new based on the given data.
/// Thrown when is null.
/// Thrown when transformation would not result
/// in a valid transformed instance.
public static PipingSoilLayer Transform(SoilLayer1D soilLayer)
{
if (soilLayer == null)
{
throw new ArgumentNullException(nameof(soilLayer));
}
ValidateStochasticParameters(soilLayer);
var pipingSoilLayer = new PipingSoilLayer(soilLayer.Top)
{
IsAquifer = soilLayer.IsAquifer,
MaterialName = soilLayer.MaterialName,
Color = soilLayer.Color
};
SetStochasticParameters(pipingSoilLayer, soilLayer);
return pipingSoilLayer;
}
public static IEnumerable Transform(SoilLayer2D soilLayer, double atX, out double bottom)
{
if (soilLayer == null)
{
throw new ArgumentNullException(nameof(soilLayer));
}
ValidateStochasticParameters(soilLayer);
bottom = double.MaxValue;
if (soilLayer.OuterLoop == null)
{
return Enumerable.Empty();
}
double[] outerLoopIntersectionHeights = GetLoopIntersectionHeights(soilLayer.OuterLoop, atX).ToArray();
if (!outerLoopIntersectionHeights.Any())
{
return Enumerable.Empty();
}
var soilLayers = new Collection();
IEnumerable> innerLoopsIntersectionHeights = soilLayer.InnerLoops.Select(loop => GetLoopIntersectionHeights(loop, atX));
IEnumerable> innerLoopIntersectionHeightPairs = GetOrderedStartAndEndPairsIn1D(innerLoopsIntersectionHeights).ToList();
IEnumerable> outerLoopIntersectionHeightPairs = GetOrderedStartAndEndPairsIn1D(outerLoopIntersectionHeights).ToList();
double currentBottom = outerLoopIntersectionHeightPairs.First().Item1;
var heights = new List();
heights.AddRange(innerLoopIntersectionHeightPairs.Where(p => p.Item1 >= currentBottom).Select(p => p.Item1));
heights.AddRange(outerLoopIntersectionHeightPairs.Select(p => p.Item2));
foreach (double height in heights.Where(height => !innerLoopIntersectionHeightPairs.Any(tuple => HeightInInnerLoop(tuple, height))))
{
var pipingSoilLayer = new PipingSoilLayer(height)
{
IsAquifer = soilLayer.IsAquifer,
MaterialName = soilLayer.MaterialName,
Color = soilLayer.Color
};
SetStochasticParameters(pipingSoilLayer, soilLayer);
soilLayers.Add(pipingSoilLayer);
}
bottom = EnsureBottomOutsideInnerLoop(innerLoopIntersectionHeightPairs, currentBottom);
return soilLayers;
}
///
/// Validates whether the values of the distribution and shift for the stochastic parameters
/// are correct for creating a .
///
/// Thrown when any of the distributions of the
/// stochastic parameters is not defined as lognormal or is shifted when it should not be.
private static void ValidateStochasticParameters(SoilLayerBase soilLayer)
{
ValidateIsLogNormal(
soilLayer.BelowPhreaticLevelDistribution,
Resources.SoilLayer_BelowPhreaticLevelDistribution_Description);
ValidateIsNonShiftedLogNormal(
soilLayer.DiameterD70Distribution,
soilLayer.DiameterD70Shift,
Resources.SoilLayer_DiameterD70Distribution_Description);
ValidateIsNonShiftedLogNormal(
soilLayer.PermeabilityDistribution,
soilLayer.PermeabilityShift,
Resources.SoilLayer_PermeabilityDistribution_Description);
}
///
/// Sets the values of the stochastic parameters for the given .
///
/// The to set the property values for.
/// The to get the properties from.
/// This method does not perform validation. Use to
/// verify whether the distributions for the stochastic parameters are correctly defined.
private static void SetStochasticParameters(PipingSoilLayer pipingSoilLayer, SoilLayerBase soilLayer1D)
{
pipingSoilLayer.BelowPhreaticLevelMean = soilLayer1D.BelowPhreaticLevelMean;
pipingSoilLayer.BelowPhreaticLevelDeviation = soilLayer1D.BelowPhreaticLevelDeviation;
pipingSoilLayer.BelowPhreaticLevelShift = soilLayer1D.BelowPhreaticLevelShift;
pipingSoilLayer.DiameterD70Mean = soilLayer1D.DiameterD70Mean;
pipingSoilLayer.DiameterD70CoefficientOfVariation = soilLayer1D.DiameterD70CoefficientOfVariation;
pipingSoilLayer.PermeabilityMean = soilLayer1D.PermeabilityMean;
pipingSoilLayer.PermeabilityCoefficientOfVariation = soilLayer1D.PermeabilityCoefficientOfVariation;
}
private static void ValidateIsNonShiftedLogNormal(long? distribution, double shift, string incorrectDistibutionParameter)
{
if (distribution.HasValue && (distribution.Value != SoilLayerConstants.LogNormalDistributionValue
|| Math.Abs(shift) > 1e-6))
{
throw new ImportedDataTransformException(string.Format(
RingtoetsCommonResources.SoilLayer_Stochastic_parameter_0_has_no_lognormal_distribution,
incorrectDistibutionParameter));
}
}
private static void ValidateIsLogNormal(long? distribution, string incorrectDistibutionParameter)
{
if (distribution.HasValue && distribution != SoilLayerConstants.LogNormalDistributionValue)
{
throw new ImportedDataTransformException(string.Format(
RingtoetsCommonResources.SoilLayer_Stochastic_parameter_0_has_no_shifted_lognormal_distribution,
incorrectDistibutionParameter));
}
}
private static bool HeightInInnerLoop(Tuple tuple, double height)
{
return height <= tuple.Item2 && height > tuple.Item1;
}
private static bool BottomInInnerLoop(Tuple tuple, double height)
{
return height < tuple.Item2 && height >= tuple.Item1;
}
private static double EnsureBottomOutsideInnerLoop(IEnumerable> innerLoopIntersectionHeightPairs, double bottom)
{
double newBottom = bottom;
List> heightPairArray = innerLoopIntersectionHeightPairs.ToList();
Tuple overlappingInnerLoop = heightPairArray.FirstOrDefault(t => BottomInInnerLoop(t, newBottom));
while (overlappingInnerLoop != null)
{
newBottom = overlappingInnerLoop.Item2;
overlappingInnerLoop = heightPairArray.FirstOrDefault(t => BottomInInnerLoop(t, newBottom));
}
return newBottom;
}
private static IEnumerable> GetOrderedStartAndEndPairsIn1D(IEnumerable> innerLoopsIntersectionPoints)
{
var result = new Collection>();
foreach (IEnumerable innerLoopIntersectionPoints in innerLoopsIntersectionPoints)
{
foreach (Tuple tuple in GetOrderedStartAndEndPairsIn1D(innerLoopIntersectionPoints))
{
result.Add(tuple);
}
}
return result;
}
private static Collection> GetOrderedStartAndEndPairsIn1D(IEnumerable innerLoopIntersectionPoints)
{
var result = new Collection>();
List orderedHeights = innerLoopIntersectionPoints.OrderBy(v => v).ToList();
for (var i = 0; i < orderedHeights.Count; i = i + 2)
{
double first = orderedHeights[i];
double second = orderedHeights[i + 1];
result.Add(Tuple.Create(first, second));
}
return result;
}
///
/// Gets a of heights where the intersects the
/// vertical line at .
///
/// The sequence of which together create a loop.
/// The point on the x-axis where the vertical line is constructed do determine intersections with.
/// A of , representing the height at which the
/// intersects the vertical line at .
/// Thrown when a segment is vertical at and thus
/// no deterministic intersection points can be determined.
private static IEnumerable GetLoopIntersectionHeights(IEnumerable loop, double atX)
{
Segment2D[] segment2Ds = loop.ToArray();
if (segment2Ds.Any(segment => IsVerticalAtX(segment, atX)))
{
string message = string.Format(Resources.Error_Can_not_determine_1D_profile_with_vertical_segments_at_X_0_, atX);
throw new ImportedDataTransformException(message);
}
return Math2D.SegmentsIntersectionWithVerticalLine(segment2Ds, atX).Select(p => p.Y);
}
private static bool IsVerticalAtX(Segment2D segment, double atX)
{
return segment.FirstPoint.X.Equals(atX) && segment.IsVertical();
}
}
}