437 lines
14 KiB
Python
437 lines
14 KiB
Python
# ***************************************************************************
|
|
# * *
|
|
# * Copyright (c) 2016 - Markus Hovorka <m.hovorka@live.de> *
|
|
# * *
|
|
# * This program is free software; you can redistribute it and/or modify *
|
|
# * it under the terms of the GNU Lesser General Public License (LGPL) *
|
|
# * as published by the Free Software Foundation; either version 2 of *
|
|
# * the License, or (at your option) any later version. *
|
|
# * for detail see the LICENCE text file. *
|
|
# * *
|
|
# * This program is distributed in the hope that it will be useful, *
|
|
# * but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
# * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
# * GNU Library General Public License for more details. *
|
|
# * *
|
|
# * You should have received a copy of the GNU Library General Public *
|
|
# * License along with this program; if not, write to the Free Software *
|
|
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
# * USA *
|
|
# * *
|
|
# ***************************************************************************
|
|
|
|
|
|
import collections
|
|
|
|
|
|
SIMULATION = "Simulation"
|
|
CONSTANTS = "Constants"
|
|
BODY = "Body"
|
|
MATERIAL = "Material"
|
|
BODY_FORCE = "Body Force"
|
|
EQUATION = "Equation"
|
|
SOLVER = "Solver"
|
|
BOUNDARY_CONDITION = "Boundary Condition"
|
|
INITIAL_CONDITION = "Initial Condition"
|
|
COMPONENT = "Component"
|
|
|
|
|
|
_VALID_SECTIONS = (
|
|
SIMULATION,
|
|
CONSTANTS,
|
|
BODY,
|
|
MATERIAL,
|
|
BODY_FORCE,
|
|
EQUATION,
|
|
SOLVER,
|
|
BOUNDARY_CONDITION,
|
|
INITIAL_CONDITION,
|
|
COMPONENT,
|
|
)
|
|
|
|
|
|
_NUMBERED_SECTIONS = (
|
|
BODY,
|
|
MATERIAL,
|
|
BODY_FORCE,
|
|
EQUATION,
|
|
SOLVER,
|
|
BOUNDARY_CONDITION,
|
|
INITIAL_CONDITION,
|
|
COMPONENT,
|
|
)
|
|
|
|
|
|
_SECTION_DELIM = "End"
|
|
_WHITESPACE = " "
|
|
_INDENT = " " * 2
|
|
_NEWLINE = "\n"
|
|
|
|
|
|
_TYPE_REAL = "Real"
|
|
_TYPE_INTEGER = "Integer"
|
|
_TYPE_LOGICAL = "Logical"
|
|
_TYPE_STRING = "String"
|
|
_TYPE_FILE = "File"
|
|
|
|
|
|
WARN = "Warn"
|
|
IGNORE = "Ignore"
|
|
ABORT = "Abort"
|
|
SILENT = "Silent"
|
|
|
|
|
|
def createSection(name):
|
|
section = Section(name)
|
|
if not isValid(section):
|
|
raise ValueError("Invalid section name: %s" % name)
|
|
return section
|
|
|
|
|
|
def writeSections(sections, stream):
|
|
ids = _IdManager()
|
|
_Writer(ids, sections, stream).write()
|
|
|
|
|
|
def isNumbered(section):
|
|
return section.name in _NUMBERED_SECTIONS
|
|
|
|
|
|
def isValid(section):
|
|
return section.name in _VALID_SECTIONS
|
|
|
|
|
|
class Builder(object):
|
|
|
|
_ACTIVE_SOLVERS = "Active Solvers"
|
|
|
|
def __init__(self):
|
|
self._customSections = []
|
|
self._simulation = createSection(SIMULATION)
|
|
self._constants = createSection(CONSTANTS)
|
|
self._bodies = collections.OrderedDict()
|
|
self._boundaries = collections.OrderedDict()
|
|
|
|
def getBoundaryNames(self):
|
|
return self._boundaries.keys()
|
|
|
|
def getBodyNames(self):
|
|
return self._bodies.keys()
|
|
|
|
def simulation(self, key, attr):
|
|
self._simulation[key] = attr
|
|
|
|
def constant(self, key, attr):
|
|
self._constants[key] = attr
|
|
|
|
def initial(self, body, key, attr):
|
|
section = self._getFromBody(body, INITIAL_CONDITION)
|
|
section[key] = attr
|
|
|
|
def material(self, body, key, attr):
|
|
section = self._getFromBody(body, MATERIAL)
|
|
section[key] = attr
|
|
|
|
def equation(self, body, key, attr):
|
|
section = self._getFromBody(body, EQUATION)
|
|
section[key] = attr
|
|
|
|
def bodyForce(self, body, key, attr):
|
|
section = self._getFromBody(body, BODY_FORCE)
|
|
section[key] = attr
|
|
|
|
def addSolver(self, body, solverSection):
|
|
section = self._getFromBody(body, EQUATION)
|
|
if self._ACTIVE_SOLVERS not in section:
|
|
section[self._ACTIVE_SOLVERS] = []
|
|
section[self._ACTIVE_SOLVERS].append(solverSection)
|
|
|
|
def boundary(self, boundary, key, attr):
|
|
if boundary not in self._boundaries:
|
|
self._boundaries[boundary] = createSection(BOUNDARY_CONDITION)
|
|
self._boundaries[boundary][key] = attr
|
|
|
|
def addSection(self, section):
|
|
if section not in self._customSections:
|
|
self._customSections.append(section)
|
|
|
|
def _getFromBody(self, body, key):
|
|
if body not in self._bodies:
|
|
self._bodies[body] = createSection(BODY)
|
|
if key not in self._bodies[body]:
|
|
self._bodies[body][key] = createSection(key)
|
|
return self._bodies[body][key]
|
|
|
|
def __iter__(self):
|
|
allSections = list(self._customSections)
|
|
allSections.append(self._simulation)
|
|
allSections.append(self._constants)
|
|
for name, section in self._bodies.items():
|
|
section["Name"] = name
|
|
allSections.append(section)
|
|
if MATERIAL in section and section[MATERIAL] not in allSections:
|
|
allSections.append(section[MATERIAL])
|
|
if EQUATION in section and section[EQUATION] not in allSections:
|
|
eqSection = section[EQUATION]
|
|
allSections.append(eqSection)
|
|
if self._ACTIVE_SOLVERS in eqSection:
|
|
for solverSection in eqSection[self._ACTIVE_SOLVERS]:
|
|
if solverSection not in allSections:
|
|
allSections.append(solverSection)
|
|
if (BODY_FORCE in section and
|
|
section[BODY_FORCE] not in allSections):
|
|
allSections.append(section[BODY_FORCE])
|
|
if (INITIAL_CONDITION in section and
|
|
section[INITIAL_CONDITION] not in allSections):
|
|
allSections.append(section[INITIAL_CONDITION])
|
|
for name, section in self._boundaries.items():
|
|
section["Name"] = name
|
|
allSections.append(section)
|
|
return iter(allSections)
|
|
|
|
|
|
class Sif(object):
|
|
|
|
_CHECK_KEYWORDS = "Check Keywords"
|
|
_HEADER = "Header"
|
|
_MESHDB_ATTR = "Mesh DB"
|
|
_INCLUDE_ATTR = "Include Path"
|
|
_RESULT_ATTR = "Results Directory"
|
|
|
|
def __init__(self, sections=[], meshLocation="."):
|
|
self.sections = sections
|
|
self.meshPath = meshLocation
|
|
self.checkKeywords = WARN
|
|
self.incPath = ""
|
|
self.resPath = ""
|
|
|
|
def write(self, stream):
|
|
self._writeCheckKeywords(stream)
|
|
stream.write(_NEWLINE * 2)
|
|
self._writeHeader(stream)
|
|
stream.write(_NEWLINE * 2)
|
|
writeSections(self.sections, stream)
|
|
|
|
def _writeCheckKeywords(self, stream):
|
|
stream.write(self._CHECK_KEYWORDS)
|
|
stream.write(_WHITESPACE)
|
|
stream.write(self.checkKeywords)
|
|
|
|
def _writeHeader(self, stream):
|
|
stream.write(self._HEADER)
|
|
stream.write(_NEWLINE)
|
|
self._writeAttr(self._MESHDB_ATTR, self.meshPath, stream)
|
|
stream.write(_NEWLINE)
|
|
if self.incPath:
|
|
self._writeAttr(self._INCLUDE_ATTR, self.incPath, stream)
|
|
stream.write(_NEWLINE)
|
|
if self.resPath:
|
|
self._writeAttr(self._RESULT_ATTR, self.resPath, stream)
|
|
stream.write(_NEWLINE)
|
|
stream.write(_SECTION_DELIM)
|
|
|
|
def _writeAttr(self, name, value, stream):
|
|
stream.write(_INDENT)
|
|
stream.write(name)
|
|
stream.write(_WHITESPACE)
|
|
stream.write('"%s"' % value)
|
|
|
|
|
|
class Section(object):
|
|
|
|
def __init__(self, name):
|
|
self.name = name
|
|
self.priority = 0
|
|
self._attrs = dict()
|
|
|
|
def __setitem__(self, key, value):
|
|
self._attrs[key] = value
|
|
|
|
def __getitem__(self, key):
|
|
return self._attrs[key]
|
|
|
|
def __delitem__(self, key):
|
|
del self._attrs[key]
|
|
|
|
def __iter__(self):
|
|
return self._attrs.items()
|
|
|
|
def iterkeys(self):
|
|
return self._attrs.keys()
|
|
|
|
def __contains__(self, item):
|
|
return item in self._attrs
|
|
|
|
def __str__(self):
|
|
return str(self._attrs)
|
|
|
|
def __repr__(self):
|
|
return self.name
|
|
|
|
|
|
class FileAttr(str):
|
|
pass
|
|
|
|
|
|
class _Writer(object):
|
|
|
|
def __init__(self, idManager, sections, stream):
|
|
self._idMgr = idManager
|
|
self._sections = sections
|
|
self._stream = stream
|
|
|
|
def write(self):
|
|
sortedSections = sorted(
|
|
self._sections, key=lambda s: s.priority, reverse=True)
|
|
for s in sortedSections:
|
|
self._writeSection(s)
|
|
self._stream.write(_NEWLINE)
|
|
|
|
def _writeSection(self, s):
|
|
self._writeSectionHeader(s)
|
|
self._writeSectionBody(s)
|
|
self._writeSectionFooter(s)
|
|
self._stream.write(_NEWLINE)
|
|
|
|
def _writeSectionHeader(self, s):
|
|
self._stream.write(s.name)
|
|
self._stream.write(_WHITESPACE)
|
|
if isNumbered(s):
|
|
self._stream.write(str(self._idMgr.getId(s)))
|
|
|
|
def _writeSectionFooter(self, s):
|
|
self._stream.write(_NEWLINE)
|
|
self._stream.write(_SECTION_DELIM)
|
|
|
|
def _writeSectionBody(self, s):
|
|
for key in sorted(s.iterkeys()): # def iterkeys() from class sifio.Section is called
|
|
self._writeAttribute(key, s[key])
|
|
|
|
def _writeAttribute(self, key, data):
|
|
if isinstance(data, Section):
|
|
self._stream.write(_NEWLINE)
|
|
self._writeScalarAttr(key, data)
|
|
elif isinstance(data, FileAttr):
|
|
self._stream.write(_NEWLINE)
|
|
self._writeFileAttr(key, data)
|
|
elif self._isCollection(data):
|
|
if len(data) == 1:
|
|
scalarData = self._getOnlyElement(data)
|
|
self._stream.write(_NEWLINE)
|
|
self._writeScalarAttr(key, scalarData)
|
|
elif len(data) > 1:
|
|
self._stream.write(_NEWLINE)
|
|
self._writeArrAttr(key, data)
|
|
else:
|
|
self._stream.write(_NEWLINE)
|
|
self._writeScalarAttr(key, data)
|
|
|
|
def _getOnlyElement(self, collection):
|
|
it = iter(collection)
|
|
return it.next()
|
|
|
|
def _isCollection(self, data):
|
|
return (not isinstance(data, str) and
|
|
isinstance(data, collections.Iterable))
|
|
|
|
def _checkScalar(self, dataType):
|
|
if issubclass(dataType, int):
|
|
return self._genIntAttr
|
|
if issubclass(dataType, float):
|
|
return self._genFloatAttr
|
|
if issubclass(dataType, bool):
|
|
return self._genBoolAttr
|
|
if issubclass(dataType, str):
|
|
return self._genStrAttr
|
|
return None
|
|
|
|
def _writeScalarAttr(self, key, data):
|
|
attrType = self._getAttrTypeScalar(data)
|
|
if attrType is None:
|
|
raise ValueError("Unsupported data type: %s" % type(data))
|
|
self._stream.write(_INDENT)
|
|
self._stream.write(key)
|
|
self._stream.write(_WHITESPACE)
|
|
self._stream.write("=")
|
|
self._stream.write(_WHITESPACE)
|
|
self._stream.write(attrType)
|
|
self._stream.write(_WHITESPACE)
|
|
self._stream.write(self._preprocess(data, type(data)))
|
|
|
|
def _writeArrAttr(self, key, data):
|
|
attrType = self._getAttrTypeArr(data)
|
|
self._stream.write(_INDENT)
|
|
self._stream.write(key)
|
|
self._stream.write("(%d)" % len(data))
|
|
self._stream.write(_WHITESPACE)
|
|
self._stream.write("=")
|
|
self._stream.write(_WHITESPACE)
|
|
self._stream.write(attrType)
|
|
for val in data:
|
|
self._stream.write(_WHITESPACE)
|
|
self._stream.write(self._preprocess(val, type(val)))
|
|
|
|
def _writeFileAttr(self, key, data):
|
|
self._stream.write(_INDENT)
|
|
self._stream.write(key)
|
|
self._stream.write(_WHITESPACE)
|
|
self._stream.write("=")
|
|
self._stream.write(_WHITESPACE)
|
|
self._stream.write(_TYPE_FILE)
|
|
for val in data.split("/"):
|
|
if val:
|
|
self._stream.write(_WHITESPACE)
|
|
self._stream.write('"%s"' % val)
|
|
|
|
def _getSifDataType(self, dataType):
|
|
if issubclass(dataType, Section):
|
|
return _TYPE_INTEGER
|
|
if issubclass(dataType, bool):
|
|
return _TYPE_LOGICAL
|
|
if issubclass(dataType, int):
|
|
return _TYPE_INTEGER
|
|
if issubclass(dataType, float):
|
|
return _TYPE_REAL
|
|
if issubclass(dataType, str):
|
|
return _TYPE_STRING
|
|
raise ValueError("Unsupported data type: %s" % dataType)
|
|
|
|
def _preprocess(self, data, dataType):
|
|
if issubclass(dataType, Section):
|
|
return str(self._idMgr.getId(data))
|
|
if issubclass(dataType, str):
|
|
return '"%s"' % data
|
|
return str(data)
|
|
|
|
def _getAttrTypeScalar(self, data):
|
|
return self._getSifDataType(type(data))
|
|
|
|
def _getAttrTypeArr(self, data):
|
|
if not data:
|
|
raise ValueError("Collections must not be empty.")
|
|
it = iter(data)
|
|
dataType = type(next(it))
|
|
for entry in it:
|
|
if not isinstance(entry, dataType):
|
|
raise ValueError("Collection must be homogenueous")
|
|
return self._getSifDataType(dataType)
|
|
|
|
|
|
class _IdManager(object):
|
|
|
|
def __init__(self, firstId=1):
|
|
self._pool = dict()
|
|
self._ids = dict()
|
|
self.firstId = firstId
|
|
|
|
def setId(self, section):
|
|
if section.name not in self._pool:
|
|
self._pool[section.name] = self.firstId
|
|
self._ids[section] = self._pool[section.name]
|
|
self._pool[section.name] += 1
|
|
|
|
def getId(self, section):
|
|
if section not in self._ids:
|
|
self.setId(section)
|
|
return self._ids[section]
|