Index: DamEngine/trunk/src/Deltares.DamEngine.Data/General/DAMEnumerations.cs =================================================================== diff -u -r1192 -r1640 --- DamEngine/trunk/src/Deltares.DamEngine.Data/General/DAMEnumerations.cs (.../DAMEnumerations.cs) (revision 1192) +++ DamEngine/trunk/src/Deltares.DamEngine.Data/General/DAMEnumerations.cs (.../DAMEnumerations.cs) (revision 1640) @@ -274,9 +274,7 @@ /// public enum TimeSerieParameters { - [Obsolete("Use StabilityInsideFactor instead")] - SafetyFactor, // <- Is same as StabilityInsideFactor, kept for legacy code - PipingFactorWti, + PipingFactorWbiSellmeijerRevised, PipingFactorBligh, PipingFactorSellmeijer, ProbabilityOfFailurePipingSellmeijer, Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/DikesOperational/OperationalCalculator.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/DikesOperational/OperationalCalculator.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/DikesOperational/OperationalCalculator.cs (revision 1640) @@ -0,0 +1,334 @@ +// Copyright (C) Stichting Deltares 2018. All rights reserved. +// +// This file is part of the Dam Engine. +// +// The Dam Engine is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero 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.Diagnostics; +using System.Linq; +using Deltares.DamEngine.Calculators.DikesDesign; +using Deltares.DamEngine.Calculators.Sensors; +using Deltares.DamEngine.Data.General; +using Deltares.DamEngine.Data.General.Results; +using Deltares.DamEngine.Data.General.Specifications.Extensions; +using Deltares.DamEngine.Data.General.Specifications; +using Deltares.DamEngine.Data.General.TimeSeries; +using Deltares.DamEngine.Data.General.Sensors; +using Deltares.DamEngine.Data.Standard; +using Deltares.DamEngine.Data.Standard.Logging; + +namespace Deltares.DamEngine.Calculators.DikesOperational +{ + public class OperationalCalculator + { + /// + /// Specifies a location with valid sensor data. The objects that are satisfied by the + /// the specification (predicate) will be used in this calculator. + /// See ReadSensorDataAndPrepareOutputSeries and Locations.GetBySpecification(p) + /// + private class LocationsWithSensorData : PredicateSpecification + { + public LocationsWithSensorData() + : base(l => l.HasSensorLocation && l.SensorLocation.SensorCount > 0) + { + } + } + /// + /// Holds a reference to a dictionary of output date time entries + /// The keys represent the time step and the value part of the dictionary represents + /// the argument for the calculation + /// + /// TimeStep -> Location -> (Sensor, Value) + /// + /// Cardinality: + /// 1 -> N -> M + /// + private readonly IDictionary>> values = + new Dictionary>>(); + + private TimeSerieCollection inputTimeSerieCollection; + private TimeSerieCollection outputTimeSerieCollection; + private string outputParameter; + + /// + /// Executes operational calculation. + /// + /// The dam project data. + public void Execute(DamProjectData damProjectData) + { + outputParameter = DetermineOutputParameter(damProjectData.DamProjectCalculationSpecification.CurrentSpecification); + // counter to determine if locations are processed + int locationCount = 0; + + inputTimeSerieCollection = damProjectData.Dike.InputTimeSerieCollection; + outputTimeSerieCollection = new TimeSerieCollection(); + + var locations = damProjectData.Dike.Locations.GetBySpecification(new LocationsWithSensorData()); + foreach (var location in locations) + { + PrepareSensorDataLookup(location); + InitializeOutputSeries(location); + locationCount++; + } + damProjectData.CalculationMessages.Add(new LogMessage(LogMessageType.Info, null, + string.Format("There are {0} locations with sensor data", locationCount))); + if (!locations.Any()) + { + damProjectData.CalculationMessages.Add(new LogMessage(LogMessageType.Error, null, "No location to process.")); + return; + } + var designCalculatorTasks = new List(); + foreach (var series in outputTimeSerieCollection.Series) + { + foreach (var entry in series.Entries) + { + var location = locations.First(l => series.LocationId == l.Name); + var sensorValues = values[entry.DateTime][location]; + if (!ContainsMissingValues(sensorValues, series.MissVal)) + { + //var soiProfileProbability = location.Segment.GetMostProbableProfile2DStiFileName() + + } + else + { + damProjectData.CalculationMessages.Add(new LogMessage(LogMessageType.Warning, null, + String.Format("In location '{0}' missing values are found in timestep {1}", location.Name, entry.DateTime))); + } + } + } + damProjectData.OutputTimeSerieCollection = outputTimeSerieCollection; + } + + private void RunDesignCalculatorTask(object designCalculatorTask) + { + DesignCalculatorTask task = (DesignCalculatorTask)designCalculatorTask; + Debug.WriteLine("Start calculation Location '{0}', soilprofile '{1}'", task.Location, task.SoiProfileProbability); + CalculateOneScenario(task.Location, task.SoiProfileProbability, task.ProjectPath, + task.CalculationMap, task.FailureMechanismeCalculationSpecification, task.DesignResults, task.CalculationMessages); + Debug.WriteLine("End calculation Location '{0}', soilprofile '{1}'", task.Location, task.SoiProfileProbability); + } + private void CalculateOneScenario(Location location, SoilGeometryProbability soiProfileProbability, + string projectPath, string calculationMap, + DamFailureMechanismeCalculationSpecification damFailureMechanismeCalculationSpecification, + List designResults, List calculationMessages) + { + + } + private string DetermineOutputParameter(DamFailureMechanismeCalculationSpecification currentSpecification) + { + string parameter = ""; + switch (currentSpecification.FailureMechanismSystemType) + { + case FailureMechanismSystemType.HorizontalBalance: + throw new NotImplementedException(); + case FailureMechanismSystemType.StabilityOutside: + throw new NotImplementedException(); + case FailureMechanismSystemType.StabilityInside: + parameter = TimeSerieParameters.StabilityInsideFactor.ToString(); + break; + case FailureMechanismSystemType.Piping: + switch (currentSpecification.PipingModelType) + { + case PipingModelType.Bligh: + throw new NotImplementedException(); + case PipingModelType.SellmeijerVnk: + throw new NotImplementedException(); + case PipingModelType.Sellmeijer4Forces: + throw new NotImplementedException(); + case PipingModelType.Wti2017: + throw new NotImplementedException(); + default: + throw new NotImplementedException(); + } + } + return parameter; + } + + /// + /// Initializes the output series. + /// + private void InitializeOutputSeries(Location location) + { + var firstTimeSeries = inputTimeSerieCollection.Series.First(); + var copyOfSeries = firstTimeSeries.GetShallowCopy(); + foreach (var entry in firstTimeSeries.Entries) + { + // get copy of the input entry + var copyOfEntry = entry.GetShallowCopy(); + + // set parameter and location id's and add the projected entry to the output + copyOfSeries.LocationId = location.Name; + copyOfSeries.ParameterId = outputParameter; + copyOfSeries.Entries.Add(copyOfEntry); + } + + // add the output series to the output collection + outputTimeSerieCollection.Series.Add(copyOfSeries); + } + + /// + /// Prepares the output time series and the calculation arguments (sensor data). + /// + /// The location. + private void PrepareSensorDataLookup(Location location) + { + ThrowIfSensorsAreMissingInInputFile(location); + + // these variable are used to determine differences in time series entries + var firstSeriesEntries = new HashSet(); + bool hasFirstSeriesEntries = false; + + foreach (var sensor in location.SensorLocation.Sensors) + { + IEnumerable series = inputTimeSerieCollection.GetSeriesByLocationId(sensor.Name); + ThrowIfSensorNotExists(sensor, series.Any()); + + // Prepare the output time series and set sensor values + foreach (var timeSeries in series) + { + ThrowIfTimeEntryCountDontMatch(firstSeriesEntries, timeSeries, hasFirstSeriesEntries); + + foreach (var entry in timeSeries.Entries) + { + var key = entry.DateTime; + if (hasFirstSeriesEntries) + { + ThrowIfTimeEntriesKeysDontMatch(key, firstSeriesEntries); + } + + if (!hasFirstSeriesEntries) + { + firstSeriesEntries.Add(key); + } + + // everything ok set data into internal lookup + SetSensorValue(key, entry.Value, sensor, location); + } + } + hasFirstSeriesEntries = true; + } + } + + /// + /// Sets the sensor value. + /// + /// The time step. + /// The value. + /// The sensor. + /// The location. + private void SetSensorValue(DateTime timeStep, double value, Sensor sensor, Location location) + { + if (!values.ContainsKey(timeStep)) + values.Add(timeStep, new Dictionary>()); + + if (!values[timeStep].ContainsKey(location)) + values[timeStep].Add(location, new Dictionary()); + + if (values[timeStep][location].ContainsKey(sensor)) + { + var message = string.Format("Values for sensor with id {0} and name {1} and location {2} and time step {3} already exists. Check the time series data.", + sensor.ID, sensor.Name, location.Name, timeStep); + + throw new SensorTimeSeriesProcessorException(message); + } + + values[timeStep][location].Add(sensor, value); + } + + /// + /// Throws when the sensor not exists. + /// + /// The sensor. + /// if set to true [has series by sensor ID]. + private static void ThrowIfSensorNotExists(Sensor sensor, bool hasSeriesBySensorID) + { + if (!hasSeriesBySensorID) + { + // TODO log info + string message = + string.Format("Sensor with name '{0}' and parameter id '{1}' not found in the input time series", + sensor.Name, TimeSerie.WaterPressureParameterId); + + throw new SensorTimeSeriesProcessorException(message); + } + } + + /// + /// Throws if time entry count dont match. + /// + /// The first series entries. + /// The time series. + /// if set to true [has first series entries]. + /// Invalid data in time series entries. Number of entries differ on each sensor + private static void ThrowIfTimeEntryCountDontMatch(HashSet firstSeriesEntries, TimeSerie timeSeries, + bool hasFirstSeriesEntries) + { + if (hasFirstSeriesEntries) + { + // TODO log info + if (timeSeries.Entries.Count != firstSeriesEntries.Count) + throw new SensorTimeSeriesProcessorException("Invalid data in time series entries. Number of entries differ on each sensor"); + } + } + + private void ThrowIfSensorsAreMissingInInputFile(Location location) + { + var sensorsNotFound = ( + from sensor in location.SensorLocation.Sensors + let series = inputTimeSerieCollection.GetSeriesByLocationId(sensor.Name) + where !series.Any() + select sensor.Name).ToList(); + + if (sensorsNotFound.Any()) + { + // TODO log info + string message = + string.Format("Sensor with name '{0}' and parameter id '{1}' not found in the input time series", + string.Join(", ", sensorsNotFound.ToArray()), TimeSerie.WaterPressureParameterId); + + throw new SensorTimeSeriesProcessorException(message); + } + } + + private static void ThrowIfTimeEntriesKeysDontMatch(DateTime key, HashSet firstSeriesEntries) + { + // TODO log info + if (!firstSeriesEntries.Contains(key)) + throw new SensorTimeSeriesProcessorException("Invalid data in time series entries. Time entries (date time values) don't match"); + } + /// + /// Determines whether any of sensor values contains a missing value. + /// + /// The sensor values. + /// + /// + private bool ContainsMissingValues(IDictionary sensorValues, double missingValue) + { + foreach (var sensorValue in sensorValues) + { + if (sensorValues[sensorValue.Key].AlmostEquals(missingValue)) + { + return true; + } + } + return false; + } + } +} Index: DamEngine/trunk/src/Deltares.DamEngine.Interface.Tests/TestFiles/OperationalSet2.xml =================================================================== diff -u -r1635 -r1640 --- DamEngine/trunk/src/Deltares.DamEngine.Interface.Tests/TestFiles/OperationalSet2.xml (.../OperationalSet2.xml) (revision 1635) +++ DamEngine/trunk/src/Deltares.DamEngine.Interface.Tests/TestFiles/OperationalSet2.xml (.../OperationalSet2.xml) (revision 1640) @@ -1,28 +1,28 @@ - + - + - + - + @@ -195,63 +195,93 @@ - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + - + + + Index: DamEngine/trunk/src/Deltares.DamEngine.Interface/EngineInterface.cs =================================================================== diff -u -r1632 -r1640 --- DamEngine/trunk/src/Deltares.DamEngine.Interface/EngineInterface.cs (.../EngineInterface.cs) (revision 1632) +++ DamEngine/trunk/src/Deltares.DamEngine.Interface/EngineInterface.cs (.../EngineInterface.cs) (revision 1640) @@ -23,6 +23,7 @@ using System.Collections.Generic; using Deltares.DamEngine.Calculators.DikesAssessmentRegional; using Deltares.DamEngine.Calculators.DikesDesign; +using Deltares.DamEngine.Calculators.DikesOperational; using Deltares.DamEngine.Data.General; using Deltares.DamEngine.Data.Standard.Calculation; using Deltares.DamEngine.Data.Standard.Logging; @@ -191,7 +192,9 @@ regionalScenariosCalculator.GetResults(); break; case DamProjectType.Operational: - throw new NotImplementedException(); + var operationalCalculator = new OperationalCalculator(); + operationalCalculator.Execute(DamProjectData); + break; case DamProjectType.Design: DesignCalculator designCalculator = new DesignCalculator(); designCalculator.RegisterProgress(progressDelegate); Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/Sensors/SensorTimeSeriesProcessor.cs =================================================================== diff -u -r1602 -r1640 --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/Sensors/SensorTimeSeriesProcessor.cs (.../SensorTimeSeriesProcessor.cs) (revision 1602) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/Sensors/SensorTimeSeriesProcessor.cs (.../SensorTimeSeriesProcessor.cs) (revision 1640) @@ -24,7 +24,6 @@ using System.IO; using System.Linq; using Deltares.Dam.Data.Sensors; -using Deltares.DamEngine.Calculators.KernelWrappers.DamMacroStabilityCommon; using Deltares.DamEngine.Calculators.PlLinesCreator; using Deltares.DamEngine.Data.General; using Deltares.DamEngine.Data.General.PlLines; @@ -33,7 +32,6 @@ using Deltares.DamEngine.Data.General.Specifications.Extensions; using Deltares.DamEngine.Data.General.TimeSeries; using Deltares.DamEngine.Data.Standard; -using log4net.Repository.Hierarchy; namespace Deltares.DamEngine.Calculators.Sensors { @@ -184,7 +182,7 @@ // requiredSafetyFactorPiping, location.GaugePlLines, location.Gauges, location.UpliftCriterionPiping.Value); // PlLines = CreateAllPlLines(sensorValues, sensorSetup, lineTypes); // double? pipingFactor = pipingCalculatorWti2017.CalculatePipingFactor(location, location.SurfaceLine, -// location.Segment.GetMostProbableProfile(FailureMechanismSystemType.Piping), PlLines); +// location.Segment.GetMostProbableProfile1D(FailureMechanismSystemType.Piping), PlLines); // entry.Value = pipingFactor != null ? pipingFactor.Value: series.MissVal; // // entry.Value = CalculationHelper.DetermineSafetyFactor(calculationQueue[entry], ref basisFileName, stabilityExePath); // // entry.BasisFileName = Path.GetFileNameWithoutExtension(basisFileName); @@ -323,7 +321,7 @@ if (parameterId == TimeSerieParameters.StabilityOutsideFactor.ToString()) return FailureMechanismSystemType.StabilityOutside; - if (parameterId == TimeSerieParameters.PipingFactorWti.ToString()) + if (parameterId == TimeSerieParameters.PipingFactorWbiSellmeijerRevised.ToString()) return FailureMechanismSystemType.Piping; // #the Here something should be done with piping models throw new SensorTimeSeriesProcessorException("There is no failure mechanism mapping for parameterID " + parameterId); @@ -378,7 +376,7 @@ return TimeSerieParameters.StabilityOutsideFactor.ToString(); case FailureMechanismSystemType.Piping: // #The Piping now only supports 1 model: WTI 2017. But when more piping models are implemented, pipinggmodelType is needed here too! - return TimeSerieParameters.PipingFactorWti.ToString(); + return TimeSerieParameters.PipingFactorWbiSellmeijerRevised.ToString(); // case FailureMechanismType.PipingSellmeijer: // return TimeSerieParameters.PipingFactorSellmeijer.ToString(); #Bka Piping not yet used. But when used, pipingmodelType is needed here too! default: Index: DamEngine/trunk/src/Deltares.DamEngine.Calculators/DikesOperational/OperationCalculatorTask.cs =================================================================== diff -u --- DamEngine/trunk/src/Deltares.DamEngine.Calculators/DikesOperational/OperationCalculatorTask.cs (revision 0) +++ DamEngine/trunk/src/Deltares.DamEngine.Calculators/DikesOperational/OperationCalculatorTask.cs (revision 1640) @@ -0,0 +1,43 @@ +// Copyright (C) Stichting Deltares 2018. All rights reserved. +// +// This file is part of the Dam Engine. +// +// The Dam Engine is free software: you can redistribute it and/or modify +// it under the terms of the GNU Affero 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 Affero General Public License for more details. +// +// You should have received a copy of the GNU Affero 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 System.Text; +using System.Threading.Tasks; +using Deltares.DamEngine.Data.General; +using Deltares.DamEngine.Data.General.Results; +using Deltares.DamEngine.Data.Standard.Logging; + +namespace Deltares.DamEngine.Calculators.DikesOperational +{ + public class OperationCalculatorTask + { + public Location Location { get; set; } + public SoilGeometryProbability SoiProfileProbability { get; set; } + public string ProjectPath { get; set; } + public string CalculationMap { get; set; } + public DamFailureMechanismeCalculationSpecification FailureMechanismeCalculationSpecification { get; set; } + public List DesignResults { get; set; } + public List CalculationMessages { get; set; } + } +}