// 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.ComponentModel; using System.IO; using System.Linq; using System.Security; using Core.Common.Base.IO; using Core.Common.Util; using log4net; using Ringtoets.Common.Data.AssessmentSection; using Ringtoets.Common.Data.DikeProfiles; using Ringtoets.Common.Data.Exceptions; using Ringtoets.Common.Data.Hydraulics; using Ringtoets.Common.Data.IllustrationPoints; using Ringtoets.Common.IO.HydraRing; using Ringtoets.Common.Service; using Ringtoets.Common.Service.IllustrationPoints; using Ringtoets.Common.Service.ValidationRules; using Ringtoets.GrassCoverErosionInwards.Data; using Ringtoets.GrassCoverErosionInwards.Service.Properties; using Ringtoets.HydraRing.Calculation.Calculator; using Ringtoets.HydraRing.Calculation.Calculator.Factory; using Ringtoets.HydraRing.Calculation.Data.Input; using Ringtoets.HydraRing.Calculation.Data.Input.Hydraulics; using Ringtoets.HydraRing.Calculation.Data.Input.Overtopping; using Ringtoets.HydraRing.Calculation.Exceptions; using RingtoetsCommonServiceResources = Ringtoets.Common.Service.Properties.Resources; using RingtoetsCommonForms = Ringtoets.Common.Forms.Properties.Resources; using HydraRingGeneralResult = Ringtoets.HydraRing.Calculation.Data.Output.IllustrationPoints.GeneralResult; namespace Ringtoets.GrassCoverErosionInwards.Service { /// /// Service that provides methods for performing Hydra-Ring calculations for grass cover erosion inwards calculations. /// public class GrassCoverErosionInwardsCalculationService { private static readonly ILog log = LogManager.GetLogger(typeof(GrassCoverErosionInwardsCalculationService)); public OnProgressChanged OnProgress; private bool canceled; private IOvertoppingCalculator overtoppingCalculator; private IHydraulicLoadsCalculator dikeHeightCalculator; private IHydraulicLoadsCalculator overtoppingRateCalculator; /// /// Performs validation over the values on the given . Error and status information is logged during /// the execution of the operation. /// /// The for which to validate the values. /// The for which to validate the values. /// true if has no validation errors; false otherwise. public static bool Validate(GrassCoverErosionInwardsCalculation calculation, IAssessmentSection assessmentSection) { CalculationServiceHelper.LogValidationBegin(); string[] messages = ValidateInput(calculation.InputParameters, assessmentSection); CalculationServiceHelper.LogMessagesAsError(messages); CalculationServiceHelper.LogValidationEnd(); return !messages.Any(); } /// /// Cancels any currently running grass cover erosion inwards calculation. /// public void Cancel() { overtoppingCalculator?.Cancel(); dikeHeightCalculator?.Cancel(); overtoppingRateCalculator?.Cancel(); canceled = true; } /// /// Performs a grass cover erosion inwards calculation based on the supplied /// and sets if the calculation was successful. Error and status /// information is logged during the execution of the operation. /// /// The that holds all the information required to perform the calculation. /// The that holds information about the norm used in the calculation. /// Calculation input parameters that apply to all instances. /// The amount of contribution for this failure mechanism in the assessment section. /// The path which points to the hydraulic boundary database file. /// Thrown when one of the following parameters is null: /// /// /// /// /// /// Thrown when: /// /// The contains invalid characters. /// The contribution of the failure mechanism is zero. /// The target probability or the calculated probability of a dike height calculation falls outside /// the [0.0, 1.0] range and is not . /// The target probability or the calculated probability of an overtopping rate calculation falls outside /// the [0.0, 1.0] range and is not . /// /// /// Thrown when: /// /// No settings database file could be found at the location of /// with the same name. /// Unable to open settings database file. /// Unable to read required data from database file. /// /// /// Thrown when the temporary working directory can't be accessed due to missing permissions. /// Thrown when the specified path is not valid, the network name is not known /// or an I/O error occurred while opening the file. /// Thrown when the directory can't be created due to missing /// the required permissions. /// Thrown when /// is not the same with already added input. /// Thrown when there was an error in opening the associated file /// or the wait setting could not be accessed. /// Thrown when an error occurs during parsing of the Hydra-Ring output. /// Thrown when an error occurs during the calculation. internal void Calculate(GrassCoverErosionInwardsCalculation calculation, IAssessmentSection assessmentSection, GeneralGrassCoverErosionInwardsInput generalInput, double failureMechanismContribution, string hydraulicBoundaryDatabaseFilePath) { if (calculation == null) { throw new ArgumentNullException(nameof(calculation)); } if (assessmentSection == null) { throw new ArgumentNullException(nameof(assessmentSection)); } if (generalInput == null) { throw new ArgumentNullException(nameof(generalInput)); } string effectivePreprocessorDirectory = assessmentSection.HydraulicBoundaryDatabase.EffectivePreprocessorDirectory(); bool usePreprocessor = !string.IsNullOrEmpty(effectivePreprocessorDirectory); int numberOfCalculators = CreateCalculators(calculation, Path.GetDirectoryName(hydraulicBoundaryDatabaseFilePath), effectivePreprocessorDirectory); CalculationServiceHelper.LogCalculationBegin(); try { OvertoppingOutput overtoppingOutput = CalculateOvertopping(calculation, generalInput, hydraulicBoundaryDatabaseFilePath, usePreprocessor, assessmentSection.FailureMechanismContribution.Norm, failureMechanismContribution, numberOfCalculators); if (canceled) { return; } DikeHeightOutput dikeHeightOutput = CalculateDikeHeight(calculation, assessmentSection, generalInput, failureMechanismContribution, hydraulicBoundaryDatabaseFilePath, usePreprocessor, numberOfCalculators); if (canceled) { return; } OvertoppingRateOutput overtoppingRateOutput = CalculateOvertoppingRate(calculation, assessmentSection, generalInput, failureMechanismContribution, hydraulicBoundaryDatabaseFilePath, usePreprocessor, numberOfCalculators); if (canceled) { return; } calculation.Output = new GrassCoverErosionInwardsOutput( overtoppingOutput, dikeHeightOutput, overtoppingRateOutput); } finally { CalculationServiceHelper.LogCalculationEnd(); overtoppingCalculator = null; dikeHeightCalculator = null; overtoppingRateCalculator = null; } } private int CreateCalculators(GrassCoverErosionInwardsCalculation calculation, string hlcdDirectory, string preprocessorDirectory) { var numberOfCalculators = 1; overtoppingCalculator = HydraRingCalculatorFactory.Instance.CreateOvertoppingCalculator(hlcdDirectory, preprocessorDirectory); if (calculation.InputParameters.DikeHeightCalculationType != DikeHeightCalculationType.NoCalculation) { dikeHeightCalculator = HydraRingCalculatorFactory.Instance.CreateDikeHeightCalculator(hlcdDirectory, preprocessorDirectory); numberOfCalculators++; } if (calculation.InputParameters.OvertoppingRateCalculationType != OvertoppingRateCalculationType.NoCalculation) { overtoppingRateCalculator = HydraRingCalculatorFactory.Instance.CreateOvertoppingRateCalculator(hlcdDirectory, preprocessorDirectory); numberOfCalculators++; } return numberOfCalculators; } /// /// Performs an overtopping calculation. /// /// The calculation containing the input for the overtopping calculation. /// The general grass cover erosion inwards calculation input parameters. /// The path which points to the hydraulic boundary database file. /// Indicator whether to use the preprocessor in the calculation. /// The norm which has been defined on the assessment section. /// The amount of contribution for this failure mechanism in the assessment section /// The total number of calculations to perform. /// A . /// Thrown when an error occurs while performing the calculation. private OvertoppingOutput CalculateOvertopping(GrassCoverErosionInwardsCalculation calculation, GeneralGrassCoverErosionInwardsInput generalInput, string hydraulicBoundaryDatabaseFilePath, bool usePreprocessor, double norm, double failureMechanismContribution, int numberOfCalculators) { NotifyProgress(string.Format(Resources.GrassCoverErosionInwardsCalculationService_Calculate_Executing_calculation_of_type_0, Resources.GrassCoverErosionInwardsCalculationService_Overtopping), 1, numberOfCalculators); OvertoppingCalculationInput overtoppingCalculationInput = CreateOvertoppingInput(calculation, generalInput, hydraulicBoundaryDatabaseFilePath, usePreprocessor); PerformCalculation(() => overtoppingCalculator.Calculate(overtoppingCalculationInput), () => overtoppingCalculator.LastErrorFileContent, () => overtoppingCalculator.OutputDirectory, calculation.Name, Resources.GrassCoverErosionInwardsCalculationService_Overtopping); GeneralResult generalResult = null; try { generalResult = calculation.InputParameters.ShouldOvertoppingOutputIllustrationPointsBeCalculated ? ConvertIllustrationPointsResult(overtoppingCalculator.IllustrationPointsResult, overtoppingCalculator.IllustrationPointsParserErrorMessage) : null; } catch (ArgumentException e) { log.Warn(string.Format(Resources.GrassCoverErosionInwardsCalculationService_CalculateOvertopping_Error_in_reading_illustrationPoints_for_CalculationName_0_overtopping_with_ErrorMessage_1, calculation.Name, e.Message)); } var overtoppingOutput = new OvertoppingOutput(overtoppingCalculator.WaveHeight, overtoppingCalculator.IsOvertoppingDominant, overtoppingCalculator.ExceedanceProbabilityBeta, generalResult); return overtoppingOutput; } private DikeHeightOutput CalculateDikeHeight(GrassCoverErosionInwardsCalculation calculation, IAssessmentSection assessmentSection, GeneralGrassCoverErosionInwardsInput generalInput, double failureMechanismContribution, string hydraulicBoundaryDatabaseFilePath, bool usePreprocessor, int numberOfCalculators) { if (dikeHeightCalculator == null) { return null; } NotifyProgress(string.Format(Resources.GrassCoverErosionInwardsCalculationService_Calculate_Executing_calculation_of_type_0, Resources.GrassCoverErosionInwardsCalculationService_DikeHeight), 2, numberOfCalculators); double norm = calculation.InputParameters.DikeHeightCalculationType == DikeHeightCalculationType.CalculateByAssessmentSectionNorm ? assessmentSection.FailureMechanismContribution.Norm : RingtoetsCommonDataCalculationService.ProfileSpecificRequiredProbability( assessmentSection.FailureMechanismContribution.Norm, failureMechanismContribution, generalInput.N); DikeHeightCalculationInput dikeHeightCalculationInput = CreateDikeHeightInput(calculation, norm, generalInput, hydraulicBoundaryDatabaseFilePath, usePreprocessor); var dikeHeightCalculated = true; try { PerformCalculation(() => dikeHeightCalculator.Calculate(dikeHeightCalculationInput), () => dikeHeightCalculator.LastErrorFileContent, () => dikeHeightCalculator.OutputDirectory, calculation.Name, Resources.GrassCoverErosionInwardsCalculationService_DikeHeight); } catch (HydraRingCalculationException) { dikeHeightCalculated = false; } if (canceled || !dikeHeightCalculated) { return null; } DikeHeightOutput output = CreateDikeHeightOutput(dikeHeightCalculator, calculation.Name, dikeHeightCalculationInput.Beta, norm, calculation.InputParameters.ShouldDikeHeightIllustrationPointsBeCalculated); return output; } private OvertoppingRateOutput CalculateOvertoppingRate(GrassCoverErosionInwardsCalculation calculation, IAssessmentSection assessmentSection, GeneralGrassCoverErosionInwardsInput generalInput, double failureMechanismContribution, string hydraulicBoundaryDatabaseFilePath, bool usePreprocessor, int numberOfCalculators) { if (overtoppingRateCalculator == null) { return null; } NotifyProgress(string.Format(Resources.GrassCoverErosionInwardsCalculationService_Calculate_Executing_calculation_of_type_0, Resources.GrassCoverErosionInwardsCalculationService_OvertoppingRate), numberOfCalculators, numberOfCalculators); double norm = calculation.InputParameters.OvertoppingRateCalculationType == OvertoppingRateCalculationType.CalculateByAssessmentSectionNorm ? assessmentSection.FailureMechanismContribution.Norm : RingtoetsCommonDataCalculationService.ProfileSpecificRequiredProbability( assessmentSection.FailureMechanismContribution.Norm, failureMechanismContribution, generalInput.N); OvertoppingRateCalculationInput overtoppingRateCalculationInput = CreateOvertoppingRateInput(calculation, norm, generalInput, hydraulicBoundaryDatabaseFilePath, usePreprocessor); var overtoppingRateCalculated = true; try { PerformCalculation(() => overtoppingRateCalculator.Calculate(overtoppingRateCalculationInput), () => overtoppingRateCalculator.LastErrorFileContent, () => overtoppingRateCalculator.OutputDirectory, calculation.Name, Resources.GrassCoverErosionInwardsCalculationService_OvertoppingRate); } catch (HydraRingCalculationException) { overtoppingRateCalculated = false; } if (canceled || !overtoppingRateCalculated) { return null; } OvertoppingRateOutput output = CreateOvertoppingRateOutput(overtoppingRateCalculator, calculation.Name, overtoppingRateCalculationInput.Beta, norm, calculation.InputParameters.ShouldOvertoppingRateIllustrationPointsBeCalculated); return output; } /// /// Performs a grass cover erosion inwards calculation. /// /// The action that performs the calculation. /// The function for obtaining the last error file content. /// The function for obtaining the output directory. /// The name of the calculation to perform. /// The name of the step to perform. /// Thrown when an error occurs while performing the calculation. private void PerformCalculation(Action performCalculation, Func getLastErrorFileContent, Func getOutputDirectory, string calculationName, string stepName) { var exceptionThrown = false; try { performCalculation(); } catch (HydraRingCalculationException) { if (!canceled) { string lastErrorFileContent = getLastErrorFileContent(); if (string.IsNullOrEmpty(lastErrorFileContent)) { log.ErrorFormat( Resources.GrassCoverErosionInwardsCalculationService_Calculate_Error_in_calculation_of_type_0_for_calculation_with_name_1_no_error_report, stepName, calculationName); } else { log.ErrorFormat( Resources.GrassCoverErosionInwardsCalculationService_Calculate_Error_in_calculation_of_type_0_for_calculation_with_name_1_click_details_for_last_error_report_2, stepName, calculationName, lastErrorFileContent); } exceptionThrown = true; throw; } } finally { string lastErrorFileContent = getLastErrorFileContent(); bool errorOccurred = CalculationServiceHelper.HasErrorOccurred(canceled, exceptionThrown, lastErrorFileContent); if (errorOccurred) { log.ErrorFormat( Resources.GrassCoverErosionInwardsCalculationService_Calculate_Error_in_calculation_of_type_0_for_calculation_with_name_1_click_details_for_last_error_report_2, stepName, calculationName, lastErrorFileContent); } log.InfoFormat( Resources.GrassCoverErosionInwardsCalculationService_Calculate_Calculation_of_type_0_performed_in_temporary_directory_1, stepName, getOutputDirectory()); if (errorOccurred) { throw new HydraRingCalculationException(lastErrorFileContent); } } } /// /// Creates an instance of for calculation purposes. /// /// The calculation containing the input for the overtopping calculation. /// The general grass cover erosion inwards calculation input parameters. /// The path which points to the hydraulic boundary database file. /// Indicator whether to use the preprocessor in the calculation. /// A new instance. /// Thrown when the /// contains invalid characters. /// Thrown when: /// /// No settings database file could be found at the location of /// with the same name. /// Unable to open settings database file. /// Unable to read required data from database file. /// /// private static OvertoppingCalculationInput CreateOvertoppingInput(GrassCoverErosionInwardsCalculation calculation, GeneralGrassCoverErosionInwardsInput generalInput, string hydraulicBoundaryDatabaseFilePath, bool usePreprocessor) { var overtoppingCalculationInput = new OvertoppingCalculationInput(calculation.InputParameters.HydraulicBoundaryLocation.Id, calculation.InputParameters.Orientation, ParseProfilePoints(calculation.InputParameters.DikeGeometry), HydraRingInputParser.ParseForeshore(calculation.InputParameters), HydraRingInputParser.ParseBreakWater(calculation.InputParameters), calculation.InputParameters.DikeHeight, generalInput.CriticalOvertoppingModelFactor, generalInput.FbFactor.Mean, generalInput.FbFactor.StandardDeviation, generalInput.FbFactor.LowerBoundary, generalInput.FbFactor.UpperBoundary, generalInput.FnFactor.Mean, generalInput.FnFactor.StandardDeviation, generalInput.FnFactor.LowerBoundary, generalInput.FnFactor.UpperBoundary, generalInput.OvertoppingModelFactor, calculation.InputParameters.CriticalFlowRate.Mean, calculation.InputParameters.CriticalFlowRate.StandardDeviation, generalInput.FrunupModelFactor.Mean, generalInput.FrunupModelFactor.StandardDeviation, generalInput.FrunupModelFactor.LowerBoundary, generalInput.FrunupModelFactor.UpperBoundary, generalInput.FshallowModelFactor.Mean, generalInput.FshallowModelFactor.StandardDeviation, generalInput.FshallowModelFactor.LowerBoundary, generalInput.FshallowModelFactor.UpperBoundary); HydraRingSettingsDatabaseHelper.AssignSettingsFromDatabase(overtoppingCalculationInput, hydraulicBoundaryDatabaseFilePath, usePreprocessor); return overtoppingCalculationInput; } /// /// Creates an instance of for calculation purposes. /// /// The calculation containing the input for the dike height calculation. /// The norm to use in the calculation. /// The general grass cover erosion inwards calculation input parameters. /// The path which points to the hydraulic boundary database file. /// Indicator whether to use the preprocessor in the calculation. /// A new instance. /// Thrown when the /// contains invalid characters. /// Thrown when: /// /// No settings database file could be found at the location of /// with the same name. /// Unable to open settings database file. /// Unable to read required data from database file. /// /// private static DikeHeightCalculationInput CreateDikeHeightInput(GrassCoverErosionInwardsCalculation calculation, double norm, GeneralGrassCoverErosionInwardsInput generalInput, string hydraulicBoundaryDatabaseFilePath, bool usePreprocessor) { var dikeHeightCalculationInput = new DikeHeightCalculationInput(calculation.InputParameters.HydraulicBoundaryLocation.Id, norm, calculation.InputParameters.Orientation, ParseProfilePoints(calculation.InputParameters.DikeGeometry), HydraRingInputParser.ParseForeshore(calculation.InputParameters), HydraRingInputParser.ParseBreakWater(calculation.InputParameters), generalInput.CriticalOvertoppingModelFactor, generalInput.FbFactor.Mean, generalInput.FbFactor.StandardDeviation, generalInput.FbFactor.LowerBoundary, generalInput.FbFactor.UpperBoundary, generalInput.FnFactor.Mean, generalInput.FnFactor.StandardDeviation, generalInput.FnFactor.LowerBoundary, generalInput.FnFactor.UpperBoundary, generalInput.OvertoppingModelFactor, calculation.InputParameters.CriticalFlowRate.Mean, calculation.InputParameters.CriticalFlowRate.StandardDeviation, generalInput.FrunupModelFactor.Mean, generalInput.FrunupModelFactor.StandardDeviation, generalInput.FrunupModelFactor.LowerBoundary, generalInput.FrunupModelFactor.UpperBoundary, generalInput.FshallowModelFactor.Mean, generalInput.FshallowModelFactor.StandardDeviation, generalInput.FshallowModelFactor.LowerBoundary, generalInput.FshallowModelFactor.UpperBoundary); HydraRingSettingsDatabaseHelper.AssignSettingsFromDatabase(dikeHeightCalculationInput, hydraulicBoundaryDatabaseFilePath, usePreprocessor); return dikeHeightCalculationInput; } /// /// Creates an instance of for calculation purposes. /// /// The calculation containing the input for the overtopping rate calculation. /// The norm to use in the calculation. /// The general grass cover erosion inwards calculation input parameters. /// The path which points to the hydraulic boundary database file. /// Indicator whether to use the preprocessor in the calculation. /// A new instance. /// Thrown when the /// contains invalid characters. /// Thrown when: /// /// No settings database file could be found at the location of /// with the same name. /// Unable to open settings database file. /// Unable to read required data from database file. /// /// private static OvertoppingRateCalculationInput CreateOvertoppingRateInput(GrassCoverErosionInwardsCalculation calculation, double norm, GeneralGrassCoverErosionInwardsInput generalInput, string hydraulicBoundaryDatabaseFilePath, bool usePreprocessor) { var overtoppingRateCalculationInput = new OvertoppingRateCalculationInput(calculation.InputParameters.HydraulicBoundaryLocation.Id, norm, calculation.InputParameters.Orientation, ParseProfilePoints(calculation.InputParameters.DikeGeometry), HydraRingInputParser.ParseForeshore(calculation.InputParameters), HydraRingInputParser.ParseBreakWater(calculation.InputParameters), calculation.InputParameters.DikeHeight, generalInput.CriticalOvertoppingModelFactor, generalInput.FbFactor.Mean, generalInput.FbFactor.StandardDeviation, generalInput.FbFactor.LowerBoundary, generalInput.FbFactor.UpperBoundary, generalInput.FnFactor.Mean, generalInput.FnFactor.StandardDeviation, generalInput.FnFactor.LowerBoundary, generalInput.FnFactor.UpperBoundary, generalInput.OvertoppingModelFactor, generalInput.FrunupModelFactor.Mean, generalInput.FrunupModelFactor.StandardDeviation, generalInput.FrunupModelFactor.LowerBoundary, generalInput.FrunupModelFactor.UpperBoundary, generalInput.FshallowModelFactor.Mean, generalInput.FshallowModelFactor.StandardDeviation, generalInput.FshallowModelFactor.LowerBoundary, generalInput.FshallowModelFactor.UpperBoundary); HydraRingSettingsDatabaseHelper.AssignSettingsFromDatabase(overtoppingRateCalculationInput, hydraulicBoundaryDatabaseFilePath, usePreprocessor); return overtoppingRateCalculationInput; } /// /// Creates the output of a dike height calculation. /// /// The calculator used for performing the calculation. /// The name of the calculation. /// The target reliability for the calculation. /// The target probability for the calculation. /// Indicates whether the illustration points /// should be calculated for the calculation. /// A . /// Thrown when /// or the calculated probability falls outside the [0.0, 1.0] range and is not . private static DikeHeightOutput CreateDikeHeightOutput(IHydraulicLoadsCalculator calculator, string calculationName, double targetReliability, double targetProbability, bool shouldIllustrationPointsBeCalculated) { double dikeHeight = calculator.Value; double reliability = calculator.ReliabilityIndex; double probability = StatisticsConverter.ReliabilityToProbability(reliability); CalculationConvergence converged = RingtoetsCommonDataCalculationService.GetCalculationConvergence(calculator.Converged); if (converged != CalculationConvergence.CalculatedConverged) { log.Warn( string.Format(Resources.GrassCoverErosionInwardsCalculationService_Calculation_of_type_0_for_calculation_with_name_1_not_converged, Resources.GrassCoverErosionInwardsCalculationService_DikeHeight, calculationName)); } GeneralResult generalResult = null; try { generalResult = shouldIllustrationPointsBeCalculated ? ConvertIllustrationPointsResult(calculator.IllustrationPointsResult, calculator.IllustrationPointsParserErrorMessage) : null; } catch (ArgumentException e) { log.Warn(string.Format(Resources.GrassCoverErosionInwardsCalculationService_CalculateOvertopping_Error_in_reading_illustrationPoints_for_CalculationName_0_dike_height_with_ErrorMessage_1, calculationName, e.Message)); } return new DikeHeightOutput(dikeHeight, targetProbability, targetReliability, probability, reliability, converged, generalResult); } /// /// Creates the output of an overtopping rate calculation. /// /// The calculator used for performing the calculation. /// The name of the calculation. /// The target reliability for the calculation. /// The target probability for the calculation. /// Indicates whether the illustration points /// should be calculated for the calculation. /// A . /// Thrown when /// or the calculated probability falls outside the [0.0, 1.0] range and is not . private static OvertoppingRateOutput CreateOvertoppingRateOutput(IHydraulicLoadsCalculator calculator, string calculationName, double targetReliability, double targetProbability, bool shouldIllustrationPointsBeCalculated) { double overtoppingRate = calculator.Value; double reliability = calculator.ReliabilityIndex; double probability = StatisticsConverter.ReliabilityToProbability(reliability); CalculationConvergence converged = RingtoetsCommonDataCalculationService.GetCalculationConvergence(calculator.Converged); if (converged != CalculationConvergence.CalculatedConverged) { log.Warn( string.Format(Resources.GrassCoverErosionInwardsCalculationService_Calculation_of_type_0_for_calculation_with_name_1_not_converged, Resources.GrassCoverErosionInwardsCalculationService_OvertoppingRate, calculationName)); } GeneralResult generalResult = null; try { generalResult = shouldIllustrationPointsBeCalculated ? ConvertIllustrationPointsResult(calculator.IllustrationPointsResult, calculator.IllustrationPointsParserErrorMessage) : null; } catch (ArgumentException e) { log.Warn(string.Format(Resources.GrassCoverErosionInwardsCalculationService_CalculateOvertopping_Error_in_reading_illustrationPoints_for_CalculationName_0_overtopping_rate_with_ErrorMessage_1, calculationName, e.Message)); } return new OvertoppingRateOutput(overtoppingRate, targetProbability, targetReliability, probability, reliability, converged, generalResult); } private static IEnumerable ParseProfilePoints(IEnumerable roughnessProfilePoints) { return roughnessProfilePoints.Select(roughnessPoint => new HydraRingRoughnessProfilePoint(roughnessPoint.Point.X, roughnessPoint.Point.Y, roughnessPoint.Roughness)).ToArray(); } private static string[] ValidateInput(GrassCoverErosionInwardsInput inputParameters, IAssessmentSection assessmentSection) { var validationResults = new List(); string preprocessorDirectory = assessmentSection.HydraulicBoundaryDatabase.EffectivePreprocessorDirectory(); string databaseFilePathValidationProblem = HydraulicBoundaryDatabaseHelper.ValidateFilesForCalculation(assessmentSection.HydraulicBoundaryDatabase.FilePath, preprocessorDirectory); if (!string.IsNullOrEmpty(databaseFilePathValidationProblem)) { validationResults.Add(databaseFilePathValidationProblem); } string preprocessorDirectoryValidationProblem = HydraulicBoundaryDatabaseHelper.ValidatePreprocessorDirectory(preprocessorDirectory); if (!string.IsNullOrEmpty(preprocessorDirectoryValidationProblem)) { validationResults.Add(preprocessorDirectoryValidationProblem); } if (validationResults.Any()) { return validationResults.ToArray(); } if (inputParameters.HydraulicBoundaryLocation == null) { validationResults.Add(RingtoetsCommonServiceResources.CalculationService_ValidateInput_No_hydraulic_boundary_location_selected); } if (inputParameters.DikeProfile == null) { validationResults.Add(RingtoetsCommonServiceResources.CalculationService_ValidateInput_No_dike_profile_selected); } else { validationResults.AddRange(new NumericInputRule(inputParameters.Orientation, ParameterNameExtractor.GetFromDisplayName(RingtoetsCommonForms.Orientation_DisplayName)).Validate()); validationResults.AddRange(new NumericInputRule(inputParameters.DikeHeight, ParameterNameExtractor.GetFromDisplayName(RingtoetsCommonForms.DikeHeight_DisplayName)).Validate()); } validationResults.AddRange(new UseBreakWaterRule(inputParameters).Validate()); return validationResults.ToArray(); } private static GeneralResult ConvertIllustrationPointsResult(HydraRingGeneralResult result, string errorMessage) { if (result == null) { log.Warn(errorMessage); return null; } try { GeneralResult generalResult = GeneralResultConverter.ConvertToGeneralResultTopLevelFaultTreeIllustrationPoint(result); return generalResult; } catch (IllustrationPointConversionException e) { log.Warn(RingtoetsCommonServiceResources.SetGeneralResult_Converting_IllustrationPointResult_Failed, e); } return null; } private void NotifyProgress(string stepName, int currentStepNumber, int totalStepNumber) { OnProgress?.Invoke(stepName, currentStepNumber, totalStepNumber); } } }