// 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.Calculation; using Ringtoets.Common.Data.DikeProfiles; using Ringtoets.Common.Data.Hydraulics; using Ringtoets.Common.Data.Probabilistics; using Ringtoets.Common.Data.Structures; using Ringtoets.Common.IO; using Ringtoets.Common.IO.Configurations; using Ringtoets.Common.IO.Configurations.Helpers; using Ringtoets.Common.IO.FileImporters; using Ringtoets.Common.IO.Schema; using Ringtoets.HeightStructures.Data; using RingtoetsCommonIOResources = Ringtoets.Common.IO.Properties.Resources; namespace Ringtoets.HeightStructures.IO { /// /// Class for importing a configuration of from an XML file and storing /// it on a . /// public class HeightStructuresCalculationConfigurationImporter : CalculationConfigurationImporter { private readonly IEnumerable availableHydraulicBoundaryLocations; private readonly IEnumerable availableForeshoreProfiles; private readonly IEnumerable availableStructures; /// /// Create new instance of /// /// The path to the XML file to import from. /// The calculation group to update. /// The hydraulic boundary locations /// used to check if the imported objects contain the right location. /// The foreshore profiles used to check if /// the imported objects contain the right foreshore profile. /// The dike profiles used to check if /// the imported objects contain the right profile. public HeightStructuresCalculationConfigurationImporter( string xmlFilePath, CalculationGroup importTarget, IEnumerable hydraulicBoundaryLocations, IEnumerable foreshoreProfiles, IEnumerable structures) : base(xmlFilePath, importTarget) { if (hydraulicBoundaryLocations == null) { throw new ArgumentNullException(nameof(hydraulicBoundaryLocations)); } if (foreshoreProfiles == null) { throw new ArgumentNullException(nameof(foreshoreProfiles)); } if (structures == null) { throw new ArgumentNullException(nameof(structures)); } availableHydraulicBoundaryLocations = hydraulicBoundaryLocations; availableForeshoreProfiles = foreshoreProfiles; availableStructures = structures; } protected override HeightStructuresCalculationConfigurationReader CreateCalculationConfigurationReader(string xmlFilePath) { return new HeightStructuresCalculationConfigurationReader(xmlFilePath); } protected override ICalculation ParseReadCalculation(HeightStructuresCalculationConfiguration readCalculation) { var calculation = new StructuresCalculation() { Name = readCalculation.Name }; if (TryReadStructure(readCalculation, calculation) && TryReadStochasts(readCalculation, calculation) && TryReadHydraulicBoundaryLocation(readCalculation, calculation) && TryReadDikeProfile(readCalculation, calculation) && TryReadOrientation(readCalculation, calculation) && TryReadFailureProbabilityStructureWithErosion(readCalculation, calculation) && TryReadWaveReduction(readCalculation, calculation)) { return calculation; } return null; } private bool TryReadStochasts(HeightStructuresCalculationConfiguration readCalculation, StructuresCalculation calculation) { if (!ValidateStochasts(readCalculation)) { return false; } return TryReadStandardDeviationStochast( calculation, HeightStructuresConfigurationSchemaIdentifiers.LevelCrestStructureStochastName, readCalculation.LevelCrestStructure, i => i.LevelCrestStructure, (i, d) => i.LevelCrestStructure = d) && TryReadStandardDeviationStochast( calculation, ConfigurationSchemaIdentifiers.AllowedLevelIncreaseStorageStochastName, readCalculation.AllowedLevelIncreaseStorage, i => i.AllowedLevelIncreaseStorage, (i, d) => i.AllowedLevelIncreaseStorage = d) && TryReadStandardDeviationStochast( calculation, ConfigurationSchemaIdentifiers.FlowWidthAtBottomProtectionStochastName, readCalculation.FlowWidthAtBottomProtection, i => i.FlowWidthAtBottomProtection, (i, d) => i.FlowWidthAtBottomProtection = d) && TryReadStandardDeviationStochast( calculation, ConfigurationSchemaIdentifiers.ModelFactorSuperCriticalFlowStochastName, readCalculation.ModelFactorSuperCriticalFlow, i => i.ModelFactorSuperCriticalFlow, (i, d) => i.ModelFactorSuperCriticalFlow = d) && TryReadStandardDeviationStochast( calculation, ConfigurationSchemaIdentifiers.WidthFlowAperturesStochastName, readCalculation.WidthFlowApertures, i => i.WidthFlowApertures, (i, d) => i.WidthFlowApertures = d) && TryReadVariationCoefficientStochast( calculation, ConfigurationSchemaIdentifiers.CriticalOvertoppingDischargeStochastName, readCalculation.CriticalOvertoppingDischarge, i => i.CriticalOvertoppingDischarge, (i, d) => i.CriticalOvertoppingDischarge = d) && TryReadVariationCoefficientStochast( calculation, ConfigurationSchemaIdentifiers.StorageStructureAreaStochastName, readCalculation.StorageStructureArea, i => i.StorageStructureArea, (i, d) => i.StorageStructureArea = d) && TryReadVariationCoefficientStochast( calculation, ConfigurationSchemaIdentifiers.StormDurationStochastName, readCalculation.StormDuration, i => i.StormDuration, (i, d) => i.StormDuration = d); } private bool ValidateStochasts(HeightStructuresCalculationConfiguration readCalculation) { if (readCalculation.StormDuration?.VariationCoefficient != null) { LogReadCalculationConversionError("Er kan geen variatiecoëfficiënt voor stochast 'stormduur' opgegeven worden.", readCalculation.Name); return false; } if (readCalculation.ModelFactorSuperCriticalFlow?.StandardDeviation != null) { LogReadCalculationConversionError("Er kan geen standaardafwijking voor stochast 'modelfactoroverloopdebiet' opgegeven worden.", readCalculation.Name); return false; } return true; } /// /// Reads the stochast parameters. /// /// The calculation to configure. /// The stochast's name. /// The configuration of the stochast. /// The function for obtaining the stochast to read. /// The function to set the stochast with the read parameters. /// true if reading all required wave reduction parameters was successful, /// false otherwise. private bool TryReadStandardDeviationStochast( StructuresCalculation calculation, string stochastName, MeanStandardDeviationStochastConfiguration stochastConfiguration, Func getStochast, Action setStochast) where T : class, IDistribution { if (stochastConfiguration == null) { return true; } var distribution = (T) getStochast(calculation.InputParameters).Clone(); if (!distribution.TrySetDistributionProperties(stochastConfiguration, stochastName, calculation.Name)) { return false; } setStochast(calculation.InputParameters, distribution); return true; } /// /// Reads the stochast parameters. /// /// The calculation to configure. /// The stochast's name. /// The configuration of the stochast. /// The function for obtaining the stochast to read. /// The function to set the stochast with the read parameters. /// true if reading all required wave reduction parameters was successful, /// false otherwise. private bool TryReadVariationCoefficientStochast( StructuresCalculation calculation, string stochastName, MeanVariationCoefficientStochastConfiguration stochastConfiguration, Func getStochast, Action setStochast) where T : class, IVariationCoefficientDistribution { if (stochastConfiguration == null) { return true; } var distribution = (T) getStochast(calculation.InputParameters).Clone(); if (!distribution.TrySetDistributionProperties(stochastConfiguration, stochastName, calculation.Name)) { return false; } setStochast(calculation.InputParameters, distribution); return true; } /// /// Reads the orientation. /// /// The calculation read from the imported file. /// The calculation to configure. /// false when the orientation is invalid or when there is an orientation but /// no structure defined, true otherwise. private bool TryReadOrientation(StructuresCalculationConfiguration readCalculation, StructuresCalculation calculation) { if (readCalculation.StructureNormalOrientation.HasValue) { if (calculation.InputParameters.Structure == null) { LogReadCalculationConversionError( "Er is geen kunstwerk opgegeven om de oriëntatie aan toe te voegen.", calculation.Name); return false; } double orientation = readCalculation.StructureNormalOrientation.Value; try { calculation.InputParameters.StructureNormalOrientation = (RoundedDouble) orientation; } catch (ArgumentOutOfRangeException e) { LogOutOfRangeException( string.Format("Een waarde van '{0}' als oriëntatie is ongeldig.", orientation), calculation.Name, e); return false; } } return true; } /// /// Reads the failure probability structure with erosion. /// /// The calculation read from the imported file. /// The calculation to configure. /// false when the orientation is invalid or when there is a failure probability /// structure with erosion but no structure defined, true otherwise. private bool TryReadFailureProbabilityStructureWithErosion(StructuresCalculationConfiguration readCalculation, StructuresCalculation calculation) { if (readCalculation.FailureProbabilityStructureWithErosion.HasValue) { if (calculation.InputParameters.Structure == null) { LogReadCalculationConversionError( "Er is geen kunstwerk opgegeven om de faalkans gegeven erosie bodem aan toe te voegen.", calculation.Name); return false; } double failureProbability = readCalculation.FailureProbabilityStructureWithErosion.Value; try { calculation.InputParameters.FailureProbabilityStructureWithErosion = (RoundedDouble) failureProbability; } catch (ArgumentOutOfRangeException e) { LogOutOfRangeException( string.Format("Een waarde van '{0}' als faalkans gegeven erosie bodem is ongeldig.", failureProbability), calculation.Name, e); return false; } } return true; } /// /// Reads the hydraulic boundary location. /// /// The calculation read from the imported file. /// The calculation to configure. /// false when the has a /// set which is not available in , true otherwise. private bool TryReadHydraulicBoundaryLocation(StructuresCalculationConfiguration readCalculation, StructuresCalculation calculation) { if (readCalculation.HydraulicBoundaryLocationName != null) { HydraulicBoundaryLocation location = availableHydraulicBoundaryLocations .FirstOrDefault(l => l.Name == readCalculation.HydraulicBoundaryLocationName); if (location == null) { LogReadCalculationConversionError( string.Format( RingtoetsCommonIOResources.CalculationConfigurationImporter_ReadHydraulicBoundaryLocation_HydraulicBoundaryLocation_0_does_not_exist, readCalculation.HydraulicBoundaryLocationName), calculation.Name); return false; } calculation.InputParameters.HydraulicBoundaryLocation = location; } return true; } /// /// Reads the hydraulic boundary location. /// /// The calculation read from the imported file. /// The calculation to configure. /// false when the has a /// set which is not available in , true otherwise. private bool TryReadStructure(StructuresCalculationConfiguration readCalculation, StructuresCalculation calculation) { if (readCalculation.StructureName != null) { HeightStructure structure = availableStructures .FirstOrDefault(l => l.Name == readCalculation.StructureName); if (structure == null) { LogReadCalculationConversionError( string.Format( RingtoetsCommonIOResources.CalculationConfigurationImporter_ReadStructure_Structure_0_does_not_exist, readCalculation.StructureName), calculation.Name); return false; } calculation.InputParameters.Structure = structure; } return true; } /// /// Reads the dike profile. /// /// The calculation read from the imported file. /// The calculation to configure. /// false when the has a /// set which is not available in , true otherwise. private bool TryReadDikeProfile(StructuresCalculationConfiguration readCalculation, StructuresCalculation calculation) { if (readCalculation.ForeshoreProfileName != null) { ForeshoreProfile foreshoreProfile = availableForeshoreProfiles.FirstOrDefault(fp => fp.Name == readCalculation.ForeshoreProfileName); if (foreshoreProfile == null) { LogReadCalculationConversionError( string.Format( "Het voorlandprofiel '{0}' bestaat niet.", readCalculation.ForeshoreProfileName), calculation.Name); return false; } calculation.InputParameters.ForeshoreProfile = foreshoreProfile; } return true; } /// /// Reads the wave reduction parameters. /// /// The calculation read from the imported file. /// The calculation to configure. /// false when there is an invalid wave reduction parameter defined, true otherwise. private bool TryReadWaveReduction(StructuresCalculationConfiguration readCalculation, StructuresCalculation calculation) { if (!ValidateWaveReduction(readCalculation, calculation)) { return false; } WaveReductionConfiguration waveReduction = readCalculation.WaveReduction; if (waveReduction == null) { return true; } if (waveReduction.UseForeshoreProfile.HasValue) { calculation.InputParameters.UseForeshore = waveReduction.UseForeshoreProfile.Value; } if (waveReduction.UseBreakWater.HasValue) { calculation.InputParameters.UseBreakWater = waveReduction.UseBreakWater.Value; } if (waveReduction.BreakWaterType.HasValue) { calculation.InputParameters.BreakWater.Type = (BreakWaterType) new SchemaBreakWaterTypeConverter().ConvertTo(waveReduction.BreakWaterType.Value, typeof(BreakWaterType)); } if (waveReduction.BreakWaterHeight.HasValue) { calculation.InputParameters.BreakWater.Height = (RoundedDouble) waveReduction.BreakWaterHeight.Value; } return true; } /// /// Validation to check if the defined wave reduction parameters are valid. /// /// The calculation read from the imported file. /// The calculation to configure. /// false when there is an invalid wave reduction parameter defined, true otherwise. private bool ValidateWaveReduction(StructuresCalculationConfiguration readCalculation, StructuresCalculation calculation) { if (calculation.InputParameters.ForeshoreProfile == null) { if (readCalculation.WaveReduction != null && (readCalculation.WaveReduction.UseBreakWater.HasValue || readCalculation.WaveReduction.UseForeshoreProfile.HasValue || readCalculation.WaveReduction.BreakWaterHeight.HasValue || readCalculation.WaveReduction.BreakWaterType.HasValue)) { LogReadCalculationConversionError( "Er is geen voorlandprofiel opgegeven om golfreductie parameters aan toe te voegen.", calculation.Name); return false; } } else if (!calculation.InputParameters.ForeshoreGeometry.Any()) { if (readCalculation.WaveReduction.UseForeshoreProfile.HasValue && readCalculation.WaveReduction.UseForeshoreProfile.Value) { LogReadCalculationConversionError( string.Format( "Het opgegeven voorlandprofiel '{0}' heeft geen geometrie en kan daarom niet gebruikt worden.", readCalculation.ForeshoreProfileName), calculation.Name); return false; } } return true; } } }