Skip to content

Commit 5728d11

Browse files
wholmgrencwhanse
authored andcommitted
move modeling paradigms to intro examples (#539)
* move modeling paradigms to intro examples * update whatsnew * update text [skip ci]
1 parent edbe4ea commit 5728d11

File tree

4 files changed

+266
-274
lines changed

4 files changed

+266
-274
lines changed

docs/sphinx/source/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ Contents
7171
:maxdepth: 1
7272

7373
package_overview
74+
introexamples
7475
whatsnew
7576
installation
7677
contributing

docs/sphinx/source/introexamples.rst

Lines changed: 255 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,255 @@
1+
.. _introexamples:
2+
3+
Intro Examples
4+
==============
5+
6+
This page contains introductory examples of pvlib python usage.
7+
8+
.. _modeling-paradigms:
9+
10+
Modeling paradigms
11+
------------------
12+
13+
The backbone of pvlib-python
14+
is well-tested procedural code that implements PV system models.
15+
pvlib-python also provides a collection of classes for users
16+
that prefer object-oriented programming.
17+
These classes can help users keep track of data in a more organized way,
18+
provide some "smart" functions with more flexible inputs,
19+
and simplify the modeling process for common situations.
20+
The classes do not add any algorithms beyond what's available
21+
in the procedural code, and most of the object methods
22+
are simple wrappers around the corresponding procedural code.
23+
24+
Let's use each of these pvlib modeling paradigms
25+
to calculate the yearly energy yield for a given hardware
26+
configuration at a handful of sites listed below.
27+
28+
.. ipython:: python
29+
30+
import pandas as pd
31+
import matplotlib.pyplot as plt
32+
33+
naive_times = pd.DatetimeIndex(start='2015', end='2016', freq='1h')
34+
35+
# very approximate
36+
# latitude, longitude, name, altitude, timezone
37+
coordinates = [(30, -110, 'Tucson', 700, 'Etc/GMT+7'),
38+
(35, -105, 'Albuquerque', 1500, 'Etc/GMT+7'),
39+
(40, -120, 'San Francisco', 10, 'Etc/GMT+8'),
40+
(50, 10, 'Berlin', 34, 'Etc/GMT-1')]
41+
42+
import pvlib
43+
44+
# get the module and inverter specifications from SAM
45+
sandia_modules = pvlib.pvsystem.retrieve_sam('SandiaMod')
46+
sapm_inverters = pvlib.pvsystem.retrieve_sam('cecinverter')
47+
module = sandia_modules['Canadian_Solar_CS5P_220M___2009_']
48+
inverter = sapm_inverters['ABB__MICRO_0_25_I_OUTD_US_208_208V__CEC_2014_']
49+
50+
# specify constant ambient air temp and wind for simplicity
51+
temp_air = 20
52+
wind_speed = 0
53+
54+
55+
Procedural
56+
^^^^^^^^^^
57+
58+
The straightforward procedural code can be used for all modeling
59+
steps in pvlib-python.
60+
61+
The following code demonstrates how to use the procedural code
62+
to accomplish our system modeling goal:
63+
64+
.. ipython:: python
65+
66+
system = {'module': module, 'inverter': inverter,
67+
'surface_azimuth': 180}
68+
69+
energies = {}
70+
71+
for latitude, longitude, name, altitude, timezone in coordinates:
72+
times = naive_times.tz_localize(timezone)
73+
system['surface_tilt'] = latitude
74+
solpos = pvlib.solarposition.get_solarposition(times, latitude, longitude)
75+
dni_extra = pvlib.irradiance.get_extra_radiation(times)
76+
airmass = pvlib.atmosphere.get_relative_airmass(solpos['apparent_zenith'])
77+
pressure = pvlib.atmosphere.alt2pres(altitude)
78+
am_abs = pvlib.atmosphere.get_absolute_airmass(airmass, pressure)
79+
tl = pvlib.clearsky.lookup_linke_turbidity(times, latitude, longitude)
80+
cs = pvlib.clearsky.ineichen(solpos['apparent_zenith'], am_abs, tl,
81+
dni_extra=dni_extra, altitude=altitude)
82+
aoi = pvlib.irradiance.aoi(system['surface_tilt'], system['surface_azimuth'],
83+
solpos['apparent_zenith'], solpos['azimuth'])
84+
total_irrad = pvlib.irradiance.get_total_irradiance(system['surface_tilt'],
85+
system['surface_azimuth'],
86+
solpos['apparent_zenith'],
87+
solpos['azimuth'],
88+
cs['dni'], cs['ghi'], cs['dhi'],
89+
dni_extra=dni_extra,
90+
model='haydavies')
91+
temps = pvlib.pvsystem.sapm_celltemp(total_irrad['poa_global'],
92+
wind_speed, temp_air)
93+
effective_irradiance = pvlib.pvsystem.sapm_effective_irradiance(
94+
total_irrad['poa_direct'], total_irrad['poa_diffuse'],
95+
am_abs, aoi, module)
96+
dc = pvlib.pvsystem.sapm(effective_irradiance, temps['temp_cell'], module)
97+
ac = pvlib.pvsystem.snlinverter(dc['v_mp'], dc['p_mp'], inverter)
98+
annual_energy = ac.sum()
99+
energies[name] = annual_energy
100+
101+
energies = pd.Series(energies)
102+
103+
# based on the parameters specified above, these are in W*hrs
104+
print(energies.round(0))
105+
106+
energies.plot(kind='bar', rot=0)
107+
@savefig proc-energies.png width=6in
108+
plt.ylabel('Yearly energy yield (W hr)')
109+
@suppress
110+
plt.close();
111+
112+
113+
.. _object-oriented:
114+
115+
Object oriented (Location, PVSystem, ModelChain)
116+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
117+
118+
The first object oriented paradigm uses a model where a
119+
:py:class:`~pvlib.pvsystem.PVSystem` object represents an assembled
120+
collection of modules, inverters, etc., a
121+
:py:class:`~pvlib.location.Location` object represents a particular
122+
place on the planet, and a :py:class:`~pvlib.modelchain.ModelChain`
123+
object describes the modeling chain used to calculate PV output at that
124+
Location. This can be a useful paradigm if you prefer to think about the
125+
PV system and its location as separate concepts or if you develop your
126+
own ModelChain subclasses. It can also be helpful if you make extensive
127+
use of Location-specific methods for other calculations. pvlib-python
128+
also includes a :py:class:`~pvlib.tracking.SingleAxisTracker` class that
129+
is a subclass of :py:class:`~pvlib.pvsystem.PVSystem`.
130+
131+
The following code demonstrates how to use
132+
:py:class:`~pvlib.location.Location`,
133+
:py:class:`~pvlib.pvsystem.PVSystem`, and
134+
:py:class:`~pvlib.modelchain.ModelChain` objects to accomplish our
135+
system modeling goal. ModelChain objects provide convenience methods
136+
that can provide default selections for models and can also fill
137+
necessary input data with modeled data. In our example below, we use
138+
convenience methods. For example, no irradiance data is provided as
139+
input, so the ModelChain object substitutes irradiance from a clear-sky
140+
model via the prepare_inputs method. Also, no irradiance transposition
141+
model is specified (keyword argument `transposition` for ModelChain) so
142+
the ModelChain defaults to the `haydavies` model. In this example,
143+
ModelChain infers the DC power model from the module provided by
144+
examining the parameters defined for module.
145+
146+
.. ipython:: python
147+
148+
from pvlib.pvsystem import PVSystem
149+
from pvlib.location import Location
150+
from pvlib.modelchain import ModelChain
151+
152+
system = PVSystem(module_parameters=module,
153+
inverter_parameters=inverter)
154+
155+
energies = {}
156+
for latitude, longitude, name, altitude, timezone in coordinates:
157+
times = naive_times.tz_localize(timezone)
158+
location = Location(latitude, longitude, name=name, altitude=altitude,
159+
tz=timezone)
160+
# very experimental
161+
mc = ModelChain(system, location,
162+
orientation_strategy='south_at_latitude_tilt')
163+
# model results (ac, dc) and intermediates (aoi, temps, etc.)
164+
# assigned as mc object attributes
165+
mc.run_model(times)
166+
annual_energy = mc.ac.sum()
167+
energies[name] = annual_energy
168+
169+
energies = pd.Series(energies)
170+
171+
# based on the parameters specified above, these are in W*hrs
172+
print(energies.round(0))
173+
174+
energies.plot(kind='bar', rot=0)
175+
@savefig modelchain-energies.png width=6in
176+
plt.ylabel('Yearly energy yield (W hr)')
177+
@suppress
178+
plt.close();
179+
180+
181+
Object oriented (LocalizedPVSystem)
182+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
183+
184+
The second object oriented paradigm uses a model where a
185+
:py:class:`~pvlib.pvsystem.LocalizedPVSystem` represents a PV system at
186+
a particular place on the planet. This can be a useful paradigm if
187+
you're thinking about a power plant that already exists.
188+
189+
The :py:class:`~pvlib.pvsystem.LocalizedPVSystem` inherits from both
190+
:py:class:`~pvlib.pvsystem.PVSystem` and
191+
:py:class:`~pvlib.location.Location`, while the
192+
:py:class:`~pvlib.tracking.LocalizedSingleAxisTracker` inherits from
193+
:py:class:`~pvlib.tracking.SingleAxisTracker` (itself a subclass of
194+
:py:class:`~pvlib.pvsystem.PVSystem`) and
195+
:py:class:`~pvlib.location.Location`. The
196+
:py:class:`~pvlib.pvsystem.LocalizedPVSystem` and
197+
:py:class:`~pvlib.tracking.LocalizedSingleAxisTracker` classes may
198+
contain bugs due to the relative difficulty of implementing multiple
199+
inheritance. The :py:class:`~pvlib.pvsystem.LocalizedPVSystem` and
200+
:py:class:`~pvlib.tracking.LocalizedSingleAxisTracker` may be deprecated
201+
in a future release. We recommend that most modeling workflows implement
202+
:py:class:`~pvlib.location.Location`,
203+
:py:class:`~pvlib.pvsystem.PVSystem`, and
204+
:py:class:`~pvlib.modelchain.ModelChain`.
205+
206+
The following code demonstrates how to use a
207+
:py:class:`~pvlib.pvsystem.LocalizedPVSystem` object to accomplish our
208+
modeling goal:
209+
210+
.. ipython:: python
211+
212+
from pvlib.pvsystem import LocalizedPVSystem
213+
214+
energies = {}
215+
for latitude, longitude, name, altitude, timezone in coordinates:
216+
localized_system = LocalizedPVSystem(module_parameters=module,
217+
inverter_parameters=inverter,
218+
surface_tilt=latitude,
219+
surface_azimuth=180,
220+
latitude=latitude,
221+
longitude=longitude,
222+
name=name,
223+
altitude=altitude,
224+
tz=timezone)
225+
times = naive_times.tz_localize(timezone)
226+
clearsky = localized_system.get_clearsky(times)
227+
solar_position = localized_system.get_solarposition(times)
228+
total_irrad = localized_system.get_irradiance(solar_position['apparent_zenith'],
229+
solar_position['azimuth'],
230+
clearsky['dni'],
231+
clearsky['ghi'],
232+
clearsky['dhi'])
233+
temps = localized_system.sapm_celltemp(total_irrad['poa_global'],
234+
wind_speed, temp_air)
235+
aoi = localized_system.get_aoi(solar_position['apparent_zenith'],
236+
solar_position['azimuth'])
237+
airmass = localized_system.get_airmass(solar_position=solar_position)
238+
effective_irradiance = localized_system.sapm_effective_irradiance(
239+
total_irrad['poa_direct'], total_irrad['poa_diffuse'],
240+
airmass['airmass_absolute'], aoi)
241+
dc = localized_system.sapm(effective_irradiance, temps['temp_cell'])
242+
ac = localized_system.snlinverter(dc['v_mp'], dc['p_mp'])
243+
annual_energy = ac.sum()
244+
energies[name] = annual_energy
245+
246+
energies = pd.Series(energies)
247+
248+
# based on the parameters specified above, these are in W*hrs
249+
print(energies.round(0))
250+
251+
energies.plot(kind='bar', rot=0)
252+
@savefig localized-pvsystem-energies.png width=6in
253+
plt.ylabel('Yearly energy yield (W hr)')
254+
@suppress
255+
plt.close();

0 commit comments

Comments
 (0)