SunPower / PVMismatch

An explicit Python PV system IV & PV curve trace calculator which can also calculate mismatch.
http://sunpower.github.io/PVMismatch/
BSD 3-Clause "New" or "Revised" License
79 stars 30 forks source link

cache last X calculations, and be smarter about checking cache before recalculating #60

Open mikofski opened 6 years ago

mikofski commented 6 years ago

This is definitely relevant to #48 and is a spinoff of @bmeyers original cache idea in the rejected PR #42 , and a continuation of the accepted PR #43, that closed the relevant issues #34 and #35 about copies, and of course the ability to delay calculation #59.

The idea is simple, every time you make a calculation in __setattr__ or in calcCells():

  1. check the cache for a similar cell, it's hash should be identical
  2. if the cell doesn't exist yet, then calculate it and store the cell in a cache, like @bmeyers does in #42 except just store the entire PVcell, no need to serialize it (the cell) right now

The cache should be in pvconstants and should probably have a limit of 100 or 1000 cells. Any strategy can be used to determine which cells to keep in the cache, for example, a count could be kept of how many times each cell was called, and then the least popular cells are culled as needed.

This cache could be repeated for modules and strings as well.

sample code:

from pvmismatch import *

# pvconstants
pvconst = pvconstants.PVconstants()
# monkey patch for example
pvconst.cache = {'pvcells': {}, 'pvmods': {}, 'pvstrs': {}}
pvconst.cache_maxsize = 100

PARAMS = ['Rs', 'Rsh', 'Isat1_T0', 'Isat2_T0', 'Isc0_T0', 'aRBD', 'bRBD', 'VRBD', 'nRBD', 'Eg', 'alpha_Isc', 'Tcell', 'Ee']

def hash_cell(cell, params=PARAMS):
    """cell params is a dictionary with cell params"""
    cell_params = vars(cell)
    cell_hash = str()
    for k in params:
        cell_hash += hex(hash(cell_params[k]))[2:]  # omit '0x'
    return '0x%s' % cell_hash

This works! Generate two different PVcells that are identical, but have different memory locations, and the calculated hash is the same.

>>> pvc = pvcell.PVcell()
>>> cell_hash = hash_cell(pvc)
'0x22f50b4c78e2706476d29247d00a1c997714032461851a3c0257edda00dc9c779a6b50b10061000d96c02215ef60x10df5081cebee80591bad56da435c033333333333334011802e8b2dc60be864cccccccccc012a1'
>>> pvconst.cache['pvcells'][cell_hash ] = pvc  # cache cell for future
>>> id(pvc)
1880798536480  # different
>>> hash(pvc)
117549908530  # different

>>> pvsys = pvsystem.PVsystem()
>>> new_cell_hash = hash_cell(pvsys.pvstrs[0].pvmods[0].pvcells[0])
'0x22f50b4c78e2706476d29247d00a1c997714032461851a3c0257edda00dc9c779a6b50b10061000d96c02215ef60x10df5081cebee80591bad56da435c033333333333334011802e8b2dc60be864cccccccccc012a1'
>>> new_cell_hash in pvconst.cache['pvcells']
True
>>> id(pvsys.pvstrs[0].pvmods[0].pvcells[0])
1880798601344  # different
hash(pvsys.pvstrs[0].pvmods[0].pvcells[0])
117549912584  # different

then in PVmodules

# in #48 update method
def update(self, cell_dicts):
    for pvc, params in cell_dicts.item():
        cell_params = vars(self.pvcells[pvc])
        cell_params.pop('Icell')
        cell_params.pop('Vcell')
        cell_params.pop('Pcell')
        cell_params.pop('pvconst')
        cell_params.pop('_calc_now')
        cell_params.update(params)
        cell_hash = pvconstants.hash_cell(cell_params)
        if cell_hash in self.pvconst.cache['pvcells']:
            # use cache
             self.pvcells[pvc] = self.pvconst.cache['pvcells'][cell_hash]
        else:
            # make a new cell
            self.pvcells[pvc] = pvcell.PVcell(**params, pvconst=self.pvconst)
            self.pvconst.cache['pvcells'][cell_hash] = self.pvcells[pvc]           
mikofski commented 6 years ago

@bmeyers check this out, you can use a unique hash for cells as the keys in a dictionary of cells that are cached. This makes returning memoized cells zeroth order because they are actually stored by their unique hash, and because the same key can't be stored twice, you can never have a duplicate in the cache.

You make the unique hash by appending hex strings of the hash of each value to make a super long string that is unique to a set of parameters, as long as you always append them in the same order.