diff --git a/docs/sphinx/source/whatsnew/v0.3.3.txt b/docs/sphinx/source/whatsnew/v0.3.3.txt index f390d1ac13..88ae44ae95 100644 --- a/docs/sphinx/source/whatsnew/v0.3.3.txt +++ b/docs/sphinx/source/whatsnew/v0.3.3.txt @@ -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 diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index a530b39f5c..be0310689c 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -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) + @@ -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: @@ -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) + @@ -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) @@ -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, diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 3b58e0b895..6ae13a0cb6 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -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) @@ -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(): @@ -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(): @@ -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') @@ -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,