Skip to content

Commit 8013982

Browse files
authored
Merge pull request #379 from NREL/release/2.1.6
Release/2.1.6
2 parents d19fa83 + 082b84f commit 8013982

18 files changed

+223
-171
lines changed

.github/workflows/nbval.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ jobs:
2525
- name: Install notebook environment
2626
run: |
2727
python -m pip install --upgrade pip wheel
28-
pip install -r requirements.txt -r docs/notebook_requirements.txt .[test]
28+
pip install --timeout=300 -r requirements.txt -r docs/notebook_requirements.txt .[test]
2929
- name: Run notebook and check output
3030
run: |
3131
# --sanitize-with: pre-process text to remove irrelevant differences (e.g. warning filepaths)

.github/workflows/pytest.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ jobs:
4040
- name: Install ${{ matrix.env }}
4141
run: |
4242
python -m pip install --upgrade pip wheel
43-
pip install ${{ matrix.env }}
43+
pip install --timeout=300 ${{ matrix.env }}
4444
- name: Test with pytest ${{ matrix.env }}
4545
run: |
4646
pytest

.github/workflows/requirements.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ jobs:
2626
- name: Install notebook environment
2727
run: |
2828
python -m pip install --upgrade pip wheel
29-
pip install -r requirements.txt -r docs/notebook_requirements.txt
29+
pip install --timeout=300 -r requirements.txt -r docs/notebook_requirements.txt

docs/TrendAnalysis_example_pvdaq4.ipynb

+29-29
Large diffs are not rendered by default.

docs/degradation_and_soiling_example.ipynb

+26-26
Large diffs are not rendered by default.

docs/degradation_and_soiling_example_pvdaq_4.ipynb

+36-36
Large diffs are not rendered by default.

docs/notebook_requirements.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ defusedxml==0.7.1
1111
entrypoints==0.2.3
1212
html5lib==1.0.1
1313
ipykernel==4.8.2
14-
ipython==7.16.3
14+
ipython==8.10.0
1515
ipython-genutils==0.2.0
1616
ipywidgets==7.3.0
17-
jedi==0.12.1
17+
jedi==0.16.0
1818
Jinja2==3.0.0
1919
jsonschema==2.6.0
2020
jupyter==1.0.0
@@ -32,11 +32,11 @@ nest-asyncio==1.5.5
3232
notebook==6.4.12
3333
numexpr==2.8.0
3434
pandocfilters==1.4.2
35-
parso==0.3.1
35+
parso==0.5.2
3636
pexpect==4.6.0
3737
pickleshare==0.7.5
3838
prometheus-client==0.3.0
39-
prompt-toolkit==3.0.27
39+
prompt-toolkit==3.0.30
4040
ptyprocess==0.6.0
4141
pycparser==2.20
4242
Pygments==2.7.4

docs/sphinx/source/changelog.rst

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
RdTools Change Log
22
==================
3+
.. include:: changelog/v2.1.6.rst
34
.. include:: changelog/v2.1.5.rst
45
.. include:: changelog/v2.1.4.rst
56
.. include:: changelog/v2.1.3.rst
+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
**********************
2+
v2.1.6 (July 31, 2023)
3+
**********************
4+
5+
Bug Fixes
6+
---------
7+
* Fix NonExistentTimeError in :py:func:`rdtools.clearsky_temperature.get_clearsky_tamb`
8+
(:issue:`372` :pull:`373`)
9+
10+
Enhancements
11+
------------
12+
* :py:func:`rdtools.degradation.degradation_classical_decomposition` now
13+
executes significantly faster. (:pull:`371`)
14+
15+
Requirements
16+
------------
17+
* Increased the minimum versions of several dependencies: (:pull:`371`)
18+
19+
+ pandas increased to 1.3.0 (released July 2, 2021)
20+
+ numpy to 1.17.3 (released October 17, 2019)
21+
+ statsmodels to 0.11.0 (released February 21, 2020)
22+
+ scipy to 1.2.0 (released December 17, 2018)
23+
24+
* Add support for pvlib 0.10 (:pull:`378`)
25+
* Updated notebook requirements (:pull:`360`)
26+
* Bumps certifi from 2020.12.5 to 2022.12.7 (:pull:`357`)
27+
28+
Testing
29+
-------
30+
* Extended pip timeout (:pull:`360`)
31+
* Updated example notebooks with new figure sizes (:pull:`360`)
32+
33+
Contributors
34+
------------
35+
* Michael Deceglie (:ghuser:`mdeceglie`)
36+
* Bernat Nicolau (:ghuser:`BernatNicolau`)
37+
* Kevin Anderson (:ghuser:`kandersolar`)

docs/system_availability_example.ipynb

+18-15
Large diffs are not rendered by default.

rdtools/clearsky_temperature.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,9 @@ def get_clearsky_tamb(times, latitude, longitude, window_size=40,
5757
freq_actual = times.freq
5858

5959
dt_daily = pd.date_range(times.date[0] - buffer, times.date[-1] + buffer,
60-
freq='D', tz=times.tz)
60+
freq='D')
61+
dt_daily = dt_daily.tz_localize(times.tz, ambiguous='infer',
62+
nonexistent='shift_forward')
6163

6264
f = h5py.File(filepath, "r")
6365

rdtools/degradation.py

+5-15
Original file line numberDiff line numberDiff line change
@@ -126,17 +126,9 @@ def degradation_classical_decomposition(energy_normalized,
126126

127127
# Compute yearly rolling mean to isolate trend component using
128128
# moving average
129-
it = df.iterrows()
130-
energy_ma = []
131-
for i, row in it:
132-
if row.years - 0.5 >= min(df.years) and \
133-
row.years + 0.5 <= max(df.years):
134-
roll = df[(df.years <= row.years + 0.5) &
135-
(df.years >= row.years - 0.5)]
136-
energy_ma.append(roll.energy_normalized.mean())
137-
else:
138-
energy_ma.append(np.nan)
139-
129+
energy_ma = df['energy_normalized'].rolling('365d', center=True).mean()
130+
has_full_year = (df['years'] >= df['years'][0] + 0.5) & (df['years'] <= df['years'][-1] - 0.5)
131+
energy_ma[~has_full_year] = np.nan
140132
df['energy_ma'] = energy_ma
141133

142134
# add intercept-constant to the exogeneous variable
@@ -332,10 +324,8 @@ def _mk_test(x, alpha=0.05):
332324
n = len(x)
333325

334326
# calculate S
335-
s = 0
336-
for k in range(n - 1):
337-
for j in range(k + 1, n):
338-
s += np.sign(x[j] - x[k])
327+
x = np.array(x)
328+
s = np.sum(np.triu(np.sign(-np.subtract.outer(x, x)), 1))
339329

340330
# calculate the unique data
341331
unique_x = np.unique(x)

rdtools/test/analysis_chains_test.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -407,11 +407,11 @@ def test_srr_soiling(soiling_analysis_sensor):
407407
ci = srr_results['sratio_confidence_interval']
408408
renorm_factor = srr_results['calc_info']['renormalizing_factor']
409409
print(f'soiling ci:{ci}')
410-
assert 0.965 == pytest.approx(sratio, abs=1e-3),\
410+
assert 0.965 == pytest.approx(sratio, abs=1e-3), \
411411
'Soiling ratio different from expected value in TrendAnalysis.srr_soiling'
412-
assert [0.96, 0.97] == pytest.approx(ci, abs=1e-2),\
412+
assert [0.96, 0.97] == pytest.approx(ci, abs=1e-2), \
413413
'Soiling confidence interval different from expected value in TrendAnalysis.srr_soiling'
414-
assert 0.974 == pytest.approx(renorm_factor, abs=1e-3),\
414+
assert 0.974 == pytest.approx(renorm_factor, abs=1e-3), \
415415
'Renormalization factor different from expected value in TrendAnalysis.srr_soiling'
416416

417417

rdtools/test/clearsky_temperature_test.py

+20
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import pytest
2+
import datetime
23
import pandas as pd
34

5+
46
from rdtools.clearsky_temperature import get_clearsky_tamb
57

68

@@ -35,3 +37,21 @@ def test_not_on_land():
3537
with pytest.warns(UserWarning, match='possibly invalid Lat/Lon coordinates'):
3638
ocean_cs_tamb = get_clearsky_tamb(dt, 40, -60)
3739
assert ocean_cs_tamb.isnull().all()
40+
41+
42+
def test_with_tricky_timezones():
43+
# Some timezones have DST shifts at midnight, which
44+
# can lead to NonExistentTimeError. This tests for the
45+
# problem in issue #372
46+
47+
tz = 'America/Santiago'
48+
start_date = datetime.datetime(2018, 8, 10, 0, 0, 0)
49+
end_date = datetime.datetime(2018, 8, 14, 23, 0, 0)
50+
freq = 'H'
51+
lat = -24
52+
lon = -70
53+
54+
times = pd.date_range(start=start_date, end=end_date, freq=freq)
55+
times = times.tz_localize(tz=tz, ambiguous='infer',
56+
nonexistent='shift_forward')
57+
get_clearsky_tamb(times, lat, lon)

rdtools/test/soiling_test.py

+28-28
Original file line numberDiff line numberDiff line change
@@ -13,17 +13,17 @@ def test_soiling_srr(soiling_normalized_daily, soiling_insolation, soiling_times
1313
reps = 10
1414
np.random.seed(1977)
1515
sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily, soiling_insolation, reps=reps)
16-
assert 0.964369 == pytest.approx(sr, abs=1e-6),\
16+
assert 0.964369 == pytest.approx(sr, abs=1e-6), \
1717
'Soiling ratio different from expected value'
18-
assert np.array([0.962540, 0.965295]) == pytest.approx(sr_ci, abs=1e-6),\
18+
assert np.array([0.962540, 0.965295]) == pytest.approx(sr_ci, abs=1e-6), \
1919
'Confidence interval different from expected value'
20-
assert 0.960205 == pytest.approx(soiling_info['exceedance_level'], abs=1e-6),\
20+
assert 0.960205 == pytest.approx(soiling_info['exceedance_level'], abs=1e-6), \
2121
'Exceedance level different from expected value'
22-
assert 0.984079 == pytest.approx(soiling_info['renormalizing_factor'], abs=1e-6),\
22+
assert 0.984079 == pytest.approx(soiling_info['renormalizing_factor'], abs=1e-6), \
2323
'Renormalizing factor different from expected value'
24-
assert len(soiling_info['stochastic_soiling_profiles']) == reps,\
24+
assert len(soiling_info['stochastic_soiling_profiles']) == reps, \
2525
'Length of soiling_info["stochastic_soiling_profiles"] different than expected'
26-
assert isinstance(soiling_info['stochastic_soiling_profiles'], list),\
26+
assert isinstance(soiling_info['stochastic_soiling_profiles'], list), \
2727
'soiling_info["stochastic_soiling_profiles"] is not a list'
2828

2929
# Check soiling_info['soiling_interval_summary']
@@ -33,12 +33,12 @@ def test_soiling_srr(soiling_normalized_daily, soiling_insolation, soiling_times
3333
actual_summary_columns = soiling_info['soiling_interval_summary'].columns.values
3434

3535
for x in actual_summary_columns:
36-
assert x in expected_summary_columns,\
36+
assert x in expected_summary_columns, \
3737
f"'{x}' not an expected column in soiling_info['soiling_interval_summary']"
3838
for x in expected_summary_columns:
39-
assert x in actual_summary_columns,\
39+
assert x in actual_summary_columns, \
4040
f"'{x}' was expected as a column, but not in soiling_info['soiling_interval_summary']"
41-
assert isinstance(soiling_info['soiling_interval_summary'], pd.DataFrame),\
41+
assert isinstance(soiling_info['soiling_interval_summary'], pd.DataFrame), \
4242
'soiling_info["soiling_interval_summary"] not a dataframe'
4343
expected_means = pd.Series({'soiling_rate': -0.002644544,
4444
'soiling_rate_low': -0.002847504,
@@ -57,9 +57,9 @@ def test_soiling_srr(soiling_normalized_daily, soiling_insolation, soiling_times
5757
pd.testing.assert_index_equal(soiling_info['soiling_ratio_perfect_clean'].index, soiling_times,
5858
check_names=False)
5959
sr_mean = soiling_info['soiling_ratio_perfect_clean'].mean()
60-
assert 0.968265 == pytest.approx(sr_mean, abs=1e-6),\
60+
assert 0.968265 == pytest.approx(sr_mean, abs=1e-6), \
6161
"The mean of soiling_info['soiling_ratio_perfect_clean'] differs from expected"
62-
assert isinstance(soiling_info['soiling_ratio_perfect_clean'], pd.Series),\
62+
assert isinstance(soiling_info['soiling_ratio_perfect_clean'], pd.Series), \
6363
'soiling_info["soiling_ratio_perfect_clean"] not a pandas series'
6464

6565

@@ -74,7 +74,7 @@ def test_soiling_srr_consecutive_invalid(soiling_normalized_daily, soiling_insol
7474
np.random.seed(1977)
7575
sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily, soiling_insolation, reps=reps,
7676
max_relative_slope_error=20.0, method=method)
77-
assert expected_sr == pytest.approx(sr, abs=1e-6),\
77+
assert expected_sr == pytest.approx(sr, abs=1e-6), \
7878
f'Soiling ratio different from expected value for {method} with consecutive invalid intervals' # noqa: E501
7979

8080

@@ -96,7 +96,7 @@ def test_soiling_srr_with_precip(soiling_normalized_daily, soiling_insolation, s
9696
np.random.seed(1977)
9797
sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily, soiling_insolation,
9898
clean_criterion=clean_criterion, **kwargs)
99-
assert expected_sr == pytest.approx(sr, abs=1e-6),\
99+
assert expected_sr == pytest.approx(sr, abs=1e-6), \
100100
f"Soiling ratio with clean_criterion='{clean_criterion}' different from expected"
101101

102102

@@ -105,9 +105,9 @@ def test_soiling_srr_confidence_levels(soiling_normalized_daily, soiling_insolat
105105
np.random.seed(1977)
106106
sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily, soiling_insolation,
107107
confidence_level=95, reps=10, exceedance_prob=80.0)
108-
assert np.array([0.959322, 0.966066]) == pytest.approx(sr_ci, abs=1e-6),\
108+
assert np.array([0.959322, 0.966066]) == pytest.approx(sr_ci, abs=1e-6), \
109109
'Confidence interval with confidence_level=95 different than expected'
110-
assert 0.962691 == pytest.approx(soiling_info['exceedance_level'], abs=1e-6),\
110+
assert 0.962691 == pytest.approx(soiling_info['exceedance_level'], abs=1e-6), \
111111
'soiling_info["exceedance_level"] different than expected when exceedance_prob=80'
112112

113113

@@ -125,7 +125,7 @@ def test_soiling_srr_clean_threshold(soiling_normalized_daily, soiling_insolatio
125125
np.random.seed(1977)
126126
sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily, soiling_insolation, reps=10,
127127
clean_threshold=0.01)
128-
assert 0.964369 == pytest.approx(sr, abs=1e-6),\
128+
assert 0.964369 == pytest.approx(sr, abs=1e-6), \
129129
'Soiling ratio with specified clean_threshold different from expected value'
130130

131131
with pytest.raises(NoValidIntervalError):
@@ -139,9 +139,9 @@ def test_soiling_srr_trim(soiling_normalized_daily, soiling_insolation):
139139
sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily, soiling_insolation, reps=10,
140140
trim=True)
141141

142-
assert 0.978093 == pytest.approx(sr, abs=1e-6),\
142+
assert 0.978093 == pytest.approx(sr, abs=1e-6), \
143143
'Soiling ratio with trim=True different from expected value'
144-
assert len(soiling_info['soiling_interval_summary']) == 1,\
144+
assert len(soiling_info['soiling_interval_summary']) == 1, \
145145
'Wrong number of soiling intervals found with trim=True'
146146

147147

@@ -153,7 +153,7 @@ def test_soiling_srr_method(soiling_normalized_daily, soiling_insolation, method
153153
np.random.seed(1977)
154154
sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily, soiling_insolation, reps=10,
155155
method=method)
156-
assert expected_sr == pytest.approx(sr, abs=1e-6),\
156+
assert expected_sr == pytest.approx(sr, abs=1e-6), \
157157
f'Soiling ratio with method="{method}" different from expected value'
158158

159159

@@ -174,9 +174,9 @@ def test_soiling_srr_recenter_false(soiling_normalized_daily, soiling_insolation
174174
np.random.seed(1977)
175175
sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily, soiling_insolation, reps=10,
176176
recenter=False)
177-
assert 1 == soiling_info['renormalizing_factor'],\
177+
assert 1 == soiling_info['renormalizing_factor'], \
178178
'Renormalizing factor != 1 with recenter=False'
179-
assert 0.966387 == pytest.approx(sr, abs=1e-6),\
179+
assert 0.966387 == pytest.approx(sr, abs=1e-6), \
180180
'Soiling ratio different than expected when recenter=False'
181181

182182

@@ -188,11 +188,11 @@ def test_soiling_srr_negative_step(soiling_normalized_daily, soiling_insolation)
188188
with pytest.warns(UserWarning, match='20% or more of the daily data'):
189189
sr, sr_ci, soiling_info = soiling_srr(stepped_daily, soiling_insolation, reps=10)
190190

191-
assert list(soiling_info['soiling_interval_summary']['valid'].values) == [True, False, True],\
191+
assert list(soiling_info['soiling_interval_summary']['valid'].values) == [True, False, True], \
192192
'Soiling interval validity differs from expected when a large negative step\
193193
is incorporated into the data'
194194

195-
assert 0.936932 == pytest.approx(sr, abs=1e-6),\
195+
assert 0.936932 == pytest.approx(sr, abs=1e-6), \
196196
'Soiling ratio different from expected when a large negative step is incorporated into the data' # noqa: E501
197197

198198

@@ -202,10 +202,10 @@ def test_soiling_srr_max_negative_slope_error(soiling_normalized_daily, soiling_
202202
sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily, soiling_insolation,
203203
reps=10, max_relative_slope_error=45.0)
204204

205-
assert list(soiling_info['soiling_interval_summary']['valid'].values) == [True, True, False],\
205+
assert list(soiling_info['soiling_interval_summary']['valid'].values) == [True, True, False], \
206206
'Soiling interval validity differs from expected when max_relative_slope_error=45.0'
207207

208-
assert 0.958761 == pytest.approx(sr, abs=1e-6),\
208+
assert 0.958761 == pytest.approx(sr, abs=1e-6), \
209209
'Soiling ratio different from expected when max_relative_slope_error=45.0'
210210

211211

@@ -220,14 +220,14 @@ def test_soiling_srr_with_nan_interval(soiling_normalized_daily, soiling_insolat
220220
np.random.seed(1977)
221221
with pytest.warns(UserWarning, match='20% or more of the daily data'):
222222
sr, sr_ci, soiling_info = soiling_srr(normalized_corrupt, soiling_insolation, reps=reps)
223-
assert 0.948792 == pytest.approx(sr, abs=1e-6),\
223+
assert 0.948792 == pytest.approx(sr, abs=1e-6), \
224224
'Soiling ratio different from expected value when an entire interval was NaN'
225225

226226

227227
def test_soiling_srr_outlier_factor(soiling_normalized_daily, soiling_insolation):
228228
_, _, info = soiling_srr(soiling_normalized_daily, soiling_insolation,
229229
reps=1, outlier_factor=8)
230-
assert len(info['soiling_interval_summary']) == 2,\
230+
assert len(info['soiling_interval_summary']) == 2, \
231231
'Increasing the outlier_factor did not result in the expected number of soiling intervals'
232232

233233

@@ -256,7 +256,7 @@ def test_soiling_srr_min_interval_length_default(soiling_normalized_daily, soili
256256
np.random.seed(1977)
257257
sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily[start:],
258258
soiling_insolation[start:], reps=reps)
259-
assert expected_sr == pytest.approx(sr, abs=1e-6),\
259+
assert expected_sr == pytest.approx(sr, abs=1e-6), \
260260
'Soiling ratio different from expected value'
261261

262262

requirements-min.txt

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
h5py==2.8.0
22
matplotlib==3.0.0
3-
numpy==1.15
4-
pandas==0.23.2
3+
numpy==1.17.3
4+
pandas==1.3.0
55
pvlib==0.7.0
6-
scipy==1.1.0
7-
statsmodels==0.9.0
6+
scipy==1.2.0
7+
statsmodels==0.11.0
88
tables==3.5.1
99
numexpr==2.7.1 # https://github.com/pydata/numexpr/issues/369
1010
plotly==4.0.0

requirements.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
cached-property==1.5.2
2-
certifi==2020.12.5
2+
certifi==2022.12.7
33
chardet==4.0.0
44
cycler==0.10.0
55
fonttools==4.37.1

0 commit comments

Comments
 (0)