Salome HOME
Add test for .mesh file format
[tools/medcoupling.git] / src / MEDLoader / Swig / CaseReader.py
1 #  -*- coding: iso-8859-1 -*-
2 # Copyright (C) 2007-2024  CEA, EDF
3 #
4 # This library is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU Lesser General Public
6 # License as published by the Free Software Foundation; either
7 # version 2.1 of the License, or (at your option) any later version.
8 #
9 # This library is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 # Lesser General Public License for more details.
13 #
14 # You should have received a copy of the GNU Lesser General Public
15 # License along with this library; if not, write to the Free Software
16 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 #
18 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 #
20 # Author : Anthony GEAY (CEA/DEN/DM2S/STMF/LGLS)
21
22 # http://www-vis.lbl.gov/NERSC/Software/ensight/doc/OnlineHelp/UM-C11.pdf
23 import numpy as np
24 from medcoupling import *
25 from CaseIO import CaseIO
26 import sys,re,os
27
28 class CaseReader(CaseIO):
29     """ Converting a file in the Case format (Ensight) to the MED format.
30     A new file with the same base name and the .med extension is created.
31     """
32
33     @classmethod
34     def New(cls,fileName):
35         """ Static constructor. """
36         return CaseReader(fileName)
37         pass
38
39     def __init__(self,fileName):
40         """ Constructor """
41         CaseIO.__init__(self)
42         self._fileName=fileName
43         self._dirName=os.path.dirname(self._fileName)
44         pass
45
46     def __traduceMesh(self,name,typ,coords,cells):
47         """ Convert a CASE mesh into a MEDCouplingUMesh. """
48         name = name.decode("ascii")
49         nbCoords=len(coords)
50         coo=np.array(coords,dtype="float64") ; coo=coo.reshape(nbCoords,3)
51         coo=DataArrayDouble(coo) ; coo=coo.fromNoInterlace()
52         ct=self.dictMCTyp2[typ]
53         m=MEDCouplingUMesh(name,MEDCouplingUMesh.GetDimensionOfGeometricType(ct))
54         m.setCoords(coo)
55         nbNodesPerCell=MEDCouplingMesh.GetNumberOfNodesOfGeometricType(ct)
56         cI=DataArrayInt(len(cells)+1) ; cI.iota() ; cI*=nbNodesPerCell+1
57         #
58         cells2=cells.reshape(len(cells),nbNodesPerCell)
59         c2=DataArrayInt(np.array(cells2,dtype="int{}".format(MEDCouplingSizeOfIDs())) )
60         c=DataArrayInt(len(cells),nbNodesPerCell+1) ; c[:,0]=ct ; c[:,1:]=c2-1 ; c.rearrange(1)
61         m.setConnectivity(c,cI,True)
62         m.checkConsistency()
63         return m
64
65     def __traduceMeshForPolyhed(self,name,coords,arr0,arr1,arr2):
66         nbCoords=len(coords)
67         coo=np.array(coords,dtype="float64") ; coo=coo.reshape(nbCoords,3)
68         coo=DataArrayDouble(coo) ; coo=coo.fromNoInterlace()
69         m=MEDCouplingUMesh(name,3)
70         m.setCoords(coo)
71         #
72         arr2=arr2[:]-1
73         arr0mc0=DataArrayInt(arr0) ; arr0mc0.computeOffsetsFull()
74         arr0mc1=DataArrayInt(arr0).deepCopy()
75         arr0mc2=DataArrayInt(len(arr0),2) ; arr0mc2[:,0]=DataArrayInt(arr0)-1 ; arr0mc2[:,1]=1 ; arr0mc2.rearrange(1) ; arr0mc2.computeOffsetsFull()
76         arr0mc3=DataArrayInt.Range(0,2*len(arr0),2).buildExplicitArrByRanges(arr0mc2)
77         arr1mc0=DataArrayInt(arr1) ; arr1mc0.computeOffsetsFull()
78         arr1mc1=arr1mc0[arr0mc0] ; arr1mc1[1:]+=arr0mc0[1:]
79         arr1mc2=DataArrayInt(arr1).deepCopy() ; arr1mc2+=1 ; arr1mc2.computeOffsetsFull()
80         arr2mc0=(arr1mc2[1:])[arr0mc3]
81         #
82         c=DataArrayInt(arr1.size+arr2.size)
83         c[arr1mc1[:-1]]=NORM_POLYHED
84         c[arr2mc0]=-1
85         a=arr2mc0.buildUnion(arr1mc1[:-1]).buildComplement(len(c))
86         c[a]=DataArrayInt(arr2)
87         #
88         m.setConnectivity(c,arr1mc1,True)
89         m.checkConsistency()
90         return m
91
92     def __traduceMeshForPolygon(self,name,coords,arr0,arr1):
93         nbCoords=len(coords)
94         coo=np.array(coords,dtype="float64") ; coo=coo.reshape(nbCoords,3)
95         coo=DataArrayDouble(coo) ; coo=coo.fromNoInterlace()
96         m=MEDCouplingUMesh(name,2)
97         m.setCoords(coo)
98         #
99         arr0_0=DataArrayInt(arr0+1) ; arr0_0.computeOffsetsFull()
100         arr0_1=DataArrayInt(len(arr0),2) ; arr0_1[:,1]=DataArrayInt(arr0) ; arr0_1[:,0]=1 ; arr0_1.rearrange(1) ; arr0_1.computeOffsetsFull()
101         arr0_2=DataArrayInt.Range(1,2*len(arr0),2).buildExplicitArrByRanges(arr0_1)
102         c=DataArrayInt(len(arr0)+len(arr1)) ; c[:]=0 ; c[arr0_0[:-1]]=NORM_POLYGON
103         c[arr0_2]=DataArrayInt(arr1-1)
104         #
105         m.setConnectivity(c,arr0_0,True)
106         m.checkConsistency()
107         return m
108
109     def __convertGeo2MED(self,geoFileName):
110         """ Convert all the geometry (all the meshes) contained in the CASE file into MEDCouplingUMesh'es. """
111         fd=open(os.path.join(self._dirName,geoFileName),"r+b") ; fd.seek(0,2) ; end=fd.tell() ; fd.seek(0) ; title=fd.read(80)
112         title=title.strip().lower()
113         if b"binary" not in title:
114             raise Exception("Error only binary geo files are supported for the moment !")
115             pass
116         zeType=True
117         if b"fortran" in title:
118             mcmeshes=self.__convertGeo2MEDFortran(fd,end) ; zeType=False
119         else:
120             mcmeshes=self.__convertGeo2MEDC(fd,end)
121         #
122         ms=MEDFileMeshes()
123         ms.resize(len(mcmeshes))
124         for i,m in enumerate(mcmeshes):
125             mlm=MEDFileUMesh()
126             mlm.setMeshAtLevel(0,m)
127             ms.setMeshAtPos(i,mlm)
128             pass
129         return mcmeshes,ms,zeType
130
131     def __convertGeo2MEDFortran(self,fd,end):
132         mcmeshes=[]
133         fd.read(80) # comment 1
134         fd.read(80) # comment 2
135         fd.read(80) # node id
136         fd.read(80) # element id
137         pos=fd.tell()
138         elt=fd.read(80) ; elt=elt.strip() ; pos=fd.tell()
139         mcmeshes2=[]
140         typ=b"part"
141         nbOfTurn=0
142         while abs(pos-end)>8 and b"part" in typ:
143             if b"part" not in elt:
144                 raise Exception("Error on reading mesh fortran #1 !")
145             fd.seek(fd.tell()+4)# skip #
146             tmp=fd.read(80) ; meshName=tmp.split("P")[-1]
147             tmp=fd.read(80)
148             if b"coordinates" not in tmp:
149                 raise Exception("Error on reading mesh fortran #2 !")
150             pos=fd.tell() # 644
151             if nbOfTurn==0:
152                 pos+=76 # what else ?
153             else:
154                 pos+=40
155                 pass
156             nbNodes=np.memmap(fd,dtype='>i4',mode='r',offset=int(pos),shape=(1,)).tolist()[0]
157             pos+=12 # what else ?
158             a=np.memmap(fd,dtype='>f4',mode='r',offset=int(pos),shape=(nbNodes))
159             b=np.memmap(fd,dtype='>f4',mode='r',offset=int(pos+nbNodes*4+2*4),shape=(nbNodes))
160             c=np.memmap(fd,dtype='>f4',mode='r',offset=int(pos+nbNodes*2*4+4*4),shape=(nbNodes))
161             coo=np.zeros(dtype=">f4",shape=(nbNodes*3))
162             coo[:nbNodes]=a ; coo[nbNodes:2*nbNodes]=b ; coo[2*nbNodes:]=c
163             coo=coo.reshape(nbNodes,3)
164             pos+=nbNodes*3*4 ; fd.seek(pos)#np.array(0,dtype='float%i'%(typeOfCoo)).nbytes
165             typ=fd.read(80).strip() ; pos=fd.tell()
166             zeK=b""
167             for k in self.dictMCTyp2:
168                 if k in typ:
169                     zeK=k
170                     break
171                     pass
172                 pass
173             pos+=8*4 # yeh man !
174             nbCellsOfType=np.memmap(fd,dtype='>i4',mode='r',offset=int(pos),shape=(1,)).tolist()[0]
175             pos+=4 # for the number of cells
176             pos+=2*4 # because it's great !
177             nbNodesPerCell=MEDCouplingMesh.GetNumberOfNodesOfGeometricType(self.dictMCTyp2[zeK])
178             nodalConn=np.memmap(fd,dtype='>i4',mode='r',offset=pos,shape=(nbCellsOfType,nbNodesPerCell))
179             meshName=meshName.strip()
180             mcmeshes2.append(self.__traduceMesh(meshName,zeK,coo,nodalConn))
181             pos+=nbNodesPerCell*nbCellsOfType*4
182             if abs(pos-end)>8:
183                 fd.seek(pos) ;elt=fd.read(80) ; typ=elt[:] ; pos+=80
184                 pass
185             nbOfTurn+=1
186             pass
187         #coo=mcmeshes2[0].getCoords() ; name=mcmeshes2[0].getName()
188         #for itmesh in mcmeshes2: itmesh.setCoords(coo)
189         #m=MEDCouplingUMesh.MergeUMeshesOnSameCoords(mcmeshes2) ; m.setName(name)
190         #mcmeshes.append(m)
191         return mcmeshes2
192
193     def __convertGeo2MEDC(self,fd,end):
194         #fd.readline()
195         #name=fd.readline().strip() ; fd.readline() ; fd.readline()
196         name=fd.read(80)
197         descrip=fd.read(80).strip() ; fd.read(80) ; fd.read(80)
198         pos=fd.tell()
199         mcmeshes=[]
200         elt=fd.read(80) ; elt=elt.strip() ; pos+=80
201         while pos!=end:
202             if b"part" not in elt:
203                 raise Exception("Error on reading mesh #1 !")
204             fd.seek(fd.tell()+4)
205             meshName=fd.read(80).strip()
206             if fd.read(len("coordinates"))!=b"coordinates":
207                 raise Exception("Error on reading mesh #2 !")
208             pos=fd.tell()
209             typeOfCoo=np.memmap(fd,dtype='byte',mode='r',offset=int(pos),shape=(1)).tolist()[0]
210             pos+=1+17*4
211             nbNodes=np.memmap(fd,dtype='int32',mode='r',offset=int(pos),shape=(1,)).tolist()[0]
212             pos+=4
213             coo=np.memmap(fd,dtype='float32',mode='r',offset=int(pos),shape=(nbNodes,3))
214             pos+=nbNodes*3*4 ; fd.seek(pos)#np.array(0,dtype='float%i'%(typeOfCoo)).nbytes
215             typ=fd.read(80).strip() ; pos=fd.tell()
216             mcmeshes2=[]
217             while pos!=end and typ!=b"part":
218                 if typ[0]==0: pos+=1; continue
219                 mctyp=self.dictMCTyp2[typ]
220                 nbCellsOfType=np.memmap(fd,dtype='int32',mode='r',offset=int(pos),shape=(1,)).tolist()[0]
221                 pos+=4
222                 if mctyp!=NORM_POLYHED and mctyp!=NORM_POLYGON:
223                     nbNodesPerCell=MEDCouplingMesh.GetNumberOfNodesOfGeometricType(mctyp)
224                     cells=np.memmap(fd,dtype='int32',mode='r',offset=pos,shape=(nbCellsOfType,nbNodesPerCell))
225                     pos+=nbCellsOfType*nbNodesPerCell*4
226                     fd.seek(pos)
227                     mcmeshes2.append(self.__traduceMesh(meshName,typ,coo,cells))
228                 elif mctyp==NORM_POLYHED:
229                     nbOfFacesPerCell=np.memmap(fd,dtype='int32',mode='r',offset=int(pos),shape=(nbCellsOfType,))
230                     pos+=nbCellsOfType*4
231                     szOfNbOfNodesPerFacePerCellArr=int(nbOfFacesPerCell.sum())
232                     arr1=np.memmap(fd,dtype='int32',mode='r',offset=int(pos),shape=(szOfNbOfNodesPerFacePerCellArr,))#arr1 -> nbOfNodesPerFacePerCellArr
233                     pos+=szOfNbOfNodesPerFacePerCellArr*4
234                     szOfNodesPerFacePerCellArr=arr1.sum()
235                     arr2=np.memmap(fd,dtype='int32',mode='r',offset=int(pos),shape=(szOfNodesPerFacePerCellArr,))#arr2 -> nodesPerFacePerCellArr
236                     pos+=szOfNodesPerFacePerCellArr*4 ; fd.seek(pos)
237                     mcmeshes2.append(self.__traduceMeshForPolyhed(meshName,coo,nbOfFacesPerCell,arr1,arr2))
238                     pass
239                 else:
240                     nbOfNodesPerCell=np.memmap(fd,dtype='int32',mode='r',offset=int(pos),shape=(nbCellsOfType,))
241                     pos+=nbCellsOfType*4
242                     szOfNbOfNodesPerCellArr=int(nbOfNodesPerCell.sum())
243                     arr1=np.memmap(fd,dtype='int32',mode='r',offset=int(pos),shape=(szOfNbOfNodesPerCellArr,))
244                     pos+=szOfNbOfNodesPerCellArr*4  ; fd.seek(pos)
245                     mcmeshes2.append(self.__traduceMeshForPolygon(meshName,coo,nbOfNodesPerCell,arr1))
246                 if pos!=end:
247                     elt=fd.read(80) ; elt=elt.strip() ; typ=elt[:] ; pos+=80
248                     pass
249                 pass
250             if mcmeshes2:
251                 coo=mcmeshes2[0].getCoords() ; name=mcmeshes2[0].getName()
252                 for itmesh in mcmeshes2: itmesh.setCoords(coo)
253                 m=MEDCouplingUMesh.MergeUMeshesOnSameCoords(mcmeshes2) ; m.setName(name)
254                 mcmeshes.append(m)
255             pass
256         return mcmeshes
257
258
259     def __convertField(self,mlfields, mcmeshes, fileName, fieldName, discr, nbCompo, locId, it):
260         """ Convert the fields. """
261         stars=re.search("[\*]+",fileName).group()
262         st="%0"+str(len(stars))+"i"
263         trueFileName=fileName.replace(stars,st%(it))
264         fd=open(os.path.join(self._dirName,trueFileName),"r+b") ; fd.seek(0,2) ; end=fd.tell() ; fd.seek(0)
265         name=fd.read(80).strip().split(b" ")[0].decode("ascii")
266         if name!=fieldName:
267             raise Exception("ConvertField : mismatch")
268         pos=fd.tell()
269         st=fd.read(80) ; st=st.strip() ; pos=fd.tell()
270         while pos!=end:
271             if st!=b"part":
272                 raise Exception("ConvertField : mismatch #2")
273             fdisc=MEDCouplingFieldDiscretization.New(self.discSpatial2[discr])
274             meshId=np.memmap(fd,dtype='int32',mode='r',offset=int(pos),shape=(1)).tolist()[0]-1
275             if meshId >= len( mcmeshes ):
276                 return
277             nbOfValues=fdisc.getNumberOfTuples(mcmeshes[meshId])
278             vals2=DataArrayDouble(nbOfValues,nbCompo)
279             fd.seek(pos+4)
280             st=fd.read(80).strip() ; pos=fd.tell()
281             offset=0
282             while pos!=end and st!=b"part":
283                 if st!=b"coordinates":
284                     nbOfValsOfTyp=mcmeshes[meshId].getNumberOfCellsWithType(self.dictMCTyp2[st])
285                 else:
286                     nbOfValsOfTyp=nbOfValues
287                     pass
288                 vals=np.memmap(fd,dtype='float32',mode='r',offset=int(pos),shape=(nbOfValsOfTyp,nbCompo))#np.memmap(fd,dtype='int32',mode='r',offset=159,shape=(1))
289                 vals2[offset:offset+nbOfValsOfTyp]=DataArrayDouble(np.array(vals,dtype='float64')).fromNoInterlace()
290                 pos+=nbOfValsOfTyp*nbCompo*4 ; fd.seek(pos)
291                 st=fd.read(80) ; st=st.strip() ; pos=fd.tell()
292                 offset+=nbOfValsOfTyp
293                 pass
294             f=MEDCouplingFieldDouble(self.discSpatial2[discr],ONE_TIME) ; f.setName("%s_%s"%(fieldName,mcmeshes[meshId].getName()))
295             f.setMesh(mcmeshes[meshId]) ; f.setArray(vals2) ; f.setTime(float(it),it,-1)
296             f.checkConsistencyLight()
297             mlfields[locId+meshId].appendFieldNoProfileSBT(f)
298             pass
299
300     def __convertFieldFortran(self,mlfields, mcmeshes, fileName, fieldName, discr, nbCompo, locId, it):
301         """ Convert the fields. """
302         if re.search("[\*]+",fileName):
303             stars=re.search("[\*]+",fileName).group()
304             st="%0"+str(len(stars))+"i"
305             trueFileName=fileName.replace(stars,st%(it))
306             pass
307         else:
308             trueFileName=fileName
309             pass
310         fd=open(os.path.join(self._dirName,trueFileName),"r+b") ; fd.seek(0,2) ; end=fd.tell() ; fd.seek(0)
311         name=fd.read(80)
312         if fieldName not in name:
313             raise Exception("ConvertField : mismatch")
314         pos=fd.tell()
315         st=fd.read(80) ; st=st.strip() ; pos=fd.tell()
316         if b"part" not in st:
317             raise Exception("ConvertField : mismatch #2")
318         st=fd.read(80).strip() ; pos=fd.tell()
319         pos+=12 # I love it
320         offset=0
321         nbTurn=0
322         while pos!=end and b"part" not in st:
323             fdisc=MEDCouplingFieldDiscretization.New(self.discSpatial2[discr])
324             nbOfValues=fdisc.getNumberOfTuples(mcmeshes[nbTurn])
325             vals2=DataArrayDouble(nbOfValues,nbCompo)
326             pos+=24 # I love it again !
327             nbOfValsOfTyp=np.memmap(fd,dtype='>i4',mode='r',offset=pos,shape=(1)).tolist()[0]/4
328             pos+=4
329             vals=np.zeros(dtype=">f4",shape=(nbOfValsOfTyp*nbCompo))
330             for iii in range(nbCompo):
331                 valsTmp=np.memmap(fd,dtype='>f4',mode='r',offset=int(pos),shape=(nbOfValsOfTyp))
332                 vals[iii*nbOfValsOfTyp:(iii+1)*nbOfValsOfTyp]=valsTmp
333                 pos+=nbOfValsOfTyp*4
334                 pos+=2*4 ## hey hey, that is the ultimate class !
335                 vals2.setInfoOnComponent(iii,chr(ord('X')+iii))
336                 pass
337             if pos>end:
338                 pos=end
339                 pass
340             vals=vals.reshape(nbOfValsOfTyp,nbCompo)
341             vals2[offset:offset+nbOfValsOfTyp]=DataArrayDouble(np.array(vals,dtype='float64')).fromNoInterlace()
342             if pos!=end:
343                 fd.seek(pos)
344                 st=fd.read(80) ; st=st.strip() ; pos=fd.tell()
345                 st=fd.read(80) ; st=st.strip() ; pos=fd.tell()
346                 pass
347             f=MEDCouplingFieldDouble(self.discSpatial2[discr],ONE_TIME) ; f.setName("%s_%s"%(fieldName,mcmeshes[nbTurn].getName()))
348             f.setMesh(mcmeshes[nbTurn]) ; f.setArray(vals2) ; f.setTime(float(it),it,-1)
349             f.checkConsistencyLight()
350             mlfields[locId+nbTurn].appendFieldNoProfileSBT(f)
351             nbTurn+=1
352             pass
353         pass
354
355     def loadInMEDFileDS(self):
356         """ Load a CASE file into a MEDFileData object. """
357         f=open(self._fileName)
358         lines=f.readlines()
359         ind=lines.index("GEOMETRY\n")
360         if ind==-1:
361             raise Exception("Error with file %s"%(fname))
362         geoName=re.match("model:([\W]*)([\w\.]+)",lines[ind+1]).group(2)
363         m1,m2,typeOfFile=self.__convertGeo2MED(geoName)
364         fieldsInfo=[] ; nbOfTimeSteps=0
365         if "VARIABLE\n" in lines:
366             ind=lines.index("VARIABLE\n")
367             end=len(lines)-1
368             if "TIME\n" in lines:
369                 end=lines.index("TIME\n")
370                 pass
371             for i in range(ind + 1,end):
372                 m=re.match("^([\w]+)[\s]+per[\s]+([\w]+)[\s]*\:[\s]*[0-9]*[\s]*([\w]+)[\s]+([\S]+)$",lines[i])
373                 if m:
374                     if m.groups()[0]=="constant":
375                         continue
376                     spatialDisc=m.groups()[1] ; fieldName=m.groups()[2] ; nbOfCompo=self.dictCompo2[m.groups()[0]] ; fieldFileName=m.groups()[3]
377                     if "*" in fieldFileName:
378                       fieldsInfo.append((fieldName,spatialDisc,nbOfCompo,fieldFileName))
379                     pass
380                 pass
381
382             expr=re.compile("number[\s]+of[\s]+steps[\s]*\:[\s]*([\d]+)")
383             tmp = [line for line in lines if expr.search(line)]
384             if tmp:
385                 nbOfTimeSteps = int(expr.search(tmp[0]).group(1))
386                 expr=re.compile("filename[\s]+start[\s]+number[\s]*\:[\s]*([\d]+)")
387                 startIt = int(expr.search([line for line in lines if expr.search(line)][0]).group(1))
388                 expr=re.compile("filename[\s]+increment[\s]*\:[\s]*([\d]+)")
389                 incrIt = int(expr.search([line for line in lines if expr.search(line)][0]).group(1))
390             else:
391                 nbOfTimeSteps=1
392                 startIt=0
393                 incrIt=1
394                 pass
395             curIt=startIt
396             pass
397         mlfields=MEDFileFields()
398         mlfields.resize(len(fieldsInfo)*len(m1))
399         i=0
400         for field in fieldsInfo:
401             for m in m1:
402                 mlfields.setFieldAtPos(i,MEDFileFieldMultiTS())
403                 i+=1
404                 pass
405             pass
406         for ts in range(nbOfTimeSteps):
407             i=0
408             for field in fieldsInfo:
409                 if typeOfFile:
410                     self.__convertField(mlfields,m1,field[3],field[0],field[1],field[2],i,curIt);
411                 else:
412                     self.__convertFieldFortran(mlfields,m1,field[3],field[0],field[1],field[2],i,curIt)
413                     pass
414                 i+=len(m1)
415                 pass
416             curIt+=incrIt
417             pass
418         ret=MEDFileData()
419         ret.setMeshes(m2)
420         del mlfields[[x for x in range(len(mlfields)) if len(mlfields[x]) == 0]]
421         ret.setFields(mlfields)
422         return ret
423
424     pass