pyNastran
0.5.0
pyNastran BDF Reader/Writer, OP2 Parser, and GUI
|
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