Files
create/src/Mod/Fem/importZ88Mesh.py
Kunda 91bd0fc536 FEM: Doxygen tweeks
Discussion in [FEM subforum](https://forum.freecadweb.org/viewtopic.php?f=18&t=12833&start=40#p158684)
...Calculix FRD Reader doxygen tweek
...Calculix INP Reader doxygen tweek
...Result import and export VTK file library doxygen tweek
...Z88 Mesh reader and writer doxygen tweek
...Z88 Disp Reader doxygen tweek
...Command show result doxygen tweek
2017-02-16 14:00:40 +01:00

439 lines
20 KiB
Python

# ***************************************************************************
# * *
# * Copyright (c) 2016 - Bernd Hahnebach <bernd@bimstatik.org> *
# * *
# * 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 Z88 Mesh reader and writer"
__author__ = "Bernd Hahnebach "
__url__ = "http://www.freecadweb.org"
## @package importZ88Mesh
# \ingroup FEM
# \brief FreeCAD Z88 Mesh reader and writer for FEM workbench
import FreeCAD
import os
import FemMeshTools
Debug = False
if open.__module__ == '__builtin__':
pyopen = open # because we'll redefine open below
def open(filename):
"called when freecad opens a file"
docname = os.path.splitext(os.path.basename(filename))[0]
insert(filename, docname)
def insert(filename, docname):
"called when freecad wants to import a file"
try:
doc = FreeCAD.getDocument(docname)
except NameError:
doc = FreeCAD.newDocument(docname)
FreeCAD.ActiveDocument = doc
import_z88_mesh(filename)
def import_z88_mesh(filename, analysis=None):
'''insert a FreeCAD FEM Mesh object in the ActiveDocument
'''
mesh_data = read_z88_mesh(filename)
mesh_name = os.path.basename(os.path.splitext(filename)[0])
femmesh = FemMeshTools.make_femmesh(mesh_data)
if femmesh:
mesh_object = FreeCAD.ActiveDocument.addObject('Fem::FemMeshObject', mesh_name)
mesh_object.FemMesh = femmesh
def read_z88_mesh(z88_mesh_input):
''' reads a z88 mesh file z88i1.txt (Z88OSV14) or z88structure.txt (Z88AuroraV3)
and extracts the nodes and elements
'''
nodes = {}
elements_hexa8 = {}
elements_penta6 = {}
elements_tetra4 = {}
elements_tetra10 = {}
elements_penta15 = {}
elements_hexa20 = {}
elements_tria3 = {}
elements_tria6 = {}
elements_quad4 = {}
elements_quad8 = {}
elements_seg2 = {}
input_continues = False
# elem = -1
z88_element_type = 0
z88_mesh_file = pyopen(z88_mesh_input, "r")
mesh_info = z88_mesh_file.readline().strip().split()
nodes_dimension = int(mesh_info[0])
nodes_count = int(mesh_info[1])
elements_count = int(mesh_info[2])
kflag = int(mesh_info[4])
if kflag: # for non rotational elements ist --> kflag = 0 --> karthesian, kflag = 1 polar koordinates
FreeCAD.Console.PrintError("KFLAG = 1, Rotational koordinates not supported at the moment\n")
return {}
nodes_first_line = 2 # first line is mesh_info
nodes_last_line = nodes_count + 1
elemts_first_line = nodes_last_line + 1
elements_last_line = elemts_first_line - 1 + elements_count * 2
if Debug:
print(nodes_count)
print(elements_count)
print(nodes_last_line)
print(elemts_first_line)
print(elements_last_line)
z88_mesh_file.seek(0) # go back to the beginning of the file
for no, line in enumerate(z88_mesh_file):
lno = no + 1
linecolumns = line.split()
if lno >= nodes_first_line and lno <= nodes_last_line:
# node line
node_no = int(linecolumns[0])
node_x = float(linecolumns[2])
node_y = float(linecolumns[3])
if nodes_dimension == 2:
node_z = 0.0
elif nodes_dimension == 3:
node_z = float(linecolumns[4])
nodes[node_no] = FreeCAD.Vector(node_x, node_y, node_z)
if lno >= elemts_first_line and lno <= elements_last_line:
# first element line
if not input_continues:
elem_no = int(linecolumns[0])
z88_element_type = int(linecolumns[1])
input_continues = True
# second element line
elif input_continues:
# not supported elements
if z88_element_type == 8:
# torus8
FreeCAD.Console.PrintError("Z88 Element No. 8, torus8\n")
FreeCAD.Console.PrintError("Rotational elements are not supported at the moment\n")
return {}
elif z88_element_type == 12:
# torus12
FreeCAD.Console.PrintError("Z88 Element No. 12, torus12\n")
FreeCAD.Console.PrintError("Rotational elements are not supported at the moment\n")
return {}
elif z88_element_type == 15:
# torus6
FreeCAD.Console.PrintError("Z88 Element No. 15, torus6\n")
FreeCAD.Console.PrintError("Rotational elements are not supported at the moment\n")
return {}
elif z88_element_type == 19:
# platte16
FreeCAD.Console.PrintError("Z88 Element No. 19, platte16\n")
FreeCAD.Console.PrintError("Not supported at the moment\n")
return {}
elif z88_element_type == 21:
# schale16, mixture made from hexa8 und hexa20 (thickness is linear)
FreeCAD.Console.PrintError("Z88 Element No. 21, schale16\n")
FreeCAD.Console.PrintError("Not supported at the moment\n")
return {}
elif z88_element_type == 22:
# schale12, mixtrue made from prism6 and prism15 (thickness is linear)
FreeCAD.Console.PrintError("Z88 Element No. 22, schale12\n")
FreeCAD.Console.PrintError("Not supported at the moment\n")
return {}
# supported elements
elif z88_element_type == 2 or z88_element_type == 4 or z88_element_type == 5 or z88_element_type == 9 or z88_element_type == 13 or z88_element_type == 25:
# stab4 or stab5 or welle5 or beam13 or beam25 Z88 --> seg2 FreeCAD
# N1, N2
nd1 = int(linecolumns[0])
nd2 = int(linecolumns[1])
elements_seg2[elem_no] = (nd1, nd2)
input_continues = False
elif z88_element_type == 3 or z88_element_type == 14 or z88_element_type == 24:
# scheibe3 or scheibe14 or schale24 Z88 --> tria6 FreeCAD
# N1, N2, N3, N4, N5, N6
nd1 = int(linecolumns[0])
nd2 = int(linecolumns[1])
nd3 = int(linecolumns[2])
nd4 = int(linecolumns[3])
nd5 = int(linecolumns[4])
nd6 = int(linecolumns[5])
elements_tria6[elem_no] = (nd1, nd2, nd3, nd4, nd5, nd6)
input_continues = False
elif z88_element_type == 7 or z88_element_type == 20 or z88_element_type == 23:
# scheibe7 or platte20 or schale23 Z88 --> quad8 FreeCAD
# N1, N2, N3, N4, N5, N6, N7, N8
nd1 = int(linecolumns[0])
nd2 = int(linecolumns[1])
nd3 = int(linecolumns[2])
nd4 = int(linecolumns[3])
nd5 = int(linecolumns[4])
nd6 = int(linecolumns[5])
nd7 = int(linecolumns[6])
nd8 = int(linecolumns[7])
elements_quad8[elem_no] = (nd1, nd2, nd3, nd4, nd5, nd6, nd7, nd8)
input_continues = False
elif z88_element_type == 17:
# volume17 Z88 --> tetra4 FreeCAD
# N4, N2, N3, N1
nd1 = int(linecolumns[0])
nd2 = int(linecolumns[1])
nd3 = int(linecolumns[2])
nd4 = int(linecolumns[3])
elements_tetra4[elem_no] = (nd4, nd2, nd3, nd1)
input_continues = False
elif z88_element_type == 16:
# volume16 Z88 --> tetra10 FreeCAD
# N4, N2, N3, N1, N9, N6, N10, N5, N7, N8
nd1 = int(linecolumns[0])
nd2 = int(linecolumns[1])
nd3 = int(linecolumns[2])
nd4 = int(linecolumns[3])
nd5 = int(linecolumns[4])
nd6 = int(linecolumns[5])
nd7 = int(linecolumns[6])
nd8 = int(linecolumns[7])
nd9 = int(linecolumns[8])
nd10 = int(linecolumns[9])
elements_tetra10[elem_no] = (nd4, nd2, nd3, nd1, nd9, nd6, nd10, nd5, nd7, nd8)
input_continues = False
elif z88_element_type == 1:
# volume1 Z88 --> hexa8 FreeCAD
# N1, N2, N3, N4, N5, N6, N7, N8
nd1 = int(linecolumns[0])
nd2 = int(linecolumns[1])
nd3 = int(linecolumns[2])
nd4 = int(linecolumns[3])
nd5 = int(linecolumns[4])
nd6 = int(linecolumns[5])
nd7 = int(linecolumns[6])
nd8 = int(linecolumns[7])
elements_hexa8[elem_no] = (nd1, nd2, nd3, nd4, nd5, nd6, nd7, nd8)
input_continues = False
elif z88_element_type == 10:
# volume10 Z88 --> hexa20 FreeCAD
# N2, N3, N4, N1, N6, N7, N8, N5, N10, N11, N12, N9, N14, N15, N16, N13, N18, N19, N20, N17
# or turn by 90 degree and they match !
# N1, N2, N3, N4, N5, N6, N7, N8, N9, N10, N11, N12, N13, N14, N15, N16, N17, N18, N19, N20
nd1 = int(linecolumns[0])
nd2 = int(linecolumns[1])
nd3 = int(linecolumns[2])
nd4 = int(linecolumns[3])
nd5 = int(linecolumns[4])
nd6 = int(linecolumns[5])
nd7 = int(linecolumns[6])
nd8 = int(linecolumns[7])
nd9 = int(linecolumns[8])
nd10 = int(linecolumns[9])
nd11 = int(linecolumns[10])
nd12 = int(linecolumns[11])
nd13 = int(linecolumns[12])
nd14 = int(linecolumns[13])
nd15 = int(linecolumns[14])
nd16 = int(linecolumns[15])
nd17 = int(linecolumns[16])
nd18 = int(linecolumns[17])
nd19 = int(linecolumns[18])
nd20 = int(linecolumns[19])
elements_hexa20[elem_no] = (nd1, nd2, nd3, nd4, nd5, nd6, nd7, nd8, nd9, nd10,
nd11, nd12, nd13, nd14, nd15, nd16, nd17, nd18, nd19, nd20)
input_continues = False
# not known elements, some example have -1 for some teaching reasons to show some other stuff
else:
FreeCAD.Console.PrintError("Not known element\n")
return {}
if Debug:
for n in nodes:
print(n, ' ', nodes[n])
for e in elements_tria6:
print(e, ' ', elements_tria6[e])
z88_mesh_file.close()
return {'Nodes': nodes,
'Hexa8Elem': elements_hexa8, 'Penta6Elem': elements_penta6, 'Tetra4Elem': elements_tetra4, 'Tetra10Elem': elements_tetra10,
'Penta15Elem': elements_penta15, 'Hexa20Elem': elements_hexa20, 'Tria3Elem': elements_tria3, 'Tria6Elem': elements_tria6,
'Quad4Elem': elements_quad4, 'Quad8Elem': elements_quad8, 'Seg2Elem': elements_seg2,
}
# export z88 Mesh
def export(objectslist, filename):
"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
femnodes_mesh = obj.FemMesh.Nodes
femelement_table = FemMeshTools.get_femelement_table(obj.FemMesh)
z88_element_type = get_z88_element_type(obj.FemMesh, femelement_table)
f = pyopen(filename, "wb")
write_z88_mesh_to_file(femnodes_mesh, femelement_table, z88_element_type, f)
f.close()
def write_z88_mesh_to_file(femnodes_mesh, femelement_table, z88_element_type, f):
node_dimension = 3 # 2 for 2D not supported
if (z88_element_type == 4 or
z88_element_type == 17 or z88_element_type == 16 or
z88_element_type == 1 or z88_element_type == 10):
node_dof = 3
elif z88_element_type == 23 or z88_element_type == 24:
node_dof = 6 # schalenelemente
else:
print("Error: wrong z88_element_type")
return
node_count = len(femnodes_mesh)
element_count = len(femelement_table)
dofs = node_dof * node_count
unknown_flag = 0
written_by = "written by FreeCAD"
# first line, some z88 specific stuff
f.write("{0} {1} {2} {3} {4} {5}\n".format(node_dimension, node_count, element_count, dofs, unknown_flag, written_by))
# nodes
for node in femnodes_mesh:
vec = femnodes_mesh[node]
f.write("{0} {1} {2:.6f} {3:.6f} {4:.6f}\n".format(node, node_dof, vec.x, vec.y, vec.z, node))
# elements
for element in femelement_table:
# z88_element_type is checked for every element, but mixed elements are not supported up to date
n = femelement_table[element]
if z88_element_type == 2 or z88_element_type == 4 or z88_element_type == 5 or z88_element_type == 9 or z88_element_type == 13 or z88_element_type == 25:
# seg2 FreeCAD --> stab4 Z88
# N1, N2
f.write("{0} {1}\n".format(element, z88_element_type, element))
f.write("{0} {1}\n".format(
n[0], n[1]))
elif z88_element_type == 3 or z88_element_type == 14 or z88_element_type == 24:
# tria6 FreeCAD --> schale24 Z88
# N1, N2, N3, N4, N5, N6
f.write("{0} {1}\n".format(element, z88_element_type, element))
f.write("{0} {1} {2} {3} {4} {5}\n".format(
n[0], n[1], n[2], n[3], n[4], n[5]))
elif z88_element_type == 7 or z88_element_type == 20 or z88_element_type == 23:
# quad8 FreeCAD --> schale23 Z88
# N1, N2, N3, N4, N5, N6, N7, N8
f.write("{0} {1}\n".format(element, z88_element_type, element))
f.write("{0} {1} {2} {3} {4} {5} {6} {7}\n".format(
n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]))
elif z88_element_type == 17:
# tetra4 FreeCAD --> volume17 Z88#
# N4, N2, N3, N1
f.write("{0} {1}\n".format(element, z88_element_type, element))
f.write("{0} {1} {2} {3}\n".format(
n[3], n[1], n[2], n[0]))
elif z88_element_type == 16:
# tetra10 FreeCAD --> volume16 Z88
# N4, N2, N3, N1, N9, N6, N10, N5, N7, N8
f.write("{0} {1}\n".format(element, z88_element_type, element))
f.write("{0} {1} {2} {3} {4} {5} {6} {7} {8} {9}\n".format(
n[3], n[1], n[2], n[0], n[8], n[5], n[9], n[4], n[6], n[7]))
elif z88_element_type == 1:
# hexa8 FreeCAD --> volume1 Z88
# N1, N2, N3, N4, N5, N6, N7, N8
f.write("{0} {1}\n".format(element, z88_element_type, element))
f.write("{0} {1} {2} {3} {4} {5} {6} {7}\n".format(
n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7]))
elif z88_element_type == 10:
# hexa20 FreeCAD --> volume10 Z88
# N2, N3, N4, N1, N6, N7, N8, N5, N10, N11, N12, N9, N14, N15, N16, N13, N18, N19, N20, N17
# or turn by 90 degree and they match !
# N1, N2, N3, N4, N5, N6, N7, N8, N9, N10, N11, N12, N13, N14, N15, N16, N17, N18, N19, N20
f.write("{0} {1}\n".format(element, z88_element_type, element))
f.write("{0} {1} {2} {3} {4} {5} {6} {7} {8} {9} {10} {11} {12} {13} {14} {15} {16} {17} {18} {19}\n".format(
n[0], n[1], n[2], n[3], n[4], n[5], n[6], n[7], n[8], n[9], n[10], n[11], n[12], n[13], n[14], n[15], n[16], n[17], n[18], n[19]))
else:
FreeCAD.Console.PrintError("Writing of Z88 elementtype {0} not supported.\n".format(z88_element_type))
# TODO support schale12 (made from prism15) and schale16 (made from hexa20)
return
# Helper
def get_z88_element_type(femmesh, femelement_table=None):
if not femmesh:
print("Error: No femmesh!")
if not femelement_table:
print("We need to get the femelement_table first!")
femelement_table = FemMeshTools.get_femelement_table(femmesh)
# in some cases lowest key in femelement_table is not [1]
for elem in sorted(femelement_table):
elem_length = len(femelement_table[elem])
print elem_length
break # break after the first elem
if FemMeshTools.is_solid_femmesh(femmesh):
if femmesh.TetraCount == femmesh.VolumeCount:
if elem_length == 4:
return 17
elif elem_length == 10:
return 16
else:
print('Tetra with neiter 4 nor 10 nodes')
elif femmesh.HexaCount == femmesh.VolumeCount:
if elem_length == 8:
return 1
elif elem_length == 20:
return 10
else:
print('Hexa with neither 8 nor 20 nodes')
return 0
else:
print('no tetra, no hexa or Mixed Volume Elements')
elif FemMeshTools.is_face_femmesh(femmesh):
if femmesh.TriangleCount == femmesh.FaceCount:
if elem_length == 3:
print('tria3mesh, not supported by z88')
return 0
elif elem_length == 6:
return 24
else:
print('Tria with neither 3 nor 6 nodes')
return 0
elif femmesh.QuadrangleCount == femmesh.FaceCount:
if elem_length == 4:
print('quad4mesh, not supported by z88')
return 0
elif elem_length == 8:
return 23
else:
print('Quad with neiter 4 nor 8 nodes')
return 0
else:
print('no tria, no quad')
return 0
elif FemMeshTools.is_edge_femmesh(femmesh):
print('Edge femmesh will be exported as 3D truss element nr 4')
return 4
else:
print('Neither, edge, face or solid femmesh')
return 0
return 0