def test_soiling_srr_with_precip(normalized_daily, insolation, times): precip = pd.Series(index=times, data=0) precip['2019-01-18 00:00:00-07:00'] = 1 precip['2019-02-20 00:00:00-07:00'] = 1 kwargs = {'reps': 10, 'precipitation_daily': precip} np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_daily, insolation, clean_criterion='precip_and_shift', **kwargs) assert 0.983270 == pytest.approx(sr, abs=1e-6),\ "Soiling ratio with clean_criterion='precip_and_shift' different from expected" np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_daily, insolation, clean_criterion='precip_or_shift', **kwargs) assert 0.973228 == pytest.approx(sr, abs=1e-6),\ "Soiling ratio with clean_criterion='precip_or_shift' different from expected" np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_daily, insolation, clean_criterion='precip', **kwargs) assert 0.976196 == pytest.approx(sr, abs=1e-6),\ "Soiling ratio with clean_criterion='precip' different from expected" np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_daily, insolation, clean_criterion='shift', **kwargs) assert 0.963133 == pytest.approx(sr, abs=1e-6),\ "Soiling ratio with clean_criterion='shift' different from expected"
def test_soiling_srr_min_interval_length(normalized_daily, insolation): 'Test that a long minimum interval length prevents finding shorter intervals' with pytest.raises(NoValidIntervalError): np.random.seed(1977) # normalized_daily intervals are 25 days long, so min=26 should fail: _ = soiling_srr(normalized_daily, insolation, confidence_level=68.2, reps=10, min_interval_length=26) # but min=24 should be fine: _ = soiling_srr(normalized_daily, insolation, confidence_level=68.2, reps=10, min_interval_length=24)
def test_soiling_srr_method(normalized_daily, insolation): np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_daily, insolation, reps=10, method='random_clean') assert 0.918767 == pytest.approx(sr, abs=1e-6),\ 'Soiling ratio with method="random_clean" different from expected value' np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_daily, insolation, reps=10, method='perfect_clean') assert 0.965653 == pytest.approx(sr, abs=1e-6),\ 'Soiling ratio with method="perfect_clean" different from expected value'
def test_soiling_srr_clean_threshold(normalized_daily, insolation): '''Test that clean test_soiling_srr_clean_threshold works with a float and can cause no soiling intervals to be found''' np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_daily, insolation, reps=10, clean_threshold=0.01) assert 0.963133 == pytest.approx(sr, abs=1e-6),\ 'Soiling ratio with specified clean_threshold different from expected value' with pytest.raises(NoValidIntervalError): np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_daily, insolation, reps=10, clean_threshold=0.1)
def test_soiling_srr_outlier_factor(soiling_normalized_daily, soiling_insolation): _, _, info = soiling_srr(soiling_normalized_daily, soiling_insolation, reps=1, outlier_factor=8) assert len(info['soiling_interval_summary']) == 2,\ 'Increasing the outlier_factor did not result in the expected number of soiling intervals'
def test_soiling_srr_recenter_false(normalized_daily, insolation): np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_daily, insolation, reps=10, recenter=False) assert 1 == soiling_info['renormalizing_factor'],\ 'Renormalizing factor != 1 with recenter=False' assert 0.965158 == pytest.approx(sr, abs=1e-6),\ 'Soiling ratio different than expected when recenter=False'
def test_soiling_srr_method(soiling_normalized_daily, soiling_insolation, method, expected_sr): np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily, soiling_insolation, reps=10, method=method) assert expected_sr == pytest.approx(sr, abs=1e-6),\ f'Soiling ratio with method="{method}" different from expected value'
def test_soiling_srr_dayscale(normalized_daily, insolation): 'Test that a long dayscale can prevent valid intervals from being found' with pytest.raises(NoValidIntervalError): np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_daily, insolation, confidence_level=68.2, reps=10, day_scale=90)
def test_soiling_srr_trim(normalized_daily, insolation): np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_daily, insolation, reps=10, trim=True) assert 0.978369 == pytest.approx(sr, abs=1e-6),\ 'Soiling ratio with trim=True different from expected value' assert len(soiling_info['soiling_interval_summary']) == 1,\ 'Wrong number of soiling intervals found with trim=True'
def test_soiling_srr_confidence_levels(normalized_daily, insolation): 'Tests SRR with different confidence level settingsf from above' np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_daily, insolation, confidence_level=95, reps=10, exceedance_prob=80.0) assert np.array([0.957272, 0.964763]) == pytest.approx(sr_ci, abs=1e-6),\ 'Confidence interval with confidence_level=95 different than expected' assert 0.961285 == pytest.approx(soiling_info['exceedance_level'], abs=1e-6),\ 'soiling_info["exceedance_level"] different than expected when exceedance_prob=80'
def test_soiling_srr_max_negative_slope_error(normalized_daily, insolation): np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_daily, insolation, reps=10, max_relative_slope_error=50.0) assert list(soiling_info['soiling_interval_summary']['valid'].values) == [True, True, False],\ 'Soiling interval validity differs from expected when max_relative_slope_error=50.0' assert 0.952995 == pytest.approx(sr, abs=1e-6),\ 'Soiling ratio different from expected when max_relative_slope_error=50.0'
def test_soiling_srr_argument_checks(soiling_normalized_daily, soiling_insolation): ''' Make sure various argument validation warnings and errors are raised ''' kwargs = { 'energy_normalized_daily': soiling_normalized_daily, 'insolation_daily': soiling_insolation, 'reps': 10 } with pytest.warns(UserWarning, match='An even value of day_scale was passed'): _ = soiling_srr(day_scale=12, **kwargs) with pytest.raises(ValueError, match='clean_criterion must be one of'): _ = soiling_srr(clean_criterion='bad', **kwargs) with pytest.raises(ValueError, match='Invalid method specification'): _ = soiling_srr(method='bad', **kwargs)
def test_soiling_srr_kwargs(monkeypatch, normalized_daily, insolation): ''' Make sure that all soiling_srr parameters get passed on to SRRAnalysis and SRRAnalysis.run(), i.e. all necessary inputs to SRRAnalysis are provided by soiling_srr. Done by removing the SRRAnalysis default param values and making sure everything still runs. ''' # the __defaults__ attr is the tuple of default values in py3 monkeypatch.delattr(SRRAnalysis.__init__, "__defaults__") monkeypatch.delattr(SRRAnalysis.run, "__defaults__") _ = soiling_srr(normalized_daily, insolation, reps=10)
def test_soiling_srr_with_nan_interval(normalized_daily, insolation, times): ''' Previous versions had a bug which would have raised an error when an entire interval was NaN. See https://github.com/NREL/rdtools/issues/129 ''' reps = 10 normalized_corrupt = normalized_daily.copy() normalized_corrupt[26:50] = np.nan np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_corrupt, insolation, reps=reps) assert 0.947416 == pytest.approx(sr, abs=1e-6),\ 'Soiling ratio different from expected value when an entire interval was NaN'
def test_soiling_srr_consecutive_invalid(soiling_normalized_daily, soiling_insolation, soiling_times, method, expected_sr): reps = 10 np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily, soiling_insolation, reps=reps, max_relative_slope_error=20.0, method=method) assert expected_sr == pytest.approx(sr, abs=1e-6),\ f'Soiling ratio different from expected value for {method} with consecutive invalid intervals' # noqa: E501
def test_soiling_srr_negative_step(normalized_daily, insolation): stepped_daily = normalized_daily.copy() stepped_daily.iloc[37:] = stepped_daily.iloc[25:] - 0.1 np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(stepped_daily, insolation, reps=10) assert list(soiling_info['soiling_interval_summary']['valid'].values) == [True, False, True],\ 'Soiling interval validity differs from expected when a large negative step\ is incorporated into the data' assert 0.934927 == pytest.approx(sr, abs=1e-6),\ 'Soiling ratio different from expected when a large negative step is incorporated into the data'
def test_soiling_srr(normalized_daily, insolation, times): reps = 10 np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(normalized_daily, insolation, reps=reps) assert 0.963133 == pytest.approx(sr, abs=1e-6),\ 'Soiling ratio different from expected value' assert np.array([0.961054, 0.964019]) == pytest.approx(sr_ci, abs=1e-6),\ 'Confidence interval different from expected value' assert 0.958292 == pytest.approx(soiling_info['exceedance_level'], abs=1e-6),\ 'Exceedance level different from expected value' assert 0.984079 == pytest.approx(soiling_info['renormalizing_factor'], abs=1e-6),\ 'Renormalizing factor different from expected value' assert len(soiling_info['stochastic_soiling_profiles']) == reps,\ 'Length of soiling_info["stochastic_soiling_profiles"] different than expected' assert isinstance(soiling_info['stochastic_soiling_profiles'], list),\ 'soiling_info["stochastic_soiling_profiles"] is not a list' # Check soiling_info['soiling_interval_summary'] expected_summary_columns = ['start', 'end', 'soiling_rate', 'soiling_rate_low', 'soiling_rate_high', 'inferred_start_loss', 'inferred_end_loss', 'length', 'valid'] actual_summary_columns = soiling_info['soiling_interval_summary'].columns.values for x in actual_summary_columns: assert x in expected_summary_columns,\ "'{}' not an expected column in soiling_info['soiling_interval_summary']".format(x) for x in expected_summary_columns: assert x in actual_summary_columns,\ "'{}' was expected as a column, but not in soiling_info['soiling_interval_summary']".format(x) assert isinstance(soiling_info['soiling_interval_summary'], pd.DataFrame),\ 'soiling_info["soiling_interval_summary"] not a dataframe' expected_means = pd.Series({'soiling_rate': -0.002617290, 'soiling_rate_low': -0.002828525, 'soiling_rate_high': -0.002396639, 'inferred_start_loss': 1.021514, 'inferred_end_loss': 0.9572880, 'length': 24.0, 'valid': 1.0}) expected_means = expected_means[['soiling_rate', 'soiling_rate_low', 'soiling_rate_high', 'inferred_start_loss', 'inferred_end_loss', 'length', 'valid']] pd.testing.assert_series_equal(expected_means, soiling_info['soiling_interval_summary'].mean(), check_exact=False, check_less_precise=6) # Check soiling_info['soiling_ratio_perfect_clean'] pd.testing.assert_index_equal(soiling_info['soiling_ratio_perfect_clean'].index, times, check_names=False) assert 0.967170 == pytest.approx(soiling_info['soiling_ratio_perfect_clean'].mean(), abs=1e-6),\ "The mean of soiling_info['soiling_ratio_perfect_clean'] differs from expected" assert isinstance(soiling_info['soiling_ratio_perfect_clean'], pd.Series),\ 'soiling_info["soiling_ratio_perfect_clean"] not a pandas series'
def test_soiling_srr_max_negative_slope_error(soiling_normalized_daily, soiling_insolation): np.random.seed(1977) with pytest.warns(UserWarning, match='20% or more of the daily data'): sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily, soiling_insolation, reps=10, max_relative_slope_error=45.0) assert list(soiling_info['soiling_interval_summary']['valid'].values) == [True, True, False],\ 'Soiling interval validity differs from expected when max_relative_slope_error=45.0' assert 0.958761 == pytest.approx(sr, abs=1e-6),\ 'Soiling ratio different from expected when max_relative_slope_error=45.0'
def test_soiling_srr_min_interval_length_default(soiling_normalized_daily, soiling_insolation, start, expected_sr): ''' Make sure that the default value of min_interval_length is 7 days by testing on a cropped version of the example data ''' reps = 10 np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily[start:], soiling_insolation[start:], reps=reps) assert expected_sr == pytest.approx(sr, abs=1e-6),\ 'Soiling ratio different from expected value'
def test_soiling_srr_with_precip(soiling_normalized_daily, soiling_insolation, soiling_times, clean_criterion, expected_sr): precip = pd.Series(index=soiling_times, data=0) precip['2019-01-18 00:00:00-07:00'] = 1 precip['2019-02-20 00:00:00-07:00'] = 1 kwargs = {'reps': 10, 'precipitation_daily': precip} np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily, soiling_insolation, clean_criterion=clean_criterion, **kwargs) assert expected_sr == pytest.approx(sr, abs=1e-6),\ f"Soiling ratio with clean_criterion='{clean_criterion}' different from expected"
def test_soiling_srr_with_nan_interval(soiling_normalized_daily, soiling_insolation): ''' Previous versions had a bug which would have raised an error when an entire interval was NaN. See https://github.com/NREL/rdtools/issues/129 ''' reps = 10 normalized_corrupt = soiling_normalized_daily.copy() normalized_corrupt[26:50] = np.nan np.random.seed(1977) with pytest.warns(UserWarning, match='20% or more of the daily data'): sr, sr_ci, soiling_info = soiling_srr(normalized_corrupt, soiling_insolation, reps=reps) assert 0.948792 == pytest.approx(sr, abs=1e-6),\ 'Soiling ratio different from expected value when an entire interval was NaN'
def test_soiling_srr_negative_step(soiling_normalized_daily, soiling_insolation): stepped_daily = soiling_normalized_daily.copy() stepped_daily.iloc[37:] = stepped_daily.iloc[37:] - 0.1 np.random.seed(1977) with pytest.warns(UserWarning, match='20% or more of the daily data'): sr, sr_ci, soiling_info = soiling_srr(stepped_daily, soiling_insolation, reps=10) assert list(soiling_info['soiling_interval_summary']['valid'].values) == [True, False, True],\ 'Soiling interval validity differs from expected when a large negative step\ is incorporated into the data' assert 0.936932 == pytest.approx(sr, abs=1e-6),\ 'Soiling ratio different from expected when a large negative step is incorporated into the data' # noqa: E501
def soiling_info(soiling_normalized_daily, soiling_insolation): ''' Return results of running soiling_srr. Returns ------- calc_info : dict with keys: ['renormalizing_factor', 'exceedance_level', 'stochastic_soiling_profiles', 'soiling_interval_summary', 'soiling_ratio_perfect_clean'] ''' reps = 10 np.random.seed(1977) sr, sr_ci, calc_info = soiling_srr(soiling_normalized_daily, soiling_insolation, reps=reps) return calc_info
def _srr_soiling(self, energy_normalized_daily, insolation_daily, **kwargs): ''' Perform stochastic rate and recovery soiling analysis. Parameters --------- energy_normalized_daily : pandas.Series Time Series of insolation-weighted aggregated normalized PV energy insolation_daily : pandas.Series Time Series of insolation, aggregated at same level as energy_normalized_daily kwargs : Extra parameters passed to soiling.soiling_srr() Returns ------- dict Soiling results with keys: 'p50_sratio' : The median insolation-weighted soiling ratio 'sratio_confidence_interval' : list of lower and upper bounds of insolation-weighted soiling ratio confidence interval 'calc_info' : Dict of detailed results (see soiling.soiling_srr() docs) ''' from rdtools import soiling daily_freq = pd.tseries.offsets.Day() if (energy_normalized_daily.index.freq != daily_freq or insolation_daily.index.freq != daily_freq): raise ValueError( 'Soiling SRR analysis requires daily aggregation.') sr, sr_ci, soiling_info = soiling.soiling_srr(energy_normalized_daily, insolation_daily, **kwargs) srr_results = { 'p50_sratio': sr, 'sratio_confidence_interval': sr_ci, 'calc_info': soiling_info } return srr_results
def test_soiling_srr(soiling_normalized_daily, soiling_insolation, soiling_times): reps = 10 np.random.seed(1977) sr, sr_ci, soiling_info = soiling_srr(soiling_normalized_daily, soiling_insolation, reps=reps) assert 0.964369 == pytest.approx(sr, abs=1e-6),\ 'Soiling ratio different from expected value' assert np.array([0.962540, 0.965295]) == pytest.approx(sr_ci, abs=1e-6),\ 'Confidence interval different from expected value' assert 0.960205 == pytest.approx(soiling_info['exceedance_level'], abs=1e-6),\ 'Exceedance level different from expected value' assert 0.984079 == pytest.approx(soiling_info['renormalizing_factor'], abs=1e-6),\ 'Renormalizing factor different from expected value' assert len(soiling_info['stochastic_soiling_profiles']) == reps,\ 'Length of soiling_info["stochastic_soiling_profiles"] different than expected' assert isinstance(soiling_info['stochastic_soiling_profiles'], list),\ 'soiling_info["stochastic_soiling_profiles"] is not a list' # Check soiling_info['soiling_interval_summary'] expected_summary_columns = [ 'start', 'end', 'soiling_rate', 'soiling_rate_low', 'soiling_rate_high', 'inferred_start_loss', 'inferred_end_loss', 'length', 'valid' ] actual_summary_columns = soiling_info[ 'soiling_interval_summary'].columns.values for x in actual_summary_columns: assert x in expected_summary_columns,\ f"'{x}' not an expected column in soiling_info['soiling_interval_summary']" for x in expected_summary_columns: assert x in actual_summary_columns,\ f"'{x}' was expected as a column, but not in soiling_info['soiling_interval_summary']" assert isinstance(soiling_info['soiling_interval_summary'], pd.DataFrame),\ 'soiling_info["soiling_interval_summary"] not a dataframe' expected_means = pd.Series({ 'soiling_rate': -0.002644544, 'soiling_rate_low': -0.002847504, 'soiling_rate_high': -0.002455915, 'inferred_start_loss': 1.020124, 'inferred_end_loss': 0.9566552, 'length': 24.0, 'valid': 1.0 }) expected_means = expected_means[[ 'soiling_rate', 'soiling_rate_low', 'soiling_rate_high', 'inferred_start_loss', 'inferred_end_loss', 'length', 'valid' ]] actual_means = soiling_info['soiling_interval_summary'][ expected_means.index].mean() pd.testing.assert_series_equal(expected_means, actual_means, check_exact=False) # Check soiling_info['soiling_ratio_perfect_clean'] pd.testing.assert_index_equal( soiling_info['soiling_ratio_perfect_clean'].index, soiling_times, check_names=False) sr_mean = soiling_info['soiling_ratio_perfect_clean'].mean() assert 0.968265 == pytest.approx(sr_mean, abs=1e-6),\ "The mean of soiling_info['soiling_ratio_perfect_clean'] differs from expected" assert isinstance(soiling_info['soiling_ratio_perfect_clean'], pd.Series),\ 'soiling_info["soiling_ratio_perfect_clean"] not a pandas series'