Skip to content

coefficient estimation method following DeSoto(2006) #784

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

Merged
merged 50 commits into from
Oct 31, 2019
Merged
Show file tree
Hide file tree
Changes from 41 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
c5287ea
function getparams_desoto added to pvsystem module. This commit is ju…
tylunel Aug 21, 2019
5a39fea
getparams_desoto moved from pvsystem to singlediode.
tylunel Aug 21, 2019
7b24201
- Modification of getparams_from_specs so as it follows better the pr…
tylunel Aug 22, 2019
c16f1d0
- Bug corrected in DeSoto(2006) procedure
tylunel Aug 22, 2019
e7ef571
- test_getparams_from_specs_desoto() finished
tylunel Aug 23, 2019
cdb2a73
Not sure of all changes brought by this commit because of holidays.
tylunel Sep 9, 2019
0cfe13f
- function '_parse_raw_sam_df' modified. The parser engine is now def…
tylunel Sep 10, 2019
0c27829
- ModelChain attribute 'diode_params' transformed from tuple containi…
tylunel Sep 11, 2019
d8653cd
- singlediode.get_params_from_specs_desoto() output changed. 'a_ref' …
tylunel Sep 12, 2019
bbb4580
read_epw changed. A line has been added to convert the precipitable w…
tylunel Sep 23, 2019
6b8fadf
- read_epw changed. If condition added to make the conversion only in…
tylunel Sep 30, 2019
121cb06
* Pull and Merge done with upstream pvlib/pvlib-python: fork up to date
tylunel Oct 2, 2019
47e1be4
- argument diode_params changed from tuple to pd.DataFrame
tylunel Oct 2, 2019
c1b62c4
- get_params_from_specs_desoto removed from singlediode.py
tylunel Oct 2, 2019
4e62d1a
- function fit_sdm_desoto added. Still need to be formatted
tylunel Oct 2, 2019
747ed48
- change on type of self.diode_params removed. Go check on branch dio…
tylunel Oct 2, 2019
9ea2ed8
- Function 'fit_sdm_desoto' cleaned and variables names named as in '…
tylunel Oct 2, 2019
bd39dbd
- all changes made on other files than ivtools.py removed (cleaning f…
tylunel Oct 2, 2019
ae5c8be
- other differences cleaned
tylunel Oct 2, 2019
2d4f8f3
- renaming of one variable and minor documentation modifications
tylunel Oct 2, 2019
485dae6
- Beginning of writting of test_fit_sdm_desoto. Coverage around 90-95…
tylunel Oct 4, 2019
77d88a2
-minor format changes
tylunel Oct 4, 2019
6fcafc7
- changes made according to feedbacks of markcampanelli
tylunel Oct 4, 2019
2d53d5d
- some cleaning on fit_sdm_desoto to make it more readable
tylunel Oct 4, 2019
fdbf5ec
- minor code cleaning
tylunel Oct 4, 2019
0e1b4b3
- check on importation of scipy removed
tylunel Oct 7, 2019
d2d8c45
- minor cleaning
tylunel Oct 7, 2019
a70debb
- attempt to reach 100% coverage
tylunel Oct 8, 2019
e01f262
- description added in docs/sphinx/source/whatsnew/v0.7.0.rst
tylunel Oct 8, 2019
17f8617
- changes made according to cwhanse review. Except alpha_sc and beta_…
tylunel Oct 10, 2019
e14ad40
- minor correction and adaptation of test
tylunel Oct 10, 2019
32049b0
- sign correction on 3rd equation
tylunel Oct 11, 2019
bfe3994
- changes on units of alpha_sc and beta_voc inputs. Now in A/K and V/…
tylunel Oct 16, 2019
7ff8a96
- other line of test added for better coverage
tylunel Oct 16, 2019
7f08d80
- some changes to try feedbacks of cwhanse and markcampanelli, not fi…
tylunel Oct 17, 2019
27ab961
-OptimizeResult added in output
tylunel Oct 18, 2019
79ef920
- includes all feedbacks made on the 21/10, except moving of pv_fct()…
tylunel Oct 21, 2019
5e9cf8d
- pv_fct moved out of the fit_sdm_desoto function and renamed in _sys…
tylunel Oct 21, 2019
c02e74f
- minor modification: Boltzmann k given in specs to avoid import of s…
tylunel Oct 22, 2019
fa41eb8
- cleaning and minor modifications to docstring
tylunel Oct 22, 2019
753d312
- references added to docstring in _system_of_equations
tylunel Oct 22, 2019
b1b405c
- modification for removing last lint error
tylunel Oct 22, 2019
22d613c
- modification for removing last lint error
tylunel Oct 22, 2019
ae19a68
- modification for removing lint error
tylunel Oct 22, 2019
6913a52
- modif for removing of lint error
tylunel Oct 22, 2019
05051e1
- integration of adriesse suggestions
tylunel Oct 28, 2019
d4772c6
Merge branch 'master' into fit_sdm_desoto
cwhanse Oct 29, 2019
d821494
- adding of tylunel to the list of contributors
tylunel Oct 29, 2019
9d23552
Merge branch 'fit_sdm_desoto' of https://github.com/tylunel/pvlib-pyt…
tylunel Oct 29, 2019
5037da6
- adding of mark requires_scipy
tylunel Oct 29, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/sphinx/source/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ Functions for fitting PV models

ivtools.fit_sde_sandia
ivtools.fit_sdm_cec_sam
ivtools.fit_sdm_desoto

Other
-----
Expand Down
4 changes: 3 additions & 1 deletion docs/sphinx/source/whatsnew/v0.7.0.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.. _whatsnew_0700:
.. _whatsnew_0700:

v0.7.0 (MONTH DAY, YEAR)
------------------------
Expand Down Expand Up @@ -96,6 +96,8 @@ Enhancements
the single diode equation to an IV curve.
* Add :py:func:`~pvlib.ivtools.fit_sdm_cec_sam`, a wrapper for the CEC single
diode model fitting function '6parsolve' from NREL's System Advisor Model.
* Add :py:func:`~pvlib.ivtools.fit_sdm_desoto`, a method to fit the De Soto single
diode model to the typical specifications given in manufacturers datasheets.

Bug fixes
~~~~~~~~~
Expand Down
197 changes: 197 additions & 0 deletions pvlib/ivtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,142 @@ def fit_sde_sandia(voltage, current, v_oc=None, i_sc=None, v_mp_i_mp=None,
v_oc)


def fit_sdm_desoto(v_mp, i_mp, v_oc, i_sc, alpha_sc, beta_voc,
cells_in_series, EgRef=1.121, dEgdT=-0.0002677,
temp_ref=25, irrad_ref=1000):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a function parameter root_kwargs which accepts a dictionary of arguments to pass on to root?

"""
Calculates the parameters for the De Soto single diode model using the
procedure described in [1]. This procedure has the advantage of
using common specifications given by manufacturers in the
datasheets of PV modules.
The solution is found using the scipy.optimize.root() function,
with the corresponding default solver method 'hybr'.
No restriction is put on the fit variables, i.e. series
or shunt resistance could go negative. Nevertheless, if it happens,
check carefully the inputs and their units; alpha_sc and beta_voc are
often given in %/K in manufacturers datasheets and should be given
in A/K and V/K here.
The parameters returned by this function can be used by
pvsystem.calcparams_desoto to calculate the values at different
irradiance and cell temperature.

Parameters
----------
v_mp: float
Module voltage at the maximum-power point at reference conditions [V].
i_mp: float
Module current at the maximum-power point at reference conditions [A].
v_oc: float
Open-circuit voltage at reference conditions [V].
i_sc: float
Short-circuit current at reference conditions [A].
alpha_sc: float
The short-circuit current (i_sc) temperature coefficient of the
module [A/K].
beta_voc: float
The open-circuit voltage (v_oc) temperature coefficient of the
module [V/K].
cells_in_series: float
Number of cell in the module.
EgRef: float, default 1.121 eV - value for silicon
Energy of bandgap of semi-conductor used [eV]
dEgdT: float, default -0.0002677 - value for silicon
Variation of bandgap according to temperature [eV/K]
temp_ref: float, default 25
Reference temperature condition [C]
irrad_ref: float, default 1000
Reference irradiance condition [W/m2]

Returns
-------
Tuple of the following elements:

* Dictionary with the following elements:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The other two fitting functions return tuples of values rather than a dictionary. Is there a trend one way or another?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree that it’s good to be consistent. However, I very much prefer interfaces where order does not matter, because this ordering is a form of complexity, which is harder to maintain/extend without breakages. Also, the dictionary output is perhaps best paired with keyword-only inputs to subsequent functions/methods, but it still works with pvlib’s current mixture of named positional and keyword arguments. However, it would not be compatible with Python 3.8’s new positional-only arguments.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think there's a right answer. It would be most helpful if people could provide some good references for pros/cons of modern scientific python design patterns. I am not aware of any references that tackle non-trivial cases.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

24 minutes into this keynote talk on simplicity, Rich Hickey (creator of Clojure) discusses the complexity of ordering things in lists:

https://m.youtube.com/watch?v=rI8tNMsozo0&t=3s

This is not gospel of course, and indeed ordering can provide performance gains, etc. (which is why dictionaries are now ordered in CPython, which I personally treat as an implementation detail to not to be relied upon).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's address consistency among functions returns in a follow-on PR. I can't say I had a pattern in mind when I started ivtools.py

I_L_ref: float
Light-generated current at reference conditions [A]
I_o_ref: float
Diode saturation current at reference conditions [A]
R_s: float
Series resistance [ohms]
R_sh_ref: float
Shunt resistance at reference conditions [ohms].
a_ref: float
Modified ideality factor at reference conditions.
The product of the usual diode ideality factor (n, unitless),
number of cells in series (Ns), and cell thermal voltage at
specified effective irradiance and cell temperature.
alpha_sc: float
The short-circuit current (i_sc) temperature coefficient of the
module [A/K].
EgRef: float
Energy of bandgap of semi-conductor used [eV]
dEgdT: float
Variation of bandgap according to temperature [eV/K]
irrad_ref: float
Reference irradiance condition [W/m2]
temp_ref: float
Reference temperature condition [C]
* scipy.optimize.OptimizeResult
Optimization result of scipy.optimize.root().
See scipy.optimize.OptimizeResult for more details.

References
----------
[1] W. De Soto et al., "Improvement and validation of a model for
photovoltaic array performance", Solar Energy, vol 80, pp. 78-88,
2006.

[2] John A Duffie, William A Beckman, "Solar Engineering of Thermal
Processes", Wiley, 2013
"""

try:
from scipy.optimize import root
from scipy import constants
except ImportError:
raise ImportError("The fit_sdm_desoto function requires scipy.")

# Constants
k = constants.value('Boltzmann constant in eV/K')
Tref = temp_ref + 273.15 # [K]

# initial guesses of variables for computing convergence:
# Values are taken from [2], p753
Rsh_i = 100.0
a_i = 1.5*k*Tref*cells_in_series
IL_i = i_sc
Io_i = i_sc * np.exp(-v_oc/a_i)
Rs_i = (a_i*np.log1p((IL_i-i_mp)/Io_i) - v_mp)/i_mp
# params_i : initial values vector
params_i = np.array([IL_i, Io_i, a_i, Rsh_i, Rs_i])

# specs of module
specs = ((i_sc, v_oc, i_mp, v_mp, beta_voc, alpha_sc, EgRef, dEgdT,
Tref, k),)

# computing with system of equations described in [1]
optimize_result = root(_system_of_equations, x0=params_i, args=specs)

if optimize_result.success:
sdm_params = optimize_result.x
else:
raise RuntimeError('Parameter estimation failed:\n'
+ optimize_result.message)

# results
return ({'I_L_ref': sdm_params[0],
'I_o_ref': sdm_params[1],
'a_ref': sdm_params[2],
'R_sh_ref': sdm_params[3],
'R_s': sdm_params[4],
'alpha_sc': alpha_sc,
'EgRef': EgRef,
'dEgdT': dEgdT,
'irrad_ref': irrad_ref,
'temp_ref': temp_ref},
optimize_result)


def _find_mp(voltage, current):
"""
Finds voltage and current at maximum power point.
Expand Down Expand Up @@ -348,3 +484,64 @@ def _calculate_sde_parameters(beta0, beta1, beta3, beta4, v_mp, i_mp, v_oc):
else: # I0_voc > 0
I0 = I0_voc
return (IL, I0, Rsh, Rs, nNsVth)


def _system_of_equations(params, specs):
"""Evaluates the systems of equations used to solve for the single
diode equation parameters. Function designed to be used by
scipy.optimize.root() in fit_sdm_desoto().

Parameters
----------
params: ndarray
Array with parameters of the De Soto single diode model. Must be
given in the following order: IL, Io, a, Rsh, Rs
specs: tuple
Specifications of pv module given by manufacturer. Must be given
in the following order: Isc, Voc, Imp, Vmp, betaoc, alphasc

Returns
-------
system of equations to solve with scipy.optimize.root().


References
----------
[1] W. De Soto et al., "Improvement and validation of a model for
photovoltaic array performance", Solar Energy, vol 80, pp. 78-88,
2006.

[2] John A Duffie, William A Beckman, "Solar Engineering of Thermal
Processes", Wiley, 2013
"""

# six input known variables
Isc, Voc, Imp, Vmp, betaoc, alphasc, EgRef, dEgdT, Tref, k = specs

# five parameters vector to find
IL, Io, a, Rsh, Rs = params

# five equation vector
y = [0, 0, 0, 0, 0]

# 1st equation - short-circuit - eq(3) in [1]
y[0] = Isc - IL + Io*np.expm1(Isc*Rs/a) + Isc*Rs/Rsh
# 2nd equation - open-circuit Tref - eq(4) in [1]
y[1] = -IL + Io*np.expm1(Voc/a) + Voc/Rsh
# 3rd equation - Imp & Vmp - eq(5) in [1]
y[2] = Imp - IL + Io*np.expm1((Vmp+Imp*Rs)/a) + \
(Vmp+Imp*Rs)/Rsh
# 4th equation - Pmp derivated=0 - eq23.2.6 in [2]
# caution: eq(6) in [1] has a sign error
y[3] = Imp - Vmp * ((Io/a)*np.exp((Vmp+Imp*Rs)/a) + 1.0/Rsh) / \
(1.0 + (Io*Rs/a)*np.exp((Vmp+Imp*Rs)/a) + Rs/Rsh)
# 5th equation - open-circuit T2 - eq (4) at temperature T2 in [1]
T2 = Tref + 2
Voc2 = (T2 - Tref)*betaoc + Voc # eq (7) in [1]
a2 = a*T2/Tref # eq (8) in [1]
IL2 = IL + alphasc*(T2-Tref) # eq (11) in [1]
Eg2 = EgRef*(1 + dEgdT*(T2-Tref)) # eq (10) in [1]
Io2 = Io * (T2/Tref)**3 * np.exp(1/k * (EgRef/Tref-Eg2/T2)) # eq (9)
y[4] = -IL2 + Io2*np.expm1(Voc2/a2) + Voc2/Rsh # eq (4) at T2

return y
28 changes: 28 additions & 0 deletions pvlib/test/test_ivtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,34 @@ def test_fit_sdm_cec_sam(get_cec_params_cansol_cs5p_220p):
cells_in_series=1, temp_ref=25)


@requires_scipy
def test_fit_sdm_desoto():
result, _ = ivtools.fit_sdm_desoto(v_mp=31.0, i_mp=8.71, v_oc=38.3,
i_sc=9.43, alpha_sc=0.005658,
beta_voc=-0.13788,
cells_in_series=60)
result_expected = {'I_L_ref': 9.452324509050774,
'I_o_ref': 3.2246097466679494e-10,
'a_ref': 1.5912875522463978,
'R_sh_ref': 125.79869968976132,
'R_s': 0.2978148476600744,
'alpha_sc': 0.005658,
'EgRef': 1.121,
'dEgdT': -0.0002677,
'irrad_ref': 1000,
'temp_ref': 25}
assert np.allclose(pd.Series(result), pd.Series(result_expected),
rtol=1e-4)


def test_fit_sdm_desoto_failure():
with pytest.raises(RuntimeError) as exc:
ivtools.fit_sdm_desoto(v_mp=31.0, i_mp=8.71, v_oc=38.3, i_sc=9.43,
alpha_sc=0.005658, beta_voc=-0.13788,
cells_in_series=10)
assert ('Parameter estimation failed') in str(exc.value)


@pytest.fixture
def get_bad_iv_curves():
# v1, i1 produces a bad value for I0_voc
Expand Down