// Copyright (C) Stichting Deltares 2023. All rights reserved. // // This file is part of the application DAM - Live. // // DAM - Live 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.ComponentModel; using System.IO; using System.Linq; using System.Xml.Serialization; using Deltares.Geotechnics.Converter; using Deltares.Geotechnics.Soils; using Deltares.Geotechnics.SurfaceLines; using Deltares.Geotechnics.Validation; using Deltares.Soilbase; using Deltares.Standard; using Deltares.Standard.Attributes; using Deltares.Standard.Data; using Deltares.Standard.Extensions; using Deltares.Standard.Language; using Deltares.Standard.Logging; using Deltares.Standard.Validation; namespace Deltares.Dam.Data; public class DikeException : Exception { public DikeException(string message) : base(message) {} } public class DikeParameterNames { public const string MapForSoilGeometries2D = "MapForSoilGeometries2D"; } [TrackChanges] public class Dike : IVisibleEnabled, IDisposable { private IList locations; private IList pl1Lines; private SoilList soilList; private IList soilProfiles; private string soilDatabaseName = ""; private List databaseSoils = new List(); public Dike() { Name = "Dijkring"; MapForSoilGeometries2D = ""; locations = new List(); soilProfiles = new List(); surfaceLines = new DelegatedList { AddMethod = ConvertAddedOldSurfaceLineToNewFormat }; SurfaceLines2 = new List(); pl1Lines = new List(); soilList = new SoilList(); IsRemoveStiFiles = true; } public virtual string MapForSoilGeometries2D { get; set; } public bool IsRemoveStiFiles { get; set; } public MStabShearStrength ShearStrengthModel { get; set; } public virtual string Name { get; set; } public virtual string SoilDatabaseName { get { return soilDatabaseName; } set { soilDatabaseName = value; UpdateLocationsDatabaseName(); } } [XmlIgnore] public SoilbaseDB SoilBaseDB { get; set; } /// /// Gets the locations. /// /// /// The locations. /// public virtual IList Locations { get { return locations; } private set { locations = value; } } public IList SurfaceLines2 { get; set; } public virtual IList PL1Lines { get { return pl1Lines; } set { pl1Lines = value; } } public virtual IList SoilProfiles { get { return soilProfiles; } set { soilProfiles = value; } } public virtual SoilList SoilList { get { return soilList; } set { soilList = value; } } [Browsable(false)] public virtual IList Gauges { get; set; } = new List(); [Browsable(false)] public virtual IList GaugePLLines { get; set; } = new List(); public bool UsesGauges { get { return GaugePLLines != null && GaugePLLines.Count > 0 && Gauges != null; } } public virtual List Scenarios { get { var scenarios = new List(); foreach (Location location in Locations) { scenarios.AddRange(location.Scenarios); } return scenarios; } } public string Description { get; set; } = ""; /// /// Updates the locations for scenarios. /// public void UpdateLocationsForScenarios() { foreach (Location location in Locations) { foreach (Scenario scenario in location.Scenarios) { scenario.Location = location; } } } /// /// Updates the name soil database for all locations. /// public void UpdateLocationsDatabaseName() { foreach (Location location in Locations) { location.SoildatabaseName = SoilDatabaseName; } } /// /// Sorts the locations. /// public void SortLocations() { locations = Locations.OrderBy(o => o.Name).ToList(); } public void Validate() { if (Locations == null || Locations.Count < 1) { throw new DikeException("The dike ring has no locations defined"); } foreach (Location location in Locations) { if (location.LocalXZSurfaceLine2 != null) { var validator = new SurfaceLine2Validator(); ValidationResult[] validationResults = validator.ValidateCharacteristicPointsAreOrdered(location.LocalXZSurfaceLine2) .Concat(validator.ValidateGeometryPointsAreOrdered(location.LocalXZSurfaceLine2)).ToArray(); if (validationResults.Length > 0) { throw new SurfaceLineException(validationResults[0].Text); } } } } public void CreateSoilBase() { if (SoilDatabaseName == null || !File.Exists(SoilDatabaseName)) { throw new DikeException($"The soil database '{SoilDatabaseName}' cannot be found"); } SoilBaseDB = SoilbaseDB.Create(SoilDatabaseName); } /// /// Read all the soils and their parameters from the database /// public void FillDataBaseSoilListFromSoilBase() { using (var geoDatabase = new GeoDatabase(SoilDatabaseName)) { geoDatabase.ReUseSoils = true; SoilList newSoilList = geoDatabase.ReadSoils(soilList.Soils); databaseSoils = newSoilList.Soils; } } /// /// Add 1D-soilprofiles from MGeobase database /// public void AddSoilProfilesFromDB() { if (SoilDatabaseName == null || !File.Exists(SoilDatabaseName)) { throw new DikeException($"The MGeobase database '{SoilDatabaseName}' cannot be found"); } if (soilList.Soils.Count == 0) { FillDataBaseSoilListFromSoilBase(); soilList.Soils.AddRange(databaseSoils); } if (soilList.Soils.Count == 0) { throw new DikeException($"The MGeobase database '{SoilDatabaseName}' does not contain soils and can not be used."); } var mgbDB = new MGeobaseDB(soilList); IList addedSoilProfiles = mgbDB.AddSoilProfiles(SoilDatabaseName); foreach (SoilProfile1D addedSoilProfile in addedSoilProfiles) { soilProfiles.Add(addedSoilProfile); } } /// /// Adapt data so it is consistent /// public List MakeDataConsistent() { List errorSoils = TryToMakeSoilDataConsistent(); var logMessages = new List(); // Delete all locations without surfaceline logMessages.AddRange(DeleteLocationsWithoutSurfaceLines()); // Delete all locations that have profiles (in their segment) which hold soils // that are not in the soil database and so have no parameters. logMessages.AddRange(DeleteLocationsWithProfilesWithUnknownSoils(errorSoils)); return logMessages; } public Dictionary GetParametersAsNameValuePairs() { var nameValuePairs = new Dictionary(); nameValuePairs.Add(DikeParameterNames.MapForSoilGeometries2D, MapForSoilGeometries2D); return nameValuePairs; } public void SetParameterFromNameValuePair(string parameterName, string parameterValue) { if (parameterName.Equals(DikeParameterNames.MapForSoilGeometries2D)) { MapForSoilGeometries2D = parameterValue; } } public void UpdateLocation(Location location) { location.SoildatabaseName = SoilDatabaseName; location.SoilList = SoilList; location.MapForSoilGeometries2D = MapForSoilGeometries2D; location.Gauges.Clear(); location.Gauges.AddRange(Gauges); location.GaugePLLines.Clear(); location.GaugePLLines.AddRange(GaugePLLines); } public override string ToString() { return Name; } public void Dispose() { foreach (Location location in Locations) { location.Dispose(); } foreach (SurfaceLine2 surfaceLine2 in SurfaceLines2) { surfaceLine2.Dispose(); } } public bool IsVisible(string property) { return true; } public bool IsEnabled(string property) { switch (property) { case "Name": return false; case "SoilList": return false; default: return true; } } /// /// Tries to make the soil data as read for 1D profiles consistent with the data in the soil database. /// In the end we have a neat soil list with parameters for every soil as read from the database. /// We might have a list with errors (soils that were not to be found in the database so soils for which /// no parameters could be found). /// /// private List TryToMakeSoilDataConsistent() { var errorSoils = new List(); // Fill the list of errorSoils with soils that are in the current soillist (as result of importing // 1D profiles) but that are not found in the soil database because that are errors foreach (Soil soil in soilList.Soils) { Soil fs = databaseSoils.Find(t => String.Equals(t.Name, soil.Name, StringComparison.CurrentCultureIgnoreCase)); if (fs == null) { errorSoils.Add(soil); } } // Remove the error soils form the list foreach (Soil errorSoil in errorSoils) { soilList.Soils.Remove(errorSoil); } // Get the parameters for every soil in the now proper soil list from the database. Add soils // that are in the database but not yet in the soil list. foreach (Soil soil in databaseSoils) { Soil existingSoil = soilList.GetSoilByName(soil.Name); if (existingSoil == null) { soilList.Soils.Add(soil); } else { existingSoil.Assign(soil); } } return errorSoils; } /// /// Removes all locations which have profiles that have invalid soils /// private List DeleteLocationsWithProfilesWithUnknownSoils(List invalidSoils) { var logMessages = new List(); var invalidLocations = new List(); string soilProf; string invSoil; foreach (Location location in locations) { bool isInValid; var message = ""; if (location.Segment == null) { isInValid = true; message = String.Format(LocalizationManager.GetTranslatedText(GetType(), "LocationWitNameHasNoSegment"), location.Name); } else { isInValid = DoesLocationHaveInvalidSoils(invalidSoils, location, out soilProf, out invSoil); if (isInValid) { message = String.Format(LocalizationManager.GetTranslatedText(GetType(), "LocationHasProfileWithInvalidSoils"), location.Name, soilProf, invSoil); } } if (isInValid) { invalidLocations.Add(location); logMessages.Add(new LogMessage(LogMessageType.Warning, this, message)); } } foreach (Location invalidLocation in invalidLocations) { locations.Remove(invalidLocation); } return logMessages; } /// /// Checks wether a location (or rather the soilprofiles in the segement of the location) contains invalid soils. /// A soil is hereby considered invalid if it is not found in the database. /// /// /// /// /// /// private bool DoesLocationHaveInvalidSoils(List invalidSoils, Location location, out string soilProf, out string invSoil) { soilProf = " "; invSoil = " "; foreach (SoilGeometryProbability spp in location.Segment.SoilProfileProbabilities) { foreach (Soil invalidSoil in invalidSoils) { SoilLayer1D fl = spp.SoilProfile.Layers.Find(l => String.Equals(l.Soil.Name, invalidSoil.Name, StringComparison.CurrentCultureIgnoreCase)); if (fl != null) { soilProf = spp.SoilProfile.Name; invSoil = invalidSoil.Name; return true; } } } return false; } /// /// Delete all locations without surfacelines /// private List DeleteLocationsWithoutSurfaceLines() { var logMessages = new List(); //Add all locations with valid surfaceline var newLocations = new List(); newLocations.AddRange(Locations.Where(loc => loc.SurfaceLine2 != null)); // Report which locations are not added because no valid surfaceline is found var deletedLocations = new List(); deletedLocations.AddRange(Locations.Where(loc => loc.SurfaceLine2 == null)); foreach (Location deletedLocation in deletedLocations) { string locationHasNoSurfaceLine = LocalizationManager.GetTranslatedText(GetType(), "LocationHasNoSurfaceLine"); logMessages.Add(new LogMessage(LogMessageType.Warning, this, String.Format(locationHasNoSurfaceLine, deletedLocation.Name))); } Locations = newLocations; return logMessages; } #region Backwards compatibility private IList surfaceLines; /// /// Gets or sets all surface lines instances associated with this dike. /// /// Composite owner of all that are part of the /// application that are associated with this dike. [Obsolete("Do not use this member; Only exists for backwards compatibility.", true)] public virtual IList SurfaceLines { get { return surfaceLines; } set { surfaceLines = value; } } /// /// Performs backwards compatibility conversion from old-style surfaceline to new-style /// surfaceline, and ensures that shared references are set correctly. /// /// Persisted version of the old-style surfaceline. private void ConvertAddedOldSurfaceLineToNewFormat(SurfaceLine argument) { SurfaceLine2 surfaceLine2 = new OldSurfaceLineToNewConverter().Convert(argument); SurfaceLines2.Add(surfaceLine2); foreach (Location location in Locations) { location.SetNewSurfaceLineIfMatchesWithOldPersistedSurfaceLine(surfaceLine2); } // Remove old entry; it is no longer required. surfaceLines.Remove(argument); argument.Dispose(); } #endregion }