Completed
Pull Request — develop (#73)
by Angeline
03:18 queued 02:12
created

test_helpers.TestHelpers.test_getcosIm()   A

Complexity

Conditions 1

Size

Total Lines 12
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 12
rs 9.85
c 0
b 0
f 0
cc 1
nop 3
1
# -*- coding: utf-8 -*-
2
"""Test the apexpy.helper submodule
3
4
Notes
5
-----
6
Whenever function outputs are tested against hard-coded numbers, the test
7
results (numbers) were obtained by running the code that is tested.  Therefore,
8
these tests below only check that nothing changes when refactoring, etc., and
9
not if the results are actually correct.
10
11
These results are expected to change when IGRF is updated.
12
13
"""
14
15
import datetime as dt
16
import numpy as np
17
import pytest
18
19
from apexpy import helpers
20
21
22
class TestHelpers():
23
    def setup(self):
24
        self.in_shape = None
25
        self.calc_val = None
26
        self.test_val = None
27
28
    def teardown(self):
29
        del self.in_shape, self.calc_val, self.test_val
30
31
    def eval_output(self, rtol=1e-7, atol=0.0):
32
        """Evaluate the values and shape of the calculated and expected output.
33
        """
34
        np.testing.assert_allclose(self.calc_val, self.test_val, rtol=rtol,
35
                                   atol=atol)
36
        assert np.asarray(self.calc_val).shape == self.in_shape
37
        return
38
39
    def datetime64_to_datetime(self, dt64):
40
        """Convert numpy datetime64 object to a datetime datetime object.
41
42
        Parameters
43
        ----------
44
        dt64 : np.datetime64
45
            Numpy datetime64 object
46
47
        Returns
48
        -------
49
        dt.datetime
50
            Equivalent datetime object with a resolution of days
51
52
        Notes
53
        -----
54
        Works outside 32 bit int second range of 1970
55
56
        """
57
        year_floor = dt64.astype('datetime64[Y]')
58
        month_floor = dt64.astype('datetime64[M]')
59
        day_floor = dt64.astype('datetime64[D]')
60
        year = year_floor.astype(int) + 1970
61
        month = (month_floor
62
                 - year_floor).astype('timedelta64[M]').astype(int) + 1
63
        day = (day_floor - month_floor).astype('timedelta64[D]').astype(int) + 1
64
        return dt.datetime(year, month, day)
65
66
    @pytest.mark.parametrize('lat', [90, 0, -90, np.nan])
67
    def test_checklat_scalar(self, lat):
68
        """Test good latitude check with scalars."""
69
        if np.isnan(lat):
70
            assert np.isnan(helpers.checklat(lat))
71
        else:
72
            assert helpers.checklat(lat) == lat
73
        return
74
75
    @pytest.mark.parametrize('lat', [(90 + 1e-5), (-90 - 1e-5)])
76
    def test_checklat_scalar_clip(self, lat):
77
        """Test good latitude check with scalars just beyond the lat limits."""
78
        assert helpers.checklat(lat) == np.sign(lat) * np.floor(abs(lat))
79
        return
80
81
    @pytest.mark.parametrize('in_args,msg',
82
                             [([90 + 1e-4], "lat must be in"),
83
                              ([-90 - 1e-4, 'glat'], "glat must be in"),
84
                              ([[-90 - 1e-5, -90, 0, 90, 90 + 1e-4], 'glat'],
85
                               "glat must be in"),
86
                              ([[-90 - 1e-4, -90, np.nan, np.nan, 90 + 1e-5]],
87
                               'lat must be in')])
88
    def test_checklat_error(self, in_args, msg):
89
        """Test bad latitude raises ValueError with appropriate message."""
90
        with pytest.raises(ValueError) as verr:
91
            helpers.checklat(*in_args)
92
93
        assert str(verr.value).startswith(msg)
94
        return
95
96
    @pytest.mark.parametrize('lat,test_lat',
97
                             [(np.linspace(-90 - 1e-5, 90 + 1e-5, 3),
98
                               [-90, 0, 90]),
99
                              (np.linspace(-90, 90, 3), [-90, 0, 90]),
100
                              ([-90 - 1e-5, 0, 90, np.nan],
101
                               [-90, 0, 90, np.nan])])
102
    def test_checklat_array(self, lat, test_lat):
103
        """Test good latitude with finite values."""
104
        self.calc_val = helpers.checklat(lat)
105
        self.in_shape = np.asarray(lat).shape
106
        self.test_val = test_lat
107
        self.eval_output(atol=1e-8)
108
        return
109
110
    @pytest.mark.parametrize('lat,test_sin', [
111
        (60, 0.96076892283052284), (10, 0.33257924500670238),
112
        ([60, 10], [0.96076892283052284, 0.33257924500670238]),
113
        ([[60, 10], [60, 10]], [[0.96076892283052284, 0.33257924500670238],
114
                                [0.96076892283052284, 0.33257924500670238]])])
115
    def test_getsinIm(self, lat, test_sin):
116
        """Test sin(Im) calculation for scalar and array inputs."""
117
        self.calc_val = helpers.getsinIm(lat)
118
        self.in_shape = np.asarray(lat).shape
119
        self.test_val = test_sin
120
        self.eval_output()
121
        return
122
123
    @pytest.mark.parametrize('lat,test_cos', [
124
        (60, 0.27735009811261463), (10, 0.94307531289434765),
125
        ([60, 10], [0.27735009811261463, 0.94307531289434765]),
126
        ([[60, 10], [60, 10]], [[0.27735009811261463, 0.94307531289434765],
127
                                [0.27735009811261463, 0.94307531289434765]])])
128
    def test_getcosIm(self, lat, test_cos):
129
        """Test cos(Im) calculation for scalar and array inputs."""
130
        self.calc_val = helpers.getcosIm(lat)
131
        self.in_shape = np.asarray(lat).shape
132
        self.test_val = test_cos
133
        self.eval_output()
134
        return
135
136
    @pytest.mark.parametrize('in_time,year', [
137
        (dt.datetime(2001, 1, 1), 2001), (dt.date(2001, 1, 1), 2001),
138
        (dt.datetime(2002, 1, 1), 2002),
139
        (dt.datetime(2005, 2, 3, 4, 5, 6), 2005.090877283105),
140
        (dt.datetime(2005, 12, 11, 10, 9, 8), 2005.943624682902)])
141
    def test_toYearFraction(self, in_time, year):
142
        """Test the datetime to fractional year calculation."""
143
        self.calc_val = helpers.toYearFraction(in_time)
144
        np.testing.assert_allclose(self.calc_val, year)
145
        return
146
147
    @pytest.mark.parametrize('gc_lat,gd_lat', [
148
        (0, 0), (90, 90), (30, 30.166923849507356), (60, 60.166364190170931),
149
        ([0, 90, 30], [0, 90, 30.166923849507356]),
150
        ([[0, 30], [90, 60]], [[0, 30.16692384950735],
151
                               [90, 60.166364190170931]])])
152
    def test_gc2gdlat(self, gc_lat, gd_lat):
153
        """Test geocentric to geodetic calculation."""
154
        self.calc_val = helpers.gc2gdlat(gc_lat)
155
        self.in_shape = np.asarray(gc_lat).shape
156
        self.test_val = gd_lat
157
        self.eval_output()
158
        return
159
160
    @pytest.mark.parametrize('in_time,test_loc', [
161
        (dt.datetime(2005, 2, 3, 4, 5, 6), (-16.505391672592904,
162
                                            122.17768157084515)),
163
        (dt.datetime(2010, 12, 11, 10, 9, 8), (-23.001554595838947,
164
                                               26.008999999955023)),
165
        (dt.datetime(1601, 1, 1, 0, 0, 0), (-23.06239721771427,
166
                                            -178.90131731228584)),
167
        (dt.datetime(2100, 12, 31, 23, 59, 59), (-23.021061422069053,
168
                                                 -179.23129780639425))])
169
    def test_subsol(self, in_time, test_loc):
170
        """Test the subsolar location calculation."""
171
        self.calc_val = helpers.subsol(in_time)
172
        np.testing.assert_allclose(self.calc_val, test_loc)
173
        return
174
175
    @pytest.mark.parametrize('in_time', [dt.datetime(1600, 12, 31, 23, 59, 59),
176
                                         dt.datetime(2101, 1, 1, 0, 0, 0)])
177
    def test_bad_subsol(self, in_time):
178
        """Test raises ValueError for bad time in subsolar calculation."""
179
        with pytest.raises(ValueError) as verr:
180
            helpers.subsol(in_time)
181
182
        assert str(verr.value).startswith('Year must be in')
183
        return
184
185
    def test_subsol_array(self):
186
        """Verify subsolar point calculation using an array of np.datetime64.
187
188
        Notes
189
        -----
190
        Tested by ensuring the array of np.datetime64 is equivalent to
191
        converting using single dt.datetime values
192
193
        """
194
        in_dates = np.arange(np.datetime64("1601"), np.datetime64("2100"),
195
                             np.timedelta64(100, 'D')).astype('datetime64[s]')
196
        sslat, sslon = helpers.subsol(in_dates)
197
198
        # Test the shape of the output
199
        assert sslat.shape == in_dates.shape
200
        assert sslon.shape == in_dates.shape
201
202
        # Test the values
203
        for i, in_date in enumerate(in_dates):
204
            dtime = self.datetime64_to_datetime(in_date)
205
            true_sslat, true_sslon = helpers.subsol(dtime)
206
            assert sslat[i] == true_sslat
207
            assert sslon[i] == true_sslon
208
        return
209