// 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.AreEqual(0, errorCount, "There should be no errors during the calculation.");
Assert.AreEqual(output.Results.CalculationMessages.Length, engineInterface.DamProjectData.CalculationMessages.Count);
Assert.IsTrue(engineInterface.DamProjectData.DamProjectType == DamProjectType.Operational);
Assert.AreEqual(8, output.Results.OperationalOutputTimeSeries.Length);
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.AreEqual(16, numberOfResults);
Assert.AreEqual(16, numberOfRealResults);
}
[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.IsTrue(engineInterface.DamProjectData.DamProjectType == DamProjectType.Design);
Output output = GeneralHelper.RunAfterInputValidation(engineInterface, true, "Output.xml");
int errorCount = GeneralHelper.DetermineNumberOfCalculationErrors(engineInterface.DamProjectData.CalculationMessages);
Assert.AreEqual(0, errorCount, "There should be no errors during the calculation.");
Assert.AreEqual(output.Results.CalculationMessages.Length, engineInterface.DamProjectData.CalculationMessages.Count);
Assert.AreEqual(8, output.Results.CalculationResults.Length);
Assert.AreEqual(1.409, output.Results.CalculationResults[0].StabilityDesignResults.SafetyFactor, tolerance3Decimals);
Assert.AreEqual(1.331, output.Results.CalculationResults[1].StabilityDesignResults.SafetyFactor, tolerance3Decimals);
Assert.AreEqual(1.206, output.Results.CalculationResults[2].StabilityDesignResults.SafetyFactor, tolerance3Decimals);
Assert.AreEqual(1.475, output.Results.CalculationResults[3].StabilityDesignResults.SafetyFactor, tolerance3Decimals);
Assert.AreEqual(1.248, output.Results.CalculationResults[4].StabilityDesignResults.SafetyFactor, tolerance3Decimals);
Assert.AreEqual(1.223, output.Results.CalculationResults[5].StabilityDesignResults.SafetyFactor, tolerance3Decimals);
Assert.AreEqual(1.393, output.Results.CalculationResults[6].StabilityDesignResults.SafetyFactor, tolerance3Decimals);
Assert.AreEqual(1.373, output.Results.CalculationResults[7].StabilityDesignResults.SafetyFactor, 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.IsNotNull(engineInterface.DamProjectData);
engineInterface.DamProjectData.MaxCalculationCores = maxCores;
Assert.IsTrue(engineInterface.DamProjectData.DamProjectType == 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.AreEqual(0, errorCount, "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.IsNotNull(engineInterface.DamProjectData);
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.AreEqual(18, output.Results.CalculationResults.Length);
Assert.AreEqual(24, output.Results.CalculationMessages.Length);
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);
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.IsNotNull(engineInterface.DamProjectData);
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)
{
// 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.AreEqual(9, output.Results.CalculationResults.Length);
Assert.AreEqual(1424, output.Results.CalculationMessages.Length);
}
else
{
// Note that all results are based on the single core version of this tests that can be found at:
// DesignBishopOptimizedSlopeAndShoulderAdaptionWithScenariosForHeadPL3CalculatesCorrect in MacroStabilityInwardsTests
Assert.AreEqual(18, output.Results.CalculationResults.Length);
Assert.AreEqual(3008, output.Results.CalculationMessages.Length);
}
int resultsFound = CheckLargeResultsSets.CheckResultsDesignBishopAdaptionWithScenariosForHeadPl3CalculatesCorrect(
output.Results.CalculationResults);
if (justOneScenario)
{
Assert.AreEqual(9, resultsFound);
}
else
{
Assert.AreEqual(18, resultsFound);
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.IsNotNull(engineInterface.DamProjectData);
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);
// 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.AreEqual(2768, output.Results.CalculationMessages.Length);
Assert.AreEqual(646, output.Results.CalculationResults.Length);
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);
}
}
}