// Copyright (C) Stichting Deltares 2016. All rights reserved. // // This file is part of Ringtoets. // // Ringtoets 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 System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; using Core.Common.Base; using Core.Common.Base.IO; using Core.Common.IO.Exceptions; using Core.Common.IO.Readers; using log4net; using Ringtoets.Common.IO.FileImporters.MessageProviders; using Ringtoets.Piping.Data; using Ringtoets.Piping.IO.Exceptions; using Ringtoets.Piping.IO.Properties; using Ringtoets.Piping.IO.SoilProfile; using Ringtoets.Piping.Primitives; using RingtoestCommonIOResources = Ringtoets.Common.IO.Properties.Resources; namespace Ringtoets.Piping.IO.Importers { /// /// Imports .soil files (SqlLite database files) created with the D-Soil Model application. /// public class StochasticSoilModelImporter : FileImporterBase { private readonly ILog log = LogManager.GetLogger(typeof(StochasticSoilModelImporter)); private readonly IImporterMessageProvider messageProvider; private readonly IStochasticSoilModelUpdateModelStrategy modelUpdateStrategy; /// /// Initializes a new instance of the class. /// /// The collection to update. /// The path to the file to import from. /// The message provider to provide messages during importer actions. /// The to use /// when updating the . /// Thrown when any of the input parameters is null. public StochasticSoilModelImporter(StochasticSoilModelCollection importTarget, string filePath, IImporterMessageProvider messageProvider, IStochasticSoilModelUpdateModelStrategy modelUpdateStrategy) : base(filePath, importTarget) { if (modelUpdateStrategy == null) { throw new ArgumentNullException(nameof(modelUpdateStrategy)); } if (messageProvider == null) { throw new ArgumentNullException(nameof(messageProvider)); } this.messageProvider = messageProvider; this.modelUpdateStrategy = modelUpdateStrategy; } protected override void DoPostImportUpdates() { foreach (IObservable observable in UpdatedInstances) { observable.NotifyObservers(); } } protected override bool OnImport() { ReadResult importSoilProfileResult = ReadSoilProfiles(); if (importSoilProfileResult.CriticalErrorOccurred || Canceled) { return false; } ReadResult importStochasticSoilModelResult = ReadStochasticSoilModels(); if (importStochasticSoilModelResult.CriticalErrorOccurred || Canceled) { return false; } AddSoilProfilesToStochasticSoilModels(importSoilProfileResult.Items, importStochasticSoilModelResult.Items); MergeStochasticSoilProfiles(importStochasticSoilModelResult.Items); CheckIfAllProfilesAreUsed(importSoilProfileResult.Items, importStochasticSoilModelResult.Items); if (Canceled) { return false; } UpdatedInstances = modelUpdateStrategy.UpdateModelWithImportedData(ImportTarget, GetValidStochasticSoilModels(importStochasticSoilModelResult), FilePath); return true; } protected override void LogImportCanceledMessage() { log.Info(Resources.StochasticSoilModelImporter_Import_Import_canceled); } private IEnumerable UpdatedInstances { get; set; } = Enumerable.Empty(); /// /// Validate the definition of a . /// /// The to validate. /// false when the stochastic soil model does not contain any stochastic soil profiles /// or when a stochastic soil profile does not have a definition for a soil profile; true /// otherwise. private bool ValidateStochasticSoilModel(StochasticSoilModel stochasticSoilModel) { if (!stochasticSoilModel.StochasticSoilProfiles.Any()) { log.WarnFormat(Resources.StochasticSoilModelImporter_ValidateStochasticSoilModel_No_profiles_found_in_stochastic_soil_model_0, stochasticSoilModel.Name); return false; } if (stochasticSoilModel.StochasticSoilProfiles.Any(ssp => ssp.SoilProfile == null)) { log.WarnFormat(Resources.StochasticSoilModelImporter_ValidateStochasticSoilModel_SoilModel_0_with_stochastic_soil_profile_without_profile, stochasticSoilModel.Name); return false; } if (!IsSumOfAllProbabilitiesEqualToOne(stochasticSoilModel)) { log.WarnFormat(Resources.StochasticSoilModelImporter_ValidateStochasticSoilModel_Sum_of_probabilities_of_stochastic_soil_model_0_is_not_correct, stochasticSoilModel.Name); } return true; } private IEnumerable GetValidStochasticSoilModels(ReadResult importStochasticSoilModelResult) { var currentStep = 1; StochasticSoilModel[] importedModels = importStochasticSoilModelResult.Items.ToArray(); string addDataToModelProgressText = messageProvider.GetAddDataToModelProgressText(); foreach (StochasticSoilModel importedModel in importedModels) { NotifyProgress(addDataToModelProgressText, currentStep, importedModels.Length); if (ValidateStochasticSoilModel(importedModel)) { yield return importedModel; } currentStep++; } } private static bool IsSumOfAllProbabilitiesEqualToOne(StochasticSoilModel stochasticSoilModel) { double sumOfAllScenarioProbabilities = stochasticSoilModel.StochasticSoilProfiles .Where(s => s.SoilProfile != null) .Sum(s => s.Probability); return Math.Abs(sumOfAllScenarioProbabilities - 1.0) < 1e-6; } private void AddSoilProfilesToStochasticSoilModels(ICollection soilProfiles, ICollection stochasticSoilModels) { foreach (StochasticSoilModel stochasticSoilModel in stochasticSoilModels) { foreach (StochasticSoilProfile stochasticSoilProfile in stochasticSoilModel.StochasticSoilProfiles) { PipingSoilProfile soilProfile = soilProfiles.FirstOrDefault(s => s.SoilProfileType == stochasticSoilProfile.SoilProfileType && s.PipingSoilProfileId == stochasticSoilProfile.SoilProfileId); if (soilProfile != null) { stochasticSoilProfile.SoilProfile = soilProfile; } } } } private void MergeStochasticSoilProfiles(ICollection stochasticSoilModels) { foreach (StochasticSoilModel stochasticSoilModel in stochasticSoilModels) { StochasticSoilProfile[] profiles = stochasticSoilModel.StochasticSoilProfiles.OrderBy(sp => sp.SoilProfileId).ToArray(); for (var i = 1; i < profiles.Length; i++) { StochasticSoilProfile previousProfile = profiles[i - 1]; StochasticSoilProfile currentProfile = profiles[i]; if (currentProfile.SoilProfileId == previousProfile.SoilProfileId && currentProfile.SoilProfileType == previousProfile.SoilProfileType) { log.Warn(string.Format(Resources.StochasticSoilModelImporter_MergeStochasticSoilProfiles_Multiple_SoilProfile_0_used_in_StochasticSoilModel_1_Probabilities_added, previousProfile.SoilProfile.Name, stochasticSoilModel.Name)); previousProfile.AddProbability(currentProfile.Probability); stochasticSoilModel.StochasticSoilProfiles.Remove(currentProfile); } } } } private void CheckIfAllProfilesAreUsed(ICollection soilProfiles, ICollection stochasticSoilModels) { NotifyProgress(Resources.StochasticSoilModelImporter_CheckIfAllProfilesAreUsed_Start_checking_soil_profiles, 1, 1); foreach (PipingSoilProfile soilProfile in soilProfiles.Where(soilProfile => !PipingSoilProfileIsUsed(soilProfile, stochasticSoilModels))) { log.WarnFormat(Resources.StochasticSoilModelImporter_CheckIfAllProfilesAreUsed_SoilProfile_0_is_not_used_in_any_stochastic_soil_model, soilProfile.Name); } } private static bool PipingSoilProfileIsUsed(PipingSoilProfile soilProfile, ICollection stochasticSoilModels) { return stochasticSoilModels.Any( stochasticSoilModel => stochasticSoilModel .StochasticSoilProfiles .Any(stochasticSoilProfile => ReferenceEquals(stochasticSoilProfile.SoilProfile, soilProfile))); } private void HandleException(Exception e) { string message = string.Format(Resources.StochasticSoilModelImporter_CriticalErrorMessage_0_File_Skipped, e.Message); log.Error(message); } #region read stochastic soil models private ReadResult ReadStochasticSoilModels() { NotifyProgress(Resources.StochasticSoilModelImporter_Reading_database, 1, 1); try { using (var stochasticSoilModelReader = new StochasticSoilModelReader(FilePath)) { return GetStochasticSoilModelReadResult(stochasticSoilModelReader); } } catch (CriticalFileReadException e) { HandleException(e); } return new ReadResult(true); } private ReadResult GetStochasticSoilModelReadResult(StochasticSoilModelReader stochasticSoilModelReader) { int totalNumberOfSteps = stochasticSoilModelReader.PipingStochasticSoilModelCount; var currentStep = 1; var soilModels = new Collection(); while (stochasticSoilModelReader.HasNext) { if (Canceled) { return new ReadResult(false); } try { NotifyProgress(Resources.StochasticSoilModelImporter_GetStochasticSoilModelReadResult_Reading_stochastic_soil_models_from_database, currentStep++, totalNumberOfSteps); soilModels.Add(stochasticSoilModelReader.ReadStochasticSoilModel()); } catch (StochasticSoilProfileReadException e) { string message = string.Format(Resources.StochasticSoilModelImporter_GetStochasticSoilModelReadResult_Error_0_stochastic_soil_model_skipped, e.Message); log.Error(message); } } return new ReadResult(false) { Items = soilModels }; } #endregion #region read soil profiles private ReadResult ReadSoilProfiles() { NotifyProgress(Resources.StochasticSoilModelImporter_Reading_database, 1, 1); try { using (var soilProfileReader = new PipingSoilProfileReader(FilePath)) { return GetProfileReadResult(soilProfileReader); } } catch (CriticalFileReadException e) { HandleException(e); } return new ReadResult(true); } private ReadResult GetProfileReadResult(PipingSoilProfileReader soilProfileReader) { int totalNumberOfSteps = soilProfileReader.Count; var currentStep = 1; var profiles = new Collection(); while (soilProfileReader.HasNext) { if (Canceled) { return new ReadResult(false); } try { NotifyProgress(Resources.StochasticSoilModelImporter_ReadingSoilProfiles, currentStep++, totalNumberOfSteps); profiles.Add(soilProfileReader.ReadProfile()); } catch (PipingSoilProfileReadException e) { string message = string.Format(Resources.StochasticSoilModelImporter_ReadSoilProfiles_ParseErrorMessage_0_SoilProfile_skipped, e.Message); log.Error(message); } catch (CriticalFileReadException e) { string message = string.Format(Resources.StochasticSoilModelImporter_CriticalErrorMessage_0_File_Skipped, FilePath, e.Message); log.Error(message); return new ReadResult(true); } } return new ReadResult(false) { Items = profiles }; } #endregion } }