33from plotting_test import assert_isinstance
44import pytest
55import pvlib
6- import numpy as np
76import pandas as pd
87import 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