-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add functions to fit and convert IAM models #1827
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
Changes from 55 commits
9816a3f
dacc1f0
fc968af
441d8cc
77641d0
d7cbf5e
b4dcecf
6d054f4
21a66ee
b4714a4
96de7d9
de20629
91f811e
e4d4cdc
3789890
2efa830
c5c2d09
af65bc0
d505ac9
109e20e
554d862
e8d83b6
ee9c686
e95993d
991e962
484cb5a
47ebdac
317fb35
3996cab
ba87f7e
ac4e717
529e512
6b211fd
3fc2c00
a9f9b74
ac160b8
536cb9f
2882912
9bb36b5
753d72b
fc1316c
935443b
c9f697d
88a9dfc
8ace9d6
3475bf4
f216d94
cb4cb05
ed35731
fdcc952
9b1cfd8
d78265a
38bfb58
04121de
69cd00a
520a74e
e5cd24b
6ce34e4
743931d
fe9a39c
d56cbcf
2a0b815
4e165a0
d3d8cfd
313386c
b0e45dd
32aa64a
8df7bf6
6250182
74c2e54
f9c8888
79af432
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
|
||
""" | ||
IAM Model Conversion | ||
==================== | ||
|
||
Illustrates how to convert from one IAM model to a different model using | ||
:py:func:`~pvlib.iam.convert` | ||
|
||
""" | ||
|
||
# %% | ||
# An incidence angle modifier (IAM) model quantifies the fraction of direct | ||
# irradiance is that is reflected away from a module's surface. Three popular | ||
# IAM models are Martin-Ruiz :py:func:`~pvlib.iam.martin_ruiz`, physical | ||
# :py:func:`~pvlib.iam.physical`, and ASHRAE py:func:`~pvlib.iam.ashrae`. | ||
# Each model requires one or more parameters. | ||
# | ||
# Here, we show how to use | ||
# :py:func:`~pvlib.iam.convert` to estimate parameters for a desired target | ||
# IAM model from a source IAM model. Model conversion requires a weight | ||
# function that assigns more influence to some AOI values than others. | ||
# We illustrate how to provide a custom weight function to | ||
# :py:func:`~pvlib.iam.convert`. | ||
|
||
import numpy as np | ||
import matplotlib.pyplot as plt | ||
|
||
from pvlib.tools import cosd | ||
from pvlib.iam import (ashrae, martin_ruiz, physical, convert) | ||
|
||
# %% | ||
# Converting from one IAM model to another model | ||
# ---------------------------------------------- | ||
# | ||
# Here we'll show how to convert from the Martin-Ruiz model to the | ||
# physical and the ASHRAE models. | ||
|
||
# Compute IAM values using the martin_ruiz model. | ||
aoi = np.linspace(0, 90, 100) | ||
martin_ruiz_params = {'a_r': 0.16} | ||
martin_ruiz_iam = martin_ruiz(aoi, **martin_ruiz_params) | ||
|
||
# Get parameters for the physical model and compute IAM using these parameters. | ||
physical_params = convert('martin_ruiz', martin_ruiz_params, 'physical') | ||
physical_iam = physical(aoi, **physical_params) | ||
|
||
# Get parameters for the ASHRAE model and compute IAM using these parameters. | ||
ashrae_params = convert('martin_ruiz', martin_ruiz_params, 'ashrae') | ||
ashrae_iam = ashrae(aoi, **ashrae_params) | ||
|
||
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(11, 5), sharey=True) | ||
|
||
# Plot each model's IAM vs. angle-of-incidence (AOI). | ||
ax1.plot(aoi, martin_ruiz_iam, label='Martin-Ruiz') | ||
ax1.plot(aoi, physical_iam, label='physical') | ||
ax1.set_xlabel('AOI (degrees)') | ||
ax1.set_title('Convert from Martin-Ruiz to physical') | ||
ax1.legend() | ||
|
||
ax2.plot(aoi, martin_ruiz_iam, label='Martin-Ruiz') | ||
ax2.plot(aoi, ashrae_iam, label='ASHRAE') | ||
ax2.set_xlabel('AOI (degrees)') | ||
ax2.set_title('Convert from Martin-Ruiz to ASHRAE') | ||
ax2.legend() | ||
|
||
ax1.set_ylabel('IAM') | ||
plt.show() | ||
|
||
|
||
# %% | ||
# The weight function | ||
# ------------------- | ||
# :py:func:`pvlib.iam.convert` uses a weight function when computing residuals | ||
# between the two models. The default weight | ||
# function is :math:`1 - \sin(aoi)`. We can instead pass a custom weight | ||
# function to :py:func:`pvlib.iam.convert`. | ||
# | ||
# In some cases, the choice of weight function has a minimal effect on the | ||
# returned model parameters. This is especially true when converting between | ||
# the Martin-Ruiz and physical models, because the curves described by these | ||
# models can match quite closely. However, when conversion involves the ASHRAE | ||
# model, the choice of weight function can have a meaningful effect on the | ||
# returned parameters for the target model. | ||
# | ||
# Here we'll show examples of both of these cases, starting with an example | ||
# where the choice of weight function does not have much impact. In doing | ||
# so, we'll show how to pass in a custom weight function of our choice. | ||
|
||
# Compute IAM using the Martin-Ruiz model. | ||
aoi = np.linspace(0, 90, 100) | ||
martin_ruiz_params = {'a_r': 0.16} | ||
martin_ruiz_iam = martin_ruiz(aoi, **martin_ruiz_params) | ||
|
||
# Get parameters for the physical model ... | ||
|
||
# ... using the default weight function. | ||
physical_params_default = convert('martin_ruiz', martin_ruiz_params, | ||
'physical') | ||
physical_iam_default = physical(aoi, **physical_params_default) | ||
|
||
|
||
# ... using a custom weight function. | ||
def weight_function(aoi): | ||
return cosd(aoi) | ||
|
||
|
||
physical_params_custom = convert('martin_ruiz', martin_ruiz_params, 'physical', | ||
weight=weight_function) | ||
physical_iam_custom = physical(aoi, **physical_params_custom) | ||
|
||
# Plot IAM vs AOI. | ||
plt.plot(aoi, martin_ruiz_iam, label='Martin-Ruiz') | ||
plt.plot(aoi, physical_iam_default, label='Default weight function') | ||
plt.plot(aoi, physical_iam_custom, label='Custom weight function') | ||
plt.xlabel('AOI (degrees)') | ||
plt.ylabel('IAM') | ||
plt.title('Martin-Ruiz to physical') | ||
plt.legend() | ||
plt.show() | ||
|
||
# %% | ||
# For this choice of source and target models, the weight function has little | ||
# effect on the target model's parameters. | ||
# | ||
# Now we'll look at an example where the weight function does affect the | ||
# output. | ||
|
||
# Get parameters for the ASHRAE model ... | ||
|
||
# ... using the default weight function. | ||
ashrae_params_default = convert('martin_ruiz', martin_ruiz_params, 'ashrae') | ||
ashrae_iam_default = ashrae(aoi, **ashrae_params_default) | ||
|
||
# ... using the custom weight function | ||
ashrae_params_custom = convert('martin_ruiz', martin_ruiz_params, 'ashrae', | ||
weight=weight_function) | ||
ashrae_iam_custom = ashrae(aoi, **ashrae_params_custom) | ||
|
||
# Plot IAM vs AOI. | ||
plt.plot(aoi, martin_ruiz_iam, label='Martin-Ruiz') | ||
plt.plot(aoi, ashrae_iam_default, label='Default weight function') | ||
plt.plot(aoi, ashrae_iam_custom, label='Custom weight function') | ||
plt.xlabel('AOI (degrees)') | ||
plt.ylabel('IAM') | ||
plt.title('Martin-Ruiz to ASHRAE') | ||
plt.legend() | ||
plt.show() | ||
|
||
# %% | ||
# In this case, each of the two ASHRAE looks quite different. | ||
# Finding the right weight function and parameters in such cases will require | ||
# knowing where you want the target model to be more accurate. The default | ||
# weight function was chosen because it yielded IAM models that produce | ||
# similar annual insolation for a simulated PV system TODO add reference. |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,98 @@ | ||||||
|
||||||
""" | ||||||
IAM Model Fitting | ||||||
================================ | ||||||
|
||||||
Illustrates how to fit an IAM model to data using :py:func:`~pvlib.iam.fit` | ||||||
|
||||||
""" | ||||||
|
||||||
# %% | ||||||
# An incidence angle modifier (IAM) model quantifies the fraction of direct | ||||||
# irradiance is that is reflected away from a module's surface. Three popular | ||||||
# IAM models are Martin-Ruiz :py:func:`~pvlib.iam.martin_ruiz`, physical | ||||||
# :py:func:`~pvlib.iam.physical`, and ASHRAE py:func:`~pvlib.iam.ashrae`. | ||||||
cwhanse marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
# Each model requires one or more parameters. | ||||||
# | ||||||
# Here, we show how to use | ||||||
# :py:func:`~pvlib.iam.fit` to estimate a model's parameters from data. | ||||||
# | ||||||
# Model fitting require a weight function that assigns | ||||||
# more influence to some AOI values than others. We illustrate how to provide | ||||||
# a custom weight function to :py:func:`~pvlib.iam.fit`. | ||||||
|
||||||
import numpy as np | ||||||
from random import uniform | ||||||
import matplotlib.pyplot as plt | ||||||
|
||||||
from pvlib.tools import cosd | ||||||
from pvlib.iam import (martin_ruiz, physical, fit) | ||||||
|
||||||
|
||||||
# %% | ||||||
# Fitting an IAM model to data | ||||||
# ---------------------------- | ||||||
# | ||||||
# Here, we'll show how to fit an IAM model to data. | ||||||
# We'll generate some data by perturbing output from the Martin-Ruiz model to | ||||||
# mimic measured data and then we'll fit the physical model to the perturbed | ||||||
# data. | ||||||
|
||||||
# Create and perturb IAM data. | ||||||
aoi = np.linspace(0, 90, 100) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
This would more representative for measurements. |
||||||
params = {'a_r': 0.16} | ||||||
iam = martin_ruiz(aoi, **params) | ||||||
data = iam * np.array([uniform(0.98, 1.02) for _ in range(len(iam))]) | ||||||
|
||||||
# Get parameters for the physical model by fitting to the perturbed data. | ||||||
physical_params = fit(aoi, data, 'physical') | ||||||
|
||||||
# Compute IAM with the fitted physical model parameters. | ||||||
physical_iam = physical(aoi, **physical_params) | ||||||
|
||||||
# Plot IAM vs. AOI | ||||||
plt.scatter(aoi, data, c='darkorange', label='Data') | ||||||
plt.plot(aoi, physical_iam, label='physical') | ||||||
plt.xlabel('AOI (degrees)') | ||||||
plt.ylabel('IAM') | ||||||
plt.title('Fitting the physical model to data') | ||||||
plt.legend() | ||||||
plt.show() | ||||||
|
||||||
|
||||||
# %% | ||||||
# The weight function | ||||||
# ------------------- | ||||||
# :py:func:`pvlib.iam.fit` uses a weight function when computing residuals | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest you say a bit more about the shape and purpose of these functions. I see there is more in the other example, but I think it is more relevant or important here. |
||||||
# between the model abd data. The default weight | ||||||
cwhanse marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
# function is :math:`1 - \sin(aoi)`. We can instead pass a custom weight | ||||||
# function to :py:func:`pvlib.iam.fit`. | ||||||
# | ||||||
|
||||||
# Define a custom weight function. | ||||||
def weight_function(aoi): | ||||||
return cosd(aoi) | ||||||
|
||||||
|
||||||
physical_params_custom = fit(aoi, data, 'physical', weight=weight_function) | ||||||
|
||||||
physical_iam_custom = physical(aoi, **physical_params_custom) | ||||||
|
||||||
# Plot IAM vs AOI. | ||||||
fig, ax = plt.subplots(2, 1, figsize=(5, 8)) | ||||||
ax[0].plot(aoi, data, '.', label='Data (from Martin-Ruiz model)') | ||||||
ax[0].plot(aoi, physical_iam, label='With default weight function') | ||||||
ax[0].plot(aoi, physical_iam_custom, label='With custom weight function') | ||||||
ax[0].set_xlabel('AOI (degrees)') | ||||||
ax[0].set_ylabel('IAM') | ||||||
ax[0].legend() | ||||||
|
||||||
ax[1].plot(aoi, physical_iam_custom - physical_iam, label='Custom - default') | ||||||
ax[1].set_xlabel('AOI (degrees)') | ||||||
ax[1].set_ylabel('Diff. in IAM') | ||||||
ax[1].legend() | ||||||
plt.tight_layout() | ||||||
plt.show() | ||||||
|
||||||
print("Parameters with default weights: " + str(physical_params)) | ||||||
print("Parameters with custom weights: " + str(physical_params_custom)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,3 +17,5 @@ Incident angle modifiers | |
iam.marion_integrate | ||
iam.schlick | ||
iam.schlick_diffuse | ||
iam.convert | ||
iam.fit |
Uh oh!
There was an error while loading. Please reload this page.