Skip to content

Commit 94259c5

Browse files
author
Andrew Godbehere
committed
Implements closed-form solution for optimization problem in clearsky. Updates test.
1 parent 5d23f14 commit 94259c5

File tree

3 files changed

+26
-36
lines changed

3 files changed

+26
-36
lines changed

docs/sphinx/source/whatsnew/v0.11.1.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,10 @@ Enhancements
3030
* Added function for calculating wind speed at different heights,
3131
:py:func:`pvlib.atmosphere.windspeed_powerlaw`.
3232
(:issue:`2118`, :pull:`2124`)
33+
* Implemented closed-form solution for alpha in detect_clearsky, obviating
34+
the call to scipy.optimize that was prone to runtime errors and minimizing
35+
computation, :py:func:`pvlib.clearsky.detect_clearsky`. Resolves :issue:`2712`.
36+
3337

3438
Bug fixes
3539
~~~~~~~~~
@@ -74,3 +78,4 @@ Contributors
7478
* Marcos R. Escudero (:ghuser:`marc-resc`)
7579
* Bernat Nicolau (:ghuser:`BernatNicolau`)
7680
* Eduardo Sarquis (:ghuser:`EduardoSarquis`)
81+
* Andrew B Godbehere (:ghuser:`agodbehere`)

pvlib/clearsky.py

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,14 @@
33
to calculate clear sky GHI, DNI, and DHI.
44
"""
55

6+
import calendar
67
import os
78
from collections import OrderedDict
8-
import calendar
99

10+
import h5py
1011
import numpy as np
1112
import pandas as pd
12-
from scipy.optimize import minimize_scalar
1313
from scipy.linalg import hankel
14-
import h5py
1514

1615
from pvlib import atmosphere, tools
1716
from pvlib.tools import _degrees_to_index
@@ -874,24 +873,11 @@ def detect_clearsky(measured, clearsky, times=None, infer_limits=False,
874873
clear_meas = meas[clear_samples]
875874
clear_clear = clear[clear_samples]
876875

877-
def rmse(alpha):
878-
return np.sqrt(np.mean((clear_meas - alpha*clear_clear)**2))
879-
880-
optimize_result = minimize_scalar(rmse)
881-
if not optimize_result.success:
882-
try:
883-
message = "Optimizer exited unsuccessfully: " \
884-
+ optimize_result.message
885-
except AttributeError:
886-
message = "Optimizer exited unsuccessfully: \
887-
No message explaining the failure was returned. \
888-
If you would like to see this message, please \
889-
update your scipy version (try version 1.8.0 \
890-
or beyond)."
891-
raise RuntimeError(message)
892-
893-
else:
894-
alpha = optimize_result.x
876+
# Compute arg min of MSE between model and observations
877+
C = (clear_clear**2).sum()
878+
if not (pd.isna(C) or C == 0): # safety check
879+
# only update alpha if C is strictly positive
880+
alpha = (clear_meas * clear_clear).sum() / C
895881

896882
if round(alpha*10000) == round(previous_alpha*10000):
897883
break

pvlib/tests/test_clearsky.py

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,17 @@
11
from collections import OrderedDict
22

33
import numpy as np
4-
from numpy import nan
54
import pandas as pd
6-
import pytz
7-
from scipy.linalg import hankel
8-
95
import pytest
6+
import pytz
7+
from numpy import nan
108
from numpy.testing import assert_allclose
11-
from .conftest import assert_frame_equal, assert_series_equal
9+
from scipy.linalg import hankel
1210

11+
from pvlib import atmosphere, clearsky, irradiance, solarposition
1312
from pvlib.location import Location
14-
from pvlib import clearsky
15-
from pvlib import solarposition
16-
from pvlib import atmosphere
17-
from pvlib import irradiance
1813

19-
from .conftest import DATA_DIR
14+
from .conftest import DATA_DIR, assert_frame_equal, assert_series_equal
2015

2116

2217
def test_ineichen_series():
@@ -677,12 +672,16 @@ def test_detect_clearsky_not_enough_data(detect_clearsky_data):
677672
with pytest.raises(ValueError, match='have at least'):
678673
clearsky.detect_clearsky(expected['GHI'], cs['ghi'], window_length=60)
679674

680-
681-
def test_detect_clearsky_optimizer_failed(detect_clearsky_data):
675+
@pytest.mark.parametrize("window_length", [5, 10, 15, 20, 25])
676+
def test_detect_clearsky_optimizer_not_failed(
677+
detect_clearsky_data,
678+
window_length
679+
):
682680
expected, cs = detect_clearsky_data
683-
with pytest.raises(RuntimeError, match='Optimizer exited unsuccessfully'):
684-
clearsky.detect_clearsky(expected['GHI'], cs['ghi'], window_length=15)
685-
681+
clear_samples = clearsky.detect_clearsky(
682+
expected["GHI"], cs["ghi"], window_length=window_length
683+
)
684+
assert isinstance(clear_samples, pd.Series)
686685

687686
@pytest.fixture
688687
def detect_clearsky_helper_data():

0 commit comments

Comments
 (0)