FEM: added import/export of mesh as YAML/JSON

This commit is contained in:
joha2
2019-06-08 22:19:10 +02:00
committed by Bernd Hahnebach
parent c1d0c9854e
commit d78f5a1151
7 changed files with 374 additions and 0 deletions

View File

@@ -52,6 +52,7 @@ SET(FemInOut_SRCS
feminout/importInpMesh.py
feminout/importToolsFem.py
feminout/importVTKResults.py
feminout/importYamlJsonMesh.py
feminout/importZ88Mesh.py
feminout/importZ88O2Results.py
feminout/readFenicsXDMF.py
@@ -183,6 +184,7 @@ SET(FemTestsMesh_SRCS
femtest/testfiles/mesh/tetra10_mesh.inp
femtest/testfiles/mesh/tetra10_mesh.unv
femtest/testfiles/mesh/tetra10_mesh.vtk
femtest/testfiles/mesh/tetra10_mesh.yml
femtest/testfiles/mesh/tetra10_mesh.z88
)

View File

@@ -39,6 +39,13 @@ FreeCAD.addImportType("FEM result CalculiX (*.frd)", "feminout.importCcxFrdResul
FreeCAD.addImportType("FEM mesh Fenics (*.xml *.xdmf)", "feminout.importFenicsMesh")
FreeCAD.addExportType("FEM mesh Fenics (*.xml *.xdmf)", "feminout.importFenicsMesh")
FreeCAD.addImportType(
"FEM mesh YAML/JSON (*.meshyaml *.meshjson *.yaml *.json)", "feminout.importYamlJsonMesh"
)
FreeCAD.addExportType(
"FEM mesh YAML/JSON (*.meshyaml *.meshjson *.yaml *.json)", "feminout.importYamlJsonMesh"
)
FreeCAD.addImportType("FEM mesh Z88 (*i1.txt)", "feminout.importZ88Mesh")
FreeCAD.addExportType("FEM mesh Z88 (*i1.txt)", "feminout.importZ88Mesh")

View File

@@ -117,6 +117,7 @@ gf()
./bin/FreeCADCmd --run-test "femtest.testmesh.TestMeshEleTetra10.test_tetra10_inp"
./bin/FreeCADCmd --run-test "femtest.testmesh.TestMeshEleTetra10.test_tetra10_unv"
./bin/FreeCADCmd --run-test "femtest.testmesh.TestMeshEleTetra10.test_tetra10_vkt"
./bin/FreeCADCmd --run-test "femtest.testmesh.TestMeshEleTetra10.test_tetra10_yml"
./bin/FreeCADCmd --run-test "femtest.testmesh.TestMeshEleTetra10.test_tetra10_z88"
./bin/FreeCADCmd --run-test "femtest.testobject.TestObjectCreate.test_femobjects_make"
./bin/FreeCADCmd --run-test "femtest.testobject.TestObjectType.test_femobjects_type"
@@ -185,6 +186,9 @@ unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName("femtest.t
import unittest
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName("femtest.testmesh.TestMeshEleTetra10.test_tetra10_vkt"))
import unittest
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName("femtest.testmesh.TestMeshEleTetra10.test_tetra10_yml"))
import unittest
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName("femtest.testmesh.TestMeshEleTetra10.test_tetra10_z88"))

View File

@@ -242,6 +242,96 @@ def make_femmesh(
return mesh
def make_dict_from_femmesh(
femmesh
):
"""
Converts FemMesh into dictionary structure which can immediately used
from importToolsFem.make_femmesh(mesh_data) to create a valid FEM mesh.
"""
# this dict can be easily saved and reloaded by yaml
# see importYamlJasonMesh for a implementation
mesh_data = {}
seg2 = []
seg3 = []
tri3 = []
tri6 = []
quad4 = []
quad8 = []
tet4 = []
tet10 = []
hex8 = []
hex20 = []
pent6 = []
pent15 = []
# associations for lengths of tuples to different
# edge, face, and volume elements
len_to_edge = {2: seg2, 3: seg3}
len_to_face = {3: tri3, 6: tri6, 4: quad4, 8: quad8}
len_to_volume = {
4: tet4,
10: tet10,
8: hex8,
20: hex20,
6: pent6,
15: pent15
}
# analyze edges
for e in femmesh.Edges:
t = femmesh.getElementNodes(e)
len_to_edge[len(t)].append((e, t))
# analyze faces
for f in femmesh.Faces:
t = femmesh.getElementNodes(f)
len_to_face[len(t)].append((f, t))
# analyze volumes
for v in femmesh.Volumes:
t = femmesh.getElementNodes(v)
len_to_volume[len(t)].append((v, t))
mesh_data = {
'Nodes': dict([(k, (v.x, v.y, v.z))
for (k, v) in femmesh.Nodes.items()]),
'Seg2Elem': dict(seg2),
'Seg3Elem': dict(seg3),
'Tria3Elem': dict(tri3),
'Tria6Elem': dict(tri6),
'Quad4Elem': dict(quad4),
'Quad8Elem': dict(quad8),
'Tetra4Elem': dict(tet4),
'Tetra10Elem': dict(tet10),
'Hexa8Elem': dict(hex8),
'Hexa20Elem': dict(hex20),
'Penta6Elem': dict(pent6),
'Penta15Elem': dict(pent15),
'Groups': dict([(
group_num, (
femmesh.getGroupName(group_num),
femmesh.getGroupElements(group_num)
)
) for group_num in femmesh.Groups])
}
# no pyr5, pyr13?
# no groups?
return mesh_data
def fill_femresult_mechanical(
res_obj,
result_set

View File

@@ -0,0 +1,220 @@
# ***************************************************************************
# * *
# * Copyright (c) 2019 - Johannes Hartung <j.hartung@gmx.net> *
# * *
# * 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__ = "FreeCAD YAML and JSON mesh reader and writer"
__author__ = "Johannes Hartung"
__url__ = "http://www.freecadweb.org"
## @package importYAMLJSONMesh
# \ingroup FEM
# \brief FreeCAD YAML and JSON Mesh reader and writer for FEM workbench
import json
import os
import FreeCAD
from . import importToolsFem
has_yaml = True
try:
import yaml
except ImportError:
FreeCAD.Console.PrintMessage(
"No YAML available (import yaml failure), "
"yaml import/export won't work\n"
)
has_yaml = False
# ****************************************************************************
# ********* generic FreeCAD import and export methods ************************
# names are fix given from FreeCAD, these methods are called from FreeCAD
# they are set in FEM modules Init.py
if open.__module__ == '__builtin__':
# because we'll redefine open below (Python2)
pyopen = open
elif open.__module__ == 'io':
# because we'll redefine open below (Python3)
pyopen = open
def open(
filename
):
'''called when freecad opens a file
a FEM mesh object is created in a new document'''
docname = os.path.splitext(os.path.basename(filename))[0]
return insert(filename, docname)
def insert(
filename,
docname
):
'''called when freecad wants to import a file"
a FEM mesh object is created in a existing document'''
try:
doc = FreeCAD.getDocument(docname)
except NameError:
doc = FreeCAD.newDocument(docname)
FreeCAD.ActiveDocument = doc
import_yaml_json_mesh(filename)
return doc
def export(objectslist, fileString):
"called when freecad exports a file"
if len(objectslist) != 1:
FreeCAD.Console.PrintError(
"This exporter can only "
"export one object.\n")
return
obj = objectslist[0]
if not obj.isDerivedFrom("Fem::FemMeshObject"):
FreeCAD.Console.PrintError("No FEM mesh object selected.\n")
return
write(fileString, obj.FemMesh)
# ****************************************************************************
# ********* module specific methods ******************************************
# reader:
# - a method uses a FemMesh instance, creates the FEM mesh document object and
# returns this object
# - a method read the data from file creates FemMesh instance out of the
# FEM mesh dictionary. This instance is returned
# - a converts the raw read data into the FEM mesh dictionary which
# can be used to create a FemMesh instance
#
#
# writer:
# - a method directly writes a FemMesh to the mesh file
# ********* reader ***********************************************************
def import_yaml_json_mesh(
fileString
):
"""
read a FemMesh from a yaml/json mesh file
insert a FreeCAD FEM Mesh object in the ActiveDocument
return the FEM mesh document object
"""
mesh_name = os.path.basename(os.path.splitext(fileString)[0])
femmesh = read(fileString)
if femmesh:
mesh_object = FreeCAD.ActiveDocument.addObject(
'Fem::FemMeshObject',
mesh_name
)
mesh_object.FemMesh = femmesh
return mesh_object
def read(
fileString
):
'''read a FemMesh from a yaml/json mesh file and return the FemMesh
'''
# no document object is created, just the FemMesh is returned
fileExtension = os.path.basename(os.path.splitext(fileString)[1])
raw_mesh_data = {}
if fileExtension.lower() == ".meshjson" or\
fileExtension.lower() == ".json":
fp = pyopen(fileString, "rt")
raw_mesh_data = json.load(fp)
fp.close()
elif (
fileExtension.lower() == ".meshyaml"
or fileExtension.lower() == ".meshyml"
or fileExtension.lower() == ".yaml"
or fileExtension.lower() == ".yml"
) and has_yaml:
fp = pyopen(fileString, "rt")
raw_mesh_data = yaml.load(fp)
fp.close()
else:
FreeCAD.Console.PrintError(
"Unknown extension, "
"please select other importer.\n")
FreeCAD.Console.PrintMessage("Converting indices to integer numbers ...")
mesh_data = convert_raw_data_to_mesh_data(raw_mesh_data)
FreeCAD.Console.PrintMessage("OK\n")
return importToolsFem.make_femmesh(mesh_data)
def convert_raw_data_to_mesh_data(
raw_mesh_data
):
"""
Converts raw dictionary data from JSON or YAML file to proper dict
for importToolsFem.make_femmesh(mesh_data). This is necessary since
JSON and YAML save dict keys as strings while make_femmesh expects
integers.
"""
mesh_data = {}
for (type_key, type_dict) in raw_mesh_data.items():
if type_key.lower() != "groups":
mesh_data[type_key] = dict([
(int(k), v) for (k, v) in type_dict.items()
])
return mesh_data
# ********* writer ***********************************************************
def write(
fileString,
fem_mesh
):
'''directly write a FemMesh to a yaml/json mesh file
fem_mesh: a FemMesh'''
mesh_data = importToolsFem.make_dict_from_femmesh(fem_mesh)
if fileString != "":
fileName, fileExtension = os.path.splitext(fileString)
if fileExtension.lower() == ".json" \
or fileExtension.lower() == ".meshjson":
fp = pyopen(fileString, "wt")
json.dump(mesh_data, fp, indent=4)
fp.close()
elif (
fileExtension.lower() == ".meshyaml"
or fileExtension.lower() == ".meshyml"
or fileExtension.lower() == ".yaml"
or fileExtension.lower() == ".yml"
) and has_yaml:
fp = pyopen(fileString, "wt")
yaml.safe_dump(mesh_data, fp)
fp.close()

View File

@@ -0,0 +1,25 @@
Groups: {}
Hexa20Elem: {}
Hexa8Elem: {}
Nodes:
1: [6.0, 12.0, 18.0]
2: [0.0, 0.0, 18.0]
3: [12.0, 0.0, 18.0]
4: [6.0, 6.0, 0.0]
5: [3.0, 6.0, 18.0]
6: [6.0, 0.0, 18.0]
7: [9.0, 6.0, 18.0]
8: [6.0, 9.0, 9.0]
9: [3.0, 3.0, 9.0]
10: [9.0, 3.0, 9.0]
Penta15Elem: {}
Penta6Elem: {}
Quad4Elem: {}
Quad8Elem: {}
Seg2Elem: {}
Seg3Elem: {}
Tetra10Elem:
1: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Tetra4Elem: {}
Tria3Elem: {}
Tria6Elem: {}

View File

@@ -456,6 +456,32 @@ class TestMeshEleTetra10(unittest.TestCase):
else:
fcc_print('FEM_VTK post processing is disabled.')
# ********************************************************************************************
def test_tetra10_yml(
self
):
# tetra10 element: reading from and writing to yaml/json mesh file format
file_extension = 'yml'
outfile, testfile = self.get_file_paths(file_extension)
# TODO: implement yaml/json mesh reader writer method calls in C++
# self.femmesh.write(outfile) # write the mesh
# femmesh_testfile = Fem.read(outfile) # read the mesh from written mesh
# femmesh_outfile = Fem.read(testfile) # read the mesh from test mesh
# directly use Python methods to read and write files
from feminout.importYamlJsonMesh import write
write(self.femmesh, outfile)
from feminout.importYamlJsonMesh import read
femmesh_testfile = read(outfile)
femmesh_outfile = read(testfile)
self.compare_mesh_files(
femmesh_testfile,
femmesh_outfile,
file_extension
)
# ********************************************************************************************
def test_tetra10_z88(
self