// Copyright (C) Stichting Deltares 2018. All rights reserved.
//
// This file is part of the Dam Engine.
//
// The Dam Engine 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.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using Deltares.DamEngine.Calculators.Dikes_Operational;
using Deltares.DamEngine.Calculators.PlLinesCreator;
using Deltares.DamEngine.Calculators.Stability;
using Deltares.DamEngine.Calculators.Uplift;
using Deltares.DamEngine.Data.General;
using Deltares.DamEngine.Data.General.PlLines;
using Deltares.DamEngine.Data.General.Results;
using Deltares.DamEngine.Data.Geotechnics;
namespace Deltares.DamEngine.Calculators.General
{
public class StabilityProjectFileCreationArguments
{
public string DikeName { get; set; }
public Location Location { get; set; }
public int EntryCount { get; set; }
public DateTime EntryDateTime { get; set; }
public MStabModelType Model { get; set; }
public string StabilityWorkingPath { get; set; }
public string SoilGeometry2DName { get; set; }
public SoilProfileType SoilProfileType { get; set; }
public PLLines PLLines { get; set; }
public FailureMechanismSystemType FailureMechanismType { get; set; }
public MStabParameters StabilityParameters { get; set; }
public string StabilityExePath { get; set; }
}
internal class CalculationHelper
{
internal static string CreateStabilityProjectFile(StabilityProjectFileCreationArguments arg)
{
var location = arg.Location;
var model = arg.Model;
var fileName = GetProjectFileName(arg.DikeName, location, null, model, arg.StabilityWorkingPath, arg.EntryDateTime);
// get all the parameters needed for the calculation
var damCalculation = GetCalculationSpecification(arg, fileName);
// Create the project file
CreateMStabProjectFile(damCalculation.FailureMechanismParametersMStab, arg.StabilityExePath);
return fileName;
}
internal static IEnumerable GetStabilityModels(bool isCombinedBishopUpliftVan, Location location, PLLines plLines, string soilGeometry2DName, MStabModelType defaultModel)
{
double? upliftFactor = GetLowestUpliftFactor(location.SurfaceLine,
location.GetMostProbableProfile(FailureMechanismSystemType.StabilityInside), soilGeometry2DName, plLines, location);
return !isCombinedBishopUpliftVan ? new List { defaultModel } : GetMStabModelsToCalculate(upliftFactor);
}
internal static DamFailureMechanismeCalculationSpecification GetCalculationSpecification(StabilityProjectFileCreationArguments arguments, string projectFileName)
{
return GetCalculationSpecification(arguments.FailureMechanismType, arguments.Location,
arguments.SoilGeometry2DName, arguments.SoilProfileType,
arguments.PLLines, arguments.StabilityParameters, arguments.Model,
arguments.Location.StabilityOptions.SoilDatabase, projectFileName);
}
internal static DamFailureMechanismeCalculationSpecification GetCalculationSpecification(
FailureMechanismSystemType failureMechanismType,
Location location, string soilGeometry2DName,
SoilProfileType soilProfileType,
PLLines plLines,
MStabParameters mstabParameters, MStabModelType model,
string soilDatabasePath, string projectFileName)
{
// Note: local use of new calculationSpecification for now ok but might be unwanted in future when you want to use multiple specifications
var calculationSpecification = new DamFailureMechanismeCalculationSpecification();
BuildDamCalculation(failureMechanismType, location, soilGeometry2DName, soilProfileType, plLines, mstabParameters, model, calculationSpecification, soilDatabasePath, projectFileName);
return calculationSpecification;
}
///
/// Fill damCalculation with the appropriate values
///
internal static void BuildDamCalculation(FailureMechanismSystemType failureMechanismType, Location location, string soilGeometry2DName, SoilProfileType soilProfileType,
PLLines plLines, MStabParameters mstabParameters, MStabModelType model,
DamFailureMechanismeCalculationSpecification damCalculation, string soilDatabasePath, string projectFileName)
{
damCalculation.FailureMechanismParametersMStab =
new FailureMechanismParametersMStab
{
Location = location,
SurfaceLine = location.SurfaceLine,
PLLines = plLines,
SoilProfile = location.GetMostProbableProfile(FailureMechanismSystemType.StabilityInside),
MStabParameters =
new MStabParameters
{
SoilDatabaseName = soilDatabasePath,
Model = model,
ProjectFileName = projectFileName,
GeometryCreationOptions =
{
SoilProfileType = soilProfileType,
SoilGeometry2DFilename = soilGeometry2DName,
MaterialForDike = location.DikeEmbankmentMaterial,
MaterialForShoulder = location.ShoulderEmbankmentMaterial,
XOffsetSoilGeometry2DOrigin = -location.XSoilGeometry2DOrigin,
IsUseOriginalPLLineAssignments = location.IsUseOriginalPLLineAssignments
}
}
};
if (location.StabilityOptions?.TrafficLoad != null)
{
damCalculation.FailureMechanismParametersMStab.TrafficLoad = location.StabilityOptions.TrafficLoad.Value;
}
damCalculation.FailureMechanismParametersMStab.MStabParameters.GridPosition =
failureMechanismType == FailureMechanismSystemType.StabilityOutside ? MStabGridPosition.Left : MStabGridPosition.Right;
MStabParameters parameters = damCalculation.FailureMechanismParametersMStab.MStabParameters;
parameters.GeometryCreationOptions.PLLineAssignment = PLLineCreationMethod2PLLineAssignment(location.ModelParametersForPLLines.PLLineCreationMethod);
if (location.IsUseOriginalPLLineAssignments)
parameters.GeometryCreationOptions.PLLineAssignment = PLLineAssignment.OrginalPLLines;
parameters.GeometryCreationOptions.IntrusionVerticalWaterPressureType = location.IntrusionVerticalWaterPressure.Value;
parameters.GeometryCreationOptions.PenetrationLength = location.ModelParametersForPLLines.PenetrationLength;
switch (failureMechanismType)
{
case FailureMechanismSystemType.StabilityOutside:
parameters.GridPosition = MStabGridPosition.Left;
break;
default:
parameters.GridPosition = MStabGridPosition.Right;
break;
}
parameters.ShearStrength = mstabParameters.ShearStrength;
parameters.IsProbabilistic = mstabParameters.IsProbabilistic;
parameters.SearchMethod = mstabParameters.SearchMethod;
parameters.CalculationOptions = mstabParameters.CalculationOptions;
if (location.StabilityOptions?.MinimalCircleDepth != null)
{
parameters.CalculationOptions.MinimalCircleDepth = location.StabilityOptions.MinimalCircleDepth.Value;
}
parameters.ZoneAreas = new MStabZoneAreas
{
SafetyFactorZone1A = location.ModelFactors.RequiredSafetyFactorStabilityInnerSlope.Value,
SafetyFactorZone1B = location.ModelFactors.RequiredSafetyFactorStabilityInnerSlope.Value,
DikeTableHeight = location.SurfaceLine.GetDefaultDikeTableHeight().Value,
XCoordinateDikeTopAtPolder = location.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtPolder).X,
XCoordinateDikeTopAtRiver = location.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtRiver).X,
XCoordinateStartRestProfile = location.SurfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtRiver).X
};
if (location.StabilityOptions?.ZoneAreaRestSlopeCrestWidth != null)
{
parameters.ZoneAreas.DikeTableWidth = location.StabilityOptions.ZoneAreaRestSlopeCrestWidth.Value;
}
// Slip circle definition for Uplift Van; TODO: Combine with code in StabilityCalculation
parameters.SlipCircleDefinition.Assign(mstabParameters.SlipCircleDefinition);
if (parameters.Model == MStabModelType.UpliftVan)
{
// Determine right side of slip plane grid (right grid)
// This is the location with the lowest uplift factor
SurfaceLine2 surfaceLine = location.SurfaceLine;
var upliftLocationAndResult = GetLocationWithLowestUpliftFactor(surfaceLine, location.GetMostProbableProfile(FailureMechanismSystemType.StabilityInside), soilGeometry2DName, plLines, location);
double upliftCriterion = location.UpliftCriterionStability.Value;
bool isUplift = !(upliftLocationAndResult == null) && (upliftLocationAndResult.UpliftFactor < upliftCriterion);
double xCoordinateLastUpliftPoint = isUplift ? upliftLocationAndResult.X : surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeToeAtPolder).X;
parameters.SlipCircleDefinition.XCoordinateLastUpliftPoint = xCoordinateLastUpliftPoint;
}
if (parameters.CalculationOptions.ZonesType.Equals(MStabZonesType.ForbiddenZone))
{
var surfaceLine = damCalculation.FailureMechanismParametersMStab.SurfaceLine;
double maxZoneX = surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtPolder).X +
location.StabilityOptions.ForbiddenZoneFactor.Value * (surfaceLine.GetDikeToeInward().X - surfaceLine.CharacteristicPoints.GetGeometryPoint(CharacteristicPointType.DikeTopAtPolder).X);
parameters.ForbiddenZone = new MStabForbiddenZone
{
IsXEntryMinUsed = false,
XEntryMin = 0.0,
IsXEntryMaxUsed = true,
XEntryMax = maxZoneX
};
}
}
///
/// Converts PLLineCreationMethod (from location) to PLLineAssignment
///
/// The pl line creation method.
/// the pl line assignment method
public static PLLineAssignment PLLineCreationMethod2PLLineAssignment(PLLineCreationMethod plLineCreationMethod)
{
PLLineAssignment plLineAssignment = PLLineAssignment.ExpertKnowledge;
switch (plLineCreationMethod)
{
case PLLineCreationMethod.ExpertKnowledgeRRD:
case PLLineCreationMethod.ExpertKnowledgeLinearInDike:
case PLLineCreationMethod.GaugesWithFallbackToExpertKnowledgeRRD:
plLineAssignment = PLLineAssignment.ExpertKnowledge;
break;
}
return plLineAssignment;
}
///
/// Gets the location with lowest uplift factor.
///
/// The surface line.
/// The soil profile.
/// Name of the soil geometry2 D.
/// The pl lines.
/// The location.
///
static public UpliftLocationAndResult GetLocationWithLowestUpliftFactor(SurfaceLine2 surfaceLine, SoilProfile1D soilProfile, string soilGeometry2DName, PLLines plLines, Location location)
{
UpliftLocationDeterminator upliftLocationDeterminator = new UpliftLocationDeterminator()
{
SurfaceLine = surfaceLine,
SoilProfile = soilProfile,
SoilGeometry2DName = soilGeometry2DName,
SoilList = location.SoilList,
DikeEmbankmentMaterial = location.GetDikeEmbankmentSoil(),
PLLines = plLines,
XSoilGeometry2DOrigin = location.XSoilGeometry2DOrigin
};
return upliftLocationDeterminator.GetLocationAtWithLowestUpliftFactor();
}
///
/// Calculate all generated MStab files
///
///
///
internal static void CalculateMStabProjects(string directory, string mstabExePath)
{
var agent = new StabilityServiceAgent { MStabExePath = mstabExePath };
agent.CalculateMStabDirectory(directory);
}
///
/// Calculate all generated MStab files
///
internal static void CalculateMStabProjects(IEnumerable projectFilenames, string stabilityExePath)
{
var agent = new StabilityServiceAgent { MStabExePath = stabilityExePath };
foreach (string mstabProjectFilename in projectFilenames)
{
string mstabProjectFullFilename = Path.GetFullPath(mstabProjectFilename);
agent.CalculateMStabProject(mstabProjectFullFilename);
}
}
///
/// Determine name of Stability project file
///
internal static string GetProjectFileName(string dikeName, Location location, int? jobCount, MStabModelType? model, string stabilityWorkingPath, DateTime? dateTime)
{
string calculationName = String.Format("Dik({0})_Loc({1})", dikeName, location.Name);
if (jobCount != null)
{
calculationName = calculationName + String.Format("_Stp({0})", jobCount);
}
if (model != null)
{
calculationName = calculationName + String.Format("_Mdl({0})", model);
}
if (dateTime != null)
{
calculationName = calculationName + "_" + DateToTimeStamp(dateTime.Value);
}
calculationName = Regex.Replace(calculationName, @"[\\\/:\*\?""'<>|.]", "_");
// assemble the target project file name
return Path.Combine(stabilityWorkingPath, calculationName + ".sti");
}
///
/// Convert Date to time stamp as needed by TNO AnySense.
///
/// The date time.
///
public static string DateToTimeStamp(DateTime dateTime)
{
// Following 2 lines is an example how to handle customization of this format.
// TNO at the last moment decided they did not need this change so we change it back to
// the default format
// string customFormat = "yyyy-MM-dd_HH-mm-ss";
// return dateTime.ToString(customFormat);
return dateTime.ToString("s", DateTimeFormatInfo.InvariantInfo);
}
///
/// Select which models to calculate dependent on uplift factor
///
internal static IList GetMStabModelsToCalculate(double? upliftFactor)
{
const double CBishopMinimum = 1.0;
const double CLiftVanMaximum = 1.2;
var models = new List();
if (!upliftFactor.HasValue || upliftFactor >= CBishopMinimum)
models.Add(MStabModelType.Bishop);
if (!upliftFactor.HasValue || upliftFactor <= CLiftVanMaximum)
models.Add(MStabModelType.UpliftVan);
return models;
}
///
/// Determine where lowest uplift factor occurs and the value of that factor
///
internal static double? GetLowestUpliftFactor(SurfaceLine2 surfaceLine, SoilProfile1D soilProfile, string soilGeometry2DName, PLLines plLines, Location location)
{
var upliftLocationDeterminator = new UpliftLocationDeterminator()
{
SurfaceLine = surfaceLine,
SoilProfile = soilProfile,
SoilGeometry2DName = soilGeometry2DName,
SoilList = location.SoilList,
DikeEmbankmentMaterial = location.GetDikeEmbankmentSoil(),
PLLines = plLines,
XSoilGeometry2DOrigin = location.XSoilGeometry2DOrigin
};
UpliftLocationAndResult upliftLocationAndResult = upliftLocationDeterminator.GetLocationAtWithLowestUpliftFactor();
if (upliftLocationAndResult != null)
return upliftLocationAndResult.UpliftFactor;
return null;
}
///
/// Creates all PL lines.
///
/// The water level.
/// The location.
/// Name of the soil geometry2 D.
/// Type of the soil geometry.
///
internal static PLLines CreateAllPLLines(double waterLevel, Location location, string soilGeometry2DName, SoilProfileType soilProfileType)
{
// When calculating with timeseries, we want PL3 and PL4 to derive the head from the waterlevel.
// We can force that by overruling the location HeadPl3 and HeadPl4 with null,
// because then in PLLinesCreator the waterlevel is used as head for Pl3 and Pl4
// for stability this must set to true
var plLinesCreator = new PLLinesCreator
{
WaterLevelRiverHigh = waterLevel,
SurfaceLine = location.SurfaceLine,
WaterLevelPolder = location.PolderLevel,
HeadInPLLine2 = location.HeadPl2,
HeadInPLLine3 = null,
HeadInPLLine4 = null,
ModelParametersForPLLines = location.ModelParametersForPLLines,
SoilProfile = location.GetMostProbableProfile(FailureMechanismSystemType.StabilityInside),
SoilGeometry2DName = soilGeometry2DName,
SoilProfileType = soilProfileType,
GaugePLLines = location.GaugePLLines,
Gauges = location.Gauges,
GaugeMissVal = location.GaugeMissVal,
IsAdjustPL3AndPL4SoNoUpliftWillOccurEnabled = true,
PlLineOffsetBelowDikeTopAtRiver = location.PlLineOffsetBelowDikeTopAtRiver,
PlLineOffsetBelowDikeTopAtPolder = location.PlLineOffsetBelowDikeTopAtPolder,
PlLineOffsetBelowDikeCrestMiddle = location.PlLineOffsetBelowDikeCrestMiddle,
PlLineOffsetFactorBelowShoulderCrest = location.PlLineOffsetFactorBelowShoulderCrest,
UsePlLineOffsetBelowDikeCrestMiddle = location.UsePlLineOffsetBelowDikeCrestMiddle,
UsePlLineOffsetFactorBelowShoulderCrest = location.UsePlLineOffsetFactorBelowShoulderCrest,
SoilList = location.SoilList,
DikeEmbankmentMaterial = location.SoilList.GetSoilByName(location.DikeEmbankmentMaterial),
XSoilGeometry2DOrigin = location.XSoilGeometry2DOrigin
};
PLLines plLines = plLinesCreator.CreateAllPLLines(location);
return plLines;
}
///
/// Create and write the XML definiton file for the MStab project
/// Call the stability agent to create the stability project file
///
internal static void CreateMStabProjectFile(FailureMechanismParametersMStab failureMechanismParametersMStab, string stabilityExePath)
{
// var assembler = new DamMStabAssembler();
// XDocument xDocument = assembler.CreateDataTransferObject(failureMechanismParametersMStab);
// var fileName = failureMechanismParametersMStab.MStabParameters.ProjectFileName + ".xml";
// xDocument.Save(fileName);
//
// // call the stability agent to create a MStab specific (xml) file
// var agent = new StabilityServiceAgent { MStabExePath = stabilityExePath };
// agent.CreateProjectFile(xDocument.ToString()); ##Bka: to be rplaced by interface call to kernel.
}
///
/// Determine the minimal safety factor for a set of MStab projects
///
internal static double DetermineSafetyFactor(IEnumerable mstabProjectPaths, ref string basisFileName, string stabilityExePath)
{
var agent = new StabilityServiceAgent { MStabExePath = stabilityExePath };
double? minimumSafetyFactor = null;
foreach (string path in mstabProjectPaths)
{
MStabResults mStabResults = agent.ExtractStabilityResults(path);
if (!minimumSafetyFactor.HasValue || mStabResults.zone1.safetyFactor < minimumSafetyFactor)
{
minimumSafetyFactor = mStabResults.zone1.safetyFactor;
basisFileName = path;
}
}
return minimumSafetyFactor ?? 0;
}
///
/// Determines the type of the soil geometry (1D or 2D).
///
/// The location.
/// Type of the soil geometry.
/// Name of the soil geometry2 D.
internal static void DetermineSoilGeometryType(Location location, out SoilProfileType soilProfileType, out string soilGeometry2DName)
{
SoilProfile1D soilProfile = location.GetMostProbableProfile(FailureMechanismSystemType.StabilityInside);
soilGeometry2DName = location.GetMostProbableGeometry2DName(FailureMechanismSystemType.StabilityInside);
string mapForSoilGeometries2D = "";
if (location.StabilityOptions != null)
{
mapForSoilGeometries2D = location.StabilityOptions.MapForSoilGeometries2D;
}
if ((soilGeometry2DName != null) && (mapForSoilGeometries2D != null))
{
soilGeometry2DName = Path.Combine(mapForSoilGeometries2D, soilGeometry2DName);
}
if (soilProfile != null)
{
soilProfileType = SoilProfileType.ProfileType1D;
}
else
{
if (soilGeometry2DName == null)
{
throw new TimeSerieStabilityCalculatorException(String.Format("Location {0} does not have a soilprofile assigned", location.Name));
}
soilProfileType = SoilProfileType.ProfileTypeStiFile;
}
}
}
}