// 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();
}
}
}