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.
Files changed (30) hide show
  1. pyIntensityFeatures/__init__.py +30 -0
  2. pyIntensityFeatures/_main.py +500 -0
  3. pyIntensityFeatures/instruments/__init__.py +9 -0
  4. pyIntensityFeatures/instruments/satellites.py +137 -0
  5. pyIntensityFeatures/proc/__init__.py +10 -0
  6. pyIntensityFeatures/proc/boundaries.py +420 -0
  7. pyIntensityFeatures/proc/fitting.py +374 -0
  8. pyIntensityFeatures/proc/intensity.py +251 -0
  9. pyIntensityFeatures/tests/__init__.py +1 -0
  10. pyIntensityFeatures/tests/test_instruments_satellites.py +210 -0
  11. pyIntensityFeatures/tests/test_main.py +734 -0
  12. pyIntensityFeatures/tests/test_proc_boundaries.py +613 -0
  13. pyIntensityFeatures/tests/test_proc_fitting.py +218 -0
  14. pyIntensityFeatures/tests/test_proc_intensity.py +205 -0
  15. pyIntensityFeatures/tests/test_utils_checks.py +933 -0
  16. pyIntensityFeatures/tests/test_utils_coords.py +197 -0
  17. pyIntensityFeatures/tests/test_utils_distributions.py +236 -0
  18. pyIntensityFeatures/tests/test_utils_grids.py +189 -0
  19. pyIntensityFeatures/tests/test_utils_output.py +433 -0
  20. pyIntensityFeatures/utils/__init__.py +13 -0
  21. pyIntensityFeatures/utils/checks.py +420 -0
  22. pyIntensityFeatures/utils/coords.py +157 -0
  23. pyIntensityFeatures/utils/distributions.py +199 -0
  24. pyIntensityFeatures/utils/grids.py +113 -0
  25. pyIntensityFeatures/utils/output.py +276 -0
  26. pyintensityfeatures-0.1.0.dist-info/METADATA +360 -0
  27. pyintensityfeatures-0.1.0.dist-info/RECORD +30 -0
  28. pyintensityfeatures-0.1.0.dist-info/WHEEL +5 -0
  29. pyintensityfeatures-0.1.0.dist-info/licenses/LICENSE +28 -0
  30. 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