// 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 Deltares.Geometry;
using Deltares.Geotechnics.Soils;
using Deltares.Geotechnics.SurfaceLines;
using Deltares.Standard.Logging;
namespace Deltares.Dam.Data;
public class WaterBoardPostProcessRelativeProfilesException : Exception
{
public WaterBoardPostProcessRelativeProfilesException(string message)
: base(message) {}
}
public static class WaterBoardPostProcessRelativeProfiles
{
///
/// Creates the absolute profiles based on the original profiles.
/// The original profiles are considered to be relative to the specified characteristic point
/// 1) Create a copy of the assigned segment for each location
/// 2) Create a copy of the soil profile and assign it to the new segment
/// 3) In the copy of the soil profile the toplevel will be moved to the z-coordinate of the specified characteristic point
/// 4) Add the new soil profile to the dike.SoilProfileList
///
/// The water board.
///
public static void CreateAbsoluteProfiles(WaterBoard waterBoard, CharacteristicPointType characteristicPointType)
{
const double moveAccuracy = 1e-6;
foreach (Dike dike in waterBoard.Dikes)
{
foreach (Location location in dike.Locations)
{
Segment orgSegment = location.Segment;
if (orgSegment != null)
{
double topSurfaceline = location.SurfaceLine2.CharacteristicPoints.Geometry.GetMaxZ();
GeometryPoint characteristicGeometryPoint = location.SurfaceLine2.CharacteristicPoints.GetGeometryPoint(characteristicPointType);
var errorFound = false;
var newSegment = new Segment();
newSegment.Name = $"Segment {orgSegment.Name}-{location.Name}";
var soilProbabilityIndex = 0;
foreach (SoilGeometryProbability orgSoilGeometryProbability in orgSegment.SoilProfileProbabilities)
{
soilProbabilityIndex++;
try
{
if (characteristicGeometryPoint == null)
{
throw new WaterBoardPostProcessRelativeProfilesException($"Characteristic point {characteristicPointType} is not defined");
}
if (orgSoilGeometryProbability.SoilProfileType.Equals(SoilProfileType.SoilProfile2D))
{
throw new WaterBoardPostProcessRelativeProfilesException("Cannot apply relative profiles for 2d soilprofiles");
}
// Create a copy of the original SoilGeometryProbability
var newSoilGeometryProbability = new SoilGeometryProbability();
newSoilGeometryProbability.Assign(orgSoilGeometryProbability);
// Move all layer boundaries so toplevel of the soilprofile1D will be at level of specified characteristic point
// Dependent if deltaZ is positive or negative, the bottomLevel should be adjusted before or after adjusting the layers
// This is necessary, because the bottomlevel is adjusted automatically during adjustment of layers
SoilProfile1D soilProfile1D = newSoilGeometryProbability.SoilProfile;
soilProfile1D.Name = $"{orgSoilGeometryProbability.SoilProfile.Name}-{location.Name}-{soilProbabilityIndex}";
double deltaZ = characteristicGeometryPoint.Z - soilProfile1D.TopLevel;
if (deltaZ < 0)
{
soilProfile1D.BottomLevel += deltaZ;
}
foreach (SoilLayer1D soilLayer1D in soilProfile1D.Layers)
{
soilLayer1D.TopLevel += deltaZ;
}
if (deltaZ > 0)
{
soilProfile1D.BottomLevel += deltaZ;
}
double profileTopLevel = soilProfile1D.TopLevel;
// If the top of the profile is moved below the top of the surface level, a gap appears which will be filled in later in the UI
// to the combined 2D. However, for Piping calculation the 1D profile itself needs to start at least at the top of the surface line
// too in order to perform a correct calculation. So extend the 1D profile here too!
if (Math.Abs(deltaZ) > moveAccuracy && profileTopLevel < topSurfaceline)
{
var newTopLayer = new SoilLayer1D
{
SoilProfile = soilProfile1D,
MaintainLayerOrder = true,
TopLevel = topSurfaceline,
IsAquifer = false,
Soil = dike.SoilList.GetSoilByName(location.DikeEmbankmentMaterial)
};
soilProfile1D.Layers.Insert(0, newTopLayer);
}
// Add new SoilGeometryProbability to new segment and assign new segment to location
newSegment.SoilProfileProbabilities.Add(newSoilGeometryProbability);
// Add new soil profile to existing list.
dike.SoilProfiles.Add(soilProfile1D);
}
catch (Exception e)
{
errorFound = true;
LogManager.Messages.Add(new LogMessage(LogMessageType.Error, location,
$"Error in location '{location.Name}' applying relative height for soilprofile '{orgSoilGeometryProbability.SoilGeometryName}': {e.Message} "));
}
}
if (!errorFound)
{
location.Segment = newSegment;
waterBoard.Segments.Add(newSegment);
}
}
}
}
}
}