// Copyright (C) Stichting Deltares 2024. 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.Generic;
using System.ComponentModel;
using System.Linq;
using Deltares.Dam.Data.CsvImporters;
using Deltares.Geotechnics.Converter;
using Deltares.Geotechnics.Soils;
using Deltares.Geotechnics.SurfaceLines;
using Deltares.Geotechnics.Validation;
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;
public Dike()
{
Name = "Dijkring";
MapForSoilGeometries2D = "";
locations = new List();
soilProfiles = new List();
SurfaceLines2 = new List();
pl1Lines = new List();
soilList = new SoilList();
}
public virtual string MapForSoilGeometries2D { get; set; }
public virtual string Name { get; set; }
///
/// Gets the locations.
///
///
/// The locations.
///
public virtual IList Locations
{
get => locations;
private set => locations = value;
}
public IList SurfaceLines2 { get; set; }
public virtual IList PL1Lines
{
get => pl1Lines;
set => pl1Lines = value;
}
public virtual IList SoilProfiles
{
get => soilProfiles;
set => soilProfiles = value;
}
public virtual SoilList SoilList
{
get => soilList;
set => soilList = value;
}
public virtual IList Aquifers { get; set; } = new List();
public virtual IList TrafficLoadDegreeOfConsolidations { get; set; } = new List();
[Browsable(false)] public virtual IList Gauges { get; set; } = new List();
[Browsable(false)] public virtual IList GaugePLLines { get; set; } = new List();
public bool UsesGauges => (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;
}
}
}
///
/// 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);
}
}
}
}
///
/// Add to soil data as read from the csv-file to the dike soils
///
public void FillImportedCsvSoilsFromCsvFile(IEnumerable soilRecords)
{
if (soilRecords == null)
{
return;
}
foreach (CsvImporterSoils.SoilRecord soilRecord in soilRecords)
{
var soil = new Soil();
soil.Name = soilRecord.SoilName;
soil.Color = soilRecord.SoilColor;
soil.SoilType = soilRecord.SoilType;
if (soilRecord.UnsaturatedUnitWeight.HasValue)
{
soil.AbovePhreaticLevel = soilRecord.UnsaturatedUnitWeight.Value;
}
if (soilRecord.SaturatedUnitWeight.HasValue)
{
soil.BelowPhreaticLevel = soilRecord.SaturatedUnitWeight.Value;
}
if (soilRecord.Cohesion.HasValue)
{
soil.Cohesion = soilRecord.Cohesion.Value;
}
if (soilRecord.FrictionAngle.HasValue)
{
soil.FrictionAngle = soilRecord.FrictionAngle.Value;
}
if (soilRecord.DiameterD70.HasValue)
{
soil.DiameterD70 = soilRecord.DiameterD70.Value;
}
if (soilRecord.PermeabilityX.HasValue)
{
soil.PermeabKx = soilRecord.PermeabilityX.Value;
}
soil.UseDefaultShearStrengthModel = false;
soil.ShearStrengthModel = soilRecord.ShearStrengthModel;
StressCurve stressCurve = ImportedCsvStressCurves.Find(sc => sc.Name == soilRecord.SigmaTauCurveName);
if (stressCurve == null)
{
stressCurve = new StressCurve{ Name = soilRecord.SigmaTauCurveName};
}
soil.StressTable = stressCurve;
// Note: if the stress curve was not found, the soil will be removed from the soil list in TryToMakeSoilDataConsistent()
SigmaSuCurve sigmaSuCurve = ImportedCsvSuTables.Find(sc => sc.Name == soilRecord.SuTableName);
if (sigmaSuCurve == null)
{
sigmaSuCurve = new SigmaSuCurve{ Name = soilRecord.SuTableName};
}
soil.SuTable = sigmaSuCurve;
// Note: if the su table was not found, the soil will be removed from the soil list in TryToMakeSoilDataConsistent()
if (soilRecord.StrengthIncreaseExponent.HasValue)
{
soil.StrengthIncreaseExponent = soilRecord.StrengthIncreaseExponent.Value;
}
if (soilRecord.RatioSuPc.HasValue)
{
soil.RatioCuPc = soilRecord.RatioSuPc.Value;
}
if (soilRecord.Pop.HasValue)
{
soil.POP = soilRecord.Pop.Value;
}
ImportedCsvSoils.Add(soil);
}
}
///
/// Add to traffic load degree of consolidation as read from the csv-file to the dike soils.
/// For soil profile 1D, only the soils used by the soil profiles are added.
///
public void FillTrafficLoadDegreeOfConsolidationFromCsvFile(IEnumerable soilRecords)
{
if (soilRecords == null)
{
return;
}
foreach (CsvImporterSoils.SoilRecord soilRecord in soilRecords)
{
if (soilList.Soils.Count > 0 && soilList.GetSoilByName(soilRecord.SoilName) == null)
{
continue;
}
var degreeOfConsolidation = new TrafficLoadDegreeOfConsolidation
{
SoilName = soilRecord.SoilName
};
if (soilRecord.TrafficLoadDegreeOfConsolidation.HasValue)
{
degreeOfConsolidation.DegreeOfConsolidation = soilRecord.TrafficLoadDegreeOfConsolidation.Value;
}
TrafficLoadDegreeOfConsolidations.Add(degreeOfConsolidation);
}
}
///
/// Fill the imported sigma tau curves from the csv file
///
///
public void FillImportedSigmaTauCurvesFromCsvFile(IEnumerable sigmaTauCurveRecords)
{
if (sigmaTauCurveRecords == null)
{
return;
}
foreach (CsvImporterSigmaTauCurves.SigmaTauCurveRecord sigmaTauCurveRecord in sigmaTauCurveRecords)
{
StressCurve sigmaTauCurve = ImportedCsvStressCurves.Find(sc => sc.Name == sigmaTauCurveRecord.SigmaTauCurveName);
if (sigmaTauCurve == null)
{
sigmaTauCurve = new StressCurve();
sigmaTauCurve.Name = sigmaTauCurveRecord.SigmaTauCurveName;
ImportedCsvStressCurves.Add(sigmaTauCurve);
}
sigmaTauCurve.SigmaTaus.Add(new SigmaTau
{
Sigma = sigmaTauCurveRecord.Sigma,
Tau = sigmaTauCurveRecord.Tau
});
}
}
///
/// Fill the imported su table curves from the csv file
///
///
public void FillImportedSuTablesFromCsvFile(IEnumerable suTableRecords)
{
if (suTableRecords == null)
{
return;
}
foreach (CsvImporterSuTables.SuTableRecord suTableRecord in suTableRecords)
{
SigmaSuCurve sigmaSuCurve = ImportedCsvSuTables.Find(sc => sc.Name == suTableRecord.SuTableName);
if (sigmaSuCurve == null)
{
sigmaSuCurve = new SigmaSuCurve();
sigmaSuCurve.Name = suTableRecord.SuTableName;
ImportedCsvSuTables.Add(sigmaSuCurve);
}
sigmaSuCurve.SigmaSus.Add(new SigmaSu()
{
Sigma = suTableRecord.Sigma,
Su = suTableRecord.Su
});
}
}
///
/// Fill the imported aquifers from the csv file
///
///
public void FillImportedAquifersFromCsvFile(IEnumerable aquiferRecords)
{
if (aquiferRecords == null)
{
return;
}
Aquifers = new List();
foreach (CsvImporterAquifers.AquiferRecord aquiferRecord in aquiferRecords)
{
Aquifer aquifer = Aquifers.Find(sc => sc.StixFileName == aquiferRecord.StixFileName && sc.LayerName == aquiferRecord.LayerName);
if (aquifer != null)
{
continue;
}
aquifer = new Aquifer
{
StixFileName = aquiferRecord.StixFileName,
LayerName = aquiferRecord.LayerName
};
Aquifers.Add(aquifer);
}
}
///
/// Adapt data so it is consistent
///
/// The soils as imported from the csv file
public IEnumerable MakeDataConsistent(List importedCsvSoils)
{
List soilErrorMessages;
var logMessages = new List();
List errorSoils = TryToMakeSoilDataConsistent(importedCsvSoils, out soilErrorMessages);
for (var i = 0; i < soilErrorMessages.Count; i++)
{
logMessages.Add(new LogMessage(LogMessageType.Warning, this, soilErrorMessages[i]));
}
// 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 csv-file 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.SoilList = SoilList;
location.MapForSoilGeometries2D = MapForSoilGeometries2D;
location.Gauges.Clear();
location.Gauges.AddRange(Gauges);
location.GaugePLLines.Clear();
location.GaugePLLines.AddRange(GaugePLLines);
}
///
/// Transfer the properties of the imported soils to the existing soils in the soil list.
///
public void AssignImportedCsvSoilsPropertiesToSoilListSoils()
{
foreach (Soil soil in ImportedCsvSoils)
{
Soil existingSoil = soilList.GetSoilByName(soil.Name);
if (existingSoil != null)
{
existingSoil.Assign(soil);
}
}
}
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;
}
}
internal List ImportedCsvSoils { get; } = new();
internal List ImportedCsvStressCurves { get; } = new();
internal List ImportedCsvSuTables { get; } = new();
///
/// 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).
///
/// The soils as imported from the csv file
///
private List TryToMakeSoilDataConsistent(List importedCsvSoils, out List soilErrorMessages)
{
soilErrorMessages = new List();
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 there are errors
foreach (Soil soil in soilList.Soils)
{
Soil fs = importedCsvSoils.Find(t => string.Equals(t.Name, soil.Name, StringComparison.CurrentCultureIgnoreCase));
if (fs == null)
{
errorSoils.Add(soil);
soilErrorMessages.Add(string.Format(LocalizationManager.GetTranslatedText(GetType(), "SoilNotFoundInCsvFile"), soil.Name));
}
}
// 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 importedCsvSoils)
{
if ((soil.StressTable.SigmaTaus.Count == 0) && (soil.StressTable.Name != ""))
{
soilList.Soils.Remove(soil);
errorSoils.Add(soil);
soilErrorMessages.Add(string.Format(LocalizationManager.GetTranslatedText(GetType(), "SigmaTauCurveNotFoundInCsvFile"),
soil.StressTable.Name, soil.Name));
}
if ((soil.SuTable.SigmaSus.Count == 0) && (soil.SuTable.Name != ""))
{
soilList.Soils.Remove(soil);
errorSoils.Add(soil);
soilErrorMessages.Add(string.Format(LocalizationManager.GetTranslatedText(GetType(), "SuTableNotFoundInCsvFile"),
soil.SuTable.Name, soil.Name));
}
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)
{
if (spp.SoilProfile != null)
{
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;
}
}