using System.Diagnostics; using System.Text.RegularExpressions; using Deltares.Geotechnics; using Deltares.Geotechnics.Soils; using Deltares.Standard.EventPublisher; using Deltares.Standard.IO; using Deltares.Standard.IO.Xml; using Deltares.Standard.Language; using Deltares.Standard.Logging; namespace Deltares.Dam.Data { using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using Deltares.Soilbase; using Deltares.Standard; /// /// 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; } } 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; } } 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(); 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.Location, String.Format("Error: {0}", e.Message))); } } /// /// Select which job processor to use, depending on failuremechanism /// /// private void ProcessJob(RWScenarioProfileResult job) { DataEventPublisher.InvokeWithoutPublishingEvents(() => { 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(DamProject.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)); } } private string GetCalculationNameForPipingCalculator(RWScenarioProfileResult job) { string calculationName; switch (job.PipingModelOption) { case PipingModelType.Sellmeijer : calculationName = String.Format("Calc(Sellmeijer)_Loc({0})_Pro({1}))", job.Location.Name, job.SoilGeometryProbability.SoilProfile.Name); break; case PipingModelType.Sellmeijer2Forces: calculationName = String.Format("Calc(Sellmeijer2Forces)_Loc({0})_Pro({1}))", job.Location.Name, job.SoilGeometryProbability.SoilProfile.Name); break; case PipingModelType.Sellmeijer4Forces: calculationName = String.Format("Calc(Sellmeijer4Forces)_Loc({0})_Pro({1}))", job.Location.Name, job.SoilGeometryProbability.SoilProfile.Name); break; // Set Sellmeijer4Forces as default. default: calculationName = String.Format("Calc(Sellmeijer4Forces)_Loc({0})_Pro({1}))", job.Location.Name, job.SoilGeometryProbability.SoilProfile.Name); 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.Sellmeijer: 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; logMessage.Detail = job.SoilGeometryProbability.SoilProfile.Name; } } 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.Location.Name.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; } } } } }