// Copyright (C) Stichting Deltares 2017. 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.Diagnostics;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using Deltares.DamEngine.Calculators.SchematizationFactor;
using Deltares.DamEngine.Data.General;
using Deltares.DamEngine.Data.General.Results;
using Deltares.DamEngine.Data.General.SchematizationFactor;
using Deltares.DamEngine.Data.Geotechnics;
using Deltares.DamEngine.Data.RWScenarios;
using Deltares.DamEngine.Data.Standard.Calculation;
using Deltares.DamEngine.Data.Standard.Language;
using Deltares.DamEngine.Data.Standard.Logging;
using Deltares.Stability.Calculation;
namespace Deltares.DamEngine.Calculators.Dikes_Assessment_Regional
{
///
/// Exception for RWScenariosCalculation class
///
public class RWScenariosCalculationException : ApplicationException
{
public RWScenariosCalculationException() { }
public RWScenariosCalculationException(string message) : base(message) { }
}
public class RWScenariosCalculation : ICalculation
{
private EvaluationJob evaluationJob = null;
private GetValuesDelegate getValuesDelegate = null;
private ProgressDelegate progressDelegate = null;
private SendMessageDelegate sendMessageDelegate = null;
private string mstabExePath = @".\DGeoStability.exe";
private int maxCalculationCores = 255;
private Dictionary runningJobs = new Dictionary();
private bool isSkipStabilityCalculation = false;
private SchematizationFactorData schematizationFactorData;
public PipingModelType PipingModelType { get; set; }
public MStabParameters MStabParameters { get; set; }
public RWScenariosCalculation()
{
}
#region ICalculation Members
public CalculationResult GetResults(ref string results)
{
// try
// {
// XmlSerializer serializer = new XmlSerializer();
// results = serializer.SerializeToString(this.evaluationJob);
// return CalculationResult.Succeeded;
// }
// catch
// {
// return CalculationResult.UnexpectedError;
// }##Bka
return CalculationResult.UnexpectedError;
}
public CalculationResult Load(string input)
{
// try
// {
// XmlDeserializer deserializer = new XmlDeserializer();
// this.evaluationJob = (EvaluationJob)deserializer.XmlDeserializeFromString(input, typeof(EvaluationJob), new DefaultClassFactory());
// return CalculationResult.Succeeded;
// }
// catch
// {
// return CalculationResult.UnexpectedError;
// }##Bka
return CalculationResult.UnexpectedError;
}
public CalculationResult RegisterGetValues(GetValuesDelegate getValuesDelegate)
{
this.getValuesDelegate = getValuesDelegate;
return CalculationResult.Succeeded;
}
public CalculationResult RegisterProgress(ProgressDelegate progressDelegate)
{
this.progressDelegate = progressDelegate;
return CalculationResult.Succeeded;
}
public CalculationResult RegisterSendDebugInfo(SendDebugInfodelegate sendDebugInfoDelegate)
{
return CalculationResult.Succeeded;
}
public CalculationResult RegisterSendMessage(SendMessageDelegate sendMessageDelegate)
{
this.sendMessageDelegate = sendMessageDelegate;
return CalculationResult.Succeeded;
}
public CalculationResult RegisterSetValues(SetValuesDelegate setValuesDelegate)
{
return CalculationResult.Succeeded;
}
public CalculationResult RegisterUserAbort(UserAbortDelegate userAbortDelegate)
{
return CalculationResult.Succeeded;
}
public CalculationResult Run()
{
try
{
List tasks = this.FillQueue();
General.Parallel.Run(tasks, this.RunTask, this.progressDelegate, this.MaxCalculationCores);
this.FillResults(tasks);
return CalculationResult.Succeeded;
}
catch(Exception exception)
{
sendMessageDelegate(new LogMessage(LogMessageType.Warning, null, "Unexpected error:" + exception.Message));
throw exception;
}
}
public CalculationResult Validate()
{
return CalculationResult.Succeeded;
}
#endregion
public string Version
{
get { return System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(); }
}
public string MStabExePath
{
get { return mstabExePath; }
set { mstabExePath = value; }
}
public bool IsSkipStabilityCalculation
{
get { return isSkipStabilityCalculation; }
set { isSkipStabilityCalculation = value; }
}
public int MaxCalculationCores
{
get { return maxCalculationCores; }
set { maxCalculationCores = value; }
}
public SchematizationFactorData SchematizationFactorData
{
get { return schematizationFactorData; }
set { schematizationFactorData = value; }
}
private List FillQueue()
{
List tasks = new List();
this.evaluationJob.FailedEvaluatedLocations = new List();
foreach (Location location in this.evaluationJob.Locations)
{
if (location.Segment == null)
{
// Add this location to the failed locations
if (this.evaluationJob.FailedEvaluatedLocations.IndexOf(location) < 0)
{
this.evaluationJob.FailedEvaluatedLocations.Add(location);
var locationHasNoSegment = LocalizationManager.GetTranslatedText(this.GetType(), "LocationHasNoSegment");
sendMessageDelegate(new LogMessage(LogMessageType.Error, location, locationHasNoSegment));
}
}
else
{
// TODO: Ask Erik Vastenburg how to handle piping and stability soilprofiles when determining RWScenarios
// For now we only use the stability profiles.
var soilGeometryProbabilities =
location.Segment.SoilProfileProbabilities.Where(s => (s.SegmentFailureMechanismType == null) ||
(s.SegmentFailureMechanismType.Value ==
FailureMechanismSystemType.StabilityInside)).ToList();
if (soilGeometryProbabilities.Count == 0)
{
this.evaluationJob.FailedEvaluatedLocations.Add(location);
sendMessageDelegate(
new LogMessage(LogMessageType.Warning, location, String.Format("Location has no soilprofiles: ") +
String.Format("Segment: {0}", location.Segment.Name)));
}
else
{
foreach (SoilGeometryProbability soilGeometryProbability in soilGeometryProbabilities)
{
if (soilGeometryProbability.SoilGeometryType == SoilGeometryType.SoilGeometry2D)
{
this.evaluationJob.FailedEvaluatedLocations.Add(location);
sendMessageDelegate(new LogMessage(LogMessageType.Warning, location, LocalizationManager.GetTranslatedText(this, "Geometry2DNotSupportedInRegionalAssessment") +
String.Format("Segment: {0}", location.Segment.Name)));
}
else
{
SoilProfile soilProfile = soilGeometryProbability.SoilProfile;
IList rwScenarios = null;
try
{
rwScenarios = this.GetRWScenarios(location, soilGeometryProbability);
}
catch (Exception e)
{
rwScenarios = null;
// Add this location to the failed locations
if (this.evaluationJob.FailedEvaluatedLocations.IndexOf(location) < 0)
{
this.evaluationJob.FailedEvaluatedLocations.Add(location);
sendMessageDelegate(
new LogMessage(LogMessageType.Warning, location, String.Format("Cannot generate scenarios: {0}", e.Message) +
String.Format("Soilprofile: {0}", soilProfile.Name)));
}
}
if (rwScenarios != null)
{
foreach (RWScenarioProfileResult job in rwScenarios)
{
tasks.Add(job);
}
}
}
}
}
}
}
return tasks;
}
private IList GetRWScenarios(Location location, SoilGeometryProbability soilGeometryProbability)
{
RWScenarioSelector selector = new RWScenarioSelector();
selector.PipingModelType = PipingModelType;
selector.MStabParameters = MStabParameters;
return selector.GetScenarios(location, soilGeometryProbability);
}
private void RunTask(object task)
{
RWScenarioProfileResult job = (RWScenarioProfileResult)task;
try
{
if (!IsSkipStabilityCalculation)
{
ProcessJob(job);
}
else
{
job.CalculationResult = CalculationResult.NoRun;
}
}
catch (Exception e)
{
job.CalculationResult = CalculationResult.UnexpectedError;
sendMessageDelegate(new LogMessage(LogMessageType.Warning, job, String.Format(job.LocationName + " Error: {0}", e.Message)));
}
}
///
/// Select which job processor to use, depending on failuremechanism
///
///
private void ProcessJob(RWScenarioProfileResult job)
{
Debug.WriteLine(String.Format("Job {0}, location {1}, Scenario {2}", job.FailureMechanismType.ToString(), job.LocationName, job.ScenarioType.ToString()));
switch (job.FailureMechanismType)
{
case FailureMechanismSystemType.StabilityInside:
ProcessJobStability(job);
break;
case FailureMechanismSystemType.Piping:
ProcessJobPiping(job);
break;
default:
throw new RWScenariosCalculationException(String.Format("Failuremechanism {0} not yet implemented for scenario calculation", job.FailureMechanismType));
}
}
///
/// Process a job for failuremechanism Piping
///
///
private void ProcessJobPiping(RWScenarioProfileResult job)
{
// if (job.Location.ModelFactors.UpliftCriterionPiping.HasValue)
// {
// var modelParametersForPLLines = new ModelParametersForPLLines();
// var calculator = GetCalculatorForPipingModel(job, modelParametersForPLLines);
// double waterLevel;
// switch (job.LoadSituation)
// {
// case LoadSituation.Dry:
// waterLevel = job.Location.BoezemLevelLbp;
// break;
// default: // LoadSituation.Wet
// waterLevel = job.Location.BoezemLevelTp;
// break;
// }
// //job.SoilGeometryProbability.SoilProfile.EnsureUniqueLayerIds();
// var calculationName = GetCalculationNameForPipingCalculator(job);
// calculator.FilenameCalculation = Path.Combine(Path.Combine(DamProjectData.ProjectWorkingPath, job.FailureMechanismType.ToString()), calculationName);
// calculator.IsHydraulicShortcut = (job.HydraulicShortcutType == HydraulicShortcutType.HydraulicShortcut);
// double? pipingFactor = calculator.CalculatePipingFactor(job.Location, job.Location.LocalXZSurfaceLine2, job.SoilGeometryProbability.SoilProfile, waterLevel);
// job.BaseFileName = calculator.FilenameCalculation;
//
// job.RwResultType = RWResultType.SafetyFactor;
// if (pipingFactor.HasValue)
// {
// job.SafetyFactor = pipingFactor.Value;
// job.CalculationResult = CalculationResult.Succeeded;
// job.ProbabilityOfFailure = double.NaN;
// job.RwResultType = RWResultType.SafetyFactor;
// }
//
// else
// {
// job.SafetyFactor = double.NaN;
// job.CalculationResult = CalculationResult.RunFailed;
// }
// }
// else
// {
// throw new RWScenariosCalculationException(String.Format("Uplift criterion not defined for location {0}", job.Location.Name));
// } //##Bka
}
private string GetCalculationNameForPipingCalculator(RWScenarioProfileResult job)
{
string calculationName;
switch (job.PipingModelOption)
{
case PipingModelType.SellmeijerVnk : calculationName = String.Format("Calc(SellmeijerVnk)_Loc({0})_Pro({1}))",
job.LocationName, job.SoilProfileName);
break;
case PipingModelType.Sellmeijer4Forces: calculationName = String.Format("Calc(Sellmeijer4Forces)_Loc({0})_Pro({1}))",
job.LocationName, job.SoilProfileName);
break;
// Set Sellmeijer4Forces as default.
default: calculationName = String.Format("Calc(Sellmeijer4Forces)_Loc({0})_Pro({1}))",
job.LocationName, job.SoilProfileName);
break;
}
calculationName = Regex.Replace(calculationName, @"[\\\/:\*\?""'<>|.]", "_");
return calculationName;
}
///
/// Determines the proper calculator for pipng
///
///
///
/// proper piping calculator
// private PipingCalculator GetCalculatorForPipingModel(RWScenarioProfileResult job, ModelParametersForPLLines modelParametersForPLLines)
// {
// PipingCalculator calculator;
// switch (job.PipingModelOption)
// {
// case PipingModelType.SellmeijerVnk: calculator = new PipingCalculatorSellmeijer(modelParametersForPLLines,
// 1.0, null, null, null, job.Location.ModelFactors.UpliftCriterionPiping.Value);
// break;
// case PipingModelType.Sellmeijer2Forces: calculator = new PipingCalculatorSellmeijer2Forces(modelParametersForPLLines,
// 1.0, null, null, job.Location.ModelFactors.UpliftCriterionPiping.Value);
// break;
// case PipingModelType.Sellmeijer4Forces: calculator = new PipingCalculatorSellmeijer4Forces(modelParametersForPLLines,
// 1.0, null, null, job.Location.ModelFactors.UpliftCriterionPiping.Value);
// break;
// case PipingModelType.Bligh: calculator = new PipingCalculatorBligh(modelParametersForPLLines,
// 1.0, null, null, job.Location.ModelFactors.UpliftCriterionPiping.Value);
// break;
// default:
// throw new RWScenariosCalculationException(String.Format("Piping model {0} not yet implemented for scenario calculation", job.PipingModelOption));
// }
// return calculator;
// }
///
/// Process a job for failuremechanism Stability
///
///
private void ProcessJobStability(RWScenarioProfileResult job)
{
StabilityCalculation calculator = new StabilityCalculation();
lock (runningJobs)
{
runningJobs[calculator] = job;
}
// calculator.MStabExePath = this.MStabExePath;
calculator.RegisterSendMessage(this.SendStabilityMessage);
string soilDatabaseName = job.Location.SoildatabaseName;
// DamFailureMechanismeCalculationSpecification damCalculation =
// calculator.GetSpecification(this.evaluationJob.DikeName, soilDatabaseName, job.Location, new SoilGeometry(job.SoilGeometryProbability.SoilProfile, null),
// (MStabModelType)job.MstabModelOption, job.LoadSituation, job.DikeDrySensitivity, job.HydraulicShortcutType, MStabParameters);
//
// calculator.SaveToFile(damCalculation.FailureMechanismeParamatersMStab);
// string inputFile = damCalculation.FailureMechanismeParamatersMStab.MStabParameters.ProjectFileName;
//
// job.BaseFileName = inputFile.Replace(DamProject.ProjectWorkingPath, @"").Replace(".sti", "");
// calculator.Load(inputFile);
job.CalculationResult = calculator.Run();
if (job.CalculationResult == CalculationResult.Succeeded)
{
string results = "";
job.CalculationResult = calculator.GetResults(ref results);
//XmlDeserializer deserializer = new XmlDeserializer();
// RWResult result = (RWResult)deserializer.XmlDeserializeFromString(results, typeof(RWResult));
// job.SafetyFactor = result.SafetyFactor;
// job.ProbabilityOfFailure = result.ProbabilityOfFailure;
// job.RwResultType = result.RwResultType;
// job.CalculationResult = result.CalculationResult;
}
else
{
// job.RwResultType = (damCalculation.FailureMechanismeParamatersMStab.MStabParameters.IsProbabilistic ? RWResultType.ProbabilityOfFailure : RWResultType.SafetyFactor);
job.SafetyFactor = double.NaN;
job.ProbabilityOfFailure = double.NaN;
}
lock (runningJobs)
{
runningJobs.Remove(calculator);
}
}
///
/// Log messages
///
///
private void SendStabilityMessage(LogMessage logMessage)
{
lock (runningJobs)
{
if (logMessage.Subject != null)
{
RWScenarioProfileResult job = (RWScenarioProfileResult)runningJobs[(ICalculation)logMessage.Subject];
logMessage.Subject = job.Location;
}
}
this.sendMessageDelegate(logMessage);
}
///
/// Fill the results for the scenarios
///
private void FillResults(List tasks)
{
// Fill scenariosResult structure with jobs just run
foreach (Location location in this.evaluationJob.Locations)
{
try
{
RWScenariosResult scenariosResult = new RWScenariosResult();
if (this.evaluationJob.FailedEvaluatedLocations.IndexOf(location) < 0)
{
// scenarios were succesfully created, so results are available
foreach (RWScenarioProfileResult job in tasks)
{
if (job.LocationName.Equals(location.Name))
{
RWScenarioResult scenarioResult = null;
foreach (RWScenarioResult existingScenarioResult in scenariosResult.RWScenarioResults)
{
if (existingScenarioResult.ScenarioType == job.ScenarioType)
{
scenarioResult = existingScenarioResult;
}
}
if (scenarioResult == null)
{
scenarioResult = new RWScenarioResult();
scenarioResult.ScenarioType = job.ScenarioType;
scenariosResult.RWScenarioResults.Add(scenarioResult);
}
scenarioResult.RWScenarioProfileResults.Add(job);
}
}
// Combine results
foreach (RWScenarioResult scenarioResult in scenariosResult.RWScenarioResults)
{
this.CombineProfiles(scenarioResult);
}
this.CombineScenarios(scenariosResult);
}
else
{
// scenarios were not succesfully created, so results are not available
// no succesful calculations found
scenariosResult.CalculationResult = CalculationResult.RunFailed;
scenariosResult.SafetyFactor = double.NaN;
}
if (schematizationFactorData != null)
{
var results = AddSchematizationFactors(location, scenariosResult);
evaluationJob.SchematizationFactorResults.Add(results);
}
// scenariosResult are the results of all scenarios for one location.
this.evaluationJob.Results.Add(scenariosResult);
}
catch (Exception e)
{
RWScenariosResult scenariosResult = new RWScenariosResult
{
CalculationResult = CalculationResult.RunFailed,
SafetyFactor = double.NaN
};
sendMessageDelegate(new LogMessage(LogMessageType.Warning, location, String.Format("Error in location {0}: {1}", location.Name, e.Message)));
}
}
}
private RWSchematizationFactorsResult AddSchematizationFactors(Location location, RWScenariosResult scenariosResult)
{
var schematizationFactorCalculation = new SchematizationFactorCalculation();
schematizationFactorCalculation.ScenariosResult = scenariosResult;
schematizationFactorCalculation.Location = location;
schematizationFactorCalculation.SchematizationFactorData = SchematizationFactorData;
schematizationFactorCalculation.DetrimentFactor = location.DetrimentFactor;
var results = schematizationFactorCalculation.CalculateSchematizationFactorResults();
return results;
}
private void CombineProfiles(RWScenarioResult scenarioResult)
{
// combine results of profiles
scenarioResult.SafetyFactor = Double.MaxValue;
foreach (RWScenarioProfileResult profileResult in scenarioResult.RWScenarioProfileResults)
{
if (profileResult.CalculationResult == CalculationResult.Succeeded)
{
if (profileResult.SafetyFactor < scenarioResult.SafetyFactor)
{
scenarioResult.SafetyFactor = profileResult.SafetyFactor;
}
scenarioResult.CalculationResult = CalculationResult.Succeeded;
}
}
if (scenarioResult.CalculationResult != CalculationResult.Succeeded)
{
// no succesful calculations found
scenarioResult.CalculationResult = scenarioResult.RWScenarioProfileResults[0].CalculationResult;
scenarioResult.SafetyFactor = scenarioResult.RWScenarioProfileResults[0].SafetyFactor;
}
}
private void CombineScenarios(RWScenariosResult scenariosResult)
{
// combine results of scenarios
scenariosResult.SafetyFactor = Double.MaxValue;
foreach (RWScenarioResult scenarioResult in scenariosResult.RWScenarioResults)
{
if (scenarioResult.CalculationResult == CalculationResult.Succeeded)
{
if (scenarioResult.SafetyFactor < scenariosResult.SafetyFactor)
{
scenariosResult.SafetyFactor = scenarioResult.SafetyFactor;
}
scenariosResult.CalculationResult = CalculationResult.Succeeded;
}
}
}
}
}