1import petsc4py 2from petsc4py import PETSc 3import unittest 4import os 5import filecmp 6import numpy as np 7import importlib 8 9# -------------------------------------------------------------------- 10 11ERR_ARG_OUTOFRANGE = 63 12 13 14class BaseTestPlex: 15 COMM = PETSc.COMM_WORLD 16 DIM = 1 17 CELLS = [[0, 1], [1, 2]] 18 COORDS = [[0.0], [0.5], [1.0]] 19 COMP = 1 20 DOFS = [1, 0] 21 22 def setUp(self): 23 self.plex = PETSc.DMPlex().createFromCellList( 24 self.DIM, self.CELLS, self.COORDS, comm=self.COMM 25 ) 26 27 def tearDown(self): 28 self.plex.destroy() 29 self.plex = None 30 PETSc.garbage_cleanup() 31 32 def testTopology(self): 33 rank = self.COMM.rank 34 dim = self.plex.getDimension() 35 pStart, pEnd = self.plex.getChart() 36 cStart, cEnd = self.plex.getHeightStratum(0) 37 vStart, vEnd = self.plex.getDepthStratum(0) 38 numDepths = self.plex.getLabelSize('depth') 39 coords_raw = self.plex.getCoordinates().getArray() 40 coords = np.reshape(coords_raw, (vEnd - vStart, dim)) 41 self.assertEqual(dim, self.DIM) 42 self.assertEqual(numDepths, self.DIM + 1) 43 if rank == 0 and self.CELLS is not None: 44 self.assertEqual(cEnd - cStart, len(self.CELLS)) 45 if rank == 0 and self.COORDS is not None: 46 self.assertEqual(vEnd - vStart, len(self.COORDS)) 47 self.assertTrue((coords == self.COORDS).all()) 48 49 def testClosure(self): 50 pStart, pEnd = self.plex.getChart() 51 for p in range(pStart, pEnd): 52 closure = self.plex.getTransitiveClosure(p)[0] 53 for c in closure: 54 cone = self.plex.getCone(c) 55 self.assertEqual(self.plex.getConeSize(c), len(cone)) 56 for i in cone: 57 self.assertIn(i, closure) 58 star = self.plex.getTransitiveClosure(p, useCone=False)[0] 59 for s in star: 60 support = self.plex.getSupport(s) 61 self.assertEqual(self.plex.getSupportSize(s), len(support)) 62 for i in support: 63 self.assertIn(i, star) 64 65 def testAdjacency(self): 66 PETSc.DMPlex.setAdjacencyUseAnchors(self.plex, False) 67 flag = PETSc.DMPlex.getAdjacencyUseAnchors(self.plex) 68 self.assertFalse(flag) 69 PETSc.DMPlex.setAdjacencyUseAnchors(self.plex, True) 70 flag = PETSc.DMPlex.getAdjacencyUseAnchors(self.plex) 71 self.assertTrue(flag) 72 PETSc.DMPlex.setBasicAdjacency(self.plex, False, False) 73 flagA, flagB = PETSc.DMPlex.getBasicAdjacency(self.plex) 74 self.assertFalse(flagA) 75 self.assertFalse(flagB) 76 PETSc.DMPlex.setBasicAdjacency(self.plex, True, True) 77 flagA, flagB = PETSc.DMPlex.getBasicAdjacency(self.plex) 78 self.assertTrue(flagA) 79 self.assertTrue(flagB) 80 pStart, pEnd = self.plex.getChart() 81 for p in range(pStart, pEnd): 82 adjacency = self.plex.getAdjacency(p) 83 self.assertTrue(p in adjacency) 84 self.assertTrue(len(adjacency) > 1) 85 86 def testSectionDofs(self): 87 self.plex.setNumFields(1) 88 section = self.plex.createSection([self.COMP], [self.DOFS]) 89 size = section.getStorageSize() 90 entity_dofs = [ 91 self.plex.getStratumSize('depth', d) * self.DOFS[d] 92 for d in range(self.DIM + 1) 93 ] 94 self.assertEqual(sum(entity_dofs), size) 95 96 def testSectionClosure(self): 97 section = self.plex.createSection([self.COMP], [self.DOFS]) 98 self.plex.setSection(section) 99 vec = self.plex.createLocalVec() 100 pStart, pEnd = self.plex.getChart() 101 for p in range(pStart, pEnd): 102 for i in range(section.getDof(p)): 103 off = section.getOffset(p) 104 vec.setValue(off + i, p) 105 106 for p in range(pStart, pEnd): 107 point_closure = self.plex.getTransitiveClosure(p)[0] 108 dof_closure = self.plex.vecGetClosure(section, vec, p) 109 for p in dof_closure: 110 self.assertIn(p, point_closure) 111 112 def testBoundaryLabel(self): 113 pStart, pEnd = self.plex.getChart() 114 if pEnd - pStart == 0: 115 return 116 117 self.assertFalse(self.plex.hasLabel('boundary')) 118 self.plex.markBoundaryFaces('boundary') 119 self.assertTrue(self.plex.hasLabel('boundary')) 120 121 faces = self.plex.getStratumIS('boundary', 1) 122 for f in faces.getIndices(): 123 points, orient = self.plex.getTransitiveClosure(f, useCone=True) 124 for p in points: 125 self.plex.setLabelValue('boundary', p, 1) 126 127 for p in range(pStart, pEnd): 128 if self.plex.getLabelValue('boundary', p) != 1: 129 self.plex.setLabelValue('boundary', p, 2) 130 131 numBoundary = self.plex.getStratumSize('boundary', 1) 132 numInterior = self.plex.getStratumSize('boundary', 2) 133 self.assertNotEqual(numBoundary, pEnd - pStart) 134 self.assertNotEqual(numInterior, pEnd - pStart) 135 self.assertEqual(numBoundary + numInterior, pEnd - pStart) 136 137 def testMetric(self): 138 if self.DIM == 1: 139 return 140 self.plex.distribute() 141 if self.CELLS is None and not self.plex.isSimplex(): 142 return 143 self.plex.orient() 144 145 h_min = 1.0e-30 146 h_max = 1.0e30 147 a_max = 1.0e10 148 target = 8.0 149 p = 1.0 150 beta = 1.3 151 hausd = 0.01 152 self.plex.metricSetUniform(False) 153 self.plex.metricSetIsotropic(False) 154 self.plex.metricSetRestrictAnisotropyFirst(False) 155 self.plex.metricSetNoInsertion(False) 156 self.plex.metricSetNoSwapping(False) 157 self.plex.metricSetNoMovement(False) 158 self.plex.metricSetNoSurf(False) 159 self.plex.metricSetVerbosity(-1) 160 self.plex.metricSetNumIterations(3) 161 self.plex.metricSetMinimumMagnitude(h_min) 162 self.plex.metricSetMaximumMagnitude(h_max) 163 self.plex.metricSetMaximumAnisotropy(a_max) 164 self.plex.metricSetTargetComplexity(target) 165 self.plex.metricSetNormalizationOrder(p) 166 self.plex.metricSetGradationFactor(beta) 167 self.plex.metricSetHausdorffNumber(hausd) 168 169 self.assertFalse(self.plex.metricIsUniform()) 170 self.assertFalse(self.plex.metricIsIsotropic()) 171 self.assertFalse(self.plex.metricRestrictAnisotropyFirst()) 172 self.assertFalse(self.plex.metricNoInsertion()) 173 self.assertFalse(self.plex.metricNoSwapping()) 174 self.assertFalse(self.plex.metricNoMovement()) 175 self.assertFalse(self.plex.metricNoSurf()) 176 self.assertTrue(self.plex.metricGetVerbosity() == -1) 177 self.assertTrue(self.plex.metricGetNumIterations() == 3) 178 self.assertTrue(np.isclose(self.plex.metricGetMinimumMagnitude(), h_min)) 179 self.assertTrue(np.isclose(self.plex.metricGetMaximumMagnitude(), h_max)) 180 self.assertTrue(np.isclose(self.plex.metricGetMaximumAnisotropy(), a_max)) 181 self.assertTrue(np.isclose(self.plex.metricGetTargetComplexity(), target)) 182 self.assertTrue(np.isclose(self.plex.metricGetNormalizationOrder(), p)) 183 self.assertTrue(np.isclose(self.plex.metricGetGradationFactor(), beta)) 184 self.assertTrue(np.isclose(self.plex.metricGetHausdorffNumber(), hausd)) 185 186 metric1 = self.plex.metricCreateUniform(0.5) 187 metric2 = self.plex.metricCreateUniform(1.0) 188 metric = self.plex.metricCreate() 189 det = self.plex.metricDeterminantCreate() 190 self.plex.metricAverage2(metric1, metric2, metric) 191 metric1.array[:] *= 1.5 192 self.assertTrue(np.allclose(metric.array, metric1.array)) 193 self.plex.metricIntersection2(metric1, metric2, metric) 194 self.assertTrue(np.allclose(metric.array, metric2.array)) 195 self.plex.metricEnforceSPD(metric, metric1, det[0]) 196 self.assertTrue(np.allclose(metric.array, metric1.array)) 197 198 if self.DIM == 2 and PETSc.COMM_WORLD.getSize() > 6: 199 # Error with 7 processes in 2D: normalization factor is -1 200 return 201 202 self.plex.metricNormalize( 203 metric, metric1, det[0], restrictSizes=False, restrictAnisotropy=False 204 ) 205 metric2.scale(pow(target, 2.0 / self.DIM)) 206 self.assertTrue(np.allclose(metric1.array, metric2.array)) 207 208 def testAdapt(self): 209 if self.DIM == 1: 210 return 211 if self.DIM == 3 and PETSc.COMM_WORLD.getSize() > 4: 212 # Error with 5 processes in 3D 213 # ---------------------------- 214 # Warning: MMG5_mmgIntextmet: Unable to diagonalize at least 1 metric. 215 # Error: MMG3D_defsiz_ani: unable to intersect metrics at point 8. 216 # Metric undefined. Exit program. 217 # MMG remeshing problem. Exit program. 218 return 219 self.plex.orient() 220 plex = self.plex.refine() 221 plex.distribute() 222 if self.CELLS is None and not plex.isSimplex(): 223 return 224 if sum(self.DOFS) > 1: 225 return 226 metric = plex.metricCreateUniform(9.0) 227 try: 228 newplex = plex.adaptMetric(metric, '') 229 plex.destroy() 230 newplex.destroy() 231 except PETSc.Error as exc: 232 plex.destroy() 233 if exc.ierr != ERR_ARG_OUTOFRANGE: 234 raise 235 236 237# -------------------------------------------------------------------- 238 239 240class BaseTestPlex_2D(BaseTestPlex): 241 DIM = 2 242 CELLS = [ 243 [0, 1, 3], 244 [1, 3, 4], 245 [1, 2, 4], 246 [2, 4, 5], 247 [3, 4, 6], 248 [4, 6, 7], 249 [4, 5, 7], 250 [5, 7, 8], 251 ] 252 COORDS = [ 253 [0.0, 0.0], 254 [0.5, 0.0], 255 [1.0, 0.0], 256 [0.0, 0.5], 257 [0.5, 0.5], 258 [1.0, 0.5], 259 [0.0, 1.0], 260 [0.5, 1.0], 261 [1.0, 1.0], 262 ] 263 DOFS = [1, 0, 0] 264 265 266class BaseTestPlex_3D(BaseTestPlex): 267 DIM = 3 268 CELLS = [ 269 [0, 2, 3, 7], 270 [0, 2, 6, 7], 271 [0, 4, 6, 7], 272 [0, 1, 3, 7], 273 [0, 1, 5, 7], 274 [0, 4, 5, 7], 275 ] 276 COORDS = [ 277 [0.0, 0.0, 0.0], 278 [1.0, 0.0, 0.0], 279 [0.0, 1.0, 0.0], 280 [1.0, 1.0, 0.0], 281 [0.0, 0.0, 1.0], 282 [1.0, 0.0, 1.0], 283 [0.0, 1.0, 1.0], 284 [1.0, 1.0, 1.0], 285 ] 286 DOFS = [1, 0, 0, 0] 287 288 289# -------------------------------------------------------------------- 290 291 292class TestPlex_1D(BaseTestPlex, unittest.TestCase): 293 pass 294 295 296class TestPlex_2D(BaseTestPlex_2D, unittest.TestCase): 297 def testTransform(self): 298 plex = self.plex 299 cstart, cend = plex.getHeightStratum(0) 300 tr = PETSc.DMPlexTransform().create(comm=PETSc.COMM_WORLD) 301 tr.setType(PETSc.DMPlexTransformType.REFINEALFELD) 302 tr.setDM(plex) 303 tr.setUp() 304 newplex = tr.apply(plex) 305 tr.destroy() 306 newcstart, newcend = newplex.getHeightStratum(0) 307 newplex.destroy() 308 self.assertTrue((newcend - newcstart) == 3 * (cend - cstart)) 309 310 311class TestPlex_3D(BaseTestPlex_3D, unittest.TestCase): 312 pass 313 314 315class TestPlex_2D_P3(BaseTestPlex_2D, unittest.TestCase): 316 DOFS = [1, 2, 1] 317 318 319class TestPlex_3D_P3(BaseTestPlex_3D, unittest.TestCase): 320 DOFS = [1, 2, 1, 0] 321 322 323class TestPlex_3D_P4(BaseTestPlex_3D, unittest.TestCase): 324 DOFS = [1, 3, 3, 1] 325 326 327class TestPlex_2D_BoxTensor(BaseTestPlex_2D, unittest.TestCase): 328 CELLS = None 329 COORDS = None 330 331 def setUp(self): 332 self.plex = PETSc.DMPlex().createBoxMesh([3, 3], simplex=False) 333 334 335class TestPlex_3D_BoxTensor(BaseTestPlex_3D, unittest.TestCase): 336 CELLS = None 337 COORDS = None 338 339 def setUp(self): 340 self.plex = PETSc.DMPlex().createBoxMesh([3, 3, 3], simplex=False) 341 342 343# FIXME 344try: 345 raise PETSc.Error 346 PETSc.DMPlex().createBoxMesh([2, 2], simplex=True, comm=PETSc.COMM_SELF).destroy() 347except PETSc.Error: 348 pass 349else: 350 351 class TestPlex_2D_Box(BaseTestPlex_2D, unittest.TestCase): 352 CELLS = None 353 COORDS = None 354 355 def setUp(self): 356 self.plex = PETSc.DMPlex().createBoxMesh([1, 1], simplex=True) 357 358 class TestPlex_2D_Boundary(BaseTestPlex_2D, unittest.TestCase): 359 CELLS = None 360 COORDS = None 361 362 def setUp(self): 363 boundary = PETSc.DMPlex().create(self.COMM) 364 boundary.createSquareBoundary([0.0, 0.0], [1.0, 1.0], [2, 2]) 365 boundary.setDimension(self.DIM - 1) 366 self.plex = PETSc.DMPlex().generate(boundary) 367 368 class TestPlex_3D_Box(BaseTestPlex_3D, unittest.TestCase): 369 CELLS = None 370 COORDS = None 371 372 def setUp(self): 373 self.plex = PETSc.DMPlex().createBoxMesh([1, 1, 1], simplex=True) 374 375 class TestPlex_3D_Boundary(BaseTestPlex_3D, unittest.TestCase): 376 CELLS = None 377 COORDS = None 378 379 def setUp(self): 380 boundary = PETSc.DMPlex().create(self.COMM) 381 boundary.createCubeBoundary([0.0, 0.0, 0.0], [1.0, 1.0, 1.0], [1, 1, 1]) 382 boundary.setDimension(self.DIM - 1) 383 self.plex = PETSc.DMPlex().generate(boundary) 384 385# -------------------------------------------------------------------- 386 387PETSC_DIR = petsc4py.get_config()['PETSC_DIR'] 388 389 390def check_dtype(method): 391 def wrapper(self, *args, **kwargs): 392 if PETSc.ScalarType is PETSc.ComplexType: 393 return None 394 return method(self, *args, **kwargs) 395 396 return wrapper 397 398 399def check_package(method): 400 def wrapper(self, *args, **kwargs): 401 if not PETSc.Sys.hasExternalPackage('hdf5'): 402 return None 403 if self.PARTITIONERTYPE != 'simple' and not PETSc.Sys.hasExternalPackage( 404 self.PARTITIONERTYPE 405 ): 406 return None 407 return method(self, *args, **kwargs) 408 409 return wrapper 410 411 412def check_nsize(method): 413 def wrapper(self, *args, **kwargs): 414 if PETSc.COMM_WORLD.size != self.NSIZE: 415 return None 416 return method(self, *args, **kwargs) 417 418 return wrapper 419 420 421class BaseTestPlexHDF5: 422 NSIZE = 4 423 NTIMES = 3 424 425 def tearDown(self): 426 if not PETSc.COMM_WORLD.rank: 427 if os.path.exists(self.outfile()): 428 os.remove(self.outfile()) 429 if os.path.exists(self.tmp_output_file()): 430 os.remove(self.tmp_output_file()) 431 432 def _name(self): 433 return f'{self.SUFFIX}_outformat-{self.OUTFORMAT}_{self.PARTITIONERTYPE}' 434 435 def infile(self): 436 return os.path.join( 437 PETSC_DIR, 'share/petsc/datafiles/', 'meshes/blockcylinder-50.h5' 438 ) 439 440 def outfile(self): 441 return os.path.join('./temp_test_dmplex_%s.h5' % self._name()) 442 443 def informat(self): 444 return PETSc.Viewer.Format.HDF5_XDMF 445 446 def outformat(self): 447 d = { 448 'hdf5_petsc': PETSc.Viewer.Format.HDF5_PETSC, 449 'hdf5_xdmf': PETSc.Viewer.Format.HDF5_XDMF, 450 } 451 return d[self.OUTFORMAT] 452 453 def partitionerType(self): 454 d = { 455 'simple': PETSc.Partitioner.Type.SIMPLE, 456 'ptscotch': PETSc.Partitioner.Type.PTSCOTCH, 457 'parmetis': PETSc.Partitioner.Type.PARMETIS, 458 } 459 return d[self.PARTITIONERTYPE] 460 461 def ref_output_file(self): 462 return os.path.join( 463 PETSC_DIR, 464 'src/dm/impls/plex/tutorials/', 465 'output/ex5_%s.out' % self._name(), 466 ) 467 468 def tmp_output_file(self): 469 return os.path.join('./temp_test_dmplex_%s.out' % self._name()) 470 471 def outputText(self, msg, comm): 472 if not comm.rank: 473 with open(self.tmp_output_file(), 'a') as f: 474 f.write(msg) 475 476 def outputPlex(self, plex): 477 txtvwr = PETSc.Viewer().createASCII( 478 self.tmp_output_file(), mode='a', comm=plex.comm 479 ) 480 plex.view(viewer=txtvwr) 481 txtvwr.destroy() 482 483 @check_dtype 484 @check_package 485 @check_nsize 486 def testViewLoadCycle(self): 487 if importlib.util.find_spec('mpi4py') is None: 488 self.skipTest('mpi4py') # throws special exception to signal test skip 489 grank = PETSc.COMM_WORLD.rank 490 for i in range(self.NTIMES): 491 if i == 0: 492 infname = self.infile() 493 informt = self.informat() 494 else: 495 infname = self.outfile() 496 informt = self.outformat() 497 if self.HETEROGENEOUS: 498 mycolor = grank > self.NTIMES - i 499 else: 500 mycolor = 0 501 mpicomm = PETSc.COMM_WORLD.tompi4py() 502 comm = PETSc.Comm(comm=mpicomm.Split(color=mycolor, key=grank)) 503 if mycolor == 0: 504 self.outputText('Begin cycle %d\n' % i, comm) 505 plex = PETSc.DMPlex() 506 vwr = PETSc.ViewerHDF5() 507 # Create plex 508 plex.create(comm=comm) 509 plex.setName('DMPlex Object') 510 # Load data from XDMF into dm in parallel 511 vwr.create(infname, mode='r', comm=comm) 512 vwr.pushFormat(format=informt) 513 plex.load(viewer=vwr) 514 plex.setOptionsPrefix('loaded_') 515 plex.distributeSetDefault(False) 516 plex.setFromOptions() 517 vwr.popFormat() 518 vwr.destroy() 519 self.outputPlex(plex) 520 # Test DM is indeed distributed 521 flg = plex.isDistributed() 522 self.outputText( 523 'Loaded mesh distributed? %s\n' % str(flg).upper(), comm 524 ) 525 # Interpolate 526 plex.interpolate() 527 plex.setOptionsPrefix('interpolated_') 528 plex.setFromOptions() 529 self.outputPlex(plex) 530 # Redistribute 531 part = plex.getPartitioner() 532 part.setType(self.partitionerType()) 533 sf = plex.distribute(overlap=0) 534 if sf: 535 sf.destroy() 536 part.destroy() 537 plex.setName('DMPlex Object') 538 plex.setOptionsPrefix('redistributed_') 539 plex.setFromOptions() 540 self.outputPlex(plex) 541 # Save redistributed dm to XDMF in parallel 542 vwr.create(self.outfile(), mode='w', comm=comm) 543 vwr.pushFormat(format=self.outformat()) 544 plex.setName('DMPlex Object') 545 plex.view(viewer=vwr) 546 vwr.popFormat() 547 vwr.destroy() 548 # Destroy plex 549 plex.destroy() 550 self.outputText('End cycle %d\n--------\n' % i, comm) 551 comm.tompi4py().Free() 552 PETSc.COMM_WORLD.Barrier() 553 # Check that the output is identical to that of plex/tutorial/ex5.c. 554 self.assertTrue( 555 filecmp.cmp(self.tmp_output_file(), self.ref_output_file(), shallow=False), 556 f'Contents of the files not the same. Reference file: {self.ref_output_file()}', 557 ) 558 PETSc.COMM_WORLD.Barrier() 559 560 561class BaseTestPlexHDF5Homogeneous(BaseTestPlexHDF5): 562 """Test save on N / load on N.""" 563 564 SUFFIX = 0 565 HETEROGENEOUS = False 566 567 568class BaseTestPlexHDF5Heterogeneous(BaseTestPlexHDF5): 569 """Test save on N / load on M.""" 570 571 SUFFIX = 1 572 HETEROGENEOUS = True 573 574 575class TestPlexHDF5PETSCSimpleHomogeneous( 576 BaseTestPlexHDF5Homogeneous, unittest.TestCase 577): 578 OUTFORMAT = 'hdf5_petsc' 579 PARTITIONERTYPE = 'simple' 580 581 582""" 583Skipping. PTScotch produces different distributions when run 584in a sequence in a single session. 585 586class TestPlexHDF5PETSCPTScotchHomogeneous(BaseTestPlexHDF5Homogeneous, 587 unittest.TestCase): 588 OUTFORMAT = "hdf5_petsc" 589 PARTITIONERTYPE = "ptscotch" 590""" 591 592 593class TestPlexHDF5PETSCParmetisHomogeneous( 594 BaseTestPlexHDF5Homogeneous, unittest.TestCase 595): 596 OUTFORMAT = 'hdf5_petsc' 597 PARTITIONERTYPE = 'parmetis' 598 599 600class TestPlexHDF5XDMFSimpleHomogeneous(BaseTestPlexHDF5Homogeneous, unittest.TestCase): 601 OUTFORMAT = 'hdf5_xdmf' 602 PARTITIONERTYPE = 'simple' 603 604 605""" 606Skipping. PTScotch produces different distributions when run 607in a sequence in a single session. 608 609class TestPlexHDF5XDMFPTScotchHomogeneous(BaseTestPlexHDF5Homogeneous, 610 unittest.TestCase): 611 OUTFORMAT = "hdf5_xdmf" 612 PARTITIONERTYPE = "ptscotch" 613""" 614 615 616class TestPlexHDF5XDMFParmetisHomogeneous( 617 BaseTestPlexHDF5Homogeneous, unittest.TestCase 618): 619 OUTFORMAT = 'hdf5_xdmf' 620 PARTITIONERTYPE = 'parmetis' 621 622 623class TestPlexHDF5PETSCSimpleHeterogeneous( 624 BaseTestPlexHDF5Heterogeneous, unittest.TestCase 625): 626 OUTFORMAT = 'hdf5_petsc' 627 PARTITIONERTYPE = 'simple' 628 629 630""" 631Skipping. PTScotch produces different distributions when run 632in a sequence in a single session. 633 634class TestPlexHDF5PETSCPTScotchHeterogeneous(BaseTestPlexHDF5Heterogeneous, 635 unittest.TestCase): 636 OUTFORMAT = "hdf5_petsc" 637 PARTITIONERTYPE = "ptscotch" 638""" 639 640 641class TestPlexHDF5PETSCParmetisHeterogeneous( 642 BaseTestPlexHDF5Heterogeneous, unittest.TestCase 643): 644 OUTFORMAT = 'hdf5_petsc' 645 PARTITIONERTYPE = 'parmetis' 646 647 648class TestPlexHDF5XDMFSimpleHeterogeneous( 649 BaseTestPlexHDF5Heterogeneous, unittest.TestCase 650): 651 OUTFORMAT = 'hdf5_xdmf' 652 PARTITIONERTYPE = 'simple' 653 654 655class TestPlexHDF5XDMFPTScotchHeterogeneous( 656 BaseTestPlexHDF5Heterogeneous, unittest.TestCase 657): 658 OUTFORMAT = 'hdf5_xdmf' 659 PARTITIONERTYPE = 'ptscotch' 660 661 662class TestPlexHDF5XDMFParmetisHeterogeneous( 663 BaseTestPlexHDF5Heterogeneous, unittest.TestCase 664): 665 OUTFORMAT = 'hdf5_xdmf' 666 PARTITIONERTYPE = 'parmetis' 667 668 669# -------------------------------------------------------------------- 670 671if __name__ == '__main__': 672 unittest.main() 673