FEM: Mystran solver, add solver, writer, tasks and constraint force and
fixed writing
This commit is contained in:
@@ -255,6 +255,19 @@ SET(FemSolverFenics_SRCS
|
||||
femsolver/fenics/fenics_tools.py
|
||||
)
|
||||
|
||||
SET(FemSolverMystran_SRCS
|
||||
femsolver/mystran/__init__.py
|
||||
femsolver/mystran/add_con_fixed.py
|
||||
femsolver/mystran/add_con_force.py
|
||||
femsolver/mystran/add_femelement_geometry.py
|
||||
femsolver/mystran/add_femelement_material.py
|
||||
femsolver/mystran/add_mesh.py
|
||||
femsolver/mystran/add_solver_control.py
|
||||
femsolver/mystran/solver.py
|
||||
femsolver/mystran/tasks.py
|
||||
femsolver/mystran/writer.py
|
||||
)
|
||||
|
||||
SET(FemSolverZ88_SRCS
|
||||
femsolver/z88/__init__.py
|
||||
femsolver/z88/solver.py
|
||||
@@ -281,6 +294,7 @@ SET(FemTestsApp_SRCS
|
||||
femtest/app/test_result.py
|
||||
femtest/app/test_solver_calculix.py
|
||||
femtest/app/test_solver_elmer.py
|
||||
femtest/app/test_solver_mystran.py
|
||||
femtest/app/test_solver_z88.py
|
||||
)
|
||||
|
||||
@@ -452,6 +466,7 @@ SET(FemAllScripts
|
||||
${FemSolverElmer_SRCS}
|
||||
${FemSolverElmerEquations_SRCS}
|
||||
${FemSolverFenics_SRCS}
|
||||
${FemSolverMystran_SRCS}
|
||||
${FemSolverZ88_SRCS}
|
||||
${FemTests_SRCS}
|
||||
${FemTestsApp_SRCS}
|
||||
@@ -489,6 +504,7 @@ INSTALL(FILES ${FemSolverCalculix_SRCS} DESTINATION Mod/Fem/femsolver/calculix)
|
||||
INSTALL(FILES ${FemSolverElmer_SRCS} DESTINATION Mod/Fem/femsolver/elmer)
|
||||
INSTALL(FILES ${FemSolverElmerEquations_SRCS} DESTINATION Mod/Fem/femsolver/elmer/equations)
|
||||
INSTALL(FILES ${FemSolverFenics_SRCS} DESTINATION Mod/Fem/femsolver/fenics)
|
||||
INSTALL(FILES ${FemSolverMystran_SRCS} DESTINATION Mod/Fem/femsolver/mystran)
|
||||
INSTALL(FILES ${FemSolverZ88_SRCS} DESTINATION Mod/Fem/femsolver/z88)
|
||||
INSTALL(FILES ${FemTests_SRCS} DESTINATION Mod/Fem/femtest)
|
||||
INSTALL(FILES ${FemTestsApp_SRCS} DESTINATION Mod/Fem/femtest/app)
|
||||
|
||||
@@ -799,6 +799,17 @@ def makeSolverElmer(
|
||||
return obj
|
||||
|
||||
|
||||
def makeSolverMystran(
|
||||
doc,
|
||||
name="SolverMystran"
|
||||
):
|
||||
"""makeSolverMystran(document, [name]):
|
||||
makes a Mystran solver object"""
|
||||
import femsolver.mystran.solver
|
||||
obj = femsolver.mystran.solver.create(doc, name)
|
||||
return obj
|
||||
|
||||
|
||||
def makeSolverZ88(
|
||||
doc,
|
||||
name="SolverZ88"
|
||||
|
||||
0
src/Mod/Fem/femsolver/mystran/__init__.py
Normal file
0
src/Mod/Fem/femsolver/mystran/__init__.py
Normal file
72
src/Mod/Fem/femsolver/mystran/add_con_fixed.py
Normal file
72
src/Mod/Fem/femsolver/mystran/add_con_fixed.py
Normal file
@@ -0,0 +1,72 @@
|
||||
# ***************************************************************************
|
||||
# * 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__ = "Mystran add fixed constraint"
|
||||
__author__ = "Bernd Hahnebach"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
|
||||
## \addtogroup FEM
|
||||
# @{
|
||||
|
||||
|
||||
def add_con_fixed(f, model, mystran_writer):
|
||||
|
||||
# generate pyNastran code
|
||||
# spc1 card
|
||||
spc_ids = []
|
||||
fixed_code = "# spc1 card, Defines a set of single-point constraints\n"
|
||||
for i, femobj in enumerate(mystran_writer.fixed_objects):
|
||||
|
||||
conid = i + 2 # 1 will be the conid of the spcadd card
|
||||
spc_ids.append(conid)
|
||||
fixed_obj = femobj["Object"]
|
||||
# print(fixed_obj.Name)
|
||||
fixed_code += "# {}\n".format(fixed_obj.Name)
|
||||
# node set
|
||||
fixed_code += "nodes_{} = {}\n".format(fixed_obj.Name, femobj["Nodes"])
|
||||
# all nodes in one line may be to long ... FIXME
|
||||
fixed_code += (
|
||||
"model.add_spc1(conid={}, components={}, nodes=nodes_{})\n\n"
|
||||
.format(conid, "123456", fixed_obj.Name)
|
||||
)
|
||||
|
||||
# spcadd card
|
||||
spcadd_code = "# spcadd card, Single-Point Constraint Set Combination from SPC or SPC1 cards\n"
|
||||
spcadd_code += (
|
||||
"model.add_spcadd(conid=1, sets={})\n\n".format(spc_ids)
|
||||
)
|
||||
|
||||
pynas_code = "{}\n{}".format(fixed_code, spcadd_code)
|
||||
# print(pynas_code)
|
||||
|
||||
# write the pyNastran code
|
||||
f.write(pynas_code)
|
||||
|
||||
# execute pyNastran code to add data to the model
|
||||
# print(model.get_bdf_stats())
|
||||
exec(pynas_code)
|
||||
# print(model.get_bdf_stats())
|
||||
return model
|
||||
|
||||
|
||||
## @}
|
||||
81
src/Mod/Fem/femsolver/mystran/add_con_force.py
Normal file
81
src/Mod/Fem/femsolver/mystran/add_con_force.py
Normal file
@@ -0,0 +1,81 @@
|
||||
# ***************************************************************************
|
||||
# * 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__ = "Mystran add force constraint"
|
||||
__author__ = "Bernd Hahnebach"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
|
||||
## \addtogroup FEM
|
||||
# @{
|
||||
|
||||
|
||||
def add_con_force(f, model, mystran_writer):
|
||||
|
||||
# generate pyNastran code
|
||||
# force card
|
||||
scale_factors = []
|
||||
load_ids = []
|
||||
force_code = "# force cards, mesh node loads\n"
|
||||
for i, femobj in enumerate(mystran_writer.force_objects):
|
||||
|
||||
sid = i + 2 # 1 will be the id of the load card
|
||||
scale_factors.append(1.0)
|
||||
load_ids.append(sid)
|
||||
force_obj = femobj["Object"]
|
||||
# print(force_obj.Name)
|
||||
|
||||
force_code += "# {}\n".format(force_obj.Name)
|
||||
dirvec = femobj["Object"].DirectionVector
|
||||
print(femobj["NodeLoadTable"])
|
||||
for ref_shape in femobj["NodeLoadTable"]:
|
||||
force_code += "# {}\n".format(ref_shape[0])
|
||||
for n in sorted(ref_shape[1]):
|
||||
node_load = ref_shape[1][n]
|
||||
force_code += (
|
||||
"model.add_force(sid={}, node={}, mag={}, xyz=({}, {}, {}))\n"
|
||||
.format(sid, n, node_load, dirvec.x, dirvec.y, dirvec.z)
|
||||
)
|
||||
force_code += "\n"
|
||||
|
||||
# generate calce factors lists
|
||||
# load card, static load combinations
|
||||
load_code = (
|
||||
"model.add_load(sid=1, scale=1.0, scale_factors={}, load_ids={})\n\n\n"
|
||||
.format(scale_factors, load_ids)
|
||||
)
|
||||
|
||||
pynas_code = "{}\n{}".format(force_code, load_code)
|
||||
# print(pynas_code)
|
||||
|
||||
# write the pyNastran code
|
||||
f.write(pynas_code)
|
||||
|
||||
# execute pyNastran code to add data to the model
|
||||
# print(model.get_bdf_stats())
|
||||
exec(pynas_code)
|
||||
# print(model.get_bdf_stats())
|
||||
|
||||
return model
|
||||
|
||||
|
||||
## @}
|
||||
86
src/Mod/Fem/femsolver/mystran/add_femelement_geometry.py
Normal file
86
src/Mod/Fem/femsolver/mystran/add_femelement_geometry.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# ***************************************************************************
|
||||
# * 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__ = "Mystran add femelement geometry"
|
||||
__author__ = "Bernd Hahnebach"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
|
||||
## \addtogroup FEM
|
||||
# @{
|
||||
|
||||
|
||||
def add_femelement_geometry(f, model, mystran_writer):
|
||||
|
||||
# generate pyNastran code
|
||||
# HACK, the if statemant needs improvement, see calculix solver
|
||||
if mystran_writer.member.geos_beamsection:
|
||||
beamsec_obj = mystran_writer.member.geos_beamsection[0]["Object"]
|
||||
if beamsec_obj.SectionType == "Rectangular":
|
||||
height = beamsec_obj.RectHeight.getValueAs("mm").Value
|
||||
width = beamsec_obj.RectWidth.getValueAs("mm").Value
|
||||
pynas_code = "# pbarl card, properties of a simple beam element (CBAR entry)\n"
|
||||
pynas_code += "# defined by cross-sectional dimensions\n"
|
||||
pynas_code += (
|
||||
"dim = [{}, {}]\n"
|
||||
.format(width, height)
|
||||
)
|
||||
pynas_code += (
|
||||
"model.add_pbarl(pid=1, mid=1, Type={}, dim=dim, nsm=0.0)\n"
|
||||
.format('"BAR"')
|
||||
)
|
||||
pynas_code += "# pbarl.validate()\n\n\n"
|
||||
else:
|
||||
return
|
||||
elif mystran_writer.member.geos_shellthickness:
|
||||
# only use the first shellthickness object
|
||||
shellth_obj = mystran_writer.member.geos_shellthickness[0]["Object"]
|
||||
thickness = shellth_obj.Thickness.getValueAs("mm").Value
|
||||
pynas_code = "# pshell card, thin shell element properties\n"
|
||||
pynas_code += (
|
||||
"model.add_pshell(pid=1, mid1=1, t={}, mid2=1, mid3=1)\n\n\n"
|
||||
.format(thickness)
|
||||
)
|
||||
else:
|
||||
pynas_code = "# psolid card, defines solid element\n"
|
||||
pynas_code += "model.add_psolid(pid=1, mid=1)\n\n\n"
|
||||
|
||||
# write the pyNastran code
|
||||
f.write(pynas_code)
|
||||
|
||||
# execute pyNastran code to add data to the model
|
||||
# print(model.get_bdf_stats())
|
||||
exec(pynas_code)
|
||||
# print(model.get_bdf_stats())
|
||||
|
||||
return model
|
||||
|
||||
|
||||
pynas_code = """
|
||||
# pshell card, thin shell element properties
|
||||
model.add_pshell(1, mid1=1, t=0.3, mid2=1, mid3=1)
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
## @}
|
||||
60
src/Mod/Fem/femsolver/mystran/add_femelement_material.py
Normal file
60
src/Mod/Fem/femsolver/mystran/add_femelement_material.py
Normal file
@@ -0,0 +1,60 @@
|
||||
# ***************************************************************************
|
||||
# * 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__ = "Mystran add femelement materials"
|
||||
__author__ = "Bernd Hahnebach"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
|
||||
## \addtogroup FEM
|
||||
# @{
|
||||
|
||||
|
||||
from FreeCAD import Units
|
||||
|
||||
|
||||
def add_femelement_material(f, model, mystran_writer):
|
||||
|
||||
# generate pyNastran code
|
||||
# only use the first material object
|
||||
mat_obj = mystran_writer.member.mats_linear[0]["Object"]
|
||||
YM = Units.Quantity(mat_obj.Material["YoungsModulus"])
|
||||
YM_in_MPa = YM.getValueAs("MPa").Value
|
||||
PR = float(mat_obj.Material["PoissonRatio"])
|
||||
pynas_code = "# mat1 card, material properties for linear isotropic material\n"
|
||||
pynas_code += (
|
||||
"mat = model.add_mat1(mid=1, E={:.1f}, G=None, nu={})\n\n\n"
|
||||
.format(YM_in_MPa, PR)
|
||||
)
|
||||
|
||||
# write the pyNastran code
|
||||
f.write(pynas_code)
|
||||
|
||||
# execute pyNastran code to add data to the model
|
||||
# print(model.get_bdf_stats())
|
||||
exec(pynas_code)
|
||||
# print(model.get_bdf_stats())
|
||||
|
||||
return model
|
||||
|
||||
|
||||
## @}
|
||||
68
src/Mod/Fem/femsolver/mystran/add_mesh.py
Normal file
68
src/Mod/Fem/femsolver/mystran/add_mesh.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# ***************************************************************************
|
||||
# * 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__ = "Mystran add fem mesh"
|
||||
__author__ = "Bernd Hahnebach"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
|
||||
## \addtogroup FEM
|
||||
# @{
|
||||
|
||||
from feminout import exportNastranMesh
|
||||
from femmesh import meshtools
|
||||
|
||||
|
||||
def add_mesh(f, model, mystran_writer):
|
||||
|
||||
# needed basic data
|
||||
if not mystran_writer.femnodes_mesh:
|
||||
mystran_writer.femnodes_mesh = mystran_writer.femmesh.Nodes
|
||||
if not mystran_writer.femelement_table:
|
||||
mystran_writer.femelement_table = meshtools.get_femelement_table(
|
||||
mystran_writer.femmesh
|
||||
)
|
||||
mesh_eletype = exportNastranMesh.get_export_element_type(
|
||||
mystran_writer.femmesh,
|
||||
mystran_writer.femelement_table
|
||||
)
|
||||
|
||||
# get the pynas code
|
||||
mesh_pynas_code = exportNastranMesh.get_pynastran_mesh(
|
||||
mystran_writer.femnodes_mesh,
|
||||
mystran_writer.femelement_table,
|
||||
mesh_eletype
|
||||
)
|
||||
# print(mesh_pynas_code)
|
||||
|
||||
# write the pyNastran code
|
||||
f.write(mesh_pynas_code)
|
||||
|
||||
# execute pyNastran code to add grid to the model
|
||||
# print(model.get_bdf_stats())
|
||||
exec(mesh_pynas_code)
|
||||
# print(model.get_bdf_stats())
|
||||
|
||||
return model
|
||||
|
||||
|
||||
## @}
|
||||
72
src/Mod/Fem/femsolver/mystran/add_solver_control.py
Normal file
72
src/Mod/Fem/femsolver/mystran/add_solver_control.py
Normal file
@@ -0,0 +1,72 @@
|
||||
# ***************************************************************************
|
||||
# * 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__ = "Mystran add solver control"
|
||||
__author__ = "Bernd Hahnebach"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
|
||||
## \addtogroup FEM
|
||||
# @{
|
||||
|
||||
|
||||
def add_solver_control(f, model, mystran_writer):
|
||||
|
||||
# write the pyNastran code which will be executed into the file
|
||||
f.write(pynas_code)
|
||||
|
||||
# print(model.get_bdf_stats())
|
||||
exec(pynas_code)
|
||||
# print(model.get_bdf_stats())
|
||||
|
||||
return model
|
||||
|
||||
|
||||
pynas_code = """
|
||||
# executive control
|
||||
model.sol = 101
|
||||
|
||||
|
||||
# params cards
|
||||
model.add_param(key="POST", values=-1)
|
||||
# model.add_param(key="PRTMAXIM", values="YES") # not recognized by Mystran
|
||||
|
||||
|
||||
# case control
|
||||
from pyNastran.bdf.bdf import CaseControlDeck
|
||||
cc = CaseControlDeck([
|
||||
"ECHO = NONE",
|
||||
"TITLE = pyNastran for generating solverinput for for Mystran",
|
||||
"SUBCASE 1",
|
||||
" SUBTITLE = Default",
|
||||
" LOAD = 1",
|
||||
" SPC = 1",
|
||||
" SPCFORCES(SORT1,REAL) = ALL",
|
||||
" STRESS(SORT1,REAL,VONMISES,BILIN) = ALL",
|
||||
" DISPLACEMENT(SORT1,REAL) = ALL",
|
||||
])
|
||||
model.case_control_deck = cc
|
||||
# model.validate() # creates an error
|
||||
"""
|
||||
|
||||
|
||||
## @}
|
||||
95
src/Mod/Fem/femsolver/mystran/solver.py
Normal file
95
src/Mod/Fem/femsolver/mystran/solver.py
Normal file
@@ -0,0 +1,95 @@
|
||||
# ***************************************************************************
|
||||
# * 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__ = "FreeCAD FEM solver object Mystran"
|
||||
__author__ = "Bernd Hahnebach"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
|
||||
## @package SolverMystran
|
||||
# \ingroup FEM
|
||||
|
||||
import glob
|
||||
import os
|
||||
|
||||
import FreeCAD
|
||||
|
||||
from . import tasks
|
||||
from .. import run
|
||||
from .. import solverbase
|
||||
from femtools import femutils
|
||||
|
||||
if FreeCAD.GuiUp:
|
||||
import FemGui
|
||||
|
||||
ANALYSIS_TYPES = ["static"]
|
||||
|
||||
|
||||
def create(doc, name="SolverMystran"):
|
||||
return femutils.createObject(
|
||||
doc, name, Proxy, ViewProxy)
|
||||
|
||||
|
||||
class Proxy(solverbase.Proxy):
|
||||
"""The Fem::FemSolver's Proxy python type, add solver specific properties
|
||||
"""
|
||||
|
||||
Type = "Fem::SolverMystran"
|
||||
|
||||
def __init__(self, obj):
|
||||
super(Proxy, self).__init__(obj)
|
||||
obj.Proxy = self
|
||||
|
||||
# mystran_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Mystran")
|
||||
|
||||
obj.addProperty("App::PropertyEnumeration", "AnalysisType", "Fem", "Type of the analysis")
|
||||
obj.AnalysisType = ANALYSIS_TYPES
|
||||
obj.AnalysisType = ANALYSIS_TYPES[0]
|
||||
|
||||
def createMachine(self, obj, directory, testmode=False):
|
||||
return run.Machine(
|
||||
solver=obj, directory=directory,
|
||||
check=tasks.Check(),
|
||||
prepare=tasks.Prepare(),
|
||||
solve=tasks.Solve(),
|
||||
results=tasks.Results(),
|
||||
testmode=testmode)
|
||||
|
||||
def editSupported(self):
|
||||
return True
|
||||
|
||||
def edit(self, directory):
|
||||
pattern = os.path.join(directory, "*.bdf") # TODO Mystran file ending
|
||||
FreeCAD.Console.PrintMessage("{}\n".format(pattern))
|
||||
f = glob.glob(pattern)[0]
|
||||
FemGui.open(f)
|
||||
# see comment in oofem solver file
|
||||
|
||||
def execute(self, obj):
|
||||
return
|
||||
|
||||
|
||||
class ViewProxy(solverbase.ViewProxy):
|
||||
pass
|
||||
|
||||
|
||||
## @}
|
||||
179
src/Mod/Fem/femsolver/mystran/tasks.py
Normal file
179
src/Mod/Fem/femsolver/mystran/tasks.py
Normal file
@@ -0,0 +1,179 @@
|
||||
# ***************************************************************************
|
||||
# * 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__ = "FreeCAD FEM solver Mystran tasks"
|
||||
__author__ = "Bernd Hahnebach"
|
||||
__url__ = "https://www.freecadweb.org"
|
||||
|
||||
## \addtogroup FEM
|
||||
# @{
|
||||
|
||||
import os
|
||||
import os.path
|
||||
import subprocess
|
||||
|
||||
import FreeCAD
|
||||
|
||||
|
||||
try:
|
||||
import hfcMystranNeuIn
|
||||
result_reading = True
|
||||
except Exception:
|
||||
FreeCAD.Console.PrintWarning("Module to read results not found.\n")
|
||||
result_reading = False
|
||||
|
||||
|
||||
from . import writer
|
||||
from .. import run
|
||||
from .. import settings
|
||||
from femmesh import meshsetsgetter
|
||||
from femtools import femutils
|
||||
from femtools import membertools
|
||||
|
||||
|
||||
_inputFileName = None
|
||||
|
||||
|
||||
class Check(run.Check):
|
||||
|
||||
def run(self):
|
||||
self.pushStatus("Checking analysis...\n")
|
||||
self.check_mesh_exists()
|
||||
self.check_material_exists()
|
||||
self.check_material_single() # no multiple material
|
||||
self.check_geos_beamsection_single() # no multiple beamsection
|
||||
self.check_geos_shellthickness_single() # no multiple shellsection
|
||||
self.check_geos_beamsection_and_shellthickness() # either beams or shells
|
||||
|
||||
|
||||
class Prepare(run.Prepare):
|
||||
|
||||
def run(self):
|
||||
global _inputFileName
|
||||
self.pushStatus("Preparing input files...\n")
|
||||
|
||||
mesh_obj = membertools.get_mesh_to_solve(self.analysis)[0] # pre check done already
|
||||
|
||||
# get mesh set data
|
||||
# TODO evaluate if it makes sense to add new task
|
||||
# between check and prepare to the solver frame work
|
||||
meshdatagetter = meshsetsgetter.MeshSetsGetter(
|
||||
self.analysis,
|
||||
self.solver,
|
||||
mesh_obj,
|
||||
membertools.AnalysisMember(self.analysis),
|
||||
)
|
||||
meshdatagetter.get_mesh_sets()
|
||||
|
||||
# write input file
|
||||
w = writer.FemInputWriterMystran(
|
||||
self.analysis,
|
||||
self.solver,
|
||||
mesh_obj,
|
||||
meshdatagetter.member,
|
||||
self.directory,
|
||||
meshdatagetter.mat_geo_sets
|
||||
)
|
||||
path = w.write_solver_input()
|
||||
# report to user if task succeeded
|
||||
if path != "":
|
||||
self.pushStatus("Write completed!")
|
||||
else:
|
||||
self.pushStatus("Writing CalculiX input file failed!")
|
||||
_inputFileName = os.path.splitext(os.path.basename(path))[0]
|
||||
|
||||
|
||||
class Solve(run.Solve):
|
||||
|
||||
def run(self):
|
||||
# print(_inputFileName)
|
||||
if not _inputFileName:
|
||||
# TODO do not run solver, do not try to read results in a smarter way than an Exception
|
||||
raise Exception("Error on writing Mystran input file.\n")
|
||||
infile = _inputFileName + ".bdf"
|
||||
|
||||
# TODO use solver framework status system
|
||||
FreeCAD.Console.PrintMessage("Mystran: solver input file: {} \n\n".format(infile))
|
||||
|
||||
# get binary
|
||||
self.pushStatus("Get solver...\n")
|
||||
binary = settings.get_binary("Mystran")
|
||||
# use preferences editor to add a group Mystran and the prefs:
|
||||
# "UseStandardMystranLocation" --> bool, set to False
|
||||
# "mystranBinaryPath, string" --> the binary path
|
||||
if binary is None:
|
||||
return # a print has been made in settings module
|
||||
|
||||
# run solver
|
||||
self.pushStatus("Executing solver...\n")
|
||||
self._process = subprocess.Popen(
|
||||
args=[binary, infile], # pass empty param fails! [binary, "", infile]
|
||||
cwd=self.directory,
|
||||
stdout=subprocess.PIPE,
|
||||
stderr=subprocess.PIPE
|
||||
)
|
||||
self.signalAbort.add(self._process.terminate)
|
||||
self._process.communicate()
|
||||
self.signalAbort.remove(self._process.terminate)
|
||||
|
||||
# for chatching the output see CalculiX or Elmer solver tasks module
|
||||
|
||||
|
||||
class Results(run.Results):
|
||||
|
||||
def run(self):
|
||||
prefs = FreeCAD.ParamGet(
|
||||
"User parameter:BaseApp/Preferences/Mod/Fem/General")
|
||||
if not prefs.GetBool("KeepResultsOnReRun", False):
|
||||
self.purge_results()
|
||||
if result_reading is True:
|
||||
self.load_results() # ToDo in all solvers generischer name
|
||||
|
||||
def purge_results(self):
|
||||
for m in membertools.get_member(self.analysis, "Fem::FemResultObject"):
|
||||
if femutils.is_of_type(m.Mesh, "Fem::MeshResult"):
|
||||
self.analysis.Document.removeObject(m.Mesh.Name)
|
||||
self.analysis.Document.removeObject(m.Name)
|
||||
self.analysis.Document.recompute()
|
||||
# deletes all results from any solver
|
||||
# TODO: delete only the mystran results, fix in all solver
|
||||
|
||||
def load_results(self):
|
||||
self.pushStatus("Import results...\n")
|
||||
neu_result_file = os.path.join(self.directory, _inputFileName + ".NEU")
|
||||
if os.path.isfile(neu_result_file):
|
||||
hfcMystranNeuIn.import_neu(neu_result_file)
|
||||
# Workaround to move result object into analysis
|
||||
for o in self.analysis.Document.Objects:
|
||||
if o.Name == "Displacement0":
|
||||
self.analysis.addObject(o)
|
||||
break
|
||||
else:
|
||||
# TODO: use solver framework error and status message system
|
||||
FreeCAD.Console.PrintError(
|
||||
"FEM: No results found at {}!\n".format(neu_result_file)
|
||||
)
|
||||
return
|
||||
|
||||
|
||||
## @}
|
||||
133
src/Mod/Fem/femsolver/mystran/writer.py
Normal file
133
src/Mod/Fem/femsolver/mystran/writer.py
Normal file
@@ -0,0 +1,133 @@
|
||||
# ***************************************************************************
|
||||
# * 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__ = "Mystran Writer"
|
||||
__author__ = "Bernd Hahnebach"
|
||||
__url__ = "http://www.freecadweb.org"
|
||||
|
||||
## \addtogroup FEM
|
||||
# @{
|
||||
|
||||
import time
|
||||
from os.path import join
|
||||
|
||||
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 . import add_mesh
|
||||
from . import add_femelement_material
|
||||
from . import add_femelement_geometry
|
||||
from . import add_con_force
|
||||
from . import add_con_fixed
|
||||
from . import add_solver_control
|
||||
from .. import writerbase
|
||||
|
||||
|
||||
class FemInputWriterMystran(writerbase.FemInputWriter):
|
||||
def __init__(
|
||||
self,
|
||||
analysis_obj,
|
||||
solver_obj,
|
||||
mesh_obj,
|
||||
member,
|
||||
dir_name=None,
|
||||
mat_geo_sets=None
|
||||
):
|
||||
writerbase.FemInputWriter.__init__(
|
||||
self,
|
||||
analysis_obj,
|
||||
solver_obj,
|
||||
mesh_obj,
|
||||
member,
|
||||
dir_name,
|
||||
mat_geo_sets
|
||||
)
|
||||
# basename (only for implementation purpose later delete this code
|
||||
# the mesh should never be None for Calculix solver
|
||||
# working dir and input file
|
||||
if self.mesh_object is not None:
|
||||
self.basename = self.mesh_object.Name
|
||||
else:
|
||||
self.basename = "Mesh"
|
||||
self.solverinput_file = join(self.dir_name, self.basename + ".bdf")
|
||||
self.pynasinput_file = join(self.dir_name, self.basename + ".py")
|
||||
FreeCAD.Console.PrintLog(
|
||||
"FemInputWriterMystran --> self.dir_name --> {}\n"
|
||||
.format(self.dir_name)
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"FemInputWriterMystra --> self.solverinput_file --> {}\n"
|
||||
.format(self.solverinput_file)
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(
|
||||
"FemInputWriterMystra --> self.pynasf_name --> {}\n"
|
||||
.format(self.pynasinput_file)
|
||||
)
|
||||
|
||||
def write_solver_input(self):
|
||||
|
||||
timestart = time.process_time()
|
||||
|
||||
model = BDF()
|
||||
|
||||
pynasf = open(self.pynasinput_file, "w")
|
||||
|
||||
# comment and model init
|
||||
pynasf.write("# written by FreeCAD\n\n")
|
||||
pynasf.write("from pyNastran.bdf.bdf import BDF\n")
|
||||
pynasf.write("model = BDF()\n\n")
|
||||
|
||||
model = add_mesh.add_mesh(pynasf, model, self)
|
||||
model = add_femelement_material.add_femelement_material(pynasf, model, self)
|
||||
model = add_femelement_geometry.add_femelement_geometry(pynasf, model, self)
|
||||
model = add_con_force.add_con_force(pynasf, model, self)
|
||||
model = add_con_fixed.add_con_fixed(pynasf, model, self)
|
||||
model = add_solver_control.add_solver_control(pynasf, model, self)
|
||||
|
||||
pynasf.write(
|
||||
"\n\nmodel.write_bdf('{}', enddata=True)\n"
|
||||
.format(join(self.dir_name, self.basename + "_pyNas.bdf"))
|
||||
)
|
||||
|
||||
pynasf.close()
|
||||
|
||||
# print(model.get_bdf_stats())
|
||||
model.write_bdf(self.solverinput_file, enddata=True)
|
||||
|
||||
writing_time_string = (
|
||||
"Writing time input file: {} seconds"
|
||||
.format(round((time.process_time() - timestart), 2))
|
||||
)
|
||||
FreeCAD.Console.PrintMessage(writing_time_string + " \n\n")
|
||||
|
||||
return self.solverinput_file
|
||||
|
||||
|
||||
## @}
|
||||
@@ -34,6 +34,7 @@ are supported:
|
||||
|
||||
- Calculix
|
||||
- ElmerSolver
|
||||
- Mystran
|
||||
- Z88
|
||||
|
||||
To query settings about those solver the solver name must be given exactly in
|
||||
@@ -239,6 +240,11 @@ _SOLVER_PARAM = {
|
||||
param_path=_PARAM_PATH + "Elmer",
|
||||
use_default="UseStandardGridLocation",
|
||||
custom_path="gridBinaryPath"),
|
||||
"Mystran": _SolverDlg(
|
||||
default="mystran",
|
||||
param_path=_PARAM_PATH + "Mystran",
|
||||
use_default="UseStandardMystranLocation",
|
||||
custom_path="mystranBinaryPath"),
|
||||
"Z88": _SolverDlg(
|
||||
default="z88r",
|
||||
param_path=_PARAM_PATH + "Z88",
|
||||
|
||||
@@ -322,6 +322,10 @@ class TestObjectType(unittest.TestCase):
|
||||
"Fem::SolverElmer",
|
||||
type_of_obj(solverelmer)
|
||||
)
|
||||
self.assertEqual(
|
||||
"Fem::SolverMystran",
|
||||
type_of_obj(ObjectsFem.makeSolverMystran(doc))
|
||||
)
|
||||
self.assertEqual(
|
||||
"Fem::SolverZ88",
|
||||
type_of_obj(ObjectsFem.makeSolverZ88(doc))
|
||||
@@ -537,6 +541,10 @@ class TestObjectType(unittest.TestCase):
|
||||
solverelmer,
|
||||
"Fem::SolverElmer"
|
||||
))
|
||||
self.assertTrue(is_of_type(
|
||||
ObjectsFem.makeSolverMystran(doc),
|
||||
"Fem::SolverMystran"
|
||||
))
|
||||
self.assertTrue(is_of_type(
|
||||
ObjectsFem.makeSolverZ88(doc),
|
||||
"Fem::SolverZ88"
|
||||
@@ -1217,6 +1225,25 @@ class TestObjectType(unittest.TestCase):
|
||||
"Fem::SolverElmer"
|
||||
))
|
||||
|
||||
# SolverMystran
|
||||
solver_mystran = ObjectsFem.makeSolverMystran(doc)
|
||||
self.assertTrue(is_derived_from(
|
||||
solver_mystran,
|
||||
"App::DocumentObject"
|
||||
))
|
||||
self.assertTrue(is_derived_from(
|
||||
solver_mystran,
|
||||
"Fem::FemSolverObject"
|
||||
))
|
||||
self.assertTrue(is_derived_from(
|
||||
solver_mystran,
|
||||
"Fem::FemSolverObjectPython"
|
||||
))
|
||||
self.assertTrue(is_derived_from(
|
||||
solver_mystran,
|
||||
"Fem::SolverMystran"
|
||||
))
|
||||
|
||||
# SolverZ88
|
||||
solver_z88 = ObjectsFem.makeSolverZ88(doc)
|
||||
self.assertTrue(is_derived_from(
|
||||
@@ -1548,6 +1575,11 @@ class TestObjectType(unittest.TestCase):
|
||||
self.assertTrue(
|
||||
solverelmer.isDerivedFrom("Fem::FemSolverObjectPython")
|
||||
)
|
||||
self.assertTrue(
|
||||
ObjectsFem.makeSolverMystran(
|
||||
doc
|
||||
).isDerivedFrom("Fem::FemSolverObjectPython")
|
||||
)
|
||||
self.assertTrue(
|
||||
ObjectsFem.makeSolverZ88(
|
||||
doc
|
||||
@@ -1657,6 +1689,7 @@ def create_all_fem_objects_doc(
|
||||
analysis.addObject(ObjectsFem.makeSolverCalculixCcxTools(doc))
|
||||
analysis.addObject(ObjectsFem.makeSolverCalculix(doc))
|
||||
sol = analysis.addObject(ObjectsFem.makeSolverElmer(doc))[0]
|
||||
analysis.addObject(ObjectsFem.makeSolverMystran(doc))
|
||||
analysis.addObject(ObjectsFem.makeSolverZ88(doc))
|
||||
|
||||
ObjectsFem.makeEquationElasticity(doc, sol)
|
||||
|
||||
Reference in New Issue
Block a user