|
| 1 | +""" |
| 2 | +Sun path diagram |
| 3 | +================ |
| 4 | +
|
| 5 | +Examples of generating sunpath diagrams. |
| 6 | +""" |
| 7 | + |
| 8 | +#%% |
| 9 | +# This example shows basic usage of pvlib's solar position calculations with |
| 10 | +# :py:meth:`pvlib.solarposition.get_solarposition`. The examples shown here |
| 11 | +# will generate sunpath diagrams that shows solar position over a year. |
| 12 | +# |
| 13 | +# Polar plot |
| 14 | +# ---------- |
| 15 | +# |
| 16 | +# Below is an example plot of solar position in |
| 17 | +# `polar coordinates <https://en.wikipedia.org/wiki/Polar_coordinate_system>`_. |
| 18 | + |
| 19 | +from pvlib import solarposition |
| 20 | +import pandas as pd |
| 21 | +import numpy as np |
| 22 | +import matplotlib.pyplot as plt |
| 23 | + |
| 24 | +tz = 'Asia/Calcutta' |
| 25 | +lat, lon = 28.6, 77.2 |
| 26 | + |
| 27 | +times = pd.date_range('2019-01-01 00:00:00', '2020-01-01', closed='left', |
| 28 | + freq='H', tz=tz) |
| 29 | +solpos = solarposition.get_solarposition(times, lat, lon) |
| 30 | +# remove nighttime |
| 31 | +solpos = solpos.loc[solpos['apparent_elevation'] > 0, :] |
| 32 | + |
| 33 | +ax = plt.subplot(1, 1, 1, projection='polar') |
| 34 | +# draw the analemma loops |
| 35 | +points = ax.scatter(np.radians(solpos.azimuth), solpos.apparent_zenith, |
| 36 | + s=2, label=None, c=solpos.index.dayofyear) |
| 37 | +ax.figure.colorbar(points) |
| 38 | + |
| 39 | +# draw hour labels |
| 40 | +for hour in np.unique(solpos.index.hour): |
| 41 | + # choose label position by the smallest radius for each hour |
| 42 | + subset = solpos.loc[solpos.index.hour == hour, :] |
| 43 | + r = subset.apparent_zenith |
| 44 | + pos = solpos.loc[r.idxmin(), :] |
| 45 | + ax.text(np.radians(pos['azimuth']), pos['apparent_zenith'], str(hour)) |
| 46 | + |
| 47 | +# draw individual days |
| 48 | +for date in pd.to_datetime(['2019-03-21', '2019-06-21', '2019-12-21']): |
| 49 | + times = pd.date_range(date, date+pd.Timedelta('24h'), freq='5min', tz=tz) |
| 50 | + solpos = solarposition.get_solarposition(times, lat, lon) |
| 51 | + solpos = solpos.loc[solpos['apparent_elevation'] > 0, :] |
| 52 | + label = date.strftime('%Y-%m-%d') |
| 53 | + ax.plot(np.radians(solpos.azimuth), solpos.apparent_zenith, label=label) |
| 54 | + |
| 55 | +ax.figure.legend(loc='upper left') |
| 56 | + |
| 57 | +# change coordinates to be like a compass |
| 58 | +ax.set_theta_zero_location('N') |
| 59 | +ax.set_theta_direction(-1) |
| 60 | +ax.set_rmax(90) |
| 61 | + |
| 62 | +plt.show() |
| 63 | + |
| 64 | +#%% |
| 65 | +# This is a polar plot of hourly solar zenith and azimuth. The figure-8 |
| 66 | +# patterns are called `analemmas <https://en.wikipedia.org/wiki/Analemma>`_ and |
| 67 | +# show how the sun's path slowly shifts over the course of the year . The |
| 68 | +# colored lines show the single-day sun paths for the winter and summer |
| 69 | +# solstices as well as the spring equinox. |
| 70 | +# |
| 71 | +# The soltice paths mark the boundary of the sky area that the sun traverses |
| 72 | +# over a year. The diagram shows that there is no point in the |
| 73 | +# year when is the sun directly overhead (zenith=0) -- note that this location |
| 74 | +# is north of the Tropic of Cancer. |
| 75 | +# |
| 76 | +# Examining the sun path for the summer solstice in particular shows that |
| 77 | +# the sun rises north of east, crosses into the southern sky around 10 AM for a |
| 78 | +# few hours before crossing back into the northern sky around 3 PM and setting |
| 79 | +# north of west. In contrast, the winter solstice sun path remains in the |
| 80 | +# southern sky the entire day. Moreover, the diagram shows that the winter |
| 81 | +# solstice is a shorter day than the summer soltice -- in December, the sun |
| 82 | +# rises after 7 AM and sets before 6 PM, whereas in June the sun is up before |
| 83 | +# 6 AM and sets after 7 PM. |
| 84 | +# |
| 85 | +# Another use of this diagram is to determine what times of year the sun is |
| 86 | +# blocked by obstacles. For instance, for a mountain range on the western side |
| 87 | +# of an array that extends 10 degrees above the horizon, the sun is blocked: |
| 88 | +# |
| 89 | +# - after about 6:30 PM on the summer solstice |
| 90 | +# - after about 5:30 PM on the spring equinox |
| 91 | +# - after about 4:30 PM on the winter solstice |
| 92 | + |
| 93 | +#%% |
| 94 | +# PVSyst Plot |
| 95 | +# ----------- |
| 96 | +# |
| 97 | +# PVSyst users will be more familiar with sunpath diagrams in Cartesian |
| 98 | +# coordinates: |
| 99 | + |
| 100 | +from pvlib import solarposition |
| 101 | +import pandas as pd |
| 102 | +import numpy as np |
| 103 | +import matplotlib.pyplot as plt |
| 104 | + |
| 105 | +tz = 'Asia/Calcutta' |
| 106 | +lat, lon = 28.6, 77.2 |
| 107 | +times = pd.date_range('2019-01-01 00:00:00', '2020-01-01', closed='left', |
| 108 | + freq='H', tz=tz) |
| 109 | + |
| 110 | +solpos = solarposition.get_solarposition(times, lat, lon) |
| 111 | +# remove nighttime |
| 112 | +solpos = solpos.loc[solpos['apparent_elevation'] > 0, :] |
| 113 | + |
| 114 | +fig, ax = plt.subplots() |
| 115 | +points = ax.scatter(solpos.azimuth, solpos.apparent_elevation, s=2, |
| 116 | + c=solpos.index.dayofyear, label=None) |
| 117 | +fig.colorbar(points) |
| 118 | + |
| 119 | +for hour in np.unique(solpos.index.hour): |
| 120 | + # choose label position by the largest elevation for each hour |
| 121 | + subset = solpos.loc[solpos.index.hour == hour, :] |
| 122 | + height = subset.apparent_elevation |
| 123 | + pos = solpos.loc[height.idxmax(), :] |
| 124 | + ax.text(pos['azimuth'], pos['apparent_elevation'], str(hour)) |
| 125 | + |
| 126 | +for date in pd.to_datetime(['2019-03-21', '2019-06-21', '2019-12-21']): |
| 127 | + times = pd.date_range(date, date+pd.Timedelta('24h'), freq='5min', tz=tz) |
| 128 | + solpos = solarposition.get_solarposition(times, lat, lon) |
| 129 | + solpos = solpos.loc[solpos['apparent_elevation'] > 0, :] |
| 130 | + label = date.strftime('%Y-%m-%d') |
| 131 | + ax.plot(solpos.azimuth, solpos.apparent_elevation, label=label) |
| 132 | + |
| 133 | +ax.figure.legend(loc='upper left') |
| 134 | +ax.set_xlabel('Solar Azimuth (degrees)') |
| 135 | +ax.set_ylabel('Solar Elevation (degrees)') |
| 136 | + |
| 137 | +plt.show() |
0 commit comments