From a8b14fa803d359b148d5632f702ff52c6a160eaa Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Mon, 12 Jul 2021 21:16:06 +0200 Subject: [PATCH] FEM: calculix writer, move write footer and step in separate modules MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bitte geben Sie eine Commit-Beschreibung für Ihre Änderungen ein. Zeilen, --- src/Mod/Fem/CMakeLists.txt | 3 + .../Fem/femsolver/calculix/write_footer.py | 54 +++++ .../femsolver/calculix/write_step_equation.py | 153 +++++++++++++ .../femsolver/calculix/write_step_output.py | 79 +++++++ src/Mod/Fem/femsolver/calculix/writer.py | 207 +----------------- 5 files changed, 298 insertions(+), 198 deletions(-) create mode 100644 src/Mod/Fem/femsolver/calculix/write_footer.py create mode 100644 src/Mod/Fem/femsolver/calculix/write_step_equation.py create mode 100644 src/Mod/Fem/femsolver/calculix/write_step_output.py diff --git a/src/Mod/Fem/CMakeLists.txt b/src/Mod/Fem/CMakeLists.txt index 1289ee8151..75ba0c05ab 100755 --- a/src/Mod/Fem/CMakeLists.txt +++ b/src/Mod/Fem/CMakeLists.txt @@ -202,6 +202,9 @@ SET(FemSolverCalculix_SRCS femsolver/calculix/write_constraint_temperature.py femsolver/calculix/write_constraint_tie.py femsolver/calculix/write_constraint_transform.py + femsolver/calculix/write_footer.py + femsolver/calculix/write_step_equation.py + femsolver/calculix/write_step_output.py femsolver/calculix/writer.py ) diff --git a/src/Mod/Fem/femsolver/calculix/write_footer.py b/src/Mod/Fem/femsolver/calculix/write_footer.py new file mode 100644 index 0000000000..81b5136a76 --- /dev/null +++ b/src/Mod/Fem/femsolver/calculix/write_footer.py @@ -0,0 +1,54 @@ +# *************************************************************************** +# * Copyright (c) 2021 Bernd Hahnebach * +# * * +# * 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 calculix write inpfile footer" +__author__ = "Bernd Hahnebach" +__url__ = "https://www.freecadweb.org" + + +import os +import time + + +def write_footer(f, ccxwriter): + + f.write("\n{}\n".format(59 * "*")) + f.write("** CalculiX Input file\n") + f.write("** written by --> FreeCAD {}.{}.{}\n".format( + ccxwriter.fc_ver[0], + ccxwriter.fc_ver[1], + ccxwriter.fc_ver[2] + )) + f.write("** written on --> {}\n".format( + time.ctime() + )) + f.write("** file name --> {}\n".format( + os.path.basename(ccxwriter.document.FileName) + )) + f.write("** analysis name --> {}\n".format( + ccxwriter.analysis.Name + )) + f.write("**\n") + f.write("**\n") + f.write(ccxwriter.units_information) + f.write("**\n") diff --git a/src/Mod/Fem/femsolver/calculix/write_step_equation.py b/src/Mod/Fem/femsolver/calculix/write_step_equation.py new file mode 100644 index 0000000000..a9f79f8a77 --- /dev/null +++ b/src/Mod/Fem/femsolver/calculix/write_step_equation.py @@ -0,0 +1,153 @@ +# *************************************************************************** +# * Copyright (c) 2021 Bernd Hahnebach * +# * * +# * 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 calculix write inpfile step equation" +__author__ = "Bernd Hahnebach" +__url__ = "https://www.freecadweb.org" + + +import FreeCAD + + +def write_step_equation(f, ccxwriter): + + f.write("\n{}\n".format(59 * "*")) + f.write("** At least one step is needed to run an CalculiX analysis of FreeCAD\n") + + # build STEP line + step = "*STEP" + if ccxwriter.solver_obj.GeometricalNonlinearity == "nonlinear": + if ccxwriter.analysis_type == "static" or ccxwriter.analysis_type == "thermomech": + # https://www.comsol.com/blogs/what-is-geometric-nonlinearity + step += ", NLGEOM" + elif ccxwriter.analysis_type == "frequency": + FreeCAD.Console.PrintMessage( + "Analysis type frequency and geometrical nonlinear " + "analysis are not allowed together, linear is used instead!\n" + ) + if ccxwriter.solver_obj.IterationsThermoMechMaximum: + if ccxwriter.analysis_type == "thermomech": + step += ", INC={}".format(ccxwriter.solver_obj.IterationsThermoMechMaximum) + elif ( + ccxwriter.analysis_type == "static" + or ccxwriter.analysis_type == "frequency" + or ccxwriter.analysis_type == "buckling" + ): + # parameter is for thermomechanical analysis only, see ccx manual *STEP + pass + # write STEP line + f.write(step + "\n") + + # CONTROLS line + # all analysis types, ... really in frequency too?!? + if ccxwriter.solver_obj.IterationsControlParameterTimeUse: + f.write("*CONTROLS, PARAMETERS=TIME INCREMENTATION\n") + f.write(ccxwriter.solver_obj.IterationsControlParameterIter + "\n") + f.write(ccxwriter.solver_obj.IterationsControlParameterCutb + "\n") + + # ANALYSIS type line + # analysis line --> analysis type + if ccxwriter.analysis_type == "static": + analysis_type = "*STATIC" + elif ccxwriter.analysis_type == "frequency": + analysis_type = "*FREQUENCY" + elif ccxwriter.analysis_type == "thermomech": + analysis_type = "*COUPLED TEMPERATURE-DISPLACEMENT" + elif ccxwriter.analysis_type == "check": + analysis_type = "*NO ANALYSIS" + elif ccxwriter.analysis_type == "buckling": + analysis_type = "*BUCKLE" + # analysis line --> solver type + # https://forum.freecadweb.org/viewtopic.php?f=18&t=43178 + if ccxwriter.solver_obj.MatrixSolverType == "default": + pass + elif ccxwriter.solver_obj.MatrixSolverType == "spooles": + analysis_type += ", SOLVER=SPOOLES" + elif ccxwriter.solver_obj.MatrixSolverType == "iterativescaling": + analysis_type += ", SOLVER=ITERATIVE SCALING" + elif ccxwriter.solver_obj.MatrixSolverType == "iterativecholesky": + analysis_type += ", SOLVER=ITERATIVE CHOLESKY" + # analysis line --> user defined incrementations --> parameter DIRECT + # --> completely switch off ccx automatic incrementation + if ccxwriter.solver_obj.IterationsUserDefinedIncrementations: + if ccxwriter.analysis_type == "static": + analysis_type += ", DIRECT" + elif ccxwriter.analysis_type == "thermomech": + analysis_type += ", DIRECT" + elif ccxwriter.analysis_type == "frequency": + FreeCAD.Console.PrintMessage( + "Analysis type frequency and IterationsUserDefinedIncrementations " + "are not allowed together, it is ignored\n" + ) + # analysis line --> steadystate --> thermomech only + if ccxwriter.solver_obj.ThermoMechSteadyState: + # bernd: I do not know if STEADY STATE is allowed with DIRECT + # but since time steps are 1.0 it makes no sense IMHO + if ccxwriter.analysis_type == "thermomech": + analysis_type += ", STEADY STATE" + # Set time to 1 and ignore user inputs for steady state + ccxwriter.solver_obj.TimeInitialStep = 1.0 + ccxwriter.solver_obj.TimeEnd = 1.0 + elif ( + ccxwriter.analysis_type == "static" + or ccxwriter.analysis_type == "frequency" + or ccxwriter.analysis_type == "buckling" + ): + pass # not supported for static and frequency! + + # ANALYSIS parameter line + analysis_parameter = "" + if ccxwriter.analysis_type == "static" or ccxwriter.analysis_type == "check": + if ccxwriter.solver_obj.IterationsUserDefinedIncrementations is True \ + or ccxwriter.solver_obj.IterationsUserDefinedTimeStepLength is True: + analysis_parameter = "{},{}".format( + ccxwriter.solver_obj.TimeInitialStep, + ccxwriter.solver_obj.TimeEnd + ) + elif ccxwriter.analysis_type == "frequency": + if ccxwriter.solver_obj.EigenmodeLowLimit == 0.0 \ + and ccxwriter.solver_obj.EigenmodeHighLimit == 0.0: + analysis_parameter = "{}\n".format(ccxwriter.solver_obj.EigenmodesCount) + else: + analysis_parameter = "{},{},{}\n".format( + ccxwriter.solver_obj.EigenmodesCount, + ccxwriter.solver_obj.EigenmodeLowLimit, + ccxwriter.solver_obj.EigenmodeHighLimit + ) + elif ccxwriter.analysis_type == "thermomech": + # OvG: 1.0 increment, total time 1 for steady state will cut back automatically + analysis_parameter = "{},{}".format( + ccxwriter.solver_obj.TimeInitialStep, + ccxwriter.solver_obj.TimeEnd + ) + elif ccxwriter.analysis_type == "buckling": + analysis_parameter = "{}\n".format(ccxwriter.solver_obj.BucklingFactors) + + # write analysis type line, analysis parameter line + f.write(analysis_type + "\n") + f.write(analysis_parameter + "\n") + + +def write_step_end(f, ccxwriter): + f.write("\n{}\n".format(59 * "*")) + f.write("*END STEP \n") diff --git a/src/Mod/Fem/femsolver/calculix/write_step_output.py b/src/Mod/Fem/femsolver/calculix/write_step_output.py new file mode 100644 index 0000000000..acfa933def --- /dev/null +++ b/src/Mod/Fem/femsolver/calculix/write_step_output.py @@ -0,0 +1,79 @@ +# *************************************************************************** +# * Copyright (c) 2021 Bernd Hahnebach * +# * * +# * 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 calculix write inpfile step output" +__author__ = "Bernd Hahnebach" +__url__ = "https://www.freecadweb.org" + + +def write_step_output(f, ccxwriter): + + f.write("\n{}\n".format(59 * "*")) + f.write("** Outputs --> frd file\n") + if ( + ccxwriter.beamsection_objects + or ccxwriter.shellthickness_objects + or ccxwriter.fluidsection_objects + ): + if ccxwriter.solver_obj.BeamShellResultOutput3D is False: + f.write("*NODE FILE, OUTPUT=2d\n") + else: + f.write("*NODE FILE, OUTPUT=3d\n") + else: + f.write("*NODE FILE\n") + # MPH write out nodal temperatures if thermomechanical + if ccxwriter.analysis_type == "thermomech": + if not ccxwriter.fluidsection_objects: + f.write("U, NT\n") + else: + f.write("MF, PS\n") + else: + f.write("U\n") + if not ccxwriter.fluidsection_objects: + f.write("*EL FILE\n") + if ccxwriter.solver_obj.MaterialNonlinearity == "nonlinear": + f.write("S, E, PEEQ\n") + else: + f.write("S, E\n") + + # dat file + # reaction forces: freecadweb.org/tracker/view.php?id=2934 + if ccxwriter.fixed_objects: + f.write("** outputs --> dat file\n") + # reaction forces for all Constraint fixed + f.write("** reaction forces for Constraint fixed\n") + for femobj in ccxwriter.fixed_objects: + # femobj --> dict, FreeCAD document object is femobj["Object"] + fix_obj_name = femobj["Object"].Name + f.write("*NODE PRINT, NSET={}, TOTALS=ONLY\n".format(fix_obj_name)) + f.write("RF\n") + # TODO: add Constraint Displacement if nodes are restrained + f.write("\n") + + # there is no need to write all integration point results + # as long as there is no reader for them + # see https://forum.freecadweb.org/viewtopic.php?f=18&t=29060 + # f.write("*NODE PRINT , NSET=" + ccxwriter.ccx_nall + "\n") + # f.write("U \n") + # f.write("*EL PRINT , ELSET=" + ccxwriter.ccx_eall + "\n") + # f.write("S \n") diff --git a/src/Mod/Fem/femsolver/calculix/writer.py b/src/Mod/Fem/femsolver/calculix/writer.py index 00bc97117d..48e0b484ee 100644 --- a/src/Mod/Fem/femsolver/calculix/writer.py +++ b/src/Mod/Fem/femsolver/calculix/writer.py @@ -30,7 +30,6 @@ __url__ = "https://www.freecadweb.org" # @{ import codecs -import os import six import time from os.path import join @@ -53,6 +52,9 @@ from . import write_constraint_selfweight as con_selfweight from . import write_constraint_temperature as con_temperature from . import write_constraint_tie as con_tie from . import write_constraint_transform as con_transform +from . import write_footer +from . import write_step_equation +from . import write_step_output from .. import writerbase from femmesh import meshtools from femtools import constants @@ -111,6 +113,7 @@ class FemInputWriterCcx(writerbase.FemInputWriter): self.file_name = self.include + ".inp" self.femmesh_file = "" # the file the femmesh is in, no matter if one or split input file self.gravity = int(Units.Quantity(constants.gravity()).getValueAs("mm/s^2")) # 9820 mm/s2 + self.units_information = units_information # ******************************************************************************************** # write calculix input @@ -193,8 +196,8 @@ class FemInputWriterCcx(writerbase.FemInputWriter): self.write_constraints_data(inpfile, self.tie_objects, con_tie) self.write_constraints_data(inpfile, self.transform_objects, con_transform) - # step begin - self.write_step_begin(inpfile) + # step equation + write_step_equation.write_step_equation(inpfile, self) # constraints dependent from steps self.write_constraints_data(inpfile, self.fixed_objects, con_fixed) @@ -209,11 +212,11 @@ class FemInputWriterCcx(writerbase.FemInputWriter): con_fluidsection.write_constraints_fluidsection(inpfile, self) # output and step end - self.write_outputs_types(inpfile) - self.write_step_end(inpfile) + write_step_output.write_step_output(inpfile, self) + write_step_equation.write_step_end(inpfile, self) # footer - self.write_footer(inpfile) + write_footer.write_footer(inpfile, self) inpfile.close() # ******************************************************************************************** @@ -337,198 +340,6 @@ class FemInputWriterCcx(writerbase.FemInputWriter): if write_after != "": f.write(write_after) - # ******************************************************************************************** - # step begin and end - def write_step_begin(self, f): - f.write("\n***********************************************************\n") - f.write("** At least one step is needed to run an CalculiX analysis of FreeCAD\n") - # STEP line - step = "*STEP" - if self.solver_obj.GeometricalNonlinearity == "nonlinear": - if self.analysis_type == "static" or self.analysis_type == "thermomech": - # https://www.comsol.com/blogs/what-is-geometric-nonlinearity - step += ", NLGEOM" - elif self.analysis_type == "frequency": - FreeCAD.Console.PrintMessage( - "Analysis type frequency and geometrical nonlinear " - "analysis are not allowed together, linear is used instead!\n" - ) - if self.solver_obj.IterationsThermoMechMaximum: - if self.analysis_type == "thermomech": - step += ", INC={}".format(self.solver_obj.IterationsThermoMechMaximum) - elif ( - self.analysis_type == "static" - or self.analysis_type == "frequency" - or self.analysis_type == "buckling" - ): - # parameter is for thermomechanical analysis only, see ccx manual *STEP - pass - # write step line - f.write(step + "\n") - # CONTROLS line - # all analysis types, ... really in frequency too?!? - if self.solver_obj.IterationsControlParameterTimeUse: - f.write("*CONTROLS, PARAMETERS=TIME INCREMENTATION\n") - f.write(self.solver_obj.IterationsControlParameterIter + "\n") - f.write(self.solver_obj.IterationsControlParameterCutb + "\n") - # ANALYSIS type line - # analysis line --> analysis type - if self.analysis_type == "static": - analysis_type = "*STATIC" - elif self.analysis_type == "frequency": - analysis_type = "*FREQUENCY" - elif self.analysis_type == "thermomech": - analysis_type = "*COUPLED TEMPERATURE-DISPLACEMENT" - elif self.analysis_type == "check": - analysis_type = "*NO ANALYSIS" - elif self.analysis_type == "buckling": - analysis_type = "*BUCKLE" - # analysis line --> solver type - # https://forum.freecadweb.org/viewtopic.php?f=18&t=43178 - if self.solver_obj.MatrixSolverType == "default": - pass - elif self.solver_obj.MatrixSolverType == "spooles": - analysis_type += ", SOLVER=SPOOLES" - elif self.solver_obj.MatrixSolverType == "iterativescaling": - analysis_type += ", SOLVER=ITERATIVE SCALING" - elif self.solver_obj.MatrixSolverType == "iterativecholesky": - analysis_type += ", SOLVER=ITERATIVE CHOLESKY" - # analysis line --> user defined incrementations --> parameter DIRECT - # --> completely switch off ccx automatic incrementation - if self.solver_obj.IterationsUserDefinedIncrementations: - if self.analysis_type == "static": - analysis_type += ", DIRECT" - elif self.analysis_type == "thermomech": - analysis_type += ", DIRECT" - elif self.analysis_type == "frequency": - FreeCAD.Console.PrintMessage( - "Analysis type frequency and IterationsUserDefinedIncrementations " - "are not allowed together, it is ignored\n" - ) - # analysis line --> steadystate --> thermomech only - if self.solver_obj.ThermoMechSteadyState: - # bernd: I do not know if STEADY STATE is allowed with DIRECT - # but since time steps are 1.0 it makes no sense IMHO - if self.analysis_type == "thermomech": - analysis_type += ", STEADY STATE" - # Set time to 1 and ignore user inputs for steady state - self.solver_obj.TimeInitialStep = 1.0 - self.solver_obj.TimeEnd = 1.0 - elif ( - self.analysis_type == "static" - or self.analysis_type == "frequency" - or self.analysis_type == "buckling" - ): - pass # not supported for static and frequency! - # ANALYSIS parameter line - analysis_parameter = "" - if self.analysis_type == "static" or self.analysis_type == "check": - if self.solver_obj.IterationsUserDefinedIncrementations is True \ - or self.solver_obj.IterationsUserDefinedTimeStepLength is True: - analysis_parameter = "{},{}".format( - self.solver_obj.TimeInitialStep, - self.solver_obj.TimeEnd - ) - elif self.analysis_type == "frequency": - if self.solver_obj.EigenmodeLowLimit == 0.0 \ - and self.solver_obj.EigenmodeHighLimit == 0.0: - analysis_parameter = "{}\n".format(self.solver_obj.EigenmodesCount) - else: - analysis_parameter = "{},{},{}\n".format( - self.solver_obj.EigenmodesCount, - self.solver_obj.EigenmodeLowLimit, - self.solver_obj.EigenmodeHighLimit - ) - elif self.analysis_type == "thermomech": - # OvG: 1.0 increment, total time 1 for steady state will cut back automatically - analysis_parameter = "{},{}".format( - self.solver_obj.TimeInitialStep, - self.solver_obj.TimeEnd - ) - elif self.analysis_type == "buckling": - analysis_parameter = "{}\n".format(self.solver_obj.BucklingFactors) - - # write analysis type line, analysis parameter line - f.write(analysis_type + "\n") - f.write(analysis_parameter + "\n") - - def write_step_end(self, f): - f.write("\n***********************************************************\n") - f.write("*END STEP \n") - - # ******************************************************************************************** - # output types - def write_outputs_types(self, f): - f.write("\n***********************************************************\n") - f.write("** Outputs --> frd file\n") - if self.beamsection_objects or self.shellthickness_objects or self.fluidsection_objects: - if self.solver_obj.BeamShellResultOutput3D is False: - f.write("*NODE FILE, OUTPUT=2d\n") - else: - f.write("*NODE FILE, OUTPUT=3d\n") - else: - f.write("*NODE FILE\n") - # MPH write out nodal temperatures if thermomechanical - if self.analysis_type == "thermomech": - if not self.fluidsection_objects: - f.write("U, NT\n") - else: - f.write("MF, PS\n") - else: - f.write("U\n") - if not self.fluidsection_objects: - f.write("*EL FILE\n") - if self.solver_obj.MaterialNonlinearity == "nonlinear": - f.write("S, E, PEEQ\n") - else: - f.write("S, E\n") - - # dat file - # reaction forces: freecadweb.org/tracker/view.php?id=2934 - if self.fixed_objects: - f.write("** outputs --> dat file\n") - # reaction forces for all Constraint fixed - f.write("** reaction forces for Constraint fixed\n") - for femobj in self.fixed_objects: - # femobj --> dict, FreeCAD document object is femobj["Object"] - fix_obj_name = femobj["Object"].Name - f.write("*NODE PRINT, NSET={}, TOTALS=ONLY\n".format(fix_obj_name)) - f.write("RF\n") - # TODO: add Constraint Displacement if nodes are restrained - f.write("\n") - - # there is no need to write all integration point results - # as long as there is no reader for them - # see https://forum.freecadweb.org/viewtopic.php?f=18&t=29060 - # f.write("*NODE PRINT , NSET=" + self.ccx_nall + "\n") - # f.write("U \n") - # f.write("*EL PRINT , ELSET=" + self.ccx_eall + "\n") - # f.write("S \n") - - # ******************************************************************************************** - # footer - def write_footer(self, f): - f.write("\n***********************************************************\n") - f.write("** CalculiX Input file\n") - f.write("** written by --> FreeCAD {}.{}.{}\n".format( - self.fc_ver[0], - self.fc_ver[1], - self.fc_ver[2] - )) - f.write("** written on --> {}\n".format( - time.ctime() - )) - f.write("** file name --> {}\n".format( - os.path.basename(self.document.FileName) - )) - f.write("** analysis name --> {}\n".format( - self.analysis.Name - )) - f.write("**\n") - f.write("**\n") - f.write(units_information) - f.write("**\n") - # ******************************************************************************************** # material and fem element geometry # def write_element_sets_material_and_femelement_geometry(self, f):