Source code for openpnm.utils._workspace

import logging
import pickle
import re
import sys
from datetime import datetime
from uuid import uuid4

from openpnm.utils import SettingsAttr

logger = logging.getLogger("openpnm")

__all__ = ['Workspace']


class WorkspaceSettings(SettingsAttr):
    r"""

    Parameters
    ----------
    default_solver : str
        The solver to use by default, if user does not specify one explicitly.
        The default values is PardisoSpsolve, but a good option is ScipySpsolve
        if the Pardiso is causing problems.
    loglevel : int
        Sets the threshold for the severity of logger message which appear.
        Ranges are as follows:

        ======= ==============================================================
        Level   Description
        ======= ==============================================================
        10      DEBUG: Detailed information, typically of interest only when
                diagnosing problems.
        20      INFO: Confirmation that things are working as expected.
        30      WARNING: An indication that something unexpected happened, or
                indicative of some problem in the near future (e.g. ‘disk
                space low’). The software is still working as expected.
        40      ERROR: Due to a more serious problem, the software has not
                been able to perform some function.
        50      CRITICAL: A serious error, indicating that the program itself
                may be unable to continue running.
        ======= ==============================================================
    """
    # Pardiso requires MKL, which is not available on new Apple chips
    if sys.platform == 'darwin':
        default_solver = 'ScipySpsolve'
    else:
        try:
            import pypardiso
            default_solver = 'PardisoSpsolve'
        except ImportError:
            default_solver = 'ScipySpsolve'
            msg = (
                'PARDISO solver not installed, run `pip install pypardiso`. '
                'Otherwise, simulations will be slow. Apple M chips not supported.'
            )
            logger.error(msg)

    @property
    def loglevel(self):
        return self._loglevel

    @loglevel.setter
    def loglevel(self, value):
        if isinstance(value, str):
            options = {
                "TRACE": 5,
                "DEBUG": 10,
                "INFO": 20,
                "SUCESS": 25,
                "WARNING": 30,
                "ERROR": 40,
                "CRITICAL": 50
            }
            value = options[value]
        self._loglevel = value
        logger.setLevel(value)


[docs] class Workspace(dict): r""" The Workspace object provides the highest level of adminstrative control over active OpenPNM sessions. It is a ``dictionary`` that stores a list of all open Projects by name. This class is a `singleton <https://en.wikipedia.org/wiki/Singleton_pattern>`_ so that whenever and wherever a Workspace is instantiated, the same instance is obtained. This allows it to maintain a definitive record of all open Projects. See Also -------- Project Notes ----- The Workspace object contains a variety of functions that one might expect from the 'file-menu' in a typical GUI. """ __instance__ = None # This __new__ method makes the Workspace a Singleton def __new__(cls, *args, **kwargs): if Workspace.__instance__ is None: Workspace.__instance__ = dict.__new__(cls) return Workspace.__instance__ def __init__(self): super().__init__() self.settings = WorkspaceSettings() self.settings.loglevel = 30
[docs] def copy(self): """Brief explanation of 'copy'""" raise Exception('Cannot copy Workspace, only one can exist at a time')
def _create_console_handles(self, project): r""" Adds all objects in the given project to the console as variables with handle names taken from each object's name. """ import __main__ for item in project: __main__.__dict__[item.name] = item def _validate_name(self, name=None): r""" Generates a valid name for projects """ if name in [None, '']: name = 'proj_01' # Give basic name, then let rest of func fix it if name in self.keys(): # If proposed name is taken, increment it if not re.search(r'_\d+$', name): # If name does not end with _## name = name + '_01' prefix, count = name.rsplit('_', 1) n = [0] for item in list(self.keys()): if item.startswith(prefix+'_'): n.append(int(item.split(prefix+'_')[1])) name = prefix+'_'+str(max(n)+1).zfill(2) return name
[docs] def save_workspace(self, filename=None): r""" Saves all projects in the current workspace as a single file Parameters ---------- filename : str The filename to use when saving. If not provided, the present date and time are used. Notes ----- The file is actually zip archive containing ``pnm`` files, one for each project in the workspace. This archive can be extracted and each ``pnm`` file can be loaded manually using ``load_project`` or the ``openpnm.io.PNM`` class. """ if filename is None: dt = datetime.now() filename = dt.strftime("%Y_%m_%d_%H_%M_%S") from zipfile import ZipFile with ZipFile(filename + '.wrk', 'w') as z: for prj in self.values(): prj.save_project() z.write(prj.name + '.pnm')
[docs] def load_workspace(self, filename): r""" Loads project(s) from a saved workspace into current workspace Parameters ---------- filename : str or Path The filename containing the saved workspace """ from zipfile import ZipFile with ZipFile(filename, 'r') as z: logger.info('Loading projects contained in ' + filename) files = z.filelist for f in files: self.load_project(f.orig_filename)
[docs] def save_project(self, project, filename=None): r""" Saves given Project to a ``pnm`` file This will include all of associated objects, including algorithms. Parameters ---------- project : Project The project to save. filename : str, optional If no filename is given, the given project name is used. See Notes for more information. See Also -------- save_workspace Notes ----- The filename can be a string such as 'saved_file.pnm'. The string can include absolute path such as 'C:\networks\saved_file.pnm', or can be a relative path such as '..\..\saved_file.pnm', which will look 2 directories above the current working directory. Can also be a path object object such as that produced by ``pathlib`` or ``os.path`` in the Python standard library. """ if filename is None: dt = datetime.now() filename = dt.strftime("%Y_%m_%d_%H_%M_%S") with open(filename.split('.')[-1]+'.pnm', 'wb') as f: pickle.dump(project, f)
[docs] def load_project(self, filename): r""" Loads a Project from the specified 'pnm' file The loaded project is added to the Workspace. This will *not* delete any existing Projects in the Workspace and will rename any Projects being loaded if necessary. Parameters ---------- filename : str or Path The name of the file to open. See Notes for more information. See Also -------- load_workspace Notes ----- The filename can be a string such as 'saved_file.pnm'. The string can include absolute path such as 'C:\networks\saved_file.pnm', or can be a relative path such as '..\..\saved_file.pnm', which will look 2 directories above the current working directory. Can also be a path object object such as that produced by ``pathlib`` or ``os.path`` in the Python standard library. """ with open(filename, 'rb') as f: proj = pickle.load(f) proj.settings.uuid = str(uuid4()) proj.name = self._validate_name(proj.name) self[proj.name] = proj return proj
[docs] def close_project(self, project): r""" Removes the specified Project from the Workspace This does not save the project, so any changes will be lost. Parameters ---------- project : Project The Project object to be copied """ _ = self.pop(project.name, None)
[docs] def copy_project(self, project, name=None): r""" Makes a copy of an existing Project Parameters ---------- project : Project The Project object to be copied name : str, optional A name for the new copy of the project. If not supplied, then one will be generated (e.g. 'proj_02') Returns ------- proj : list A handle to the new Project """ proj = project.copy(name) return proj
[docs] def new_project(self, name=None): r""" Creates a new empty Project object Parameters ---------- name : str, optional The unique name to give to the project. If none is given, one will be automatically generated (e.g. 'proj_01`) Returns ------- proj : list An empty Project object, suitable for passing into a Network generator """ from openpnm.utils import Project proj = Project(name=name) return proj
def __str__(self): # pragma: no cover hr = '―'*78 s = hr + '\n' for item in self.values(): s += item.name + '\n' s += item.__str__() + '\n' return s