@@ -256,7 +256,7 @@ class ModelChainResult:
256256 _per_array_fields = {'total_irrad' , 'aoi' , 'aoi_modifier' ,
257257 'spectral_modifier' , 'cell_temperature' ,
258258 'effective_irradiance' , 'dc' , 'diode_params' ,
259- 'dc_ohmic_losses' }
259+ 'dc_ohmic_losses' , 'weather' }
260260
261261 # system-level information
262262 solar_position : Optional [pd .DataFrame ] = field (default = None )
@@ -278,6 +278,9 @@ class ModelChainResult:
278278 diode_params : Optional [PerArray [pd .DataFrame ]] = field (default = None )
279279 dc_ohmic_losses : Optional [PerArray [pd .Series ]] = field (default = None )
280280
281+ weather : Optional [PerArray [pd .DataFrame ]] = None
282+ times : Optional [pd .DatetimeIndex ] = None
283+
281284 def _result_type (self , value ):
282285 """Coerce `value` to the correct type according to
283286 ``self._singleton_tuples``."""
@@ -375,7 +378,8 @@ class ModelChain:
375378 _deprecated_attrs = ['solar_position' , 'airmass' , 'total_irrad' ,
376379 'aoi' , 'aoi_modifier' , 'spectral_modifier' ,
377380 'cell_temperature' , 'effective_irradiance' ,
378- 'dc' , 'ac' , 'diode_params' , 'tracking' ]
381+ 'dc' , 'ac' , 'diode_params' , 'tracking' ,
382+ 'weather' , 'times' ]
379383
380384 def __init__ (self , system , location ,
381385 clearsky_model = 'ineichen' ,
@@ -406,9 +410,6 @@ def __init__(self, system, location,
406410 self .dc_ohmic_model = dc_ohmic_model
407411 self .losses_model = losses_model
408412
409- self .weather = None
410- self .times = None
411-
412413 self .results = ModelChainResult ()
413414
414415 def __getattr__ (self , key ):
@@ -908,7 +909,7 @@ def infer_spectral_model(self):
908909
909910 def first_solar_spectral_loss (self ):
910911 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' ),
912913 self .results .airmass ['airmass_absolute' ]
913914 )
914915 return self
@@ -1005,8 +1006,8 @@ def _set_celltemp(self, model):
10051006
10061007 poa = _irrad_for_celltemp (self .results .total_irrad ,
10071008 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' )
10101011 kwargs = {}
10111012 if model == self .system .noct_sam_celltemp :
10121013 kwargs ['effective_irradiance' ] = self .results .effective_irradiance
@@ -1161,7 +1162,7 @@ def complete_irradiance(self, weather):
11611162
11621163 Notes
11631164 -----
1164- Assigns attributes: ``weather``
1165+ Assigns attributes to ``results``: ``times``, ``weather``
11651166
11661167 Examples
11671168 --------
@@ -1174,7 +1175,7 @@ def complete_irradiance(self, weather):
11741175 >>> # my_weather containing 'dhi' and 'ghi'.
11751176 >>> mc = ModelChain(my_system, my_location) # doctest: +SKIP
11761177 >>> mc.complete_irradiance(my_weather) # doctest: +SKIP
1177- >>> mc.run_model(mc.weather) # doctest: +SKIP
1178+ >>> mc.run_model(mc.results. weather) # doctest: +SKIP
11781179
11791180 >>> # my_weather containing 'dhi', 'ghi' and 'dni'.
11801181 >>> mc = ModelChain(my_system, my_location) # doctest: +SKIP
@@ -1184,16 +1185,16 @@ def complete_irradiance(self, weather):
11841185 self ._check_multiple_input (weather )
11851186 # Don't use ModelChain._assign_weather() here because it adds
11861187 # temperature and wind-speed columns which we do not need here.
1187- self .weather = _copy (weather )
1188+ self .results . weather = _copy (weather )
11881189 self ._assign_times ()
11891190 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 )
11911192
11921193 if isinstance (weather , tuple ):
1193- for w in self .weather :
1194+ for w in self .results . weather :
11941195 self ._complete_irradiance (w )
11951196 else :
1196- self ._complete_irradiance (self .weather )
1197+ self ._complete_irradiance (self .results . weather )
11971198
11981199 return self
11991200
@@ -1238,7 +1239,7 @@ def _prep_inputs_solar_pos(self, weather):
12381239 pass
12391240
12401241 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 ,
12421243 ** kwargs )
12431244 return self
12441245
@@ -1301,15 +1302,23 @@ def _verify(data, index=None):
13011302 for (i , array_data ) in enumerate (data ):
13021303 _verify (array_data , i )
13031304
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.
13061308
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.
13101319 """
13111320 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
13131322 )
13141323
13151324 def _assign_weather (self , data ):
@@ -1321,13 +1330,13 @@ def _build_weather(data):
13211330 if weather .get ('temp_air' ) is None :
13221331 weather ['temp_air' ] = 20
13231332 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 )
13261336 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
13311340 self ._assign_times ()
13321341 return self
13331342
@@ -1344,18 +1353,20 @@ def _build_irrad(data):
13441353 return self
13451354
13461355 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.
13541365 """
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
13571368 else :
1358- self .times = self .weather .index
1369+ self .results . times = self . results .weather .index
13591370
13601371 def prepare_inputs (self , weather ):
13611372 """
@@ -1386,8 +1397,8 @@ def prepare_inputs(self, weather):
13861397
13871398 Notes
13881399 -----
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``
13911402
13921403 See also
13931404 --------
@@ -1421,9 +1432,9 @@ def prepare_inputs(self, weather):
14211432 self .results .solar_position ['azimuth' ])
14221433
14231434 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' ),
14271438 airmass = self .results .airmass ['airmass_relative' ],
14281439 model = self .transposition_model
14291440 )
@@ -1482,8 +1493,8 @@ def prepare_inputs_from_poa(self, data):
14821493
14831494 Notes
14841495 -----
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``.
14871498
14881499 See also
14891500 --------
@@ -1642,10 +1653,12 @@ def run_model(self, weather):
16421653
16431654 Notes
16441655 -----
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).
16491662
16501663 See also
16511664 --------
@@ -1701,10 +1714,12 @@ def run_model_from_poa(self, data):
17011714
17021715 Notes
17031716 -----
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).
17081723
17091724 See also
17101725 --------
@@ -1798,7 +1813,7 @@ def run_model_from_effective_irradiance(self, data=None):
17981813 If optional column ``'poa_global'`` is present, these data are used.
17991814 If ``'poa_global'`` is not present, ``'effective_irradiance'`` is used.
18001815
1801- Assigns attributes: ``weather``, ``total_irrad``,
1816+ Assigns attributes to results: ``times``, ``weather``, ``total_irrad``,
18021817 ``effective_irradiance``, ``cell_temperature``, ``dc``, ``ac``,
18031818 ``losses``, ``diode_params`` (if dc_model is a single diode model).
18041819
0 commit comments