pvlib 0.11.1__py3-none-any.whl → 0.12.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.
- pvlib/__init__.py +1 -0
- pvlib/_deprecation.py +73 -0
- pvlib/atmosphere.py +77 -7
- pvlib/bifacial/infinite_sheds.py +4 -3
- pvlib/bifacial/utils.py +2 -1
- pvlib/clearsky.py +35 -22
- pvlib/iam.py +4 -4
- pvlib/iotools/midc.py +1 -1
- pvlib/iotools/psm3.py +1 -1
- pvlib/iotools/pvgis.py +10 -12
- pvlib/iotools/tmy.py +3 -69
- pvlib/irradiance.py +112 -55
- pvlib/ivtools/sdm.py +75 -52
- pvlib/location.py +73 -33
- pvlib/modelchain.py +18 -35
- pvlib/pvsystem.py +139 -94
- pvlib/snow.py +64 -28
- pvlib/solarposition.py +46 -30
- pvlib/spa.py +4 -2
- pvlib/spectrum/__init__.py +0 -1
- pvlib/spectrum/irradiance.py +2 -64
- pvlib/spectrum/mismatch.py +3 -3
- pvlib/spectrum/spectrl2.py +2 -1
- pvlib/temperature.py +49 -3
- pvlib/tools.py +6 -5
- {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info}/METADATA +14 -11
- pvlib-0.12.0.dist-info/RECORD +75 -0
- {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info}/WHEEL +1 -1
- pvlib/data/BIRD_08_16_2012.csv +0 -8761
- pvlib/data/BIRD_08_16_2012_patm.csv +0 -8761
- pvlib/data/Burlington, United States SolarAnywhere Time Series 2021 Lat_44_465 Lon_-73_205 TMY3 format.csv +0 -8762
- pvlib/data/Burlington, United States SolarAnywhere Time Series 20210101 to 20210103 Lat_44_4675 Lon_-73_2075 SA format.csv +0 -578
- pvlib/data/Burlington, United States SolarAnywhere Typical GHI Year Lat_44_465 Lon_-73_205 SA format.csv +0 -74
- pvlib/data/CPS SCH275KTL-DO-US-800-250kW_275kVA_1.OND +0 -146
- pvlib/data/CRNS0101-05-2019-AZ_Tucson_11_W.txt +0 -4
- pvlib/data/CRN_with_problems.txt +0 -3
- pvlib/data/ET-M772BH550GL.PAN +0 -75
- pvlib/data/NLD_Amsterdam062400_IWEC.epw +0 -8768
- pvlib/data/PVsyst_demo.csv +0 -10757
- pvlib/data/PVsyst_demo_model.csv +0 -3588
- pvlib/data/SRML-day-EUPO1801.txt +0 -1441
- pvlib/data/abq19056.dat +0 -6
- pvlib/data/aod550_tcwv_20121101_test.nc +0 -0
- pvlib/data/bishop88_numerical_precision.csv +0 -101
- pvlib/data/bsrn-lr0100-pay0616.dat +0 -86901
- pvlib/data/bsrn-pay0616.dat.gz +0 -0
- pvlib/data/cams_mcclear_1min_verbose.csv +0 -60
- pvlib/data/cams_mcclear_monthly.csv +0 -42
- pvlib/data/cams_radiation_1min_verbose.csv +0 -72
- pvlib/data/cams_radiation_monthly.csv +0 -47
- pvlib/data/detect_clearsky_data.csv +0 -35
- pvlib/data/detect_clearsky_threshold_data.csv +0 -126
- pvlib/data/greensboro_kimber_soil_manwash.dat +0 -8761
- pvlib/data/greensboro_kimber_soil_nowash.dat +0 -8761
- pvlib/data/inverter_fit_snl_meas.csv +0 -127
- pvlib/data/inverter_fit_snl_sim.csv +0 -19
- pvlib/data/ivtools_numdiff.csv +0 -52
- pvlib/data/midc_20181014.txt +0 -1441
- pvlib/data/midc_raw_20181018.txt +0 -1441
- pvlib/data/midc_raw_short_header_20191115.txt +0 -1441
- pvlib/data/msn19056.dat +0 -6
- pvlib/data/precise_iv_curves1.json +0 -10251
- pvlib/data/precise_iv_curves2.json +0 -10251
- pvlib/data/precise_iv_curves_parameter_sets1.csv +0 -33
- pvlib/data/precise_iv_curves_parameter_sets2.csv +0 -33
- pvlib/data/pvgis_hourly_Timeseries_45.000_8.000_SA2_10kWp_CIS_5_2a_2013_2014.json +0 -1
- pvlib/data/pvgis_hourly_Timeseries_45.000_8.000_SA_30deg_0deg_2016_2016.csv +0 -35
- pvlib/data/pvgis_tmy_meta.json +0 -32
- pvlib/data/pvgis_tmy_test.dat +0 -8761
- pvlib/data/pvwatts_8760_rackmount.csv +0 -8779
- pvlib/data/pvwatts_8760_roofmount.csv +0 -8779
- pvlib/data/singleaxis_tracker_wslope.csv +0 -8761
- pvlib/data/spectrl2_example_spectra.csv +0 -123
- pvlib/data/surfrad-slv16001.dat +0 -1442
- pvlib/data/test_psm3_2017.csv +0 -17521
- pvlib/data/test_psm3_2019_5min.csv +0 -289
- pvlib/data/test_psm3_tmy-2017.csv +0 -8761
- pvlib/data/test_read_psm3.csv +0 -17523
- pvlib/data/test_read_pvgis_horizon.csv +0 -49
- pvlib/data/tmy_45.000_8.000_2005_2020.csv +0 -8789
- pvlib/data/tmy_45.000_8.000_2005_2020.epw +0 -8768
- pvlib/data/tmy_45.000_8.000_2005_2020.json +0 -1
- pvlib/data/tmy_45.000_8.000_2005_2020.txt +0 -8761
- pvlib/data/tmy_45.000_8.000_userhorizon.json +0 -1
- pvlib/data/variables_style_rules.csv +0 -56
- pvlib/spa_c_files/README.md +0 -81
- pvlib/spa_c_files/cspa_py.pxd +0 -43
- pvlib/spa_c_files/spa_py.pyx +0 -30
- pvlib/tests/__init__.py +0 -0
- pvlib/tests/bifacial/__init__.py +0 -0
- pvlib/tests/bifacial/test_infinite_sheds.py +0 -317
- pvlib/tests/bifacial/test_losses_models.py +0 -54
- pvlib/tests/bifacial/test_pvfactors.py +0 -82
- pvlib/tests/bifacial/test_utils.py +0 -192
- pvlib/tests/conftest.py +0 -476
- pvlib/tests/iotools/__init__.py +0 -0
- pvlib/tests/iotools/test_acis.py +0 -213
- pvlib/tests/iotools/test_bsrn.py +0 -131
- pvlib/tests/iotools/test_crn.py +0 -95
- pvlib/tests/iotools/test_epw.py +0 -23
- pvlib/tests/iotools/test_midc.py +0 -89
- pvlib/tests/iotools/test_panond.py +0 -32
- pvlib/tests/iotools/test_psm3.py +0 -198
- pvlib/tests/iotools/test_pvgis.py +0 -644
- pvlib/tests/iotools/test_sodapro.py +0 -298
- pvlib/tests/iotools/test_solaranywhere.py +0 -287
- pvlib/tests/iotools/test_solargis.py +0 -68
- pvlib/tests/iotools/test_solcast.py +0 -324
- pvlib/tests/iotools/test_solrad.py +0 -152
- pvlib/tests/iotools/test_srml.py +0 -124
- pvlib/tests/iotools/test_surfrad.py +0 -75
- pvlib/tests/iotools/test_tmy.py +0 -133
- pvlib/tests/ivtools/__init__.py +0 -0
- pvlib/tests/ivtools/test_sde.py +0 -230
- pvlib/tests/ivtools/test_sdm.py +0 -407
- pvlib/tests/ivtools/test_utils.py +0 -173
- pvlib/tests/spectrum/__init__.py +0 -0
- pvlib/tests/spectrum/conftest.py +0 -40
- pvlib/tests/spectrum/test_irradiance.py +0 -138
- pvlib/tests/spectrum/test_mismatch.py +0 -304
- pvlib/tests/spectrum/test_response.py +0 -124
- pvlib/tests/spectrum/test_spectrl2.py +0 -72
- pvlib/tests/test_albedo.py +0 -84
- pvlib/tests/test_atmosphere.py +0 -204
- pvlib/tests/test_clearsky.py +0 -878
- pvlib/tests/test_conftest.py +0 -81
- pvlib/tests/test_iam.py +0 -555
- pvlib/tests/test_inverter.py +0 -213
- pvlib/tests/test_irradiance.py +0 -1441
- pvlib/tests/test_location.py +0 -356
- pvlib/tests/test_modelchain.py +0 -2020
- pvlib/tests/test_numerical_precision.py +0 -124
- pvlib/tests/test_pvarray.py +0 -71
- pvlib/tests/test_pvsystem.py +0 -2495
- pvlib/tests/test_scaling.py +0 -207
- pvlib/tests/test_shading.py +0 -391
- pvlib/tests/test_singlediode.py +0 -608
- pvlib/tests/test_snow.py +0 -212
- pvlib/tests/test_soiling.py +0 -230
- pvlib/tests/test_solarposition.py +0 -933
- pvlib/tests/test_spa.py +0 -425
- pvlib/tests/test_temperature.py +0 -470
- pvlib/tests/test_tools.py +0 -146
- pvlib/tests/test_tracking.py +0 -474
- pvlib/tests/test_transformer.py +0 -60
- pvlib-0.11.1.dist-info/RECORD +0 -192
- {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info/licenses}/AUTHORS.md +0 -0
- {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info/licenses}/LICENSE +0 -0
- {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info}/top_level.txt +0 -0
pvlib/tests/test_snow.py
DELETED
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
import numpy as np
|
|
2
|
-
import pandas as pd
|
|
3
|
-
|
|
4
|
-
from .conftest import assert_series_equal
|
|
5
|
-
|
|
6
|
-
from pvlib import snow
|
|
7
|
-
from pvlib.tools import sind
|
|
8
|
-
|
|
9
|
-
import pytest
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def test_fully_covered_nrel():
|
|
13
|
-
dt = pd.date_range(start="2019-1-1 12:00:00", end="2019-1-1 18:00:00",
|
|
14
|
-
freq='1h')
|
|
15
|
-
snowfall_data = pd.Series([1, 5, .6, 4, .23, -5, 19], index=dt)
|
|
16
|
-
expected = pd.Series([False, True, False, True, False, False, True],
|
|
17
|
-
index=dt)
|
|
18
|
-
fully_covered = snow.fully_covered_nrel(snowfall_data)
|
|
19
|
-
assert_series_equal(expected, fully_covered)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
def test_coverage_nrel_hourly():
|
|
23
|
-
surface_tilt = 45
|
|
24
|
-
slide_amount_coefficient = 0.197
|
|
25
|
-
dt = pd.date_range(start="2019-1-1 10:00:00", end="2019-1-1 17:00:00",
|
|
26
|
-
freq='1h')
|
|
27
|
-
poa_irradiance = pd.Series([400, 200, 100, 1234, 134, 982, 100, 100],
|
|
28
|
-
index=dt)
|
|
29
|
-
temp_air = pd.Series([10, 2, 10, 1234, 34, 982, 10, 10], index=dt)
|
|
30
|
-
snowfall_data = pd.Series([1, .5, .6, .4, .23, -5, .1, .1], index=dt)
|
|
31
|
-
snow_coverage = snow.coverage_nrel(
|
|
32
|
-
snowfall_data, poa_irradiance, temp_air, surface_tilt,
|
|
33
|
-
threshold_snowfall=0.6)
|
|
34
|
-
|
|
35
|
-
slide_amt = slide_amount_coefficient * sind(surface_tilt)
|
|
36
|
-
covered = 1.0 - slide_amt * np.array([0, 1, 2, 3, 4, 5, 6, 7])
|
|
37
|
-
expected = pd.Series(covered, index=dt)
|
|
38
|
-
assert_series_equal(expected, snow_coverage)
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
def test_coverage_nrel_subhourly():
|
|
42
|
-
surface_tilt = 45
|
|
43
|
-
slide_amount_coefficient = 0.197
|
|
44
|
-
dt = pd.date_range(start="2019-1-1 11:00:00", end="2019-1-1 14:00:00",
|
|
45
|
-
freq='15min')
|
|
46
|
-
poa_irradiance = pd.Series([400, 200, 100, 1234, 134, 982, 100, 100, 100,
|
|
47
|
-
100, 100, 100, 0],
|
|
48
|
-
index=dt)
|
|
49
|
-
temp_air = pd.Series([10, 2, 10, 1234, 34, 982, 10, 10, 10, 10, -10, -10,
|
|
50
|
-
10], index=dt)
|
|
51
|
-
snowfall_data = pd.Series([1, .5, .6, .4, .23, -5, .1, .1, 0., 1., 0., 0.,
|
|
52
|
-
0.], index=dt)
|
|
53
|
-
snow_coverage = snow.coverage_nrel(
|
|
54
|
-
snowfall_data, poa_irradiance, temp_air, surface_tilt)
|
|
55
|
-
slide_amt = slide_amount_coefficient * sind(surface_tilt) * 0.25
|
|
56
|
-
covered = np.append(np.array([1., 1., 1., 1.]),
|
|
57
|
-
1.0 - slide_amt * np.array([1, 2, 3, 4, 5]))
|
|
58
|
-
covered = np.append(covered, np.array([1., 1., 1., 1. - slide_amt]))
|
|
59
|
-
expected = pd.Series(covered, index=dt)
|
|
60
|
-
assert_series_equal(expected, snow_coverage)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def test_fully_covered_nrel_irregular():
|
|
64
|
-
# test when frequency is not specified and can't be inferred
|
|
65
|
-
dt = pd.DatetimeIndex(["2019-1-1 11:00:00", "2019-1-1 14:30:00",
|
|
66
|
-
"2019-1-1 15:07:00", "2019-1-1 14:00:00"])
|
|
67
|
-
snowfall_data = pd.Series([1, .5, .6, .4], index=dt)
|
|
68
|
-
snow_coverage = snow.fully_covered_nrel(snowfall_data,
|
|
69
|
-
threshold_snowfall=0.5)
|
|
70
|
-
covered = np.array([False, False, True, False])
|
|
71
|
-
expected = pd.Series(covered, index=dt)
|
|
72
|
-
assert_series_equal(expected, snow_coverage)
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def test_coverage_nrel_initial():
|
|
76
|
-
surface_tilt = 45
|
|
77
|
-
slide_amount_coefficient = 0.197
|
|
78
|
-
dt = pd.date_range(start="2019-1-1 10:00:00", end="2019-1-1 17:00:00",
|
|
79
|
-
freq='1h')
|
|
80
|
-
poa_irradiance = pd.Series([400, 200, 100, 1234, 134, 982, 100, 100],
|
|
81
|
-
index=dt)
|
|
82
|
-
temp_air = pd.Series([10, 2, 10, 1234, 34, 982, 10, 10], index=dt)
|
|
83
|
-
snowfall_data = pd.Series([0, .5, .6, .4, .23, -5, .1, .1], index=dt)
|
|
84
|
-
snow_coverage = snow.coverage_nrel(
|
|
85
|
-
snowfall_data, poa_irradiance, temp_air, surface_tilt,
|
|
86
|
-
initial_coverage=0.5, threshold_snowfall=1.)
|
|
87
|
-
slide_amt = slide_amount_coefficient * sind(surface_tilt)
|
|
88
|
-
covered = 0.5 - slide_amt * np.array([0, 1, 2, 3, 4, 5, 6, 7])
|
|
89
|
-
covered = np.where(covered < 0, 0., covered)
|
|
90
|
-
expected = pd.Series(covered, index=dt)
|
|
91
|
-
assert_series_equal(expected, snow_coverage)
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
def test_dc_loss_nrel():
|
|
95
|
-
num_strings = 8
|
|
96
|
-
snow_coverage = pd.Series([1, 1, .5, .6, .2, .4, 0])
|
|
97
|
-
expected = pd.Series([1, 1, .5, .625, .25, .5, 0])
|
|
98
|
-
actual = snow.dc_loss_nrel(snow_coverage, num_strings)
|
|
99
|
-
assert_series_equal(expected, actual)
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
def test__townsend_effective_snow():
|
|
103
|
-
snow_total = np.array([25.4, 25.4, 12.7, 2.54, 0, 0, 0, 0, 0, 0, 12.7,
|
|
104
|
-
25.4])
|
|
105
|
-
snow_events = np.array([2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3])
|
|
106
|
-
expected = np.array([19.05, 19.05, 12.7, 0, 0, 0, 0, 0, 0, 0, 9.525,
|
|
107
|
-
254 / 15])
|
|
108
|
-
actual = snow._townsend_effective_snow(snow_total, snow_events)
|
|
109
|
-
np.testing.assert_allclose(expected, actual, rtol=1e-07)
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
def test_loss_townsend():
|
|
113
|
-
# hand-calculated solution
|
|
114
|
-
snow_total = np.array([25.4, 25.4, 12.7, 2.54, 0, 0, 0, 0, 0, 0, 12.7,
|
|
115
|
-
25.4])
|
|
116
|
-
snow_events = np.array([2, 2, 1, 0, 0, 0, 0, 0, 0, 0, 2, 3])
|
|
117
|
-
surface_tilt = 20
|
|
118
|
-
relative_humidity = np.array([80, 80, 80, 80, 80, 80, 80, 80, 80, 80,
|
|
119
|
-
80, 80])
|
|
120
|
-
temp_air = np.array([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
|
|
121
|
-
poa_global = np.array([350000, 350000, 350000, 350000, 350000, 350000,
|
|
122
|
-
350000, 350000, 350000, 350000, 350000, 350000])
|
|
123
|
-
angle_of_repose = 40
|
|
124
|
-
string_factor = 1.0
|
|
125
|
-
slant_height = 2.54
|
|
126
|
-
lower_edge_height = 0.254
|
|
127
|
-
expected = np.array([0.07696253, 0.07992262, 0.06216201, 0.01715392, 0, 0,
|
|
128
|
-
0, 0, 0, 0, 0.02643821, 0.06068194])
|
|
129
|
-
actual = snow.loss_townsend(snow_total, snow_events, surface_tilt,
|
|
130
|
-
relative_humidity, temp_air,
|
|
131
|
-
poa_global, slant_height,
|
|
132
|
-
lower_edge_height, string_factor,
|
|
133
|
-
angle_of_repose)
|
|
134
|
-
np.testing.assert_allclose(expected, actual, rtol=1e-05)
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
@pytest.mark.parametrize(
|
|
138
|
-
'poa_global,surface_tilt,slant_height,lower_edge_height,string_factor,expected', # noQA: E501
|
|
139
|
-
[
|
|
140
|
-
(np.asarray(
|
|
141
|
-
[60., 80., 100., 125., 175., 225., 225., 210., 175., 125., 90.,
|
|
142
|
-
60.], dtype=float) * 1000.,
|
|
143
|
-
2.,
|
|
144
|
-
79. / 39.37,
|
|
145
|
-
3. / 39.37,
|
|
146
|
-
1.0,
|
|
147
|
-
np.asarray(
|
|
148
|
-
[44, 34, 20, 9, 3, 1, 0, 0, 0, 2, 6, 25], dtype=float)
|
|
149
|
-
),
|
|
150
|
-
(np.asarray(
|
|
151
|
-
[60., 80., 100., 125., 175., 225., 225., 210., 175., 125., 90.,
|
|
152
|
-
60.], dtype=float) * 1000.,
|
|
153
|
-
5.,
|
|
154
|
-
316 / 39.37,
|
|
155
|
-
120. / 39.37,
|
|
156
|
-
0.75,
|
|
157
|
-
np.asarray(
|
|
158
|
-
[22, 16, 9, 4, 1, 0, 0, 0, 0, 1, 2, 12], dtype=float)
|
|
159
|
-
),
|
|
160
|
-
(np.asarray(
|
|
161
|
-
[60., 80., 100., 125., 175., 225., 225., 210., 175., 125., 90.,
|
|
162
|
-
60.], dtype=float) * 1000.,
|
|
163
|
-
23.,
|
|
164
|
-
158 / 39.27,
|
|
165
|
-
12 / 39.37,
|
|
166
|
-
0.75,
|
|
167
|
-
np.asarray(
|
|
168
|
-
[28, 21, 13, 6, 2, 0, 0, 0, 0, 1, 4, 16], dtype=float)
|
|
169
|
-
),
|
|
170
|
-
(np.asarray(
|
|
171
|
-
[80., 100., 125., 150., 225., 300., 300., 275., 225., 150., 115.,
|
|
172
|
-
80.], dtype=float) * 1000.,
|
|
173
|
-
52.,
|
|
174
|
-
39.5 / 39.37,
|
|
175
|
-
34. / 39.37,
|
|
176
|
-
0.75,
|
|
177
|
-
np.asarray(
|
|
178
|
-
[7, 5, 3, 1, 0, 0, 0, 0, 0, 0, 1, 4], dtype=float)
|
|
179
|
-
),
|
|
180
|
-
(np.asarray(
|
|
181
|
-
[80., 100., 125., 150., 225., 300., 300., 275., 225., 150., 115.,
|
|
182
|
-
80.], dtype=float) * 1000.,
|
|
183
|
-
60.,
|
|
184
|
-
39.5 / 39.37,
|
|
185
|
-
25. / 39.37,
|
|
186
|
-
1.,
|
|
187
|
-
np.asarray(
|
|
188
|
-
[7, 5, 3, 1, 0, 0, 0, 0, 0, 0, 1, 3], dtype=float)
|
|
189
|
-
)
|
|
190
|
-
]
|
|
191
|
-
)
|
|
192
|
-
def test_loss_townsend_cases(poa_global, surface_tilt, slant_height,
|
|
193
|
-
lower_edge_height, string_factor, expected):
|
|
194
|
-
# test cases from Townsend, 1/27/2023, addeed by cwh
|
|
195
|
-
# snow_total in inches, convert to cm for pvlib
|
|
196
|
-
snow_total = np.asarray(
|
|
197
|
-
[20, 15, 10, 4, 1.5, 0, 0, 0, 0, 1.5, 4, 15], dtype=float) * 2.54
|
|
198
|
-
# snow events are an average for each month
|
|
199
|
-
snow_events = np.asarray(
|
|
200
|
-
[5, 4.2, 2.8, 1.3, 0.8, 0, 0, 0, 0, 0.5, 1.5, 4.5], dtype=float)
|
|
201
|
-
# air temperature in C
|
|
202
|
-
temp_air = np.asarray(
|
|
203
|
-
[-6., -2., 1., 4., 7., 10., 13., 16., 14., 12., 7., -3.], dtype=float)
|
|
204
|
-
# relative humidity in %
|
|
205
|
-
relative_humidity = np.asarray(
|
|
206
|
-
[78., 80., 75., 65., 60., 55., 55., 55., 50., 55., 60., 70.],
|
|
207
|
-
dtype=float)
|
|
208
|
-
actual = snow.loss_townsend(
|
|
209
|
-
snow_total, snow_events, surface_tilt, relative_humidity, temp_air,
|
|
210
|
-
poa_global, slant_height, lower_edge_height, string_factor)
|
|
211
|
-
actual = np.around(actual * 100)
|
|
212
|
-
assert np.allclose(expected, actual)
|
pvlib/tests/test_soiling.py
DELETED
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
"""Test losses"""
|
|
2
|
-
|
|
3
|
-
import datetime
|
|
4
|
-
import numpy as np
|
|
5
|
-
import pandas as pd
|
|
6
|
-
from .conftest import assert_series_equal
|
|
7
|
-
from pvlib.soiling import hsu, kimber
|
|
8
|
-
from pvlib.iotools import read_tmy3
|
|
9
|
-
from .conftest import DATA_DIR
|
|
10
|
-
import pytest
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@pytest.fixture
|
|
14
|
-
def expected_output():
|
|
15
|
-
# Sample output (calculated manually)
|
|
16
|
-
dt = pd.date_range(start=pd.Timestamp(2019, 1, 1, 0, 0, 0),
|
|
17
|
-
end=pd.Timestamp(2019, 1, 1, 23, 59, 0), freq='1h')
|
|
18
|
-
|
|
19
|
-
expected_no_cleaning = pd.Series(
|
|
20
|
-
data=[0.96998483, 0.94623958, 0.92468139, 0.90465654, 0.88589707,
|
|
21
|
-
0.86826366, 0.85167258, 0.83606715, 0.82140458, 0.80764919,
|
|
22
|
-
0.79476875, 0.78273241, 0.77150951, 0.76106905, 0.75137932,
|
|
23
|
-
0.74240789, 0.73412165, 0.72648695, 0.71946981, 0.7130361,
|
|
24
|
-
0.70715176, 0.70178307, 0.69689677, 0.69246034],
|
|
25
|
-
index=dt)
|
|
26
|
-
return expected_no_cleaning
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
@pytest.fixture
|
|
30
|
-
def expected_output_1():
|
|
31
|
-
dt = pd.date_range(start=pd.Timestamp(2019, 1, 1, 0, 0, 0),
|
|
32
|
-
end=pd.Timestamp(2019, 1, 1, 23, 59, 0), freq='1h')
|
|
33
|
-
expected_output_1 = pd.Series(
|
|
34
|
-
data=[0.98484972, 0.97277367, 0.96167471, 0.95119603, 1.,
|
|
35
|
-
0.98484972, 0.97277367, 0.96167471, 1., 1.,
|
|
36
|
-
0.98484972, 0.97277367, 0.96167471, 0.95119603, 0.94118234,
|
|
37
|
-
0.93154854, 0.922242, 0.91322759, 0.90448058, 0.89598283,
|
|
38
|
-
0.88772062, 0.87968325, 0.8718622, 0.86425049],
|
|
39
|
-
index=dt)
|
|
40
|
-
return expected_output_1
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
@pytest.fixture
|
|
44
|
-
def expected_output_2():
|
|
45
|
-
dt = pd.date_range(start=pd.Timestamp(2019, 1, 1, 0, 0, 0),
|
|
46
|
-
end=pd.Timestamp(2019, 1, 1, 23, 59, 0), freq='1h')
|
|
47
|
-
expected_output_2 = pd.Series(
|
|
48
|
-
data=[0.95036261, 0.91178179, 0.87774818, 0.84732079, 1.,
|
|
49
|
-
1., 1., 0.95036261, 1., 1.,
|
|
50
|
-
1., 1., 0.95036261, 0.91178179, 0.87774818,
|
|
51
|
-
0.84732079, 0.8201171, 1., 1., 1.,
|
|
52
|
-
1., 0.95036261, 0.91178179, 0.87774818],
|
|
53
|
-
index=dt)
|
|
54
|
-
return expected_output_2
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
@pytest.fixture
|
|
58
|
-
def expected_output_3():
|
|
59
|
-
dt = pd.date_range(start=pd.Timestamp(2019, 1, 1, 0, 0, 0),
|
|
60
|
-
end=pd.Timestamp(2019, 1, 1, 23, 59, 0), freq='1h')
|
|
61
|
-
timedelta = [0, 0, 0, 0, 0, 30, 0, 30, 0, 30, 0, -30,
|
|
62
|
-
-30, -30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
63
|
-
dt_new = dt + pd.to_timedelta(timedelta, 'm')
|
|
64
|
-
expected_output_3 = pd.Series(
|
|
65
|
-
data=[0.96576705, 0.9387675, 0.91437615, 0.89186852, 1.,
|
|
66
|
-
1., 0.98093819, 0.9387675, 1., 1.,
|
|
67
|
-
1., 1., 0.96576705, 0.9387675, 0.90291005,
|
|
68
|
-
0.88122293, 0.86104089, 1., 1., 1.,
|
|
69
|
-
0.96576705, 0.9387675, 0.91437615, 0.89186852],
|
|
70
|
-
index=dt_new)
|
|
71
|
-
return expected_output_3
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
@pytest.fixture
|
|
75
|
-
def rainfall_input():
|
|
76
|
-
|
|
77
|
-
dt = pd.date_range(start=pd.Timestamp(2019, 1, 1, 0, 0, 0),
|
|
78
|
-
end=pd.Timestamp(2019, 1, 1, 23, 59, 0), freq='1h')
|
|
79
|
-
rainfall = pd.Series(
|
|
80
|
-
data=[0., 0., 0., 0., 1., 0., 0., 0., 0.5, 0.5, 0., 0., 0., 0., 0.,
|
|
81
|
-
0., 0.3, 0.3, 0.3, 0.3, 0., 0., 0., 0.], index=dt)
|
|
82
|
-
return rainfall
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
def test_hsu_no_cleaning(rainfall_input, expected_output):
|
|
86
|
-
"""Test Soiling HSU function"""
|
|
87
|
-
|
|
88
|
-
rainfall = rainfall_input
|
|
89
|
-
pm2_5 = 1.0
|
|
90
|
-
pm10 = 2.0
|
|
91
|
-
depo_veloc = {'2_5': 1.0e-5, '10': 1.0e-4}
|
|
92
|
-
tilt = 0.
|
|
93
|
-
expected_no_cleaning = expected_output
|
|
94
|
-
|
|
95
|
-
result = hsu(rainfall=rainfall, cleaning_threshold=10., surface_tilt=tilt,
|
|
96
|
-
pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc,
|
|
97
|
-
rain_accum_period=pd.Timedelta('1h'))
|
|
98
|
-
assert_series_equal(result, expected_no_cleaning)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
def test_hsu(rainfall_input, expected_output_2):
|
|
102
|
-
"""Test Soiling HSU function with cleanings"""
|
|
103
|
-
|
|
104
|
-
rainfall = rainfall_input
|
|
105
|
-
pm2_5 = 1.0
|
|
106
|
-
pm10 = 2.0
|
|
107
|
-
depo_veloc = {'2_5': 1.0e-4, '10': 1.0e-4}
|
|
108
|
-
tilt = 0.
|
|
109
|
-
|
|
110
|
-
# three cleaning events at 4:00-6:00, 8:00-11:00, and 17:00-20:00
|
|
111
|
-
result = hsu(rainfall=rainfall, cleaning_threshold=0.5, surface_tilt=tilt,
|
|
112
|
-
pm2_5=pm2_5, pm10=pm10, depo_veloc=depo_veloc,
|
|
113
|
-
rain_accum_period=pd.Timedelta('3h'))
|
|
114
|
-
|
|
115
|
-
assert_series_equal(result, expected_output_2)
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
def test_hsu_defaults(rainfall_input, expected_output_1):
|
|
119
|
-
"""
|
|
120
|
-
Test Soiling HSU function with default deposition velocity and default rain
|
|
121
|
-
accumulation period.
|
|
122
|
-
"""
|
|
123
|
-
result = hsu(rainfall=rainfall_input, cleaning_threshold=0.5,
|
|
124
|
-
surface_tilt=0.0, pm2_5=1.0e-2, pm10=2.0e-2)
|
|
125
|
-
assert np.allclose(result.values, expected_output_1)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def test_hsu_variable_time_intervals(rainfall_input, expected_output_3):
|
|
129
|
-
"""
|
|
130
|
-
Test Soiling HSU function with variable time intervals.
|
|
131
|
-
"""
|
|
132
|
-
depo_veloc = {'2_5': 1.0e-4, '10': 1.0e-4}
|
|
133
|
-
rain = pd.DataFrame(data=rainfall_input)
|
|
134
|
-
# define time deltas in minutes
|
|
135
|
-
timedelta = [0, 0, 0, 0, 0, 30, 0, 30, 0, 30, 0, -30,
|
|
136
|
-
-30, -30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
|
|
137
|
-
rain['mins_added'] = pd.to_timedelta(timedelta, 'm')
|
|
138
|
-
rain['new_time'] = rain.index + rain['mins_added']
|
|
139
|
-
rain_var_times = rain.set_index('new_time').iloc[:, 0]
|
|
140
|
-
result = hsu(
|
|
141
|
-
rainfall=rain_var_times, cleaning_threshold=0.5, surface_tilt=50.0,
|
|
142
|
-
pm2_5=1, pm10=2, depo_veloc=depo_veloc,
|
|
143
|
-
rain_accum_period=pd.Timedelta('2h'))
|
|
144
|
-
assert np.allclose(result, expected_output_3)
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
@pytest.fixture
|
|
148
|
-
def greensboro_rain():
|
|
149
|
-
# get TMY3 data with rain
|
|
150
|
-
greensboro, _ = read_tmy3(DATA_DIR / '723170TYA.CSV', coerce_year=1990,
|
|
151
|
-
map_variables=True)
|
|
152
|
-
return greensboro['Lprecip depth (mm)']
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
@pytest.fixture
|
|
156
|
-
def expected_kimber_nowash():
|
|
157
|
-
return pd.read_csv(
|
|
158
|
-
DATA_DIR / 'greensboro_kimber_soil_nowash.dat',
|
|
159
|
-
parse_dates=True, index_col='timestamp')
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
def test_kimber_nowash(greensboro_rain, expected_kimber_nowash):
|
|
163
|
-
"""Test Kimber soiling model with no manual washes"""
|
|
164
|
-
# Greensboro typical expected annual rainfall is 8345mm
|
|
165
|
-
assert greensboro_rain.sum() == 8345
|
|
166
|
-
# calculate soiling with no wash dates
|
|
167
|
-
nowash = kimber(greensboro_rain)
|
|
168
|
-
# test no washes
|
|
169
|
-
assert np.allclose(nowash.values, expected_kimber_nowash['soiling'].values)
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
@pytest.fixture
|
|
173
|
-
def expected_kimber_manwash():
|
|
174
|
-
return pd.read_csv(
|
|
175
|
-
DATA_DIR / 'greensboro_kimber_soil_manwash.dat',
|
|
176
|
-
parse_dates=True, index_col='timestamp')
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
def test_kimber_manwash(greensboro_rain, expected_kimber_manwash):
|
|
180
|
-
"""Test Kimber soiling model with a manual wash"""
|
|
181
|
-
# a manual wash date
|
|
182
|
-
manwash = [datetime.date(1990, 2, 15), ]
|
|
183
|
-
# calculate soiling with manual wash
|
|
184
|
-
manwash = kimber(greensboro_rain, manual_wash_dates=manwash)
|
|
185
|
-
# test manual wash
|
|
186
|
-
assert np.allclose(
|
|
187
|
-
manwash.values,
|
|
188
|
-
expected_kimber_manwash['soiling'].values)
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
@pytest.fixture
|
|
192
|
-
def expected_kimber_norain():
|
|
193
|
-
# expected soiling reaches maximum
|
|
194
|
-
soiling_loss_rate = 0.0015
|
|
195
|
-
max_loss_rate = 0.3
|
|
196
|
-
norain = np.ones(8760) * soiling_loss_rate/24
|
|
197
|
-
norain[0] = 0.0
|
|
198
|
-
norain = np.cumsum(norain)
|
|
199
|
-
return np.where(norain > max_loss_rate, max_loss_rate, norain)
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
def test_kimber_norain(greensboro_rain, expected_kimber_norain):
|
|
203
|
-
"""Test Kimber soiling model with no rain"""
|
|
204
|
-
# a year with no rain
|
|
205
|
-
norain = pd.Series(0, index=greensboro_rain.index)
|
|
206
|
-
# calculate soiling with no rain
|
|
207
|
-
norain = kimber(norain)
|
|
208
|
-
# test no rain, soiling reaches maximum
|
|
209
|
-
assert np.allclose(norain.values, expected_kimber_norain)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
@pytest.fixture
|
|
213
|
-
def expected_kimber_initial_soil():
|
|
214
|
-
# expected soiling reaches maximum
|
|
215
|
-
soiling_loss_rate = 0.0015
|
|
216
|
-
max_loss_rate = 0.3
|
|
217
|
-
norain = np.ones(8760) * soiling_loss_rate/24
|
|
218
|
-
norain[0] = 0.1
|
|
219
|
-
norain = np.cumsum(norain)
|
|
220
|
-
return np.where(norain > max_loss_rate, max_loss_rate, norain)
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
def test_kimber_initial_soil(greensboro_rain, expected_kimber_initial_soil):
|
|
224
|
-
"""Test Kimber soiling model with initial soiling"""
|
|
225
|
-
# a year with no rain
|
|
226
|
-
norain = pd.Series(0, index=greensboro_rain.index)
|
|
227
|
-
# calculate soiling with no rain
|
|
228
|
-
norain = kimber(norain, initial_soiling=0.1)
|
|
229
|
-
# test no rain, soiling reaches maximum
|
|
230
|
-
assert np.allclose(norain.values, expected_kimber_initial_soil)
|