Intro Examples¶
This page contains introductory examples of pvlib python usage.
Modeling paradigms¶
The backbone of pvlib-python is well-tested procedural code that implements PV system models. pvlib-python also provides a collection of classes for users that prefer object-oriented programming. These classes can help users keep track of data in a more organized way, provide some “smart” functions with more flexible inputs, and simplify the modeling process for common situations. The classes do not add any algorithms beyond what’s available in the procedural code, and most of the object methods are simple wrappers around the corresponding procedural code.
Let’s use each of these pvlib modeling paradigms to calculate the yearly energy yield for a given hardware configuration at a handful of sites listed below.
In [1]: import pandas as pd
In [2]: import matplotlib.pyplot as plt
In [3]: naive_times = pd.date_range(start='2015', end='2016', freq='1h')
# very approximate
# latitude, longitude, name, altitude, timezone
In [4]: coordinates = [(30, -110, 'Tucson', 700, 'Etc/GMT+7'),
...: (35, -105, 'Albuquerque', 1500, 'Etc/GMT+7'),
...: (40, -120, 'San Francisco', 10, 'Etc/GMT+8'),
...: (50, 10, 'Berlin', 34, 'Etc/GMT-1')]
...:
In [5]: import pvlib
# get the module and inverter specifications from SAM
In [6]: sandia_modules = pvlib.pvsystem.retrieve_sam('SandiaMod')
In [7]: sapm_inverters = pvlib.pvsystem.retrieve_sam('cecinverter')
In [8]: module = sandia_modules['Canadian_Solar_CS5P_220M___2009_']
In [9]: inverter = sapm_inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_']
---------------------------------------------------------------------------
KeyError Traceback (most recent call last)
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
2896 try:
-> 2897 return self._engine.get_loc(key)
2898 except KeyError:
pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()
pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()
pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()
pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()
KeyError: 'ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'
During handling of the above exception, another exception occurred:
KeyError Traceback (most recent call last)
<ipython-input-9-cb0c5fa8a3e2> in <module>
----> 1 inverter = sapm_inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_']
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/core/frame.py in __getitem__(self, key)
2978 if self.columns.nlevels > 1:
2979 return self._getitem_multilevel(key)
-> 2980 indexer = self.columns.get_loc(key)
2981 if is_integer(indexer):
2982 indexer = [indexer]
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/core/indexes/base.py in get_loc(self, key, method, tolerance)
2897 return self._engine.get_loc(key)
2898 except KeyError:
-> 2899 return self._engine.get_loc(self._maybe_cast_indexer(key))
2900 indexer = self.get_indexer([key], method=method, tolerance=tolerance)
2901 if indexer.ndim > 1 or indexer.size > 1:
pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()
pandas/_libs/index.pyx in pandas._libs.index.IndexEngine.get_loc()
pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()
pandas/_libs/hashtable_class_helper.pxi in pandas._libs.hashtable.PyObjectHashTable.get_item()
KeyError: 'ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_'
In [10]: temperature_model_parameters = pvlib.temperature.TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_glass']
# specify constant ambient air temp and wind for simplicity
In [11]: temp_air = 20
In [12]: wind_speed = 0
Procedural¶
The straightforward procedural code can be used for all modeling steps in pvlib-python.
The following code demonstrates how to use the procedural code to accomplish our system modeling goal:
In [13]: system = {'module': module, 'inverter': inverter,
....: 'surface_azimuth': 180}
....:
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-13-8f44442cad28> in <module>
----> 1 system = {'module': module, 'inverter': inverter,
2 'surface_azimuth': 180}
NameError: name 'inverter' is not defined
In [14]: energies = {}
In [15]: for latitude, longitude, name, altitude, timezone in coordinates:
....: times = naive_times.tz_localize(timezone)
....: system['surface_tilt'] = latitude
....: solpos = pvlib.solarposition.get_solarposition(times, latitude, longitude)
....: dni_extra = pvlib.irradiance.get_extra_radiation(times)
....: airmass = pvlib.atmosphere.get_relative_airmass(solpos['apparent_zenith'])
....: pressure = pvlib.atmosphere.alt2pres(altitude)
....: am_abs = pvlib.atmosphere.get_absolute_airmass(airmass, pressure)
....: tl = pvlib.clearsky.lookup_linke_turbidity(times, latitude, longitude)
....: cs = pvlib.clearsky.ineichen(solpos['apparent_zenith'], am_abs, tl,
....: dni_extra=dni_extra, altitude=altitude)
....: aoi = pvlib.irradiance.aoi(system['surface_tilt'], system['surface_azimuth'],
....: solpos['apparent_zenith'], solpos['azimuth'])
....: total_irrad = pvlib.irradiance.get_total_irradiance(system['surface_tilt'],
....: system['surface_azimuth'],
....: solpos['apparent_zenith'],
....: solpos['azimuth'],
....: cs['dni'], cs['ghi'], cs['dhi'],
....: dni_extra=dni_extra,
....: model='haydavies')
....: tcell = pvlib.temperature.sapm_cell(total_irrad['poa_global'],
....: temp_air, wind_speed,
....: **temperature_model_parameters)
....: effective_irradiance = pvlib.pvsystem.sapm_effective_irradiance(
....: total_irrad['poa_direct'], total_irrad['poa_diffuse'],
....: am_abs, aoi, module)
....: dc = pvlib.pvsystem.sapm(effective_irradiance, tcell, module)
....: ac = pvlib.pvsystem.snlinverter(dc['v_mp'], dc['p_mp'], inverter)
....: annual_energy = ac.sum()
....: energies[name] = annual_energy
....:
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-15-aa55f107abd4> in <module>
1 for latitude, longitude, name, altitude, timezone in coordinates:
2 times = naive_times.tz_localize(timezone)
----> 3 system['surface_tilt'] = latitude
4 solpos = pvlib.solarposition.get_solarposition(times, latitude, longitude)
5 dni_extra = pvlib.irradiance.get_extra_radiation(times)
NameError: name 'system' is not defined
In [16]: energies = pd.Series(energies)
# based on the parameters specified above, these are in W*hrs
In [17]: print(energies.round(0))
Series([], dtype: float64)
In [18]: energies.plot(kind='bar', rot=0)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-18-ce643011a4ea> in <module>
----> 1 energies.plot(kind='bar', rot=0)
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/plotting/_core.py in __call__(self, *args, **kwargs)
792 data.columns = label_name
793
--> 794 return plot_backend.plot(data, kind=kind, **kwargs)
795
796 def line(self, x=None, y=None, **kwargs):
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/plotting/_matplotlib/__init__.py in plot(data, kind, **kwargs)
60 kwargs["ax"] = getattr(ax, "left_ax", ax)
61 plot_obj = PLOT_CLASSES[kind](data, **kwargs)
---> 62 plot_obj.generate()
63 plot_obj.draw()
64 return plot_obj.result
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/plotting/_matplotlib/core.py in generate(self)
277 def generate(self):
278 self._args_adjust()
--> 279 self._compute_plot_data()
280 self._setup_subplots()
281 self._make_plot()
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/plotting/_matplotlib/core.py in _compute_plot_data(self)
412 # no non-numeric frames or series allowed
413 if is_empty:
--> 414 raise TypeError("no numeric data to plot")
415
416 # GH25587: cast ExtensionArray of pandas (IntegerArray, etc.) to
TypeError: no numeric data to plot
In [19]: plt.ylabel('Yearly energy yield (W hr)')
Out[19]: Text(0, 0.5, 'Yearly energy yield (W hr)')
Object oriented (Location, PVSystem, ModelChain)¶
The first object oriented paradigm uses a model where a
PVSystem object represents an assembled
collection of modules, inverters, etc., a
Location object represents a particular
place on the planet, and a ModelChain
object describes the modeling chain used to calculate PV output at that
Location. This can be a useful paradigm if you prefer to think about the
PV system and its location as separate concepts or if you develop your
own ModelChain subclasses. It can also be helpful if you make extensive
use of Location-specific methods for other calculations. pvlib-python
also includes a SingleAxisTracker class that
is a subclass of PVSystem.
The following code demonstrates how to use
Location,
PVSystem, and
ModelChain objects to accomplish our
system modeling goal. ModelChain objects provide convenience methods
that can provide default selections for models and can also fill
necessary input with modeled data. For example, no air temperature
or wind speed data is provided in the input weather DataFrame,
so the ModelChain object defaults to 20 C and 0 m/s. Also, no irradiance
transposition model is specified (keyword argument transposition for
ModelChain) so the ModelChain defaults to the haydavies model. In this
example, ModelChain infers the DC power model from the module provided
by examining the parameters defined for the module.
In [20]: from pvlib.pvsystem import PVSystem
In [21]: from pvlib.location import Location
In [22]: from pvlib.modelchain import ModelChain
In [23]: system = PVSystem(module_parameters=module,
....: inverter_parameters=inverter,
....: temperature_model_parameters=temperature_model_parameters)
....:
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-23-8644c4c6fde8> in <module>
1 system = PVSystem(module_parameters=module,
----> 2 inverter_parameters=inverter,
3 temperature_model_parameters=temperature_model_parameters)
NameError: name 'inverter' is not defined
In [24]: energies = {}
In [25]: for latitude, longitude, name, altitude, timezone in coordinates:
....: times = naive_times.tz_localize(timezone)
....: location = Location(latitude, longitude, name=name, altitude=altitude,
....: tz=timezone)
....: weather = location.get_clearsky(times)
....: mc = ModelChain(system, location,
....: orientation_strategy='south_at_latitude_tilt')
....: mc.run_model(times=times, weather=weather)
....: annual_energy = mc.ac.sum()
....: energies[name] = annual_energy
....:
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-25-09581f5c3eaf> in <module>
4 tz=timezone)
5 weather = location.get_clearsky(times)
----> 6 mc = ModelChain(system, location,
7 orientation_strategy='south_at_latitude_tilt')
8 mc.run_model(times=times, weather=weather)
NameError: name 'system' is not defined
In [26]: energies = pd.Series(energies)
# based on the parameters specified above, these are in W*hrs
In [27]: print(energies.round(0))
Series([], dtype: float64)
In [28]: energies.plot(kind='bar', rot=0)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-28-ce643011a4ea> in <module>
----> 1 energies.plot(kind='bar', rot=0)
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/plotting/_core.py in __call__(self, *args, **kwargs)
792 data.columns = label_name
793
--> 794 return plot_backend.plot(data, kind=kind, **kwargs)
795
796 def line(self, x=None, y=None, **kwargs):
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/plotting/_matplotlib/__init__.py in plot(data, kind, **kwargs)
60 kwargs["ax"] = getattr(ax, "left_ax", ax)
61 plot_obj = PLOT_CLASSES[kind](data, **kwargs)
---> 62 plot_obj.generate()
63 plot_obj.draw()
64 return plot_obj.result
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/plotting/_matplotlib/core.py in generate(self)
277 def generate(self):
278 self._args_adjust()
--> 279 self._compute_plot_data()
280 self._setup_subplots()
281 self._make_plot()
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/plotting/_matplotlib/core.py in _compute_plot_data(self)
412 # no non-numeric frames or series allowed
413 if is_empty:
--> 414 raise TypeError("no numeric data to plot")
415
416 # GH25587: cast ExtensionArray of pandas (IntegerArray, etc.) to
TypeError: no numeric data to plot
In [29]: plt.ylabel('Yearly energy yield (W hr)')
Out[29]: Text(0, 0.5, 'Yearly energy yield (W hr)')
Object oriented (LocalizedPVSystem)¶
The second object oriented paradigm uses a model where a
LocalizedPVSystem represents a PV system at
a particular place on the planet. This can be a useful paradigm if
you’re thinking about a power plant that already exists.
The LocalizedPVSystem inherits from both
PVSystem and
Location, while the
LocalizedSingleAxisTracker inherits from
SingleAxisTracker (itself a subclass of
PVSystem) and
Location. The
LocalizedPVSystem and
LocalizedSingleAxisTracker classes may
contain bugs due to the relative difficulty of implementing multiple
inheritance. The LocalizedPVSystem and
LocalizedSingleAxisTracker may be deprecated
in a future release. We recommend that most modeling workflows implement
Location,
PVSystem, and
ModelChain.
The following code demonstrates how to use a
LocalizedPVSystem object to accomplish our
modeling goal:
In [30]: from pvlib.pvsystem import LocalizedPVSystem
In [31]: energies = {}
In [32]: for latitude, longitude, name, altitude, timezone in coordinates:
....: localized_system = LocalizedPVSystem(module_parameters=module,
....: inverter_parameters=inverter,
....: temperature_model_parameters=temperature_model_parameters,
....: surface_tilt=latitude,
....: surface_azimuth=180,
....: latitude=latitude,
....: longitude=longitude,
....: name=name,
....: altitude=altitude,
....: tz=timezone)
....: times = naive_times.tz_localize(timezone)
....: clearsky = localized_system.get_clearsky(times)
....: solar_position = localized_system.get_solarposition(times)
....: total_irrad = localized_system.get_irradiance(solar_position['apparent_zenith'],
....: solar_position['azimuth'],
....: clearsky['dni'],
....: clearsky['ghi'],
....: clearsky['dhi'])
....: tcell = localized_system.sapm_celltemp(total_irrad['poa_global'],
....: temp_air, wind_speed)
....: aoi = localized_system.get_aoi(solar_position['apparent_zenith'],
....: solar_position['azimuth'])
....: airmass = localized_system.get_airmass(solar_position=solar_position)
....: effective_irradiance = localized_system.sapm_effective_irradiance(
....: total_irrad['poa_direct'], total_irrad['poa_diffuse'],
....: airmass['airmass_absolute'], aoi)
....: dc = localized_system.sapm(effective_irradiance, tcell)
....: ac = localized_system.snlinverter(dc['v_mp'], dc['p_mp'])
....: annual_energy = ac.sum()
....: energies[name] = annual_energy
....:
---------------------------------------------------------------------------
NameError Traceback (most recent call last)
<ipython-input-32-5b001ffad2db> in <module>
1 for latitude, longitude, name, altitude, timezone in coordinates:
2 localized_system = LocalizedPVSystem(module_parameters=module,
----> 3 inverter_parameters=inverter,
4 temperature_model_parameters=temperature_model_parameters,
5 surface_tilt=latitude,
NameError: name 'inverter' is not defined
In [33]: energies = pd.Series(energies)
# based on the parameters specified above, these are in W*hrs
In [34]: print(energies.round(0))
Series([], dtype: float64)
In [35]: energies.plot(kind='bar', rot=0)
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-35-ce643011a4ea> in <module>
----> 1 energies.plot(kind='bar', rot=0)
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/plotting/_core.py in __call__(self, *args, **kwargs)
792 data.columns = label_name
793
--> 794 return plot_backend.plot(data, kind=kind, **kwargs)
795
796 def line(self, x=None, y=None, **kwargs):
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/plotting/_matplotlib/__init__.py in plot(data, kind, **kwargs)
60 kwargs["ax"] = getattr(ax, "left_ax", ax)
61 plot_obj = PLOT_CLASSES[kind](data, **kwargs)
---> 62 plot_obj.generate()
63 plot_obj.draw()
64 return plot_obj.result
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/plotting/_matplotlib/core.py in generate(self)
277 def generate(self):
278 self._args_adjust()
--> 279 self._compute_plot_data()
280 self._setup_subplots()
281 self._make_plot()
~/checkouts/readthedocs.org/user_builds/tylunelpvlib-python/envs/stable/lib/python3.7/site-packages/pandas/plotting/_matplotlib/core.py in _compute_plot_data(self)
412 # no non-numeric frames or series allowed
413 if is_empty:
--> 414 raise TypeError("no numeric data to plot")
415
416 # GH25587: cast ExtensionArray of pandas (IntegerArray, etc.) to
TypeError: no numeric data to plot
In [36]: plt.ylabel('Yearly energy yield (W hr)')
Out[36]: Text(0, 0.5, 'Yearly energy yield (W hr)')