diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index 3c557c0..1e9e1d3 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -171,11 +171,11 @@ def __init__(self, cell_pos=STD96, pvcells=None, pvconst=PVconstants(), 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 keey same pvconst for all objects + # 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 = [copy(pvcells) for _ in xrange(self.numberCells)] + pvcells = [pvcells] * self.numberCells if len(pvcells) != self.numberCells: # TODO: use pvexception raise Exception( @@ -235,21 +235,56 @@ def setSuns(self, Ee, cells=None): """ if cells is None: if np.isscalar(Ee): - for pvc in self.pvcells: + new_pvcells = range(self.numberCells) # new list of cells + old_pvcells = dict.fromkeys(self.pvcells) # same as set(pvcells) + for cell_id, pvcell in enumerate(self.pvcells): + if old_pvcells[pvcell] is None: + new_pvcells[cell_id] = copy(pvcell) + old_pvcells[pvcell] = new_pvcells[cell_id] + else: + new_pvcells[cell_id] = old_pvcells[pvcell] + self.pvcells = new_pvcells + pvcell_set = old_pvcells.itervalues() + for pvc in pvcell_set: pvc.Ee = Ee elif np.size(Ee) == self.numberCells: - for pvc, Ee_idx in zip(self.pvcells, Ee): - pvc.Ee = Ee_idx + self.pvcells = copy(self.pvcells) # copy list first + for cell_idx, Ee_idx in enumerate(Ee): + self.pvcells[cell_idx] = copy(self.pvcells[cell_idx]) + self.pvcells[cell_idx].Ee = Ee_idx else: raise Exception("Input irradiance value (Ee) for each cell!") else: Ncells = np.size(cells) + self.pvcells = copy(self.pvcells) # copy list first if np.isscalar(Ee): - for cell_idx in cells: - self.pvcells[cell_idx].Ee = Ee + cells_to_update = [self.pvcells[i] for i in cells] + old_pvcells = dict.fromkeys(cells_to_update) + for cell_id, pvcell in zip(cells, cells_to_update): + if old_pvcells[pvcell] is None: + self.pvcells[cell_id] = copy(pvcell) + self.pvcells[cell_id].Ee = Ee + old_pvcells[pvcell] = self.pvcells[cell_id] + else: + self.pvcells[cell_id] = old_pvcells[pvcell] elif np.size(Ee) == Ncells: - for cell_idx, Ee_idx in zip(cells, Ee): - self.pvcells[cell_idx].Ee = Ee_idx + # Find unique irradiance values + # TODO possible "cleaner" alternative by grouping cells into tuples that match the set irradiance + # E.g: pvsys.setSuns({X: {Y: {'Ee': (0.33, 0.99), 'cells': [(2, 3), 17]}}}) + cells = np.array(cells) + Ee = np.array(Ee) + unique_ee = np.unique(Ee) + for a_Ee in unique_ee: + cells_subset = cells[np.where(Ee == a_Ee)] + cells_to_update = [self.pvcells[i] for i in cells_subset] + old_pvcells = dict.fromkeys(cells_to_update) + for cell_id, pvcell in zip(cells_subset, cells_to_update): + if old_pvcells[pvcell] is None: + self.pvcells[cell_id] = copy(pvcell) + self.pvcells[cell_id].Ee = a_Ee + old_pvcells[pvcell] = self.pvcells[cell_id] + else: + self.pvcells[cell_id] = old_pvcells[pvcell] else: raise Exception("Input irradiance value (Ee) for each cell!") self.Imod, self.Vmod, self.Pmod, self.Isubstr, self.Vsubstr = self.calcMod() diff --git a/pvmismatch/pvmismatch_lib/pvstring.py b/pvmismatch/pvmismatch_lib/pvstring.py index 02b32ea..84c33b0 100644 --- a/pvmismatch/pvmismatch_lib/pvstring.py +++ b/pvmismatch/pvmismatch_lib/pvstring.py @@ -5,7 +5,7 @@ """ import numpy as np -from copy import deepcopy +from copy import copy from matplotlib import pyplot as plt # use absolute imports instead of relative, so modules are portable from pvmismatch.pvmismatch_lib.pvconstants import PVconstants, NUMBERMODS @@ -30,7 +30,7 @@ def __init__(self, numberMods=NUMBERMODS, pvmods=None, # use deepcopy instead of making each object in for-loop, 2x faster pvmods = PVmodule(pvconst=self.pvconst) if isinstance(pvmods, PVmodule): - pvmods = [deepcopy(pvmods) for _ in xrange(self.numberMods)] + pvmods = [pvmods] * self.numberMods # reset pvconsts in all pvcells and pvmodules for p in pvmods: for c in p.pvcells: @@ -87,11 +87,23 @@ def setSuns(self, Ee): """ if np.isscalar(Ee): + new_pvmods = range(self.numberMods) # new list of modules + old_pvmods = dict.fromkeys(self.pvmods) # same as set(pvmods) + for mod_id, pvmod in enumerate(self.pvmods): + if old_pvmods[pvmod] is None: + new_pvmods[mod_id] = copy(pvmod) + old_pvmods[pvmod] = new_pvmods[mod_id] + else: + new_pvmods[mod_id] = old_pvmods[pvmod] + self.pvmods = new_pvmods for pvmod in iter(self.pvmods): pvmod.setSuns(Ee) else: + self.pvmods = copy(self.pvmods) # copy list first try: for pvmod, cell_Ee in Ee.iteritems(): + pvmod = int(pvmod) + self.pvmods[pvmod] = copy(self.pvmods[pvmod]) if hasattr(cell_Ee, 'keys'): self.pvmods[pvmod].setSuns(**cell_Ee) else: @@ -100,9 +112,12 @@ def setSuns(self, Ee): except TypeError: self.pvmods[pvmod].setSuns(cell_Ee) except AttributeError: - Ee = Ee[0] - for pvmod in iter(self.pvmods): - pvmod.setSuns(Ee) + # Ee was a list? just take first item in list + if len(Ee) > 1: + raise TypeError('Irradiance, Ee, should be scalar or dict') + for mod_id, pvmod in enumerate(self.pvmods): + self.pvmods[mod_id] = copy(pvmod) + self.pvmods[mod_id].setSuns(Ee[0]) # update modules self.Istring, self.Vstring, self.Pstring = self.calcString() diff --git a/pvmismatch/pvmismatch_lib/pvsystem.py b/pvmismatch/pvmismatch_lib/pvsystem.py index 857ea24..4f5a74a 100644 --- a/pvmismatch/pvmismatch_lib/pvsystem.py +++ b/pvmismatch/pvmismatch_lib/pvsystem.py @@ -5,7 +5,7 @@ """ import numpy as np -from copy import deepcopy +from copy import copy from matplotlib import pyplot as plt # use absolute imports instead of relative, so modules are portable from pvmismatch.pvmismatch_lib.pvconstants import PVconstants, NUMBERMODS, \ @@ -34,7 +34,7 @@ def __init__(self, pvconst=PVconstants(), numberStrs=NUMBERSTRS, pvconst=self.pvconst) # use deep copy instead of making each object in a for-loop if isinstance(pvstrs, PVstring): - pvstrs = [deepcopy(pvstrs) for _ in xrange(self.numberStrs)] + pvstrs = [pvstrs] * self.numberStrs if len(pvstrs) != self.numberStrs: # TODO: use pvmismatch excecptions raise Exception("Number of strings don't match.") @@ -118,6 +118,8 @@ def setSuns(self, Ee): pvstr.setSuns(Ee) else: for pvstr, pvmod_Ee in Ee.iteritems(): + 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, diff --git a/pvmismatch/tests/test_setsuns.py b/pvmismatch/tests/test_setsuns.py index 73c0046..f11f9d4 100644 --- a/pvmismatch/tests/test_setsuns.py +++ b/pvmismatch/tests/test_setsuns.py @@ -7,6 +7,10 @@ import numpy as np from nose.tools import ok_ from pvmismatch.pvmismatch_lib.pvsystem import PVsystem +import logging + +logging.basicConfig(level=logging.DEBUG) +LOGGER = logging.getLogger(__name__) def test_basic(): @@ -48,3 +52,105 @@ def test_set_str_2(): Ee = {1: .1} pvsys.setSuns(Ee) ok_(np.isclose(pvsys.Pmp, 29136.544447716446)) + + +def test_gh34_35(): + pvsys = PVsystem() + # display unique id numbers + LOGGER.debug('\n*** unique id numbers ***') + LOGGER.debug('pvstrs:\n%r', set(pvsys.pvstrs)) + LOGGER.debug('pvmods:\n%r', set([x for y in pvsys.pvmods for x in y])) + LOGGER.debug( + 'pvcells:\n%r', + {hex(int(id(z))): z for y in pvsys.pvmods for x in y for z in x.pvcells} + ) + # test strings references same object + assert pvsys.pvstrs[0] == pvsys.pvstrs[1] + # modules references same object + assert pvsys.pvmods[0][0] == pvsys.pvmods[1][1] + # cells reference same object + assert pvsys.pvmods[0][0].pvcells[0] == pvsys.pvmods[1][1].pvcells[1] + + # test set suns on just string #2 + pvsys.setSuns({2: 0.88}) + # display unique id numbers + LOGGER.debug('pvstrs:\n%r', set(pvsys.pvstrs)) + LOGGER.debug('pvmods:\n%r', set([x for y in pvsys.pvmods for x in y])) + LOGGER.debug( + 'pvcells:\n%r', + {hex(int(id(z))): z for y in pvsys.pvmods for x in y for z in x.pvcells} + ) + # test other string not changed + assert (pvsys.pvstrs[0].pvmods[0].Ee == 1.0).all() + assert (pvsys.pvstrs[1].pvmods[1].Ee == 1.0).all() + # test all modules in string #2 changed + assert (pvsys.pvstrs[2].pvmods[0].Ee == 0.88).all() + assert (pvsys.pvstrs[2].pvmods[2].Ee == 0.88).all() + # test strings references same object + assert pvsys.pvstrs[0] == pvsys.pvstrs[1] + # modules references same object + assert pvsys.pvmods[0][0] == pvsys.pvmods[1][1] + # cells reference same object + assert pvsys.pvmods[0][0].pvcells[0] == pvsys.pvmods[1][1].pvcells[1] + assert pvsys.pvmods[2][0].pvcells[0] == pvsys.pvmods[2][2].pvcells[2] + + # test set suns on just module #4 in string #2 + pvsys.setSuns({2: {4: 0.75}}) + # display unique id numbers + LOGGER.debug('pvstrs:\n%r', set(pvsys.pvstrs)) + LOGGER.debug('pvmods:\n%r', set([x for y in pvsys.pvmods for x in y])) + LOGGER.debug( + 'pvcells:\n%r', + {hex(int(id(z))): z for y in pvsys.pvmods for x in y for z in x.pvcells} + ) + assert (pvsys.pvstrs[0].pvmods[0].Ee == 1.0).all() + assert (pvsys.pvstrs[1].pvmods[1].Ee == 1.0).all() + assert (pvsys.pvstrs[1].pvmods[4].Ee == 1.0).all() + assert (pvsys.pvstrs[2].pvmods[0].Ee == 0.88).all() + assert (pvsys.pvstrs[2].pvmods[2].Ee == 0.88).all() + assert (pvsys.pvstrs[2].pvmods[4].Ee == 0.75).all() + assert pvsys.pvstrs[0] == pvsys.pvstrs[1] + assert pvsys.pvmods[0][0] == pvsys.pvmods[1][1] + assert pvsys.pvmods[0][0].pvcells[0] == pvsys.pvmods[1][1].pvcells[1] + + # set just cells #0 and #2 in module #4 in string #2 + pvsys.setSuns({2: {4: {'Ee': 0.66, 'cells': [0, 2]}}}) + # display unique id numbers + LOGGER.debug('pvstrs:\n%r', set(pvsys.pvstrs)) + LOGGER.debug('pvmods:\n%r', set([x for y in pvsys.pvmods for x in y])) + LOGGER.debug( + 'pvcells:\n%r', + {hex(int(id(z))): z for y in pvsys.pvmods for x in y for z in x.pvcells} + ) + assert (pvsys.pvstrs[0].pvmods[0].Ee == 1.0).all() + assert (pvsys.pvstrs[1].pvmods[1].Ee == 1.0).all() + assert (pvsys.pvstrs[1].pvmods[4].Ee == 1.0).all() + assert (pvsys.pvstrs[2].pvmods[0].Ee == 0.88).all() + assert (pvsys.pvstrs[2].pvmods[2].Ee == 0.88).all() + assert pvsys.pvstrs[2].pvmods[4].pvcells[0].Ee == 0.66 + assert pvsys.pvstrs[2].pvmods[4].pvcells[1].Ee == 0.75 + assert pvsys.pvstrs[2].pvmods[4].pvcells[2].Ee == 0.66 + assert pvsys.pvstrs[0] == pvsys.pvstrs[1] + assert pvsys.pvmods[0][0] == pvsys.pvmods[1][1] + assert pvsys.pvmods[0][0].pvcells[0] == pvsys.pvmods[1][1].pvcells[1] + assert pvsys.pvstrs[2].pvmods[4].pvcells[0] == pvsys.pvstrs[2].pvmods[4].pvcells[2] + + # set cells 3 and 4 to one irradiance and 5 to another. should only make two new cell objects + pvsys.setSuns({2: {4: {'Ee': (0.33, 0.99, 0.33), 'cells': (3, 4, 5)}}}) + assert (pvsys.pvstrs[0].pvmods[0].Ee == 1.0).all() + assert (pvsys.pvstrs[1].pvmods[1].Ee == 1.0).all() + assert (pvsys.pvstrs[1].pvmods[4].Ee == 1.0).all() + assert (pvsys.pvstrs[2].pvmods[0].Ee == 0.88).all() + assert (pvsys.pvstrs[2].pvmods[2].Ee == 0.88).all() + assert pvsys.pvstrs[2].pvmods[4].pvcells[0].Ee == 0.66 + assert pvsys.pvstrs[2].pvmods[4].pvcells[1].Ee == 0.75 + assert pvsys.pvstrs[2].pvmods[4].pvcells[2].Ee == 0.66 + assert pvsys.pvstrs[0] == pvsys.pvstrs[1] + assert pvsys.pvmods[0][0] == pvsys.pvmods[1][1] + assert pvsys.pvmods[0][0].pvcells[0] == pvsys.pvmods[1][1].pvcells[1] + assert pvsys.pvstrs[2].pvmods[4].pvcells[0] == pvsys.pvstrs[2].pvmods[4].pvcells[2] + assert pvsys.pvstrs[2].pvmods[4].pvcells[3].Ee == 0.33 + assert pvsys.pvstrs[2].pvmods[4].pvcells[4].Ee == 0.99 + assert pvsys.pvstrs[2].pvmods[4].pvcells[5].Ee == 0.33 + assert pvsys.pvstrs[2].pvmods[4].pvcells[3] == pvsys.pvstrs[2].pvmods[4].pvcells[5] +