From 8bad8eb9fde7fadb0a6262d9321068f0b96b87c8 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Thu, 14 Jul 2016 20:11:22 -0700 Subject: [PATCH 1/7] make more pvsystem functions less pandas centric --- pvlib/pvsystem.py | 127 +++++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 63 deletions(-) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 812c74583f..aa54f91278 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -656,7 +656,7 @@ def systemdef(meta, surface_tilt, surface_azimuth, albedo, modules_per_string, return system -def ashraeiam(b, aoi): +def ashraeiam(aoi, b=0.05): ''' Determine the incidence angle modifier using the ASHRAE transmission model. @@ -671,27 +671,26 @@ def ashraeiam(b, aoi): Parameters ---------- + aoi : numeric + The angle of incidence between the module normal vector and the + sun-beam vector in degrees. + b : float A parameter to adjust the modifier as a function of angle of incidence. Typical values are on the order of 0.05 [3]. - aoi : Series - The angle of incidence between the module normal vector and the - sun-beam vector in degrees. Returns ------- - IAM : Series - + IAM : numeric The incident angle modifier calculated as 1-b*(sec(aoi)-1) as described in [2,3]. - Returns nan for all abs(aoi) >= 90 and for all IAM values - that would be less than 0. + Returns nan for all abs(aoi) >= 90 and for all IAM values that + would be less than 0. References ---------- - - [1] Souka A.F., Safwat H.H., "Determindation of the optimum + [1] Souka A.F., Safwat H.H., "Determination of the optimum orientations for the double exposure flat-plate collector and its reflections". Solar Energy vol .10, pp 170-174. 1966. @@ -707,15 +706,18 @@ def ashraeiam(b, aoi): physicaliam ''' - IAM = 1 - b*((1/np.cos(np.radians(aoi)) - 1)) + iam = 1 - b*((1/np.cos(np.radians(aoi)) - 1)) + + iam = np.where(np.abs(aoi) >= 90, np.nan, iam) + iam = np.maximum(0, iam) - IAM[abs(aoi) >= 90] = np.nan - IAM[IAM < 0] = np.nan + if isinstance(iam, pd.Series): + iam = pd.Series(iam, index=aoi.index) - return IAM + return iam -def physicaliam(K, L, n, aoi): +def physicaliam(aoi, n=1.526, K=4., L=0.002): ''' Determine the incidence angle modifier using refractive index, glazing thickness, and extinction coefficient @@ -733,33 +735,33 @@ def physicaliam(K, L, n, aoi): Parameters ---------- - K : float + aoi : numeric + The angle of incidence between the module normal vector and the + sun-beam vector in degrees. + + n : numeric + The effective index of refraction (unitless). Reference [1] + indicates that a value of 1.526 is acceptable for glass. n must + be a numeric scalar or vector with all values >=0. If n is a + vector, it must be the same size as all other input vectors. + + K : numeric The glazing extinction coefficient in units of 1/meters. Reference [1] indicates that a value of 4 is reasonable for "water white" glass. K must be a numeric scalar or vector with all values >=0. If K is a vector, it must be the same size as all other input vectors. - L : float + L : numeric The glazing thickness in units of meters. Reference [1] indicates that 0.002 meters (2 mm) is reasonable for most glass-covered PV panels. L must be a numeric scalar or vector with all values >=0. If L is a vector, it must be the same size as all other input vectors. - n : float - The effective index of refraction (unitless). Reference [1] - indicates that a value of 1.526 is acceptable for glass. n must - be a numeric scalar or vector with all values >=0. If n is a - vector, it must be the same size as all other input vectors. - - aoi : Series - The angle of incidence between the module normal vector and the - sun-beam vector in degrees. - Returns ------- - IAM : float or Series + IAM : numeric The incident angle modifier as specified in eqns. 14-16 of [1]. IAM is a column vector with the same number of elements as the largest input vector. @@ -803,12 +805,14 @@ def physicaliam(K, L, n, aoi): ((tools.tand(thetar_deg0 - zeroang)) ** 2) / ((tools.tand(thetar_deg0 + zeroang)) ** 2)))))) - IAM = tau / tau0 + iam = tau / tau0 - IAM[abs(aoi) >= 90] = np.nan - IAM[IAM < 0] = np.nan + iam = np.where(np.abs(aoi) >= 90 | iam < 0, np.nan, iam) - return IAM + if isinstance(aoi, pd.Series): + iam = pd.Series(iam, index=aoi.index) + + return iam def calcparams_desoto(poa_global, temp_cell, alpha_isc, module_parameters, @@ -826,10 +830,10 @@ def calcparams_desoto(poa_global, temp_cell, alpha_isc, module_parameters, Parameters ---------- - poa_global : float or Series + poa_global : numeric The irradiance (in W/m^2) absorbed by the module. - temp_cell : float or Series + temp_cell : numeric The average cell temperature of cells within a module in C. alpha_isc : float @@ -869,7 +873,7 @@ def calcparams_desoto(poa_global, temp_cell, alpha_isc, module_parameters, condition (this may be useful if dEgdT is a function of temperature). - M : float or Series (optional, default=1) + M : numeric (optional, default=1) An optional airmass modifier, if omitted, M is given a value of 1, which assumes absolute (pressure corrected) airmass = 1.5. In this code, M is equal to M/Mref as described in [1] (i.e. Mref @@ -891,11 +895,11 @@ def calcparams_desoto(poa_global, temp_cell, alpha_isc, module_parameters, ------- Tuple of the following results: - photocurrent : float or Series + photocurrent : numeric Light-generated current in amperes at irradiance=S and cell temperature=Tcell. - saturation_current : float or Series + saturation_current : numeric Diode saturation curent in amperes at irradiance S and cell temperature Tcell. @@ -903,11 +907,11 @@ def calcparams_desoto(poa_global, temp_cell, alpha_isc, module_parameters, Series resistance in ohms at irradiance S and cell temperature Tcell. - resistance_shunt : float or Series + resistance_shunt : numeric Shunt resistance in ohms at irradiance S and cell temperature Tcell. - nNsVth : float or Series + nNsVth : numeric Modified diode ideality factor at irradiance S and cell temperature Tcell. Note that in source [1] nNsVth = a (equation 2). nNsVth is the product of the usual diode ideality factor @@ -1001,7 +1005,7 @@ def calcparams_desoto(poa_global, temp_cell, alpha_isc, module_parameters, Source: [4] ''' - M = np.max(M, 0) + M = np.maximum(M, 0) a_ref = module_parameters['a_ref'] IL_ref = module_parameters['I_L_ref'] I0_ref = module_parameters['I_o_ref'] @@ -1754,15 +1758,15 @@ def v_from_i(resistance_shunt, resistance_series, nNsVth, current, Parameters ---------- - resistance_shunt : float or Series + resistance_shunt : numeric Shunt resistance in ohms under desired IV curve conditions. Often abbreviated ``Rsh``. - resistance_series : float or Series + resistance_series : numeric Series resistance in ohms under desired IV curve conditions. Often abbreviated ``Rs``. - nNsVth : float or Series + nNsVth : numeric 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 @@ -1771,14 +1775,14 @@ def v_from_i(resistance_shunt, resistance_series, nNsVth, current, 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 + current : numeric The current in amperes under desired IV curve conditions. - saturation_current : float or Series + saturation_current : numeric Diode saturation current in amperes under desired IV curve conditions. Often abbreviated ``I_0``. - photocurrent : float or Series + photocurrent : numeric Light-generated current (photocurrent) in amperes under desired IV curve conditions. Often abbreviated ``I_L``. @@ -1820,15 +1824,15 @@ def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage, Parameters ---------- - resistance_shunt : float or Series + resistance_shunt : numeric Shunt resistance in ohms under desired IV curve conditions. Often abbreviated ``Rsh``. - resistance_series : float or Series + resistance_series : numeric Series resistance in ohms under desired IV curve conditions. Often abbreviated ``Rs``. - nNsVth : float or Series + nNsVth : numeric 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 @@ -1837,14 +1841,14 @@ def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage, temp_cell is the temperature of the p-n junction in Kelvin, and q is the charge of an electron (coulombs). - voltage : float or Series + voltage : numeric The voltage in Volts under desired IV curve conditions. - saturation_current : float or Series + saturation_current : numeric Diode saturation current in amperes under desired IV curve conditions. Often abbreviated ``I_0``. - photocurrent : float or Series + photocurrent : numeric Light-generated current (photocurrent) in amperes under desired IV curve conditions. Often abbreviated ``I_L``. @@ -1895,7 +1899,7 @@ def snlinverter(inverter, v_dc, p_dc): Parameters ---------- - inverter : dict or Series + inverter : dict-like A dict-like object defining the inverter to be used, giving the inverter performance parameters according to the Sandia Grid-Connected Photovoltaic Inverter Model (SAND 2007-5036) [1]. @@ -1931,16 +1935,16 @@ def snlinverter(inverter, v_dc, p_dc): maintain circuitry required to sense PV array voltage (W) ====== ============================================================ - v_dc : float or Series + v_dc : numeric DC voltages, in volts, which are provided as input to the inverter. Vdc must be >= 0. - p_dc : float or Series + p_dc : numeric A scalar or DataFrame of DC powers, in watts, which are provided as input to the inverter. Pdc must be >= 0. Returns ------- - ac_power : float or Series + ac_power : numeric Modeled AC power output given the input DC voltage, Vdc, and input DC power, Pdc. When ac_power would be greater than Pac0, it is set to Pac0 to represent inverter "clipping". When @@ -1978,15 +1982,12 @@ def snlinverter(inverter, v_dc, p_dc): B = Pso * (1 + C2*(v_dc - Vdco)) C = C0 * (1 + C3*(v_dc - Vdco)) - # ensures that function works with scalar or Series input - p_dc = pd.Series(p_dc) - ac_power = (Paco/(A-B) - C*(A-B)) * (p_dc-B) + C*((p_dc-B)**2) - ac_power[ac_power > Paco] = Paco - ac_power[p_dc < Pso] = - 1.0 * abs(Pnt) + ac_power = np.minimum(Paco, ac_power) + ac_power = np.where(p_dc < Pso, -1.0 * abs(Pnt), ac_power) - if len(ac_power) == 1: - ac_power = ac_power.ix[0] + if isinstance(p_dc, pd.Series): + ac_power = pd.Series(ac_power, index=p_dc.index) return ac_power From d02bbf07e73228f97656f65585d7e080f8c47acd Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Fri, 15 Jul 2016 09:28:30 -0700 Subject: [PATCH 2/7] update tests --- pvlib/pvsystem.py | 29 ++++++++++++++++++----------- pvlib/test/test_pvsystem.py | 18 ++++++++---------- pvlib/test/test_tools.py | 13 +++++++++++++ pvlib/tools.py | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 72 insertions(+), 21 deletions(-) create mode 100644 pvlib/test/test_tools.py diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index aa54f91278..160d08a689 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -16,6 +16,7 @@ import pandas as pd from pvlib import tools +from pvlib.tools import _build_kwargs from pvlib.location import Location from pvlib import irradiance, atmosphere @@ -227,6 +228,8 @@ def ashraeiam(self, aoi): ``self.module_parameters['b']``, ``aoi``, and the :py:func:`ashraeiam` function. + Uses default arguments if keys not in module_parameters. + Parameters ---------- aoi : numeric @@ -237,30 +240,34 @@ def ashraeiam(self, aoi): modifier : numeric The AOI modifier. """ - b = self.module_parameters['b'] - return ashraeiam(b, aoi) + kwargs = _build_kwargs(['b'], self.module_parameters) + + return ashraeiam(aoi, **kwargs) def physicaliam(self, aoi): """ - Determine the incidence angle modifier using + Determine the incidence angle modifier using ``aoi``, ``self.module_parameters['K']``, ``self.module_parameters['L']``, ``self.module_parameters['n']``, - ``aoi``, and the + and the :py:func:`physicaliam` function. + Uses default arguments if keys not in module_parameters. + Parameters ---------- - See pvsystem.physicaliam for details + aoi : numeric + The angle of incidence in degrees. Returns ------- - See pvsystem.physicaliam for details + modifier : numeric + The AOI modifier. """ - K = self.module_parameters['K'] - L = self.module_parameters['L'] - n = self.module_parameters['n'] - return physicaliam(K, L, n, aoi) + kwargs = _build_kwargs(['K', 'L', 'n'], self.module_parameters) + + return physicaliam(aoi, **kwargs) def calcparams_desoto(self, poa_global, temp_cell, **kwargs): """ @@ -807,7 +814,7 @@ def physicaliam(aoi, n=1.526, K=4., L=0.002): iam = tau / tau0 - iam = np.where(np.abs(aoi) >= 90 | iam < 0, np.nan, iam) + iam = np.where((np.abs(aoi) >= 90) | (iam < 0), np.nan, iam) if isinstance(aoi, pd.Series): iam = pd.Series(iam, index=aoi.index) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 3c8aa08a71..15c801c6f4 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -94,40 +94,38 @@ def test_systemdef_dict(): def test_ashraeiam(): thetas = np.linspace(-90, 90, 9) - iam = pvsystem.ashraeiam(.05, thetas) + iam = pvsystem.ashraeiam(thetas, .05) expected = np.array([ nan, 0.9193437 , 0.97928932, 0.99588039, 1. , 0.99588039, 0.97928932, 0.9193437 , nan]) - assert np.isclose(iam, expected, equal_nan=True).all() + assert_allclose(iam, expected, equal_nan=True) def test_PVSystem_ashraeiam(): module_parameters = pd.Series({'b': 0.05}) - system = pvsystem.PVSystem(module='blah', inverter='blarg', - module_parameters=module_parameters) + system = pvsystem.PVSystem(module_parameters=module_parameters) thetas = np.linspace(-90, 90, 9) iam = system.ashraeiam(thetas) expected = np.array([ nan, 0.9193437 , 0.97928932, 0.99588039, 1. , 0.99588039, 0.97928932, 0.9193437 , nan]) - assert np.isclose(iam, expected, equal_nan=True).all() + assert_allclose(iam, expected, equal_nan=True) def test_physicaliam(): thetas = np.linspace(-90, 90, 9) - iam = pvsystem.physicaliam(4, 0.002, 1.526, thetas) + iam = pvsystem.physicaliam(thetas, 1.526, 0.002, 4) expected = np.array([ nan, 0.8893998 , 0.98797788, 0.99926198, nan, 0.99926198, 0.98797788, 0.8893998 , nan]) - assert np.isclose(iam, expected, equal_nan=True).all() + assert_allclose(iam, expected, equal_nan=True) def test_PVSystem_physicaliam(): module_parameters = pd.Series({'K': 4, 'L': 0.002, 'n': 1.526}) - system = pvsystem.PVSystem(module='blah', inverter='blarg', - module_parameters=module_parameters) + system = pvsystem.PVSystem(module_parameters=module_parameters) thetas = np.linspace(-90, 90, 9) iam = system.physicaliam(thetas) expected = np.array([ nan, 0.8893998 , 0.98797788, 0.99926198, nan, 0.99926198, 0.98797788, 0.8893998 , nan]) - assert np.isclose(iam, expected, equal_nan=True).all() + assert_allclose(iam, expected, equal_nan=True) # if this completes successfully we'll be able to do more tests below. diff --git a/pvlib/test/test_tools.py b/pvlib/test/test_tools.py new file mode 100644 index 0000000000..dfecea2047 --- /dev/null +++ b/pvlib/test/test_tools.py @@ -0,0 +1,13 @@ +import pytest + +from pvlib import tools + +@pytest.mark.parametrize('keys, input_dict, expected', [ + (['a', 'b'], {'a': 1, 'b': 2, 'c': 3}, {'a': 1, 'b': 2}), + (['a', 'b', 'd'], {'a': 1, 'b': 2, 'c': 3}, {'a': 1, 'b': 2}), + (['a'], {}, {}), + (['a'], {'b': 2}, {}) +]) +def test_build_kwargs(keys, input_dict, expected): + kwargs = tools._build_kwargs(keys, input_dict) + assert kwargs == expected diff --git a/pvlib/tools.py b/pvlib/tools.py index bab3ab66e0..c002e56330 100644 --- a/pvlib/tools.py +++ b/pvlib/tools.py @@ -8,7 +8,10 @@ import datetime as dt import numpy as np +<<<<<<< 8bad8eb9fde7fadb0a6262d9321068f0b96b87c8 import pandas as pd +======= +>>>>>>> update tests import pytz @@ -118,6 +121,10 @@ def localize_to_utc(time, location): pvl_logger.debug('tz_localize to %s and then tz_convert to UTC', location.tz) +<<<<<<< 8bad8eb9fde7fadb0a6262d9321068f0b96b87c8 +======= + +>>>>>>> update tests return time_utc @@ -170,6 +177,7 @@ def djd_to_datetime(djd, tz='UTC'): return utc_time.astimezone(pytz.timezone(tz)) +<<<<<<< 8bad8eb9fde7fadb0a6262d9321068f0b96b87c8 def _pandas_to_doy(pd_object): """ Finds the day of year for a pandas datetime-like object. @@ -232,3 +240,28 @@ def _array_out(input): output = input return output +======= +def _build_kwargs(keys, input_dict): + """ + Parameters + ---------- + keys : iterable + Typically a list of strings. + adict : dict-like + A dictionary from which to attempt to pull each key. + + Returns + ------- + kwargs : dict + A dictionary with only the keys that were in input_dict + """ + + kwargs = {} + for key in keys: + try: + kwargs[key] = input_dict[key] + except KeyError: + pass + + return kwargs +>>>>>>> update tests From 4d8f657098b7a4022c7dd9d96c7d1ac5f2bb6144 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Fri, 15 Jul 2016 09:39:23 -0700 Subject: [PATCH 3/7] change snlinverter api. update whatsnew --- docs/sphinx/source/whatsnew/v0.4.0.txt | 3 +++ pvlib/modelchain.py | 2 +- pvlib/pvsystem.py | 19 ++++++++++--------- pvlib/test/test_pvsystem.py | 6 +++--- 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/docs/sphinx/source/whatsnew/v0.4.0.txt b/docs/sphinx/source/whatsnew/v0.4.0.txt index 03eaa8a90d..fd71ee7646 100644 --- a/docs/sphinx/source/whatsnew/v0.4.0.txt +++ b/docs/sphinx/source/whatsnew/v0.4.0.txt @@ -19,6 +19,9 @@ API Changes in addition to arrays and Series. Furthermore, these functions no longer promote scalar or array input to Series output. Also applies to atmosphere.relativeairmass. (:issue:`201`, :issue:`214`) +* Reorder the ashraeiam, physicaliam, and snlinverter arguments to put + the most variable arguments first. Adds default arguments for the IAM + functions. (:issue:`197`) * The irradiance.extraradiation function input/output type consistency across different methods has been dramatically improved. (:issue:`217`, :issue:`219`) diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index 161da931cc..ee90e39062 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -176,7 +176,7 @@ def basic_chain(times, latitude, longitude, dc = pvsystem.sapm(module_parameters, effective_irradiance, temps['temp_cell']) - ac = pvsystem.snlinverter(inverter_parameters, dc['v_mp'], dc['p_mp']) + ac = pvsystem.snlinverter(dc['v_mp'], dc['p_mp'], inverter_parameters) return dc, ac diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index 160d08a689..a0b83ea221 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -455,7 +455,7 @@ def snlinverter(self, v_dc, p_dc): ------- See pvsystem.snlinverter for details """ - return snlinverter(self.inverter_parameters, v_dc, p_dc) + return snlinverter(v_dc, p_dc, self.inverter_parameters) def scale_voltage_current_power(self, data): """ @@ -1891,7 +1891,7 @@ def i_from_v(resistance_shunt, resistance_series, nNsVth, voltage, return I.real -def snlinverter(inverter, v_dc, p_dc): +def snlinverter(v_dc, p_dc, inverter): ''' Converts DC power and voltage to AC power using Sandia's Grid-Connected PV Inverter model. @@ -1906,6 +1906,14 @@ def snlinverter(inverter, v_dc, p_dc): Parameters ---------- + v_dc : numeric + DC voltages, in volts, which are provided as input to the + inverter. Vdc must be >= 0. + + p_dc : numeric + A scalar or DataFrame of DC powers, in watts, which are provided + as input to the inverter. Pdc must be >= 0. + inverter : dict-like A dict-like object defining the inverter to be used, giving the inverter performance parameters according to the Sandia @@ -1942,13 +1950,6 @@ def snlinverter(inverter, v_dc, p_dc): maintain circuitry required to sense PV array voltage (W) ====== ============================================================ - v_dc : numeric - DC voltages, in volts, which are provided as input to the - inverter. Vdc must be >= 0. - p_dc : numeric - A scalar or DataFrame of DC powers, in watts, which are provided - as input to the inverter. Pdc must be >= 0. - Returns ------- ac_power : numeric diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 15c801c6f4..1facb30501 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -498,7 +498,7 @@ def test_snlinverter(sam_data): idcs = pd.Series(np.linspace(0,11,3)) pdcs = idcs * vdcs - pacs = pvsystem.snlinverter(inverters[testinv], vdcs, pdcs) + pacs = pvsystem.snlinverter(vdcs, pdcs, inverters[testinv]) assert_series_equal(pacs, pd.Series([-0.020000, 132.004308, 250.000000])) @@ -522,7 +522,7 @@ def test_snlinverter_float(sam_data): idcs = 5.5 pdcs = idcs * vdcs - pacs = pvsystem.snlinverter(inverters[testinv], vdcs, pdcs) + pacs = pvsystem.snlinverter(vdcs, pdcs, inverters[testinv]) assert_allclose(pacs, 132.004278, 5) @@ -533,7 +533,7 @@ def test_snlinverter_Pnt_micro(sam_data): idcs = pd.Series(np.linspace(0,11,3)) pdcs = idcs * vdcs - pacs = pvsystem.snlinverter(inverters[testinv], vdcs, pdcs) + pacs = pvsystem.snlinverter(vdcs, pdcs, inverters[testinv]) assert_series_equal(pacs, pd.Series([-0.043000, 132.545914746, 240.000000])) From c95da964929c3ab126ab900802dc196612e9ce13 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Wed, 20 Jul 2016 09:32:21 -0700 Subject: [PATCH 4/7] fix bad rebase in tools --- pvlib/tools.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/pvlib/tools.py b/pvlib/tools.py index c002e56330..c779703cf8 100644 --- a/pvlib/tools.py +++ b/pvlib/tools.py @@ -8,10 +8,7 @@ import datetime as dt import numpy as np -<<<<<<< 8bad8eb9fde7fadb0a6262d9321068f0b96b87c8 import pandas as pd -======= ->>>>>>> update tests import pytz @@ -120,11 +117,6 @@ def localize_to_utc(time, location): time_utc = time.tz_localize(location.tz).tz_convert('UTC') pvl_logger.debug('tz_localize to %s and then tz_convert to UTC', location.tz) - -<<<<<<< 8bad8eb9fde7fadb0a6262d9321068f0b96b87c8 -======= - ->>>>>>> update tests return time_utc @@ -177,7 +169,6 @@ def djd_to_datetime(djd, tz='UTC'): return utc_time.astimezone(pytz.timezone(tz)) -<<<<<<< 8bad8eb9fde7fadb0a6262d9321068f0b96b87c8 def _pandas_to_doy(pd_object): """ Finds the day of year for a pandas datetime-like object. @@ -240,7 +231,8 @@ def _array_out(input): output = input return output -======= + + def _build_kwargs(keys, input_dict): """ Parameters @@ -264,4 +256,3 @@ def _build_kwargs(keys, input_dict): pass return kwargs ->>>>>>> update tests From c108a075804d7a2c8cb173237084de13230ebec8 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Wed, 20 Jul 2016 09:59:37 -0700 Subject: [PATCH 5/7] add test decorators --- pvlib/test/test_pvsystem.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index 1facb30501..e5610d5f43 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -92,6 +92,7 @@ def test_systemdef_dict(): assert expected == pvsystem.systemdef(meta, 5, 0, .1, 5, 5) +@needs_numpy_1_10 def test_ashraeiam(): thetas = np.linspace(-90, 90, 9) iam = pvsystem.ashraeiam(thetas, .05) @@ -100,6 +101,7 @@ def test_ashraeiam(): assert_allclose(iam, expected, equal_nan=True) +@needs_numpy_1_10 def test_PVSystem_ashraeiam(): module_parameters = pd.Series({'b': 0.05}) system = pvsystem.PVSystem(module_parameters=module_parameters) @@ -110,6 +112,7 @@ def test_PVSystem_ashraeiam(): assert_allclose(iam, expected, equal_nan=True) +@needs_numpy_1_10 def test_physicaliam(): thetas = np.linspace(-90, 90, 9) iam = pvsystem.physicaliam(thetas, 1.526, 0.002, 4) @@ -118,6 +121,7 @@ def test_physicaliam(): assert_allclose(iam, expected, equal_nan=True) +@needs_numpy_1_10 def test_PVSystem_physicaliam(): module_parameters = pd.Series({'K': 4, 'L': 0.002, 'n': 1.526}) system = pvsystem.PVSystem(module_parameters=module_parameters) From b939cea67d87b578f899a133aa8b8b8501035904 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Wed, 20 Jul 2016 19:51:55 -0700 Subject: [PATCH 6/7] reorder sapm function arguments --- docs/sphinx/source/package_overview.rst | 8 ++-- pvlib/modelchain.py | 8 ++-- pvlib/pvsystem.py | 60 ++++++++++++------------- pvlib/test/test_pvsystem.py | 27 ++++++----- 4 files changed, 54 insertions(+), 49 deletions(-) diff --git a/docs/sphinx/source/package_overview.rst b/docs/sphinx/source/package_overview.rst index c9e733d7ab..35eb00abcc 100644 --- a/docs/sphinx/source/package_overview.rst +++ b/docs/sphinx/source/package_overview.rst @@ -105,10 +105,10 @@ to accomplish our system modeling goal: temps = pvlib.pvsystem.sapm_celltemp(total_irrad['poa_global'], wind_speed, temp_air) effective_irradiance = pvlib.pvsystem.sapm_effective_irradiance( - module, total_irrad['poa_direct'], total_irrad['poa_diffuse'], - am_abs, aoi) - dc = pvlib.pvsystem.sapm(module, effective_irradiance, temps['temp_cell']) - ac = pvlib.pvsystem.snlinverter(inverter, dc['v_mp'], dc['p_mp']) + total_irrad['poa_direct'], total_irrad['poa_diffuse'], + am_abs, aoi, module) + dc = pvlib.pvsystem.sapm(effective_irradiance, temps['temp_cell'], module) + ac = pvlib.pvsystem.snlinverter(dc['v_mp'], dc['p_mp'], inverter) annual_energy = ac.sum() energies[name] = annual_energy diff --git a/pvlib/modelchain.py b/pvlib/modelchain.py index ee90e39062..a797301207 100644 --- a/pvlib/modelchain.py +++ b/pvlib/modelchain.py @@ -170,11 +170,11 @@ def basic_chain(times, latitude, longitude, weather['temp_air']) effective_irradiance = pvsystem.sapm_effective_irradiance( - module_parameters, total_irrad['poa_direct'], - total_irrad['poa_diffuse'], airmass, aoi) + total_irrad['poa_direct'], total_irrad['poa_diffuse'], airmass, aoi, + module_parameters) - dc = pvsystem.sapm(module_parameters, effective_irradiance, - temps['temp_cell']) + dc = pvsystem.sapm(effective_irradiance, temps['temp_cell'], + module_parameters) ac = pvsystem.snlinverter(dc['v_mp'], dc['p_mp'], inverter_parameters) diff --git a/pvlib/pvsystem.py b/pvlib/pvsystem.py index a0b83ea221..70b6526b6e 100644 --- a/pvlib/pvsystem.py +++ b/pvlib/pvsystem.py @@ -326,7 +326,7 @@ def sapm(self, effective_irradiance, temp_cell, **kwargs): ------- See pvsystem.sapm for details """ - return sapm(self.module_parameters, effective_irradiance, temp_cell) + return sapm(effective_irradiance, temp_cell, self.module_parameters) def sapm_celltemp(self, irrad, wind, temp): """Uses :py:func:`sapm_celltemp` to calculate module and cell @@ -358,7 +358,7 @@ def sapm_spectral_loss(self, airmass_absolute): F1 : numeric The SAPM spectral loss coefficient. """ - return sapm_spectral_loss(self.module_parameters, airmass_absolute) + return sapm_spectral_loss(airmass_absolute, self.module_parameters) def sapm_aoi_loss(self, aoi): """ @@ -375,7 +375,7 @@ def sapm_aoi_loss(self, aoi): F2 : numeric The SAPM angle of incidence loss coefficient. """ - return sapm_aoi_loss(self.module_parameters, aoi) + return sapm_aoi_loss(aoi, self.module_parameters) def sapm_effective_irradiance(self, poa_direct, poa_diffuse, airmass_absolute, aoi, @@ -407,10 +407,9 @@ def sapm_effective_irradiance(self, poa_direct, poa_diffuse, effective_irradiance : numeric The SAPM effective irradiance. """ - return sapm_effective_irradiance(self.module_parameters, - poa_direct, poa_diffuse, - airmass_absolute, aoi, - reference_irradiance) + return sapm_effective_irradiance( + poa_direct, poa_diffuse, airmass_absolute, aoi, + self.module_parameters, reference_irradiance=reference_irradiance) def singlediode(self, photocurrent, saturation_current, resistance_series, resistance_shunt, nNsVth): @@ -1162,7 +1161,7 @@ def _parse_raw_sam_df(csvdata): return df -def sapm(module, effective_irradiance, temp_cell): +def sapm(effective_irradiance, temp_cell, module): ''' The Sandia PV Array Performance Model (SAPM) generates 5 points on a PV module's I-V curve (Voc, Isc, Ix, Ixx, Vmp/Imp) according to @@ -1170,16 +1169,16 @@ def sapm(module, effective_irradiance, temp_cell): Parameters ---------- - module : dict-like - A dict, Series, or DataFrame defining the SAPM performance - parameters. See the notes section for more details. - effective_irradiance : numeric Effective irradiance (suns). temp_cell : numeric The cell temperature (degrees C). + module : dict-like + A dict, Series, or DataFrame defining the SAPM performance + parameters. See the notes section for more details. + Returns ------- A DataFrame with the columns: @@ -1387,20 +1386,20 @@ def sapm_celltemp(poa_global, wind_speed, temp_air, return pd.DataFrame({'temp_cell': temp_cell, 'temp_module': temp_module}) -def sapm_spectral_loss(module, airmass_absolute): +def sapm_spectral_loss(airmass_absolute, module): """ Calculates the SAPM spectral loss coefficient, F1. Parameters ---------- + airmass_absolute : numeric + Absolute airmass + module : dict-like A dict, Series, or DataFrame defining the SAPM performance parameters. See the :py:func:`sapm` notes section for more details. - airmass_absolute : numeric - Absolute airmass - Returns ------- F1 : numeric @@ -1424,21 +1423,21 @@ def sapm_spectral_loss(module, airmass_absolute): return spectral_loss -def sapm_aoi_loss(module, aoi, upper=None): +def sapm_aoi_loss(aoi, module, upper=None): """ Calculates the SAPM angle of incidence loss coefficient, F2. Parameters ---------- + aoi : numeric + Angle of incidence in degrees. Negative input angles will return + nan values. + module : dict-like A dict, Series, or DataFrame defining the SAPM performance parameters. See the :py:func:`sapm` notes section for more details. - aoi : numeric - Angle of incidence in degrees. Negative input angles will return - nan values. - upper : None or float Upper limit on the results. @@ -1482,20 +1481,14 @@ def sapm_aoi_loss(module, aoi, upper=None): return aoi_loss -def sapm_effective_irradiance(module, poa_direct, poa_diffuse, - airmass_absolute, aoi, - reference_irradiance=1000): +def sapm_effective_irradiance(poa_direct, poa_diffuse, airmass_absolute, aoi, + module, reference_irradiance=1000): """ Calculates the SAPM effective irradiance using the SAPM spectral loss and SAPM angle of incidence loss functions. Parameters ---------- - module : dict-like - A dict, Series, or DataFrame defining the SAPM performance - parameters. See the :py:func:`sapm` notes section for more - details. - poa_direct : numeric The direct irradiance incident upon the module. @@ -1508,6 +1501,11 @@ def sapm_effective_irradiance(module, poa_direct, poa_diffuse, aoi : numeric Angle of incidence in degrees. + module : dict-like + A dict, Series, or DataFrame defining the SAPM performance + parameters. See the :py:func:`sapm` notes section for more + details. + reference_irradiance : numeric Reference irradiance by which to divide the input irradiance. @@ -1517,8 +1515,8 @@ def sapm_effective_irradiance(module, poa_direct, poa_diffuse, The SAPM effective irradiance. """ - F1 = sapm_spectral_loss(module, airmass_absolute) - F2 = sapm_aoi_loss(module, aoi) + F1 = sapm_spectral_loss(airmass_absolute, module) + F2 = sapm_aoi_loss(aoi, module) E0 = reference_irradiance diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index e5610d5f43..c51821a07a 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -156,7 +156,7 @@ def test_sapm(sapm_module_params): effective_irradiance = pd.Series([-1, 0.5, 1.1, np.nan, 1], index=times) temp_cell = pd.Series([10, 25, 50, 25, np.nan], index=times) - out = pvsystem.sapm(sapm_module_params, effective_irradiance, temp_cell) + out = pvsystem.sapm(effective_irradiance, temp_cell, sapm_module_params) expected = pd.DataFrame(np.array( [[ -5.0608322 , -4.65037767, nan, nan, @@ -174,7 +174,7 @@ def test_sapm(sapm_module_params): assert_frame_equal(out, expected, check_less_precise=4) - out = pvsystem.sapm(sapm_module_params, 1, 25) + out = pvsystem.sapm(1, 25, sapm_module_params) expected = OrderedDict() expected['i_sc'] = 5.09115 @@ -189,8 +189,8 @@ def test_sapm(sapm_module_params): assert_allclose(out[k], v, atol=1e-4) # just make sure it works with a dict input - pvsystem.sapm(sapm_module_params.to_dict(), effective_irradiance, - temp_cell) + pvsystem.sapm(effective_irradiance, temp_cell, + sapm_module_params.to_dict()) def test_PVSystem_sapm(sapm_module_params): @@ -210,7 +210,7 @@ def test_PVSystem_sapm(sapm_module_params): ]) def test_sapm_spectral_loss(sapm_module_params, airmass, expected): - out = pvsystem.sapm_spectral_loss(sapm_module_params, airmass) + out = pvsystem.sapm_spectral_loss(airmass, sapm_module_params) if isinstance(airmass, pd.Series): assert_series_equal(out, expected, check_less_precise=4) @@ -235,7 +235,7 @@ def test_PVSystem_sapm_spectral_loss(sapm_module_params): ]) def test_sapm_aoi_loss(sapm_module_params, aoi, expected): - out = pvsystem.sapm_aoi_loss(sapm_module_params, aoi) + out = pvsystem.sapm_aoi_loss(aoi, sapm_module_params) if isinstance(aoi, pd.Series): assert_series_equal(out, expected, check_less_precise=4) @@ -245,13 +245,13 @@ def test_sapm_aoi_loss(sapm_module_params, aoi, expected): def test_sapm_aoi_loss_limits(): module_parameters = {'B0': 5, 'B1': 0, 'B2': 0, 'B3': 0, 'B4': 0, 'B5': 0} - assert pvsystem.sapm_aoi_loss(module_parameters, 1) == 5 + assert pvsystem.sapm_aoi_loss(1, module_parameters) == 5 module_parameters = {'B0': 5, 'B1': 0, 'B2': 0, 'B3': 0, 'B4': 0, 'B5': 0} - assert pvsystem.sapm_aoi_loss(module_parameters, 1, upper=1) == 1 + assert pvsystem.sapm_aoi_loss(1, module_parameters, upper=1) == 1 module_parameters = {'B0': -5, 'B1': 0, 'B2': 0, 'B3': 0, 'B4': 0, 'B5': 0} - assert pvsystem.sapm_aoi_loss(module_parameters, 1) == 0 + assert pvsystem.sapm_aoi_loss(1, module_parameters) == 0 def test_PVSystem_sapm_aoi_loss(sapm_module_params): @@ -277,7 +277,14 @@ def test_PVSystem_sapm_aoi_loss(sapm_module_params): ]) def test_sapm_effective_irradiance(sapm_module_params, test_input, expected): - out = pvsystem.sapm_effective_irradiance(sapm_module_params, *test_input) + try: + kwargs = {'reference_irradiance': test_input[4]} + test_input = test_input[:-1] + except IndexError: + kwargs = {} + + out = pvsystem.sapm_effective_irradiance(*test_input, sapm_module_params, + **kwargs) if isinstance(test_input, pd.Series): assert_series_equal(out, expected, check_less_precise=4) From 1827d6c713364ec6fb3c9bbd1b3d0d1eeef24868 Mon Sep 17 00:00:00 2001 From: Will Holmgren Date: Wed, 20 Jul 2016 20:00:58 -0700 Subject: [PATCH 7/7] fix pre-python 3.5 syntax error in test --- pvlib/test/test_pvsystem.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pvlib/test/test_pvsystem.py b/pvlib/test/test_pvsystem.py index c51821a07a..12da350092 100644 --- a/pvlib/test/test_pvsystem.py +++ b/pvlib/test/test_pvsystem.py @@ -283,8 +283,9 @@ def test_sapm_effective_irradiance(sapm_module_params, test_input, expected): except IndexError: kwargs = {} - out = pvsystem.sapm_effective_irradiance(*test_input, sapm_module_params, - **kwargs) + test_input.append(sapm_module_params) + + out = pvsystem.sapm_effective_irradiance(*test_input, **kwargs) if isinstance(test_input, pd.Series): assert_series_equal(out, expected, check_less_precise=4)