FEM: Add documentation to femutils module

This commit is contained in:
Markus Hovorka
2019-09-23 21:16:37 +02:00
committed by Bernd Hahnebach
parent 8b9dfab46d
commit 9c728a03cc

View File

@@ -20,6 +20,13 @@
# * USA *
# * *
# ***************************************************************************
""" Collection of functions for the Fem module.
This module contains function for managing a analysis and all the differnet
types of objects it contains, helper for executing a simulation, function for
extracting relevant parts of geometry and a few unrelated function useful at
various places in the Fem module.
"""
__title__ = "FEM Utilities"
@@ -38,8 +45,23 @@ if FreeCAD.GuiUp:
from PySide import QtGui
# analysis and its members
def createObject(doc, name, proxy, viewProxy):
""" Add python object to document using python type string.
Add a document object suitable for the *proxy* and the *viewProxy* to *doc*
and attach it to the *proxy* and the *viewProxy*. This function can only be
used with python proxies that specify their C++ type via the BaseType class
member (e.g. Cube.BaseType). If there already exists a object with *name* a
suitable unique name is generated. To auto generate a name pass ``""``.
:param doc: document object to which the object is added
:param name: string of the name of new object in *doc*, use
``""`` to generate a name
:param proxy: python proxy for new object
:param viewProxy: view proxy for new object
:returns: reference to new object
"""
obj = doc.addObject(proxy.BaseType, name)
proxy(obj)
if FreeCAD.GuiUp:
@@ -48,6 +70,14 @@ def createObject(doc, name, proxy, viewProxy):
def findAnalysisOfMember(member):
""" Find Analysis the *member* belongs to.
:param member: a document object
:returns:
If a analysis that contains *member* can be found a reference is returned.
If no such object exists in the document of *member*, ``None`` is returned.
"""
if member is None:
raise ValueError("Member must not be None")
for obj in member.Document.Objects:
@@ -69,6 +99,22 @@ def _searchGroups(member, objs):
def get_member(analysis, t):
""" Return list of all members of *analysis* of type *t*.
Search *analysis* for members of type *t*. This method checks the custom
python typesytem (BaseType class property) used by the Fem module if
possible. If the object doens't use the python typesystem the usual
isDerivedFrom from the C++ dynamic type system is used.
:param analysis: only objects part of this analysis are considered
:param t: only objects of this type are returned
:note:
Inheritance of Fem types is not checked. If *obj* uses Fems typesystem the
type is just checked for equality. If the type doesn't match
``obj.isDerivedFrom`` is called as usual. See
https://forum.freecadweb.org/viewtopic.php?f=10&t=32625
"""
if analysis is None:
raise ValueError("Analysis must not be None")
matching = []
@@ -81,12 +127,58 @@ def get_member(analysis, t):
def get_single_member(analysis, t):
""" Return one object of type *t* and part of *analysis*.
Search *analysis* for members of type *t* and return the first one that's
found. This method checks the custom python typesytem (BaseType class
property) used by the Fem module if possible. If the object doesn't use the
python typesystem the usual isDerivedFrom from the C++ dynamic type system
is used.
:param analysis: only objects part of this analysis are considered
:param t: only a object of this type is returned
:note:
Inheritance of Fem types is not checked. If *obj* uses Fems typesystem the
type is just checked for equality. If the type doesn't match
``obj.isDerivedFrom`` is called as usual. See
https://forum.freecadweb.org/viewtopic.php?f=10&t=32625
"""
objs = get_member(analysis, t)
return objs[0] if objs else None
# collect analysis members used in CalculiX and Z88
def get_several_member(analysis, t):
""" Get members and pack them for Calculix/Z88.
Collect members by calling :py:func:`get_member` and pack them into a
data structure that can be consumed by calculix and Z88 solver modules.
:param analysis: see :py:func:`get_member`
:param t: see :py:func:`get_member`
:returns:
A list containing one dict per member. Each dict has two entries:
``"Object"`` and ``"RefShapeType"``. ``dict["Object"]`` contains the
member document object. ``dict["RefShapeType"]`` contains the shape type
of the *References* property of the member (used by constraints) as a
string ("Vertex", "Edge", "Face" or "Solid"). If the member doesn't have a
*References* property ``dict["RefShapeType"]`` is the empty string ``""``.
:note:
Undefined behaviour if one of the members has a *References* property
which is empty.
:note:
Undefined behaviour if the type of the references of one object arn't all
the same.
:note:
Inheritance of Fem types is not checked. If *obj* uses Fems typesystem the
type is just checked for equality. If the type doesn't match
``obj.isDerivedFrom`` is called as usual. See
https://forum.freecadweb.org/viewtopic.php?f=10&t=32625
"""
# if no member is found, an empty list is returned
objs = get_member(analysis, t)
members = []
@@ -99,6 +191,14 @@ def get_several_member(analysis, t):
def get_mesh_to_solve(analysis):
""" Find one and only mesh object of *analysis*.
:returns:
A tuple ``(object, message)``. If and only if the analysis contains
exactely one mesh object the first value of the tuple is the mesh document
object. Otherwise the first value is ``None`` and the second value is a
error message indicating what went wrong.
"""
mesh_to_solve = None
for m in analysis.Group:
if m.isDerivedFrom("Fem::FemMeshObject") and not is_of_type(m, "Fem::FemMeshResult"):
@@ -114,27 +214,46 @@ def get_mesh_to_solve(analysis):
# typeID and object type defs
def type_of_obj(obj):
"""returns objects TypeId (C++ objects) or Proxy.Type (Python objects)"""
""" Return type of *obj* honoring the special typesystem of Fem.
Python objects of the Fem workbench define their type via a class member
``<Class>.Type``. Return this type if the property exists. If not return
the conventional ``TypeId`` value.
:para obj: a document object
"""
if hasattr(obj, "Proxy") and hasattr(obj.Proxy, "Type"):
return obj.Proxy.Type
return obj.TypeId
def is_of_type(obj, ty):
"""returns True if an object is of
a given TypeId (C++ objects) or Proxy.Type (Python Features)"""
# only returns true if the exact TypeId is given.
# For FeaturPythons the Proxy.Type has to be given.
# Keep in mind the TypeId for them is the TypeId from the C++ father class
""" Compare type of *obj* with *ty* honoring Fems typesystem.
See :py:func:`type_of_obj` for more info about the special typesystem of
the Fem module.
:returns:
``True`` if *obj* is of type *ty*, ``False`` otherwise. Type must match
exactely: Derived objects are not considered to be of type of one of their
super classes.
"""
return type_of_obj(obj) == ty
def is_derived_from(obj, t):
"""returns True if an object or its inheritance chain is of a
given TypeId (C++ objects) or Proxy.Type (Python objects)"""
# returns true for all FEM objects if given t == "App::DocumentObject"
# since this is a father of the given object
# see https://forum.freecadweb.org/viewtopic.php?f=10&t=32625
""" Check if *obj* is derived from *t* honoring Fems typesytem.
Essentially just call ``obj.isDerivedFrom(t)`` and return it's value. For
objects using Fems typesystem (see :py:func:`type_of_obj`) return always
True if the Fem type is equal to *t*.
:note:
Inheritance of Fem types is not checked. If *obj* uses Fems typesystem the
type is just checked for equality. If the type doesn't match
``obj.isDerivedFrom`` is called as usual. See
https://forum.freecadweb.org/viewtopic.php?f=10&t=32625
"""
if (hasattr(obj, "Proxy") and hasattr(obj.Proxy, "Type") and obj.Proxy.Type == t):
return True
return obj.isDerivedFrom(t)
@@ -143,8 +262,16 @@ def is_derived_from(obj, t):
# ************************************************************************************************
# working dir
def get_pref_working_dir(solver_obj):
# _dirTypes from run are not used
# be aware beside could get an error if the document has not been saved
""" Return working directory for solver honoring user settings.
:throws femsolver.run.MustSaveError:
If user setting is set to BESIDE and the document isn't saved.
:note:
Not working correctely for most cases because this circumvents directory
caching of the solver framework. For solver use getMachine from run.py
instead.
"""
dir_setting = settings.get_dir_setting()
if dir_setting == settings.TEMPORARY:
setting_working_dir = get_temp_dir(solver_obj)
@@ -255,6 +382,13 @@ def get_part_to_mesh(mesh_obj):
def getBoundBoxOfAllDocumentShapes(doc):
""" Calculate bounding box containing all objects inside *doc*.
:returns:
A bounding box containing all objects that have a *Shape* attribute (all
Part and PartDesign objects). If the document contains no such objects or
no objects at all return ``None``.
"""
overalboundbox = None
for o in doc.Objects:
# netgen mesh obj has an attribute Shape which is an Document obj, which has no BB
@@ -271,6 +405,16 @@ def getBoundBoxOfAllDocumentShapes(doc):
def getSelectedFace(selectionex):
""" Return selected face if exactly one face is selected.
:returns:
The selcted face as a ``Part::TopoShape`` if exactly one face is selected.
Otherwise return ``None``.
:param selectionex:
A list of selection object like the one Gui.Selection.getSelectionEx()
returns.
"""
aFace = None
# print(selectionex)
if len(selectionex) != 1:
@@ -290,14 +434,27 @@ def getSelectedFace(selectionex):
def get_refshape_type(fem_doc_object):
# returns the reference shape type
# for force object:
# in GUI defined frc_obj all frc_obj have at least one ref_shape
# and ref_shape have all the same shape type
# for material object:
# in GUI defined material_obj could have no RefShape and RefShapes could be different type
# we're going to need the RefShapes to be the same type inside one fem_doc_object
# TODO: check if all RefShapes inside the object really have the same type
""" Return shape type the constraints references.
Determine single shape type of references of *fem_doc_object* which must be
a constraint (=have a *References* property). All references must be of the
same type which is than returned as a string. A type can be "Vertex",
"Edge", "Face" or "Solid".
:param fem_doc_object:
A constraint object with a *References* property.
:returns:
A string representing the shape type ("Vertex", "Edge", "Face" or
"Solid"). If *fem_doc_object* isn't a contraint ``""`` is returned.
:note:
Undefined behaviour if the type of the references of one object arn't all
the same.
:note:
Undefined behaviour if constraint contains no references (empty list).
"""
import femmesh.meshtools as FemMeshTools
if hasattr(fem_doc_object, "References") and fem_doc_object.References:
first_ref_obj = fem_doc_object.References[0]
@@ -315,6 +472,12 @@ def get_refshape_type(fem_doc_object):
def pydecode(bytestring):
""" Return *bytestring* as a unicode string for python 2 and 3.
For python 2 *bytestring* is converted to a string of type ``unicode``. For
python 3 it is returned as is because it uses unicode for it's ``str`` type
already.
"""
if sys.version_info.major < 3:
return bytestring
else: