diff --git a/src/Mod/Fem/femobjects/solver_calculix.py b/src/Mod/Fem/femobjects/solver_calculix.py index d7b74f4b7f..6405da800d 100644 --- a/src/Mod/Fem/femobjects/solver_calculix.py +++ b/src/Mod/Fem/femobjects/solver_calculix.py @@ -31,16 +31,294 @@ __url__ = "https://www.freecad.org" # \brief solver CalculiX object from . import base_fempythonobject -from femsolver.calculix.solver import _BaseSolverCalculix + +_PropHelper = base_fempythonobject._PropHelper -class SolverCalculiX(base_fempythonobject.BaseFemPythonObject, _BaseSolverCalculix): +class SolverCalculiX(base_fempythonobject.BaseFemPythonObject): Type = "Fem::SolverCalculiX" def __init__(self, obj): super().__init__(obj) - self.add_attributes(obj) - def onDocumentRestored(self, obj): - self.on_restore_of_document(obj) + for prop in self._get_properties(): + prop.add_to_object(obj) + + def _get_properties(self): + prop = [] + + prop.append( + _PropHelper( + type="App::PropertyEnumeration", + name="AnalysisType", + group="Solver", + doc="Type of the analysis", + value=["static", "frequency", "thermomech", "check", "buckling", "electromagnetic"], + ) + ) + prop.append( + _PropHelper( + type="App::PropertyEnumeration", + name="GeometricalNonlinearity", + group="Solver", + doc="Set geometrical nonlinearity", + value=["linear", "nonlinear"], + ) + ) + prop.append( + _PropHelper( + type="App::PropertyEnumeration", + name="MaterialNonlinearity", + group="Solver", + doc="Set material nonlinearity", + value=["linear", "nonlinear"], + ) + ) + prop.append( + _PropHelper( + type="App::PropertyIntegerConstraint", + name="EigenmodesCount", + group="Solver", + doc="Number of modes for frequency calculations", + value=(10, 1, 100, 1), + ) + ) + prop.append( + _PropHelper( + type="App::PropertyFrequency", + name="EigenmodeLowLimit", + group="Solver", + doc="Low frequency limit for eigenmode calculations", + value=0.0, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyFrequency", + name="EigenmodeHighLimit", + group="Solver", + doc="High frequency limit for eigenmode calculations", + value=1000000.0, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyIntegerConstraint", + name="IterationsMaximum", + group="Solver", + doc="Maximum Number of iterations in each time step before stopping jobs", + value=2000, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyIntegerConstraint", + name="BucklingFactors", + group="Solver", + doc="Calculates the lowest buckling modes to the corresponding buckling factors", + value=1, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyTime", + name="TimeInitialStep", + group="Solver", + doc="Initial time steps", + value=0.01, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyTime", + name="TimeEnd", + group="Solver", + doc="End time analysis", + value=1.0, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyTime", + name="TimeMinimumStep", + group="Solver", + doc="Minimum time step", + value=0.00001, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyTime", + name="TimeMaximumStep", + group="Solver", + doc="Maximum time step", + value=1.0, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyBool", + name="ThermoMechSteadyState", + group="Solver", + doc="Choose between steady state thermo mech or transient thermo mech analysis", + value=True, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyBool", + name="IterationsControlParameterTimeUse", + group="Solver", + doc="Use the user defined time incrementation control parameter", + value=False, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyBool", + name="SplitInputWriter", + group="Solver", + doc="Split writing of ccx input file", + value=False, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyString", + name="IterationsControlParameterIter", + group="Solver", + doc="User defined time incrementation iterations control parameter", + value="{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="", + ), + ) + ) + prop.append( + _PropHelper( + type="App::PropertyString", + name="IterationsControlParameterCutb", + group="Solver", + doc="User defined time incrementation cutbacks control parameter", + value="{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="", + ), + ) + ) + prop.append( + _PropHelper( + type="App::PropertyBool", + name="IterationsUserDefinedIncrementations", + group="Solver", + doc="Set to True to switch off the ccx automatic incrementation completely\n" + + "(ccx parameter DIRECT). Use with care. Analysis may not converge!", + value=False, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyBool", + name="IterationsUserDefinedTimeStepLength", + group="Solver", + doc="Set to True to use the user defined time steps.\n" + + "They are set with TimeInitialStep, TimeEnd, TimeMinimum and TimeMaximum", + value=False, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyEnumeration", + name="MatrixSolverType", + group="Solver", + doc="Type of solver to use", + value=[ + "default", + "pastix", + "pardiso", + "spooles", + "iterativescaling", + "iterativecholesky", + ], + ) + ) + prop.append( + _PropHelper( + type="App::PropertyBool", + name="BeamShellResultOutput3D", + group="Solver", + doc="Output 3D results for 1D and 2D analysis ", + value=True, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyBool", + name="BeamReducedIntegration", + group="Solver", + doc="Set to True to use beam elements with reduced integration", + value=True, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyIntegerConstraint", + name="OutputFrequency", + group="Solver", + doc="Set the output frequency in increments", + value=1, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyEnumeration", + name="ModelSpace", + group="Solver", + doc="Type of model space", + value=["3D", "plane stress", "plane strain", "axisymmetric"], + ) + ) + prop.append( + _PropHelper( + type="App::PropertyEnumeration", + name="ThermoMechType", + group="Solver", + doc="Type of thermomechanical analysis", + value=["coupled", "uncoupled", "pure heat transfer"], + ) + ) + prop.append( + _PropHelper( + type="App::PropertyFloatConstraint", + name="BucklingAccuracy", + group="Solver", + doc="Accuracy for buckling analysis", + value=0.01, + ) + ) + prop.append( + _PropHelper( + type="App::PropertyEnumeration", + name="ElectromagneticMode", + group="Solver", + doc="Electromagnetic mode", + value=["electrostatic"], + ) + ) + + return prop diff --git a/src/Mod/Fem/femsolver/calculix/solver.py b/src/Mod/Fem/femsolver/calculix/solver.py index ff18bb01b8..b589f206c1 100644 --- a/src/Mod/Fem/femsolver/calculix/solver.py +++ b/src/Mod/Fem/femsolver/calculix/solver.py @@ -109,25 +109,43 @@ class _BaseSolverCalculix: ) obj.EigenmodesCount = (10, 1, 100, 1) + low_limit = 0.0 + if ( + hasattr(obj, "EigenmodeLowLimit") + and obj.getTypeIdOfProperty("EigenmodeLowLimit") == "App::PropertyFloatConstraint" + ): + low_limit = obj.EigenmodeLowLimit + obj.setPropertyStatus("EigenmodeLowLimit", "-LockDynamic") + obj.removeProperty("EigenmodeLowLimit") + if not hasattr(obj, "EigenmodeLowLimit"): obj.addProperty( - "App::PropertyFloatConstraint", + "App::PropertyFrequency", "EigenmodeLowLimit", "Fem", "Low frequency limit for eigenmode calculations", locked=True, ) - obj.EigenmodeLowLimit = (0.0, 0.0, 1000000.0, 10000.0) + obj.EigenmodeLowLimit = low_limit + + high_limit = 1000000.0 + if ( + hasattr(obj, "EigenmodeHighLimit") + and obj.getTypeIdOfProperty("EigenmodeHighLimit") == "App::PropertyFloatConstraint" + ): + high_limit = obj.EigenmodeHighLimit + obj.setPropertyStatus("EigenmodeHighLimit", "-LockDynamic") + obj.removeProperty("EigenmodeHighLimit") if not hasattr(obj, "EigenmodeHighLimit"): obj.addProperty( - "App::PropertyFloatConstraint", + "App::PropertyFrequency", "EigenmodeHighLimit", "Fem", "High frequency limit for eigenmode calculations", locked=True, ) - obj.EigenmodeHighLimit = (1000000.0, 0.0, 1000000.0, 10000.0) + obj.EigenmodeHighLimit = high_limit if not hasattr(obj, "IterationsMaximum"): help_string_IterationsMaximum = ( @@ -156,41 +174,75 @@ class _BaseSolverCalculix: ) obj.BucklingFactors = 1 + time_initial_step = 0.01 + if ( + hasattr(obj, "TimeInitialStep") + and obj.getTypeIdOfProperty("TimeInitialStep") == "App::PropertyFloatConstraint" + ): + time_initial_step = obj.TimeInitialStep + obj.setPropertyStatus("TimeInitialStep", "-LockDynamic") + obj.removeProperty("TimeInitialStep") + if not hasattr(obj, "TimeInitialStep"): obj.addProperty( - "App::PropertyFloatConstraint", + "App::PropertyTime", "TimeInitialStep", "Fem", "Initial time steps", locked=True, ) - obj.TimeInitialStep = 0.01 + obj.TimeInitialStep = time_initial_step + + time_end = 1.0 + if ( + hasattr(obj, "TimeEnd") + and obj.getTypeIdOfProperty("TimeEnd") == "App::PropertyFloatConstraint" + ): + time_end = obj.TimeEnd + obj.setPropertyStatus("TimeEnd", "-LockDynamic") + obj.removeProperty("TimeEnd") if not hasattr(obj, "TimeEnd"): - obj.addProperty( - "App::PropertyFloatConstraint", "TimeEnd", "Fem", "End time analysis", locked=True - ) - obj.TimeEnd = 1.0 + obj.addProperty("App::PropertyTime", "TimeEnd", "Fem", "End time analysis", locked=True) + obj.TimeEnd = time_end + + time_minimum_step = 0.00001 + if ( + hasattr(obj, "TimeMinimumStep") + and obj.getTypeIdOfProperty("TimeMinimumStep") == "App::PropertyFloatConstraint" + ): + time_minimum_step = obj.TimeMinimumStep + obj.setPropertyStatus("TimeMinimumStep", "-LockDynamic") + obj.removeProperty("TimeMinimumStep") if not hasattr(obj, "TimeMinimumStep"): obj.addProperty( - "App::PropertyFloatConstraint", + "App::PropertyTime", "TimeMinimumStep", "Fem", "Minimum time step", locked=True, ) - obj.TimeMinimumStep = 0.00001 + obj.TimeMinimumStep = time_minimum_step + + time_maximum_step = 1.0 + if ( + hasattr(obj, "TimeMaximumStep") + and obj.getTypeIdOfProperty("TimeMaximumStep") == "App::PropertyFloatConstraint" + ): + time_maximum_step = obj.TimeMaximumStep + obj.setPropertyStatus("TimeMaximumStep", "-LockDynamic") + obj.removeProperty("TimeMaximumStep") if not hasattr(obj, "TimeMaximumStep"): obj.addProperty( - "App::PropertyFloatConstraint", + "App::PropertyTime", "TimeMaximumStep", "Fem", "Maximum time step", locked=True, ) - obj.TimeMaximumStep = 1.0 + obj.TimeMaximumStep = time_maximum_step if not hasattr(obj, "ThermoMechSteadyState"): obj.addProperty( diff --git a/src/Mod/Fem/femsolver/calculix/write_step_equation.py b/src/Mod/Fem/femsolver/calculix/write_step_equation.py index 1bce3a9623..5c57dfb27a 100644 --- a/src/Mod/Fem/femsolver/calculix/write_step_equation.py +++ b/src/Mod/Fem/femsolver/calculix/write_step_equation.py @@ -128,10 +128,10 @@ def write_step_equation(f, ccxwriter): or ccxwriter.solver_obj.IterationsUserDefinedTimeStepLength is True ): analysis_parameter = "{},{},{},{}".format( - ccxwriter.solver_obj.TimeInitialStep, - ccxwriter.solver_obj.TimeEnd, - ccxwriter.solver_obj.TimeMinimumStep, - ccxwriter.solver_obj.TimeMaximumStep, + ccxwriter.solver_obj.TimeInitialStep.getValueAs("s").Value, + ccxwriter.solver_obj.TimeEnd.getValueAs("s").Value, + ccxwriter.solver_obj.TimeMinimumStep.getValueAs("s").Value, + ccxwriter.solver_obj.TimeMaximumStep.getValueAs("s").Value, ) elif ccxwriter.analysis_type == "frequency": if ( @@ -142,16 +142,16 @@ def write_step_equation(f, ccxwriter): else: analysis_parameter = "{},{},{}\n".format( ccxwriter.solver_obj.EigenmodesCount, - ccxwriter.solver_obj.EigenmodeLowLimit, - ccxwriter.solver_obj.EigenmodeHighLimit, + ccxwriter.solver_obj.EigenmodeLowLimit.getValueAs("Hz").Value, + ccxwriter.solver_obj.EigenmodeHighLimit.getValueAs("Hz").Value, ) 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, - ccxwriter.solver_obj.TimeMinimumStep, - ccxwriter.solver_obj.TimeMaximumStep, + ccxwriter.solver_obj.TimeInitialStep.getValueAs("s").Value, + ccxwriter.solver_obj.TimeEnd.getValueAs("s").Value, + ccxwriter.solver_obj.TimeMinimumStep.getValueAs("s").Value, + ccxwriter.solver_obj.TimeMaximumStep.getValueAs("s").Value, ) elif ccxwriter.analysis_type == "buckling": analysis_parameter = "{},{}".format(