From 35cde485889b8b6c9980fcb80723bfe97e06a31c Mon Sep 17 00:00:00 2001 From: UR-0 Date: Mon, 17 Feb 2020 18:53:43 +0100 Subject: [PATCH] FEM: result task panel, Histograms added to result object instead of average stats values --- src/Mod/Fem/Gui/Resources/ui/ResultShow.ui | 56 +++++------- .../_ViewProviderFemResultMechanical.py | 46 +++++++--- src/Mod/Fem/femresult/resulttools.py | 85 ++++++++----------- 3 files changed, 90 insertions(+), 97 deletions(-) diff --git a/src/Mod/Fem/Gui/Resources/ui/ResultShow.ui b/src/Mod/Fem/Gui/Resources/ui/ResultShow.ui index 987ff49db6..b91e446639 100644 --- a/src/Mod/Fem/Gui/Resources/ui/ResultShow.ui +++ b/src/Mod/Fem/Gui/Resources/ui/ResultShow.ui @@ -122,37 +122,10 @@ - - + + - Avg: - - - - - - - true - - - mm - - - - - - - Max: - - - - - - - true - - - mm + Min: @@ -166,10 +139,27 @@ - - + + - Min: + Max: + + + + + + + true + + + mm + + + + + + + Histogram diff --git a/src/Mod/Fem/femguiobjects/_ViewProviderFemResultMechanical.py b/src/Mod/Fem/femguiobjects/_ViewProviderFemResultMechanical.py index cc528e4c17..f30f074895 100644 --- a/src/Mod/Fem/femguiobjects/_ViewProviderFemResultMechanical.py +++ b/src/Mod/Fem/femguiobjects/_ViewProviderFemResultMechanical.py @@ -39,6 +39,8 @@ from PySide import QtGui from PySide.QtCore import Qt from PySide.QtGui import QApplication import numpy as np +import matplotlib.pyplot as plt + False if FemGui.__name__ else True # flake8, dummy FemGui usage @@ -208,6 +210,11 @@ class _TaskPanelFemResultShow: self.peeq_selected ) + # stats + self.result_widget.show_histogram.clicked.connect( + self.show_histogram_clicked + ) + # displacement QtCore.QObject.connect( self.result_widget.cb_show_displacement, @@ -228,9 +235,7 @@ class _TaskPanelFemResultShow: ) # user defined equation - QtCore.QObject.connect( - self.result_widget.user_def_eq, - QtCore.SIGNAL("textchanged()"), + self.result_widget.user_def_eq.textChanged.connect( self.user_defined_text ) QtCore.QObject.connect( @@ -331,8 +336,10 @@ class _TaskPanelFemResultShow: def none_selected(self, state): FreeCAD.FEM_dialog["results_type"] = "None" - self.set_result_stats("mm", 0.0, 0.0, 0.0) + self.set_result_stats("mm", 0.0, 0.0) self.reset_mesh_color() + if len(plt.get_fignums()) > 0: + plt.close() # if an analysis has different result types and one has # stress and the other not the restore result dialog @@ -434,6 +441,9 @@ class _TaskPanelFemResultShow: self.result_widget.rb_none.setChecked(True) self.none_selected(True) + def show_histogram_clicked(self): + plt.show() + def user_defined_text(self, equation): FreeCAD.FEM_dialog["results_type"] = "user" self.result_widget.user_def_eq.toPlainText() @@ -521,9 +531,8 @@ class _TaskPanelFemResultShow: if UserDefinedFormula: self.result_obj.UserDefined = UserDefinedFormula minm = min(UserDefinedFormula) - avg = sum(UserDefinedFormula) / len(UserDefinedFormula) maxm = max(UserDefinedFormula) - self.update_colors_stats(UserDefinedFormula, "", minm, avg, maxm) + self.update_colors_stats(UserDefinedFormula, "", minm, maxm) # Dummy use of the variables to get around flake8 error del x, y, z, T, vM, Peeq, P1, P2, P3 @@ -540,24 +549,32 @@ class _TaskPanelFemResultShow: def result_selected(self, res_type, res_values, res_unit): FreeCAD.FEM_dialog["results_type"] = res_type - (minm, avg, maxm) = self.get_result_stats(res_type) - self.update_colors_stats(res_values, res_unit, minm, avg, maxm) + (minm, maxm) = self.get_result_stats(res_type) + self.update_colors_stats(res_values, res_unit, minm, maxm) - def update_colors_stats(self, res_values, res_unit, minm, avg, maxm): + if len(plt.get_fignums()) > 0: + plt.close() + plt.hist(res_values, bins = 50, alpha = 0.5, facecolor = "blue") + plt.xlabel(res_unit) + plt.title("Histogram of {}".format(res_type)) + plt.ylabel("Nodes") + plt.grid(True) + fig_manager = plt.get_current_fig_manager() + fig_manager.window.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) # stay ontop + + def update_colors_stats(self, res_values, res_unit, minm, maxm): 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, avg, maxm) + self.set_result_stats(res_unit, minm, maxm) QtGui.QApplication.restoreOverrideCursor() - def set_result_stats(self, unit, minm, avg, maxm): + def set_result_stats(self, unit, minm, maxm): self.result_widget.le_min.setProperty("unit", unit) self.result_widget.le_min.setProperty("rawText", "{:.6} {}".format(minm, unit)) - self.result_widget.le_avg.setProperty("unit", unit) - self.result_widget.le_avg.setProperty("rawText", "{:.6} {}".format(avg, unit)) self.result_widget.le_max.setProperty("unit", unit) self.result_widget.le_max.setProperty("rawText", "{:.6} {}".format(maxm, unit)) @@ -620,7 +637,7 @@ class _TaskPanelFemResultShow: DisplacementLengths --> rb_abs_displacement DisplacementVectors --> rb_x_displacement, rb_y_displacement, rb_z_displacement Temperature --> rb_temperature - vonMises --> rb_vm_stress + vonMises --> rb_vm_stress PrincipalMax --> rb_maxprin PrincipalMin --> rb_minprin MaxShear --> rb_max_shear_stress @@ -681,6 +698,7 @@ class _TaskPanelFemResultShow: def reject(self): self.reset_result_mesh() + plt.close() # if the tasks panel is called from Command obj is not in edit mode # thus reset edit does not close the dialog, maybe don't call but set in edit instead FreeCADGui.Control.closeDialog() diff --git a/src/Mod/Fem/femresult/resulttools.py b/src/Mod/Fem/femresult/resulttools.py index 050e1aebec..be35b03775 100644 --- a/src/Mod/Fem/femresult/resulttools.py +++ b/src/Mod/Fem/femresult/resulttools.py @@ -171,7 +171,7 @@ def show_color_by_scalar_with_cutoff(resultobj, values, limit=None): def get_stats(res_obj, result_type): - """Returns minimum, average and maximum value for provided result type + """Returns minimum and maximum value for provided result type Parameters ---------- @@ -180,12 +180,12 @@ def get_stats(res_obj, result_type): result_type : str type of FEM result allowed are: see dict keys in def get_all_stats() - None - always return (0.0, 0.0, 0.0) + None - always return (0.0, 0.0) """ match_table = get_all_stats(res_obj) - match_table["None"] = (0.0, 0.0, 0.0) + match_table["None"] = (0.0, 0.0) stats = () if result_type in match_table: stats = match_table[result_type] @@ -235,19 +235,19 @@ def get_all_stats(res_obj): m = res_obj.Stats stats_dict = { - "U1": (m[0], m[1], m[2]), - "U2": (m[3], m[4], m[5]), - "U3": (m[6], m[7], m[8]), - "Uabs": (m[9], m[10], m[11]), - "Sabs": (m[12], m[13], m[14]), - "MaxPrin": (m[15], m[16], m[17]), - "MidPrin": (m[18], m[19], m[20]), - "MinPrin": (m[21], m[22], m[23]), - "MaxShear": (m[24], m[25], m[26]), - "Peeq": (m[27], m[28], m[29]), - "Temp": (m[30], m[31], m[32]), - "MFlow": (m[33], m[34], m[35]), - "NPress": (m[36], m[37], m[38]) + "U1": (m[0], m[1]), + "U2": (m[2], m[3]), + "U3": (m[4], m[5]), + "Uabs": (m[6], m[7]), + "Sabs": (m[8], m[9]), + "MaxPrin": (m[10], m[11]), + "MidPrin": (m[12], m[13]), + "MinPrin": (m[14], m[15]), + "MaxShear": (m[16], m[17]), + "Peeq": (m[18], m[19]), + "Temp": (m[20], m[21]), + "MFlow": (m[22], m[23]), + "NPress": (m[24], m[25]) } return stats_dict @@ -264,76 +264,61 @@ def fill_femresult_stats(res_obj): FreeCAD.Console.PrintLog( "Calculate stats list for result obj: " + res_obj.Name + "\n" ) - no_of_values = 1 # to avoid division by zero # set stats values to 0, they may not exist in res_obj - x_min = y_min = z_min = x_max = y_max = z_max = x_avg = y_avg = z_avg = 0 - a_max = a_min = a_avg = s_max = s_min = s_avg = 0 - p1_min = p1_avg = p1_max = p2_min = p2_avg = p2_max = p3_min = p3_avg = p3_max = 0 - ms_min = ms_avg = ms_max = peeq_min = peeq_avg = peeq_max = 0 - temp_min = temp_avg = temp_max = 0 - mflow_min = mflow_avg = mflow_max = npress_min = npress_avg = npress_max = 0 + x_min = y_min = z_min = x_max = y_max = z_max = 0 + a_max = a_min = s_max = s_min = 0 + p1_min = p1_max = p2_min = p2_max = p3_min = p3_max = 0 + ms_min = ms_max = peeq_min = peeq_max = 0 + temp_min = temp_max = 0 + mflow_min = mflow_max = npress_min = npress_max = 0 if res_obj.DisplacementVectors: - no_of_values = len(res_obj.DisplacementVectors) x_max, y_max, z_max = map(max, zip(*res_obj.DisplacementVectors)) x_min, y_min, z_min = map(min, zip(*res_obj.DisplacementVectors)) - sum_list = map(sum, zip(*res_obj.DisplacementVectors)) - x_avg, y_avg, z_avg = [i / no_of_values for i in sum_list] a_min = min(res_obj.DisplacementLengths) - a_avg = sum(res_obj.DisplacementLengths) / no_of_values a_max = max(res_obj.DisplacementLengths) if res_obj.vonMises: s_min = min(res_obj.vonMises) - s_avg = sum(res_obj.vonMises) / no_of_values s_max = max(res_obj.vonMises) if res_obj.PrincipalMax: p1_min = min(res_obj.PrincipalMax) - p1_avg = sum(res_obj.PrincipalMax) / no_of_values p1_max = max(res_obj.PrincipalMax) if res_obj.PrincipalMed: p2_min = min(res_obj.PrincipalMed) - p2_avg = sum(res_obj.PrincipalMed) / no_of_values p2_max = max(res_obj.PrincipalMed) if res_obj.PrincipalMin: p3_min = min(res_obj.PrincipalMin) - p3_avg = sum(res_obj.PrincipalMin) / no_of_values p3_max = max(res_obj.PrincipalMin) if res_obj.MaxShear: ms_min = min(res_obj.MaxShear) - ms_avg = sum(res_obj.MaxShear) / no_of_values ms_max = max(res_obj.MaxShear) if res_obj.Peeq: peeq_min = min(res_obj.Peeq) - peeq_avg = sum(res_obj.Peeq) / no_of_values peeq_max = max(res_obj.Peeq) if res_obj.Temperature: temp_min = min(res_obj.Temperature) - temp_avg = sum(res_obj.Temperature) / no_of_values temp_max = max(res_obj.Temperature) if res_obj.MassFlowRate: # DisplacementVectors is empty, no_of_values needs to be set - no_of_values = len(res_obj.MassFlowRate) mflow_min = min(res_obj.MassFlowRate) - mflow_avg = sum(res_obj.MassFlowRate) / no_of_values mflow_max = max(res_obj.MassFlowRate) if res_obj.NetworkPressure: npress_min = min(res_obj.NetworkPressure) - npress_avg = sum(res_obj.NetworkPressure) / no_of_values npress_max = max(res_obj.NetworkPressure) - res_obj.Stats = [x_min, x_avg, x_max, - y_min, y_avg, y_max, - z_min, z_avg, z_max, - a_min, a_avg, a_max, - s_min, s_avg, s_max, - p1_min, p1_avg, p1_max, - p2_min, p2_avg, p2_max, - p3_min, p3_avg, p3_max, - ms_min, ms_avg, ms_max, - peeq_min, peeq_avg, peeq_max, - temp_min, temp_avg, temp_max, - mflow_min, mflow_avg, mflow_max, - npress_min, npress_avg, npress_max] + res_obj.Stats = [x_min, x_max, + y_min, y_max, + z_min, z_max, + a_min, a_max, + s_min, s_max, + p1_min, p1_max, + p2_min, p2_max, + p3_min, p3_max, + ms_min, ms_max, + peeq_min, peeq_max, + temp_min, temp_max, + mflow_min, mflow_max, + npress_min, npress_max] """ stat_types = [ "U1",