pyNastran  0.5.0
pyNastran BDF Reader/Writer, OP2 Parser, and GUI
fortranFile.py
Go to the documentation of this file.
00001 ## GNU Lesser General Public License
00002 ## 
00003 ## Program pyNastran - a python interface to NASTRAN files
00004 ## Copyright (C) 2011-2012  Steven Doyle, Al Danial
00005 ## 
00006 ## Authors and copyright holders of pyNastran
00007 ## Steven Doyle <mesheb82@gmail.com>
00008 ## Al Danial    <al.danial@gmail.com>
00009 ## 
00010 ## This file is part of pyNastran.
00011 ## 
00012 ## pyNastran is free software: you can redistribute it and/or modify
00013 ## it under the terms of the GNU Lesser General Public License as published by
00014 ## the Free Software Foundation, either version 3 of the License, or
00015 ## (at your option) any later version.
00016 ## 
00017 ## pyNastran is distributed in the hope that it will be useful,
00018 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
00019 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00020 ## GNU General Public License for more details.
00021 ## 
00022 ## You should have received a copy of the GNU Lesser General Public License
00023 ## along with pyNastran.  If not, see <http://www.gnu.org/licenses/>.
00024 ## 
00025 
00026 from __future__ import division, print_function
00027 #import sys
00028 from struct import unpack, pack
00029 
00030 from pyNastran.op2.op2Errors import (InvalidMarkerError, InvalidMarkersError,
00031                                      ZeroBufferError, EndOfFileError)
00032 
00033 class FortranFile(object):
00034     def __init__(self):
00035         ## the endian of the processor (typically '<' for Windows/Linux/Mac,
00036         ##                              '>' for old HPCs)
00037         ## currently does nothing
00038         self.endian = '<'
00039         ## currently does nothing
00040         self.bufferSize = 65535
00041     
00042     def setEndian(self, endian='<'):
00043         """
00044         sets the endian
00045         @todo hasnt been implemented
00046         """
00047         self.endian = endian
00048 
00049     def readHollerith(self):
00050         """
00051         doesnt really read a hollerith, it's an integer
00052         of value=528 which corresponds to the length of
00053         iTable=3
00054         """
00055         self.skip(4)
00056 
00057     def readHeader(self, expected=None, debug=True):
00058         """
00059         a header is defined as (4,i,4), where i is an integer
00060         """
00061         #print(self.printSection(60))
00062         #data = self.op2.read(12)
00063         ints = self.readFullIntBlock()
00064         #print("header ints = %s" % (repr(ints)))
00065         #self.n += 12*4
00066         
00067         if len(ints) == 5:
00068             #print "   buffer block..."
00069             ## flag to help know if a buffer was found
00070             self.hasBuffer = True
00071             ints = self.readFullIntBlock()
00072             if debug and self.makeOp2Debug:
00073                 self.op2Debug.write('bufferBlock = |%s|\n' % (str(ints)))
00074             
00075         elif len(ints) == 0:
00076             return None
00077         
00078         if not(ints[0] == ints[2] == 4):
00079             msg  = "pyNastran reading failed because an improperly formatted (or unsupported) table is in the OP2.\n"
00080             msg += "If you remove the offending table (make sure you're using PARAM,POST,-1 first) the code should work.\n"
00081             msg += "header ints=(%s) expected=%s\n" % (str(ints[0:5]), expected)
00082             msg += 'tableName=|%s|' % (self.tableName)
00083             raise InvalidMarkerError(msg)
00084         
00085         
00086         #if ints[1]==2:  # buffer???
00087         #    ints = self.readFullIntBlock()
00088         #    print("bufferInts1=%s" % (ints))
00089         #    ints = self.readFullIntBlock()
00090         #    print("bufferInts2=%s" % (ints))
00091         #print("marker=%s" % (ints[1]))
00092             
00093         if debug and self.makeOp2Debug:
00094             self.op2Debug.write('[4,%s,4]\n' % (ints[1]))
00095         return ints[1]
00096 
00097     def readString(self, nData):
00098         """
00099         reads nCharacters that are assumed to be a string
00100         """
00101         data = self.op2.read(nData)
00102         string = self.getStrings(data)
00103         if self.makeOp2Debug:
00104             self.op2Debug.write('|%s|\n' % (str(string)))
00105         self.n += nData
00106         return string
00107 
00108     #def readString(self, nData):
00109     #    data = self.op2.read(nData)
00110     #    self.n += nData
00111     #    words = self.getStrings(data)
00112     #    return ''.join(words)
00113 
00114     def readInts(self,nInts,debug=True):
00115         """
00116         reads a list of nIntegers
00117         @param self the object pointer
00118         @param nInts the number of ints to read
00119         @debug developer debug combined with makeOp2Debug
00120         """
00121         nData = 4*nInts
00122         #print "nData = ",nData
00123         data = self.op2.read(nData)
00124 
00125         iFormat = str(nInts)+'i'
00126         iFormat = bytes(iFormat)
00127         ints = unpack(iFormat,data)
00128         if debug and self.makeOp2Debug:
00129             self.op2Debug.write('|%s|\n' % (str(ints)))
00130 
00131         self.n+=nData
00132         return ints
00133 
00134     def readDoubles(self, nData, debug=True):
00135         """
00136         reads a list of nDoubles
00137         @param self the object pointer
00138         @param nData the number of doubles to read
00139         @debug developer debug combined with makeOp2Debug
00140         """
00141         data = self.op2.read(nData)
00142         self.n += nData
00143         doubles = self.getDoubles(data)
00144         if debug and self.makeOp2Debug:
00145             self.op2Debug.write('|%s|\n' % (str(doubles)))
00146         return doubles
00147 
00148     def readFloats(self, nData, debug=True):
00149         """
00150         reads nFloats
00151         """
00152         data = self.op2.read(nData)
00153         self.n += nData
00154         floats = self.getFloats(data)
00155         if debug and self.makeOp2Debug:
00156             self.op2Debug.write('|%s|\n' % (str(floats)))
00157         return floats
00158 
00159     def getStrings(self, data):
00160         """
00161         unpacks a data set into a series of characters
00162         """
00163         n = len(data)
00164         iFormat = str(n)+'s'
00165         iFormat = bytes(iFormat)
00166         strings, = unpack(iFormat, data)
00167         return strings #.encode('utf-8')
00168 
00169     def getStrings2(self, data, endian):
00170         """
00171         unpacks a data set into a series of characters
00172         """
00173         n = len(data)
00174         iFormat = endian+str(n)+'s'
00175         iFormat = bytes(iFormat)
00176         strings = unpack(iFormat, data)
00177         return strings
00178 
00179     def getInts(self, data, debug=True):
00180         """
00181         unpacks a data set into a series of ints
00182         """
00183         n = len(data)
00184         nInts = n//4
00185         #print "nInts = ",nInts
00186         iFormat = str(nInts)+'i'
00187         iFormat = bytes(iFormat)
00188         ints = unpack(iFormat, data[:nInts*4])
00189         return ints
00190 
00191     def getInts2(self, data, endian, debug=True):
00192         """
00193         unpacks a data set into a series of ints
00194         """
00195         n = len(data)
00196         nInts = n//4
00197         iFormat = endian + str(nInts) + 'i'
00198         iFormat = bytes(iFormat)
00199         ints = unpack(iFormat, data[:nInts*4])
00200         return ints
00201 
00202     def getLongs(self, data):
00203         """
00204         unpacks a data set into a series of longs
00205         """
00206         n = len(data)
00207         nLongs = n//4
00208         #print "nLongs = ", nLongs
00209         #a = pack('l', 200)
00210         #print "len(a) = ", len(a)
00211         
00212         iFormat = str(nLongs)+'l'
00213         iFormat = bytes(iFormat)
00214         longs = unpack(iFormat, data[:nLongs*4])
00215         return longs
00216 
00217     def getFloats(self, data):
00218         """
00219         unpacks a data set into a series of floats
00220         """
00221         n = len(data)
00222         nFloats = n//4
00223         iFormat = str(nFloats)+'f'
00224         iFormat = bytes(iFormat)
00225         ints = unpack(iFormat, data[:nFloats*4])
00226         return ints
00227 
00228     def getFloats2(self, data, endian):
00229         """
00230         unpacks a data set into a series of floats
00231         """
00232         n = len(data)
00233         nFloats = n//4
00234         iFormat = endian+str(nFloats)+'f'
00235         iFormat = bytes(iFormat)
00236         ints = unpack(iFormat, data[:nFloats*4])
00237         return ints
00238 
00239     def getDoubles(self, data):
00240         """
00241         unpacks a data set into a series of doubles
00242         """
00243         n = len(data)
00244         nDoubles = n//8
00245         iFormat = str(nDoubles)+'d'
00246         iFormat = bytes(iFormat)
00247         ints = unpack(iFormat,data[:nDoubles*8])
00248         return ints
00249     
00250     def printBlock(self, data, nMax=200):
00251         """
00252         prints a data set in int/float/double/string format to
00253         determine table info.  doesn't move cursor.
00254         @note this is a great function for debugging
00255         """
00256         data2 = data
00257         nData = len(data)
00258         #if nData>nMax:
00259             #print("oops!  too much data...limiting to %s bytes.  nData=%s" % (nMax,nData))
00260             #data2 = data[:nMax]
00261         
00262         msg = ''
00263         ints    = self.getInts(data2)
00264         #longs   = self.getLongs(data2)
00265         floats  = self.getFloats(data2)
00266         #doubles = self.getDoubles(data2)
00267         strings = self.getStrings(data2)
00268         msg += "n       = %s\n" % (self.n)
00269         msg += "ints    = %s\n" % (str(ints))
00270         #msg += "longs  = %s\n" % (longs)
00271         msg += "floats  = %s\n" % (str(floats))
00272         #msg += "doubles = %s\n" % (str(doubles))
00273         msg += "strings = |%r|\n" % (strings)
00274         msg += "nWords  = %s\n" % (len(data)//4)
00275         #msg += "tell    = %s\n" % (self.op2.tell())
00276         return msg
00277 
00278     def printBlock2(self, data, endian):
00279         """
00280         prints a data set in int/float/double/string format to
00281         determine table info.  doesn't move cursor.
00282         @note this is a great function for debugging
00283         """
00284         msg = ''
00285         ints    = self.getInts2(data,endian)
00286         #longs   = self.getLongs(data)
00287         floats  = self.getFloats2(data,endian)
00288         #doubles = self.getDoubles(data)
00289         strings = self.getStrings2(data,endian)
00290         msg += "ints    = %s\n" % (str(ints))
00291         #msg += "longs  = %s\n" % (longs)
00292         msg += "floats  = %s\n" % (str(floats))
00293         #msg += "doubles = %s\n" % (doubles)
00294         msg += "strings = |b%r|\n" % (''.join(strings))
00295         msg += "nWords  = %s\n" % (len(data)//4)
00296         #msg += "tell    = %s\n" % (self.op2.tell())
00297         return msg
00298 
00299     def getData(self,n):
00300         """
00301         gets a data set of length N
00302         """
00303         if n<=0:
00304             raise ZeroBufferError()
00305 
00306         #assert self.op2.tell()==self.n,'tell=%s n=%s' % (self.op2.tell(),self.n)
00307         data = self.op2.read(n)
00308         self.n+=n
00309         #print "n =",n
00310         #assert self.op2.tell()==self.n,'tell=%s n=%s' % (self.op2.tell(),self.n)
00311         return data
00312 
00313     def readData(self,n):
00314         return self.getData(n)
00315 
00316     def getBlockIntEntry(self, data, n):
00317         """
00318         given a data set, grabs the nth word and casts it as an integer
00319         """
00320         data2 = data[4*(n-1):4*(n-1)+4]
00321         iFormat = 'i'
00322         iFormat = bytes(iFormat)
00323         return unpack(iFormat, data2)[0]
00324 
00325     def printSection(self,nBytes):
00326         """
00327         prints data, but doesn't move the cursor
00328         @param self the object pointer
00329         @param nBytes the number of bytes to print the data specs on
00330         @retval msg ints/floats/strings of the next nBytes (handles poorly sized nBytes; uncrashable :) )
00331         @note this the BEST function when adding new cards/tables/debugging
00332         """
00333         data = self.op2.read(nBytes)
00334         msg = self.printBlock(data)
00335         self.op2.seek(self.n)
00336         return msg
00337     
00338     def printSection2(self,nBytes,endian):
00339         """
00340         prints data, but doesn't move the cursor
00341         @param self the object pointer
00342         @param nBytes the number of bytes to print the data specs on
00343         @retval msg ints/floats/strings of the next nBytes (handles poorly sized nBytes; uncrashable :) )
00344         @note this the BEST function when adding new cards/tables/debugging
00345         """
00346         data = self.op2.read(nBytes)
00347         msg = self.printBlock2(data,endian)
00348         self.op2.seek(self.n)
00349         return msg
00350     
00351     def skip(self,n):
00352         """skips nBits"""
00353         self.n += n
00354         self.op2.seek(self.n)
00355 
00356     def scan(self,n):
00357         """same as skip, but actually reads the data instead of using seek"""
00358         self.op2.read(n)
00359         self.n+=n
00360 
00361     def getTableCode(self,expected=None,debug=True):
00362         tableCode = self.readHeader(expected,debug)
00363         return tableCode
00364 
00365     def getMarker(self,expected=None,debug=True):
00366         tableCode = self.readHeader(expected,debug)
00367         return tableCode
00368         
00369     def readMarker(self,expected=None):
00370         return self.getMarker(expected)
00371         
00372     def readMarkers(self,markers,tableName=None,debug=False,printErrorOnFailure=True):
00373         """
00374         Reads a set of predefined markers e.g. [-3,1,0]
00375         and makes sure it is correct.
00376         
00377         A marker (e.g. a -3) is a series of 3 integers [4,-3,4].  Typically 3
00378         markers are put together (e.g. [-3,1,0]) such that the integers are
00379         [4,-3,4, 4,1,4, 4,0,4] to mark important parts of the table. 
00380         
00381         Markers will "increment" during table reading, such that the first marker
00382         is [-1,1,0], then [-2,1,0], etc.  Tables will end (or really the next table starts)
00383         when a [-1,1,0] or a [0,1,0] marker is found.
00384         
00385         # Verify the following statement...
00386         Occassionally, buffer markers will be embedded inside the 
00387         marker [-3,1,0], (e.g. [4,2^16,4] <- the default BUFFSIZE), which make
00388         reading the marker more difficult.
00389         """
00390         #print("markers = ",markers)
00391         foundMarkers = []
00392         for marker in markers:
00393             tableCode = self.readHeader(marker,debug)
00394             #if tableCode==2:
00395             #    tableCode = self.readHeader(marker,debug)
00396 
00397             #print("tableCode=",tableCode)
00398             if tableCode==None:
00399                 return
00400             if marker!=tableCode:
00401                 msg = ''
00402                 if printErrorOnFailure:
00403                     msg  = '\nmarkers=%s foundMarkers=%s\n' % (markers,foundMarkers)
00404                     msg += 'tableName=%s found=%s expected=%s leftover=%s' % (tableName,tableCode,marker,self.printSection(40))
00405                     #print(msg)
00406                 raise InvalidMarkersError(msg)
00407             foundMarkers.append(marker)
00408             ###
00409         ###
00410         msg = ''
00411         for i in markers:
00412             msg += '[4,'+str(i)+',4] + '
00413         if self.makeOp2Debug:
00414             self.op2Debug.write(msg[:-3]+'\n')
00415         if debug:
00416             self.log.debug("@markers = %s" % (markers))
00417             self.log.debug("")
00418         ###
00419 
00420     def getNMarkers(self,nMarkers,rewind=False):
00421         """gets the next N markers, verifies they're correct"""
00422         markers = []
00423         for iMarker in xrange(nMarkers):
00424             tableCode = self.readHeader(None)
00425             markers.append(tableCode)
00426         ###
00427         if rewind:
00428             self.n -= 12*nMarkers
00429             self.op2.seek(self.n)
00430             
00431         return markers
00432 
00433     def isTableDone(self,expectedMarkers):
00434         markers = self.getNMarkers(len(expectedMarkers),rewind=True)
00435         #print "getMarkers = ",markers
00436         
00437         if markers==[-1,7]:
00438             return True
00439         elif markers==expectedMarkers:
00440             #sys.exit(expectedMarkers)
00441             return False
00442         else:
00443             raise RuntimeError('this should never happen...invalid markers...expected=%s markers=%s' % (expectedMarkers,markers))
00444 
00445     def goto(self,n):
00446         """
00447         jumps to position n in the file
00448         @param self the object pointer
00449         @param n the position to goto
00450         @note n>0
00451         """
00452         #print "goto n = |%s|" % (n)
00453         assert n>0
00454         self.n = n
00455         self.op2.seek(n)
00456 
00457     def readBlock(self):
00458         """
00459         reads a fortran formatted data block
00460         nWords  data1 data2 data3 nWords
00461         """
00462         data = self.op2.read(4)
00463         if len(data) == 0:
00464             raise EndOfFileError("data=('')")
00465         
00466         iFormat = 'i'
00467         iFormat = bytes(iFormat)
00468         nValues, = unpack(iFormat, data)
00469         self.n += 4
00470         data = self.op2.read(nValues)
00471         self.n += nValues+4
00472         self.goto(self.n)
00473         return data
00474 
00475     def readFullBlock(self):
00476         """
00477         reads a fortran formatted data block
00478         nWords  data1 data2 data3 nWords
00479         includes nWords in the output
00480         """
00481         data = self.op2.read(4)
00482         iFormat = 'i'
00483         iFormat = bytes(iFormat)
00484         nValues, = unpack(iFormat,data)
00485         self.n += 4
00486         data = self.op2.read(nValues)
00487         self.n += nValues+4
00488         self.goto(self.n)
00489 
00490     def readFullIntBlock(self):
00491         """
00492         reads a fortran formatted block
00493         assumes that the data is made up of integers only
00494         """
00495         """
00496         reads a fortran formatted data block
00497         nWords  data1 data2 data3 nWords
00498         includes nWords in the output
00499         """
00500         data = self.op2.read(4)
00501         if len(data) == 0:
00502             self.log.debug("found the end of the file...")
00503             return []
00504         iFormat = 'i'
00505         iFormat = bytes(iFormat)
00506         nValues, = unpack(iFormat,data)
00507         self.n += 4
00508         data = self.op2.read(nValues)
00509         self.n += nValues+4
00510         self.goto(self.n)
00511 
00512         nInts = len(data)//4
00513         iFormat = str(nInts)+'i'
00514         iFormat = bytes(iFormat)
00515         ints = unpack(iFormat,data)
00516         return [nValues]+list(ints)+[nValues]
00517 
00518     def readStringBlock(self,debug=True):
00519         """
00520         reads a fortran formatted block
00521         assumes that the data is made up of characters only
00522         """
00523         data = self.readBlock()
00524         nLetters = len(data)
00525         iFormat = str(nLetters) + 's'
00526         iFormat = bytes(iFormat)
00527         word, = unpack(iFormat, data)
00528 
00529         #print "word = |%s|" % (word)
00530         #print "nLetters=%s word=|%s|" % (nLetters,word)
00531         if debug and self.makeOp2Debug:
00532             self.op2Debug.write('|%s|\n' % (str(word)))
00533         return word
00534 
00535     def readIntBlock(self):
00536         """
00537         reads a fortran formatted block
00538         assumes that the data is made up of integers only
00539         """
00540         data = self.readBlock()
00541         nInts = len(data)//4
00542         iFormat = str(nInts)+'i'
00543         iFormat = bytes(iFormat)
00544         ints = unpack(iFormat, data)
00545         return ints
00546 
00547     def readFloatBlock(self):
00548         """
00549         reads a fortran formatted block
00550         assumes that the data is made up of floats only
00551         """
00552         data = self.readBlock()
00553         nFloats = len(data)//4
00554         iFormat = str(nFloats)+'f'
00555         iFormat = bytes(iFormat)
00556         floats = unpack(iFormat, data)
00557         return floats
00558 
00559     def readDoubleBlock(self):
00560         """
00561         reads a fortran formatted block
00562         assumes that the data is made up of doubles only
00563         """
00564         data = self.readBlock()
00565         nDoubles = len(data)//8
00566         iFormat = str(nDoubles)+'d'
00567         iFormat = bytes(iFormat)
00568         doubles = unpack(iFormat, data)
00569         return doubles
00570 
00571     def rewind(self, n):
00572         """
00573         rewinds the file nBytes
00574         @warning
00575             doesnt support a full rewind, only a partial
00576         """
00577         self.n -= n
00578         self.op2.seek(self.n)
00579 
00580     def readTableName(self, rewind=True, debug=True, stopOnFailure=True):
00581         """
00582         peeks into a table to check it's name
00583         """
00584         #debug = True
00585         if rewind:
00586             debug = False
00587         n = self.n
00588         try:
00589             #print ""
00590             self.readMarkers([0,2],debug)
00591             word = self.readStringBlock(debug)
00592             #print("*word = |%r|" % (word))
00593 
00594             #print "n      = ",n
00595             #print "self.n = ",self.n
00596             #print "op2.tell = ",self.op2.tell()
00597             #print "******"
00598             if rewind:
00599                 self.n = n
00600                 self.op2.seek(n)
00601             #print "n      = ",n
00602             #print "self.n = ",self.n
00603             #print "op2.tell = ",self.op2.tell()
00604             tableName = word.strip()
00605             return tableName
00606         except:
00607             if rewind and not stopOnFailure:
00608                 self.n = n
00609                 self.op2.seek(n)
00610                 return
00611             raise
00612             ###
00613         ###
00614 
00615     def skipNextTable(self,bufferSize=10000):
00616         """
00617         skips a table
00618         @todo fix bugs
00619         """
00620         tableName = self.readTableName(rewind=False) # GEOM1
00621         self.tableInit(tableName)
00622         self.log.debug("skippingTable |%s|" % (tableName))
00623         self.log.debug("self.n = %s" % (self.n))
00624 
00625         self.readMarkers([-1, 7], tableName)
00626         
00627         dataPack = (4,1,4,  4,0,4,  4,0,4)  # marks the end of the table
00628         binaryData = pack('9i',*dataPack)
00629         #[1,0,0] marks the end of the table
00630         
00631         i = 0
00632         error = 80
00633         n = self.n
00634         endIndex = -1
00635         data = "dummy"
00636         while endIndex== -1 and len(data)>0:
00637             data     = self.op2.read(bufferSize+error)
00638             endIndex = data.find(binaryData)
00639             
00640             self.op2.seek(n+i*bufferSize)
00641             i+=1
00642         
00643         #print "i = ",i
00644         assert endIndex>0,'couldnt find the end of the table'
00645         self.n = self.n+(i-1)*bufferSize + endIndex # 36 so it gets to the end of the table markersNext=[0] or [2]
00646         
00647         n = self.n
00648         self.n += 36  ## @todo sometimes this is needed
00649         #ints = self.readIntBlock()
00650         #print "*?*ints = ",ints
00651         #if len(ints)==0:
00652         #    pass
00653 
00654         self.op2.seek(self.n)
00655         self.log.debug("self.op2.tell() = %s" % (self.op2.tell()))
00656         #self.printSection(200)
00657         marker = self.getMarker()
00658         #print "marker = ",marker
00659         if marker==2:
00660             isAnotherTable = True
00661         else:# marker=0
00662             isAnotherTable = False
00663         isAnotherTable = True
00664         ###
00665         #print "isAnotherTable = ",isAnotherTable
00666         self.n -= 24  # subtract off the header [0,2] or [0,0]
00667         self.op2.seek(self.n)
00668         self.log.debug("self.n = %s" % (self.n))
00669         self.log.debug("---table %s is skipped---" % (tableName))
00670         
00671         return isAnotherTable
00672 
00673     def hasMoreTables(self):
00674         #print self.printSection(120)
00675         try:
00676             marker1 = self.getMarker('[4,0,4]')
00677             marker2 = self.getMarker('[4,0,4] or [4,2,4]')
00678 
00679             marker = [marker1,marker2]
00680             #print "marker = ",marker
00681             if marker==[0,2]:
00682                 isAnotherTable = True
00683             else:# marker=0
00684                 isAnotherTable = False
00685             ###
00686             #print "isAnotherTable = ",isAnotherTable
00687             self.n -= 24  # subtract off the header [0,2] or [0,0]
00688             self.op2.seek(self.n)
00689         except IndexError:
00690             isAnotherTable = False
00691         return isAnotherTable
00692     
 All Classes Namespaces Files Functions Variables