// Copyright (C) Stichting Deltares 2023. 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.Linq.Expressions; using Deltares.DamEngine.Data.Design; using Deltares.DamEngine.Data.General.Sensors.Specifications; using Deltares.DamEngine.Data.General.Specifications.Extensions; using Deltares.DamEngine.Data.Geometry; using Deltares.DamEngine.Data.Geotechnics; using Deltares.DamEngine.Data.Standard; namespace Deltares.DamEngine.Data.General.Sensors; /// /// Association class for sensor data and a location /// public class SensorLocation { /// /// Constructor is needed for XML deserialization /// public SensorLocation() { SourceTypePl1WaterLevelAtRiver = DataSourceTypeSensors.LocationData; SourceTypePl1WaterLevelAtPolder = DataSourceTypeSensors.LocationData; } /// /// Gets the PL line offset below dike top at river. /// public double PlLineOffsetBelowDikeTopAtRiver { get { return Location.PlLineOffsetBelowDikeTopAtRiver; } } /// /// Gets the PL line offset below dike top at polder. /// public double PlLineOffsetBelowDikeTopAtPolder { get { return Location.PlLineOffsetBelowDikeTopAtPolder; } } /// /// Gets the PL line offset below dike toe at polder. /// public double PlLineOffsetBelowDikeToeAtPolder { get { return Location.PlLineOffsetBelowDikeToeAtPolder; } } /// /// Gets the PL line offset dry below shoulder base inside. /// public double PlLineOffsetDryBelowShoulderBaseInside { get { return Location.PlLineOffsetBelowShoulderBaseInside; } } /// /// Gets or sets the DAM location. /// /// /// The location. /// public Location Location { get; set; } /// /// Gets the location Name. /// /// /// If the Name is empty it could indicate that there is no reference /// to a valid location /// public string LocationName { get { return (Location != null) ? Location.Name : string.Empty; } } /// /// Gets or sets the sensor sensorGroup. /// /// /// The sensorGroup. /// public SensorGroup SensorGroup { get; set; } /// /// Gets the sensorGroup ID. /// /// /// If the ID has value -1 then the there is no valid reference (null) /// or the sensorGroup is transient. /// public int GroupId { get { if (SensorGroup == null) { return -1; } return SensorGroup.ID; } } /// /// Gets the sensor selection. /// public IEnumerable Sensors { get { if (SensorGroup == null) { return new Sensor[0]; } return SensorGroup.Selection; } } /// /// Gets or sets the Pl3 data source type. /// /// /// The Pl3. /// public DataSourceTypeSensors SourceTypePl3 { get; set; } /// /// Gets or sets the Pl4 data source type. /// /// /// The Pl4. /// public DataSourceTypeSensors SourceTypePl4 { get; set; } /// /// Gets or sets the outer water level data source type. /// /// /// The outer water level. /// public DataSourceTypeSensors SourceTypePl1WaterLevelAtRiver { get; set; } /// /// Gets or sets the Pl1 PL line offset below dike top at river data source type. /// /// /// Data source type for Pl1 PLLine offset below dike top at river. /// public DataSourceTypeSensors SourceTypePl1PlLineOffsetBelowDikeTopAtRiver { get; set; } /// /// Gets or sets the Pl1 PL line offset below dike top at polder data source type /// /// /// The Pl1 PL line offset below dike top at polder. /// public DataSourceTypeSensors SourceTypePl1PlLineOffsetBelowDikeTopAtPolder { get; set; } /// /// Gets or sets the Pl1 PL line offset below shoulder base inside data source type /// /// /// The Pl1 PL line offset below shoulder base inside. /// public DataSourceTypeSensors SourceTypePl1PlLineOffsetBelowShoulderBaseInside { get; set; } /// /// Gets or sets the Pl1 PL line offset below dike toe at polder data source type. /// /// /// The Pl1 PL line offset below dike toe at polder. /// public DataSourceTypeSensors SourceTypePl1PlLineOffsetBelowDikeToeAtPolder { get; set; } /// /// Gets or sets the Pl1 polder level data source type. /// /// /// The Pl1 polder level. /// public DataSourceTypeSensors SourceTypePl1WaterLevelAtPolder { get; set; } /// /// Gets the sensor count. /// public int SensorCount { get { return SensorGroup == null ? 0 : SensorGroup.SensorCount; } } /// /// Gets the surface line. /// /// /// The surface line. /// public SurfaceLine2 SurfaceLine { get { return Location.SurfaceLine; } } /// /// Gets the river level. /// /// /// The river level. /// public double RiverLevel { get { return Location.RiverLevel; } } private DesignScenario CurrentScenario { get { var scenario = Location.CurrentScenario; if (scenario == null) { scenario = Location.Scenarios[0]; } return scenario; } } /// /// Gets the polder level. /// /// /// The polder level. /// public double PolderLevel { get { return CurrentScenario.PolderLevel; } } /// /// Gets the head PL3. /// /// /// The head PL3. /// public double? HeadPl3 { get { return CurrentScenario.HeadPl3; } } public double? HeadPl4 { get { return CurrentScenario.HeadPl4; } } /// /// Gets or sets the profile. /// /// /// The profile. /// public string Profile { get; //{ return SoilProfile2D } ; set; } /// /// Gets or sets the MStab file. /// /// /// The MStab file. /// public string MStabFile { get; //{ return SoilProfile2D } ; set; } /// /// 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 /// public IDictionary>> SensorValues { get; set; } /// /// Gets or sets the GetGroups function (injectable list, for UI purposes). /// /// /// The list of available Groups. /// public static Func> GetGroups { get; set; } /// /// Gets the PiezometricHead type sensors sorted by relative location along profile. /// /// Type of the pl line. /// public SortedDictionary GetSensorsSortedByRelativeLocationAlongProfile(PlLineType plLineType) { IDictionary calculatedRelativeLocations = BuildRelativeLocationTable(); var table = new SortedDictionary(); // leave out the water level and polder level sensors IEnumerable candidateSensors = Sensors.GetBySpecification(new PiezometricHeadSensorSpecification()); foreach (Sensor sensor in candidateSensors) { if (sensor.PlLineMappings.Contains(plLineType)) { double relativeLocation = sensor.RelativeLocationSpecified ? sensor.RelativeLocation : calculatedRelativeLocations[sensor]; if (table.ContainsKey(relativeLocation)) { throw new InvalidOperationException( "Error creating lookup table with sensors sorted by relative location along profile. The x-location " + relativeLocation + " already exists. Sensor " + sensor); } table.Add(relativeLocation, sensor); } } return table; } /// /// Gets the sensor value. /// /// The expression. /// The sensor values. /// The sensor. /// /// /// sensorValues /// or /// sensor /// /// The given sensor is not an item/key in the table of sensor values public double? GetValue(Expression> expression, IDictionary sensorValues, Sensor sensor) { if (sensorValues == null) { throw new ArgumentNullException("sensorValues"); } if (sensor == null) { throw new ArgumentNullException("sensor"); } if (!sensorValues.ContainsKey(sensor)) { throw new ArgumentException("The given sensor is not an item/key in the table of sensor values"); } string memberName = StaticReflection.GetMemberName(expression); if (memberName == MemberNames.OffsetBelowDikeToeAtPolder) { if (SourceTypePl1PlLineOffsetBelowDikeToeAtPolder == DataSourceTypeSensors.LocationData) { return Location.PlLineOffsetBelowDikeToeAtPolder; } } if (memberName == MemberNames.OffsetBelowDikeTopAtPolder) { if (SourceTypePl1PlLineOffsetBelowDikeTopAtPolder == DataSourceTypeSensors.LocationData) { return Location.PlLineOffsetBelowDikeTopAtPolder; } } if (memberName == MemberNames.OffsetBelowDikeTopAtRiver) { if (SourceTypePl1PlLineOffsetBelowDikeTopAtRiver == DataSourceTypeSensors.LocationData) { return Location.PlLineOffsetBelowDikeTopAtRiver; } } if (memberName == MemberNames.OffsetBelowShoulderBaseInside) { if (SourceTypePl1PlLineOffsetBelowShoulderBaseInside == DataSourceTypeSensors.LocationData) { return Location.PlLineOffsetBelowShoulderBaseInside; } } if (memberName == MemberNames.WaterLevelAtRiver) { if (SourceTypePl1WaterLevelAtRiver == DataSourceTypeSensors.LocationData) { return Location.RiverLevel; } if (SourceTypePl1WaterLevelAtRiver == DataSourceTypeSensors.Sensor) { return sensorValues[sensor]; } } if (memberName == MemberNames.PolderLevel) { if (SourceTypePl1WaterLevelAtPolder == DataSourceTypeSensors.LocationData) { return CurrentScenario.PolderLevel; } if (SourceTypePl1WaterLevelAtPolder == DataSourceTypeSensors.Sensor) { return sensorValues[sensor]; } } if (memberName == MemberNames.Pl3) { if (SourceTypePl3 == DataSourceTypeSensors.LocationData) { return CurrentScenario.HeadPl3; } if (SourceTypePl3 == DataSourceTypeSensors.Sensor) { return sensorValues[sensor]; } } if (memberName == MemberNames.Pl4) { if (SourceTypePl4 == DataSourceTypeSensors.LocationData) { return CurrentScenario.HeadPl4; } if (SourceTypePl4 == DataSourceTypeSensors.Sensor) { return sensorValues[sensor]; } } return null; } /// /// Builds the relative location table. /// /// internal IDictionary BuildRelativeLocationTable() { GeometryPoint surfaceLevelOutside = SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.SurfaceLevelOutside); if (surfaceLevelOutside == null) { throw new InvalidOperationException("No SurfaceLevelOutside point on surface line defined"); } double startPoint = surfaceLevelOutside.X; var dict = new Dictionary(); foreach (Sensor sensor in Sensors) { double relativeLocation = startPoint + (Location.XRdDikeLine - sensor.XRd); dict.Add(sensor, relativeLocation); } return dict; } internal static class MemberNames { internal static readonly string OffsetBelowDikeToeAtPolder = StaticReflection.GetMemberName(x => x.SourceTypePl1PlLineOffsetBelowDikeToeAtPolder); internal static readonly string OffsetBelowDikeTopAtPolder = StaticReflection.GetMemberName(x => x.SourceTypePl1PlLineOffsetBelowDikeTopAtPolder); internal static readonly string OffsetBelowDikeTopAtRiver = StaticReflection.GetMemberName(x => x.SourceTypePl1PlLineOffsetBelowDikeTopAtRiver); internal static readonly string OffsetBelowShoulderBaseInside = StaticReflection.GetMemberName(x => x.SourceTypePl1PlLineOffsetBelowShoulderBaseInside); internal static readonly string WaterLevelAtRiver = StaticReflection.GetMemberName(x => x.SourceTypePl1WaterLevelAtRiver); internal static readonly string PolderLevel = StaticReflection.GetMemberName(x => x.SourceTypePl1WaterLevelAtPolder); internal static readonly string Pl3 = StaticReflection.GetMemberName(x => x.SourceTypePl3); internal static readonly string Pl4 = StaticReflection.GetMemberName(x => x.SourceTypePl4); } }