Skip to content

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

@mikofski

Description

@mikofski

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 Better memory #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]           

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions