// 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 RingtoetsCommonIOResources = 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 = TransformIsAquifer(soilLayer.IsAquifer, soilLayer.MaterialName),
MaterialName = soilLayer.MaterialName,
Color = SoilLayerColorConverter.Convert(soilLayer.Color)
};
SetStochasticParameters(pipingSoilLayer, soilLayer);
return pipingSoilLayer;
}
///
/// Transforms the generic into one or more .
///
/// The soil layer to use in the transformation.
/// The 1D intersection of the profile.
/// The bottom of the soil layer.
/// A collection of based on the given data.
/// Thrown when is null.
/// Thrown when transformation would not result
/// in a valid transformed instance.
public static IEnumerable Transform(SoilLayer2D soilLayer, double atX, out double bottom)
{
bottom = double.MaxValue;
var soilLayers = new Collection();
Transform(soilLayer, atX, soilLayers, ref bottom);
return soilLayers;
}
///
/// Transforms the generic into one or more .
///
/// The soil layer to use in the transformation.
/// The 1D intersection of the profile.
/// The collection of transformed piping soil layers to add the
/// transformation to.
/// The bottom of the soil layer.
/// Thrown when is null.
/// Thrown when transformation would not result
/// in a valid transformed instance.
private static void Transform(SoilLayer2D soilLayer, double atX, ICollection soilLayers, ref double bottom)
{
if (soilLayer == null)
{
throw new ArgumentNullException(nameof(soilLayer));
}
ValidateStochasticParameters(soilLayer);
if (soilLayer.OuterLoop == null)
{
return;
}
string soilLayerName = soilLayer.MaterialName;
double[] outerLoopIntersectionHeights = GetLoopIntersectionHeights(soilLayer.OuterLoop.Segments, atX, soilLayerName).ToArray();
if (!outerLoopIntersectionHeights.Any())
{
return;
}
IEnumerable> innerLoopsIntersectionHeights = soilLayer.NestedLayers.Select(l => GetLoopIntersectionHeights(l.OuterLoop.Segments,
atX,
soilLayerName));
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 = TransformIsAquifer(soilLayer.IsAquifer, soilLayerName),
MaterialName = soilLayer.MaterialName,
Color = SoilLayerColorConverter.Convert(soilLayer.Color)
};
SetStochasticParameters(pipingSoilLayer, soilLayer);
soilLayers.Add(pipingSoilLayer);
}
bottom = currentBottom < bottom ? currentBottom : bottom;
foreach (SoilLayer2D nestedLayer in soilLayer.NestedLayers)
{
Transform(nestedLayer, atX, soilLayers, ref bottom);
}
}
///
/// 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)
{
try
{
DistributionHelper.ValidateShiftedLogNormalDistribution(
soilLayer.BelowPhreaticLevelDistributionType,
Resources.SoilLayer_BelowPhreaticLevelDistribution_DisplayName);
DistributionHelper.ValidateLogNormalDistribution(
soilLayer.DiameterD70DistributionType,
soilLayer.DiameterD70Shift,
Resources.SoilLayer_DiameterD70Distribution_DisplayName);
DistributionHelper.ValidateLogNormalDistribution(
soilLayer.PermeabilityDistributionType,
soilLayer.PermeabilityShift,
Resources.SoilLayer_PermeabilityDistribution_DisplayName);
}
catch (ImportedDataTransformException e)
{
string errorMessage = string.Format(RingtoetsCommonIOResources.Transform_Error_occurred_when_transforming_SoilLayer_0_errorMessage_1,
soilLayer.MaterialName,
e.Message);
throw new ImportedDataTransformException(errorMessage, e);
}
}
///
/// 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 soilLayer)
{
pipingSoilLayer.BelowPhreaticLevelMean = soilLayer.BelowPhreaticLevelMean;
pipingSoilLayer.BelowPhreaticLevelDeviation = soilLayer.BelowPhreaticLevelDeviation;
pipingSoilLayer.BelowPhreaticLevelShift = soilLayer.BelowPhreaticLevelShift;
pipingSoilLayer.DiameterD70Mean = soilLayer.DiameterD70Mean;
pipingSoilLayer.DiameterD70CoefficientOfVariation = soilLayer.DiameterD70CoefficientOfVariation;
pipingSoilLayer.PermeabilityMean = soilLayer.PermeabilityMean;
pipingSoilLayer.PermeabilityCoefficientOfVariation = soilLayer.PermeabilityCoefficientOfVariation;
}
private static bool HeightInInnerLoop(Tuple tuple, double height)
{
return height <= tuple.Item2 && height > tuple.Item1;
}
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.
/// The name of the soil layer.
/// 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,
string soilLayerName)
{
Segment2D[] segment2Ds = loop.ToArray();
if (segment2Ds.Any(segment => IsVerticalAtX(segment, atX)))
{
string message = CreateExceptionMessage(soilLayerName,
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 string CreateExceptionMessage(string soilLayerName, string errorMessage)
{
return string.Format(RingtoetsCommonIOResources.Transform_Error_occurred_when_transforming_SoilLayer_0_errorMessage_1,
soilLayerName,
errorMessage);
}
private static bool IsVerticalAtX(Segment2D segment, double atX)
{
return segment.FirstPoint.X.Equals(atX) && segment.IsVertical();
}
///
/// Transforms a to a for the
/// .
///
/// The value to transform.
/// The name of the soil layer.
/// A based on .
/// Thrown when
/// could not be transformed.
private static bool TransformIsAquifer(double? isAquifer, string soilLayerName)
{
try
{
return SoilLayerIsAquiferConverter.Convert(isAquifer);
}
catch (NotSupportedException e)
{
string message = CreateExceptionMessage(soilLayerName,
string.Format(RingtoetsCommonIOResources.Transform_Invalid_value_ParameterName_0,
RingtoetsCommonIOResources.SoilLayerData_IsAquifer_DisplayName));
throw new ImportedDataTransformException(message, e);
}
}
}
}