Source code for symenergy.auxiliary.io

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
@author: mcsoini
"""

import os
import errno
from hashlib import md5
import pandas as pd
from symenergy import _get_logger
import symenergy
from symenergy.auxiliary import params

logger = _get_logger(__name__)

[docs]class CacheParams(params.RcParams): items_opts = {'path': {'types': {str: 'none'}, 'values': (), # only strings, no other value allowed 'cond': 'string'}} items_default = {'path': os.path.join(list(symenergy.__path__)[0], 'cache')}
cache_params = CacheParams()
[docs]def mkdirs(newdir): try: os.makedirs(newdir) except OSError as err: # Reraise the error unless it's about an already existing directory if err.errno != errno.EEXIST or not os.path.isdir(newdir): raise
[docs]class Cache(): ''' Handles model cache files. Cache files store the model results. They are automatically written to pickle files whose filename is generated is generated from a hash of the model objects. Existing cache files are automatically read to skip the model solution process. ''' prefix = None cache_name = 'cache' def __init__(self, m_name): ''' Cache instances take the model or evaluator hash as input. It is used to generate the filename. Parameters ---------- m_name : str Attributes ---------- fn -- str Name of cache file fn_name -- str Shorter cache file name for logging. ''' self._m_name = m_name self.fn = self.fn_name= None self._log_str = None self._path = cache_params['path'] mkdirs(self.path) self.fn = self.get_name(self.path) sep = os.path.sep self.fn_name = (f'...{sep}{self.fn.split(sep)[-2]}{sep}' + os.path.basename(self.fn)) self._make_log_str() @property def path(self): return self._path @path.setter def path(self, _): raise AttributeError("Attempt to change cache path. Modify " "the symenergy.cache_params['path'] value " "instead, prior to initializing the Model or " "Evaluator class.") def _make_log_str(self): self._log_str = (f'Loading from cache file {self.fn_name}.', ('Please delete this file to re-solve model: ' f'Model.{self.cache_name}.delete()'))
[docs] def load(self): ''' Load model results from cache file. Returns ------- pandas.DataFrame Dataframe containing model results. ''' smax = len(max(self._log_str, key=len)) sep_str = ('*' * smax,) * 2 [logger.warning(st) for st in sep_str + self._log_str + sep_str] return pd.read_pickle(self.fn)
[docs] def write(self, df): ''' Write dataframe to cache file. Parameters ---------- df : pandas.DataFrame Table with model results ''' df.to_pickle(self.fn)
@property def file_exists(self): ''' Checks whether the cache file exists. Returns ------- bool True if the cache file corresponding to the hashed filename exists. False otherwise. ''' return os.path.isfile(self.fn)
[docs] def delete(self): ''' Deletes cache file. ''' if os.path.isfile(self.fn): logger.info('Removing file %s'%self.fn_name) os.remove(self.fn) else: logger.info('File doesn\'t exist. ' 'Could not remove %s'%self.fn_name)
[docs] def get_name(self, _dir): ''' Returns a unique hashed model name based on the constraint, variable, multiplier, and parameter names. Parameters ---------- m : model.Model SymEnergy model instance ''' if not self.prefix: try: prefix_dict = {'cache': 'm', 'cache_eval': 'e', 'cache_lambd': 'l'} prefix = prefix_dict[self.cache_name] except KeyError as e: raise KeyError(str(e) + '. Please define "prefix" class ' 'attribute in subclassed symenergy cache.') else: prefix = self.prefix m_name = self._m_name[:12].upper() fn = f'{prefix}{m_name}.pickle' fn = os.path.join(_dir, fn) fn = os.path.abspath(fn) return fn
def __repr__(self): return (f'Symenergy cache instance id={id(self)}\n' f' * fn = {self.fn}\n' f' * fn_name = {self.fn_name}\n' f' * file exists: {os.path.isfile(self.fn)}')
[docs]class EvaluatorCache(Cache): def __init__(self, name, cache_name): self.cache_name = cache_name super().__init__(name) def _make_log_str(self): self._log_str = (f'Loading from cache file {self.fn_name}.', ('Please delete this file to re-evaluate: ' f'Evaluator.{self.cache_name}.delete()'))