pyIntensityFeatures 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyIntensityFeatures/__init__.py +30 -0
- pyIntensityFeatures/_main.py +500 -0
- pyIntensityFeatures/instruments/__init__.py +9 -0
- pyIntensityFeatures/instruments/satellites.py +137 -0
- pyIntensityFeatures/proc/__init__.py +10 -0
- pyIntensityFeatures/proc/boundaries.py +420 -0
- pyIntensityFeatures/proc/fitting.py +374 -0
- pyIntensityFeatures/proc/intensity.py +251 -0
- pyIntensityFeatures/tests/__init__.py +1 -0
- pyIntensityFeatures/tests/test_instruments_satellites.py +210 -0
- pyIntensityFeatures/tests/test_main.py +734 -0
- pyIntensityFeatures/tests/test_proc_boundaries.py +613 -0
- pyIntensityFeatures/tests/test_proc_fitting.py +218 -0
- pyIntensityFeatures/tests/test_proc_intensity.py +205 -0
- pyIntensityFeatures/tests/test_utils_checks.py +933 -0
- pyIntensityFeatures/tests/test_utils_coords.py +197 -0
- pyIntensityFeatures/tests/test_utils_distributions.py +236 -0
- pyIntensityFeatures/tests/test_utils_grids.py +189 -0
- pyIntensityFeatures/tests/test_utils_output.py +433 -0
- pyIntensityFeatures/utils/__init__.py +13 -0
- pyIntensityFeatures/utils/checks.py +420 -0
- pyIntensityFeatures/utils/coords.py +157 -0
- pyIntensityFeatures/utils/distributions.py +199 -0
- pyIntensityFeatures/utils/grids.py +113 -0
- pyIntensityFeatures/utils/output.py +276 -0
- pyintensityfeatures-0.1.0.dist-info/METADATA +360 -0
- pyintensityfeatures-0.1.0.dist-info/RECORD +30 -0
- pyintensityfeatures-0.1.0.dist-info/WHEEL +5 -0
- pyintensityfeatures-0.1.0.dist-info/licenses/LICENSE +28 -0
- pyintensityfeatures-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env python
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Full license can be found in License.md
|
|
4
|
+
#
|
|
5
|
+
# DISTRIBUTION STATEMENT A: Approved for public release. Distribution is
|
|
6
|
+
# unlimited.
|
|
7
|
+
# -----------------------------------------------------------------------------
|
|
8
|
+
"""Tests for functions in `instruments.satellites`."""
|
|
9
|
+
|
|
10
|
+
import datetime as dt
|
|
11
|
+
import numpy as np
|
|
12
|
+
import unittest
|
|
13
|
+
|
|
14
|
+
from pyIntensityFeatures.instruments import satellites
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class TestSatelliteFuncs(unittest.TestCase):
|
|
18
|
+
"""Tests for the satellite functions."""
|
|
19
|
+
|
|
20
|
+
def setUp(self):
|
|
21
|
+
"""Set up the test runs."""
|
|
22
|
+
# Intialize the inputs
|
|
23
|
+
self.time_data = [dt.datetime(1999, 2, 11) + dt.timedelta(seconds=i)
|
|
24
|
+
for i in range(86400)]
|
|
25
|
+
self.glat = np.ones(shape=(len(self.time_data), 30))
|
|
26
|
+
self.glon = np.ones(shape=self.glat.shape)
|
|
27
|
+
self.intensity = np.ones(shape=self.glat.shape)
|
|
28
|
+
self.clean_mask = np.zeros(shape=self.glat.shape).astype(bool)
|
|
29
|
+
self.min_colat = 40.0
|
|
30
|
+
self.start_time = None
|
|
31
|
+
|
|
32
|
+
self.glat[0] = np.linspace(-5.0, 5.0, num=self.glat.shape[1])
|
|
33
|
+
self.glon[0] = np.linspace(230.0, 210.0, num=self.glon.shape[1])
|
|
34
|
+
|
|
35
|
+
for i, lat in enumerate(self.glat[0]):
|
|
36
|
+
self.glat[:, i] = lat + (90.0 - abs(lat)) * np.sin(
|
|
37
|
+
np.linspace(0, 2.0 * np.pi, self.glat.shape[0]))
|
|
38
|
+
self.glon[:, i] = self.glon[0, i] + (
|
|
39
|
+
360.0 - self.glon[0, i]) * np.sin(
|
|
40
|
+
np.linspace(0, 2.0 * np.pi, self.glon.shape[0]))
|
|
41
|
+
|
|
42
|
+
# Initalize the outputs
|
|
43
|
+
self.out_intensity = None
|
|
44
|
+
self.out_glat = None
|
|
45
|
+
self.out_glon = None
|
|
46
|
+
self.out_times = None
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
def tearDown(self):
|
|
50
|
+
"""Tear down the test environment."""
|
|
51
|
+
del self.time_data, self.glat, self.glon, self.intensity
|
|
52
|
+
del self.clean_mask, self.min_colat, self.out_intensity, self.out_glat
|
|
53
|
+
del self.out_glon, self.out_times, self.start_time
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
def eval_auroral_slice(self):
|
|
57
|
+
"""Evaluate the output from `get_auroral_slice`."""
|
|
58
|
+
# Ensure there is auroral data in the slice
|
|
59
|
+
self.assertGreater(abs(self.out_glat).max(), self.min_colat)
|
|
60
|
+
|
|
61
|
+
# Ensure the lat/lon/intensity output have the same shape
|
|
62
|
+
self.assertTupleEqual(self.out_intensity.shape, self.out_glat.shape)
|
|
63
|
+
self.assertTupleEqual(self.out_glon.shape, self.out_glat.shape)
|
|
64
|
+
|
|
65
|
+
# Ensure the intensity value is appropriate
|
|
66
|
+
self.assertTrue(np.all(self.out_intensity == self.intensity.min()),
|
|
67
|
+
msg="output intensities differ, check input")
|
|
68
|
+
|
|
69
|
+
# Evaluate the times
|
|
70
|
+
self.eval_times(is_last=False)
|
|
71
|
+
return
|
|
72
|
+
|
|
73
|
+
def eval_times(self, is_last=False):
|
|
74
|
+
"""Evaluate the output times.
|
|
75
|
+
|
|
76
|
+
Parameters
|
|
77
|
+
----------
|
|
78
|
+
is_last : bool
|
|
79
|
+
Assert the last time is equal to the last time of the input times
|
|
80
|
+
|
|
81
|
+
"""
|
|
82
|
+
# Ensure the start time is appropriate
|
|
83
|
+
if self.start_time is None:
|
|
84
|
+
self.assertTrue(self.out_times[0] >= self.time_data[0])
|
|
85
|
+
else:
|
|
86
|
+
self.assertTrue(self.out_times[0] >= self.start_time)
|
|
87
|
+
|
|
88
|
+
# Ensure the end time is appropriate
|
|
89
|
+
if is_last:
|
|
90
|
+
self.assertTrue(self.out_times[1] == self.time_data[-1])
|
|
91
|
+
else:
|
|
92
|
+
self.assertTrue(self.out_times[1] <= self.time_data[-1])
|
|
93
|
+
return
|
|
94
|
+
|
|
95
|
+
def test_get_auroral_slice_bad_intensity_shape(self):
|
|
96
|
+
"""Test raises ValueError with the bad intensity data shape."""
|
|
97
|
+
# Reshape the intensity data
|
|
98
|
+
self.intensity = self.intensity.transpose()
|
|
99
|
+
|
|
100
|
+
# Run the slice ID function and evaluate the error
|
|
101
|
+
args = [self.time_data, self.glat, self.glon, self.intensity]
|
|
102
|
+
self.assertRaisesRegex(ValueError, "first dimension of intensity data",
|
|
103
|
+
satellites.get_auroral_slice, *args)
|
|
104
|
+
return
|
|
105
|
+
|
|
106
|
+
def test_get_auroral_slice_bad_lat_shape(self):
|
|
107
|
+
"""Test raises ValueError with the bad latitude data shape."""
|
|
108
|
+
# Reshape the intensity data
|
|
109
|
+
self.glat = self.glat.transpose()
|
|
110
|
+
|
|
111
|
+
# Run the slice ID function and evaluate the error
|
|
112
|
+
args = [self.time_data, self.glat, self.glon, self.intensity]
|
|
113
|
+
self.assertRaisesRegex(ValueError, "intensity and location input ",
|
|
114
|
+
satellites.get_auroral_slice, *args)
|
|
115
|
+
return
|
|
116
|
+
|
|
117
|
+
def test_get_auroral_slice_bad_lon_shape(self):
|
|
118
|
+
"""Test raises ValueError with the bad longitude data shape."""
|
|
119
|
+
# Reshape the intensity data
|
|
120
|
+
self.glon = self.glon.transpose()
|
|
121
|
+
|
|
122
|
+
# Run the slice ID function and evaluate the error
|
|
123
|
+
args = [self.time_data, self.glat, self.glon, self.intensity]
|
|
124
|
+
self.assertRaisesRegex(ValueError, "intensity and location input ",
|
|
125
|
+
satellites.get_auroral_slice, *args)
|
|
126
|
+
return
|
|
127
|
+
|
|
128
|
+
def test_get_auroral_slice_bad_clean_mask_shape(self):
|
|
129
|
+
"""Test raises ValueError with the bad clean mask shape."""
|
|
130
|
+
# Reshape the intensity data
|
|
131
|
+
self.clean_mask = self.clean_mask[0, :]
|
|
132
|
+
|
|
133
|
+
# Run the slice ID function and evaluate the error
|
|
134
|
+
args = [self.time_data, self.glat, self.glon, self.intensity,
|
|
135
|
+
self.clean_mask]
|
|
136
|
+
self.assertRaisesRegex(ValueError, "clean mask shape differs from ",
|
|
137
|
+
satellites.get_auroral_slice, *args)
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
def test_get_auroral_slice_no_mask(self):
|
|
141
|
+
"""Test slice output without a mask."""
|
|
142
|
+
# Cycle through both hemispheres
|
|
143
|
+
for hemi in [-1, 1]:
|
|
144
|
+
# Cycle through start time options
|
|
145
|
+
for self.start_time in [None, self.time_data[20000],
|
|
146
|
+
self.time_data[20000]
|
|
147
|
+
+ dt.timedelta(microseconds=1)]:
|
|
148
|
+
with self.subTest(hemi=hemi, start_time=self.start_time):
|
|
149
|
+
# Run the slice ID function
|
|
150
|
+
(self.out_intensity, self.out_glat, self.out_glon,
|
|
151
|
+
self.out_times) = satellites.get_auroral_slice(
|
|
152
|
+
self.time_data, hemi * self.glat, self.glon,
|
|
153
|
+
self.intensity, start_time=self.start_time,
|
|
154
|
+
hemisphere=hemi, min_colat=self.min_colat)
|
|
155
|
+
|
|
156
|
+
# Evaluate the output
|
|
157
|
+
self.eval_auroral_slice()
|
|
158
|
+
return
|
|
159
|
+
|
|
160
|
+
def test_get_auroral_slice_mask(self):
|
|
161
|
+
"""Test slice output with a mask that removes all data."""
|
|
162
|
+
# Cycle through both hemispheres
|
|
163
|
+
for hemi in [-1, 1]:
|
|
164
|
+
# Cycle through start time options
|
|
165
|
+
for self.start_time in [None, self.time_data[20000],
|
|
166
|
+
self.time_data[20000]
|
|
167
|
+
+ dt.timedelta(microseconds=1)]:
|
|
168
|
+
with self.subTest(hemi=hemi, start_time=self.start_time):
|
|
169
|
+
# Run the slice ID function
|
|
170
|
+
(self.out_intensity, self.out_glat, self.out_glon,
|
|
171
|
+
self.out_times) = satellites.get_auroral_slice(
|
|
172
|
+
self.time_data, hemi * self.glat, self.glon,
|
|
173
|
+
self.intensity, clean_mask=self.clean_mask,
|
|
174
|
+
start_time=self.start_time, hemisphere=hemi,
|
|
175
|
+
min_colat=self.min_colat)
|
|
176
|
+
|
|
177
|
+
# Evaluate the output
|
|
178
|
+
self.assertTupleEqual(self.out_intensity.shape, (0, ))
|
|
179
|
+
self.assertTupleEqual(self.out_glat.shape, (0, ))
|
|
180
|
+
self.assertTupleEqual(self.out_glon.shape, (0, ))
|
|
181
|
+
self.eval_times(is_last=True)
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
def test_get_auroral_slice_bad_lat_range(self):
|
|
185
|
+
"""Test slice output with a lat range that doesn't contain a slice."""
|
|
186
|
+
self.time_data = self.time_data[:5837]
|
|
187
|
+
self.glat = self.glat[:5837, :]
|
|
188
|
+
self.glon = self.glon[:5837, :]
|
|
189
|
+
self.intensity = self.intensity[:5837, :]
|
|
190
|
+
|
|
191
|
+
# Cycle through both hemispheres
|
|
192
|
+
for hemi in [-1, 1]:
|
|
193
|
+
# Cycle through start time options
|
|
194
|
+
with self.subTest(hemi=hemi):
|
|
195
|
+
# Run the slice ID function
|
|
196
|
+
(self.out_intensity, self.out_glat, self.out_glon,
|
|
197
|
+
self.out_times) = satellites.get_auroral_slice(
|
|
198
|
+
self.time_data, hemi * self.glat, self.glon,
|
|
199
|
+
self.intensity, start_time=self.start_time,
|
|
200
|
+
hemisphere=hemi, min_colat=self.min_colat)
|
|
201
|
+
|
|
202
|
+
# Evaluate the output
|
|
203
|
+
self.assertTupleEqual(self.out_intensity.shape,
|
|
204
|
+
(1, self.intensity.shape[1]))
|
|
205
|
+
self.assertTupleEqual(self.out_glat.shape,
|
|
206
|
+
(1, self.glat.shape[1]))
|
|
207
|
+
self.assertTupleEqual(self.out_glon.shape,
|
|
208
|
+
(1, self.glon.shape[1]))
|
|
209
|
+
self.eval_times(is_last=True)
|
|
210
|
+
return
|