-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Add Prilliman et al transience model to pvlib.temperature #1391
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 12 commits
7619d04
ff2c006
95bff13
e5f83db
f92e73a
92afe49
56519e2
c384415
d92250c
985baba
01c4afc
188e3df
be1704e
5e2c6da
2197f3c
3095afb
7cecbbe
5fda720
b8d5479
0a0a1f3
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 |
|---|---|---|
|
|
@@ -7,6 +7,10 @@ | |
| import pandas as pd | ||
| from pvlib.tools import sind | ||
| from pvlib._deprecation import warn_deprecated | ||
| from pvlib.clearsky import _get_sample_intervals | ||
| import scipy | ||
| import warnings | ||
|
|
||
|
|
||
| TEMPERATURE_MODEL_PARAMETERS = { | ||
| 'sapm': { | ||
|
|
@@ -821,3 +825,101 @@ def noct_sam(poa_global, temp_air, wind_speed, noct, module_efficiency, | |
| heat_loss = 1 - module_efficiency / tau_alpha | ||
| wind_loss = 9.5 / (5.7 + 3.8 * wind_adj) | ||
| return temp_air + cell_temp_init * heat_loss * wind_loss | ||
|
|
||
|
|
||
| def prilliman(temp_cell, wind_speed, unit_mass=11.1, coefficients=None): | ||
| """ | ||
| Smooth short-term cell temperature transients using the Prilliman model. | ||
|
|
||
| The Prilliman et al. model [1]_ applies a weighted moving average to | ||
wholmgren marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| the output of a steady-state cell temperature model to account for | ||
| a module's thermal inertia by smoothing the cell temperature's | ||
| response to changing weather conditions. | ||
|
|
||
| .. warning:: | ||
| This implementation requires the time series inputs to be regularly | ||
| sampled in time with frequency less than 20 minutes. Data with | ||
| irregular time steps should be resampled prior to using this function. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| temp_cell : pandas Series with DatetimeIndex | ||
kandersolar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Cell temperature modeled with steady-state assumptions. [C] | ||
|
|
||
| wind_speed : pandas Series | ||
kandersolar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Wind speed, adjusted to correspond to array height [m/s] | ||
|
|
||
| unit_mass : float, default 11.1 | ||
| Total mass of module divided by its one-sided surface area [kg/m^2] | ||
|
|
||
| coefficients : 4-element list-like, optional | ||
| Values for coefficients a_0 through a_3, see Eq. 9 of [1]_ | ||
|
|
||
| Returns | ||
| ------- | ||
| temp_cell : pandas Series | ||
kandersolar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Smoothed version of the input cell temperature [C] | ||
|
|
||
|
Member
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. Add here that the original data is returned if too coarsely sampled. |
||
| Notes | ||
| ----- | ||
| This smoothing model was developed and validated using the SAPM | ||
| cell temperature model for the steady-state input. | ||
|
|
||
| References | ||
|
Member
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. One more suggestion: explain in Notes how |
||
| ---------- | ||
| .. [1] M. Prilliman, J. S. Stein, D. Riley and G. Tamizhmani, | ||
| "Transient Weighted Moving-Average Model of Photovoltaic Module | ||
| Back-Surface Temperature," IEEE Journal of Photovoltaics, 2020. | ||
| :doi:`10.1109/JPHOTOV.2020.2992351` | ||
| """ | ||
|
|
||
| time_step, window = _get_sample_intervals(temp_cell.index, 20) | ||
kandersolar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| if time_step >= 20: | ||
| warnings.warn("temperature.prilliman only applies smoothing when " | ||
| "the sampling interval is shorter than 20 minutes " | ||
| f"(input sampling interval: {time_step} minutes)") | ||
| # too coarsely sampled for smoothing to be relevant | ||
|
Member
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 think a warning here would be appropriate
Member
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. Maybe extend the message to say that the return values are the original temperature? |
||
| return temp_cell | ||
|
|
||
| window = min(window, # time series > 20 minutes total | ||
| len(temp_cell)) # time series < 20 minutes total | ||
kandersolar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| # prefix with NaNs so that the rolling window is "full", | ||
| # even for the first actual value: | ||
| prefix = np.full(window, np.nan) | ||
| temp_cell_prefixed = np.append(prefix, temp_cell.values) | ||
|
|
||
| # get one row per 20-minute window | ||
kandersolar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| H = scipy.linalg.hankel(np.arange(window), | ||
| np.arange(window - 1, len(temp_cell_prefixed))) | ||
| subsets = temp_cell_prefixed[H].T | ||
|
|
||
| # calculate weights for the values in each window | ||
| if coefficients is not None: | ||
| a = coefficients | ||
| else: | ||
| # values from [1], Table II | ||
| a = [0.0046, 0.00046, -0.00023, -1.6e-5] | ||
|
|
||
| wind_speed = wind_speed.values | ||
| P = a[0] + a[1]*wind_speed + a[2]*unit_mass + a[3]*wind_speed*unit_mass | ||
| timedeltas = np.arange(window, 0, -1) * (time_step*60) # s to min | ||
kandersolar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| weights = np.exp(-P[:, np.newaxis] * timedeltas) | ||
|
|
||
kandersolar marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| # set weights corresponding to the prefix values to zero; otherwise the | ||
| # denominator of the weighted average below would be wrong | ||
| mask_idx = np.triu_indices(window) | ||
| np.fliplr(weights)[mask_idx] = 0 | ||
|
|
||
| # change the first row of weights from zero to nan -- this is a | ||
| # trick to prevent div by zero warning when dividing by summed weights | ||
| weights[0, :] = np.nan | ||
|
|
||
| # finally, take the weighted average of each window | ||
| numerator = np.nansum(subsets[:-1] * weights, axis=1) | ||
| denominator = np.sum(weights, axis=1) | ||
kandersolar marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| smoothed = numerator / denominator | ||
|
Member
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. @kanderso-nrel this line emitted a warning despite the comment on lines 967-969 that suggested otherwise. Is this a version-specific issue? My versions are below...
Member
Author
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. Hmm, the only way I can reproduce this is if there is a stretch of NaN in the input lasting longer than 20 minutes -- is that true for your case? That comment is referring to a div by zero warning cause by a quirk of this implementation (no weights for the very first value), not a div by zero warning caused by nans in the input.
Member
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. Ah, thanks. Yes, I had 38 minutes of NaN temperatures. I filled only the middle point with a valid number and the warning disappeared. |
||
| smoothed[0] = temp_cell.values[0] | ||
| smoothed = pd.Series(smoothed, index=temp_cell.index) | ||
| return smoothed | ||
Uh oh!
There was an error while loading. Please reload this page.