FEM: elmer, add it to the new solver framework and add equation object for elmer

This commit is contained in:
Markus Hovorka
2017-12-01 19:43:45 +01:00
committed by wmayer
parent 42b3c3353b
commit 412df6f4c1
31 changed files with 3097 additions and 0 deletions

View File

View File

@@ -0,0 +1,68 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - 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 *
# * *
# ***************************************************************************
__title__ = "Elasticity"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import FemUtils
from ... import equationbase
from . import linear
def create(doc, name="Elasticity"):
return FemUtils.createObject(
doc, name, Proxy, ViewProxy)
class Proxy(linear.Proxy, equationbase.ElasticityProxy):
Type = "Fem::FemEquationElmerElasticity"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
obj.addProperty(
"App::PropertyBool", "DoFrequencyAnalysis",
"Elasticity", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyInteger", "EigenmodesCount",
"Elasticity", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyBool", "CalculateStrains",
"Elasticity", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyBool", "CalculateStresses",
"Elasticity", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyBool", "CalculatePrincipal",
"Elasticity", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyBool", "CalculatePangle",
"Elasticity", "Select type of solver for linear system")
obj.EigenmodesCount = 5
obj.Priority = 10
class ViewProxy(linear.ViewProxy, equationbase.ElasticityViewProxy):
pass

View File

@@ -0,0 +1,111 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - 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 *
# * *
# ***************************************************************************
__title__ = "Equation"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import FreeCAD as App
from ... import equationbase
import FemUtils
if App.GuiUp:
import FreeCADGui as Gui
import PyGui.FemSelectionWidgets as FemSelectionWidgets
class Proxy(equationbase.BaseProxy):
def __init__(self, obj):
super(Proxy, self).__init__(obj)
obj.addProperty(
"App::PropertyInteger", "Priority",
"Base", "Select type of solver for linear system")
class ViewProxy(equationbase.BaseViewProxy):
def setEdit(self, vobj, mode=0):
task = _TaskPanel(vobj.Object)
Gui.Control.showDialog(task)
def unsetEdit(self, vobj, mode=0):
Gui.Control.closeDialog()
def doubleClicked(self, vobj):
if Gui.Control.activeDialog():
Gui.Control.closeDialog()
Gui.ActiveDocument.setEdit(vobj.Object.Name)
return True
def getTaskWidget(self, vobj):
return None
class _TaskPanel(object):
def __init__(self, obj):
self._obj = obj
self._refWidget = FemSelectionWidgets.SolidSelector()
self._refWidget.setReferences(obj.References)
propWidget = obj.ViewObject.Proxy.getTaskWidget(
obj.ViewObject)
if propWidget is None:
self.form = self._refWidget
else:
self.form = [self.refWidget, propWidget]
analysis = FemUtils.findAnalysisOfMember(obj)
self._mesh = FemUtils.getSingleMember(analysis, "Fem::FemMeshObject")
self._part = self._mesh.Part if self._mesh is not None else None
self._partVisible = None
self._meshVisible = None
def open(self):
if self._mesh is not None and self._part is not None:
self._meshVisible = self._mesh.ViewObject.isVisible()
self._partVisible = self._part.ViewObject.isVisible()
self._mesh.ViewObject.hide()
self._part.ViewObject.show()
def reject(self):
self._restoreVisibility()
return True
def accept(self):
if self._obj.References != self._refWidget.references():
self._obj.References = self._refWidget.references()
self._obj.Document.recompute()
self._restoreVisibility()
return True
def _restoreVisibility(self):
if self._mesh is not None and self._part is not None:
if self._meshVisible:
self._mesh.ViewObject.show()
else:
self._mesh.ViewObject.hide()
if self._partVisible:
self._part.ViewObject.show()
else:
self._part.ViewObject.hide()

View File

@@ -0,0 +1,49 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - 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 *
# * *
# ***************************************************************************
__title__ = "Flow"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import FemUtils
from . import nonlinear
from ... import equationbase
def create(doc, name="Flow"):
return FemUtils.createObject(
doc, name, Proxy, ViewProxy)
class Proxy(nonlinear.Proxy, equationbase.FlowProxy):
Type = "Fem::FemEquationElmerFlow"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
obj.Priority = 10
class ViewProxy(nonlinear.ViewProxy, equationbase.FlowViewProxy):
pass

View File

@@ -0,0 +1,49 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - 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 *
# * *
# ***************************************************************************
__title__ = "Heat"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import FemUtils
from . import nonlinear
from ... import equationbase
def create(doc, name="Heat"):
return FemUtils.createObject(
doc, name, Proxy, ViewProxy)
class Proxy(nonlinear.Proxy, equationbase.HeatProxy):
Type = "Fem::FemEquationElmerHeat"
def __init__(self, obj):
super(Proxy, self).__init__(obj)
obj.Priority = 20
class ViewProxy(nonlinear.ViewProxy, equationbase.HeatViewProxy):
pass

View File

@@ -0,0 +1,103 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - 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 *
# * *
# ***************************************************************************
__title__ = "_Linear"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
from . import equation
LINEAR_SOLVER = ["Direct", "Iterative"]
LINEAR_DIRECT = ["Banded", "umfpack"]
LINEAR_ITERATIVE = [
"CG",
"CGS",
"BiCGStab",
"BiCGStabl",
"TFQMR",
"GMRES",
"GCR",
]
LINEAR_PRECONDITIONING = [
"None",
"Diagonal",
"ILU0",
"ILU1",
"ILU2",
"ILU3",
"ILU4",
]
class Proxy(equation.Proxy):
def __init__(self, obj):
super(Proxy, self).__init__(obj)
obj.addProperty(
"App::PropertyEnumeration", "LinearSolverType",
"Linear System", "Select type of solver for linear system")
obj.LinearSolverType = LINEAR_SOLVER
obj.LinearSolverType = "Iterative"
obj.addProperty(
"App::PropertyEnumeration", "LinearDirectMethod",
"Linear System", "Select type of solver for linear system")
obj.LinearDirectMethod = LINEAR_DIRECT
obj.addProperty(
"App::PropertyEnumeration", "LinearIterativeMethod",
"Linear System", "Select type of solver for linear system")
obj.LinearIterativeMethod = LINEAR_ITERATIVE
obj.LinearIterativeMethod = "BiCGStab"
obj.addProperty(
"App::PropertyInteger", "BiCGstablDegree",
"Linear System", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyEnumeration", "LinearPreconditioning",
"Linear System", "Select type of solver for linear system")
obj.LinearPreconditioning = LINEAR_PRECONDITIONING
obj.LinearPreconditioning = "ILU0"
obj.addProperty(
"App::PropertyFloat", "LinearTolerance",
"Linear System", "Select type of solver for linear system")
obj.LinearTolerance = 1e-8
obj.addProperty(
"App::PropertyInteger", "LinearIterations",
"Linear System", "Select type of solver for linear system")
obj.LinearIterations = 500
obj.addProperty(
"App::PropertyFloat", "SteadyStateTolerance",
"Steady State", "Select type of solver for linear system")
obj.SteadyStateTolerance = 1e-5
obj.addProperty(
"App::PropertyBool", "Stabilize",
"Base", "Select type of solver for linear system")
obj.Stabilize = True
obj.addProperty(
"App::PropertyBool", "Bubbles",
"Base", "Select type of solver for linear system")
obj.Bubbles = False
class ViewProxy(equation.ViewProxy):
pass

View File

@@ -0,0 +1,59 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - 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 *
# * *
# ***************************************************************************
__title__ = "_NonLinear"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
from . import linear
class Proxy(linear.Proxy):
def __init__(self, obj):
super(Proxy, self).__init__(obj)
obj.addProperty(
"App::PropertyFloat", "NonlinearTolerance",
"Nonlinear System", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyInteger", "NonlinearIterations",
"Nonlinear System", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyFloat", "RelaxationFactor",
"Nonlinear System", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyInteger", "NonlinearNewtonAfterIterations",
"Nonlinear System", "Select type of solver for linear system")
obj.addProperty(
"App::PropertyFloat", "NonlinearNewtonAfterTolerance",
"Nonlinear System", "Select type of solver for linear system")
obj.NonlinearTolerance = 1e-8
obj.NonlinearIterations = 500
obj.RelaxationFactor = 1
obj.NonlinearNewtonAfterIterations = 3
obj.NonlinearNewtonAfterTolerance = 1e-3
class ViewProxy(linear.ViewProxy):
pass

View File

@@ -0,0 +1,429 @@
# ***************************************************************************
# * *
# * 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 = set()
self._simulation = createSection(SIMULATION)
self._constants = createSection(CONSTANTS)
self._bodies = {}
self._boundaries = {}
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):
self._customSections.add(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 = set(self._customSections)
allSections.add(self._simulation)
allSections.add(self._constants)
for name, section in self._bodies.iteritems():
section["Name"] = name
allSections.add(section)
if MATERIAL in section:
allSections.add(section[MATERIAL])
if EQUATION in section:
eqSection = section[EQUATION]
allSections.add(eqSection)
if self._ACTIVE_SOLVERS in eqSection:
for solverSection in eqSection[self._ACTIVE_SOLVERS]:
allSections.add(solverSection)
if BODY_FORCE in section:
allSections.add(section[BODY_FORCE])
if INITIAL_CONDITION in section:
allSections.add(section[INITIAL_CONDITION])
for name, section in self._boundaries.iteritems():
section["Name"] = name
allSections.add(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.iteritems()
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, data in s:
self._writeAttribute(key, data)
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, basestring)
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, basestring):
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, basestring):
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, basestring):
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(it.next())
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]

View File

@@ -0,0 +1,93 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - 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 *
# * *
# ***************************************************************************
__title__ = "Elmer"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import FemUtils
from .. import run
from .. import solverbase
from . import tasks
from .equations import heat
from .equations import elasticity
from .equations import flow
def create(doc, name="ElmerSolver"):
return FemUtils.createObject(
doc, name, Proxy, ViewProxy)
class Proxy(solverbase.Proxy):
"""Proxy for FemSolverElmers Document Object."""
Type = "Fem::FemSolverObjectElmer"
_EQUATIONS = {
"Heat": heat,
"Elasticity": elasticity,
"Flow": flow,
}
def __init__(self, obj):
super(Proxy, self).__init__(obj)
obj.addProperty(
"App::PropertyInteger", "SteadyStateMaxIterations",
"Steady State", "")
obj.addProperty(
"App::PropertyInteger", "SteadyStateMinIterations",
"Steady State", "")
obj.addProperty(
"App::PropertyLink", "ElmerResult",
"Base", "", 4 | 8)
obj.addProperty(
"App::PropertyLink", "ElmerOutput",
"Base", "", 4 | 8)
obj.SteadyStateMaxIterations = 1
obj.SteadyStateMinIterations = 0
def createMachine(self, obj, directory):
return run.Machine(
solver=obj, directory=directory,
check=tasks.Check(),
prepare=tasks.Prepare(),
solve=tasks.Solve(),
results=tasks.Results())
def createEquation(self, doc, eqId):
return self._EQUATIONS[eqId].create(doc)
def isSupported(self, eqId):
return eqId in self._EQUATIONS
class ViewProxy(solverbase.ViewProxy):
"""Proxy for FemSolverElmers View Provider."""
def getIcon(self):
return ":/icons/fem-elmer.png"

View File

@@ -0,0 +1,148 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - 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 *
# * *
# ***************************************************************************
__title__ = "FemElmerTasks"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import subprocess
import os.path
import FemUtils
from .. import run
from .. import settings
from . import writer
class Check(run.Check):
def run(self):
self.pushStatus("Checking analysis...\n")
if (self.checkMesh()):
self.checkMeshType()
self.checkMaterial()
self.checkEquations()
def checkMeshType(self):
mesh = FemUtils.getSingleMember(self.analysis, "Fem::FemMeshObject")
if not FemUtils.isOfType(mesh, "FemMeshGmsh"):
self.report.error(
"Unsupported type of mesh. "
"Mesh must be created with gmsh.")
self.fail()
return False
return True
def checkEquations(self):
equations = self.solver.Group
if not equations:
self.report.error(
"Solver has no equations. "
"Add at least one equation.")
self.fail()
class Prepare(run.Prepare):
def run(self):
self.pushStatus("Preparing input files...\n")
w = writer.Writer(self.solver, self.directory)
try:
w.write()
self.checkHandled(w)
except writer.WriteError as e:
self.report.error(str(e))
self.fail()
except IOError as e:
self.report.error("Can't access working directory.")
self.fail()
def checkHandled(self, w):
handled = w.getHandledConstraints()
allConstraints = FemUtils.getMember(self.analysis, "Fem::Constraint")
for obj in set(allConstraints) - handled:
self.report.warning("Ignored constraint %s." % obj.Label)
class Solve(run.Solve):
def run(self):
self.pushStatus("Executing solver...\n")
binary = settings.getBinary("ElmerSolver")
if binary is not None:
self._process = subprocess.Popen(
[binary], cwd=self.directory,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
self.signalAbort.add(self._process.terminate)
output = self._observeSolver(self._process)
self._process.communicate()
self.signalAbort.remove(self._process.terminate)
if not self.aborted:
self._updateOutput(output)
else:
self.report.error("ElmerSolver executable not found.")
self.fail()
def _observeSolver(self, process):
output = ""
line = process.stdout.readline()
self.pushStatus(line)
output += line
line = process.stdout.readline()
while line:
line = "\n%s" % line.rstrip()
self.pushStatus(line)
output += line
line = process.stdout.readline()
return output
def _updateOutput(self, output):
if self.solver.ElmerOutput is None:
self._createOutput()
self.solver.ElmerOutput.Text = output
def _createOutput(self):
self.solver.ElmerOutput = self.analysis.Document.addObject(
"App::TextDocument", self.solver.Name + "Output")
self.solver.ElmerOutput.Label = self.solver.Label + "Output"
self.solver.ElmerOutput.ReadOnly = True
self.analysis.Member += [self.solver.ElmerOutput]
class Results(run.Results):
def run(self):
if self.solver.ElmerResult is None:
self._createResults()
postPath = os.path.join(self.directory, "case0001.vtu")
self.solver.ElmerResult.read(postPath)
self.solver.ElmerResult.getLastPostObject().touch()
self.solver.Document.recompute()
def _createResults(self):
self.solver.ElmerResult = self.analysis.Document.addObject(
"Fem::FemPostPipeline", self.solver.Name + "Result")
self.solver.ElmerResult.Label = self.solver.Label + "Result"
self.analysis.Member += [self.solver.ElmerResult]

View File

@@ -0,0 +1,682 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - 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 *
# * *
# ***************************************************************************
__title__ = "FemWriterElmer"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import os
import os.path
import subprocess
import tempfile
from FreeCAD import Units
import Fem
import FemUtils
import FemGmshTools
from .. import settings
from . import sifio
_STARTINFO_NAME = "ELMERSOLVER_STARTINFO"
_SIF_NAME = "case.sif"
_ELMERGRID_IFORMAT = "8"
_ELMERGRID_OFORMAT = "2"
_SOLID_PREFIX = "Solid"
UNITS = {
"L": "mm",
"M": "kg",
"T": "s",
"I": "A",
"O": "K",
"N": "mol",
"J": "cd",
}
CONSTS_DEF = {
"Gravity": "9.82 m/s^2",
"StefanBoltzmann": "5.67e-8 W/(m^2*K^4)",
"PermittivityOfVacuum": "8.8542e-12 s^4*A^2/(m*kg)",
"BoltzmannConstant": "1.3807e-23 J/K",
}
def getFromUi(value, unit, outputDim):
quantity = Units.Quantity(str(value) + str(unit))
return convert(quantity, outputDim)
def convert(quantityStr, unit):
quantity = Units.Quantity(quantityStr)
for key, setting in UNITS.iteritems():
unit = unit.replace(key, setting)
return float(quantity.getValueAs(unit))
def _getAllSubObjects(obj):
s = ["Solid" + str(i + 1) for i in range(len(obj.Shape.Solids))]
s.extend(("Face" + str(i + 1) for i in range(len(obj.Shape.Faces))))
s.extend(("Edge" + str(i + 1) for i in range(len(obj.Shape.Edges))))
s.extend(("Vertex" + str(i + 1) for i in range(len(obj.Shape.Vertexes))))
return s
def getConstant(name, dimension):
return convert(CONSTS_DEF[name], dimension)
class Writer(object):
def __init__(self, solver, directory):
self.analysis = FemUtils.findAnalysisOfMember(solver)
self.solver = solver
self.directory = directory
self._usedVarNames = set()
self._builder = sifio.Builder()
self._handledObjects = set()
def getHandledConstraints(self):
return self._handledObjects
def write(self):
self._handleSimulation()
self._handleHeat()
self._handleElasticity()
self._handleFlow()
self._addOutputSolver()
self._writeSif()
self._writeMesh()
self._writeStartinfo()
def _writeMesh(self):
mesh = FemUtils.getSingleMember(self.analysis, "Fem::FemMeshObject")
unvPath = os.path.join(self.directory, "mesh.unv")
groups = []
groups.extend(self._builder.getBodyNames())
groups.extend(self._builder.getBoundaryNames())
self._exportToUnv(groups, mesh, unvPath)
binary = settings.getBinary("ElmerGrid")
if binary is None:
raise WriteError("Couldn't find ElmerGrid binary.")
args = [binary,
_ELMERGRID_IFORMAT,
_ELMERGRID_OFORMAT,
unvPath,
"-out", self.directory]
subprocess.call(args)
def _writeStartinfo(self):
path = os.path.join(self.directory, _STARTINFO_NAME)
with open(path, 'w') as f:
f.write(_SIF_NAME)
def _exportToUnv(self, groups, mesh, meshPath):
unvGmshFd, unvGmshPath = tempfile.mkstemp(suffix=".unv")
brepFd, brepPath = tempfile.mkstemp(suffix=".brep")
geoFd, geoPath = tempfile.mkstemp(suffix=".geo")
os.close(brepFd)
os.close(geoFd)
os.close(unvGmshFd)
tools = FemGmshTools.FemGmshTools(mesh)
tools.group_elements = {g: [g] for g in groups}
tools.ele_length_map = {}
tools.temp_file_geometry = brepPath
tools.temp_file_geo = geoPath
tools.temp_file_mesh = unvGmshPath
tools.get_dimension()
tools.get_gmsh_command()
tools.get_region_data()
tools.get_boundary_layer_data()
tools.write_part_file()
tools.write_geo()
tools.run_gmsh_with_geo()
ioMesh = Fem.FemMesh()
ioMesh.read(unvGmshPath)
ioMesh.write(meshPath)
os.remove(brepPath)
os.remove(geoPath)
os.remove(unvGmshPath)
def _handleSimulation(self):
self._simulation("Coordinate System", "Cartesian 3D")
self._simulation("Coordinate Mapping", (1, 2, 3))
self._simulation("Simulation Type", "Steady state")
self._simulation("Steady State Max Iterations", 1)
self._simulation("Output Intervals", 1)
self._simulation("Timestepping Method", "BDF")
self._simulation("BDF Order", 1)
self._simulation("Use Mesh Names", True)
self._simulation(
"Steady State Max Iterations",
self.solver.SteadyStateMaxIterations)
self._simulation(
"Steady State Min Iterations",
self.solver.SteadyStateMinIterations)
def _handleHeat(self):
activeIn = []
for equation in self.solver.Group:
if FemUtils.isOfType(equation, "Fem::FemEquationElmerHeat"):
if equation.References:
activeIn = equation.References[0][1]
else:
activeIn = self._getAllBodies()
solverSection = self._getHeatSolver(equation)
for body in activeIn:
self._addSolver(body, solverSection)
if activeIn:
self._handleHeatConstants()
self._handleHeatBndConditions()
self._handleHeatInitial(activeIn)
self._handleHeatBodyForces(activeIn)
self._handleHeatMaterial(activeIn)
def _getHeatSolver(self, equation):
s = self._createNonlinearSolver(equation)
s["Equation"] = equation.Name
s["Procedure"] = sifio.FileAttr("HeatSolve/HeatSolver")
s["Variable"] = self._getUniqueVarName("Temperature")
s["Exec Solver"] = "Always"
s["Stabilize"] = equation.Stabilize
s["Bubbles"] = equation.Bubbles
s["Optimize Bandwidth"] = True
return s
def _handleHeatConstants(self):
self._constant(
"Stefan Boltzmann",
getConstant("StefanBoltzmann", "M/(O^4*T^3)"))
def _handleHeatBndConditions(self):
for obj in self._getMember("Fem::ConstraintTemperature"):
if obj.References:
for name in obj.References[0][1]:
if obj.ConstraintType == "Temperature":
temp = getFromUi(obj.Temperature, "K", "O")
self._boundary(name, "Temperature", temp)
elif obj.ConstraintType == "CFlux":
flux = getFromUi(obj.CFlux, "kg*mm^2*s^-3", "M*L^2*T^-3")
self._boundary(name, "Temperature Load", flux)
self._handled(obj)
for obj in self._getMember("Fem::ConstraintHeatflux"):
if obj.References:
for name in obj.References[0][1]:
if obj.ConstraintType == "Convection":
film = getFromUi(obj.FilmCoef, "W/(m^2*K)", "M/(T^3*O)")
temp = getFromUi(obj.AmbientTemp, "K", "O")
self._boundary(name, "Heat Transfer Coefficient", film)
self._boundary(name, "External Temperature", temp)
elif obj.ConstraintType == "DFlux":
flux = getFromUi(obj.DFlux, "W/m^2", "M*T^-3")
self._boundary(name, "Heat Flux BC", True)
self._boundary(name, "Heat Flux", flux)
self._handled(obj)
def _handleHeatInitial(self, bodies):
obj = self._getSingleMember("Fem::ConstraintInitialTemperature")
if obj is not None:
for name in bodies:
temp = getFromUi(obj.initialTemperature, "K", "O")
self._initial(name, "Temperature", temp)
self._handled(obj)
def _handleHeatBodyForces(self, bodies):
obj = self._getSingleMember("Fem::FemConstraintBodyHeatSource")
if obj is not None:
for name in bodies:
heatSource = getFromUi(obj.HeatFlux, "W/kg", "L^2*T^-3")
self._bodyForce(name, "Heat Source", heatSource)
self._handled(obj)
def _handleHeatMaterial(self, bodies):
tempObj = self._getSingleMember("Fem::ConstraintInitialTemperature")
if tempObj is not None:
refTemp = getFromUi(tempObj.initialTemperature, "K", "O")
for name in bodies:
self._material(name, "Reference Temperature", refTemp)
for obj in self._getMember("App::MaterialObject"):
m = obj.Material
refs = (
obj.References[0][1]
if obj.References
else self._getAllBodies())
for name in (n for n in refs if n in bodies):
self._material(
name, "Density",
self._getDensity(m))
self._material(
name, "Heat Conductivity",
convert(m["ThermalConductivity"], "M*L/(T^3*O)"))
self._material(
name, "Heat Capacity",
convert(m["SpecificHeat"], "L^2/(T^2*O)"))
def _handleElasticity(self):
activeIn = []
for equation in self.solver.Group:
if FemUtils.isOfType(equation, "Fem::FemEquationElmerElasticity"):
if equation.References:
activeIn = equation.References[0][1]
else:
activeIn = self._getAllBodies()
solverSection = self._getElasticitySolver(equation)
for body in activeIn:
self._addSolver(body, solverSection)
if activeIn:
self._handleElasticityConstants()
self._handleElasticityBndConditions()
self._handleElasticityInitial(activeIn)
self._handleElasticityBodyForces(activeIn)
self._handleElasticityMaterial(activeIn)
def _getElasticitySolver(self, equation):
s = self._createLinearSolver(equation)
s["Equation"] = equation.Name
s["Procedure"] = sifio.FileAttr("StressSolve/StressSolver")
s["Variable"] = self._getUniqueVarName("Displacement")
s["Variable DOFs"] = 3
s["Eigen Analysis"] = equation.DoFrequencyAnalysis
s["Eigen System Values"] = equation.EigenmodesCount
s["Calculate Strains"] = equation.CalculateStrains
s["Calculate Stresses"] = equation.CalculateStresses
s["Calculate Principal"] = equation.CalculatePrincipal
s["Calculate Pangle"] = equation.CalculatePangle
s["Displace mesh"] = False
s["Exec Solver"] = "Always"
s["Stabilize"] = equation.Stabilize
s["Bubbles"] = equation.Bubbles
s["Optimize Bandwidth"] = True
return s
def _handleElasticityConstants(self):
pass
def _handleElasticityBndConditions(self):
for obj in self._getMember("Fem::ConstraintPressure"):
if obj.References:
for name in obj.References[0][1]:
pressure = getFromUi(obj.Pressure, "MPa", "M/(L*T^2)")
if not obj.Reversed:
pressure *= -1
self._boundary(name, "Normal Force", pressure)
self._handled(obj)
for obj in self._getMember("Fem::ConstraintFixed"):
if obj.References:
for name in obj.References[0][1]:
self._boundary(name, "Displacement 1", 0.0)
self._boundary(name, "Displacement 2", 0.0)
self._boundary(name, "Displacement 3", 0.0)
self._handled(obj)
for obj in self._getMember("Fem::ConstraintForce"):
if obj.References:
for name in obj.References[0][1]:
force = getFromUi(obj.Force, "N", "M*L*T^-2")
self._boundary(name, "Force 1", obj.DirectionVector.x * force)
self._boundary(name, "Force 2", obj.DirectionVector.y * force)
self._boundary(name, "Force 3", obj.DirectionVector.z * force)
self._boundary(name, "Force 1 Normalize by Area", True)
self._boundary(name, "Force 2 Normalize by Area", True)
self._boundary(name, "Force 3 Normalize by Area", True)
self._handled(obj)
for obj in self._getMember("Fem::ConstraintDisplacement"):
if obj.References:
for name in obj.References[0][1]:
if not obj.xFree:
self._boundary(
name, "Displacement 1", obj.xDisplacement * 0.001)
elif obj.xFix:
self._boundary(name, "Displacement 1", 0.0)
if not obj.yFree:
self._boundary(
name, "Displacement 2", obj.yDisplacement * 0.001)
elif obj.yFix:
self._boundary(name, "Displacement 2", 0.0)
if not obj.zFree:
self._boundary(
name, "Displacement 3", obj.zDisplacement * 0.001)
elif obj.zFix:
self._boundary(name, "Displacement 3", 0.0)
self._handled(obj)
def _handleElasticityInitial(self, bodies):
pass
def _handleElasticityBodyForces(self, bodies):
obj = self._getSingleMember("FemConstraintSelfWeight")
if obj is not None:
for name in bodies:
gravity = getConstant("Gravity", "L/T^2")
m = self._getBodyMaterial(name).Material
densityQuantity = Units.Quantity(m["Density"])
dimension = "M/L^3"
if name.startswith("Edge"):
density.Unit = Units.Unit(-2, 1)
dimension = "M/L^2"
density = convert(densityQuantity, dimension)
force1 = gravity * obj.Gravity_x * density
force2 = gravity * obj.Gravity_y * density
force3 = gravity * obj.Gravity_z * density
self._bodyForce(name, "Stress Bodyforce 1", force1)
self._bodyForce(name, "Stress Bodyforce 2", force2)
self._bodyForce(name, "Stress Bodyforce 3", force3)
self._handled(obj)
def _getBodyMaterial(self, name):
for obj in self._getMember("App::MaterialObject"):
if not obj.References or name in obj.References[0][1]:
return obj
return None
def _handleElasticityMaterial(self, bodies):
tempObj = self._getSingleMember("Fem::ConstraintInitialTemperature")
if tempObj is not None:
refTemp = getFromUi(tempObj.initialTemperature, "K", "O")
for name in bodies:
self._material(name, "Reference Temperature", refTemp)
for obj in self._getMember("App::MaterialObject"):
m = obj.Material
refs = (
obj.References[0][1]
if obj.References
else self._getAllBodies())
for name in (n for n in refs if n in bodies):
self._material(
name, "Density",
self._getDensity(m))
self._material(
name, "Youngs Modulus",
self._getYoungsModulus(m))
self._material(
name, "Poisson ratio",
float(m["PoissonRatio"]))
self._material(
name, "Heat expansion Coefficient",
convert(m["ThermalExpansionCoefficient"], "O^-1"))
def _getDensity(self, m):
density = convert(m["Density"], "M/L^3")
if self._getMeshDimension() == 2:
density *= 1e3
return density
def _getYoungsModulus(self, m):
youngsModulus = convert(m["YoungsModulus"], "M/(L*T^2)")
if self._getMeshDimension() == 2:
youngsModulus *= 1e3
return youngsModulus
def _handleFlow(self):
activeIn = []
for equation in self.solver.Group:
if FemUtils.isOfType(equation, "Fem::FemEquationElmerFlow"):
if equation.References:
activeIn = equation.References[0][1]
else:
activeIn = self._getAllBodies()
solverSection = self._getFlowSolver(equation)
for body in activeIn:
self._addSolver(body, solverSection)
if activeIn:
self._handleFlowConstants()
self._handleFlowBndConditions()
self._handleFlowInitialVelocity(activeIn)
#self._handleFlowInitial(activeIn)
#self._handleFlowBodyForces(activeIn)
self._handleFlowMaterial(activeIn)
self._handleFlowEquation(activeIn)
def _getFlowSolver(self, equation):
s = self._createNonlinearSolver(equation)
s["Equation"] = "Navier-Stokes"
#s["Equation"] = equation.Name
s["Procedure"] = sifio.FileAttr("FlowSolve/FlowSolver")
s["Exec Solver"] = "Always"
s["Stabilize"] = equation.Stabilize
s["Bubbles"] = equation.Bubbles
s["Optimize Bandwidth"] = True
return s
def _handleFlowConstants(self):
gravity = getConstant("Gravity", "L/T^2")
self._constant("Gravity", (0.0, -1.0, 0.0, gravity))
def _handleFlowMaterial(self, bodies):
tempObj = self._getSingleMember("Fem::ConstraintInitialTemperature")
if tempObj is not None:
refTemp = getFromUi(tempObj.initialTemperature, "K", "O")
for name in bodies:
self._material(name, "Reference Temperature", refTemp)
for obj in self._getMember("App::MaterialObject"):
m = obj.Material
refs = (
obj.References[0][1]
if obj.References
else self._getAllBodies())
for name in (n for n in refs if n in bodies):
if "Density" in m:
self._material(
name, "Density",
self._getDensity(m))
if "ThermalConductivity" in m:
self._material(
name, "Heat Conductivity",
convert(m["ThermalConductivity"], "M*L/(T^3*O)"))
if "KinematicViscosity" in m:
density = self._getDensity(m)
kViscosity = convert(m["KinematicViscosity"], "L^2/T")
self._material(
name, "Viscosity", kViscosity * density)
if "ThermalExpansionCoefficient" in m:
value = convert(m["ThermalExpansionCoefficient"], "O^-1")
if value > 0:
self._material(
name, "Heat expansion Coefficient", value)
if "ReferencePressure" in m:
pressure = convert(m["ReferencePressure"], "M/(L*T^2)")
self._material(name, "Reference Pressure", pressure)
if "SpecificHeatRatio" in m:
self._material(
name, "Specific Heat Ratio",
float(m["SpecificHeatRatio"]))
if "CompressibilityModel" in m:
self._material(
name, "Compressibility Model",
m["CompressibilityModel"])
def _handleFlowInitialVelocity(self, bodies):
obj = self._getSingleMember("Fem::ConstraintInitialFlowVelocity")
if obj is not None:
for name in bodies:
if obj.VelocityXEnabled:
velocity = getFromUi(obj.VelocityX, "m/s", "L/T")
self._initial(name, "Velocity 1", velocity)
if obj.VelocityYEnabled:
velocity = getFromUi(obj.VelocityY, "m/s", "L/T")
self._initial(name, "Velocity 2", velocity)
if obj.VelocityZEnabled:
velocity = getFromUi(obj.VelocityZ, "m/s", "L/T")
self._initial(name, "Velocity 3", velocity)
self._handled(obj)
def _handleFlowBndConditions(self):
for obj in self._getMember("Fem::ConstraintFlowVelocity"):
if obj.References:
for name in obj.References[0][1]:
if obj.VelocityXEnabled:
velocity = getFromUi(obj.VelocityX, "m/s", "L/T")
self._boundary(name, "Velocity 1", velocity)
if obj.VelocityYEnabled:
velocity = getFromUi(obj.VelocityY, "m/s", "L/T")
self._boundary(name, "Velocity 2", velocity)
if obj.VelocityZEnabled:
velocity = getFromUi(obj.VelocityZ, "m/s", "L/T")
self._boundary(name, "Velocity 3", velocity)
if obj.NormalToBoundary:
self._boundary(name, "Normal-Tangential Velocity", True)
self._handled(obj)
def _handleFlowEquation(self, bodies):
for b in bodies:
self._equation(b, "Convection", "Computed")
def _createLinearSolver(self, equation):
s = sifio.createSection(sifio.SOLVER)
s.priority = equation.Priority
s["Linear System Solver"] = equation.LinearSolverType
if equation.LinearSolverType == "Direct":
s["Linear System Direct Method"] = \
equation.LinearDirectMethod
elif equation.LinearSolverType == "Iterative":
s["Linear System Iterative Method"] = \
equation.LinearIterativeMethod
if equation.LinearIterativeMethod == "BiCGStabl":
s["BiCGstabl polynomial degree"] = \
equation.BiCGstablDegree
s["Linear System Max Iterations"] = \
equation.LinearIterations
s["Linear System Convergence Tolerance"] = \
equation.LinearTolerance
s["Linear System Preconditioning"] = \
equation.LinearPreconditioning
s["Steady State Convergence Tolerance"] = \
equation.SteadyStateTolerance
s["Linear System Abort Not Converged"] = False
s["Linear System Residual Output"] = 1
s["Linear System Precondition Recompute"] = 1
return s
def _createNonlinearSolver(self, equation):
s = self._createLinearSolver(equation)
s["Nonlinear System Max Iterations"] = \
equation.NonlinearIterations
s["Nonlinear System Convergence Tolerance"] = \
equation.NonlinearTolerance
s["Nonlinear System Relaxation Factor"] = \
equation.RelaxationFactor
s["Nonlinear System Newton After Iterations"] = \
equation.NonlinearNewtonAfterIterations
s["Nonlinear System Newton After Tolerance"] = \
equation.NonlinearNewtonAfterTolerance
return s
def _getUniqueVarName(self, varName):
postfix = 1
if varName in self._usedVarNames:
varName += "%2d" % postfix
while varName in self._usedVarNames:
postfix += 1
varName = varName[:-2] + "%2d" % postfix
self._usedVarNames.add(varName)
return varName
def _getAllBodies(self):
obj = FemUtils.getSingleMember(self.analysis, "Fem::FemMeshObject")
bodyCount = 0
prefix = ""
if obj.Part.Shape.Solids:
prefix = "Solid"
bodyCount = len(obj.Part.Shape.Solids)
elif obj.Part.Shape.Faces:
prefix = "Face"
bodyCount = len(obj.Part.Shape.Faces)
elif obj.Part.Shape.Edges:
prefix = "Edge"
bodyCount = len(obj.Part.Shape.Edges)
return [prefix + str(i + 1) for i in range(bodyCount)]
def _getMeshDimension(self):
obj = FemUtils.getSingleMember(self.analysis, "Fem::FemMeshObject")
if obj.Part.Shape.Solids:
return 3
elif obj.Part.Shape.Faces:
return 2
elif obj.Part.Shape.Edges:
return 1
return None
def _addOutputSolver(self):
s = sifio.createSection(sifio.SOLVER)
s["Equation"] = "ResultOutput"
s["Exec Solver"] = "After simulation"
s["Procedure"] = sifio.FileAttr("ResultOutputSolve/ResultOutputSolver")
s["Output File Name"] = sifio.FileAttr("case")
s["Vtu Format"] = True
for name in self._getAllBodies():
self._addSolver(name, s)
def _writeSif(self):
sifPath = os.path.join(self.directory, _SIF_NAME)
with open(sifPath, 'w') as fstream:
sif = sifio.Sif(self._builder)
sif.write(fstream)
def _handled(self, obj):
self._handledObjects.add(obj)
def _simulation(self, key, attr):
self._builder.simulation(key, attr)
def _constant(self, key, attr):
self._builder.constant(key, attr)
def _initial(self, body, key, attr):
self._builder.initial(body, key, attr)
def _material(self, body, key, attr):
self._builder.material(body, key, attr)
def _equation(self, body, key, attr):
self._builder.equation(body, key, attr)
def _bodyForce(self, body, key, attr):
self._builder.bodyForce(body, key, attr)
def _addSolver(self, body, solverSection):
self._builder.addSolver(body, solverSection)
def _boundary(self, boundary, key, attr):
self._builder.boundary(boundary, key, attr)
def _addSection(self, section):
self._builder.addSection(section)
def _getMember(self, t):
return FemUtils.getMember(self.analysis, t)
def _getSingleMember(self, t):
return FemUtils.getSingleMember(self.analysis, t)
class WriteError(Exception):
pass

View File

@@ -0,0 +1,96 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - 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 *
# * *
# ***************************************************************************
__title__ = "_Base"
__author__ = "Markus Hovorka"
__url__ = "http://www.freecadweb.org"
import FreeCAD
if FreeCAD.GuiUp:
from pivy import coin
class BaseProxy(object):
BaseType = "App::FeaturePython"
def __init__(self, obj):
obj.Proxy = self
obj.addProperty(
"App::PropertyLinkSubList", "References",
"Base", "")
def execute(self, obj):
return True
class BaseViewProxy(object):
def __init__(self, vobj):
vobj.Proxy = self
def attach(self, vobj):
default = coin.SoGroup()
vobj.addDisplayMode(default, "Default")
def getDisplayModes(self, obj):
"Return a list of display modes."
modes = ["Default"]
return modes
def getDefaultDisplayMode(self):
return "Default"
def setDisplayMode(self, mode):
return mode
class HeatProxy(BaseProxy):
pass
class HeatViewProxy(BaseViewProxy):
def getIcon(self):
return ":/icons/fem-equation-heat.svg"
class ElasticityProxy(BaseProxy):
pass
class ElasticityViewProxy(BaseViewProxy):
def getIcon(self):
return ":/icons/fem-equation-elasticity.svg"
class FlowProxy(BaseProxy):
pass
class FlowViewProxy(BaseViewProxy):
def getIcon(self):
return ":/icons/fem-equation-flow.svg"