
- move test files to a separate directory - use real life model to generate .inp files (model included) - change format of csv node/volume files to allow direct copy&paste from .inp files - use pre prepared real-life .dat & .frd files to test analysis results loading functions - much more fine grained error reporting - remove empty lines for output - add helper function to print to console Example run of the FEM test: --------------- Start of FEM tests --------------- Checking FEM new analysis... Checking FEM new mesh... sh: SMDS_MemoryLimit: command not found Checking FEM new material... Checking FEM new fixed constraint... Checking FEM new force constraint... Checking FEM new pressure constraint... Checking FEM inp file prerequisites... Checking FEM inp file write... Setting up working directory /tmp/FEM_static Writing /tmp/FEM_static/Mesh.inp for static analysis Comparing /home/przemo/software/FreeCAD/build/Mod/Fem/test_files/cube_static.inp to /tmp/FEM_static/Mesh.inp Setting up working directory to /home/przemo/software/FreeCAD/build/Mod/Fem/test_files in order to read simulated calculations Setting base name to read test cube_static.frd file... Checking FEM frd file read from static analysis... Result object created as "Results" Reading stats from result object for static analysis... Setting analysis type to 'frequency" Setting up working directory /tmp/FEM_frequency Writing /tmp/FEM_frequency/Mesh.inp for frequency analysis Comparing /home/przemo/software/FreeCAD/build/Mod/Fem/test_files/cube_frequency.inp to /tmp/FEM_frequency/Mesh.inp Setting working directory to read simulated calculations... Setting base name to read test cube_frequency.frd file... Checking FEM frd file read from frequency analysis... Last result object created as "Mode_10_results" Reading stats from result object for frequency analysis... --------------- End of FEM tests --------------- Signed-off-by: Przemo Firszt <przemo@firszt.eu>
262 lines
12 KiB
Python
262 lines
12 KiB
Python
# Unit test for the FEM module
|
|
|
|
#***************************************************************************
|
|
#* Copyright (c) 2015 - FreeCAD Developers *
|
|
#* Author: Przemo Firszt <przemo@firszt.eu> *
|
|
#* *
|
|
#* This file is part of the FreeCAD CAx development system. *
|
|
#* *
|
|
#* This program is free software; you can redistribute it and/or modify *
|
|
#* it under the terms of the GNU Lesser General Public License (LGPL) *
|
|
#* as published by the Free Software Foundation; either version 2 of *
|
|
#* the License, or (at your option) any later version. *
|
|
#* for detail see the LICENCE text file. *
|
|
#* *
|
|
#* FreeCAD is distributed in the hope that it will be useful, *
|
|
#* but WITHOUT ANY WARRANTY; without even the implied warranty of *
|
|
#* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
|
|
#* GNU Library General Public License for more details. *
|
|
#* *
|
|
#* You should have received a copy of the GNU Library General Public *
|
|
#* License along with FreeCAD; if not, write to the Free Software *
|
|
#* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
|
|
#* USA *
|
|
#* *
|
|
#***************************************************************************/
|
|
|
|
import Fem
|
|
import FemTools
|
|
import FreeCAD
|
|
import MechanicalAnalysis
|
|
import csv
|
|
import tempfile
|
|
import unittest
|
|
|
|
mesh_name = 'Mesh'
|
|
|
|
home_path = FreeCAD.getHomePath()
|
|
temp_dir = tempfile.gettempdir()
|
|
test_file_dir = home_path + 'Mod/Fem/test_files'
|
|
|
|
static_base_name = 'cube_static'
|
|
frequency_base_name = 'cube_frequency'
|
|
static_analysis_dir = temp_dir + '/FEM_static'
|
|
frequency_analysis_dir = temp_dir + '/FEM_frequency'
|
|
static_analysis_inp_file = test_file_dir + '/' + static_base_name + '.inp'
|
|
static_expected_values = test_file_dir + "/cube_static_expected_values"
|
|
frequency_analysis_inp_file = test_file_dir + '/' + frequency_base_name + '.inp'
|
|
frequency_expected_values = test_file_dir + "/cube_frequency_expected_values"
|
|
mesh_points_file = test_file_dir + '/mesh_points.csv'
|
|
mesh_volumes_file = test_file_dir + '/mesh_volumes.csv'
|
|
|
|
|
|
def fcc_print(message):
|
|
FreeCAD.Console.PrintMessage(message + '\n')
|
|
|
|
|
|
class FemTest(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
try:
|
|
FreeCAD.setActiveDocument("FemTest")
|
|
except:
|
|
FreeCAD.newDocument("FemTest")
|
|
finally:
|
|
FreeCAD.setActiveDocument("FemTest")
|
|
self.active_doc = FreeCAD.ActiveDocument
|
|
self.box = self.active_doc.addObject("Part::Box", "Box")
|
|
self.active_doc.recompute()
|
|
|
|
def create_new_analysis(self):
|
|
self.analysis = MechanicalAnalysis.makeMechanicalAnalysis('MechanicalAnalysis')
|
|
self.active_doc.recompute()
|
|
|
|
def create_new_mesh(self):
|
|
self.mesh_object = self.active_doc.addObject('Fem::FemMeshObject', mesh_name)
|
|
self.mesh = Fem.FemMesh()
|
|
with open(mesh_points_file, 'r') as points_file:
|
|
reader = csv.reader(points_file)
|
|
for p in reader:
|
|
self.mesh.addNode(float(p[1]), float(p[2]), float(p[3]), int(p[0]))
|
|
|
|
with open(mesh_volumes_file, 'r') as volumes_file:
|
|
reader = csv.reader(volumes_file)
|
|
for v in reader:
|
|
self.mesh.addVolume([int(v[2]), int(v[1]), int(v[3]), int(v[4]), int(v[5]),
|
|
int(v[7]), int(v[6]), int(v[9]), int(v[8]), int(v[10])],
|
|
int(v[0]))
|
|
self.mesh_object.FemMesh = self.mesh
|
|
self.active_doc.recompute()
|
|
|
|
def create_new_material(self):
|
|
self.new_material_object = self.active_doc.addObject("App::MaterialObjectPython", 'MechanicalMaterial')
|
|
mat = self.new_material_object.Material
|
|
mat['Name'] = "Steel"
|
|
mat['YoungsModulus'] = "200000 MPa"
|
|
mat['PoissonRatio'] = "0.30"
|
|
mat['Density'] = "7900 kg/m^3"
|
|
self.new_material_object.Material = mat
|
|
|
|
def create_fixed_constraint(self):
|
|
self.fixed_constraint = self.active_doc.addObject("Fem::ConstraintFixed", "FemConstraintFixed")
|
|
self.fixed_constraint.References = [(self.box, "Face1")]
|
|
|
|
def create_force_constraint(self):
|
|
self.force_constraint = self.active_doc.addObject("Fem::ConstraintForce", "FemConstraintForce")
|
|
self.force_constraint.References = [(self.box, "Face2")]
|
|
self.force_constraint.Force = 10.000000
|
|
self.force_constraint.Direction = (self.box, ["Edge5"])
|
|
self.force_constraint.Reversed = True
|
|
|
|
def create_pressure_constraint(self):
|
|
self.pressure_constraint = self.active_doc.addObject("Fem::ConstraintPressure", "FemConstraintPressure")
|
|
self.pressure_constraint.References = [(self.box, "Face2")]
|
|
self.pressure_constraint.Pressure = 10.000000
|
|
self.pressure_constraint.Reversed = True
|
|
|
|
def compare_inp_files(self, file_name1, file_name2):
|
|
file1 = open(file_name1, 'r')
|
|
file2 = open(file_name2, 'r')
|
|
f1 = file1.readlines()
|
|
f2 = file2.readlines()
|
|
lf1 = [l for l in f1 if not l.startswith('**')]
|
|
lf2 = [l for l in f2 if not l.startswith('**')]
|
|
import difflib
|
|
diff = difflib.unified_diff(lf1, lf2, n=0)
|
|
result = ''
|
|
for l in diff:
|
|
result += l
|
|
file1.close()
|
|
file2.close()
|
|
return result
|
|
|
|
def compare_stats(self, fea, stat_file=None):
|
|
if stat_file:
|
|
sf = open(stat_file, 'r')
|
|
sf_content = sf.readlines()
|
|
sf.close()
|
|
stat_types = ["U1", "U2", "U3", "Uabs", "Sabs"]
|
|
stats = []
|
|
for s in stat_types:
|
|
stats.append("{}: {}\n".format(s, fea.get_stats(s)))
|
|
if sf_content != stats:
|
|
fcc_print("Expected stats from {}".format(stat_file))
|
|
fcc_print(sf_content)
|
|
fcc_print("Stats read from {}.frd file".format(fea.base_name))
|
|
fcc_print(stats)
|
|
return True
|
|
return False
|
|
|
|
def test_new_analysis(self):
|
|
fcc_print('--------------- Start of FEM tests ---------------')
|
|
fcc_print('Checking FEM new analysis...')
|
|
self.create_new_analysis()
|
|
self.assertTrue(self.analysis, "FemTest of new analysis failed")
|
|
|
|
fcc_print('Checking FEM new mesh...')
|
|
self.create_new_mesh()
|
|
self.assertTrue(self.mesh, "FemTest of new mesh failed")
|
|
self.analysis.Member = self.analysis.Member + [self.mesh_object]
|
|
|
|
fcc_print('Checking FEM new material...')
|
|
self.create_new_material()
|
|
self.assertTrue(self.new_material_object, "FemTest of new material failed")
|
|
self.analysis.Member = self.analysis.Member + [self.new_material_object]
|
|
|
|
fcc_print('Checking FEM new fixed constraint...')
|
|
self.create_fixed_constraint()
|
|
self.assertTrue(self.fixed_constraint, "FemTest of new fixed constraint failed")
|
|
self.analysis.Member = self.analysis.Member + [self.fixed_constraint]
|
|
|
|
fcc_print('Checking FEM new force constraint...')
|
|
self.create_force_constraint()
|
|
self.assertTrue(self.force_constraint, "FemTest of new force constraint failed")
|
|
self.analysis.Member = self.analysis.Member + [self.force_constraint]
|
|
|
|
fcc_print('Checking FEM new pressure constraint...')
|
|
self.create_pressure_constraint()
|
|
self.assertTrue(self.pressure_constraint, "FemTest of new pressure constraint failed")
|
|
self.analysis.Member = self.analysis.Member + [self.pressure_constraint]
|
|
|
|
fea = FemTools.FemTools(self.analysis)
|
|
fcc_print('Checking FEM inp file prerequisites...')
|
|
error = fea.check_prerequisites()
|
|
self.assertFalse(error, "FemTools check_prerequisites returned error message: {}".format(error))
|
|
|
|
fcc_print('Checking FEM inp file write...')
|
|
|
|
fcc_print('Setting up working directory {}'.format(static_analysis_dir))
|
|
fea.setup_working_dir(static_analysis_dir)
|
|
self.assertTrue(True if fea.working_dir == static_analysis_dir else False,
|
|
"Setting working directory {} failed".format(static_analysis_dir))
|
|
|
|
fcc_print('Writing {}/{}.inp for static analysis'.format(static_analysis_dir, mesh_name))
|
|
error = fea.write_inp_file()
|
|
self.assertFalse(error, "Writing failed")
|
|
|
|
fcc_print('Comparing {} to {}/{}.inp'.format(static_analysis_inp_file, static_analysis_dir, mesh_name))
|
|
ret = self.compare_inp_files(static_analysis_inp_file, static_analysis_dir + "/" + mesh_name + '.inp')
|
|
self.assertFalse(ret, "FemTools write_inp_file test failed.\n{}".format(ret))
|
|
|
|
fcc_print('Setting up working directory to {} in order to read simulated calculations'.format(test_file_dir))
|
|
fea.setup_working_dir(test_file_dir)
|
|
self.assertTrue(True if fea.working_dir == test_file_dir else False,
|
|
"Setting working directory {} failed".format(test_file_dir))
|
|
|
|
fcc_print('Setting base name to read test {}.frd file...'.format('cube_static'))
|
|
fea.set_base_name(static_base_name)
|
|
self.assertTrue(True if fea.base_name == static_base_name else False,
|
|
"Setting base name to {} failed".format(static_base_name))
|
|
|
|
fcc_print('Checking FEM frd file read from static analysis...')
|
|
fea.load_results()
|
|
fcc_print('Result object created as \"{}\"'.format(fea.result_object.Name))
|
|
self.assertTrue(fea.results_present, "Cannot read results from {}.frd frd file".format(fea.base_name))
|
|
|
|
fcc_print('Reading stats from result object for static analysis...')
|
|
ret = self.compare_stats(fea, static_expected_values)
|
|
self.assertFalse(ret, "Invalid results read from .frd file")
|
|
|
|
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")
|
|
|
|
fcc_print('Setting up working directory to {} in order to write frequency calculations'.format(frequency_analysis_dir))
|
|
fea.setup_working_dir(frequency_analysis_dir)
|
|
self.assertTrue(True if fea.working_dir == frequency_analysis_dir else False,
|
|
"Setting working directory {} failed".format(frequency_analysis_dir))
|
|
|
|
fcc_print('Writing {}/{}.inp for frequency analysis'.format(frequency_analysis_dir, mesh_name))
|
|
error = fea.write_inp_file()
|
|
self.assertFalse(error, "Writing failed")
|
|
|
|
fcc_print('Comparing {} to {}/{}.inp'.format(frequency_analysis_inp_file, frequency_analysis_dir, mesh_name))
|
|
ret = self.compare_inp_files(frequency_analysis_inp_file, frequency_analysis_dir + "/" + mesh_name + '.inp')
|
|
self.assertFalse(ret, "FemTools write_inp_file test failed.\n{}".format(ret))
|
|
|
|
fcc_print('Setting up working directory to {} in order to read simulated calculations'.format(test_file_dir))
|
|
fea.setup_working_dir(test_file_dir)
|
|
self.assertTrue(True if fea.working_dir == test_file_dir else False,
|
|
"Setting working directory {} failed".format(test_file_dir))
|
|
|
|
fcc_print('Setting base name to read test {}.frd file...'.format(frequency_base_name))
|
|
fea.set_base_name(frequency_base_name)
|
|
self.assertTrue(True if fea.base_name == frequency_base_name else False,
|
|
"Setting base name to {} failed".format(frequency_base_name))
|
|
|
|
fcc_print('Checking FEM frd file read from frequency analysis...')
|
|
fea.load_results()
|
|
|
|
fcc_print('Last result object created as \"{}\"'.format(fea.result_object.Name))
|
|
self.assertTrue(fea.results_present, "Cannot read results from {}.frd frd file".format(fea.base_name))
|
|
|
|
fcc_print('Reading stats from result object for frequency analysis...')
|
|
ret = self.compare_stats(fea, frequency_expected_values)
|
|
self.assertFalse(ret, "Invalid results read from .frd file")
|
|
|
|
fcc_print('--------------- End of FEM tests ---------------')
|
|
|
|
def tearDown(self):
|
|
FreeCAD.closeDocument("FemTest")
|
|
pass
|