// 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); } } } } } }