Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ModelChainResult.__repr__ #1236

Merged
merged 12 commits into from Jun 23, 2023
Merged

ModelChainResult.__repr__ #1236

merged 12 commits into from Jun 23, 2023

Conversation

cwhanse
Copy link
Member

@cwhanse cwhanse commented May 18, 2021

  • Closes Improve ModelChainResult.__repr__ #1110
  • I am familiar with the contributing guidelines
  • [ ] Tests added
  • Updates entries to docs/sphinx/source/api.rst for API changes.
  • [ ] Adds description and name entries in the appropriate "what's new" file in docs/sphinx/source/whatsnew for all changes. Includes link to the GitHub Issue with :issue:`num` or this Pull Request with :pull:`num`. Includes contributor name and/or GitHub username (link with :ghuser:`user`).
  • New code is fully documented. Includes numpydoc compliant docstrings, examples, and comments where necessary.
  • Pull request is nearly complete and ready for detailed review.
  • Maintainer: Appropriate GitHub Labels and Milestone are assigned to the Pull Request and linked Issue.

@cwhanse
Copy link
Member Author

cwhanse commented Jun 22, 2023

I'm not unhappy with the print of ModelChainResult here. The code is clumsy, because ModelChainResult doesn't hew to any standards for the type of each attribute (e.g., with 2 Arrays, the value can be None, a single float, or a tuple of something).

Here's what the report looks like for a simple ModelChain (system with one Array). I'm "head"ing any Series or DataFrames.

Any if you read this far, look at the name of the last Series for ModelChainResult.ac.

=== ModelChainResult === 
weather 
                                   ghi         dhi  ...  temp_air  wind_speed
2023-06-10 10:00:00-07:00  775.999173  108.933862  ...        25           0
2023-06-10 11:00:00-07:00  898.985531  116.633882  ...        25           0
2023-06-10 12:00:00-07:00  969.592761  120.879109  ...        25           0
2023-06-10 13:00:00-07:00  982.786788  121.660833  ...        25           0
2023-06-10 14:00:00-07:00  937.637996  118.971374  ...        25           0

[5 rows x 5 columns] 

 solar_position 
                            apparent_zenith  ...  equation_of_time
2023-06-10 10:00:00-07:00        37.382226  ...          0.539325
2023-06-10 11:00:00-07:00        25.474558  ...          0.530968
2023-06-10 12:00:00-07:00        15.254955  ...          0.522605
2023-06-10 13:00:00-07:00        12.488151  ...          0.514235
2023-06-10 14:00:00-07:00        20.492312  ...          0.505857

[5 rows x 6 columns] 

 airmass 
                            airmass_relative  airmass_absolute
2023-06-10 10:00:00-07:00          1.257469          1.257469
2023-06-10 11:00:00-07:00          1.107109          1.107109
2023-06-10 12:00:00-07:00          1.036112          1.036112
2023-06-10 13:00:00-07:00          1.023853          1.023853
2023-06-10 14:00:00-07:00          1.067070          1.067070 

 
 Number of Arrays: 1 
------------------- 
  Array 0 
------------------- 
  tracking 
 None 

  aoi 
 2023-06-10 10:00:00-07:00    37.382226
2023-06-10 11:00:00-07:00    25.474558
2023-06-10 12:00:00-07:00    15.254955
2023-06-10 13:00:00-07:00    12.488151
2023-06-10 14:00:00-07:00    20.492312
Freq: H, Name: aoi, dtype: float64 

  aoi_modifier 
 1.0 

  total_irrad 
                            poa_global  ...  poa_ground_diffuse
2023-06-10 10:00:00-07:00  775.994182  ...                 0.0
2023-06-10 11:00:00-07:00  898.983256  ...                 0.0
2023-06-10 12:00:00-07:00  969.591954  ...                 0.0
2023-06-10 13:00:00-07:00  982.786249  ...                 0.0
2023-06-10 14:00:00-07:00  937.636532  ...                 0.0

[5 rows x 5 columns] 

  spectral_modifier 
 1 

  effective_irradiance 
 2023-06-10 10:00:00-07:00    775.994182
2023-06-10 11:00:00-07:00    898.983256
2023-06-10 12:00:00-07:00    969.591954
2023-06-10 13:00:00-07:00    982.786249
2023-06-10 14:00:00-07:00    937.636532
Freq: H, dtype: float64 

  cell_temperature 
 2023-06-10 10:00:00-07:00    51.474617
2023-06-10 11:00:00-07:00    55.670639
2023-06-10 12:00:00-07:00    58.079598
2023-06-10 13:00:00-07:00    58.529749
2023-06-10 14:00:00-07:00    56.989374
Freq: H, dtype: float64 

  dc 
 2023-06-10 10:00:00-07:00    173.454396
2023-06-10 11:00:00-07:00    197.173423
2023-06-10 12:00:00-07:00    210.324276
2023-06-10 13:00:00-07:00    212.743986
2023-06-10 14:00:00-07:00    204.414727
Freq: H, Name: p_mp, dtype: float64 

  dc_ohmic_losses 
 None 

 End of Arrays 
------------------- 

    losses 
1 
 
  ac 
2023-06-10 10:00:00-07:00    166.923393
2023-06-10 11:00:00-07:00    189.648371
2023-06-10 12:00:00-07:00    202.216782
2023-06-10 13:00:00-07:00    204.526893
2023-06-10 14:00:00-07:00    196.571725
Freq: H, Name: p_mp, dtype: float64 

@cwhanse cwhanse marked this pull request as ready for review June 22, 2023 16:42
@cwhanse
Copy link
Member Author

cwhanse commented Jun 22, 2023

@kandersolar your call if this is for v0.10 or later.

@kandersolar
Copy link
Member

I think using head for each attribute takes up a ton of space in the output just to display what is going to be mostly boring nighttime values for most simulations. How about just showing the type and shape of the data, but not any actual values? For example, something like this:

ModelChainResult:
  weather: DataFrame (145 rows x 5 columns)
  solar_position: DataFrame (145 rows x 6 columns)
  airmass: DataFrame (145 rows x 2 columns)
  tracking: None
  aoi: Tuple (Series (length 145), Series (length 145))
  aoi_modifier: Tuple (Series (length 145), Series (length 145))
  total_irrad: Tuple (DataFrame (145 rows x 5 columns), DataFrame (145 rows x 5 columns))
  spectral_modifier: Tuple (1, 1)
  effective_irradiance: Tuple (Series (length 145), Series (length 145))
  cell_temperature: Tuple (Series (length 145), Series (length 145))
  dc: Tuple (Series (length 145), Series (length 145))
  dc_ohmic_losses: None
  losses: 1
  ac: Series (length 145)

Code for the above:

def _mcr_repr(obj):
    if isinstance(obj, tuple):
        return "Tuple (" + ", ".join([_mcr_repr(o) for o in obj]) + ")"
    if isinstance(obj, pd.DataFrame):
        return "DataFrame ({} rows x {} columns)".format(*obj.shape)
    if isinstance(obj, pd.Series):
        return "Series (length {})".format(len(obj))
    # scalar, None, other?
    return repr(obj)

def __repr__(self):
    lines = ['ModelChainResult:']
    attrs = [
        'weather', 'solar_position', 'airmass',
        'tracking', 'aoi', 'aoi_modifier', 'total_irrad', 'spectral_modifier',
        'effective_irradiance', 'cell_temperature', 'dc', 'dc_ohmic_losses', 'losses', 'ac'
    ]
    for attr in attrs:
        lines.append(f"  {attr}: " + _mcr_repr(getattr(self, attr)))

    return "\n".join(lines)

@cwhanse
Copy link
Member Author

cwhanse commented Jun 23, 2023

I had three outcomes in mind:

  • report number of Arrays
  • report attributes and their shape. I was doing this by writing a report for each Array, but beyond 2 or 3 Arrays, that will be text barf on the screen.
  • provide a summary of key data, e.g., the DatetimeIndex. I could do that once rather than using head() for each DataFrame or Series.

So I'll drop the head except for times

@cwhanse
Copy link
Member Author

cwhanse commented Jun 23, 2023

Code I'm using to display the report for different PVSystems

import pytz
import pandas as pd
import pvlib


loc = pvlib.location.Location(35, -116)
dr = pd.date_range(start='2023-06-10 10:00:00', end='2023-06-11 10:00:00',
                   freq='H', tz=pytz.timezone('Etc/GMT+7'))

weather = loc.get_clearsky(dr)
weather['temp_air']  = 25
module_parameters = {'pdc0': 250, 'gamma_pdc': -0.004}
inverter_parameters = {'pdc0': 250}
temperature_parameters = pvlib.temperature.TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass']
pvs = pvlib.pvsystem.PVSystem(module_parameters=module_parameters,
                              inverter_parameters=inverter_parameters,
                              temperature_model_parameters=temperature_parameters)

mc = pvlib.modelchain.ModelChain(pvs, loc, aoi_model='no_loss',
                                 spectral_model='no_loss',
                                 temperature_model='sapm')
mc.run_model(weather)
mcres = mc.results
print(mcres)


mount = pvlib.pvsystem.FixedMount(surface_tilt=30, surface_azimuth=180,
                                  racking_model='open_rack')
array = pvlib.pvsystem.Array(mount=mount, albedo=0.3,
                             module_parameters=module_parameters,
                             temperature_model_parameters=temperature_parameters)
pvs2 = pvlib.pvsystem.PVSystem(arrays=(array, array),
                             inverter_parameters=inverter_parameters)
mc2 = pvlib.modelchain.ModelChain(pvs2, loc, aoi_model='no_loss',
                                  spectral_model='no_loss',
                                  temperature_model='sapm')
mc2.run_model(weather)
mcres2 = mc2.results
print(mcres2)


mc3 = pvlib.modelchain.ModelChain(pvs2, loc, aoi_model='no_loss',
                                  spectral_model='no_loss',
                                  temperature_model='sapm')
mc3.run_model((weather, weather))
mcres3 = mc3.results
print(mcres3)

Copy link
Member

@kandersolar kandersolar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably worth a test? We used to have a ModelChain.__repr__ test, but it looks like I incorrectly removed it in #1181, oops. We should probably bring that back.

pvlib/modelchain.py Show resolved Hide resolved
desc2 = (f'Number of Arrays: {num_arrays} \n')
attr = 'times'
desc3 = ('Times (first 3)\n' +
f'{_head(_getmcattr(self, attr))}' +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
f'{_head(_getmcattr(self, attr))}' +
f'{self.times[:3]}' +

Seems like this should be fine as long as it's safe to assume self.times is a DatetimeIndex

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't work if times is a DatetimeStamp. Maybe that can't happen, idk.

pvlib/modelchain.py Outdated Show resolved Hide resolved
@cwhanse
Copy link
Member Author

cwhanse commented Jun 23, 2023

Probably worth a test? We used to have a ModelChain.__repr__ test, but it looks like I incorrectly removed it in #1181, oops. We should probably bring that back.

I added the Modelchain.__repr__ test back.

For the ModelChainResult.__repr__ test, I'm checking that all the non-private attributes appear. That provides some future-proofing, without a cumbersome string==string check.

@kandersolar kandersolar added this to the 0.10.0 milestone Jun 23, 2023
Comment on lines 2068 to 2070
@pytest.mark.parametrize('strategy, strategy_str', [
('south_at_latitude_tilt', 'south_at_latitude_tilt'),
(None, 'None')]) # GitHub issue 352
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for putting this test back. I think this decorator is no longer needed and can be deleted.

'aoi_modifier', 'total_irrad', 'spectral_modifier',
'effective_irradiance', 'cell_temperature', 'diode_params',
'dc', 'dc_ohmic_losses', 'losses', 'ac']
mc_attrs = dir(self)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using dir sorts the list of attributes alphabetically instead of conceptually. Not necessarily a downside.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, once I saw the alphabetized list my thought was "that's easier to use"

@kandersolar kandersolar merged commit 209376e into pvlib:main Jun 23, 2023
26 of 29 checks passed
@kandersolar
Copy link
Member

Thanks @cwhanse!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Improve ModelChainResult.__repr__
2 participants