using System; using System.Collections; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Runtime.ExceptionServices; using System.Xml.Serialization; using Deltares.Geographic; using Deltares.Geometry; using Deltares.Geotechnics.Soils; using Deltares.Geotechnics.SurfaceLines; using Deltares.Standard; using Deltares.Standard.Attributes; using Deltares.Standard.EventPublisher; using Deltares.Standard.EventPublisher.Enum; using Deltares.Standard.Extensions; using Deltares.Standard.Language; using Deltares.Standard.Units; using Deltares.Standard.Validation; namespace Deltares.Geotechnics.WaternetCreator { /// /// Different options for generating the waternet - either letting it be generated or filling in values self /// public enum WaternetCreationMode { [XmlOldName("True")] CreateWaternet, CreatePhreaticAndHeadLinesOnly, [XmlOldName("False")] FillInWaternetValues } public enum DikeSoilScenario { ClayDikeOnClay, SandDikeOnClay, ClayDikeOnSand, SandDikeOnSand } [XmlOldName("Deltares.Stability.RingtoetsLocation")] public class Location : IGeographicPoint, IVisibleEnabled, IDisposable, IDomain { private bool adjustPl3And4ForUplift = true; private DikeSoilScenario dikeSoilScenario = DikeSoilScenario.ClayDikeOnClay; private bool drainageConstructionPresent = false; private double headInPLLine3 = double.NaN; private double headInPLLine4 = double.NaN; private bool inwards = true; private double minimumLevelPhreaticLineAtDikeTopPolder = double.NaN; private double minimumLevelPhreaticLineAtDikeTopRiver = double.NaN; private PhreaticAdaptionType nwoPhreaticAdaption = PhreaticAdaptionType.None; private double penetrationLength = double.NaN; private PiezometricHeads piezometricHeads = new PiezometricHeads(); private PlLineCreationMethod plLineCreationMethod = PlLineCreationMethod.RingtoetsWti2017; private double plLineOffsetBelowDikeToeAtPolder = 0.1; private double plLineOffsetBelowDikeTopAtPolder = 1.5; private double plLineOffsetBelowDikeTopAtRiver = 0.5; private double plLineOffsetBelowPointBRingtoetsWti2017 = 0.5; private double plLineOffsetBelowShoulderBaseInside = 0.1; private double slopeDampingPiezometricHeightPolderSide = double.NaN; private SurfaceLine2 surfaceline; private bool useDefaultOffsets = true; private double waterLevelRiver = double.NaN; private double waterLevelRiverAverage = double.NaN; private double waterLevelRiverLow = double.NaN; private WaternetCreationMode waternetCreationMode = WaternetCreationMode.FillInWaternetValues; private double xCoordMiddleDrainageConstruction = double.NaN; private double zCoordMiddleDrainageConstruction = double.NaN; public Location() { DataEventPublisher.OnAfterChange += DataEventPublisher_OnAfterChange; DataEventPublisher.AddListenerForSenders(EventType.AfterChange, this, piezometricHeads); } [Browsable(false)] [Validate] public virtual SoilProfile2D SoilProfile2D { get; set; } [Browsable(false)] [Validate] public virtual SoilProfile1D SoilProfile1D { get; set; } [Browsable(false)] [Validate] public virtual SurfaceLine2 Surfaceline { get { return surfaceline; } set { DataEventPublisher.BeforeChange(this, "Surfaceline"); surfaceline = value; DataEventPublisher.AfterChange(this, "Surfaceline"); } } [Label("NWO phreatic adaption")] [Browsable(false)] public PhreaticAdaptionType NWOPhreaticAdaption { get { return nwoPhreaticAdaption; } set { DataEventPublisher.BeforeChange(this, "NWOPhreaticAdaption"); nwoPhreaticAdaption = value; DataEventPublisher.AfterChange(this, "NWOPhreaticAdaption"); } } [Browsable(false)] public virtual PhreaticLine LocalXzpl1Line { get; set; } /// /// Gets or sets the general setting - autogenerate or self filling in. /// Sets the Auto and the UserDefinedPl3AndPl4 parameters of Location. /// [PropertyOrder(10, 1)] [XmlOldName("Auto")] public WaternetCreationMode WaternetCreationMode { get { return waternetCreationMode; } set { DataEventPublisher.BeforeChange(this, "WaternetCreationMode"); waternetCreationMode = value; DataEventPublisher.AfterChange(this, "WaternetCreationMode"); } } [PropertyOrder(10, 2)] [Format("F2")] public DikeSoilScenario DikeSoilScenario { get { return dikeSoilScenario; } set { DataEventPublisher.BeforeChange(this, "DikeSoilScenario"); dikeSoilScenario = value; if (dikeSoilScenario == DikeSoilScenario.ClayDikeOnClay || dikeSoilScenario == DikeSoilScenario.ClayDikeOnSand) { DrainageConstructionPresent = false; } DataEventPublisher.AfterChange(this, "DikeSoilScenario"); } } [PropertyOrder(10, 3)] [Format("F2")] public PlLineCreationMethod PlLineCreationMethod { get { return plLineCreationMethod; } set { DataEventPublisher.BeforeChange(this, "PlLineCreationMethod"); plLineCreationMethod = value; DataEventPublisher.AfterChange(this, "PlLineCreationMethod"); if (waternetCreationMode == WaternetCreationMode.CreatePhreaticAndHeadLinesOnly && plLineCreationMethod != PlLineCreationMethod.RingtoetsWti2017) { waternetCreationMode = WaternetCreationMode.CreateWaternet; } } } [PropertyOrder(20, 10)] [Unit(UnitType.Depth)] [Format("F2")] [Clearable] public double WaterLevelRiver { get { return waterLevelRiver; } set { DataEventPublisher.BeforeChange(this, l => l.WaterLevelRiver); headInPLLine4 = value; headInPLLine3 = value; waterLevelRiver = value; DataEventPublisher.AfterChange(this, l => l.WaterLevelRiver); } } [PropertyOrder(20, 11)] [Unit(UnitType.Depth)] [Format("F2")] [Clearable] public double WaterLevelRiverAverage { get { return waterLevelRiverAverage; } set { DataEventPublisher.BeforeChange(this, "WaterLevelRiverAverage"); waterLevelRiverAverage = value; DataEventPublisher.AfterChange(this, "WaterLevelRiverAverage"); } } [PropertyOrder(20, 12)] [Unit(UnitType.Depth)] [Format("F2")] [Clearable] public double WaterLevelRiverLow { get { return waterLevelRiverLow; } set { DataEventPublisher.BeforeChange(this, "WaterLevelRiverLow"); waterLevelRiverLow = value; DataEventPublisher.AfterChange(this, "WaterLevelRiverLow"); } } [PropertyOrder(20, 13)] [Unit(UnitType.Depth)] [Format("F2")] [Clearable] [NotClear] [XmlIgnore] public double WaterLevelPolder { get { return piezometricHeads.HeadPl1; } set { DataEventPublisher.BeforeChange(this, "WaterLevelPolder"); piezometricHeads.HeadPl1 = value; DataEventPublisher.AfterChange(this, "WaterLevelPolder"); } } [PropertyOrder(20, 14)] public bool DrainageConstructionPresent { get { return drainageConstructionPresent; } set { var newValue = dikeSoilScenario == DikeSoilScenario.ClayDikeOnClay || dikeSoilScenario == DikeSoilScenario.ClayDikeOnSand ? false : value; this.SetAndNotify2(out drainageConstructionPresent, newValue, l => l.DrainageConstructionPresent); } } [PropertyOrder(20, 15)] [Unit(UnitType.Length)] [Format("F3")] public double XCoordMiddleDrainageConstruction { get { return xCoordMiddleDrainageConstruction; } set { DataEventPublisher.BeforeChange(this, "XCoordMiddleDrainageConstruction"); xCoordMiddleDrainageConstruction = value; DataEventPublisher.AfterChange(this, "XCoordMiddleDrainageConstruction"); } } [PropertyOrder(20, 16)] [Unit(UnitType.Depth)] [Format("F3")] public double ZCoordMiddleDrainageConstruction { get { return zCoordMiddleDrainageConstruction; } set { DataEventPublisher.BeforeChange(this, "ZCoordMiddleDrainageConstruction"); zCoordMiddleDrainageConstruction = value; DataEventPublisher.AfterChange(this, "ZCoordMiddleDrainageConstruction"); } } [PropertyOrder(20, 17)] [Unit(UnitType.Depth)] [Format("F3")] [Clearable] public double MinimumLevelPhreaticLineAtDikeTopRiver { get { return minimumLevelPhreaticLineAtDikeTopRiver; } set { DataEventPublisher.BeforeChange(this, "MinimumLevelPhreaticLineAtDikeTopRiver"); minimumLevelPhreaticLineAtDikeTopRiver = value; DataEventPublisher.AfterChange(this, "MinimumLevelPhreaticLineAtDikeTopRiver"); } } [PropertyOrder(20, 18)] [Unit(UnitType.Depth)] [Format("F3")] [Clearable] public double MinimumLevelPhreaticLineAtDikeTopPolder { get { return minimumLevelPhreaticLineAtDikeTopPolder; } set { DataEventPublisher.BeforeChange(this, "MinimumLevelPhreaticLineAtDikeTopPolder"); minimumLevelPhreaticLineAtDikeTopPolder = value; DataEventPublisher.AfterChange(this, "MinimumLevelPhreaticLineAtDikeTopPolder"); } } [PropertyOrder(20, 19)] public bool UseDefaultOffsets { get { return useDefaultOffsets; } set { DataEventPublisher.BeforeChange(this, "UseDefaultOffsets"); useDefaultOffsets = value; DataEventPublisher.AfterChange(this, "UseDefaultOffsets"); } } [PropertyOrder(20, 30)] [Unit(UnitType.Length)] [Format("F2")] public double PlLineOffsetBelowDikeTopAtRiver { get { return plLineOffsetBelowDikeTopAtRiver; } set { DataEventPublisher.BeforeChange(this, "PlLineOffsetBelowDikeTopAtRiver"); plLineOffsetBelowDikeTopAtRiver = value; DataEventPublisher.AfterChange(this, "PlLineOffsetBelowDikeTopAtRiver"); } } /// /// The offset of PL-line 1 (= phreatic line) below point B, according to Ringtoets WTI 2017 mehtod: /// - for clay dikes: point B is situated 1 meter at the right side of point A (intersection dike with phreatic line at river side) /// - for sand dike on clay: point B is situated 1 cm at the right side of point A /// - for sand dike on sand: point B is dike top at river side /// [PropertyOrder(20, 31)] [Unit(UnitType.Length)] [Format("F2")] public double PlLineOffsetBelowPointBRingtoetsWti2017 { get { return plLineOffsetBelowPointBRingtoetsWti2017; } set { DataEventPublisher.BeforeChange(this, "PlLineOffsetBelowPointBRingtoetsWti2017"); plLineOffsetBelowPointBRingtoetsWti2017 = value; DataEventPublisher.AfterChange(this, "PlLineOffsetBelowPointBRingtoetsWti2017"); } } [PropertyOrder(20, 32)] [Unit(UnitType.Length)] [Format("F2")] public double PlLineOffsetBelowDikeTopAtPolder { get { return plLineOffsetBelowDikeTopAtPolder; } set { DataEventPublisher.BeforeChange(this, "PlLineOffsetBelowDikeTopAtPolder"); plLineOffsetBelowDikeTopAtPolder = value; DataEventPublisher.AfterChange(this, "PlLineOffsetBelowDikeTopAtPolder"); } } [PropertyOrder(20, 33)] [Unit(UnitType.Length)] [Format("F2")] public double PlLineOffsetBelowShoulderBaseInside { get { return plLineOffsetBelowShoulderBaseInside; } set { DataEventPublisher.BeforeChange(this, "PlLineOffsetBelowShoulderBaseInside"); plLineOffsetBelowShoulderBaseInside = value; DataEventPublisher.AfterChange(this, "PlLineOffsetBelowShoulderBaseInside"); } } [PropertyOrder(20, 34)] [Unit(UnitType.Length)] [Format("F2")] public double PlLineOffsetBelowDikeToeAtPolder { get { return plLineOffsetBelowDikeToeAtPolder; } set { DataEventPublisher.BeforeChange(this, "PlLineOffsetBelowDikeToeAtPolder"); plLineOffsetBelowDikeToeAtPolder = value; DataEventPublisher.AfterChange(this, "PlLineOffsetBelowDikeToeAtPolder"); } } [Clearable] [ReadOnly(true)] [PropertyOrder(30, 1)] [Unit(UnitType.Depth)] [Format("F2")] public double HeadInPLLine3 { get { return headInPLLine3; } set { DataEventPublisher.BeforeChange(this, "HeadInPLLine3"); headInPLLine3 = value; DataEventPublisher.AfterChange(this, "HeadInPLLine3"); } } [ReadOnly(true)] [PropertyOrder(30, 2)] [Unit(UnitType.Depth)] [Format("F2")] public double HeadInPLLine4 { get { return headInPLLine4; } set { DataEventPublisher.BeforeChange(this, "HeadInPLLine4"); headInPLLine4 = value; DataEventPublisher.AfterChange(this, "HeadInPLLine4"); } } [PropertyOrder(30, 10)] [Format("F2")] public bool AdjustPl3And4ForUplift { get { return adjustPl3And4ForUplift; } set { DataEventPublisher.BeforeChange(this, "AdjustPl3And4ForUplift"); adjustPl3And4ForUplift = value; DataEventPublisher.AfterChange(this, "AdjustPl3And4ForUplift"); } } [Clearable] [PropertyOrder(40, 50)] [Unit(UnitType.Length)] [Format("F2")] [XmlIgnore] public double LeakageLengthOutwardsPl3 { get { return piezometricHeads.LeakageLengthOutwardsPl3; } set { DataEventPublisher.BeforeChange(this, "LeakageLengthOutwardsPl3"); piezometricHeads.LeakageLengthOutwardsPl3 = value; DataEventPublisher.AfterChange(this, "LeakageLengthOutwardsPl3"); } } [Clearable] [PropertyOrder(40, 51)] [Unit(UnitType.Length)] [Format("F2")] [XmlIgnore] public double LeakageLengthInwardsPl3 { get { return piezometricHeads.LeakageLengthInwardsPl3; } set { DataEventPublisher.BeforeChange(this, "LeakageLengthInwardsPl3"); piezometricHeads.LeakageLengthInwardsPl3 = value; DataEventPublisher.AfterChange(this, "LeakageLengthInwardsPl3"); } } [Clearable] [PropertyOrder(40, 52)] [Unit(UnitType.Length)] [Format("F2")] [XmlIgnore] public double LeakageLengthOutwardsPl4 { get { return piezometricHeads.LeakageLengthOutwardsPl4; } set { DataEventPublisher.BeforeChange(this, "LeakageLengthOutwardsPl4"); piezometricHeads.LeakageLengthOutwardsPl4 = value; DataEventPublisher.AfterChange(this, "LeakageLengthOutwardsPl4"); } } [Clearable] [PropertyOrder(40, 53)] [Unit(UnitType.Length)] [Format("F2")] [XmlIgnore] public double LeakageLengthInwardsPl4 { get { return piezometricHeads.LeakageLengthInwardsPl4; } set { DataEventPublisher.BeforeChange(this, "LeakageLengthInwardsPl4"); piezometricHeads.LeakageLengthInwardsPl4 = value; DataEventPublisher.AfterChange(this, "LeakageLengthInwardsPl4"); } } [PropertyOrder(40, 70)] [Unit(UnitType.Angle)] [Format("F2")] [Browsable(false)] public double SlopeDampingPiezometricHeightPolderSide { get { return slopeDampingPiezometricHeightPolderSide; } set { DataEventPublisher.BeforeChange(this, "SlopeDampingPiezometricHeightPolderSide"); slopeDampingPiezometricHeightPolderSide = value; DataEventPublisher.AfterChange(this, "SlopeDampingPiezometricHeightPolderSide"); } } [Clearable] [Validate] [PropertyOrder(50, 1)] [Unit(UnitType.Depth)] [Format("F2")] [XmlIgnore] public double HeadInPLLine2Outwards { get { return piezometricHeads.HeadPl2; } set { DataEventPublisher.BeforeChange(this, "HeadInPLLine2Outwards"); piezometricHeads.HeadPl2 = value; DataEventPublisher.AfterChange(this, "HeadInPLLine2Outwards"); } } [Browsable(false)] [Unit(UnitType.Depth)] [Format("F2")] [XmlIgnore] public double CorrectedHeadInPLLine2Outwards { get { if (!double.IsNaN(waterLevelRiverAverage)) { return Math.Min(waterLevelRiverAverage, HeadInPLLine2Outwards); } else if (!double.IsNaN(waterLevelRiver)) { return Math.Min(waterLevelRiver, HeadInPLLine2Outwards); } else { return HeadInPLLine2Outwards; } } } [Clearable] [Validate] [PropertyOrder(50, 2)] [Unit(UnitType.Depth)] [Format("F2")] [XmlIgnore] public double HeadInPLLine2Inwards { get { return piezometricHeads.HeadInPlLine2Inwards; } set { DataEventPublisher.BeforeChange(this, "HeadInPLLine2Inwards"); piezometricHeads.HeadInPlLine2Inwards = value; DataEventPublisher.AfterChange(this, "HeadInPLLine2Inwards"); } } [Browsable(false)] [Unit(UnitType.Depth)] [Format("F2")] [XmlIgnore] public double CorrectedHeadInPLLine2Inwards { get { if (!double.IsNaN(waterLevelRiverAverage)) { return Math.Min(waterLevelRiverAverage, HeadInPLLine2Inwards); } else if (!double.IsNaN(waterLevelRiver)) { return Math.Min(waterLevelRiver, HeadInPLLine2Inwards); } else { return HeadInPLLine2Inwards; } } } [Validate] [Unit(UnitType.Length)] [Format("F3")] [Minimum(0)] [PropertyOrder(50, 10)] public double PenetrationLength { get { return penetrationLength; } set { DataEventPublisher.BeforeChange(this, "PenetrationLength"); penetrationLength = value; DataEventPublisher.AfterChange(this, "PenetrationLength"); } } [Browsable(false)] public PiezometricHeads PiezometricHeads { get { return piezometricHeads; } set { if (piezometricHeads != value) { DataEventPublisher.RemoveListenerForSenders(EventType.AfterChange, this, piezometricHeads); DataEventPublisher.BeforeChange(this, "PiezometricHeads"); piezometricHeads = value; DataEventPublisher.AfterChange(this, "PiezometricHeads"); DataEventPublisher.AddListenerForSenders(EventType.AfterChange, this, piezometricHeads); } } } [Browsable(false)] public virtual bool Inwards { get { return inwards; } set { inwards = value; } } #region IVisibleEnabled public virtual bool IsEnabled(string property) { switch (property) { case "DrainageConstructionPresent": return (dikeSoilScenario != DikeSoilScenario.ClayDikeOnClay && dikeSoilScenario != DikeSoilScenario.ClayDikeOnSand); // PL 4 is created only for Clay/Sand dike on clay (1A and 2A) and only if an in-between aquifer is present case "LeakageLengthOutwardsPl4": case "LeakageLengthInwardsPl4": case "HeadInPLLine4": if (dikeSoilScenario == DikeSoilScenario.SandDikeOnSand || dikeSoilScenario == DikeSoilScenario.ClayDikeOnSand) { return false; } else { GeometryPoint xDikeTopAtPolder = Surfaceline.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtPolder); if (SoilProfile2D != null && Surfaceline != null && xDikeTopAtPolder != null) { // The check of the presence of an in-between aquifer is done at Dike Top At River // If not present, the input parameters for the creation of PL 4 are not needed return (SoilProfile2D.GetSoilProfile1D(xDikeTopAtPolder.X).InBetweenAquiferLayer != null); } else { return true; } } case "LeakageLengthInwardsPl3": case "LeakageLengthOutwardsPl3": return (dikeSoilScenario != DikeSoilScenario.SandDikeOnSand); default: return true; } } public virtual bool IsVisible(string property) { if (WaternetCreationMode == WaternetCreationMode.FillInWaternetValues) { return property == "WaternetCreationMode"; } else { switch (property) { case "WaterLevelRiverLow": return (!Inwards); case "XCoordMiddleDrainageConstruction": case "ZCoordMiddleDrainageConstruction": return drainageConstructionPresent; case "MinimumLevelPhreaticLineAtDikeTopRiver": case "MinimumLevelPhreaticLineAtDikeTopPolder": return plLineCreationMethod == PlLineCreationMethod.RingtoetsWti2017; case "PlLineOffsetBelowDikeTopAtRiver": return plLineCreationMethod != PlLineCreationMethod.RingtoetsWti2017; case "PlLineOffsetBelowPointBRingtoetsWti2017": return plLineCreationMethod == PlLineCreationMethod.RingtoetsWti2017 && !useDefaultOffsets; case "PlLineOffsetBelowDikeTopAtPolder": case "PlLineOffsetBelowDikeToeAtPolder": case "PlLineOffsetBelowShoulderBaseInside": if (plLineCreationMethod == PlLineCreationMethod.RingtoetsWti2017 && useDefaultOffsets) { return false; } else { return true; } case "HeadInPLLine3": case "HeadInPLLine4": return (PlLineCreationMethod != PlLineCreationMethod.RingtoetsWti2017); default: return true; } } } #endregion #region Validation [Validate] public ValidationResult[] Validate() { var results = new List(); if (waternetCreationMode == WaternetCreationMode.CreateWaternet) { if (Surfaceline == null || Surfaceline.Geometry.Count < 2) { results.Add(BuildErrorValidationResultForThis("SurfaceLineNotPresent")); return results.ToArray(); } if (!Surfaceline.IsDefined(CharacteristicPointType.SurfaceLevelOutside)) { results.Add(BuildErrorValidationResultForThis("SurfaceLevelOutsideNotPresent")); } if (!Surfaceline.IsDefined(CharacteristicPointType.SurfaceLevelInside)) { results.Add(BuildErrorValidationResultForThis("SurfaceLevelInsideNotPresent")); } if (!Surfaceline.IsDefined(CharacteristicPointType.DikeToeAtPolder)) { results.Add(BuildErrorValidationResultForThis("DikeToeAtPolderNotPresent")); } if (!Surfaceline.IsDefined(CharacteristicPointType.DikeToeAtRiver)) { results.Add(BuildErrorValidationResultForThis("DikeToeAtRiverNotPresent")); } if (!Surfaceline.IsDefined(CharacteristicPointType.DikeTopAtPolder)) { results.Add(BuildErrorValidationResultForThis("DikeTopAtPolderNotPresent")); } if (!Surfaceline.IsDefined(CharacteristicPointType.DikeTopAtRiver)) { results.Add(BuildErrorValidationResultForThis("DikeTopAtRiverNotPresent")); } if (!Surfaceline.IsDefined(CharacteristicPointType.DitchDikeSide) || !Surfaceline.IsDefined(CharacteristicPointType.DitchPolderSide)) { results.Add(BuildWarningValidationResultForThis("DitchPointsNotComplete")); } if (!Surfaceline.IsDefined(CharacteristicPointType.TrafficLoadInside) || !Surfaceline.IsDefined(CharacteristicPointType.TrafficLoadOutside)) { results.Add(BuildWarningValidationResultForThis("TrafficLoadNotPresent")); } if (Surfaceline.IsDefined(CharacteristicPointType.DikeTopAtPolder)) { if (WaterLevelPolder > Surfaceline.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtPolder).Z) { results.Add(BuildErrorValidationResultForThis("WaterLevelPolderGreaterThanDikeTopAtPolder")); } } if (Surfaceline.IsDefined(CharacteristicPointType.SurfaceLevelInside)) { if (PlLineCreationMethod == PlLineCreationMethod.RingtoetsWti2017 && (WaterLevelPolder > Surfaceline.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.SurfaceLevelInside).Z)) { results.Add(BuildErrorValidationResultForThis("WaterLevelPolderGreaterThanSurfaceLevelInside")); } } if (Surfaceline.IsDefined(CharacteristicPointType.DikeTopAtRiver)) { if (WaterLevelRiver > Surfaceline.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtRiver).Z) { results.Add(BuildErrorValidationResultForThis("WaterLevelRiverGreaterThanDikeTopAtRiver")); } } if (double.IsNaN(LeakageLengthInwardsPl3) || double.IsNaN(LeakageLengthOutwardsPl3)) { results.Add(BuildErrorValidationResultForThis("LeakageLengthValuesNotPresent")); } if (double.IsNaN(HeadInPLLine2Outwards) || double.IsNaN(HeadInPLLine2Inwards)) { results.Add(BuildErrorValidationResultForThis("HeadPLLine2ValuesNotPresent")); } if (double.IsNaN(WaterLevelPolder)) { results.Add(BuildErrorValidationResultForThis("WaterLevelPolderNotPresent")); } if (!Inwards && plLineCreationMethod != PlLineCreationMethod.RingtoetsWti2017 && double.IsNaN(WaterLevelRiverLow)) { results.Add(BuildErrorValidationResultForThis("WaterLevelRiverLowNotPresent")); } if (PlLineCreationMethod == PlLineCreationMethod.RingtoetsWti2017 && (double.IsNaN(minimumLevelPhreaticLineAtDikeTopPolder) || double.IsNaN(minimumLevelPhreaticLineAtDikeTopRiver))) { results.Add(BuildErrorValidationResultForThis("MinimumLevelsPhreaticLineNotPresent")); } if (results.Count > 0) { return results.ToArray(); } // Validation of the drainage construction double zTopDike = Surfaceline.Geometry.GetZAtX(XCoordMiddleDrainageConstruction); double xDikeToeAtPolder = Surfaceline.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtPolder).X; double xDikeTopAtPolder = Surfaceline.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtPolder).X; bool isDrainageConstruction = (PlLineCreationMethod == PlLineCreationMethod.RingtoetsWti2017 && (DikeSoilScenario == DikeSoilScenario.SandDikeOnClay || DikeSoilScenario == DikeSoilScenario.SandDikeOnSand) && DrainageConstructionPresent); if (isDrainageConstruction && (double.IsNaN(XCoordMiddleDrainageConstruction) || double.IsNaN(ZCoordMiddleDrainageConstruction))) { results.Add(BuildErrorValidationResultForThis("CoordinatesDrainageConstructionNotPresent")); } if (isDrainageConstruction && (XCoordMiddleDrainageConstruction > xDikeToeAtPolder || XCoordMiddleDrainageConstruction < xDikeTopAtPolder)) { results.Add(BuildErrorValidationResultForThis("DrainageConstructionMustBeSituatedBetweenDikeTopAndDikeToe")); } if (isDrainageConstruction && ZCoordMiddleDrainageConstruction > zTopDike) { results.Add(BuildErrorValidationResultForThis("DrainageConstructionOutsideTheDike")); } if (isDrainageConstruction && ZCoordMiddleDrainageConstruction < WaterLevelPolder) { results.Add(BuildErrorValidationResultForThis("DrainageConstructionBelowWaterLevelPolder")); } //if (HeadInPLLine2Inwards > waterLevelRiverAverage) //{ // results.Add(BuildErrorValidationResultForThis("HeadPL2InwardsMoreThanAverageHighWaterLevel")); //} //if (HeadInPLLine2Outwards > waterLevelRiverAverage) //{ // results.Add(BuildErrorValidationResultForThis("HeadPL2OutwardsMoreThanAverageHighWaterLevel")); //} if (waterLevelRiverAverage > waterLevelRiver) { results.Add(BuildErrorValidationResultForThis("AverageHighWaterLevelMoreThanWaterLevelRiver")); } //if (double.IsNaN(waterLevelRiverAverage) && // (HeadInPLLine2Outwards > waterLevelRiver || HeadInPLLine2Inwards > waterLevelRiver)) //{ // results.Add(BuildErrorValidationResultForThis("HeadPL2MoreThanWaterLevelRiver")); //} double firstX = Surfaceline.Geometry.Points.First(p => !double.IsNaN(p.X)).X; double lastX = Surfaceline.Geometry.Points.Last(p => !double.IsNaN(p.X)).X; double slopeHead2 = Math.Abs((firstX - lastX) / (HeadInPLLine2Inwards - HeadInPLLine2Outwards)); if (slopeHead2 < 100.0) { results.Add(BuildErrorValidationResultForThis("SlopeOfHead2TooSteep")); } results.AddRange(ValidateAquifers()); } return results.ToArray(); } /// /// Validates whether aquifer definitions are all right /// /// /// Don't decorate with Validate attribute, since it is called by the Validate method /// /// /// Validation results /// public ValidationResult[] ValidateAquifers() { List results = new List(); const double margin = 0.0001; bool hasAquifer = false; foreach (var soilLayer in SoilProfile2D.Surfaces) { if (soilLayer.IsAquifer) { hasAquifer = true; if (soilLayer.GeometrySurface.OuterLoop.GetMinX() > SoilProfile2D.Geometry.Left + margin || soilLayer.GeometrySurface.OuterLoop.GetMaxX() < SoilProfile2D.Geometry.Right - margin) { results.Add(new ValidationResult(ValidationResultType.Error, LocalizationManager.GetTranslatedText(typeof(Location), "NoAquiferCompleteWidth"), soilLayer)); } } } if (!hasAquifer) { results.Add(new ValidationResult(ValidationResultType.Error, LocalizationManager.GetTranslatedText(typeof(Location), "NoAquifer"), SoilProfile2D)); } if (dikeSoilScenario == DikeSoilScenario.ClayDikeOnClay) { double xNoDike; if (Surfaceline.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtPolder) != null) { xNoDike = Surfaceline.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtPolder).X + 0.001; } else { xNoDike = Surfaceline.Geometry.GetMaxX(); } SoilProfile1D profile1DWithoutDike = SoilProfile2D.GetSoilProfile1D(xNoDike); profile1DWithoutDike.Layers.Sort(); if (profile1DWithoutDike.Layers[0].IsAquifer) { results.Add(new ValidationResult(ValidationResultType.Error, LocalizationManager.GetTranslatedText(typeof(Location), "FirstLayerBelowDikeIsAquifer"), SoilProfile2D)); } } if (dikeSoilScenario == DikeSoilScenario.ClayDikeOnSand) { double xTopDike; if (Surfaceline.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtPolder) != null) { xTopDike = Surfaceline.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtPolder).X; } else { xTopDike = Surfaceline.Geometry.GetMaxX(); } SoilProfile1D profile1DWithoutDike = SoilProfile2D.GetSoilProfile1D(xTopDike); profile1DWithoutDike.Layers.Sort(); if (profile1DWithoutDike.Layers[0].IsAquifer) { results.Add(new ValidationResult(ValidationResultType.Error, LocalizationManager.GetTranslatedText(typeof(Location), "DikeIsAquifer"), SoilProfile2D)); } } return results.ToArray(); } /// /// Validatates case when Toetspeil was not calculated yet and therefor there is no value for WaterLevelRiver /// /// The collection with all detected validation problems [Validate] public ValidationResult[] ValidateWaterLevelRiver() { var results = new List(); if ((Inwards || plLineCreationMethod == PlLineCreationMethod.RingtoetsWti2017) && double.IsNaN(WaterLevelRiver) && waternetCreationMode == WaternetCreationMode.CreateWaternet) { results.Add(BuildErrorValidationResultForThis("WaterLevelRiverNotPresent")); } return results.ToArray(); } /// /// Validates if it has the expected number of aquifer layers. /// /// The collection with all detected validation problems. /// This method has sideseffects: setting . [Validate] public ValidationResult[] ValidateSoilProfile() { var results = new List(); if (waternetCreationMode == WaternetCreationMode.CreateWaternet && Surfaceline != null) { // TODO: This path creates side-effects: refactor to make this method Pure. if (SoilProfile2D != null && Surfaceline.IsDefined(CharacteristicPointType.DikeToeAtRiver)) { var xStart = Surfaceline.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtRiver).X; SoilProfile1D = SoilProfile2D.GetSoilProfile1D(xStart); } else { SoilProfile1D = SoilProfile1D; } // Early exit in a similar fashion if surface line is missing: if (SoilProfile1D == null) { return results.ToArray(); } var toppestLayerOfDeepestClusterOfAquifers = SoilProfile1D.BottomAquiferLayer; if (toppestLayerOfDeepestClusterOfAquifers == null) { return results.ToArray(); } IList sortedLayers = SoilProfile1D.Layers.OrderBy(l => l.TopLevel).ToList(); int numberOfInBetweenClustersOfAquifers = 0; for (int i = 1; i < sortedLayers.Count; i++) { if (sortedLayers[i].TopLevel > toppestLayerOfDeepestClusterOfAquifers.TopLevel && sortedLayers[i].IsAquifer) { if (sortedLayers[i].IsAquifer && !sortedLayers[i - 1].IsAquifer) { ++numberOfInBetweenClustersOfAquifers; } } } if (numberOfInBetweenClustersOfAquifers > 1) { results.Add(BuildErrorValidationResultForThis("MoreThanOneInBetweenAquiferPresent")); } } return results.ToArray(); } private ValidationResult BuildWarningValidationResultForThis(string message) { var correctlyTranslatedMessage = LocalizationManager.GetTranslatedText(typeof(Location), message); return new ValidationResult(ValidationResultType.Warning, correctlyTranslatedMessage, this); } private ValidationResult BuildErrorValidationResultForThis(string message) { var correctlyTranslatedMessage = LocalizationManager.GetTranslatedText(typeof(Location), message); return new ValidationResult(ValidationResultType.Error, correctlyTranslatedMessage, this); } #endregion [Browsable(false)] public double X { get; set; } [Browsable(false)] public double Y { get; set; } public void Dispose() { DataEventPublisher.RemoveListenerForSenders(EventType.AfterChange, this, piezometricHeads); DataEventPublisher.OnAfterChange -= DataEventPublisher_OnAfterChange; } private void DataEventPublisher_OnAfterChange(object sender, PublishEventArgs e) { if (sender == piezometricHeads) { DataEventPublisher.AfterChange(this); } } public ICollection GetDomain(string property) { switch (property) { case "WaternetCreationMode": if (PlLineCreationMethod == PlLineCreationMethod.RingtoetsWti2017) { return new[] { WaternetCreationMode.CreateWaternet, WaternetCreationMode.CreatePhreaticAndHeadLinesOnly, WaternetCreationMode.FillInWaternetValues }; } else { return new[] { WaternetCreationMode.CreateWaternet, WaternetCreationMode.FillInWaternetValues }; } default: return null; } } } }