FEM: added import/export of mesh as YAML/JSON
This commit is contained in:
@@ -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
|
||||
)
|
||||
|
||||
|
||||
@@ -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")
|
||||
|
||||
|
||||
@@ -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"))
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
220
src/Mod/Fem/feminout/importYamlJsonMesh.py
Normal file
220
src/Mod/Fem/feminout/importYamlJsonMesh.py
Normal 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()
|
||||
25
src/Mod/Fem/femtest/testfiles/mesh/tetra10_mesh.yml
Normal file
25
src/Mod/Fem/femtest/testfiles/mesh/tetra10_mesh.yml
Normal 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: {}
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user