@@ -256,7 +256,7 @@ class ModelChainResult:
256
256
_per_array_fields = {'total_irrad' , 'aoi' , 'aoi_modifier' ,
257
257
'spectral_modifier' , 'cell_temperature' ,
258
258
'effective_irradiance' , 'dc' , 'diode_params' ,
259
- 'dc_ohmic_losses' }
259
+ 'dc_ohmic_losses' , 'weather' }
260
260
261
261
# system-level information
262
262
solar_position : Optional [pd .DataFrame ] = field (default = None )
@@ -278,6 +278,9 @@ class ModelChainResult:
278
278
diode_params : Optional [PerArray [pd .DataFrame ]] = field (default = None )
279
279
dc_ohmic_losses : Optional [PerArray [pd .Series ]] = field (default = None )
280
280
281
+ weather : Optional [PerArray [pd .DataFrame ]] = None
282
+ times : Optional [pd .DatetimeIndex ] = None
283
+
281
284
def _result_type (self , value ):
282
285
"""Coerce `value` to the correct type according to
283
286
``self._singleton_tuples``."""
@@ -375,7 +378,8 @@ class ModelChain:
375
378
_deprecated_attrs = ['solar_position' , 'airmass' , 'total_irrad' ,
376
379
'aoi' , 'aoi_modifier' , 'spectral_modifier' ,
377
380
'cell_temperature' , 'effective_irradiance' ,
378
- 'dc' , 'ac' , 'diode_params' , 'tracking' ]
381
+ 'dc' , 'ac' , 'diode_params' , 'tracking' ,
382
+ 'weather' , 'times' ]
379
383
380
384
def __init__ (self , system , location ,
381
385
clearsky_model = 'ineichen' ,
@@ -406,9 +410,6 @@ def __init__(self, system, location,
406
410
self .dc_ohmic_model = dc_ohmic_model
407
411
self .losses_model = losses_model
408
412
409
- self .weather = None
410
- self .times = None
411
-
412
413
self .results = ModelChainResult ()
413
414
414
415
def __getattr__ (self , key ):
@@ -908,7 +909,7 @@ def infer_spectral_model(self):
908
909
909
910
def first_solar_spectral_loss (self ):
910
911
self .results .spectral_modifier = self .system .first_solar_spectral_loss (
911
- _tuple_from_dfs (self .weather , 'precipitable_water' ),
912
+ _tuple_from_dfs (self .results . weather , 'precipitable_water' ),
912
913
self .results .airmass ['airmass_absolute' ]
913
914
)
914
915
return self
@@ -1005,8 +1006,8 @@ def _set_celltemp(self, model):
1005
1006
1006
1007
poa = _irrad_for_celltemp (self .results .total_irrad ,
1007
1008
self .results .effective_irradiance )
1008
- temp_air = _tuple_from_dfs (self .weather , 'temp_air' )
1009
- wind_speed = _tuple_from_dfs (self .weather , 'wind_speed' )
1009
+ temp_air = _tuple_from_dfs (self .results . weather , 'temp_air' )
1010
+ wind_speed = _tuple_from_dfs (self .results . weather , 'wind_speed' )
1010
1011
kwargs = {}
1011
1012
if model == self .system .noct_sam_celltemp :
1012
1013
kwargs ['effective_irradiance' ] = self .results .effective_irradiance
@@ -1161,7 +1162,7 @@ def complete_irradiance(self, weather):
1161
1162
1162
1163
Notes
1163
1164
-----
1164
- Assigns attributes: ``weather``
1165
+ Assigns attributes to ``results``: ``times``, ``weather``
1165
1166
1166
1167
Examples
1167
1168
--------
@@ -1174,7 +1175,7 @@ def complete_irradiance(self, weather):
1174
1175
>>> # my_weather containing 'dhi' and 'ghi'.
1175
1176
>>> mc = ModelChain(my_system, my_location) # doctest: +SKIP
1176
1177
>>> mc.complete_irradiance(my_weather) # doctest: +SKIP
1177
- >>> mc.run_model(mc.weather) # doctest: +SKIP
1178
+ >>> mc.run_model(mc.results. weather) # doctest: +SKIP
1178
1179
1179
1180
>>> # my_weather containing 'dhi', 'ghi' and 'dni'.
1180
1181
>>> mc = ModelChain(my_system, my_location) # doctest: +SKIP
@@ -1184,16 +1185,16 @@ def complete_irradiance(self, weather):
1184
1185
self ._check_multiple_input (weather )
1185
1186
# Don't use ModelChain._assign_weather() here because it adds
1186
1187
# temperature and wind-speed columns which we do not need here.
1187
- self .weather = _copy (weather )
1188
+ self .results . weather = _copy (weather )
1188
1189
self ._assign_times ()
1189
1190
self .results .solar_position = self .location .get_solarposition (
1190
- self .times , method = self .solar_position_method )
1191
+ self .results . times , method = self .solar_position_method )
1191
1192
1192
1193
if isinstance (weather , tuple ):
1193
- for w in self .weather :
1194
+ for w in self .results . weather :
1194
1195
self ._complete_irradiance (w )
1195
1196
else :
1196
- self ._complete_irradiance (self .weather )
1197
+ self ._complete_irradiance (self .results . weather )
1197
1198
1198
1199
return self
1199
1200
@@ -1238,7 +1239,7 @@ def _prep_inputs_solar_pos(self, weather):
1238
1239
pass
1239
1240
1240
1241
self .results .solar_position = self .location .get_solarposition (
1241
- self .times , method = self .solar_position_method ,
1242
+ self .results . times , method = self .solar_position_method ,
1242
1243
** kwargs )
1243
1244
return self
1244
1245
@@ -1301,15 +1302,23 @@ def _verify(data, index=None):
1301
1302
for (i , array_data ) in enumerate (data ):
1302
1303
_verify (array_data , i )
1303
1304
1304
- def _configure_results (self ):
1305
- """Configure the type used for per-array fields in ModelChainResult.
1305
+ def _configure_results (self , per_array_data ):
1306
+ """Configure the type used for per-array fields in
1307
+ ModelChainResult.
1306
1308
1307
- Must be called after ``self.weather`` has been assigned. If
1308
- ``self.weather`` is a tuple and the number of arrays in the system
1309
- is 1, then per-array results are stored as length-1 tuples.
1309
+ If ``per_array_data`` is True and the number of arrays in the
1310
+ system is 1, then per-array results are stored as length-1
1311
+ tuples. This overrides the PVSystem defaults of unpacking a 1
1312
+ length tuple into a singleton.
1313
+
1314
+ Parameters
1315
+ ----------
1316
+ per_array_data : bool
1317
+ If input data is provided for each array, pass True. If a
1318
+ single input data is provided for all arrays, pass False.
1310
1319
"""
1311
1320
self .results ._singleton_tuples = (
1312
- self .system .num_arrays == 1 and isinstance ( self . weather , tuple )
1321
+ self .system .num_arrays == 1 and per_array_data
1313
1322
)
1314
1323
1315
1324
def _assign_weather (self , data ):
@@ -1321,13 +1330,13 @@ def _build_weather(data):
1321
1330
if weather .get ('temp_air' ) is None :
1322
1331
weather ['temp_air' ] = 20
1323
1332
return weather
1324
- if not isinstance (data , tuple ):
1325
- self .weather = _build_weather (data )
1333
+ if isinstance (data , tuple ):
1334
+ weather = tuple (_build_weather (wx ) for wx in data )
1335
+ self ._configure_results (per_array_data = True )
1326
1336
else :
1327
- self .weather = tuple (
1328
- _build_weather (weather ) for weather in data
1329
- )
1330
- self ._configure_results ()
1337
+ weather = _build_weather (data )
1338
+ self ._configure_results (per_array_data = False )
1339
+ self .results .weather = weather
1331
1340
self ._assign_times ()
1332
1341
return self
1333
1342
@@ -1344,18 +1353,20 @@ def _build_irrad(data):
1344
1353
return self
1345
1354
1346
1355
def _assign_times (self ):
1347
- """Assign self.times according the the index of self.weather.
1348
-
1349
- If there are multiple DataFrames in self.weather then the index
1350
- of the first one is assigned. It is assumed that the indices of
1351
- each data frame in `weather` are the same. This can be verified
1352
- by calling :py:func:`_all_same_index` or
1353
- :py:meth:`self._check_multiple_weather` before calling this method.
1356
+ """Assign self.results.times according the the index of
1357
+ self.results.weather.
1358
+
1359
+ If there are multiple DataFrames in self.results.weather then
1360
+ the index of the first one is assigned. It is assumed that the
1361
+ indices of each DataFrame in self.results.weather are the same.
1362
+ This can be verified by calling :py:func:`_all_same_index` or
1363
+ :py:meth:`self._check_multiple_weather` before calling this
1364
+ method.
1354
1365
"""
1355
- if isinstance (self .weather , tuple ):
1356
- self .times = self .weather [0 ].index
1366
+ if isinstance (self .results . weather , tuple ):
1367
+ self .results . times = self . results .weather [0 ].index
1357
1368
else :
1358
- self .times = self .weather .index
1369
+ self .results . times = self . results .weather .index
1359
1370
1360
1371
def prepare_inputs (self , weather ):
1361
1372
"""
@@ -1386,8 +1397,8 @@ def prepare_inputs(self, weather):
1386
1397
1387
1398
Notes
1388
1399
-----
1389
- Assigns attributes: ``weather``, ``solar_position ``, ``airmass ``,
1390
- ``total_irrad``, ``aoi``
1400
+ Assigns attributes to ``results``: ``times ``, ``weather ``,
1401
+ ``solar_position``, ``airmass``, `` total_irrad``, ``aoi``
1391
1402
1392
1403
See also
1393
1404
--------
@@ -1421,9 +1432,9 @@ def prepare_inputs(self, weather):
1421
1432
self .results .solar_position ['azimuth' ])
1422
1433
1423
1434
self .results .total_irrad = get_irradiance (
1424
- _tuple_from_dfs (self .weather , 'dni' ),
1425
- _tuple_from_dfs (self .weather , 'ghi' ),
1426
- _tuple_from_dfs (self .weather , 'dhi' ),
1435
+ _tuple_from_dfs (self .results . weather , 'dni' ),
1436
+ _tuple_from_dfs (self .results . weather , 'ghi' ),
1437
+ _tuple_from_dfs (self .results . weather , 'dhi' ),
1427
1438
airmass = self .results .airmass ['airmass_relative' ],
1428
1439
model = self .transposition_model
1429
1440
)
@@ -1482,8 +1493,8 @@ def prepare_inputs_from_poa(self, data):
1482
1493
1483
1494
Notes
1484
1495
-----
1485
- Assigns attributes: ``weather``, ``total_irrad ``, ``solar_position ``,
1486
- ``airmass``, ``aoi``.
1496
+ Assigns attributes to ``results``: ``times ``, ``weather ``,
1497
+ ``total_irrad``, ``solar_position``, `` airmass``, ``aoi``.
1487
1498
1488
1499
See also
1489
1500
--------
@@ -1642,10 +1653,12 @@ def run_model(self, weather):
1642
1653
1643
1654
Notes
1644
1655
-----
1645
- Assigns attributes: ``solar_position``, ``airmass``, ``weather``,
1646
- ``total_irrad``, ``aoi``, ``aoi_modifier``, ``spectral_modifier``,
1647
- and ``effective_irradiance``, ``cell_temperature``, ``dc``, ``ac``,
1648
- ``losses``, ``diode_params`` (if dc_model is a single diode model).
1656
+ Assigns attributes to ``results``: ``times``, ``weather``,
1657
+ ``solar_position``, ``airmass``, ``total_irrad``, ``aoi``,
1658
+ ``aoi_modifier``, ``spectral_modifier``, and
1659
+ ``effective_irradiance``, ``cell_temperature``, ``dc``, ``ac``,
1660
+ ``losses``, ``diode_params`` (if dc_model is a single diode
1661
+ model).
1649
1662
1650
1663
See also
1651
1664
--------
@@ -1701,10 +1714,12 @@ def run_model_from_poa(self, data):
1701
1714
1702
1715
Notes
1703
1716
-----
1704
- Assigns attributes: ``solar_position``, ``airmass``, ``weather``,
1705
- ``total_irrad``, ``aoi``, ``aoi_modifier``, ``spectral_modifier``,
1706
- and ``effective_irradiance``, ``cell_temperature``, ``dc``, ``ac``,
1707
- ``losses``, ``diode_params`` (if dc_model is a single diode model).
1717
+ Assigns attributes to results: ``times``, ``weather``,
1718
+ ``solar_position``, ``airmass``, ``total_irrad``, ``aoi``,
1719
+ ``aoi_modifier``, ``spectral_modifier``, and
1720
+ ``effective_irradiance``, ``cell_temperature``, ``dc``, ``ac``,
1721
+ ``losses``, ``diode_params`` (if dc_model is a single diode
1722
+ model).
1708
1723
1709
1724
See also
1710
1725
--------
@@ -1798,7 +1813,7 @@ def run_model_from_effective_irradiance(self, data=None):
1798
1813
If optional column ``'poa_global'`` is present, these data are used.
1799
1814
If ``'poa_global'`` is not present, ``'effective_irradiance'`` is used.
1800
1815
1801
- Assigns attributes: ``weather``, ``total_irrad``,
1816
+ Assigns attributes to results: ``times``, ``weather``, ``total_irrad``,
1802
1817
``effective_irradiance``, ``cell_temperature``, ``dc``, ``ac``,
1803
1818
``losses``, ``diode_params`` (if dc_model is a single diode model).
1804
1819
0 commit comments