Skip to content

add v_from_i, improve singlediode speed by 2x #190

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 7 commits into from
Jun 14, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 4 additions & 0 deletions docs/sphinx/source/whatsnew/v0.3.3.txt
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ Enhancements
(:issue:`169`)
* Add ``__repr__`` method to PVSystem, LocalizedPVSystem, ModelChain,
SingleAxisTracker, Location. (:issue:`142`)
* Add ``v_from_i`` function for solving the single diode model.
(:issue:`190`)
* Improve speed of ``singlediode`` function by using ``v_from_i`` to
determine ``v_oc``. Speed is ~2x faster. (:issue:`190`)


Bug fixes
Expand Down
80 changes: 69 additions & 11 deletions pvlib/pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ def __init__(self,

# needed for tying together Location and PVSystem in LocalizedPVSystem
super(PVSystem, self).__init__(**kwargs)

def __repr__(self):
return ('PVSystem with tilt:' + str(self.surface_tilt) +
' and azimuth: ' + str(self.surface_azimuth) +
Expand Down Expand Up @@ -443,7 +443,7 @@ def __init__(self, pvsystem=None, location=None, **kwargs):

# get and combine attributes from the pvsystem and/or location
# with the rest of the kwargs

if pvsystem is not None:
pv_dict = pvsystem.__dict__
else:
Expand All @@ -459,7 +459,7 @@ def __init__(self, pvsystem=None, location=None, **kwargs):
list(kwargs.items()))

super(LocalizedPVSystem, self).__init__(**new_kwargs)

def __repr__(self):
return ('LocalizedPVSystem with tilt:' + str(self.surface_tilt) +
' and azimuth: ' + str(self.surface_azimuth) +
Expand Down Expand Up @@ -1395,15 +1395,16 @@ def singlediode(module, photocurrent, saturation_current,
i_sc = i_from_v(resistance_shunt, resistance_series, nNsVth, 0.01,
saturation_current, photocurrent)

# Find open circuit voltage using Lambert W
v_oc = v_from_i(resistance_shunt, resistance_series, nNsVth, 0.0,
saturation_current, photocurrent)

params = {'r_sh': resistance_shunt,
'r_s': resistance_series,
'nNsVth': nNsVth,
'i_0': saturation_current,
'i_l': photocurrent}

__, v_oc = _golden_sect_DataFrame(params, 0, module['V_oc_ref']*1.6,
_v_oc_optfcn)

p_mp, v_mp = _golden_sect_DataFrame(params, 0, module['V_oc_ref']*1.14,
_pwr_optfcn)

Expand Down Expand Up @@ -1534,13 +1535,70 @@ def _pwr_optfcn(df, loc):
return I*df[loc]


def _v_oc_optfcn(df, loc):
def v_from_i(resistance_shunt, resistance_series, nNsVth, current,
saturation_current, photocurrent):
'''
Function to find the open circuit voltage from ``i_from_v``.
Calculates voltage from current per Eq 3 Jain and Kapoor 2004 [1].

Parameters
----------
resistance_shunt : float or Series
Shunt resistance in ohms under desired IV curve conditions.
Often abbreviated ``Rsh``.

resistance_series : float or Series
Series resistance in ohms under desired IV curve conditions.
Often abbreviated ``Rs``.

nNsVth : float or Series
The product of three components. 1) The usual diode ideal factor
(n), 2) the number of cells in series (Ns), and 3) the cell
thermal voltage under the desired IV curve conditions (Vth). The
thermal voltage of the cell (in volts) may be calculated as
``k*temp_cell/q``, where k is Boltzmann's constant (J/K),
temp_cell is the temperature of the p-n junction in Kelvin, and
q is the charge of an electron (coulombs).

current : float or Series
The current in amperes under desired IV curve conditions.

saturation_current : float or Series
Diode saturation current in amperes under desired IV curve
conditions. Often abbreviated ``I_0``.

photocurrent : float or Series
Light-generated current (photocurrent) in amperes under desired
IV curve conditions. Often abbreviated ``I_L``.

Returns
-------
current : np.array

References
----------
[1] A. Jain, A. Kapoor, "Exact analytical solutions of the
parameters of real solar cells using Lambert W-function", Solar
Energy Materials and Solar Cells, 81 (2004) 269-277.
'''
I = -abs(i_from_v(df['r_sh'], df['r_s'], df['nNsVth'],
df[loc], df['i_0'], df['i_l']))
return I
try:
from scipy.special import lambertw
except ImportError:
raise ImportError('This function requires scipy')

Rsh = resistance_shunt
Rs = resistance_series
I0 = saturation_current
IL = photocurrent
I = current

argW = I0 * Rsh / nNsVth * np.exp(Rsh *(-I + IL + I0) / nNsVth)
lambertwterm = lambertw(argW)

# Eqn. 3 in Jain and Kapoor, 2004

V = -I*(Rs + Rsh) + IL*Rsh - nNsVth*lambertwterm + I0*Rsh

return V.real


def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage,
Expand Down
26 changes: 18 additions & 8 deletions pvlib/test/test_pvsystem.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,16 @@ def test_PVSystem_calcparams_desoto():
assert_almost_equals(nNsVth, 0.473)


def test_v_from_i():
output = pvsystem.v_from_i(20, .1, .5, 3, 6e-7, 7)
assert_almost_equals(7.5049875193450521, output, 5)


def test_v_from_i_big():
output = pvsystem.v_from_i(500, 10, 4.06, 0, 6e-10, 1.2)
assert_almost_equals(86.320000493521079, output, 5)


def test_i_from_v():
output = pvsystem.i_from_v(20, .1, .5, 40, 6e-7, 7)
assert_almost_equals(-299.746389916, output, 5)
Expand Down Expand Up @@ -263,16 +273,16 @@ def test_singlediode_floats():
module = 'Example_Module'
module_parameters = sam_data['cecmod'][module]
out = pvsystem.singlediode(module_parameters, 7, 6e-7, .1, 20, .5)
expected = {'i_xx': 4.2549732697234193,
expected = {'i_xx': 4.2685798754011426,
'i_mp': 6.1390251797935704,
'v_oc': 8.1147298764528042,
'v_oc': 8.1063001465863085,
'p_mp': 38.194165464983037,
'i_x': 6.7556075876880621,
'i_sc': 6.9646747613963198,
'v_mp': 6.221535886625464}
assert isinstance(out, dict)
for k, v in out.items():
assert_almost_equals(expected[k], v, 5)
yield assert_almost_equals, expected[k], v, 3


def test_PVSystem_singlediode_floats():
Expand All @@ -281,16 +291,16 @@ def test_PVSystem_singlediode_floats():
system = pvsystem.PVSystem(module=module,
module_parameters=module_parameters)
out = system.singlediode(7, 6e-7, .1, 20, .5)
expected = {'i_xx': 4.2549732697234193,
expected = {'i_xx': 4.2685798754011426,
'i_mp': 6.1390251797935704,
'v_oc': 8.1147298764528042,
'v_oc': 8.1063001465863085,
'p_mp': 38.194165464983037,
'i_x': 6.7556075876880621,
'i_sc': 6.9646747613963198,
'v_mp': 6.221535886625464}
assert isinstance(out, dict)
for k, v in out.items():
assert_almost_equals(expected[k], v, 5)
yield assert_almost_equals, expected[k], v, 3


def test_scale_voltage_current_power():
Expand Down Expand Up @@ -478,7 +488,7 @@ def test_PVSystem___repr__():

assert system.__repr__()==('PVSystem with tilt:0 and azimuth:'+
' 180 with Module: blah and Inverter: blarg')


def test_PVSystem_localize___repr__():
system = pvsystem.PVSystem(module='blah', inverter='blarg')
Expand All @@ -504,7 +514,7 @@ def test_LocalizedPVSystem_creation():
assert localized_system.latitude == 32
assert localized_system.longitude == -111


def test_LocalizedPVSystem___repr__():
localized_system = pvsystem.LocalizedPVSystem(latitude=32,
longitude=-111,
Expand Down