using System;
using System.Collections.Generic;
using System.Linq;
using Deltares.Geotechnics;
using Deltares.Geotechnics.Soils;
using Deltares.Standard;
using Deltares.Standard.Data;
using Deltares.Standard.EventPublisher;
using Deltares.Standard.Extensions;
using Deltares.Standard.Language;
using Deltares.Standard.Reflection;
using Deltares.Standard.Validation;
namespace Deltares.AssessmentMechanism
{
///
/// Controller for managing instances for a
/// with regards to failure mechanism calculations.
///
public abstract class AssessmentScenarioManager : IDisposable where T : DikeLocationInfo
{
protected readonly T ScenarioOwner;
protected readonly CreatingDelegatedList scenarios;
protected AssessmentScenario activeScenario;
///
/// Creates a scenario controller for managing calculation scenarios.
///
/// The owner of the scenarios.
protected AssessmentScenarioManager(T locationInfo)
{
scenarios = new CreatingDelegatedList
{
AddMethod = AddMethod,
DeleteRequestMethod = DeleteRequestMethod,
CreateMethod = CopyFromActiveScenario
};
ScenarioOwner = locationInfo;
}
public virtual void Dispose()
{
DataEventPublisher.OnAfterChange -= DataEventPublisherOnOnAfterChange;
scenarios.AddMethod = null;
scenarios.DeleteRequestMethod = null;
if (SupportsSoilProfile1D || SupportsSoilProfile2D)
{
foreach (var assessmentScenario in Scenarios)
{
foreach (var scenarioItem in assessmentScenario.ScenarioItems)
{
if (scenarioItem.Name == StaticReflection.GetMemberName(dli => dli.SoilSurfaceProfile) && scenarioItem.Value != null)
{
((SoilSurfaceProfile)scenarioItem.Value).Dispose();
}
else if (scenarioItem.Name == StaticReflection.GetMemberName(dli => dli.SoilSurfaceProfile2D) && scenarioItem.Value != null)
{
((SoilSurfaceProfile2D)scenarioItem.Value).Dispose();
}
}
}
}
}
///
/// Gets or sets the currently active scenario.
///
public virtual AssessmentScenario ActiveScenario
{
get
{
return activeScenario;
}
set
{
if (ReferenceEquals(activeScenario, value))
{
return;
}
try
{
Delayed.BeginDelay();
DataEventPublisher.BeforeChange(this, m => m.ActiveScenario);
if (activeScenario != null)
{
activeScenario.Synchronize();
// optimization: Only send events for the final property change:
if (value == null)
{
activeScenario.Unapply();
}
else
{
DataEventPublisher.InvokeWithoutPublishingEvents(() => activeScenario.Unapply());
}
}
activeScenario = value;
if (activeScenario != null)
{
activeScenario.Apply();
}
DataEventPublisher.AfterChange(this, m => m.ActiveScenario);
DataEventPublisher.ActionCompleted(); // Trigger validation
}
finally
{
Delayed.EndDelay();
}
}
}
///
/// Collection of all managed scenarios.
///
public virtual IList Scenarios
{
get
{
return scenarios;
}
}
///
/// Creates the scenarios for the passed in to
/// .
///
public void InitializeScenarios()
{
DataEventPublisher.InvokeWithoutPublishingEvents(() =>
{
ActiveScenario = null;
scenarios.AddMethod = null;
scenarios.DeleteRequestMethod = null;
// Clear any previous scenarios
scenarios.Clear();
// Create and add scenarios
scenarios.AddRange(CreateAssessmentScenarios());
// If necessary, add a dummy scenario
if (scenarios.Count == 0)
{
scenarios.Add(new AssessmentScenario
{
Probability = 1
});
}
scenarios.AddMethod = AddMethod;
scenarios.DeleteRequestMethod = DeleteRequestMethod;
});
ActiveScenario = scenarios.First();
}
///
/// Updates the dike embankment material in all scenarios.
///
/// The new dike embankement material.
public void UpdateDikeEmbankmentMaterial(Soil newDikeEmbankementMaterial)
{
if (!SupportsSoilProfile1D && !SupportsSoilProfile2D)
{
return;
}
foreach (var assessmentScenario in Scenarios.Where(s => !IsDefaultAssessmentScenario(s)))
{
foreach (var scenarioItem in assessmentScenario.ScenarioItems)
{
var soilSurfaceProfile1D = scenarioItem.Value as SoilSurfaceProfile;
var soilSurfaceProfile2D = scenarioItem.Value as SoilSurfaceProfile2D;
if (soilSurfaceProfile1D != null && scenarioItem.Name == StaticReflection.GetMemberName(dli => dli.SoilSurfaceProfile))
{
soilSurfaceProfile1D.DikeEmbankmentMaterial = newDikeEmbankementMaterial;
}
else if (soilSurfaceProfile2D != null && scenarioItem.Name == StaticReflection.GetMemberName(dli => dli.SoilSurfaceProfile2D))
{
soilSurfaceProfile2D.DikeEmbankmentMaterial = newDikeEmbankementMaterial;
}
}
}
}
///
/// Validates that all scenarios have valid probabilities and they combine to create
/// a full 100% definition.
///
/// A collection of all validation issues found.
///
[Validate]
public IValidationResult[] ValidateScenarioProbabilities()
{
var results = new List();
if (!Scenarios.Sum(s => s.Probability).AlmostEquals(1.0))
{
string messageText = LocalizationManager.GetTranslatedText(typeof(AssessmentScenarioManager<>), "SumOfScenarioProbabilitiesMustBeOne");
results.Add(new ValidationResult(ValidationResultType.Error, messageText, new ScenarioSubject
{
ActualSelectedObject = ScenarioOwner
}));
}
foreach (var assessmentScenario in Scenarios)
{
foreach (var result in Validator.Validate(assessmentScenario))
{
results.Add(new ScenarioValidationResult(result, ScenarioOwner, assessmentScenario, true, false));
}
}
return results.ToArray();
}
///
/// Validates all miscellaneous scenarios related topics.
///
/// A collection of all validation issues found.
///
[Validate]
public IValidationResult[] Validate()
{
var results = new List();
// Set subject so that we communicate scenario specific validation for the 'scenarioOwner'
var stabilityScenarioSubject = new ScenarioSubject
{
ActualSelectedObject = ScenarioOwner
};
if (Scenarios.Count > 15)
{
string messageText = LocalizationManager.GetTranslatedText(typeof(AssessmentScenarioManager<>), "TooManyScenariosCanHaveNegativePerformance");
results.Add(new ValidationResult(ValidationResultType.Warning, messageText, stabilityScenarioSubject));
}
var allPossibleSoilProfiles = GetAvailableStochasticSoilProfiles();
var soilProfilesNotInScenarios = allPossibleSoilProfiles.Select(ssp => ssp.Profile).Except(Scenarios.Select(s => s.SoilProfile)).ToArray();
if (soilProfilesNotInScenarios.Any())
{
string translatedText = LocalizationManager.GetTranslatedText(typeof(AssessmentScenarioManager<>), "MissingScenariosForFollowingProfiles_0_");
string missingProfileNames = String.Join(", ", soilProfilesNotInScenarios.Select(sp => sp.Name));
string messageText = String.Format(translatedText, missingProfileNames);
results.Add(new ValidationResult(ValidationResultType.Error, messageText, stabilityScenarioSubject));
}
return results.ToArray();
}
///
/// Gets a value indicating whether this scenario manager supports
/// instances from a . If true, a
/// will be created for for that soil profile.
/// If false, the soil profile will be ignored.
///
protected abstract bool SupportsSoilProfile1D { get; }
///
/// Gets a value indicating whether this scenario manager supports
/// instances from a . If true, a
/// will be created for for that soil profile.
/// If false, the soil profile will be ignored.
///
protected abstract bool SupportsSoilProfile2D { get; }
///
/// Creates all the assessment scenarios for .
///
/// A collection of assessment mechanism specific scenarios, or a 'default scenario'
/// in case data is lacking to derive scenarios (A 'default scenario' causes no changes
/// to ).
protected virtual IEnumerable CreateAssessmentScenarios()
{
if (ScenarioOwner.StochasticSoilModel != null)
{
for (int index = 0; index < ScenarioOwner.StochasticSoilModel.StochasticSoilProfiles.Count; index++)
{
StochasticSoilProfile stochasticSoilProfile = ScenarioOwner.StochasticSoilModel.StochasticSoilProfiles[index];
var scenario = new AssessmentScenario
{
Name = "Scenario " + index,
Probability = stochasticSoilProfile.Probability,
GetAvailableStochasticSoilProfiles = GetAvailableStochasticSoilProfiles
};
// Add items for input
scenario.AddScenarioItem(ScenarioOwner, ScenarioOwner.GetMemberName(sdli => sdli.CurrentStochasticSoilProfile), stochasticSoilProfile.Clone(), stochasticSoilProfile.Profile.Name, true);
if (SupportsSoilProfile1D)
{
var profileToUse = stochasticSoilProfile.Profile as SoilProfile1D;
SoilSurfaceProfile soilSurfaceProfile1D = CreateSoilSurfaceProfile1DForScenario(profileToUse);
string soilSurfaceProfilePropertyName1 = StaticReflection.GetMemberName(dli => dli.SoilSurfaceProfile);
scenario.AddScenarioItem(ScenarioOwner, soilSurfaceProfilePropertyName1, soilSurfaceProfile1D, soilSurfaceProfilePropertyName1, false);
}
if (SupportsSoilProfile2D)
{
var profileToUse = stochasticSoilProfile.Profile as SoilProfile2D;
SoilSurfaceProfile2D soilSurfaceProfile2D = CreateSoilSurfaceProfile2DForScenario(profileToUse);
string soilSurfaceProfile2DPropertyName1 = StaticReflection.GetMemberName(dli => dli.SoilSurfaceProfile2D);
scenario.AddScenarioItem(ScenarioOwner, soilSurfaceProfile2DPropertyName1, soilSurfaceProfile2D, soilSurfaceProfile2DPropertyName1, false);
}
AddAssessmentMechanismSpecificScenarioItems(scenario);
yield return scenario;
}
}
}
///
/// Adds the assessment mechanism specific scenario items for each scenario dependent
/// parameter.
///
/// The target scenario.
protected abstract void AddAssessmentMechanismSpecificScenarioItems(AssessmentScenario scenario);
///
/// Gets the available stochastic soil profiles for .
///
protected virtual StochasticSoilProfile[] GetAvailableStochasticSoilProfiles()
{
return ScenarioOwner.StochasticSoilModel.StochasticSoilProfiles.ToArray();
}
///
/// Determines whether a scenario is a 'default scenario' that will not change a
/// when applied or not..
///
/// The argument.
/// True if the scenario is a 'default scenario'; False otherwise.
protected virtual bool IsDefaultAssessmentScenario(AssessmentScenario scenarioToCheck)
{
return !scenarioToCheck.ScenarioItems.Any();
}
///
/// Gets a unique name for a scenario based on the ones in .
///
protected virtual string GetUniqueScenarioName()
{
const string nameFormat = "Scenario {0}";
var i = 0;
var name = string.Format(nameFormat, i++);
while (Scenarios.Any(s => s.Name == name))
{
name = string.Format(nameFormat, i++);
}
return name;
}
///
/// Checks if a is allowed to be removed from
/// or not.
///
/// The scenario to be removed.
/// True if deletion is allowed; False if not.
protected virtual bool DeleteRequestMethod(AssessmentScenario scenarioToDelete)
{
return scenarios.Count > 1;
}
///
/// Handles the addition of a new scenario, copying
/// from if the added scenarios doesn't have any
/// items defined yet.
///
/// The newly added scenario.
protected virtual void AddMethod(AssessmentScenario scenario)
{
scenario.GetAvailableStochasticSoilProfiles = GetAvailableStochasticSoilProfiles;
}
protected virtual AssessmentScenario CopyFromActiveScenario()
{
ActiveScenario.Synchronize();
var scenario = new AssessmentScenario
{
Name = GetUniqueScenarioName(),
GetAvailableStochasticSoilProfiles = GetAvailableStochasticSoilProfiles
};
foreach (var scenarioItem in ActiveScenario.ScenarioItems)
{
if (scenarioItem.Name == StaticReflection.GetMemberName(dli => dli.SoilSurfaceProfile))
{
var scenarioValue = CreateCopyOfSoilSurfaceProfile((SoilSurfaceProfile)scenarioItem.Value);
scenario.AddScenarioItem(scenarioItem.Target, scenarioItem.Property, scenarioValue, scenarioItem.Name, scenarioItem.Visible);
}
else if (scenarioItem.Name == StaticReflection.GetMemberName(dli => dli.SoilSurfaceProfile2D))
{
var scenarioValue = CreateCopyOfSoilSurfaceProfile2D((SoilSurfaceProfile2D)scenarioItem.Value);
scenario.AddScenarioItem(scenarioItem.Target, scenarioItem.Property, scenarioValue, scenarioItem.Name, scenarioItem.Visible);
}
else
{
ICloneable cloneableValue = scenarioItem.Value as ICloneable;
object scenarioValue = cloneableValue != null ? cloneableValue.Clone() : scenarioItem.Value;
scenario.AddScenarioItem(scenarioItem.Target, scenarioItem.Property, scenarioValue, scenarioItem.Name, scenarioItem.Visible);
}
}
return scenario;
}
private SoilSurfaceProfile CreateSoilSurfaceProfile1DForScenario(SoilProfile1D soilProfile1D)
{
SoilSurfaceProfile soilSurfaceProfile1D = null;
if (soilProfile1D != null)
{
soilSurfaceProfile1D = new SoilSurfaceProfile
{
DikeEmbankmentMaterial = ScenarioOwner.DefaultDikeEmbankmentMaterial,
// TODO RT-2486: Sync when another value is set on scenarioOwner
SurfaceLine = ScenarioOwner.SurfaceLine,
SoilProfile = soilProfile1D
};
}
return soilSurfaceProfile1D;
}
private SoilSurfaceProfile2D CreateSoilSurfaceProfile2DForScenario(SoilProfile2D soilProfile2D)
{
SoilSurfaceProfile2D soilSurfaceProfile2D = null;
if (soilProfile2D != null)
{
soilSurfaceProfile2D = new SoilSurfaceProfile2D
{
DikeEmbankmentMaterial = ScenarioOwner.DefaultDikeEmbankmentMaterial,
// TODO RT-2486: Sync when another value is set on scenarioOwner
SurfaceLine = ScenarioOwner.SurfaceLine,
SoilProfile2D = soilProfile2D
};
}
return soilSurfaceProfile2D;
}
private SoilSurfaceProfile CreateCopyOfSoilSurfaceProfile(SoilSurfaceProfile originalSoilSurfaceProfile)
{
SoilSurfaceProfile scenarioValue = null;
if (originalSoilSurfaceProfile != null)
{
scenarioValue = (SoilSurfaceProfile)originalSoilSurfaceProfile.Clone();
scenarioValue.DikeEmbankmentMaterial = ScenarioOwner.DefaultDikeEmbankmentMaterial;
scenarioValue.SurfaceLine = ScenarioOwner.SurfaceLine;
scenarioValue.SoilProfile = originalSoilSurfaceProfile.SoilProfile;
// Restore IsAquifer because that data was lost from the clone when assigning original SoilProfile.
for (int i = 0; i < originalSoilSurfaceProfile.Surfaces.Count; i++)
{
scenarioValue.Surfaces[i].IsAquifer = originalSoilSurfaceProfile.Surfaces[i].IsAquifer;
}
}
return scenarioValue;
}
private SoilSurfaceProfile2D CreateCopyOfSoilSurfaceProfile2D(SoilSurfaceProfile2D originalSoilSurfaceProfile)
{
SoilSurfaceProfile2D scenarioValue = null;
if (originalSoilSurfaceProfile != null)
{
scenarioValue = (SoilSurfaceProfile2D)originalSoilSurfaceProfile.Clone();
scenarioValue.DikeEmbankmentMaterial = ScenarioOwner.DefaultDikeEmbankmentMaterial;
scenarioValue.SurfaceLine = ScenarioOwner.SurfaceLine;
scenarioValue.SoilProfile2D = originalSoilSurfaceProfile.SoilProfile2D;
// Restore IsAquifer because that data was lost from the clone when assigning original SoilProfile2D.
for (int i = 0; i < originalSoilSurfaceProfile.Surfaces.Count; i++)
{
scenarioValue.Surfaces[i].IsAquifer = originalSoilSurfaceProfile.Surfaces[i].IsAquifer;
}
}
return scenarioValue;
}
private void DataEventPublisherOnOnAfterChange(object sender, PublishEventArgs e)
{
var assessmentScenario = sender as AssessmentScenario;
// If SoilProfile has changed by user, we should update CurrentStochasticSoilProfile accordingly:
if (assessmentScenario != null && e.Property == assessmentScenario.GetMemberName(s => s.SoilProfile) && scenarios.Contains(sender))
{
var stochsticSoilProfileItem = assessmentScenario.ScenarioItems.First(si => si.Value is StochasticSoilProfile);
var target = (DikeLocationInfo)stochsticSoilProfileItem.Target;
if (ReferenceEquals(target.CurrentScenario, assessmentScenario))
{
target.CurrentStochasticSoilProfile = (StochasticSoilProfile)stochsticSoilProfileItem.Value;
}
}
}
}
}