Source code for openpnm.utils._project
import logging
import pickle
import re
import uuid
from copy import deepcopy
from datetime import datetime
import numpy as np
from openpnm.utils import SettingsAttr, Workspace
ws = Workspace()
logger = logging.getLogger(__name__)
__all__ = [
'Project',
]
class ProjectSettings(SettingsAttr):
r"""
uuid : str
A universally unique identifier for the object to keep things straight
"""
uuid = ''
original_uuid = ''
@property
def name(self):
return self._name
@name.setter
def name(self, name):
name = ws._validate_name(name)
for v in list(ws.values()):
if v.settings is self:
ws[name] = ws.pop(v.settings.name)
self._name = name
[docs]
class Project(list):
r"""
This class provides a container for all OpenPNM objects in a given
simulation.
A simulation is defined as a Network and all of it's associated objects.
When instantiating a Network, a Project can be passed as an argument, but
if not given one is created. When instantiating any other object either
a Network or a Project can be supplied. In the former case, the
Network's Project is retrieved and used. The end result is that all
objects are stored in a specific Project.
The Project to which any object belongs can be retrieved with
``obj.project``. Conversely, printing a Project displays a list of all
objects it contains.
Moreover, all Projects are registered with the Workspace. Since there can
be only instance of the Workspace it is possible to view all open Projects
by printing the Workspace.
See Also
--------
Workspace
"""
def __init__(self, *args, **kwargs):
self.settings = ProjectSettings()
self.settings['name'] = kwargs.pop('name', None)
self.settings['uuid'] = str(uuid.uuid4())
self.settings['original_uuid'] = self.settings['uuid']
super().__init__(*args, **kwargs)
ws[self.settings['name']] = self
def __getitem__(self, key):
try:
return super().__getitem__(key)
except TypeError:
if isinstance(key, str): # Enable dict-style lookup if key is a string
for item in self:
if item.name == key:
return item
raise KeyError(key)
[docs]
def copy(self, name=None):
r"""
Creates a deep copy of the current project
A deep copy means that new, unique versions of all the objects are
created but with identical data and properties.
Parameters
----------
name : str
The name to give to the new project. If not supplied, a name
is automatically generated.
Returns
-------
proj : list
A new Project object containing copies of all objects
Notes
-----
Because they are new objects, they are given a new uuid
(``obj.settings['uuid']``), but the uuid of the original object
is also stored (``obj.settings['original_uuid']``) for reference.
"""
name = ws._validate_name(name)
proj = deepcopy(self)
for item in proj:
item.settings['uuid'] = str(uuid.uuid4())
proj.settings['uuid'] = str(uuid.uuid4())
proj.settings['name'] = name
ws[name] = proj
return proj
def _set_name(self, name):
self.settings['name'] = name
def _get_name(self):
return self.settings['name']
name = property(fget=_get_name, fset=_set_name)
def _validate_name(self, name):
if name in self.names:
raise Exception('Another object is already named '+name)
for item in self:
for key in item.keys():
if key.split('.')[1] == name:
raise Exception('A property/label is already named '+name)
def _generate_name(self, name=''):
if name in [None, '']:
name = 'obj_01' # Give basic name, then let rest of func fix it
warn = True
if name.endswith('_?'):
name = name.replace('_?', '_01')
warn = False
if name in self.names: # If proposed name is taken, increment it
proposed_name = name
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 self:
if item.name.startswith(prefix+'_'):
n.append(int(item.name.split(prefix+'_')[1]))
name = prefix+'_'+str(max(n)+1).zfill(2)
if warn:
logger.warn(f'{proposed_name} is already taken, using {name} instead')
self._validate_name(name)
return name
@property
def names(self):
names = [i.name for i in self]
return names
@property
def network(self):
for item in self:
if ('throat.conns' in item.keys()) or ('pore.coords' in item.keys()):
return item
@property
def phases(self):
from openpnm.phase import Phase
phases = []
for item in self:
if isinstance(item, Phase):
phases.append(item)
return phases
@property
def algorithms(self):
from openpnm.algorithms import Algorithm
algs = []
for item in self:
if isinstance(item, Algorithm):
algs.append(item)
return algs
@property
def workspace(self):
return ws
def _get_locations(self, label):
r"""
Find locations indicated by the given label regardless of which object
it is defined on
Parameters
----------
label : str
The label whose locations are sought, such as 'pore.left'
Returns
-------
locations : ndarray
A boolean array with ``True`` values indicating which locations
have the given label
Notes
-----
The returns the first instance of ``label`` that it finds
"""
for item in self:
try:
return item[label]
except KeyError:
pass
raise KeyError(label)
def __str__(self): # pragma: no cover
hr = '―'*78
s = '═'*78 + '\n'
s += 'Object Name : Object Class and ID' + '\n'
s += hr + '\n'
for item in self:
s += item.__repr__() + '\n'
s += hr
return s