// 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
{
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;
}
}
}