Skip to content

Commit

Permalink
FEM: add methods to to edit mesh groups:
Browse files Browse the repository at this point in the history
- add addGroup, addGroupElements, removeGroup to C++ mesh class
- expose the methods to Python
- add a unit test class
- update test commands file
- add test to fem test app module
  • Loading branch information
joha2 authored and berndhahnebach committed May 23, 2020
1 parent 3ec7cf9 commit f90a88c
Show file tree
Hide file tree
Showing 7 changed files with 401 additions and 3 deletions.
66 changes: 66 additions & 0 deletions src/Mod/Fem/App/FemMesh.cpp
Expand Up @@ -2053,3 +2053,69 @@ Base::Quantity FemMesh::getVolume(void)const


}

int FemMesh::addGroup(const std::string TypeString, const std::string Name, const int theId)
{
// define mapping between typestring and ElementType
// TODO: remove code doubling by providing mappings for all FemMesh functions
typedef std::map<std::string, SMDSAbs_ElementType> string_eltype_map;
string_eltype_map mapping;
mapping["All"] = SMDSAbs_All;
mapping["Node"] = SMDSAbs_Node;
mapping["Edge"] = SMDSAbs_Edge;
mapping["Face"] = SMDSAbs_Face;
mapping["Volume"] = SMDSAbs_Volume;
mapping["0DElement"] = SMDSAbs_0DElement;
mapping["Ball"] = SMDSAbs_Ball;

int aId = theId;

// check whether typestring is valid
bool typeStringValid = false;
for (string_eltype_map::const_iterator it = mapping.begin(); it != mapping.end(); ++it)
{
std::string key = it->first;
if (key == TypeString)
typeStringValid = true;
}
if (!typeStringValid)
throw std::runtime_error("AddGroup: Invalid type string! Allowed: All, Node, Edge, Face, Volume, 0DElement, Ball");
// add group to mesh
SMESH_Group* group = this->getSMesh()->AddGroup(mapping[TypeString], Name.c_str(), aId);
if (!group)
throw std::runtime_error("AddGroup: Failed to create new group.");
return aId;
}

void FemMesh::addGroupElements(const int GroupId, const std::set<int> ElementIds)
{
SMESH_Group* group = this->getSMesh()->GetGroup(GroupId);
if (!group) {
throw std::runtime_error("AddGroupElements: No group for given id.");
}
SMESHDS_Group* groupDS = dynamic_cast<SMESHDS_Group*>(group->GetGroupDS());
// TODO: is this dynamic_cast OK?

// Traverse the full mesh and add elements to group if id is in set 'ids'
// and if group type is compatible with element
SMDSAbs_ElementType aElementType = groupDS->GetType();

SMDS_ElemIteratorPtr aElemIter = this->getSMesh()->GetMeshDS()->elementsIterator(aElementType);
while (aElemIter->more()) {
const SMDS_MeshElement* aElem = aElemIter->next();
std::set<int>::iterator it;
it = ElementIds.find(aElem->GetID());
if (it != ElementIds.end())
{
// the element was in the list
if (!groupDS->Contains(aElem)) // check whether element is already in group
groupDS->Add(aElem); // if not, add it
}
}
}

bool FemMesh::removeGroup(int GroupId)
{
return this->getSMesh()->RemoveGroup(GroupId);
}

12 changes: 12 additions & 0 deletions src/Mod/Fem/App/FemMesh.h
Expand Up @@ -32,6 +32,7 @@
#include <list>
#include <boost/shared_ptr.hpp>
#include <SMESH_Version.h>
#include <SMDSAbs_ElementType.hxx>

class SMESH_Gen;
class SMESH_Mesh;
Expand Down Expand Up @@ -131,6 +132,17 @@ class AppFemExport FemMesh : public Data::ComplexGeoData
void transformGeometry(const Base::Matrix4D &rclMat);
//@}

/** @name Group management */
//@{
/// Adds group to mesh
int addGroup(const std::string, const std::string, const int=-1);
/// Adds elements to group (int due to int used by raw SMESH functions)
void addGroupElements(int, std::set<int>);
/// Remove group (Name due to similarity to SMESH basis functions)
bool removeGroup(int);
//@}


struct FemMeshInfo {
int numFaces;
int numNode;
Expand Down
31 changes: 31 additions & 0 deletions src/Mod/Fem/App/FemMeshPy.xml
Expand Up @@ -158,6 +158,37 @@
<UserDocu>Return a tuple of ElementIDs to a given group ID</UserDocu>
</Documentation>
</Methode>
<Methode Name="addGroup" Const="true">
<Documentation>
<UserDocu>Add a group to mesh with specific name and type
addGroup(name, typestring, [id])
name: string
typestring: \"All\", \"Node\", \"Edge\", \"Face\", \"Volume\", \"0DElement\", \"Ball\"
id: int
Optional id is used to force specific id for group, but does
not work, yet.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="addGroupElements" Const="true">
<Documentation>
<UserDocu>Add a tuple of ElementIDs to a given group ID
addGroupElements(groupid, list_of_elements)
groupid: int
list_of_elements: list of int
Notice that the elements have to be in the mesh.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="removeGroup" Const="true">
<Documentation>
<UserDocu>Remove a group with a given group ID
removeGroup(groupid)
groupid: int
Returns boolean.
</UserDocu>
</Documentation>
</Methode>
<Methode Name="getElementType" Const="true">
<Documentation>
<UserDocu>Return the element type of a given ID</UserDocu>
Expand Down
97 changes: 97 additions & 0 deletions src/Mod/Fem/App/FemMeshPyImp.cpp
Expand Up @@ -1033,6 +1033,103 @@ PyObject* FemMeshPy::getGroupElements(PyObject *args)
return Py::new_reference_to(tuple);
}

/*
Add Groups and elements to these.
*/

PyObject* FemMeshPy::addGroup(PyObject *args)
{
// get name and typestring from arguments
char* Name;
char* typeString;
int theId = -1;
if (!PyArg_ParseTuple(args, "etet|i","utf-8", &Name, "utf-8", &typeString, &theId))
return 0;
std::string EncodedName = std::string(Name);
std::string EncodedTypeString = std::string(typeString);

int retId = -1;

try
{
retId = getFemMeshPtr()->addGroup(EncodedTypeString, EncodedName, theId);
}
catch (Standard_Failure& e) {
PyErr_SetString(Base::BaseExceptionFreeCADError, e.GetMessageString());
return 0;
}
std::cout << "Added Group: Name: \'" << EncodedName << "\' Type: \'" << EncodedTypeString << "\' id: " << retId << std::endl;

#if PY_MAJOR_VERSION >= 3
return PyLong_FromLong(retId);
#else
return PyInt_FromLong(retId);
#endif
}

PyObject* FemMeshPy::addGroupElements(PyObject *args)
{
int id;
// the second object should be a list
// see https://stackoverflow.com/questions/22458298/extending-python-with-c-pass-a-list-to-pyarg-parsetuple
PyObject *pList;
PyObject *pItem;
Py_ssize_t n;

if (!PyArg_ParseTuple(args, "iO!", &id, &PyList_Type, &pList))
{
PyErr_SetString(PyExc_TypeError, "AddGroupElements: 2nd Parameter must be a list.");
return 0;
}

std::set<Py_ssize_t> ids;
n = PyList_Size(pList);
std::cout << "AddGroupElements: num elements: " << n << " sizeof: " << sizeof(n) << std::endl;
for (Py_ssize_t i = 0; i < n; i++) {
pItem = PyList_GetItem(pList, i);
#if PY_MAJOR_VERSION >= 3
if(!PyLong_Check(pItem)) {
#else
if(!PyInt_Check(pItem)) {
#endif
PyErr_SetString(PyExc_TypeError, "AddGroupElements: List items must be integers.");
return 0;
}
#if PY_MAJOR_VERSION >= 3
ids.insert(PyLong_AsSsize_t(pItem));
#else
ids.insert(PyInt_AsSsize_t(pItem));
#endif
// Py_ssize_t transparently handles maximum array lengths on 32bit and 64bit machines
// See: https://www.python.org/dev/peps/pep-0353/
}

// Downcast Py_ssize_t to int to be compatible with SMESH functions
std::set<int> int_ids;
for (std::set<Py_ssize_t>::iterator it = ids.begin(); it != ids.end(); ++it)
int_ids.insert(Py_SAFE_DOWNCAST(*it, Py_ssize_t, int));

try
{
getFemMeshPtr()->addGroupElements(id, int_ids);
}
catch (Standard_Failure& e) {
PyErr_SetString(Base::BaseExceptionFreeCADError, e.GetMessageString());
return 0;
}

Py_Return;
}

PyObject* FemMeshPy::removeGroup(PyObject *args)
{
int theId;
if (!PyArg_ParseTuple(args, "i", &theId))
return 0;
return PyBool_FromLong((long)(getFemMeshPtr()->removeGroup(theId)));
}


PyObject* FemMeshPy::getElementType(PyObject *args)
{
int id;
Expand Down
8 changes: 5 additions & 3 deletions src/Mod/Fem/TestFemApp.py
Expand Up @@ -32,9 +32,10 @@
from femtest.app.test_material import TestMaterialUnits as FemTest06
from femtest.app.test_mesh import TestMeshCommon as FemTest07
from femtest.app.test_mesh import TestMeshEleTetra10 as FemTest08
from femtest.app.test_result import TestResult as FemTest09
from femtest.app.test_ccxtools import TestCcxTools as FemTest10
from femtest.app.test_solverframework import TestSolverFrameWork as FemTest11
from femtest.app.test_mesh import TestMeshGroups as FemTest09
from femtest.app.test_result import TestResult as FemTest10
from femtest.app.test_ccxtools import TestCcxTools as FemTest11
from femtest.app.test_solverframework import TestSolverFrameWork as FemTest12

# dummy usage to get flake8 and lgtm quiet
False if FemTest01.__name__ else True
Expand All @@ -48,3 +49,4 @@
False if FemTest09.__name__ else True
False if FemTest10.__name__ else True
False if FemTest11.__name__ else True
False if FemTest12.__name__ else True

0 comments on commit f90a88c

Please sign in to comment.