## Note that the function name starts with a `_`.
# This indicates that the function is only intended to be used *internally*, so from
# other functions of this module, but not from users of the module.
# This is only a convention, though, and highlights the intent of the programmer
# to the user. Nevertheless, users can still call the function like any other funtion
# from the module, if they choose to do so.
def _normalize_angle_difference(a, b):
"""
Compute and normalize the difference between two angles 'a' and 'b' to the range [0, 360) degrees,
where 0 represent south, and angles increase clockwise.
Parameters
----------
a (float): The first angle in degrees.
b (float): The second angle in degrees.
Returns
-------
float: The normalized angle difference between 'a' and 'b' in degrees, within the range [0, 360),
where 0 represent south, and angles increase clockwise.
This function calculates the difference between two angles 'a' and 'b' and ensures that
the result is within the range [0, 360) degrees. It handles cases where the difference
may cross the boundary of 360 degrees by adding or subtracting 360 as needed.
"""
= a - b
delta = np.where(delta < 0, delta + 360, delta)
delta_normalized = np.where(delta_normalized >= 360, delta_normalized - 360, delta_normalized)
delta_normalized return delta_normalized
def comp_cos_incidenceangle(elevangle, solarazimuth, incline, pvazimuth=0):
"""
Compute the cosine of the incidence angle of direct sunlight onto an inclined surface
Parameters
----------
elevangle: array-like, float
elevation angle of the sun in degrees
solarazimuth: array-like, float
solar azimuth as given by function `comp_solarazimuth()`
incline: float
incline of the surface in degrees [0, 90]
Returns
-------
cos_theta: array-like, float
cosine of incidence angle
"""
= (np.cos(np.deg2rad(incline)) * np.sin(np.deg2rad(elevangle)) +
cos_theta * np.cos(np.deg2rad(elevangle)) * np.cos(np.deg2rad(_normalize_angle_difference(solarazimuth, pvazimuth))))
np.sin(np.deg2rad(incline)) < 0] = 0
cos_theta[cos_theta return cos_theta
Assignment #08
Unit 08
Until next week, work through the material provided in Unit 8 and solve the following exercises.
This week focuses on gridded data visualization and the task of plotting multiple artists from a data frame. The first exercise provides an opportunity to enhance your proficiency in matrix indexing and for-loops. The second exercise is important for refining your skills in reshaping data frames and efficiently creating informative working plots.
Before you get started with your own code, I have prepared an update of the comp_cos_incidenceangle
function from our module solar
. Take the following code block that defines two functions and replace the old function comp_cos_incidenceangle
in the module. (Optimally, you module solar
is located in our own development package mytoolbox
).
After you have done that, start a notebook and try to solve the task. The following code blocks give you some starting points.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from importlib import reload
from mytoolbox import solar
reload(solar)
= pd.read_csv('../06_unit/solar_Dornbirn.csv', parse_dates=True, index_col='datetime')
sdo print(sdo)
We will perform the grid search by calling a function that does all the computations for us. In the following, I define that function and within the function body I have left you an opportunity for exercise. This task is not essential to continue with the remaining tasks. Do not get hung up here. Tip: Use the method pd.Series.diff()
and the timedelta method .dt.total_seconds()
to compute the time sampling in hours. If the time sampling is not equal between all time steps, raise an error.
You can either put the function into solar
or directly into your notebook.
## Define a function that will compute the energy produced by the PV module
def comp_energy_produced(iswr0, beta, pvazimuth, elevangle, azimuth, datetime = None, efficiency=0.2, area=1):
"""
Calculate the energy produced by a PV module based on solar irradiance data.
Parameters
----------
iswr0 (pandas.Series): Solar irradiance data onto horizontal surface.
beta (float): Incline angle of the PV module (in degrees).
pvazimuth (float): Azimuth angle of the PV module (in degrees, 0 in the south).
elevangle (array-like): Elevation angle of the sun (in degrees).
azimuth (array-like): Azimuth angle of the sun (in degrees, 0 in the south).
datetime (pandas.Series or None, optional): Timestamps associated with the irradiance data. If None,
the function assumes regular time intervals based on the index of 'iswr0'.
efficiency (float, optional): Efficiency of the photovoltaic system (default is 0.2).
area (float, optional): Area of the PV module (default is 1).
Returns
-------
float: Total energy produced by the photovoltaic system (in Wh).
If 'datetime' is not provided, the function assumes regular time intervals based on the index of 'iswr0'.
The function raises a ValueError if the time intervals are irregular.
"""
if datetime is None:
= iswr0.index.to_series()
datetime
= 1 # hard coded time sampling of 1 hour
dt
# Replace the hard coded time sampling dt:
# Retrieve unique time sampling dt of `datetime` in (hours) and
# raise an error if datetime is irregularly sampled
#
# < your code goes here >
# Calculate iswr onto inclined PV module
= solar.comp_irradiance_incline(iswr0,
iswr_beta
solar.comp_cos_incidenceangle(elevangle, azimuth, beta, pvazimuth),
elevangle)# Calculate generated power and energy
= iswr_beta * area * efficiency
power = power.sum() * dt
energy
return energy
Finally, you can perform the grid search:
## Perform the grid search
= np.arange(-180, 180, 5) # azimuth angle of PV module (0 in the south, -90 West, 90 East)
alpha_pv = np.arange(0, 95, 5) # incline of PV module
beta_pv
## Create a grid Z based on the previous PV parameters
## and iterate through each element of Z
# Each element of Z can be computed by `comp_energy_produced()`
# The data frame `sdo` holds the elevangle (h), solar azimuth (alpha) and iswr0 (iswr_clearsky)
# The PV parameters are defined above.
# Tip: If you don't know how to setup the loop, read the Notes section of the documentation of np.meshgrid().
# < your code goes here >
## Normalize Z with the maximum value of Z that is not NaN
# < your code goes here >
## Plot the contour map
# < your code goes here >