// Copyright (C) Stichting Deltares 2024. 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.Globalization; using System.IO; using System.Threading; using Deltares.DamEngine.Data.General; using Deltares.DamEngine.Interface; using Deltares.DamEngine.Io.XmlOutput; using Deltares.DamEngine.TestHelpers; using NUnit.Framework; using TimeSerie = Deltares.DamEngine.Io.XmlOutput.TimeSerie; namespace Deltares.DamEngine.IntegrationTests.IntegrationTests; [TestFixture] public class MultiCoreMacroStabilityTests { private const string mapTestFiles = @"TestFiles\"; const string testWorkingFolder = @"MultiCore\"; private const double tolerance3Decimals = 0.00051; [Test, Category(Categories.MultiCore)] [TestCase("OperationalBishopGrid1Core.xml")] [TestCase("OperationalBishopGrid2Cores.xml")] [TestCase("OperationalBishopGrid4Cores.xml")] [TestCase("OperationalBishopGrid8Cores.xml")] [TestCase("OperationalBishopGrid18Cores.xml")] public void OperationalBishopGridTestsWithXmlInputFile(string aFileName) { string fullInputFilename = Path.Combine(mapTestFiles, aFileName); Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; string inputString = File.ReadAllText(fullInputFilename); var engineInterface = new EngineInterface(inputString); Output output = GeneralHelper.RunAfterInputValidation(engineInterface, true, "Output.xml"); int errorCount = GeneralHelper.DetermineNumberOfCalculationErrors(engineInterface.DamProjectData.CalculationMessages); Assert.That(errorCount, Is.EqualTo(0), "There should be no errors during the calculation."); Assert.That(engineInterface.DamProjectData.CalculationMessages.Count, Is.EqualTo(output.Results.CalculationMessages.Length)); Assert.That(engineInterface.DamProjectData.DamProjectType, Is.EqualTo(DamProjectType.Operational)); Assert.That(output.Results.OperationalOutputTimeSeries.Length, Is.EqualTo(8)); var numberOfResults = 0; var numberOfRealResults = 0; foreach (TimeSerie resultSerie in output.Results.OperationalOutputTimeSeries) { numberOfResults += resultSerie.Entries.TimeSerieEntry.Length; foreach (TimeSerieEntriesTimeSerieEntry timeSerieEntriesTimeSerieEntry in resultSerie.Entries.TimeSerieEntry) { if (!Double.IsNaN(timeSerieEntriesTimeSerieEntry.Value) && timeSerieEntriesTimeSerieEntry.Value is > 0 and < 100000) { numberOfRealResults++; } } } Assert.That(numberOfResults, Is.EqualTo(16)); Assert.That(numberOfRealResults, Is.EqualTo(16)); } [Test, Category(Categories.MultiCore)] [TestCase("DesignBishopGrid1Core.xml")] [TestCase("DesignBishopGrid2Cores.xml")] [TestCase("DesignBishopGrid4Cores.xml")] [TestCase("DesignBishopGrid8Cores.xml")] [TestCase("DesignBishopGrid18Cores.xml")] public void DebugDesignWithXmlInputFile(string aFileName) { string fullInputFilename = Path.Combine(mapTestFiles, aFileName); Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; string inputString = File.ReadAllText(fullInputFilename); var engineInterface = new EngineInterface(inputString); engineInterface.DamProjectData.ProjectPath = @"MultiCore\"; Assert.That(engineInterface.DamProjectData.DamProjectType, Is.EqualTo(DamProjectType.Design)); Output output = GeneralHelper.RunAfterInputValidation(engineInterface, true, "Output.xml"); int errorCount = GeneralHelper.DetermineNumberOfCalculationErrors(engineInterface.DamProjectData.CalculationMessages); Assert.Multiple(() => { Assert.That(errorCount, Is.EqualTo(0), "There should be no errors during the calculation."); Assert.That(engineInterface.DamProjectData.CalculationMessages, Has.Count.EqualTo(output.Results.CalculationMessages.Length)); Assert.That(output.Results.CalculationResults, Has.Length.EqualTo(8)); }); Assert.Multiple(() => { Assert.That(output.Results.CalculationResults[0].StabilityDesignResults.SafetyFactor, Is.EqualTo(1.409).Within(tolerance3Decimals)); Assert.That(output.Results.CalculationResults[1].StabilityDesignResults.SafetyFactor, Is.EqualTo(1.331).Within(tolerance3Decimals)); Assert.That(output.Results.CalculationResults[2].StabilityDesignResults.SafetyFactor, Is.EqualTo(1.206).Within(tolerance3Decimals)); Assert.That(output.Results.CalculationResults[3].StabilityDesignResults.SafetyFactor, Is.EqualTo(1.475).Within(tolerance3Decimals)); Assert.That(output.Results.CalculationResults[4].StabilityDesignResults.SafetyFactor, Is.EqualTo(1.248).Within(tolerance3Decimals)); Assert.That(output.Results.CalculationResults[5].StabilityDesignResults.SafetyFactor, Is.EqualTo(1.223).Within(tolerance3Decimals)); Assert.That(output.Results.CalculationResults[6].StabilityDesignResults.SafetyFactor, Is.EqualTo(1.393).Within(tolerance3Decimals)); Assert.That(output.Results.CalculationResults[7].StabilityDesignResults.SafetyFactor, Is.EqualTo(1.373).Within(tolerance3Decimals)); }); } [Test, Category(Categories.MultiCore)] [TestCase(1)] [TestCase(10)] [TestCase(24)] public void OperationalBeeSwarmMultiCoreWithXmlInputFile(int maxCores) { const string inputFilename = "InputForDebuggingBeeSwarm.xml"; string fullInputFilename = Path.Combine(mapTestFiles, inputFilename); Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; string inputString = File.ReadAllText(fullInputFilename); var engineInterface = new EngineInterface(inputString); Assert.That(engineInterface.DamProjectData, Is.Not.Null); engineInterface.DamProjectData.MaxCalculationCores = maxCores; Assert.That(engineInterface.DamProjectData.DamProjectType, Is.EqualTo(DamProjectType.Operational)); Output output = GeneralHelper.RunAfterInputValidation(engineInterface, true, "Output" + maxCores + ".xml"); //Output output = DamXmlSerialization.LoadOutputFromXmlString(outputString); //TimeSerieCollection outputTimeSeriesCollection = new TimeSerieCollection(); //outputTimeSeriesCollection.Series.AddRange(engineInterface.DamProjectData.OutputTimeSerieCollection.Series); List series = engineInterface.DamProjectData.OutputTimeSerieCollection.Series; int errorCount = GeneralHelper.DetermineNumberOfCalculationErrors(engineInterface.DamProjectData.CalculationMessages); Assert.That(errorCount, Is.EqualTo(0), "There should be no errors during the calculation."); CheckLargeResultsSets.CheckResultsOperationalBeeSwarmMultiCoreWithXmlInputFile(series); } [Test, Category(Categories.MultiCore)] [TestCase(1)] [TestCase(4)] [TestCase(16)] public void DesignBishopWithScenariosForHeadPl3CalculatesCorrect(int maxCores) { const string inputFilename = "InputFileMultiCoreTestForScenario.xml"; string fullInputFilename = Path.Combine(mapTestFiles, inputFilename); Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; string inputString = File.ReadAllText(fullInputFilename); var engineInterface = new EngineInterface(inputString) { DamProjectData = { MaxCalculationCores = maxCores } }; string calcDir = engineInterface.DamProjectData.CalculationMap + "_Cores_" + maxCores; engineInterface.DamProjectData.CalculationMap = calcDir; Assert.That(engineInterface.DamProjectData, Is.Not.Null); calcDir = Directory.GetCurrentDirectory() + "\\" + calcDir; if (Directory.Exists(calcDir)) { Directory.Delete(calcDir, true); // delete previous results } Output output = GeneralHelper.RunAfterInputValidation(engineInterface, true, "Results_Cores_" + maxCores + ".xml"); Assert.That(output.Results.CalculationResults.Length, Is.EqualTo(18)); Assert.That(output.Results.CalculationMessages.Length, Is.EqualTo(30)); CheckLargeResultsSets.CheckResultsDesignBishopWithScenariosForHeadPl3CalculatesCorrect(output.Results.CalculationResults); } [Test, Category(Categories.MultiCore)] [TestCase(1, true)] [TestCase(4, true)] [TestCase(16, true)] [TestCase(23, true)] [TestCase(4, false)] [TestCase(16, false)] [TestCase(23, false)] public void DesignBishopAdaptionWithScenariosForHeadPl3CalculatesCorrect(int maxCores, bool justOneScenario) { const string inputFilename = "InputFileMultiCoreTestForScenarioAdaption.xml"; string fullInputFilename = Path.Combine(mapTestFiles, inputFilename); Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; string inputString = File.ReadAllText(fullInputFilename); inputString = XmlAdapter.ChangeValueInXml(inputString, "ProjectPath", "DAM 19.3 Probleem WF"); // Current directory will be used var engineInterface = new EngineInterface(inputString) { DamProjectData = { MaxCalculationCores = maxCores } }; string calcDir = engineInterface.DamProjectData.CalculationMap + "_Cores_" + maxCores; if (justOneScenario) { engineInterface.DamProjectData.Dike.ClearLocationScenariosExceptFirst(); calcDir = engineInterface.DamProjectData.CalculationMap + "_OneScenario_Cores_" + maxCores; } engineInterface.DamProjectData.CalculationMap = calcDir; Assert.That(engineInterface.DamProjectData, Is.Not.Null); calcDir = Directory.GetCurrentDirectory() + "\\" + calcDir; if (Directory.Exists(calcDir)) { Directory.Delete(calcDir, true); // delete previous results } string outputFileName = "Results_Cores_" + maxCores + ".xml"; if (justOneScenario) { outputFileName = "Results_OneScenario_Cores_" + maxCores + ".xml"; } Output output = GeneralHelper.RunAfterInputValidation(engineInterface, true, outputFileName); if (justOneScenario) { Assert.Multiple(() => { // This test has to make sure that the results are the same as the single core version of this test // If the values change below, just change them. Assert.That(output.Results.CalculationResults, Has.Length.EqualTo(9)); Assert.That(output.Results.CalculationMessages, Has.Length.EqualTo(1758)); }); } else { Assert.Multiple(() => { // Note that all results are based on the single core version of this test that can be found at: // DesignBishopOptimizedSlopeAndShoulderAdaptionWithScenariosForHeadPL3CalculatesCorrect in MacroStabilityInwardsTests Assert.That(output.Results.CalculationResults, Has.Length.EqualTo(18)); Assert.That(output.Results.CalculationMessages, Has.Length.EqualTo(3278)); }); } int resultsFound = CheckLargeResultsSets.CheckResultsDesignBishopAdaptionWithScenariosForHeadPl3CalculatesCorrect( output.Results.CalculationResults); if (justOneScenario) { Assert.That(resultsFound, Is.EqualTo(7)); } else { Assert.That(resultsFound, Is.EqualTo(14)); CheckLargeResultsSets.CheckBasicResultsBasedOnReferenceResultsFile( "TestFiles\\DesignBishopAdaptionWithScenariosForHeadPl3_Results.xml", outputFileName, output.Results.CalculationResults); } } [Test, Category("MultiCore")] [TestCase(1)] [TestCase(16)] [TestCase(23)] public void LargeInsideBishopNoAdaptionGridCalculatesCorrect(int maxCores) { const string inputFilename = "LargeInsideBishopNoAdaptionGrid.xml"; string fullInputFilename = Path.Combine(mapTestFiles, inputFilename); Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture; string inputString = File.ReadAllText(fullInputFilename); inputString = XmlAdapter.ChangeValueInXml(inputString, "ProjectPath", "LargeInsideBishopNoAdaptionGrid"); // Current directory will be used inputString = XmlAdapter.ChangeValueInXml(inputString, "CalculationMap", "Calc"); // Current directory will be used var engineInterface = new EngineInterface(inputString) { DamProjectData = { MaxCalculationCores = maxCores } }; string calcDir = engineInterface.DamProjectData.CalculationMap + "_Cores_" + maxCores; engineInterface.DamProjectData.CalculationMap = calcDir; Assert.That(engineInterface.DamProjectData, Is.Not.Null); calcDir = Directory.GetCurrentDirectory() + "\\" + calcDir; if (Directory.Exists(calcDir)) { Directory.Delete(calcDir, true); // delete previous results } string outputFileName = "LargeInsideBishopNoAdaptionGridResults_Cores_" + maxCores + ".xml"; Output output = GeneralHelper.RunAfterInputValidation(engineInterface, true, outputFileName); Assert.Multiple(() => { // This test has to make sure that the results are the same as the single core version of this test // If the values change below, just change them. Assert.That(output.Results.CalculationMessages, Has.Length.EqualTo(2485)); //#Bka was 2766 Assert.That(output.Results.CalculationResults, Has.Length.EqualTo(724)); //#Bka was 646 }); // This file is created in the Debug/Release folder while running this test. // If this file changes just replace it with the file that is created during the test. // The purpose of this test is just comparing the multicore test with the single core test. CheckLargeResultsSets.CheckBasicResultsBasedOnReferenceResultsFile( "TestFiles\\LargeInsideBishopNoAdaptionGridResults_Cores_1.xml", outputFileName, output.Results.CalculationResults); } [OneTimeSetUp] public void Setup() { RemoveTestWorkingDirectory(); // to be sure no test directory exist from previous tests } [OneTimeTearDown] public void TearDown() { RemoveTestWorkingDirectory(); // to be sure no test directory exist after the tests } private static void RemoveTestWorkingDirectory() { if (Directory.Exists(testWorkingFolder)) { const bool recursive = true; Directory.Delete(testWorkingFolder, recursive); } } }