From f62cc7e47fa09d1ebf361f89bd9e0ffc40f552e8 Mon Sep 17 00:00:00 2001 From: Raymond Hickey Date: Mon, 26 Feb 2018 09:51:58 -0800 Subject: [PATCH 1/5] Merging v4 branch before submitting PR (#1) * improve python 3 compatibility in pvsys, strs and mods, require future * change dict.iter*() to iter*(dic), fix import * in tests * fix two diode test for SymPy-1.1.1 * fixes #55 * replace string keys in expected_data with the actual symbol objects, so instead of {'ic': IC}, use {ic: IC} where ic is the SymPy symbol object * replace string substitutions in expressions with the actual SymPy symbols, so use didv.subs(vc + rs*ic, 'vd') instead of didv.subs('vc + rs * ic(vc)', 'vd') which is error prone anyway, because the string could change, how do you know what string to use? * leave these subs in for now, but they are not necessary, since vd = vc + ic*rs can evaluate just fine * when numerically evaluating expressions using evalf(subs=expected_data) substitute values for the symbol di_dv, NOT the string "Derivative(ic(vc), vc)" since this doesn't work anymore! * don't substitute for the string 'ic(vc)', instead use the SymPy symbols ic, since parentheses don't work in SymPy substitutions or evaluations anymore, and this is more explicit since ic was defined as f(vc) when it was initialized! * freeze versions in requirements so this doesn't an issue again * ignore .spyproject - the new spyder-IDE project file Signed-off-by: Mark Mikofski * update travis to test Py3, update setup with correct install and test reqs, add setup.cfg for universal wheel * ENH: BUG: add _calc_now flag to determine when cell is recalculated (GH59) (#61) * fixes #59 * add class attr _calc_now * override _calc_now with instance attribte at the end of constructor * check if _calc_now is True to decide to call self.calcCell() instead of checking for pvconst * use self.__dict__.update() instead of using super(PVcell, self).__setattr__(key, value) * in update, remove TODO, b/c even tho __dict__.update() would bypass __setattr__() then would have to check for floats, and still set _calc_now = True to trigger recalc, so instead, just set _calc_now = False first to turn calculations off, until all attr are set, then recalc * don't set pvcell.pvconst in pvstring * just raise an exception if they don't match for now * remove comments about "deepcopy" everywhere * oops, recalculate means _calc_now = True, duh! * remove commented legacy code in __setattr__, add comment in update re checking for floats * add test for new _calc_now flag * if _calc_now == False, then calcCell() is not called in __setattr__ * if _calc_now == True, then calcCell() is called in __setattr__ * BUG: pvconst is not a singleton, inconsistent, doesn't update (GH38) (#62) * fixes #59 * add class attr _calc_now * override _calc_now with instance attribte at the end of constructor * check if _calc_now is True to decide to call self.calcCell() instead of checking for pvconst * use self.__dict__.update() instead of using super(PVcell, self).__setattr__(key, value) * in update, remove TODO, b/c even tho __dict__.update() would bypass __setattr__() then would have to check for floats, and still set _calc_now = True to trigger recalc, so instead, just set _calc_now = False first to turn calculations off, until all attr are set, then recalc * don't set pvcell.pvconst in pvstring * just raise an exception if they don't match for now * remove comments about "deepcopy" everywhere * oops, recalculate means _calc_now = True, duh! * remove commented legacy code in __setattr__, add comment in update re checking for floats * add test for new _calc_now flag * if _calc_now == False, then calcCell() is not called in __setattr__ * if _calc_now == True, then calcCell() is called in __setattr__ * closes #38 consistent pvconst behavior * use duck typing to check pvstrs in PVsystem() for list, object or none * set pvconst from pvstrs if given * set numbstrs from pvstrs if given * set numbermods form pvstrs.pvmods if given * check that pvconst and pvmods are consistent * add tests * apply same changes in pvsystem from last commit to pvstr * change default pvconst arg to None * use duck typing to check if pvmods is a list, an object, or None * relax requirement that all strings have same number of modules * change pvsys.numberMods to a list of number of modules in each string * add test to check pvstring * check that all pvconst are the same for all modules * add docstring for members * also test that pvsys.numberMods is now a list * each item in list is number of modules in corresponding string * use ducktyping to determine if pvcells is list or obj or None * add missing blank lines and wrap long lines per pep8 * add pvcell object to pvcells docstring arg type * add tests in test_module to check that pvconst is the same for module and all cells * add test for update method * replace npts attr with a property * add new private _npts attribute, return for npts in getter * set npts, pts, negpts, Imod_pts, and Imod_negpts in setter * add test to show that changing npts changes pts, negpts, Imod_pts, and Imod_negpts * add pvsystem update method * make system calculation DRY, use everywhere calcSystem and calcMPP_IscVocFFeff are called back to back to set Isys, Vsys, Psys, etc. * also makes it explicity to recalc the system after a change, like change pvconst.npts * Update example.py Isat2 => Isat2_T0 * reverting changes - didn't mean to commit master * Isat2fix (#64) * Added temperature dependance for Isat2, i.e. Isat2(Tcell) * test for wide range of Tcell values - added after implementing Isat2 as a function of Tcell * generated new iv curve * Isat2 to Isat2_T0 * Update example.py Isat2 => Isat2_T0 * Update test_diode.py * Deleting the old IV curve * updated IV curve test file * fixed isat2 typos caused by replace * use entire iec matrix * Update .travis.yml added new pipy credentials --- .gitignore | 1 + .travis.yml | 5 +- pvmismatch/contrib/gen_coeffs/__init__.py | 4 +- pvmismatch/contrib/gen_coeffs/example.py | 14 +-- .../contrib/gen_coeffs/tests/test_diode.py | 2 +- .../gen_coeffs/tests/test_two_diode.py | 54 +++++------ pvmismatch/pvmismatch_lib/pvcell.py | 48 +++++++--- pvmismatch/pvmismatch_lib/pvconstants.py | 40 ++++++--- pvmismatch/pvmismatch_lib/pvmodule.py | 49 +++++++--- pvmismatch/pvmismatch_lib/pvstring.py | 48 ++++++---- pvmismatch/pvmismatch_lib/pvsystem.py | 89 +++++++++++-------- pvmismatch/tests/__init__.py | 3 +- pvmismatch/tests/calc_series_test_iv.dat | 4 +- pvmismatch/tests/test_pvcell.py | 33 ++++++- pvmismatch/tests/test_pvconstants.py | 21 +++++ pvmismatch/tests/test_pvmodule.py | 25 ++++++ pvmismatch/tests/test_pvstring.py | 24 +++++ pvmismatch/tests/test_pvsystem.py | 25 ++++++ pvmismatch/tests/test_settemps.py | 25 ++++++ requirements.txt | 20 +++-- setup.cfg | 2 + setup.py | 72 +++++++++++---- 22 files changed, 443 insertions(+), 165 deletions(-) create mode 100644 pvmismatch/tests/test_pvconstants.py create mode 100644 pvmismatch/tests/test_pvstring.py create mode 100644 pvmismatch/tests/test_pvsystem.py create mode 100644 setup.cfg diff --git a/.gitignore b/.gitignore index 7591ef3..37fc2f7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # ide .spyderproject +.spyproject .project .pydevproject .settings/ diff --git a/.travis.yml b/.travis.yml index 93e5175..966ca73 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,7 @@ language: python +python: + - "2.7" + - "3.6" cache: pip sudo: required before_install: @@ -18,7 +21,7 @@ deploy: on: tags: true - provider: pypi - user: bwanamarko + user: perfspwr password: $PYPI_PASSWORD distributions: "sdist bdist_wheel" # Your distributions here skip_upload_docs: true diff --git a/pvmismatch/contrib/gen_coeffs/__init__.py b/pvmismatch/contrib/gen_coeffs/__init__.py index 027f7b3..ea9e616 100644 --- a/pvmismatch/contrib/gen_coeffs/__init__.py +++ b/pvmismatch/contrib/gen_coeffs/__init__.py @@ -6,7 +6,7 @@ import numpy as np from scipy import optimize from pvmismatch.contrib.gen_coeffs import diode, two_diode -from pvmismatch.pvmismatch_lib.pvcell import ISAT1_T0, ISAT2, RS, RSH +from pvmismatch.pvmismatch_lib.pvcell import ISAT1_T0, ISAT2_T0, RS, RSH # IEC 61853 test matrix TC_C = [15.0, 25.0, 50.0, 75.0] @@ -37,7 +37,7 @@ def gen_two_diode(isc, voc, imp, vmp, nseries, nparallel, vmp_cell = vmp / nseries if x0 is None: isat1 = ISAT1_T0 # [A] - isat2 = ISAT2 + isat2 = ISAT2_T0 rs = RS # [ohms] rsh = RSH # [ohms] else: diff --git a/pvmismatch/contrib/gen_coeffs/example.py b/pvmismatch/contrib/gen_coeffs/example.py index 9f0e514..aa24659 100644 --- a/pvmismatch/contrib/gen_coeffs/example.py +++ b/pvmismatch/contrib/gen_coeffs/example.py @@ -23,16 +23,16 @@ iec61853 = gen_coeffs.gen_iec_61853_from_sapm(gen_coeffs.PVMODULES[PROD_NAME]) iec61853['i_mp'] = iec61853['p_mp'] / iec61853['v_mp'] #isc0, alpha_isc = gen_coeffs.gen_sapm(iec61853) -x, sol = gen_coeffs.gen_two_diode(ISC0, VOC0, IMP0, VMP0, NS, NP, T0) -#x, sol = gen_coeffs.gen_two_diode( -# iec61853['i_sc'], iec61853['v_oc'], iec61853['i_mp'], -# iec61853['v_mp'], NS, NP, tc=TC, method='lm', -# x0=(2.25e-11, 1.5e-6, 0.004, 10.0) -#) +#x, sol = gen_coeffs.gen_two_diode(ISC0, VOC0, IMP0, VMP0, NS, NP, T0) +x, sol = gen_coeffs.gen_two_diode( + iec61853['i_sc'], iec61853['v_oc'], iec61853['i_mp'], + iec61853['v_mp'], NS, NP, tc=TC, method='lm', + x0=(2.25e-11, 1.5e-6, 0.004, 10.0) +) isat1, isat2, rs, rsh = x pvc = pvcell.PVcell( - Rs=rs, Rsh=rsh, Isat1_T0=isat1, Isat2=isat2, + Rs=rs, Rsh=rsh, Isat1_T0=isat1, Isat2_T0=isat2, Isc0_T0=ISC0/NP, alpha_Isc=AISC ) f1 = plt.figure() diff --git a/pvmismatch/contrib/gen_coeffs/tests/test_diode.py b/pvmismatch/contrib/gen_coeffs/tests/test_diode.py index 4f1e6fe..eea9933 100644 --- a/pvmismatch/contrib/gen_coeffs/tests/test_diode.py +++ b/pvmismatch/contrib/gen_coeffs/tests/test_diode.py @@ -3,7 +3,7 @@ """ from pvmismatch.pvmismatch_lib.pvcell import ( - RS as RS_2, RSH as RSH_2, ISAT1_T0 as ISAT1_2, ISAT2 as ISAT2_2 + RS as RS_2, RSH as RSH_2, ISAT1_T0 as ISAT1_2, ISAT2_T0 as ISAT2_2 ) from pvmismatch.contrib.gen_coeffs import diode from pvmismatch.contrib.gen_coeffs import PVMODULES diff --git a/pvmismatch/contrib/gen_coeffs/tests/test_two_diode.py b/pvmismatch/contrib/gen_coeffs/tests/test_two_diode.py index af18d26..8b02393 100644 --- a/pvmismatch/contrib/gen_coeffs/tests/test_two_diode.py +++ b/pvmismatch/contrib/gen_coeffs/tests/test_two_diode.py @@ -56,22 +56,22 @@ def test_didv_dpdv_frsh(): 'rsh': RSH_2, 'ic': IC, 'vc': VC, 'vt': VT} fdidv_test, jdidv_test = fdidv(**test_data) expected_data = { - 'isat1': ISAT1_2, 'isat2': ISAT2_2, 'rs': RS_2, 'rsh': RSH_2, - 'ic(vc)': IC, 'vc': VC, 'vd': VD_2, 'vt': VT + isat1: ISAT1_2, isat2: ISAT2_2, rs: RS_2, rsh: RSH_2, + ic: IC, vc: VC, 'vd': VD_2, vt: VT } - didv_simple = didv.subs('vc + ic(vc) * rs', 'vd') + didv_simple = didv.subs(vc + ic * rs, 'vd') fdidv_expected = np.float(didv_simple.evalf(subs=expected_data)) LOGGER.debug('fdidv test: %g, expected: %g', fdidv_test, fdidv_expected) assert np.isclose(fdidv_test, fdidv_expected) # jacobian - d_didv_isat1 = didv.diff(isat1).subs('vc + ic(vc) * rs', 'vd') - d_didv_isat2 = didv.diff(isat2).subs('vc + ic(vc) * rs', 'vd') - d_didv_rs = didv.diff(rs).subs('vc + ic(vc) * rs', 'vd') - d_didv_rsh = didv.diff(rsh).subs('vc + ic(vc) * rs', 'vd') - d_didv_ic = didv.diff(ic).subs('vc + ic(vc) * rs', 'vd') - d_didv_vc = didv.diff(vc).subs('vc + ic(vc) * rs', 'vd') + d_didv_isat1 = didv.diff(isat1).subs(vc + ic * rs, 'vd') + d_didv_isat2 = didv.diff(isat2).subs(vc + ic * rs, 'vd') + d_didv_rs = didv.diff(rs).subs(vc + ic * rs, 'vd') + d_didv_rsh = didv.diff(rsh).subs(vc + ic * rs, 'vd') + d_didv_ic = didv.diff(ic).subs(vc + ic * rs, 'vd') + d_didv_vc = didv.diff(vc).subs(vc + ic * rs, 'vd') # update expected test data with calculated derivative - expected_data['Derivative(ic(vc), vc)'] = fdidv_expected + expected_data[di_dv] = fdidv_expected jdidv_expected = np.array([ d_didv_isat1.evalf(subs=expected_data), d_didv_isat2.evalf(subs=expected_data), @@ -88,17 +88,17 @@ def test_didv_dpdv_frsh(): dpdv = didv * vc + ic # test fdpdv fdpdv_test, jdpdv_test = fdpdv(**test_data) - dpdv_simple = dpdv.subs('vc + ic(vc) * rs', 'vd') + dpdv_simple = dpdv.subs(vc + ic * rs, 'vd') fdpdv_expected = np.float(dpdv_simple.evalf(subs=expected_data)) LOGGER.debug('fdpdv test: %g, expected: %g', fdpdv_test, fdpdv_expected) assert np.isclose(fdpdv_test, fdpdv_expected) # jacobian - d_dpdv_isat1 = dpdv.diff(isat1).subs('vc + ic(vc) * rs', 'vd') - d_dpdv_isat2 = dpdv.diff(isat2).subs('vc + ic(vc) * rs', 'vd') - d_dpdv_rs = dpdv.diff(rs).subs('vc + ic(vc) * rs', 'vd') - d_dpdv_rsh = dpdv.diff(rsh).subs('vc + ic(vc) * rs', 'vd') - d_dpdv_ic = dpdv.diff(ic).subs('vc + ic(vc) * rs', 'vd') - d_dpdv_vc = dpdv.diff(vc).subs('vc + ic(vc) * rs', 'vd') + d_dpdv_isat1 = dpdv.diff(isat1).subs(vc + ic * rs, 'vd') + d_dpdv_isat2 = dpdv.diff(isat2).subs(vc + ic * rs, 'vd') + d_dpdv_rs = dpdv.diff(rs).subs(vc + ic * rs, 'vd') + d_dpdv_rsh = dpdv.diff(rsh).subs(vc + ic * rs, 'vd') + d_dpdv_ic = dpdv.diff(ic).subs(vc + ic * rs, 'vd') + d_dpdv_vc = dpdv.diff(vc).subs(vc + ic * rs, 'vd') jdpdv_expected = np.array([ d_dpdv_isat1.evalf(subs=expected_data), d_dpdv_isat2.evalf(subs=expected_data), @@ -117,23 +117,23 @@ def test_didv_dpdv_frsh(): del test_data['ic'], test_data['vc'] # remove Ic, Vc test_data['isc'] = ISC0 # add Isc frsh_test, jfrsh_test = fjrsh(**test_data) - frsh_simple = frsh.subs('vc + ic(vc) * rs', 'vd') + frsh_simple = frsh.subs(vc + ic * rs, 'vd') # update expected test data with calculated derivative - expected_data['ic(vc)'] = ISC0 - expected_data['vc'] = 0 + expected_data[ic] = ISC0 + expected_data[vc] = 0 expected_data['vd'] = ISC0 * RS_2 didv_isc = np.float(didv_simple.evalf(subs=expected_data)) - expected_data['Derivative(ic(vc), vc)'] = didv_isc + expected_data[di_dv] = didv_isc frsh_expected = np.float(frsh_simple.evalf(subs=expected_data)) LOGGER.debug('frsh test: %r, expected: %r', frsh_test, frsh_expected) assert np.isclose(frsh_test, frsh_expected) # jacobian - dfrsh_isat1 = frsh.diff(isat1).subs('vc + ic(vc) * rs', 'vd') - dfrsh_isat2 = frsh.diff(isat2).subs('vc + ic(vc) * rs', 'vd') - dfrsh_rs = frsh.diff(rs).subs('vc + ic(vc) * rs', 'vd') - dfrsh_rsh = frsh.diff(rsh).subs('vc + ic(vc) * rs', 'vd') - dfrsh_ic = frsh.diff(ic).subs('vc + ic(vc) * rs', 'vd') - dfrsh_vc = frsh.diff(vc).subs('vc + ic(vc) * rs', 'vd') + dfrsh_isat1 = frsh.diff(isat1).subs(vc + ic * rs, 'vd') + dfrsh_isat2 = frsh.diff(isat2).subs(vc + ic * rs, 'vd') + dfrsh_rs = frsh.diff(rs).subs(vc + ic * rs, 'vd') + dfrsh_rsh = frsh.diff(rsh).subs(vc + ic * rs, 'vd') + dfrsh_ic = frsh.diff(ic).subs(vc + ic * rs, 'vd') + dfrsh_vc = frsh.diff(vc).subs(vc + ic * rs, 'vd') jfrsh_expected = np.array([ dfrsh_isat1.evalf(subs=expected_data), dfrsh_isat2.evalf(subs=expected_data), diff --git a/pvmismatch/pvmismatch_lib/pvcell.py b/pvmismatch/pvmismatch_lib/pvcell.py index 28f0566..22b94f9 100644 --- a/pvmismatch/pvmismatch_lib/pvcell.py +++ b/pvmismatch/pvmismatch_lib/pvcell.py @@ -5,6 +5,8 @@ object which is used by modules, strings and systems. """ +from __future__ import absolute_import +from future.utils import iteritems from pvmismatch.pvmismatch_lib.pvconstants import PVconstants import numpy as np from matplotlib import pyplot as plt @@ -14,7 +16,7 @@ RS = 0.004267236774264931 # [ohm] series resistance RSH = 10.01226369025448 # [ohm] shunt resistance ISAT1_T0 = 2.286188161253440E-11 # [A] diode one saturation current -ISAT2 = 1.117455042372326E-6 # [A] diode two saturation current +ISAT2_T0 = 1.117455042372326E-6 # [A] diode two saturation current ISC0_T0 = 6.3056 # [A] reference short circuit current TCELL = 298.15 # [K] cell temperature ARBD = 1.036748445065697E-4 # reverse breakdown coefficient 1 @@ -45,7 +47,10 @@ class PVcell(object): :param pvconst: configuration constants object :type pvconst: :class:`~pvmismatch.pvmismatch_lib.pvconstants.PVconstants` """ - def __init__(self, Rs=RS, Rsh=RSH, Isat1_T0=ISAT1_T0, Isat2=ISAT2, + + _calc_now = False #: if True ``calcCells()`` is called in ``__setattr__`` + + def __init__(self, Rs=RS, Rsh=RSH, Isat1_T0=ISAT1_T0, Isat2_T0=ISAT2_T0, Isc0_T0=ISC0_T0, aRBD=ARBD, bRBD=BRBD, VRBD=VRBD_, nRBD=NRBD, Eg=EG, alpha_Isc=ALPHA_ISC, Tcell=TCELL, Ee=1., pvconst=PVconstants()): @@ -53,7 +58,7 @@ def __init__(self, Rs=RS, Rsh=RSH, Isat1_T0=ISAT1_T0, Isat2=ISAT2, self.Rs = Rs #: [ohm] series resistance self.Rsh = Rsh #: [ohm] shunt resistance self.Isat1_T0 = Isat1_T0 #: [A] diode one sat. current at T0 - self.Isat2 = Isat2 #: [A] diode two saturation current + self.Isat2_T0 = Isat2_T0 #: [A] diode two saturation current self.Isc0_T0 = Isc0_T0 #: [A] short circuit current at T0 self.aRBD = aRBD #: reverse breakdown coefficient 1 self.bRBD = bRBD #: reverse breakdown coefficient 2 @@ -67,6 +72,8 @@ def __init__(self, Rs=RS, Rsh=RSH, Isat1_T0=ISAT1_T0, Isat2=ISAT2, self.Icell = None #: cell currents on IV curve [A] self.Vcell = None #: cell voltages on IV curve [V] self.Pcell = None #: cell power on IV curve [W] + # set calculation flag + self._calc_now = True # overwrites the class attribute def __str__(self): fmt = '' @@ -76,27 +83,28 @@ def __repr__(self): return str(self) def __setattr__(self, key, value): + # check for floats try: value = np.float64(value) except (TypeError, ValueError): - pass + pass # fail silently if not float, eg: pvconst or _calc_now super(PVcell, self).__setattr__(key, value) - # after all attributes have been initialized, recalculate IV curve - # every time __setattr__() is called - if hasattr(self, 'pvconst'): + # recalculate IV curve + if self._calc_now: Icell, Vcell, Pcell = self.calcCell() - super(PVcell, self).__setattr__('Icell', Icell) - super(PVcell, self).__setattr__('Vcell', Vcell) - super(PVcell, self).__setattr__('Pcell', Pcell) + self.__dict__.update(Icell=Icell, Vcell=Vcell, Pcell=Pcell) def update(self, **kwargs): """ Update user-defined constants. """ - # TODO: use __dict__.update(), check for floats and update IV curve - # self.__dict__.update(kwargs) - for k, v in kwargs.iteritems(): + # turn off calculation flag until all attributes are updated + self._calc_now = False + # don't use __dict__.update() instead use setattr() to go through + # custom __setattr__() so that numbers are cast to floats + for k, v in iteritems(kwargs): setattr(self, k, v) + self._calc_now = True # recalculate @property def Vt(self): @@ -134,6 +142,18 @@ def Isat1(self): ) return self.Isat1_T0 * _Tstar * _expTstar # [A] Isat1(Tcell) + @property + def Isat2(self): + """ + Diode two saturation current at Tcell in amps. + """ + _Tstar = self.Tcell ** 3. / self.pvconst.T0 ** 3. # scaled temperature + _inv_delta_T = 1. / self.pvconst.T0 - 1. / self.Tcell # [1/K] + _expTstar = np.exp( + self.Eg * self.pvconst.q / (2.0 * self.pvconst.k) * _inv_delta_T + ) + return self.Isat2_T0 * _Tstar * _expTstar # [A] Isat2(Tcell) + @property def Isc0(self): """ @@ -274,4 +294,4 @@ def plot(self): plt.xlim(0, self.Voc) plt.ylim(0, (self.Isc + 1) * self.Voc) plt.grid() - return cell_plot \ No newline at end of file + return cell_plot diff --git a/pvmismatch/pvmismatch_lib/pvconstants.py b/pvmismatch/pvmismatch_lib/pvconstants.py index f9d2d03..5c17c8b 100644 --- a/pvmismatch/pvmismatch_lib/pvconstants.py +++ b/pvmismatch/pvmismatch_lib/pvconstants.py @@ -74,23 +74,39 @@ class PVconstants(object): T0 = 298.15 #: [K] reference temperature def __init__(self, npts=NPTS): - # set number of points in IV curve(s) - self.npts = npts #: number of points in IV curves - # point spacing from 0 to 1, used for Vcell, Vmod, Vsys and Istring - # decrease point spacing as voltage approaches Voc by using logspace - pts = (11. - np.logspace(np.log10(11.), 0., self.npts)) / 10. - pts[0] = 0. # first point must be exactly zero - self.pts = pts.reshape((self.npts, 1)) + self._npts = None + self.pts = None """array of points with decreasing spacing from 0 to 1""" - negpts = (11. - np.logspace(np.log10(11. - 1. / float(self.npts)), - 0., self.npts)) / 10. - negpts = negpts.reshape((self.npts, 1)) - self.Imod_negpts = 1 + 1. / float(self.npts) / 10. - negpts + self.Imod_negpts = None """array of points with decreasing spacing from 1 to just less than but not including zero""" - self.negpts = np.flipud(negpts) # reverse the order + self.negpts = None """array of points with increasing spacing from 1 to just less than but not including zero""" + self.Imod_pts = None + """array of points with increasing spacing from 0 to 1""" + # call property setter + self.npts = npts #: number of points in IV curves + + @property + def npts(self): + """number of points in IV curves""" + return self._npts + + @npts.setter + def npts(self, npts): + # set number of points in IV curve(s) + self._npts = npts # number of points in IV curves + # point spacing from 0 to 1, used for Vcell, Vmod, Vsys and Istring + # decrease point spacing as voltage approaches Voc by using logspace + pts = (11. - np.logspace(np.log10(11.), 0., self._npts)) / 10. + pts[0] = 0. # first point must be exactly zero + self.pts = pts.reshape((self._npts, 1)) + negpts = (11. - np.logspace(np.log10(11. - 1. / float(self._npts)), + 0., self._npts)) / 10. + negpts = negpts.reshape((self._npts, 1)) + self.Imod_negpts = 1 + 1. / float(self._npts) / 10. - negpts + self.negpts = np.flipud(negpts) # reverse the order # shift and concatenate pvconst.negpts and pvconst.pts # so that tight spacing is around MPP and RBD self.Imod_pts = 1 - np.flipud(self.pts) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index 926cf46..8613985 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -3,6 +3,10 @@ This module defines the :class:`~pvmismatch.pvmismatch_lib.pvmodule.PVmodule`. """ +from __future__ import absolute_import +from past.builtins import xrange, range +from builtins import zip +from six import itervalues import numpy as np from copy import copy from matplotlib import pyplot as plt @@ -53,6 +57,7 @@ def standard_cellpos_pat(nrows, ncols_per_substr): cellpos.append(newsubstr) return cellpos + # standard cell positions presets STD24 = standard_cellpos_pat(1, [1] * 24) STD72 = standard_cellpos_pat(12, [2, 2, 2]) @@ -118,6 +123,7 @@ def crosstied_cellpos_pat(nrows_per_substrs, ncols, partial=False): Substrings have 27, 28 and 27 rows of cells per diode """ + def combine_parallel_circuits(IVprev_cols, pvconst): """ Combine crosstied circuits in a substring @@ -147,6 +153,7 @@ def combine_parallel_circuits(IVprev_cols, pvconst): Irows, Vrows, Isc_rows.mean(), Imax_rows.max() ) + class PVmodule(object): """ A Class for PV modules. @@ -154,28 +161,42 @@ class PVmodule(object): :param cell_pos: cell position pattern :type cell_pos: dict :param pvcells: list of :class:`~pvmismatch.pvmismatch_lib.pvcell.PVcell` - :type pvcells: list + :type pvcells: list, :class:`~pvmismatch.pvmismatch_lib.pvcell.PVcell` :param pvconst: An object with common parameters and constants. :type pvconst: :class:`~pvmismatch.pvmismatch_lib.pvconstants.PVconstants` :param Vbypass: bypass diode trigger voltage [V] :param cellArea: cell area [cm^2] """ - def __init__(self, cell_pos=STD96, pvcells=None, pvconst=PVconstants(), + def __init__(self, cell_pos=STD96, pvcells=None, pvconst=None, Vbypass=VBYPASS, cellArea=CELLAREA): # TODO: check cell position pattern self.cell_pos = cell_pos #: cell position pattern dictionary self.numberCells = sum([len(c) for s in self.cell_pos for c in s]) """number of cells in the module""" + # is pvcells a list? + try: + pvc0 = pvcells[0] + except TypeError: + # is pvcells an object? + try: + pvconst = pvcells.pvconst + except AttributeError: + # try to use the pvconst arg or create one if none + if not pvconst: + pvconst = PVconstants() + # create pvcell + pvcells = PVcell(pvconst=pvconst) + # expand pvcells to list + pvcells = [pvcells] * self.numberCells + else: + pvconst = pvc0.pvconst + for p in pvcells: + if p.pvconst is not pvconst: + raise Exception('PVconstant must be the same for all cells') self.pvconst = pvconst #: configuration constants self.Vbypass = Vbypass #: [V] trigger voltage of bypass diode self.cellArea = cellArea #: [cm^2] cell area - if pvcells is None: - # faster to use copy instead of making each object in a for-loop - # use copy instead of deepcopy to keep same pvconst for all objects - # PVcell.calcCell() creates new np.ndarray if attributes change - pvcells = PVcell(pvconst=self.pvconst) - if isinstance(pvcells, PVcell): - pvcells = [pvcells] * self.numberCells + # check cell position pattern matches list of cells if len(pvcells) != self.numberCells: # TODO: use pvexception raise Exception( @@ -244,7 +265,7 @@ def setSuns(self, Ee, cells=None): else: new_pvcells[cell_id] = old_pvcells[pvcell] self.pvcells = new_pvcells - pvcell_set = old_pvcells.itervalues() + pvcell_set = itervalues(old_pvcells) for pvc in pvcell_set: pvc.Ee = Ee elif np.size(Ee) == self.numberCells: @@ -289,8 +310,10 @@ def setSuns(self, Ee, cells=None): raise Exception("Input irradiance value (Ee) for each cell!") self.Imod, self.Vmod, self.Pmod, self.Isubstr, self.Vsubstr = self.calcMod() -# TODO setTemps is a nearly identical copy of setSuns. The DRY principle says that we should not be copying code. -# TODO Replace both setSuns() and setTemps() with a single method for updating cell parameters that works for all params + # TODO setTemps is a nearly identical copy of setSuns. The DRY principle + # says that we should not be copying code. + # TODO Replace both setSuns() and setTemps() with a single method for + # updating cell parameters that works for all params def setTemps(self, Tc, cells=None): """ @@ -314,7 +337,7 @@ def setTemps(self, Tc, cells=None): else: new_pvcells[cell_id] = old_pvcells[pvcell] self.pvcells = new_pvcells - pvcell_set = old_pvcells.itervalues() + pvcell_set = itervalues(old_pvcells) for pvc in pvcell_set: pvc.Tcell = Tc elif np.size(Tc) == self.numberCells: diff --git a/pvmismatch/pvmismatch_lib/pvstring.py b/pvmismatch/pvmismatch_lib/pvstring.py index e0693a0..86a27f0 100644 --- a/pvmismatch/pvmismatch_lib/pvstring.py +++ b/pvmismatch/pvmismatch_lib/pvstring.py @@ -4,6 +4,9 @@ class. """ +from __future__ import absolute_import +from past.builtins import range +from future.utils import iteritems import numpy as np from copy import copy from matplotlib import pyplot as plt @@ -23,23 +26,32 @@ class PVstring(object): :param pvconst: a configuration constants object """ def __init__(self, numberMods=NUMBERMODS, pvmods=None, - pvconst=PVconstants()): - self.pvconst = pvconst - self.numberMods = numberMods - if pvmods is None: - # use deepcopy instead of making each object in for-loop, 2x faster - pvmods = PVmodule(pvconst=self.pvconst) - if isinstance(pvmods, PVmodule): - pvmods = [pvmods] * self.numberMods - # reset pvconsts in all pvcells and pvmodules + pvconst=None): + # is pvmods a list? + try: + pvmod0 = pvmods[0] + except TypeError: + # is pvmods an object? + try: + pvconst = pvmods.pvcons + except AttributeError: + # try to use the pvconst arg or create one if none + if not pvconst: + pvconst = PVconstants() + # create pvmod + pvmods = PVmodule(pvconst=pvconst) + # expand pvmods to list + pvmods = [pvmods] * numberMods + else: + pvconst = pvmod0.pvconst + numberMods = len(pvmods) for p in pvmods: - for c in p.pvcells: - c.pvconst = self.pvconst - p.pvconst = self.pvconst - if len(pvmods) != self.numberMods: - # TODO: use pvmismatch exceptions - raise Exception("Number of modules doesn't match.") - self.pvmods = pvmods + if p.pvconst is not pvconst: + raise Exception('pvconst must be the same for all modules') + self.pvconst = pvconst #: ``PVconstants`` used in ``PVstring`` + self.numberMods = numberMods #: number of module in string + self.pvmods = pvmods #: list of ``PVModule`` in ``PVstring`` + # calculate string self.Istring, self.Vstring, self.Pstring = self.calcString() # TODO: use __getattr__ to check for updates to pvcells @@ -101,7 +113,7 @@ def setSuns(self, Ee): else: self.pvmods = copy(self.pvmods) # copy list first try: - for pvmod, cell_Ee in Ee.iteritems(): + for pvmod, cell_Ee in iteritems(Ee): pvmod = int(pvmod) self.pvmods[pvmod] = copy(self.pvmods[pvmod]) if hasattr(cell_Ee, 'keys'): @@ -164,7 +176,7 @@ def setTemps(self, Tc): else: self.pvmods = copy(self.pvmods) # copy list first try: - for pvmod, cell_Tc in Tc.iteritems(): + for pvmod, cell_Tc in iteritems(Tc): pvmod = int(pvmod) self.pvmods[pvmod] = copy(self.pvmods[pvmod]) if hasattr(cell_Tc, 'keys'): diff --git a/pvmismatch/pvmismatch_lib/pvsystem.py b/pvmismatch/pvmismatch_lib/pvsystem.py index 0c9a68d..02f4e3b 100644 --- a/pvmismatch/pvmismatch_lib/pvsystem.py +++ b/pvmismatch/pvmismatch_lib/pvsystem.py @@ -4,6 +4,9 @@ class. """ +from __future__ import absolute_import +from past.builtins import basestring +from future.utils import iteritems import numpy as np from copy import copy from matplotlib import pyplot as plt @@ -24,21 +27,42 @@ class PVsystem(object): :param numberMods: number of modules per string :param pvmods: list of modules, a ``PVmodule`` object or None """ - def __init__(self, pvconst=PVconstants(), numberStrs=NUMBERSTRS, + def __init__(self, pvconst=None, numberStrs=NUMBERSTRS, pvstrs=None, numberMods=NUMBERMODS, pvmods=None): - self.pvconst = pvconst - self.numberStrs = numberStrs - self.numberMods = numberMods - if pvstrs is None: - pvstrs = PVstring(numberMods=self.numberMods, pvmods=pvmods, - pvconst=self.pvconst) - # use deep copy instead of making each object in a for-loop - if isinstance(pvstrs, PVstring): - pvstrs = [pvstrs] * self.numberStrs - if len(pvstrs) != self.numberStrs: - # TODO: use pvmismatch excecptions - raise Exception("Number of strings don't match.") - self.pvstrs = pvstrs + # is pvstrs a list? + try: + pvstr0 = pvstrs[0] + except TypeError: + # is pvstrs a PVstring object? + try: + pvconst = pvstrs.pvconst + except AttributeError: + # try to use the pvconst arg or create one if none + if not pvconst: + pvconst = PVconstants() + # create a pvstring + pvstrs = PVstring(numberMods=numberMods, pvmods=pvmods, + pvconst=pvconst) + # expand pvstrs to list + pvstrs = [pvstrs] * numberStrs + numberMods = [numberMods] * numberStrs + else: + pvconst = pvstr0.pvconst + numberStrs = len(pvstrs) + numberMods = [] + for p in pvstrs: + if p.pvconst is not pvconst: + raise Exception('pvconst must be the same for all strings') + numberMods.append(len(p.pvmods)) + self.pvconst = pvconst #: ``PVconstants`` used in ``PVsystem`` + self.numberStrs = numberStrs #: number strings in the system + self.numberMods = numberMods #: list of number of modules per string + self.pvstrs = pvstrs #: list of ``PVstring`` in system + # calculate pvsystem + self.update() + + def update(self): + """Update system calculations.""" self.Isys, self.Vsys, self.Psys = self.calcSystem() (self.Imp, self.Vmp, self.Pmp, self.Isc, self.Voc, self.FF, self.eff) = self.calcMPP_IscVocFFeff() @@ -117,13 +141,12 @@ def setSuns(self, Ee): for pvstr in self.pvstrs: pvstr.setSuns(Ee) else: - for pvstr, pvmod_Ee in Ee.iteritems(): + for pvstr, pvmod_Ee in iteritems(Ee): pvstr = int(pvstr) self.pvstrs[pvstr] = copy(self.pvstrs[pvstr]) self.pvstrs[pvstr].setSuns(pvmod_Ee) - self.Isys, self.Vsys, self.Psys = self.calcSystem() - (self.Imp, self.Vmp, self.Pmp, - self.Isc, self.Voc, self.FF, self.eff) = self.calcMPP_IscVocFFeff() + # calculate pvsystem + self.update() def setTemps(self, Tc): """ @@ -155,34 +178,26 @@ def setTemps(self, Tc): for pvstr in self.pvstrs: pvstr.setTemps(Tc) else: - for pvstr, pvmod_Tc in Tc.iteritems(): + for pvstr, pvmod_Tc in iteritems(Tc): pvstr = int(pvstr) self.pvstrs[pvstr] = copy(self.pvstrs[pvstr]) self.pvstrs[pvstr].setTemps(pvmod_Tc) - self.Isys, self.Vsys, self.Psys = self.calcSystem() - (self.Imp, self.Vmp, self.Pmp, - self.Isc, self.Voc, self.FF, self.eff) = self.calcMPP_IscVocFFeff() + # calculate pvsystem + self.update() def plotSys(self, sysPlot=None): """ Plot system I-V curves. - Arguments sysPlot : matplotlib.figure.Figure - Returns sysPlot : matplotlib.figure.Figure + + :param sysPlot: integer, string, or existing figure + :returns: new figure """ - # create new figure if sysPlot is None - # or make the specified sysPlot current and clear it - if not sysPlot: - sysPlot = plt.figure() - elif isinstance(sysPlot, (int, basestring)): + # create new figure if sysPlot or make the specified sysPlot current + # and clear it + try: + sysPlot.clear() + except (AttributeError, SyntaxError): sysPlot = plt.figure(sysPlot) - else: - try: - sysPlot = plt.figure(sysPlot.number) - except TypeError as e: - print '%s is not a figure.' % sysPlot - print 'Sorry, "plotSys" takes a "int", "str" or "Figure".' - raise e - sysPlot.clear() plt.subplot(2, 1, 1) plt.plot(self.Vsys, self.Isys) plt.xlim(0, self.Voc * 1.1) diff --git a/pvmismatch/tests/__init__.py b/pvmismatch/tests/__init__.py index 98d0635..6cdf796 100644 --- a/pvmismatch/tests/__init__.py +++ b/pvmismatch/tests/__init__.py @@ -5,5 +5,4 @@ from nose.tools import ok_ def test_parallel_cals_is_gone(): - from pvmismatch import * - + from pvmismatch import pvconstants, pvcell, pvmodule, pvstring, pvsystem diff --git a/pvmismatch/tests/calc_series_test_iv.dat b/pvmismatch/tests/calc_series_test_iv.dat index feef269..8b87aac 100644 --- a/pvmismatch/tests/calc_series_test_iv.dat +++ b/pvmismatch/tests/calc_series_test_iv.dat @@ -1,2 +1,2 @@ -2.669944976071763776e+01 2.613032759910023373e+01 2.557469007107548720e+01 2.503221767521580787e+01 2.450259848027790710e+01 2.398552794583688552e+01 2.348070874717004486e+01 2.298785060428993887e+01 2.250667011502820003e+01 2.203689059207428969e+01 2.157824190387539076e+01 2.113046031930595703e+01 2.069328835601767125e+01 2.026647463238250069e+01 1.984977372294382647e+01 1.944294601729244576e+01 1.904575758228635252e+01 1.865798002753500739e+01 1.827939037407083234e+01 1.790977092613237431e+01 1.754890914598532703e+01 1.719659753170961380e+01 1.685263349788209908e+01 1.651681925908636117e+01 1.618896171618258606e+01 1.586887234527211632e+01 1.555636708929292666e+01 1.525126625218346632e+01 1.495339439555430872e+01 1.466258023780793351e+01 1.437865655564878153e+01 1.410146008792689898e+01 1.383083144175985879e+01 1.356661500087907513e+01 1.330865883614763234e+01 1.305681461819839129e+01 1.281093753214193676e+01 1.257088619429546661e+01 1.233652257088466975e+01 1.210771189867184106e+01 1.188432260746466440e+01 1.166622624446097589e+01 1.145329740038615718e+01 1.124541363738058308e+01 1.104245541859565449e+01 1.084430603945801863e+01 1.065085156056234794e+01 1.046198074215418572e+01 1.027758498016509314e+01 1.009755824376340883e+01 9.921797014384612368e+00 9.750200226206345633e+00 9.582669208033740560e+00 9.419107626561764590e+00 9.259421430981848999e+00 9.103518798900990916e+00 8.951310083542285412e+00 8.802707762196401831e+00 8.657626385894511500e+00 8.515982530273577211e+00 8.377694747605900716e+00 8.242683519965208205e+00 8.110871213502454324e+00 7.982182033804972932e+00 7.856541982313340000e+00 7.733878813770909133e+00 7.614121994681481098e+00 7.497202662751325519e+00 7.383053587292115694e+00 7.271609130562099566e+00 7.162805210023236668e+00 7.056579261492607280e+00 6.952870203166881424e+00 6.851618400499221551e+00 6.752765631908337340e+00 6.656255055300074552e+00 6.562031175382167625e+00 6.470039811753497183e+00 6.380228067749364129e+00 6.292544300025006088e+00 6.206938088859729419e+00 6.123360209164695256e+00 6.041762602177612074e+00 5.962098347828096223e+00 5.884321637757793155e+00 5.808387748979784604e+00 5.734253018162040583e+00 5.661874816520262144e+00 5.591211525305553209e+00 5.522222511872925566e+00 5.454868106316775211e+00 5.389109578660011834e+00 5.324909116583625313e+00 5.262229803683930562e+00 5.201035598244977187e+00 5.141291312513935452e+00 5.082962592467512586e+00 5.026015898057764275e+00 4.970418483925993947e+00 4.916138380573573663e+00 4.863144375978934342e+00 4.858265015226658434e+00 4.846309541342376015e+00 4.834064033207210898e+00 4.821521454724768674e+00 4.808674599106222303e+00 4.795516084729388062e+00 4.782038350897351187e+00 4.768233653494201363e+00 4.754094060535364541e+00 4.739611447609998862e+00 4.724777493212829249e+00 4.709583673962719708e+00 4.694021259705258409e+00 4.678081308496541801e+00 4.661754661465254301e+00 4.645031937550106349e+00 4.627903528109619025e+00 4.610359591401121726e+00 4.592390046925836522e+00 4.573984569636761144e+00 4.555132584006038599e+00 4.535823257948413456e+00 4.516045496597267395e+00 4.495787935929662638e+00 4.475038936236737541e+00 4.453786575435692008e+00 4.432018642219527571e+00 4.409722629040600417e+00 4.386885724923965668e+00 4.363494808106365141e+00 4.339536438496654824e+00 4.314996849953309344e+00 4.289861942374597170e+00 4.264117273596860436e+00 4.237748051096248858e+00 4.210739123489144475e+00 4.183074971826389543e+00 4.154739700676322478e+00 4.125717028991478408e+00 4.095990280753735391e+00 4.065542375392505292e+00 4.034355817970482150e+00 4.002412689131294776e+00 3.969694634803298428e+00 3.936182855653584500e+00 3.901858096286147770e+00 3.866700634178006357e+00 3.830690268346921279e+00 3.793806307744192807e+00 3.756027559365878510e+00 3.717332316075590892e+00 3.677698344131880681e+00 3.637102870413045252e+00 3.595522569332014307e+00 3.552933549433792138e+00 3.509311339667769758e+00 3.464630875326998982e+00 3.418866483646366916e+00 3.371991869051390367e+00 3.323980098049143628e+00 3.274803583752656788e+00 3.224434070029878363e+00 3.172842615268099742e+00 3.119999575744511588e+00 3.065874588593340277e+00 3.010436554359774863e+00 2.953653619130651720e+00 2.895493156231651266e+00 2.835921747480469435e+00 2.774905163985200041e+00 2.712408346476895993e+00 2.648395385165008165e+00 2.582829499104125848e+00 2.515673015060161610e+00 2.446887345863846264e+00 2.376432968239082566e+00 2.304269400093426512e+00 2.230355177257648869e+00 2.154647829661010761e+00 2.077103856928554038e+00 1.997678703386402077e+00 1.916326732460700288e+00 1.833001200455476098e+00 1.747654229694379335e+00 1.660236781010836271e+00 1.570698625570840168e+00 1.478988316012171289e+00 1.385053156883461867e+00 1.288839174366143148e+00 1.190291085261840465e+00 1.089352265227425676e+00 9.859647162394797881e-01 8.800690332694305340e-01 7.716043701502739438e-01 6.605084046152134292e-01 5.467173024881697430e-01 4.301656810055600744e-01 3.107865712482871245e-01 1.885113796623381077e-01 6.326984864590112778e-02 -6.500998381967448836e-02 --1.658825364743083242e+01 -1.657850972000861844e+01 -1.656899666268759574e+01 -1.655970900529117884e+01 -1.655042916679985154e+01 -1.654097102920747631e+01 -1.653173699028583954e+01 -1.652261843222667892e+01 -1.651325342234010662e+01 -1.650411030458043982e+01 -1.649492950671403690e+01 -1.648569026386044101e+01 -1.647666993324214246e+01 -1.646786332800913755e+01 -1.645926538420732044e+01 -1.645087115786659382e+01 -1.644267582215803003e+01 -1.643467466461832061e+01 -1.642652680291198664e+01 -1.641821864858097157e+01 -1.641010734551412753e+01 -1.640218822956842359e+01 -1.639445432881890241e+01 -1.638628671865030384e+01 -1.637831262972877155e+01 -1.637052747681183362e+01 -1.636289552996160168e+01 -1.635491225213807809e+01 -1.634711812803861974e+01 -1.633932897206909374e+01 -1.633110807898532357e+01 -1.632308196962026514e+01 -1.631524602881876262e+01 -1.630759575077595613e+01 -1.630012673644636578e+01 -1.629266062964801165e+01 -1.628485571781282459e+01 -1.627723573354401410e+01 -1.626964182948981374e+01 -1.626144531655762293e+01 -1.625344300968712474e+01 -1.624514962379390681e+01 -1.623692826947009848e+01 -1.622890170979349733e+01 -1.622106532935002221e+01 -1.621341462208200568e+01 -1.620441658154821951e+01 -1.619525718925444480e+01 -1.618631481723740961e+01 -1.617758432347469721e+01 -1.616906068777763750e+01 -1.616042530355715812e+01 -1.615094812307471273e+01 -1.614142275375474611e+01 -1.613173797583825575e+01 -1.612157720058961985e+01 -1.611023848876418896e+01 -1.609872727441375773e+01 -1.608748880380480628e+01 -1.607609562872138653e+01 -1.606334323900365746e+01 -1.605089300111619366e+01 -1.603706533420859159e+01 -1.602165327771594150e+01 -1.600529343835346907e+01 -1.598678155456367556e+01 -1.596663282757943847e+01 -1.594435716623476296e+01 -1.591740260929074502e+01 -1.588379900194431471e+01 -1.584105131328757743e+01 -1.577867859305574427e+01 -1.566738535562564749e+01 -1.536097512109854080e+01 -1.455840740606439709e+01 -1.360102364730117763e+01 -1.265195593853743006e+01 -1.172164659217995286e+01 -1.081289248955581428e+01 -1.016765923409469963e+01 -1.011186169765755416e+01 -1.008324767350937634e+01 -1.006010150356997102e+01 -1.003852552250499386e+01 -1.001788499907803853e+01 -9.996613079559985238e+00 -9.973824951048971599e+00 -9.949199722116123468e+00 -9.920097506088513484e+00 -9.885331579649950129e+00 -9.842228496683393502e+00 -9.781867187528421326e+00 -9.687145239793986562e+00 -9.504051019236808173e+00 -9.120388322626450162e+00 -8.574693718337451642e+00 -7.997560914387051412e+00 -7.425232657879871745e+00 -6.863394478158901357e+00 -6.313535960837567629e+00 -5.776402388610589078e+00 -5.726937327513828890e+00 -5.605739828023134308e+00 -5.481612497779218884e+00 -5.354468901958479243e+00 -5.224236399269633679e+00 -5.090840544018309544e+00 -4.953965776907400809e+00 -4.813564810837702268e+00 -4.669766723090186922e+00 -4.522688642095947564e+00 -4.375310635569198858e+00 -4.259519764393067121e+00 -4.209132587758952937e+00 -4.184429531612392950e+00 -4.168283841969996573e+00 -4.155893403050063384e+00 -4.145630483468648819e+00 -4.135909366917984720e+00 -4.127113842536297206e+00 -4.118924621741168579e+00 -4.111174015752409261e+00 -4.103732830744796445e+00 -4.096512894953026951e+00 -4.089059051688860080e+00 -4.081111338720711856e+00 -4.073198788938771742e+00 -4.065282634824468389e+00 -4.057443256492121009e+00 -4.048327954597137790e+00 -4.038879858724790495e+00 -4.029437426794290289e+00 -4.018715537011026129e+00 -4.007024171503541510e+00 -3.994872208631435218e+00 -3.980267285150523193e+00 -3.964718289801147044e+00 -3.946338744281925770e+00 -3.924675605493419983e+00 -3.898789750169147972e+00 -3.866769883903027782e+00 -3.825019567355987249e+00 -3.767755220525852877e+00 -3.683899727810909575e+00 -3.552162498560579529e+00 -3.347438117988351003e+00 -3.067802994855495236e+00 -2.741332681943918992e+00 -2.390199437956931128e+00 -2.024063661719449847e+00 -1.646258473800311428e+00 -1.258106825243687776e+00 -8.599683739310437502e-01 -4.518903593203638103e-01 -3.378715455928027467e-02 3.945525410735678529e-01 8.332528632880469299e-01 1.282514390424396966e+00 1.638708948387605613e+00 1.702167262737608322e+00 1.725856303942045589e+00 1.740747909605590005e+00 1.752017489785776538e+00 1.761203401540412328e+00 1.769112455474719070e+00 1.776173158061720603e+00 1.782560657972596108e+00 1.788538772854757708e+00 1.794096744496906037e+00 1.799379563775051594e+00 1.804395945411128022e+00 1.809236409061615314e+00 1.813933929730815464e+00 1.818473747833789789e+00 1.822918286980924174e+00 1.827221277083542095e+00 1.831469715592299652e+00 1.835631956244101914e+00 1.839736781687066891e+00 1.843823264941407469e+00 1.847822968713473291e+00 1.851791129104003009e+00 1.855726273960377704e+00 1.859671398299962197e+00 1.863542520943712422e+00 1.867409268089441632e+00 1.871298432368030173e+00 1.875134852408609998e+00 1.878983664413016008e+00 1.882827424958724905e+00 1.886676081316792652e+00 1.890502755522739831e+00 1.894367055205807837e+00 1.898221514963866596e+00 1.902074897043092072e+00 1.905970410028552831e+00 1.909849934358521573e+00 1.913757055634064841e+00 1.917690008271412561e+00 1.921630723239804439e+00 1.925583701796290903e+00 1.929603196819139610e+00 +2.669945233166134102e+01 2.613033010303713155e+01 2.557469250959322693e+01 2.503222004986441007e+01 2.450260079257066792e+01 2.398553019725125068e+01 2.348071093914845164e+01 2.298785273824064745e+01 2.250667219232610350e+01 2.203689261406170630e+01 2.157824387186283133e+01 2.113046223457288164e+01 2.069329021981323180e+01 2.026647644592624076e+01 1.984977548742640252e+01 1.944294773387629860e+01 1.904575925210638232e+01 1.865798165169922740e+01 1.827939195366099412e+01 1.790977246220460728e+01 1.754891063957073527e+01 1.719659898381486585e+01 1.685263490949001763e+01 1.651682063115647736e+01 1.618896304965170074e+01 1.586887364105483300e+01 1.555636834828217019e+01 1.525126747525101578e+01 1.495339558355128418e+01 1.466258139156528628e+01 1.437865767597778088e+01 1.410146117561958334e+01 1.383083249758950473e+01 1.356661602560063784e+01 1.330865983049817913e+01 1.305681558289752431e+01 1.281093846789221047e+01 1.257088710178278745e+01 1.233652345077869228e+01 1.210771275162635519e+01 1.188432343411796666e+01 1.166622704543624067e+01 1.145329817629179381e+01 1.124541438881058397e+01 1.104245614612993975e+01 1.084430674366276648e+01 1.065085224199032155e+01 1.046198140134505472e+01 1.027758561764573564e+01 1.009755886004822045e+01 9.921797609975804733e+00 9.750200801594225197e+00 9.582669763697001741e+00 9.419108162967756215e+00 9.259421948586846085e+00 9.103519298150462902e+00 8.951310564871141295e+00 8.802708226029247740e+00 8.657626832645895831e+00 8.515982960348219777e+00 8.377695161398937884e+00 8.242683917862414944e+00 8.110871595880457363e+00 7.982182401031483465e+00 7.856542334747352641e+00 7.733879151762913295e+00 7.614122318573662618e+00 7.497202972877762051e+00 7.383053883978968557e+00 7.271609414127802040e+00 7.162805480778679623e+00 7.056579519741313256e+00 6.952870449205181380e+00 6.851618634616425396e+00 6.752765854386900912e+00 6.656255266415761263e+00 6.562031375404206557e+00 6.470040000944737635e+00 6.380228246366428380e+00 6.292544468318435946e+00 6.206938247074130999e+00 6.123360357538877530e+00 6.041762740944727206e+00 5.962098477215771908e+00 5.884321757988263180e+00 5.808387860270016745e+00 5.734253120723862729e+00 5.661874910560483087e+00 5.591211611026082551e+00 5.522222589470886511e+00 5.454868175984623591e+00 5.389109640585640015e+00 5.324909170950475890e+00 5.262229850671099385e+00 5.201035638027317276e+00 5.141291345262154699e+00 5.082962618348275896e+00 5.026015917233788599e+00 4.970418496556138876e+00 4.916138386812934691e+00 4.863144375978934342e+00 4.858265122421303595e+00 4.846309911186750696e+00 4.834064672073075997e+00 4.821522369138460995e+00 4.808675795752403559e+00 4.795517570454887135e+00 4.782040132715098757e+00 4.768235738587258687e+00 4.754096456261049042e+00 4.739614161504114520e+00 4.724780532993994697e+00 4.709587047536802906e+00 4.694024975169920566e+00 4.678085374145891429e+00 4.661759085794607849e+00 4.645036729260874964e+00 4.627908696114305442e+00 4.610365144828441508e+00 4.592395995125965413e+00 4.573990922186702335e+00 4.555139350715132096e+00 4.535830448863967135e+00 4.516053122010334597e+00 4.495796006380951226e+00 4.475047462522668162e+00 4.453795568614601130e+00 4.432028113618022402e+00 4.409732590260067475e+00 4.386896187847233008e+00 4.363505784904535112e+00 4.339547941636094031e+00 4.315008892202812696e+00 4.289874536812725125e+00 4.264130433619452987e+00 4.237761790424121600e+00 4.210753456175975629e+00 4.183089912266792609e+00 4.154755263614116068e+00 4.125733229528161239e+00 4.096007134357160240e+00 4.065559897905769660e+00 4.034374025621027648e+00 4.002431598540233892e+00 3.969714262994964216e+00 3.936203220065312980e+00 3.901879214778296134e+00 3.866722525044215431e+00 3.830712950324627553e+00 3.793829800025394494e+00 3.756051881608162279e+00 3.717357488413427724e+00 3.677724387188193944e+00 3.637129805311062114e+00 3.595550417707400470e+00 3.552962333447085008e+00 3.509341082017110924e+00 3.464661599261175784e+00 3.418898212978172424e+00 3.372024628171305327e+00 3.324013911939349697e+00 3.274838478001391717e+00 3.224470070846138903e+00 3.172879749496700708e+00 3.120037870881510855e+00 3.065914072801837698e+00 3.010477256486092656e+00 2.953695568720911435e+00 2.895536383548749093e+00 2.835966283521466291e+00 2.774951040499130883e+00 2.712455595983012113e+00 2.648444040971463131e+00 2.582879595327113531e+00 2.515724586643519167e+00 2.446940428599131412e+00 2.376487598786140687e+00 2.304325616001452470e+00 2.230413016986765307e+00 2.154707332604363312e+00 2.077165063434949666e+00 1.997741654783489196e+00 1.916391471078717768e+00 1.833067769651574697e+00 1.747722673877534216e+00 1.660307145667363926e+00 1.570770957290533953e+00 1.479062662515070681e+00 1.385129567047272747e+00 1.288917698254322897e+00 1.190371774152360729e+00 1.089435171642239908e+00 9.860498939746982217e-01 8.801565374262327879e-01 7.716942571665690309e-01 6.606007322980662622e-01 5.468121300470185631e-01 4.302630690862390139e-01 3.108865819678667819e-01 1.886140766448214379e-01 6.337529705878086617e-02 -6.490171722797288822e-02 +-1.658825367061012912e+01 -1.657850974204069416e+01 -1.656899668359963584e+01 -1.655970902510971499e+01 -1.655042917632658117e+01 -1.654097103762063625e+01 -1.653173699761180515e+01 -1.652261843680760478e+01 -1.651325342581841937e+01 -1.650411030698226966e+01 -1.649492950953737846e+01 -1.648569026559598427e+01 -1.647666993391565526e+01 -1.646786332764578731e+01 -1.645926538283167062e+01 -1.645087115550263235e+01 -1.644267581882917284e+01 -1.643467466034742941e+01 -1.642652678184029469e+01 -1.641821862653110031e+01 -1.641010732250925841e+01 -1.640218820563117674e+01 -1.639445429998721693e+01 -1.638628668885698048e+01 -1.637831259899660452e+01 -1.637052744516306291e+01 -1.636289549842984314e+01 -1.635491221966638875e+01 -1.634711809464927512e+01 -1.633932891258334408e+01 -1.633110801853167260e+01 -1.632308190822163851e+01 -1.631524596649755665e+01 -1.630759568755402711e+01 -1.630012667234505841e+01 -1.629266055741767261e+01 -1.628485564466355484e+01 -1.627723565949759177e+01 -1.626964171658705638e+01 -1.626144520268983484e+01 -1.625344289487716765e+01 -1.624514950831694193e+01 -1.623692815302517545e+01 -1.622890159240355246e+01 -1.622106521103744114e+01 -1.621341450286865538e+01 -1.620441639445639481e+01 -1.619525700108422228e+01 -1.618631462801433685e+01 -1.617758413322372135e+01 -1.616906049652311594e+01 -1.616042503368588967e+01 -1.615094785208763284e+01 -1.614142248096893439e+01 -1.613173770191218637e+01 -1.612157690771130802e+01 -1.611023808780480593e+01 -1.609872687209907127e+01 -1.608748840016694004e+01 -1.607609507978980190e+01 -1.606334268857064274e+01 -1.605089244921732217e+01 -1.603706456414053605e+01 -1.602165250583331613e+01 -1.600529241322813334e+01 -1.598678052725880150e+01 -1.596663147657240955e+01 -1.594435540343845403e+01 -1.591740029220724928e+01 -1.588379603924751393e+01 -1.584104755458223224e+01 -1.577867130305049415e+01 -1.566736970957890662e+01 -1.536092043635840909e+01 -1.455831210324631186e+01 -1.360092413392142596e+01 -1.265185585323075301e+01 -1.172154623852128452e+01 -1.081279591369598592e+01 -1.019796654513543643e+01 -1.012977886666629956e+01 -1.009711297008852604e+01 -1.007184243130435775e+01 -1.004885109160030865e+01 -1.002731209210761243e+01 -1.000530453304772216e+01 -9.981940187750595328e+00 -9.956839671683859194e+00 -9.927353871121455953e+00 -9.892265656201432833e+00 -9.848905920387606372e+00 -9.788337638206783708e+00 -9.693388761463495484e+00 -9.510065199300123950e+00 -9.126274923702730746e+00 -8.580389957406246992e+00 -8.003133374102789688e+00 -7.430670376863545812e+00 -6.868730619617537059e+00 -6.318753350967470261e+00 -5.781535991720929246e+00 -5.732064307779182855e+00 -5.610845372944864629e+00 -5.486682560575538048e+00 -5.359523035892302723e+00 -5.229274220669563711e+00 -5.095861686093680731e+00 -4.958970230894122366e+00 -4.818556953074322635e+00 -4.674771632810086786e+00 -4.528160856326114647e+00 -4.386717960676890016e+00 -4.287680590552382931e+00 -4.238518345310968272e+00 -4.210856518780546232e+00 -4.192030494325236667e+00 -4.177664175680254388e+00 -4.165810191735625168e+00 -4.154827817686422620e+00 -4.145041279493751674e+00 -4.135952274032373310e+00 -4.127439495627632127e+00 -4.119352970976949635e+00 -4.111591242767024923e+00 -4.103640840380074373e+00 -4.095213716447545949e+00 -4.086878412990546217e+00 -4.078589242702843620e+00 -4.070388367473668723e+00 -4.060965517367282729e+00 -4.051245787051383829e+00 -4.041459897400883960e+00 -4.030517287782224400e+00 -4.018541272103885120e+00 -4.006184144845954975e+00 -3.991371979635998546e+00 -3.975580073455596164e+00 -3.957034447507025376e+00 -3.935125047061408310e+00 -3.909104085043796140e+00 -3.876875988950297725e+00 -3.835018018827938580e+00 -3.777552474186947506e+00 -3.693593326024526391e+00 -3.561698840608622518e+00 -3.356904756622792974e+00 -3.077169807775146815e+00 -2.750591035016633157e+00 -2.399334261682617875e+00 -2.033063183417098241e+00 -1.655152682289718236e+00 -1.266904373031822351e+00 -8.686472086312366780e-01 -4.604528085049592878e-01 -4.223061541108918959e-02 3.861884383825868738e-01 8.249780884773688650e-01 1.274315063875723686e+00 1.620956386932553617e+00 1.686835442986216016e+00 1.712269301357662865e+00 1.728263552678166892e+00 1.740243827132279986e+00 1.750006339987827175e+00 1.758340769353772037e+00 1.765710628002465032e+00 1.772490986523870049e+00 1.778680956759285614e+00 1.784485889288053961e+00 1.789985364958998959e+00 1.795239957521919116e+00 1.800286919687673848e+00 1.805125352251729876e+00 1.809854674748267556e+00 1.814430952720931245e+00 1.818912477558262131e+00 1.823293325911702567e+00 1.827621332545496857e+00 1.831874753574757264e+00 1.836033493832013530e+00 1.840177425997550298e+00 1.844275200152882821e+00 1.848352538713884385e+00 1.852349491313715291e+00 1.856367484121530964e+00 1.860340518969445078e+00 1.864311205262959792e+00 1.868261778426164366e+00 1.872201132508362287e+00 1.876151146445521167e+00 1.880079232064796102e+00 1.884013931115486429e+00 1.887957221018348619e+00 1.891907000956012608e+00 1.895838555185854535e+00 1.899823079561612893e+00 1.903779298325629199e+00 1.907770425469904829e+00 1.911778255563924445e+00 1.915798268672276539e+00 1.919825867868985902e+00 1.923919460586516639e+00 diff --git a/pvmismatch/tests/test_pvcell.py b/pvmismatch/tests/test_pvcell.py index ce395a6..bed2465 100644 --- a/pvmismatch/tests/test_pvcell.py +++ b/pvmismatch/tests/test_pvcell.py @@ -69,6 +69,37 @@ def test_pvcell_calc_rbd(): pvc2 = PVcell(bRBD=-0.056) ok_(isinstance(pvc2, PVcell)) - + +def test_pvcell_calc_now_flag(): + """ + Test ``_calc_now`` turns off recalc in ``__setattr__``. + """ + pvc = PVcell() + itest, vtest, ptest = pvc.Icell, pvc.Vcell, pvc.Pcell + pvc._calc_now = False + pvc.Rs = 0.001 + assert np.allclose(itest, pvc.Icell) + assert np.allclose(vtest, pvc.Vcell) + assert np.allclose(ptest, pvc.Pcell) + icell, vcell, pcell = pvc.calcCell() + pvc._calc_now = True + assert np.allclose(icell, pvc.Icell) + assert np.allclose(vcell, pvc.Vcell) + assert np.allclose(pcell, pvc.Pcell) + + +def test_update(): + pvc = PVcell() + Rs = pvc.Rs + itest = pvc.Icell[170] + pvc.update(Rs=0.001) + assert np.isclose(pvc.Icell[170], 5.79691674) + pvc._calc_now = False + pvc.Rs = Rs + pvc.update() # resets _calc_now to True + assert np.isclose(pvc.Icell[170], itest) + assert pvc._calc_now + + if __name__ == "__main__": test_calc_series() diff --git a/pvmismatch/tests/test_pvconstants.py b/pvmismatch/tests/test_pvconstants.py new file mode 100644 index 0000000..1cf535c --- /dev/null +++ b/pvmismatch/tests/test_pvconstants.py @@ -0,0 +1,21 @@ +from pvmismatch import * + + +def test_pvconst_npts_setter(): + """Test pvconst property and setter methods""" + pvconst = pvconstants.PVconstants() + assert pvconst.npts == pvconstants.NPTS + assert len(pvconst.pts) == pvconst.npts + assert pvconst.pts[0] == 0 + assert pvconst.pts[-1] == 1 + assert len(pvconst.negpts) == pvconst.npts + assert pvconst.negpts[0] == 1 + assert pvconst.negpts[-1] > 0 + pvconst.npts = 1001 + assert pvconst.npts == 1001 + assert len(pvconst.pts) == pvconst.npts + assert pvconst.pts[0] == 0 + assert pvconst.pts[-1] == 1 + assert len(pvconst.negpts) == pvconst.npts + assert pvconst.negpts[0] == 1 + assert pvconst.negpts[-1] > 0 diff --git a/pvmismatch/tests/test_pvmodule.py b/pvmismatch/tests/test_pvmodule.py index 4b91a68..060d33e 100644 --- a/pvmismatch/tests/test_pvmodule.py +++ b/pvmismatch/tests/test_pvmodule.py @@ -4,6 +4,7 @@ from nose.tools import ok_ from pvmismatch.pvmismatch_lib.pvmodule import PVmodule, TCT492, PCT492 +from pvmismatch.pvmismatch_lib.pvcell import PVcell import numpy as np from copy import copy @@ -40,6 +41,30 @@ def test_calc_pct_bridges(): pvmod = PVmodule(cell_pos=pct492_bridges) return pvmod + +def check_same_pvconst_and_lengths(pvmod): + assert len(pvmod.pvcells) == 96 + for p in pvmod.pvcells: + assert p.pvconst is pvmod.pvconst + + +def test_pvmodule_with_pvcells_list(): + pvcells = [PVcell()] * 96 + pvmod = PVmodule(pvcells=pvcells) + check_same_pvconst_and_lengths(pvmod) + + +def test_pvmodule_with_pvcells_obj(): + pvcells = PVcell() + pvmod = PVmodule(pvcells=pvcells) + check_same_pvconst_and_lengths(pvmod) + + +def test_pvmodule_with_no_pvcells(): + pvmod = PVmodule() + check_same_pvconst_and_lengths(pvmod) + + if __name__ == "__main__": test_calc_mod() test_calc_tct_mod() diff --git a/pvmismatch/tests/test_pvstring.py b/pvmismatch/tests/test_pvstring.py new file mode 100644 index 0000000..3a47aa9 --- /dev/null +++ b/pvmismatch/tests/test_pvstring.py @@ -0,0 +1,24 @@ +from pvmismatch import * + + +def check_same_pvconst_and_lengths(pvstr): + assert len(pvstr.pvmods) == pvstring.NUMBERMODS + for p in pvstr.pvmods: + assert p.pvconst is pvstr.pvconst + + +def test_pvstring_with_pvmods_list(): + pvmods = [pvmodule.PVmodule()] * pvstring.NUMBERMODS + pvstr = pvstring.PVstring(pvmods=pvmods) + check_same_pvconst_and_lengths(pvstr) + + +def test_pvstring_with_pvmods_obj(): + pvmods = pvmodule.PVmodule() + pvstr = pvstring.PVstring(pvmods=pvmods) + check_same_pvconst_and_lengths(pvstr) + + +def test_pvstring_with_no_pvmods(): + pvstr = pvstring.PVstring() + check_same_pvconst_and_lengths(pvstr) diff --git a/pvmismatch/tests/test_pvsystem.py b/pvmismatch/tests/test_pvsystem.py new file mode 100644 index 0000000..99549bd --- /dev/null +++ b/pvmismatch/tests/test_pvsystem.py @@ -0,0 +1,25 @@ +from pvmismatch import * + + +def check_same_pvconst_and_lengths(pvsys): + assert len(pvsys.pvstrs) == pvsystem.NUMBERSTRS + for n, p in enumerate(pvsys.pvstrs): + assert p.pvconst is pvsys.pvconst + assert len(p.pvmods) == pvsys.numberMods[n] + + +def test_pvsystem_with_pvstrs_list(): + pvstrs = [pvstring.PVstring()] * pvsystem.NUMBERSTRS + pvsys = pvsystem.PVsystem(pvstrs=pvstrs) + check_same_pvconst_and_lengths(pvsys) + + +def test_pvsystem_with_pvstrs_obj(): + pvstrs = pvstring.PVstring() + pvsys = pvsystem.PVsystem(pvstrs=pvstrs) + check_same_pvconst_and_lengths(pvsys) + + +def test_pvsystem_with_no_pvstrs(): + pvsys = pvsystem.PVsystem() + check_same_pvconst_and_lengths(pvsys) diff --git a/pvmismatch/tests/test_settemps.py b/pvmismatch/tests/test_settemps.py index 6798f9e..ade40d9 100644 --- a/pvmismatch/tests/test_settemps.py +++ b/pvmismatch/tests/test_settemps.py @@ -7,6 +7,7 @@ import numpy as np from nose.tools import ok_ from pvmismatch.pvmismatch_lib.pvsystem import PVsystem +from pvmismatch.pvmismatch_lib.pvcell import PVcell import logging logging.basicConfig(level=logging.DEBUG) @@ -150,3 +151,27 @@ def test_settemp(): assert pvsys.pvstrs[2].pvmods[4].pvcells[5].Tcell == 310 assert pvsys.pvstrs[2].pvmods[4].pvcells[3] == pvsys.pvstrs[2].pvmods[4].pvcells[5] assert pvsys.pvstrs[1].pvmods[0] == pvsys.pvstrs[1].pvmods[2] + +def test_settemp_cell(): + """ + Test setTemp method for a wide range of temperatures. + Test added after implementing Isat2 as Isat2(Tcell) + """ + pvc = PVcell() + Pmp_arr = [] + Vmp_arr = [] + Voc_arr = [] + Isc_arr = [] + + temps = [-85, -60, -40, -25, 0, 25, 40, 60, 85] + for t in temps: + pvc.Tcell = float(t) + 273.15 + Pmp_arr.append(pvc.Pcell.max()) + Vmp_arr.append(pvc.Vcell[pvc.Pcell.argmax()]) + Voc_arr.append(pvc.calcVcell(0)) + Isc_arr.append(pvc.Isc) + + assert(np.all(np.gradient(np.squeeze(Vmp_arr)) < 0)) + assert(np.all(np.gradient(np.squeeze(Voc_arr)) < 0)) + assert(np.all(np.gradient(Isc_arr) > 0)) + diff --git a/requirements.txt b/requirements.txt index 8dd2fbc..27bf2ba 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,9 +1,11 @@ -nose -numpy -scipy -matplotlib -sphinx -dulwich -sympy -pvlib -pytest +nose==1.3.7 +numpy==1.13.3 +scipy==1.0.0 +matplotlib==2.1.0 +Sphinx==1.6.3 +dulwich==0.18.6 +sympy==1.1.1 +pvlib==0.5.1 +pytest==3.2.1 +future==0.16.0 +six==1.11.0 diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..2a9acf1 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,2 @@ +[bdist_wheel] +universal = 1 diff --git a/setup.py b/setup.py index 8ad8d15..35fb9ee 100644 --- a/setup.py +++ b/setup.py @@ -14,22 +14,56 @@ except IOError: pass -setup(name=__name__, - version=__version__, - description='PV Mismatch Calculator', - long_description=README, - author=__author__, - author_email=__email__, - url=__url__, - license='BSD 3-clause', - packages=['pvmismatch', 'pvmismatch.pvmismatch_lib', - 'pvmismatch.pvmismatch_tk', 'pvmismatch.contrib', - 'pvmismatch.contrib.gen_coeffs'], - requires=['numpy (>=1.8)', 'matplotlib (>=1.3)', 'scipy (>=0.12.0)'], - scripts=['pv_tk.py'], - package_data={'pvmismatch': - ['pvmismatch_json/messagetext.English.json', - 'pvmismatch_json/validationConstants.json', - 'res/logo.png', 'res/logo_invert.png', - 'res/logo_bg.png', 'docs/conf.py', 'docs/*.rst', - 'docs/Makefile', 'docs/make.bat']}) +INSTALL_REQUIRES = [ + 'numpy>=1.13.3', 'matplotlib>=2.1.0', 'scipy>=1.0.0', 'future>=0.16.0', + 'six>=1.11.0' +] + +TESTS_REQUIRES = [ + 'nose>=1.3.7', 'pytest>=3.2.1', 'sympy>=1.1.1', 'pvlib>=0.5.1' +] + +CLASSIFIERS = [ + 'Development Status :: 4 - Beta', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Intended Audience :: Science/Research', + 'Programming Language :: Python', + 'Programming Language :: Python :: 2', + 'Programming Language :: Python :: 2.7', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3.3', + 'Programming Language :: Python :: 3.4', + 'Programming Language :: Python :: 3.5', + 'Programming Language :: Python :: 3.6', + 'Topic :: Scientific/Engineering', +] + +setup( + name=__name__, + version=__version__, + description='PV Mismatch Calculator', + long_description=README, + author=__author__, + author_email=__email__, + url=__url__, + license='BSD 3-clause', + packages=[ + 'pvmismatch', 'pvmismatch.pvmismatch_lib', + 'pvmismatch.pvmismatch_tk', 'pvmismatch.contrib', + 'pvmismatch.contrib.gen_coeffs' + ], + install_requires=INSTALL_REQUIRES, + tests_require=TESTS_REQUIRES, + scripts=['pv_tk.py'], + package_data={ + 'pvmismatch': [ + 'pvmismatch_json/messagetext.English.json', + 'pvmismatch_json/validationConstants.json', + 'res/logo.png', 'res/logo_invert.png', + 'res/logo_bg.png', 'docs/conf.py', 'docs/*.rst', + 'docs/Makefile', 'docs/make.bat' + ] + }, + classifiers=CLASSIFIERS +) From df4e90c9939cadc0301c8469b65ccba3f3795f41 Mon Sep 17 00:00:00 2001 From: rayhickey Date: Mon, 26 Feb 2018 10:40:54 -0800 Subject: [PATCH 2/5] Fixes MPP Sensitivity to Temperature Fixes I_mp and V_mp sensitivity to temperature caused by selecting Isys and Vsys values at the maximum Psys index. This is accomplished by linear interpolation between the points surrounding MPP. --- pvmismatch/pvmismatch_lib/pvsystem.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/pvmismatch/pvmismatch_lib/pvsystem.py b/pvmismatch/pvmismatch_lib/pvsystem.py index 02f4e3b..bbf0020 100644 --- a/pvmismatch/pvmismatch_lib/pvsystem.py +++ b/pvmismatch/pvmismatch_lib/pvsystem.py @@ -94,9 +94,26 @@ def calcSystem(self): def calcMPP_IscVocFFeff(self): mpp = np.argmax(self.Psys) - Pmp = self.Psys[mpp] - Vmp = self.Vsys[mpp] - Imp = self.Isys[mpp] + + P = self.Psys[mpp - 1:mpp + 2, 0] + V = self.Vsys[mpp - 1:mpp + 2, 0] + I = self.Isys[mpp - 1:mpp + 2, 0] + + # calculate derivative dP/dV using central difference + dP = np.diff(P, axis=0) # size is (2, 1) + dV = np.diff(V, axis=0) # size is (2, 1) + Pv = dP / dV # size is (2, 1) + + # dP/dV is central difference at midpoints, + Vmid = (V[1:] + V[:-1]) / 2.0 # size is (2, 1) + Imid = (I[1:] + I[:-1]) / 2.0 # size is (2, 1) + + # interpolate to find Vmp + Vmp = -Pv[0] * np.diff(Vmid, axis=0) / np.diff(Pv, axis=0) + Vmid[0] + Imp = -Pv[0] * np.diff(Imid, axis=0) / np.diff(Pv, axis=0) + Imid[0] + # calculate max power at Pv = 0 + Pmp = Imp * Vmp + # calculate Voc, current must be increasing so flipup() Voc = np.interp(np.float64(0), np.flipud(self.Isys), np.flipud(self.Vsys)) From 3bcdf20dc74aa38d17f5c30163deafba16def73d Mon Sep 17 00:00:00 2001 From: rayhickey Date: Mon, 26 Feb 2018 12:02:35 -0800 Subject: [PATCH 3/5] Correct indices --- pvmismatch/pvmismatch_lib/pvsystem.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pvmismatch/pvmismatch_lib/pvsystem.py b/pvmismatch/pvmismatch_lib/pvsystem.py index bbf0020..29017c0 100644 --- a/pvmismatch/pvmismatch_lib/pvsystem.py +++ b/pvmismatch/pvmismatch_lib/pvsystem.py @@ -95,9 +95,9 @@ def calcSystem(self): def calcMPP_IscVocFFeff(self): mpp = np.argmax(self.Psys) - P = self.Psys[mpp - 1:mpp + 2, 0] - V = self.Vsys[mpp - 1:mpp + 2, 0] - I = self.Isys[mpp - 1:mpp + 2, 0] + P = self.Psys[mpp - 1:mpp + 2] + V = self.Vsys[mpp - 1:mpp + 2] + I = self.Isys[mpp - 1:mpp + 2] # calculate derivative dP/dV using central difference dP = np.diff(P, axis=0) # size is (2, 1) From b25ea1847f759e6d9d414355152a98d7c492b095 Mon Sep 17 00:00:00 2001 From: rayhickey Date: Mon, 26 Feb 2018 12:04:01 -0800 Subject: [PATCH 4/5] Cleanup --- pvmismatch/pvmismatch_lib/pvsystem.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/pvmismatch/pvmismatch_lib/pvsystem.py b/pvmismatch/pvmismatch_lib/pvsystem.py index 29017c0..cbac58c 100644 --- a/pvmismatch/pvmismatch_lib/pvsystem.py +++ b/pvmismatch/pvmismatch_lib/pvsystem.py @@ -94,26 +94,21 @@ def calcSystem(self): def calcMPP_IscVocFFeff(self): mpp = np.argmax(self.Psys) - P = self.Psys[mpp - 1:mpp + 2] V = self.Vsys[mpp - 1:mpp + 2] I = self.Isys[mpp - 1:mpp + 2] - # calculate derivative dP/dV using central difference dP = np.diff(P, axis=0) # size is (2, 1) dV = np.diff(V, axis=0) # size is (2, 1) Pv = dP / dV # size is (2, 1) - # dP/dV is central difference at midpoints, Vmid = (V[1:] + V[:-1]) / 2.0 # size is (2, 1) Imid = (I[1:] + I[:-1]) / 2.0 # size is (2, 1) - # interpolate to find Vmp Vmp = -Pv[0] * np.diff(Vmid, axis=0) / np.diff(Pv, axis=0) + Vmid[0] Imp = -Pv[0] * np.diff(Imid, axis=0) / np.diff(Pv, axis=0) + Imid[0] # calculate max power at Pv = 0 Pmp = Imp * Vmp - # calculate Voc, current must be increasing so flipup() Voc = np.interp(np.float64(0), np.flipud(self.Isys), np.flipud(self.Vsys)) From 990ce8b4b1a5043ba1f6d407fa511d8055ab38b8 Mon Sep 17 00:00:00 2001 From: rayhickey Date: Mon, 19 Mar 2018 11:58:26 -0700 Subject: [PATCH 5/5] Changed setsuns test for new MPP calculation Also changed MPP calc to return float rather than array --- pvmismatch/pvmismatch_lib/pvsystem.py | 4 ++-- pvmismatch/tests/test_setsuns.py | 15 +++++++++------ 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/pvmismatch/pvmismatch_lib/pvsystem.py b/pvmismatch/pvmismatch_lib/pvsystem.py index cbac58c..3014a9a 100644 --- a/pvmismatch/pvmismatch_lib/pvsystem.py +++ b/pvmismatch/pvmismatch_lib/pvsystem.py @@ -105,8 +105,8 @@ def calcMPP_IscVocFFeff(self): Vmid = (V[1:] + V[:-1]) / 2.0 # size is (2, 1) Imid = (I[1:] + I[:-1]) / 2.0 # size is (2, 1) # interpolate to find Vmp - Vmp = -Pv[0] * np.diff(Vmid, axis=0) / np.diff(Pv, axis=0) + Vmid[0] - Imp = -Pv[0] * np.diff(Imid, axis=0) / np.diff(Pv, axis=0) + Imid[0] + Vmp = (-Pv[0] * np.diff(Vmid, axis=0) / np.diff(Pv, axis=0) + Vmid[0]).item() + Imp = (-Pv[0] * np.diff(Imid, axis=0) / np.diff(Pv, axis=0) + Imid[0]).item() # calculate max power at Pv = 0 Pmp = Imp * Vmp # calculate Voc, current must be increasing so flipup() diff --git a/pvmismatch/tests/test_setsuns.py b/pvmismatch/tests/test_setsuns.py index 4c47c4a..2e553c1 100644 --- a/pvmismatch/tests/test_setsuns.py +++ b/pvmismatch/tests/test_setsuns.py @@ -16,42 +16,45 @@ def test_basic(): pvsys = PVsystem() pvsys.setSuns(.75) - ok_(np.isclose(pvsys.Pmp, 23907.936630685774)) + ok_(np.isclose(pvsys.Pmp, 23903.15106763)) def test_dictionary(): pvsys = PVsystem() Ee = {1: {3: {'cells': np.arange(30), 'Ee': [.25] * 30}}} pvsys.setSuns(Ee) - ok_(np.isclose(pvsys.Pmp, 31618.1813179655)) + ok_(np.isclose(pvsys.Pmp, 31610.21081155355)) def test_set_mod_1(): pvsys = PVsystem() Ee = {1: {3: [.2], 0: [.1]}} pvsys.setSuns(Ee) - ok_(np.isclose(pvsys.Pmp, 29566.303088387336)) + ok_(np.isclose(pvsys.Pmp, 29559.422265401034)) def test_set_mod_2(): pvsys = PVsystem() Ee = {1: {3: .2, 0: .1}} pvsys.setSuns(Ee) - ok_(np.isclose(pvsys.Pmp, 29566.303088387336)) + ok_(np.isclose(pvsys.Pmp, 29559.422265401034)) + # 1001 points linear: [ 29579.12191565] def test_set_str_1(): pvsys = PVsystem() Ee = {1: [.1]} pvsys.setSuns(Ee) - ok_(np.isclose(pvsys.Pmp, 29136.544447716446)) + ok_(np.isclose(pvsys.Pmp, 29133.81083801798)) + # 1001 points linear: [ 29141.73034719] def test_set_str_2(): pvsys = PVsystem() Ee = {1: .1} pvsys.setSuns(Ee) - ok_(np.isclose(pvsys.Pmp, 29136.544447716446)) + ok_(np.isclose(pvsys.Pmp, 29133.81083801798)) + # 1001 points linear: [ 29141.73034719] def test_gh34_35():