2 # -*- coding: utf-8 -*-
4 # Copyright (C) 2008-2022 CEA/DEN, EDF R&D
6 # This library is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU Lesser General Public
8 # License as published by the Free Software Foundation; either
9 # version 2.1 of the License, or (at your option) any later version.
11 # This library is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # Lesser General Public License for more details.
16 # You should have received a copy of the GNU Lesser General Public
17 # License along with this library; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
24 run coherency tests on one and some mesh.
25 initially used for test output(s) mg_tetra_hpc_mpi.exe
27 WARNING: is for small meshes, obviously no optimisation.
28 WARNING: printing and .mesh indices are 1 to n when stored list python 0 to n-1
32 ./testMesh.py --verbose --testall --files ./GHS3DPRL_out.000001.mesh ./GHS3DPRL_out.000002.mesh
33 ./testMesh.py -a -f /tmp/GHS3DPRL_out.00000?.mesh
40 import pprint as PP # pretty print
51 #########################################
54 def okToSys(aResult, verbose=False):
55 """to get windows or linux result of script"""
57 def extendList(alist):
58 """utility extend list of lists of string results with ok or KO"""
59 # bad: list(itertools.chain.from_list(alist)) iterate on str
61 if type(alist) != list:
68 res.extend(extendList(i))
71 resList = extendList(aResult)
73 if verbose: print("WARNING: result no clear: []")
82 if verbose: print("INFO: result: %s" % rr)
88 def getDirAndName(datafile):
89 path, namefile = os.path.split(os.path.realpath(datafile))
90 rootpath = os.getcwd()
91 return (path, rootpath, namefile)
93 def resumeList(aList):
98 res = [aList[0], "...", aList[-1]]
101 def resumeLines(aList, ilines):
102 if verbose: print("INFO: resumeLines", ilines)
109 if i != None: # if not existing tetrahedra for example
110 resi = [ii.strip("\n") for ii in aList[i:i + 4]]
116 #########################################
118 """Vertices, Nodes"""
119 def __init__(self, x, y, z, color=0, indexglobal=0):
124 self.indexglobal = indexglobal
126 def compare(self, vb, args, withAll=True):
127 if self.x != vb.x: return False
128 if self.y != vb.y: return False
129 if self.z != vb.z: return False
132 if self.color != vb.color: return False
134 if self.indexglobal != vb.indexglobal: return False
137 def __eq__(self, vb):
138 """equality test without color or indexglobal"""
139 # print "vertice equality"
140 if self.x != vb.x: return False
141 if self.y != vb.y: return False
142 if self.z != vb.z: return False
145 def __ne__(self, vb):
146 """inequality test without color or indexglobal"""
147 # print "vertice inequality"
148 if self.x == vb.x and self.y == vb.y and self.z != vb.z:
153 return "XXVert(%.4f %.4f %.4f (%i %i))" % \
154 (self.x, self.y, self.z, self.color, self.indexglobal)
157 return "(%s %s %s (%i %i))" % \
158 (self.x, self.y, self.z, self.color, self.indexglobal)
161 res = (self.x - vb.x) ** 2 + (self.y - vb.y) ** 2 + (self.z - vb.z) ** 2
165 #########################################
168 def __init__(self, a, b, color=0, indexglobal=0):
172 self.indexglobal = indexglobal
174 def compare(self, eb, args):
175 res = self.a.compare(eb.a, args) and \
176 self.b.compare(eb.b, args)
179 if self.color != eb.color: return False
181 if self.indexglobal != eb.indexglobal: return False
185 return "XXEdge(%i %i (%i %i))" % \
186 (self.a, self.b, self.color, self.indexglobal)
189 return "(%i %i (%i %i))" % \
190 (self.a, self.b, self.color, self.indexglobal)
192 def inTria(self, tria, args):
193 t = [tria.a, tria.b, tria.c]
194 if not self.a in t: return False
195 if not self.b in t: return False
198 def getVertices(self, mesh):
199 v1 = mesh.verts[self.a - 1]
200 v2 = mesh.verts[self.b - 1]
204 #########################################
206 """Triangles, Faces, 3 nodes"""
207 def __init__(self, a, b, c, color=0, indexglobal=0):
212 self.indexglobal = indexglobal
214 def compare(self, trb, args):
215 res = self.a.compare(trb.a, args) and \
216 self.b.compare(trb.b, args) and \
217 self.c.compare(trb.c, args)
220 if self.color != trb.color: return False
222 if self.indexglobal != trb.indexglobal: return False
226 return "XXTria(%i %i %i (%i %i))" % \
227 (self.a, self.b, self.c, self.color, self.indexglobal)
230 return "(%i %i %i (%i %i))" % \
231 (self.a, self.b, self.c, self.color, self.indexglobal)
233 def inTetra(self, tetra, args):
234 t = [tetra.a, tetra.b, tetra.c, tetra.d]
235 if not self.a in t: return False
236 if not self.b in t: return False
237 if not self.c in t: return False
240 def getVertices(self, mesh):
241 v1 = mesh.verts[self.a - 1]
242 v2 = mesh.verts[self.b - 1]
243 v3 = mesh.verts[self.c - 1]
247 #########################################
250 def __init__(self, a, b, c, d, color=0, indexglobal=0):
256 self.indexglobal = indexglobal
258 def compare(self, teb, args):
259 res = self.a.compare(teb.a, args) and \
260 self.b.compare(teb.b, args) and \
261 self.c.compare(teb.c, args) and \
262 self.d.compare(teb.d, args)
265 if self.color != teb.color: return False
267 if self.indexglobal != teb.indexglobal: return False
271 return "XXTetra(%i %i %i %i (%i %i))" % \
272 (self.a, self.b, self.c, self.d, self.color, self.indexglobal)
275 return "(%i %i %i %i (%i %i))" % \
276 (self.a, self.b, self.c, self.d, self.color, self.indexglobal)
278 def getVertices(self, mesh):
279 v1 = mesh.verts[self.a - 1]
280 v2 = mesh.verts[self.b - 1]
281 v3 = mesh.verts[self.c - 1]
282 v4 = mesh.verts[self.d - 1]
283 return [v1, v2, v3, v4]
286 #########################################
288 """Mesh: vertices, edges, triangles, tetrahedra"""
296 def initFromFileMesh(self, fileName, args, withGlobal=True):
297 if not os.path.isfile(fileName):
298 raise Exception("ERROR: inexisting file '%s'" % fileName)
299 with open(fileName, "r") as f:
300 lines = f.readlines()
301 iverts, iedges, itrias, itetras = self.getIndexInMeshFile(lines)
302 self.verts = self.getMeshVerts(lines, iverts)
303 self.edges = self.getMeshEdges(lines, iedges)
304 self.trias = self.getMeshTrias(lines, itrias)
305 self.tetras = self.getMeshTetras(lines, itetras)
306 self.nameFile = fileName
307 if args.globalNumerotation == True and withGlobal == True:
308 self.initFromFileGlobal(fileName, args)
310 print("\nINFO: initFromFileMesh: read file: %s" % str(self))
311 print(self.strResume())
312 print(PP.pformat(resumeLines(lines, [iverts, iedges, itrias, itetras])))
314 def initFromFileGlobal(self, fileNameMeshOrGlobal, args):
315 shortname, extension = os.path.splitext(fileNameMeshOrGlobal)
316 if extension == ".mesh":
317 fileName = shortname + ".global"
318 elif extension == "global":
319 fileName = fileNameMeshOrGlobal
321 raise Exception("ERROR: initFromFileGlobal: unexpected file '%s'" % fileName)
322 if not os.path.isfile(fileName):
323 raise Exception("ERROR: initFromFileGlobal: inexisting file '%s'" % fileName)
325 with open(fileName, "r") as f:
326 lines = f.readlines()
327 nbverts, nbedges, nbtrias, nbtetras = [int(i) for i in lines[0].split()]
329 print("\nINFO: initFromFileGlobal: read file: %s" % str(self))
330 print(" nbverts %i\n nbedges %i\n nbtrias %i\n nbtetras %i" % (nbverts, nbedges, nbtrias, nbtetras))
331 if nbverts != len(self.verts):
332 raise Exception("ERROR: in file '%s' unexpected number of Vertices %i<>%i" % (fileName, nbverts, len(self.verts)))
333 if nbedges != len(self.edges):
334 raise Exception("ERROR: in file '%s' unexpected number of Edges %i<>%i" % (fileName, nbedges, len(self.edges)))
335 if nbtrias != len(self.trias):
336 raise Exception("ERROR: in file '%s' unexpected number of Triangles %i<>%i" % (fileName, nbtrias, len(self.trias)))
337 if nbtetras != len(self.tetras):
338 raise Exception("ERROR: in file '%s' unexpected number of Tetrahedra %i<>%i" % (fileName, nbtetras, len(self.tetras)))
339 i = 1 # begin index line 1
340 for ii in range(nbverts):
341 self.verts[ii].indexglobal = int(lines[i])
343 for ii in range(nbedges):
344 self.edges[ii].indexglobal = int(lines[i])
346 for ii in range(nbtrias):
347 self.trias[ii].indexglobal = int(lines[i])
349 for ii in range(nbtetras):
350 self.tetras[ii].indexglobal = int(lines[i])
355 return "XXMesh(nameFile='%s', nbverts=%i, nbedges=%i, nbtrias=%i, nbtetras=%i)" % \
356 (self.nameFile, len(self.verts), len(self.edges), len(self.trias), len(self.tetras))
361 "Vertices": resumeList(self.verts),
362 "Edges": resumeList(self.edges),
363 "Triangles": resumeList(self.trias),
364 "Tetrahedra": resumeList(self.tetras),
366 res = res + "\n" + PP.pformat(contents)
369 def getIndexInMeshFile(self, lines):
371 for s in ["Vertices", "Edges", "Triangles", "Tetrahedra"]:
373 i = lines.index(s + "\n")
379 def getMeshVerts(self, lines, i):
383 ilen = int(lines[i + 1])
385 for line in lines[idep:ifin]:
387 x, y, z, color = float(li[0]), float(li[1]), float(li[2]), int(li[3])
388 res.append(XXVert(x, y, z, color))
393 def getMeshEdges(self, lines, i):
397 ilen = int(lines[i + 1])
399 for line in lines[idep:ifin]:
401 a, b, color = int(li[0]), int(li[1]), int(li[2])
402 res.append(XXEdge(a, b, color))
407 def getMeshTrias(self, lines, i):
411 ilen = int(lines[i + 1])
413 for line in lines[idep:ifin]:
415 a, b, c, color = int(li[0]), int(li[1]), int(li[2]), int(li[3])
416 res.append(XXTria(a, b, c, color))
421 def getMeshTetras(self, lines, i):
425 ilen = int(lines[i + 1])
427 for line in lines[idep:ifin]:
429 a, b, c, d, color = int(li[0]), int(li[1]), int(li[2]), int(li[3]), int(li[4])
430 res.append(XXTetra(a, b, c, d, color))
435 def haveVertsDistinct(self, args):
436 """stop a first KO"""
439 for v1 in verts[:-1]:
444 if v1.compare(v2, args):
445 # printing indices 1 to n
446 print("ERROR: %s vert[%i] equal vert[%i]: v1=%s v2=%s" % (self.nameFile, i, j, v1, v2))
447 return KO + " ERROR: %s some equal vertices" % self.nameFile # stop a first KO
448 return OK + " INFO: no equal vertices"
450 def getVertices(self, elem):
451 """functionnal raccourci to XXElem.getVertices(XXMesh)"""
452 return elem.getVertices(self)
454 def compareListOfVertices(self, v1s, v2s, ordered=False):
455 """not ordered for now"""
457 res = [i for i, j in zip(v1s, v2s) if i == j]
458 return len(res) == len(v1s)
466 return res == len(v1s)
469 def getCommonVerts(self, mesh, args):
471 for v1 in self.verts:
472 for v2 in mesh.verts:
473 if v1.compare(v2, args, withAll=False):
477 def getCommonEdges(self, mesh, args):
479 for e1 in self.edges:
480 v1s = self.getVertices(e1)
481 for e2 in mesh.edges:
482 v2s = mesh.getVertices(e2)
483 if self.compareListOfVertices(v1s, v2s):
487 def getCommonTriangles(self, mesh, args):
489 for e1 in self.trias:
490 v1s = self.getVertices(e1)
491 for e2 in mesh.trias:
492 v2s = mesh.getVertices(e2)
493 if self.compareListOfVertices(v1s, v2s):
497 def getCommonTetras(self, mesh, args):
499 for e1 in self.tetras:
500 v1s = self.getVertices(e1)
501 for e2 in mesh.tetras:
502 v2s = mesh.getVertices(e2)
503 if self.compareListOfVertices(v1s, v2s):
507 def areEdgesInTrias(self, args):
508 """stop a first KO"""
513 res = OK + " INFO: %s all edges in trias" % self.nameFile
520 if e.inTria(t, args):
521 # if verbose: print("INFO: %s edges[%i] in trias[%i]: edge=%s tria=%s" % (self.nameFile, i, j, e, t))
525 print("ERROR: %s edges[%i] not in trias: edge=%s" % (self.nameFile, i, e))
526 if verbose and not done:
527 print("Triangles:\n%s" % PP.pformat(self.trias))
529 res = KO + " ERROR: %s some edges not in trias" % (self.nameFile)
533 def areTriasInTetras(self, args):
534 """no stop a first KO"""
539 if tetras == []: # supposed skin without tetrahedra
540 res = OK + " WARNING: %s no tetrahedra in mesh" % (self.nameFile)
542 res = OK + " INFO: %s all trias in tetras" % self.nameFile
549 if t.inTetra(h, args):
550 # if verbose: print("INFO: %s trias[%i] in tetras[%i]: tria=%s tetra=%s" % (self.nameFile, i, j, t, h))
554 if verbose: print("ERROR: %s trias[%i] not in tetras: tria=%s" % (self.nameFile, i, t))
555 if verbose and not done:
556 print("INFO: Tetrahedra:\n%s" % PP.pformat(self.tetras))
558 res = KO + " ERROR: %s some trias not in tetras" % (self.nameFile)
561 def testIntersection(self, mesh, args):
562 """intersection coherency between self and mesh"""
564 def storeAndInfoIntersection():
565 """used as macro: avoid duplicate code"""
566 # store info in args to use later...
567 args.intersections[title + name] = commons
569 res.append(OK + " INFO: no %s" % title + name)
571 res.append(OK + " INFO: existing %s" % title + name)
575 name = "%s<->%s" % (self.nameFile, mesh.nameFile)
577 title = "Vertices intersection: "
578 commons = self.getCommonVerts(mesh, args)
579 storeAndInfoIntersection()
581 title = "Edges intersection: "
582 commons = self.getCommonEdges(mesh, args)
583 storeAndInfoIntersection()
585 title = "Triangles intersection: "
586 commons = self.getCommonTriangles(mesh, args)
587 storeAndInfoIntersection()
589 title = "Tetrahedra intersection: "
590 commons = self.getCommonTetras(mesh, args)
591 storeAndInfoIntersection()
595 def testIndexGlobal(self, mesh, args):
596 """global index coherency between self and mesh"""
598 def storeAndInfoIndexGlobal():
599 """used as macro: avoid duplicate code"""
600 # store info in args to use later...
601 args.indexglobal[title + name] = problems
602 if verbose: print("\nINFO: %s\n%s" % (title + name, PP.pformat(problems)))
604 res.append(OK + " INFO: coherent %s" % title + name)
606 res.append(KO + " ERROR: some problems %s" % title + name)
609 def testIndexGlobal():
610 """used as macro: avoid duplicate code"""
611 nameElem = title.split(' ')[0]
612 # something like 'Vertices intersection: /tmp/GHS3DPRL_out.000002.mesh<->/tmp/GHS3DPRL_out.000003.mesh'
613 commonsTitle = nameElem + " intersection: " + name
614 # if verbose: print "testIndexGlobal",title,commonsTitle
616 intersection = args.intersections[commonsTitle]
620 for ii, jj in intersection:
621 if ii.indexglobal != jj.indexglobal:
622 problems.append((ii, jj))
626 name = "%s<->%s" % (self.nameFile, mesh.nameFile)
628 title = "Vertices indexglobal: "
629 problems = testIndexGlobal()
630 storeAndInfoIndexGlobal()
632 title = "Edges indexglobal: "
633 problems = testIndexGlobal()
634 storeAndInfoIndexGlobal()
636 title = "Triangles indexglobal: "
637 problems = testIndexGlobal()
638 storeAndInfoIndexGlobal()
640 title = "Tetrahedra indexglobal: "
641 problems = testIndexGlobal()
642 storeAndInfoIndexGlobal()
647 #########################################
651 """test all on meshes from tetra_hpc_mpi"""
653 if verbose: print("\n*****testAll*****\n")
655 if args.skinInputFile != None:
656 args.skinMesh = XXMesh()
657 # a priori no global numerotation file.global for input tetra_hpc_mpi mesh
658 args.skinMesh.initFromFileMesh(args.skinInputFile, args, withGlobal=False)
659 res.append(testStandaloneMesh(args.skinMesh, args))
660 print("\nINFO: testAll skin input file:\n%s" % (PP.pformat(args.skinMesh)))
663 for fileName in args.files:
665 xxmesh.initFromFileMesh(fileName, args)
666 meshes.append(xxmesh)
667 print("\nINFO: testAll ouput files:\n%s\n" % (PP.pformat(meshes)))
668 # test coherence of one by one meshes
670 res.append(testStandaloneMesh(mesh, args))
671 # test coherence of intersections an global numerotation of tetra_hpc_mpi output meshes
672 res.append(testParallelMesh(meshes, args))
673 res.append(testParallelMeshAndSkin(meshes, args))
674 res.append(testParallelMeshAndSkinColor(meshes, args))
678 def testStandaloneMesh(mesh, args):
679 """test coherence of one mesh alone"""
680 if verbose: print("\nINFO: testStandaloneMesh:\n%s" % PP.pformat(mesh))
682 res.append(mesh.haveVertsDistinct(args))
683 res.append(mesh.areEdgesInTrias(args))
684 res.append(mesh.areTriasInTetras(args))
688 def testParallelMesh(meshes, args):
689 """test intersection and overriding in tetra_hpc_mpi outputs GHS3DPRL_out.00000?.mesh"""
692 args.intersections = {}
693 args.indexglobal = {}
694 for m1 in meshes[:-1]:
696 for m2 in meshes[i:]:
697 res.append(m1.testIntersection(m2, args))
698 res.append(m1.testIndexGlobal(m2, args))
700 print("\nINFO: intersections\n%s" % PP.pformat(args.intersections))
701 print("\nINFO: indexglobal\n%s" % PP.pformat(args.indexglobal))
704 def testParallelMeshAndSkin(meshes, args):
705 """test coherency between input skin and tetra_hpc_mpi outputs GHS3DPRL_out.00000?.mesh"""
707 if args.skinMesh == None:
708 print("INFO: no skin Mesh for testing intersectionsSkin\n")
709 res = OK + "INFO: no skin Mesh for testing intersectionsSkin"
711 nbtriasskin = len(args.skinMesh.trias)
713 res.append(args.skinMesh.testIntersection(m1, args))
714 res.append(args.skinMesh.testIndexGlobal(m1, args))
716 # test total Triangles in output parallel meshes vs input skin mesh
720 for k in list(args.intersections.keys()):
721 if args.skinMesh.nameFile in k:
722 ll = len(args.intersections[k])
723 if "Triangles intersection" in k:
725 kk[k] = len(args.intersections[k])
726 print("INFO: skin intersections\n%s\n" % PP.pformat(kk))
727 if nbtriaspara < nbtriasskin:
728 res.append(KO + " ERROR: problem all skin triangles not in parallel meshes: %i<->%i" % (nbtriasskin, nbtriaspara))
731 def testParallelMeshAndSkinColor(meshes, args):
732 """test coherency between color input skin and tetra_hpc_mpi outputs GHS3DPRL_out.00000?.mesh"""
734 if args.color == True:
735 res.append(KO + " ERROR: test color TODO!!!")
737 res.append(OK + " WARNING: test color not done")
740 if __name__ == '__main__':
741 parser = AP.ArgumentParser(description='launch test(s) on tetra_hpc_mpi mesh(es)', argument_default=None)
744 help='test all on all meshes',
749 help='set verbose, for deep debug',
753 '-g', '--globalNumerotation',
754 help='read and set files .global, if associated',
759 help='read and test with color',
764 help='launch test(s) on file(s)',
766 metavar='.../file.mesh'
769 '-s', '--skinInputFile',
770 help='launch test(s) on tetra_hpc_mpi input file',
772 metavar='.../skinInputFile.mesh'
776 '-x', '--xoneargument',
780 help='one argument, for example',
787 args is Namespace, use it as global to store
788 parameters, data, used arrays and results and other...
790 args = parser.parse_args()
792 verbose = args.verbose
793 if verbose: print("INFO: args:\n%s" % PP.pformat(args))
795 if len(sys.argv) == 1: # no args as --help
799 if args.files == None:
800 print("\nERROR: Nothing to do: no files\n%s" % PP.pformat(args))
805 result = testAll(args)
808 print("\nERROR: Nothing to do:\n%s" % PP.pformat(args))
809 sys.exit(okToSys(result, verbose=True))