From 0aaf3fb0b2264c021dfc0bdca4eb6bbf50c2196a Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Mon, 23 Jan 2017 20:33:55 -0800 Subject: [PATCH 01/18] resolves issue #35: don't make copies * don't make a copy of every string, module and object, instead use the same object * TODO: need to make copies as needed in `setSuns()` see #34 --- pvmismatch/pvmismatch_lib/pvmodule.py | 4 +++- pvmismatch/pvmismatch_lib/pvstring.py | 4 +++- pvmismatch/pvmismatch_lib/pvsystem.py | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index 3c557c0..5cda372 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -175,7 +175,9 @@ def __init__(self, cell_pos=STD96, pvcells=None, pvconst=PVconstants(), # 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)] + # GH35: don't make copies, use same reference for all objects + #pvcells = [copy(pvcells) for _ in xrange(self.numberCells)] + pvcells = [pvcells] * self.numberCells if len(pvcells) != self.numberCells: # TODO: use pvexception raise Exception( diff --git a/pvmismatch/pvmismatch_lib/pvstring.py b/pvmismatch/pvmismatch_lib/pvstring.py index 02b32ea..17f084d 100644 --- a/pvmismatch/pvmismatch_lib/pvstring.py +++ b/pvmismatch/pvmismatch_lib/pvstring.py @@ -30,7 +30,9 @@ 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)] + # GH35: don't make copies, use same reference for all objects + #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: diff --git a/pvmismatch/pvmismatch_lib/pvsystem.py b/pvmismatch/pvmismatch_lib/pvsystem.py index 857ea24..3148ae1 100644 --- a/pvmismatch/pvmismatch_lib/pvsystem.py +++ b/pvmismatch/pvmismatch_lib/pvsystem.py @@ -34,7 +34,9 @@ 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)] + # GH35: don't make copies, use same reference for all objects + #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.") From b9c49db6a0c46ae07784699e7374f1a625d5a6c9 Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Mon, 23 Jan 2017 23:59:38 -0800 Subject: [PATCH 02/18] address #34 make new objects as needed from copies * use copy instead of deepcopy reduce unnecessary new objects * make new strings and modules if irradiance is dictionary * make new cells if given list of cells and irradiance, if given new irradiances for all cells or new irradiances for specified cells --- pvmismatch/pvmismatch_lib/pvmodule.py | 12 +++++++++--- pvmismatch/pvmismatch_lib/pvstring.py | 4 +++- pvmismatch/pvmismatch_lib/pvsystem.py | 4 +++- 3 files changed, 15 insertions(+), 5 deletions(-) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index 5cda372..a01067b 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -170,13 +170,13 @@ def __init__(self, cell_pos=STD96, pvcells=None, pvconst=PVconstants(), 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 keey same pvconst for all objects - # PVcell.calcCell() creates new np.ndarray if attributes change pvcells = PVcell(pvconst=self.pvconst) if isinstance(pvcells, PVcell): # GH35: don't make copies, use same reference for all objects #pvcells = [copy(pvcells) for _ in xrange(self.numberCells)] + # 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 = [pvcells] * self.numberCells if len(pvcells) != self.numberCells: # TODO: use pvexception @@ -241,6 +241,8 @@ def setSuns(self, Ee, cells=None): pvc.Ee = Ee elif np.size(Ee) == self.numberCells: for pvc, Ee_idx in zip(self.pvcells, Ee): + # gh34: make new objects as needed from copies + pvc = copy(pvc) pvc.Ee = Ee_idx else: raise Exception("Input irradiance value (Ee) for each cell!") @@ -248,9 +250,13 @@ def setSuns(self, Ee, cells=None): Ncells = np.size(cells) if np.isscalar(Ee): for cell_idx in cells: + # gh34: make new objects as needed from copies + self.pvcells[cell_idx] = copy(self.pvcells[cell_idx]) self.pvcells[cell_idx].Ee = Ee elif np.size(Ee) == Ncells: for cell_idx, Ee_idx in zip(cells, Ee): + # gh34: make new objects as needed from copies + 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!") diff --git a/pvmismatch/pvmismatch_lib/pvstring.py b/pvmismatch/pvmismatch_lib/pvstring.py index 17f084d..99a8909 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 @@ -94,6 +94,8 @@ def setSuns(self, Ee): else: try: for pvmod, cell_Ee in Ee.iteritems(): + # gh34: make new objects as needed from copies + self.pvmods[pvmod] = copy(self.pvmods[pvmod]) if hasattr(cell_Ee, 'keys'): self.pvmods[pvmod].setSuns(**cell_Ee) else: diff --git a/pvmismatch/pvmismatch_lib/pvsystem.py b/pvmismatch/pvmismatch_lib/pvsystem.py index 3148ae1..a22c036 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, \ @@ -120,6 +120,8 @@ def setSuns(self, Ee): pvstr.setSuns(Ee) else: for pvstr, pvmod_Ee in Ee.iteritems(): + # gh34: make new objects as needed from copies + 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, From 68e3fd27226034201886007689ca5d3557d86435 Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Tue, 24 Jan 2017 01:08:14 -0800 Subject: [PATCH 03/18] make new modules in string even if irradiance is scalar * fix make new cells if given all irradiance, only assign new object to a list --- pvmismatch/pvmismatch_lib/pvmodule.py | 6 +++--- pvmismatch/pvmismatch_lib/pvstring.py | 14 +++++++++++++- pvmismatch/pvmismatch_lib/pvsystem.py | 2 +- 3 files changed, 17 insertions(+), 5 deletions(-) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index a01067b..81dca07 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -240,10 +240,10 @@ def setSuns(self, Ee, cells=None): for pvc in self.pvcells: pvc.Ee = Ee elif np.size(Ee) == self.numberCells: - for pvc, Ee_idx in zip(self.pvcells, Ee): + for cell_idx, Ee_idx in enumerate(Ee): # gh34: make new objects as needed from copies - pvc = copy(pvc) - pvc.Ee = Ee_idx + 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: diff --git a/pvmismatch/pvmismatch_lib/pvstring.py b/pvmismatch/pvmismatch_lib/pvstring.py index 99a8909..0345473 100644 --- a/pvmismatch/pvmismatch_lib/pvstring.py +++ b/pvmismatch/pvmismatch_lib/pvstring.py @@ -66,7 +66,7 @@ def calcString(self): Pstring = Istring * Vstring return Istring, Vstring, Pstring - def setSuns(self, Ee): + def setSuns(self, Ee, make_new=False): """ Set irradiance on cells in modules of string in system. If Ee is ... @@ -76,6 +76,8 @@ def setSuns(self, Ee): :meth:`~pvmismatch.pvmismatch_lib.pvmodule.PVmodule.setSuns()` :param Ee: irradiance [W/m^2] + :param make_new: make a new module + :type make_new: boolean :type Ee: dict or float For Example:: @@ -89,6 +91,16 @@ def setSuns(self, Ee): """ if np.isscalar(Ee): + if make_new: + 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: diff --git a/pvmismatch/pvmismatch_lib/pvsystem.py b/pvmismatch/pvmismatch_lib/pvsystem.py index a22c036..2f32e9e 100644 --- a/pvmismatch/pvmismatch_lib/pvsystem.py +++ b/pvmismatch/pvmismatch_lib/pvsystem.py @@ -122,7 +122,7 @@ def setSuns(self, Ee): for pvstr, pvmod_Ee in Ee.iteritems(): # gh34: make new objects as needed from copies self.pvstrs[pvstr] = copy(self.pvstrs[pvstr]) - self.pvstrs[pvstr].setSuns(pvmod_Ee) + self.pvstrs[pvstr].setSuns(pvmod_Ee, make_new=True) 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() From 31547915d0ea928fdd3c993bf1f2f696926a5c3d Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Tue, 24 Jan 2017 01:56:47 -0800 Subject: [PATCH 04/18] always make new objects * remove `make_new` * add test * always copy cells even if scalar --- pvmismatch/pvmismatch_lib/pvmodule.py | 9 +++++++ pvmismatch/pvmismatch_lib/pvstring.py | 23 ++++++++--------- pvmismatch/pvmismatch_lib/pvsystem.py | 2 +- pvmismatch/tests/test_setsuns.py | 37 +++++++++++++++++++++++++++ 4 files changed, 57 insertions(+), 14 deletions(-) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index 81dca07..23d1a8d 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -237,6 +237,15 @@ def setSuns(self, Ee, cells=None): """ if cells is None: if np.isscalar(Ee): + 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 for pvc in self.pvcells: pvc.Ee = Ee elif np.size(Ee) == self.numberCells: diff --git a/pvmismatch/pvmismatch_lib/pvstring.py b/pvmismatch/pvmismatch_lib/pvstring.py index 0345473..48b36e4 100644 --- a/pvmismatch/pvmismatch_lib/pvstring.py +++ b/pvmismatch/pvmismatch_lib/pvstring.py @@ -66,7 +66,7 @@ def calcString(self): Pstring = Istring * Vstring return Istring, Vstring, Pstring - def setSuns(self, Ee, make_new=False): + def setSuns(self, Ee): """ Set irradiance on cells in modules of string in system. If Ee is ... @@ -76,8 +76,6 @@ def setSuns(self, Ee, make_new=False): :meth:`~pvmismatch.pvmismatch_lib.pvmodule.PVmodule.setSuns()` :param Ee: irradiance [W/m^2] - :param make_new: make a new module - :type make_new: boolean :type Ee: dict or float For Example:: @@ -91,16 +89,15 @@ def setSuns(self, Ee, make_new=False): """ if np.isscalar(Ee): - if make_new: - 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 + 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: diff --git a/pvmismatch/pvmismatch_lib/pvsystem.py b/pvmismatch/pvmismatch_lib/pvsystem.py index 2f32e9e..a22c036 100644 --- a/pvmismatch/pvmismatch_lib/pvsystem.py +++ b/pvmismatch/pvmismatch_lib/pvsystem.py @@ -122,7 +122,7 @@ def setSuns(self, Ee): for pvstr, pvmod_Ee in Ee.iteritems(): # gh34: make new objects as needed from copies self.pvstrs[pvstr] = copy(self.pvstrs[pvstr]) - self.pvstrs[pvstr].setSuns(pvmod_Ee, make_new=True) + 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() diff --git a/pvmismatch/tests/test_setsuns.py b/pvmismatch/tests/test_setsuns.py index 73c0046..c9f89e9 100644 --- a/pvmismatch/tests/test_setsuns.py +++ b/pvmismatch/tests/test_setsuns.py @@ -48,3 +48,40 @@ def test_set_str_2(): Ee = {1: .1} pvsys.setSuns(Ee) ok_(np.isclose(pvsys.Pmp, 29136.544447716446)) + + +def test_gh34_35(): + pvsys = PVsystem() + 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] + pvsys.setSuns({2: 0.88}) + assert (pvsys.pvstrs[0].pvmods[0].Ee == 1.0).all() + assert (pvsys.pvstrs[1].pvmods[1].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[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.pvmods[2][0].pvcells[0] == pvsys.pvmods[2][2].pvcells[2] + pvsys.setSuns({2: {4: 0.75}}) + assert (pvsys.pvstrs[0].pvmods[0].Ee == 1.0).all() + assert (pvsys.pvstrs[1].pvmods[1].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] + pvsys.setSuns({2: {4: {'Ee': 0.66, 'cells': [0, 2]}}}) + assert (pvsys.pvstrs[0].pvmods[0].Ee == 1.0).all() + assert (pvsys.pvstrs[1].pvmods[1].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] + From c113a41acccf37a41199eecb1718506d3ea81696 Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Tue, 24 Jan 2017 02:09:40 -0800 Subject: [PATCH 05/18] remove comment legacy code * move comment about cells/modules back to where it was originally --- pvmismatch/pvmismatch_lib/pvmodule.py | 7 +++---- pvmismatch/pvmismatch_lib/pvstring.py | 1 - pvmismatch/pvmismatch_lib/pvsystem.py | 1 - 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index 23d1a8d..5999a39 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -170,13 +170,12 @@ def __init__(self, cell_pos=STD96, pvcells=None, pvconst=PVconstants(), self.Vbypass = Vbypass #: [V] trigger voltage of bypass diode self.cellArea = cellArea #: [cm^2] cell area if pvcells is None: - pvcells = PVcell(pvconst=self.pvconst) - if isinstance(pvcells, PVcell): - # GH35: don't make copies, use same reference for all objects - #pvcells = [copy(pvcells) for _ in xrange(self.numberCells)] # 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): + # GH35: don't make copies, use same reference for all objects pvcells = [pvcells] * self.numberCells if len(pvcells) != self.numberCells: # TODO: use pvexception diff --git a/pvmismatch/pvmismatch_lib/pvstring.py b/pvmismatch/pvmismatch_lib/pvstring.py index 48b36e4..2c08f47 100644 --- a/pvmismatch/pvmismatch_lib/pvstring.py +++ b/pvmismatch/pvmismatch_lib/pvstring.py @@ -31,7 +31,6 @@ def __init__(self, numberMods=NUMBERMODS, pvmods=None, pvmods = PVmodule(pvconst=self.pvconst) if isinstance(pvmods, PVmodule): # GH35: don't make copies, use same reference for all objects - #pvmods = [deepcopy(pvmods) for _ in xrange(self.numberMods)] pvmods = [pvmods] * self.numberMods # reset pvconsts in all pvcells and pvmodules for p in pvmods: diff --git a/pvmismatch/pvmismatch_lib/pvsystem.py b/pvmismatch/pvmismatch_lib/pvsystem.py index a22c036..97ff388 100644 --- a/pvmismatch/pvmismatch_lib/pvsystem.py +++ b/pvmismatch/pvmismatch_lib/pvsystem.py @@ -35,7 +35,6 @@ def __init__(self, pvconst=PVconstants(), numberStrs=NUMBERSTRS, # use deep copy instead of making each object in a for-loop if isinstance(pvstrs, PVstring): # GH35: don't make copies, use same reference for all objects - #pvstrs = [deepcopy(pvstrs) for _ in xrange(self.numberStrs)] pvstrs = [pvstrs] * self.numberStrs if len(pvstrs) != self.numberStrs: # TODO: use pvmismatch excecptions From ad14c33ce8e650a73f41b011a0d67c38d48981e0 Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Tue, 24 Jan 2017 02:43:23 -0800 Subject: [PATCH 06/18] copy list of modules first --- pvmismatch/pvmismatch_lib/pvstring.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pvmismatch/pvmismatch_lib/pvstring.py b/pvmismatch/pvmismatch_lib/pvstring.py index 2c08f47..888f0ad 100644 --- a/pvmismatch/pvmismatch_lib/pvstring.py +++ b/pvmismatch/pvmismatch_lib/pvstring.py @@ -103,6 +103,7 @@ def setSuns(self, Ee): try: for pvmod, cell_Ee in Ee.iteritems(): # gh34: make new objects as needed from copies + self.pvmods = copy(self.pvmods) # copy list first self.pvmods[pvmod] = copy(self.pvmods[pvmod]) if hasattr(cell_Ee, 'keys'): self.pvmods[pvmod].setSuns(**cell_Ee) From a13ed8b2a32517254e70135fd7bb3165b01aff08 Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Tue, 24 Jan 2017 10:40:30 -0800 Subject: [PATCH 07/18] copy list of cells first, log unique obj id numbers in test --- pvmismatch/pvmismatch_lib/pvmodule.py | 1 + pvmismatch/tests/test_setsuns.py | 49 +++++++++++++++++++++++++++ 2 files changed, 50 insertions(+) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index 5999a39..ce8ac83 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -256,6 +256,7 @@ def setSuns(self, Ee, cells=None): 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: # gh34: make new objects as needed from copies diff --git a/pvmismatch/tests/test_setsuns.py b/pvmismatch/tests/test_setsuns.py index c9f89e9..806eb35 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(): @@ -52,30 +56,75 @@ def test_set_str_2(): 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 From 2b451d550d36d62e66b68e38d4e63dc35125602b Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Tue, 24 Jan 2017 11:34:33 -0800 Subject: [PATCH 08/18] also copy cells first if changing all cell irradiance --- pvmismatch/pvmismatch_lib/pvmodule.py | 1 + 1 file changed, 1 insertion(+) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index ce8ac83..5f57388 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -248,6 +248,7 @@ def setSuns(self, Ee, cells=None): for pvc in self.pvcells: pvc.Ee = Ee elif np.size(Ee) == self.numberCells: + self.pvcells = copy(self.pvcells) # copy list first for cell_idx, Ee_idx in enumerate(Ee): # gh34: make new objects as needed from copies self.pvcells[cell_idx] = copy(self.pvcells[cell_idx]) From 6517a4adca5ca9be3511b5b047a40019fefd850d Mon Sep 17 00:00:00 2001 From: Mark Mikofski Date: Tue, 24 Jan 2017 11:45:10 -0800 Subject: [PATCH 09/18] fix if string Ee is a list of one Ee, move copy pvmods list before loops - duh --- pvmismatch/pvmismatch_lib/pvstring.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/pvmismatch/pvmismatch_lib/pvstring.py b/pvmismatch/pvmismatch_lib/pvstring.py index 888f0ad..91e76be 100644 --- a/pvmismatch/pvmismatch_lib/pvstring.py +++ b/pvmismatch/pvmismatch_lib/pvstring.py @@ -100,10 +100,10 @@ def setSuns(self, Ee): 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(): # gh34: make new objects as needed from copies - self.pvmods = copy(self.pvmods) # copy list first self.pvmods[pvmod] = copy(self.pvmods[pvmod]) if hasattr(cell_Ee, 'keys'): self.pvmods[pvmod].setSuns(**cell_Ee) @@ -113,9 +113,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() From f65f5abf5a4929e5f292c5d50846a824a3a39bc3 Mon Sep 17 00:00:00 2001 From: Bennet Meyers Date: Wed, 25 Jan 2017 14:56:00 -0800 Subject: [PATCH 10/18] Added allowance for Ee dictionary keys that are numbers formatted as strings --- pvmismatch/pvmismatch_lib/pvstring.py | 1 + pvmismatch/pvmismatch_lib/pvsystem.py | 1 + 2 files changed, 2 insertions(+) diff --git a/pvmismatch/pvmismatch_lib/pvstring.py b/pvmismatch/pvmismatch_lib/pvstring.py index 91e76be..bd37562 100644 --- a/pvmismatch/pvmismatch_lib/pvstring.py +++ b/pvmismatch/pvmismatch_lib/pvstring.py @@ -103,6 +103,7 @@ def setSuns(self, Ee): self.pvmods = copy(self.pvmods) # copy list first try: for pvmod, cell_Ee in Ee.iteritems(): + pvmod = int(pvmod) # gh34: make new objects as needed from copies self.pvmods[pvmod] = copy(self.pvmods[pvmod]) if hasattr(cell_Ee, 'keys'): diff --git a/pvmismatch/pvmismatch_lib/pvsystem.py b/pvmismatch/pvmismatch_lib/pvsystem.py index 97ff388..516d0b8 100644 --- a/pvmismatch/pvmismatch_lib/pvsystem.py +++ b/pvmismatch/pvmismatch_lib/pvsystem.py @@ -120,6 +120,7 @@ def setSuns(self, Ee): else: for pvstr, pvmod_Ee in Ee.iteritems(): # gh34: make new objects as needed from copies + pvstr = int(pvstr) self.pvstrs[pvstr] = copy(self.pvstrs[pvstr]) self.pvstrs[pvstr].setSuns(pvmod_Ee) self.Isys, self.Vsys, self.Psys = self.calcSystem() From 131db4fd208b273dcc63e5ef9491b561859538a1 Mon Sep 17 00:00:00 2001 From: Bennet Meyers Date: Wed, 25 Jan 2017 15:13:51 -0800 Subject: [PATCH 11/18] Easy efficiency improvement by only iterating over unique cells when setting a whole module with one Ee. --- pvmismatch/pvmismatch_lib/pvmodule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index 5f57388..e7636f6 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -245,7 +245,7 @@ def setSuns(self, Ee, cells=None): else: new_pvcells[cell_id] = old_pvcells[pvcell] self.pvcells = new_pvcells - for pvc in self.pvcells: + for pvc in np.unique(self.pvcells): pvc.Ee = Ee elif np.size(Ee) == self.numberCells: self.pvcells = copy(self.pvcells) # copy list first From df67e45caec7059ccba64b05f1b00bc14e90b42b Mon Sep 17 00:00:00 2001 From: Bennet Meyers Date: Wed, 25 Jan 2017 15:44:05 -0800 Subject: [PATCH 12/18] Setting subset of cells in a module with scalar Ee only copies as necessary. --- pvmismatch/pvmismatch_lib/pvmodule.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index e7636f6..0e1848e 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -259,10 +259,15 @@ def setSuns(self, Ee, cells=None): Ncells = np.size(cells) self.pvcells = copy(self.pvcells) # copy list first if np.isscalar(Ee): - for cell_idx in cells: - # gh34: make new objects as needed from copies - self.pvcells[cell_idx] = copy(self.pvcells[cell_idx]) - 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): # gh34: make new objects as needed from copies From 9b4108c2b04f54caae048e2149efa3f12ea631e2 Mon Sep 17 00:00:00 2001 From: Bennet Meyers Date: Wed, 25 Jan 2017 16:21:01 -0800 Subject: [PATCH 13/18] Same improvement on when to make copies for case when given list of cell ids and Ee values. --- pvmismatch/pvmismatch_lib/pvmodule.py | 19 +++++++++++++++---- pvmismatch/tests/test_setsuns.py | 1 + 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index 0e1848e..d92da48 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -269,10 +269,21 @@ def setSuns(self, Ee, cells=None): else: self.pvcells[cell_id] = old_pvcells[pvcell] elif np.size(Ee) == Ncells: - for cell_idx, Ee_idx in zip(cells, Ee): - # gh34: make new objects as needed from copies - self.pvcells[cell_idx] = copy(self.pvcells[cell_idx]) - self.pvcells[cell_idx].Ee = Ee_idx + # Find unique irradiance values + 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/tests/test_setsuns.py b/pvmismatch/tests/test_setsuns.py index 806eb35..b7dcb30 100644 --- a/pvmismatch/tests/test_setsuns.py +++ b/pvmismatch/tests/test_setsuns.py @@ -133,4 +133,5 @@ def test_gh34_35(): 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] From 87609a1d1e4e08fdbf5ec85d59651fcd40a55bf2 Mon Sep 17 00:00:00 2001 From: Bennet Meyers Date: Wed, 25 Jan 2017 16:38:41 -0800 Subject: [PATCH 14/18] Use set instead of np.unique. --- pvmismatch/pvmismatch_lib/pvmodule.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index d92da48..abde07a 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -245,7 +245,7 @@ def setSuns(self, Ee, cells=None): else: new_pvcells[cell_id] = old_pvcells[pvcell] self.pvcells = new_pvcells - for pvc in np.unique(self.pvcells): + for pvc in set(self.pvcells): pvc.Ee = Ee elif np.size(Ee) == self.numberCells: self.pvcells = copy(self.pvcells) # copy list first From 8df6386cce4c2869080ec4422cbcb1465bc690c5 Mon Sep 17 00:00:00 2001 From: Bennet Meyers Date: Wed, 1 Feb 2017 20:47:06 -0800 Subject: [PATCH 15/18] Removing gh34 comments. --- pvmismatch/pvmismatch_lib/pvmodule.py | 2 -- pvmismatch/pvmismatch_lib/pvstring.py | 2 -- pvmismatch/pvmismatch_lib/pvsystem.py | 2 -- 3 files changed, 6 deletions(-) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index abde07a..9d65dbb 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -175,7 +175,6 @@ def __init__(self, cell_pos=STD96, pvcells=None, pvconst=PVconstants(), # PVcell.calcCell() creates new np.ndarray if attributes change pvcells = PVcell(pvconst=self.pvconst) if isinstance(pvcells, PVcell): - # GH35: don't make copies, use same reference for all objects pvcells = [pvcells] * self.numberCells if len(pvcells) != self.numberCells: # TODO: use pvexception @@ -250,7 +249,6 @@ def setSuns(self, Ee, cells=None): elif np.size(Ee) == self.numberCells: self.pvcells = copy(self.pvcells) # copy list first for cell_idx, Ee_idx in enumerate(Ee): - # gh34: make new objects as needed from copies self.pvcells[cell_idx] = copy(self.pvcells[cell_idx]) self.pvcells[cell_idx].Ee = Ee_idx else: diff --git a/pvmismatch/pvmismatch_lib/pvstring.py b/pvmismatch/pvmismatch_lib/pvstring.py index bd37562..84c33b0 100644 --- a/pvmismatch/pvmismatch_lib/pvstring.py +++ b/pvmismatch/pvmismatch_lib/pvstring.py @@ -30,7 +30,6 @@ 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): - # GH35: don't make copies, use same reference for all objects pvmods = [pvmods] * self.numberMods # reset pvconsts in all pvcells and pvmodules for p in pvmods: @@ -104,7 +103,6 @@ def setSuns(self, Ee): try: for pvmod, cell_Ee in Ee.iteritems(): pvmod = int(pvmod) - # gh34: make new objects as needed from copies self.pvmods[pvmod] = copy(self.pvmods[pvmod]) if hasattr(cell_Ee, 'keys'): self.pvmods[pvmod].setSuns(**cell_Ee) diff --git a/pvmismatch/pvmismatch_lib/pvsystem.py b/pvmismatch/pvmismatch_lib/pvsystem.py index 516d0b8..4f5a74a 100644 --- a/pvmismatch/pvmismatch_lib/pvsystem.py +++ b/pvmismatch/pvmismatch_lib/pvsystem.py @@ -34,7 +34,6 @@ 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): - # GH35: don't make copies, use same reference for all objects pvstrs = [pvstrs] * self.numberStrs if len(pvstrs) != self.numberStrs: # TODO: use pvmismatch excecptions @@ -119,7 +118,6 @@ def setSuns(self, Ee): pvstr.setSuns(Ee) else: for pvstr, pvmod_Ee in Ee.iteritems(): - # gh34: make new objects as needed from copies pvstr = int(pvstr) self.pvstrs[pvstr] = copy(self.pvstrs[pvstr]) self.pvstrs[pvstr].setSuns(pvmod_Ee) From cbc0b23ce9284f13488c372061a274dd8ecee41d Mon Sep 17 00:00:00 2001 From: Bennet Meyers Date: Wed, 1 Feb 2017 20:50:45 -0800 Subject: [PATCH 16/18] Replacing set(self.pvcells) with old_pvcells.itervalues() --- pvmismatch/pvmismatch_lib/pvmodule.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index 9d65dbb..a1ea791 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -244,7 +244,8 @@ def setSuns(self, Ee, cells=None): else: new_pvcells[cell_id] = old_pvcells[pvcell] self.pvcells = new_pvcells - for pvc in set(self.pvcells): + pvcell_set = old_pvcells.itervalues() + for pvc in pvcell_set: pvc.Ee = Ee elif np.size(Ee) == self.numberCells: self.pvcells = copy(self.pvcells) # copy list first From 2ea7540349c2094da1c7b3489dce46ac34a3fe44 Mon Sep 17 00:00:00 2001 From: Bennet Meyers Date: Wed, 1 Feb 2017 20:58:27 -0800 Subject: [PATCH 17/18] Adding TODO for better setSun interface --- pvmismatch/pvmismatch_lib/pvmodule.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pvmismatch/pvmismatch_lib/pvmodule.py b/pvmismatch/pvmismatch_lib/pvmodule.py index a1ea791..1e9e1d3 100644 --- a/pvmismatch/pvmismatch_lib/pvmodule.py +++ b/pvmismatch/pvmismatch_lib/pvmodule.py @@ -269,6 +269,8 @@ def setSuns(self, Ee, cells=None): self.pvcells[cell_id] = old_pvcells[pvcell] elif np.size(Ee) == Ncells: # 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) From 54e0719d362dd1028aea79d9b08aff8f2d29035d Mon Sep 17 00:00:00 2001 From: Bennet Meyers Date: Wed, 1 Feb 2017 21:04:46 -0800 Subject: [PATCH 18/18] add a test to show that if the user sets the sun on multiple cells to different irradiance, that extra cells are not created unnecessarily --- pvmismatch/tests/test_setsuns.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/pvmismatch/tests/test_setsuns.py b/pvmismatch/tests/test_setsuns.py index b7dcb30..f11f9d4 100644 --- a/pvmismatch/tests/test_setsuns.py +++ b/pvmismatch/tests/test_setsuns.py @@ -135,3 +135,22 @@ def test_gh34_35(): 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] +