diff --git a/src/Mod/Fem/App/CMakeLists.txt b/src/Mod/Fem/App/CMakeLists.txt index e1a0d689bf..84ab61850a 100644 --- a/src/Mod/Fem/App/CMakeLists.txt +++ b/src/Mod/Fem/App/CMakeLists.txt @@ -126,6 +126,12 @@ SET(FemSolver_SRCS femsolver/signal.py ) +SET(FemCalculix_SRCS + femsolver/calculix/__init__.py + femsolver/calculix/solver.py + femsolver/calculix/tasks.py +) + SET(FemZ88_SRCS femsolver/z88/__init__.py femsolver/z88/solver.py @@ -335,6 +341,7 @@ fc_target_copy_resource(Fem ${FemGuiScripts_SRCS} ${FemTests_SRCS} ${FemSolver_SRCS} + ${FemCalculix_SRCS} ${FemZ88_SRCS} ) diff --git a/src/Mod/Fem/CMakeLists.txt b/src/Mod/Fem/CMakeLists.txt index a173868b03..0f40d9245b 100755 --- a/src/Mod/Fem/CMakeLists.txt +++ b/src/Mod/Fem/CMakeLists.txt @@ -81,6 +81,15 @@ INSTALL( Mod/Fem/femsolver ) +INSTALL( + FILES + femsolver/calculix/__init__.py + femsolver/calculix/solver.py + femsolver/calculix/tasks.py + DESTINATION + Mod/Fem/femsolver/calculix +) + INSTALL( FILES femsolver/z88/__init__.py diff --git a/src/Mod/Fem/ObjectsFem.py b/src/Mod/Fem/ObjectsFem.py index d4829673cd..6ff4000e45 100644 --- a/src/Mod/Fem/ObjectsFem.py +++ b/src/Mod/Fem/ObjectsFem.py @@ -310,8 +310,8 @@ def makeResultMechanical(doc, name="MechanicalResult"): ########## solver objects ########## -def makeSolverCalculix(doc, name="CalculiX"): - '''makeSolverCalculix(document, [name]): makes a Calculix solver object''' +def makeSolverCalculixOld(doc, name="CalculiXOld"): + '''makeSolverCalculixOld(document, [name]): makes a depreciated Calculix solver object''' obj = doc.addObject("Fem::FemSolverObjectPython", name) import PyObjects._FemSolverCalculix PyObjects._FemSolverCalculix._FemSolverCalculix(obj) @@ -321,6 +321,13 @@ def makeSolverCalculix(doc, name="CalculiX"): return obj +def makeSolverCalculix(doc, name="SolverCalculiX"): + '''makeSolverCalculix(document, [name]): makes a Calculix solver object''' + import femsolver.calculix.solver + obj = femsolver.calculix.solver.create(doc, name) + return obj + + def makeSolverZ88(doc, name="SolverZ88"): '''makeSolverZ88(document, [name]): makes a Z88 solver object''' import femsolver.z88.solver diff --git a/src/Mod/Fem/PyGui/_CommandFemAnalysis.py b/src/Mod/Fem/PyGui/_CommandFemAnalysis.py index 48def5f490..3eca4796f1 100644 --- a/src/Mod/Fem/PyGui/_CommandFemAnalysis.py +++ b/src/Mod/Fem/PyGui/_CommandFemAnalysis.py @@ -49,7 +49,13 @@ class _CommandFemAnalysis(FemCommands): FreeCADGui.addModule("ObjectsFem") FreeCADGui.doCommand("ObjectsFem.makeAnalysis(FreeCAD.ActiveDocument, 'Analysis')") FreeCADGui.doCommand("FemGui.setActiveAnalysis(FreeCAD.ActiveDocument.ActiveObject)") - FreeCADGui.doCommand("ObjectsFem.makeSolverCalculix(FreeCAD.ActiveDocument, 'CalculiX')") + ccx_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Ccx") + use_old_solver_frame_work = ccx_prefs.GetBool("useOldSolverFrameWork", False) + use_new_solver_frame_work = ccx_prefs.GetBool("useNewSolverFrameWork", True) + if use_old_solver_frame_work and not use_new_solver_frame_work: + FreeCADGui.doCommand("ObjectsFem.makeSolverCalculixOld(FreeCAD.ActiveDocument)") + else: + FreeCADGui.doCommand("ObjectsFem.makeSolverCalculix(FreeCAD.ActiveDocument)") FreeCADGui.doCommand("FemGui.getActiveAnalysis().addObject(FreeCAD.ActiveDocument.ActiveObject)") diff --git a/src/Mod/Fem/PyGui/_CommandFemSolverCalculix.py b/src/Mod/Fem/PyGui/_CommandFemSolverCalculix.py index 2507a12630..49ab731922 100644 --- a/src/Mod/Fem/PyGui/_CommandFemSolverCalculix.py +++ b/src/Mod/Fem/PyGui/_CommandFemSolverCalculix.py @@ -45,19 +45,34 @@ class _CommandFemSolverCalculix(FemCommands): self.is_active = 'with_analysis' def Activated(self): - has_nonlinear_material_obj = False - for m in FemGui.getActiveAnalysis().Group: - if hasattr(m, "Proxy") and m.Proxy.Type == "FemMaterialMechanicalNonlinear": - has_nonlinear_material_obj = True - FreeCAD.ActiveDocument.openTransaction("Create SolverCalculix") - FreeCADGui.addModule("ObjectsFem") - if has_nonlinear_material_obj: - FreeCADGui.doCommand("solver = ObjectsFem.makeSolverCalculix(FreeCAD.ActiveDocument)") - FreeCADGui.doCommand("solver.GeometricalNonlinearity = 'nonlinear'") - FreeCADGui.doCommand("solver.MaterialNonlinearity = 'nonlinear'") - FreeCADGui.doCommand("FemGui.getActiveAnalysis().addObject(solver)") + ccx_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Ccx") + use_old_solver_frame_work = ccx_prefs.GetBool("useOldSolverFrameWork", False) + use_new_solver_frame_work = ccx_prefs.GetBool("useNewSolverFrameWork", True) + if use_old_solver_frame_work and not use_new_solver_frame_work: + has_nonlinear_material_obj = False + for m in FemGui.getActiveAnalysis().Member: + if hasattr(m, "Proxy") and m.Proxy.Type == "FemMaterialMechanicalNonlinear": + has_nonlinear_material_obj = True + FreeCAD.ActiveDocument.openTransaction("Create SolverCalculix") + FreeCADGui.addModule("ObjectsFem") + FreeCADGui.addModule("FemGui") + if has_nonlinear_material_obj: + FreeCADGui.doCommand("solver = ObjectsFem.makeSolverCalculixOld(FreeCAD.ActiveDocument)") + FreeCADGui.doCommand("solver.GeometricalNonlinearity = 'nonlinear'") + FreeCADGui.doCommand("solver.MaterialNonlinearity = 'nonlinear'") + FreeCADGui.doCommand("FemGui.getActiveAnalysis().addObject(solver)") + else: + FreeCADGui.doCommand("FemGui.getActiveAnalysis().addObject(ObjectsFem.makeSolverCalculix(FreeCAD.ActiveDocument))") else: - FreeCADGui.doCommand("FemGui.getActiveAnalysis().addObject(ObjectsFem.makeSolverCalculix(FreeCAD.ActiveDocument))") + analysis = FemGui.getActiveAnalysis() + FreeCAD.ActiveDocument.openTransaction("Create CalculiX solver object") + FreeCADGui.addModule("ObjectsFem") + FreeCADGui.doCommand( + "FreeCAD.ActiveDocument.%s.Member += " + "[ObjectsFem.makeSolverCalculix(FreeCAD.ActiveDocument)]" + % analysis.Name) + FreeCAD.ActiveDocument.commitTransaction() + FreeCAD.ActiveDocument.recompute() FreeCADGui.addCommand('FEM_SolverCalculix', _CommandFemSolverCalculix()) diff --git a/src/Mod/Fem/PyGui/_CommandFemSolverRun.py b/src/Mod/Fem/PyGui/_CommandFemSolverRun.py index e0804c73e4..58b2f3b383 100644 --- a/src/Mod/Fem/PyGui/_CommandFemSolverRun.py +++ b/src/Mod/Fem/PyGui/_CommandFemSolverRun.py @@ -54,6 +54,8 @@ class _CommandFemSolverRun(FemCommands): self.solver = FreeCADGui.Selection.getSelection()[0] # see 'with_solver' in FemCommands for selection check if FemUtils.isDerivedFrom(self.solver, "Fem::FemSolverObjectZ88"): self._newActivated() + elif FemUtils.isDerivedFrom(self.solver, "Fem::FemSolverObjectCalculix"): + self._newActivated() elif self.solver.SolverType == "FemSolverCalculix": import FemToolsCcx self.fea = FemToolsCcx.FemToolsCcx(None, self.solver) diff --git a/src/Mod/Fem/TestFem.py b/src/Mod/Fem/TestFem.py index 4f862c9383..414452c1ef 100644 --- a/src/Mod/Fem/TestFem.py +++ b/src/Mod/Fem/TestFem.py @@ -431,7 +431,7 @@ class FemCcxAnalysisTest(unittest.TestCase): self.assertTrue(analysis, "FemTest of new analysis failed") fcc_print('Checking FEM new solver...') - solver_object = ObjectsFem.makeSolverCalculix(self.active_doc, 'CalculiX') + solver_object = ObjectsFem.makeSolverCalculixOld(self.active_doc, 'CalculiX') solver_object.GeometricalNonlinearity = 'linear' solver_object.ThermoMechSteadyState = False solver_object.MatrixSolverType = 'default' @@ -605,7 +605,7 @@ class FemCcxAnalysisTest(unittest.TestCase): self.assertTrue(analysis, "FemTest of new analysis failed") fcc_print('Checking FEM new solver...') - solver_object = ObjectsFem.makeSolverCalculix(self.active_doc, 'CalculiX') + solver_object = ObjectsFem.makeSolverCalculixOld(self.active_doc, 'CalculiX') solver_object.AnalysisType = 'thermomech' solver_object.GeometricalNonlinearity = 'linear' solver_object.ThermoMechSteadyState = True @@ -759,7 +759,7 @@ class FemCcxAnalysisTest(unittest.TestCase): self.assertTrue(analysis, "FemTest of new analysis failed") fcc_print('Checking FEM new solver...') - solver_object = ObjectsFem.makeSolverCalculix(self.active_doc, 'CalculiX') + solver_object = ObjectsFem.makeSolverCalculixOld(self.active_doc, 'CalculiX') solver_object.AnalysisType = 'thermomech' solver_object.GeometricalNonlinearity = 'linear' solver_object.ThermoMechSteadyState = True diff --git a/src/Mod/Fem/femsolver/calculix/__init__.py b/src/Mod/Fem/femsolver/calculix/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/Mod/Fem/femsolver/calculix/solver.py b/src/Mod/Fem/femsolver/calculix/solver.py new file mode 100644 index 0000000000..d46b012255 --- /dev/null +++ b/src/Mod/Fem/femsolver/calculix/solver.py @@ -0,0 +1,187 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2017 - Bernd Hahnebach * +# * * +# * 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__ = "CalculiX SolverObject" +__author__ = "Bernd Hahnebach" +__url__ = "http://www.freecadweb.org" + +## @package SolverCalculix +# \ingroup FEM + +import os +import glob + +import FreeCAD +import FemUtils + +from .. import run +from .. import solverbase +from . import tasks + + +if FreeCAD.GuiUp: + import FemGui + +ANALYSIS_TYPES = ["static", "frequency", "thermomech"] + + +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::FemSolverObjectCalculix" + + def __init__(self, obj): + super(Proxy, self).__init__(obj) + obj.Proxy = self + + # fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/General") # not needed ATM + ccx_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Ccx") + + 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] + + 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 + + choices_material_nonlinear = ["linear", "nonlinear"] + obj.addProperty("App::PropertyEnumeration", "MaterialNonlinearity", "Fem", "Set material nonlinearity (needs geometrical nonlinearity)") + obj.MaterialNonlinearity = choices_material_nonlinear + obj.MaterialNonlinearity = choices_material_nonlinear[0] + + obj.addProperty("App::PropertyIntegerConstraint", "EigenmodesCount", "Fem", "Number of modes for frequency calculations") + noe = ccx_prefs.GetInt("EigenmodesCount", 10) + obj.EigenmodesCount = (noe, 1, 100, 1) + + 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) + + 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) + + obj.addProperty("App::PropertyIntegerConstraint", "IterationsThermoMechMaximum", "Fem", "Maximum Number of thermo mechanical iterations in each time step before stopping jobs") + niter = ccx_prefs.GetInt("AnalysisMaxIterations", 200) + obj.IterationsThermoMechMaximum = niter + + obj.addProperty("App::PropertyFloatConstraint", "TimeInitialStep", "Fem", "Initial time steps") + ini = ccx_prefs.GetFloat("AnalysisTimeInitialStep", 1.0) + obj.TimeInitialStep = ini + + obj.addProperty("App::PropertyFloatConstraint", "TimeEnd", "Fem", "End time analysis") + eni = ccx_prefs.GetFloat("AnalysisTime", 1.0) + obj.TimeEnd = eni + + 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 + + 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 + + obj.addProperty("App::PropertyBool", "SplitInputWriter", "Fem", "Split writing of ccx input file") + split = ccx_prefs.GetBool("SplitInputWriter", False) + obj.SplitInputWriter = split + + ccx_default_time_incrementation_control_parameter = { + # iteration parameter + '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': None, + 'I_A': 200, # ccx default = 5 + 'I_J': None, + 'I_T': None, + # cutback parameter + 'D_f': 0.25, + 'D_C': 0.5, + 'D_B': 0.75, + 'D_A': 0.85, + 'D_S': None, + 'D_H': None, + 'D_D': 1.5, + 'W_G': None} + p = ccx_default_time_incrementation_control_parameter + p_iter = '{0},{1},{2},{3},{4},{5},{6},{7},{8},{9}'.format(p['I_0'], p['I_R'], p['I_P'], p['I_C'], p['I_L'], p['I_G'], '', p['I_A'], '', '') + p_cutb = '{0},{1},{2},{3},{4},{5},{6},{7}'.format(p['D_f'], p['D_C'], p['D_B'], p['D_A'], '', '', p['D_D'], '') + obj.addProperty("App::PropertyString", "IterationsControlParameterIter", "Fem", "User defined time incrementation iterations control parameter") + obj.IterationsControlParameterIter = p_iter + obj.addProperty("App::PropertyString", "IterationsControlParameterCutb", "Fem", "User defined time incrementation cutbacks control parameter") + obj.IterationsControlParameterCutb = p_cutb + + obj.addProperty("App::PropertyBool", "IterationsUserDefinedIncrementations", "Fem", "Set to True to switch off the ccx automatic incrementation completely (ccx parameter DIRECT). Use with care. Analysis may not converge!") + obj.IterationsUserDefinedIncrementations = False + + obj.addProperty("App::PropertyBool", "IterationsUserDefinedTimeStepLength", "Fem", "Set to True to use the user defined time steps. The time steps are set with TimeInitialStep and TimeEnd") + obj.IterationsUserDefinedTimeStepLength = False + + known_ccx_solver_types = ["default", "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] + + obj.addProperty("App::PropertyBool", "BeamShellResultOutput3D", "Fem", "Output 3D results for 1D and 2D anlysis ") + dimout = ccx_prefs.GetBool("BeamShellOutput", False) + obj.BeamShellResultOutput3D = dimout + + def createMachine(self, obj, directory): + return run.Machine( + solver=obj, directory=directory, + check=tasks.Check(), + prepare=tasks.Prepare(), + solve=tasks.Solve(), + results=tasks.Results()) + + def editSupported(self): + return True + + def edit(self, directory): + pattern = os.path.join(directory, "*.inp") + print(pattern) + f = glob.glob(pattern)[0] + FemGui.open(f) + + def execute(self, obj): + return + + +class ViewProxy(solverbase.ViewProxy): + pass diff --git a/src/Mod/Fem/femsolver/calculix/tasks.py b/src/Mod/Fem/femsolver/calculix/tasks.py new file mode 100644 index 0000000000..c9c718d3c0 --- /dev/null +++ b/src/Mod/Fem/femsolver/calculix/tasks.py @@ -0,0 +1,263 @@ +# *************************************************************************** +# * * +# * Copyright (c) 2017 - Markus Hovorka * +# * Copyright (c) 2017 - Bernd Hahnebach * +# * * +# * 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__ = "CalculiX Tasks" +__author__ = "Markus Hovorka, Bernd Hahnebach" +__url__ = "http://www.freecadweb.org" + + +import os +import subprocess +import os.path + +import FreeCAD as App +import FemUtils +import importCcxFrdResults +import importCcxDatResults + +from .. import run +from .. import settings +from FemInputWriterCcx + + +_inputFileName = None + + +class Check(run.Check): + + def run(self): + self.pushStatus("Checking analysis...\n") + self.checkMesh() + self.checkMaterial() + + +class Prepare(run.Prepare): + + def run(self): + global _inputFileName + self.pushStatus("Preparing input files...\n") + c = _Container(self.analysis) + w = FemInputWriterCcx.FemInputWriterCcx( + self.analysis, self.solver, c.mesh, c.materials_linear, + c.materials_nonlinear, c.fixed_constraints, + c.displacement_constraints, c.contact_constraints, + c.planerotation_constraints, c.transform_constraints, + c.selfweight_constraints, c.force_constraints, + c.pressure_constraints, c.temperature_constraints, + c.heatflux_constraints, c.initialtemperature_constraints, + c.beam_sections, c.shell_thicknesses, c.fluid_sections, + self.solver.AnalysisType, self.directory) + path = w.write_calculix_input_file() + _inputFileName = os.path.splitext(os.path.basename(path))[0] + + +class Solve(run.Solve): + + def run(self): + self.pushStatus("Executing solver...\n") + binary = settings.getBinary("Calculix") + self._process = subprocess.Popen( + [binary, "-i", _inputFileName], + cwd=self.directory, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE) + self.signalAbort.add(self._process.terminate) + output = self._observeSolver(self._process) + self._process.communicate() + self.signalAbort.remove(self._process.terminate) + # if not self.aborted: + # self._updateOutput(output) + del output # get flake8 quiet + + def _observeSolver(self, process): + output = "" + line = process.stdout.readline() + self.pushStatus(line) + output += line + line = process.stdout.readline() + while line: + line = "\n%s" % line.rstrip() + self.pushStatus(line) + output += line + line = process.stdout.readline() + return output + + +class Results(run.Results): + + def run(self): + prefs = App.ParamGet( + "User parameter:BaseApp/Preferences/Mod/Fem/General") + if not prefs.GetBool("KeepResultsOnReRun", False): + self.purge_results() + self.load_results_ccxfrd() + self.load_results_ccxdat() + + def purge_results(self): + for m in FemUtils.getMember(self.analysis, "Fem::FemResultObject"): + if FemUtils.isOfType(m.Mesh, "FemMeshResult"): + self.analysis.Document.removeObject(m.Mesh.Name) + self.analysis.Document.removeObject(m.Name) + App.ActiveDocument.recompute() + + def load_results_ccxfrd(self): + frd_result_file = os.path.join( + self.directory, _inputFileName + '.frd') + if os.path.isfile(frd_result_file): + result_name_prefix = 'CalculiX_' + self.solver.AnalysisType + '_' + importCcxFrdResults.importFrd( + frd_result_file, self.analysis, result_name_prefix) + else: + raise Exception( + 'FEM: No results found at {}!'.format(frd_result_file)) + + def load_results_ccxdat(self): + dat_result_file = os.path.join( + self.directory, _inputFileName + '.dat') + if os.path.isfile(dat_result_file): + mode_frequencies = importCcxDatResults.import_dat( + dat_result_file, self.analysis) + else: + raise Exception( + 'FEM: No .dat results found at {}!'.format(dat_result_file)) + if mode_frequencies: + for m in FemUtils.getMember(self.analysis, "Fem::FemResultObject"): + if m.Eigenmode > 0: + for mf in mode_frequencies: + if m.Eigenmode == mf['eigenmode']: + m.EigenmodeFrequency = mf['frequency'] + + +class _Container(object): + + def __init__(self, analysis): + self.mesh = None + self.materials_linear = [] + self.materials_nonlinear = [] + self.fixed_constraints = [] + self.selfweight_constraints = [] + self.force_constraints = [] + self.pressure_constraints = [] + self.beam_sections = [] + self.fluid_sections = [] + self.shell_thicknesses = [] + self.displacement_constraints = [] + self.temperature_constraints = [] + self.heatflux_constraints = [] + self.initialtemperature_constraints = [] + self.planerotation_constraints = [] + self.contact_constraints = [] + self.transform_constraints = [] + + for m in analysis.Member: + if m.isDerivedFrom("Fem::FemMeshObject"): + if not self.mesh: + self.mesh = m + else: + raise Exception('FEM: Multiple mesh in analysis not yet supported!') + elif m.isDerivedFrom("App::MaterialObjectPython"): + material_linear_dict = {} + material_linear_dict['Object'] = m + self.materials_linear.append(material_linear_dict) + elif hasattr(m, "Proxy") and m.Proxy.Type == "FemMaterialMechanicalNonlinear": + material_nonlinear_dict = {} + material_nonlinear_dict['Object'] = m + self.materials_nonlinear.append(material_nonlinear_dict) + elif m.isDerivedFrom("Fem::ConstraintFixed"): + fixed_constraint_dict = {} + fixed_constraint_dict['Object'] = m + self.fixed_constraints.append(fixed_constraint_dict) + elif hasattr(m, "Proxy") and m.Proxy.Type == "FemConstraintSelfWeight": + selfweight_dict = {} + selfweight_dict['Object'] = m + self.selfweight_constraints.append(selfweight_dict) + elif m.isDerivedFrom("Fem::ConstraintForce"): + force_constraint_dict = {} + force_constraint_dict['Object'] = m + force_constraint_dict['RefShapeType'] = self.get_refshape_type(m) + self.force_constraints.append(force_constraint_dict) + elif m.isDerivedFrom("Fem::ConstraintPressure"): + PressureObjectDict = {} + PressureObjectDict['Object'] = m + self.pressure_constraints.append(PressureObjectDict) + elif m.isDerivedFrom("Fem::ConstraintDisplacement"): + displacement_constraint_dict = {} + displacement_constraint_dict['Object'] = m + self.displacement_constraints.append(displacement_constraint_dict) + elif m.isDerivedFrom("Fem::ConstraintTemperature"): + temperature_constraint_dict = {} + temperature_constraint_dict['Object'] = m + self.temperature_constraints.append(temperature_constraint_dict) + elif m.isDerivedFrom("Fem::ConstraintHeatflux"): + heatflux_constraint_dict = {} + heatflux_constraint_dict['Object'] = m + self.heatflux_constraints.append(heatflux_constraint_dict) + elif m.isDerivedFrom("Fem::ConstraintInitialTemperature"): + initialtemperature_constraint_dict = {} + initialtemperature_constraint_dict['Object'] = m + self.initialtemperature_constraints.append( + initialtemperature_constraint_dict) + elif m.isDerivedFrom("Fem::ConstraintPlaneRotation"): + planerotation_constraint_dict = {} + planerotation_constraint_dict['Object'] = m + self.planerotation_constraints.append(planerotation_constraint_dict) + elif m.isDerivedFrom("Fem::ConstraintContact"): + contact_constraint_dict = {} + contact_constraint_dict['Object'] = m + self.contact_constraints.append(contact_constraint_dict) + elif m.isDerivedFrom("Fem::ConstraintTransform"): + transform_constraint_dict = {} + transform_constraint_dict['Object'] = m + self.transform_constraints.append(transform_constraint_dict) + elif hasattr(m, "Proxy") and m.Proxy.Type == "FemElementGeometry1D": + beam_section_dict = {} + beam_section_dict['Object'] = m + self.beam_sections.append(beam_section_dict) + elif hasattr(m, "Proxy") and m.Proxy.Type == "FemElementFluid1D": + fluid_section_dict = {} + fluid_section_dict['Object'] = m + self.fluid_sections.append(fluid_section_dict) + elif hasattr(m, "Proxy") and m.Proxy.Type == "FemElementGeometry2D": + shell_thickness_dict = {} + shell_thickness_dict['Object'] = m + self.shell_thicknesses.append(shell_thickness_dict) + + def get_refshape_type(self, fem_doc_object): + # returns the reference shape type + # for force object: + # in GUI defined frc_obj all frc_obj have at leas one ref_shape and ref_shape have all the same shape type + # for material object: + # in GUI defined material_obj could have no RefShape and RefShapes could be different type + # we gone need the RefShapes to be the same type inside one fem_doc_object + # TODO here: check if all RefShapes inside the object really have the same type + import FemMeshTools + if hasattr(fem_doc_object, 'References') and fem_doc_object.References: + first_ref_obj = fem_doc_object.References[0] + first_ref_shape = FemMeshTools.get_element(first_ref_obj[0], first_ref_obj[1][0]) + st = first_ref_shape.ShapeType + print(fem_doc_object.Name + ' has ' + st + ' reference shapes.') + return st + else: + print(fem_doc_object.Name + ' has empty References.') + return '' diff --git a/src/Mod/Fem/femsolver/settings.py b/src/Mod/Fem/femsolver/settings.py index 5996a66201..9193f5df27 100644 --- a/src/Mod/Fem/femsolver/settings.py +++ b/src/Mod/Fem/femsolver/settings.py @@ -36,6 +36,7 @@ CUSTOM = "custom" _ELMER_PARAM = "User parameter:BaseApp/Preferences/Mod/Fem/Elmer" _GRID_PARAM = "User parameter:BaseApp/Preferences/Mod/Fem/Grid" +_CCX_PARAM = "User parameter:BaseApp/Preferences/Mod/Fem/Ccx" _Z88_PARAM = "User parameter:BaseApp/Preferences/Mod/Fem/Z88" @@ -66,6 +67,11 @@ _BINARIES = { param=_GRID_PARAM, useDefault="UseStandardGridLocation", customPath="gridBinaryPath"), + "Calculix": _BinaryDlg( + default="ccx", + param=_CCX_PARAM, + useDefault="UseStandardCcxLocation", + customPath="ccxBinaryPath"), "Z88": _BinaryDlg( default="z88r", param=_Z88_PARAM,