FEM: add methods to to edit mesh groups:
- 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
This commit is contained in:
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
@@ -131,6 +132,17 @@ public:
|
||||
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;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -32,9 +32,10 @@ from femtest.app.test_open import TestObjectOpen as FemTest05
|
||||
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
|
||||
@@ -48,3 +49,4 @@ False if FemTest08.__name__ else True
|
||||
False if FemTest09.__name__ else True
|
||||
False if FemTest10.__name__ else True
|
||||
False if FemTest11.__name__ else True
|
||||
False if FemTest12.__name__ else True
|
||||
|
||||
@@ -529,3 +529,174 @@ class TestMeshEleTetra10(unittest.TestCase):
|
||||
femmesh_outfile,
|
||||
file_extension
|
||||
)
|
||||
|
||||
|
||||
# ************************************************************************************************
|
||||
# ************************************************************************************************
|
||||
# TODO: add elements to group with another type. Should be empty at the end.
|
||||
class TestMeshGroups(unittest.TestCase):
|
||||
fcc_print("import TestMeshGroups")
|
||||
|
||||
# ********************************************************************************************
|
||||
def setUp(
|
||||
self
|
||||
):
|
||||
# setUp is executed before every test
|
||||
|
||||
# new document
|
||||
self.document = FreeCAD.newDocument(self.__class__.__name__)
|
||||
|
||||
# ********************************************************************************************
|
||||
def tearDown(
|
||||
self
|
||||
):
|
||||
# tearDown is executed after every test
|
||||
FreeCAD.closeDocument(self.document.Name)
|
||||
|
||||
# ********************************************************************************************
|
||||
def test_00print(
|
||||
self
|
||||
):
|
||||
# since method name starts with 00 this will be run first
|
||||
# this test just prints a line with stars
|
||||
|
||||
fcc_print("\n{0}\n{1} run FEM TestMeshGroups tests {2}\n{0}".format(
|
||||
100 * "*",
|
||||
10 * "*",
|
||||
57 * "*"
|
||||
))
|
||||
|
||||
# ********************************************************************************************
|
||||
def test_add_groups(self):
|
||||
"""
|
||||
Create different groups with different names. Check whether the
|
||||
ids are correct, the names are correct, and whether the GroupCount is
|
||||
correct.
|
||||
"""
|
||||
|
||||
from femexamples.meshes.mesh_canticcx_tetra10 import create_elements
|
||||
from femexamples.meshes.mesh_canticcx_tetra10 import create_nodes
|
||||
|
||||
fm = Fem.FemMesh()
|
||||
control = create_nodes(fm)
|
||||
if not control:
|
||||
fcc_print("failed to create nodes")
|
||||
control = create_elements(fm)
|
||||
if not control:
|
||||
fcc_print("failed to create elements")
|
||||
|
||||
# information
|
||||
# fcc_print(fm)
|
||||
|
||||
expected_dict = {}
|
||||
expected_dict["ids"] = []
|
||||
expected_dict["names"] = [
|
||||
"MyNodeGroup",
|
||||
"MyEdgeGroup",
|
||||
"MyVolumeGroup",
|
||||
"My0DElementGroup",
|
||||
"MyBallGroup"
|
||||
]
|
||||
expected_dict["types"] = [
|
||||
"Node",
|
||||
"Edge",
|
||||
"Volume",
|
||||
"0DElement",
|
||||
"Ball"
|
||||
]
|
||||
expected_dict["count"] = fm.GroupCount + 5
|
||||
result_dict = {}
|
||||
|
||||
mygrpids = []
|
||||
for (name, typ) in zip(expected_dict["names"], expected_dict["types"]):
|
||||
mygrpids.append(fm.addGroup(name, typ))
|
||||
|
||||
expected_dict["ids"] = sorted(tuple(mygrpids))
|
||||
|
||||
# fcc_print("expected dict")
|
||||
# fcc_print(expected_dict)
|
||||
|
||||
result_dict["count"] = fm.GroupCount
|
||||
result_dict["ids"] = sorted(fm.Groups)
|
||||
result_dict["types"] = list([fm.getGroupElementType(g)
|
||||
for g in fm.Groups])
|
||||
result_dict["names"] = list([fm.getGroupName(g) for g in fm.Groups])
|
||||
|
||||
# fcc_print("result dict")
|
||||
# fcc_print(result_dict)
|
||||
|
||||
self.assertEqual(
|
||||
expected_dict,
|
||||
result_dict,
|
||||
msg="expected: {0}\n\nresult: {1}\n\n differ".format(expected_dict, result_dict)
|
||||
)
|
||||
|
||||
def test_delete_groups(self):
|
||||
"""
|
||||
Adds a number of groups to FemMesh and deletes them
|
||||
afterwards. Checks whether GroupCount is OK
|
||||
"""
|
||||
from femexamples.meshes.mesh_canticcx_tetra10 import create_elements
|
||||
from femexamples.meshes.mesh_canticcx_tetra10 import create_nodes
|
||||
|
||||
fm = Fem.FemMesh()
|
||||
control = create_nodes(fm)
|
||||
if not control:
|
||||
fcc_print("failed to create nodes")
|
||||
control = create_elements(fm)
|
||||
if not control:
|
||||
fcc_print("failed to create elements")
|
||||
|
||||
# information
|
||||
# fcc_print(fm)
|
||||
old_group_count = fm.GroupCount
|
||||
myids = []
|
||||
for i in range(1000):
|
||||
myids.append(fm.addGroup("group" + str(i), "Node"))
|
||||
for grpid in myids:
|
||||
fm.removeGroup(grpid)
|
||||
new_group_count = fm.GroupCount
|
||||
self.assertEqual(
|
||||
old_group_count,
|
||||
new_group_count,
|
||||
msg=(
|
||||
"GroupCount before and after adding and deleting groups differ: {0} != {1}"
|
||||
.format(old_group_count, new_group_count)
|
||||
)
|
||||
)
|
||||
|
||||
def test_add_group_elements(self):
|
||||
"""
|
||||
Add a node group, add elements to it. Verify that elements added
|
||||
and elements in getGroupElements are the same.
|
||||
"""
|
||||
from femexamples.meshes.mesh_canticcx_tetra10 import create_elements
|
||||
from femexamples.meshes.mesh_canticcx_tetra10 import create_nodes
|
||||
|
||||
fm = Fem.FemMesh()
|
||||
control = create_nodes(fm)
|
||||
if not control:
|
||||
fcc_print("failed to create nodes")
|
||||
control = create_elements(fm)
|
||||
if not control:
|
||||
fcc_print("failed to create elements")
|
||||
|
||||
# information
|
||||
# fcc_print(fm)
|
||||
|
||||
elements_to_be_added = [1, 2, 3, 4, 49, 64, 88, 100, 102, 188, 189, 190, 191]
|
||||
myid = fm.addGroup("mynodegroup", "Node")
|
||||
|
||||
# fcc_print(fm.getGroupElements(myid))
|
||||
|
||||
fm.addGroupElements(myid, elements_to_be_added)
|
||||
elements_returned = list(fm.getGroupElements(myid)) # returns tuple
|
||||
# fcc_print(elements_returned)
|
||||
self.assertEqual(
|
||||
elements_to_be_added,
|
||||
elements_returned,
|
||||
msg=(
|
||||
"elements to be added {0} and elements returned {1} differ".
|
||||
format(elements_to_be_added, elements_returned)
|
||||
)
|
||||
)
|
||||
|
||||
@@ -26,6 +26,7 @@ make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_femimport.TestObjectExistance
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_material.TestMaterialUnits
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_mesh.TestMeshCommon
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_mesh.TestMeshEleTetra10
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_mesh.TestMeshGroups
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_object.TestObjectCreate
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_object.TestObjectType
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_open.TestObjectOpen
|
||||
@@ -61,6 +62,9 @@ make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_mesh.TestMeshEleTetra10.test_t
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_mesh.TestMeshEleTetra10.test_tetra10_vkt
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_mesh.TestMeshEleTetra10.test_tetra10_yml
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_mesh.TestMeshEleTetra10.test_tetra10_z88
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_mesh.TestMeshGroups.test_add_groups
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_mesh.TestMeshGroups.test_delete_groups
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_mesh.TestMeshGroups.test_add_group_elements
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_object.TestObjectCreate.test_femobjects_make
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_object.TestObjectType.test_femobjects_type
|
||||
make -j 4 && ./bin/FreeCADCmd -t femtest.app.test_object.TestObjectType.test_femobjects_isoftype
|
||||
@@ -215,6 +219,21 @@ unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName(
|
||||
'femtest.app.test_mesh.TestMeshEleTetra10.test_tetra10_z88'
|
||||
))
|
||||
|
||||
import unittest
|
||||
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName(
|
||||
'femtest.app.test_mesh.TestMeshGroups.test_add_groups'
|
||||
))
|
||||
|
||||
import unittest
|
||||
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName(
|
||||
'femtest.app.test_mesh.TestMeshGroups.test_delete_groups'
|
||||
))
|
||||
|
||||
import unittest
|
||||
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName(
|
||||
'femtest.app.test_mesh.TestMeshGroups.test_add_group_elements'
|
||||
))
|
||||
|
||||
import unittest
|
||||
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName(
|
||||
'femtest.app.test_object.TestObjectCreate.test_femobjects_make'
|
||||
|
||||
Reference in New Issue
Block a user