// 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.Utils.Extensions; using Ringtoets.Common.IO.Properties; namespace Ringtoets.Common.IO.Structures { /// /// This class contains validations methods for objects. /// public static class StructuresParameterRowsValidator { /// /// Denotes a small enough value, taking possible rounding into account, that the /// value is too close to the value 0.0 that makes a coefficient of variation /// too unreliable. /// private const double valueTooCloseToZero = 1e-4; private static readonly List closingStructureInflowModelTypeRuleKeywords = new List { StructureFilesKeywords.InflowModelTypeVerticalWall, StructureFilesKeywords.InflowModelTypeLowSill, StructureFilesKeywords.InflowModelTypeFloodedCulvert }; private static readonly List stabilityPointStructureInflowModelTypeRuleKeywords = new List { StructureFilesKeywords.InflowModelTypeLowSill, StructureFilesKeywords.InflowModelTypeFloodedCulvert }; private static readonly Dictionary>> heightStructuresRules = new Dictionary>> { { StructureFilesKeywords.HeightStructureParameterKeyword1, StructureNormalOrientation }, { StructureFilesKeywords.HeightStructureParameterKeyword2, NormalDistributionRule }, { StructureFilesKeywords.HeightStructureParameterKeyword3, LogNormalDistributionRule }, { StructureFilesKeywords.HeightStructureParameterKeyword4, VariationCoefficientLogNormalDistributionRule }, { StructureFilesKeywords.HeightStructureParameterKeyword5, VariationCoefficientNormalDistributionRule }, { StructureFilesKeywords.HeightStructureParameterKeyword6, ProbabilityRule }, { StructureFilesKeywords.HeightStructureParameterKeyword7, VariationCoefficientLogNormalDistributionRule }, { StructureFilesKeywords.HeightStructureParameterKeyword8, LogNormalDistributionRule } }; private static readonly Dictionary>> closingStructuresRules = new Dictionary>> { { StructureFilesKeywords.ClosingStructureParameterKeyword1, VariationCoefficientLogNormalDistributionRule }, { StructureFilesKeywords.ClosingStructureParameterKeyword2, LogNormalDistributionRule }, { StructureFilesKeywords.ClosingStructureParameterKeyword3, StructureNormalOrientation }, { StructureFilesKeywords.ClosingStructureParameterKeyword4, VariationCoefficientNormalDistributionRule }, { StructureFilesKeywords.ClosingStructureParameterKeyword5, NormalDistributionRule }, { StructureFilesKeywords.ClosingStructureParameterKeyword6, NormalDistributionRule }, { StructureFilesKeywords.ClosingStructureParameterKeyword7, NormalDistributionRule }, { StructureFilesKeywords.ClosingStructureParameterKeyword8, LogNormalDistributionRule }, { StructureFilesKeywords.ClosingStructureParameterKeyword9, VariationCoefficientLogNormalDistributionRule }, { StructureFilesKeywords.ClosingStructureParameterKeyword10, LogNormalDistributionRule }, { StructureFilesKeywords.ClosingStructureParameterKeyword11, ProbabilityRule }, { StructureFilesKeywords.ClosingStructureParameterKeyword12, ProbabilityRule }, { StructureFilesKeywords.ClosingStructureParameterKeyword13, IdenticalApertures }, { StructureFilesKeywords.ClosingStructureParameterKeyword14, ProbabilityRule }, { StructureFilesKeywords.ClosingStructureParameterKeyword15, ClosingStructureInflowModelTypeRule } }; private static readonly Dictionary>> stabilityPointStructuresRules = new Dictionary>> { { StructureFilesKeywords.StabilityPointStructureParameterKeyword1, StructureNormalOrientation }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword2, VariationCoefficientLogNormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword3, LogNormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword4, VariationCoefficientNormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword5, NormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword6, NormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword7, VariationCoefficientLogNormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword8, LogNormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword9, VariationCoefficientLogNormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword10, VariationCoefficientLogNormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword11, NormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword12, NormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword13, DoubleRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword14, NormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword15, PositiveDoubleRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword16, ProbabilityRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword17, PositiveDoubleRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword18, VariationCoefficientNormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword19, VariationCoefficientNormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword20, PositiveDoubleRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword21, ProbabilityRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword22, NormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword23, VariationCoefficientLogNormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword24, VariationCoefficientLogNormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword25, LogNormalDistributionRule }, { StructureFilesKeywords.StabilityPointStructureParameterKeyword26, StabilityPointStructureInflowModelTypeRule } }; /// /// Validates a collection of for a height structure. /// /// The objects to validate. /// A object containing the validation result. /// Thrown when is null. public static ValidationResult ValidateHeightStructuresParameters(IList structureParameterRows) { return ValidateStructuresParameters(structureParameterRows, heightStructuresRules); } /// /// Validates a collection of for a closing structure. /// /// The objects to validate. /// A object containing the validation result. /// Thrown when is null. public static ValidationResult ValidateClosingStructuresParameters(IList structureParameterRows) { return ValidateStructuresParameters(structureParameterRows, closingStructuresRules); } /// /// Validates a collection of for a stability point structure. /// /// The objects to validate. /// A object containing the validation result. /// Thrown when is null. public static ValidationResult ValidateStabilityPointStructuresParameters(IList structureParameterRows) { return ValidateStructuresParameters(structureParameterRows, stabilityPointStructuresRules); } private static ValidationResult ValidateStructuresParameters(IList structureParameterRows, Dictionary>> rules) { if (structureParameterRows == null) { throw new ArgumentNullException("structureParameterRows"); } var errorMessages = new List(); foreach (string name in rules.Keys) { int count = structureParameterRows.Count(row => string.Equals(row.ParameterId, name, StringComparison.OrdinalIgnoreCase)); if (count < 1) { errorMessages.Add(string.Format(Resources.StructuresParameterRowsValidator_Parameter_0_missing_or_invalid, name)); continue; } if (count > 1) { errorMessages.Add(string.Format(Resources.StructuresParameterRowsValidator_Parameter_0_repeated, name)); } errorMessages.AddRange(rules[name](structureParameterRows.First(row => string.Equals(row.ParameterId, name, StringComparison.OrdinalIgnoreCase)))); } return new ValidationResult(errorMessages); } private static List DoubleRule(StructuresParameterRow row) { return ValidateDoubleParameter(row, StructureFilesKeywords.NumericalValueColumnName); } private static List PositiveDoubleRule(StructuresParameterRow row) { return ValidatePositiveDoubleParameter(row, StructureFilesKeywords.NumericalValueColumnName); } private static List ValidateDoubleParameter(StructuresParameterRow row, string columnName) { var messages = new List(); double value = GetValueFromRowForColumn(row, columnName); if (double.IsNaN(value) || double.IsInfinity(value)) { messages.Add(string.Format(Resources.StructuresParameterRowsValidator_ValidateDoubleParameter_ParameterId_0_Line_1_ColumnName_2_not_number, row.ParameterId, row.LineNumber, columnName.FirstToUpper())); } return messages; } private static List ValidatePositiveDoubleParameter(StructuresParameterRow row, string columnName) { var messages = new List(); double value = GetValueFromRowForColumn(row, columnName); if (double.IsNaN(value) || double.IsInfinity(value) || value < 0) { messages.Add(string.Format(Resources.StructuresParameterRowsValidator_ValidatePositiveDoubleParameter_ParameterId_0_Line_1_ColumnName_2_must_be_a_positive_number, row.ParameterId, row.LineNumber, columnName.FirstToUpper())); } return messages; } private static double GetValueFromRowForColumn(StructuresParameterRow row, string columnName) { switch (columnName) { case StructureFilesKeywords.NumericalValueColumnName: return row.NumericalValue; case StructureFilesKeywords.VariationValueColumnName: return row.VarianceValue; default: throw new NotImplementedException(); } } private static List ProbabilityRule(StructuresParameterRow row) { var messages = new List(); double mean = row.NumericalValue; if (double.IsNaN(mean) || double.IsInfinity(mean) || mean <= 0 || mean > 1) { messages.Add(string.Format(Resources.StructuresParameterRowsValidator_ProbabilityRule_ParameterId_0_Line_1_ColumnName_2_probability_out_of_range, row.ParameterId, row.LineNumber, StructureFilesKeywords.NumericalValueColumnName.FirstToUpper())); } return messages; } private static List NormalDistributionRule(StructuresParameterRow row) { return ValidateStochasticVariableParameters(row, false, true); } private static List VariationCoefficientNormalDistributionRule(StructuresParameterRow row) { return ValidateStochasticVariableParameters(row, false, false); } private static List LogNormalDistributionRule(StructuresParameterRow row) { return ValidateStochasticVariableParameters(row, true, true); } private static List VariationCoefficientLogNormalDistributionRule(StructuresParameterRow row) { return ValidateStochasticVariableParameters(row, true, false); } private static List ValidateStochasticVariableParameters(StructuresParameterRow row, bool meanMustBeGreaterThanZero, bool variationAsStandardDeviation) { var messages = new List(); double mean = row.NumericalValue; var numericalValueColumn1 = StructureFilesKeywords.NumericalValueColumnName; messages.AddRange(meanMustBeGreaterThanZero ? ValidatePositiveDoubleParameter(row, numericalValueColumn1) : ValidateDoubleParameter(row, numericalValueColumn1)); VarianceType type = row.VarianceType; if (type != VarianceType.StandardDeviation && type != VarianceType.CoefficientOfVariation) { messages.Add(string.Format(Resources.StructuresParameterRowsValidator_ParameterId_0_Line_1_ColumnName_2_invalid_variancetype_value, row.ParameterId, row.LineNumber, StructureFilesKeywords.VariationTypeColumnName.FirstToUpper())); } messages.AddRange(ValidatePositiveDoubleParameter(row, StructureFilesKeywords.VariationValueColumnName)); double absoluteMean = Math.Abs(mean); if (variationAsStandardDeviation) { if (row.VarianceType == VarianceType.CoefficientOfVariation && absoluteMean < valueTooCloseToZero) { messages.Add(string.Format(Resources.StructuresParameterRowsValidator_ParameterId_0_Line_1_ColumnName_2_mean_too_small_for_reliable_variation_value_conversion, row.ParameterId, row.LineNumber, StructureFilesKeywords.NumericalValueColumnName.FirstToUpper())); } } else { if (row.VarianceType == VarianceType.StandardDeviation && absoluteMean < valueTooCloseToZero) { messages.Add(string.Format(Resources.StructuresParameterRowsValidator_ParameterId_0_Line_1_ColumnName_2_mean_too_small_for_reliable_variation_value_conversion, row.ParameterId, row.LineNumber, StructureFilesKeywords.NumericalValueColumnName.FirstToUpper())); } } return messages; } private static List StructureNormalOrientation(StructuresParameterRow row) { var messages = new List(); double orientation = row.NumericalValue; if (!(orientation >= 0 && orientation <= 360)) { messages.Add(string.Format(Resources.StructuresParameterRowsValidator_ParameterId_0_Line_1_ColumnName_2_orientation_out_of_range, row.ParameterId, row.LineNumber, StructureFilesKeywords.NumericalValueColumnName.FirstToUpper())); } return messages; } private static List IdenticalApertures(StructuresParameterRow row) { var messages = new List(); double value = row.NumericalValue; if (!IsValueWholeNumber(value) || value < 0) { messages.Add(string.Format(Resources.StructuresParameterRowsValidator_ParameterId_0_Line_1_ColumnName_2_value_must_be_positive_whole_number, row.ParameterId, row.LineNumber, StructureFilesKeywords.NumericalValueColumnName.FirstToUpper())); } return messages; } private static bool IsValueWholeNumber(double value) { return (value%1) < double.Epsilon; } private static List ClosingStructureInflowModelTypeRule(StructuresParameterRow row) { var messages = new List(); string value = row.AlphanumericValue.ToLower(); if (!closingStructureInflowModelTypeRuleKeywords.Contains(value)) { messages.Add(string.Format(Resources.StructuresParameterRowsValidator_ParameterId_0_Line_1_ColumnName_2_structure_type_invalid, row.ParameterId, row.LineNumber, StructureFilesKeywords.AlphanumericalValueColumnName.FirstToUpper())); } return messages; } private static List StabilityPointStructureInflowModelTypeRule(StructuresParameterRow row) { var messages = new List(); string value = row.AlphanumericValue.ToLower(); if (!stabilityPointStructureInflowModelTypeRuleKeywords.Contains(value)) { messages.Add(string.Format(Resources.StructuresParameterRowsValidator_ParameterId_0_Line_1_ColumnName_2_structure_type_invalid, row.ParameterId, row.LineNumber, StructureFilesKeywords.AlphanumericalValueColumnName.FirstToUpper())); } return messages; } } }