pvlib 0.11.0a1__py3-none-any.whl → 0.11.2__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.
- pvlib/_deprecation.py +73 -0
- pvlib/atmosphere.py +236 -1
- pvlib/bifacial/__init__.py +4 -4
- pvlib/bifacial/loss_models.py +163 -0
- pvlib/clearsky.py +53 -51
- pvlib/data/pvgis_tmy_meta.json +32 -93
- pvlib/data/pvgis_tmy_test.csv +8761 -0
- pvlib/data/tmy_45.000_8.000_2005_2023.csv +8789 -0
- pvlib/data/tmy_45.000_8.000_2005_2023.epw +8768 -0
- pvlib/data/tmy_45.000_8.000_2005_2023.json +1 -0
- pvlib/data/tmy_45.000_8.000_2005_2023.txt +8761 -0
- pvlib/data/tmy_45.000_8.000_userhorizon.json +1 -1
- pvlib/iam.py +4 -4
- pvlib/iotools/midc.py +1 -1
- pvlib/iotools/pvgis.py +39 -13
- pvlib/irradiance.py +237 -173
- pvlib/ivtools/sdm.py +75 -52
- pvlib/location.py +5 -5
- pvlib/modelchain.py +1 -1
- pvlib/pvsystem.py +134 -86
- pvlib/shading.py +8 -8
- pvlib/singlediode.py +1 -1
- pvlib/solarposition.py +101 -80
- pvlib/spa.py +28 -24
- pvlib/spectrum/__init__.py +9 -4
- pvlib/spectrum/irradiance.py +273 -0
- pvlib/spectrum/mismatch.py +118 -508
- pvlib/spectrum/response.py +280 -0
- pvlib/spectrum/spectrl2.py +18 -17
- pvlib/temperature.py +49 -3
- pvlib/tests/bifacial/test_losses_models.py +54 -0
- pvlib/tests/iotools/test_pvgis.py +58 -12
- pvlib/tests/ivtools/test_sdm.py +23 -1
- pvlib/tests/spectrum/__init__.py +0 -0
- pvlib/tests/spectrum/conftest.py +40 -0
- pvlib/tests/spectrum/test_irradiance.py +138 -0
- pvlib/tests/{test_spectrum.py → spectrum/test_mismatch.py} +32 -306
- pvlib/tests/spectrum/test_response.py +124 -0
- pvlib/tests/spectrum/test_spectrl2.py +72 -0
- pvlib/tests/test__deprecation.py +97 -0
- pvlib/tests/test_atmosphere.py +218 -0
- pvlib/tests/test_clearsky.py +44 -26
- pvlib/tests/test_conftest.py +0 -44
- pvlib/tests/test_irradiance.py +62 -16
- pvlib/tests/test_pvsystem.py +17 -1
- pvlib/tests/test_solarposition.py +117 -36
- pvlib/tests/test_spa.py +30 -1
- pvlib/tools.py +26 -2
- pvlib/tracking.py +53 -47
- {pvlib-0.11.0a1.dist-info → pvlib-0.11.2.dist-info}/METADATA +34 -31
- {pvlib-0.11.0a1.dist-info → pvlib-0.11.2.dist-info}/RECORD +55 -47
- {pvlib-0.11.0a1.dist-info → pvlib-0.11.2.dist-info}/WHEEL +1 -1
- pvlib/data/aod550_tcwv_20121101_test.nc +0 -0
- pvlib/data/pvgis_tmy_test.dat +0 -8761
- pvlib/data/tmy_45.000_8.000_2005_2016.csv +0 -8789
- pvlib/data/tmy_45.000_8.000_2005_2016.epw +0 -8768
- pvlib/data/tmy_45.000_8.000_2005_2016.json +0 -1
- pvlib/data/tmy_45.000_8.000_2005_2016.txt +0 -8761
- pvlib/data/variables_style_rules.csv +0 -55
- {pvlib-0.11.0a1.dist-info → pvlib-0.11.2.dist-info}/AUTHORS.md +0 -0
- {pvlib-0.11.0a1.dist-info → pvlib-0.11.2.dist-info}/LICENSE +0 -0
- {pvlib-0.11.0a1.dist-info → pvlib-0.11.2.dist-info}/top_level.txt +0 -0
pvlib/tests/test_conftest.py
CHANGED
|
@@ -1,50 +1,6 @@
|
|
|
1
1
|
import pytest
|
|
2
|
-
import pandas
|
|
3
2
|
|
|
4
3
|
from pvlib.tests import conftest
|
|
5
|
-
from pvlib.tests.conftest import fail_on_pvlib_version
|
|
6
|
-
|
|
7
|
-
from pvlib._deprecation import pvlibDeprecationWarning, deprecated
|
|
8
|
-
|
|
9
|
-
@pytest.mark.xfail(strict=True,
|
|
10
|
-
reason='fail_on_pvlib_version should cause test to fail')
|
|
11
|
-
@fail_on_pvlib_version('0.0')
|
|
12
|
-
def test_fail_on_pvlib_version():
|
|
13
|
-
pass
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@fail_on_pvlib_version('100000.0')
|
|
17
|
-
def test_fail_on_pvlib_version_pass():
|
|
18
|
-
pass
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
@pytest.mark.xfail(strict=True, reason='ensure that the test is called')
|
|
22
|
-
@fail_on_pvlib_version('100000.0')
|
|
23
|
-
def test_fail_on_pvlib_version_fail_in_test():
|
|
24
|
-
raise Exception
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
# set up to test using fixtures with function decorated with
|
|
28
|
-
# conftest.fail_on_pvlib_version
|
|
29
|
-
@pytest.fixture()
|
|
30
|
-
def some_data():
|
|
31
|
-
return "some data"
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def alt_func(*args):
|
|
35
|
-
return args
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
deprec_func = deprecated('350.8', alternative='alt_func',
|
|
39
|
-
name='deprec_func', removal='350.9')(alt_func)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
@fail_on_pvlib_version('350.9')
|
|
43
|
-
def test_use_fixture_with_decorator(some_data):
|
|
44
|
-
# test that the correct data is returned by the some_data fixture
|
|
45
|
-
assert some_data == "some data"
|
|
46
|
-
with pytest.warns(pvlibDeprecationWarning): # test for deprecation warning
|
|
47
|
-
deprec_func(some_data)
|
|
48
4
|
|
|
49
5
|
|
|
50
6
|
@pytest.mark.parametrize('function_name', ['assert_index_equal',
|
pvlib/tests/test_irradiance.py
CHANGED
|
@@ -15,7 +15,8 @@ from .conftest import (
|
|
|
15
15
|
assert_frame_equal,
|
|
16
16
|
assert_series_equal,
|
|
17
17
|
requires_ephem,
|
|
18
|
-
requires_numba
|
|
18
|
+
requires_numba,
|
|
19
|
+
fail_on_pvlib_version,
|
|
19
20
|
)
|
|
20
21
|
|
|
21
22
|
from pvlib._deprecation import pvlibDeprecationWarning
|
|
@@ -59,7 +60,7 @@ def ephem_data(times):
|
|
|
59
60
|
|
|
60
61
|
|
|
61
62
|
@pytest.fixture
|
|
62
|
-
def dni_et(
|
|
63
|
+
def dni_et():
|
|
63
64
|
return np.array(
|
|
64
65
|
[1321.1655834833093, 1321.1655834833093, 1321.1655834833093,
|
|
65
66
|
1321.1655834833093])
|
|
@@ -106,7 +107,7 @@ def test_get_extra_radiation_epoch_year():
|
|
|
106
107
|
@requires_numba
|
|
107
108
|
def test_get_extra_radiation_nrel_numba(times):
|
|
108
109
|
with warnings.catch_warnings():
|
|
109
|
-
# don't warn on method reload
|
|
110
|
+
# don't warn on method reload
|
|
110
111
|
warnings.simplefilter("ignore")
|
|
111
112
|
result = irradiance.get_extra_radiation(
|
|
112
113
|
times, method='nrel', how='numba', numthreads=4)
|
|
@@ -603,11 +604,11 @@ def test_poa_components(irrad_data, ephem_data, dni_et, relative_airmass):
|
|
|
603
604
|
|
|
604
605
|
@pytest.mark.parametrize('pressure,expected', [
|
|
605
606
|
(93193, [[830.46567, 0.79742, 0.93505],
|
|
606
|
-
[676.
|
|
607
|
+
[676.18340, 0.63782, 3.02102]]),
|
|
607
608
|
(None, [[868.72425, 0.79742, 1.01664],
|
|
608
|
-
[680.
|
|
609
|
+
[680.73800, 0.63782, 3.28463]]),
|
|
609
610
|
(101325, [[868.72425, 0.79742, 1.01664],
|
|
610
|
-
[680.
|
|
611
|
+
[680.73800, 0.63782, 3.28463]])
|
|
611
612
|
])
|
|
612
613
|
def test_disc_value(pressure, expected):
|
|
613
614
|
# see GH 449 for pressure=None vs. 101325.
|
|
@@ -1063,7 +1064,7 @@ def test_dirindex(times):
|
|
|
1063
1064
|
np.array([0., 79.73860422, 1042.48031487, 257.20751138]),
|
|
1064
1065
|
index=times
|
|
1065
1066
|
)
|
|
1066
|
-
|
|
1067
|
+
dni_clear = pd.Series(
|
|
1067
1068
|
np.array([0., 316.1949056, 939.95469881, 646.22886049]),
|
|
1068
1069
|
index=times
|
|
1069
1070
|
)
|
|
@@ -1073,14 +1074,14 @@ def test_dirindex(times):
|
|
|
1073
1074
|
)
|
|
1074
1075
|
pressure = 93193.
|
|
1075
1076
|
tdew = 10.
|
|
1076
|
-
out = irradiance.dirindex(ghi, ghi_clearsky,
|
|
1077
|
+
out = irradiance.dirindex(ghi, ghi_clearsky, dni_clear,
|
|
1077
1078
|
zenith, times, pressure=pressure,
|
|
1078
1079
|
temp_dew=tdew)
|
|
1079
1080
|
dirint_close_values = irradiance.dirint(ghi, zenith, times,
|
|
1080
1081
|
pressure=pressure,
|
|
1081
1082
|
use_delta_kt_prime=True,
|
|
1082
1083
|
temp_dew=tdew).values
|
|
1083
|
-
expected_out = np.array([np.nan, 0., 748.
|
|
1084
|
+
expected_out = np.array([np.nan, 0., 748.31562800, 630.73752100])
|
|
1084
1085
|
|
|
1085
1086
|
tolerance = 1e-8
|
|
1086
1087
|
assert np.allclose(out, expected_out, rtol=tolerance, atol=0,
|
|
@@ -1094,6 +1095,20 @@ def test_dirindex(times):
|
|
|
1094
1095
|
equal_nan=True)
|
|
1095
1096
|
|
|
1096
1097
|
|
|
1098
|
+
@fail_on_pvlib_version("0.13")
|
|
1099
|
+
def test_dirindex_ghi_clearsky_deprecation():
|
|
1100
|
+
times = pd.DatetimeIndex(['2014-06-24T18-1200'])
|
|
1101
|
+
ghi = pd.Series([1038.62], index=times)
|
|
1102
|
+
ghi_clearsky = pd.Series([1042.48031487], index=times)
|
|
1103
|
+
dni_clearsky = pd.Series([939.95469881], index=times)
|
|
1104
|
+
zenith = pd.Series([10.56413562], index=times)
|
|
1105
|
+
pressure, tdew = 93193, 10
|
|
1106
|
+
with pytest.warns(pvlibDeprecationWarning, match='ghi_clear'):
|
|
1107
|
+
irradiance.dirindex(
|
|
1108
|
+
ghi=ghi, ghi_clearsky=ghi_clearsky, dni_clear=dni_clearsky,
|
|
1109
|
+
zenith=zenith, times=times, pressure=pressure, temp_dew=tdew)
|
|
1110
|
+
|
|
1111
|
+
|
|
1097
1112
|
def test_dirindex_min_cos_zenith_max_zenith():
|
|
1098
1113
|
# map out behavior under difficult conditions with various
|
|
1099
1114
|
# limiting kwargs settings
|
|
@@ -1101,38 +1116,51 @@ def test_dirindex_min_cos_zenith_max_zenith():
|
|
|
1101
1116
|
times = pd.DatetimeIndex(['2014-06-24T12-0700', '2014-06-24T18-0700'])
|
|
1102
1117
|
ghi = pd.Series([0, 1], index=times)
|
|
1103
1118
|
ghi_clearsky = pd.Series([0, 1], index=times)
|
|
1104
|
-
|
|
1119
|
+
dni_clear = pd.Series([0, 5], index=times)
|
|
1105
1120
|
solar_zenith = pd.Series([90, 89.99], index=times)
|
|
1106
1121
|
|
|
1107
|
-
out = irradiance.dirindex(ghi, ghi_clearsky,
|
|
1122
|
+
out = irradiance.dirindex(ghi, ghi_clearsky, dni_clear, solar_zenith,
|
|
1108
1123
|
times)
|
|
1109
1124
|
expected = pd.Series([nan, nan], index=times)
|
|
1110
1125
|
assert_series_equal(out, expected)
|
|
1111
1126
|
|
|
1112
|
-
out = irradiance.dirindex(ghi, ghi_clearsky,
|
|
1127
|
+
out = irradiance.dirindex(ghi, ghi_clearsky, dni_clear, solar_zenith,
|
|
1113
1128
|
times, min_cos_zenith=0)
|
|
1114
1129
|
expected = pd.Series([nan, nan], index=times)
|
|
1115
1130
|
assert_series_equal(out, expected)
|
|
1116
1131
|
|
|
1117
|
-
out = irradiance.dirindex(ghi, ghi_clearsky,
|
|
1132
|
+
out = irradiance.dirindex(ghi, ghi_clearsky, dni_clear, solar_zenith,
|
|
1118
1133
|
times, max_zenith=90)
|
|
1119
1134
|
expected = pd.Series([nan, nan], index=times)
|
|
1120
1135
|
assert_series_equal(out, expected)
|
|
1121
1136
|
|
|
1122
|
-
out = irradiance.dirindex(ghi, ghi_clearsky,
|
|
1137
|
+
out = irradiance.dirindex(ghi, ghi_clearsky, dni_clear, solar_zenith,
|
|
1123
1138
|
times, min_cos_zenith=0, max_zenith=100)
|
|
1124
1139
|
expected = pd.Series([nan, 5.], index=times)
|
|
1125
1140
|
assert_series_equal(out, expected)
|
|
1126
1141
|
|
|
1127
1142
|
|
|
1143
|
+
@fail_on_pvlib_version("0.13")
|
|
1144
|
+
def test_dirindex_dni_clearsky_deprecation():
|
|
1145
|
+
times = pd.DatetimeIndex(['2014-06-24T12-0700', '2014-06-24T18-0700'])
|
|
1146
|
+
ghi = pd.Series([0, 1], index=times)
|
|
1147
|
+
ghi_clearsky = pd.Series([0, 1], index=times)
|
|
1148
|
+
dni_clear = pd.Series([0, 5], index=times)
|
|
1149
|
+
solar_zenith = pd.Series([90, 89.99], index=times)
|
|
1150
|
+
with pytest.warns(pvlibDeprecationWarning, match='dni_clear'):
|
|
1151
|
+
irradiance.dirindex(ghi, ghi_clearsky, dni_clearsky=dni_clear,
|
|
1152
|
+
zenith=solar_zenith, times=times,
|
|
1153
|
+
min_cos_zenith=0)
|
|
1154
|
+
|
|
1155
|
+
|
|
1128
1156
|
def test_dni():
|
|
1129
1157
|
ghi = pd.Series([90, 100, 100, 100, 100])
|
|
1130
1158
|
dhi = pd.Series([100, 90, 50, 50, 50])
|
|
1131
1159
|
zenith = pd.Series([80, 100, 85, 70, 85])
|
|
1132
|
-
|
|
1160
|
+
dni_clear = pd.Series([50, 50, 200, 50, 300])
|
|
1133
1161
|
|
|
1134
1162
|
dni = irradiance.dni(ghi, dhi, zenith,
|
|
1135
|
-
|
|
1163
|
+
dni_clear=dni_clear, clearsky_tolerance=2)
|
|
1136
1164
|
assert_series_equal(dni,
|
|
1137
1165
|
pd.Series([float('nan'), float('nan'), 400,
|
|
1138
1166
|
146.190220008, 573.685662283]))
|
|
@@ -1143,6 +1171,17 @@ def test_dni():
|
|
|
1143
1171
|
146.190220008, 573.685662283]))
|
|
1144
1172
|
|
|
1145
1173
|
|
|
1174
|
+
@fail_on_pvlib_version("0.13")
|
|
1175
|
+
def test_dni_dni_clearsky_deprecation():
|
|
1176
|
+
ghi = pd.Series([90, 100, 100, 100, 100])
|
|
1177
|
+
dhi = pd.Series([100, 90, 50, 50, 50])
|
|
1178
|
+
zenith = pd.Series([80, 100, 85, 70, 85])
|
|
1179
|
+
dni_clear = pd.Series([50, 50, 200, 50, 300])
|
|
1180
|
+
with pytest.warns(pvlibDeprecationWarning, match='dni_clear'):
|
|
1181
|
+
irradiance.dni(ghi, dhi, zenith,
|
|
1182
|
+
clearsky_dni=dni_clear, clearsky_tolerance=2)
|
|
1183
|
+
|
|
1184
|
+
|
|
1146
1185
|
@pytest.mark.parametrize(
|
|
1147
1186
|
'surface_tilt,surface_azimuth,solar_zenith,' +
|
|
1148
1187
|
'solar_azimuth,aoi_expected,aoi_proj_expected',
|
|
@@ -1235,6 +1274,13 @@ def test_clearsky_index():
|
|
|
1235
1274
|
assert_series_equal(out, expected)
|
|
1236
1275
|
|
|
1237
1276
|
|
|
1277
|
+
@fail_on_pvlib_version("0.13")
|
|
1278
|
+
def test_clearsky_index_clearsky_ghi_deprecation():
|
|
1279
|
+
with pytest.warns(pvlibDeprecationWarning, match='ghi_clear'):
|
|
1280
|
+
ghi, clearsky_ghi = 200, 300
|
|
1281
|
+
irradiance.clearsky_index(ghi, clearsky_ghi=clearsky_ghi)
|
|
1282
|
+
|
|
1283
|
+
|
|
1238
1284
|
def test_clearness_index():
|
|
1239
1285
|
ghi = np.array([-1, 0, 1, 1000])
|
|
1240
1286
|
solar_zenith = np.array([180, 90, 89.999, 0])
|
pvlib/tests/test_pvsystem.py
CHANGED
|
@@ -1870,7 +1870,6 @@ def test_PVSystem_get_irradiance(solar_pos):
|
|
|
1870
1870
|
irrads['dni'],
|
|
1871
1871
|
irrads['ghi'],
|
|
1872
1872
|
irrads['dhi'])
|
|
1873
|
-
|
|
1874
1873
|
expected = pd.DataFrame(data=np.array(
|
|
1875
1874
|
[[883.65494055, 745.86141676, 137.79352379, 126.397131, 11.39639279],
|
|
1876
1875
|
[0., -0., 0., 0., 0.]]),
|
|
@@ -1881,6 +1880,23 @@ def test_PVSystem_get_irradiance(solar_pos):
|
|
|
1881
1880
|
assert_frame_equal(irradiance, expected, check_less_precise=2)
|
|
1882
1881
|
|
|
1883
1882
|
|
|
1883
|
+
def test_PVSystem_get_irradiance_float():
|
|
1884
|
+
system = pvsystem.PVSystem(surface_tilt=32, surface_azimuth=135)
|
|
1885
|
+
irrads = {'dni': 900., 'ghi': 600., 'dhi': 100.}
|
|
1886
|
+
zenith = 55.366831
|
|
1887
|
+
azimuth = 172.320038
|
|
1888
|
+
irradiance = system.get_irradiance(zenith,
|
|
1889
|
+
azimuth,
|
|
1890
|
+
irrads['dni'],
|
|
1891
|
+
irrads['ghi'],
|
|
1892
|
+
irrads['dhi'])
|
|
1893
|
+
expected = {'poa_global': 884.80903423, 'poa_direct': 745.84258835,
|
|
1894
|
+
'poa_diffuse': 138.96644588, 'poa_sky_diffuse': 127.57005309,
|
|
1895
|
+
'poa_ground_diffuse': 11.39639279}
|
|
1896
|
+
for k, v in irradiance.items():
|
|
1897
|
+
assert np.isclose(v, expected[k], rtol=1e-6)
|
|
1898
|
+
|
|
1899
|
+
|
|
1884
1900
|
def test_PVSystem_get_irradiance_albedo(solar_pos):
|
|
1885
1901
|
system = pvsystem.PVSystem(surface_tilt=32, surface_azimuth=135)
|
|
1886
1902
|
irrads = pd.DataFrame({'dni': [900, 0], 'ghi': [600, 0], 'dhi': [100, 0],
|
|
@@ -8,6 +8,7 @@ import pandas as pd
|
|
|
8
8
|
from .conftest import assert_frame_equal, assert_series_equal
|
|
9
9
|
from numpy.testing import assert_allclose
|
|
10
10
|
import pytest
|
|
11
|
+
import pytz
|
|
11
12
|
|
|
12
13
|
from pvlib.location import Location
|
|
13
14
|
from pvlib import solarposition, spa
|
|
@@ -139,7 +140,8 @@ def test_spa_python_numpy_physical_dst(expected_solpos, golden):
|
|
|
139
140
|
assert_frame_equal(expected_solpos, ephem_data[expected_solpos.columns])
|
|
140
141
|
|
|
141
142
|
|
|
142
|
-
|
|
143
|
+
@pytest.mark.parametrize('delta_t', [65.0, None, np.array([65, 65])])
|
|
144
|
+
def test_sun_rise_set_transit_spa(expected_rise_set_spa, golden, delta_t):
|
|
143
145
|
# solution from NREL SAP web calculator
|
|
144
146
|
south = Location(-35.0, 0.0, tz='UTC')
|
|
145
147
|
times = pd.DatetimeIndex([datetime.datetime(1996, 7, 5, 0),
|
|
@@ -160,7 +162,7 @@ def test_sun_rise_set_transit_spa(expected_rise_set_spa, golden):
|
|
|
160
162
|
|
|
161
163
|
result = solarposition.sun_rise_set_transit_spa(times, south.latitude,
|
|
162
164
|
south.longitude,
|
|
163
|
-
delta_t=
|
|
165
|
+
delta_t=delta_t)
|
|
164
166
|
result_rounded = pd.DataFrame(index=result.index)
|
|
165
167
|
# need to iterate because to_datetime does not accept 2D data
|
|
166
168
|
# the rounding fails on pandas < 0.17
|
|
@@ -172,7 +174,7 @@ def test_sun_rise_set_transit_spa(expected_rise_set_spa, golden):
|
|
|
172
174
|
# test for Golden, CO compare to NREL SPA
|
|
173
175
|
result = solarposition.sun_rise_set_transit_spa(
|
|
174
176
|
expected_rise_set_spa.index, golden.latitude, golden.longitude,
|
|
175
|
-
delta_t=
|
|
177
|
+
delta_t=delta_t)
|
|
176
178
|
|
|
177
179
|
# round to nearest minute
|
|
178
180
|
result_rounded = pd.DataFrame(index=result.index)
|
|
@@ -477,20 +479,20 @@ def test_get_solarposition_altitude(
|
|
|
477
479
|
|
|
478
480
|
|
|
479
481
|
@pytest.mark.parametrize("delta_t, method", [
|
|
480
|
-
(None, '
|
|
481
|
-
pytest.param(
|
|
482
|
-
None, 'nrel_numba',
|
|
483
|
-
marks=[pytest.mark.xfail(
|
|
484
|
-
reason='spa.calculate_deltat not implemented for numba yet')]),
|
|
482
|
+
(None, 'nrel_numba'),
|
|
485
483
|
(67.0, 'nrel_numba'),
|
|
484
|
+
(np.array([67.0, 67.0]), 'nrel_numba'),
|
|
485
|
+
# minimize reloads, with numpy being last
|
|
486
|
+
(None, 'nrel_numpy'),
|
|
486
487
|
(67.0, 'nrel_numpy'),
|
|
487
|
-
])
|
|
488
|
+
(np.array([67.0, 67.0]), 'nrel_numpy'),
|
|
489
|
+
])
|
|
488
490
|
def test_get_solarposition_deltat(delta_t, method, expected_solpos_multi,
|
|
489
491
|
golden):
|
|
490
492
|
times = pd.date_range(datetime.datetime(2003, 10, 17, 13, 30, 30),
|
|
491
493
|
periods=2, freq='D', tz=golden.tz)
|
|
492
494
|
with warnings.catch_warnings():
|
|
493
|
-
# don't warn on method reload
|
|
495
|
+
# don't warn on method reload
|
|
494
496
|
warnings.simplefilter("ignore")
|
|
495
497
|
ephem_data = solarposition.get_solarposition(times, golden.latitude,
|
|
496
498
|
golden.longitude,
|
|
@@ -505,6 +507,21 @@ def test_get_solarposition_deltat(delta_t, method, expected_solpos_multi,
|
|
|
505
507
|
assert_frame_equal(this_expected, ephem_data[this_expected.columns])
|
|
506
508
|
|
|
507
509
|
|
|
510
|
+
@pytest.mark.parametrize("method", ['nrel_numba', 'nrel_numpy'])
|
|
511
|
+
def test_spa_array_delta_t(method):
|
|
512
|
+
# make sure that time-varying delta_t produces different answers
|
|
513
|
+
times = pd.to_datetime(["2019-01-01", "2019-01-01"]).tz_localize("UTC")
|
|
514
|
+
expected = pd.Series([257.26969492, 257.2701359], index=times)
|
|
515
|
+
with warnings.catch_warnings():
|
|
516
|
+
# don't warn on method reload
|
|
517
|
+
warnings.simplefilter("ignore")
|
|
518
|
+
ephem_data = solarposition.get_solarposition(times, 40, -80,
|
|
519
|
+
delta_t=np.array([67, 0]),
|
|
520
|
+
method=method)
|
|
521
|
+
|
|
522
|
+
assert_series_equal(ephem_data['azimuth'], expected, check_names=False)
|
|
523
|
+
|
|
524
|
+
|
|
508
525
|
def test_get_solarposition_no_kwargs(expected_solpos, golden):
|
|
509
526
|
times = pd.date_range(datetime.datetime(2003, 10, 17, 13, 30, 30),
|
|
510
527
|
periods=1, freq='D', tz=golden.tz)
|
|
@@ -529,20 +546,22 @@ def test_get_solarposition_method_pyephem(expected_solpos, golden):
|
|
|
529
546
|
assert_frame_equal(expected_solpos, ephem_data[expected_solpos.columns])
|
|
530
547
|
|
|
531
548
|
|
|
532
|
-
|
|
549
|
+
@pytest.mark.parametrize('delta_t', [64.0, None, np.array([64, 64])])
|
|
550
|
+
def test_nrel_earthsun_distance(delta_t):
|
|
533
551
|
times = pd.DatetimeIndex([datetime.datetime(2015, 1, 2),
|
|
534
552
|
datetime.datetime(2015, 8, 2)]
|
|
535
553
|
).tz_localize('MST')
|
|
536
|
-
result = solarposition.nrel_earthsun_distance(times, delta_t=
|
|
554
|
+
result = solarposition.nrel_earthsun_distance(times, delta_t=delta_t)
|
|
537
555
|
expected = pd.Series(np.array([0.983289204601, 1.01486146446]),
|
|
538
556
|
index=times)
|
|
539
557
|
assert_series_equal(expected, result)
|
|
540
558
|
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
559
|
+
if np.size(delta_t) == 1: # skip the array delta_t
|
|
560
|
+
times = datetime.datetime(2015, 1, 2)
|
|
561
|
+
result = solarposition.nrel_earthsun_distance(times, delta_t=delta_t)
|
|
562
|
+
expected = pd.Series(np.array([0.983289204601]),
|
|
563
|
+
index=pd.DatetimeIndex([times, ]))
|
|
564
|
+
assert_series_equal(expected, result)
|
|
546
565
|
|
|
547
566
|
|
|
548
567
|
def test_equation_of_time():
|
|
@@ -579,19 +598,20 @@ def test_declination():
|
|
|
579
598
|
def test_analytical_zenith():
|
|
580
599
|
times = pd.date_range(start="1/1/2015 0:00", end="12/31/2015 23:00",
|
|
581
600
|
freq="h").tz_localize('Etc/GMT+8')
|
|
601
|
+
times_utc = times.tz_convert('UTC')
|
|
582
602
|
lat, lon = 37.8, -122.25
|
|
583
603
|
lat_rad = np.deg2rad(lat)
|
|
584
604
|
output = solarposition.spa_python(times, lat, lon, 100)
|
|
585
605
|
solar_zenith = np.deg2rad(output['zenith']) # spa
|
|
586
606
|
# spencer
|
|
587
|
-
eot = solarposition.equation_of_time_spencer71(
|
|
607
|
+
eot = solarposition.equation_of_time_spencer71(times_utc.dayofyear)
|
|
588
608
|
hour_angle = np.deg2rad(solarposition.hour_angle(times, lon, eot))
|
|
589
|
-
decl = solarposition.declination_spencer71(
|
|
609
|
+
decl = solarposition.declination_spencer71(times_utc.dayofyear)
|
|
590
610
|
zenith_1 = solarposition.solar_zenith_analytical(lat_rad, hour_angle, decl)
|
|
591
611
|
# pvcdrom and cooper
|
|
592
|
-
eot = solarposition.equation_of_time_pvcdrom(
|
|
612
|
+
eot = solarposition.equation_of_time_pvcdrom(times_utc.dayofyear)
|
|
593
613
|
hour_angle = np.deg2rad(solarposition.hour_angle(times, lon, eot))
|
|
594
|
-
decl = solarposition.declination_cooper69(
|
|
614
|
+
decl = solarposition.declination_cooper69(times_utc.dayofyear)
|
|
595
615
|
zenith_2 = solarposition.solar_zenith_analytical(lat_rad, hour_angle, decl)
|
|
596
616
|
assert np.allclose(zenith_1, solar_zenith, atol=0.015)
|
|
597
617
|
assert np.allclose(zenith_2, solar_zenith, atol=0.025)
|
|
@@ -600,22 +620,23 @@ def test_analytical_zenith():
|
|
|
600
620
|
def test_analytical_azimuth():
|
|
601
621
|
times = pd.date_range(start="1/1/2015 0:00", end="12/31/2015 23:00",
|
|
602
622
|
freq="h").tz_localize('Etc/GMT+8')
|
|
623
|
+
times_utc = times.tz_convert('UTC')
|
|
603
624
|
lat, lon = 37.8, -122.25
|
|
604
625
|
lat_rad = np.deg2rad(lat)
|
|
605
626
|
output = solarposition.spa_python(times, lat, lon, 100)
|
|
606
627
|
solar_azimuth = np.deg2rad(output['azimuth']) # spa
|
|
607
628
|
solar_zenith = np.deg2rad(output['zenith'])
|
|
608
629
|
# spencer
|
|
609
|
-
eot = solarposition.equation_of_time_spencer71(
|
|
630
|
+
eot = solarposition.equation_of_time_spencer71(times_utc.dayofyear)
|
|
610
631
|
hour_angle = np.deg2rad(solarposition.hour_angle(times, lon, eot))
|
|
611
|
-
decl = solarposition.declination_spencer71(
|
|
632
|
+
decl = solarposition.declination_spencer71(times_utc.dayofyear)
|
|
612
633
|
zenith = solarposition.solar_zenith_analytical(lat_rad, hour_angle, decl)
|
|
613
634
|
azimuth_1 = solarposition.solar_azimuth_analytical(lat_rad, hour_angle,
|
|
614
635
|
decl, zenith)
|
|
615
636
|
# pvcdrom and cooper
|
|
616
|
-
eot = solarposition.equation_of_time_pvcdrom(
|
|
637
|
+
eot = solarposition.equation_of_time_pvcdrom(times_utc.dayofyear)
|
|
617
638
|
hour_angle = np.deg2rad(solarposition.hour_angle(times, lon, eot))
|
|
618
|
-
decl = solarposition.declination_cooper69(
|
|
639
|
+
decl = solarposition.declination_cooper69(times_utc.dayofyear)
|
|
619
640
|
zenith = solarposition.solar_zenith_analytical(lat_rad, hour_angle, decl)
|
|
620
641
|
azimuth_2 = solarposition.solar_azimuth_analytical(lat_rad, hour_angle,
|
|
621
642
|
decl, zenith)
|
|
@@ -665,21 +686,77 @@ def test_hour_angle():
|
|
|
665
686
|
'2015-01-02 12:04:44.6340'
|
|
666
687
|
]).tz_localize('Etc/GMT+7')
|
|
667
688
|
eot = np.array([-3.935172, -4.117227, -4.026295])
|
|
668
|
-
|
|
689
|
+
hourangle = solarposition.hour_angle(times, longitude, eot)
|
|
669
690
|
expected = (-70.682338, 70.72118825000001, 0.000801250)
|
|
670
691
|
# FIXME: there are differences from expected NREL SPA calculator values
|
|
671
692
|
# sunrise: 4 seconds, sunset: 48 seconds, transit: 0.2 seconds
|
|
672
693
|
# but the differences may be due to other SPA input parameters
|
|
673
|
-
assert np.allclose(
|
|
694
|
+
assert np.allclose(hourangle, expected)
|
|
695
|
+
|
|
696
|
+
hours = solarposition._hour_angle_to_hours(
|
|
697
|
+
times, hourangle, longitude, eot)
|
|
698
|
+
result = solarposition._times_to_hours_after_local_midnight(times)
|
|
699
|
+
assert np.allclose(result, hours)
|
|
700
|
+
|
|
701
|
+
result = solarposition._local_times_from_hours_since_midnight(times, hours)
|
|
702
|
+
assert result.equals(times)
|
|
703
|
+
|
|
704
|
+
times = times.tz_convert(None)
|
|
705
|
+
with pytest.raises(ValueError):
|
|
706
|
+
solarposition.hour_angle(times, longitude, eot)
|
|
707
|
+
with pytest.raises(ValueError):
|
|
708
|
+
solarposition._hour_angle_to_hours(times, hourangle, longitude, eot)
|
|
709
|
+
with pytest.raises(ValueError):
|
|
710
|
+
solarposition._times_to_hours_after_local_midnight(times)
|
|
711
|
+
with pytest.raises(ValueError):
|
|
712
|
+
solarposition._local_times_from_hours_since_midnight(times, hours)
|
|
713
|
+
|
|
714
|
+
|
|
715
|
+
def test_hour_angle_with_tricky_timezones():
|
|
716
|
+
# GH 2132
|
|
717
|
+
# tests timezones that have a DST shift at midnight
|
|
718
|
+
|
|
719
|
+
eot = np.array([-3.935172, -4.117227, -4.026295, -4.026295])
|
|
720
|
+
|
|
721
|
+
longitude = 70.6693
|
|
722
|
+
times = pd.DatetimeIndex([
|
|
723
|
+
'2014-09-06 23:00:00',
|
|
724
|
+
'2014-09-07 00:00:00',
|
|
725
|
+
'2014-09-07 01:00:00',
|
|
726
|
+
'2014-09-07 02:00:00',
|
|
727
|
+
]).tz_localize('America/Santiago', nonexistent='shift_forward')
|
|
728
|
+
|
|
729
|
+
with pytest.raises(pytz.exceptions.NonExistentTimeError):
|
|
730
|
+
times.normalize()
|
|
731
|
+
|
|
732
|
+
# should not raise `pytz.exceptions.NonExistentTimeError`
|
|
733
|
+
solarposition.hour_angle(times, longitude, eot)
|
|
734
|
+
|
|
735
|
+
longitude = 82.3666
|
|
736
|
+
times = pd.DatetimeIndex([
|
|
737
|
+
'2014-11-01 23:00:00',
|
|
738
|
+
'2014-11-02 00:00:00',
|
|
739
|
+
'2014-11-02 01:00:00',
|
|
740
|
+
'2014-11-02 02:00:00',
|
|
741
|
+
]).tz_localize('America/Havana', ambiguous=[True, True, False, False])
|
|
742
|
+
|
|
743
|
+
with pytest.raises(pytz.exceptions.AmbiguousTimeError):
|
|
744
|
+
solarposition.hour_angle(times, longitude, eot)
|
|
674
745
|
|
|
675
746
|
|
|
676
747
|
def test_sun_rise_set_transit_geometric(expected_rise_set_spa, golden_mst):
|
|
677
748
|
"""Test geometric calculations for sunrise, sunset, and transit times"""
|
|
678
749
|
times = expected_rise_set_spa.index
|
|
750
|
+
times_utc = times.tz_convert('UTC')
|
|
679
751
|
latitude = golden_mst.latitude
|
|
680
752
|
longitude = golden_mst.longitude
|
|
681
|
-
eot = solarposition.equation_of_time_spencer71(
|
|
682
|
-
|
|
753
|
+
eot = solarposition.equation_of_time_spencer71(
|
|
754
|
+
times_utc.dayofyear) # minutes
|
|
755
|
+
decl = solarposition.declination_spencer71(times_utc.dayofyear) # radians
|
|
756
|
+
with pytest.raises(ValueError):
|
|
757
|
+
solarposition.sun_rise_set_transit_geometric(
|
|
758
|
+
times.tz_convert(None), latitude=latitude, longitude=longitude,
|
|
759
|
+
declination=decl, equation_of_time=eot)
|
|
683
760
|
sr, ss, st = solarposition.sun_rise_set_transit_geometric(
|
|
684
761
|
times, latitude=latitude, longitude=longitude, declination=decl,
|
|
685
762
|
equation_of_time=eot)
|
|
@@ -742,6 +819,7 @@ def test__datetime_to_unixtime_units(unit, tz):
|
|
|
742
819
|
|
|
743
820
|
|
|
744
821
|
@requires_pandas_2_0
|
|
822
|
+
@pytest.mark.parametrize('tz', [None, 'utc', 'US/Eastern'])
|
|
745
823
|
@pytest.mark.parametrize('method', [
|
|
746
824
|
'nrel_numpy',
|
|
747
825
|
'ephemeris',
|
|
@@ -749,7 +827,6 @@ def test__datetime_to_unixtime_units(unit, tz):
|
|
|
749
827
|
pytest.param('nrel_numba', marks=requires_numba),
|
|
750
828
|
pytest.param('nrel_c', marks=requires_spa_c),
|
|
751
829
|
])
|
|
752
|
-
@pytest.mark.parametrize('tz', [None, 'utc', 'US/Eastern'])
|
|
753
830
|
def test_get_solarposition_microsecond_index(method, tz):
|
|
754
831
|
# https://github.com/pvlib/pvlib-python/issues/1932
|
|
755
832
|
|
|
@@ -758,8 +835,12 @@ def test_get_solarposition_microsecond_index(method, tz):
|
|
|
758
835
|
index_ns = pd.date_range(unit='ns', **kwargs)
|
|
759
836
|
index_us = pd.date_range(unit='us', **kwargs)
|
|
760
837
|
|
|
761
|
-
|
|
762
|
-
|
|
838
|
+
with warnings.catch_warnings():
|
|
839
|
+
# don't warn on method reload
|
|
840
|
+
warnings.simplefilter("ignore")
|
|
841
|
+
|
|
842
|
+
sp_ns = solarposition.get_solarposition(index_ns, 0, 0, method=method)
|
|
843
|
+
sp_us = solarposition.get_solarposition(index_us, 0, 0, method=method)
|
|
763
844
|
|
|
764
845
|
assert_frame_equal(sp_ns, sp_us, check_index_type=False)
|
|
765
846
|
|
|
@@ -781,7 +862,7 @@ def test_nrel_earthsun_distance_microsecond_index(tz):
|
|
|
781
862
|
|
|
782
863
|
|
|
783
864
|
@requires_pandas_2_0
|
|
784
|
-
@pytest.mark.parametrize('tz', [
|
|
865
|
+
@pytest.mark.parametrize('tz', ['utc', 'US/Eastern'])
|
|
785
866
|
def test_hour_angle_microsecond_index(tz):
|
|
786
867
|
# https://github.com/pvlib/pvlib-python/issues/1932
|
|
787
868
|
|
|
@@ -813,7 +894,7 @@ def test_rise_set_transit_spa_microsecond_index(tz):
|
|
|
813
894
|
|
|
814
895
|
|
|
815
896
|
@requires_pandas_2_0
|
|
816
|
-
@pytest.mark.parametrize('tz', [
|
|
897
|
+
@pytest.mark.parametrize('tz', ['utc', 'US/Eastern'])
|
|
817
898
|
def test_rise_set_transit_geometric_microsecond_index(tz):
|
|
818
899
|
# https://github.com/pvlib/pvlib-python/issues/1932
|
|
819
900
|
|
|
@@ -838,7 +919,7 @@ def test_spa_python_numba_physical(expected_solpos, golden_mst):
|
|
|
838
919
|
times = pd.date_range(datetime.datetime(2003, 10, 17, 12, 30, 30),
|
|
839
920
|
periods=1, freq='D', tz=golden_mst.tz)
|
|
840
921
|
with warnings.catch_warnings():
|
|
841
|
-
# don't warn on method reload
|
|
922
|
+
# don't warn on method reload
|
|
842
923
|
# ensure that numpy is the most recently used method so that
|
|
843
924
|
# we can use the warns filter below
|
|
844
925
|
warnings.simplefilter("ignore")
|
|
@@ -865,7 +946,7 @@ def test_spa_python_numba_physical_dst(expected_solpos, golden):
|
|
|
865
946
|
periods=1, freq='D', tz=golden.tz)
|
|
866
947
|
|
|
867
948
|
with warnings.catch_warnings():
|
|
868
|
-
# don't warn on method reload
|
|
949
|
+
# don't warn on method reload
|
|
869
950
|
warnings.simplefilter("ignore")
|
|
870
951
|
ephem_data = solarposition.spa_python(times, golden.latitude,
|
|
871
952
|
golden.longitude, pressure=82000,
|
pvlib/tests/test_spa.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import datetime as dt
|
|
3
3
|
import warnings
|
|
4
|
+
import pytest
|
|
5
|
+
import pvlib
|
|
4
6
|
|
|
5
7
|
try:
|
|
6
8
|
from importlib import reload
|
|
@@ -234,7 +236,7 @@ class SpaBase:
|
|
|
234
236
|
|
|
235
237
|
def test_solar_position(self):
|
|
236
238
|
with warnings.catch_warnings():
|
|
237
|
-
# don't warn on method reload
|
|
239
|
+
# don't warn on method reload
|
|
238
240
|
warnings.simplefilter("ignore")
|
|
239
241
|
spa_out_0 = self.spa.solar_position(
|
|
240
242
|
unixtimes, lat, lon, elev, pressure, temp, delta_t,
|
|
@@ -423,3 +425,30 @@ class NumbaSpaTest(unittest.TestCase, SpaBase):
|
|
|
423
425
|
nresult, self.spa.solar_position(
|
|
424
426
|
times, lat, lon, elev, pressure, temp, delta_t,
|
|
425
427
|
atmos_refract, numthreads=3, sst=True)[:3], 5)
|
|
428
|
+
|
|
429
|
+
|
|
430
|
+
# Define extra test cases for issue #2077
|
|
431
|
+
test_cases_issue_2207 = [
|
|
432
|
+
((2000, 1, 1, 12, 0, 0), 2451545.0),
|
|
433
|
+
((1999, 1, 1, 0, 0, 0), 2451179.5),
|
|
434
|
+
((1987, 1, 27, 0, 0, 0), 2446822.5),
|
|
435
|
+
((1987, 6, 19, 12, 0, 0), 2446966.0),
|
|
436
|
+
((1988, 1, 27, 0, 0, 0), 2447187.5),
|
|
437
|
+
((1988, 6, 19, 12, 0, 0), 2447332.0),
|
|
438
|
+
((1900, 1, 1, 0, 0, 0), 2415020.5),
|
|
439
|
+
((1600, 1, 1, 0, 0, 0), 2305447.5),
|
|
440
|
+
((1600, 12, 31, 0, 0, 0), 2305812.5),
|
|
441
|
+
((837, 4, 10, 7, 12, 0), 2026871.8),
|
|
442
|
+
((-123, 12, 31, 0, 0, 0), 1676496.5),
|
|
443
|
+
((-122, 1, 1, 0, 0, 0), 1676497.5),
|
|
444
|
+
((-1000, 7, 12, 12, 0, 0), 1356001.0),
|
|
445
|
+
((-1000, 2, 29, 0, 0, 0), 1355866.5),
|
|
446
|
+
((-1001, 8, 17, 21, 36, 0), 1355671.4),
|
|
447
|
+
((-4712, 1, 1, 12, 0, 0), 0.0),
|
|
448
|
+
]
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
@pytest.mark.parametrize("inputs, expected", test_cases_issue_2207)
|
|
452
|
+
def test_julian_day_issue_2207(inputs, expected):
|
|
453
|
+
result = pvlib.spa.julian_day_dt(*inputs, microsecond=0)
|
|
454
|
+
assert result == expected, f"Failed for inputs {inputs}"
|
pvlib/tools.py
CHANGED
|
@@ -206,8 +206,32 @@ def _pandas_to_doy(pd_object):
|
|
|
206
206
|
Returns
|
|
207
207
|
-------
|
|
208
208
|
dayofyear
|
|
209
|
+
|
|
210
|
+
Notes
|
|
211
|
+
-----
|
|
212
|
+
Day of year is determined using UTC, since pandas uses local hour
|
|
213
|
+
"""
|
|
214
|
+
return _pandas_to_utc(pd_object).dayofyear
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def _pandas_to_utc(pd_object):
|
|
209
218
|
"""
|
|
210
|
-
|
|
219
|
+
Converts a pandas datetime-like object to UTC, if localized.
|
|
220
|
+
Otherwise, assume UTC.
|
|
221
|
+
|
|
222
|
+
Parameters
|
|
223
|
+
----------
|
|
224
|
+
pd_object : DatetimeIndex or Timestamp
|
|
225
|
+
|
|
226
|
+
Returns
|
|
227
|
+
-------
|
|
228
|
+
pandas object localized to or assumed to be UTC.
|
|
229
|
+
"""
|
|
230
|
+
try:
|
|
231
|
+
pd_object_utc = pd_object.tz_convert('UTC')
|
|
232
|
+
except TypeError:
|
|
233
|
+
pd_object_utc = pd_object
|
|
234
|
+
return pd_object_utc
|
|
211
235
|
|
|
212
236
|
|
|
213
237
|
def _doy_to_datetimeindex(doy, epoch_year=2014):
|
|
@@ -230,7 +254,7 @@ def _doy_to_datetimeindex(doy, epoch_year=2014):
|
|
|
230
254
|
|
|
231
255
|
|
|
232
256
|
def _datetimelike_scalar_to_doy(time):
|
|
233
|
-
return
|
|
257
|
+
return _pandas_to_doy(_datetimelike_scalar_to_datetimeindex(time))
|
|
234
258
|
|
|
235
259
|
|
|
236
260
|
def _datetimelike_scalar_to_datetimeindex(time):
|