-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Mount gallery examples #1266
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
Merged
Merged
Mount gallery examples #1266
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
4556a6a
create discontinuous tracking example
kandersolar b4bc3ac
create dual-axis example
kandersolar dbad4ca
create mixed-orientation example
kandersolar 76555b7
create seasonal tilt example
kandersolar 6e72f25
stickler
kandersolar bfefa72
whatsnew
kandersolar dbf0602
bsrn -> clear-sky sim
kandersolar e6866d2
stickler, d'oh
kandersolar File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
""" | ||
Discontinuous Tracking | ||
====================== | ||
|
||
Example of a custom Mount class. | ||
""" | ||
|
||
# %% | ||
# Many real-world tracking arrays adjust their position in discrete steps | ||
# rather than through continuous movement. This example shows how to model | ||
# this discontinuous tracking by implementing a custom Mount class. | ||
|
||
from pvlib import tracking, pvsystem, location, modelchain | ||
from pvlib.temperature import TEMPERATURE_MODEL_PARAMETERS | ||
import matplotlib.pyplot as plt | ||
import pandas as pd | ||
|
||
|
||
# %% | ||
# We'll define our custom Mount by extending | ||
# :py:class:`~pvlib.pvsystem.SingleAxisTrackerMount` for convenience. | ||
# Another approach would be to extend ``AbstractMount`` directly; see | ||
# the source code of :py:class:`~pvlib.pvsystem.SingleAxisTrackerMount` | ||
# and :py:class:`~pvlib.pvsystem.FixedMount` for how that is done. | ||
|
||
|
||
class DiscontinuousTrackerMount(pvsystem.SingleAxisTrackerMount): | ||
# inherit from SingleAxisTrackerMount so that we get the | ||
# constructor and tracking attributes (axis_tilt etc) automatically | ||
|
||
def get_orientation(self, solar_zenith, solar_azimuth): | ||
# Different trackers update at different rates; in this example we'll | ||
# assume a relatively slow update interval of 15 minutes to make the | ||
# effect more visually apparent. | ||
zenith_subset = solar_zenith.resample('15min').first() | ||
azimuth_subset = solar_azimuth.resample('15min').first() | ||
|
||
tracking_data_15min = tracking.singleaxis( | ||
zenith_subset, azimuth_subset, | ||
self.axis_tilt, self.axis_azimuth, | ||
self.max_angle, self.backtrack, | ||
self.gcr, self.cross_axis_tilt | ||
) | ||
# propagate the 15-minute positions to 1-minute stair-stepped values: | ||
tracking_data_1min = tracking_data_15min.reindex(solar_zenith.index, | ||
method='ffill') | ||
return tracking_data_1min | ||
|
||
|
||
# %% | ||
# Let's take a look at the tracker rotation curve it produces: | ||
|
||
times = pd.date_range('2019-06-01', '2019-06-02', freq='1min', tz='US/Eastern') | ||
loc = location.Location(40, -80) | ||
solpos = loc.get_solarposition(times) | ||
mount = DiscontinuousTrackerMount(axis_azimuth=180, gcr=0.4) | ||
tracker_data = mount.get_orientation(solpos.apparent_zenith, solpos.azimuth) | ||
tracker_data['tracker_theta'].plot() | ||
plt.ylabel('Tracker Rotation [degree]') | ||
plt.show() | ||
|
||
# %% | ||
# With our custom tracking logic defined, we can create the corresponding | ||
# Array and PVSystem, and then run a ModelChain as usual: | ||
|
||
module_parameters = {'pdc0': 1, 'gamma_pdc': -0.004, 'b': 0.05} | ||
temp_params = TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_polymer'] | ||
array = pvsystem.Array(mount=mount, module_parameters=module_parameters, | ||
temperature_model_parameters=temp_params) | ||
system = pvsystem.PVSystem(arrays=[array], inverter_parameters={'pdc0': 1}) | ||
mc = modelchain.ModelChain(system, loc, spectral_model='no_loss') | ||
|
||
# simple simulated weather, just to show the effect of discrete tracking | ||
weather = loc.get_clearsky(times) | ||
weather['temp_air'] = 25 | ||
weather['wind_speed'] = 1 | ||
mc.run_model(weather) | ||
|
||
fig, axes = plt.subplots(2, 1, sharex=True) | ||
mc.results.effective_irradiance.plot(ax=axes[0]) | ||
axes[0].set_ylabel('Effective Irradiance [W/m^2]') | ||
mc.results.ac.plot(ax=axes[1]) | ||
axes[1].set_ylabel('AC Power') | ||
fig.show() | ||
|
||
# %% | ||
# The effect of discontinuous tracking creates a "jagged" effect in the | ||
# simulated plane-of-array irradiance, which then propagates through to | ||
# the AC power output. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
""" | ||
Dual-Axis Tracking | ||
================== | ||
|
||
Example of a custom Mount class. | ||
""" | ||
|
||
# %% | ||
# Dual-axis trackers can track the sun in two dimensions across the sky dome | ||
# instead of just one like single-axis trackers. This example shows how to | ||
# model a simple dual-axis tracking system using ModelChain with a custom | ||
# Mount class. | ||
|
||
from pvlib import pvsystem, location, modelchain | ||
import pandas as pd | ||
import matplotlib.pyplot as plt | ||
|
||
# %% | ||
# New Mount classes should extend ``pvlib.pvsystem.AbstractMount`` | ||
# and must implement a ``get_orientation(solar_zenith, solar_azimuth)`` method: | ||
|
||
|
||
class DualAxisTrackerMount(pvsystem.AbstractMount): | ||
def get_orientation(self, solar_zenith, solar_azimuth): | ||
# no rotation limits, no backtracking | ||
return {'surface_tilt': solar_zenith, 'surface_azimuth': solar_azimuth} | ||
|
||
|
||
loc = location.Location(40, -80) | ||
array = pvsystem.Array( | ||
mount=DualAxisTrackerMount(), | ||
module_parameters=dict(pdc0=1, gamma_pdc=-0.004, b=0.05), | ||
temperature_model_parameters=dict(a=-3.56, b=-0.075, deltaT=3)) | ||
system = pvsystem.PVSystem(arrays=[array], inverter_parameters=dict(pdc0=3)) | ||
mc = modelchain.ModelChain(system, loc, spectral_model='no_loss') | ||
|
||
times = pd.date_range('2019-01-01 06:00', '2019-01-01 18:00', freq='5min', | ||
tz='Etc/GMT+5') | ||
weather = loc.get_clearsky(times) | ||
mc.run_model(weather) | ||
|
||
mc.results.ac.plot() | ||
plt.ylabel('Output Power') | ||
plt.show() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
""" | ||
Mixed Orientation | ||
================= | ||
|
||
Using multiple Arrays in a single PVSystem. | ||
""" | ||
|
||
# %% | ||
# Residential and Commercial systems often have fixed-tilt arrays | ||
# installed at different azimuths. This can be modeled by using | ||
# multiple :py:class:`~pvlib.pvsystem.Array` objects (one for each | ||
# orientation) with a single :py:class:`~pvlib.pvsystem.PVSystem` object. | ||
# | ||
# This particular example has one east-facing array (azimuth=90) and one | ||
# west-facing array (azimuth=270), which aside from orientation are identical. | ||
|
||
|
||
from pvlib import pvsystem, modelchain, location | ||
import pandas as pd | ||
import matplotlib.pyplot as plt | ||
|
||
array_kwargs = dict( | ||
module_parameters=dict(pdc0=1, gamma_pdc=-0.004), | ||
temperature_model_parameters=dict(a=-3.56, b=-0.075, deltaT=3) | ||
) | ||
|
||
arrays = [ | ||
pvsystem.Array(pvsystem.FixedMount(30, 270), name='West-Facing Array', | ||
**array_kwargs), | ||
pvsystem.Array(pvsystem.FixedMount(30, 90), name='East-Facing Array', | ||
**array_kwargs), | ||
] | ||
loc = location.Location(40, -80) | ||
system = pvsystem.PVSystem(arrays=arrays, inverter_parameters=dict(pdc0=3)) | ||
mc = modelchain.ModelChain(system, loc, aoi_model='physical', | ||
spectral_model='no_loss') | ||
|
||
times = pd.date_range('2019-01-01 06:00', '2019-01-01 18:00', freq='5min', | ||
tz='Etc/GMT+5') | ||
weather = loc.get_clearsky(times) | ||
mc.run_model(weather) | ||
|
||
fig, ax = plt.subplots() | ||
for array, pdc in zip(system.arrays, mc.results.dc): | ||
pdc.plot(label=f'{array.name}') | ||
mc.results.ac.plot(label='Inverter') | ||
plt.ylabel('System Output') | ||
plt.legend() | ||
plt.show() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
""" | ||
Seasonal Tilt | ||
============= | ||
|
||
Example of a custom Mount class. | ||
""" | ||
|
||
# %% | ||
# Some PV systems are built with the option to adjust the module | ||
# tilt to follow seasonal changes in solar position. For example, | ||
# SAM calls this strategy "Seasonal Tilt". This example shows how | ||
# to use a custom Mount class to use the Seasonal Tilt strategy | ||
# with :py:class:`~pvlib.modelchain.ModelChain`. | ||
|
||
import pvlib | ||
from pvlib import pvsystem, location, modelchain, iotools | ||
from pvlib.temperature import TEMPERATURE_MODEL_PARAMETERS | ||
import pandas as pd | ||
import pathlib | ||
import matplotlib.pyplot as plt | ||
from dataclasses import dataclass | ||
|
||
|
||
# %% | ||
# New Mount classes should extend ``pvlib.pvsystem.AbstractMount`` | ||
# and must implement a ``get_orientation(solar_zenith, solar_azimuth)`` method: | ||
|
||
|
||
@dataclass | ||
class SeasonalTiltMount(pvsystem.AbstractMount): | ||
monthly_tilts: list # length 12, one tilt per calendar month | ||
surface_azimuth: float = 180.0 | ||
|
||
def get_orientation(self, solar_zenith, solar_azimuth): | ||
tilts = [self.monthly_tilts[m-1] for m in solar_zenith.index.month] | ||
return pd.DataFrame({ | ||
'surface_tilt': tilts, | ||
'surface_azimuth': self.surface_azimuth, | ||
}, index=solar_zenith.index) | ||
|
||
|
||
# %% | ||
# First let's grab some weather data and make sure our mount produces tilts | ||
# like we expect: | ||
|
||
DATA_DIR = pathlib.Path(pvlib.__file__).parent / 'data' | ||
tmy, metadata = iotools.read_tmy3(DATA_DIR / '723170TYA.CSV', coerce_year=1990) | ||
# shift from TMY3 right-labeled index to left-labeled index: | ||
tmy.index = tmy.index - pd.Timedelta(hours=1) | ||
weather = pd.DataFrame({ | ||
'ghi': tmy['GHI'], 'dhi': tmy['DHI'], 'dni': tmy['DNI'], | ||
'temp_air': tmy['DryBulb'], 'wind_speed': tmy['Wspd'], | ||
}) | ||
loc = location.Location.from_tmy(metadata) | ||
solpos = loc.get_solarposition(weather.index) | ||
# same default monthly tilts as SAM: | ||
tilts = [40, 40, 40, 20, 20, 20, 20, 20, 20, 40, 40, 40] | ||
mount = SeasonalTiltMount(monthly_tilts=tilts) | ||
orientation = mount.get_orientation(solpos.apparent_zenith, solpos.azimuth) | ||
orientation['surface_tilt'].plot() | ||
plt.ylabel('Surface Tilt [degrees]') | ||
plt.show() | ||
|
||
# %% | ||
# With our custom tilt strategy defined, we can create the corresponding | ||
# Array and PVSystem, and then run a ModelChain as usual: | ||
|
||
module_parameters = {'pdc0': 1, 'gamma_pdc': -0.004, 'b': 0.05} | ||
temp_params = TEMPERATURE_MODEL_PARAMETERS['sapm']['open_rack_glass_polymer'] | ||
array = pvsystem.Array(mount=mount, module_parameters=module_parameters, | ||
temperature_model_parameters=temp_params) | ||
system = pvsystem.PVSystem(arrays=[array], inverter_parameters={'pdc0': 1}) | ||
mc = modelchain.ModelChain(system, loc, spectral_model='no_loss') | ||
|
||
_ = mc.run_model(weather) | ||
|
||
# %% | ||
# Now let's re-run the simulation assuming tilt=30 for the entire year: | ||
|
||
array2 = pvsystem.Array(mount=pvsystem.FixedMount(30, 180), | ||
module_parameters=module_parameters, | ||
temperature_model_parameters=temp_params) | ||
system2 = pvsystem.PVSystem(arrays=[array2], inverter_parameters={'pdc0': 1}) | ||
mc2 = modelchain.ModelChain(system2, loc, spectral_model='no_loss') | ||
_ = mc2.run_model(weather) | ||
|
||
# %% | ||
# And finally, compare simulated monthly generation between the two tilt | ||
# strategies: | ||
|
||
# sphinx_gallery_thumbnail_number = 2 | ||
results = pd.DataFrame({ | ||
'Seasonal 20/40 Production': mc.results.ac, | ||
'Fixed 30 Production': mc2.results.ac, | ||
}) | ||
results.resample('m').sum().plot() | ||
plt.ylabel('Monthly Production') | ||
plt.show() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is the underscore to suppress output? I'm not sure about sphinx-gallery, but the ipython directive will suppress output if you end the line in
;
. No big deal to keep it.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, to suppress output. Semicolon doesn't suppress in sphinx-gallery, at least in my local build: