Skip to content

Commit

Permalink
Statistical decisionmaking tools added to results screen (#62)
Browse files Browse the repository at this point in the history
* Correlation coefficients and pareto graphs (#57)

* Deneme

* Correlation coef and Pareto graphs

* Anova F values (#59)

* Deneme

* Correlation coef and Pareto graphs

* Anova F values

---------

Co-authored-by: Serdar Turgut İnce <[email protected]>

* version number changed

---------

Co-authored-by: ibrahim halil bedük <[email protected]>
  • Loading branch information
Serince and ibrabdk authored Sep 19, 2024
1 parent 63e6008 commit 01bed9f
Show file tree
Hide file tree
Showing 5 changed files with 717 additions and 364 deletions.
204 changes: 201 additions & 3 deletions fembygen/Results.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import FreeCAD
import FreeCADGui
import PySide
from PySide2.QtWidgets import QTableWidgetItem
import matplotlib.pyplot as plt
import os.path
import numpy as np
from fembygen import Common
import glob
import functools
from femresult.resulttools import fill_femresult_stats
from scipy.stats import pearsonr , f_oneway

from femresult.resulttools import fill_femresult_stats
from FreeCAD.Plot import Plot

def makeResult():
try:
Expand Down Expand Up @@ -88,11 +92,14 @@ def __init__(self, object):
FreeCAD.Console.PrintMessage("Calculating metrics...\n")
self.calcAndSaveFEAMetrics()
self.form.arrange.clicked.connect(self.ranking)
self.form.comboBox_3.currentIndexChanged.connect(self.update_anova_table)
self.form.comboBox_2.currentIndexChanged.connect(self.plotParetoGraph)
self.form.comboBox.currentIndexChanged.connect(self.update_table)

self.updateResultsTableAll()
self.updateResultsTableSum()
self.doc.save()

def updateResultsTableAll(self):
header = self.doc.Results.FEAMetricsAll[0]
items = self.doc.Results.FEAMetricsAll[1:]
Expand Down Expand Up @@ -422,14 +429,205 @@ def ranking(self):
normTable = np.zeros((row, column))
for i in range(column):
normTable[:, i] = 1-self.normalize(table[:, i])

# Calculating score by using weight and normalized results
score = volume*normTable[:, 0] + maxs*normTable[:, 1] + maxd*normTable[:, 2] + \
means*normTable[:, 3] + intEn * \
normTable[:, 4] + std*normTable[:, 5]
self.updateResultsTableSum(score)
except:
FreeCAD.Console.PrintError('Total weignt needs to be 100\n')

def corelation(self):
master = self.doc
results = np.array(
master.Results.FEAMetricsSum[1:], dtype=np.dtype("float"))
parameters1 = np.array(
master.Generate.GeneratedParameters, dtype=np.dtype("float"))
parameterValues = np.transpose(parameters1)
parameters = master.Generate.ParametersName

cor_table = []
for index, param in enumerate(parameters):
row = [param]
for j in range(len(results[0])):
corr, _ = pearsonr(parameterValues[index], [item[j] for item in results])
row.append(round(corr, 4))
cor_table.append(row)

return cor_table

def update_table(self, index):
cor_table = self.corelation()
self.form.tableWidget.setColumnCount(2)
self.form.tableWidget.setRowCount(len(cor_table))
self.form.tableWidget.setHorizontalHeaderLabels(["Parameters", "Correlation Coef."])

for i, row in enumerate(cor_table):
item_param = QTableWidgetItem(row[0])
item_coef = QTableWidgetItem(str(row[index + 1]))
self.form.tableWidget.setItem(i, 0, item_param)
self.form.tableWidget.setItem(i, 1, item_coef)
item_coef.setToolTip("As this value approaches -1 or 1, the strength of the effect increases - negative effect, + means positive effect.")

def calculate_pareto_data(self):
cor_table = self.corelation()
Responses_names = ["Volume", "Max Stress", "Max Displacement", "Mean Stress", "Internal Energy", "Standard Dev. of En. Den."]
Responses = {}
parameters = [row[0] for row in cor_table]

for i, response_name in enumerate(Responses_names):
response_values = [abs(row[i + 1]) for row in cor_table]
Responses[response_name] = np.abs(response_values)

return Responses, parameters


def plotParetoGraph(self, index):
Responses, parameters = self.calculate_pareto_data()
response_name = self.form.comboBox_2.itemText(index) if index != 0 else self.form.comboBox_2.currentText()

response_matrix = Responses[response_name]
tot_cor = np.sum(response_matrix)
percentage_effects= [(miktar / tot_cor) * 100 for miktar in response_matrix.flatten()]
effects_of_parameters= zip(parameters, percentage_effects)
percentage_rank= sorted(effects_of_parameters, key=lambda x: x[1], reverse=True)
cumulative_percentage= [sum([x[1] for x in percentage_rank[:i + 1]]) for i in range(len(percentage_rank))]

fig = Plot.figure(winTitle=f'Pareto Graph - {response_name}')
ax1 = Plot.axesList()[0]

ax1.bar([x[0] for x in percentage_rank], [x[1] for x in percentage_rank], color='skyblue')
ax2 = ax1.twinx()
ax2.plot([x[0] for x in percentage_rank], cumulative_percentage, color='r', marker='o')

ax1.set_xlabel('Parameters')
ax1.set_ylabel('Percentage Effect')
ax2.set_ylabel('Cumulative Percentage')
ax2.set_ylim(0, 100)
ax1.set_ylim(0, 100)
Plot.plt.show()
fig.canvas.flush_events()



def corelation(self):
master = self.doc
results = np.array(
master.Results.FEAMetricsSum[1:], dtype=np.dtype("float"))
parameters1 = np.array(
master.Generate.GeneratedParameters, dtype=np.dtype("float"))
parameterValues = np.transpose(parameters1)
parameters = master.Generate.ParametersName

cor_table = []
for index, param in enumerate(parameters):
row = [param]
for j in range(len(results[0])):
corr, _ = pearsonr(parameterValues[index], [item[j] for item in results])
row.append(round(corr, 4))
cor_table.append(row)

return cor_table

def update_table(self, index):
cor_table = self.corelation()
self.form.tableWidget.setColumnCount(2)
self.form.tableWidget.setRowCount(len(cor_table))
self.form.tableWidget.setHorizontalHeaderLabels(["Parameters", "Correlation Coef."])

for i, row in enumerate(cor_table):
item_param = QTableWidgetItem(row[0])
item_coef = QTableWidgetItem(str(row[index + 1]))
self.form.tableWidget.setItem(i, 0, item_param)
self.form.tableWidget.setItem(i, 1, item_coef)
item_coef.setToolTip("As this value approaches -1 or 1, the strength of the effect increases - negative effect, + means positive effect.")

def anova(self):
master = self.doc
results = np.array(
master.Results.FEAMetricsSum[1:], dtype=np.dtype("float"))
parameters1 = np.array(
master.Generate.GeneratedParameters, dtype=np.dtype("float"))
parameterValues = np.transpose(parameters1)
parameters = master.Generate.ParametersName

anova_table = []
for index, param in enumerate(parameters):
row = [param]
unique_values = np.unique(parameterValues[index])

for j in range(len(results[0])):
# Create groups for ANOVA test
groups = [results[:, j][parameterValues[index] == value] for value in unique_values]
FreeCAD.Console.PrintMessage(f"Parameter: {param}, Unique Values: {unique_values}, Groups: {groups}\n")

# Check if any group is empty
if any(len(group) == 0 for group in groups):
row.append(None) # Append None or any placeholder for invalid ANOVA result

else:
# Perform ANOVA test
f_val, _ = f_oneway(*groups)
row.append(round(f_val, 4)) # Round the F-value for consistency

anova_table.append(row)

return anova_table


def update_anova_table(self, index):
anova_table= self.anova()
self.form.tableWidget_2.setColumnCount(2)
self.form.tableWidget_2.setRowCount(len(anova_table))
self.form.tableWidget_2.setHorizontalHeaderLabels(["Parameters", "F values"])

for i, row in enumerate(anova_table):
item_param = QTableWidgetItem(row[0])
item_coef = QTableWidgetItem(str(row[index + 1]))
self.form.tableWidget_2.setItem(i, 0, item_param)
self.form.tableWidget_2.setItem(i, 1, item_coef)


def calculate_pareto_data(self):
cor_table = self.corelation()
Responses_names = ["Volume", "Max Stress", "Max Displacement", "Mean Stress", "Internal Energy", "Standard Dev. of En. Den."]
Responses = {}
parameters = [row[0] for row in cor_table]

for i, response_name in enumerate(Responses_names):
response_values = [abs(row[i + 1]) for row in cor_table]
Responses[response_name] = np.abs(response_values)

return Responses, parameters


def plotParetoGraph(self, index):
Responses, parameters = self.calculate_pareto_data()
response_name = self.form.comboBox_2.itemText(index) if index != 0 else self.form.comboBox_2.currentText()

response_matrix = Responses[response_name]
tot_cor = np.sum(response_matrix)
percentage_effects= [(miktar / tot_cor) * 100 for miktar in response_matrix.flatten()]
effects_of_parameters= zip(parameters, percentage_effects)
percentage_rank= sorted(effects_of_parameters, key=lambda x: x[1], reverse=True)
cumulative_percentage= [sum([x[1] for x in percentage_rank[:i + 1]]) for i in range(len(percentage_rank))]

fig = Plot.figure(winTitle=f'Pareto Graph - {response_name}')
ax1 = Plot.axesList()[0]

ax1.bar([x[0] for x in percentage_rank], [x[1] for x in percentage_rank], color='skyblue')
ax2 = ax1.twinx()
ax2.plot([x[0] for x in percentage_rank], cumulative_percentage, color='r', marker='o')

ax1.set_xlabel('Parameters')
ax1.set_ylabel('Percentage Effect')
ax2.set_ylabel('Cumulative Percentage')
ax2.set_ylim(0, 100)
ax1.set_ylim(0, 100)
Plot.plt.show()
fig.canvas.flush_events()


def normalize(self, vals):
"""Resuls range can be different. So, the function make results between 0~1 to calculate ranking score.
Expand Down
5 changes: 3 additions & 2 deletions fembygen/topology/beso_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ def size_tria(elm_category):
((x1 - x3) ** 2 + (y1 - y3) ** 2 + (z1 - z3) ** 2) ** 0.5 +
((x2 - x3) ** 2 + (y2 - y3) ** 2 + (z2 - z3) ** 2) ** 0.5
) / 3

# noktalar arası uzakıkların hesaplanması

def size_quad(elm_category):
for en in elm_category:
x1, y1, z1 = nodes[elm_category[en][0]]
Expand Down Expand Up @@ -106,7 +107,7 @@ def get_filter_range(size_elm, domains, filtered_dn):
for en in domains[dn]:
size_sum += size_elm[en]
return size_sum / len_filtered_dn

# noktalar arası uzaklıklar toplamının eleman sayısınıa bölünmesi

def sround(x, s):
"""round float number x to s significant digits
Expand Down
Loading

0 comments on commit 01bed9f

Please sign in to comment.