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 # pylint: disable=R0904,R0902,C0103 00026 00027 from __future__ import division, print_function 00028 import sys 00029 import copy 00030 from pyNastran.bdf.subcase import Subcase 00031 from pyNastran.bdf.errors import ParamParseError 00032 00033 class CaseControlDeck(object): 00034 def __init__(self, lines, log=None): 00035 """ 00036 @param self 00037 the case control deck object 00038 @param lines 00039 list of lines that represent the case control deck ending with 00040 BEGIN BULK 00041 @param log 00042 a logger object 00043 """ 00044 if log is None: 00045 #if 1: 00046 from pyNastran.general.logger import dummyLogger 00047 word = 'debug' 00048 loggerObj = dummyLogger() 00049 log = loggerObj.startLog(word) # or info 00050 self.debug = False 00051 #self.debug = True 00052 00053 self.log = log 00054 self.lines = lines 00055 self.subcases = {0:Subcase(id=0)} 00056 self._read(self.lines) 00057 00058 def hasParameter(self, iSubcase, paramName): 00059 """@see has_parameter""" 00060 return self.has_parameter(iSubcase, paramName) 00061 00062 def getSubcaseParameter(self, iSubcase, paramName): 00063 """@see get_subcase_parameter""" 00064 return self.get_subcase_parameter(iSubcase, paramName) 00065 00066 def hasSubcase(self, iSubcase): 00067 """@see has_subcase""" 00068 return self.has_subcase(iSubcase) 00069 00070 def createNewSubcase(self, iSubcase): 00071 """@see create_new_subcase""" 00072 self.create_new_subcase(iSubcase) 00073 00074 def deleteSubcase(self, iSubcase): 00075 """@see delete_subcase""" 00076 self.delete_subcase(iSubcase) 00077 00078 def copySubcase(self, iFromSubcase, iToSubcase, overwriteSubcase=True): 00079 """@see copy_subcase""" 00080 self.copy_subcase(iFromSubcase, iToSubcase, overwriteSubcase=True) 00081 00082 def getSubcaseList(self): 00083 """@see get_subcase_list""" 00084 return self.get_subcase_list() 00085 00086 def getLocalSubcaseList(self): 00087 """@see get_local_subcase_list""" 00088 self.get_local_subcase_list() 00089 00090 def updateSolution(self, iSubcase, sol): 00091 """@see update_solution""" 00092 self.update_solution(iSubcase, sol) 00093 00094 def addParameterToGlobalSubcase(self, param): 00095 """@see add_parameter_to_global_subcase""" 00096 self.add_parameter_to_global_subcase(param) 00097 00098 def addParameterToLocalSubcase(self, iSubcase, param): 00099 """@see add_parameter_to_local_subcase""" 00100 self.add_parameter_to_local_subcase(iSubcase, param) 00101 00102 #----------------------- 00103 def has_parameter(self, iSubcase, paramName): 00104 if self.hasSubcase(iSubcase): 00105 return self.subcases[iSubcase].hasParameter(paramName.upper()) 00106 00107 def get_subcase_parameter(self, iSubcase, paramName): 00108 if self.hasSubcase(iSubcase): 00109 return self.subcases[iSubcase].getParameter(paramName.upper()) 00110 raise RuntimeError('iSubcase=%s does not exist...' %(iSubcase)) 00111 00112 def has_subcase(self, iSubcase): 00113 """ 00114 Checks to see if a subcase exists. 00115 @param self the case control deck object 00116 @param iSubcase the subcase ID 00117 @retval does_subcase_exist (type = bool) 00118 """ 00119 if iSubcase in self.subcases: 00120 return True 00121 return False 00122 00123 def create_new_subcase(self, iSubcase): 00124 """ 00125 @warning 00126 be careful you dont add data to the global subcase after running 00127 this...is this True??? 00128 """ 00129 if self.hasSubcase(iSubcase): 00130 sys.stderr.write('subcase=%s already exists...skipping\n' %(iSubcase)) 00131 self.copy_subcase(iFromSubcase=0, iToSubcase=iSubcase,overwriteSubcase=True) 00132 #self.subcases[iSubcase] = Subcase(id=iSubcase) 00133 00134 def delete_subcase(self, iSubcase): 00135 if not self.hasSubcase(iSubcase): 00136 sys.stderr.write('subcase %s doesnt exist...skipping\n' %(iSubcase)) 00137 del self.subcases[iSubcase] 00138 00139 def copy_subcase(self, iFromSubcase, iToSubcase, overwriteSubcase=True): 00140 """ 00141 overwrites the parameters from one subcase to another 00142 @param self the case control deck object 00143 @param iFromSubcase the subcase to pull the data from 00144 @param iToSubcase the subcase to map the data to 00145 @param overwriteSubcase NULLs iToSubcase before copying iFromSubcase 00146 """ 00147 if not self.hasSubcase(iFromSubcase): 00148 msg = 'iFromSubcase=|%s| does not exist' %(iFromSubcase) 00149 raise RuntimeError(msg) 00150 subcaseFrom = self.subcases[iFromSubcase] 00151 if overwriteSubcase: 00152 subcaseTo = copy.deepcopy(subcaseFrom) 00153 subcaseTo.id = iToSubcase 00154 self.subcases[iToSubcase] = subcaseTo 00155 else: 00156 if not self.has_subcase(iToSubcase): 00157 msg = 'iToSubcase=|%s| does not exist' %(iToSubcase) 00158 raise RuntimeError(msg) 00159 subcaseTo = self.subcases[iToSubcase] 00160 for key,param in subcaseFrom.iteritems(): 00161 subcaseTo[key] = copy.deepcopy(param) 00162 ### 00163 ### 00164 00165 def get_subcase_list(self): 00166 return sorted(self.subcases.keys()) 00167 00168 def get_local_subcase_list(self): 00169 keyList = [key for key in self.subcases if key != 0] # skip the global 00170 return sorted(keyList) 00171 00172 def update_solution(self, iSubcase, sol): 00173 """sol = STATICS, FLUTTER, MODAL, etc.""" 00174 self.add_parameter_to_local_subcase(iSubcase, 'ANALYSIS %s' %(sol)) 00175 00176 def add_parameter_to_global_subcase(self, param): 00177 """ 00178 takes in a single-lined string 00179 @note 00180 dont worry about overbounding the line 00181 """ 00182 (j, key, value, options, paramType) = self._parse_data_from_user(param) 00183 subcaseList = self.get_subcase_list() 00184 for iSubcase in subcaseList: 00185 self._add_parameter_to_subcase(key, value, options, paramType, 00186 iSubcase) 00187 00188 def add_parameter_to_local_subcase(self, iSubcase, param): 00189 (j, key, value, options, paramType) = self._parse_data_from_user(param) 00190 self._addParameterToSubcase(key, value, options, paramType, iSubcase) 00191 00192 def _parse_data_from_user(self, param): 00193 if '\n' in param or '\r' in param or '\t' in param: 00194 msg = 'doesnt support embedded endline/tab characters\n' 00195 msg += ' param = |%r|' %(param) 00196 raise SyntaxError(msg) 00197 #self.read([param]) 00198 lines = self.clean_lines([param]) 00199 (j, key, value, options, paramType) = self._parse_entry(lines) 00200 return (j, key, value, options, paramType) 00201 00202 def _clean_lines(self, lines): 00203 """removes comment characters $""" 00204 lines2 = [] 00205 for line in lines: 00206 line = line.strip(' \n\r').split('$')[0].rstrip() 00207 if line: 00208 lines2.append(line) 00209 ### 00210 ### 00211 #for line in lines2: 00212 #print "L2 = ",line 00213 return lines2 00214 00215 def _read(self, lines): 00216 """ 00217 reads the case control deck 00218 @note supports comment lines 00219 @warning 00220 doesnt check for 72 character width lines, but will follow that 00221 when it's written out 00222 """ 00223 iSubcase = 0 00224 lines = self._clean_lines(lines) 00225 i = 0 00226 while i < len(lines): 00227 line = lines[i] 00228 #print "rawLine = |%s|" %(line) 00229 #self.log.debug("rawLine = |%r|" %(line)) 00230 00231 lines2 = [line] 00232 while ',' in lines[i][-1]: 00233 #print "lines[%s] = %s" %(i,lines[i]) 00234 i+=1 00235 lines2.append(lines[i]) 00236 if i>10000: 00237 msg = 'There are too many lines in case control deck.\n' 00238 msg += 'Assuming an infinite loop was found.' 00239 raise RuntimeError(msg) 00240 (j, key, value, options, paramType) = self._parse_entry(lines2) 00241 i+=1 00242 #print "" 00243 #print "key=|%s| value=|%s| options=|%s| paramType=%s" %(key,value,options,paramType) 00244 iSubcase = self._addParameterToSubcase(key, value, options, paramType, iSubcase) 00245 #print "--------------" 00246 if i == 10000: 00247 msg = 'too many lines in Case Control Deck < 10000...' 00248 raise RuntimeError(msg) 00249 ### 00250 #print "done with while loop...\n" 00251 00252 #print str(self) 00253 self.finish_subcases() 00254 ### 00255 00256 def _parse_entry(self, lines): 00257 """ 00258 @brief 00259 internal method for parsing a card of the case control deck 00260 00261 parses a single case control deck card into 4 sections 00262 1. paramName - obvious 00263 2. Value - still kind of obvious 00264 3. options - rarely used data 00265 4. paramType - STRESS-type, SUBCASE-type, PARAM-type, SET-type, BEGIN_BULK-type 00266 00267 It's easier with examples: 00268 00269 paramType = SUBCASE-type 00270 SUBCASE 1 -> paramName=SUBCASE value=1 options=[] 00271 paramType = STRESS-type 00272 STRESS = ALL -> paramName=STRESS value=ALL options=[] 00273 STRAIN(PLOT) = 5 -> paramName=STRAIN value=5 options=[PLOT] 00274 TITLE = stuff -> paramName=TITLE value=stuff options=[] 00275 paramType = SET-type 00276 SET 1 = 10,20,30 -> paramName=SET value=[10,20,30] options = 1 00277 paramType = BEGIN_BULK-type 00278 BEGIN BULK -> paramName=BEGIN value=BULK options = [] 00279 paramType = CSV-type 00280 PARAM,FIXEDB,-1 -> paramName=PARAM value=FIXEDB options = [-1] 00281 00282 The paramType is the "macro" form of the data (similar to integer, float, string). 00283 The value is generally whats on the RHS of the equals sign (assuming it's there). 00284 Options are modifiers on the data. Form things like the PARAM card or the SET card 00285 they arent as clear, but the paramType lets the program know how to format it 00286 when writing it out. 00287 00288 @param self the case control deck object 00289 @param lines list of lines 00290 @retval paramName see brief 00291 @retval value see brief 00292 @retval options see brief 00293 @retval paramType see brief 00294 """ 00295 i = 0 00296 options = [] 00297 value = None 00298 key = None 00299 paramType = None 00300 00301 line = lines[i] 00302 #print line 00303 #print "*****lines = ", lines 00304 00305 equalsCount = 0 00306 for letter in line: 00307 if letter == '=': 00308 equalsCount += 1 00309 lineUpper = line.upper() 00310 00311 if lineUpper.startswith('SUBCASE'): 00312 #print "line = |%r|" %(line) 00313 line2 = line.replace('=', '') 00314 sline = line2.split() 00315 if len(sline) != 2: 00316 msg = "trying to parse |%s|..." %(line) 00317 raise RuntimeError(msg) 00318 (key, iSubcase) = sline 00319 #print "key=|%s| iSubcase=|%s|" %(key,iSubcase) 00320 value = int(iSubcase) 00321 #self.iSubcase = int(iSubcase) 00322 paramType = 'SUBCASE-type' 00323 elif (lineUpper.startswith('LABEL') or lineUpper.startswith('SUBTITLE') 00324 or lineUpper.startswith('TITLE')): 00325 try: 00326 eIndex = line.index('=') 00327 except: 00328 msg = "cannot find an = sign in LABEL/SUBTITLE/TITLE line\n" 00329 msg += "line = |%s|" %(lineUpper.strip()) 00330 raise RuntimeError(msg) 00331 00332 key = line[0:eIndex].strip() 00333 value = line[eIndex+1:].strip() 00334 options = [] 00335 paramType = 'STRING-type' 00336 elif equalsCount == 1: # STRESS 00337 if '=' in line: 00338 (key, value) = line.strip().split('=') 00339 else: 00340 msg = 'expected item of form "name = value" line=|%r|' %(line.strip()) 00341 raise RuntimeError(msg) 00342 00343 key = key.strip() 00344 value = value.strip() 00345 if self.debug: 00346 self.log.debug("key=|%s| value=|%s|" %(key, value)) 00347 paramType = 'STRESS-type' 00348 00349 if '(' in key: # comma may be in line - STRESS-type 00350 #paramType = 'STRESS-type' 00351 sline = key.strip(')').split('(') 00352 key = sline[0] 00353 options = sline[1].split(',') 00354 00355 # handle TEMPERATURE(INITIAL) and TEMPERATURE(LOAD) cards 00356 if key == 'TEMPERATURE' or key == 'TEMP': 00357 key = 'TEMPERATURE(%s)' %(options[0]) 00358 options = [] 00359 #print "key=|%s| options=%s" %(key,options) 00360 00361 elif ' ' in key and ',' in value: # SET-type 00362 (key, ID) = key.split() 00363 key = key+' '+ID 00364 00365 if self.debug: 00366 self.log.debug('SET-type key=%s ID=%s' %(key, ID)) 00367 fivalues = value.rstrip(' ,').split(',') # float/int values 00368 00369 ## @todo should be more efficient multiline reader... 00370 # read more lines.... 00371 if line[-1].strip() == ',': 00372 i+=1 00373 #print "rawSETLine = |%r|" %(lines[i]) 00374 while 1: 00375 if ','== lines[i].strip()[-1]: 00376 fivalues += lines[i][:-1].split(',') 00377 else: # last case 00378 fivalues += lines[i].split(',') 00379 #print "fivalues last = i=%s |%r|" %(i,lines[i]) 00380 i+=1 00381 break 00382 i+=1 00383 ### 00384 ### 00385 #print "len(fivalues) = ",len(fivalues) 00386 value = fivalues 00387 00388 options = ID # needed a place to put it... 00389 paramType = 'SET-type' 00390 elif ',' in value: # STRESS-type; special TITLE = stuffA,stuffB 00391 #print 'A ??? line = ',line 00392 #raise RuntimeError(line) 00393 pass 00394 else: # STRESS-type; TITLE = stuff 00395 #print 'B ??? line = ',line 00396 pass 00397 ### 00398 ### = in line 00399 elif lineUpper.startswith('BEGIN'): # begin bulk 00400 try: 00401 (key, value) = lineUpper.split(' ') 00402 except: 00403 msg = 'excepted "BEGIN BULK" found=|%r|' %(line) 00404 raise RuntimeError(msg) 00405 paramType = 'BEGIN_BULK-type' 00406 elif 'PARAM' in lineUpper: # param 00407 sline = line.split(',') 00408 if len(sline) != 3: 00409 raise ParamParseError("trying to parse |%s|..." %(line)) 00410 (key, value, options) = sline 00411 ### 00412 paramType = 'CSV-type' 00413 elif ' ' not in line: 00414 key = line.strip() 00415 value = line.strip() 00416 options = None 00417 paramType = 'KEY-type' 00418 else: 00419 msg = 'generic catch all...line=|%r|' %(line) 00420 #print 'C ??? line = ',line 00421 #raise RuntimeError(msg) 00422 key = '' 00423 value = line 00424 options = None 00425 paramType = 'KEY-type' 00426 ### 00427 i+=1 00428 #print "done with ",key 00429 return (i, key, value, options, paramType) 00430 00431 def finish_subcases(self): 00432 """ 00433 removes any unwanted data in the subcase...specifically the SUBCASE 00434 data member. Otherwise it will print out after a key like stress. 00435 """ 00436 for (iSubcase, subcase) in sorted(self.subcases.iteritems()): 00437 subcase.finish_subcase() 00438 ### 00439 ### 00440 00441 def _addParameterToSubcase(self, key, value, options, paramType, iSubcase): 00442 """internal method""" 00443 if self.debug: 00444 a = 'key=|%s|' %(key) 00445 b = 'value=|%s|' %(value) 00446 c = 'options=|%s|' %(options) 00447 d = 'paramType=|%s|' %(paramType) 00448 msg = "_adding iSubcase=%s %-12s %-12s %-12s %-12s" %(iSubcase, a, 00449 b, c, d) 00450 self.log.debug(msg) 00451 00452 if key == 'SUBCASE': 00453 assert value not in self.subcases 00454 assert isinstance(value, int) 00455 iSubcase = value 00456 #print "value=", value 00457 self.copy_subcase(iFromSubcase=0, iToSubcase=iSubcase, 00458 overwriteSubcase=True) 00459 if self.debug: 00460 msg = "copied subcase iFromSubcase=%s to iToSubcase=%s" %(0, iSubcase) 00461 self.log.debug(msg) 00462 elif iSubcase not in self.subcases: # initialize new subcase 00463 #self.iSubcase += 1 # is handled in the read code 00464 msg = 'iSubcase=%s is not a valid subcase...' %(iSubcase) 00465 raise RuntimeError(msg) 00466 00467 subcase = self.subcases[iSubcase] 00468 subcase._add_data(key,value,options,paramType) 00469 00470 #print "\n%s\n" %(self.subcases[iSubcase]) 00471 return iSubcase 00472 00473 #def __str__(self): 00474 # return self.__repr__() 00475 00476 def crossReference(self, model): 00477 for (iSubcase, subcase) in sorted(self.subcases.iteritems()): 00478 subcase.crossReference(model) 00479 00480 def get_op2_data(self): 00481 """ 00482 returns the relevant op2 parameters required for a given subcase 00483 """ 00484 cases = {} 00485 for (iSubcase, subcase) in sorted(self.subcases.iteritems()): 00486 if iSubcase != 0: 00487 cases[iSubcase] = subcase.getOp2Data(self.sol, 00488 self.solmap_toValue) 00489 return cases 00490 00491 def __repr__(self): 00492 msg = '' 00493 subcase0 = self.subcases[0] 00494 for (iSubcase, subcase) in sorted(self.subcases.iteritems()): 00495 #if iSubcase==0: 00496 #print("iSubcase = %s" %(iSubcase)) 00497 #print(subcase) 00498 #print("********") 00499 msg += subcase.write_subcase(subcase0) 00500 #msg += str(subcase) 00501 #else: 00502 # msg += subcase.writeSubcase(subcase0) 00503 #print "\n" 00504 #break 00505 if len(self.subcases) == 1: 00506 msg += 'BEGIN BULK\n' 00507 #print msg 00508 return msg 00509 ### 00510 ### 00511 00512 #def parseParam(self,param): 00513 # """ 00514 # @warning doesnt support comment characters 00515 # """ 00516 # param = param.substr('\n','').substr('\r','') # remove line endings 00517 # parse(param) 00518 # #param2 = [''.join(param)] 00519 # #print 'param2 = ',param2 00520 00521 def test1(): 00522 lines = ['SPC=2', 00523 'MPC =3', 00524 'STRESS= ALL', 00525 'DISPLACEMENT(PLOT,PUNCH) = 8',] 00526 deck = CaseControlDeck(lines) 00527 print("has SPC True = %s" %(deck.has_parameter(0, 'SPC'))) 00528 print("has sPC True = %s" %(deck.has_parameter(0, 'sPC'))) 00529 print("has junk False = %s" %(deck.has_parameter(0, 'JUNK'))) 00530 00531 print("getSubcaseParameter(MPC) 3 = ", deck.get_subcase_parameter(0, 'MPC')) 00532 deck.add_parameter_to_global_subcase('GPFORCE = 7') 00533 print("") 00534 print(deck) 00535 deck.create_new_subcase(1) 00536 deck.create_new_subcase(2) 00537 print(deck) 00538 00539 deck.addParameterToLocalSubcase(1, 'STRAIN = 7') 00540 #print "-----added----" 00541 00542 out = deck.getSubcaseParameter(0, 'GPFORCE') 00543 print("getSubcaseParameter(STRAIN) 7 = %s" %(out)) 00544 00545 deck.addParameterToLocalSubcase(1, 'ANALYSIS = SAERO') 00546 deck.addParameterToLocalSubcase(2, 'ANALYSIS = STATIC') 00547 print("-----added2----") 00548 out = deck.getSubcaseParameter(2, 'ANALYSIS') 00549 print("getSubcaseParameter(ANALYSIS) = %s" %(out)) 00550 00551 00552 deck.addParameterToLocalSubcase(1, 'SET 1 = 100') 00553 deck.addParameterToLocalSubcase(1, 'SET 2 = 200') 00554 print(deck) 00555 00556 if __name__=='__main__': 00557 test1() 00558 lines = [ 00559 'SUBCASE 1', 00560 ' ACCELERATION(PLOT,PRINT,PHASE) = ALL', 00561 ' DISPLACEMENT(PLOT,PRINT,PHASE) = ALL', 00562 ' DLOAD = 32', 00563 ' M2GG = 111', 00564 ' SET 88 = 5, 6, 7, 8, 9, 10 THRU 55 EXCEPT 15, 16, 77, 78, 79, 100 THRU 300', 00565 ' SET 99 = 1 THRU 10', 00566 ' SET 105 = 1.009, 10.2, 13.4, 14.0, 15.0', 00567 ' SET 111 = MAAX1,MAAX2', 00568 ' SET 1001 = 101/T1, 501/T3, 991/R3', 00569 ' SET = ALL', 00570 ' SPC = 42', 00571 ' TSTEPNL = 22', 00572 ' VELOCITY(PLOT,PRINT,PHASE) = ALL', 00573 'BEGIN BULK', 00574 ] 00575 deck = CaseControlDeck(lines) 00576 deck.createNewSubcase(2) 00577 deck.addParameterToLocalSubcase(1,'SET 2 = 11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,1000000000000000000000000000000000000000000000000000000,33') 00578 print(deck+'\n\n') 00579 00580 deck2 = CaseControlDeck(['ACCELERATION(PLOT,PRINT,PHASE) = ALL', 00581 'DISPLACEMENT(PLOT,PRINT,PHASE) = ALL', 00582 'BEGIN BULK']) 00583 print('\n\n'+deck2)