Files
create/src/Mod/Fem/feminout/exportNastranMesh.py
2024-06-23 16:26:36 +03:00

210 lines
7.6 KiB
Python

# ***************************************************************************
# * Copyright (c) 2021 Bernd Hahnebach <bernd@bimstatik.org> *
# * *
# * This file is part of the FreeCAD CAx development system. *
# * *
# * 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__ = "Mesh export for Nastran mesh file format"
__author__ = "Bernd Hahnebach"
__url__ = "https://www.freecad.org"
## @package exportPyNastranMesh
# \ingroup FEM
# \brief FreeCAD pyNastran Mesh writer for FEM workbench
import FreeCAD
# we need to import FreeCAD before the non FreeCAD library because of the print
try:
from pyNastran.bdf.bdf import BDF
except Exception:
FreeCAD.Console.PrintError(
"Module pyNastran not found. Writing Mystran solver input will not be work.\n"
)
from FreeCAD import Console
from femmesh import meshtools
# ************************************************************************************************
# ********* generic FreeCAD export methods *******************************************************
# names are fix given from FreeCAD, these methods are called from FreeCAD
# they are set in FEM modules Init.py
def export(objectslist, filename):
"called when freecad exports a file"
if len(objectslist) != 1:
Console.PrintError("This exporter can only export one object.\n")
return
obj = objectslist[0]
if not obj.isDerivedFrom("Fem::FemMeshObject"):
Console.PrintError("No FEM mesh object selected.\n")
return
write(obj.FemMesh, filename)
# ************************************************************************************************
# ********* module specific methods **************************************************************
# writer:
# - a method directly writes a FemMesh to the mesh file
# - a method generates the pyNastran code
# ********* writer *******************************************************************************
def write(fem_mesh, filename):
"""directly write a FemMesh to a pyNastran mesh file format
fem_mesh: a FemMesh"""
if not fem_mesh.isDerivedFrom("Fem::FemMesh"):
Console.PrintError("Not a FemMesh was given as parameter.\n")
return
femnodes_mesh = fem_mesh.Nodes
femelement_table = meshtools.get_femelement_table(fem_mesh)
export_element_type = get_export_element_type(fem_mesh, femelement_table)
model = BDF()
mesh_pynas_code = get_pynastran_mesh(femnodes_mesh, femelement_table, export_element_type)
mesh_pynas_code += missing_code_pnynasmesh
# pynas file
basefilename = filename[: len(filename) - 4] # TODO basename is more failsafe
pynasf = open(basefilename + ".py", "w")
pynasf.write("# written by FreeCAD\n\n\n")
pynasf.write("from pyNastran.bdf.bdf import BDF\n")
pynasf.write("model = BDF()\n\n\n")
pynasf.write(mesh_pynas_code)
pynasf.write("model.write_bdf('{}', enddata=True)\n".format(basefilename + "_pyNas.bdf"))
pynasf.close()
# execute pyNastran code to add grid to the model
# print(model)
# print(model.get_bdf_stats())
exec(mesh_pynas_code)
# print(model)
# print(model.get_bdf_stats())
# write Nastran mesh file
model.write_bdf(filename, enddata=True) # TODO FIXME "BEGIN BULK" is missing
def get_pynastran_mesh(
femnodes_mesh,
femelement_table,
export_element_type,
):
if export_element_type is None:
Console.PrintError("Error: wrong export_element_type.\n")
return
# nodes
pynas_nodes = "# grid cards, geometric mesh points\n"
for node in femnodes_mesh:
vec = femnodes_mesh[node]
pynas_nodes += f"model.add_grid({node}, [{vec.x}, {vec.y}, {vec.z}])\n"
# print(pynas_nodes)
# elements
# Nastran seems to have the same node order as SMESH (FreeCAD) has
# thus just write the nodes at once
pynas_elements = "# elements cards\n"
for element in femelement_table:
nodes = femelement_table[element]
# print(element) # eleid
# print(n) # tuple of nodes
if export_element_type == "cbar":
pynas_elements += (
"model.add_{ele_keyword}({eid}, {pid}, {nodes}, "
"{orientation_vec}, {gnull})\n".format(
ele_keyword=export_element_type,
eid=element,
pid=1,
nodes=nodes,
orientation_vec="x=[0.0, 0.0, 1.0]",
gnull="g0=None",
)
)
else:
if export_element_type == "ctetra4":
ele_keyword = "ctetra"
# N1, N3, N2, N4
the_nodes = [nodes[0], nodes[2], nodes[1], nodes[3]]
elif export_element_type == "ctetra10":
ele_keyword = "ctetra"
# N1, N3, N2, N4, N7, N6, N5, N8, N10, N9
the_nodes = [
nodes[0],
nodes[2],
nodes[1],
nodes[3],
nodes[6],
nodes[5],
nodes[4],
nodes[7],
nodes[9],
nodes[8],
]
else:
ele_keyword = export_element_type
the_nodes = nodes
pynas_elements += "model.add_{ele_keyword}({eid}, {pid}, {nodes})\n".format(
ele_keyword=ele_keyword, eid=element, pid=1, nodes=the_nodes
)
# print(pynas_elements)
mesh_pynas_code = f"{pynas_nodes}\n\n{pynas_elements}\n\n"
return mesh_pynas_code
# Helper
def get_export_element_type(femmesh, femelement_table=None):
return nastran_ele_types[meshtools.get_femmesh_eletype(femmesh, femelement_table)]
nastran_ele_types = {
"tetra4": "ctetra4",
"tetra10": "ctetra10",
"hexa8": None,
"hexa20": None,
"tria3": "ctria3",
"tria6": None,
"quad4": "cquad4",
"quad8": None,
"seg2": "cbar",
"seg3": None,
"None": None,
}
missing_code_pnynasmesh = """
model.sol = 101 # is this needed?
# case control
from pyNastran.bdf.bdf import CaseControlDeck
cc = CaseControlDeck([
#"ECHO = NONE",
"TITLE = pyNastran for generating solverinput for for Mystran",
#"SUBCASE 1",
])
model.case_control_deck = cc\n\n
"""