-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Create ivtools #718
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
Create ivtools #718
Changes from 45 commits
f475120
7454e4d
9c5e1a9
593f355
4ab1fed
8a8890f
b72b0b4
921aea8
4f4ad0f
eaee814
d65067b
4744bde
aae2731
e1697b0
e779c7a
b0f0a9d
df33113
bec1915
b64cf57
c8004ff
586a134
6d6733d
98af4ff
26c8f38
1deb148
3c66a1d
8308635
2601dc5
632234e
d975f32
37a362d
c956486
bbd5720
01f0dba
a32bb35
5779b1e
0f45925
58070da
9bf7566
723b454
ad9bc2d
6d5ff04
d506140
2517b33
320ebf9
d66701c
fe4bdfe
6879b36
0328755
cff625b
f8a4860
e8be716
9af20f7
ee19ed1
aa47f48
e55330f
fb3be8c
efe1cfb
8d68c8c
ec6fee5
0270633
6a3735e
227d8b5
16ab69f
11b8dda
f84000d
6cd1095
dde20b5
4c39e2a
eab687a
c02a1ab
d6e21b7
012ef50
b8a713b
3aae45a
35b90e5
fe89a87
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 |
|---|---|---|
|
|
@@ -24,4 +24,5 @@ dependencies: | |
| - shapely # pvfactors dependency | ||
| - siphon # conda-forge | ||
| - pip: | ||
| - nrel-pysam | ||
| - pvfactors==1.0.1 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,4 +24,5 @@ dependencies: | |
| - shapely # pvfactors dependency | ||
| - siphon # conda-forge | ||
| - pip: | ||
| - nrel-pysam | ||
| - pvfactors==1.0.1 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,4 +24,5 @@ dependencies: | |
| - shapely # pvfactors dependency | ||
| - siphon # conda-forge | ||
| - pip: | ||
| - nrel-pysam | ||
| - pvfactors==1.0.1 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -8,8 +8,30 @@ recommend all users of v0.6.3 upgrade to this release. | |
|
|
||
| **Python 2.7 support ended on June 1, 2019**. (:issue:`501`) | ||
|
|
||
| API Changes | ||
| ~~~~~~~~~~~ | ||
|
|
||
|
|
||
| Enhancements | ||
| ~~~~~~~~~~~~ | ||
| * Add `ivtools` module to contain functions for IV model fitting. | ||
| * Add :py:func:`~pvlib.ivtools.fit_sde_sandia`, a simple method to fit the | ||
| single diode equation to an IV curve. | ||
| * Add :py:func:`~pvlib.ivtools.fit_cec_sam`, a wrapper to access the | ||
| model fitting function '6parsolve' from NREL's System Advisor Model. | ||
|
|
||
|
|
||
| Bug fixes | ||
| ~~~~~~~~~ | ||
|
|
||
|
|
||
| Testing | ||
| ~~~~~~~ | ||
|
|
||
|
|
||
| Contributors | ||
| ~~~~~~~~~~~~ | ||
| * Mark Campanellli (:ghuser:`markcampanelli`) | ||
| * Will Holmgren (:ghuser:`wholmgren`) | ||
| * Cliff Hansen (:ghuser:`cwhanse`) | ||
|
|
||
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,334 @@ | ||
| # -*- coding: utf-8 -*- | ||
| """ | ||
| Created on Fri Mar 29 10:34:10 2019 | ||
|
|
||
| @author: cwhanse | ||
| """ | ||
|
|
||
| import numpy as np | ||
|
|
||
|
|
||
| def fit_cec_sam(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, | ||
| gamma_pmp, cells_in_series, temp_ref=25): | ||
| ''' | ||
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Estimates parameters for the CEC single diode model [1] using the SAM SDK. | ||
|
|
||
| Parameters | ||
cwhanse marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ---------- | ||
| celltype : str | ||
| Value is one of 'monoSi', 'multiSi', 'polySi', 'cis', 'cigs', 'cdte', | ||
| 'amorphous' | ||
| v_mp : float | ||
| Voltage at maximum power point [V] | ||
| i_mp : float | ||
| Current at maximum power point [A] | ||
| v_oc : float | ||
| Open circuit voltage [V] | ||
| i_sc : float | ||
| Short circuit current [A] | ||
| alpha_sc : float | ||
| Temperature coefficient of short circuit current [A/C] | ||
| beta_voc : float | ||
cwhanse marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| Temperature coefficient of open circuit voltage [V/C] | ||
| gamma_pmp : float | ||
| Temperature coefficient of power at maximum point point [%/C] | ||
| cells_in_series : int | ||
| Number of cells in series | ||
| temp_ref : float, default 25C | ||
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Reference temperature condition | ||
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Returns | ||
| ------- | ||
| tuple of the following elements: | ||
|
|
||
| a_ref : float | ||
| The product of the usual diode ideality factor ``n`` (unitless), | ||
| number of cells in series ``Ns``, and cell thermal voltage at | ||
| reference conditions [V] | ||
|
|
||
| I_L_ref : float | ||
| The light-generated current (or photocurrent) at reference | ||
| conditions [A] | ||
|
|
||
| I_o_ref : float | ||
| The dark or diode reverse saturation current at reference | ||
| conditions [A] | ||
|
|
||
| R_sh_ref : float | ||
| The shunt resistance at reference conditions, in ohms. | ||
|
|
||
| R_s : float | ||
| The series resistance at reference conditions, in ohms. | ||
|
|
||
| Adjust : float | ||
| The adjustment to the temperature coefficient for short circuit | ||
| current, in percent. | ||
|
|
||
| Raises: | ||
| ImportError if NREL-PySAM is not installed. | ||
| RuntimeError if parameter extraction is not successful. | ||
|
|
||
| Notes | ||
| ----- | ||
| Inputs ``v_mp``, ``v_oc``, ``i_mp`` and ``i_sc`` are assumed to be from a | ||
| single IV curve at constant irradiance and cell temperature. Irradiance is | ||
| not explicitly used by the fitting procedure. The irradiance level at which | ||
| the input IV curve is determined and the specified cell temperature | ||
| ``Tref`` are the reference conditions for the output parameters | ||
| ``I_L_ref``, ``I_o_ref``, ``R_sh_ref``, ``R_s`` and ``Adjust``. | ||
|
||
|
|
||
| References | ||
| ---------- | ||
| [1] A. Dobos, "An Improved Coefficient Calculator for the California | ||
| Energy Commission 6 Parameter Photovoltaic Module Model", Journal of | ||
| Solar Energy Engineering, vol 134, 2012. | ||
| ''' | ||
|
|
||
| try: | ||
| from PySAM import PySSC | ||
| except ImportError as e: | ||
| raise("Requires NREL's PySAM package at " | ||
| "https://pypi.org/project/NREL-PySAM/.") from e | ||
|
|
||
| datadict = {'tech_model': '6parsolve', 'financial_model': 'none', | ||
| 'celltype': celltype, 'Vmp': v_mp, | ||
| 'Imp': i_mp, 'Voc': v_oc, 'Isc': i_sc, 'alpha_isc': alpha_sc, | ||
| 'beta_voc': beta_voc, 'gamma_pmp': gamma_pmp, | ||
| 'Nser': cells_in_series, 'Tref': temp_ref} | ||
|
|
||
| result = PySSC.ssc_sim_from_dict(datadict) | ||
|
Contributor
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. Need to check for fitting success before assigning results, because dictionary entries are apparently missing altogether when it fails causing a if result['cmod_success'] == 1:
# Assign everything
else:
# Assign nan's like in fit_sde_sandia()?This behavior also needs to be tested and documented. Here's how I broke it: v_mp = 0.45
i_mp = 5.25
v_oc = 0.55
i_sc = 5.5
alpha_sc = 0.0005 * i_sc
beta_voc = 0.005 * v_oc
gamma_pmp = 0.0055
cells_in_series = 1
temp_ref = 25
for celltype in ['monoSi', 'multiSi', 'polySi', 'cis', 'cigs', 'cdte', 'amorphous']:
ivtools.fit_cec_sam(celltype, v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc, gamma_pmp, cells_in_series, temp_ref)
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. Thanks! |
||
| if result['cmod_success'] == 1: | ||
| return tuple([result[k] for k in ['Il', 'Io', 'Rsh', 'Rs', 'a', | ||
| 'Adj']]) | ||
| else: | ||
| raise RuntimeError('Parameter estimation failed') | ||
|
|
||
|
|
||
| def fit_sde_sandia(v, i, v_oc=None, i_sc=None, mp=None, vlim=0.2, ilim=0.1): | ||
| """ Fits the single diode equation to an IV curve. | ||
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Parameters | ||
cwhanse marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| ---------- | ||
| v : ndarray | ||
|
||
| 1D array of `float` type containing voltage at each point on the IV | ||
| curve, increasing from 0 to v_oc inclusive [V] | ||
|
|
||
| i : ndarray | ||
| 1D array of `float` type containing current at each point on the IV | ||
| curve, from i_sc to 0 inclusive, of `float` type [A] | ||
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| v_oc : float, default None | ||
| Open circuit voltage [V]. If not provided, ``v_oc`` is taken as | ||
| ``v[-1]``. | ||
|
||
|
|
||
| i_sc : float, default None | ||
| Short circuit current [A]. If not provided, ``i_sc`` is taken as | ||
| ``i[0]``. | ||
|
||
|
|
||
| mp : tuple of float, default None | ||
|
||
| Voltage, current at maximum power point in units of [V], [A]. | ||
| If not provided, values are found from inputs ``v`` and ``i``. | ||
|
||
|
|
||
| vlim : float, default 0.2 | ||
| defines portion of IV curve where the exponential term in the single | ||
|
||
| diode equation can be neglected, i.e. V <= vlim * v_oc [V] | ||
|
|
||
| ilim : float, default 0.1 | ||
| defines portion of the IV curve where the exponential term in the | ||
|
||
| single diode equation is signficant, approximately defined by | ||
| I < (1 - ilim) * i_sc [A] | ||
|
|
||
| Returns | ||
| ------- | ||
| tuple of the following elements: | ||
|
|
||
| photocurrent : float | ||
| photocurrent [A] | ||
|
|
||
| saturation_current : float | ||
| dark (saturation) current [A] | ||
|
|
||
| resistance_shunt : float | ||
| shunt (parallel) resistance, ohm | ||
|
|
||
| resistance_series : float | ||
| series resistance, ohm | ||
|
|
||
| nNsVth : float | ||
| product of thermal voltage ``Vth`` [V], diode ideality factor | ||
| ``n``, and number of series cells ``Ns`` | ||
|
|
||
| Raises: | ||
| RuntimeError if parameter extraction is not successful. | ||
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| Notes | ||
| ----- | ||
| Inputs ``v``, ``i``, ``v_mp``, ``v_oc``, ``i_mp`` and ``i_sc`` are assumed | ||
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| to be from a single IV curve at constant irradiance and cell temperature. | ||
|
|
||
| :py:func:`fit_sde_sandia` obtains values for the five parameters for the | ||
| single diode equation [1]: | ||
|
|
||
| .. math:: | ||
|
|
||
| I = IL - I0*[exp((V+I*Rs)/(nNsVth))-1] - (V + I*Rs)/Rsh | ||
|
|
||
| :py:func:`pvsystem.singlediode` for definition of the parameters. | ||
|
|
||
| The extraction method [2] proceeds in four steps: | ||
| 1) In the single diode equation, replace Rsh = 1/Gp and re-arrange | ||
|
||
|
|
||
| .. math:: | ||
|
|
||
| I = IL - I0*exp((V+I*Rs)/(nNsVth) - 1) - (V + I*Rs)/Rsh | ||
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| .. math:: | ||
|
|
||
| I = IL/(1+Gp*Rs) - (Gp*V)/(1+Gp*Rs) - | ||
| I0/(1+Gp*Rs)*exp((V+I*Rs)/(nNsVth) - 1) | ||
|
|
||
| 2) fit the linear portion of the IV curve defined as V <= vlim * v_oc, | ||
| where I0/(1+Gp*Rs)*exp((V+I*Rs)/(nNsVth) - 1) ≈ 0 | ||
|
|
||
| .. math:: | ||
|
|
||
| I ~ IL/(1+Gp*Rs) - (Gp*V)/(1+Gp*Rs) = beta0 + beta1*V | ||
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| 3) fit the exponential portion of the IV curve defined by | ||
| beta0 + beta1*V - I > ilim * i_sc, where | ||
| exp((V+I*Rs)/(nNsVth)) >> 1 so that | ||
| exp((V+I*Rs)/(nNsVth)) - 1 ≈ exp((V+I*Rs)/(nNsVth)) | ||
|
|
||
| .. math:: | ||
|
|
||
| log(beta0 - beta1*V - I) ~ log((I0)/(1+Gp*Rs)) + (V)/(nNsVth) + | ||
| (Rs*I)/(nNsVth) = beta2 + beta3*V + beta4*I | ||
|
|
||
| 4) calculate values for ``IL, I0, Rs, Rsh,`` and ``nNsVth`` from the | ||
| regression coefficents beta0, beta1, beta3 and beta4. | ||
|
|
||
|
|
||
| References | ||
| ---------- | ||
| [1] S.R. Wenham, M.A. Green, M.E. Watt, "Applied Photovoltaics" ISBN | ||
| 0 86758 909 4 | ||
| [2] C. B. Jones, C. W. Hansen, Single Diode Parameter Extraction from | ||
| In-Field Photovoltaic I-V Curves on a Single Board Computer, 46th IEEE | ||
| Photovoltaic Specialist Conference, Chicago, IL, 2019 | ||
| """ | ||
|
|
||
| # If not provided, extract v_oc, i_sc, v_mp and i_mp from the IV curve data | ||
| if v_oc is None: | ||
| v_oc = v[-1] | ||
| if i_sc is None: | ||
| i_sc = i[0] | ||
| if mp is not None: | ||
| v_mp, i_mp = mp | ||
| else: | ||
| v_mp, i_mp = _find_mp(v, i) | ||
|
|
||
| # Find beta0 and beta1 from linear portion of the IV curve | ||
| beta0, beta1 = _find_beta0_beta1(v, i, vlim, v_oc) | ||
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| if not np.isnan(beta0): | ||
| beta3, beta4 = _find_beta3_beta4(v, i, beta0, beta1, ilim, i_sc) | ||
|
|
||
| # calculate single diode parameters from regression coefficients | ||
| result = _calculate_sde_parameters(beta0, beta1, beta3, beta4, v_mp, i_mp, | ||
| v_oc) | ||
|
|
||
| if result is None: | ||
| raise RuntimeError("Parameter extraction failed. Try increasing the" | ||
| "number of data points in the IV curve") | ||
| else: | ||
| IL, I0, Rsh, Rs, nNsVth = result | ||
| return IL, I0, Rsh, Rs, nNsVth | ||
|
||
|
|
||
|
|
||
| def _find_mp(v, i): | ||
| """ | ||
| Finds voltage and current at maximum power point. | ||
|
|
||
| Parameters | ||
| ---------- | ||
| v : ndarray | ||
| 1D array containing voltage at each point on the IV curve, increasing | ||
| from 0 to v_oc inclusive, of `float` type [V] | ||
|
|
||
| i : ndarray | ||
| 1D array containing current at each point on the IV curve, decreasing | ||
| from i_sc to 0 inclusive, of `float` type [A] | ||
|
|
||
| Returns | ||
| ------- | ||
| v, i : tuple | ||
| voltage ``v`` and current ``i`` at the maximum power point [V], [A] | ||
| """ | ||
| p = v * i | ||
| idx = np.argmax(p) | ||
| return v[idx], i[idx] | ||
|
|
||
|
|
||
| def _calc_I0(IL, I, V, Gp, Rs, beta3): | ||
| return (IL - I - Gp * V - Gp * Rs * I) / np.exp(beta3 * (V + Rs * I)) | ||
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| def _find_beta0_beta1(v, i, vlim, v_oc): | ||
| # Get intercept and slope of linear portion of IV curve. | ||
| # Start with V =< vlim * v_oc, extend by adding points until slope is | ||
| # negative (downward). | ||
| beta0 = np.nan | ||
| beta1 = np.nan | ||
| idx = np.searchsorted(v, vlim * v_oc) | ||
| while idx <= len(v): | ||
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| coef = np.polyfit(v[:idx], i[:idx], deg=1) | ||
| if coef[0] < 0: | ||
| # intercept term | ||
| beta0 = coef[1].item() | ||
| # sign change of slope to get positive parameter value | ||
| beta1 = -coef[0].item() | ||
| break | ||
| else: | ||
| idx += 1 | ||
| return beta0, beta1 | ||
|
|
||
|
|
||
| def _find_beta3_beta4(v, i, beta0, beta1, ilim, i_sc): | ||
| # Subtract the IV curve from the linear fit. | ||
| y = beta0 - beta1 * v - i | ||
| x = np.array([np.ones_like(v), v, i]).T | ||
| # Select points where y > ilim * i_sc to regress log(y) onto x | ||
| result = np.linalg.lstsq(x[y > ilim * i_sc], np.log(y[y > ilim * i_sc]), | ||
| rcond=None) | ||
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| coef = result[0] | ||
| beta3 = coef[1].item() | ||
| beta4 = coef[2].item() | ||
| return beta3, beta4 | ||
|
|
||
|
|
||
| def _calculate_sde_parameters(beta0, beta1, beta3, beta4, v_mp, i_mp, v_oc): | ||
| failed = False | ||
| if any(np.isnan([beta0, beta1, beta3, beta4])): | ||
| failed = True | ||
|
||
| else: | ||
| nNsVth = 1.0 / beta3 | ||
| Rs = beta4 / beta3 | ||
| Gp = beta1 / (1.0 - Rs * beta1) | ||
| Rsh = 1.0 / Gp | ||
| IL = (1 + Gp * Rs) * beta0 | ||
| # calculate I0 | ||
| I0_v_mp = _calc_I0(IL, i_mp, v_mp, Gp, Rs, beta3) | ||
| I0_v_oc = _calc_I0(IL, 0, v_oc, Gp, Rs, beta3) | ||
cwhanse marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if (I0_v_mp > 0) and (I0_v_oc > 0): | ||
| I0 = 0.5 * (I0_v_mp + I0_v_oc) | ||
| elif (I0_v_mp > 0): | ||
| I0 = I0_v_mp | ||
| elif (I0_v_oc > 0): | ||
| I0 = I0_v_oc | ||
| else: | ||
| failed = True | ||
| if failed: | ||
| result = None | ||
| else: | ||
| result = (IL, I0, Rsh, Rs, nNsVth) | ||
| return result | ||
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.
Maybe
fit_single_diode_cec_samto more closely mimic the sandia function?Uh oh!
There was an error while loading. Please reload this page.
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.
I kinda like the single-diode equation (SDE) vs. single-diode model (SDM) distinction, and I don’t mind the abbreviations if they are expanded in the docstring. This leads to something succinct and descriptive like:
fit_sde_sandiafit_sdm_cec_samaction_thing[_variant]_sourceI don’t know if you want to add a variant to the SDE (to cover potential variants, such as with a reverse breakdown component). Also, the variant and source could repeat in some cases, leading to some stuttering.
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.
@markcampanelli good suggestion for naming patterns. There are two different applications here: fitting the SD equation to an IV curve, and fitting a SD model (a set of equations) to some data (in the case of the CEC model and the SAM method, datasheet information). I changed
sdetosingle_diodein one function name, but I don't care forsingle_diode_equationandsingle_diode_modelin the names.I'm going to open a new issue to extend the single diode documentation to lay out the terminology: single diode equation, single diode model, 5 parameters vs. model parameters.
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.
Works for me.