Skip to content

Commit

Permalink
Implemented closed-form solution in detect_clearsky (#2217)
Browse files Browse the repository at this point in the history
* Implements closed-form solution for optimization problem in clearsky. Updates test.

* Revert "Implements closed-form solution for optimization problem in clearsky. Updates test."

This reverts commit 94259c5.

* Reintroduced changes without re-sorting the imports

* pep8 formatting adjustments

* Update whatsnew as per comments

---------

Co-authored-by: Andrew Godbehere <andrew.godbehere@leidos.com>
  • Loading branch information
agodbehere and Andrew Godbehere authored Sep 23, 2024
1 parent 5d23f14 commit d6baf97
Show file tree
Hide file tree
Showing 3 changed files with 19 additions and 24 deletions.
5 changes: 5 additions & 0 deletions docs/sphinx/source/whatsnew/v0.11.1.rst
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ Enhancements
* Added function for calculating wind speed at different heights,
:py:func:`pvlib.atmosphere.windspeed_powerlaw`.
(:issue:`2118`, :pull:`2124`)
* Implemented closed-form solution for alpha in :py:func:`pvlib.clearsky.detect_clearsky`,
obviating the call to scipy.optimize that was prone to runtime errors and minimizing
computation. (:issue:`2171`, :issue:`2216`, :pull:`2217`).


Bug fixes
~~~~~~~~~
Expand Down Expand Up @@ -74,3 +78,4 @@ Contributors
* Marcos R. Escudero (:ghuser:`marc-resc`)
* Bernat Nicolau (:ghuser:`BernatNicolau`)
* Eduardo Sarquis (:ghuser:`EduardoSarquis`)
* Andrew B Godbehere (:ghuser:`agodbehere`)
25 changes: 5 additions & 20 deletions pvlib/clearsky.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@

import numpy as np
import pandas as pd
from scipy.optimize import minimize_scalar
from scipy.linalg import hankel
import h5py

Expand Down Expand Up @@ -874,25 +873,11 @@ def detect_clearsky(measured, clearsky, times=None, infer_limits=False,
clear_meas = meas[clear_samples]
clear_clear = clear[clear_samples]

def rmse(alpha):
return np.sqrt(np.mean((clear_meas - alpha*clear_clear)**2))

optimize_result = minimize_scalar(rmse)
if not optimize_result.success:
try:
message = "Optimizer exited unsuccessfully: " \
+ optimize_result.message
except AttributeError:
message = "Optimizer exited unsuccessfully: \
No message explaining the failure was returned. \
If you would like to see this message, please \
update your scipy version (try version 1.8.0 \
or beyond)."
raise RuntimeError(message)

else:
alpha = optimize_result.x

# Compute arg min of MSE between model and observations
C = (clear_clear**2).sum()
if not (pd.isna(C) or C == 0): # safety check
# only update alpha if C is strictly positive
alpha = (clear_meas * clear_clear).sum() / C
if round(alpha*10000) == round(previous_alpha*10000):
break
else:
Expand Down
13 changes: 9 additions & 4 deletions pvlib/tests/test_clearsky.py
Original file line number Diff line number Diff line change
Expand Up @@ -675,13 +675,18 @@ def test_detect_clearsky_missing_index(detect_clearsky_data):
def test_detect_clearsky_not_enough_data(detect_clearsky_data):
expected, cs = detect_clearsky_data
with pytest.raises(ValueError, match='have at least'):
clearsky.detect_clearsky(expected['GHI'], cs['ghi'], window_length=60)
clearsky.detect_clearsky(expected['GHI'], cs['ghi'], window_length=60)


def test_detect_clearsky_optimizer_failed(detect_clearsky_data):
@pytest.mark.parametrize("window_length", [5, 10, 15, 20, 25])
def test_detect_clearsky_optimizer_not_failed(
detect_clearsky_data, window_length
):
expected, cs = detect_clearsky_data
with pytest.raises(RuntimeError, match='Optimizer exited unsuccessfully'):
clearsky.detect_clearsky(expected['GHI'], cs['ghi'], window_length=15)
clear_samples = clearsky.detect_clearsky(
expected["GHI"], cs["ghi"], window_length=window_length
)
assert isinstance(clear_samples, pd.Series)


@pytest.fixture
Expand Down

0 comments on commit d6baf97

Please sign in to comment.