366 lines
11 KiB
Python
366 lines
11 KiB
Python
# ***************************************************************************
|
|
# * Copyright (c) 2018 - FreeCAD Developers *
|
|
# * Author: Bernd Hahnebach <bernd@bimstatik.org> *
|
|
# * *
|
|
# * 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 *
|
|
# * *
|
|
# ***************************************************************************/
|
|
|
|
__title__ = "Tools for FEM unit tests"
|
|
__author__ = "Bernd Hahnebach"
|
|
__url__ = "http://www.freecadweb.org"
|
|
|
|
|
|
import os
|
|
import unittest
|
|
import tempfile
|
|
import FreeCAD
|
|
from os.path import join
|
|
|
|
|
|
def get_fem_test_home_dir(
|
|
):
|
|
return join(FreeCAD.getHomePath(), 'Mod', 'Fem', 'femtest', 'testfiles')
|
|
|
|
|
|
def get_fem_test_tmp_dir(
|
|
):
|
|
temp_dir = join(tempfile.gettempdir(), 'FEM_unittests')
|
|
if not os.path.exists(temp_dir):
|
|
os.makedirs(temp_dir)
|
|
return(temp_dir)
|
|
|
|
|
|
def get_unit_test_tmp_dir(
|
|
temp_dir,
|
|
unittestdir
|
|
):
|
|
testdir = join(temp_dir, unittestdir)
|
|
if not os.path.exists(testdir):
|
|
os.makedirs(testdir)
|
|
return testdir
|
|
|
|
|
|
def fcc_print(
|
|
message
|
|
):
|
|
FreeCAD.Console.PrintMessage('{} \n'.format(message))
|
|
|
|
|
|
def get_defmake_count(
|
|
fem_vtk_post=True
|
|
):
|
|
'''
|
|
count the def make in module ObjectsFem
|
|
could also be done in bash with
|
|
grep -c "def make" src/Mod/Fem/ObjectsFem.py
|
|
'''
|
|
name_modfile = join(FreeCAD.getHomePath(), 'Mod', 'Fem', 'ObjectsFem.py')
|
|
modfile = open(name_modfile, 'r')
|
|
lines_modefile = modfile.readlines()
|
|
modfile.close()
|
|
lines_defmake = [l for l in lines_modefile if l.startswith('def make')]
|
|
if not fem_vtk_post:
|
|
# FEM VTK post processing is disabled
|
|
# we are not able to create VTK post objects
|
|
new_lines = []
|
|
for l in lines_defmake:
|
|
if "PostVtk" not in l:
|
|
new_lines.append(l)
|
|
lines_defmake = new_lines
|
|
return len(lines_defmake)
|
|
|
|
|
|
def get_fem_test_defs(
|
|
inout='out'
|
|
):
|
|
test_path = join(FreeCAD.getHomePath(), 'Mod', 'Fem', 'femtest')
|
|
collected_test_modules = []
|
|
collected_test_methods = []
|
|
for tfile in sorted(os.listdir(test_path)):
|
|
if tfile.startswith("test") and tfile.endswith(".py"):
|
|
collected_test_modules.append(join(test_path, tfile))
|
|
for f in collected_test_modules:
|
|
tfile = open(f, 'r')
|
|
module_name = os.path.splitext(os.path.basename(f))[0]
|
|
class_name = ''
|
|
for ln in tfile:
|
|
ln = ln.lstrip()
|
|
ln = ln.rstrip()
|
|
if ln.startswith('class '):
|
|
ln = ln.lstrip('class ')
|
|
ln = ln.split('(')[0]
|
|
class_name = ln
|
|
if ln.startswith('def test'):
|
|
ln = ln.lstrip('def ')
|
|
ln = ln.split('(')[0]
|
|
collected_test_methods.append(
|
|
'femtest.{}.{}.{}'.format(module_name, class_name, ln)
|
|
)
|
|
tfile.close()
|
|
print('')
|
|
for m in collected_test_methods:
|
|
run_outside_fc = './bin/FreeCADCmd --run-test "{}"'.format(m)
|
|
run_inside_fc = (
|
|
'unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName("{}"))'
|
|
.format(m)
|
|
)
|
|
if inout == 'in':
|
|
print('\nimport unittest')
|
|
print(run_inside_fc)
|
|
else:
|
|
print(run_outside_fc)
|
|
|
|
|
|
def compare_inp_files(
|
|
file_name1,
|
|
file_name2
|
|
):
|
|
file1 = open(file_name1, 'r')
|
|
f1 = file1.readlines()
|
|
file1.close()
|
|
# l.startswith('17671.0,1') is a temporary workaround
|
|
# for python3 problem with 1DFlow input
|
|
# TODO as soon as the 1DFlow result reading is fixed
|
|
# this should be triggered in the 1DFlow unit test
|
|
lf1 = [l for l in f1 if not (
|
|
l.startswith('** written ') or l.startswith('** file ') or l.startswith('17671.0,1')
|
|
)]
|
|
lf1 = force_unix_line_ends(lf1)
|
|
file2 = open(file_name2, 'r')
|
|
f2 = file2.readlines()
|
|
file2.close()
|
|
# TODO see comment on file1
|
|
lf2 = [l for l in f2 if not (
|
|
l.startswith('** written ') or l.startswith('** file ') or l.startswith('17671.0,1')
|
|
)]
|
|
lf2 = force_unix_line_ends(lf2)
|
|
import difflib
|
|
diff = difflib.unified_diff(lf1, lf2, n=0)
|
|
result = ''
|
|
for l in diff:
|
|
result += l
|
|
if result:
|
|
result = (
|
|
"Comparing {} to {} failed!\n"
|
|
.format(file_name1, file_name2) + result
|
|
)
|
|
return result
|
|
|
|
|
|
def compare_files(
|
|
file_name1,
|
|
file_name2
|
|
):
|
|
file1 = open(file_name1, 'r')
|
|
f1 = file1.readlines()
|
|
file1.close()
|
|
# workaround to compare geos of elmer test and temporary file path
|
|
# (not only names change, path changes with operating system)
|
|
lf1 = [l for l in f1 if not (
|
|
l.startswith('Merge "') or l.startswith('Save "') or l.startswith('// ')
|
|
)]
|
|
lf1 = 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('Merge "') or l.startswith('Save "') or l.startswith('// ')
|
|
)]
|
|
lf2 = force_unix_line_ends(lf2)
|
|
import difflib
|
|
diff = difflib.unified_diff(lf1, lf2, n=0)
|
|
result = ''
|
|
for l in diff:
|
|
result += l
|
|
if result:
|
|
result = "Comparing {} to {} failed!\n".format(file_name1, file_name2) + result
|
|
return result
|
|
|
|
|
|
def compare_stats(
|
|
fea,
|
|
stat_file,
|
|
res_obj_name,
|
|
loc_stat_types=None
|
|
):
|
|
import femresult.resulttools as resulttools
|
|
|
|
# get the stat types which should be compared
|
|
stat_types = [
|
|
"U1",
|
|
"U2",
|
|
"U3",
|
|
"Uabs",
|
|
"Sabs",
|
|
"MaxPrin",
|
|
"MidPrin",
|
|
"MinPrin",
|
|
"MaxShear",
|
|
"Peeq",
|
|
"Temp",
|
|
"MFlow",
|
|
"NPress"
|
|
]
|
|
if not loc_stat_types:
|
|
loc_stat_types = stat_types
|
|
|
|
# get stats from result obj which should be compared
|
|
obj = FreeCAD.ActiveDocument.getObject(res_obj_name)
|
|
# fcc_print(obj)
|
|
if obj:
|
|
# fcc_print(obj.Name)
|
|
stats = []
|
|
for s in loc_stat_types:
|
|
statval = resulttools.get_stats(obj, s)
|
|
stats.append(
|
|
"{0}: ({1:.14g}, {2:.14g}, {3:.14g})\n"
|
|
.format(s, statval[0], statval[1], statval[2])
|
|
)
|
|
else:
|
|
fcc_print("Result object not found. Name: {}".format(res_obj_name))
|
|
return False
|
|
|
|
# get stats to compare with
|
|
sf = open(stat_file, 'r')
|
|
sf_content = []
|
|
for l in sf.readlines():
|
|
for st in loc_stat_types:
|
|
if l.startswith(st):
|
|
sf_content.append(l)
|
|
sf.close()
|
|
sf_content = force_unix_line_ends(sf_content)
|
|
|
|
# compare stats
|
|
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 force_unix_line_ends(
|
|
line_list
|
|
):
|
|
new_line_list = []
|
|
for ln in line_list:
|
|
if ln.endswith("\r\n"):
|
|
ln = ln[:-2] + '\n'
|
|
new_line_list.append(ln)
|
|
return new_line_list
|
|
|
|
|
|
def collect_python_modules(
|
|
femsubdir=None
|
|
):
|
|
if not femsubdir:
|
|
pydir = join(FreeCAD.ConfigGet("AppHomePath"), 'Mod', 'Fem')
|
|
else:
|
|
pydir = join(FreeCAD.ConfigGet("AppHomePath"), 'Mod', 'Fem', femsubdir)
|
|
collected_modules = []
|
|
fcc_print(pydir)
|
|
for pyfile in sorted(os.listdir(pydir)):
|
|
if pyfile.endswith(".py") and not pyfile.startswith('Init'):
|
|
if not femsubdir:
|
|
collected_modules.append(
|
|
os.path.splitext(os.path.basename(pyfile))[0]
|
|
)
|
|
else:
|
|
collected_modules.append(
|
|
femsubdir.replace('/', '.') + '.' + os.path.splitext(
|
|
os.path.basename(pyfile)
|
|
)[0]
|
|
)
|
|
return collected_modules
|
|
|
|
|
|
def all_test_files(
|
|
):
|
|
# open all files
|
|
cube_frequency()
|
|
cube_static()
|
|
Flow1D_thermomech()
|
|
multimat()
|
|
spine_thermomech()
|
|
|
|
|
|
# run the specific test case of the file
|
|
# open the file in FreeCAD GUI and return the doc identifier
|
|
def cube_frequency(
|
|
):
|
|
testname = "femtest.testccxtools.TestCcxTools.test_3_freq_analysis"
|
|
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName(testname))
|
|
doc = FreeCAD.open(join(
|
|
get_fem_test_tmp_dir(),
|
|
'FEM_ccx_frequency',
|
|
'cube_frequency.FCStd')
|
|
)
|
|
return doc
|
|
|
|
|
|
def cube_static(
|
|
):
|
|
testname = "femtest.testccxtools.TestCcxTools.test_1_static_analysis"
|
|
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName(testname))
|
|
doc = FreeCAD.open(
|
|
join(get_fem_test_tmp_dir(),
|
|
'FEM_ccx_static',
|
|
'cube_static.FCStd')
|
|
)
|
|
return doc
|
|
|
|
|
|
def Flow1D_thermomech(
|
|
):
|
|
testname = "femtest.testccxtools.TestCcxTools.test_5_Flow1D_thermomech_analysis"
|
|
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName(testname))
|
|
doc = FreeCAD.open(join(
|
|
get_fem_test_tmp_dir(),
|
|
'FEM_ccx_Flow1D_thermomech',
|
|
'Flow1D_thermomech.FCStd')
|
|
)
|
|
return doc
|
|
|
|
|
|
def multimat(
|
|
):
|
|
testname = "femtest.testccxtools.TestCcxTools.test_2_static_multiple_material"
|
|
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName(testname))
|
|
doc = FreeCAD.open(join(
|
|
get_fem_test_tmp_dir(),
|
|
'FEM_ccx_multimat',
|
|
'multimat.FCStd')
|
|
)
|
|
return doc
|
|
|
|
|
|
def spine_thermomech(
|
|
):
|
|
testname = "femtest.testccxtools.TestCcxTools.test_4_thermomech_analysis"
|
|
unittest.TextTestRunner().run(unittest.TestLoader().loadTestsFromName(testname))
|
|
doc = FreeCAD.open(join(
|
|
get_fem_test_tmp_dir(),
|
|
'FEM_ccx_thermomech',
|
|
'spine_thermomech.FCStd')
|
|
)
|
|
return doc
|