| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | __title__ = "FemToolsCcx" |
| | __author__ = "Przemo Firszt, Bernd Hahnebach" |
| | __url__ = "https://www.freecad.org" |
| |
|
| | |
| | |
| |
|
| | import os |
| | import sys |
| | import subprocess |
| | import shutil |
| |
|
| | import FreeCAD |
| |
|
| | from femtools import femutils |
| | from femtools import membertools |
| |
|
| | from PySide import QtCore |
| |
|
| | if FreeCAD.GuiUp: |
| | from PySide import QtGui |
| | import FemGui |
| |
|
| |
|
| | class FemToolsCcx(QtCore.QRunnable, QtCore.QObject): |
| | """ |
| | |
| | Attributes |
| | ---------- |
| | analysis : Fem::FemAnalysis |
| | FEM group analysis object |
| | has to be present, will be set in __init__ |
| | solver : Fem::FemSolverObjectPython |
| | FEM solver object |
| | has to be present, will be set in __init__ |
| | base_name : str |
| | name of .inp/.frd file (without extension) |
| | It is used to construct .inp file path that is passed to CalculiX ccx |
| | ccx_binary : str |
| | working_dir : str |
| | results_present : bool |
| | indicating if there are calculation results ready for us |
| | members : class femtools/membertools/AnalysisMember |
| | contains references to all analysis member except solvers and mesh |
| | Updated with update_objects |
| | """ |
| |
|
| | finished = QtCore.Signal(int) |
| |
|
| | def __init__(self, analysis=None, solver=None, test_mode=False): |
| | """The constructor |
| | |
| | Parameters |
| | ---------- |
| | analysis : Fem::FemAnalysis, optional |
| | analysis group as a container for all objects needed for the analysis |
| | solver : Fem::FemSolverObjectPython, optional |
| | solver object to be used for this solve |
| | test_mode : bool, optional |
| | mainly used in unit tests |
| | """ |
| |
|
| | QtCore.QRunnable.__init__(self) |
| | QtCore.QObject.__init__(self) |
| |
|
| | self.ccx_binary_present = False |
| | self.analysis = None |
| | self.solver = None |
| |
|
| | |
| | |
| |
|
| | if analysis: |
| | self.analysis = analysis |
| | if solver: |
| | |
| | self.solver = solver |
| | else: |
| | |
| | self.find_solver() |
| | if not self.solver: |
| | raise Exception("FEM: No solver found!") |
| | else: |
| | if solver: |
| | |
| | self.solver = solver |
| | self.find_solver_analysis() |
| | if not self.analysis: |
| | raise Exception( |
| | "FEM: The solver was given as parameter, " |
| | "but no analysis for this solver was found!" |
| | ) |
| | else: |
| | |
| | self.find_analysis() |
| | if not self.analysis: |
| | raise Exception( |
| | "FEM: No solver was given and either no active analysis " |
| | "or no analysis at all or more than one analysis found!" |
| | ) |
| | self.find_solver() |
| | if not self.solver: |
| | raise Exception("FEM: No solver found!") |
| |
|
| | if self.analysis.Document is not self.solver.Document: |
| | raise Exception("FEM: The analysis and solver are not in the same document!") |
| | if self.solver not in self.analysis.Group: |
| | raise Exception("FEM: The solver is not part of the analysis Group!") |
| |
|
| | |
| | |
| | if self.analysis and self.solver: |
| | self.working_dir = "" |
| | self.ccx_binary = "" |
| | self.base_name = "" |
| | self.results_present = False |
| | if test_mode: |
| | self.test_mode = True |
| | self.ccx_binary_present = True |
| | else: |
| | self.test_mode = False |
| | self.ccx_binary_present = False |
| | self.result_object = None |
| | else: |
| | raise Exception( |
| | "FEM: Something went wrong, the exception should have been raised earlier!" |
| | ) |
| |
|
| | def purge_results(self): |
| | """Remove all result objects and result meshes from an analysis group""" |
| | from femresult.resulttools import purge_results as pr |
| |
|
| | pr(self.analysis) |
| |
|
| | def reset_mesh_purge_results_checked(self): |
| | """Reset mesh color, deformation and removes all result objects |
| | if preferences to keep them is not set. |
| | """ |
| | self.fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/General") |
| | keep_results_on_rerun = self.fem_prefs.GetBool("KeepResultsOnReRun", False) |
| | if not keep_results_on_rerun: |
| | |
| | |
| | |
| | from femresult.resulttools import purge_result_objects as purge |
| |
|
| | purge(self.analysis) |
| |
|
| | def reset_all(self): |
| | """Reset mesh color, deformation and removes all result objects""" |
| | self.purge_results() |
| |
|
| | def _get_several_member(self, obj_type): |
| | return membertools.get_several_member(self.analysis, obj_type) |
| |
|
| | def find_analysis(self): |
| | if FreeCAD.GuiUp: |
| | self.analysis = FemGui.getActiveAnalysis() |
| | if self.analysis: |
| | return |
| | found_analysis = False |
| | |
| | for m in FreeCAD.activeDocument().Objects: |
| | if femutils.is_of_type(m, "Fem::FemAnalysis"): |
| | if not found_analysis: |
| | self.analysis = m |
| | found_analysis = True |
| | else: |
| | self.analysis = None |
| | if self.analysis: |
| | if FreeCAD.GuiUp: |
| | FemGui.setActiveAnalysis(self.analysis) |
| |
|
| | def find_solver_analysis(self): |
| | """get the analysis group the solver belongs to""" |
| | if self.solver.getParentGroup(): |
| | obj = self.solver.getParentGroup() |
| | if femutils.is_of_type(obj, "Fem::FemAnalysis"): |
| | self.analysis = obj |
| | if FreeCAD.GuiUp: |
| | FemGui.setActiveAnalysis(self.analysis) |
| |
|
| | def find_solver(self): |
| | found_solver_for_use = False |
| | for m in self.analysis.Group: |
| | if femutils.is_of_type(m, "Fem::SolverCcxTools"): |
| | |
| | |
| | |
| | |
| | |
| | |
| | if not found_solver_for_use: |
| | |
| | self.solver = m |
| | found_solver_for_use = True |
| | else: |
| | |
| | |
| | self.solver = None |
| | FreeCAD.Console.PrintLog( |
| | "FEM: More than one solver in the analysis " |
| | "and no solver given to analyze. " |
| | "No solver is set!\n" |
| | ) |
| |
|
| | def update_objects(self): |
| | |
| | |
| | self.mesh = None |
| | mesh, message = membertools.get_mesh_to_solve(self.analysis) |
| | if mesh is not None: |
| | self.mesh = mesh |
| | else: |
| | |
| | |
| | FreeCAD.Console.PrintWarning(f"{message} The prerequisite check will fail.\n") |
| |
|
| | |
| | |
| | self.member = membertools.AnalysisMember(self.analysis) |
| |
|
| | def check_prerequisites(self): |
| | FreeCAD.Console.PrintMessage("\n") |
| | FreeCAD.Console.PrintMessage("Check prerequisites...\n") |
| | message = "" |
| | |
| | if not self.analysis: |
| | message += "No active Analysis\n" |
| | |
| | if not self.solver: |
| | message += "No solver object defined in the analysis\n" |
| | if not self.working_dir: |
| | message += "Working directory not set\n" |
| | if not os.path.isdir(self.working_dir): |
| | message += f"Working directory '{self.working_dir}' doesn't exist." |
| | from femtools.checksanalysis import check_member_for_solver_calculix |
| |
|
| | message += check_member_for_solver_calculix( |
| | self.analysis, self.solver, self.mesh, self.member |
| | ) |
| | return message |
| |
|
| | def set_base_name(self, base_name=None): |
| | """ |
| | Set base_name |
| | |
| | Parameters |
| | ---------- |
| | base_name : str, optional |
| | base_name base name of .inp/.frd file (without extension). |
| | It is used to construct .inp file path that is passed to CalculiX ccx |
| | """ |
| | if base_name is None: |
| | self.base_name = "" |
| | else: |
| | self.base_name = base_name |
| | |
| | self.set_inp_file_name() |
| |
|
| | def set_inp_file_name(self, inp_file_name=None): |
| | """ |
| | Set inp file name. Normally inp file name is set by write_inp_file. |
| | That name is also used to determine location and name of frd result file. |
| | |
| | Parameters |
| | ---------- |
| | inp_file_name : str, optional |
| | input file name path |
| | """ |
| | if inp_file_name is not None: |
| | self.inp_file_name = inp_file_name |
| | else: |
| | self.inp_file_name = os.path.join(self.working_dir, (self.base_name + ".inp")) |
| |
|
| | def setup_working_dir(self, param_working_dir=None, create=False): |
| | """Set working dir for solver execution. |
| | |
| | Parameters |
| | ---------- |
| | param_working_dir : str, optional |
| | directory to be used for writing |
| | create : bool, optional |
| | Should the working directory be created if it does not exist |
| | """ |
| | self.working_dir = "" |
| | |
| | fem_general_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/General") |
| | if param_working_dir is not None: |
| | self.working_dir = param_working_dir |
| | if femutils.check_working_dir(self.working_dir) is not True: |
| | if create is True: |
| | FreeCAD.Console.PrintMessage( |
| | f"Dir given as parameter '{self.working_dir}' doesn't exist.\n" |
| | ) |
| | else: |
| | FreeCAD.Console.PrintError( |
| | "Dir given as parameter '{}' doesn't exist " |
| | "and create parameter is set to False.\n".format(self.working_dir) |
| | ) |
| | self.working_dir = femutils.get_pref_working_dir(self.solver) |
| | FreeCAD.Console.PrintMessage( |
| | f"Dir '{self.working_dir}' will be used instead.\n" |
| | ) |
| | elif fem_general_prefs.GetBool("OverwriteSolverWorkingDirectory", True) is False: |
| | self.working_dir = self.solver.WorkingDir |
| | if femutils.check_working_dir(self.working_dir) is not True: |
| | if self.working_dir == "": |
| | FreeCAD.Console.PrintError( |
| | "Working Dir is set to be used from solver object " |
| | "but Dir from solver object '{}' is empty.\n".format(self.working_dir) |
| | ) |
| | else: |
| | FreeCAD.Console.PrintError( |
| | f"Dir from solver object '{self.working_dir}' doesn't exist.\n" |
| | ) |
| | self.working_dir = femutils.get_pref_working_dir(self.solver) |
| | FreeCAD.Console.PrintMessage(f"Dir '{self.working_dir}' will be used instead.\n") |
| | else: |
| | self.working_dir = femutils.get_pref_working_dir(self.solver) |
| |
|
| | |
| | if femutils.check_working_dir(self.working_dir) is not True: |
| | FreeCAD.Console.PrintError( |
| | f"Dir '{self.working_dir}' doesn't exist or cannot be created.\n" |
| | ) |
| | self.working_dir = femutils.get_temp_dir(self.solver) |
| | FreeCAD.Console.PrintMessage(f"Dir '{self.working_dir}' will be used instead.\n") |
| |
|
| | |
| | self.set_inp_file_name() |
| |
|
| | def write_inp_file(self): |
| |
|
| | |
| | |
| | from femmesh import meshsetsgetter |
| |
|
| | meshdatagetter = meshsetsgetter.MeshSetsGetter( |
| | self.analysis, |
| | self.solver, |
| | self.mesh, |
| | membertools.AnalysisMember(self.analysis), |
| | ) |
| | |
| | meshdatagetter.get_mesh_sets() |
| |
|
| | |
| | import femsolver.calculix.writer as iw |
| |
|
| | self.inp_file_name = "" |
| | try: |
| | inp_writer = iw.FemInputWriterCcx( |
| | self.analysis, |
| | self.solver, |
| | self.mesh, |
| | meshdatagetter.member, |
| | self.working_dir, |
| | meshdatagetter.mat_geo_sets, |
| | ) |
| | self.inp_file_name = inp_writer.write_solver_input() |
| | except Exception: |
| | FreeCAD.Console.PrintError( |
| | f"Unexpected error when writing CalculiX input file: {sys.exc_info()[1]}\n" |
| | ) |
| | raise |
| |
|
| | def setup_ccx(self, ccx_binary=None, ccx_binary_sig="CalculiX"): |
| | """Set Calculix binary path and validate its execution. |
| | |
| | Parameters |
| | ---------- |
| | ccx_binary : str, optional |
| | It defaults to `None`. The path to the `ccx` binary. If it is `None`, |
| | the path is guessed. |
| | ccx_binary_sig : str, optional |
| | Defaults to 'CalculiX'. Expected output from `ccx` when run empty. |
| | |
| | """ |
| | error_title = self.tr("No or wrong CalculiX binary ccx") |
| | self.ccx_binary = ccx_binary |
| | if self.ccx_binary is None: |
| | self.ccx_binary = FreeCAD.ParamGet( |
| | "User parameter:BaseApp/Preferences/Mod/Fem/Ccx" |
| | ).GetString("ccxBinaryPath", "") |
| |
|
| | if not self.ccx_binary: |
| | |
| | self.ccx_binary = shutil.which("ccx") |
| | else: |
| | |
| | self.ccx_binary = shutil.which(self.ccx_binary) |
| |
|
| | if self.ccx_binary is None: |
| | raise FileNotFoundError( |
| | "CalculiX binary not found\n" |
| | "Install CalculiX or set path to binary in FEM user preferences" |
| | ) |
| |
|
| | ccx_stdout = None |
| | ccx_stderr = None |
| | try: |
| | p = subprocess.Popen( |
| | [self.ccx_binary], |
| | stdout=subprocess.PIPE, |
| | stderr=subprocess.PIPE, |
| | shell=False, |
| | startupinfo=femutils.startProgramInfo(""), |
| | ) |
| | ccx_stdout, ccx_stderr = p.communicate() |
| | if ccx_binary_sig in str(ccx_stdout): |
| | self.ccx_binary_present = True |
| | else: |
| | error_message = self.tr("FEM: wrong ccx binary") |
| | if FreeCAD.GuiUp: |
| | QtGui.QMessageBox.critical(None, error_title, error_message) |
| | FreeCAD.Console.PrintError(error_message) |
| | |
| | |
| | |
| | except OSError as e: |
| | FreeCAD.Console.PrintError(f"{e}\n") |
| | if e.errno == 2: |
| | error_message = self.tr( |
| | "FEM: CalculiX binary ccx '{}' not found. " |
| | "Please set the CalculiX binary ccx path in " |
| | "FEM preferences tab CalculiX." |
| | ).format(self.ccx_binary) |
| | if FreeCAD.GuiUp: |
| | QtGui.QMessageBox.critical(None, error_title, error_message) |
| | FreeCAD.Console.PrintError(error_message) |
| |
|
| | except Exception as e: |
| | FreeCAD.Console.PrintError(f"{e}\n") |
| | error_message = self.tr( |
| | "FEM: CalculiX ccx '{}' output '{}' doesn't " |
| | "contain expected phrase '{}'. " |
| | "There are some problems when running the ccx binary. " |
| | "Check if ccx runs standalone without FreeCAD." |
| | ).format(self.ccx_binary, ccx_stdout, ccx_binary_sig) |
| | if FreeCAD.GuiUp: |
| | QtGui.QMessageBox.critical(None, error_title, error_message) |
| | FreeCAD.Console.PrintError(error_message) |
| |
|
| | def start_ccx(self): |
| | import multiprocessing |
| |
|
| | self.ccx_stdout = "" |
| | self.ccx_stderr = "" |
| | ont_backup = os.environ.get("OMP_NUM_THREADS") |
| | self.ccx_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/Ccx") |
| | |
| | num_cpu_pref = self.ccx_prefs.GetInt("AnalysisNumCPUs", 1) |
| | if not ont_backup: |
| | ont_backup = str(num_cpu_pref) |
| | if num_cpu_pref > 1: |
| | |
| | _env = os.putenv("OMP_NUM_THREADS", str(num_cpu_pref)) |
| | else: |
| | _env = os.putenv("OMP_NUM_THREADS", str(multiprocessing.cpu_count())) |
| | |
| | |
| | cwd = QtCore.QDir.currentPath() |
| | f = QtCore.QFileInfo(self.inp_file_name) |
| | QtCore.QDir.setCurrent(f.path()) |
| | p = subprocess.Popen( |
| | [self.ccx_binary, "-i ", f.baseName()], |
| | stdout=subprocess.PIPE, |
| | stderr=subprocess.PIPE, |
| | shell=False, |
| | env=_env, |
| | ) |
| | self.ccx_stdout, self.ccx_stderr = p.communicate() |
| | self.ccx_stdout = self.ccx_stdout.decode() |
| | self.ccx_stderr = self.ccx_stderr.decode() |
| | os.putenv("OMP_NUM_THREADS", ont_backup) |
| | QtCore.QDir.setCurrent(cwd) |
| | return p.returncode |
| |
|
| | def get_ccx_version(self): |
| | self.setup_ccx() |
| | import re |
| | from platform import system |
| |
|
| | ccx_stdout = None |
| | ccx_stderr = None |
| | |
| | p = subprocess.Popen( |
| | [self.ccx_binary, "-v"], |
| | stdout=subprocess.PIPE, |
| | stderr=subprocess.PIPE, |
| | shell=False, |
| | startupinfo=femutils.startProgramInfo(""), |
| | ) |
| | ccx_stdout, ccx_stderr = p.communicate() |
| | ccx_stdout = ccx_stdout.decode() |
| | m = re.search(r"(\d+).(\d+)", ccx_stdout) |
| | return (int(m.group(1)), int(m.group(2))) |
| |
|
| | def ccx_run(self): |
| | ret_code = None |
| | FreeCAD.Console.PrintMessage("\n") |
| | FreeCAD.Console.PrintMessage("CalculiX solver run...\n") |
| | if self.test_mode: |
| | FreeCAD.Console.PrintError("CalculiX can not be run if test_mode is True.\n") |
| | return |
| | self.setup_ccx() |
| | if self.ccx_binary_present is False: |
| | error_message = ( |
| | "FEM: CalculiX binary ccx '{}' not found. " |
| | "Please set the CalculiX binary ccx path in FEM preferences tab CalculiX.\n".format( |
| | self.ccx_binary |
| | ) |
| | ) |
| | if FreeCAD.GuiUp: |
| | QtGui.QMessageBox.critical(None, "No CalculiX binary ccx", error_message) |
| | return |
| | progress_bar = FreeCAD.Base.ProgressIndicator() |
| | progress_bar.start("Everything seems fine. CalculiX ccx will be executed ...", 0) |
| | ret_code = self.start_ccx() |
| | self.finished.emit(ret_code) |
| | progress_bar.stop() |
| | if ret_code or self.ccx_stderr: |
| | if ret_code == 201 and self.solver.AnalysisType == "check": |
| | FreeCAD.Console.PrintMessage( |
| | "It seems we run into NOANALYSIS problem, " |
| | "thus workaround for wrong exit code for *NOANALYSIS check " |
| | "and set ret_code to 0.\n" |
| | ) |
| | |
| | ret_code = 0 |
| | else: |
| | FreeCAD.Console.PrintError(f"CalculiX failed with exit code {ret_code}\n") |
| | FreeCAD.Console.PrintMessage("--------start of stderr-------\n") |
| | FreeCAD.Console.PrintMessage(self.ccx_stderr) |
| | FreeCAD.Console.PrintMessage("--------end of stderr---------\n") |
| | FreeCAD.Console.PrintMessage("--------start of stdout-------\n") |
| | FreeCAD.Console.PrintMessage(self.ccx_stdout) |
| | FreeCAD.Console.PrintMessage("\n--------end of stdout---------\n") |
| | FreeCAD.Console.PrintMessage("--------start problems---------\n") |
| | self.has_no_material_assigned() |
| | self.has_nonpositive_jacobians() |
| | FreeCAD.Console.PrintMessage("\n--------end problems---------\n") |
| | else: |
| | |
| | if FreeCAD.GuiUp: |
| | self.mesh.ViewObject.HighlightedNodes = [] |
| |
|
| | FreeCAD.Console.PrintMessage("CalculiX finished without error.\n") |
| | return ret_code |
| |
|
| | def run(self): |
| | self.update_objects() |
| | self.setup_working_dir() |
| | message = self.check_prerequisites() |
| | if message: |
| | text = "CalculiX can not be started due to missing prerequisites:\n" |
| | error_app = f"{text}{message}" |
| | error_gui = f"{text}\n{message}" |
| | FreeCAD.Console.PrintError(error_app) |
| | if FreeCAD.GuiUp: |
| | QtGui.QMessageBox.critical(None, "Missing prerequisite", error_gui) |
| | return False |
| | else: |
| | self.write_inp_file() |
| | if self.inp_file_name == "": |
| | error_message = "Error on writing CalculiX input file.\n" |
| | FreeCAD.Console.PrintError(error_message) |
| | if FreeCAD.GuiUp: |
| | QtGui.QMessageBox.critical(None, "Error", error_message) |
| | return False |
| | else: |
| | FreeCAD.Console.PrintLog("Writing CalculiX input file completed.\n") |
| | ret_code = self.ccx_run() |
| | if ret_code is None: |
| | error_message = "CalculiX has not been run. The CalculiX binary search returned: {}.\n".format( |
| | self.ccx_binary_present |
| | ) |
| | FreeCAD.Console.PrintError(error_message) |
| | if FreeCAD.GuiUp: |
| | QtGui.QMessageBox.critical(None, "Error", error_message) |
| | return False |
| | if ret_code != 0: |
| | error_message = f"CalculiX finished with error {ret_code}.\n" |
| | FreeCAD.Console.PrintError(error_message) |
| | if FreeCAD.GuiUp: |
| | QtGui.QMessageBox.critical(None, "Error", error_message) |
| | return False |
| | else: |
| | FreeCAD.Console.PrintLog("Try to read result files\n") |
| | self.load_results() |
| | |
| | return True |
| |
|
| | def has_no_material_assigned(self): |
| | if " *ERROR in calinput: no material was assigned" in self.ccx_stdout: |
| | without_material_elements = [] |
| | without_material_elemnodes = [] |
| | for line in self.ccx_stdout.splitlines(): |
| | if "to element" in line: |
| | |
| | |
| | non_mat_ele = int(line.split()[2]) |
| | |
| | if non_mat_ele not in without_material_elements: |
| | without_material_elements.append(non_mat_ele) |
| | for e in without_material_elements: |
| | for n in self.mesh.FemMesh.getElementNodes(e): |
| | without_material_elemnodes.append(n) |
| | without_material_elements = sorted(without_material_elements) |
| | without_material_elemnodes = sorted(without_material_elemnodes) |
| | command_for_withoutmatnodes = "without_material_elemnodes = {}".format( |
| | without_material_elemnodes |
| | ) |
| | command_to_highlight = ( |
| | "Gui.ActiveDocument.{}.HighlightedNodes = without_material_elemnodes".format( |
| | self.mesh.Name |
| | ) |
| | ) |
| | |
| | FreeCAD.Console.PrintError( |
| | "\n\nCalculiX returned an error due to elements without materials.\n" |
| | ) |
| | FreeCAD.Console.PrintMessage( |
| | f"without_material_elements = {without_material_elements}\n" |
| | ) |
| | FreeCAD.Console.PrintMessage(command_for_withoutmatnodes + "\n") |
| | if FreeCAD.GuiUp: |
| | import FreeCADGui |
| |
|
| | |
| | |
| | FreeCADGui.doCommand(command_for_withoutmatnodes) |
| | FreeCAD.Console.PrintMessage("\n") |
| | FreeCADGui.doCommand(command_to_highlight) |
| | FreeCAD.Console.PrintMessage( |
| | "\nFollowing some commands to copy. " |
| | "They will highlight the elements without materials " |
| | "or to reset the highlighted nodes:\n" |
| | ) |
| | FreeCAD.Console.PrintMessage(command_to_highlight + "\n") |
| | |
| | FreeCAD.Console.PrintMessage( |
| | f"Gui.ActiveDocument.{self.mesh.Name}.HighlightedNodes = []\n\n" |
| | ) |
| | return True |
| | else: |
| | return False |
| |
|
| | def has_nonpositive_jacobians(self): |
| | if "*ERROR in e_c3d: nonpositive jacobian" in self.ccx_stdout: |
| | nonpositive_jacobian_elements = [] |
| | nonpositive_jacobian_elenodes = [] |
| | for line in self.ccx_stdout.splitlines(): |
| | if "determinant in element" in line: |
| | |
| | |
| | non_posjac_ele = int(line.split()[3]) |
| | |
| | if non_posjac_ele not in nonpositive_jacobian_elements: |
| | nonpositive_jacobian_elements.append(non_posjac_ele) |
| | for e in nonpositive_jacobian_elements: |
| | for n in self.mesh.FemMesh.getElementNodes(e): |
| | nonpositive_jacobian_elenodes.append(n) |
| | nonpositive_jacobian_elements = sorted(nonpositive_jacobian_elements) |
| | nonpositive_jacobian_elenodes = sorted(nonpositive_jacobian_elenodes) |
| | command_for_nonposjacnodes = "nonpositive_jacobian_elenodes = {}".format( |
| | nonpositive_jacobian_elenodes |
| | ) |
| | command_to_highlight = ( |
| | "Gui.ActiveDocument.{}.HighlightedNodes = nonpositive_jacobian_elenodes".format( |
| | self.mesh.Name |
| | ) |
| | ) |
| | |
| | FreeCAD.Console.PrintError( |
| | "\n\nCalculiX returned an error due to nonpositive jacobian elements.\n" |
| | ) |
| | FreeCAD.Console.PrintMessage( |
| | f"nonpositive_jacobian_elements = {nonpositive_jacobian_elements}\n" |
| | ) |
| | FreeCAD.Console.PrintMessage(command_for_nonposjacnodes + "\n") |
| | if FreeCAD.GuiUp: |
| | import FreeCADGui |
| |
|
| | |
| | |
| | FreeCADGui.doCommand(command_for_nonposjacnodes) |
| | FreeCAD.Console.PrintMessage("\n") |
| | FreeCADGui.doCommand(command_to_highlight) |
| | FreeCAD.Console.PrintMessage( |
| | "\nFollowing some commands to copy. " |
| | "They highlight the nonpositive jacobians " |
| | "or to reset the highlighted nodes:\n" |
| | ) |
| | FreeCAD.Console.PrintMessage(command_to_highlight + "\n") |
| | |
| | FreeCAD.Console.PrintMessage( |
| | f"Gui.ActiveDocument.{self.mesh.Name}.HighlightedNodes = []\n\n" |
| | ) |
| | return True |
| | else: |
| | return False |
| |
|
| | def load_results(self): |
| | FreeCAD.Console.PrintMessage("\n") |
| | FreeCAD.Console.PrintMessage("CalculiX read results...\n") |
| | self.results_present = False |
| | self.load_results_ccxfrd() |
| | self.load_results_ccxdat() |
| | self.analysis.Document.recompute() |
| |
|
| | def load_results_ccxfrd(self): |
| | """Load results of ccx calculations from .frd file.""" |
| | import feminout.importCcxFrdResults as importCcxFrdResults |
| |
|
| | frd_result_file = os.path.splitext(self.inp_file_name)[0] + ".frd" |
| | if os.path.isfile(frd_result_file): |
| | importCcxFrdResults.importFrd( |
| | frd_result_file, self.analysis, "CCX_", self.solver.AnalysisType |
| | ) |
| | for m in self.analysis.Group: |
| | if m.isDerivedFrom("Fem::FemResultObject"): |
| | self.results_present = True |
| | break |
| | else: |
| | if self.solver.AnalysisType == "check": |
| | for m in self.analysis.Group: |
| | if m.isDerivedFrom("Fem::FemMeshObjectPython"): |
| | |
| | |
| | break |
| | else: |
| | FreeCAD.Console.PrintError("FEM: No result object in active Analysis.\n") |
| | else: |
| | FreeCAD.Console.PrintError(f"FEM: No frd result file found at {frd_result_file}\n") |
| |
|
| | def load_results_ccxdat(self): |
| | """Load results of ccx calculations from .dat file.""" |
| | import feminout.importCcxDatResults as importCcxDatResults |
| |
|
| | dat_result_file = os.path.splitext(self.inp_file_name)[0] + ".dat" |
| | mode_frequencies = None |
| | dat_content = None |
| |
|
| | if os.path.isfile(dat_result_file): |
| | mode_frequencies = importCcxDatResults.import_dat(dat_result_file, self.analysis) |
| |
|
| | dat_file = open(dat_result_file) |
| | dat_content = dat_file.read() |
| | dat_file.close() |
| | else: |
| | FreeCAD.Console.PrintError(f"FEM: No dat result file found at {dat_result_file}\n") |
| |
|
| | if mode_frequencies: |
| | |
| | for m in self.analysis.Group: |
| | if m.isDerivedFrom("Fem::FemResultObject") and m.Eigenmode > 0: |
| | for mf in mode_frequencies: |
| | if m.Eigenmode == mf["eigenmode"]: |
| | m.EigenmodeFrequency = mf["frequency"] |
| |
|
| | if dat_content: |
| | |
| | dat_text_obj = self.analysis.Document.addObject("App::TextDocument", "ccx_dat_file") |
| | dat_text_obj.Text = dat_content |
| | dat_text_obj.setPropertyStatus("Text", "ReadOnly") |
| | if FreeCAD.GuiUp: |
| | dat_text_obj.ViewObject.ReadOnly = True |
| | self.analysis.addObject(dat_text_obj) |
| |
|
| |
|
| | class CcxTools(FemToolsCcx): |
| |
|
| | def __init__(self, solver=None): |
| | FemToolsCcx.__init__(self, None, solver) |
| |
|
| |
|
| | |
| |
|