Skip to content

Commit

Permalink
More unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tnipen committed Mar 11, 2024
1 parent fc15fcd commit 674b226
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 21 deletions.
5 changes: 5 additions & 0 deletions src/api/curve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
using namespace gridpp;

float gridpp::apply_curve(float input, const vec& curve_ref, const vec& curve_fcst, gridpp::Extrapolation policy_below, gridpp::Extrapolation policy_above) {
if(curve_ref.size() != curve_fcst.size())
throw std::invalid_argument("curve_ref and curve_fcst must be the same size");
if(curve_ref.size() == 0 || curve_ref.size() == 0)
throw std::invalid_argument("curve_ref and curve_fcst cannot have size 0");

int C = curve_fcst.size();
float smallestObs = curve_ref[0];
float smallestFcst = curve_fcst[0];
Expand Down
9 changes: 5 additions & 4 deletions src/api/transform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using namespace gridpp;

// These two are included because of SWIG. See note about SWIG requirement in gridpp.h
float gridpp::Transform::forward(float value) const {
return -1;
}
Expand Down Expand Up @@ -125,12 +126,12 @@ float gridpp::BoxCox::backward(float value) const {
gridpp::Gamma::Gamma(float shape, float scale, float tolerance) : m_gamma_dist(1, 1), m_norm_dist(), m_tolerance(tolerance) {
// Initialize the gamma distribution to something that works, and then overwrite it so that
// we can check for argument errors gracefully
if(shape <= 0)
if(!gridpp::is_valid(shape) || shape <= 0)
throw std::invalid_argument("Shape parameter must be > 0 in the gamma distribution");
if(scale <= 0)
if(!gridpp::is_valid(scale) || scale <= 0)
throw std::invalid_argument("Scale parameter must be > 0 in the gamma distribution");
if(tolerance < 0)
throw std::invalid_argument("Tolerance must be > 0 in the gamma distribution");
if(!gridpp::is_valid(tolerance) || tolerance < 0)
throw std::invalid_argument("Tolerance must be >= 0 in the gamma distribution");
m_gamma_dist = boost::math::gamma_distribution<> (shape, scale);
}
float gridpp::Gamma::forward(float value) const {
Expand Down
63 changes: 52 additions & 11 deletions tests/test_apply_curve.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,54 @@
class Test(unittest.TestCase):
def test_empty_curve(self):
"""Check for exception on empty curve"""
with self.assertRaises(Exception) as e:
gridpp.apply_curve([0, 1], [[], []], gridpp.OneToOne, gridpp.OneToOne)
with self.assertRaises(Exception) as e:
gridpp.apply_curve([0, 1], [[1, 2], []], gridpp.OneToOne, gridpp.OneToOne)
with self.assertRaises(Exception) as e:
gridpp.apply_curve([0, 1], [[], [1, 2]], gridpp.OneToOne, gridpp.OneToOne)
inputs = [0, [0, 1], [[0], [1]]]
for input in inputs:
with self.subTest(input=input):
with self.assertRaises(ValueError) as e:
gridpp.apply_curve(input, [], [], gridpp.OneToOne, gridpp.OneToOne)
with self.assertRaises(ValueError) as e:
gridpp.apply_curve(input, [1, 2], [], gridpp.OneToOne, gridpp.OneToOne)
with self.assertRaises(ValueError) as e:
gridpp.apply_curve(input, [], [1, 2], gridpp.OneToOne, gridpp.OneToOne)

def test_invalid_curve(self):
"""Check for exception on invalid curve"""
with self.assertRaises(Exception) as e:
gridpp.apply_curve([0, 1], [1, 2, 3], [1, 2], gridpp.OneToOne, gridpp.OneToOne)
with self.assertRaises(Exception) as e:
gridpp.apply_curve([0, 1], [1, 2], [1, 2, 3], gridpp.OneToOne, gridpp.OneToOne)
inputs = [0, [0, 1], [[0], [1]]]
for input in inputs:
with self.subTest(input=input):
with self.assertRaises(ValueError) as e:
gridpp.apply_curve(input, [1, 2, 3], [1, 2], gridpp.OneToOne, gridpp.OneToOne)
with self.assertRaises(ValueError) as e:
gridpp.apply_curve(input, [1, 2], [1, 2, 3], gridpp.OneToOne, gridpp.OneToOne)

def test_invalid_3d_curve(self):
input0 = np.reshape(np.arange(8), [2, 4])
input = np.reshape(np.arange(6), [2, 3])
x0 = np.reshape(np.arange(18), [2, 3, 3])
y0 = np.reshape(np.arange(18), [2, 3, 3])
x = np.reshape(np.arange(24), [2, 3, 4])
y = np.reshape(np.arange(24), [2, 3, 4])
with self.assertRaises(ValueError) as e:
gridpp.apply_curve(input0, x, y, gridpp.OneToOne, gridpp.OneToOne)
with self.assertRaises(ValueError) as e:
gridpp.apply_curve(input, x0, y, gridpp.OneToOne, gridpp.OneToOne)
with self.assertRaises(ValueError) as e:
gridpp.apply_curve(input, x, y0, gridpp.OneToOne, gridpp.OneToOne)

# Test empty curve
with self.assertRaises(ValueError) as e:
gridpp.apply_curve(input, [[]], [[]], gridpp.OneToOne, gridpp.OneToOne)

def test_empty_fcst(self):
"""Check for empty result on empty input"""
# 1D input
q = gridpp.apply_curve([], [1, 2], [1, 2], gridpp.OneToOne, gridpp.OneToOne)
np.testing.assert_array_equal(q, [])

# 2D input
q = gridpp.apply_curve([[]], [1, 2], [1, 2], gridpp.OneToOne, gridpp.OneToOne)
np.testing.assert_array_equal(q, [[]])

def test_edge(self):
"""Check values on edge of curve"""
x = [1, 2, 3]
Expand All @@ -37,7 +66,7 @@ def test_edge(self):
output = gridpp.apply_curve([val], y, x, policy, policy)
np.testing.assert_array_equal(output, [val+1])

def test_extrapolation(self):
def test_extrapolation_1d(self):
"""Check values outside curve"""
x = [1, 2, 3]
y = [2, 5, 6]
Expand All @@ -48,6 +77,18 @@ def test_extrapolation(self):
np.testing.assert_array_equal(gridpp.apply_curve([0,4], y, x, gridpp.NearestSlope, gridpp.NearestSlope), [-1,7])
np.testing.assert_array_equal(gridpp.apply_curve([0,4], y, x, gridpp.Unchanged, gridpp.Unchanged), [0,4])

def test_extrapolation_2d(self):
"""Check values outside curve"""
x = [1, 2, 3]
y = [2, 5, 6]
input = [[0],[4]]
policies = [gridpp.OneToOne, gridpp.Zero, gridpp.MeanSlope, gridpp.NearestSlope]
np.testing.assert_array_equal(gridpp.apply_curve(input, y, x, gridpp.OneToOne, gridpp.OneToOne), [[1],[7]])
np.testing.assert_array_equal(gridpp.apply_curve(input, y, x, gridpp.Zero, gridpp.Zero), [[2],[6]])
np.testing.assert_array_equal(gridpp.apply_curve(input, y, x, gridpp.MeanSlope, gridpp.MeanSlope), [[0],[8]])
np.testing.assert_array_equal(gridpp.apply_curve(input, y, x, gridpp.NearestSlope, gridpp.NearestSlope), [[-1],[7]])
np.testing.assert_array_equal(gridpp.apply_curve(input, y, x, gridpp.Unchanged, gridpp.Unchanged), [[0],[4]])

def test_3d(self):
curve_fcst = np.random.rand(3, 2, 4)
curve_ref = np.random.rand(3, 2, 4)
Expand Down
1 change: 1 addition & 0 deletions tests/test_fill.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ def test_invalid_radii(self):

def test_invalid_number_of_radii(self):
values = np.zeros([3, 3])
value = 1
for outside in [False, True]:
with self.assertRaises(Exception) as e:
gridpp.fill(grid, values, points, [1], value, outside)
Expand Down
26 changes: 20 additions & 6 deletions tests/test_humidity.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ def test_invalid_input(self):

def test_relative_humidity(self):
# NOTE: RH above > 100 C are 1 in the implementation
t = [293.15, 293.15, 300, 400]
td = [293.15, 289.783630, 300, 370]
rh = [1, 0.817590594291687, 1, 1]
t = [270, 270, 293.15, 293.15, 300, 400]
td = [160, 260, 293.15, 289.783630, 300, 370]
rh = [0, 0.4605, 1, 0.817590594291687, 1, 1]
for i in range(len(t)):
self.assertAlmostEqual(gridpp.relative_humidity(t[i], td[i]), rh[i], 4)
np.testing.assert_almost_equal(gridpp.relative_humidity(t, td), rh, 4)
Expand All @@ -37,6 +37,8 @@ def test_dewpoint(self):
td = [293.15, 289.783630, 300]
for i in range(len(t)):
self.assertAlmostEqual(gridpp.dewpoint(t[i], rh[i]), td[i], 4)

# Vector version
np.testing.assert_almost_equal(gridpp.dewpoint(t, rh), td, 4)

def test_dewpoint_invalid(self):
Expand All @@ -56,13 +58,25 @@ def test_wetbulb(self):
np.testing.assert_almost_equal(gridpp.wetbulb(t, p, rh), ans, 4)

def test_wetbulb_invalid(self):
t = [np.nan, np.nan, 293.15, 293.15, np.nan, 293.15]
p = [101325, 101325, 101325, np.nan, np.nan, np.nan]
rh = [0.9, np.nan, np.nan, 0.9, np.nan, 0]
t = [np.nan, np.nan, 293.15, 293.15, np.nan, 293.15, 273.15]
p = [101325, 101325, 101325, np.nan, np.nan, np.nan, 0]
rh = [0.9, np.nan, np.nan, 0.9, np.nan, 0, 0]
for i in range(len(t)):
self.assertTrue(np.isnan(gridpp.wetbulb(t[i], p[i], rh[i])))
self.assertTrue(np.isnan(gridpp.wetbulb(t, p, rh)).all())

def test_wetbulb_invalid_arguments(self):
t = [273, 274, 275]
p = [101325, 101325, 101325]
rh = [0.5, 0.5, 0.5]
with self.assertRaises(ValueError) as e:
gridpp.wetbulb(t[1:], p, rh)
with self.assertRaises(ValueError) as e:
gridpp.wetbulb(t, p[1:], rh)
with self.assertRaises(ValueError) as e:
gridpp.wetbulb(t, p, rh[1:])



if __name__ == '__main__':
unittest.main()
35 changes: 35 additions & 0 deletions tests/test_qnh.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from __future__ import print_function
import unittest
import gridpp
import numpy as np
import os


class Test(unittest.TestCase):
def test_invalid_input(self):
"""Check that dimension missmatch results in error"""
with self.assertRaises(Exception) as e:
gridpp.qnh([101325], [0, 20])

def test_invalid_values(self):
self.assertTrue(np.isnan(gridpp.qnh([-1], [0])))

def test_1(self):
p = [101325, 90000, 90000, 110000]
alt = [0, 1000, 0, -1000]
expected = [101325, 101463.21875, 90000, 97752.90742927508]
for i in range(len(p)):
self.assertAlmostEqual(gridpp.qnh(p[i], alt[i]), expected[i], 1)
np.testing.assert_almost_equal(gridpp.qnh(p, alt), expected, 1)

def test_no_pressure(self):
for altitude in [-1000, 0, 1000]:
self.assertEqual(gridpp.qnh([0], [altitude]), [0])
self.assertEqual(gridpp.qnh(0, altitude), 0)

def test_empty(self):
np.testing.assert_almost_equal(gridpp.qnh([],[]), [])


if __name__ == '__main__':
unittest.main()
26 changes: 26 additions & 0 deletions tests/test_transform.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,32 @@ def test_gamma(self):
output = transform.backward(a)
np.testing.assert_almost_equal(output, i, 5)

def test_gamma_nan_value(self):
transform = gridpp.Gamma(1, 2, 0.01)
self.assertTrue(np.isnan(transform.forward(np.nan)))
self.assertTrue(np.isnan(transform.backward(np.nan)))
self.assertTrue(np.isnan(transform.forward([np.nan])).all())
self.assertTrue(np.isnan(transform.backward([np.nan])).all())


def test_gamma_tolerance0(self):
transform = gridpp.Gamma(1, 2, 0)

def test_gamma_invalid_arguments(self):
"""Test exception when shape and/or scale are 0 or less"""
for value in [-1, 0, np.nan]:
with self.assertRaises(ValueError) as e:
transform = gridpp.Gamma(value, 2, 0.01)
with self.assertRaises(ValueError) as e:
transform = gridpp.Gamma(2, value, 0.01)
with self.assertRaises(ValueError) as e:
transform = gridpp.Gamma(value, value, 0.01)

# Tolerance must be >= 0
for value in [-1, np.nan]:
with self.assertRaises(ValueError) as e:
transform = gridpp.Gamma(1, 2, value)

def test_zero_size(self):
x = np.zeros([0, 1])
transform = gridpp.Identity()
Expand Down
5 changes: 5 additions & 0 deletions tests/test_util.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,5 +182,10 @@ def test_is_valid_lon(self):
with self.subTest(v=v):
self.assertFalse(gridpp.is_valid_lon(v, gridpp.Geodetic))

def test_set_debug_level(self):
for level in [0, 1, 10]:
gridpp.set_debug_level(level)
self.assertEqual(level, gridpp.get_debug_level())

if __name__ == '__main__':
unittest.main()

0 comments on commit 674b226

Please sign in to comment.