Merge branch 'master' into bugfix/dogbone-on-straight-edges-and-noop-moves

This commit is contained in:
mlampert
2021-03-17 20:13:56 -07:00
committed by GitHub
10 changed files with 145 additions and 114 deletions

View File

@@ -45,19 +45,19 @@ def init_doc(doc=None):
def get_information():
info = {"name": "Box Analysis Static",
"meshtype": "solid",
"meshelement": "Tet10",
"constraints": [],
"solvers": ["calculix", "elmer"],
"material": "solid",
"equation": "elasticity"
}
return info
return {
"name": "NonGui Tutorial 01 - Eigenvalue of elastic beam",
"meshtype": "solid",
"meshelement": "Tet10",
"constraints": [],
"solvers": ["calculix", "ccxtools", "elmer"],
"material": "solid",
"equation": "elasticity" # "frequency", but list not allowed here
}
def setup_base(doc=None, solvertype="ccxtools"):
# setup box base model
def setup(doc=None, solvertype="elmer"):
# setup model
if doc is None:
doc = init_doc()
@@ -76,6 +76,41 @@ def setup_base(doc=None, solvertype="ccxtools"):
# analysis
analysis = ObjectsFem.makeAnalysis(doc, "Analysis")
# solver
if solvertype == "calculix":
solver_object = analysis.addObject(
ObjectsFem.makeSolverCalculix(doc, "SolverCalculiX")
)[0]
elif solvertype == "ccxtools":
solver_object = analysis.addObject(
ObjectsFem.makeSolverCalculixCcxTools(doc, "CalculiXccxTools")
)[0]
solver_object.WorkingDir = u""
elif solvertype == "elmer":
solver_object = analysis.addObject(
ObjectsFem.makeSolverElmer(doc, "SolverElmer")
)[0]
eq_obj = ObjectsFem.makeEquationElasticity(doc, solver_object)
eq_obj.LinearSolverType = "Direct"
# direct solver was used in the turorial, thus used here too
# the iterative is much faster and gives the same results
eq_obj.DoFrequencyAnalysis = True
eq_obj.CalculateStresses = True
else:
FreeCAD.Console.PrintWarning(
"Not known or not supported solver type: {}. "
"No solver object was created.\n".format(solvertype)
)
if solvertype == "calculix" or solvertype == "ccxtools":
solver_object.AnalysisType = "frequency"
solver_object.GeometricalNonlinearity = "linear"
solver_object.ThermoMechSteadyState = False
solver_object.MatrixSolverType = "default"
solver_object.IterationsControlParameterTimeUse = False
solver_object.EigenmodesCount = 5
solver_object.EigenmodeHighLimit = 1000000.0
solver_object.EigenmodeLowLimit = 0.01
# material
material_object = analysis.addObject(
ObjectsFem.makeMaterialSolid(doc, "MechanicalMaterial")
@@ -87,6 +122,15 @@ def setup_base(doc=None, solvertype="ccxtools"):
mat["Density"] = "2330 kg/m^3"
material_object.Material = mat
# fixed_constraint
fixed_constraint = analysis.addObject(
ObjectsFem.makeConstraintFixed(doc, name="FemConstraintFixed")
)[0]
fixed_constraint.References = [
(geom_obj, "Face1"),
(geom_obj, "Face2")
]
# mesh
from .meshes.mesh_eigenvalue_of_elastic_beam_tetra10 import create_nodes
from .meshes.mesh_eigenvalue_of_elastic_beam_tetra10 import create_elements
@@ -106,41 +150,3 @@ def setup_base(doc=None, solvertype="ccxtools"):
doc.recompute()
return doc
def setup(doc=None, solvertype="elmer"):
# setup box static, add a fixed, force and a pressure constraint
doc = setup_base(doc, solvertype)
analysis = doc.Analysis
# solver
if solvertype == "calculix":
solver_object = analysis.addObject(
ObjectsFem.makeSolverCalculix(doc, "SolverCalculiX")
)[0]
elif solvertype == "ccxtools":
solver_object = analysis.addObject(
ObjectsFem.makeSolverCalculixCcxTools(doc, "CalculiXccxTools")
)[0]
solver_object.WorkingDir = u""
elif solvertype == "elmer":
solver_object = analysis.addObject(
ObjectsFem.makeSolverElmer(doc, "SolverElmer")
)[0]
ObjectsFem.makeEquationElasticity(doc, solver_object)
else:
FreeCAD.Console.PrintWarning(
"Not known or not supported solver type: {}. "
"No solver object was created.\n".format(solvertype)
)
if solvertype == "calculix" or solvertype == "ccxtools":
solver_object.SplitInputWriter = False
solver_object.AnalysisType = "static"
solver_object.GeometricalNonlinearity = "linear"
solver_object.ThermoMechSteadyState = False
solver_object.MatrixSolverType = "default"
solver_object.IterationsControlParameterTimeUse = False
doc.recompute()
return doc

View File

@@ -370,32 +370,36 @@ class GmshTools():
def get_gmsh_version(self):
self.get_gmsh_command()
if os.path.exists(self.gmsh_bin):
found_message = "executable found: " + self.gmsh_bin
found_message = "file found: " + self.gmsh_bin
Console.PrintMessage(found_message + "\n")
else:
found_message = "executable not found: " + self.gmsh_bin
found_message = "file not found: " + self.gmsh_bin
Console.PrintError(found_message + "\n")
return (None, None, None), found_message
command_list = [self.gmsh_bin, "--info"]
p = subprocess.Popen(
command_list,
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
gmsh_stdout, gmsh_stderr = p.communicate()
try:
p = subprocess.Popen(
command_list,
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True
)
except Exception as e:
Console.PrintMessage(str(e) + "\n")
return (None, None, None), found_message + "\n\n" + "Error: " + str(e)
gmsh_stdout, gmsh_stderr = p.communicate()
Console.PrintMessage("Gmsh: StdOut:\n" + gmsh_stdout + "\n")
if gmsh_stderr:
Console.PrintError("Gmsh: StdErr:\n" + gmsh_stderr + "\n")
match = re.search("^Version\s*:\s*(\d+)\.(\d+)\.(\d+)", gmsh_stdout)
if match:
return match.group(1, 2, 3), found_message + "\n\n" + gmsh_stdout # major, minor, patch, fullmessage
return match.group(1, 2, 3), found_message + "\n\n" + gmsh_stdout # (major, minor, patch), fullmessage
else:
return (None, None, None), found_message + "\n\n" + gmsh_stdout
return (None, None, None), found_message + "\n\n" + "Warning: Output not recognized\n\n" + gmsh_stdout
def get_region_data(self):
# mesh regions

View File

@@ -636,11 +636,24 @@ class Writer(object):
return None
def _handleElasticityMaterial(self, bodies):
# density
# is needed for self weight constraints and frequency analysis
density_needed = False
for equation in self.solver.Group:
if femutils.is_of_type(equation, "Fem::EquationElmerElasticity"):
if equation.DoFrequencyAnalysis is True:
density_needed = True
break # there could be a second equation without frequency
gravObj = self._getSingleMember("Fem::ConstraintSelfWeight")
if gravObj is not None:
density_needed = True
# temperature
tempObj = self._getSingleMember("Fem::ConstraintInitialTemperature")
if tempObj is not None:
refTemp = self._getFromUi(tempObj.initialTemperature, "K", "O")
for name in bodies:
self._material(name, "Reference Temperature", refTemp)
# get the material data for all boddies
for obj in self._getMember("App::MaterialObject"):
m = obj.Material
refs = (
@@ -649,12 +662,11 @@ class Writer(object):
else self._getAllBodies()
)
for name in (n for n in refs if n in bodies):
# density has to be written even without self weight constraint
# https://forum.freecadweb.org/viewtopic.php?f=18&t=56590#p487117
self._material(
name, "Density",
self._getDensity(m)
)
if density_needed is True:
self._material(
name, "Density",
self._getDensity(m)
)
self._material(
name, "Youngs Modulus",
self._getYoungsModulus(m)

View File

@@ -171,7 +171,11 @@ class _TaskPanel:
def get_gmsh_version(self):
from femmesh import gmshtools
version, full_message = gmshtools.GmshTools(self.mesh_obj, self.analysis).get_gmsh_version()
QtGui.QMessageBox.information(
if version[0] and version[1] and version[2]:
messagebox = QtGui.QMessageBox.information
else:
messagebox = QtGui.QMessageBox.warning
messagebox(
None,
"Gmsh - Information",
full_message

View File

@@ -53,7 +53,6 @@ Body 1
End
Material 1
Density = Real 7.9e-06
Poisson ratio = Real 0.3
Youngs Modulus = Real 200000000.0
End

View File

@@ -53,7 +53,6 @@ Body 1
End
Material 1
Density = Real 7.9e-06
Poisson ratio = Real 0.3
Youngs Modulus = Real 210000000.0
End

View File

@@ -54,7 +54,6 @@ Body 1
End
Material 1
Density = Real 7900.0
Poisson ratio = Real 0.3
Youngs Modulus = Real 210000000000.0
End

View File

@@ -53,7 +53,6 @@ Body 1
End
Material 1
Density = Real 7.9e-06
Poisson ratio = Real 0.3
Youngs Modulus = Real 210000000.0
End

View File

@@ -53,7 +53,6 @@ Body 1
End
Material 1
Density = Real 7.9e-06
Poisson ratio = Real 0.3
Youngs Modulus = Real 210000000.0
End

View File

@@ -74,7 +74,7 @@ class TestImportCSG(unittest.TestCase):
def test_import_sphere(self):
with tempfile.TemporaryDirectory() as temp_dir:
filename = temp_dir + os.pathsep + "sphere.scad"
filename = temp_dir + os.path.sep + "sphere.scad"
f = open(filename,"w+")
f.write("sphere(10.0);")
f.close()
@@ -86,7 +86,7 @@ class TestImportCSG(unittest.TestCase):
def test_import_cylinder(self):
with tempfile.TemporaryDirectory() as temp_dir:
filename = temp_dir + os.pathsep + "cylinder.scad"
filename = temp_dir + os.path.sep + "cylinder.scad"
f = open(filename,"w+")
f.write("cylinder(50.0,d=10.0);")
f.close()
@@ -99,7 +99,7 @@ class TestImportCSG(unittest.TestCase):
def test_import_cube(self):
with tempfile.TemporaryDirectory() as temp_dir:
filename = temp_dir + os.pathsep + "cube.scad"
filename = temp_dir + os.path.sep + "cube.scad"
f = open(filename,"w+")
f.write("cube([1.0,2.0,3.0]);")
f.close()
@@ -113,7 +113,7 @@ class TestImportCSG(unittest.TestCase):
def test_import_circle(self):
with tempfile.TemporaryDirectory() as temp_dir:
filename = temp_dir + os.pathsep + "circle.scad"
filename = temp_dir + os.path.sep + "circle.scad"
f = open(filename,"w+")
f.write("circle(10.0);")
f.close()
@@ -125,7 +125,7 @@ class TestImportCSG(unittest.TestCase):
def test_import_square(self):
with tempfile.TemporaryDirectory() as temp_dir:
filename = temp_dir + os.pathsep + "square.scad"
filename = temp_dir + os.path.sep + "square.scad"
f = open(filename,"w+")
f.write("square([1.0,2.0]);")
f.close()
@@ -138,7 +138,7 @@ class TestImportCSG(unittest.TestCase):
def test_import_text(self):
with tempfile.TemporaryDirectory() as temp_dir:
filename = temp_dir + os.pathsep + "text.scad"
filename = temp_dir + os.path.sep + "text.scad"
f = open(filename,"w+")
f.write("text(\"X\");") # Keep it short to keep the test fast-ish
f.close()
@@ -152,7 +152,7 @@ class TestImportCSG(unittest.TestCase):
def test_import_polygon_nopath(self):
with tempfile.TemporaryDirectory() as temp_dir:
filename = temp_dir + os.pathsep + "polygon_nopath.scad"
filename = temp_dir + os.path.sep + "polygon_nopath.scad"
f = open(filename,"w+")
f.write("polygon(points=[[0,0],[100,0],[130,50],[30,50]]);")
f.close()
@@ -164,7 +164,7 @@ class TestImportCSG(unittest.TestCase):
def test_import_polygon_path(self):
with tempfile.TemporaryDirectory() as temp_dir:
filename = temp_dir + os.pathsep + "polygon_path.scad"
filename = temp_dir + os.path.sep + "polygon_path.scad"
f = open(filename,"w+")
f.write("polygon([[0,0],[100,0],[130,50],[30,50]], paths=[[0,1,2,3]]);")
f.close()
@@ -176,7 +176,7 @@ class TestImportCSG(unittest.TestCase):
def test_import_polyhedron(self):
with tempfile.TemporaryDirectory() as temp_dir:
filename = temp_dir + os.pathsep + "polyhedron.scad"
filename = temp_dir + os.path.sep + "polyhedron.scad"
f = open(filename,"w+")
f.write(
"""
@@ -197,7 +197,7 @@ polyhedron(
def utility_create_scad(self, scadCode, name):
with tempfile.TemporaryDirectory() as temp_dir:
filename = temp_dir + os.pathsep + name + ".scad"
filename = temp_dir + os.path.sep + name + ".scad"
f = open(filename,"w+")
f.write(scadCode)
f.close()
@@ -313,38 +313,48 @@ polyhedron(
FreeCAD.closeDocument(doc.Name)
def test_import_surface(self):
testfile = join(self.test_dir, "Surface.dat").replace('\\','/')
doc = self.utility_create_scad(f"surface(file = \"{testfile}\", center = true, convexity = 5);", "surface_simple_dat")
object = doc.ActiveObject
self.assertTrue (object is not None)
self.assertAlmostEqual (object.Shape.Volume, 275.000000, 6)
self.assertAlmostEqual (object.Shape.BoundBox.XMin, -4.5, 6)
self.assertAlmostEqual (object.Shape.BoundBox.XMax, 4.5, 6)
self.assertAlmostEqual (object.Shape.BoundBox.YMin, -4.5, 6)
self.assertAlmostEqual (object.Shape.BoundBox.YMax, 4.5, 6)
FreeCAD.closeDocument(doc.Name)
testfile = join(self.test_dir, "Surface.dat").replace('\\','/')
doc = self.utility_create_scad(f"surface(file = \"{testfile}\", convexity = 5);", "surface_uncentered_dat")
object = doc.ActiveObject
self.assertTrue (object is not None)
self.assertAlmostEqual (object.Shape.Volume, 275.000000, 6)
self.assertAlmostEqual (object.Shape.BoundBox.XMin, 0, 6)
self.assertAlmostEqual (object.Shape.BoundBox.XMax, 9, 6)
self.assertAlmostEqual (object.Shape.BoundBox.YMin, 0, 6)
self.assertAlmostEqual (object.Shape.BoundBox.YMax, 9, 6)
FreeCAD.closeDocument(doc.Name)
# Workaround for absolute vs. relative path issue
# Inside the OpenSCAD file an absolute path name to Surface.dat is used
# but by using the OpenSCAD executable to create a CSG file it's converted
# into a path name relative to the output filename.
# In order to open the CAG file correctly the cwd must be temporarily changed
with tempfile.TemporaryDirectory() as temp_dir:
cwd = os.getcwd()
os.chdir(temp_dir)
testfile = join(self.test_dir, "Surface2.dat").replace('\\','/')
doc = self.utility_create_scad(f"surface(file = \"{testfile}\", center = true, convexity = 5);", "surface_rectangular_dat")
object = doc.ActiveObject
self.assertTrue (object is not None)
self.assertAlmostEqual (object.Shape.Volume, 24.5500000, 6)
self.assertAlmostEqual (object.Shape.BoundBox.XMin, -2, 6)
self.assertAlmostEqual (object.Shape.BoundBox.XMax, 2, 6)
self.assertAlmostEqual (object.Shape.BoundBox.YMin, -1.5, 6)
self.assertAlmostEqual (object.Shape.BoundBox.YMax, 1.5, 6)
FreeCAD.closeDocument(doc.Name)
testfile = join(self.test_dir, "Surface.dat").replace('\\','/')
doc = self.utility_create_scad(f"surface(file = \"{testfile}\", center = true, convexity = 5);", "surface_simple_dat")
object = doc.ActiveObject
self.assertTrue (object is not None)
self.assertAlmostEqual (object.Shape.Volume, 275.000000, 6)
self.assertAlmostEqual (object.Shape.BoundBox.XMin, -4.5, 6)
self.assertAlmostEqual (object.Shape.BoundBox.XMax, 4.5, 6)
self.assertAlmostEqual (object.Shape.BoundBox.YMin, -4.5, 6)
self.assertAlmostEqual (object.Shape.BoundBox.YMax, 4.5, 6)
FreeCAD.closeDocument(doc.Name)
testfile = join(self.test_dir, "Surface.dat").replace('\\','/')
doc = self.utility_create_scad(f"surface(file = \"{testfile}\", convexity = 5);", "surface_uncentered_dat")
object = doc.ActiveObject
self.assertTrue (object is not None)
self.assertAlmostEqual (object.Shape.Volume, 275.000000, 6)
self.assertAlmostEqual (object.Shape.BoundBox.XMin, 0, 6)
self.assertAlmostEqual (object.Shape.BoundBox.XMax, 9, 6)
self.assertAlmostEqual (object.Shape.BoundBox.YMin, 0, 6)
self.assertAlmostEqual (object.Shape.BoundBox.YMax, 9, 6)
FreeCAD.closeDocument(doc.Name)
testfile = join(self.test_dir, "Surface2.dat").replace('\\','/')
doc = self.utility_create_scad(f"surface(file = \"{testfile}\", center = true, convexity = 5);", "surface_rectangular_dat")
object = doc.ActiveObject
self.assertTrue (object is not None)
self.assertAlmostEqual (object.Shape.Volume, 24.5500000, 6)
self.assertAlmostEqual (object.Shape.BoundBox.XMin, -2, 6)
self.assertAlmostEqual (object.Shape.BoundBox.XMax, 2, 6)
self.assertAlmostEqual (object.Shape.BoundBox.YMin, -1.5, 6)
self.assertAlmostEqual (object.Shape.BoundBox.YMax, 1.5, 6)
FreeCAD.closeDocument(doc.Name)
os.chdir(cwd)
def test_import_projection(self):
pass