Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/workflows/run-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: Run Tests

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
run-tests:
runs-on: windows-latest

steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: true

- name: Add msbuild to PATH
uses: microsoft/setup-msbuild@v1.1

- name: Add vstest to PATH
uses: darenm/Setup-VSTest@v1.2

- name: Create empty TeeChart license file
run: |
copy /Y nul > "BlueM.Opt\Main\My Project\TeeChart.licenses"
copy /Y nul > "BlueM.Wave\source\My Project\TeeChart.licenses"
copy /Y nul > "BlueM.Opt\Tests\SensiPlot_ParameterSampling\My Project\TeeChart.licenses"
shell: cmd

- name: Build BlueM.Opt.Tests
run: msbuild BlueM.Opt\Tests\BlueM.Opt.Tests\BlueM.Opt.Tests.vbproj -restore -property:Platform=x64 -property:Configuration=Debug

- name: Run Tests
run: |
vstest.console.exe BlueM.Opt\tests\BlueM.Opt.Tests\bin\x64\Debug\net48\BlueM.Opt.Tests.exe /Settings:BlueM.Opt\tests\BlueM.Opt.Tests\tests.runsettings

- name: Upload test results
uses: actions/upload-artifact@v4
with:
name: test-results
path: BlueM.Opt\Tests\BlueM.Opt.Tests\TestResults
8 changes: 8 additions & 0 deletions BlueM.Opt.sln
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SensiPlot_ParameterSampling
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "BlueM.Opt.Tests", "BlueM.Opt.Tests", "{2846C8D3-E679-4D5C-95B4-4CF3E4BF7122}"
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "BlueM.Opt.Tests", "BlueM.Opt\Tests\BlueM.Opt.Tests\BlueM.Opt.Tests.vbproj", "{69B4CC2F-9BBB-DE40-7F29-C0F416AF0222}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Expand Down Expand Up @@ -162,6 +164,11 @@ Global
{2BC34399-890A-4C37-85A4-3F8D633835B9}.Debug|x86.Build.0 = Debug|x86
{2BC34399-890A-4C37-85A4-3F8D633835B9}.Release|x64.ActiveCfg = Release|x64
{2BC34399-890A-4C37-85A4-3F8D633835B9}.Release|x86.ActiveCfg = Release|x86
{69B4CC2F-9BBB-DE40-7F29-C0F416AF0222}.Debug|x64.ActiveCfg = Debug|x64
{69B4CC2F-9BBB-DE40-7F29-C0F416AF0222}.Debug|x64.Build.0 = Debug|x64
{69B4CC2F-9BBB-DE40-7F29-C0F416AF0222}.Debug|x86.ActiveCfg = Debug|x64
{69B4CC2F-9BBB-DE40-7F29-C0F416AF0222}.Release|x64.ActiveCfg = Release|x64
{69B4CC2F-9BBB-DE40-7F29-C0F416AF0222}.Release|x86.ActiveCfg = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -175,6 +182,7 @@ Global
{A9272445-616D-4B4F-ACFF-DE6F5591AF63} = {44D440EE-4641-44A1-B177-2DD0FDD6FB75}
{6FCD5583-4A2A-4298-8A48-D46FAEFC5D7C} = {44D440EE-4641-44A1-B177-2DD0FDD6FB75}
{2BC34399-890A-4C37-85A4-3F8D633835B9} = {2846C8D3-E679-4D5C-95B4-4CF3E4BF7122}
{69B4CC2F-9BBB-DE40-7F29-C0F416AF0222} = {2846C8D3-E679-4D5C-95B4-4CF3E4BF7122}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4558EFAB-BB97-40C0-BF67-4380356C931B}
Expand Down
2 changes: 1 addition & 1 deletion BlueM.Opt/Common/ObjectiveFunctions/ObjectiveFunction.vb
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ Public MustInherit Class ObjectiveFunction
''' <param name="[Function]">comparison function</param>
''' <returns>function value</returns>
''' <remarks>Konstante und gleiche Zeitschrittweiten vorausgesetzt! (#151)</remarks>
Protected Shared Function compareSeries(ByVal SimSeries As Wave.TimeSeries, ByVal RefSeries As Wave.TimeSeries, ByVal [Function] As String) As Double
Public Shared Function compareSeries(ByVal SimSeries As Wave.TimeSeries, ByVal RefSeries As Wave.TimeSeries, ByVal [Function] As String) As Double

Dim objectiveValue As Double
Dim i As Integer
Expand Down
25 changes: 25 additions & 0 deletions BlueM.Opt/Tests/BlueM.Opt.Tests/BlueM.Opt.Tests.vbproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<Project Sdk="MSTest.Sdk/3.6.4">

<PropertyGroup>
<TargetFramework>net48</TargetFramework>
<LangVersion>latest</LangVersion>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<!--
Displays error on console in addition to the log file. Note that this feature comes with a performance impact.
For more information, visit https://learn.microsoft.com/dotnet/core/testing/unit-testing-platform-integration-dotnet-test#show-failure-per-test
-->
<TestingPlatformShowTestsFailure>true</TestingPlatformShowTestsFailure>
<Platforms>x64</Platforms>
</PropertyGroup>

<ItemGroup>
<Compile Remove="Test1.vb" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\..\BlueM.Wave\source\Wave.vbproj" />
<ProjectReference Include="..\..\Common\BlueM.Opt.Common.vbproj" />
</ItemGroup>

</Project>
3 changes: 3 additions & 0 deletions BlueM.Opt/Tests/BlueM.Opt.Tests/MSTestSettings.vb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Imports Microsoft.VisualStudio.TestTools.UnitTesting

<Assembly: Parallelize(Scope:=ExecutionScope.MethodLevel)>
182 changes: 182 additions & 0 deletions BlueM.Opt/Tests/BlueM.Opt.Tests/TestObjectiveFunctions.vb
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
'BlueM.Opt
'Copyright (C) BlueM Dev Group
'Website: <https://www.bluemodel.org>
'
'This program 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 <https://www.gnu.org/licenses/>.
'
Imports Microsoft.VisualStudio.TestTools.UnitTesting
Imports BlueM.Opt.Common
Imports BlueM.Wave

<TestClass>
Public Class TestObjectiveFunctions

Private Function CreateTimeSeries(values As Double(), Optional title As String = "Test") As TimeSeries
Dim ts As New TimeSeries()
ts.Title = title
ts.Interpretation = TimeSeries.InterpretationEnum.BlockRight
Dim startdate As New DateTime(2020, 1, 1)
For i = 0 To values.Length - 1
ts.AddNode(startdate.AddDays(i), values(i))
Next
Return ts
End Function

<TestMethod>
Public Sub CompareSeries_SSE_ReturnsSumOfSquaredErrors()
Dim sim As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
Dim ref As TimeSeries = CreateTimeSeries({2.0, 2.0, 2.0})
Dim result = ObjectiveFunction.compareSeries(sim, ref, "SSE")
Assert.AreEqual(2.0, result, 0.000001)
End Sub

<TestMethod>
Public Sub CompareSeries_MSE_ReturnsMeanSquaredError()
Dim sim As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
Dim ref As TimeSeries = CreateTimeSeries({2.0, 2.0, 2.0})
Dim result = ObjectiveFunction.compareSeries(sim, ref, "MSE")
Assert.AreEqual(2.0 / 3.0, result, 0.000001)
End Sub

<TestMethod>
Public Sub CompareSeries_SAE_ReturnsSumOfAbsoluteErrors()
Dim sim As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
Dim ref As TimeSeries = CreateTimeSeries({2.0, 2.0, 2.0})
Dim result = ObjectiveFunction.compareSeries(sim, ref, "SAE")
Assert.AreEqual(2.0, result, 0.000001)
End Sub

<TestMethod>
Public Sub CompareSeries_MAE_ReturnsMeanAbsoluteError()
Dim sim As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
Dim ref As TimeSeries = CreateTimeSeries({2.0, 2.0, 2.0})
Dim result = ObjectiveFunction.compareSeries(sim, ref, "MAE")
Assert.AreEqual(2.0 / 3.0, result, 0.000001)
End Sub

<TestMethod>
Public Sub CompareSeries_BIAS_ReturnsAbsoluteVolumeError()
Dim sim As TimeSeries = CreateTimeSeries({2.0, 3.0, 4.0})
Dim ref As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
Dim result = ObjectiveFunction.compareSeries(sim, ref, "BIAS")
Assert.AreEqual((9.0 - 6.0) / 6.0 * 100.0, result, 0.000001)
End Sub

<TestMethod>
Public Sub CompareSeries_NLT_ReturnsRelativeNumberOfTimestepsLessThan()
Dim sim As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
Dim ref As TimeSeries = CreateTimeSeries({2.0, 2.0, 2.0})
Dim result = ObjectiveFunction.compareSeries(sim, ref, "NLT")
Assert.AreEqual(1.0 / 3.0 * 100, result, 0.000001)
End Sub

<TestMethod>
Public Sub CompareSeries_SLT_ReturnsSumOfTimestepsLessThan()
Dim sim As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
Dim ref As TimeSeries = CreateTimeSeries({2.0, 2.0, 2.0})
Dim result = ObjectiveFunction.compareSeries(sim, ref, "SLT")
Assert.AreEqual(1.0, result, 0.000001)
End Sub

<TestMethod>
Public Sub CompareSeries_NGT_ReturnsRelativeNumberOfTimestepsGreaterThan()
Dim sim As TimeSeries = CreateTimeSeries({3.0, 2.0, 1.0})
Dim ref As TimeSeries = CreateTimeSeries({2.0, 2.0, 2.0})
Dim result = ObjectiveFunction.compareSeries(sim, ref, "NGT")
Assert.AreEqual(1.0 / 3.0 * 100, result, 0.000001)
End Sub

<TestMethod>
Public Sub CompareSeries_SGT_ReturnsSumOfTimestepsGreaterThan()
Dim sim As TimeSeries = CreateTimeSeries({3.0, 2.0, 1.0})
Dim ref As TimeSeries = CreateTimeSeries({2.0, 2.0, 2.0})
Dim result = ObjectiveFunction.compareSeries(sim, ref, "SGT")
Assert.AreEqual(1.0, result, 0.000001)
End Sub

<TestMethod>
Public Sub CompareSeries_APFB_ReturnsAnnualPeakFlowBias()
'create timeseries with two years of monthly data
Dim sim As New TimeSeries("sim")
sim.Interpretation = TimeSeries.InterpretationEnum.BlockRight
For i As Integer = 1 To 12
sim.AddNode(New DateTime(2020, i, 1), i)
sim.AddNode(New DateTime(2021, i, 1), i * 2)
Next
Dim ref As New TimeSeries("ref")
ref.Interpretation = TimeSeries.InterpretationEnum.BlockRight
For i As Integer = 1 To 12
ref.AddNode(New DateTime(2020, i, 1), i * 1.1)
ref.AddNode(New DateTime(2021, i, 1), i * 0.9 * 2)
Next
Dim peakSim = {12.0, 24.0}
Dim peakRef = {13.2, 21.6}
Dim expected = Math.Sqrt(Math.Pow((peakSim.Average() / peakRef.Average()) - 1.0, 2))
Dim result = ObjectiveFunction.compareSeries(sim, ref, "APFB")
Assert.AreEqual(expected, result, 0.000001)
End Sub

<TestMethod>
Public Sub CompareSeries_NSE_ReturnsNashSutcliffeEfficiency()
Dim sim As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
Dim ref As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
' Identical series, NSE = 1
Dim result = ObjectiveFunction.compareSeries(sim, ref, "NSE")
Assert.AreEqual(1.0, result, 0.000001)
End Sub

<TestMethod>
Public Sub CompareSeries_LNNSE_ReturnsLnNashSutcliffeEfficiency()
Dim sim As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
Dim ref As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
' Identical series, lnNSE = 1
Dim result = ObjectiveFunction.compareSeries(sim, ref, "LNNSE")
Assert.AreEqual(1.0, result, 0.000001)
End Sub

<TestMethod>
Public Sub CompareSeries_DET_ReturnsCoefficientOfDetermination()
Dim sim As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
Dim ref As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
' Identical series, DET = 1
Dim result = ObjectiveFunction.compareSeries(sim, ref, "DET")
Assert.AreEqual(1.0, result, 0.000001)
End Sub

<TestMethod>
Public Sub CompareSeries_KGE_ReturnsKlingGuptaEfficiency()
Dim sim As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
Dim ref As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
' Identical series, KGE = 1
Dim result = ObjectiveFunction.compareSeries(sim, ref, "KGE")
Assert.AreEqual(1.0, result, 0.000001)
End Sub

<TestMethod>
<ExpectedException(GetType(Exception))>
Public Sub CompareSeries_DifferentLength_ThrowsException()
Dim sim As TimeSeries = CreateTimeSeries({1.0, 2.0})
Dim ref As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
ObjectiveFunction.compareSeries(sim, ref, "SSE")
End Sub

<TestMethod>
<ExpectedException(GetType(Exception))>
Public Sub CompareSeries_UnsupportedFunction_ThrowsException()
Dim sim As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
Dim ref As TimeSeries = CreateTimeSeries({1.0, 2.0, 3.0})
ObjectiveFunction.compareSeries(sim, ref, "UNSUPPORTED")
End Sub

End Class
52 changes: 52 additions & 0 deletions BlueM.Opt/Tests/BlueM.Opt.Tests/tests.runsettings
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<RunSettings>
<!-- Configurations that affect the Test Framework -->
<RunConfiguration>
<!-- Use 0 for maximum process-level parallelization. This does not force parallelization within the test DLL (on the thread-level). You can also change it from the Test menu; choose "Run tests in parallel". Unchecked = 1 (only 1), checked = 0 (max). -->
<MaxCpuCount>1</MaxCpuCount>
<!-- Path relative to directory that contains .runsettings file-->
<ResultsDirectory>.\TestResults</ResultsDirectory>

<!-- Omit the whole tag for auto-detection. -->
<!-- [x86] or x64, ARM, ARM64, s390x -->
<!-- You can also change it from the Test menu; choose "Processor Architecture for AnyCPU Projects" -->
<TargetPlatform>x64</TargetPlatform>

<!-- Any TargetFramework moniker or omit the whole tag for auto-detection. -->
<!-- net48, [net40], net6.0, net5.0, netcoreapp3.1, uap10.0 etc. -->
<TargetFrameworkVersion>net48</TargetFrameworkVersion>

</RunConfiguration>

<!-- Configuration for loggers -->
<LoggerRunSettings>
<Loggers>
<Logger friendlyName="console" enabled="True" />
<Logger friendlyName="trx" enabled="True">
<Configuration>
<LogFileName>test-results.trx</LogFileName>
</Configuration>
</Logger>
<Logger friendlyName="html" enabled="True">
<Configuration>
<LogFileName>test-results.html</LogFileName>
</Configuration>
</Logger>
<Logger friendlyName="blame" enabled="True" />
</Loggers>
</LoggerRunSettings>

<!-- Adapter Specific sections -->

<!-- MSTest adapter -->
<MSTest>
<MapInconclusiveToFailed>True</MapInconclusiveToFailed>
<CaptureTraceOutput>false</CaptureTraceOutput>
<DeleteDeploymentDirectoryAfterTestRunIsComplete>False</DeleteDeploymentDirectoryAfterTestRunIsComplete>
<DeploymentEnabled>False</DeploymentEnabled>
<AssemblyResolution>
<Directory path="bin\x64\Debug\net48" includeSubDirectories="false"/>
</AssemblyResolution>
</MSTest>

</RunSettings>