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.
Files changed (149) hide show
  1. pvlib/__init__.py +1 -0
  2. pvlib/_deprecation.py +73 -0
  3. pvlib/atmosphere.py +77 -7
  4. pvlib/bifacial/infinite_sheds.py +4 -3
  5. pvlib/bifacial/utils.py +2 -1
  6. pvlib/clearsky.py +35 -22
  7. pvlib/iam.py +4 -4
  8. pvlib/iotools/midc.py +1 -1
  9. pvlib/iotools/psm3.py +1 -1
  10. pvlib/iotools/pvgis.py +10 -12
  11. pvlib/iotools/tmy.py +3 -69
  12. pvlib/irradiance.py +112 -55
  13. pvlib/ivtools/sdm.py +75 -52
  14. pvlib/location.py +73 -33
  15. pvlib/modelchain.py +18 -35
  16. pvlib/pvsystem.py +139 -94
  17. pvlib/snow.py +64 -28
  18. pvlib/solarposition.py +46 -30
  19. pvlib/spa.py +4 -2
  20. pvlib/spectrum/__init__.py +0 -1
  21. pvlib/spectrum/irradiance.py +2 -64
  22. pvlib/spectrum/mismatch.py +3 -3
  23. pvlib/spectrum/spectrl2.py +2 -1
  24. pvlib/temperature.py +49 -3
  25. pvlib/tools.py +6 -5
  26. {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info}/METADATA +14 -11
  27. pvlib-0.12.0.dist-info/RECORD +75 -0
  28. {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info}/WHEEL +1 -1
  29. pvlib/data/BIRD_08_16_2012.csv +0 -8761
  30. pvlib/data/BIRD_08_16_2012_patm.csv +0 -8761
  31. pvlib/data/Burlington, United States SolarAnywhere Time Series 2021 Lat_44_465 Lon_-73_205 TMY3 format.csv +0 -8762
  32. pvlib/data/Burlington, United States SolarAnywhere Time Series 20210101 to 20210103 Lat_44_4675 Lon_-73_2075 SA format.csv +0 -578
  33. pvlib/data/Burlington, United States SolarAnywhere Typical GHI Year Lat_44_465 Lon_-73_205 SA format.csv +0 -74
  34. pvlib/data/CPS SCH275KTL-DO-US-800-250kW_275kVA_1.OND +0 -146
  35. pvlib/data/CRNS0101-05-2019-AZ_Tucson_11_W.txt +0 -4
  36. pvlib/data/CRN_with_problems.txt +0 -3
  37. pvlib/data/ET-M772BH550GL.PAN +0 -75
  38. pvlib/data/NLD_Amsterdam062400_IWEC.epw +0 -8768
  39. pvlib/data/PVsyst_demo.csv +0 -10757
  40. pvlib/data/PVsyst_demo_model.csv +0 -3588
  41. pvlib/data/SRML-day-EUPO1801.txt +0 -1441
  42. pvlib/data/abq19056.dat +0 -6
  43. pvlib/data/aod550_tcwv_20121101_test.nc +0 -0
  44. pvlib/data/bishop88_numerical_precision.csv +0 -101
  45. pvlib/data/bsrn-lr0100-pay0616.dat +0 -86901
  46. pvlib/data/bsrn-pay0616.dat.gz +0 -0
  47. pvlib/data/cams_mcclear_1min_verbose.csv +0 -60
  48. pvlib/data/cams_mcclear_monthly.csv +0 -42
  49. pvlib/data/cams_radiation_1min_verbose.csv +0 -72
  50. pvlib/data/cams_radiation_monthly.csv +0 -47
  51. pvlib/data/detect_clearsky_data.csv +0 -35
  52. pvlib/data/detect_clearsky_threshold_data.csv +0 -126
  53. pvlib/data/greensboro_kimber_soil_manwash.dat +0 -8761
  54. pvlib/data/greensboro_kimber_soil_nowash.dat +0 -8761
  55. pvlib/data/inverter_fit_snl_meas.csv +0 -127
  56. pvlib/data/inverter_fit_snl_sim.csv +0 -19
  57. pvlib/data/ivtools_numdiff.csv +0 -52
  58. pvlib/data/midc_20181014.txt +0 -1441
  59. pvlib/data/midc_raw_20181018.txt +0 -1441
  60. pvlib/data/midc_raw_short_header_20191115.txt +0 -1441
  61. pvlib/data/msn19056.dat +0 -6
  62. pvlib/data/precise_iv_curves1.json +0 -10251
  63. pvlib/data/precise_iv_curves2.json +0 -10251
  64. pvlib/data/precise_iv_curves_parameter_sets1.csv +0 -33
  65. pvlib/data/precise_iv_curves_parameter_sets2.csv +0 -33
  66. pvlib/data/pvgis_hourly_Timeseries_45.000_8.000_SA2_10kWp_CIS_5_2a_2013_2014.json +0 -1
  67. pvlib/data/pvgis_hourly_Timeseries_45.000_8.000_SA_30deg_0deg_2016_2016.csv +0 -35
  68. pvlib/data/pvgis_tmy_meta.json +0 -32
  69. pvlib/data/pvgis_tmy_test.dat +0 -8761
  70. pvlib/data/pvwatts_8760_rackmount.csv +0 -8779
  71. pvlib/data/pvwatts_8760_roofmount.csv +0 -8779
  72. pvlib/data/singleaxis_tracker_wslope.csv +0 -8761
  73. pvlib/data/spectrl2_example_spectra.csv +0 -123
  74. pvlib/data/surfrad-slv16001.dat +0 -1442
  75. pvlib/data/test_psm3_2017.csv +0 -17521
  76. pvlib/data/test_psm3_2019_5min.csv +0 -289
  77. pvlib/data/test_psm3_tmy-2017.csv +0 -8761
  78. pvlib/data/test_read_psm3.csv +0 -17523
  79. pvlib/data/test_read_pvgis_horizon.csv +0 -49
  80. pvlib/data/tmy_45.000_8.000_2005_2020.csv +0 -8789
  81. pvlib/data/tmy_45.000_8.000_2005_2020.epw +0 -8768
  82. pvlib/data/tmy_45.000_8.000_2005_2020.json +0 -1
  83. pvlib/data/tmy_45.000_8.000_2005_2020.txt +0 -8761
  84. pvlib/data/tmy_45.000_8.000_userhorizon.json +0 -1
  85. pvlib/data/variables_style_rules.csv +0 -56
  86. pvlib/spa_c_files/README.md +0 -81
  87. pvlib/spa_c_files/cspa_py.pxd +0 -43
  88. pvlib/spa_c_files/spa_py.pyx +0 -30
  89. pvlib/tests/__init__.py +0 -0
  90. pvlib/tests/bifacial/__init__.py +0 -0
  91. pvlib/tests/bifacial/test_infinite_sheds.py +0 -317
  92. pvlib/tests/bifacial/test_losses_models.py +0 -54
  93. pvlib/tests/bifacial/test_pvfactors.py +0 -82
  94. pvlib/tests/bifacial/test_utils.py +0 -192
  95. pvlib/tests/conftest.py +0 -476
  96. pvlib/tests/iotools/__init__.py +0 -0
  97. pvlib/tests/iotools/test_acis.py +0 -213
  98. pvlib/tests/iotools/test_bsrn.py +0 -131
  99. pvlib/tests/iotools/test_crn.py +0 -95
  100. pvlib/tests/iotools/test_epw.py +0 -23
  101. pvlib/tests/iotools/test_midc.py +0 -89
  102. pvlib/tests/iotools/test_panond.py +0 -32
  103. pvlib/tests/iotools/test_psm3.py +0 -198
  104. pvlib/tests/iotools/test_pvgis.py +0 -644
  105. pvlib/tests/iotools/test_sodapro.py +0 -298
  106. pvlib/tests/iotools/test_solaranywhere.py +0 -287
  107. pvlib/tests/iotools/test_solargis.py +0 -68
  108. pvlib/tests/iotools/test_solcast.py +0 -324
  109. pvlib/tests/iotools/test_solrad.py +0 -152
  110. pvlib/tests/iotools/test_srml.py +0 -124
  111. pvlib/tests/iotools/test_surfrad.py +0 -75
  112. pvlib/tests/iotools/test_tmy.py +0 -133
  113. pvlib/tests/ivtools/__init__.py +0 -0
  114. pvlib/tests/ivtools/test_sde.py +0 -230
  115. pvlib/tests/ivtools/test_sdm.py +0 -407
  116. pvlib/tests/ivtools/test_utils.py +0 -173
  117. pvlib/tests/spectrum/__init__.py +0 -0
  118. pvlib/tests/spectrum/conftest.py +0 -40
  119. pvlib/tests/spectrum/test_irradiance.py +0 -138
  120. pvlib/tests/spectrum/test_mismatch.py +0 -304
  121. pvlib/tests/spectrum/test_response.py +0 -124
  122. pvlib/tests/spectrum/test_spectrl2.py +0 -72
  123. pvlib/tests/test_albedo.py +0 -84
  124. pvlib/tests/test_atmosphere.py +0 -204
  125. pvlib/tests/test_clearsky.py +0 -878
  126. pvlib/tests/test_conftest.py +0 -81
  127. pvlib/tests/test_iam.py +0 -555
  128. pvlib/tests/test_inverter.py +0 -213
  129. pvlib/tests/test_irradiance.py +0 -1441
  130. pvlib/tests/test_location.py +0 -356
  131. pvlib/tests/test_modelchain.py +0 -2020
  132. pvlib/tests/test_numerical_precision.py +0 -124
  133. pvlib/tests/test_pvarray.py +0 -71
  134. pvlib/tests/test_pvsystem.py +0 -2495
  135. pvlib/tests/test_scaling.py +0 -207
  136. pvlib/tests/test_shading.py +0 -391
  137. pvlib/tests/test_singlediode.py +0 -608
  138. pvlib/tests/test_snow.py +0 -212
  139. pvlib/tests/test_soiling.py +0 -230
  140. pvlib/tests/test_solarposition.py +0 -933
  141. pvlib/tests/test_spa.py +0 -425
  142. pvlib/tests/test_temperature.py +0 -470
  143. pvlib/tests/test_tools.py +0 -146
  144. pvlib/tests/test_tracking.py +0 -474
  145. pvlib/tests/test_transformer.py +0 -60
  146. pvlib-0.11.1.dist-info/RECORD +0 -192
  147. {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info/licenses}/AUTHORS.md +0 -0
  148. {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info/licenses}/LICENSE +0 -0
  149. {pvlib-0.11.1.dist-info → pvlib-0.12.0.dist-info}/top_level.txt +0 -0
@@ -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)