pvlib 0.11.1__py3-none-any.whl → 0.12.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pvlib/__init__.py +1 -0
- pvlib/_deprecation.py +73 -0
- pvlib/atmosphere.py +77 -7
- pvlib/bifacial/infinite_sheds.py +4 -3
- pvlib/bifacial/utils.py +2 -1
- pvlib/clearsky.py +35 -22
- pvlib/iam.py +4 -4
- pvlib/iotools/midc.py +1 -1
- pvlib/iotools/psm3.py +1 -1
- pvlib/iotools/pvgis.py +10 -12
- pvlib/iotools/tmy.py +3 -69
- pvlib/irradiance.py +112 -55
- pvlib/ivtools/sdm.py +75 -52
- pvlib/location.py +73 -33
- pvlib/modelchain.py +18 -35
- pvlib/pvsystem.py +139 -94
- pvlib/snow.py +64 -28
- pvlib/solarposition.py +46 -30
- pvlib/spa.py +4 -2
- pvlib/spectrum/__init__.py +0 -1
- pvlib/spectrum/irradiance.py +2 -64
- pvlib/spectrum/mismatch.py +3 -3
- pvlib/spectrum/spectrl2.py +2 -1
- pvlib/temperature.py +49 -3
- pvlib/tools.py +6 -5
- {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info}/METADATA +14 -11
- pvlib-0.12.0.dist-info/RECORD +75 -0
- {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info}/WHEEL +1 -1
- pvlib/data/BIRD_08_16_2012.csv +0 -8761
- pvlib/data/BIRD_08_16_2012_patm.csv +0 -8761
- pvlib/data/Burlington, United States SolarAnywhere Time Series 2021 Lat_44_465 Lon_-73_205 TMY3 format.csv +0 -8762
- pvlib/data/Burlington, United States SolarAnywhere Time Series 20210101 to 20210103 Lat_44_4675 Lon_-73_2075 SA format.csv +0 -578
- pvlib/data/Burlington, United States SolarAnywhere Typical GHI Year Lat_44_465 Lon_-73_205 SA format.csv +0 -74
- pvlib/data/CPS SCH275KTL-DO-US-800-250kW_275kVA_1.OND +0 -146
- pvlib/data/CRNS0101-05-2019-AZ_Tucson_11_W.txt +0 -4
- pvlib/data/CRN_with_problems.txt +0 -3
- pvlib/data/ET-M772BH550GL.PAN +0 -75
- pvlib/data/NLD_Amsterdam062400_IWEC.epw +0 -8768
- pvlib/data/PVsyst_demo.csv +0 -10757
- pvlib/data/PVsyst_demo_model.csv +0 -3588
- pvlib/data/SRML-day-EUPO1801.txt +0 -1441
- pvlib/data/abq19056.dat +0 -6
- pvlib/data/aod550_tcwv_20121101_test.nc +0 -0
- pvlib/data/bishop88_numerical_precision.csv +0 -101
- pvlib/data/bsrn-lr0100-pay0616.dat +0 -86901
- pvlib/data/bsrn-pay0616.dat.gz +0 -0
- pvlib/data/cams_mcclear_1min_verbose.csv +0 -60
- pvlib/data/cams_mcclear_monthly.csv +0 -42
- pvlib/data/cams_radiation_1min_verbose.csv +0 -72
- pvlib/data/cams_radiation_monthly.csv +0 -47
- pvlib/data/detect_clearsky_data.csv +0 -35
- pvlib/data/detect_clearsky_threshold_data.csv +0 -126
- pvlib/data/greensboro_kimber_soil_manwash.dat +0 -8761
- pvlib/data/greensboro_kimber_soil_nowash.dat +0 -8761
- pvlib/data/inverter_fit_snl_meas.csv +0 -127
- pvlib/data/inverter_fit_snl_sim.csv +0 -19
- pvlib/data/ivtools_numdiff.csv +0 -52
- pvlib/data/midc_20181014.txt +0 -1441
- pvlib/data/midc_raw_20181018.txt +0 -1441
- pvlib/data/midc_raw_short_header_20191115.txt +0 -1441
- pvlib/data/msn19056.dat +0 -6
- pvlib/data/precise_iv_curves1.json +0 -10251
- pvlib/data/precise_iv_curves2.json +0 -10251
- pvlib/data/precise_iv_curves_parameter_sets1.csv +0 -33
- pvlib/data/precise_iv_curves_parameter_sets2.csv +0 -33
- pvlib/data/pvgis_hourly_Timeseries_45.000_8.000_SA2_10kWp_CIS_5_2a_2013_2014.json +0 -1
- pvlib/data/pvgis_hourly_Timeseries_45.000_8.000_SA_30deg_0deg_2016_2016.csv +0 -35
- pvlib/data/pvgis_tmy_meta.json +0 -32
- pvlib/data/pvgis_tmy_test.dat +0 -8761
- pvlib/data/pvwatts_8760_rackmount.csv +0 -8779
- pvlib/data/pvwatts_8760_roofmount.csv +0 -8779
- pvlib/data/singleaxis_tracker_wslope.csv +0 -8761
- pvlib/data/spectrl2_example_spectra.csv +0 -123
- pvlib/data/surfrad-slv16001.dat +0 -1442
- pvlib/data/test_psm3_2017.csv +0 -17521
- pvlib/data/test_psm3_2019_5min.csv +0 -289
- pvlib/data/test_psm3_tmy-2017.csv +0 -8761
- pvlib/data/test_read_psm3.csv +0 -17523
- pvlib/data/test_read_pvgis_horizon.csv +0 -49
- pvlib/data/tmy_45.000_8.000_2005_2020.csv +0 -8789
- pvlib/data/tmy_45.000_8.000_2005_2020.epw +0 -8768
- pvlib/data/tmy_45.000_8.000_2005_2020.json +0 -1
- pvlib/data/tmy_45.000_8.000_2005_2020.txt +0 -8761
- pvlib/data/tmy_45.000_8.000_userhorizon.json +0 -1
- pvlib/data/variables_style_rules.csv +0 -56
- pvlib/spa_c_files/README.md +0 -81
- pvlib/spa_c_files/cspa_py.pxd +0 -43
- pvlib/spa_c_files/spa_py.pyx +0 -30
- pvlib/tests/__init__.py +0 -0
- pvlib/tests/bifacial/__init__.py +0 -0
- pvlib/tests/bifacial/test_infinite_sheds.py +0 -317
- pvlib/tests/bifacial/test_losses_models.py +0 -54
- pvlib/tests/bifacial/test_pvfactors.py +0 -82
- pvlib/tests/bifacial/test_utils.py +0 -192
- pvlib/tests/conftest.py +0 -476
- pvlib/tests/iotools/__init__.py +0 -0
- pvlib/tests/iotools/test_acis.py +0 -213
- pvlib/tests/iotools/test_bsrn.py +0 -131
- pvlib/tests/iotools/test_crn.py +0 -95
- pvlib/tests/iotools/test_epw.py +0 -23
- pvlib/tests/iotools/test_midc.py +0 -89
- pvlib/tests/iotools/test_panond.py +0 -32
- pvlib/tests/iotools/test_psm3.py +0 -198
- pvlib/tests/iotools/test_pvgis.py +0 -644
- pvlib/tests/iotools/test_sodapro.py +0 -298
- pvlib/tests/iotools/test_solaranywhere.py +0 -287
- pvlib/tests/iotools/test_solargis.py +0 -68
- pvlib/tests/iotools/test_solcast.py +0 -324
- pvlib/tests/iotools/test_solrad.py +0 -152
- pvlib/tests/iotools/test_srml.py +0 -124
- pvlib/tests/iotools/test_surfrad.py +0 -75
- pvlib/tests/iotools/test_tmy.py +0 -133
- pvlib/tests/ivtools/__init__.py +0 -0
- pvlib/tests/ivtools/test_sde.py +0 -230
- pvlib/tests/ivtools/test_sdm.py +0 -407
- pvlib/tests/ivtools/test_utils.py +0 -173
- pvlib/tests/spectrum/__init__.py +0 -0
- pvlib/tests/spectrum/conftest.py +0 -40
- pvlib/tests/spectrum/test_irradiance.py +0 -138
- pvlib/tests/spectrum/test_mismatch.py +0 -304
- pvlib/tests/spectrum/test_response.py +0 -124
- pvlib/tests/spectrum/test_spectrl2.py +0 -72
- pvlib/tests/test_albedo.py +0 -84
- pvlib/tests/test_atmosphere.py +0 -204
- pvlib/tests/test_clearsky.py +0 -878
- pvlib/tests/test_conftest.py +0 -81
- pvlib/tests/test_iam.py +0 -555
- pvlib/tests/test_inverter.py +0 -213
- pvlib/tests/test_irradiance.py +0 -1441
- pvlib/tests/test_location.py +0 -356
- pvlib/tests/test_modelchain.py +0 -2020
- pvlib/tests/test_numerical_precision.py +0 -124
- pvlib/tests/test_pvarray.py +0 -71
- pvlib/tests/test_pvsystem.py +0 -2495
- pvlib/tests/test_scaling.py +0 -207
- pvlib/tests/test_shading.py +0 -391
- pvlib/tests/test_singlediode.py +0 -608
- pvlib/tests/test_snow.py +0 -212
- pvlib/tests/test_soiling.py +0 -230
- pvlib/tests/test_solarposition.py +0 -933
- pvlib/tests/test_spa.py +0 -425
- pvlib/tests/test_temperature.py +0 -470
- pvlib/tests/test_tools.py +0 -146
- pvlib/tests/test_tracking.py +0 -474
- pvlib/tests/test_transformer.py +0 -60
- pvlib-0.11.1.dist-info/RECORD +0 -192
- {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info/licenses}/AUTHORS.md +0 -0
- {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info/licenses}/LICENSE +0 -0
- {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info}/top_level.txt +0 -0
pvlib/tests/test_modelchain.py
DELETED
|
@@ -1,2020 +0,0 @@
|
|
|
1
|
-
import sys
|
|
2
|
-
|
|
3
|
-
import numpy as np
|
|
4
|
-
import pandas as pd
|
|
5
|
-
|
|
6
|
-
from pvlib import iam, modelchain, pvsystem, temperature, inverter
|
|
7
|
-
from pvlib.modelchain import ModelChain
|
|
8
|
-
from pvlib.pvsystem import PVSystem
|
|
9
|
-
from pvlib.location import Location
|
|
10
|
-
from pvlib._deprecation import pvlibDeprecationWarning
|
|
11
|
-
|
|
12
|
-
from .conftest import assert_series_equal, assert_frame_equal
|
|
13
|
-
import pytest
|
|
14
|
-
|
|
15
|
-
from .conftest import fail_on_pvlib_version
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@pytest.fixture(scope='function')
|
|
19
|
-
def sapm_dc_snl_ac_system(sapm_module_params, cec_inverter_parameters,
|
|
20
|
-
sapm_temperature_cs5p_220m):
|
|
21
|
-
module = 'Canadian_Solar_CS5P_220M___2009_'
|
|
22
|
-
module_parameters = sapm_module_params.copy()
|
|
23
|
-
temp_model_params = sapm_temperature_cs5p_220m.copy()
|
|
24
|
-
system = PVSystem(surface_tilt=32.2, surface_azimuth=180,
|
|
25
|
-
module=module,
|
|
26
|
-
module_parameters=module_parameters,
|
|
27
|
-
temperature_model_parameters=temp_model_params,
|
|
28
|
-
inverter_parameters=cec_inverter_parameters)
|
|
29
|
-
return system
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
@pytest.fixture
|
|
33
|
-
def cec_dc_snl_ac_system(cec_module_cs5p_220m, cec_inverter_parameters,
|
|
34
|
-
sapm_temperature_cs5p_220m):
|
|
35
|
-
module_parameters = cec_module_cs5p_220m.copy()
|
|
36
|
-
module_parameters['b'] = 0.05
|
|
37
|
-
module_parameters['EgRef'] = 1.121
|
|
38
|
-
module_parameters['dEgdT'] = -0.0002677
|
|
39
|
-
temp_model_params = sapm_temperature_cs5p_220m.copy()
|
|
40
|
-
system = PVSystem(surface_tilt=32.2, surface_azimuth=180,
|
|
41
|
-
module=module_parameters['Name'],
|
|
42
|
-
module_parameters=module_parameters,
|
|
43
|
-
temperature_model_parameters=temp_model_params,
|
|
44
|
-
inverter_parameters=cec_inverter_parameters)
|
|
45
|
-
return system
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@pytest.fixture
|
|
49
|
-
def cec_dc_snl_ac_arrays(cec_module_cs5p_220m, cec_inverter_parameters,
|
|
50
|
-
sapm_temperature_cs5p_220m):
|
|
51
|
-
module_parameters = cec_module_cs5p_220m.copy()
|
|
52
|
-
module_parameters['b'] = 0.05
|
|
53
|
-
module_parameters['EgRef'] = 1.121
|
|
54
|
-
module_parameters['dEgdT'] = -0.0002677
|
|
55
|
-
temp_model_params = sapm_temperature_cs5p_220m.copy()
|
|
56
|
-
array_one = pvsystem.Array(
|
|
57
|
-
mount=pvsystem.FixedMount(surface_tilt=32.2, surface_azimuth=180),
|
|
58
|
-
module=module_parameters['Name'],
|
|
59
|
-
module_parameters=module_parameters.copy(),
|
|
60
|
-
temperature_model_parameters=temp_model_params.copy()
|
|
61
|
-
)
|
|
62
|
-
array_two = pvsystem.Array(
|
|
63
|
-
mount=pvsystem.FixedMount(surface_tilt=42.2, surface_azimuth=220),
|
|
64
|
-
module=module_parameters['Name'],
|
|
65
|
-
module_parameters=module_parameters.copy(),
|
|
66
|
-
temperature_model_parameters=temp_model_params.copy()
|
|
67
|
-
)
|
|
68
|
-
system = PVSystem(
|
|
69
|
-
arrays=[array_one, array_two],
|
|
70
|
-
inverter_parameters=cec_inverter_parameters
|
|
71
|
-
)
|
|
72
|
-
return system
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
@pytest.fixture
|
|
76
|
-
def cec_dc_native_snl_ac_system(cec_module_cs5p_220m, cec_inverter_parameters,
|
|
77
|
-
sapm_temperature_cs5p_220m):
|
|
78
|
-
module_parameters = cec_module_cs5p_220m.copy()
|
|
79
|
-
temp_model_params = sapm_temperature_cs5p_220m.copy()
|
|
80
|
-
system = PVSystem(surface_tilt=32.2, surface_azimuth=180,
|
|
81
|
-
module=module_parameters['Name'],
|
|
82
|
-
module_parameters=module_parameters,
|
|
83
|
-
temperature_model_parameters=temp_model_params,
|
|
84
|
-
inverter_parameters=cec_inverter_parameters)
|
|
85
|
-
return system
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
@pytest.fixture
|
|
89
|
-
def pvsyst_dc_snl_ac_system(pvsyst_module_params, cec_inverter_parameters,
|
|
90
|
-
sapm_temperature_cs5p_220m):
|
|
91
|
-
module = 'PVsyst test module'
|
|
92
|
-
module_parameters = pvsyst_module_params
|
|
93
|
-
module_parameters['b'] = 0.05
|
|
94
|
-
temp_model_params = sapm_temperature_cs5p_220m.copy()
|
|
95
|
-
system = PVSystem(surface_tilt=32.2, surface_azimuth=180,
|
|
96
|
-
module=module,
|
|
97
|
-
module_parameters=module_parameters,
|
|
98
|
-
temperature_model_parameters=temp_model_params,
|
|
99
|
-
inverter_parameters=cec_inverter_parameters)
|
|
100
|
-
return system
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
@pytest.fixture
|
|
104
|
-
def pvsyst_dc_snl_ac_arrays(pvsyst_module_params, cec_inverter_parameters,
|
|
105
|
-
sapm_temperature_cs5p_220m):
|
|
106
|
-
module = 'PVsyst test module'
|
|
107
|
-
module_parameters = pvsyst_module_params
|
|
108
|
-
module_parameters['b'] = 0.05
|
|
109
|
-
temp_model_params = sapm_temperature_cs5p_220m.copy()
|
|
110
|
-
array_one = pvsystem.Array(
|
|
111
|
-
mount=pvsystem.FixedMount(surface_tilt=32.2, surface_azimuth=180),
|
|
112
|
-
module=module,
|
|
113
|
-
module_parameters=module_parameters.copy(),
|
|
114
|
-
temperature_model_parameters=temp_model_params.copy()
|
|
115
|
-
)
|
|
116
|
-
array_two = pvsystem.Array(
|
|
117
|
-
mount=pvsystem.FixedMount(surface_tilt=42.2, surface_azimuth=220),
|
|
118
|
-
module=module,
|
|
119
|
-
module_parameters=module_parameters.copy(),
|
|
120
|
-
temperature_model_parameters=temp_model_params.copy()
|
|
121
|
-
)
|
|
122
|
-
system = PVSystem(
|
|
123
|
-
arrays=[array_one, array_two],
|
|
124
|
-
inverter_parameters=cec_inverter_parameters
|
|
125
|
-
)
|
|
126
|
-
return system
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
@pytest.fixture
|
|
130
|
-
def cec_dc_adr_ac_system(sam_data, cec_module_cs5p_220m,
|
|
131
|
-
sapm_temperature_cs5p_220m):
|
|
132
|
-
module_parameters = cec_module_cs5p_220m.copy()
|
|
133
|
-
module_parameters['b'] = 0.05
|
|
134
|
-
module_parameters['EgRef'] = 1.121
|
|
135
|
-
module_parameters['dEgdT'] = -0.0002677
|
|
136
|
-
temp_model_params = sapm_temperature_cs5p_220m.copy()
|
|
137
|
-
inverters = sam_data['adrinverter']
|
|
138
|
-
inverter = inverters['Zigor__Sunzet_3_TL_US_240V__CEC_2011_'].copy()
|
|
139
|
-
system = PVSystem(surface_tilt=32.2, surface_azimuth=180,
|
|
140
|
-
module=module_parameters['Name'],
|
|
141
|
-
module_parameters=module_parameters,
|
|
142
|
-
temperature_model_parameters=temp_model_params,
|
|
143
|
-
inverter_parameters=inverter)
|
|
144
|
-
return system
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
@pytest.fixture
|
|
148
|
-
def pvwatts_dc_snl_ac_system(cec_inverter_parameters):
|
|
149
|
-
module_parameters = {'pdc0': 220, 'gamma_pdc': -0.003}
|
|
150
|
-
system = PVSystem(surface_tilt=32.2, surface_azimuth=180,
|
|
151
|
-
module_parameters=module_parameters,
|
|
152
|
-
inverter_parameters=cec_inverter_parameters)
|
|
153
|
-
return system
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
@pytest.fixture(scope="function")
|
|
157
|
-
def pvwatts_dc_pvwatts_ac_system(sapm_temperature_cs5p_220m):
|
|
158
|
-
module_parameters = {'pdc0': 220, 'gamma_pdc': -0.003}
|
|
159
|
-
temp_model_params = sapm_temperature_cs5p_220m.copy()
|
|
160
|
-
inverter_parameters = {'pdc0': 220, 'eta_inv_nom': 0.95}
|
|
161
|
-
system = PVSystem(surface_tilt=32.2, surface_azimuth=180,
|
|
162
|
-
module_parameters=module_parameters,
|
|
163
|
-
temperature_model_parameters=temp_model_params,
|
|
164
|
-
inverter_parameters=inverter_parameters)
|
|
165
|
-
return system
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
@pytest.fixture(scope="function")
|
|
169
|
-
def pvwatts_dc_pvwatts_ac_system_arrays(sapm_temperature_cs5p_220m):
|
|
170
|
-
module_parameters = {'pdc0': 220, 'gamma_pdc': -0.003}
|
|
171
|
-
temp_model_params = sapm_temperature_cs5p_220m.copy()
|
|
172
|
-
inverter_parameters = {'pdc0': 220, 'eta_inv_nom': 0.95}
|
|
173
|
-
array_one = pvsystem.Array(
|
|
174
|
-
mount=pvsystem.FixedMount(surface_tilt=32.2, surface_azimuth=180),
|
|
175
|
-
module_parameters=module_parameters.copy(),
|
|
176
|
-
temperature_model_parameters=temp_model_params.copy()
|
|
177
|
-
)
|
|
178
|
-
array_two = pvsystem.Array(
|
|
179
|
-
mount=pvsystem.FixedMount(surface_tilt=42.2, surface_azimuth=220),
|
|
180
|
-
module_parameters=module_parameters.copy(),
|
|
181
|
-
temperature_model_parameters=temp_model_params.copy()
|
|
182
|
-
)
|
|
183
|
-
system = PVSystem(
|
|
184
|
-
arrays=[array_one, array_two], inverter_parameters=inverter_parameters)
|
|
185
|
-
return system
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
@pytest.fixture(scope="function")
|
|
189
|
-
def pvwatts_dc_pvwatts_ac_faiman_temp_system():
|
|
190
|
-
module_parameters = {'pdc0': 220, 'gamma_pdc': -0.003}
|
|
191
|
-
temp_model_params = {'u0': 25.0, 'u1': 6.84}
|
|
192
|
-
inverter_parameters = {'pdc0': 220, 'eta_inv_nom': 0.95}
|
|
193
|
-
system = PVSystem(surface_tilt=32.2, surface_azimuth=180,
|
|
194
|
-
module_parameters=module_parameters,
|
|
195
|
-
temperature_model_parameters=temp_model_params,
|
|
196
|
-
inverter_parameters=inverter_parameters)
|
|
197
|
-
return system
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
@pytest.fixture(scope="function")
|
|
201
|
-
def pvwatts_dc_pvwatts_ac_pvsyst_temp_system():
|
|
202
|
-
module_parameters = {'pdc0': 220, 'gamma_pdc': -0.003}
|
|
203
|
-
temp_model_params = {'u_c': 29.0, 'u_v': 0.0, 'module_efficiency': 0.1,
|
|
204
|
-
'alpha_absorption': 0.9}
|
|
205
|
-
inverter_parameters = {'pdc0': 220, 'eta_inv_nom': 0.95}
|
|
206
|
-
system = PVSystem(surface_tilt=32.2, surface_azimuth=180,
|
|
207
|
-
module_parameters=module_parameters,
|
|
208
|
-
temperature_model_parameters=temp_model_params,
|
|
209
|
-
inverter_parameters=inverter_parameters)
|
|
210
|
-
return system
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
@pytest.fixture(scope="function")
|
|
214
|
-
def pvwatts_dc_pvwatts_ac_fuentes_temp_system():
|
|
215
|
-
module_parameters = {'pdc0': 220, 'gamma_pdc': -0.003}
|
|
216
|
-
temp_model_params = {'noct_installed': 45}
|
|
217
|
-
inverter_parameters = {'pdc0': 220, 'eta_inv_nom': 0.95}
|
|
218
|
-
system = PVSystem(surface_tilt=32.2, surface_azimuth=180,
|
|
219
|
-
module_parameters=module_parameters,
|
|
220
|
-
temperature_model_parameters=temp_model_params,
|
|
221
|
-
inverter_parameters=inverter_parameters)
|
|
222
|
-
return system
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
@pytest.fixture(scope="function")
|
|
226
|
-
def pvwatts_dc_pvwatts_ac_noct_sam_temp_system():
|
|
227
|
-
module_parameters = {'pdc0': 220, 'gamma_pdc': -0.003}
|
|
228
|
-
temp_model_params = {'noct': 45, 'module_efficiency': 0.2}
|
|
229
|
-
inverter_parameters = {'pdc0': 220, 'eta_inv_nom': 0.95}
|
|
230
|
-
system = PVSystem(surface_tilt=32.2, surface_azimuth=180,
|
|
231
|
-
module_parameters=module_parameters,
|
|
232
|
-
temperature_model_parameters=temp_model_params,
|
|
233
|
-
inverter_parameters=inverter_parameters)
|
|
234
|
-
return system
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
@pytest.fixture(scope="function")
|
|
238
|
-
def system_no_aoi(cec_module_cs5p_220m, sapm_temperature_cs5p_220m,
|
|
239
|
-
cec_inverter_parameters):
|
|
240
|
-
module_parameters = cec_module_cs5p_220m.copy()
|
|
241
|
-
module_parameters['EgRef'] = 1.121
|
|
242
|
-
module_parameters['dEgdT'] = -0.0002677
|
|
243
|
-
temp_model_params = sapm_temperature_cs5p_220m.copy()
|
|
244
|
-
inverter_parameters = cec_inverter_parameters.copy()
|
|
245
|
-
system = PVSystem(surface_tilt=32.2, surface_azimuth=180,
|
|
246
|
-
module_parameters=module_parameters,
|
|
247
|
-
temperature_model_parameters=temp_model_params,
|
|
248
|
-
inverter_parameters=inverter_parameters)
|
|
249
|
-
return system
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
@pytest.fixture
|
|
253
|
-
def system_no_temp(cec_module_cs5p_220m, cec_inverter_parameters):
|
|
254
|
-
module_parameters = cec_module_cs5p_220m.copy()
|
|
255
|
-
module_parameters['EgRef'] = 1.121
|
|
256
|
-
module_parameters['dEgdT'] = -0.0002677
|
|
257
|
-
inverter_parameters = cec_inverter_parameters.copy()
|
|
258
|
-
system = PVSystem(surface_tilt=32.2, surface_azimuth=180,
|
|
259
|
-
module_parameters=module_parameters,
|
|
260
|
-
inverter_parameters=inverter_parameters)
|
|
261
|
-
return system
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
@pytest.fixture
|
|
265
|
-
def location():
|
|
266
|
-
return Location(32.2, -111, altitude=700)
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
@pytest.fixture
|
|
270
|
-
def weather():
|
|
271
|
-
times = pd.date_range('20160101 1200-0700', periods=2, freq='6h')
|
|
272
|
-
weather = pd.DataFrame({'ghi': [500, 0], 'dni': [800, 0], 'dhi': [100, 0]},
|
|
273
|
-
index=times)
|
|
274
|
-
return weather
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
@pytest.fixture
|
|
278
|
-
def total_irrad(weather):
|
|
279
|
-
return pd.DataFrame({'poa_global': [800., 500.],
|
|
280
|
-
'poa_direct': [500., 300.],
|
|
281
|
-
'poa_diffuse': [300., 200.]}, index=weather.index)
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
@pytest.fixture(scope='function')
|
|
285
|
-
def sapm_dc_snl_ac_system_Array(sapm_module_params, cec_inverter_parameters,
|
|
286
|
-
sapm_temperature_cs5p_220m):
|
|
287
|
-
module = 'Canadian_Solar_CS5P_220M___2009_'
|
|
288
|
-
module_parameters = sapm_module_params.copy()
|
|
289
|
-
temp_model_params = sapm_temperature_cs5p_220m.copy()
|
|
290
|
-
array_one = pvsystem.Array(mount=pvsystem.FixedMount(surface_tilt=32,
|
|
291
|
-
surface_azimuth=180),
|
|
292
|
-
albedo=0.2, module=module,
|
|
293
|
-
module_parameters=module_parameters,
|
|
294
|
-
temperature_model_parameters=temp_model_params,
|
|
295
|
-
modules_per_string=1,
|
|
296
|
-
strings=1)
|
|
297
|
-
array_two = pvsystem.Array(mount=pvsystem.FixedMount(surface_tilt=15,
|
|
298
|
-
surface_azimuth=180),
|
|
299
|
-
albedo=0.2, module=module,
|
|
300
|
-
module_parameters=module_parameters,
|
|
301
|
-
temperature_model_parameters=temp_model_params,
|
|
302
|
-
modules_per_string=1,
|
|
303
|
-
strings=1)
|
|
304
|
-
return PVSystem(arrays=[array_one, array_two],
|
|
305
|
-
inverter_parameters=cec_inverter_parameters)
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
@pytest.fixture(scope='function')
|
|
309
|
-
def sapm_dc_snl_ac_system_same_arrays(sapm_module_params,
|
|
310
|
-
cec_inverter_parameters,
|
|
311
|
-
sapm_temperature_cs5p_220m):
|
|
312
|
-
"""A system with two identical arrays."""
|
|
313
|
-
module = 'Canadian_Solar_CS5P_220M___2009_'
|
|
314
|
-
module_parameters = sapm_module_params.copy()
|
|
315
|
-
temp_model_params = sapm_temperature_cs5p_220m.copy()
|
|
316
|
-
array_one = pvsystem.Array(mount=pvsystem.FixedMount(surface_tilt=32.2,
|
|
317
|
-
surface_azimuth=180),
|
|
318
|
-
module=module,
|
|
319
|
-
module_parameters=module_parameters,
|
|
320
|
-
temperature_model_parameters=temp_model_params,
|
|
321
|
-
modules_per_string=1,
|
|
322
|
-
strings=1)
|
|
323
|
-
array_two = pvsystem.Array(mount=pvsystem.FixedMount(surface_tilt=32.2,
|
|
324
|
-
surface_azimuth=180),
|
|
325
|
-
module=module,
|
|
326
|
-
module_parameters=module_parameters,
|
|
327
|
-
temperature_model_parameters=temp_model_params,
|
|
328
|
-
modules_per_string=1,
|
|
329
|
-
strings=1)
|
|
330
|
-
return PVSystem(arrays=[array_one, array_two],
|
|
331
|
-
inverter_parameters=cec_inverter_parameters)
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
def test_ModelChain_creation(sapm_dc_snl_ac_system, location):
|
|
335
|
-
ModelChain(sapm_dc_snl_ac_system, location)
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
def test_with_sapm(sapm_dc_snl_ac_system, location, weather):
|
|
339
|
-
mc = ModelChain.with_sapm(sapm_dc_snl_ac_system, location)
|
|
340
|
-
assert mc.dc_model == mc.sapm
|
|
341
|
-
mc.run_model(weather)
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
def test_with_pvwatts(pvwatts_dc_pvwatts_ac_system, location, weather):
|
|
345
|
-
mc = ModelChain.with_pvwatts(pvwatts_dc_pvwatts_ac_system, location)
|
|
346
|
-
assert mc.dc_model == mc.pvwatts_dc
|
|
347
|
-
assert mc.temperature_model == mc.sapm_temp
|
|
348
|
-
mc.run_model(weather)
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
def test_run_model_with_irradiance(sapm_dc_snl_ac_system, location):
|
|
352
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
353
|
-
times = pd.date_range('20160101 1200-0700', periods=2, freq='6h')
|
|
354
|
-
irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150},
|
|
355
|
-
index=times)
|
|
356
|
-
ac = mc.run_model(irradiance).results.ac
|
|
357
|
-
|
|
358
|
-
expected = pd.Series(np.array([187.80746494643176, -0.02]),
|
|
359
|
-
index=times)
|
|
360
|
-
assert_series_equal(ac, expected)
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
@pytest.fixture(scope='function')
|
|
364
|
-
def multi_array_sapm_dc_snl_ac_system(
|
|
365
|
-
sapm_temperature_cs5p_220m, sapm_module_params,
|
|
366
|
-
cec_inverter_parameters):
|
|
367
|
-
module_parameters = sapm_module_params
|
|
368
|
-
temp_model_parameters = sapm_temperature_cs5p_220m.copy()
|
|
369
|
-
inverter_parameters = cec_inverter_parameters
|
|
370
|
-
array_one = pvsystem.Array(
|
|
371
|
-
mount=pvsystem.FixedMount(surface_tilt=32.2, surface_azimuth=180),
|
|
372
|
-
module_parameters=module_parameters,
|
|
373
|
-
temperature_model_parameters=temp_model_parameters
|
|
374
|
-
)
|
|
375
|
-
array_two = pvsystem.Array(
|
|
376
|
-
mount=pvsystem.FixedMount(surface_tilt=32.2, surface_azimuth=220),
|
|
377
|
-
module_parameters=module_parameters,
|
|
378
|
-
temperature_model_parameters=temp_model_parameters
|
|
379
|
-
)
|
|
380
|
-
two_array_system = PVSystem(
|
|
381
|
-
arrays=[array_one, array_two],
|
|
382
|
-
inverter_parameters=inverter_parameters
|
|
383
|
-
)
|
|
384
|
-
array_one_system = PVSystem(
|
|
385
|
-
arrays=[array_one],
|
|
386
|
-
inverter_parameters=inverter_parameters
|
|
387
|
-
)
|
|
388
|
-
array_two_system = PVSystem(
|
|
389
|
-
arrays=[array_two],
|
|
390
|
-
inverter_parameters=inverter_parameters
|
|
391
|
-
)
|
|
392
|
-
return {'two_array_system': two_array_system,
|
|
393
|
-
'array_one_system': array_one_system,
|
|
394
|
-
'array_two_system': array_two_system}
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
def test_run_model_from_irradiance_arrays_no_loss(
|
|
398
|
-
multi_array_sapm_dc_snl_ac_system, location):
|
|
399
|
-
mc_both = ModelChain(
|
|
400
|
-
multi_array_sapm_dc_snl_ac_system['two_array_system'],
|
|
401
|
-
location,
|
|
402
|
-
aoi_model='no_loss',
|
|
403
|
-
spectral_model='no_loss',
|
|
404
|
-
losses_model='no_loss'
|
|
405
|
-
)
|
|
406
|
-
mc_one = ModelChain(
|
|
407
|
-
multi_array_sapm_dc_snl_ac_system['array_one_system'],
|
|
408
|
-
location,
|
|
409
|
-
aoi_model='no_loss',
|
|
410
|
-
spectral_model='no_loss',
|
|
411
|
-
losses_model='no_loss'
|
|
412
|
-
)
|
|
413
|
-
mc_two = ModelChain(
|
|
414
|
-
multi_array_sapm_dc_snl_ac_system['array_two_system'],
|
|
415
|
-
location,
|
|
416
|
-
aoi_model='no_loss',
|
|
417
|
-
spectral_model='no_loss',
|
|
418
|
-
losses_model='no_loss'
|
|
419
|
-
)
|
|
420
|
-
times = pd.date_range('20160101 1200-0700', periods=2, freq='6h')
|
|
421
|
-
irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150},
|
|
422
|
-
index=times)
|
|
423
|
-
mc_one.run_model(irradiance)
|
|
424
|
-
mc_two.run_model(irradiance)
|
|
425
|
-
mc_both.run_model(irradiance)
|
|
426
|
-
assert_frame_equal(
|
|
427
|
-
mc_both.results.dc[0],
|
|
428
|
-
mc_one.results.dc
|
|
429
|
-
)
|
|
430
|
-
assert_frame_equal(
|
|
431
|
-
mc_both.results.dc[1],
|
|
432
|
-
mc_two.results.dc
|
|
433
|
-
)
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
@pytest.mark.parametrize("input_type", [tuple, list])
|
|
437
|
-
def test_run_model_from_irradiance_arrays_no_loss_input_type(
|
|
438
|
-
multi_array_sapm_dc_snl_ac_system, location, input_type):
|
|
439
|
-
mc_both = ModelChain(
|
|
440
|
-
multi_array_sapm_dc_snl_ac_system['two_array_system'],
|
|
441
|
-
location,
|
|
442
|
-
aoi_model='no_loss',
|
|
443
|
-
spectral_model='no_loss',
|
|
444
|
-
losses_model='no_loss'
|
|
445
|
-
)
|
|
446
|
-
mc_one = ModelChain(
|
|
447
|
-
multi_array_sapm_dc_snl_ac_system['array_one_system'],
|
|
448
|
-
location,
|
|
449
|
-
aoi_model='no_loss',
|
|
450
|
-
spectral_model='no_loss',
|
|
451
|
-
losses_model='no_loss'
|
|
452
|
-
)
|
|
453
|
-
mc_two = ModelChain(
|
|
454
|
-
multi_array_sapm_dc_snl_ac_system['array_two_system'],
|
|
455
|
-
location,
|
|
456
|
-
aoi_model='no_loss',
|
|
457
|
-
spectral_model='no_loss',
|
|
458
|
-
losses_model='no_loss'
|
|
459
|
-
)
|
|
460
|
-
times = pd.date_range('20160101 1200-0700', periods=2, freq='6h')
|
|
461
|
-
irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150},
|
|
462
|
-
index=times)
|
|
463
|
-
mc_one.run_model(irradiance)
|
|
464
|
-
mc_two.run_model(irradiance)
|
|
465
|
-
mc_both.run_model(input_type((irradiance, irradiance)))
|
|
466
|
-
assert_frame_equal(
|
|
467
|
-
mc_both.results.dc[0], mc_one.results.dc
|
|
468
|
-
)
|
|
469
|
-
assert_frame_equal(
|
|
470
|
-
mc_both.results.dc[1], mc_two.results.dc
|
|
471
|
-
)
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
@pytest.mark.parametrize('inverter', ['adr'])
|
|
475
|
-
def test_ModelChain_invalid_inverter_params_arrays(
|
|
476
|
-
inverter, sapm_dc_snl_ac_system_same_arrays,
|
|
477
|
-
location, adr_inverter_parameters):
|
|
478
|
-
inverter_params = {'adr': adr_inverter_parameters}
|
|
479
|
-
sapm_dc_snl_ac_system_same_arrays.inverter_parameters = \
|
|
480
|
-
inverter_params[inverter]
|
|
481
|
-
with pytest.raises(ValueError,
|
|
482
|
-
match=r'adr inverter function cannot'):
|
|
483
|
-
ModelChain(sapm_dc_snl_ac_system_same_arrays, location)
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
@pytest.mark.parametrize("input_type", [tuple, list])
|
|
487
|
-
def test_prepare_inputs_multi_weather(
|
|
488
|
-
sapm_dc_snl_ac_system_Array, location, input_type):
|
|
489
|
-
times = pd.date_range(start='20160101 1200-0700',
|
|
490
|
-
end='20160101 1800-0700', freq='6h')
|
|
491
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
|
|
492
|
-
weather = pd.DataFrame({'ghi': 1, 'dhi': 1, 'dni': 1},
|
|
493
|
-
index=times)
|
|
494
|
-
mc.prepare_inputs(input_type((weather, weather)))
|
|
495
|
-
num_arrays = sapm_dc_snl_ac_system_Array.num_arrays
|
|
496
|
-
assert len(mc.results.total_irrad) == num_arrays
|
|
497
|
-
# check that albedo is transfered to mc.results from mc.system.arrays
|
|
498
|
-
assert mc.results.albedo == (0.2, 0.2)
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
@pytest.mark.parametrize("input_type", [tuple, list])
|
|
502
|
-
def test_prepare_inputs_albedo_in_weather(
|
|
503
|
-
sapm_dc_snl_ac_system_Array, location, input_type):
|
|
504
|
-
times = pd.date_range(start='20160101 1200-0700',
|
|
505
|
-
end='20160101 1800-0700', freq='6h')
|
|
506
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
|
|
507
|
-
weather = pd.DataFrame({'ghi': 1, 'dhi': 1, 'dni': 1, 'albedo': 0.5},
|
|
508
|
-
index=times)
|
|
509
|
-
# weather as a single DataFrame
|
|
510
|
-
mc.prepare_inputs(weather)
|
|
511
|
-
num_arrays = sapm_dc_snl_ac_system_Array.num_arrays
|
|
512
|
-
assert len(mc.results.albedo) == num_arrays
|
|
513
|
-
# repeat with tuple of weather
|
|
514
|
-
mc.prepare_inputs(input_type((weather, weather)))
|
|
515
|
-
num_arrays = sapm_dc_snl_ac_system_Array.num_arrays
|
|
516
|
-
assert len(mc.results.albedo) == num_arrays
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
def test_prepare_inputs_no_irradiance(sapm_dc_snl_ac_system, location):
|
|
520
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
521
|
-
weather = pd.DataFrame()
|
|
522
|
-
with pytest.raises(ValueError):
|
|
523
|
-
mc.prepare_inputs(weather)
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
def test_prepare_inputs_arrays_one_missing_irradiance(
|
|
527
|
-
sapm_dc_snl_ac_system_Array, location):
|
|
528
|
-
"""If any of the input DataFrames is missing a column then a
|
|
529
|
-
ValueError is raised."""
|
|
530
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
|
|
531
|
-
weather = pd.DataFrame(
|
|
532
|
-
{'ghi': [1], 'dhi': [1], 'dni': [1]}
|
|
533
|
-
)
|
|
534
|
-
weather_incomplete = pd.DataFrame(
|
|
535
|
-
{'ghi': [1], 'dhi': [1]}
|
|
536
|
-
)
|
|
537
|
-
with pytest.raises(ValueError,
|
|
538
|
-
match=r"Incomplete input data\. .*"):
|
|
539
|
-
mc.prepare_inputs((weather, weather_incomplete))
|
|
540
|
-
with pytest.raises(ValueError,
|
|
541
|
-
match=r"Incomplete input data\. .*"):
|
|
542
|
-
mc.prepare_inputs((weather_incomplete, weather))
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
@pytest.mark.parametrize("input_type", [tuple, list])
|
|
546
|
-
def test_prepare_inputs_weather_wrong_length(
|
|
547
|
-
sapm_dc_snl_ac_system_Array, location, input_type):
|
|
548
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
|
|
549
|
-
weather = pd.DataFrame({'ghi': [1], 'dhi': [1], 'dni': [1]})
|
|
550
|
-
with pytest.raises(ValueError,
|
|
551
|
-
match="Input must be same length as number of Arrays "
|
|
552
|
-
r"in system\. Expected 2, got 1\."):
|
|
553
|
-
mc.prepare_inputs(input_type((weather,)))
|
|
554
|
-
with pytest.raises(ValueError,
|
|
555
|
-
match="Input must be same length as number of Arrays "
|
|
556
|
-
r"in system\. Expected 2, got 3\."):
|
|
557
|
-
mc.prepare_inputs(input_type((weather, weather, weather)))
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
def test_ModelChain_times_error_arrays(sapm_dc_snl_ac_system_Array, location):
|
|
561
|
-
"""ModelChain.times is assigned a single index given multiple weather
|
|
562
|
-
DataFrames.
|
|
563
|
-
"""
|
|
564
|
-
error_str = r"Input DataFrames must have same index\."
|
|
565
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
|
|
566
|
-
irradiance = {'ghi': [1, 2], 'dhi': [1, 2], 'dni': [1, 2]}
|
|
567
|
-
times_one = pd.date_range(start='1/1/2020', freq='6h', periods=2)
|
|
568
|
-
times_two = pd.date_range(start='1/1/2020 00:15', freq='6h', periods=2)
|
|
569
|
-
weather_one = pd.DataFrame(irradiance, index=times_one)
|
|
570
|
-
weather_two = pd.DataFrame(irradiance, index=times_two)
|
|
571
|
-
with pytest.raises(ValueError, match=error_str):
|
|
572
|
-
mc.prepare_inputs((weather_one, weather_two))
|
|
573
|
-
# test with overlapping, but differently sized indices.
|
|
574
|
-
times_three = pd.date_range(start='1/1/2020', freq='6h', periods=3)
|
|
575
|
-
irradiance_three = irradiance
|
|
576
|
-
irradiance_three['ghi'].append(3)
|
|
577
|
-
irradiance_three['dhi'].append(3)
|
|
578
|
-
irradiance_three['dni'].append(3)
|
|
579
|
-
weather_three = pd.DataFrame(irradiance_three, index=times_three)
|
|
580
|
-
with pytest.raises(ValueError, match=error_str):
|
|
581
|
-
mc.prepare_inputs((weather_one, weather_three))
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
def test_ModelChain_times_arrays(sapm_dc_snl_ac_system_Array, location):
|
|
585
|
-
"""ModelChain.times is assigned a single index given multiple weather
|
|
586
|
-
DataFrames.
|
|
587
|
-
"""
|
|
588
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
|
|
589
|
-
irradiance_one = {'ghi': [1, 2], 'dhi': [1, 2], 'dni': [1, 2]}
|
|
590
|
-
irradiance_two = {'ghi': [2, 1], 'dhi': [2, 1], 'dni': [2, 1]}
|
|
591
|
-
times = pd.date_range(start='1/1/2020', freq='6h', periods=2)
|
|
592
|
-
weather_one = pd.DataFrame(irradiance_one, index=times)
|
|
593
|
-
weather_two = pd.DataFrame(irradiance_two, index=times)
|
|
594
|
-
mc.prepare_inputs((weather_one, weather_two))
|
|
595
|
-
assert mc.results.times.equals(times)
|
|
596
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
|
|
597
|
-
mc.prepare_inputs(weather_one)
|
|
598
|
-
assert mc.results.times.equals(times)
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
@pytest.mark.parametrize("missing", ['dhi', 'ghi', 'dni'])
|
|
602
|
-
def test_prepare_inputs_missing_irrad_component(
|
|
603
|
-
sapm_dc_snl_ac_system, location, missing):
|
|
604
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
605
|
-
weather = pd.DataFrame({'dhi': [1, 2], 'dni': [1, 2], 'ghi': [1, 2]})
|
|
606
|
-
weather.drop(columns=missing, inplace=True)
|
|
607
|
-
with pytest.raises(ValueError):
|
|
608
|
-
mc.prepare_inputs(weather)
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
@pytest.mark.parametrize('ac_model', ['sandia', 'pvwatts'])
|
|
612
|
-
@pytest.mark.parametrize("input_type", [tuple, list])
|
|
613
|
-
def test_run_model_arrays_weather(sapm_dc_snl_ac_system_same_arrays,
|
|
614
|
-
pvwatts_dc_pvwatts_ac_system_arrays,
|
|
615
|
-
location, ac_model, input_type):
|
|
616
|
-
system = {'sandia': sapm_dc_snl_ac_system_same_arrays,
|
|
617
|
-
'pvwatts': pvwatts_dc_pvwatts_ac_system_arrays}
|
|
618
|
-
mc = ModelChain(system[ac_model], location, aoi_model='no_loss',
|
|
619
|
-
spectral_model='no_loss')
|
|
620
|
-
times = pd.date_range('20200101 1200-0700', periods=2, freq='2h')
|
|
621
|
-
weather_one = pd.DataFrame({'dni': [900, 800],
|
|
622
|
-
'ghi': [600, 500],
|
|
623
|
-
'dhi': [150, 100]},
|
|
624
|
-
index=times)
|
|
625
|
-
weather_two = pd.DataFrame({'dni': [500, 400],
|
|
626
|
-
'ghi': [300, 200],
|
|
627
|
-
'dhi': [75, 65]},
|
|
628
|
-
index=times)
|
|
629
|
-
mc.run_model(input_type((weather_one, weather_two)))
|
|
630
|
-
assert (mc.results.dc[0] != mc.results.dc[1]).all().all()
|
|
631
|
-
assert not mc.results.ac.empty
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
def test_run_model_perez(sapm_dc_snl_ac_system, location):
|
|
635
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location,
|
|
636
|
-
transposition_model='perez')
|
|
637
|
-
times = pd.date_range('20160101 1200-0700', periods=2, freq='6h')
|
|
638
|
-
irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150},
|
|
639
|
-
index=times)
|
|
640
|
-
ac = mc.run_model(irradiance).results.ac
|
|
641
|
-
|
|
642
|
-
expected = pd.Series(np.array([187.94295642, -2.00000000e-02]),
|
|
643
|
-
index=times)
|
|
644
|
-
assert_series_equal(ac, expected)
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
def test_run_model_gueymard_perez(sapm_dc_snl_ac_system, location):
|
|
648
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location,
|
|
649
|
-
airmass_model='gueymard1993',
|
|
650
|
-
transposition_model='perez')
|
|
651
|
-
times = pd.date_range('20160101 1200-0700', periods=2, freq='6h')
|
|
652
|
-
irradiance = pd.DataFrame({'dni': 900, 'ghi': 600, 'dhi': 150},
|
|
653
|
-
index=times)
|
|
654
|
-
ac = mc.run_model(irradiance).results.ac
|
|
655
|
-
|
|
656
|
-
expected = pd.Series(np.array([187.94317405, -2.00000000e-02]),
|
|
657
|
-
index=times)
|
|
658
|
-
assert_series_equal(ac, expected)
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
def test_run_model_with_weather_sapm_temp(sapm_dc_snl_ac_system, location,
|
|
662
|
-
weather, mocker):
|
|
663
|
-
# test with sapm cell temperature model
|
|
664
|
-
weather['wind_speed'] = 5
|
|
665
|
-
weather['temp_air'] = 10
|
|
666
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
667
|
-
mc.temperature_model = 'sapm'
|
|
668
|
-
m_sapm = mocker.spy(sapm_dc_snl_ac_system, 'get_cell_temperature')
|
|
669
|
-
mc.run_model(weather)
|
|
670
|
-
assert m_sapm.call_count == 1
|
|
671
|
-
# assert_called_once_with cannot be used with series, so need to use
|
|
672
|
-
# assert_series_equal on call_args
|
|
673
|
-
assert_series_equal(m_sapm.call_args[0][1], weather['temp_air']) # temp
|
|
674
|
-
assert_series_equal(m_sapm.call_args[0][2], weather['wind_speed']) # wind
|
|
675
|
-
assert m_sapm.call_args[1]['model'] == 'sapm'
|
|
676
|
-
assert not mc.results.ac.empty
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
def test_run_model_with_weather_pvsyst_temp(sapm_dc_snl_ac_system, location,
|
|
680
|
-
weather, mocker):
|
|
681
|
-
# test with pvsyst cell temperature model
|
|
682
|
-
weather['wind_speed'] = 5
|
|
683
|
-
weather['temp_air'] = 10
|
|
684
|
-
sapm_dc_snl_ac_system.arrays[0].racking_model = 'freestanding'
|
|
685
|
-
sapm_dc_snl_ac_system.arrays[0].temperature_model_parameters = \
|
|
686
|
-
temperature._temperature_model_params('pvsyst', 'freestanding')
|
|
687
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
688
|
-
mc.temperature_model = 'pvsyst'
|
|
689
|
-
m_pvsyst = mocker.spy(sapm_dc_snl_ac_system, 'get_cell_temperature')
|
|
690
|
-
mc.run_model(weather)
|
|
691
|
-
assert m_pvsyst.call_count == 1
|
|
692
|
-
assert_series_equal(m_pvsyst.call_args[0][1], weather['temp_air'])
|
|
693
|
-
assert_series_equal(m_pvsyst.call_args[0][2], weather['wind_speed'])
|
|
694
|
-
assert m_pvsyst.call_args[1]['model'] == 'pvsyst'
|
|
695
|
-
assert not mc.results.ac.empty
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
def test_run_model_with_weather_faiman_temp(sapm_dc_snl_ac_system, location,
|
|
699
|
-
weather, mocker):
|
|
700
|
-
# test with faiman cell temperature model
|
|
701
|
-
weather['wind_speed'] = 5
|
|
702
|
-
weather['temp_air'] = 10
|
|
703
|
-
sapm_dc_snl_ac_system.arrays[0].temperature_model_parameters = {
|
|
704
|
-
'u0': 25.0, 'u1': 6.84
|
|
705
|
-
}
|
|
706
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
707
|
-
mc.temperature_model = 'faiman'
|
|
708
|
-
m_faiman = mocker.spy(sapm_dc_snl_ac_system, 'get_cell_temperature')
|
|
709
|
-
mc.run_model(weather)
|
|
710
|
-
assert m_faiman.call_count == 1
|
|
711
|
-
assert_series_equal(m_faiman.call_args[0][1], weather['temp_air'])
|
|
712
|
-
assert_series_equal(m_faiman.call_args[0][2], weather['wind_speed'])
|
|
713
|
-
assert m_faiman.call_args[1]['model'] == 'faiman'
|
|
714
|
-
assert not mc.results.ac.empty
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
def test_run_model_with_weather_fuentes_temp(sapm_dc_snl_ac_system, location,
|
|
718
|
-
weather, mocker):
|
|
719
|
-
weather['wind_speed'] = 5
|
|
720
|
-
weather['temp_air'] = 10
|
|
721
|
-
sapm_dc_snl_ac_system.arrays[0].temperature_model_parameters = {
|
|
722
|
-
'noct_installed': 45, 'surface_tilt': 30,
|
|
723
|
-
}
|
|
724
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
725
|
-
mc.temperature_model = 'fuentes'
|
|
726
|
-
m_fuentes = mocker.spy(sapm_dc_snl_ac_system, 'get_cell_temperature')
|
|
727
|
-
mc.run_model(weather)
|
|
728
|
-
assert m_fuentes.call_count == 1
|
|
729
|
-
assert_series_equal(m_fuentes.call_args[0][1], weather['temp_air'])
|
|
730
|
-
assert_series_equal(m_fuentes.call_args[0][2], weather['wind_speed'])
|
|
731
|
-
assert m_fuentes.call_args[1]['model'] == 'fuentes'
|
|
732
|
-
assert not mc.results.ac.empty
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
def test_run_model_with_weather_noct_sam_temp(sapm_dc_snl_ac_system, location,
|
|
736
|
-
weather, mocker):
|
|
737
|
-
weather['wind_speed'] = 5
|
|
738
|
-
weather['temp_air'] = 10
|
|
739
|
-
sapm_dc_snl_ac_system.arrays[0].temperature_model_parameters = {
|
|
740
|
-
'noct': 45, 'module_efficiency': 0.2
|
|
741
|
-
}
|
|
742
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
743
|
-
mc.temperature_model = 'noct_sam'
|
|
744
|
-
m_noct_sam = mocker.spy(sapm_dc_snl_ac_system, 'get_cell_temperature')
|
|
745
|
-
mc.run_model(weather)
|
|
746
|
-
assert m_noct_sam.call_count == 1
|
|
747
|
-
assert_series_equal(m_noct_sam.call_args[0][1], weather['temp_air'])
|
|
748
|
-
assert_series_equal(m_noct_sam.call_args[0][2], weather['wind_speed'])
|
|
749
|
-
# check that effective_irradiance was used
|
|
750
|
-
assert m_noct_sam.call_args[1] == {
|
|
751
|
-
'effective_irradiance': mc.results.effective_irradiance,
|
|
752
|
-
'model': 'noct_sam'}
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
def test__assign_total_irrad(sapm_dc_snl_ac_system, location, weather,
|
|
756
|
-
total_irrad):
|
|
757
|
-
data = pd.concat([weather, total_irrad], axis=1)
|
|
758
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
759
|
-
mc._assign_total_irrad(data)
|
|
760
|
-
assert_frame_equal(mc.results.total_irrad, total_irrad)
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
def test_prepare_inputs_from_poa(sapm_dc_snl_ac_system, location,
|
|
764
|
-
weather, total_irrad):
|
|
765
|
-
data = pd.concat([weather, total_irrad], axis=1)
|
|
766
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
767
|
-
mc.prepare_inputs_from_poa(data)
|
|
768
|
-
weather_expected = weather.copy()
|
|
769
|
-
weather_expected['temp_air'] = 20
|
|
770
|
-
weather_expected['wind_speed'] = 0
|
|
771
|
-
# order as expected
|
|
772
|
-
weather_expected = weather_expected[
|
|
773
|
-
['ghi', 'dhi', 'dni', 'wind_speed', 'temp_air']]
|
|
774
|
-
# weather attribute
|
|
775
|
-
assert_frame_equal(mc.results.weather, weather_expected)
|
|
776
|
-
# total_irrad attribute
|
|
777
|
-
assert_frame_equal(mc.results.total_irrad, total_irrad)
|
|
778
|
-
assert not pd.isnull(mc.results.solar_position.index[0])
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
@pytest.mark.parametrize("input_type", [tuple, list])
|
|
782
|
-
def test_prepare_inputs_from_poa_multi_data(
|
|
783
|
-
sapm_dc_snl_ac_system_Array, location, total_irrad, weather,
|
|
784
|
-
input_type):
|
|
785
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
|
|
786
|
-
poa = pd.concat([weather, total_irrad], axis=1)
|
|
787
|
-
mc.prepare_inputs_from_poa(input_type((poa, poa)))
|
|
788
|
-
num_arrays = sapm_dc_snl_ac_system_Array.num_arrays
|
|
789
|
-
assert len(mc.results.total_irrad) == num_arrays
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
@pytest.mark.parametrize("input_type", [tuple, list])
|
|
793
|
-
def test_prepare_inputs_from_poa_wrong_number_arrays(
|
|
794
|
-
sapm_dc_snl_ac_system_Array, location, total_irrad, weather,
|
|
795
|
-
input_type):
|
|
796
|
-
len_error = r"Input must be same length as number of Arrays in system\. " \
|
|
797
|
-
r"Expected 2, got [0-9]+\."
|
|
798
|
-
type_error = r"Input must be a tuple of length 2, got .*\."
|
|
799
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
|
|
800
|
-
poa = pd.concat([weather, total_irrad], axis=1)
|
|
801
|
-
with pytest.raises(TypeError, match=type_error):
|
|
802
|
-
mc.prepare_inputs_from_poa(poa)
|
|
803
|
-
with pytest.raises(ValueError, match=len_error):
|
|
804
|
-
mc.prepare_inputs_from_poa(input_type((poa,)))
|
|
805
|
-
with pytest.raises(ValueError, match=len_error):
|
|
806
|
-
mc.prepare_inputs_from_poa(input_type((poa, poa, poa)))
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
def test_prepare_inputs_from_poa_arrays_different_indices(
|
|
810
|
-
sapm_dc_snl_ac_system_Array, location, total_irrad, weather):
|
|
811
|
-
error_str = r"Input DataFrames must have same index\."
|
|
812
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
|
|
813
|
-
poa = pd.concat([weather, total_irrad], axis=1)
|
|
814
|
-
with pytest.raises(ValueError, match=error_str):
|
|
815
|
-
mc.prepare_inputs_from_poa((poa, poa.shift(periods=1, freq='6h')))
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
def test_prepare_inputs_from_poa_arrays_missing_column(
|
|
819
|
-
sapm_dc_snl_ac_system_Array, location, weather, total_irrad):
|
|
820
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
|
|
821
|
-
poa = pd.concat([weather, total_irrad], axis=1)
|
|
822
|
-
with pytest.raises(ValueError, match=r"Incomplete input data\. "
|
|
823
|
-
r"Data needs to contain .*\. "
|
|
824
|
-
r"Detected data in element 1 "
|
|
825
|
-
r"contains: .*"):
|
|
826
|
-
mc.prepare_inputs_from_poa((poa, poa.drop(columns='poa_global')))
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
def test__prepare_temperature(sapm_dc_snl_ac_system, location, weather,
|
|
830
|
-
total_irrad):
|
|
831
|
-
data = weather.copy()
|
|
832
|
-
data[['poa_global', 'poa_diffuse', 'poa_direct']] = total_irrad
|
|
833
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, aoi_model='no_loss',
|
|
834
|
-
spectral_model='no_loss')
|
|
835
|
-
# prepare_temperature expects mc.total_irrad and mc.results.weather
|
|
836
|
-
# to be set
|
|
837
|
-
mc._assign_weather(data)
|
|
838
|
-
mc._assign_total_irrad(data)
|
|
839
|
-
mc._prepare_temperature(data)
|
|
840
|
-
expected = pd.Series([48.928025, 38.080016], index=data.index)
|
|
841
|
-
assert_series_equal(mc.results.cell_temperature, expected)
|
|
842
|
-
data['module_temperature'] = [40., 30.]
|
|
843
|
-
mc._prepare_temperature(data)
|
|
844
|
-
expected = pd.Series([42.4, 31.5], index=data.index)
|
|
845
|
-
assert_series_equal(mc.results.cell_temperature, expected)
|
|
846
|
-
data['cell_temperature'] = [50., 35.]
|
|
847
|
-
mc._prepare_temperature(data)
|
|
848
|
-
assert_series_equal(mc.results.cell_temperature, data['cell_temperature'])
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
def test__prepare_temperature_len1_weather_tuple(
|
|
852
|
-
sapm_dc_snl_ac_system, location, weather, total_irrad):
|
|
853
|
-
# GH 1192
|
|
854
|
-
weather['module_temperature'] = [40., 30.]
|
|
855
|
-
data = weather.copy()
|
|
856
|
-
|
|
857
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, aoi_model='no_loss',
|
|
858
|
-
spectral_model='no_loss')
|
|
859
|
-
mc.run_model([data])
|
|
860
|
-
expected = pd.Series([42.617244212941394, 30.0], index=data.index)
|
|
861
|
-
assert_series_equal(mc.results.cell_temperature[0], expected)
|
|
862
|
-
|
|
863
|
-
data = weather.copy().rename(
|
|
864
|
-
columns={
|
|
865
|
-
"ghi": "poa_global", "dhi": "poa_diffuse", "dni": "poa_direct"}
|
|
866
|
-
)
|
|
867
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, aoi_model='no_loss',
|
|
868
|
-
spectral_model='no_loss')
|
|
869
|
-
mc.run_model_from_poa([data])
|
|
870
|
-
expected = pd.Series([41.5, 30.0], index=data.index)
|
|
871
|
-
assert_series_equal(mc.results.cell_temperature[0], expected)
|
|
872
|
-
|
|
873
|
-
data = weather.copy()[["module_temperature", "ghi"]].rename(
|
|
874
|
-
columns={"ghi": "effective_irradiance"}
|
|
875
|
-
)
|
|
876
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, aoi_model='no_loss',
|
|
877
|
-
spectral_model='no_loss')
|
|
878
|
-
mc.run_model_from_effective_irradiance([data])
|
|
879
|
-
expected = pd.Series([41.5, 30.0], index=data.index)
|
|
880
|
-
assert_series_equal(mc.results.cell_temperature[0], expected)
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
def test__prepare_temperature_arrays_weather(sapm_dc_snl_ac_system_same_arrays,
|
|
884
|
-
location, weather,
|
|
885
|
-
total_irrad):
|
|
886
|
-
data = weather.copy()
|
|
887
|
-
data[['poa_global', 'poa_direct', 'poa_diffuse']] = total_irrad
|
|
888
|
-
data_two = data.copy()
|
|
889
|
-
mc = ModelChain(sapm_dc_snl_ac_system_same_arrays, location,
|
|
890
|
-
aoi_model='no_loss', spectral_model='no_loss')
|
|
891
|
-
# prepare_temperature expects mc.results.total_irrad and mc.results.weather
|
|
892
|
-
# to be set
|
|
893
|
-
mc._assign_weather((data, data_two))
|
|
894
|
-
mc._assign_total_irrad((data, data_two))
|
|
895
|
-
mc._prepare_temperature((data, data_two))
|
|
896
|
-
expected = pd.Series([48.928025, 38.080016], index=data.index)
|
|
897
|
-
assert_series_equal(mc.results.cell_temperature[0], expected)
|
|
898
|
-
assert_series_equal(mc.results.cell_temperature[1], expected)
|
|
899
|
-
data['module_temperature'] = [40., 30.]
|
|
900
|
-
mc._prepare_temperature((data, data_two))
|
|
901
|
-
expected = pd.Series([42.4, 31.5], index=data.index)
|
|
902
|
-
assert (mc.results.cell_temperature[1] != expected).all()
|
|
903
|
-
assert_series_equal(mc.results.cell_temperature[0], expected)
|
|
904
|
-
data['cell_temperature'] = [50., 35.]
|
|
905
|
-
mc._prepare_temperature((data, data_two))
|
|
906
|
-
assert_series_equal(
|
|
907
|
-
mc.results.cell_temperature[0], data['cell_temperature'])
|
|
908
|
-
data_two['module_temperature'] = [40., 30.]
|
|
909
|
-
mc._prepare_temperature((data, data_two))
|
|
910
|
-
assert_series_equal(mc.results.cell_temperature[1], expected)
|
|
911
|
-
assert_series_equal(
|
|
912
|
-
mc.results.cell_temperature[0], data['cell_temperature'])
|
|
913
|
-
data_two['cell_temperature'] = [10.0, 20.0]
|
|
914
|
-
mc._prepare_temperature((data, data_two))
|
|
915
|
-
assert_series_equal(
|
|
916
|
-
mc.results.cell_temperature[1], data_two['cell_temperature'])
|
|
917
|
-
assert_series_equal(
|
|
918
|
-
mc.results.cell_temperature[0], data['cell_temperature'])
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
@pytest.mark.parametrize('temp_params,temp_model',
|
|
922
|
-
[({'a': -3.47, 'b': -.0594, 'deltaT': 3},
|
|
923
|
-
ModelChain.sapm_temp),
|
|
924
|
-
({'u_c': 29.0, 'u_v': 0},
|
|
925
|
-
ModelChain.pvsyst_temp),
|
|
926
|
-
({'u0': 25.0, 'u1': 6.84},
|
|
927
|
-
ModelChain.faiman_temp),
|
|
928
|
-
({'noct_installed': 45},
|
|
929
|
-
ModelChain.fuentes_temp),
|
|
930
|
-
({'noct': 45, 'module_efficiency': 0.2},
|
|
931
|
-
ModelChain.noct_sam_temp)])
|
|
932
|
-
def test_temperature_models_arrays_multi_weather(
|
|
933
|
-
temp_params, temp_model,
|
|
934
|
-
sapm_dc_snl_ac_system_same_arrays,
|
|
935
|
-
location, weather, total_irrad):
|
|
936
|
-
for array in sapm_dc_snl_ac_system_same_arrays.arrays:
|
|
937
|
-
array.temperature_model_parameters = temp_params
|
|
938
|
-
# set air temp so it does not default to the same value for both arrays
|
|
939
|
-
weather['temp_air'] = 25
|
|
940
|
-
weather_one = weather
|
|
941
|
-
weather_two = weather.copy() * 0.5
|
|
942
|
-
mc = ModelChain(sapm_dc_snl_ac_system_same_arrays, location,
|
|
943
|
-
aoi_model='no_loss', spectral_model='no_loss')
|
|
944
|
-
mc.prepare_inputs((weather_one, weather_two))
|
|
945
|
-
temp_model(mc)
|
|
946
|
-
assert (mc.results.cell_temperature[0]
|
|
947
|
-
!= mc.results.cell_temperature[1]).all()
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
def test_run_model_solar_position_weather(
|
|
951
|
-
pvwatts_dc_pvwatts_ac_system, location, weather, mocker):
|
|
952
|
-
mc = ModelChain(pvwatts_dc_pvwatts_ac_system, location,
|
|
953
|
-
aoi_model='no_loss', spectral_model='no_loss')
|
|
954
|
-
weather['pressure'] = 90000
|
|
955
|
-
weather['temp_air'] = 25
|
|
956
|
-
m = mocker.spy(location, 'get_solarposition')
|
|
957
|
-
mc.run_model(weather)
|
|
958
|
-
# assert_called_once_with cannot be used with series, so need to use
|
|
959
|
-
# assert_series_equal on call_args
|
|
960
|
-
assert_series_equal(m.call_args[1]['temperature'], weather['temp_air'])
|
|
961
|
-
assert_series_equal(m.call_args[1]['pressure'], weather['pressure'])
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
def test_run_model_from_poa(sapm_dc_snl_ac_system, location, total_irrad):
|
|
965
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, aoi_model='no_loss',
|
|
966
|
-
spectral_model='no_loss')
|
|
967
|
-
ac = mc.run_model_from_poa(total_irrad).results.ac
|
|
968
|
-
expected = pd.Series(np.array([149.280238, 96.678385]),
|
|
969
|
-
index=total_irrad.index)
|
|
970
|
-
assert_series_equal(ac, expected)
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
@pytest.mark.parametrize("input_type", [tuple, list])
|
|
974
|
-
def test_run_model_from_poa_arrays(sapm_dc_snl_ac_system_Array, location,
|
|
975
|
-
weather, total_irrad, input_type):
|
|
976
|
-
data = weather.copy()
|
|
977
|
-
data[['poa_global', 'poa_diffuse', 'poa_direct']] = total_irrad
|
|
978
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location, aoi_model='no_loss',
|
|
979
|
-
spectral_model='no_loss')
|
|
980
|
-
mc.run_model_from_poa(input_type((data, data)))
|
|
981
|
-
# arrays have different orientation, but should give same dc power
|
|
982
|
-
# because we are the same passing POA irradiance and air
|
|
983
|
-
# temperature.
|
|
984
|
-
assert_frame_equal(mc.results.dc[0], mc.results.dc[1])
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
def test_run_model_from_poa_arrays_solar_position_weather(
|
|
988
|
-
sapm_dc_snl_ac_system_Array, location, weather, total_irrad, mocker):
|
|
989
|
-
data = weather.copy()
|
|
990
|
-
data[['poa_global', 'poa_diffuse', 'poa_direct']] = total_irrad
|
|
991
|
-
data['pressure'] = 90000
|
|
992
|
-
data['temp_air'] = 25
|
|
993
|
-
data2 = data.copy()
|
|
994
|
-
data2['pressure'] = 95000
|
|
995
|
-
data2['temp_air'] = 30
|
|
996
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location, aoi_model='no_loss',
|
|
997
|
-
spectral_model='no_loss')
|
|
998
|
-
m = mocker.spy(location, 'get_solarposition')
|
|
999
|
-
mc.run_model_from_poa((data, data2))
|
|
1000
|
-
# mc uses only the first weather data for solar position corrections
|
|
1001
|
-
assert_series_equal(m.call_args[1]['temperature'], data['temp_air'])
|
|
1002
|
-
assert_series_equal(m.call_args[1]['pressure'], data['pressure'])
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
@pytest.mark.parametrize("input_type", [lambda x: x[0], tuple, list])
|
|
1006
|
-
def test_run_model_from_effective_irradiance(sapm_dc_snl_ac_system, location,
|
|
1007
|
-
weather, total_irrad, input_type):
|
|
1008
|
-
data = weather.copy()
|
|
1009
|
-
data[['poa_global', 'poa_diffuse', 'poa_direct']] = total_irrad
|
|
1010
|
-
data['effective_irradiance'] = data['poa_global']
|
|
1011
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, aoi_model='no_loss',
|
|
1012
|
-
spectral_model='no_loss')
|
|
1013
|
-
ac = mc.run_model_from_effective_irradiance(input_type((data,))).results.ac
|
|
1014
|
-
expected = pd.Series(np.array([149.280238, 96.678385]),
|
|
1015
|
-
index=data.index)
|
|
1016
|
-
assert_series_equal(ac, expected)
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
@pytest.mark.parametrize("input_type", [tuple, list])
|
|
1020
|
-
def test_run_model_from_effective_irradiance_multi_array(
|
|
1021
|
-
sapm_dc_snl_ac_system_Array, location, weather, total_irrad,
|
|
1022
|
-
input_type):
|
|
1023
|
-
data = weather.copy()
|
|
1024
|
-
data[['poa_global', 'poa_diffuse', 'poa_direct']] = total_irrad
|
|
1025
|
-
data['effective_irradiance'] = data['poa_global']
|
|
1026
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location, aoi_model='no_loss',
|
|
1027
|
-
spectral_model='no_loss')
|
|
1028
|
-
mc.run_model_from_effective_irradiance(input_type((data, data)))
|
|
1029
|
-
# arrays have different orientation, but should give same dc power
|
|
1030
|
-
# because we are the same passing POA irradiance and air
|
|
1031
|
-
# temperature.
|
|
1032
|
-
assert_frame_equal(mc.results.dc[0], mc.results.dc[1])
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
@pytest.mark.parametrize("input_type", [lambda x: x[0], tuple, list])
|
|
1036
|
-
def test_run_model_from_effective_irradiance_no_poa_global(
|
|
1037
|
-
sapm_dc_snl_ac_system, location, weather, total_irrad, input_type):
|
|
1038
|
-
data = weather.copy()
|
|
1039
|
-
data['effective_irradiance'] = total_irrad['poa_global']
|
|
1040
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, aoi_model='no_loss',
|
|
1041
|
-
spectral_model='no_loss')
|
|
1042
|
-
ac = mc.run_model_from_effective_irradiance(input_type((data,))).results.ac
|
|
1043
|
-
expected = pd.Series(np.array([149.280238, 96.678385]),
|
|
1044
|
-
index=data.index)
|
|
1045
|
-
assert_series_equal(ac, expected)
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
def test_run_model_from_effective_irradiance_poa_global_differs(
|
|
1049
|
-
sapm_dc_snl_ac_system, location, weather, total_irrad):
|
|
1050
|
-
data = weather.copy()
|
|
1051
|
-
data[['poa_global', 'poa_diffuse', 'poa_direct']] = total_irrad
|
|
1052
|
-
data['effective_irradiance'] = data['poa_global'] * 0.8
|
|
1053
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, aoi_model='no_loss',
|
|
1054
|
-
spectral_model='no_loss')
|
|
1055
|
-
ac = mc.run_model_from_effective_irradiance(data).results.ac
|
|
1056
|
-
expected = pd.Series(np.array([118.302801, 76.099841]),
|
|
1057
|
-
index=data.index)
|
|
1058
|
-
assert_series_equal(ac, expected)
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
@pytest.mark.parametrize("input_type", [tuple, list])
|
|
1062
|
-
def test_run_model_from_effective_irradiance_arrays_error(
|
|
1063
|
-
sapm_dc_snl_ac_system_Array, location, weather, total_irrad,
|
|
1064
|
-
input_type):
|
|
1065
|
-
data = weather.copy()
|
|
1066
|
-
data[['poa_global', 'poa_diffuse', 'poa_direct']] = total_irrad
|
|
1067
|
-
data['effetive_irradiance'] = data['poa_global']
|
|
1068
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
|
|
1069
|
-
len_error = r"Input must be same length as number of Arrays in system\. " \
|
|
1070
|
-
r"Expected 2, got [0-9]+\."
|
|
1071
|
-
type_error = r"Input must be a tuple of length 2, got DataFrame\."
|
|
1072
|
-
with pytest.raises(TypeError, match=type_error):
|
|
1073
|
-
mc.run_model_from_effective_irradiance(data)
|
|
1074
|
-
with pytest.raises(ValueError, match=len_error):
|
|
1075
|
-
mc.run_model_from_effective_irradiance(input_type((data,)))
|
|
1076
|
-
with pytest.raises(ValueError, match=len_error):
|
|
1077
|
-
mc.run_model_from_effective_irradiance(input_type((data, data, data)))
|
|
1078
|
-
with pytest.raises(ValueError,
|
|
1079
|
-
match=r"Input DataFrames must have same index\."):
|
|
1080
|
-
mc.run_model_from_effective_irradiance(
|
|
1081
|
-
(data, data.shift(periods=1, freq='6h'))
|
|
1082
|
-
)
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
@pytest.mark.parametrize("input_type", [tuple, list])
|
|
1086
|
-
def test_run_model_from_effective_irradiance_arrays(
|
|
1087
|
-
sapm_dc_snl_ac_system_Array, location, weather, total_irrad,
|
|
1088
|
-
input_type):
|
|
1089
|
-
data = weather.copy()
|
|
1090
|
-
data[['poa_global', 'poa_diffuse', 'poa_direct']] = total_irrad
|
|
1091
|
-
data['effective_irradiance'] = data['poa_global']
|
|
1092
|
-
data['cell_temperature'] = 40
|
|
1093
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
|
|
1094
|
-
mc.run_model_from_effective_irradiance(input_type((data, data)))
|
|
1095
|
-
# arrays have different orientation, but should give same dc power
|
|
1096
|
-
# because we are the same passing effective irradiance and cell
|
|
1097
|
-
# temperature.
|
|
1098
|
-
assert_frame_equal(mc.results.dc[0], mc.results.dc[1])
|
|
1099
|
-
# test that unequal inputs create unequal results
|
|
1100
|
-
data_two = data.copy()
|
|
1101
|
-
data_two['effective_irradiance'] = data['poa_global'] * 0.5
|
|
1102
|
-
mc.run_model_from_effective_irradiance(input_type((data, data_two)))
|
|
1103
|
-
assert (mc.results.dc[0] != mc.results.dc[1]).all().all()
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
def test_run_model_from_effective_irradiance_minimal_input(
|
|
1107
|
-
sapm_dc_snl_ac_system, sapm_dc_snl_ac_system_Array,
|
|
1108
|
-
location, total_irrad):
|
|
1109
|
-
data = pd.DataFrame({'effective_irradiance': total_irrad['poa_global'],
|
|
1110
|
-
'cell_temperature': 40},
|
|
1111
|
-
index=total_irrad.index)
|
|
1112
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
1113
|
-
mc.run_model_from_effective_irradiance(data)
|
|
1114
|
-
# make sure, for a single Array, the result is the correct type and value
|
|
1115
|
-
assert_series_equal(mc.results.cell_temperature, data['cell_temperature'])
|
|
1116
|
-
assert not mc.results.dc.empty
|
|
1117
|
-
assert not mc.results.ac.empty
|
|
1118
|
-
# test with multiple arrays
|
|
1119
|
-
mc = ModelChain(sapm_dc_snl_ac_system_Array, location)
|
|
1120
|
-
mc.run_model_from_effective_irradiance((data, data))
|
|
1121
|
-
assert_frame_equal(mc.results.dc[0], mc.results.dc[1])
|
|
1122
|
-
assert not mc.results.ac.empty
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
def test_run_model_singleton_weather_single_array(cec_dc_snl_ac_system,
|
|
1126
|
-
location, weather):
|
|
1127
|
-
mc = ModelChain(cec_dc_snl_ac_system, location,
|
|
1128
|
-
aoi_model="no_loss", spectral_model="no_loss")
|
|
1129
|
-
mc.run_model([weather])
|
|
1130
|
-
assert isinstance(mc.results.weather, tuple)
|
|
1131
|
-
assert isinstance(mc.results.total_irrad, tuple)
|
|
1132
|
-
assert isinstance(mc.results.aoi, tuple)
|
|
1133
|
-
assert isinstance(mc.results.aoi_modifier, tuple)
|
|
1134
|
-
assert isinstance(mc.results.spectral_modifier, tuple)
|
|
1135
|
-
assert isinstance(mc.results.effective_irradiance, tuple)
|
|
1136
|
-
assert isinstance(mc.results.dc, tuple)
|
|
1137
|
-
assert isinstance(mc.results.cell_temperature, tuple)
|
|
1138
|
-
assert len(mc.results.cell_temperature) == 1
|
|
1139
|
-
assert isinstance(mc.results.cell_temperature[0], pd.Series)
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
def test_run_model_from_poa_singleton_weather_single_array(
|
|
1143
|
-
sapm_dc_snl_ac_system, location, total_irrad):
|
|
1144
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location,
|
|
1145
|
-
aoi_model='no_loss', spectral_model='no_loss')
|
|
1146
|
-
ac = mc.run_model_from_poa([total_irrad]).results.ac
|
|
1147
|
-
expected = pd.Series(np.array([149.280238, 96.678385]),
|
|
1148
|
-
index=total_irrad.index)
|
|
1149
|
-
assert isinstance(mc.results.weather, tuple)
|
|
1150
|
-
assert isinstance(mc.results.cell_temperature, tuple)
|
|
1151
|
-
assert len(mc.results.cell_temperature) == 1
|
|
1152
|
-
assert isinstance(mc.results.cell_temperature[0], pd.Series)
|
|
1153
|
-
assert_series_equal(ac, expected)
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
def test_run_model_from_effective_irradiance_weather_single_array(
|
|
1157
|
-
sapm_dc_snl_ac_system, location, weather, total_irrad):
|
|
1158
|
-
data = weather.copy()
|
|
1159
|
-
data[['poa_global', 'poa_diffuse', 'poa_direct']] = total_irrad
|
|
1160
|
-
data['effective_irradiance'] = data['poa_global']
|
|
1161
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, aoi_model='no_loss',
|
|
1162
|
-
spectral_model='no_loss')
|
|
1163
|
-
ac = mc.run_model_from_effective_irradiance([data]).results.ac
|
|
1164
|
-
expected = pd.Series(np.array([149.280238, 96.678385]),
|
|
1165
|
-
index=data.index)
|
|
1166
|
-
assert isinstance(mc.results.weather, tuple)
|
|
1167
|
-
assert isinstance(mc.results.cell_temperature, tuple)
|
|
1168
|
-
assert len(mc.results.cell_temperature) == 1
|
|
1169
|
-
assert isinstance(mc.results.cell_temperature[0], pd.Series)
|
|
1170
|
-
assert isinstance(mc.results.dc, tuple)
|
|
1171
|
-
assert len(mc.results.dc) == 1
|
|
1172
|
-
assert isinstance(mc.results.dc[0], pd.DataFrame)
|
|
1173
|
-
assert_series_equal(ac, expected)
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
def poadc(mc):
|
|
1177
|
-
mc.results.dc = mc.results.total_irrad['poa_global'] * 0.2
|
|
1178
|
-
mc.results.dc.name = None # assert_series_equal will fail without this
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
@pytest.mark.parametrize('dc_model', [
|
|
1182
|
-
'sapm', 'cec', 'desoto', 'pvsyst', 'singlediode', 'pvwatts_dc'])
|
|
1183
|
-
def test_infer_dc_model(sapm_dc_snl_ac_system, cec_dc_snl_ac_system,
|
|
1184
|
-
pvsyst_dc_snl_ac_system, pvwatts_dc_pvwatts_ac_system,
|
|
1185
|
-
location, dc_model, weather, mocker):
|
|
1186
|
-
dc_systems = {'sapm': sapm_dc_snl_ac_system,
|
|
1187
|
-
'cec': cec_dc_snl_ac_system,
|
|
1188
|
-
'desoto': cec_dc_snl_ac_system,
|
|
1189
|
-
'pvsyst': pvsyst_dc_snl_ac_system,
|
|
1190
|
-
'singlediode': cec_dc_snl_ac_system,
|
|
1191
|
-
'pvwatts_dc': pvwatts_dc_pvwatts_ac_system}
|
|
1192
|
-
dc_model_function = {'sapm': 'sapm',
|
|
1193
|
-
'cec': 'calcparams_cec',
|
|
1194
|
-
'desoto': 'calcparams_desoto',
|
|
1195
|
-
'pvsyst': 'calcparams_pvsyst',
|
|
1196
|
-
'singlediode': 'calcparams_desoto',
|
|
1197
|
-
'pvwatts_dc': 'pvwatts_dc'}
|
|
1198
|
-
temp_model_function = {'sapm': 'sapm',
|
|
1199
|
-
'cec': 'sapm',
|
|
1200
|
-
'desoto': 'sapm',
|
|
1201
|
-
'pvsyst': 'pvsyst',
|
|
1202
|
-
'singlediode': 'sapm',
|
|
1203
|
-
'pvwatts_dc': 'sapm'}
|
|
1204
|
-
temp_model_params = {'sapm': {'a': -3.40641, 'b': -0.0842075, 'deltaT': 3},
|
|
1205
|
-
'pvsyst': {'u_c': 29.0, 'u_v': 0}}
|
|
1206
|
-
system = dc_systems[dc_model]
|
|
1207
|
-
for array in system.arrays:
|
|
1208
|
-
array.temperature_model_parameters = temp_model_params[
|
|
1209
|
-
temp_model_function[dc_model]]
|
|
1210
|
-
# remove Adjust from model parameters for desoto, singlediode
|
|
1211
|
-
if dc_model in ['desoto', 'singlediode']:
|
|
1212
|
-
for array in system.arrays:
|
|
1213
|
-
array.module_parameters.pop('Adjust')
|
|
1214
|
-
m = mocker.spy(pvsystem, dc_model_function[dc_model])
|
|
1215
|
-
mc = ModelChain(system, location,
|
|
1216
|
-
aoi_model='no_loss', spectral_model='no_loss',
|
|
1217
|
-
temperature_model=temp_model_function[dc_model])
|
|
1218
|
-
mc.run_model(weather)
|
|
1219
|
-
assert m.call_count == 1
|
|
1220
|
-
assert isinstance(mc.results.dc, (pd.Series, pd.DataFrame))
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
def test_infer_dc_model_incomplete(multi_array_sapm_dc_snl_ac_system,
|
|
1224
|
-
location):
|
|
1225
|
-
match = 'Could not infer DC model from the module_parameters attributes '
|
|
1226
|
-
system = multi_array_sapm_dc_snl_ac_system['two_array_system']
|
|
1227
|
-
system.arrays[0].module_parameters.pop('A0')
|
|
1228
|
-
with pytest.raises(ValueError, match=match):
|
|
1229
|
-
ModelChain(system, location)
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
@pytest.mark.parametrize('dc_model', ['cec', 'desoto', 'pvsyst'])
|
|
1233
|
-
def test_singlediode_dc_arrays(location, dc_model,
|
|
1234
|
-
cec_dc_snl_ac_arrays,
|
|
1235
|
-
pvsyst_dc_snl_ac_arrays,
|
|
1236
|
-
weather):
|
|
1237
|
-
systems = {'cec': cec_dc_snl_ac_arrays,
|
|
1238
|
-
'pvsyst': pvsyst_dc_snl_ac_arrays,
|
|
1239
|
-
'desoto': cec_dc_snl_ac_arrays}
|
|
1240
|
-
temp_sapm = {'a': -3.40641, 'b': -0.0842075, 'deltaT': 3}
|
|
1241
|
-
temp_pvsyst = {'u_c': 29.0, 'u_v': 0}
|
|
1242
|
-
temp_model_params = {'cec': temp_sapm,
|
|
1243
|
-
'desoto': temp_sapm,
|
|
1244
|
-
'pvsyst': temp_pvsyst}
|
|
1245
|
-
temp_model = {'cec': 'sapm', 'desoto': 'sapm', 'pvsyst': 'pvsyst'}
|
|
1246
|
-
system = systems[dc_model]
|
|
1247
|
-
for array in system.arrays:
|
|
1248
|
-
array.temperature_model_parameters = temp_model_params[dc_model]
|
|
1249
|
-
if dc_model == 'desoto':
|
|
1250
|
-
for array in system.arrays:
|
|
1251
|
-
array.module_parameters.pop('Adjust')
|
|
1252
|
-
mc = ModelChain(system, location,
|
|
1253
|
-
aoi_model='no_loss', spectral_model='no_loss',
|
|
1254
|
-
temperature_model=temp_model[dc_model])
|
|
1255
|
-
mc.run_model(weather)
|
|
1256
|
-
assert isinstance(mc.results.dc, tuple)
|
|
1257
|
-
assert len(mc.results.dc) == system.num_arrays
|
|
1258
|
-
for dc in mc.results.dc:
|
|
1259
|
-
assert isinstance(dc, (pd.Series, pd.DataFrame))
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
@pytest.mark.parametrize('dc_model', ['sapm', 'cec', 'cec_native'])
|
|
1263
|
-
def test_infer_spectral_model(location, sapm_dc_snl_ac_system,
|
|
1264
|
-
cec_dc_snl_ac_system,
|
|
1265
|
-
cec_dc_native_snl_ac_system, dc_model):
|
|
1266
|
-
dc_systems = {'sapm': sapm_dc_snl_ac_system,
|
|
1267
|
-
'cec': cec_dc_snl_ac_system,
|
|
1268
|
-
'cec_native': cec_dc_native_snl_ac_system}
|
|
1269
|
-
system = dc_systems[dc_model]
|
|
1270
|
-
mc = ModelChain(system, location, aoi_model='physical')
|
|
1271
|
-
assert isinstance(mc, ModelChain)
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
@pytest.mark.parametrize('temp_model', [
|
|
1275
|
-
'sapm_temp', 'faiman_temp', 'pvsyst_temp', 'fuentes_temp',
|
|
1276
|
-
'noct_sam_temp'])
|
|
1277
|
-
def test_infer_temp_model(location, sapm_dc_snl_ac_system,
|
|
1278
|
-
pvwatts_dc_pvwatts_ac_pvsyst_temp_system,
|
|
1279
|
-
pvwatts_dc_pvwatts_ac_faiman_temp_system,
|
|
1280
|
-
pvwatts_dc_pvwatts_ac_fuentes_temp_system,
|
|
1281
|
-
pvwatts_dc_pvwatts_ac_noct_sam_temp_system,
|
|
1282
|
-
temp_model):
|
|
1283
|
-
dc_systems = {'sapm_temp': sapm_dc_snl_ac_system,
|
|
1284
|
-
'pvsyst_temp': pvwatts_dc_pvwatts_ac_pvsyst_temp_system,
|
|
1285
|
-
'faiman_temp': pvwatts_dc_pvwatts_ac_faiman_temp_system,
|
|
1286
|
-
'fuentes_temp': pvwatts_dc_pvwatts_ac_fuentes_temp_system,
|
|
1287
|
-
'noct_sam_temp': pvwatts_dc_pvwatts_ac_noct_sam_temp_system}
|
|
1288
|
-
system = dc_systems[temp_model]
|
|
1289
|
-
mc = ModelChain(system, location, aoi_model='physical',
|
|
1290
|
-
spectral_model='no_loss')
|
|
1291
|
-
assert temp_model == mc.temperature_model.__name__
|
|
1292
|
-
assert isinstance(mc, ModelChain)
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
def test_infer_temp_model_invalid(location, sapm_dc_snl_ac_system):
|
|
1296
|
-
sapm_dc_snl_ac_system.arrays[0].temperature_model_parameters.pop('a')
|
|
1297
|
-
with pytest.raises(ValueError):
|
|
1298
|
-
ModelChain(sapm_dc_snl_ac_system, location,
|
|
1299
|
-
aoi_model='physical', spectral_model='no_loss')
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
def test_temperature_model_inconsistent(location, sapm_dc_snl_ac_system):
|
|
1303
|
-
with pytest.raises(ValueError):
|
|
1304
|
-
ModelChain(sapm_dc_snl_ac_system, location, aoi_model='physical',
|
|
1305
|
-
spectral_model='no_loss', temperature_model='pvsyst')
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
def test_temperature_model_not_specified():
|
|
1309
|
-
location = Location(latitude=32.2, longitude=-110.9)
|
|
1310
|
-
arrays = [pvsystem.Array(pvsystem.FixedMount(),
|
|
1311
|
-
module_parameters={'pdc0': 1, 'gamma_pdc': 0})]
|
|
1312
|
-
system = pvsystem.PVSystem(arrays,
|
|
1313
|
-
temperature_model_parameters={'u0': 1, 'u1': 1},
|
|
1314
|
-
inverter_parameters={'pdc0': 1})
|
|
1315
|
-
with pytest.raises(ValueError,
|
|
1316
|
-
match='Could not infer temperature model from '
|
|
1317
|
-
'ModelChain.system.'):
|
|
1318
|
-
_ = ModelChain(system, location,
|
|
1319
|
-
aoi_model='no_loss', spectral_model='no_loss')
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
def test_dc_model_user_func(pvwatts_dc_pvwatts_ac_system, location, weather,
|
|
1323
|
-
mocker):
|
|
1324
|
-
m = mocker.spy(sys.modules[__name__], 'poadc')
|
|
1325
|
-
mc = ModelChain(pvwatts_dc_pvwatts_ac_system, location, dc_model=poadc,
|
|
1326
|
-
aoi_model='no_loss', spectral_model='no_loss')
|
|
1327
|
-
mc.run_model(weather)
|
|
1328
|
-
assert m.call_count == 1
|
|
1329
|
-
assert isinstance(mc.results.ac, (pd.Series, pd.DataFrame))
|
|
1330
|
-
assert not mc.results.ac.empty
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
def test_pvwatts_dc_multiple_strings(pvwatts_dc_pvwatts_ac_system, location,
|
|
1334
|
-
weather, mocker):
|
|
1335
|
-
system = pvwatts_dc_pvwatts_ac_system
|
|
1336
|
-
m = mocker.spy(system, 'scale_voltage_current_power')
|
|
1337
|
-
mc1 = ModelChain(system, location,
|
|
1338
|
-
aoi_model='no_loss', spectral_model='no_loss')
|
|
1339
|
-
mc1.run_model(weather)
|
|
1340
|
-
assert m.call_count == 1
|
|
1341
|
-
system.arrays[0].modules_per_string = 2
|
|
1342
|
-
mc2 = ModelChain(system, location,
|
|
1343
|
-
aoi_model='no_loss', spectral_model='no_loss')
|
|
1344
|
-
mc2.run_model(weather)
|
|
1345
|
-
assert isinstance(mc2.results.ac, (pd.Series, pd.DataFrame))
|
|
1346
|
-
assert not mc2.results.ac.empty
|
|
1347
|
-
expected = pd.Series(data=[2., np.nan], index=mc2.results.dc.index,
|
|
1348
|
-
name='p_mp')
|
|
1349
|
-
assert_series_equal(mc2.results.dc / mc1.results.dc, expected)
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
def acdc(mc):
|
|
1353
|
-
mc.results.ac = mc.results.dc
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
@pytest.mark.parametrize('inverter_model', ['sandia', 'adr',
|
|
1357
|
-
'pvwatts', 'sandia_multi',
|
|
1358
|
-
'pvwatts_multi'])
|
|
1359
|
-
def test_ac_models(sapm_dc_snl_ac_system, cec_dc_adr_ac_system,
|
|
1360
|
-
pvwatts_dc_pvwatts_ac_system, cec_dc_snl_ac_arrays,
|
|
1361
|
-
pvwatts_dc_pvwatts_ac_system_arrays,
|
|
1362
|
-
location, inverter_model, weather, mocker):
|
|
1363
|
-
ac_systems = {'sandia': sapm_dc_snl_ac_system,
|
|
1364
|
-
'sandia_multi': cec_dc_snl_ac_arrays,
|
|
1365
|
-
'adr': cec_dc_adr_ac_system,
|
|
1366
|
-
'pvwatts': pvwatts_dc_pvwatts_ac_system,
|
|
1367
|
-
'pvwatts_multi': pvwatts_dc_pvwatts_ac_system_arrays}
|
|
1368
|
-
inverter_to_ac_model = {
|
|
1369
|
-
'sandia': 'sandia',
|
|
1370
|
-
'sandia_multi': 'sandia',
|
|
1371
|
-
'adr': 'adr',
|
|
1372
|
-
'pvwatts': 'pvwatts',
|
|
1373
|
-
'pvwatts_multi': 'pvwatts'}
|
|
1374
|
-
ac_model = inverter_to_ac_model[inverter_model]
|
|
1375
|
-
system = ac_systems[inverter_model]
|
|
1376
|
-
|
|
1377
|
-
mc_inferred = ModelChain(system, location,
|
|
1378
|
-
aoi_model='no_loss', spectral_model='no_loss')
|
|
1379
|
-
mc = ModelChain(system, location, ac_model=ac_model,
|
|
1380
|
-
aoi_model='no_loss', spectral_model='no_loss')
|
|
1381
|
-
|
|
1382
|
-
# tests ModelChain.infer_ac_model
|
|
1383
|
-
assert mc_inferred.ac_model.__name__ == mc.ac_model.__name__
|
|
1384
|
-
|
|
1385
|
-
m = mocker.spy(inverter, inverter_model)
|
|
1386
|
-
mc.run_model(weather)
|
|
1387
|
-
assert m.call_count == 1
|
|
1388
|
-
assert isinstance(mc.results.ac, pd.Series)
|
|
1389
|
-
assert not mc.results.ac.empty
|
|
1390
|
-
assert mc.results.ac.iloc[1] < 1
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
def test_ac_model_user_func(pvwatts_dc_pvwatts_ac_system, location, weather,
|
|
1394
|
-
mocker):
|
|
1395
|
-
m = mocker.spy(sys.modules[__name__], 'acdc')
|
|
1396
|
-
mc = ModelChain(pvwatts_dc_pvwatts_ac_system, location, ac_model=acdc,
|
|
1397
|
-
aoi_model='no_loss', spectral_model='no_loss')
|
|
1398
|
-
mc.run_model(weather)
|
|
1399
|
-
assert m.call_count == 1
|
|
1400
|
-
assert_series_equal(mc.results.ac, mc.results.dc)
|
|
1401
|
-
assert not mc.results.ac.empty
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
def test_ac_model_not_a_model(pvwatts_dc_pvwatts_ac_system, location, weather):
|
|
1405
|
-
exc_text = 'not a valid AC power model'
|
|
1406
|
-
with pytest.raises(ValueError, match=exc_text):
|
|
1407
|
-
ModelChain(pvwatts_dc_pvwatts_ac_system, location,
|
|
1408
|
-
ac_model='not_a_model', aoi_model='no_loss',
|
|
1409
|
-
spectral_model='no_loss')
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
def test_infer_ac_model_invalid_params(location):
|
|
1413
|
-
# only the keys are relevant here, using arbitrary values
|
|
1414
|
-
module_parameters = {'pdc0': 1, 'gamma_pdc': 1}
|
|
1415
|
-
system = pvsystem.PVSystem(
|
|
1416
|
-
arrays=[pvsystem.Array(
|
|
1417
|
-
mount=pvsystem.FixedMount(0, 180),
|
|
1418
|
-
module_parameters=module_parameters
|
|
1419
|
-
)],
|
|
1420
|
-
inverter_parameters={'foo': 1, 'bar': 2}
|
|
1421
|
-
)
|
|
1422
|
-
with pytest.raises(ValueError, match='could not infer AC model'):
|
|
1423
|
-
ModelChain(system, location)
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
def constant_aoi_loss(mc):
|
|
1427
|
-
mc.results.aoi_modifier = 0.9
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
@pytest.mark.parametrize('aoi_model', [
|
|
1431
|
-
'sapm', 'ashrae', 'physical', 'martin_ruiz'
|
|
1432
|
-
])
|
|
1433
|
-
def test_aoi_models(sapm_dc_snl_ac_system, location, aoi_model,
|
|
1434
|
-
weather, mocker):
|
|
1435
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, dc_model='sapm',
|
|
1436
|
-
aoi_model=aoi_model, spectral_model='no_loss')
|
|
1437
|
-
m = mocker.spy(sapm_dc_snl_ac_system, 'get_iam')
|
|
1438
|
-
mc.run_model(weather=weather)
|
|
1439
|
-
assert m.call_count == 1
|
|
1440
|
-
assert isinstance(mc.results.ac, pd.Series)
|
|
1441
|
-
assert not mc.results.ac.empty
|
|
1442
|
-
assert mc.results.ac.iloc[0] > 150 and mc.results.ac.iloc[0] < 200
|
|
1443
|
-
assert mc.results.ac.iloc[1] < 1
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
@pytest.mark.parametrize('aoi_model', [
|
|
1447
|
-
'sapm', 'ashrae', 'physical', 'martin_ruiz'
|
|
1448
|
-
])
|
|
1449
|
-
def test_aoi_models_singleon_weather_single_array(
|
|
1450
|
-
sapm_dc_snl_ac_system, location, aoi_model, weather):
|
|
1451
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, dc_model='sapm',
|
|
1452
|
-
aoi_model=aoi_model, spectral_model='no_loss')
|
|
1453
|
-
mc.run_model(weather=[weather])
|
|
1454
|
-
assert isinstance(mc.results.aoi_modifier, tuple)
|
|
1455
|
-
assert len(mc.results.aoi_modifier) == 1
|
|
1456
|
-
assert isinstance(mc.results.ac, pd.Series)
|
|
1457
|
-
assert not mc.results.ac.empty
|
|
1458
|
-
assert mc.results.ac.iloc[0] > 150 and mc.results.ac.iloc[0] < 200
|
|
1459
|
-
assert mc.results.ac.iloc[1] < 1
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
def test_aoi_model_no_loss(sapm_dc_snl_ac_system, location, weather):
|
|
1463
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, dc_model='sapm',
|
|
1464
|
-
aoi_model='no_loss', spectral_model='no_loss')
|
|
1465
|
-
mc.run_model(weather)
|
|
1466
|
-
assert mc.results.aoi_modifier == 1.0
|
|
1467
|
-
assert not mc.results.ac.empty
|
|
1468
|
-
assert mc.results.ac.iloc[0] > 150 and mc.results.ac.iloc[0] < 200
|
|
1469
|
-
assert mc.results.ac.iloc[1] < 1
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
def test_aoi_model_interp(sapm_dc_snl_ac_system, location, weather, mocker):
|
|
1473
|
-
# similar to test_aoi_models but requires arguments to work, so we
|
|
1474
|
-
# add 'interp' aoi losses model arguments to module
|
|
1475
|
-
iam_ref = (1., 0.85)
|
|
1476
|
-
theta_ref = (0., 80.)
|
|
1477
|
-
sapm_dc_snl_ac_system.arrays[0].module_parameters['iam_ref'] = iam_ref
|
|
1478
|
-
sapm_dc_snl_ac_system.arrays[0].module_parameters['theta_ref'] = theta_ref
|
|
1479
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location,
|
|
1480
|
-
dc_model='sapm', aoi_model='interp',
|
|
1481
|
-
spectral_model='no_loss')
|
|
1482
|
-
m = mocker.spy(iam, 'interp')
|
|
1483
|
-
mc.run_model(weather=weather)
|
|
1484
|
-
# only test kwargs
|
|
1485
|
-
assert m.call_args[1]['iam_ref'] == iam_ref
|
|
1486
|
-
assert m.call_args[1]['theta_ref'] == theta_ref
|
|
1487
|
-
assert isinstance(mc.results.ac, pd.Series)
|
|
1488
|
-
assert not mc.results.ac.empty
|
|
1489
|
-
assert mc.results.ac.iloc[0] > 150 and mc.results.ac.iloc[0] < 200
|
|
1490
|
-
assert mc.results.ac.iloc[1] < 1
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
def test_aoi_model_user_func(sapm_dc_snl_ac_system, location, weather, mocker):
|
|
1494
|
-
m = mocker.spy(sys.modules[__name__], 'constant_aoi_loss')
|
|
1495
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, dc_model='sapm',
|
|
1496
|
-
aoi_model=constant_aoi_loss, spectral_model='no_loss')
|
|
1497
|
-
mc.run_model(weather)
|
|
1498
|
-
assert m.call_count == 1
|
|
1499
|
-
assert mc.results.aoi_modifier == 0.9
|
|
1500
|
-
assert not mc.results.ac.empty
|
|
1501
|
-
assert mc.results.ac.iloc[0] > 140 and mc.results.ac.iloc[0] < 200
|
|
1502
|
-
assert mc.results.ac.iloc[1] < 1
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
@pytest.mark.parametrize('aoi_model', [
|
|
1506
|
-
'sapm', 'ashrae', 'physical', 'martin_ruiz', 'interp'
|
|
1507
|
-
])
|
|
1508
|
-
def test_infer_aoi_model(location, system_no_aoi, aoi_model):
|
|
1509
|
-
for k in iam._IAM_MODEL_PARAMS[aoi_model]:
|
|
1510
|
-
system_no_aoi.arrays[0].module_parameters.update({k: 1.0})
|
|
1511
|
-
mc = ModelChain(system_no_aoi, location, spectral_model='no_loss')
|
|
1512
|
-
assert isinstance(mc, ModelChain)
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
@pytest.mark.parametrize('aoi_model,model_kwargs', [
|
|
1516
|
-
# model_kwargs has both required and optional kwargs; test all
|
|
1517
|
-
('physical',
|
|
1518
|
-
{'n': 1.526, 'K': 4.0, 'L': 0.002, # required
|
|
1519
|
-
'n_ar': 1.8}), # extra
|
|
1520
|
-
('interp',
|
|
1521
|
-
{'theta_ref': (0, 75, 85, 90), 'iam_ref': (1, 0.8, 0.42, 0), # required
|
|
1522
|
-
'method': 'cubic', 'normalize': False})]) # extra
|
|
1523
|
-
def test_infer_aoi_model_with_extra_params(location, system_no_aoi, aoi_model,
|
|
1524
|
-
model_kwargs, weather, mocker):
|
|
1525
|
-
# test extra parameters not defined at iam._IAM_MODEL_PARAMS are passed
|
|
1526
|
-
m = mocker.spy(iam, aoi_model)
|
|
1527
|
-
system_no_aoi.arrays[0].module_parameters.update(**model_kwargs)
|
|
1528
|
-
mc = ModelChain(system_no_aoi, location, spectral_model='no_loss')
|
|
1529
|
-
assert isinstance(mc, ModelChain)
|
|
1530
|
-
mc.run_model(weather=weather)
|
|
1531
|
-
_, call_kwargs = m.call_args
|
|
1532
|
-
assert call_kwargs == model_kwargs
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
def test_infer_aoi_model_invalid(location, system_no_aoi):
|
|
1536
|
-
exc_text = 'could not infer AOI model'
|
|
1537
|
-
with pytest.raises(ValueError, match=exc_text):
|
|
1538
|
-
ModelChain(system_no_aoi, location, spectral_model='no_loss')
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
def constant_spectral_loss(mc):
|
|
1542
|
-
mc.results.spectral_modifier = 0.9
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
@pytest.mark.parametrize('spectral_model', [
|
|
1546
|
-
'sapm', 'first_solar', 'no_loss', constant_spectral_loss
|
|
1547
|
-
])
|
|
1548
|
-
def test_spectral_models(sapm_dc_snl_ac_system, location, spectral_model,
|
|
1549
|
-
weather):
|
|
1550
|
-
# add pw to weather dataframe
|
|
1551
|
-
weather['precipitable_water'] = [0.3, 0.5]
|
|
1552
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, dc_model='sapm',
|
|
1553
|
-
aoi_model='no_loss', spectral_model=spectral_model)
|
|
1554
|
-
spectral_modifier = mc.run_model(weather).results.spectral_modifier
|
|
1555
|
-
assert isinstance(spectral_modifier, (pd.Series, float, int))
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
@pytest.mark.parametrize('spectral_model', [
|
|
1559
|
-
'sapm', 'first_solar', 'no_loss', constant_spectral_loss
|
|
1560
|
-
])
|
|
1561
|
-
def test_spectral_models_singleton_weather_single_array(
|
|
1562
|
-
sapm_dc_snl_ac_system, location, spectral_model, weather):
|
|
1563
|
-
# add pw to weather dataframe
|
|
1564
|
-
weather['precipitable_water'] = [0.3, 0.5]
|
|
1565
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location, dc_model='sapm',
|
|
1566
|
-
aoi_model='no_loss', spectral_model=spectral_model)
|
|
1567
|
-
spectral_modifier = mc.run_model([weather]).results.spectral_modifier
|
|
1568
|
-
assert isinstance(spectral_modifier, tuple)
|
|
1569
|
-
assert len(spectral_modifier) == 1
|
|
1570
|
-
assert isinstance(spectral_modifier[0], (pd.Series, float, int))
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
def constant_losses(mc):
|
|
1574
|
-
mc.results.losses = 0.9
|
|
1575
|
-
mc.results.dc *= mc.results.losses
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
def dc_constant_losses(mc):
|
|
1579
|
-
mc.results.dc['p_mp'] *= 0.9
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
def test_dc_ohmic_model_ohms_from_percent(cec_dc_snl_ac_system,
|
|
1583
|
-
cec_dc_snl_ac_arrays,
|
|
1584
|
-
location,
|
|
1585
|
-
weather,
|
|
1586
|
-
mocker):
|
|
1587
|
-
|
|
1588
|
-
m = mocker.spy(pvsystem, 'dc_ohms_from_percent')
|
|
1589
|
-
|
|
1590
|
-
system = cec_dc_snl_ac_system
|
|
1591
|
-
|
|
1592
|
-
for array in system.arrays:
|
|
1593
|
-
array.array_losses_parameters = dict(dc_ohmic_percent=3)
|
|
1594
|
-
|
|
1595
|
-
mc = ModelChain(system, location,
|
|
1596
|
-
aoi_model='no_loss',
|
|
1597
|
-
spectral_model='no_loss',
|
|
1598
|
-
dc_ohmic_model='dc_ohms_from_percent')
|
|
1599
|
-
mc.run_model(weather)
|
|
1600
|
-
|
|
1601
|
-
assert m.call_count == 1
|
|
1602
|
-
|
|
1603
|
-
assert isinstance(mc.results.dc_ohmic_losses, pd.Series)
|
|
1604
|
-
|
|
1605
|
-
system = cec_dc_snl_ac_arrays
|
|
1606
|
-
|
|
1607
|
-
for array in system.arrays:
|
|
1608
|
-
array.array_losses_parameters = dict(dc_ohmic_percent=3)
|
|
1609
|
-
|
|
1610
|
-
mc = ModelChain(system, location,
|
|
1611
|
-
aoi_model='no_loss',
|
|
1612
|
-
spectral_model='no_loss',
|
|
1613
|
-
dc_ohmic_model='dc_ohms_from_percent')
|
|
1614
|
-
mc.run_model(weather)
|
|
1615
|
-
|
|
1616
|
-
assert m.call_count == 3
|
|
1617
|
-
assert len(mc.results.dc_ohmic_losses) == len(mc.system.arrays)
|
|
1618
|
-
|
|
1619
|
-
assert isinstance(mc.results.dc_ohmic_losses, tuple)
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
def test_dc_ohmic_model_no_dc_ohmic_loss(cec_dc_snl_ac_system,
|
|
1623
|
-
location,
|
|
1624
|
-
weather,
|
|
1625
|
-
mocker):
|
|
1626
|
-
|
|
1627
|
-
m = mocker.spy(modelchain.ModelChain, 'no_dc_ohmic_loss')
|
|
1628
|
-
mc = ModelChain(cec_dc_snl_ac_system, location,
|
|
1629
|
-
aoi_model='no_loss',
|
|
1630
|
-
spectral_model='no_loss',
|
|
1631
|
-
dc_ohmic_model='no_loss')
|
|
1632
|
-
mc.run_model(weather)
|
|
1633
|
-
|
|
1634
|
-
assert mc.dc_ohmic_model == mc.no_dc_ohmic_loss
|
|
1635
|
-
assert m.call_count == 1
|
|
1636
|
-
assert mc.results.dc_ohmic_losses is None
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
def test_dc_ohmic_ext_def(cec_dc_snl_ac_system, location,
|
|
1640
|
-
weather, mocker):
|
|
1641
|
-
m = mocker.spy(sys.modules[__name__], 'dc_constant_losses')
|
|
1642
|
-
mc = ModelChain(cec_dc_snl_ac_system, location,
|
|
1643
|
-
aoi_model='no_loss',
|
|
1644
|
-
spectral_model='no_loss',
|
|
1645
|
-
dc_ohmic_model=dc_constant_losses)
|
|
1646
|
-
mc.run_model(weather)
|
|
1647
|
-
|
|
1648
|
-
assert m.call_count == 1
|
|
1649
|
-
assert isinstance(mc.results.ac, (pd.Series, pd.DataFrame))
|
|
1650
|
-
assert not mc.results.ac.empty
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
def test_dc_ohmic_not_a_model(cec_dc_snl_ac_system, location,
|
|
1654
|
-
weather, mocker):
|
|
1655
|
-
exc_text = 'not_a_dc_model is not a valid losses model'
|
|
1656
|
-
with pytest.raises(ValueError, match=exc_text):
|
|
1657
|
-
ModelChain(cec_dc_snl_ac_system, location,
|
|
1658
|
-
aoi_model='no_loss',
|
|
1659
|
-
spectral_model='no_loss',
|
|
1660
|
-
dc_ohmic_model='not_a_dc_model')
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
def test_losses_models_pvwatts(pvwatts_dc_pvwatts_ac_system, location, weather,
|
|
1664
|
-
mocker):
|
|
1665
|
-
age = 1
|
|
1666
|
-
pvwatts_dc_pvwatts_ac_system.losses_parameters = dict(age=age)
|
|
1667
|
-
m = mocker.spy(pvsystem, 'pvwatts_losses')
|
|
1668
|
-
mc = ModelChain(pvwatts_dc_pvwatts_ac_system, location, dc_model='pvwatts',
|
|
1669
|
-
aoi_model='no_loss', spectral_model='no_loss',
|
|
1670
|
-
losses_model='pvwatts')
|
|
1671
|
-
mc.run_model(weather)
|
|
1672
|
-
assert m.call_count == 1
|
|
1673
|
-
m.assert_called_with(age=age)
|
|
1674
|
-
assert isinstance(mc.results.ac, (pd.Series, pd.DataFrame))
|
|
1675
|
-
assert not mc.results.ac.empty
|
|
1676
|
-
# check that we're applying correction to dc
|
|
1677
|
-
# GH 696
|
|
1678
|
-
dc_with_loss = mc.results.dc
|
|
1679
|
-
mc = ModelChain(pvwatts_dc_pvwatts_ac_system, location, dc_model='pvwatts',
|
|
1680
|
-
aoi_model='no_loss', spectral_model='no_loss',
|
|
1681
|
-
losses_model='no_loss')
|
|
1682
|
-
mc.run_model(weather)
|
|
1683
|
-
assert not np.allclose(mc.results.dc, dc_with_loss, equal_nan=True)
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
def test_losses_models_pvwatts_arrays(multi_array_sapm_dc_snl_ac_system,
|
|
1687
|
-
location, weather):
|
|
1688
|
-
age = 1
|
|
1689
|
-
system_both = multi_array_sapm_dc_snl_ac_system['two_array_system']
|
|
1690
|
-
system_both.losses_parameters = dict(age=age)
|
|
1691
|
-
mc = ModelChain(system_both, location,
|
|
1692
|
-
aoi_model='no_loss', spectral_model='no_loss',
|
|
1693
|
-
losses_model='pvwatts')
|
|
1694
|
-
mc.run_model(weather)
|
|
1695
|
-
dc_with_loss = mc.results.dc
|
|
1696
|
-
mc = ModelChain(system_both, location,
|
|
1697
|
-
aoi_model='no_loss', spectral_model='no_loss',
|
|
1698
|
-
losses_model='no_loss')
|
|
1699
|
-
mc.run_model(weather)
|
|
1700
|
-
assert not np.allclose(mc.results.dc[0], dc_with_loss[0], equal_nan=True)
|
|
1701
|
-
assert not np.allclose(mc.results.dc[1], dc_with_loss[1], equal_nan=True)
|
|
1702
|
-
assert not mc.results.ac.empty
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
def test_losses_models_ext_def(pvwatts_dc_pvwatts_ac_system, location, weather,
|
|
1706
|
-
mocker):
|
|
1707
|
-
m = mocker.spy(sys.modules[__name__], 'constant_losses')
|
|
1708
|
-
mc = ModelChain(pvwatts_dc_pvwatts_ac_system, location, dc_model='pvwatts',
|
|
1709
|
-
aoi_model='no_loss', spectral_model='no_loss',
|
|
1710
|
-
losses_model=constant_losses)
|
|
1711
|
-
mc.run_model(weather)
|
|
1712
|
-
assert m.call_count == 1
|
|
1713
|
-
assert isinstance(mc.results.ac, (pd.Series, pd.DataFrame))
|
|
1714
|
-
assert mc.results.losses == 0.9
|
|
1715
|
-
assert not mc.results.ac.empty
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
def test_losses_models_no_loss(pvwatts_dc_pvwatts_ac_system, location, weather,
|
|
1719
|
-
mocker):
|
|
1720
|
-
m = mocker.spy(pvsystem, 'pvwatts_losses')
|
|
1721
|
-
mc = ModelChain(pvwatts_dc_pvwatts_ac_system, location, dc_model='pvwatts',
|
|
1722
|
-
aoi_model='no_loss', spectral_model='no_loss',
|
|
1723
|
-
losses_model='no_loss')
|
|
1724
|
-
assert mc.losses_model == mc.no_extra_losses
|
|
1725
|
-
mc.run_model(weather)
|
|
1726
|
-
assert m.call_count == 0
|
|
1727
|
-
assert mc.results.losses == 1
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
def test_invalid_dc_model_params(sapm_dc_snl_ac_system, cec_dc_snl_ac_system,
|
|
1731
|
-
pvwatts_dc_pvwatts_ac_system, location):
|
|
1732
|
-
kwargs = {'dc_model': 'sapm', 'ac_model': 'sandia',
|
|
1733
|
-
'aoi_model': 'no_loss', 'spectral_model': 'no_loss',
|
|
1734
|
-
'temperature_model': 'sapm', 'losses_model': 'no_loss'}
|
|
1735
|
-
for array in sapm_dc_snl_ac_system.arrays:
|
|
1736
|
-
array.module_parameters.pop('A0') # remove a parameter
|
|
1737
|
-
with pytest.raises(ValueError):
|
|
1738
|
-
ModelChain(sapm_dc_snl_ac_system, location, **kwargs)
|
|
1739
|
-
|
|
1740
|
-
kwargs['dc_model'] = 'singlediode'
|
|
1741
|
-
for array in cec_dc_snl_ac_system.arrays:
|
|
1742
|
-
array.module_parameters.pop('a_ref') # remove a parameter
|
|
1743
|
-
with pytest.raises(ValueError):
|
|
1744
|
-
ModelChain(cec_dc_snl_ac_system, location, **kwargs)
|
|
1745
|
-
|
|
1746
|
-
kwargs['dc_model'] = 'pvwatts'
|
|
1747
|
-
kwargs['ac_model'] = 'pvwatts'
|
|
1748
|
-
for array in pvwatts_dc_pvwatts_ac_system.arrays:
|
|
1749
|
-
array.module_parameters.pop('pdc0')
|
|
1750
|
-
|
|
1751
|
-
match = 'one or more Arrays are missing one or more required parameters'
|
|
1752
|
-
with pytest.raises(ValueError, match=match):
|
|
1753
|
-
ModelChain(pvwatts_dc_pvwatts_ac_system, location, **kwargs)
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
@pytest.mark.parametrize('model', [
|
|
1757
|
-
'dc_model', 'ac_model', 'aoi_model', 'spectral_model',
|
|
1758
|
-
'temperature_model', 'losses_model'
|
|
1759
|
-
])
|
|
1760
|
-
def test_invalid_models(model, sapm_dc_snl_ac_system, location):
|
|
1761
|
-
kwargs = {'dc_model': 'pvwatts', 'ac_model': 'pvwatts',
|
|
1762
|
-
'aoi_model': 'no_loss', 'spectral_model': 'no_loss',
|
|
1763
|
-
'temperature_model': 'sapm', 'losses_model': 'no_loss'}
|
|
1764
|
-
kwargs[model] = 'invalid'
|
|
1765
|
-
with pytest.raises(ValueError):
|
|
1766
|
-
ModelChain(sapm_dc_snl_ac_system, location, **kwargs)
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
def test_bad_get_orientation():
|
|
1770
|
-
with pytest.raises(ValueError):
|
|
1771
|
-
modelchain.get_orientation('bad value')
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
# tests for PVSystem with multiple Arrays
|
|
1775
|
-
def test_with_sapm_pvsystem_arrays(sapm_dc_snl_ac_system_Array, location,
|
|
1776
|
-
weather):
|
|
1777
|
-
mc = ModelChain.with_sapm(sapm_dc_snl_ac_system_Array, location,
|
|
1778
|
-
ac_model='sandia')
|
|
1779
|
-
assert mc.dc_model == mc.sapm
|
|
1780
|
-
assert mc.ac_model == mc.sandia_inverter
|
|
1781
|
-
mc.run_model(weather)
|
|
1782
|
-
assert mc.results
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
def test_ModelChain_no_extra_kwargs(sapm_dc_snl_ac_system, location):
|
|
1786
|
-
with pytest.raises(TypeError, match="arbitrary_kwarg"):
|
|
1787
|
-
ModelChain(sapm_dc_snl_ac_system, location, arbitrary_kwarg='value')
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
def test_complete_irradiance_clean_run(sapm_dc_snl_ac_system, location):
|
|
1791
|
-
"""The DataFrame should not change if all columns are passed"""
|
|
1792
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
1793
|
-
times = pd.date_range('2010-07-05 9:00:00', periods=2, freq='h')
|
|
1794
|
-
i = pd.DataFrame(
|
|
1795
|
-
{'dni': [2, 3], 'dhi': [4, 6], 'ghi': [9, 5]}, index=times)
|
|
1796
|
-
|
|
1797
|
-
mc.complete_irradiance(i)
|
|
1798
|
-
|
|
1799
|
-
assert_series_equal(mc.results.weather['dni'],
|
|
1800
|
-
pd.Series([2, 3], index=times, name='dni'))
|
|
1801
|
-
assert_series_equal(mc.results.weather['dhi'],
|
|
1802
|
-
pd.Series([4, 6], index=times, name='dhi'))
|
|
1803
|
-
assert_series_equal(mc.results.weather['ghi'],
|
|
1804
|
-
pd.Series([9, 5], index=times, name='ghi'))
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
def test_complete_irradiance(sapm_dc_snl_ac_system, location, mocker):
|
|
1808
|
-
"""Check calculations"""
|
|
1809
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
1810
|
-
times = pd.date_range('2010-07-05 7:00:00-0700', periods=2, freq='h')
|
|
1811
|
-
i = pd.DataFrame({'dni': [49.756966, 62.153947],
|
|
1812
|
-
'ghi': [372.103976116, 497.087579068],
|
|
1813
|
-
'dhi': [356.543700, 465.44400]}, index=times)
|
|
1814
|
-
|
|
1815
|
-
with pytest.warns(UserWarning):
|
|
1816
|
-
mc.complete_irradiance(i[['ghi', 'dni']])
|
|
1817
|
-
assert_series_equal(mc.results.weather['dhi'],
|
|
1818
|
-
pd.Series([356.543700, 465.44400],
|
|
1819
|
-
index=times, name='dhi'))
|
|
1820
|
-
|
|
1821
|
-
with pytest.warns(UserWarning):
|
|
1822
|
-
mc.complete_irradiance(i[['dhi', 'dni']])
|
|
1823
|
-
assert_series_equal(mc.results.weather['ghi'],
|
|
1824
|
-
pd.Series([372.103976116, 497.087579068],
|
|
1825
|
-
index=times, name='ghi'))
|
|
1826
|
-
|
|
1827
|
-
# check that clearsky_model is used correctly
|
|
1828
|
-
m_ineichen = mocker.spy(location, 'get_clearsky')
|
|
1829
|
-
mc.complete_irradiance(i[['dhi', 'ghi']])
|
|
1830
|
-
assert m_ineichen.call_count == 1
|
|
1831
|
-
assert m_ineichen.call_args[1]['model'] == 'ineichen'
|
|
1832
|
-
assert_series_equal(mc.results.weather['dni'],
|
|
1833
|
-
pd.Series([49.756966, 62.153947],
|
|
1834
|
-
index=times, name='dni'))
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
@pytest.mark.filterwarnings("ignore:This function is not safe at the moment")
|
|
1838
|
-
@pytest.mark.parametrize("input_type", [tuple, list])
|
|
1839
|
-
def test_complete_irradiance_arrays(
|
|
1840
|
-
sapm_dc_snl_ac_system_same_arrays, location, input_type):
|
|
1841
|
-
"""ModelChain.complete_irradiance can accept a tuple of weather
|
|
1842
|
-
DataFrames."""
|
|
1843
|
-
times = pd.date_range(start='2020-01-01 0700-0700', periods=2, freq='h')
|
|
1844
|
-
weather = pd.DataFrame({'dni': [2, 3],
|
|
1845
|
-
'dhi': [4, 6],
|
|
1846
|
-
'ghi': [9, 5]}, index=times)
|
|
1847
|
-
mc = ModelChain(sapm_dc_snl_ac_system_same_arrays, location)
|
|
1848
|
-
with pytest.raises(ValueError,
|
|
1849
|
-
match=r"Input DataFrames must have same index\."):
|
|
1850
|
-
mc.complete_irradiance(input_type((weather, weather[1:])))
|
|
1851
|
-
mc.complete_irradiance(input_type((weather, weather)))
|
|
1852
|
-
for mc_weather in mc.results.weather:
|
|
1853
|
-
assert_series_equal(mc_weather['dni'],
|
|
1854
|
-
pd.Series([2, 3], index=times, name='dni'))
|
|
1855
|
-
assert_series_equal(mc_weather['dhi'],
|
|
1856
|
-
pd.Series([4, 6], index=times, name='dhi'))
|
|
1857
|
-
assert_series_equal(mc_weather['ghi'],
|
|
1858
|
-
pd.Series([9, 5], index=times, name='ghi'))
|
|
1859
|
-
mc = ModelChain(sapm_dc_snl_ac_system_same_arrays, location)
|
|
1860
|
-
mc.complete_irradiance(input_type((weather[['ghi', 'dhi']],
|
|
1861
|
-
weather[['dhi', 'dni']])))
|
|
1862
|
-
assert 'dni' in mc.results.weather[0].columns
|
|
1863
|
-
assert 'ghi' in mc.results.weather[1].columns
|
|
1864
|
-
mc.complete_irradiance(input_type((weather, weather[['ghi', 'dni']])))
|
|
1865
|
-
assert_series_equal(mc.results.weather[0]['dhi'],
|
|
1866
|
-
pd.Series([4, 6], index=times, name='dhi'))
|
|
1867
|
-
assert_series_equal(mc.results.weather[0]['ghi'],
|
|
1868
|
-
pd.Series([9, 5], index=times, name='ghi'))
|
|
1869
|
-
assert_series_equal(mc.results.weather[0]['dni'],
|
|
1870
|
-
pd.Series([2, 3], index=times, name='dni'))
|
|
1871
|
-
assert 'dhi' in mc.results.weather[1].columns
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
@pytest.mark.parametrize("input_type", [tuple, list])
|
|
1875
|
-
def test_complete_irradiance_arrays_wrong_length(
|
|
1876
|
-
sapm_dc_snl_ac_system_same_arrays, location, input_type):
|
|
1877
|
-
mc = ModelChain(sapm_dc_snl_ac_system_same_arrays, location)
|
|
1878
|
-
times = pd.date_range(start='2020-01-01 0700-0700', periods=2, freq='h')
|
|
1879
|
-
weather = pd.DataFrame({'dni': [2, 3],
|
|
1880
|
-
'dhi': [4, 6],
|
|
1881
|
-
'ghi': [9, 5]}, index=times)
|
|
1882
|
-
error_str = "Input must be same length as number " \
|
|
1883
|
-
r"of Arrays in system\. Expected 2, got [0-9]+\."
|
|
1884
|
-
with pytest.raises(ValueError, match=error_str):
|
|
1885
|
-
mc.complete_irradiance(input_type((weather,)))
|
|
1886
|
-
with pytest.raises(ValueError, match=error_str):
|
|
1887
|
-
mc.complete_irradiance(input_type((weather, weather, weather)))
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
def test_unknown_attribute(sapm_dc_snl_ac_system, location):
|
|
1891
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
1892
|
-
with pytest.raises(AttributeError):
|
|
1893
|
-
mc.unknown_attribute
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
def test_inconsistent_array_params(location,
|
|
1897
|
-
sapm_module_params,
|
|
1898
|
-
cec_module_params):
|
|
1899
|
-
module_error = ".* selected for the DC model but one or more Arrays are " \
|
|
1900
|
-
"missing one or more required parameters"
|
|
1901
|
-
temperature_error = 'Could not infer temperature model from ' \
|
|
1902
|
-
'ModelChain.system. '
|
|
1903
|
-
different_module_system = pvsystem.PVSystem(
|
|
1904
|
-
arrays=[
|
|
1905
|
-
pvsystem.Array(
|
|
1906
|
-
mount=pvsystem.FixedMount(0, 180),
|
|
1907
|
-
module_parameters=sapm_module_params),
|
|
1908
|
-
pvsystem.Array(
|
|
1909
|
-
mount=pvsystem.FixedMount(0, 180),
|
|
1910
|
-
module_parameters=cec_module_params),
|
|
1911
|
-
pvsystem.Array(
|
|
1912
|
-
mount=pvsystem.FixedMount(0, 180),
|
|
1913
|
-
module_parameters=cec_module_params)]
|
|
1914
|
-
)
|
|
1915
|
-
with pytest.raises(ValueError, match=module_error):
|
|
1916
|
-
ModelChain(different_module_system, location, dc_model='cec')
|
|
1917
|
-
different_temp_system = pvsystem.PVSystem(
|
|
1918
|
-
arrays=[
|
|
1919
|
-
pvsystem.Array(
|
|
1920
|
-
mount=pvsystem.FixedMount(0, 180),
|
|
1921
|
-
module_parameters=cec_module_params,
|
|
1922
|
-
temperature_model_parameters={'a': 1,
|
|
1923
|
-
'b': 1,
|
|
1924
|
-
'deltaT': 1}),
|
|
1925
|
-
pvsystem.Array(
|
|
1926
|
-
mount=pvsystem.FixedMount(0, 180),
|
|
1927
|
-
module_parameters=cec_module_params,
|
|
1928
|
-
temperature_model_parameters={'a': 2,
|
|
1929
|
-
'b': 2,
|
|
1930
|
-
'deltaT': 2}),
|
|
1931
|
-
pvsystem.Array(
|
|
1932
|
-
mount=pvsystem.FixedMount(0, 180),
|
|
1933
|
-
module_parameters=cec_module_params,
|
|
1934
|
-
temperature_model_parameters={'b': 3, 'deltaT': 3})]
|
|
1935
|
-
)
|
|
1936
|
-
with pytest.raises(ValueError, match=temperature_error):
|
|
1937
|
-
ModelChain(different_temp_system, location,
|
|
1938
|
-
ac_model='sandia',
|
|
1939
|
-
aoi_model='no_loss', spectral_model='no_loss',
|
|
1940
|
-
temperature_model='sapm')
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
def test_modelchain__common_keys():
|
|
1944
|
-
dictionary = {'a': 1, 'b': 1}
|
|
1945
|
-
series = pd.Series(dictionary)
|
|
1946
|
-
assert {'a', 'b'} == modelchain._common_keys(
|
|
1947
|
-
{'a': 1, 'b': 1}
|
|
1948
|
-
)
|
|
1949
|
-
assert {'a', 'b'} == modelchain._common_keys(
|
|
1950
|
-
pd.Series({'a': 1, 'b': 1})
|
|
1951
|
-
)
|
|
1952
|
-
assert {'a', 'b'} == modelchain._common_keys(
|
|
1953
|
-
(dictionary, series)
|
|
1954
|
-
)
|
|
1955
|
-
no_a = dictionary.copy()
|
|
1956
|
-
del no_a['a']
|
|
1957
|
-
assert {'b'} == modelchain._common_keys(
|
|
1958
|
-
(dictionary, no_a)
|
|
1959
|
-
)
|
|
1960
|
-
assert {'b'} == modelchain._common_keys(
|
|
1961
|
-
(series, pd.Series(no_a))
|
|
1962
|
-
)
|
|
1963
|
-
assert {'b'} == modelchain._common_keys(
|
|
1964
|
-
(series, no_a)
|
|
1965
|
-
)
|
|
1966
|
-
|
|
1967
|
-
|
|
1968
|
-
def test__irrad_for_celltemp():
|
|
1969
|
-
total_irrad = pd.DataFrame(index=[0, 1], columns=['poa_global'],
|
|
1970
|
-
data=[10., 20.])
|
|
1971
|
-
empty = total_irrad.drop('poa_global', axis=1)
|
|
1972
|
-
effect_irrad = pd.Series(index=total_irrad.index, data=[5., 8.])
|
|
1973
|
-
# test with single array inputs
|
|
1974
|
-
poa = modelchain._irrad_for_celltemp(total_irrad, effect_irrad)
|
|
1975
|
-
assert_series_equal(poa, total_irrad['poa_global'])
|
|
1976
|
-
poa = modelchain._irrad_for_celltemp(empty, effect_irrad)
|
|
1977
|
-
assert_series_equal(poa, effect_irrad)
|
|
1978
|
-
# test with tuples
|
|
1979
|
-
poa = modelchain._irrad_for_celltemp(
|
|
1980
|
-
(total_irrad, total_irrad), (effect_irrad, effect_irrad))
|
|
1981
|
-
assert len(poa) == 2
|
|
1982
|
-
assert_series_equal(poa[0], total_irrad['poa_global'])
|
|
1983
|
-
assert_series_equal(poa[1], total_irrad['poa_global'])
|
|
1984
|
-
poa = modelchain._irrad_for_celltemp(
|
|
1985
|
-
(empty, empty), (effect_irrad, effect_irrad))
|
|
1986
|
-
assert len(poa) == 2
|
|
1987
|
-
assert_series_equal(poa[0], effect_irrad)
|
|
1988
|
-
assert_series_equal(poa[1], effect_irrad)
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
def test_ModelChain___repr__(sapm_dc_snl_ac_system, location):
|
|
1992
|
-
|
|
1993
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location,
|
|
1994
|
-
name='my mc')
|
|
1995
|
-
|
|
1996
|
-
expected = '\n'.join([
|
|
1997
|
-
'ModelChain: ',
|
|
1998
|
-
' name: my mc',
|
|
1999
|
-
' clearsky_model: ineichen',
|
|
2000
|
-
' transposition_model: haydavies',
|
|
2001
|
-
' solar_position_method: nrel_numpy',
|
|
2002
|
-
' airmass_model: kastenyoung1989',
|
|
2003
|
-
' dc_model: sapm',
|
|
2004
|
-
' ac_model: sandia_inverter',
|
|
2005
|
-
' aoi_model: sapm_aoi_loss',
|
|
2006
|
-
' spectral_model: sapm_spectral_loss',
|
|
2007
|
-
' temperature_model: sapm_temp',
|
|
2008
|
-
' losses_model: no_extra_losses'
|
|
2009
|
-
])
|
|
2010
|
-
|
|
2011
|
-
assert mc.__repr__() == expected
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
def test_ModelChainResult___repr__(sapm_dc_snl_ac_system, location, weather):
|
|
2015
|
-
mc = ModelChain(sapm_dc_snl_ac_system, location)
|
|
2016
|
-
mc.run_model(weather)
|
|
2017
|
-
mcres = mc.results.__repr__()
|
|
2018
|
-
mc_attrs = dir(mc.results)
|
|
2019
|
-
mc_attrs = [a for a in mc_attrs if not a.startswith('_')]
|
|
2020
|
-
assert all(a in mcres for a in mc_attrs)
|