Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add missing tests #413

Merged
merged 25 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
c390d9b
add test for hour_angle_filter
martin-springer Apr 29, 2024
e366176
add test_directional_tukey_filter
martin-springer Apr 29, 2024
f99a214
add test_insolation_filter
martin-springer Apr 29, 2024
05b0af3
add test_two_way_window_filter
martin-springer Apr 29, 2024
dd06d29
add bootstrap additive test
martin-springer Apr 29, 2024
8172208
bootstrap_test fix linting
martin-springer Apr 29, 2024
c45d438
run blake
martin-springer Apr 30, 2024
4c84bdc
bootstrap test for value error
martin-springer Apr 30, 2024
f1378d9
irradiance rescale test for value error
martin-springer Apr 30, 2024
b072b57
test for ValueError
martin-springer Apr 30, 2024
6ac03a1
flake8 ignore E203
martin-springer Apr 30, 2024
73d60c0
add pvlib clearsky filter
martin-springer May 1, 2024
664a7a1
add aggregated filter tests to analysis chain tests
martin-springer May 21, 2024
0a11728
add missing line
martin-springer May 22, 2024
fb13bac
analysis chain hour angle filter test
martin-springer May 22, 2024
99d4913
liniting
martin-springer May 22, 2024
d942380
sensor_clearsky_filter vs sensor_pvlib_clearsky_filter
martin-springer Jun 25, 2024
42142c2
update pandocfilters to 1.5.1
martin-springer Jun 25, 2024
c6bf396
restrict numpy<2.0
martin-springer Jun 25, 2024
18e10f7
CODS testing turn on verbose flag
martin-springer Jun 25, 2024
e28ab84
add test coverage to changelog
martin-springer Jun 26, 2024
d82814b
add test for sensor analysis with clearsky filtering
martin-springer Jun 26, 2024
445ddab
fix flake8 error
mdeceglie Jun 26, 2024
9dcbb2b
fix duplicate expected result entry
martin-springer Jun 27, 2024
f2c48cd
unify pytest.raises to with statement convention
martin-springer Jun 27, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@
# see https://flake8.pycqa.org/en/latest/user/options.html

[flake8]
# E203 is not PEP8 compliant https://black.readthedocs.io/en/stable/the_black_code_style/current_style.html#slices
# Is excluded from flake8's own config https://flake8.pycqa.org/en/latest/user/configuration.html
extend-ignore = E203
max-line-length = 99
max-doc-length = 99
per-file-ignores =
# rdtools.x.y imported but unused
__init__.py:F401
# invalid escape sequence '\s'
versioneer.py:W605
exclude =
exclude =
docs
.eggs
build
2 changes: 1 addition & 1 deletion docs/notebook_requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ nbformat==5.1.0
nest-asyncio==1.5.5
notebook==6.4.12
numexpr==2.10.0
pandocfilters==1.4.2
pandocfilters==1.5.1
parso==0.5.2
pexpect==4.6.0
pickleshare==0.7.5
Expand Down
1 change: 1 addition & 0 deletions docs/sphinx/source/changelog/pending.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ when compared with older versions of RdTools
Enhancements
------------
* Added a new wrapper function for clearsky filters (:pull:`412`)
* Improve test coverage, especially for the newly added filter capabilities (:pull:`413`)

Bug fixes
---------
Expand Down
5 changes: 5 additions & 0 deletions rdtools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
from rdtools.filtering import logic_clip_filter
from rdtools.filtering import xgboost_clip_filter
from rdtools.filtering import normalized_filter
from rdtools.filtering import two_way_window_filter
from rdtools.filtering import insolation_filter
from rdtools.filtering import hampel_filter
from rdtools.filtering import hour_angle_filter
from rdtools.filtering import directional_tukey_filter
# from rdtools.soiling import soiling_srr
# from rdtools.soiling import soiling_cods
# from rdtools.soiling import monthly_soiling_rates
Expand Down
1 change: 1 addition & 0 deletions rdtools/analysis_chains.py
Original file line number Diff line number Diff line change
Expand Up @@ -532,6 +532,7 @@ def _call_clearsky_filter(filter_string):
filter_components["clearsky_filter"] = _call_clearsky_filter(
"clearsky_filter"
)

if "sensor_clearsky_filter" in self.filter_params:
filter_components["sensor_clearsky_filter"] = _call_clearsky_filter(
"sensor_clearsky_filter"
Expand Down
88 changes: 87 additions & 1 deletion rdtools/test/analysis_chains_test.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from rdtools import TrendAnalysis, normalization
from rdtools import TrendAnalysis, normalization, filtering
from conftest import assert_isinstance, assert_warnings
import pytest
import pvlib
Expand Down Expand Up @@ -78,6 +78,15 @@ def sensor_analysis_exp_power(sensor_parameters):
return rd_analysis


@pytest.fixture
def sensor_analysis_aggregated_no_filter(sensor_parameters):
rd_analysis = TrendAnalysis(**sensor_parameters, power_dc_rated=1.0)
rd_analysis.filter_params = {} # disable all index-based filters
rd_analysis.filter_params_aggregated = {}
rd_analysis.sensor_analysis(analyses=["yoy_degradation"])
return rd_analysis


def test_interpolation(basic_parameters, degradation_trend):

power = degradation_trend
Expand Down Expand Up @@ -213,6 +222,19 @@ def test_filter_components(sensor_parameters):
assert (poa_filter == rd_analysis.sensor_filter_components["poa_filter"]).all()


def test_filter_components_hour_angle(sensor_parameters, cs_input):
lat = cs_input["pvlib_location"].latitude
lon = cs_input["pvlib_location"].longitude
hour_angle_filter = filtering.hour_angle_filter(sensor_parameters["pv"], lat, lon)
rd_analysis = TrendAnalysis(**sensor_parameters, power_dc_rated=1.0)
rd_analysis.pvlib_location = cs_input['pvlib_location']
rd_analysis.filter_params = {'hour_angle_filter': {}}
rd_analysis.filter_params_aggregated = {}
rd_analysis.sensor_analysis(analyses=["yoy_degradation"])
assert (hour_angle_filter[1:] ==
rd_analysis.sensor_filter_components["hour_angle_filter"]).all()


def test_aggregated_filter_components(sensor_parameters):
daily_ad_hoc_filter = pd.Series(True, index=sensor_parameters["pv"].index)
daily_ad_hoc_filter[:600] = False
Expand Down Expand Up @@ -247,6 +269,51 @@ def test_aggregated_filter_components_no_filters(sensor_parameters):
assert rd_analysis.sensor_filter_components.empty


def test_aggregated_filter_components_two_way_window_filter(sensor_analysis_aggregated_no_filter):
rd_analysis = sensor_analysis_aggregated_no_filter
aggregated_no_filter = rd_analysis.sensor_aggregated_performance
rd_analysis.filter_params_aggregated = {"two_way_window_filter": {}}
rd_analysis.sensor_analysis(analyses=["yoy_degradation"])
daily_expected = filtering.two_way_window_filter(aggregated_no_filter)
pd.testing.assert_series_equal(
rd_analysis.sensor_filter_aggregated, daily_expected, check_names=False
)


def test_aggregated_filter_components_insolation_filter(sensor_analysis_aggregated_no_filter):
rd_analysis = sensor_analysis_aggregated_no_filter
aggregated_no_filter = rd_analysis.sensor_aggregated_performance
rd_analysis.filter_params_aggregated = {"insolation_filter": {}}
rd_analysis.sensor_analysis(analyses=["yoy_degradation"])
daily_expected = filtering.insolation_filter(aggregated_no_filter)
pd.testing.assert_series_equal(
rd_analysis.sensor_filter_aggregated, daily_expected, check_names=False
)


def test_aggregated_filter_components_hampel_filter(sensor_analysis_aggregated_no_filter):
rd_analysis = sensor_analysis_aggregated_no_filter
aggregated_no_filter = rd_analysis.sensor_aggregated_performance
rd_analysis.filter_params_aggregated = {"hampel_filter": {}}
rd_analysis.sensor_analysis(analyses=["yoy_degradation"])
daily_expected = filtering.hampel_filter(aggregated_no_filter)
pd.testing.assert_series_equal(
rd_analysis.sensor_filter_aggregated, daily_expected, check_names=False
)


def test_aggregated_filter_components_directional_tukey_filter(
sensor_analysis_aggregated_no_filter):
rd_analysis = sensor_analysis_aggregated_no_filter
aggregated_no_filter = rd_analysis.sensor_aggregated_performance
rd_analysis.filter_params_aggregated = {"directional_tukey_filter": {}}
rd_analysis.sensor_analysis(analyses=["yoy_degradation"])
daily_expected = filtering.directional_tukey_filter(aggregated_no_filter)
pd.testing.assert_series_equal(
rd_analysis.sensor_filter_aggregated, daily_expected, check_names=False
)


@pytest.mark.parametrize("workflow", ["sensor", "clearsky"])
def test_filter_ad_hoc_warnings(workflow, sensor_parameters):
rd_analysis = TrendAnalysis(**sensor_parameters, power_dc_rated=1.0)
Expand Down Expand Up @@ -400,6 +467,16 @@ def clearsky_optional(cs_input, clearsky_analysis):
return extras


@pytest.fixture
def sensor_clearsky_analysis(cs_input, clearsky_parameters):
rd_analysis = TrendAnalysis(**clearsky_parameters)
rd_analysis.set_clearsky(**cs_input)
rd_analysis.filter_params = {} # disable all index-based filters
rd_analysis.filter_params["sensor_clearsky_filter"] = {"model": "csi"}
rd_analysis.sensor_analysis(analyses=["yoy_degradation"])
return rd_analysis


def test_clearsky_analysis(clearsky_analysis):
yoy_results = clearsky_analysis.results["clearsky"]["yoy_degradation"]
ci = yoy_results["rd_confidence_interval"]
Expand All @@ -423,6 +500,15 @@ def test_clearsky_analysis_optional(
assert [-4.71, -4.69] == pytest.approx(ci, abs=1e-2)


def test_sensor_clearsky_analysis(sensor_clearsky_analysis):
yoy_results = sensor_clearsky_analysis.results["sensor"]["yoy_degradation"]
ci = yoy_results["rd_confidence_interval"]
rd = yoy_results["p50_rd"]
print(ci)
assert -5.18 == pytest.approx(rd, abs=1e-2)
assert [-5.18, -5.18] == pytest.approx(ci, abs=1e-2)


@pytest.fixture
def clearsky_analysis_exp_power(clearsky_parameters, clearsky_optional):
power_expected = normalization.pvwatts_dc_power(
Expand Down
78 changes: 52 additions & 26 deletions rdtools/test/bootstrap_test.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,58 @@
'''Bootstrap module tests.'''
"""Bootstrap module tests."""

from rdtools.bootstrap import _construct_confidence_intervals, \
_make_time_series_bootstrap_samples
import pytest

from rdtools.bootstrap import (
_construct_confidence_intervals,
_make_time_series_bootstrap_samples,
)
from rdtools.degradation import degradation_year_on_year


def test_bootstrap_module(cods_normalized_daily, cods_normalized_daily_wo_noise):
''' Test make time serie bootstrap samples and construct of confidence intervals. '''
# Test make bootstrap samples
bootstrap_samples = _make_time_series_bootstrap_samples(cods_normalized_daily,
cods_normalized_daily_wo_noise,
sample_nr=10,
block_length=90,
decomposition_type='multiplicative')
# Check if results are as expected
assert (bootstrap_samples.index == cods_normalized_daily.index).all(), \
"Index of bootstrapped signals is not as expected"
assert bootstrap_samples.shape[1] == 10, "Number of columns in bootstrapped signals is wrong"
@pytest.mark.parametrize("decomposition_type", ["multiplicative", "additive", "error"])
def test_bootstrap_module(
cods_normalized_daily, cods_normalized_daily_wo_noise, decomposition_type
):

if decomposition_type == "error":
with pytest.raises(ValueError):
_make_time_series_bootstrap_samples(
cods_normalized_daily,
cods_normalized_daily_wo_noise,
decomposition_type=decomposition_type)
else:
# Rest make time serie bootstrap samples and construct of confidence intervals.
# Test make bootstrap samples
bootstrap_samples = _make_time_series_bootstrap_samples(
cods_normalized_daily,
cods_normalized_daily_wo_noise,
sample_nr=10,
block_length=90,
decomposition_type=decomposition_type,
)
# Check if results are as expected
assert (
bootstrap_samples.index == cods_normalized_daily.index
).all(), "Index of bootstrapped signals is not as expected"
assert (
bootstrap_samples.shape[1] == 10
), "Number of columns in bootstrapped signals is wrong"

# Test construction of confidence intervals
confidence_intervals, exceedance_level, metrics = _construct_confidence_intervals(
bootstrap_samples, degradation_year_on_year, uncertainty_method='none')
# Test construction of confidence intervals
confidence_intervals, exceedance_level, metrics = (
_construct_confidence_intervals(
bootstrap_samples, degradation_year_on_year, uncertainty_method="none"
)
)

# Check if results are as expected
assert len(confidence_intervals) == 2, "2 confidence interval bounds not returned"
assert isinstance(confidence_intervals[0], float) and \
isinstance(confidence_intervals[1], float), "Confidence interval bounds are not float"
assert isinstance(exceedance_level, float), "Exceedance level is not float"
assert len(metrics) == 10, "Length of metrics is not as expected"
for m in metrics:
assert isinstance(m, float), "Not all metrics are float"
# Check if results are as expected
assert (
len(confidence_intervals) == 2
), "2 confidence interval bounds not returned"
assert isinstance(confidence_intervals[0], float) and isinstance(
confidence_intervals[1], float
), "Confidence interval bounds are not float"
assert isinstance(exceedance_level, float), "Exceedance level is not float"
assert len(metrics) == 10, "Length of metrics is not as expected"
for m in metrics:
assert isinstance(m, float), "Not all metrics are float"
Loading
Loading