From 3e1b16b198fff77d929902570d914e9fdcf32cea Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Wed, 17 Mar 2021 09:26:48 +0100 Subject: [PATCH 1/8] FEM: elmer writer, only write denisty if really needed, partitially revert 2d73444a66ac, unit test fix in separate commit --- src/Mod/Fem/femsolver/elmer/writer.py | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/Mod/Fem/femsolver/elmer/writer.py b/src/Mod/Fem/femsolver/elmer/writer.py index b15f214336..b8138d95a4 100644 --- a/src/Mod/Fem/femsolver/elmer/writer.py +++ b/src/Mod/Fem/femsolver/elmer/writer.py @@ -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) From 71141d09640be0cb1f0c82a8278e27fad4069547 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Wed, 17 Mar 2021 09:33:21 +0100 Subject: [PATCH 2/8] FEM: fix unit test because of denisty writing. See 3e1b16b198f. This reverts 8ec3766b00d --- src/Mod/Fem/femtest/data/elmer/box_static_0_mm.sif | 1 - src/Mod/Fem/femtest/data/elmer/ccxcantilever_faceload_0_mm.sif | 1 - src/Mod/Fem/femtest/data/elmer/ccxcantilever_faceload_1_si.sif | 1 - src/Mod/Fem/femtest/data/elmer/ccxcantilever_nodeload_0_mm.sif | 1 - .../data/elmer/ccxcantilever_prescribeddisplacement_0_mm.sif | 1 - 5 files changed, 5 deletions(-) diff --git a/src/Mod/Fem/femtest/data/elmer/box_static_0_mm.sif b/src/Mod/Fem/femtest/data/elmer/box_static_0_mm.sif index 21632ad3ea..0e4feab9b3 100644 --- a/src/Mod/Fem/femtest/data/elmer/box_static_0_mm.sif +++ b/src/Mod/Fem/femtest/data/elmer/box_static_0_mm.sif @@ -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 diff --git a/src/Mod/Fem/femtest/data/elmer/ccxcantilever_faceload_0_mm.sif b/src/Mod/Fem/femtest/data/elmer/ccxcantilever_faceload_0_mm.sif index 7a65425ef7..a9f28364b4 100644 --- a/src/Mod/Fem/femtest/data/elmer/ccxcantilever_faceload_0_mm.sif +++ b/src/Mod/Fem/femtest/data/elmer/ccxcantilever_faceload_0_mm.sif @@ -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 diff --git a/src/Mod/Fem/femtest/data/elmer/ccxcantilever_faceload_1_si.sif b/src/Mod/Fem/femtest/data/elmer/ccxcantilever_faceload_1_si.sif index 2d411d0c7c..803bdb53c2 100644 --- a/src/Mod/Fem/femtest/data/elmer/ccxcantilever_faceload_1_si.sif +++ b/src/Mod/Fem/femtest/data/elmer/ccxcantilever_faceload_1_si.sif @@ -54,7 +54,6 @@ Body 1 End Material 1 - Density = Real 7900.0 Poisson ratio = Real 0.3 Youngs Modulus = Real 210000000000.0 End diff --git a/src/Mod/Fem/femtest/data/elmer/ccxcantilever_nodeload_0_mm.sif b/src/Mod/Fem/femtest/data/elmer/ccxcantilever_nodeload_0_mm.sif index 296f05583a..6221ca7fb0 100644 --- a/src/Mod/Fem/femtest/data/elmer/ccxcantilever_nodeload_0_mm.sif +++ b/src/Mod/Fem/femtest/data/elmer/ccxcantilever_nodeload_0_mm.sif @@ -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 diff --git a/src/Mod/Fem/femtest/data/elmer/ccxcantilever_prescribeddisplacement_0_mm.sif b/src/Mod/Fem/femtest/data/elmer/ccxcantilever_prescribeddisplacement_0_mm.sif index 8a772c3e2e..fab6d80689 100644 --- a/src/Mod/Fem/femtest/data/elmer/ccxcantilever_prescribeddisplacement_0_mm.sif +++ b/src/Mod/Fem/femtest/data/elmer/ccxcantilever_prescribeddisplacement_0_mm.sif @@ -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 From 5bf22ad4ac013748a2bace6652783cd32d392c01 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Wed, 17 Mar 2021 10:45:37 +0100 Subject: [PATCH 3/8] FEM: examples, elmer tut1, eigenvalues, fix some problems --- ...uitutorial01_eigenvalue_of_elastic_beam.py | 85 ++++++++----------- 1 file changed, 37 insertions(+), 48 deletions(-) diff --git a/src/Mod/Fem/femexamples/elmer_nonguitutorial01_eigenvalue_of_elastic_beam.py b/src/Mod/Fem/femexamples/elmer_nonguitutorial01_eigenvalue_of_elastic_beam.py index 5c6d3b3449..97f0abf7a8 100644 --- a/src/Mod/Fem/femexamples/elmer_nonguitutorial01_eigenvalue_of_elastic_beam.py +++ b/src/Mod/Fem/femexamples/elmer_nonguitutorial01_eigenvalue_of_elastic_beam.py @@ -45,19 +45,20 @@ 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" - } + info = { + "name": "NonGui Tutorial 01 - Eigenvalue of elastic beam", + "meshtype": "solid", + "meshelement": "Tet10", + "constraints": [], + "solvers": ["elmer"], + "material": "solid", + "equation": "elasticity" + } return info -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 +77,23 @@ def setup_base(doc=None, solvertype="ccxtools"): # analysis analysis = ObjectsFem.makeAnalysis(doc, "Analysis") + # solver + if 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) + ) + # material material_object = analysis.addObject( ObjectsFem.makeMaterialSolid(doc, "MechanicalMaterial") @@ -87,6 +105,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 +133,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 From 0f12d6e9ec5de8dfe0fc0c2487f336bdd23eb178 Mon Sep 17 00:00:00 2001 From: UR-0 Date: Wed, 17 Mar 2021 10:58:37 +0100 Subject: [PATCH 4/8] [FEM] improve get_gmsh_version --- src/Mod/Fem/femmesh/gmshtools.py | 28 ++++++++++++--------- src/Mod/Fem/femtaskpanels/task_mesh_gmsh.py | 6 ++++- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/Mod/Fem/femmesh/gmshtools.py b/src/Mod/Fem/femmesh/gmshtools.py index d64a6e9d8e..145fad294d 100644 --- a/src/Mod/Fem/femmesh/gmshtools.py +++ b/src/Mod/Fem/femmesh/gmshtools.py @@ -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 diff --git a/src/Mod/Fem/femtaskpanels/task_mesh_gmsh.py b/src/Mod/Fem/femtaskpanels/task_mesh_gmsh.py index 31c2400304..21778d7ad5 100644 --- a/src/Mod/Fem/femtaskpanels/task_mesh_gmsh.py +++ b/src/Mod/Fem/femtaskpanels/task_mesh_gmsh.py @@ -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 From 0581409fbedaa396ccb65d9ba9305d5b8dbff2b8 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 17 Mar 2021 11:56:58 +0100 Subject: [PATCH 5/8] OpenSCAD: [skip ci] fix incorrect filename creation in unit tests --- .../OpenSCADTest/app/test_importCSG.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py b/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py index 7e4e23dfb6..915da2b077 100644 --- a/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py +++ b/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py @@ -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() From 9a105e34056d1850f36a527d797431911bc58bb9 Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 17 Mar 2021 11:59:14 +0100 Subject: [PATCH 6/8] OpenSCAD: fix utility_create_scad by changing currently working directory so that test_import_surface doesn't fail any more --- src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py b/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py index 915da2b077..0ccd8f2931 100644 --- a/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py +++ b/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py @@ -201,6 +201,7 @@ polyhedron( f = open(filename,"w+") f.write(scadCode) f.close() + os.chdir(temp_dir) return importCSG.open(filename) def test_import_difference(self): From cf241d0c936ca4ad9946b592292d56771ae6d380 Mon Sep 17 00:00:00 2001 From: Bernd Hahnebach Date: Wed, 17 Mar 2021 12:24:00 +0100 Subject: [PATCH 7/8] FEM: examples, elmer eigenvalues, add calculix solver for frequencies --- ...uitutorial01_eigenvalue_of_elastic_beam.py | 27 +++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/Mod/Fem/femexamples/elmer_nonguitutorial01_eigenvalue_of_elastic_beam.py b/src/Mod/Fem/femexamples/elmer_nonguitutorial01_eigenvalue_of_elastic_beam.py index 97f0abf7a8..ce73901a10 100644 --- a/src/Mod/Fem/femexamples/elmer_nonguitutorial01_eigenvalue_of_elastic_beam.py +++ b/src/Mod/Fem/femexamples/elmer_nonguitutorial01_eigenvalue_of_elastic_beam.py @@ -45,16 +45,15 @@ def init_doc(doc=None): def get_information(): - info = { + return { "name": "NonGui Tutorial 01 - Eigenvalue of elastic beam", "meshtype": "solid", "meshelement": "Tet10", "constraints": [], - "solvers": ["elmer"], + "solvers": ["calculix", "ccxtools", "elmer"], "material": "solid", - "equation": "elasticity" + "equation": "elasticity" # "frequency", but list not allowed here } - return info def setup(doc=None, solvertype="elmer"): @@ -78,7 +77,16 @@ def setup(doc=None, solvertype="elmer"): analysis = ObjectsFem.makeAnalysis(doc, "Analysis") # solver - if solvertype == "elmer": + 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] @@ -93,6 +101,15 @@ def setup(doc=None, solvertype="elmer"): "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( From 371b089ea0c98b640b034b39f1370554fb38d72e Mon Sep 17 00:00:00 2001 From: wmayer Date: Wed, 17 Mar 2021 12:35:15 +0100 Subject: [PATCH 8/8] OpenSCAD: fix test_import_surface by changing currently working directory --- .../OpenSCADTest/app/test_importCSG.py | 73 +++++++++++-------- 1 file changed, 41 insertions(+), 32 deletions(-) diff --git a/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py b/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py index 0ccd8f2931..97eed68900 100644 --- a/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py +++ b/src/Mod/OpenSCAD/OpenSCADTest/app/test_importCSG.py @@ -201,7 +201,6 @@ polyhedron( f = open(filename,"w+") f.write(scadCode) f.close() - os.chdir(temp_dir) return importCSG.open(filename) def test_import_difference(self): @@ -314,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