# *************************************************************************** # * Copyright (c) 2017 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 solver object CalculiX" __author__ = "Bernd Hahnebach" __url__ = "https://www.freecad.org" ## @package SolverCalculix # \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", "frequency", "thermomech", "check", "buckling"] def create(doc, name="SolverCalculiX"): return femutils.createObject( doc, name, Proxy, ViewProxy) class Proxy(solverbase.Proxy): """The Fem::FemSolver's Proxy python type, add solver specific properties """ Type = "Fem::SolverCalculix" def __init__(self, obj): super(Proxy, self).__init__(obj) obj.Proxy = self ccx_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Ccx") add_attributes(obj, ccx_prefs) def onDocumentRestored(self, obj): ccx_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Ccx") # since it is needed for the ccxtools solver too # the method is implemented outside of the class # thus we need to pass the prefs on_restore_of_document(obj, ccx_prefs) 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, "*.inp") FreeCAD.Console.PrintMessage("{}\n".format(pattern)) f = glob.glob(pattern)[0] FemGui.open(f) def execute(self, obj): return class ViewProxy(solverbase.ViewProxy): pass # ************************************************************************************************ # helper # these methods are outside of the class to be able # to use them from framework solver and ccxtools solver def on_restore_of_document(obj, ccx_prefs): # ANALYSIS_TYPES # They have been extended. If file was saved with a old FC version # not all enum types are available, because they are saved in the FC file # thus refresh the list of known ANALYSIS_TYPES # print("onRestoredFromSuperClass") # print(obj.AnalysisType) # print(obj.getEnumerationsOfProperty("AnalysisType")) temp_analysis_type = obj.AnalysisType # self.add_properties(obj) obj.AnalysisType = ANALYSIS_TYPES if temp_analysis_type in ANALYSIS_TYPES: obj.AnalysisType = temp_analysis_type else: FreeCAD.Console.PrintWarning( "Analysis type {} not found. Standard is used.\n" .format(temp_analysis_type) ) analysis_type = ccx_prefs.GetInt("AnalysisType", 0) obj.AnalysisType = ANALYSIS_TYPES[analysis_type] # add missing properties # for example BucklingFactors will be added # for all files created before buckle analysis was introduced add_attributes(obj, ccx_prefs) def add_attributes(obj, ccx_prefs): if not hasattr(obj, "AnalysisType"): obj.addProperty( "App::PropertyEnumeration", "AnalysisType", "Fem", "Type of the analysis" ) obj.AnalysisType = ANALYSIS_TYPES analysis_type = ccx_prefs.GetInt("AnalysisType", 0) obj.AnalysisType = ANALYSIS_TYPES[analysis_type] if not hasattr(obj, "GeometricalNonlinearity"): choices_geom_nonlinear = ["linear", "nonlinear"] obj.addProperty( "App::PropertyEnumeration", "GeometricalNonlinearity", "Fem", "Set geometrical nonlinearity" ) obj.GeometricalNonlinearity = choices_geom_nonlinear nonlinear_geom = ccx_prefs.GetBool("NonlinearGeometry", False) if nonlinear_geom is True: obj.GeometricalNonlinearity = choices_geom_nonlinear[1] # nonlinear else: obj.GeometricalNonlinearity = choices_geom_nonlinear[0] # linear if not hasattr(obj, "MaterialNonlinearity"): choices_material_nonlinear = ["linear", "nonlinear"] obj.addProperty( "App::PropertyEnumeration", "MaterialNonlinearity", "Fem", "Set material nonlinearity" ) obj.MaterialNonlinearity = choices_material_nonlinear obj.MaterialNonlinearity = choices_material_nonlinear[0] if not hasattr(obj, "EigenmodesCount"): obj.addProperty( "App::PropertyIntegerConstraint", "EigenmodesCount", "Fem", "Number of modes for frequency calculations" ) noem = ccx_prefs.GetInt("EigenmodesCount", 10) obj.EigenmodesCount = (noem, 1, 100, 1) if not hasattr(obj, "EigenmodeLowLimit"): obj.addProperty( "App::PropertyFloatConstraint", "EigenmodeLowLimit", "Fem", "Low frequency limit for eigenmode calculations" ) ell = ccx_prefs.GetFloat("EigenmodeLowLimit", 0.0) obj.EigenmodeLowLimit = (ell, 0.0, 1000000.0, 10000.0) if not hasattr(obj, "EigenmodeHighLimit"): obj.addProperty( "App::PropertyFloatConstraint", "EigenmodeHighLimit", "Fem", "High frequency limit for eigenmode calculations" ) ehl = ccx_prefs.GetFloat("EigenmodeHighLimit", 1000000.0) obj.EigenmodeHighLimit = (ehl, 0.0, 1000000.0, 10000.0) if not hasattr(obj, "IterationsMaximum"): help_string_IterationsMaximum = ( "Maximum Number of iterations " "in each time step before stopping jobs" ) obj.addProperty( "App::PropertyIntegerConstraint", "IterationsMaximum", "Fem", help_string_IterationsMaximum ) niter = ccx_prefs.GetInt("AnalysisMaxIterations", 200) obj.IterationsMaximum = niter if hasattr(obj, "IterationsThermoMechMaximum"): obj.IterationsMaximum = obj.IterationsThermoMechMaximum obj.removeProperty("IterationsThermoMechMaximum") if not hasattr(obj, "BucklingFactors"): obj.addProperty( "App::PropertyIntegerConstraint", "BucklingFactors", "Fem", "Calculates the lowest buckling modes to the corresponding buckling factors" ) bckl = ccx_prefs.GetInt("BucklingFactors", 1) obj.BucklingFactors = bckl if not hasattr(obj, "TimeInitialStep"): obj.addProperty( "App::PropertyFloatConstraint", "TimeInitialStep", "Fem", "Initial time steps" ) ini = ccx_prefs.GetFloat("AnalysisTimeInitialStep", 1.0) obj.TimeInitialStep = ini if not hasattr(obj, "TimeEnd"): obj.addProperty( "App::PropertyFloatConstraint", "TimeEnd", "Fem", "End time analysis" ) eni = ccx_prefs.GetFloat("AnalysisTime", 1.0) obj.TimeEnd = eni if not hasattr(obj, "TimeMinimumStep"): obj.addProperty( "App::PropertyFloatConstraint", "TimeMinimumStep", "Fem", "Minimum time step" ) mini = ccx_prefs.GetFloat("AnalysisTimeMinimumStep", 0.00001) obj.TimeMinimumStep = mini if not hasattr(obj, "TimeMaximumStep"): obj.addProperty( "App::PropertyFloatConstraint", "TimeMaximumStep", "Fem", "Maximum time step" ) maxi = ccx_prefs.GetFloat("AnalysisTimeMaximumStep", 1.0) obj.TimeMaximumStep = maxi if not hasattr(obj, "ThermoMechSteadyState"): obj.addProperty( "App::PropertyBool", "ThermoMechSteadyState", "Fem", "Choose between steady state thermo mech or transient thermo mech analysis" ) sted = ccx_prefs.GetBool("StaticAnalysis", True) obj.ThermoMechSteadyState = sted if not hasattr(obj, "IterationsControlParameterTimeUse"): obj.addProperty( "App::PropertyBool", "IterationsControlParameterTimeUse", "Fem", "Use the user defined time incrementation control parameter" ) use_non_ccx_iterations_param = ccx_prefs.GetInt("UseNonCcxIterationParam", False) obj.IterationsControlParameterTimeUse = use_non_ccx_iterations_param if not hasattr(obj, "SplitInputWriter"): obj.addProperty( "App::PropertyBool", "SplitInputWriter", "Fem", "Split writing of ccx input file" ) split = ccx_prefs.GetBool("SplitInputWriter", False) obj.SplitInputWriter = split if not hasattr(obj, "IterationsControlParameterIter"): control_parameter_iterations = ( "{I_0},{I_R},{I_P},{I_C},{I_L},{I_G},{I_S},{I_A},{I_J},{I_T}".format( I_0=4, I_R=8, I_P=9, I_C=200, # ccx default = 16 I_L=10, I_G=400, # ccx default = 4 I_S="", I_A=200, # ccx default = 5 I_J="", I_T="", ) ) obj.addProperty( "App::PropertyString", "IterationsControlParameterIter", "Fem", "User defined time incrementation iterations control parameter" ) obj.IterationsControlParameterIter = control_parameter_iterations if not hasattr(obj, "IterationsControlParameterCutb"): control_parameter_cutback = ( "{D_f},{D_C},{D_B},{D_A},{D_S},{D_H},{D_D},{W_G}".format( D_f=0.25, D_C=0.5, D_B=0.75, D_A=0.85, D_S="", D_H="", D_D=1.5, W_G="", ) ) obj.addProperty( "App::PropertyString", "IterationsControlParameterCutb", "Fem", "User defined time incrementation cutbacks control parameter" ) obj.IterationsControlParameterCutb = control_parameter_cutback if not hasattr(obj, "IterationsUserDefinedIncrementations"): stringIterationsUserDefinedIncrementations = ( "Set to True to switch off the ccx automatic incrementation completely " "(ccx parameter DIRECT). Use with care. Analysis may not converge!" ) obj.addProperty( "App::PropertyBool", "IterationsUserDefinedIncrementations", "Fem", stringIterationsUserDefinedIncrementations ) obj.IterationsUserDefinedIncrementations = False if not hasattr(obj, "IterationsUserDefinedTimeStepLength"): help_string_IterationsUserDefinedTimeStepLength = ( "Set to True to use the user defined time steps. " "They are set with TimeInitialStep, TimeEnd, TimeMinimum and TimeMaximum" ) obj.addProperty( "App::PropertyBool", "IterationsUserDefinedTimeStepLength", "Fem", help_string_IterationsUserDefinedTimeStepLength ) obj.IterationsUserDefinedTimeStepLength = False if not hasattr(obj, "MatrixSolverType"): known_ccx_solver_types = [ "default", "pastix", "pardiso", "spooles", "iterativescaling", "iterativecholesky" ] obj.addProperty( "App::PropertyEnumeration", "MatrixSolverType", "Fem", "Type of solver to use" ) obj.MatrixSolverType = known_ccx_solver_types solver_type = ccx_prefs.GetInt("Solver", 0) obj.MatrixSolverType = known_ccx_solver_types[solver_type] if not hasattr(obj, "BeamShellResultOutput3D"): obj.addProperty( "App::PropertyBool", "BeamShellResultOutput3D", "Fem", "Output 3D results for 1D and 2D analysis " ) dimout = ccx_prefs.GetBool("BeamShellOutput", True) obj.BeamShellResultOutput3D = dimout if not hasattr(obj, "BeamReducedIntegration"): obj.addProperty( "App::PropertyBool", "BeamReducedIntegration", "Fem", "Set to True to use beam elements with reduced integration" ) obj.BeamReducedIntegration = True if not hasattr(obj, "OutputFrequency"): obj.addProperty( "App::PropertyIntegerConstraint", "OutputFrequency", "Fem", "Set the output frequency in increments" ) obj.OutputFrequency = 1 if not hasattr(obj, "ModelSpace"): model_space_types = [ "3D", "plane stress", "plane strain", "axisymmetric" ] obj.addProperty( "App::PropertyEnumeration", "ModelSpace", "Fem", "Type of model space" ) obj.ModelSpace = model_space_types """ Should there be some equation object for Calculix too? Necessarily yes! The properties GeometricalNonlinearity, MaterialNonlinearity, ThermoMechSteadyState might be moved to the appropriate equation. Furthermore the material Category should not be used in writer. See common material object for more information. The equation should used instead to get this information needed in writer. """