"""
================================================================================
OpenFoam Core
================================================================================
| This stores the basic classes and functions needed to interact with OpenFoam
| files.
| Written By: Matthew Stadelman
| Date Written: 2016/03/22
| Last Modifed: 2016/08/08
"""
from collections import OrderedDict
import os
import re
[docs]class OpenFoamObject(object):
r"""
General class used to recognize other OpenFoam objects
"""
def __str__(self):
raise NotImplementedError('__str__ method must be subclassed')
[docs]class OpenFoamDict(OpenFoamObject, OrderedDict):
r"""
Class used to build the dictionary style OpenFoam input blocks
"""
[docs] def __init__(self, name, values=None):
r"""
Creates an OpenFoamDict:
name - string printed at top of dictionary in files
values - any valid iterable that can be used to initialize
a dictionary
"""
init_vals = {}
if values is not None:
init_vals = values
#
super().__init__(init_vals)
self.name = name.strip()
[docs] def __str__(self, indent=0):
r"""
Prints a formatted output readable by OpenFoam
"""
fmt_str = '\t{}\t{};\n'
#
str_rep = ('\t'*indent) + self.name + '\n'
str_rep += ('\t'*indent) + '{\n'
#
for key, val in self.items():
if isinstance(val, OpenFoamObject):
str_rep += '\n'
str_rep += val.__str__(indent=(indent+1))
else:
val = str(val).replace(',', ' ')
str_rep += ('\t'*indent) + fmt_str.format(key, val)
#
str_rep += ('\t'*indent) + '}\n'
#
return str_rep
[docs]class OpenFoamList(OpenFoamObject, list):
r"""
Class used to build the output lists used in blockMeshDict.
"""
[docs] def __init__(self, name, values=None):
r"""
Creates an OpenFoamList:
name - string printed at top of dictionary in files
values - any valid iterable that can be used to initialize
a list
"""
init_vals = []
if values is not None:
init_vals = values
#
super().__init__(init_vals)
self.name = name.strip()
[docs] def __str__(self, indent=0):
r"""
Prints a formatted output readable by OpenFoam
"""
fmt_str = '\t{}\n'
#
str_rep = ('\t'*indent) + self.name + '\n'
str_rep += ('\t'*indent) + '(\n'
#
for val in self:
if isinstance(val, OpenFoamObject):
str_rep += '\n'
str_rep += val.__str__(indent=(indent+1))
else:
val = str(val).replace(',', ' ')
str_rep += ('\t'*indent) + fmt_str.format(val)
#
str_rep += ('\t'*indent) + ');\n'
#
return str_rep
[docs]class OpenFoamFile(OpenFoamObject, OrderedDict):
r"""
Class used to build OpenFoam input files
"""
FOAM_HEADER = r"""
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: 4.0 |
| \\ / A nd | Web: www.OpenFOAM.org |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
"""
FOAM_SPACER = r"""
// ************************************************************************* //
"""
HEAD_DICT = OpenFoamDict('FoamFile', [
('version', 2.0),
('format', 'ascii'),
('class', None),
('location', None),
('object', None)
])
# trimming off leading newlines
FOAM_HEADER = FOAM_HEADER[1:]
FOAM_SPACER = FOAM_SPACER[1:]
[docs] def __init__(self, *args, **kwargs):
r"""
Creates and instance of the class passing the first three arguments
to the FileFile header dict and the final argument can be used to
initialize the OpenFoamFile object with entries.
location : string, sets the subdirectory location of the file
during output.
object_name : string, sets initial value of self.name attribute used
to name the output file and the 'object' key in the FoamFile dict
class_name : string, optional, sets the 'class' key in the FoamFile
dict, it defaults to 'dictionary'
values : iterable, optional, any valid iterable that can be used to
initialize a regular Python dictionary
"""
# choosing initialization method based on number of args
if len(args) == 1:
foam_file = OpenFoamFile._init_from_file(args[0])
#
location = foam_file.head_dict['location'].replace('"', "")
object_name = foam_file.head_dict['object']
class_name = foam_file.head_dict.get('class', 'dictionary')
values = foam_file.items()
else:
location = args[0]
object_name = args[1]
class_name = kwargs.get('class_name', 'dictionary')
values = kwargs.get('values', {})
#
# initializing class and head_dict
super().__init__(values)
self.name = object_name
self.head_dict = OpenFoamDict(OpenFoamFile.HEAD_DICT.name,
OpenFoamFile.HEAD_DICT.items())
#
# setting head dict values
self.head_dict['class'] = class_name
self.head_dict['location'] = '"' + location + '"'
self.head_dict['object'] = object_name
[docs] def __str__(self):
r"""
Prints a formatted OpenFoam input file
"""
fmt_str = '{}\t{};\n\n'
#
str_rep = OpenFoamFile.FOAM_HEADER
str_rep += str(self.head_dict)
str_rep += OpenFoamFile.FOAM_SPACER
str_rep += '\n'
#
for key, val in self.items():
if isinstance(val, OpenFoamObject):
str_rep += str(val)
str_rep += '\n'
else:
val = str(val).replace(',', ' ')
str_rep += fmt_str.format(key, val)
#
str_rep += '\n'
str_rep += OpenFoamFile.FOAM_SPACER
#
return str_rep
@staticmethod
[docs] def _init_from_file(filename):
r"""
Reads an existing OpenFoam input file and returns an OpenFoamFile
instance. Comment lines are not retained.
"""
#
def build_dict(content, match, out_obj):
r"""Recursive function used to build OpenFoamDicts"""
ofdict = OpenFoamDict(match.group(1))
content = content[match.end():]
#
while not re.match(r'^}', content) and content:
content = add_param(content, ofdict)
#
content = re.sub(r'^}\n', '', content)
if isinstance(out_obj, list):
out_obj.append(ofdict)
else:
out_obj[ofdict.name] = ofdict
#
return content
def build_list(content, match, out_obj):
r"""Recursive function used to build OpenFoamLists"""
oflist = OpenFoamList(match.group(1))
content = content[match.end():]
#
while not re.match(r'^\);', content) and content:
content = add_param(content, oflist)
#
content = re.sub(r'^\);\n', '', content)
if isinstance(out_obj, list):
out_obj.append(oflist)
else:
out_obj[oflist.name] = oflist
#
return content
def add_param(content, out_obj):
r"""Recursive function used to add params to OpenFoamObjects"""
dict_pat = re.compile(r'.*?(\w+)\n\{\n')
list_pat = re.compile(r'.*?(\w+)\n\(\n')
dict_match = dict_pat.match(content)
list_match = list_pat.match(content)
if dict_match:
content = build_dict(content, dict_match, out_obj)
elif list_match:
content = build_list(content, list_match, out_obj)
else:
line = re.match(r'.*\n', content).group()
line = re.sub(r';', '', line)
line = line.strip()
try:
key, value = re.split(r'\s+', line, maxsplit=1)
except ValueError:
key, value = line, ''
#
# removing line from content
content = re.sub(r'^.*\n', '', content)
if isinstance(out_obj, list):
out_obj.append(line)
else:
out_obj[key] = value
#
return content
#
# reading file
with open(filename, 'r') as infile:
content = infile.read()
#
if not re.search('FoamFile', content):
msg = 'Invalid OpenFoam input file, no FoamFile dict'
raise ValueError(msg)
#
# removing comments and other characters
inline_comment = re.compile(r'(//.*)')
block_comment = re.compile(r'(/[*].*?[*]/)', flags=re.S)
content = inline_comment.sub('', content)
content = block_comment.sub('', content)
content = re.sub(r'\s*$', '\n', content, flags=re.M)
content = re.sub(r'^\s*', '', content, flags=re.M)
#
# parsing content of file
foam_file_params = OrderedDict()
while content:
content = add_param(content, foam_file_params)
#
# generating OpenFoamFile
head_dict = foam_file_params.pop('FoamFile', {})
try:
location = head_dict['location'].replace('"', '')
except KeyError:
location = os.path.split(os.path.dirname(filename))[1]
#
foam_file = OpenFoamFile(location,
head_dict['object'],
values=foam_file_params)
foam_file.name = os.path.basename(filename)
for key, value in head_dict.items():
foam_file.head_dict[key] = value
#
return foam_file
[docs] def write_foam_file(self, path='.', create_dirs=True, overwrite=False):
r"""
Writes out the foam file, adding proper location directory if
create_dirs is True
"""
#
# if create_dirs then appending location directory to path
location = self.head_dict['location'].replace('"', '')
if create_dirs:
path = os.path.join(path, location)
#
try:
os.makedirs(path)
except FileExistsError:
pass
fname = os.path.join(path, self.name)
#
# checking if file exists
if not overwrite and os.path.exists(fname):
msg = 'Error - there is already a file at '+fname+'.'
msg += ' Specify "overwrite=True" to replace it'
raise FileExistsError(msg)
#
# saving file
file_content = str(self)
with open(fname, 'w') as foam_file:
foam_file.write(file_content)
#
print(self.head_dict['object'] + ' file saved as: '+fname)