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