// Copyright (C) Stichting Deltares 2025. All rights reserved. // // This file is part of the application DAM - UI. // // DAM - UI 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; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Xml.Serialization; using Deltares.Dam.Data.IO; using Deltares.Dam.Data.Sensors; using Deltares.Geotechnics.Soils; using Deltares.Standard; using Deltares.Standard.Attributes; using Deltares.Standard.EventPublisher; using Deltares.Standard.Language; using Deltares.Standard.Logging; using Deltares.Standard.Project; using Deltares.Standard.Validation; namespace Deltares.Dam.Data; public class DamProjectData : Project, IDomain, IDisposable { public readonly double MissValStabilitySafetyFactor = -1.0; private readonly List jobs = new List(); private WaterBoard waterBoard; private WaterBoardJob waterBoardJob; private DamProjectType damProjectType = DamProjectType.Design; private string damDataSourceFileName = ""; private List designCalculations; /// /// Constructor /// public DamProjectData() { VersionInfo = new VersionInfo(); waterBoard = new WaterBoard(); waterBoardJob = null; DamProjectCalculationSpecification = new DamProjectCalculationSpecification(); } /// Gets the version information. /// The version information. public VersionInfo VersionInfo { get; set; } /// /// Gets or sets the project working path. /// /// /// The project working path. /// public string ProjectPath { get { return DamProject.ProjectMap; } } /// /// Gets or sets the calculation map. /// /// /// The calculation map. /// public string CalculationMap { get { return DamProject.CalculationMap; } } /// /// Gets or sets the maximum calculation cores. /// /// /// The maximum calculation cores. /// public int MaxCalculationCores { get; set; } = 1; /// /// Toplevel object to hold waterboard data /// [Validate] public virtual WaterBoard WaterBoard { get { return waterBoard; } set { waterBoard = value; if (waterBoardJob != null && waterBoardJob.Subject != value) { waterBoardJob = null; } } } /// /// calculation specification for the project /// /// Composite relationship [Validate] public DamProjectCalculationSpecification DamProjectCalculationSpecification { get; set; } [Child] public virtual DamJob WaterBoardJob { get { if (waterBoardJob == null) { waterBoardJob = new WaterBoardJob(waterBoard); waterBoardJob.Subject = waterBoard; if (waterBoard.Dike != null) { CompositeJob dikeJob = new DikeJob(waterBoard.Dike); waterBoardJob.Jobs.Add(dikeJob); foreach (Location location in waterBoard.Dike.Locations) { var locationJob = new LocationJob(location); dikeJob.Jobs.Add(locationJob); } } } return waterBoardJob; } set { waterBoardJob = value as WaterBoardJob; } } private bool runColumVisible = true; [Browsable(false)] [XmlIgnore] public bool RunColumVisible { get => runColumVisible; set { runColumVisible = value; DataEventPublisher.AfterChange(this, "RunColumVisible"); } } [ReadOnly(true)] [Label("Calculation type")] [Description("Indicates the purpose of the calculations")] public virtual DamProjectType DamProjectType { get => damProjectType; set { DataEventPublisher.BeforeChange(this, "DamProjectType"); bool modified = damProjectType != value; damProjectType = value; Location.DamProjectType = value; DamFailureMechanismeCalculationSpecification.DamProjectType = value; if (modified) { LocationJob.DamProjectType = damProjectType; DataEventPublisher.DataListModified(LocationJobs); } DataEventPublisher.AfterChange(this, "DamProjectType"); } } [Label("DAM datasource file")] [Description("Indicates which \".defx\" import definition file to use")] public virtual string DamDataSourceFileName { get { return damDataSourceFileName; } set { DataEventPublisher.BeforeChange(this, "DamDataSourceFileName"); damDataSourceFileName = value; DataEventPublisher.AfterChange(this, "DamDataSourceFileName"); } } [XmlIgnore] public List CalculationMessages { get; set; } [XmlIgnore] [ReadOnly(true)] public List DesignCalculations { get { if (designCalculations == null) { UpdateDesignCalculations(); } return designCalculations; } set { designCalculations = value; } } [XmlIgnore] [ReadOnly(true)] [Browsable(false)] public List LocationJobs { get { if (jobs.Count == 0) { var waterboardJob = WaterBoardJob as CompositeJob; foreach (CompositeJob dijkJob in waterboardJob.Jobs) { foreach (LocationJob locationJob in dijkJob.Jobs) { jobs.Add(locationJob); } } } return jobs; } } [XmlIgnore] [ReadOnly(true)] [Browsable(false)] [Validate] public List SelectedLocationJobs { get { var selectedLocationJobs = new List(); foreach (LocationJob locationJob in LocationJobs) { if (locationJob.Run.HasValue && locationJob.Run.Value) { selectedLocationJobs.Add(locationJob); } } return selectedLocationJobs; } } [XmlIgnore] [Browsable(false)] [ReadOnly(true)] [Label("Locations")] public List Locations { get { return WaterBoard.Locations; } } [Browsable(false)] public List DataSources { get; set; } = new List(); [Browsable(false)] public string DataSourceEsriProjection { get; set; } = null; /// /// Gets or sets the sensor data. /// /// /// The sensor data. /// public SensorData SensorData { get; set; } /// /// Gets or sets the input time serie collection. /// /// /// The input time serie collection. /// public TimeSerieCollection InputTimeSerieCollection { get; set; } = null; /// /// Gets or sets the output time serie collection. /// /// /// The output time serie collection. /// public TimeSerieCollection OutputTimeSerieCollection { get; set; } = null; /// /// Clear all results /// public void ClearResults() { if (designCalculations != null) { designCalculations.Clear(); } foreach (LocationJob locationJob in LocationJobs) { locationJob.LocationResult = new LocationResult(); foreach (Scenario scenario in locationJob.Location.Scenarios) { scenario.CalculationResults.Clear(); } } } public LocationJob GetFirstLocationJobWithDesignResults() { foreach (LocationJob locationJob in LocationJobs) { if (locationJob.HasDesignScenarioResults) { return locationJob; } } return null; } public void TransferOutputTimeSerieToLocationJobs() { if (OutputTimeSerieCollection == null) { return; } foreach (LocationJob locationJob in LocationJobs) { TimeSerie timeSerie = OutputTimeSerieCollection.Series.FirstOrDefault(serie => serie.LocationId == locationJob.Location.Name); if (timeSerie != null) { if (DamProjectCalculationSpecification.CurrentSpecification.FailureMechanismSystemType == FailureMechanismSystemType.Piping) { locationJob.LocationResult.PipingTimeSerie = timeSerie; } else { locationJob.LocationResult.StabilityTimeSerie = timeSerie; } } } } public void DeleteResults() { LogManager.Messages.Clear(); if (waterBoardJob != null && waterBoardJob.Jobs != null) { waterBoardJob.Jobs.Clear(); } waterBoardJob = null; if (jobs != null) { jobs.Clear(); } // Delete calculation results in scenarios List scenarios = CreateScenarioListForDeletion(); foreach (Scenario scenario in scenarios) { scenario.ClearResults(); scenario.ClearErrors(); } if (designCalculations != null) { designCalculations.Clear(); } } [Validate] public ValidationResult[] ValidateEnoughLocationJobs() { if (SelectedLocationJobs.Count == 0) { return new[] { new ValidationResult(ValidationResultType.Error, LocalizationManager.GetTranslatedText(this, "NoLocationsSelected"), this) }; } return []; } public bool HasResults() { bool hasResults = waterBoard.Dike != null && waterBoardJob != null && waterBoardJob.Jobs.Count > 0; if (hasResults) { hasResults = GetNumberOfCalculatedJobs() > 0; } return hasResults; } /// /// Validates the geometry adaption setting. /// /// [Validate] public ValidationResult[] ValidateGeometryAdaptionSetting() { if (!IsDesignWithGeometryAdaptionAllowed()) { return new[] { new ValidationResult(ValidationResultType.Error, LocalizationManager.GetTranslatedText(this, "DesignAndAdaptionNotAllowedForStabilityOutside"), this, "DamCalculationSpecifications", "DamCalculationSpecifications", null) }; } return new ValidationResult[0]; } /// /// The validation of the DAM soil materials depends on the failure mechanism type, that's why it is performed here /// instead of just using the [Validate] attribute at the Dike.DamSoils property. /// /// A list of validation results. [Validate] public ValidationResult[] ValidateDamSoils() { const string sourceName = "DamSoil"; var validationResults = new List(); if (WaterBoard != null && WaterBoard.Dike != null) { foreach (DamSoil damSoil in WaterBoard.Dike.DamSoils) { validationResults.AddRange(Validator.ValidateProperty(damSoil, "AbovePhreaticLevel", sourceName)); validationResults.AddRange(Validator.ValidateProperty(damSoil, "BelowPhreaticLevel", sourceName)); if (DamProjectCalculationSpecification.CurrentSpecification.FailureMechanismSystemType == FailureMechanismSystemType.Piping) { validationResults.AddRange(Validator.ValidateProperty(damSoil, "DiameterD70", sourceName)); validationResults.AddRange(Validator.ValidateProperty(damSoil, "PermeabKx", sourceName)); } if (DamProjectCalculationSpecification.CurrentSpecification.FailureMechanismSystemType is FailureMechanismSystemType.StabilityOutside or FailureMechanismSystemType.StabilityInside) { switch (damSoil.ShearStrengthModel) { case ShearStrengthModel.CPhi: validationResults.AddRange(Validator.ValidateProperty(damSoil, "Cohesion", sourceName)); validationResults.AddRange(Validator.ValidateProperty(damSoil, "FrictionAngle", sourceName)); break; case ShearStrengthModel.CuCalculated: validationResults.AddRange(Validator.ValidateProperty(damSoil, "RatioCuPc", sourceName)); validationResults.AddRange(Validator.ValidateProperty(damSoil, "POP", sourceName)); validationResults.AddRange(Validator.ValidateProperty(damSoil, "StrengthIncreaseExponent", sourceName)); break; } validationResults.AddRange(Validator.ValidateProperty(damSoil, "TrafficLoadDegreeOfConsolidation", sourceName)); } } } return validationResults.ToArray(); } public void FillOverallSensorData() { if (SensorData != null) { SensorData.Sensors.Clear(); SensorData.SensorGroups.Clear(); SensorData.SensorLocations.Clear(); } else { SensorData = new SensorData(); } foreach (Location location in Locations) { SensorLocation sd = location.SensorLocation; foreach (Sensor sensor in sd.Sensors) { if (SensorData.GetSensorById(sensor.ID) == null) { SensorData.Sensors.Add(sensor); } } if (SensorData.GetGroupById(sd.Group.ID) == null && sd.Group.ID != 0) { sd.Group.PickSensors = SensorData.Sensors; SensorData.SensorGroups.Add(sd.Group); } if (SensorData.GetSensorLocationByLocationName(sd.LocationName) == null) { SensorData.SensorLocations.Add(sd); } } } public override bool IsVisible(string property) { switch (property) { case "SensorData": return damProjectType == DamProjectType.DamLiveConfiguration; case "DesignCalculations": return HasResults() && DamProjectType == DamProjectType.Design; case "RunColumVisible": return DamProjectType == DamProjectType.Design; case "DamProjectCalculationSpecification": return DamProjectCalculationSpecification.DamCalculationSpecifications.Count > 0; default: return true; } } public override string ToString() { return WaterBoard != null ? WaterBoard.Name : ""; } public void Dispose() { WaterBoard.Dispose(); DamProjectCalculationSpecification.Dispose(); } public ICollection GetDomain(string property) { switch (property) { default: return null; } } /// /// Updates the design calculations. /// private void UpdateDesignCalculations() { if (designCalculations == null) { designCalculations = new List(); } else { designCalculations.Clear(); } List scenarios = CreateScenarioListForCalculation(); foreach (Scenario scenario in scenarios) { if (scenario.CalculationResults != null && scenario.CalculationResults.Count > 0) { designCalculations.AddRange(scenario.CalculationResults); } } DataEventPublisher.DataListModified(designCalculations); } private List CreateScenarioListForDeletion() { var scenarios = new List(); List locations = LocationJobs.Select(x => x.Location).ToList(); foreach (Location location in locations) { if (location.Scenarios != null) { scenarios.AddRange(location.Scenarios); } } return scenarios; } private List CreateScenarioListForCalculation() { var scenarios = new List(); List locations = SelectedLocationJobs.Select(x => x.Location).ToList(); foreach (Location location in locations) { if (location.Scenarios != null) { scenarios.AddRange(location.Scenarios); } } return scenarios; } private int GetNumberOfCalculatedJobs() { var numberOfCalculatedJobs = 0; foreach (LocationJob locationJob in LocationJobs) { if (locationJob.HasDesignScenarioResults) { if (WaterBoard.Dike.Locations.Contains(locationJob.Location)) { WaterBoard.Dike.UpdateLocation(locationJob.Location); break; } numberOfCalculatedJobs++; } } return numberOfCalculatedJobs; } /// /// Check if design with geometry adaption is allowed /// /// private bool IsDesignWithGeometryAdaptionAllowed() { bool isAdoption = DamProjectType == DamProjectType.Design && DamProjectCalculationSpecification.SelectedAnalysisType != AnalysisType.NoAdaption; bool isStabilityOutside = DamProjectCalculationSpecification.DamCalculationSpecifications.Any(specification => specification.FailureMechanismSystemType == FailureMechanismSystemType.StabilityOutside); return (!isAdoption || !isStabilityOutside); } }