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 +)