Skip to content

Commit d761229

Browse files
committed
Adds unit tests for namelist / streams reader
1 parent 087e2f1 commit d761229

File tree

4 files changed

+228
-0
lines changed

4 files changed

+228
-0
lines changed

mpas_analysis/test/__init__.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""
2+
Unit test infrastructure, adapted from approach of xarray.
3+
4+
Phillip J. Wolfram
5+
10/07/2016
6+
"""
7+
8+
import warnings
9+
from contextlib import contextmanager
10+
11+
import os
12+
from distutils import dir_util
13+
from pytest import fixture
14+
15+
try:
16+
import unittest2 as unittest
17+
except ImportError:
18+
import unittest
19+
20+
try:
21+
import lxml
22+
has_lxml = True
23+
except ImportError:
24+
has_lxml = False
25+
26+
try:
27+
import numpy as np
28+
has_numpy = True
29+
except ImportError:
30+
has_numpy = False
31+
32+
def requires_lxml(test):
33+
return test if has_lxml else unittest.skip('requires lxml')(test)
34+
35+
def requires_numpy(test):
36+
return test if has_numpy else unittest.skip('requires numpy')(test)
37+
38+
# Adapted from
39+
# http://stackoverflow.com/questions/29627341/pytest-where-to-store-expected-data
40+
@fixture
41+
def loaddatadir(request, tmpdir):
42+
'''
43+
Fixture responsible for searching a folder with the same name of test
44+
module and, if available, moving all contents to a temporary directory so
45+
tests can use them freely.
46+
'''
47+
filename = request.module.__file__
48+
test_dir, _ = os.path.splitext(filename)
49+
50+
if os.path.isdir(test_dir):
51+
dir_util.copy_tree(test_dir, bytes(tmpdir))
52+
53+
request.cls.datadir = tmpdir
54+
55+
56+
class TestCase(unittest.TestCase):
57+
def assertEqual(self, a1, a2):
58+
assert a1 == a2 or (a1 != a1 and a2 != a2)
59+
60+
@requires_numpy
61+
def assertApproxEqual(self, a1, a2, rtol=1e-5, atol=1e-8):
62+
assert np.isclose(a1, a2, rtol=rtol, atol=atol)
63+
64+
@contextmanager
65+
def assertWarns(self, message):
66+
with warnings.catch_warnings(record=True) as w:
67+
warnings.filterwarnings('always', message)
68+
yield
69+
assert len(w) > 0
70+
assert all(message in str(wi.message) for wi in w)
71+
72+
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
"""
2+
Unit test infrastructure, adapted from approach of xarray.
3+
4+
Phillip J. Wolfram
5+
10/07/2016
6+
"""
7+
8+
import pytest
9+
from mpas_analysis.test import (TestCase, requires_lxml, loaddatadir)
10+
from mpas_analysis.shared.io import NameList, StreamsFile
11+
12+
@pytest.mark.usefixtures("loaddatadir")
13+
class TestNamelist(TestCase):
14+
def setup_namelist(self):
15+
nlpath = self.datadir.join('namelist.ocean')
16+
self.nl = NameList(bytes(nlpath))
17+
18+
def setup_streams(self):
19+
sfpath = self.datadir.join('streams.ocean')
20+
self.sf = StreamsFile(bytes(sfpath))
21+
22+
def test_open_files(self):
23+
self.setup_namelist()
24+
self.setup_streams()
25+
26+
def test_read_namelist(self):
27+
self.setup_namelist()
28+
29+
# check accessing generalized function techniques
30+
self.assertEqual(self.nl.config_dt, '00:10:00')
31+
self.assertEqual(self.nl['config_dt'], '00:10:00')
32+
33+
# check cast accessors
34+
self.assertEqual(self.nl.getint('config_num_halos'), 3)
35+
self.assertApproxEqual(self.nl.getfloat('config_min_thickness'), 1.0)
36+
self.assertEqual(self.nl.getbool('config_do_restart'), False)
37+
38+
# tests for use of ' and " for string selections
39+
self.assertEqual(self.nl.config_test_extra_equals1, 'a = b')
40+
self.assertEqual(self.nl.config_test_extra_equals2, 'a = b')
41+
42+
def test_read_streamsfile(self):
43+
self.setup_streams()
44+
45+
# check
46+
self.assertEqual(self.sf.read('output', 'type'), 'output')
47+
self.assertEqual(self.sf.read('restart', 'output_interval'),
48+
'0100_00:00:00')
49+
50+
# vim: foldmethod=marker ai ts=4 sts=4 et sw=4 ft=python
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# note, some entires are not for illustration and not used in MPAS
2+
&run_modes
3+
config_ocean_run_mode = 'forward'
4+
/
5+
&time_management
6+
config_do_restart = .false.
7+
config_restart_timestamp_name = 'Restart_timestamp'
8+
config_start_time = '0000-01-01_00:00:00'
9+
config_stop_time = 'none'
10+
config_run_duration = '1000_00:00:00'
11+
config_calendar_type = 'gregorian_noleap'
12+
/
13+
&time_integration
14+
config_dt = '00:10:00'
15+
config_time_integrator = 'split_explicit'
16+
/
17+
&decomposition
18+
config_num_halos = 3
19+
config_block_decomp_file_prefix = 'graph.info.part.'
20+
config_number_of_blocks = 0
21+
config_explicit_proc_decomp = .false.
22+
config_proc_decomp_file_prefix = 'graph.info.part.'
23+
/
24+
&ALE_vertical_grid
25+
config_vert_coord_movement = 'uniform_stretching'
26+
config_use_min_max_thickness = .false.
27+
config_min_thickness = 1.0
28+
config_max_thickness_factor = 6.0
29+
config_set_restingThickness_to_IC = .true.
30+
config_dzdk_positive = .false.
31+
/
32+
&debug_checking_NOT_REAL
33+
config_test_extra_equals1 = 'a = b'
34+
config_test_extra_equals2 = "a = b"
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<streams>
2+
<immutable_stream name="mesh"
3+
type="input"
4+
filename_template="mesh.nc"
5+
input_interval="initial_only" />
6+
7+
<immutable_stream name="input"
8+
type="input"
9+
filename_template="init.nc"
10+
input_interval="initial_only" />
11+
12+
<immutable_stream name="restart"
13+
type="input;output"
14+
filename_template="restarts/restart.$Y-$M-$D_$h.$m.$s.nc"
15+
filename_interval="output_interval"
16+
reference_time="0000-01-01_00:00:00"
17+
clobber_mode="truncate"
18+
input_interval="initial_only"
19+
output_interval="0100_00:00:00" />
20+
21+
<stream name="output"
22+
type="output"
23+
filename_template="output/output.$Y-$M-$D_$h.$m.$s.nc"
24+
filename_interval="01-00-00_00:00:00"
25+
reference_time="0000-01-01_00:00:00"
26+
clobber_mode="truncate"
27+
output_interval="0001_00:00:00" >
28+
29+
<stream name="mesh"/>
30+
<var_struct name="tracers"/>
31+
<var name="velocityZonal"/>
32+
<var name="velocityMeridional"/>
33+
<var name="density"/>
34+
<var name="divergence"/>
35+
<var name="displacedDensity"/>
36+
<var name="potentialDensity"/>
37+
<var name="boundaryLayerDepth"/>
38+
<var name="boundaryLayerDepthEdge"/>
39+
<var name="indexBoundaryLayerDepth"/>
40+
<var name="indexSurfaceLayerDepth"/>
41+
<var name="surfaceFrictionVelocity"/>
42+
<var name="windStressZonalDiag"/>
43+
<var name="windStressMeridionalDiag"/>
44+
<var name="surfaceBuoyancyForcing"/>
45+
<var name="seaSurfacePressure"/>
46+
<var name="layerThickness"/>
47+
<var name="ssh"/>
48+
<var name="maxLevelEdgeTop"/>
49+
<var name="vertCoordMovementWeights"/>
50+
<var name="edgeMask"/>
51+
<var name="vertexMask"/>
52+
<var name="cellMask"/>
53+
<var name="refZMid"/>
54+
<var name="refLayerThickness"/>
55+
<var name="xtime"/>
56+
<var name="zMid"/>
57+
<var name="zTop"/>
58+
<var name="kineticEnergyCell"/>
59+
<var name="relativeVorticityCell"/>
60+
<var name="areaCellGlobal"/>
61+
<var name="areaEdgeGlobal"/>
62+
<var name="areaTriangleGlobal"/>
63+
<var name="volumeCellGlobal"/>
64+
<var name="volumeEdgeGlobal"/>
65+
<var name="CFLNumberGlobal"/>
66+
<var name="windStressZonal"/>
67+
<var name="windStressMeridional"/>
68+
<var_struct name="tracersSurfaceRestoringFields"/>
69+
<var_struct name="tracersInteriorRestoringFields"/>
70+
</stream>
71+
72+
</streams>

0 commit comments

Comments
 (0)