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 import copy 00029 from numpy import array 00030 from struct import unpack 00031 00032 from pyNastran.op2.tables.oug.oug import OUG 00033 from pyNastran.op2.tables.oes_stressStrain.oes import OES 00034 #from pyNastran.op2.tables.oes_stressStrain.oesnlxr import OESNLXR 00035 #from pyNastran.op2.tables.oes_stressStrain.oesnlxd import OESNLXD 00036 00037 from pyNastran.op2.tables.oqg_constraintForces.oqg import OQG 00038 from pyNastran.op2.tables.oef_forces.oef import OEF 00039 from pyNastran.op2.tables.opg_appliedLoads.opg import OPG 00040 from pyNastran.op2.tables.oee_energy.oee import OEE 00041 from pyNastran.op2.tables.ogf_gridPointForces.ogf import OGF 00042 #from pyNastran.op2.tables.hisadd import HISADD - combined with R1TAB for now 00043 from pyNastran.op2.tables.r1tab import R1TAB 00044 from pyNastran.op2.tables.destab import DESTAB 00045 from pyNastran.op2.tables.lama_eigenvalues.lama import LAMA 00046 00047 00048 00049 class ResultTable(OQG,OUG,OEF,OPG,OES,OEE,OGF,R1TAB,DESTAB,LAMA): # OESNLXR,OESNLXD, 00050 00051 def readTableA_DUMMY(self): 00052 """reads a dummy geometry table""" 00053 self.iTableMap = {} 00054 self.readRecordTable('DUMMY') 00055 00056 def readTableB_DUMMY(self): 00057 """reads a dummy results table""" 00058 self.tableName = 'DUMMY' 00059 table3 = self.readTable_DUMMY_3 00060 table4Data = self.readDUMMY_Data 00061 self.readResultsTable(table3,table4Data) 00062 self.deleteAttributes_OPG() 00063 00064 def readTable_DUMMY_3(self,iTable): 00065 """sets dummy parameters""" 00066 self.analysisCode = None 00067 self.tableCode = None 00068 self.formatCode = None 00069 self.sortCode = None 00070 00071 def readDUMMY_Data(self): 00072 """creates a dummy object and skips the results""" 00073 self.obj = None 00074 self.readOES_Element() 00075 00076 def updateSort1(self): 00077 extract = self.extractSort1 00078 return 'i',extract 00079 00080 def updateSort2(self): 00081 extract = self.extractSort2 00082 return 'f' 00083 00084 def extractSort1(self,eidDevice,dt): 00085 #eidDevice, = unpack(b'i',data) 00086 #print "eidDevice=%s dt=%s eid-dev=%s out=%s" %(eidDevice,dt,eidDevice-self.deviceCode,(eidDevice-self.deviceCode)/10.) 00087 return (eidDevice-self.deviceCode)//10 00088 00089 def extractSort2(self,timeFreq,eid): 00090 #print "timeFreq=%s eid=%s" %(timeFreq,eid) 00091 #gridDevice, = unpack(b'i',data) 00092 return timeFreq 00093 00094 def addDataParameter(self,data,Name,Type,fieldNum,applyNonlinearFactor=True,fixDeviceCode=False): 00095 """ 00096 self.mode = self.getValues(data,'i',5) ## mode number 00097 """ 00098 value = self.getValues(data,Type,fieldNum) 00099 if fixDeviceCode: 00100 value = (value-self.deviceCode)//10 00101 #print "Name=%s Type=%s fieldNum=%s aCode=%s value=%s" %(Name,Type,fieldNum,self.analysisCode,value) 00102 setattr(self,Name,value) 00103 self.dataCode[Name] = value 00104 00105 if applyNonlinearFactor: 00106 self.nonlinearFactor = value 00107 self.dataCode['nonlinearFactor'] = value 00108 self.dataCode['name'] = Name 00109 00110 def setNullNonlinearFactor(self): 00111 self.nonlinearFactor = None 00112 self.dataCode['nonlinearFactor'] = None 00113 00114 def applyDataCodeValue(self,Name,value): 00115 self.dataCode[Name] = value 00116 00117 def createTransientObject(self,storageObj,classObj,debug=False): 00118 """ 00119 Creates a transient object (or None if the subcase should be skippied). 00120 @param storageObj the dictionary to store the object in (e.g. self.bars) 00121 @param classObj the class object to instantiate 00122 @note dt can also be loadStep depending on the class 00123 """ 00124 if debug: 00125 print("create Transient Object") 00126 print("***NF = %s" %(self.nonlinearFactor)) 00127 #print "DC = ",self.dataCode 00128 00129 if self.iSubcase in storageObj: 00130 #print "updating dt..." 00131 self.obj = storageObj[self.iSubcase] 00132 #print "obj = ",self.obj.__class__.__name__ 00133 #print self.obj.writeF06(['',''],'PAGE ',1)[0] 00134 00135 try: 00136 self.obj.updateDataCode(self.dataCode) 00137 #self.obj.updateDt(self.dataCode,self.nonlinearFactor) 00138 except: 00139 #try: 00140 #print "objName = ",self.obj.name() 00141 #except: 00142 #print "objName = ",self.obj 00143 raise 00144 ### 00145 else: 00146 #if self.isRegular: 00147 #self.obj = classObj(self.dataCode,not(self.isRegular),self.iSubcase,self.nonlinearFactor) 00148 #else: 00149 self.obj = classObj(self.dataCode,self.isSort1(),self.iSubcase,self.nonlinearFactor) 00150 #print "obj2 = ",self.obj.__class__.__name__ 00151 storageObj[self.iSubcase] = self.obj 00152 ### 00153 00154 def createThermalTransientObject(self,resultName,objClass,isSort1): 00155 #print resultName 00156 if self.iSubcase in resultName: 00157 self.obj = resultName[self.iSubcase] 00158 #print "returning iSubcase result=%s" %(self.iSubcase) 00159 else: 00160 self.obj = objClass(self.dataCode,isSort1,self.iSubcase,self.nonlinearFactor) 00161 resultName[self.iSubcase] = self.obj 00162 #print "creating iSubcase result=%s" %(self.iSubcase) 00163 ### 00164 #return self.obj 00165 00166 def readResultsTable(self,table3,table4Data,flag=0): 00167 self.dtMap = {} 00168 tableName = self.readTableName(rewind=False) # OEF 00169 self.tableInit(tableName) 00170 #print "tableName = |%r|" %(tableName) 00171 00172 self.readMarkers([-1,7],tableName) 00173 ints = self.readIntBlock() 00174 #print "*ints = ",ints 00175 00176 self.readMarkers([-2,1,0],tableName) # 7 00177 bufferWords = self.getMarker() 00178 #print "1-bufferWords = ",bufferWords,bufferWords*4 00179 ints = self.readIntBlock() 00180 #print "*ints = ",ints 00181 00182 markerA = -4 00183 markerB = 0 00184 00185 iTable=-3 00186 self.readMarkers([iTable,1,0],tableName) 00187 00188 exitFast = False 00189 while [markerA,markerB]!=[0,2]: 00190 self.isBufferDone = False 00191 #print self.printSection(140) 00192 #print "reading iTable3=%s" %(iTable) 00193 #self.obj = None 00194 00195 ## the results object 00196 self.obj = None 00197 ## dt/loadFactor/frequency/loadStep value (or None for static) 00198 self.nonlinearFactor = None 00199 self.dataCode = {} 00200 00201 n = self.op2.tell() 00202 marker = self.getMarker() 00203 self.goto(n) 00204 if marker != 146: 00205 self.log.debug("marker = %s" %(marker)) 00206 exitFast = True 00207 break 00208 00209 table3(iTable) 00210 self.dataCode['tableName'] = self.tableName 00211 ## developer parameter - Analysis/Table/Format/Sort Codes 00212 self.atfsCode = [self.analysisCode,self.tableCode,self.formatCode,self.sortCode] 00213 #print "self.tellA = ",self.op2.tell() 00214 00215 ## ??? 00216 self.isMarker = False 00217 00218 isBlockDone = self.readTable4(table4Data,flag,iTable-1) 00219 #self.firstPass = False 00220 00221 #print "self.tellB = ",self.op2.tell() 00222 iTable -= 2 00223 #print "isBlockDone = ",isBlockDone 00224 #sys.exit('stopping') 00225 if isBlockDone: 00226 #print "iTable = ",iTable 00227 #self.n = self.markerStart 00228 #self.op2.seek(self.n) 00229 break 00230 ### 00231 n = self.n 00232 #print self.printSection(100) 00233 self.readMarkers([iTable,1,0],tableName) 00234 #self.log.debug("") 00235 #print "i read the markers!!!" 00236 00237 ### 00238 nOld = self.op2.tell() 00239 #try: 00240 if not(exitFast): 00241 #print self.printSection(100000) 00242 self.readMarkers([iTable,1,0],tableName) 00243 #self.getMarker() 00244 #self.getMarker() 00245 #self.getMarker() 00246 #except InvalidMarkersError: 00247 # self.goto(nOld) 00248 #print self.printBlock(self.data) 00249 #print self.printSection(100) 00250 #markerZero = self.getMarker() 00251 #assert markerZero==0 00252 #self.goto(nOld) 00253 #print "finished markerZero" 00254 #return 00255 00256 00257 #print str(self.obj) 00258 if self.makeOp2Debug: 00259 self.op2Debug.write("***end of %s table***\n" %(tableName)) 00260 del self.dtMap 00261 00262 def readTable4(self,table4Data,flag,iTable): 00263 """loops over repeated table -4s""" 00264 #self.readMarkers([iTable,1,0]) 00265 markerA = 4 00266 00267 while markerA is not None: 00268 self.markerStart = copy.deepcopy(self.n) 00269 #self.printSection(180) 00270 self.readMarkers([iTable, 1, 0]) 00271 #print "starting OEF table 4..." 00272 if flag: 00273 isTable4Done,isBlockDone = table4Data(iTable) 00274 else: 00275 isTable4Done,isBlockDone = self.readTable4DataSetup(table4Data,iTable) 00276 if isTable4Done: 00277 #print "done with OEF4" 00278 self.n = self.markerStart 00279 self.op2.seek(self.n) 00280 break 00281 #print "finished reading oef table..." 00282 markerA = self.getMarker('A') 00283 self.n -= 12 00284 self.op2.seek(self.n) 00285 00286 self.n = self.op2.tell() 00287 #print "***markerA = ",markerA 00288 00289 iTable-=1 00290 #print "isBlockDone = ",isBlockDone 00291 ### 00292 #print "isBlockDone = ",isBlockDone 00293 return isBlockDone 00294 00295 def readTable4DataSetup(self,table4Data,iTable): # iTable=-4 00296 """checks to see if table 4 is done, loads the data, and handles skipping""" 00297 isTable4Done = False 00298 isBlockDone = False 00299 00300 bufferWords = self.getMarker(self.tableName) 00301 #print "bufferWords = ",bufferWords 00302 #print len(bufferWords) 00303 self.data = self.readBlock() 00304 #self.printBlock(data) 00305 00306 if bufferWords == 146: # table -4 is done, restarting table -3 00307 isTable4Done = True 00308 return isTable4Done,isBlockDone 00309 elif bufferWords == 0: 00310 #print "bufferWords 0 - done with Table4" 00311 isTable4Done = True 00312 isBlockDone = True 00313 return isTable4Done,isBlockDone 00314 00315 isBlockDone = not(bufferWords) 00316 00317 if self.isValidSubcase(): # lets the user skip a certain subcase 00318 table4Data() 00319 else: 00320 self.log.debug("***skipping table=%s iSubcase=%s" %(self.tableName,self.iSubcase)) 00321 self.skipOES_Element() 00322 ### 00323 return (isTable4Done,isBlockDone) 00324 00325 def updateDtMap(self): 00326 """ 00327 Interfaces with setTransientTimes(times) to limit the amount 00328 of output that is in the transient results. This allows 00329 for models with 1000s of time steps that would otherwise 00330 crash with memory errors to run. Every result may be extracted 00331 if the OP2 is read multiple times. 00332 00333 While not ideal, this function prevents having to drastically 00334 change the code to support large models, which would 00335 make the OP2 reader not as useful for reasonably sized models. 00336 00337 The code works by taking the user-provided array of values 00338 for a given subcase and b/c it's an array can be subtracted 00339 from the current value of dt. Then the minimum absolute value 00340 is found and that is mapped back to the original value. If a 00341 closer value to the target user-specified value is found, the 00342 previous result will be deleted, which keeps memory usage down. 00343 The new result will then be read. If a dt is not closer than 00344 an existing value to a given case, that case will not be read. 00345 """ 00346 #numArray = array([1.,2.]) 00347 iSubcase = self.iSubcase 00348 numArray = self.expectedTimes[iSubcase] 00349 #nums = [0.9,1.11, 1.89,2.1] 00350 num = self.obj.getTransients()[-1] # they're sorted so the last value is the current dt 00351 00352 readCase = True 00353 delta = numArray-num 00354 absDelta = list(abs(delta)) 00355 closest = min(absDelta) 00356 iclose = absDelta.index(closest) 00357 actualValue = numArray[iclose] 00358 00359 if iSubcase not in self.dtMap: 00360 self.dtMap[iSubcase] = {} 00361 if iclose in self.dtMap[iSubcase]: 00362 v1 = self.dtMap[iSubcase][iclose] 00363 vact = get_close_num(v1,num,actualValue) 00364 00365 if vact!=self.dtMap[iSubcase][iclose]: 00366 del self.dtMap[iSubcase][iclose] 00367 self.obj.deleteTransient(v1) 00368 #print "num=%s closest=%s iclose=%s" %(num,actualValue,iclose) 00369 #print "***deleted v1=%s num=%s vact=%s actual=%s" %(v1,num,vact,actualValue) 00370 self.dtMap[iSubcase][iclose] = vact 00371 readCase = True 00372 #print self.dtMap 00373 #print "A" 00374 else: # cleanup previous creation of empty dt case (happened in updateDt outside this function) 00375 readCase = False 00376 #print self.dtMap 00377 #print "num=%s closest=%s iclose=%s" %(num,actualValue,iclose) 00378 #print "B" 00379 self.obj.deleteTransient(num) 00380 ### 00381 else: # read case 00382 self.dtMap[iSubcase][iclose] = num 00383 readCase = True 00384 #print "num=%s closest=%s iclose=%s" %(num,actualValue,iclose) 00385 #print self.dtMap 00386 #print "C" 00387 ### 00388 #print "delta = ",delta,'\n' 00389 #print "readCase = ",readCase 00390 #if num>=0.14: 00391 #print self.obj.getTransients() 00392 #sys.exit('OUG !!!') 00393 00394 return readCase 00395 00396 def handleResultsBufferShort(self,func,debug=False): 00397 raise RuntimeError('this function has been removed...') 00398 nOld = self.n 00399 markers = self.readHeader() 00400 00401 if markers < 0: # not a buffer, the table may be done 00402 self.goto(nOld) 00403 if markers is not None and markers%2==1: 00404 self.isBufferDone = True 00405 else: 00406 data = self.readBlock() 00407 self.data += data 00408 func() 00409 ### 00410 00411 def handleResultsBufferNoRecursion(self,f,debug=False): 00412 """prototype method for getting results without recursion""" 00413 raise RuntimeError('this function has been removed...') 00414 stopBuffer = False 00415 while not(stopBuffer): 00416 f() 00417 nOld = self.n 00418 markers = self.readHeader() 00419 00420 if markers < 0: # not a buffer, the table may be done 00421 self.goto(nOld) 00422 if markers is not None and markers%2==1: 00423 self.isBufferDone = True 00424 else: 00425 data = self.readBlock() 00426 self.data += data 00427 stopBuffer = True 00428 ### 00429 ### 00430 00431 def NotImplementedOrSkip(self,msg=''): 00432 """stops if code is in development and continues otherwise""" 00433 if False: 00434 raise NotImplementedError(msg) 00435 else: 00436 self.log.info("skipping...") 00437 self.log.info("\n"+self.codeInformation()) 00438 self.skipOES_Element() 00439 00440 def handleResultsBuffer3(self, f, resultName, debug=False): 00441 """method for getting results without recursion""" 00442 #if resultName not in self.allowedResultNames: 00443 # return self.self.skipOES_Element() 00444 00445 #stopBuffer = False 00446 i = 0 00447 #print self.codeInformation() 00448 while not(self.isBufferDone): 00449 #print "n=%s len(data)=%s" %(self.n,len(self.data)) 00450 #sys.stdout.flush() 00451 f() 00452 nOld = self.n 00453 markers = self.readHeader() 00454 #print "nOld=%s markers=%s" %(nOld,markers) 00455 00456 if markers<0: # not a buffer, the table may be done 00457 self.goto(nOld) 00458 #print "markers%%2 = %s" %(markers%2) 00459 if markers is not None and markers%2 == 1: 00460 self.isBufferDone = True 00461 else: 00462 data = self.readBlock() 00463 if type(data) != type(self.data): 00464 msg = 'The function f=%s has a str error\n'%(f.__name__) 00465 msg += ("type(self.data)=%s type(data)=%s" 00466 % (type(self.data), type(data))) 00467 sys.stdout.flush() 00468 self.data += data 00469 ### 00470 i += 1 00471 if i == 2000: 00472 raise RuntimeError('Infinite Loop or a really big model...') 00473 #print "isBufferDone=%s" %(self.isBufferDone) 00474 ### 00475 #print "---------------------------------" 00476 00477 def handleResultsBuffer(self, func, debug=False): 00478 """ 00479 Works by knowing that: 00480 the end of an unbuffered table has a 00481 - [4] 00482 the end of an table with a buffer has a 00483 - [4,4,x,4] where x is the next buffer size, which may have another 00484 buffer 00485 the end of the final buffer block has 00486 - nothing! 00487 00488 @param self 00489 the object pointer 00490 @param func 00491 the function to recursively call (the function that called this) 00492 @param debug 00493 developer debug 00494 00495 @note 00496 The code knows that the large buffer is the default size and the 00497 only way there will be a smaller buffer is if there are no more 00498 buffers. So, the op2 is shifted by 1 word (4 bytes) to account for 00499 this end shift. An extra marker value is read, but no big deal. 00500 Beyond that it's just appending some data to the binary string and 00501 calling the function that's passed in 00502 @note 00503 this will soon be replaced by handleResultsBuffer3 (and then 00504 renamed to handleResultsBuffer after this is removed) 00505 00506 @warning 00507 Dont modify this without LOTS of testing. 00508 It's a VERY important function 00509 """ 00510 raise RuntimeError('this function has been removed...') 00511 #print self.obj 00512 #print "len(data) = ",len(self.data) 00513 #if marker[0]==4: 00514 # self.log.debug("found a 4 - end of unbuffered table") 00515 00516 #if debug: 00517 # self.log.debug(self.printSection(120)) 00518 00519 nOld = self.n 00520 #try: 00521 markers = self.readHeader() 00522 #except AssertionError: # end of table - poor catch 00523 # self.goto(nOld) 00524 # self.log.debug(self.printSection(120)) 00525 # return 00526 00527 #print "markers = ",markers 00528 #print self.printSection(160) 00529 00530 if markers < 0: # not a buffer, the table may be done 00531 self.goto(nOld) 00532 if markers is not None and markers%2 == 1: 00533 self.isBufferDone = True 00534 00535 #print self.printSection(120) 00536 #sys.exit('found a marker') 00537 #print 'found a marker' 00538 00539 else: 00540 #print "*******len(self.data)=%s...assuming a buffer block" %(len(self.data)) 00541 #markers = self.readHeader() 00542 #print "markers = ",markers 00543 data = self.readBlock() 00544 #if len(data)<marker: 00545 # self.goto(self.n-4) # handles last buffer not having an extra 4 00546 self.data += data 00547 func() 00548 ### 00549 00550 def readMappedScalarsOut(self, debug=False): 00551 raise RuntimeError('this function must be modified...') 00552 readCase = True 00553 #print "isSort1() = ",self.isSort1() 00554 if self.iSubcase in self.expectedTimes and len(self.expectedTimes[self.iSubcase])>0: 00555 readCase = self.updateDtMap() 00556 00557 if self.obj and readCase and self.isSort1(): 00558 self.readScalarsOut(debug=False) 00559 else: 00560 self.skipOES_Element() 00561 ### 00562 00563 def readScalarsOut(self, debug=False): 00564 """ 00565 reads len(strFormat) values and puts it into the result object 00566 the "o" in readScalars4o means "out" b/c it creates an out tuple 00567 instead of 4 values like readScalars4 00568 @note 00569 nTotal is the number of bytes 00570 strFormat = 'iiii' 00571 """ 00572 raise RuntimeError('this function has been removed...') 00573 data = self.data 00574 #print type(self.obj) 00575 (nTotal,iFormat) = self.obj.getLength() 00576 iFormat = bytes(iFormat) 00577 n = 0 00578 #print "strFormat = ",strFormat 00579 nEntries = len(data)//nTotal 00580 for i in xrange(nEntries): 00581 eData = data[n:n+nTotal] 00582 out = unpack(iFormat,eData) 00583 if debug: 00584 self.log.debug("*out = %s" % (out)) 00585 self.obj.add(out) 00586 n+=nTotal 00587 ### 00588 self.data = data[n:] 00589 #print self.printSection(200) 00590 self.handleResultsBuffer(self.readScalarsOut, debug=False) 00591 00592 def get_close_num(v1, v2, closePoint): 00593 numList = [v1, v2] 00594 delta = array([v1, v2])-closePoint 00595 #print "**delta=%s" %(delta) 00596 absDelta = list(abs(delta)) 00597 closest = min(absDelta) 00598 iclose = absDelta.index(closest) 00599 actualValue = numList[iclose] 00600 return actualValue 00601 00602