| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | __title__ = "FreeCAD result mechanical task panel" |
| | __author__ = "Qingfeng Xia, Bernd Hahnebach" |
| | __url__ = "https://www.freecad.org" |
| |
|
| | |
| | |
| | |
| |
|
| | import CreateLabels |
| | import inspect, sys |
| |
|
| | try: |
| | import matplotlib |
| |
|
| | matplotlib.use("Qt5Agg") |
| | except Exception: |
| | print("Failed to set matplotlib backend to Qt5Agg") |
| |
|
| | import matplotlib.pyplot as plt |
| | import numpy as np |
| | import time, math |
| |
|
| | from PySide import QtCore |
| | from PySide import QtGui |
| | from PySide.QtCore import Qt |
| | from PySide.QtGui import QApplication |
| |
|
| | import FreeCAD |
| | import FreeCADGui |
| |
|
| | import femresult.resulttools as resulttools |
| |
|
| | translate = FreeCAD.Qt.translate |
| |
|
| |
|
| | class _TaskPanel: |
| | """ |
| | The task panel for the post-processing |
| | """ |
| |
|
| | def __init__(self, obj): |
| | self.result_obj = obj |
| | self.mesh_obj = self.result_obj.Mesh |
| | |
| | |
| | |
| | |
| | self.mesh_obj.ViewObject.show() |
| |
|
| | ui_path = FreeCAD.getHomePath() + "Mod/Fem/Resources/ui/" |
| | self.result_widget = FreeCADGui.PySideUic.loadUi(ui_path + "ResultShow.ui") |
| | self.info_widget = FreeCADGui.PySideUic.loadUi(ui_path + "ResultHints.ui") |
| | self.form = [self.result_widget, self.info_widget] |
| | self.results_name = "No Contour Data" |
| | self.animate_inc = 1 |
| | self.startAnimate = False |
| | self.animateText = [] |
| | self.slider_max = False |
| | self.recurlim = min(200, sys.getrecursionlimit() / 2) |
| |
|
| | self.fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/General") |
| | self.restore_result_settings_in_dialog = self.fem_prefs.GetBool("RestoreResultDialog", True) |
| |
|
| | |
| | |
| | |
| | QtCore.QObject.connect( |
| | self.result_widget.rb_none, |
| | QtCore.SIGNAL("toggled(bool)"), |
| | self.none_selected, |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.rb_abs_displacement, |
| | QtCore.SIGNAL("toggled(bool)"), |
| | self.abs_displacement_selected, |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.rb_x_displacement, |
| | QtCore.SIGNAL("toggled(bool)"), |
| | self.x_displacement_selected, |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.rb_y_displacement, |
| | QtCore.SIGNAL("toggled(bool)"), |
| | self.y_displacement_selected, |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.rb_z_displacement, |
| | QtCore.SIGNAL("toggled(bool)"), |
| | self.z_displacement_selected, |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.rb_temperature, |
| | QtCore.SIGNAL("toggled(bool)"), |
| | self.temperature_selected, |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.rb_vm_stress, |
| | QtCore.SIGNAL("toggled(bool)"), |
| | self.vm_stress_selected, |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.rb_maxprin, |
| | QtCore.SIGNAL("toggled(bool)"), |
| | self.max_prin_selected, |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.rb_minprin, |
| | QtCore.SIGNAL("toggled(bool)"), |
| | self.min_prin_selected, |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.rb_max_shear_stress, |
| | QtCore.SIGNAL("toggled(bool)"), |
| | self.max_shear_selected, |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.rb_massflowrate, |
| | QtCore.SIGNAL("toggled(bool)"), |
| | self.massflowrate_selected, |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.rb_networkpressure, |
| | QtCore.SIGNAL("toggled(bool)"), |
| | self.networkpressure_selected, |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.rb_peeq, |
| | QtCore.SIGNAL("toggled(bool)"), |
| | self.peeq_selected, |
| | ) |
| |
|
| | |
| | self.result_widget.show_histogram.clicked.connect(self.show_histogram_clicked) |
| | |
| | QtCore.QObject.connect( |
| | self.result_widget.hsb_displacement_factor, |
| | QtCore.SIGNAL("valueChanged(int)"), |
| | lambda dummy="", name="scale": self.value_changed(self, dummy, name), |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.sb_displacement_factor, |
| | QtCore.SIGNAL("valueChanged(double)"), |
| | lambda dummy="", name="factor": self.value_changed(self, dummy, name), |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.startButton, |
| | QtCore.SIGNAL("clicked()"), |
| | lambda dummy="", name="startButton": self.value_changed(self, dummy, name), |
| | ) |
| |
|
| | |
| | QtCore.QObject.connect( |
| | self.result_widget.cb_show_displacement, |
| | QtCore.SIGNAL("clicked(bool)"), |
| | self.show_displacement, |
| | ) |
| | QtCore.QObject.connect( |
| | self.result_widget.hsb_displacement_factor, |
| | QtCore.SIGNAL("valueChanged(int)"), |
| | self.hsb_disp_factor_changed, |
| | ) |
| |
|
| | self.result_widget.sb_displacement_factor.valueChanged.connect(self.sb_disp_factor_changed) |
| | self.result_widget.sb_displacement_factor_max.valueChanged.connect( |
| | self.sb_disp_factor_max_changed |
| | ) |
| |
|
| | |
| | self.result_widget.user_def_eq.textChanged.connect(self.user_defined_text) |
| | QtCore.QObject.connect( |
| | self.result_widget.calculate, QtCore.SIGNAL("clicked()"), self.calculate |
| | ) |
| |
|
| | self.update() |
| | if self.restore_result_settings_in_dialog: |
| | self.restore_result_dialog() |
| | else: |
| | self.restore_initial_result_dialog() |
| | |
| | scale_factor = get_displacement_scale_factor(self.result_obj) |
| | self.result_widget.sb_displacement_factor_max.setValue(10.0 * scale_factor) |
| | self.result_widget.sb_displacement_factor.setValue(scale_factor) |
| |
|
| | def restore_result_dialog(self): |
| | try: |
| | rt = FreeCAD.FEM_dialog["results_type"] |
| | if rt == "None": |
| | self.result_widget.rb_none.setChecked(True) |
| | self.none_selected(True) |
| | elif rt == "Uabs": |
| | self.result_widget.rb_abs_displacement.setChecked(True) |
| | self.abs_displacement_selected(True) |
| | elif rt == "U1": |
| | self.result_widget.rb_x_displacement.setChecked(True) |
| | self.x_displacement_selected(True) |
| | elif rt == "U2": |
| | self.result_widget.rb_y_displacement.setChecked(True) |
| | self.y_displacement_selected(True) |
| | elif rt == "U3": |
| | self.result_widget.rb_z_displacement.setChecked(True) |
| | self.z_displacement_selected(True) |
| | elif rt == "Temp": |
| | self.result_widget.rb_temperature.setChecked(True) |
| | self.temperature_selected(True) |
| | elif rt == "Sabs": |
| | self.result_widget.rb_vm_stress.setChecked(True) |
| | self.vm_stress_selected(True) |
| | elif rt == "MaxPrin": |
| | self.result_widget.rb_maxprin.setChecked(True) |
| | self.max_prin_selected(True) |
| | elif rt == "MinPrin": |
| | self.result_widget.rb_minprin.setChecked(True) |
| | self.min_prin_selected(True) |
| | elif rt == "MaxShear": |
| | self.result_widget.rb_max_shear_stress.setChecked(True) |
| | self.max_shear_selected(True) |
| | elif rt == "MFlow": |
| | self.result_widget.rb_massflowrate.setChecked(True) |
| | self.massflowrate_selected(True) |
| | elif rt == "NPress": |
| | self.result_widget.rb_networkpressure.setChecked(True) |
| | self.networkpressure_selected(True) |
| | elif rt == "Peeq": |
| | self.result_widget.rb_peeq.setChecked(True) |
| | self.peeq_selected(True) |
| |
|
| | sd = FreeCAD.FEM_dialog["show_disp"] |
| | self.result_widget.cb_show_displacement.setChecked(sd) |
| | self.show_displacement(sd) |
| |
|
| | df = FreeCAD.FEM_dialog["disp_factor"] |
| | dfm = FreeCAD.FEM_dialog["disp_factor_max"] |
| | self.result_widget.hsb_displacement_factor.setMaximum(dfm) |
| | self.result_widget.hsb_displacement_factor.setValue(df) |
| | self.result_widget.sb_displacement_factor_max.setValue(dfm) |
| | self.result_widget.sb_displacement_factor.setValue(df) |
| | |
| | self.startAnimate = False |
| | if FreeCAD.FEM_dialog["animate"][0] != -1: |
| | self.result_widget.steps.setValue(FreeCAD.FEM_dialog["animate"][0]) |
| | self.result_widget.loops.setValue(FreeCAD.FEM_dialog["animate"][1]) |
| | self.result_widget.framerate.setValue(FreeCAD.FEM_dialog["animate"][2]) |
| | if FreeCAD.FEM_dialog["animate"][3]: |
| | self.result_widget.rb_full_cycle.setChecked(True) |
| | else: |
| | self.result_widget.rb_half_cycle.setChecked(True) |
| | self.result_widget.sb_displacement_factor.setValue(FreeCAD.FEM_dialog["animate"][4]) |
| | except Exception: |
| | self.restore_initial_result_dialog() |
| |
|
| | def restore_initial_result_dialog(self): |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | FreeCAD.FEM_dialog = { |
| | "results_type": "None", |
| | "show_disp": True, |
| | "disp_factor": 5.0, |
| | "disp_factor_max": 100.0, |
| | "animate": [-1, -1, -1, -1, -1, -1, -1], |
| | } |
| | self.result_widget.sb_displacement_factor_max.setValue(100.0) |
| |
|
| | def getStandardButtons(self): |
| | return QtGui.QDialogButtonBox.Close |
| |
|
| | def get_result_stats(self, type_name): |
| | return resulttools.get_stats(self.result_obj, type_name) |
| |
|
| | def none_selected(self, state): |
| | self.set_label(self.result_obj.Label, "No Contours") |
| | FreeCAD.FEM_dialog["results_type"] = "None" |
| | self.set_result_stats("mm", 0.0, 0.0) |
| | self.reset_mesh_color() |
| | if len(plt.get_fignums()) > 0: |
| | plt.close() |
| |
|
| | |
| | |
| | |
| | |
| | |
| | |
| |
|
| | def abs_displacement_selected(self, state): |
| | if len(self.result_obj.DisplacementLengths) > 0: |
| | self.result_selected( |
| | "Uabs", |
| | self.result_obj.DisplacementLengths, |
| | "mm", |
| | translate("FEM", "Displacement magnitude"), |
| | ) |
| | else: |
| | self.result_widget.rb_none.setChecked(True) |
| | self.none_selected(True) |
| |
|
| | def x_displacement_selected(self, state): |
| | if len(self.result_obj.DisplacementVectors) > 0: |
| | res_disp_u1 = self.get_scalar_disp_list(self.result_obj.DisplacementVectors, 0) |
| | self.result_selected("U1", res_disp_u1, "mm", translate("FEM", "Displacement X")) |
| | else: |
| | self.result_widget.rb_none.setChecked(True) |
| | self.none_selected(True) |
| |
|
| | def y_displacement_selected(self, state): |
| | if len(self.result_obj.DisplacementVectors) > 0: |
| | res_disp_u2 = self.get_scalar_disp_list(self.result_obj.DisplacementVectors, 1) |
| | self.result_selected("U2", res_disp_u2, "mm", translate("FEM", "Displacement Y")) |
| | else: |
| | self.result_widget.rb_none.setChecked(True) |
| | self.none_selected(True) |
| |
|
| | def z_displacement_selected(self, state): |
| | if len(self.result_obj.DisplacementVectors) > 0: |
| | res_disp_u3 = self.get_scalar_disp_list(self.result_obj.DisplacementVectors, 2) |
| | self.result_selected("U3", res_disp_u3, "mm", translate("FEM", "Displacement Z")) |
| | else: |
| | self.result_widget.rb_none.setChecked(True) |
| | self.none_selected(True) |
| |
|
| | def vm_stress_selected(self, state): |
| | if len(self.result_obj.vonMises) > 0: |
| | self.result_selected( |
| | "Sabs", |
| | self.result_obj.vonMises, |
| | "MPa", |
| | translate("FEM", "von Mises stress"), |
| | ) |
| | else: |
| | self.result_widget.rb_none.setChecked(True) |
| | self.none_selected(True) |
| |
|
| | def max_shear_selected(self, state): |
| | if len(self.result_obj.MaxShear) > 0: |
| | self.result_selected( |
| | "MaxShear", |
| | self.result_obj.MaxShear, |
| | "MPa", |
| | translate("FEM", "Maximum shear stress (Tresca)"), |
| | ) |
| | else: |
| | self.result_widget.rb_none.setChecked(True) |
| | self.none_selected(True) |
| |
|
| | def max_prin_selected(self, state): |
| | if len(self.result_obj.PrincipalMax) > 0: |
| | self.result_selected( |
| | "MaxPrin", |
| | self.result_obj.PrincipalMax, |
| | "MPa", |
| | translate("FEM", "Maximum principal stress"), |
| | ) |
| | else: |
| | self.result_widget.rb_none.setChecked(True) |
| | self.none_selected(True) |
| |
|
| | def temperature_selected(self, state): |
| | if len(self.result_obj.Temperature) > 0: |
| | self.result_selected( |
| | "Temp", |
| | self.result_obj.Temperature, |
| | "K", |
| | translate("FEM", "Temperature"), |
| | ) |
| | else: |
| | self.result_widget.rb_none.setChecked(True) |
| | self.none_selected(True) |
| |
|
| | def massflowrate_selected(self, state): |
| | if len(self.result_obj.MassFlowRate) > 0: |
| | self.result_selected( |
| | "MFlow", |
| | self.result_obj.MassFlowRate, |
| | "kg/s", |
| | translate("FEM", "Mass flow rate"), |
| | ) |
| | else: |
| | self.result_widget.rb_none.setChecked(True) |
| | self.none_selected(True) |
| |
|
| | def networkpressure_selected(self, state): |
| | if len(self.result_obj.NetworkPressure) > 0: |
| | self.result_selected( |
| | "NPress", |
| | self.result_obj.NetworkPressure, |
| | "MPa", |
| | translate("FEM", "Network pressure"), |
| | ) |
| | else: |
| | self.result_widget.rb_none.setChecked(True) |
| | self.none_selected(True) |
| |
|
| | def min_prin_selected(self, state): |
| | if len(self.result_obj.PrincipalMin) > 0: |
| | self.result_selected( |
| | "MinPrin", |
| | self.result_obj.PrincipalMin, |
| | "MPa", |
| | translate("FEM", "Minimum principal stress"), |
| | ) |
| | else: |
| | self.result_widget.rb_none.setChecked(True) |
| | self.none_selected(True) |
| |
|
| | def peeq_selected(self, state): |
| | if len(self.result_obj.Peeq) > 0: |
| | self.result_selected( |
| | "Peeq", |
| | self.result_obj.Peeq, |
| | "", |
| | translate("FEM", "Equivalent plastic strain"), |
| | ) |
| | else: |
| | self.result_widget.rb_none.setChecked(True) |
| | self.none_selected(True) |
| |
|
| | def show_histogram_clicked(self): |
| | if len(plt.get_fignums()) > 0: |
| | plt.show() |
| | else: |
| | |
| | |
| | |
| | if FreeCAD.FEM_dialog["results_type"] != "None": |
| | self.restore_result_dialog() |
| | if len(plt.get_fignums()) > 0: |
| | plt.show() |
| | else: |
| | QtGui.QMessageBox.information( |
| | None, |
| | self.result_obj.Label + " - " + translate("FEM", "Information"), |
| | translate( |
| | "FEM", |
| | "No histogram available.\nPlease select a result type first.", |
| | ), |
| | ) |
| |
|
| | def user_defined_text(self, equation): |
| | FreeCAD.FEM_dialog["results_type"] = "user" |
| | self.result_widget.user_def_eq.toPlainText() |
| |
|
| | def calculate(self): |
| | |
| | |
| | P1 = np.array(self.result_obj.PrincipalMax) |
| | P2 = np.array(self.result_obj.PrincipalMed) |
| | P3 = np.array(self.result_obj.PrincipalMin) |
| | MS = np.array(self.result_obj.MaxShear) |
| | vM = np.array(self.result_obj.vonMises) |
| | Peeq = np.array(self.result_obj.Peeq) |
| | T = np.array(self.result_obj.Temperature) |
| | MF = np.array(self.result_obj.MassFlowRate) |
| | NP = np.array(self.result_obj.NetworkPressure) |
| | sxx = np.array(self.result_obj.NodeStressXX) |
| | syy = np.array(self.result_obj.NodeStressYY) |
| | szz = np.array(self.result_obj.NodeStressZZ) |
| | sxy = np.array(self.result_obj.NodeStressXY) |
| | sxz = np.array(self.result_obj.NodeStressXZ) |
| | syz = np.array(self.result_obj.NodeStressYZ) |
| | exx = np.array(self.result_obj.NodeStrainXX) |
| | eyy = np.array(self.result_obj.NodeStrainYY) |
| | ezz = np.array(self.result_obj.NodeStrainZZ) |
| | exy = np.array(self.result_obj.NodeStrainXY) |
| | exz = np.array(self.result_obj.NodeStrainXZ) |
| | eyz = np.array(self.result_obj.NodeStrainYZ) |
| | rx = np.array(self.result_obj.ReinforcementRatio_x) |
| | ry = np.array(self.result_obj.ReinforcementRatio_y) |
| | rz = np.array(self.result_obj.ReinforcementRatio_z) |
| | mc = np.array(self.result_obj.MohrCoulomb) |
| | |
| | dispvectors = np.array(self.result_obj.DisplacementVectors) |
| | x = np.array(dispvectors[:, 0]) |
| | y = np.array(dispvectors[:, 1]) |
| | z = np.array(dispvectors[:, 2]) |
| | s1x, s1y, s1z = np.array([]), np.array([]), np.array([]) |
| | s2x, s2y, s2z = np.array([]), np.array([]), np.array([]) |
| | s3x, s3y, s3z = np.array([]), np.array([]), np.array([]) |
| | |
| | if self.result_obj.PS1Vector: |
| | ps1vector = np.array(self.result_obj.PS1Vector) |
| | s1x = np.array(ps1vector[:, 0]) |
| | s1y = np.array(ps1vector[:, 1]) |
| | s1z = np.array(ps1vector[:, 2]) |
| | if self.result_obj.PS2Vector: |
| | ps2vector = np.array(self.result_obj.PS2Vector) |
| | s2x = np.array(ps2vector[:, 0]) |
| | s2y = np.array(ps2vector[:, 1]) |
| | s2z = np.array(ps2vector[:, 2]) |
| | if self.result_obj.PS3Vector: |
| | ps3vector = np.array(self.result_obj.PS3Vector) |
| | s3x = np.array(ps3vector[:, 0]) |
| | s3y = np.array(ps3vector[:, 1]) |
| | s3z = np.array(ps3vector[:, 2]) |
| |
|
| | FreeCAD.FEM_dialog["results_type"] = "None" |
| | self.update() |
| | self.restore_result_dialog() |
| | userdefined_eq = self.result_widget.user_def_eq.toPlainText() |
| | self.results_name = "User Defined: " + userdefined_eq |
| |
|
| | |
| | |
| | from ply import lex |
| | from ply import yacc |
| | import femtools.tokrules as tokrules |
| |
|
| | identifiers = [ |
| | "x", |
| | "y", |
| | "z", |
| | "T", |
| | "vM", |
| | "Peeq", |
| | "P1", |
| | "P2", |
| | "P3", |
| | "sxx", |
| | "syy", |
| | "szz", |
| | "sxy", |
| | "sxz", |
| | "syz", |
| | "exx", |
| | "eyy", |
| | "ezz", |
| | "exy", |
| | "exz", |
| | "eyz", |
| | "MS", |
| | "MF", |
| | "NP", |
| | "rx", |
| | "ry", |
| | "rz", |
| | "mc", |
| | "s1x", |
| | "s1y", |
| | "s1z", |
| | "s2x", |
| | "s2y", |
| | "s2z", |
| | "s3x", |
| | "s3y", |
| | "s3z", |
| | ] |
| | tokrules.names = {} |
| | for i in identifiers: |
| | tokrules.names[i] = locals()[i] |
| |
|
| | lexer = lex.lex(module=tokrules) |
| | yacc.parse(input=f"UserDefinedFormula={userdefined_eq}", lexer=lexer) |
| | UserDefinedFormula = tokrules.names["UserDefinedFormula"].tolist() |
| | tokrules.names = {} |
| | |
| |
|
| | if UserDefinedFormula: |
| | self.result_obj.UserDefined = UserDefinedFormula |
| | minm = min(UserDefinedFormula) |
| | maxm = max(UserDefinedFormula) |
| | self.update_colors_stats(UserDefinedFormula, "", minm, maxm) |
| |
|
| | |
| | self.result_obj.Document.recompute() |
| |
|
| | def get_scalar_disp_list(self, vector_list, axis): |
| | |
| | d = list(zip(*self.result_obj.DisplacementVectors)) |
| | scalar_list = list(d[axis]) |
| | return scalar_list |
| |
|
| | def result_selected(self, res_type, res_values, res_unit, res_title): |
| | self.results_name = res_title |
| | FreeCAD.FEM_dialog["results_type"] = res_type |
| | (minm, maxm) = self.get_result_stats(res_type) |
| | self.update_colors_stats(res_values, res_unit, minm, maxm) |
| |
|
| | if len(plt.get_fignums()) > 0: |
| | plt.close() |
| | plt.ioff() |
| | plt.figure(res_title) |
| | plt.hist(res_values, bins=50, alpha=0.5, facecolor="blue") |
| | plt.xlabel(res_unit) |
| | plt.title(translate("FEM", "Histogram of {}").format(res_title)) |
| | plt.ylabel(translate("FEM", "Nodes")) |
| | plt.grid(True) |
| | fig_manager = plt.get_current_fig_manager() |
| | |
| | |
| | |
| | |
| | fig_manager.window.setParent(FreeCADGui.getMainWindow()) |
| | fig_manager.window.setWindowFlag(QtCore.Qt.Tool) |
| |
|
| | def update_colors_stats(self, res_values, res_unit, minm, maxm): |
| | self.set_label(self.result_obj.Label, self.results_name) |
| | QApplication.setOverrideCursor(Qt.WaitCursor) |
| | if self.suitable_results: |
| | self.mesh_obj.ViewObject.setNodeColorByScalars(self.result_obj.NodeNumbers, res_values) |
| | self.set_result_stats(res_unit, minm, maxm) |
| | QtGui.QApplication.restoreOverrideCursor() |
| |
|
| | def set_result_stats(self, unit, minm, maxm): |
| | self.result_widget.le_min.setProperty("unit", unit) |
| | self.result_widget.le_min.setProperty("rawText", f"{minm:.6} {unit}") |
| | self.result_widget.le_max.setProperty("unit", unit) |
| | self.result_widget.le_max.setProperty("rawText", f"{maxm:.6} {unit}") |
| |
|
| | def update_displacement(self, factor=None): |
| | if factor is None: |
| | if FreeCAD.FEM_dialog["show_disp"]: |
| | factor = self.result_widget.sb_displacement_factor.value() |
| | else: |
| | factor = 0.0 |
| | self.mesh_obj.ViewObject.applyDisplacement(factor) |
| |
|
| | def show_displacement(self, checked): |
| | QApplication.setOverrideCursor(Qt.WaitCursor) |
| | FreeCAD.FEM_dialog["show_disp"] = checked |
| | if "result_obj" in FreeCAD.FEM_dialog: |
| | if FreeCAD.FEM_dialog["result_obj"] != self.result_obj: |
| | self.update_displacement() |
| | FreeCAD.FEM_dialog["result_obj"] = self.result_obj |
| | if self.suitable_results: |
| | self.mesh_obj.ViewObject.setNodeDisplacementByVectors( |
| | self.result_obj.NodeNumbers, self.result_obj.DisplacementVectors |
| | ) |
| | self.update_displacement() |
| | QtGui.QApplication.restoreOverrideCursor() |
| |
|
| | def hsb_disp_factor_changed(self, value): |
| | self.result_widget.sb_displacement_factor.setValue( |
| | value / 100.0 * self.result_widget.sb_displacement_factor_max.value() |
| | ) |
| | self.update_displacement() |
| |
|
| | def sb_disp_factor_max_changed(self, value): |
| | self.slider_max = True |
| | FreeCAD.FEM_dialog["disp_factor_max"] = value |
| | if value < self.result_widget.sb_displacement_factor.value(): |
| | self.result_widget.sb_displacement_factor.setValue(value) |
| | if value == 0.0: |
| | self.result_widget.hsb_displacement_factor.setValue(0) |
| | else: |
| | self.result_widget.hsb_displacement_factor.setValue( |
| | round(self.result_widget.sb_displacement_factor.value() / value * 100.0) |
| | ) |
| | self.slider_max = False |
| |
|
| | def sb_disp_factor_changed(self, value): |
| | |
| | |
| | |
| | if len(inspect.stack(0)) < self.recurlim: |
| | FreeCAD.FEM_dialog["disp_factor"] = value |
| | if value > self.result_widget.sb_displacement_factor_max.value(): |
| | self.result_widget.sb_displacement_factor.setValue( |
| | self.result_widget.sb_displacement_factor_max.value() |
| | ) |
| | if self.result_widget.sb_displacement_factor_max.value() == 0.0: |
| | self.result_widget.hsb_displacement_factor.setValue(0.0) |
| | else: |
| | self.result_widget.hsb_displacement_factor.setValue( |
| | round(value / self.result_widget.sb_displacement_factor_max.value() * 100.0) |
| | ) |
| |
|
| | def disable_empty_result_buttons(self): |
| | """disable radio buttons if result does not exists in result object""" |
| | """assignments |
| | DisplacementLengths --> rb_abs_displacement |
| | DisplacementVectors --> rb_x_displacement, rb_y_displacement, rb_z_displacement |
| | Temperature --> rb_temperature |
| | vonMises --> rb_vm_stress |
| | PrincipalMax --> rb_maxprin |
| | PrincipalMin --> rb_minprin |
| | MaxShear --> rb_max_shear_stress |
| | MassFlowRate --> rb_massflowrate |
| | NetworkPressure --> rb_networkpressure |
| | Peeq --> rb_peeq""" |
| | if len(self.result_obj.DisplacementLengths) == 0: |
| | self.result_widget.rb_abs_displacement.setEnabled(0) |
| | if len(self.result_obj.DisplacementVectors) == 0: |
| | self.result_widget.rb_x_displacement.setEnabled(0) |
| | self.result_widget.rb_y_displacement.setEnabled(0) |
| | self.result_widget.rb_z_displacement.setEnabled(0) |
| | if len(self.result_obj.Temperature) == 0: |
| | self.result_widget.rb_temperature.setEnabled(0) |
| | if len(self.result_obj.vonMises) == 0: |
| | self.result_widget.rb_vm_stress.setEnabled(0) |
| | if len(self.result_obj.PrincipalMax) == 0: |
| | self.result_widget.rb_maxprin.setEnabled(0) |
| | if len(self.result_obj.PrincipalMin) == 0: |
| | self.result_widget.rb_minprin.setEnabled(0) |
| | if len(self.result_obj.MaxShear) == 0: |
| | self.result_widget.rb_max_shear_stress.setEnabled(0) |
| | if len(self.result_obj.MassFlowRate) == 0: |
| | self.result_widget.rb_massflowrate.setEnabled(0) |
| | if len(self.result_obj.NetworkPressure) == 0: |
| | self.result_widget.rb_networkpressure.setEnabled(0) |
| | if len(self.result_obj.Peeq) == 0: |
| | self.result_widget.rb_peeq.setEnabled(0) |
| |
|
| | def update(self): |
| | self.reset_result_mesh() |
| | self.suitable_results = False |
| | self.disable_empty_result_buttons() |
| | if self.mesh_obj.FemMesh.NodeCount == 0: |
| | the_error_messagetext = ( |
| | "FEM: there are no nodes in result mesh, there will be nothing to show." |
| | ) |
| | error_message = translate("FEM", the_error_messagetext) + "\n" |
| | FreeCAD.Console.PrintError(error_message) |
| | QtGui.QMessageBox.critical( |
| | None, translate("FEM", "Result mesh is empty"), error_message |
| | ) |
| | elif self.mesh_obj.FemMesh.NodeCount == len(self.result_obj.NodeNumbers): |
| | self.suitable_results = True |
| | hide_parts_constraints() |
| | else: |
| | if not self.mesh_obj.FemMesh.VolumeCount: |
| | the_error_messagetext = ( |
| | "FEM: Graphical bending stress output " |
| | "for beam or shell FEM Meshes not yet supported." |
| | ) |
| | error_message = translate("FEM", the_error_messagetext) + "\n" |
| | FreeCAD.Console.PrintError(error_message) |
| | QtGui.QMessageBox.critical( |
| | None, translate("FEM", "No result object"), error_message |
| | ) |
| | else: |
| | the_error_messagetext = ( |
| | "FEM: Result node numbers are not equal to FEM Mesh NodeCount." |
| | ) |
| | error_message = translate("FEM", the_error_messagetext) + "\n" |
| | FreeCAD.Console.PrintError(error_message) |
| | QtGui.QMessageBox.critical( |
| | None, translate("FEM", "No result object"), error_message |
| | ) |
| |
|
| | def reset_mesh_color(self): |
| | self.mesh_obj.ViewObject.NodeColor = {} |
| | self.mesh_obj.ViewObject.ElementColor = {} |
| | self.mesh_obj.ViewObject.resetNodeColor() |
| |
|
| | def reset_result_mesh(self): |
| | self.mesh_obj.ViewObject.resetNodeDisplacement() |
| | self.reset_mesh_color() |
| |
|
| | def reject(self): |
| | self.reset_result_mesh() |
| | plt.close() |
| | |
| | |
| | FreeCADGui.Control.closeDialog() |
| | FreeCADGui.ActiveDocument.resetEdit() |
| | if len(self.animateText) > 0: |
| | for a in self.animateText: |
| | a.hide() |
| | self.animateText = [] |
| | self.startAnimate = False |
| | FreeCAD.FEM_dialog["animate"][0] = self.result_widget.steps.value() |
| | FreeCAD.FEM_dialog["animate"][1] = self.result_widget.loops.value() |
| | FreeCAD.FEM_dialog["animate"][2] = self.result_widget.framerate.value() |
| | FreeCAD.FEM_dialog["animate"][3] = self.result_widget.rb_full_cycle.isChecked() |
| | try: |
| | FreeCAD.FEM_dialog["animate"][4] = self.result_widget.sb_displacement_factor.value() |
| | except: |
| | FreeCAD.FEM_dialog["animate"][4] = 1 |
| |
|
| | |
| |
|
| | def animate_displacement(self): |
| | if "result_obj" in FreeCAD.FEM_dialog: |
| | if FreeCAD.FEM_dialog["result_obj"] != self.result_obj: |
| | self.update_displacement() |
| | self.result_widget.cb_show_displacement.setChecked(True) |
| | FreeCAD.FEM_dialog["result_obj"] = self.result_obj |
| | if self.suitable_results: |
| | self.mesh_obj.ViewObject.setNodeDisplacementByVectors( |
| | self.result_obj.NodeNumbers, self.result_obj.DisplacementVectors |
| | ) |
| | self.result_widget.startButton.setText("Stop Animation") |
| | frame_rate = 10 |
| | self.hsb_displacement_factor = self.result_widget.sb_displacement_factor.value() |
| | frame_rate = self.result_widget.framerate.value() |
| | steps_per_cycle = int(self.result_widget.steps.value()) |
| | number_cycles = int(self.result_widget.loops.value()) |
| |
|
| | sinc = inc = math.pi / steps_per_cycle * 2.0 |
| | self.set_label(self.result_obj.Label, self.results_name) |
| |
|
| | done = False |
| | |
| | loops = max(1, number_cycles) * steps_per_cycle + 1 |
| | st = 0 |
| | for loop in range(0, loops): |
| | if self.result_widget.rb_half_cycle.isChecked(): |
| | if number_cycles > 0: |
| | inc = sinc / 2 |
| | else: |
| | inc = sinc / 4 |
| | |
| | if self.result_widget.rb_full_cycle.isChecked(): |
| | self.mesh_obj.ViewObject.applyDisplacement( |
| | math.sin(st * inc) * self.hsb_displacement_factor |
| | ) |
| | elif self.result_widget.rb_half_cycle.isChecked(): |
| | |
| | if number_cycles > 0: |
| | self.mesh_obj.ViewObject.applyDisplacement( |
| | abs(math.sin(st * inc)) * self.hsb_displacement_factor |
| | ) |
| | elif number_cycles <= 0: |
| | self.mesh_obj.ViewObject.applyDisplacement( |
| | abs(math.sin(st * inc)) * self.hsb_displacement_factor |
| | ) |
| | else: |
| | print("No cycle type selected") |
| | FreeCADGui.updateGui() |
| | if not self.startAnimate: |
| | done = True |
| | break |
| | time.sleep(1.0 / frame_rate) |
| | st += 1 |
| | |
| | |
| | try: |
| | self.result_widget.startButton.setText("Start Animation") |
| | except: |
| | pass |
| | QtGui.QApplication.restoreOverrideCursor() |
| | self.startAnimate = False |
| |
|
| | def value_changed(self, dummy, value, myType): |
| | |
| | if myType == "startButton": |
| | if not self.startAnimate: |
| | self.startAnimate = True |
| | self.animate_displacement() |
| | else: |
| | self.startAnimate = False |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | |
| | else: |
| | pass |
| | try: |
| | self.hsb_displacement_factor = self.result_widget.sb_displacement_factor.value() |
| |
|
| | except: |
| | pass |
| | return |
| |
|
| | def set_label(self, result_name, mesh_data): |
| | if len(self.animateText) == 0: |
| | self.animateText.append(CreateLabels.createLabel((-0.98, 0.90, 0), result_name)) |
| | self.animateText.append(CreateLabels.createLabel((-0.98, 0.70, 0), mesh_data)) |
| | else: |
| | self.animateText[1].set_text(mesh_data) |
| | pass |
| |
|
| |
|
| | |
| |
|
| |
|
| | |
| | def hide_parts_constraints(): |
| | from FemGui import getActiveAnalysis |
| |
|
| | fem_prefs = FreeCAD.ParamGet("User parameter:BaseApp/Preferences/Mod/Fem/General") |
| | hide_constraints = fem_prefs.GetBool("HideConstraint", False) |
| | if hide_constraints: |
| | for o in FreeCAD.ActiveDocument.Objects: |
| | if o.isDerivedFrom("Fem::FemAnalysis"): |
| | for acnstrmesh in getActiveAnalysis().Group: |
| | if "Constraint" in acnstrmesh.TypeId: |
| | acnstrmesh.ViewObject.Visibility = False |
| | break |
| |
|
| |
|
| | def get_displacement_scale_factor(res_obj): |
| | node_items = res_obj.Mesh.FemMesh.Nodes.items() |
| | displacements = res_obj.DisplacementVectors |
| | |
| | if len(displacements) == 0: |
| | return 1 |
| | x_max, y_max, z_max = map(max, zip(*displacements)) |
| | positions = [] |
| | for k, v in node_items: |
| | positions.append(v) |
| | p_x_max, p_y_max, p_z_max = map(max, zip(*positions)) |
| | p_x_min, p_y_min, p_z_min = map(min, zip(*positions)) |
| | x_span = abs(p_x_max - p_x_min) |
| | y_span = abs(p_y_max - p_y_min) |
| | z_span = abs(p_z_max - p_z_min) |
| | span = max(x_span, y_span, z_span) |
| | max_disp = max(x_max, y_max, z_max) |
| | if max_disp == 0.0: |
| | return 0.0 |
| | |
| | max_allowed_disp = 0.01 * span |
| | scale = max_allowed_disp / max_disp |
| | return scale |
| |
|