Skip to content

Commit 476bb0c

Browse files
authored
Merge pull request #2 from AssessingSolar/add_initial_tests
Add initial tests
2 parents 98aecb0 + 3d8c6fd commit 476bb0c

File tree

4 files changed

+412
-5
lines changed

4 files changed

+412
-5
lines changed

tests/test_example.py

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/test_refraction.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# import pandas as pd
2+
import numpy as np
3+
import pytest
4+
from solposx.refraction import hughes
5+
6+
7+
@pytest.fixture
8+
def elevation_angles():
9+
return np.array([0, 10, 86])
10+
11+
12+
def test_hughes_refraction(elevation_angles):
13+
expected = np.array([0.47856238, 0.08750312, 0.])
14+
result = hughes(elevation_angles)
15+
np.testing.assert_allclose(result, expected)

tests/test_solarposition.py

Lines changed: 343 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,343 @@
1+
import pandas as pd
2+
import numpy as np
3+
import pytest
4+
from solposx.solarposition import iqbal
5+
# from solposx.solarposition import michalsky
6+
# from solposx.solarposition import noaa
7+
# from solposx.solarposition import psa
8+
# from solposx.solarposition import usno
9+
# from solposx.solarposition import sg2
10+
# from solposx.solarposition import walraven
11+
12+
13+
def expected_iqbal():
14+
columns = ['elevation', 'zenith', 'azimuth']
15+
values = [
16+
[32.3933559, 57.6066441, 205.057264],
17+
[32.3808669, 57.6191331, 205.103653],
18+
[35.10239415, 54.89760585, 169.4252099],
19+
[18.79386827, 71.20613173, 234.3799454],
20+
[35.58304355, 54.41695645, 197.47158811],
21+
[-9.32664685, 99.32664685, 201.24829056],
22+
[66.88194467, 23.11805533, 245.62133739],
23+
[9.32664685, 80.67335315, 338.75170944],
24+
[49.89989703, 40.10010297, 326.27538784],
25+
[35.56795719, 54.43204281, 175.44720266],
26+
[-53.03010805, 143.03010805, 18.66654047],
27+
[-53.03010805, 143.03010805, 18.66654047],
28+
[32.7577876, 57.2422124, 205.13681576],
29+
[32.7577876, 57.2422124, 205.13681576],
30+
[-23.30557846, 113.30557846, 79.558556],
31+
[1.23252489, 88.76747511, 104.52245037],
32+
[32.3933559, 57.6066441, 205.057264],
33+
[32.3933559, 57.6066441, 205.057264],
34+
[32.3933559, 57.6066441, 205.057264],
35+
]
36+
data = pd.DataFrame(data=values, columns=columns)
37+
return data
38+
39+
40+
def expected_michalsky_original_julian():
41+
columns = ['elevation', 'apparent_elevation', 'zenith', 'apparent_zenith',
42+
'azimuth']
43+
values = [
44+
[32.21780263, 32.24498279, 57.78219737, 57.75501721, 204.91751387],
45+
[32.20533693, 32.23252757, 57.79466307, 57.76747243, 204.96379895],
46+
[34.92279261, 34.9478766, 55.07720739, 55.0521234, 169.37618059],
47+
[18.63629794, 18.68330498, 71.36370206, 71.31669502, 234.18898285],
48+
[35.75816111, 35.78266275, 54.24183889, 54.21733725, 197.6747502],
49+
[-9.52972383, -9.55300057, 99.52972383, 99.55300057, 201.18805829],
50+
[66.85774447, 66.87103396, 23.14225553, 23.12896604, 245.08622522],
51+
[9.52972383, 9.62043934, 80.47027617, 80.37956066, 338.81194171],
52+
[50.10984044, 50.1274089, 39.89015956, 39.8725911, 326.23438218],
53+
[35.36181097, 35.38658543, 54.63818903, 54.61341457, 175.38864631],
54+
[-53.24139895, -53.25501504, 143.24139895, 143.25501504, 18.64775639],
55+
[-53.24139895, -53.25501504, 143.24139895, 143.25501504, 18.64775639],
56+
[33.19773834, 33.2241191, 56.80226166, 56.7758809, 205.09146059],
57+
[32.08343676, 32.11073039, 57.91656324, 57.88926961, 204.90619448],
58+
[-23.40757803, -23.43615939, 113.40757803, 113.43615939, 79.54939414],
59+
[1.10847034, 1.49128689, 88.89152966, 88.50871311, 104.54053775],
60+
[32.21780263, 32.24498279, 57.78219737, 57.75501721, 204.91751387],
61+
[32.21780263, 32.24498279, 57.78219737, 57.75501721, 204.91751387],
62+
[32.21780263, 32.24498279, 57.78219737, 57.75501721, 204.91751387],
63+
]
64+
data = pd.DataFrame(data=values, columns=columns)
65+
return data
66+
67+
68+
def expected_michalsky_correct_julian():
69+
columns = ['elevation', 'apparent_elevation', 'zenith', 'apparent_zenith',
70+
'azimuth']
71+
values = [
72+
[32.21780263, 32.24498279, 57.78219737, 57.75501721, 204.91751387],
73+
[32.20533693, 32.23252757, 57.79466307, 57.76747243, 204.96379895],
74+
[34.92279261, 34.9478766, 55.07720739, 55.0521234, 169.37618059],
75+
[18.63629794, 18.68330498, 71.36370206, 71.31669502, 234.18898285],
76+
[35.75816111, 35.78266275, 54.24183889, 54.21733725, 197.6747502],
77+
[-9.52972383, -9.55300057, 99.52972383, 99.55300057, 201.18805829],
78+
[66.85774447, 66.87103396, 23.14225553, 23.12896604, 294.91377478],
79+
[9.52972383, 9.62043934, 80.47027617, 80.37956066, 201.18805829],
80+
[50.10984044, 50.1274089, 39.89015956, 39.8725911, 213.76561781],
81+
[35.36181097, 35.38658543, 54.63818903, 54.61341457, 175.38864631],
82+
[-53.24139895, -53.25501504, 143.24139895, 143.25501504, 18.64775638],
83+
[-53.24139895, -53.25501504, 143.24139895, 143.25501504, 18.64775638],
84+
[32.46357112, 32.49054619, 57.53642888, 57.50945381, 204.94138737],
85+
[32.44484719, 32.47183777, 57.55515281, 57.52816223, 204.97918825],
86+
[-23.40757803, -23.43615939, 113.40757803, 113.43615939, 79.54939414],
87+
[1.10847034, 1.49128689, 88.89152966, 88.50871311, 104.54053775],
88+
[32.21780263, 32.24498279, 57.78219737, 57.75501721, 204.91751387],
89+
[32.21780263, 32.24498279, 57.78219737, 57.75501721, 204.91751387],
90+
[32.21780263, 32.24498279, 57.78219737, 57.75501721, 204.91751387],
91+
]
92+
data = pd.DataFrame(data=values, columns=columns)
93+
return data
94+
95+
96+
def expected_noaa():
97+
columns = ['elevation', 'apparent_elevation', 'zenith', 'apparent_zenith',
98+
'azimuth']
99+
values = [
100+
[32.21715174, 32.24268539, 57.78284826, 57.75731461, 204.92232395],
101+
[32.20468378, 32.23022967, 57.79531622, 57.76977033, 204.96860803],
102+
[34.92392895, 34.94698595, 55.07607105, 55.05301405, 169.38094302],
103+
[18.63438988, 18.68174893, 71.36561012, 71.31825107, 234.19290241],
104+
[35.75618186, 35.77854313, 54.24381814, 54.22145687, 197.67357003],
105+
[-9.52911488, -9.49473872, 99.52911488, 99.49473872, 336.8166928],
106+
[66.85423515, 66.8611327, 23.14576485, 23.1388673, 245.09172279],
107+
[9.52911488, 9.62132546, 80.47088512, 80.37867454, np.nan],
108+
[50.10765752, 50.12113671, 39.89234248, 39.87886329, 326.22893168],
109+
[35.36265374, 35.38534047, 54.63734626, 54.61465953, 175.39359304],
110+
[-53.23987161, -53.23556094, 143.23987161, 143.23556094, 18.65415239],
111+
[-53.23987161, -53.23556094, 143.23987161, 143.23556094, 18.65415239],
112+
[32.46248831, 32.48778263, 57.53751169, 57.51221737, 204.95376627],
113+
[32.44117331, 32.4664883, 57.55882669, 57.5335117, 204.97518601],
114+
[-23.40444445, -23.39111232, 113.40444445, 113.39111232, 79.55187937],
115+
[1.11161226, 1.46445929, 88.88838774, 88.53554071, 104.54291476],
116+
[32.21715174, 32.24268539, 57.78284826, 57.75731461, 204.92232395],
117+
[32.21715174, 32.24268539, 57.78284826, 57.75731461, 204.92232395],
118+
[32.21715174, 32.24268539, 57.78284826, 57.75731461, 204.92232395],
119+
]
120+
data = pd.DataFrame(data=values, columns=columns)
121+
return data
122+
123+
124+
def expected_psa_2001():
125+
columns = ['elevation', 'zenith', 'azimuth']
126+
values = [
127+
[32.21434385, 57.78565615, 204.91957868],
128+
[32.20187689, 57.79812311, 204.96586258],
129+
[34.92027644, 55.07972356, 169.3788305],
130+
[18.63211707, 71.36788293, 234.19028111],
131+
[35.75446859, 54.24553141, 197.67713395],
132+
[-9.53293173, 99.53293173, 201.19017401],
133+
[66.85455175, 23.14544825, 245.08643499],
134+
[9.52811892, 80.47188108, 338.80982599],
135+
[50.10817913, 39.89182087, 326.23090017],
136+
[35.35914111, 54.64085889, 175.39125721],
137+
[-53.24316093, 143.24316093, 18.65145716],
138+
[-53.24316093, 143.24316093, 18.65145716],
139+
[32.46428802, 57.53571198, 204.93415982],
140+
[32.43981197, 57.56018803, 204.98955089],
141+
[-23.40890746, 113.40890746, 79.55160745],
142+
[1.10690714, 88.89309286, 104.54258663],
143+
[32.21434385, 57.78565615, 204.91957868],
144+
[32.21434385, 57.78565615, 204.91957868],
145+
[32.21434385, 57.78565615, 204.91957868],
146+
]
147+
data = pd.DataFrame(data=values, columns=columns)
148+
return data
149+
150+
151+
def expected_psa_2020():
152+
columns = ['elevation', 'zenith', 'azimuth']
153+
values = [
154+
[32.21595946, 57.78404054, 204.9167547],
155+
[32.20349382, 57.79650618, 204.96304003],
156+
[34.92071906, 55.07928094, 169.37535422],
157+
[18.63439275, 71.36560725, 234.1884117],
158+
[35.7535716, 54.2464284, 197.67796274],
159+
[-9.5321134, 99.5321134, 201.18736969],
160+
[66.85741664, 23.14258336, 245.08558596],
161+
[9.52730057, 80.47269943, 338.81263031],
162+
[50.10853074, 39.89146926, 326.23536385],
163+
[35.35979872, 54.64020128, 175.38781378],
164+
[-53.24299848, 143.24299848, 18.64664538],
165+
[-53.24299848, 143.24299848, 18.64664538],
166+
[32.47215358, 57.52784642, 204.9305477],
167+
[32.43510874, 57.56489126, 204.98638231],
168+
[-23.41028925, 113.41028925, 79.54884709],
169+
[1.10556787, 88.89443213, 104.54003062],
170+
[32.21595946, 57.78404054, 204.9167547],
171+
[32.21595946, 57.78404054, 204.9167547],
172+
[32.21595946, 57.78404054, 204.9167547],
173+
]
174+
data = pd.DataFrame(data=values, columns=columns)
175+
return data
176+
177+
178+
def expected_usno():
179+
columns = ['elevation', 'zenith', 'azimuth']
180+
values = [
181+
[32.21913058, 57.78086942, 204.91396212],
182+
[32.20666654, 57.79333346, 204.9602485],
183+
[34.92271632, 55.07728368, 169.37217155],
184+
[18.63848631, 71.36151369, 234.18640074],
185+
[35.75823472, 54.24176528, 197.67107057],
186+
[-9.52936557, 99.52936557, 201.18474698],
187+
[66.86088833, 23.13911167, 245.08379957],
188+
[9.52936557, 80.47063443, 338.81525302],
189+
[50.11081313, 39.88918687, 326.23927513],
190+
[35.36198031, 54.63801969, 175.38462327],
191+
[-53.24179879, 143.24179879, 18.6423076],
192+
[-53.24179879, 143.24179879, 18.6423076],
193+
[32.46463884, 57.53536116, 204.9389006],
194+
[32.44304455, 57.55695545, 204.98724112],
195+
[-23.40963192, 113.40963192, 79.54658304],
196+
[1.10645791, 88.89354209, 104.53792899],
197+
[32.21913058, 57.78086942, 204.91396212],
198+
[32.21913058, 57.78086942, 204.91396212],
199+
[32.21913058, 57.78086942, 204.91396212],
200+
]
201+
data = pd.DataFrame(data=values, columns=columns)
202+
return data
203+
204+
205+
def expected_sg2():
206+
# The expected values were generated using the official SG2 Python wrapper
207+
# and the SG2 SAE refraction correction
208+
columns = ['elevation', 'apparent_elevation', 'zenith', 'apparent_zenith',
209+
'azimuth']
210+
values = [
211+
[32.21696737, 32.24355729, 57.78303263, 57.75644271, 204.91857639],
212+
[32.2045009, 32.2311035, 57.7954991, 57.7688965, 204.96486201],
213+
[34.92230526, 34.94633034, 55.07769474, 55.05366966, 169.37656689],
214+
[18.63488143, 18.6838735, 71.36511857, 71.3161265, 234.19025564],
215+
[35.75666309, 35.77996471, 54.24333691, 54.22003529, 197.67009822],
216+
[-9.53068757, -9.49650419, 99.53068757, 99.49650419, 201.18854894],
217+
[66.85690391, 66.86409239, 23.14309609, 23.13590761, 245.09007204],
218+
[9.52588549, 9.61972777, 80.47411451, 80.38027223, 338.81145106],
219+
[50.10677054, 50.1208336, 39.89322946, 39.8791664, 326.23457619],
220+
[35.36128972, 35.38493054, 54.63871028, 54.61506946, 175.38913627],
221+
[-53.24134227, -53.23705528, 143.24134227, 143.23705528, 18.64798841],
222+
[-53.24134227, -53.23705528, 143.24134227, 143.23705528, 18.64798841],
223+
[np.nan, np.nan, np.nan, np.nan, np.nan],
224+
[np.nan, np.nan, np.nan, np.nan, np.nan],
225+
[-23.40828474, -23.39502759, 113.40828474, 113.39502759, 79.54872263],
226+
[1.10752265, 1.45828141, 88.89247735, 88.54171859, 104.53992596],
227+
[32.21696737, 32.24355729, 57.78303263, 57.75644271, 204.91857639],
228+
[32.21696737, 32.24355729, 57.78303263, 57.75644271, 204.91857639],
229+
[32.21696737, 32.24355729, 57.78303263, 57.75644271, 204.91857639],
230+
]
231+
data = pd.DataFrame(data=values, columns=columns)
232+
return data
233+
234+
235+
def expected_walraven():
236+
columns = ['elevation', 'zenith', 'azimuth']
237+
values = [
238+
[32.21577184, 57.78422816, 204.9145408],
239+
[32.20330752, 57.79669248, 204.96082497],
240+
[34.91988403, 55.08011597, 169.37445826],
241+
[18.63514306, 71.36485694, 234.18579686],
242+
[35.75942569, 54.24057431, 197.67543014],
243+
[-9.53241956, 99.53241956, 201.18624892],
244+
[66.85832643, 23.14167357, 245.07813388],
245+
[9.53241956, 80.46758044, 338.81375108],
246+
[50.11302395, 39.88697605, 326.23525905],
247+
[35.35901685, 54.64098315, 175.38665251],
248+
[-53.24443195, 143.24443195, 18.64588668],
249+
[-53.24443195, 143.24443195, 18.64588668],
250+
[33.19945919, 56.80054081, 205.08690939],
251+
[32.078221, 57.921779, 204.90430273],
252+
[-23.41074972, 113.41074972, 79.55008786],
253+
[1.10528968, 88.89471032, 104.541125],
254+
[32.21577184, 57.78422816, 204.9145408],
255+
[32.21577184, 57.78422816, 204.9145408],
256+
[32.21577184, 57.78422816, 204.9145408],
257+
]
258+
data = pd.DataFrame(data=values, columns=columns)
259+
return data
260+
261+
262+
@pytest.fixture()
263+
def test_conditions():
264+
inputs = pd.DataFrame(
265+
columns=['time', 'latitude', 'longitude', 'altitude'],
266+
data=(
267+
# Units: time, degrees, degrees, m
268+
['2020-10-17T12:30+00:00', 45, 10, None], # reference
269+
['2020-10-17T12:30:10+00:00', 45, 10, None], # non-zero seconds
270+
['2020-10-17T12:30+02:00', 45, 10, None], # positive timezone
271+
['2020-10-17T12:30-02:00', 45, 10, None], # negative timezone
272+
['2020-02-29T12:30+00:00', 45, 10, None], # leap day
273+
['2020-10-17T12:30+00:00', 90, 10, None], # 90 degree latitude
274+
['2020-10-17T12:30+00:00', 0, 10, None], # zero latitude
275+
['2020-10-17T12:30+00:00', -90, 10, None], # -90 degree latitude
276+
['2020-10-17T12:30+00:00', -45, 10, None], # negative mid-latitude
277+
['2020-10-17T12:30+00:00', 45, -15, None], # negative longitude
278+
['2020-10-17T12:30+00:00', 45, -180, None], # 180-degree longitude
279+
['2020-10-17T12:30+00:00', 45, 180, None], # 180-degree longitude
280+
['1800-10-17T12:30+00:00', 45, 10, None], # 200 years in the past
281+
['2200-10-17T12:30+00:00', 45, 10, None], # 200 years ahead
282+
['2020-10-17T03:30+00:00', 45, 10, None], # negative sun elevation
283+
['2020-10-17T05:50+00:00', 45, 10, None], # ~1-deg sun elevation
284+
['2020-10-17T12:30+00:00', 45, 10, 0], # zero altitude
285+
['2020-10-17T12:30+00:00', 45, 10, -100], # negative altitude
286+
['2020-10-17T12:30+00:00', 45, 10, 4000], # positive altitude
287+
),
288+
)
289+
inputs = inputs.set_index('time')
290+
inputs.index = [pd.Timestamp(ii) for ii in inputs.index]
291+
return inputs
292+
293+
294+
def _generate_solarposition_dataframe(inputs, algorithm, **kwargs):
295+
"""
296+
Generate DataFrame with solar positions.
297+
298+
Each row is calculated separately due pd.DatetimeIndex being unable to
299+
handle different timezones.
300+
"""
301+
dfs = []
302+
for index, row in inputs.iterrows():
303+
try:
304+
dfi = algorithm(
305+
pd.DatetimeIndex([index]),
306+
row['latitude'],
307+
row['longitude'],
308+
**kwargs,
309+
)
310+
except ValueError:
311+
# Add empty row (nans)
312+
dfi = pd.DataFrame(index=[index])
313+
dfs.append(dfi)
314+
return pd.concat(dfs, axis='rows')
315+
316+
317+
@pytest.mark.parametrize('algorithm,expected,kwargs', [
318+
(iqbal, expected_iqbal, {}),
319+
# (michalsky, expected_michalsky_original_julian,
320+
# {'spencer_correction': True, 'julian_date': 'original'}),
321+
# (michalsky, expected_michalsky_correct_julian,
322+
# {'spencer_correction': False, 'julian_date': 'pandas'}),
323+
# (noaa, expected_noaa, {}),
324+
# (psa, expected_psa_2001, {'coefficients': 2001}),
325+
# (psa, expected_psa_2020, {'coefficients': 2020}),
326+
# (usno, expected_usno, {}),
327+
# (sg2, expected_sg2, {}),
328+
# (sg2_c.sg2, expected_sg2, {}),
329+
# (walraven, expected_walraven, {}),
330+
])
331+
def test_algorithm(algorithm, expected, kwargs, test_conditions):
332+
expected = expected().set_index(test_conditions.index)
333+
result = _generate_solarposition_dataframe(
334+
test_conditions, algorithm, **kwargs)
335+
result.name = algorithm.__module__
336+
337+
pd.testing.assert_index_equal(expected.index, result.index)
338+
rtol = 1e-3 if algorithm.__name__ == 'sg2' else 1e-6
339+
print(algorithm.__name__)
340+
pd.testing.assert_frame_equal(
341+
expected, result, check_like=False, rtol=rtol)
342+
for c in expected.columns:
343+
pd.testing.assert_series_equal(expected[c], result[c], rtol=rtol)

0 commit comments

Comments
 (0)