diff --git a/src/Mod/Fem/FemTools.py b/src/Mod/Fem/FemTools.py index 93ed055f20..685ca54bf7 100644 --- a/src/Mod/Fem/FemTools.py +++ b/src/Mod/Fem/FemTools.py @@ -307,6 +307,7 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): self.shell_thicknesses.append(shell_thickness_dict) def check_prerequisites(self): + import Units message = "" # analysis if not self.analysis: @@ -352,20 +353,27 @@ class FemTools(QtCore.QRunnable, QtCore.QObject): has_no_references = True for m in self.materials_linear: mat_map = m['Object'].Material - if 'YoungsModulus' not in mat_map: + if 'YoungsModulus' in mat_map: + # print Units.Quantity(mat_map['YoungsModulus']).Value + if not Units.Quantity(mat_map['YoungsModulus']).Value: + message += "Value of YoungsModulus is set to 0.0.\n" + else: message += "No YoungsModulus defined for at least one material.\n" if 'PoissonRatio' not in mat_map: - message += "No PoissonRatio defined for at least one material.\n" + message += "No PoissonRatio defined for at least one material.\n" # PoissonRatio is allowed to be 0.0 (in ccx), but it should be set anyway. if self.analysis_type == "frequency" or self.selfweight_constraints: if 'Density' not in mat_map: message += "No Density defined for at least one material.\n" if self.analysis_type == "thermomech": - if 'ThermalConductivity' not in mat_map: + if 'ThermalConductivity' in mat_map: + if not Units.Quantity(mat_map['ThermalConductivity']).Value: + message += "Value of ThermalConductivity is set to 0.0.\n" + else: message += "Thermomechanical analysis: No ThermalConductivity defined for at least one material.\n" if 'ThermalExpansionCoefficient' not in mat_map: - message += "Thermomechanical analysis: No ThermalExpansionCoefficient defined for at least one material.\n" + message += "Thermomechanical analysis: No ThermalExpansionCoefficient defined for at least one material.\n" # allowed to be 0.0 (in ccx) if 'SpecificHeat' not in mat_map: - message += "Thermomechanical analysis: No SpecificHeat defined for at least one material.\n" + message += "Thermomechanical analysis: No SpecificHeat defined for at least one material.\n" # allowed to be 0.0 (in ccx) for m in self.materials_linear: has_nonlinear_material = False for nlm in self.materials_nonlinear: diff --git a/src/Mod/Fem/TestFem.py b/src/Mod/Fem/TestFem.py index cfa75bffd6..4c497dbb70 100644 --- a/src/Mod/Fem/TestFem.py +++ b/src/Mod/Fem/TestFem.py @@ -52,6 +52,10 @@ frequency_analysis_inp_file = test_file_dir + '/' + frequency_base_name + '.inp' frequency_expected_values = test_file_dir + "/cube_frequency_expected_values" thermomech_analysis_inp_file = test_file_dir + '/' + thermomech_base_name + '.inp' thermomech_expected_values = test_file_dir + "/spine_thermomech_expected_values" +static_save_fc_file = static_analysis_dir + '/' + static_base_name + '.fcstd' +frequency_save_fc_file = frequency_analysis_dir + '/' + frequency_base_name + '.fcstd' +thermomech_save_fc_file = thermomech_analysis_dir + '/' + thermomech_base_name + '.fcstd' + mesh_points_file = test_file_dir + '/mesh_points.csv' mesh_volumes_file = test_file_dir + '/mesh_volumes.csv' spine_points_file = test_file_dir + '/spine_points.csv' @@ -76,7 +80,7 @@ class FemTest(unittest.TestCase): self.active_doc.recompute() def create_new_analysis(self): - self.analysis = FemAnalysis.makeFemAnalysis('MechanicalAnalysis') + self.analysis = FemAnalysis.makeFemAnalysis('Analysis') self.active_doc.recompute() def create_new_solver(self): @@ -134,6 +138,9 @@ class FemTest(unittest.TestCase): self.pressure_constraint.Pressure = 1000.0 self.pressure_constraint.Reversed = False + def save_file(self, fc_file_name): + self.active_doc.saveAs(fc_file_name) + def force_unix_line_ends(self, line_list): new_line_list = [] for l in line_list: @@ -146,12 +153,12 @@ class FemTest(unittest.TestCase): file1 = open(file_name1, 'r') f1 = file1.readlines() file1.close() - lf1 = [l for l in f1 if not l.startswith('** written ')] + lf1 = [l for l in f1 if not (l.startswith('** written ') or l.startswith('** file '))] lf1 = self.force_unix_line_ends(lf1) file2 = open(file_name2, 'r') f2 = file2.readlines() file2.close() - lf2 = [l for l in f2 if not l.startswith('** written ')] + lf2 = [l for l in f2 if not (l.startswith('** written ') or l.startswith('** file '))] lf2 = self.force_unix_line_ends(lf2) import difflib diff = difflib.unified_diff(lf1, lf2, n=0) @@ -181,6 +188,7 @@ class FemTest(unittest.TestCase): return False def test_new_analysis(self): + # static fcc_print('--------------- Start of FEM tests ---------------') fcc_print('Checking FEM new analysis...') self.create_new_analysis() @@ -263,6 +271,11 @@ class FemTest(unittest.TestCase): ret = self.compare_stats(fea, static_expected_values) self.assertFalse(ret, "Invalid results read from .frd file") + fcc_print('Save FreeCAD file for static analysis to {}...'.format(static_save_fc_file)) + self.save_file(static_save_fc_file) + self.assertTrue(self.save_file, "FemTest saving of file {} failed ...".format(static_save_fc_file)) + + # frequency fcc_print('Setting analysis type to \'frequency\"') fea.set_analysis_type("frequency") self.assertTrue(True if fea.analysis_type == 'frequency' else False, "Setting anlysis type to \'frequency\' failed") @@ -307,7 +320,11 @@ class FemTest(unittest.TestCase): ret = self.compare_stats(fea, frequency_expected_values) self.assertFalse(ret, "Invalid results read from .frd file") - fcc_print('--------------- End of FEM tests ---------------') + fcc_print('Save FreeCAD file for frequency analysis to {}...'.format(frequency_save_fc_file)) + self.save_file(frequency_save_fc_file) + self.assertTrue(self.save_file, "FemTest saving of file {} failed ...".format(frequency_save_fc_file)) + + fcc_print('--------------- End of FEM tests static and frequency analysis ---------------') def tearDown(self): FreeCAD.closeDocument("FemTest") @@ -336,6 +353,7 @@ class TherMechFemTest(unittest.TestCase): def create_new_solver(self): self.solver_object = FemSolverCalculix.makeFemSolverCalculix('CalculiX') + self.solver_object.AnalysisType = 'thermomech' self.solver_object.GeometricalNonlinearity = 'linear' self.solver_object.ThermoMechSteadyState = True self.solver_object.MatrixSolverType = 'default' @@ -392,6 +410,9 @@ class TherMechFemTest(unittest.TestCase): self.heatflux_constraint.AmbientTemp = 255.3722 self.heatflux_constraint.FilmCoef = 5.678 + def save_file(self, fc_file_name): + self.active_doc.saveAs(fc_file_name) + def force_unix_line_ends(self, line_list): new_line_list = [] for l in line_list: @@ -518,26 +539,46 @@ class TherMechFemTest(unittest.TestCase): self.assertTrue(True if fea.inp_file_name == thermomech_analysis_inp_file else False, "Setting inp file name to {} failed".format(thermomech_analysis_inp_file)) + fcc_print('Checking FEM frd file read from thermomech analysis...') + fea.load_results() + self.assertTrue(fea.results_present, "Cannot read results from {}.frd frd file".format(fea.base_name)) + + fcc_print('Reading stats from result object for thermomech analysis...') + ret = self.compare_stats(fea, thermomech_expected_values) + self.assertFalse(ret, "Invalid results read from .frd file") + + fcc_print('Save FreeCAD file for thermomech analysis to {}...'.format(thermomech_save_fc_file)) + self.save_file(thermomech_save_fc_file) + self.assertTrue(self.save_file, "FemTest saving of file {} failed ...".format(thermomech_save_fc_file)) + + fcc_print('--------------- End of FEM tests thermomech analysis ---------------') + def tearDown(self): FreeCAD.closeDocument("TherMechFemTest") pass # helpers -def open_cube_test(): - cube_file = test_file_dir + '/cube.fcstd' - FreeCAD.open(cube_file) +def run_fem_unittests(): + import unittest + suite = unittest.TestSuite() + suite.addTest(unittest.defaultTestLoader.loadTestsFromName("TestFem")) + r = unittest.TextTestRunner() + r.run(suite) -def create_cube_test_results(): +def create_test_results(): + # run FEM unit tests + run_fem_unittests() + import os import shutil - cube_file = test_file_dir + '/cube.fcstd' - - FreeCAD.open(cube_file) import FemGui - FemGui.setActiveAnalysis(FreeCAD.ActiveDocument.MechanicalAnalysis) import FemToolsCcx + + # static and frequency cube + FreeCAD.open(static_save_fc_file) + FemGui.setActiveAnalysis(FreeCAD.ActiveDocument.Analysis) fea = FemToolsCcx.FemToolsCcx() # static @@ -549,7 +590,7 @@ def create_cube_test_results(): stats_static = [] # we only have one result object so we are fine for s in stat_types: stats_static.append("{}: {}\n".format(s, fea.get_stats(s))) - static_expected_values_file = temp_dir + '/cube_static_expected_values' + static_expected_values_file = static_analysis_dir + '/cube_static_expected_values' f = open(static_expected_values_file, 'w') for s in stats_static: f.write(s) @@ -559,8 +600,8 @@ def create_cube_test_results(): frd_result_file = os.path.splitext(fea.inp_file_name)[0] + '.frd' dat_result_file = os.path.splitext(fea.inp_file_name)[0] + '.dat' - frd_static_test_result_file = temp_dir + '/cube_static.frd' - dat_static_test_result_file = temp_dir + '/cube_static.dat' + frd_static_test_result_file = static_analysis_dir + '/cube_static.frd' + dat_static_test_result_file = static_analysis_dir + '/cube_static.dat' shutil.copyfile(frd_result_file, frd_static_test_result_file) shutil.copyfile(dat_result_file, dat_static_test_result_file) @@ -574,28 +615,55 @@ def create_cube_test_results(): stats_frequency = [] # since we set eigenmodeno. we only have one result object so we are fine for s in stat_types: stats_frequency.append("{}: {}\n".format(s, fea.get_stats(s))) - frequency_expected_values_file = temp_dir + '/cube_frequency_expected_values' + frequency_expected_values_file = frequency_analysis_dir + '/cube_frequency_expected_values' f = open(frequency_expected_values_file, 'w') for s in stats_frequency: f.write(s) f.close() - frd_frequency_test_result_file = temp_dir + '/cube_frequency.frd' - dat_frequency_test_result_file = temp_dir + '/cube_frequency.dat' + frd_frequency_test_result_file = frequency_analysis_dir + '/cube_frequency.frd' + dat_frequency_test_result_file = frequency_analysis_dir + '/cube_frequency.dat' shutil.copyfile(frd_result_file, frd_frequency_test_result_file) shutil.copyfile(dat_result_file, dat_frequency_test_result_file) - print('Results copied to: ' + temp_dir) + # thermomech + FreeCAD.open(thermomech_save_fc_file) + FemGui.setActiveAnalysis(FreeCAD.ActiveDocument.Analysis) + fea = FemToolsCcx.FemToolsCcx() + fea.reset_all() + fea.run() + + fea.load_results() + stat_types = ["U1", "U2", "U3", "Uabs", "Sabs"] + stats_thermomech = [] # we only have one result object so we are fine + for s in stat_types: + stats_thermomech.append("{}: {}\n".format(s, fea.get_stats(s))) + thermomech_expected_values_file = thermomech_analysis_dir + '/expected_values_thermomech' + f = open(thermomech_expected_values_file, 'w') + for s in stats_thermomech: + f.write(s) + f.close() + + # could be added in FemToolsCcx to the self object as an Attribut + frd_result_file = os.path.splitext(fea.inp_file_name)[0] + '.frd' + dat_result_file = os.path.splitext(fea.inp_file_name)[0] + '.dat' + + frd_thermomech_test_result_file = thermomech_analysis_dir + '/spine_thermomech.frd' + dat_thermomech_test_result_file = thermomech_analysis_dir + '/spine_thermomech.dat' + shutil.copyfile(frd_result_file, frd_thermomech_test_result_file) + shutil.copyfile(dat_result_file, dat_thermomech_test_result_file) + + print('Results copied to the appropriate FEM test dirs in: ' + temp_dir) ''' update the results in FEM untit tests: -start FreeCAD import TestFem -TestFem.create_cube_test_results() +TestFem.create_test_results() -copy result files from /tmp into the src dirctory +copy result files from FEM test directories into the src dirctory +compare the results with git difftool run make start FreeCAD and run FEM unit test if FEM unit test is fine --> commit new FEM unit test results diff --git a/src/Mod/Fem/_TaskPanelShowResult.py b/src/Mod/Fem/_TaskPanelShowResult.py index 8392df24d8..81b07e0377 100644 --- a/src/Mod/Fem/_TaskPanelShowResult.py +++ b/src/Mod/Fem/_TaskPanelShowResult.py @@ -231,15 +231,15 @@ class _TaskPanelShowResult: x = np.array(dispvectors[:, 0]) y = np.array(dispvectors[:, 1]) z = np.array(dispvectors[:, 2]) - stressvectors = np.array(self.result_object.StressVectors) + stressvectors = np.array(self.result_object.StressVectors) sx = np.array(stressvectors[:, 0]) sy = np.array(stressvectors[:, 1]) sz = np.array(stressvectors[:, 2]) - strainvectors = np.array(self.result_object.StrainVectors) + strainvectors = np.array(self.result_object.StrainVectors) ex = np.array(strainvectors[:, 0]) ey = np.array(strainvectors[:, 1]) ez = np.array(strainvectors[:, 2]) - userdefined_eq = x + y + z + T + Von + P1 + P2 + P3 + sx + sy + sz + ex + ey + ez # Dummy equation to get around flake8, varibles not being used + userdefined_eq = x + y + z + T + Von + P1 + P2 + P3 + sx + sy + sz + ex + ey + ez # Dummy equation to get around flake8, varibles not being used userdefined_eq = self.form.user_def_eq.toPlainText() # Get equation to be used UserDefinedFormula = eval(userdefined_eq).tolist() self.result_object.UserDefined = UserDefinedFormula diff --git a/src/Mod/Fem/ccxFrdReader.py b/src/Mod/Fem/ccxFrdReader.py index 060baeabbe..768d063b8f 100644 --- a/src/Mod/Fem/ccxFrdReader.py +++ b/src/Mod/Fem/ccxFrdReader.py @@ -256,7 +256,7 @@ def readResult(frd_input): elements_seg2[elem] = (nd1, nd2) elif elemType == 12: # B32 CalculiX --> seg3 FreeCAD - # Also D element element number + # Also D element element number # N1, N3 ,N2 Order in outpufile is 1,3,2 nd1 = int(line[3:13]) nd3 = int(line[13:23]) diff --git a/src/Mod/Fem/test_files/ccx/cube_frequency.fcstd b/src/Mod/Fem/test_files/ccx/cube_frequency.fcstd new file mode 100644 index 0000000000..63876fb2b1 Binary files /dev/null and b/src/Mod/Fem/test_files/ccx/cube_frequency.fcstd differ diff --git a/src/Mod/Fem/test_files/ccx/cube_frequency.inp b/src/Mod/Fem/test_files/ccx/cube_frequency.inp index 3e1d02013c..bb8b5323b0 100644 --- a/src/Mod/Fem/test_files/ccx/cube_frequency.inp +++ b/src/Mod/Fem/test_files/ccx/cube_frequency.inp @@ -525,7 +525,7 @@ S ** written by --> FreeCAD 0.16.5838 +1 (Git) ** written on --> Mon Oct 26 18:34:01 2015 ** file name --> -** analysis name --> MechanicalAnalysis +** analysis name --> Analysis ** ** ** diff --git a/src/Mod/Fem/test_files/ccx/cube_static.fcstd b/src/Mod/Fem/test_files/ccx/cube_static.fcstd new file mode 100644 index 0000000000..15e1a5c451 Binary files /dev/null and b/src/Mod/Fem/test_files/ccx/cube_static.fcstd differ diff --git a/src/Mod/Fem/test_files/ccx/cube_static.inp b/src/Mod/Fem/test_files/ccx/cube_static.inp index 09f6380124..7c44ef7e77 100644 --- a/src/Mod/Fem/test_files/ccx/cube_static.inp +++ b/src/Mod/Fem/test_files/ccx/cube_static.inp @@ -588,7 +588,7 @@ S ** written by --> FreeCAD 0.16.5838 +1 (Git) ** written on --> Mon Oct 26 18:28:42 2015 ** file name --> -** analysis name --> MechanicalAnalysis +** analysis name --> Analysis ** ** ** diff --git a/src/Mod/Fem/test_files/ccx/spine_thermomech.fcstd b/src/Mod/Fem/test_files/ccx/spine_thermomech.fcstd index bb7f674ca5..d93fdaba6e 100644 Binary files a/src/Mod/Fem/test_files/ccx/spine_thermomech.fcstd and b/src/Mod/Fem/test_files/ccx/spine_thermomech.fcstd differ diff --git a/src/Mod/Fem/test_files/ccx/spine_thermomech_expected_values b/src/Mod/Fem/test_files/ccx/spine_thermomech_expected_values index 2bcaec7af2..b86005a1d5 100644 --- a/src/Mod/Fem/test_files/ccx/spine_thermomech_expected_values +++ b/src/Mod/Fem/test_files/ccx/spine_thermomech_expected_values @@ -1,9 +1,5 @@ -U1: (-0.000942455, 0.00305855, 0.00729818) -U2: (-0.00163382, 0.000616589, 0.00222318) -U3: (-0.00185329, 0.000543914, 0.00220675) -Uabs: (0, 0.00364478, 0.00732571) -Sabs: (0.307123, 7.74746, 35.8618) -MaxPrin: (-5.82387, 1.31924, 10.9205) -MidPrin: (-37.7671, -4.87764, 10.9098) -MinPrin: (-39.3672, -7.02358, 6.47377) -MaxShear: (0.168182, 4.17141, 18.1004) \ No newline at end of file +U1: (-0.000942455, 0.0030585454755555556, 0.00729818) +U2: (-0.00163382, 0.0006165889555555556, 0.00222318) +U3: (-0.00185329, 0.0005439136222222222, 0.00220675) +Uabs: (0.0, 0.003644782698151031, 0.007325712241135124) +Sabs: (0.30712297252407333, 7.747458526266711, 35.86180372766652)