Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Check presence of time bounds and guess them if needed #849

Merged
merged 25 commits into from
Mar 2, 2021
Merged
Changes from 1 commit
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e3189bd
Do not push
sloosvel Oct 2, 2020
0444ef4
Add bounds for other frequencies
sloosvel Oct 27, 2020
79d11ec
Merge remote-tracking branch 'origin/master' into dev_check_time_bounds
sloosvel Oct 27, 2020
844d9bb
Fix tests
sloosvel Oct 27, 2020
3891ea4
Fix KeyError
sloosvel Oct 27, 2020
8d59972
Change for
sloosvel Oct 27, 2020
9aa51b7
Add dec frequency
sloosvel Oct 28, 2020
1c9c57d
Add tests
sloosvel Oct 28, 2020
56328e4
Fix bug in 1hr frequency
sloosvel Oct 28, 2020
a7dc456
Fix flake
sloosvel Oct 28, 2020
11217b7
Fix style issues
sloosvel Oct 28, 2020
8e4bc5a
Fix format
Jan 5, 2021
a69e5ac
Merge remote-tracking branch 'origin/master' into dev_check_time_bounds
sloosvel Jan 26, 2021
fd75ee5
Fix docstring
sloosvel Jan 26, 2021
dafe506
Create common time bounds function
sloosvel Jan 26, 2021
64cdeee
Fix flake
sloosvel Jan 26, 2021
85151f2
Update esmvalcore/preprocessor/_time.py
sloosvel Jan 28, 2021
f2b65bc
Add warning for missing bounds
sloosvel Jan 29, 2021
c0d84d2
Improve tests
sloosvel Feb 1, 2021
735f70e
Merge remote-tracking branch 'origin/master' into dev_check_time_bounds
sloosvel Feb 1, 2021
88ef9fb
Add test for differing calendars
sloosvel Feb 11, 2021
8cb99d1
Merge remote-tracking branch 'origin/master' into dev_check_time_bounds
sloosvel Feb 11, 2021
6fe3f29
Merge remote-tracking branch 'origin/master' into dev_check_time_bounds
sloosvel Feb 12, 2021
9e0d95d
Merge remote-tracking branch 'origin/master' into dev_check_time_bounds
Peter9192 Mar 1, 2021
fc31631
Move imports to right place
sloosvel Mar 1, 2021
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
Prev Previous commit
Next Next commit
Improve tests
sloosvel committed Feb 1, 2021
commit c0d84d2ae511b1d6ab5dfef9dc17196d3f1157b0
73 changes: 12 additions & 61 deletions tests/unit/cmor/test_cmor_check.py
Original file line number Diff line number Diff line change
@@ -921,67 +921,18 @@ def test_frequency_not_supported(self):
"""Fail at metadata if frequency is not supported."""
self._check_fails_in_metadata(frequency='wrong_freq')

def test_hourly_time_bounds(self):
"""Test time bounds are properly guessed for hourly freq"""
cube = self.get_cube(self.var_info, frequency='hr')
original_bounds = cube.coord('time').bounds
cube.coord('time').bounds = None
self.cube = cube
self._check_cube(automatic_fixes=True, frequency='hr')
guessed_bounds = self.cube.coord('time').bounds
assert original_bounds.all() == guessed_bounds.all()

def test_3hourly_time_bounds(self):
"""Test time bounds are properly guessed for 3hourly freq"""
cube = self.get_cube(self.var_info, frequency='3hr')
original_bounds = self.cube.coord('time').bounds
cube.coord('time').bounds = None
self.cube = cube
self._check_cube(automatic_fixes=True, frequency='3hr')
guessed_bounds = self.cube.coord('time').bounds
assert original_bounds.all() == guessed_bounds.all()

def test_6hourly_time_bounds(self):
"""Test time bounds are properly guessed for 6hourly freq"""
cube = self.get_cube(self.var_info, frequency='6hr')
original_bounds = self.cube.coord('time').bounds
cube.coord('time').bounds = None
self.cube = cube
self._check_cube(automatic_fixes=True, frequency='6hr')
guessed_bounds = self.cube.coord('time').bounds
assert original_bounds.all() == guessed_bounds.all()

def test_daily_time_bounds(self):
"""Test time bounds are properly guessed for daily freq"""
original_bounds = self.cube.coord('time').bounds
self.cube.coord('time').bounds = None
self._check_cube(automatic_fixes=True)
guessed_bounds = self.cube.coord('time').bounds
assert original_bounds.all() == guessed_bounds.all()

def test_monthly_time_bounds(self):
"""Test time bounds are guessed for monthly freq"""
cube = self.get_cube(self.var_info, frequency='mon')
cube.coord('time').bounds = None
self.cube = cube
self._check_cube(automatic_fixes=True, frequency='mon')
assert self.cube.coord('time').bounds is not None

def test_yearly_time_bounds(self):
"""Test time bounds are guessed for yearly freq"""
cube = self.get_cube(self.var_info, frequency='yr')
cube.coord('time').bounds = None
self.cube = cube
self._check_cube(automatic_fixes=True, frequency='yr')
assert self.cube.coord('time').bounds is not None

def test_dec_time_bounds(self):
"""Test time bounds are guessed for dec freq"""
cube = self.get_cube(self.var_info, frequency='dec')
cube.coord('time').bounds = None
self.cube = cube
self._check_cube(automatic_fixes=True, frequency='dec')
assert self.cube.coord('time').bounds is not None
def test_time_bounds(self):
"""Test time bounds are guessed for all frequencies"""
freqs = ['hr', '3hr', '6hr', 'day', 'mon', 'yr', 'dec']
guessed = []
for freq in freqs:
cube = self.get_cube(self.var_info, frequency=freq)
cube.coord('time').bounds = None
self.cube = cube
self._check_cube(automatic_fixes=True, frequency=freq)
if cube.coord('time').bounds is not None:
guessed.append(True)
assert len(guessed) == len(freqs)

def test_no_time_bounds(self):
"""Test time bounds are not guessed for instantaneous data"""
58 changes: 58 additions & 0 deletions tests/unit/preprocessor/_time/test_time.py
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@
import iris.coords
import numpy as np
import pytest
import datetime
from cf_units import Unit
from iris.cube import Cube
from numpy.testing import assert_array_almost_equal, assert_array_equal
@@ -764,6 +765,17 @@ def test_regrid_time_year(self):
expected = self.cube_1.data
diff_cube = newcube_2 - newcube_1
assert_array_equal(diff_cube.data, expected)
# test bounds are set at [01-01-YEAR 00:00, 01-01-NEXT_YEAR 00:00]
timeunit_1 = newcube_1.coord('time').units
for i, time in enumerate(newcube_1.coord('time').points):
year_1 = timeunit_1.num2date(time).year
expected_minbound = timeunit_1.date2num(
datetime.datetime(year_1, 1, 1))
expected_maxbound = timeunit_1.date2num(
datetime.datetime(year_1+1, 1, 1))
assert_array_equal(
newcube_1.coord('time').bounds[i],
np.array([expected_minbound, expected_maxbound]))


class TestRegridTimeMonthly(tests.Test):
@@ -800,6 +812,24 @@ def test_regrid_time_mon(self):
expected = self.cube_1.data
diff_cube = newcube_2 - newcube_1
assert_array_equal(diff_cube.data, expected)
# test bounds are set at
# [01-MONTH-YEAR 00:00, 01-NEXT_MONTH-YEAR 00:00]
timeunit_1 = newcube_1.coord('time').units
for i, time in enumerate(newcube_1.coord('time').points):
month_1 = timeunit_1.num2date(time).month
year_1 = timeunit_1.num2date(time).year
next_month = month_1 + 1
next_year = year_1
if month_1 == 12:
next_month = 1
next_year += 1
expected_minbound = timeunit_1.date2num(
datetime.datetime(year_1, month_1, 1))
expected_maxbound = timeunit_1.date2num(
datetime.datetime(next_year, next_month, 1))
assert_array_equal(
newcube_1.coord('time').bounds[i],
np.array([expected_minbound, expected_maxbound]))


class TestRegridTimeDaily(tests.Test):
@@ -846,6 +876,13 @@ def test_regrid_time_day(self):
expected = self.cube_1.data
diff_cube = newcube_2 - newcube_1
self.assert_array_equal(diff_cube.data, expected)
# test bounds are set with a dt = 12/24 days
for i, time in enumerate(newcube_1.coord('time').points):
expected_minbound = time - 12/24
expected_maxbound = time + 12/24
assert_array_equal(
newcube_1.coord('time').bounds[i],
np.array([expected_minbound, expected_maxbound]))


class TestRegridTime6Hourly(tests.Test):
@@ -892,6 +929,13 @@ def test_regrid_time_6hour(self):
expected = self.cube_1.data
diff_cube = newcube_2 - newcube_1
self.assert_array_equal(diff_cube.data, expected)
# test bounds are set with a dt = 3/24 days
for i, time in enumerate(newcube_1.coord('time').points):
expected_minbound = time - 3/24
expected_maxbound = time + 3/24
assert_array_equal(
newcube_1.coord('time').bounds[i],
np.array([expected_minbound, expected_maxbound]))


class TestRegridTime3Hourly(tests.Test):
@@ -938,6 +982,13 @@ def test_regrid_time_3hour(self):
expected = self.cube_1.data
diff_cube = newcube_2 - newcube_1
self.assert_array_equal(diff_cube.data, expected)
# test bounds are set with a dt = 1.5/24 days
for i, time in enumerate(newcube_1.coord('time').points):
expected_minbound = time - 1.5/24
expected_maxbound = time + 1.5/24
assert_array_equal(
newcube_1.coord('time').bounds[i],
np.array([expected_minbound, expected_maxbound]))


class TestRegridTime1Hourly(tests.Test):
@@ -984,6 +1035,13 @@ def test_regrid_time_hour(self):
expected = self.cube_1.data
diff_cube = newcube_2 - newcube_1
self.assert_array_equal(diff_cube.data, expected)
# test bounds are set with a dt = 0.5/24 days
for i, time in enumerate(newcube_1.coord('time').points):
expected_minbound = time - 0.5/24
expected_maxbound = time + 0.5/24
assert_array_equal(
newcube_1.coord('time').bounds[i],
np.array([expected_minbound, expected_maxbound]))


class TestTimeseriesFilter(tests.Test):