From d3509a63fda5c72087c4bfd4f77f21fd62b64dca Mon Sep 17 00:00:00 2001 From: Chris Hennes Date: Sat, 25 Sep 2021 16:05:19 -0500 Subject: [PATCH] [Mesh] Add unit tests for NASTRAN reader Also make some minor deprecation and verbosity cleanups to the Mesh unit test cases. --- src/Mod/Mesh/App/MeshTestsApp.py | 60 +++++++++++++------ .../NASTRAN_Test_Delimited_GRID_CQUAD4.bdf | 35 +++++++++++ .../TestData/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf | 29 +++++++++ .../App/TestData/NASTRAN_Test_GRID_CQUAD4.bdf | 33 ++++++++++ src/Mod/Mesh/CMakeLists.txt | 16 +++++ 5 files changed, 155 insertions(+), 18 deletions(-) create mode 100644 src/Mod/Mesh/App/TestData/NASTRAN_Test_Delimited_GRID_CQUAD4.bdf create mode 100644 src/Mod/Mesh/App/TestData/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf create mode 100644 src/Mod/Mesh/App/TestData/NASTRAN_Test_GRID_CQUAD4.bdf diff --git a/src/Mod/Mesh/App/MeshTestsApp.py b/src/Mod/Mesh/App/MeshTestsApp.py index 449d91a1f0..7d932b9484 100644 --- a/src/Mod/Mesh/App/MeshTestsApp.py +++ b/src/Mod/Mesh/App/MeshTestsApp.py @@ -12,6 +12,7 @@ try: except Exception: import thread +from os.path import join #--------------------------------------------------------------------------- # define the functions to test the FreeCAD mesh module @@ -167,7 +168,7 @@ class MeshGeoTestCases(unittest.TestCase): f1 = planarMeshObject.Facets[0] f2 = planarMeshObject.Facets[1] res=f1.intersect(f2) - self.failUnless(len(res) == 0) + self.assertTrue(len(res) == 0) def testIntersection2(self): @@ -182,7 +183,7 @@ class MeshGeoTestCases(unittest.TestCase): f2 = planarMeshObject.Facets[1] # does definitely NOT intersect res=f1.intersect(f2) - self.failUnless(len(res) == 0) + self.assertTrue(len(res) == 0) class PivyTestCases(unittest.TestCase): def setUp(self): @@ -208,11 +209,11 @@ class PivyTestCases(unittest.TestCase): rp.setRay(coin.SbVec3f(-16.05,16.0,16.0),coin.SbVec3f(0,-1,0)) rp.apply(view.getSoRenderManager().getSceneGraph()) pp=rp.getPickedPoint() - self.failUnless(pp != None) + self.assertTrue(pp != None) det=pp.getDetail() - self.failUnless(det.getTypeId() == coin.SoFaceDetail.getClassTypeId()) + self.assertTrue(det.getTypeId() == coin.SoFaceDetail.getClassTypeId()) det=coin.cast(det,str(det.getTypeId().getName())) - self.failUnless(det.getFaceIndex() == 1) + self.assertTrue(det.getFaceIndex() == 1) def testPrimitiveCount(self): if not FreeCAD.GuiUp: @@ -231,8 +232,8 @@ class PivyTestCases(unittest.TestCase): view.setAxisCross(False) pc=coin.SoGetPrimitiveCountAction() pc.apply(view.getSceneGraph()) - self.failUnless(pc.getTriangleCount() == 2) - #self.failUnless(pc.getPointCount() == 6) + self.assertTrue(pc.getTriangleCount() == 2) + #self.assertTrue(pc.getPointCount() == 6) def tearDown(self): #closing doc @@ -243,15 +244,15 @@ class PivyTestCases(unittest.TestCase): def loadFile(name): #lock.acquire() mesh=Mesh.Mesh() - FreeCAD.Console.PrintMessage("Create mesh instance\n") + #FreeCAD.Console.PrintMessage("Create mesh instance\n") #lock.release() mesh.read(name) - FreeCAD.Console.PrintMessage("Mesh loaded successfully.\n") + #FreeCAD.Console.PrintMessage("Mesh loaded successfully.\n") def createMesh(r,s): - FreeCAD.Console.PrintMessage("Create sphere (%s,%s)...\n"%(r,s)) + #FreeCAD.Console.PrintMessage("Create sphere (%s,%s)...\n"%(r,s)) mesh=Mesh.createSphere(r,s) - FreeCAD.Console.PrintMessage("... destroy sphere\n") + #FreeCAD.Console.PrintMessage("... destroy sphere\n") class LoadMeshInThreadsCases(unittest.TestCase): @@ -267,7 +268,7 @@ class LoadMeshInThreadsCases(unittest.TestCase): mesh=Mesh.createSphere(10.0,100) # a fine sphere name=tempfile.gettempdir() + os.sep + "mesh.stl" mesh.write(name) - FreeCAD.Console.PrintMessage("Write mesh to %s\n"%(name)) + #FreeCAD.Console.PrintMessage("Write mesh to %s\n"%(name)) #lock=thread.allocate_lock() for i in range(2): thread.start_new(loadFile,(name,)) @@ -295,9 +296,9 @@ class PolynomialFitCases(unittest.TestCase): v.append(FreeCAD.Vector(2,2,0.0)) d = Mesh.polynomialFit(v) c = d["Coefficients"] - print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5])) + #print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5])) for i in d["Residuals"]: - self.failUnless(math.fabs(i) < 0.0001, "Too high residual %f" % math.fabs(i)) + self.assertTrue(math.fabs(i) < 0.0001, "Too high residual %f" % math.fabs(i)) def testFitExact(self): # symmetric @@ -310,9 +311,9 @@ class PolynomialFitCases(unittest.TestCase): v.append(FreeCAD.Vector(2,1,0.0)) d = Mesh.polynomialFit(v) c = d["Coefficients"] - print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5])) + #print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5])) for i in d["Residuals"]: - self.failUnless(math.fabs(i) < 0.0001, "Too high residual %f" % math.fabs(i)) + self.assertTrue(math.fabs(i) < 0.0001, "Too high residual %f" % math.fabs(i)) def testFitBad(self): # symmetric @@ -328,9 +329,32 @@ class PolynomialFitCases(unittest.TestCase): v.append(FreeCAD.Vector(2,2,0.0)) d = Mesh.polynomialFit(v) c = d["Coefficients"] - print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5])) + #print ("Polynomial: f(x,y)=%f*x^2%+f*y^2%+f*x*y%+f*x%+f*y%+f" % (c[0],c[1],c[2],c[3],c[4],c[5])) for i in d["Residuals"]: - self.failIf(math.fabs(i) < 0.0001, "Residual %f must be higher" % math.fabs(i)) + self.assertFalse(math.fabs(i) < 0.0001, "Residual %f must be higher" % math.fabs(i)) + + def tearDown(self): + pass + + +class NastranReader(unittest.TestCase): + def setUp(self): + self.test_dir = join(FreeCAD.getHomePath(), "Mod", "Mesh", "App", "TestData") + + def testEightCharGRIDElement(self): + m = Mesh.read(f"{self.test_dir}/NASTRAN_Test_GRID_CQUAD4.bdf") + self.assertEqual(m.CountPoints,10) + self.assertEqual(m.CountFacets,8) # Quads split into two triangles + + def testDelimitedGRIDElement(self): + m = Mesh.read(f"{self.test_dir}/NASTRAN_Test_Delimited_GRID_CQUAD4.bdf") + self.assertEqual(m.CountPoints,10) + self.assertEqual(m.CountFacets,8) # Quads split into two triangles + + def testSixteenCharGRIDElement(self): + m = Mesh.read(f"{self.test_dir}/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf") + self.assertEqual(m.CountPoints,4) + self.assertEqual(m.CountFacets,2) # Quads split into two triangles def tearDown(self): pass diff --git a/src/Mod/Mesh/App/TestData/NASTRAN_Test_Delimited_GRID_CQUAD4.bdf b/src/Mod/Mesh/App/TestData/NASTRAN_Test_Delimited_GRID_CQUAD4.bdf new file mode 100644 index 0000000000..2e8bb1ec80 --- /dev/null +++ b/src/Mod/Mesh/App/TestData/NASTRAN_Test_Delimited_GRID_CQUAD4.bdf @@ -0,0 +1,35 @@ +$------------------------------------------------------------------------------- +$ Nastran case control file +$ Part of the FreeCAD unit test system. +$ This case uses space-delimited floating-point numbers that are not aligned to +$ eight-character field widths. +$------------------------------------------------------------------------------- +$ Exec control section +$------------------------------------------------------------------------------- +ID TESTCASE,Nastran +APP DISP +SOL 1 +TIME 20 +CEND +$------------------------------------------------------------------------------- +$ Case control section +$------------------------------------------------------------------------------- +MAXLINES = 1000000 +TITLE = TESTCASE +SUBTITLE = Test case for FreeCAD Mesh NASTRAN input + +GRID 1 0.00E+00 0.00E+00 0.00E+00 +GRID 2 0.00E+00 0.500000 0.00E+00 +GRID 3 0.00E+00 1.000000 0.00E+00 +GRID 4 0.00E+00 1.500000 0.00E+00 +GRID 5 0.00E+00 2.000000 0.00E+00 +GRID 6 0.00E+00 0.00E+00 0.500000 +GRID 7 0.00E+00 0.500000 0.500000 +GRID 8 0.00E+00 1.000000 0.500000 +GRID 9 0.00E+00 1.500000 0.500000 +GRID 10 0.00E+00 2.000000 0.500000 +CQUAD4 1 400 1 6 7 2 +CQUAD4 2 400 2 7 8 3 +CQUAD4 3 400 3 8 9 4 +CQUAD4 4 400 4 9 10 5 +ENDDATA diff --git a/src/Mod/Mesh/App/TestData/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf b/src/Mod/Mesh/App/TestData/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf new file mode 100644 index 0000000000..5891b060c5 --- /dev/null +++ b/src/Mod/Mesh/App/TestData/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf @@ -0,0 +1,29 @@ +------------------------------------------------------------------------------- +$ Nastran case control file +$ Part of the FreeCAD unit test system. +$ This cases uses the high-prevision 16-char width GRID* element with no +$ delimiter. +$------------------------------------------------------------------------------- +$ Exec control section +$------------------------------------------------------------------------------- +ID TESTCASE,Nastran +APP DISP +SOL 1 +TIME 20 +CEND +$------------------------------------------------------------------------------- +$ Case control section +$------------------------------------------------------------------------------- +MAXLINES = 1000000 +TITLE = TESTCASE +SUBTITLE = Test case for FreeCAD Mesh NASTRAN input + +GRID* 1 0.00000000000000-1. +* 0. +GRID* 2 1.00000000000000-1. +* 0. +GRID* 3 0.000000000000001. +* 0. +GRID* 4 1.000000000000001. +* 0. +CQUAD4 1 400 1 2 4 3 diff --git a/src/Mod/Mesh/App/TestData/NASTRAN_Test_GRID_CQUAD4.bdf b/src/Mod/Mesh/App/TestData/NASTRAN_Test_GRID_CQUAD4.bdf new file mode 100644 index 0000000000..decf720ede --- /dev/null +++ b/src/Mod/Mesh/App/TestData/NASTRAN_Test_GRID_CQUAD4.bdf @@ -0,0 +1,33 @@ +$------------------------------------------------------------------------------- +$ Nastran case control file +$ Part of the FreeCAD unit test system. +$------------------------------------------------------------------------------- +$ Exec control section +$------------------------------------------------------------------------------- +ID TESTCASE,Nastran +APP DISP +SOL 1 +TIME 20 +CEND +$------------------------------------------------------------------------------- +$ Case control section +$------------------------------------------------------------------------------- +MAXLINES = 1000000 +TITLE = TESTCASE +SUBTITLE = Test case for FreeCAD Mesh NASTRAN input + +GRID 1 0.00E+000.00E+000.00E+00 +GRID 2 0.00E+000.5000000.00E+00 +GRID 3 0.00E+001.0000000.00E+00 +GRID 4 0.00E+001.5000000.00E+00 +GRID 5 0.00E+002.0000000.00E+00 +GRID 6 0.00E+000.00E+000.500000 +GRID 7 0.00E+000.5000000.500000 +GRID 8 0.00E+001.0000000.500000 +GRID 9 0.00E+001.5000000.500000 +GRID 10 0.00E+002.0000000.500000 +CQUAD4 1 400 1 6 7 2 +CQUAD4 2 400 2 7 8 3 +CQUAD4 3 400 3 8 9 4 +CQUAD4 4 400 4 9 10 5 +ENDDATA diff --git a/src/Mod/Mesh/CMakeLists.txt b/src/Mod/Mesh/CMakeLists.txt index e9c35d1be7..c53218a8c1 100644 --- a/src/Mod/Mesh/CMakeLists.txt +++ b/src/Mod/Mesh/CMakeLists.txt @@ -10,6 +10,12 @@ set(Mesh_Scripts App/MeshTestsApp.py ) +set(MeshTestDataFiles + App/TestData/NASTRAN_Test_GRID_CQUAD4.bdf + App/TestData/NASTRAN_Test_Delimited_GRID_CQUAD4.bdf + App/TestData/NASTRAN_Test_GRIDSTAR_CQUAD4.bdf +) + if(BUILD_GUI) list (APPEND Mesh_Scripts InitGui.py) endif(BUILD_GUI) @@ -18,15 +24,25 @@ add_custom_target(MeshScripts ALL SOURCES ${Mesh_Scripts} ) +add_custom_target(MeshTestData ALL + SOURCES ${MeshTestDataFiles} +) + fc_target_copy_resource_flat(MeshScripts ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_BINARY_DIR}/Mod/Mesh ${Mesh_Scripts} ) +fc_copy_sources(MeshTestData "${CMAKE_BINARY_DIR}/Mod/Mesh/" ${MeshTestDataFiles}) + INSTALL( FILES ${Mesh_Scripts} DESTINATION Mod/Mesh ) + + +INSTALL(FILES ${MeshTestDataFiles} DESTINATION Mod/Mesh/Data) +