# -*- coding: utf-8 -*-
# pylint: disable=too-few-public-methods
"""
Caelus/OpenFOAM Input File Datatypes
"""
import abc
import re
import sys
import numpy as np
import six
[docs]
@six.add_metaclass(abc.ABCMeta)
class FoamType(object):
"""Base class for a FOAM type"""
[docs]
@abc.abstractmethod
def write_value(self, fh=sys.stdout, indent_str=''):
"""Write as a Caelus/OpenFOAM entry
This method is called by :class:`~caelus.io.printer.DictPrinter` to
format the data suitable for writing to a Caelus input file that can be
read by the solvers.
Args:
fh (file): A valid file handle
indent_str (str): Padding for indentation
"""
def __repr__(self):
return "<%s>" % self.__class__.__name__
def format_ndarray(self, value):
arr_size = value.size
arr_str = None
threshold = np.get_printoptions()['threshold']
try:
np.set_printoptions(threshold=arr_size + 10)
arr_str = np.array_str(value, max_line_width=80)
finally:
np.set_printoptions(threshold=threshold)
arr_str = re.sub(r']', ')', re.sub(r'\[', '(', arr_str))
return arr_str
[docs]
class Dimension(FoamType):
"""Caelus dimensional units
Represents the units of a dimensional quantity as an array of seven
integers that represent the powers of the fundamental units: mass, length,
time, temperature, quantity, current, and luminous intensity.
"""
dim_names = "mass length time temperature quantity current luminous_intensity".split()
def __init__(self, units=None, **kwargs):
"""Provide an array of individual units as keyword arguments
Args:
units (list): A list of 5 or 7 entries
"""
self.units = np.zeros((7,), dtype=int)
if units is None and not kwargs:
raise RuntimeError(
"Either units or dimensional types must be provided"
)
num_units = len(units)
if units:
if not ((num_units == 5) or (num_units == 7)):
raise ValueError("Incorrect units specified: %s" % units)
for i, uval in enumerate(units):
self.units[i] = uval
else:
for i, key in enumerate(self.dim_names):
if key in kwargs:
self.units[i] = kwargs[key]
[docs]
def write_value(self, fh=sys.stdout, indent_str=''):
"""Write out the dimensions
Args:
fh (file): A valid file handle
indent_str (str): Padding for indentation
"""
fh.write("[" + ' '.join("%d" % uval for uval in self.units) + "];\n")
def __str__(self):
return "[" + ' '.join("%d" % uval for uval in self.units) + "]"
def __repr__(self):
return "<%s: [%s]>" % (
self.__class__.__name__,
' '.join('%d' % uval for uval in self.units),
)
[docs]
class DimStr(FoamType):
"""String dimensions"""
def __init__(self, units):
self.units = units
[docs]
def write_value(self, fh=sys.stdout, indent_str=''):
fh.write(f"[{self.units}];\n")
def __str__(self):
return f"[{self.units}]"
[docs]
class DimValue(FoamType):
"""A dimensioned value
A dimensioned value contains three parts: the name, units, and the value.
Units are of type :class:`Dimension` and value can be a scalar, vector,
tensor or a symmetric tensor.
"""
def __init__(self, name, dims, value):
self.name = name
self.dims = dims
self.value = value
[docs]
def write_value(self, fh=sys.stdout, indent_str=''):
"""Write out the dimensional value"""
fh.write("%s " % self.name)
# fh.write("[" + ' '.join("%d"%uval for uval in self.dims.units) + "] ")
fh.write(f" {str(self.dims)} ")
fh.write(str(self.value))
fh.write(";\n")
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self.name)
[docs]
class Directive(FoamType):
"""A Caelus directive type
Directives are keyword-less entries that indicate certain processing
actions and begin with a hash (``#``) symbol. For example, the
``#includeEtc`` directive that can be used to include files from
``foamEtc`` directory.
"""
def __init__(self, directive, value):
#: Type of directive (str)
self.directive = directive
#: Value of the directive (e.g., file to be included)
self.value = value
[docs]
def write_value(self, fh=sys.stdout, indent_str='', nested=False):
"""Write out the dimensional value"""
if not nested:
fh.write("\n%s %s\n" % (self.directive, self.value))
else:
fh.write("%s%s %s\n" % (indent_str, self.directive, self.value))
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self.directive[1:])
[docs]
class CalcDirective(FoamType):
"""A ``#calc`` directive entry
Example::
radHalfAngle #calc "degToRad($halfAngle)";
"""
def __init__(self, directive, value):
self.directive = directive
self.value = value
[docs]
def write_value(self, fh=sys.stdout, indent_str='', nested=False):
"""Write out the dimensional value"""
if not nested:
fh.write("%s %s;\n" % (self.directive, self.value))
else:
fh.write("%s%s %s;\n" % (indent_str, self.directive, self.value))
[docs]
class EvalDirective(FoamType):
"""A ``#eval`` directive entry
Examples:
timeStart #eval #{ 0.1 * ${/endTime} #};
r0CosT #eval{ $r0*cos(degToRad($t )) };
"""
def __init__(self, directive, value):
self.directive = directive
self.value = value
[docs]
def write_value(self, fh=sys.stdout, indent_str=''):
if self.value[0] == '{':
fh.write("%s%s;\n" % (self.directive, self.value))
else:
fh.write("%s %s;\n" % (self.directive, self.value))
[docs]
class CodeStream(FoamType):
"""A codestream entry
Contains C++ code that can be compiled and executed to determine dictionary
parameters.
"""
def __init__(self, value):
self.directive = "#codeStream"
self.value = value
[docs]
def write_value(self, fh=sys.stdout, indent_str=''):
"""Write out the dimensional value"""
fh.write("%s\n" % self.directive + indent_str + "{\n")
indent_str += ' '
indent_str1 = indent_str
for ctype, value in self.value:
fh.write(indent_str + ctype + "\n")
lines = value.splitlines()
nskip = lines[-1].count(' ')
fh.write(indent_str + lines[0] + "\n")
for line in lines[1:-1]:
fh.write(indent_str1 + line[nskip:] + "\n")
fh.write(indent_str + lines[-1].lstrip() + ";\n\n")
fh.write("};\n")
[docs]
class MacroSubstitution(FoamType):
"""Macro substition without keyword"""
def __init__(self, value, semi=True):
self.value = value
self.semi = semi
[docs]
def write_value(self, fh=sys.stdout, indent_str='', nested=False):
"""Write standalone macro substitution"""
if self.semi:
fh.write(indent_str + "%s;\n" % self.value)
else:
fh.write(indent_str + "%s\n" % self.value)
[docs]
class Field(FoamType):
"""A field declaration
This class represents both uniform and non-uniform fields. The attribute
``ftype`` indicates the type of field and the ``value`` contains the value
for the given field. Uniform fields can be scalar, vector, tensor, or
symmetric tensors. Non-uniform fields are typically a :class:`ListTemplate`
entity.
"""
def __init__(self, ftype, value):
self.ftype = ftype
self.value = value
[docs]
def write_value(self, fh=sys.stdout, indent_str=''):
"""Write value in OpenFOAM format"""
if self.ftype == "uniform":
self.write_uniform(fh)
else:
self.write_nonuniform(fh)
def __repr__(self):
return "<%s: %s>" % (self.__class__.__name__, self.ftype)
[docs]
class BoundaryList(FoamType):
"""polyMesh/boundary file"""
def __init__(self, value):
self.value = value
[docs]
def write_value(self, fh=sys.stdout, indent_str=''):
fh.write("\n" + indent_str + "%d" % len(self.value))
[docs]
class MultipleValues(FoamType):
"""Multiple values for single keyword
Example::
laplacian(nuEff,U) Gauss linear corrected;
Here "Gauss linear corrected" is stored as an instance of this class to
disambiguate between multi-valued entries and plain lists.
"""
def __init__(self, value):
self.value = value
[docs]
def write_value(self, fh=sys.stdout, indent_str=''):
outs = []
for val in self.value:
if isinstance(val, np.ndarray):
sval = self.format_ndarray(val)
outs.append(sval)
elif isinstance(val, list):
sval = "(" + " ".join(str(v) for v in val) + ")"
outs.append(sval)
else:
outs.append(str(val))
fh.write(" ".join(outs))
fh.write(";\n")
def __repr__(self):
return "<MultipleValues: %s>" % self.value
def __str__(self):
return " ".join(str(val) for val in self.value)
[docs]
class ListTemplate(FoamType):
"""List<T> type entries"""
def __init__(self, ltype, value):
self.list_type = ltype
self.value = value
[docs]
def write_value(self, fh=sys.stdout, indent_str=''):
"""Write out a List<T> value"""
arr_size = self.value.size
arr_len = len(self.value)
arr_str = None
threshold = np.get_printoptions()['threshold']
try:
np.set_printoptions(threshold=arr_size + 10)
arr_str = np.array_str(self.value, max_line_width=80)
finally:
np.set_printoptions(threshold=threshold)
arr_str = re.sub(r']', ')', re.sub(r'\[', '(', arr_str))
fh.write("\n%s %d\n" % (self.list_type, arr_len))
fh.write(arr_str + ";\n")