443 lines
15 KiB
Python
443 lines
15 KiB
Python
# ***************************************************************************
|
|
# * Copyright (c) 2017 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 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.
|
|
"""
|