Source code for caelus.post.funcobj.funcobj
# -*- coding: utf-8 -*-
"""\
Core function object utilities
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Implements the base classes upon with the concrete function object interfaces
are built on. :class:`FunctionObject` implements common methods used by all the
subclasses.
"""
import abc
import glob
import os
from pathlib import Path
from ...io.caelusdict import CaelusDict
from ...utils import osutils
[docs]
class DictMeta(abc.ABCMeta):
"""Create property methods and add validation for properties.
This metaclass implements the boilerplate code necessary to add
getter/setters for various entries found in a Caelus input file. It expects
a class variable ``_dict_properties`` that contains tuples for the various
entries in the input file. The tuple can be of two forms:
- (name, default_value)
- (name, default_value, valid_values)
"""
# pylint: disable=no-value-for-parameter
def __init__(cls, name, bases, cdict, **kwargs):
super(DictMeta, cls).__init__(name, bases, cdict)
if "_dict_properties" in cdict:
cls.process_properties(cdict["_dict_properties"])
[docs]
def process_properties(cls, proplist):
"""Create getters/setters for properties"""
for plist in proplist:
cls.process_property(plist)
[docs]
def process_property(cls, plist):
"""Process a property"""
name = plist[0]
options = plist[2] if len(plist) == 3 else None
doc = "%s" % name
def getter(self):
"""Getter"""
return self.data.get(name, plist[1])
if options:
def setter(self, value):
"""Setter"""
if not value in options:
raise ValueError(
"%s: Invalid option for '%s'. "
"Valid options are:\n\t%s"
% (cls.__name__, name, options)
)
self.data[name] = value
else:
def setter(self, value):
"Setter"
self.data[name] = value
setattr(cls, name, property(getter, setter, doc=doc))
[docs]
class FuncObjMeta(DictMeta):
"""Specialization for Function objects"""
def __call__(cls, *args, **kwargs):
"""Check if it is a concrete type"""
if not hasattr(cls, "_funcobj_type"):
raise RuntimeError(f"Cannot instantiate {cls.__name__}")
return super().__call__(*args, **kwargs)
[docs]
class FunctionObject(metaclass=FuncObjMeta):
"""Base class representing an OpenFOAM function object"""
_run_control_opts = [
'none',
'timeStep',
'writeTime',
'runTime',
'adjustableRunTime',
'clockTime',
'cpuTime',
'onEnd',
]
_dict_properties = [
('libs', None),
('region', 'region0'),
('enabled', True),
('log', True),
('timeStart', 0),
('timeEnd', None),
('executeControl', 'timeStep', _run_control_opts),
('executeInterval', 1),
('writeControl', 'timeStep', _run_control_opts),
('writeInterval', 1),
]
[docs]
@classmethod
def funcobj_type(cls):
"""Return the string representing this functionObject type."""
return getattr(cls, "_funcobj_type")
[docs]
@classmethod
def create(cls, *, name, casedir=None, **kwargs):
"""Create a function object from scratch"""
obj = cls.__new__(cls)
obj.casedir = Path(casedir or os.getcwd())
obj.name = name
obj.data = CaelusDict()
obj.data.type = obj._funcobj_type
obj.data.libs = obj._funcobj_libs
obj.data.update(kwargs)
return obj
def __init__(self, name, obj_dict, *, casedir=None):
"""Initialize object from input dictionary.
Args:
name (str): User-defined name for this object (in functions)
obj_dict (CaelusDict): Input dictionary for this functionObject
casedir (path): Path to the case directory (default: cwd)
"""
self.casedir = Path(casedir or os.getcwd())
self.name = name
self.data = obj_dict
@property
def root(self):
"""Root path to the function object in postProcessing"""
return self.casedir / "postProcessing" / self.name
@property
def times(self):
"""Return the list of time directories available"""
with osutils.set_work_dir(self.root):
return sorted(glob.glob("[0-9]*"), key=float, reverse=True)
@property
def latest_time(self):
"""Return the latest time available"""
return self.times[0] if self.times else ""
def __repr__(self):
return f"<{self.__class__.__name__}: {self.name}>"