|
| 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