// Copyright (C) Stichting Deltares 2023. 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.IO; using System.Linq; using System.Text; using Deltares.DamEngine.Data.Design; using Deltares.DamEngine.Data.General; using Deltares.DamEngine.Data.Standard.Logging; using Deltares.DamEngine.Interface; using Deltares.DamEngine.Io; using Deltares.DamEngine.Io.XmlOutput; using DGeoSuite.Common; using NUnit.Framework; namespace Deltares.DamEngine.TestHelpers; public static class GeneralHelper { public static int DetermineNumberOfCalculationErrors(List calculationMessages) { int errorCount = 0; foreach (var logMessage in calculationMessages) { if ((logMessage.MessageType == LogMessageType.Error) || (logMessage.MessageType == LogMessageType.FatalError)) { errorCount++; } } return errorCount; } public static Output RunAfterInputValidation(string inputString, bool areResultsExpected = true, string outputXml = "") { var engineInterface = new EngineInterface(inputString); return RunAfterInputValidation(engineInterface, areResultsExpected, outputXml); } public static Output RunAfterInputValidation(EngineInterface engineInterface, bool areResultsExpected = true, string outputXml = "") { // Validate input Assert.IsNotNull(engineInterface.DamProjectData); string validationMessages = engineInterface.Validate(); string extraValidationMessage = ""; if (outputXml != "") { extraValidationMessage = ", see output xml in debugger"; } Assert.IsNull(validationMessages, "Validation should succeed but does not" + extraValidationMessage); // Run calculation string outputString = engineInterface.Run(); Assert.IsNotNull(outputString); Output output = DamXmlSerialization.LoadOutputFromXmlString(outputString); if (outputXml != "") { File.WriteAllText("Output.xml", outputString, Encoding.Unicode); } CheckDependencyOnStiFiles(output); // Evaluate results if (!areResultsExpected) { Assert.IsNull(output.Results.CalculationResults, "No results are expected"); return output; } bool isNoResultAvailable; switch (engineInterface.DamProjectData.DamProjectType) { case DamProjectType.Design: isNoResultAvailable = output.Results.CalculationResults.IsNullOrEmpty(); break; default: isNoResultAvailable = output.Results.OperationalOutputTimeSeries.IsNullOrEmpty(); break; } if (!isNoResultAvailable) { CheckConsistencyOfAdaptGeometryResults(engineInterface, output.Results); return output; } string assertMessage = "No results available."; foreach (var calcMessage in output.Results.CalculationMessages) { assertMessage = assertMessage + Environment.NewLine + calcMessage.Message1; } Assert.IsNotNull(output.Results.CalculationResults, assertMessage); return output; } private static void CheckDependencyOnStiFiles(Output output) { foreach (var calcMessage in output.Results.CalculationMessages) { if ((calcMessage.MessageType == MessageMessageType.Error) & calcMessage.Message1.Contains("The preparation for this " + "calculation failed")) { var assertMessage = "No results available due to dependency on 2D geometries (sti files) and the old " + "MacroStability kernel wrapper implementation"; Assert.IsNotNull(output.Results.CalculationResults, assertMessage); } } } private static void CheckConsistencyOfAdaptGeometryResults(EngineInterface engineInterface, OutputResults outputResults) { if (engineInterface.DamProjectData.DamProjectCalculationSpecification.AnalysisTypeForSerializationPurposeOnly == AnalysisType.AdaptGeometry) { bool isGeometryAdapted = false; foreach (DesignResult calculationResult in outputResults.CalculationResults) { bool isDesignSuccessful = IsAdaptGeometrySuccessful(outputResults.CalculationMessages, calculationResult); double fosRequired = FetchRequiredFactor(engineInterface, calculationResult.ScenarioName, calculationResult.LocationName); double fosCalculated = FetchCalculatedFactor(engineInterface, calculationResult); string fileName = calculationResult.BaseFileName; bool isGeometryAdaptedInThisLocation = fileName.Contains("Ite(") && !fileName.Contains("Ite(1)"); isGeometryAdapted = isGeometryAdapted || isGeometryAdaptedInThisLocation; if (isGeometryAdaptedInThisLocation) { if (isDesignSuccessful) { const string message = "After adapting the geometry, the calculated safety factor is less than the" + " required safety factor; this is unexpected."; Assert.IsTrue(fosCalculated >= fosRequired, message); } else { const string message = "As the design was not successful and had to stop, the calculated safety " + "factor should be less than the required safety factor but this is not the case."; Assert.IsTrue(fosRequired >= fosCalculated, message); } } } Assert.IsTrue(isGeometryAdapted, "The AnalyseType is set to AdaptGeometry in the input, however the geometry was " + "not adapted in any location. Either set the AnalysisType to NoAdaptation or " + "increase the required safety factor."); } } private static bool IsAdaptGeometrySuccessful(Message[] messages, DesignResult results) { foreach (var message in messages) { string location = "Location '" + results.LocationName + "'"; string profile = "subsoil scenario '" + results.ProfileName + "'"; string scenario = "design scenario '" + results.ScenarioName + "'"; if (message.Message1.Contains("The design was not successful.") && message.Message1.Contains(location) && message.Message1.Contains(profile) && message.Message1.Contains(scenario)) { return false; } } return true; } private static double FetchRequiredFactor(EngineInterface engineInterface, string scenarioName, string locationName) { foreach (Location location in engineInterface.DamProjectData.Dike.Locations) { foreach (DesignScenario scenario in location.Scenarios) { if (location.Name == locationName && scenario.LocationScenarioID == scenarioName) { switch (engineInterface.DamProjectData.DamProjectCalculationSpecification.CurrentSpecification.FailureMechanismSystemType) { case FailureMechanismSystemType.StabilityInside: return scenario.RequiredSafetyFactorStabilityInnerSlope; case FailureMechanismSystemType.StabilityOutside: return scenario.RequiredSafetyFactorStabilityOuterSlope; case FailureMechanismSystemType.Piping: return scenario.RequiredSafetyFactorPiping; default: throw new ArgumentOutOfRangeException(); } } } } return 999; } private static double FetchCalculatedFactor(EngineInterface engineInterface, DesignResult results) { switch (engineInterface.DamProjectData.DamProjectCalculationSpecification.CurrentSpecification.FailureMechanismSystemType) { case FailureMechanismSystemType.StabilityInside: case FailureMechanismSystemType.StabilityOutside: return results.StabilityDesignResults.SafetyFactor; case FailureMechanismSystemType.Piping: return results.PipingDesignResults.Wti2017BackwardErosionFactor; default: throw new ArgumentOutOfRangeException(); } } }