FEM: unit tests, elmer sifio and geo writing

This commit is contained in:
Bernd Hahnebach
2017-12-01 19:44:57 +01:00
committed by wmayer
parent 3dd9038472
commit c29e46d0b3
9 changed files with 290 additions and 17 deletions

View File

@@ -269,6 +269,13 @@ SET(FemTests_SRCS
test_files/ccx/Flow1D_thermomech.fcstd
)
SET(FemTestsElmer_SRCS
test_files/elmer/__init__.py
test_files/elmer/case.sif
test_files/elmer/group_mesh.geo
test_files/elmer/ELMERSOLVER_STARTINFO
)
SET(FemBase_SRCS
FemMeshObject.cpp
FemMeshObject.h
@@ -379,6 +386,7 @@ fc_target_copy_resource(Fem
${FemObjectsScripts_SRCS}
${FemGuiScripts_SRCS}
${FemTests_SRCS}
${FemTestsElmer_SRCS}
${FemSolver_SRCS}
${FemElmer_SRCS}
${FemCalculix_SRCS}

View File

@@ -249,3 +249,14 @@ INSTALL(
DESTINATION
Mod/Fem/test_files/ccx
)
INSTALL(
FILES
# changes on the file list here needs to be made in App/CMakeLists.txt as well
test_files/elmer/__init__.py
test_files/elmer/case.sif
test_files/elmer/group_mesh.geo
test_files/elmer/ELMERSOLVER_STARTINFO
DESTINATION
Mod/Fem/test_files/elmer
)

View File

@@ -42,6 +42,7 @@ temp_dir = tempfile.gettempdir() + '/FEM_unittests/'
if not os.path.exists(temp_dir):
os.makedirs(temp_dir)
test_file_dir = home_path + 'Mod/Fem/test_files/ccx/'
test_file_dir_elmer = home_path + 'Mod/Fem/test_files/elmer/'
# define some locations fot the analysis tests
# since they are also used in the helper def which create results they should stay global for the module
@@ -613,7 +614,7 @@ class FemCcxAnalysisTest(unittest.TestCase):
fcc_print('Save FreeCAD file for frequency analysis to {}...'.format(frequency_save_fc_file))
self.active_doc.saveAs(frequency_save_fc_file)
# use new solver frame work solver
# use new solver frame work ccx solver
fcc_print('Checking FEM new solver for new solver frame work...')
solver_ccx2_object = ObjectsFem.makeSolverCalculix(self.active_doc, 'SolverCalculiX')
solver_ccx2_object.GeometricalNonlinearity = 'linear'
@@ -623,12 +624,14 @@ class FemCcxAnalysisTest(unittest.TestCase):
solver_ccx2_object.EigenmodesCount = 10
solver_ccx2_object.EigenmodeHighLimit = 1000000.0
solver_ccx2_object.EigenmodeLowLimit = 0.0
self.assertTrue(solver_ccx2_object, "FemTest of new solver failed")
self.assertTrue(solver_ccx2_object, "FemTest of new ccx solver failed")
analysis.Member = analysis.Member + [solver_ccx2_object]
fcc_print('Checking inpfile writing for new solver frame work...')
if not os.path.exists(static2_analysis_dir):
if not os.path.exists(static2_analysis_dir): # new solver frameworkd does explicit not create a non existing directory
os.makedirs(static2_analysis_dir)
fcc_print('machine_ccx')
machine = solver_ccx2_object.Proxy.createMachine(solver_ccx2_object, static2_analysis_dir)
machine.target = femsolver.run.PREPARE
machine.start()
@@ -637,6 +640,46 @@ class FemCcxAnalysisTest(unittest.TestCase):
ret = compare_inp_files(static_analysis_inp_file, static2_analysis_dir + mesh_name + '.inp')
self.assertFalse(ret, "FemToolsCcx write_inp_file test failed.\n{}".format(ret))
# use new solver frame work elmer solver
solver_elmer_object = ObjectsFem.makeSolverElmer(self.active_doc, 'SolverElmer')
self.assertTrue(solver_elmer_object, "FemTest of elmer solver failed")
analysis.Member = analysis.Member + [solver_elmer_object]
solver_elmer_eqobj = ObjectsFem.makeEquationElasticity(self.active_doc, solver_elmer_object)
self.assertTrue(solver_elmer_eqobj, "FemTest of elmer elasticity equation failed")
# set ThermalExpansionCoefficient, current elmer seams to need it even on simple elasticity analysis
mat = material_object.Material
mat['ThermalExpansionCoefficient'] = "0 um/m/K" # FIXME elmer elasticity needs the dictionary key, otherwise it fails
material_object.Material = mat
mesh_gmsh = ObjectsFem.makeMeshGmsh(self.active_doc)
mesh_gmsh.CharacteristicLengthMin = "9 mm"
mesh_gmsh.FemMesh = mesh_object.FemMesh # elmer needs a GMHS mesh object, FIXME error message on Python solver run
mesh_gmsh.Part = box
analysis.Member = analysis.Member + [mesh_gmsh]
self.active_doc.removeObject(mesh_object.Name)
fcc_print('machine_elmer')
machine_elmer = solver_elmer_object.Proxy.createMachine(solver_elmer_object, static2_analysis_dir)
machine_elmer.target = femsolver.run.PREPARE
machine_elmer.start()
machine_elmer.join() # wait for the machine to finish.
fcc_print('Test writing STARTINFO file')
fcc_print('Comparing {} to {}'.format(test_file_dir_elmer + 'ELMERSOLVER_STARTINFO', static2_analysis_dir + 'ELMERSOLVER_STARTINFO'))
ret = compare_files(test_file_dir_elmer + 'ELMERSOLVER_STARTINFO', static2_analysis_dir + 'ELMERSOLVER_STARTINFO')
self.assertFalse(ret, "STARTINFO write file test failed.\n{}".format(ret))
fcc_print('Test writing case file')
fcc_print('Comparing {} to {}'.format(test_file_dir_elmer + 'case.sif', static2_analysis_dir + 'case.sif'))
ret = compare_files(test_file_dir_elmer + 'case.sif', static2_analysis_dir + 'case.sif')
self.assertFalse(ret, "case write file test failed.\n{}".format(ret))
fcc_print('Test writing GMSH geo file')
fcc_print('Comparing {} to {}'.format(test_file_dir_elmer + 'group_mesh.geo', static2_analysis_dir + 'group_mesh.geo'))
ret = compare_files(test_file_dir_elmer + 'group_mesh.geo', static2_analysis_dir + 'group_mesh.geo')
self.assertFalse(ret, "GMSH geo write file test failed.\n{}".format(ret))
fcc_print('Save FreeCAD file for static2 analysis to {}...'.format(static2_save_fc_file))
self.active_doc.saveAs(static2_save_fc_file)
@@ -1065,6 +1108,28 @@ def compare_inp_files(file_name1, file_name2):
return result
def compare_files(file_name1, file_name2):
file1 = open(file_name1, 'r')
f1 = file1.readlines()
file1.close()
# workaraound for 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=None, loc_stat_types=None, res_obj_name=None):
if not loc_stat_types:
loc_stat_types = stat_types

View File

@@ -67,6 +67,7 @@ class Prepare(run.Prepare):
def run(self):
self.pushStatus("Preparing input files...\n")
# w = writer.Writer(self.solver, self.directory, True) # test mode
w = writer.Writer(self.solver, self.directory)
try:
w.write()

View File

@@ -91,10 +91,11 @@ def getConstant(name, dimension):
class Writer(object):
def __init__(self, solver, directory):
def __init__(self, solver, directory, testmode=False):
self.analysis = FemUtils.findAnalysisOfMember(solver)
self.solver = solver
self.directory = directory
self.testmode = testmode
self._usedVarNames = set()
self._builder = sifio.Builder()
self._handledObjects = set()
@@ -122,15 +123,18 @@ class Writer(object):
groups.extend(self._builder.getBodyNames())
groups.extend(self._builder.getBoundaryNames())
self._exportToUnv(groups, mesh, unvPath)
binary = settings.getBinary("ElmerGrid")
if binary is None:
raise WriteError("Couldn't find ElmerGrid binary.")
args = [binary,
_ELMERGRID_IFORMAT,
_ELMERGRID_OFORMAT,
unvPath,
"-out", self.directory]
subprocess.call(args)
if self.testmode:
print("We are in testmode ElmerGrid may not be installed!")
else:
binary = settings.getBinary("ElmerGrid")
if binary is None:
raise WriteError("Couldn't find ElmerGrid binary.")
args = [binary,
_ELMERGRID_IFORMAT,
_ELMERGRID_OFORMAT,
unvPath,
"-out", self.directory]
subprocess.call(args)
def _writeStartinfo(self):
path = os.path.join(self.directory, _STARTINFO_NAME)
@@ -158,11 +162,16 @@ class Writer(object):
tools.get_boundary_layer_data()
tools.write_part_file()
tools.write_geo()
tools.run_gmsh_with_geo()
if self.testmode:
print("We are in testmode, GMSH may not be installed!")
import shutil
shutil.copyfile(geoPath, os.path.join(self.directory, "group_mesh.geo"))
else:
tools.run_gmsh_with_geo()
ioMesh = Fem.FemMesh()
ioMesh.read(unvGmshPath)
ioMesh.write(meshPath)
ioMesh = Fem.FemMesh()
ioMesh.read(unvGmshPath)
ioMesh.write(meshPath)
os.remove(brepPath)
os.remove(geoPath)

View File

@@ -0,0 +1 @@
case.sif

View File

@@ -0,0 +1,29 @@
# ***************************************************************************
# * *
# * Copyright (c) 2017 - Bernd Hahnebach <bernd@bimstatik.org> *
# * *
# * 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. *
# * *
# * This program 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 this program; if not, write to the Free Software *
# * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
# * USA *
# * *
# ***************************************************************************
__title__ = "Fem ccx test cases"
__author__ = "Bernd Hahnebach"
__url__ = "http://www.freecadweb.org"
## @package ccx
# \ingroup Fem
# \brief Fem ccx test cases

View File

@@ -0,0 +1,95 @@
Check Keywords Warn
Header
Mesh DB "."
End
Solver 1
Bubbles = Logical False
Calculate Pangle = Logical False
Calculate Principal = Logical False
Calculate Strains = Logical False
Calculate Stresses = Logical False
Displace mesh = Logical False
Eigen Analysis = Logical False
Eigen System Values = Integer 5
Equation = String "Elasticity"
Exec Solver = String "Always"
Linear System Abort Not Converged = Logical False
Linear System Convergence Tolerance = Real 1e-08
Linear System Iterative Method = String "BiCGStab"
Linear System Max Iterations = Integer 500
Linear System Precondition Recompute = Integer 1
Linear System Preconditioning = String "ILU0"
Linear System Residual Output = Integer 1
Linear System Solver = String "Iterative"
Optimize Bandwidth = Logical True
Procedure = File "StressSolve" "StressSolver"
Stabilize = Logical True
Steady State Convergence Tolerance = Real 1e-05
Variable = String "Displacement"
Variable DOFs = Integer 3
End
Simulation
BDF Order = Integer 1
Coordinate Mapping(3) = Integer 1 2 3
Coordinate System = String "Cartesian 3D"
Output Intervals = Integer 1
Simulation Type = String "Steady state"
Steady State Max Iterations = Integer 1
Steady State Min Iterations = Integer 0
Timestepping Method = String "BDF"
Use Mesh Names = Logical True
End
Constants
End
Body 1
Equation = Integer 1
Material = Integer 1
Name = String "Solid1"
End
Material 1
Density = Real 7.9e-06
Heat expansion Coefficient = Real 0.0
Poisson ratio = Real 0.3
Youngs Modulus = Real 200000000.0
End
Equation 1
Active Solvers(2) = Integer 1 2
End
Solver 2
Equation = String "ResultOutput"
Exec Solver = String "After simulation"
Output File Name = File "case"
Procedure = File "ResultOutputSolve" "ResultOutputSolver"
Vtu Format = Logical True
End
Boundary Condition 1
Name = String "Face2"
Normal Force = Real -1000000.0
End
Boundary Condition 2
Displacement 1 = Real 0.0
Displacement 2 = Real 0.0
Displacement 3 = Real 0.0
Name = String "Face1"
End
Boundary Condition 3
Force 1 = Real -0.0
Force 1 Normalize by Area = Logical True
Force 2 = Real -0.0
Force 2 Normalize by Area = Logical True
Force 3 = Real -40000000.0
Force 3 Normalize by Area = Logical True
Name = String "Face6"
End

View File

@@ -0,0 +1,54 @@
// geo file for meshing with GMSH meshing software created by FreeCAD
// open brep geometry
Merge "/tmp/tmp0TVZbM.brep";
// group data
Physical Surface("Face1") = {1};
Physical Surface("Face2") = {2};
Physical Surface("Face6") = {6};
Physical Volume("Solid1") = {1};
// Characteristic Length
// no boundary layer settings for this mesh
// min, max Characteristic Length
Mesh.CharacteristicLengthMax = 1e+22;
Mesh.CharacteristicLengthMin = 9.0;
// optimize the mesh
Mesh.Optimize = 1;
Mesh.OptimizeNetgen = 0;
Mesh.HighOrderOptimize = 0; // for more HighOrderOptimize parameter check http://gmsh.info/doc/texinfo/gmsh.html
// mesh order
Mesh.ElementOrder = 2;
// mesh algorithm, only a few algorithms are usable with 3D boundary layer generation
// 2D mesh algorithm (1=MeshAdapt, 2=Automatic, 5=Delaunay, 6=Frontal, 7=BAMG, 8=DelQuad)
Mesh.Algorithm = 2;
// 3D mesh algorithm (1=Delaunay, 2=New Delaunay, 4=Frontal, 5=Frontal Delaunay, 6=Frontal Hex, 7=MMG3D, 9=R-tree)
Mesh.Algorithm3D = 1;
// meshing
Geometry.Tolerance = 1e-06; // set geometrical tolerance (also used for merging nodes)
Mesh 3;
Coherence Mesh; // Remove duplicate vertices
// save
Mesh.Format = 2;
// Ignore Physical definitions and save all elements;
Mesh.SaveAll = 1;
Save "/tmp/tmpjVhNNb.unv";
//////////////////////////////////////////////////////////////////////
// GMSH documentation:
// http://gmsh.info/doc/texinfo/gmsh.html#Mesh
//
// We do not check if something went wrong, like negative jacobians etc. You can run GMSH manually yourself:
//
// to see full GMSH log, run in bash:
// /usr/local/bin/gmsh - /tmp/tmputZ_uU.geo
//
// to run GMSH and keep file in GMSH GUI (with log), run in bash:
// /usr/local/bin/gmsh /tmp/tmputZ_uU.geo