FEM: Mystran solver, add solver, writer, tasks and constraint force and

fixed writing
This commit is contained in:
Bernd Hahnebach
2021-07-30 13:07:57 +02:00
parent e100971fa0
commit cdcd271b4c
14 changed files with 912 additions and 0 deletions

View File

@@ -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)

View File

@@ -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"

View 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
## @}

View 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
## @}

View 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)
"""
## @}

View 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
## @}

View 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
## @}

View 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
"""
## @}

View 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
## @}

View 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
## @}

View 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
## @}

View 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",

View File

@@ -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)