Skip to content

Commit 14db711

Browse files
committed
rework pytests
1 parent 7205658 commit 14db711

File tree

1 file changed

+199
-140
lines changed

1 file changed

+199
-140
lines changed

rdtools/test/analysis_test.py

Lines changed: 199 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -3,151 +3,210 @@
33
from plotting_test import assert_isinstance
44
import pytest
55
import pvlib
6-
import numpy as np
76
import pandas as pd
87
import matplotlib.pyplot as plt
98

10-
meta = {"latitude": -23.762028,
11-
"longitude": 133.874886,
12-
"timezone": 'Australia/North',
13-
"tempco": -0.005,
14-
"azimuth": 0,
15-
"tilt": 20}
169

10+
@pytest.fixture
11+
def basic_parameters():
12+
# basic parameters (no time series data) for the RdAnalysis class
13+
loc = pvlib.location.Location(-23.762028, 133.874886,
14+
tz='Australia/North')
15+
parameters = dict(
16+
temperature_coefficient=-0.005,
17+
pvlib_location=loc,
18+
pv_tilt=20,
19+
pv_azimuth=0,
20+
temperature_model={'a': -3.47, 'b': -0.0594, 'deltaT': 3}
21+
)
22+
return parameters
23+
24+
25+
@pytest.fixture
26+
def degradation_trend(basic_parameters):
27+
# smooth linear multi-year decline from 1.0 from degradation_test.py
28+
29+
# hide this import inside the function so that pytest doesn't find it
30+
# and run the degradation tests as a part of this module
31+
from degradation_test import DegradationTestCase
1732

18-
19-
@pytest.fixture()
20-
def get_energy():
21-
from degradation_test import DegradationTestCase #re-use degradation test fixture
2233
rd = -0.05
2334
input_freq = 'H'
24-
corr_energy = DegradationTestCase.get_corr_energy(rd, input_freq) # degradation_test.py
25-
return corr_energy
26-
27-
@pytest.fixture()
28-
def getRd(get_energy):
29-
loc = pvlib.location.Location(meta['latitude'], meta['longitude'], tz = meta['timezone'])
30-
power = get_energy.tz_localize(meta['timezone'])
31-
rd = analysis.RdAnalysis(power, power*1000, ambient_temperature = power*0+25, temperature_coefficient=meta['tempco'],
32-
pvlib_location=loc, pv_tilt=meta['tilt'], pv_azimuth=meta['azimuth'],
33-
interp_freq='D', temperature_model = {'a': -3.47, 'b': -0.0594, 'deltaT': 3}) # temperature_model = "open_rack_glass_glass"
34-
return rd
35-
36-
@pytest.fixture()
37-
def getRdCS(get_energy):
38-
loc = pvlib.location.Location(meta['latitude'], meta['longitude'], tz = meta['timezone'])
39-
power = get_energy.tz_localize(meta['timezone'])
40-
# initialize the RdAnalysis class
41-
temp = analysis.RdAnalysis(power, power*1000, pvlib_location=loc,
42-
pv_tilt=meta['tilt'], pv_azimuth=meta['azimuth'],
43-
temperature_coefficient=0 )
44-
# get clearsky expected
45-
temp.clearsky_preprocess() # get expected clear-sky values in clearsky_poa.
46-
cs = temp.clearsky_poa
47-
48-
rdCS = analysis.RdAnalysis(power*cs, cs, temperature_coefficient=meta['tempco'],
49-
pvlib_location=loc, pv_tilt=meta['tilt'], pv_azimuth=meta['azimuth'],
50-
temperature_model = {'a': -3.47, 'b': -0.0594, 'deltaT': 3}
51-
)
52-
return rdCS
53-
54-
@pytest.fixture()
55-
def getRdSoiling(normalized_daily):
56-
57-
loc = pvlib.location.Location(meta['latitude'], meta['longitude'], tz = meta['timezone'])
35+
degradation_trend = DegradationTestCase.get_corr_energy(rd, input_freq)
36+
tz = basic_parameters['pvlib_location'].tz
37+
return degradation_trend.tz_localize(tz)
38+
39+
40+
@pytest.fixture
41+
def sensor_parameters(basic_parameters, degradation_trend):
42+
# basic parameters plus time series data
43+
power = degradation_trend
44+
poa = power*1000
45+
ambient_temperature = power*0+25
46+
basic_parameters['pv'] = power
47+
basic_parameters['poa'] = poa
48+
basic_parameters['ambient_temperature'] = ambient_temperature
49+
return basic_parameters
50+
51+
52+
@pytest.fixture
53+
def sensor_analysis(sensor_parameters):
54+
rd_analysis = analysis.RdAnalysis(**sensor_parameters)
55+
rd_analysis.sensor_analysis(analyses=['yoy_degradation'])
56+
return rd_analysis
57+
58+
59+
def test_sensor_analysis(sensor_analysis):
60+
yoy_results = sensor_analysis.results['sensor']['yoy_degradation']
61+
rd = yoy_results['p50_rd']
62+
ci = yoy_results['rd_confidence_interval']
63+
64+
assert -1 == pytest.approx(rd, abs=1e-2)
65+
assert [-1, -1] == pytest.approx(ci, abs=1e-2)
66+
67+
68+
@pytest.fixture
69+
def clearsky_parameters(basic_parameters, sensor_parameters,
70+
degradation_trend):
71+
# clear-sky weather data. Uses RdAnalysis's internal clear-sky
72+
# functions to generate the data.
73+
rd_analysis = analysis.RdAnalysis(**sensor_parameters)
74+
rd_analysis.clearsky_preprocess()
75+
poa = rd_analysis.clearsky_poa
76+
basic_parameters['poa'] = poa
77+
basic_parameters['pv'] = poa * degradation_trend
78+
return basic_parameters
79+
80+
81+
@pytest.fixture
82+
def clearsky_analysis(clearsky_parameters):
83+
rd_analysis = analysis.RdAnalysis(**clearsky_parameters)
84+
rd_analysis.clearsky_analysis(analyses=['yoy_degradation'])
85+
return rd_analysis
86+
87+
88+
@pytest.fixture
89+
def clearsky_optional(clearsky_parameters, clearsky_analysis):
90+
# optional parameters to exercise other branches
91+
times = clearsky_analysis.poa.index
92+
extras = dict(
93+
clearsky_poa=clearsky_analysis.clearsky_poa,
94+
clearsky_cell_temperature=clearsky_analysis.clearsky_cell_temperature,
95+
clearsky_ambient_temperature=clearsky_analysis.clearsky_ambient_temperature,
96+
pv=clearsky_analysis.pv_energy,
97+
98+
# series orientation instead of scalars to exercise interpolation
99+
pv_tilt=pd.Series(clearsky_parameters['pv_tilt'], index=times),
100+
pv_azimuth=pd.Series(clearsky_parameters['pv_azimuth'], index=times)
101+
)
102+
# merge dicts, favoring new params over the ones in clearsky_parameters
103+
return {**clearsky_parameters, **extras}
104+
105+
106+
def test_clearsky_analysis(clearsky_analysis):
107+
yoy_results = clearsky_analysis.results['clearsky']['yoy_degradation']
108+
ci = yoy_results['rd_confidence_interval']
109+
rd = yoy_results['p50_rd']
110+
111+
assert -4.744 == pytest.approx(rd, abs=1e-3)
112+
assert [-4.756, -4.734] == pytest.approx(ci, abs=1e-3)
113+
114+
115+
def test_clearsky_analysis_optional(clearsky_parameters, clearsky_optional):
116+
rd_analysis = analysis.RdAnalysis(**clearsky_optional,
117+
pv_input='energy')
118+
rd_analysis.pv_power = clearsky_parameters['pv']
119+
rd_analysis.clearsky_analysis()
120+
yoy_results = rd_analysis.results['clearsky']['yoy_degradation']
121+
ci = yoy_results['rd_confidence_interval']
122+
rd = yoy_results['p50_rd']
123+
124+
assert -4.744 == pytest.approx(rd, abs=1e-2)
125+
assert [-4.756, -4.734] == pytest.approx(ci, abs=1e-3)
126+
127+
128+
@pytest.fixture
129+
def soiling_parameters(basic_parameters, normalized_daily):
130+
# parameters for soiling analysis with RdAnalysis
58131
power = normalized_daily.resample('1h').interpolate()
59-
poa = power*0+1000
60-
rdSoiling = analysis.RdAnalysis(power,poa, cell_temperature = power*0+25, pvlib_location=loc,
61-
temperature_coefficient=0, interp_freq='D',
62-
pv_tilt=meta['tilt'], pv_azimuth=meta['azimuth'] )
63-
return rdSoiling
64-
65-
66-
def test_sensor_analysis_fixture(getRd):
67-
68-
getRd.sensor_analysis(analyses=['yoy_degradation'])
69-
70-
yoy_results = getRd.results['sensor']['yoy_degradation']
71-
72-
assert -1 == pytest.approx(yoy_results['p50_rd'], abs=1e-2)
73-
assert [-1, -1] == pytest.approx(yoy_results['rd_confidence_interval'], abs=1e-2)
74-
75-
76-
def test_clearsky_analysis_fixture(getRdCS):
77-
78-
getRdCS.clearsky_analysis()
79-
cs_yoy_results = getRdCS.results['clearsky']['yoy_degradation']
80-
81-
assert -4.744 == pytest.approx(cs_yoy_results['p50_rd'], abs=1e-3)
82-
assert [-4.756, -4.734] == pytest.approx(cs_yoy_results['rd_confidence_interval'], abs=1e-3)
83-
84-
# Re-run while passing some of the clearsky values back into the original instance to improve test coverage
85-
poa = getRdCS.poa
86-
cspoa = getRdCS.clearsky_poa
87-
cscell = getRdCS.clearsky_cell_temperature
88-
csamb = getRdCS.clearsky_ambient_temperature
89-
pv = getRdCS.pv_energy
90-
loc = pvlib.location.Location(meta['latitude'], meta['longitude'], tz = meta['timezone'])
91-
92-
rdCS2 = analysis.RdAnalysis(pv, poa=poa, clearsky_cell_temperature=cscell,
93-
clearsky_poa=cspoa, clearsky_ambient_temperature=csamb, pv_input='energy',
94-
pvlib_location=loc, pv_tilt=pd.Series(data=meta['tilt'], index=pv.index),
95-
pv_azimuth=pd.Series(data=meta['azimuth'], index=pv.index),
96-
temperature_model = {'a': -3.47, 'b': -0.0594, 'deltaT': 3}, interp_freq='1H',
97-
temperature_coefficient=meta['tempco'])
98-
rdCS2.pv_power = getRdCS.pv_power
99-
rdCS2.clearsky_analysis()
100-
cs_yoy_results2 = rdCS2.results['clearsky']['yoy_degradation']
101-
assert -4.744 == pytest.approx(cs_yoy_results2['p50_rd'], abs=1e-2)
102-
assert [-4.756, -4.734] == pytest.approx(cs_yoy_results2['rd_confidence_interval'], abs=1e-3)
103-
104-
105-
106-
def test_srr_soiling_fixture(getRdSoiling): # same as test_srr_soiling just using test fixtures.
107-
108-
getRdSoiling.sensor_analysis(analyses=['srr_soiling'], srr_kwargs={'reps':10})
109-
110-
srr_results = getRdSoiling.results['sensor']['srr_soiling']
111-
112-
assert 0.9583 == pytest.approx(srr_results['p50_sratio'], abs=1e-4),\
113-
'Soiling ratio different from expected value in RdAnalysis.srr_soiling'
114-
assert [0.9552, 0.9607] == pytest.approx(srr_results['sratio_confidence_interval'], abs=1e-4),\
115-
'Soiling confidence interval different from expected value in RdAnalysis.srr_soiling'
116-
assert 0.97417 == pytest.approx(srr_results['calc_info']['renormalizing_factor'], abs=1e-4),\
117-
'Renormalization factor different from expected value in RdAnalysis.srr_soiling'
118-
119-
def test_plot_degradation(getRd):
120-
getRd.sensor_analysis(analyses=['yoy_degradation'])
121-
assert_isinstance(getRd.plot_degradation_summary('sensor'), plt.Figure)
122-
assert_isinstance(getRd.plot_pv_vs_irradiance('sensor'), plt.Figure)
123-
124-
def test_plot_cs(getRdCS):
125-
getRdCS.clearsky_analysis(analyses=['yoy_degradation'])
126-
assert_isinstance(getRdCS.plot_degradation_summary('clearsky'), plt.Figure)
127-
assert_isinstance(getRdCS.plot_pv_vs_irradiance('clearsky'), plt.Figure)
128-
129-
def test_plot_soiling(getRdSoiling):
130-
getRdSoiling.sensor_analysis(analyses=['srr_soiling'], srr_kwargs={'reps':10})
131-
assert_isinstance(getRdSoiling.plot_soiling_monte_carlo('sensor'), plt.Figure)
132-
assert_isinstance(getRdSoiling.plot_soiling_interval('sensor'), plt.Figure)
133-
assert_isinstance(getRdSoiling.plot_soiling_rate_histogram('sensor'), plt.Figure)
134-
135-
def test_plot_soiling_cs(getRdSoiling):
136-
getRdSoiling.clearsky_analysis(analyses=['srr_soiling'], srr_kwargs={'reps':10})
137-
assert_isinstance(getRdSoiling.plot_soiling_monte_carlo('clearsky'), plt.Figure)
138-
assert_isinstance(getRdSoiling.plot_soiling_interval('clearsky'), plt.Figure)
139-
assert_isinstance(getRdSoiling.plot_soiling_rate_histogram('clearsky'), plt.Figure)
140-
141-
def test_errors(get_energy, getRdCS):
142-
# clearsky analysis with no pvlib.loc or tilt or azimuth
143-
power = get_energy.tz_localize(meta['timezone'])
144-
rdtemp = analysis.RdAnalysis(power)
145-
with pytest.raises(ValueError):
146-
rdtemp.sensor_preprocess() # no POA error
147-
rdtemp = analysis.RdAnalysis(power,power*1000)
148-
with pytest.raises(ValueError):
149-
rdtemp.sensor_preprocess() # no temperature error
150-
getRdCS.pvlib_location=None
151-
with pytest.raises(ValueError):
152-
getRdCS.clearsky_preprocess() #pvlib location must be provided
153-
132+
return dict(
133+
pv=power,
134+
poa=power * 0 + 1000,
135+
cell_temperature=power * 0 + 25,
136+
pvlib_location=basic_parameters['pvlib_location'],
137+
temperature_coefficient=0,
138+
interp_freq='D',
139+
pv_tilt=basic_parameters['pv_tilt'],
140+
pv_azimuth=basic_parameters['pv_azimuth']
141+
)
142+
143+
144+
@pytest.fixture
145+
def soiling_analysis_sensor(soiling_parameters):
146+
soiling_analysis = analysis.RdAnalysis(**soiling_parameters)
147+
soiling_analysis.sensor_analysis(analyses=['srr_soiling'],
148+
srr_kwargs={'reps': 10})
149+
return soiling_analysis
150+
151+
152+
@pytest.fixture
153+
def soiling_analysis_clearsky(soiling_parameters):
154+
soiling_analysis = analysis.RdAnalysis(**soiling_parameters)
155+
soiling_analysis.clearsky_analysis(analyses=['srr_soiling'],
156+
srr_kwargs={'reps': 10})
157+
return soiling_analysis
158+
159+
160+
def test_srr_soiling(soiling_analysis_sensor):
161+
srr_results = soiling_analysis_sensor.results['sensor']['srr_soiling']
162+
sratio = srr_results['p50_sratio']
163+
ci = srr_results['sratio_confidence_interval']
164+
renorm_factor = srr_results['calc_info']['renormalizing_factor']
165+
166+
assert 0.9583 == pytest.approx(sratio, abs=1e-4),\
167+
'Soiling ratio different from expected value in RdAnalysis.srr_soiling'
168+
assert [0.9552, 0.9607] == pytest.approx(ci, abs=1e-4),\
169+
'Soiling confidence interval different from expected value in RdAnalysis.srr_soiling'
170+
assert 0.97417 == pytest.approx(renorm_factor, abs=1e-4),\
171+
'Renormalization factor different from expected value in RdAnalysis.srr_soiling'
172+
173+
174+
def test_plot_degradation(sensor_analysis):
175+
assert_isinstance(sensor_analysis.plot_degradation_summary('sensor'), plt.Figure)
176+
assert_isinstance(sensor_analysis.plot_pv_vs_irradiance('sensor'), plt.Figure)
177+
178+
179+
def test_plot_cs(clearsky_analysis):
180+
assert_isinstance(clearsky_analysis.plot_degradation_summary('clearsky'), plt.Figure)
181+
assert_isinstance(clearsky_analysis.plot_pv_vs_irradiance('clearsky'), plt.Figure)
182+
183+
184+
def test_plot_soiling(soiling_analysis_sensor):
185+
assert_isinstance(soiling_analysis_sensor.plot_soiling_monte_carlo('sensor'), plt.Figure)
186+
assert_isinstance(soiling_analysis_sensor.plot_soiling_interval('sensor'), plt.Figure)
187+
assert_isinstance(soiling_analysis_sensor.plot_soiling_rate_histogram('sensor'), plt.Figure)
188+
189+
190+
def test_plot_soiling_cs(soiling_analysis_clearsky):
191+
assert_isinstance(soiling_analysis_clearsky.plot_soiling_monte_carlo('clearsky'), plt.Figure)
192+
assert_isinstance(soiling_analysis_clearsky.plot_soiling_interval('clearsky'), plt.Figure)
193+
assert_isinstance(soiling_analysis_clearsky.plot_soiling_rate_histogram('clearsky'), plt.Figure)
194+
195+
196+
def test_errors(sensor_parameters, clearsky_analysis):
197+
198+
rdtemp = analysis.RdAnalysis(sensor_parameters['pv'])
199+
with pytest.raises(ValueError, match='poa must be available'):
200+
rdtemp.sensor_preprocess()
201+
202+
# no temperature
203+
rdtemp = analysis.RdAnalysis(sensor_parameters['pv'],
204+
poa=sensor_parameters['poa'])
205+
with pytest.raises(ValueError, match='either cell or ambient temperature'):
206+
rdtemp.sensor_preprocess()
207+
208+
# clearsky analysis with no pvlib.loc
209+
clearsky_analysis.pvlib_location = None
210+
clearsky_analysis.clearsky_poa = None
211+
with pytest.raises(ValueError, match='pvlib location must be provided'):
212+
clearsky_analysis.clearsky_preprocess()

0 commit comments

Comments
 (0)