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 00026 00027 from __future__ import division, print_function 00028 import sys 00029 from itertools import izip 00030 00031 from pyNastran.bdf.fieldWriter import (printCard, set_default_if_blank, 00032 is_same) 00033 from pyNastran.bdf.bdfInterface.BDF_Card import BDFCard 00034 00035 class BaseCard(BDFCard): 00036 #def __init__(self,card): 00037 # pass 00038 00039 def writeCodeAster(self): 00040 return '# skipping %s because writeCodeAster is not implemented\n' %(self.type) 00041 00042 def writeCodeAsterLoad(self, model, gridWord='node'): 00043 return '# skipping %s (lid=%s) because writeCodeAsterLoad is not implemented\n' %(self.type, self.lid) 00044 00045 def verify(self, model, iSubcase): 00046 """ 00047 this method checks performs checks on the cards such as 00048 that the PBEAML has a proper material type 00049 """ 00050 pass 00051 00052 def isSameFields(self, fields1, fields2): 00053 for (field1, field2) in izip(fields1, fields2): 00054 if not is_same(field1, field2): 00055 return False 00056 ### 00057 ### 00058 return True 00059 00060 def Is(self, typeCheck): 00061 """retruns True if the card type is the same as the object""" 00062 if self.type==typeCheck: 00063 return True 00064 return False 00065 00066 def removeTrailingNones(self, fields): 00067 """removes blank fields at the end of a card object""" 00068 self._wipeEmptyFields(fields) 00069 00070 def printCard(self, fields, tol=0.): 00071 """prints a card object""" 00072 #print "fields = ",fields 00073 return printCard(fields, tol) 00074 00075 def crossReference(self, model): 00076 #self.mid = model.Material(self.mid) 00077 msg = "%s needs to implement the 'crossReference' method" %(self.type) 00078 raise NotImplementedError(msg) 00079 00080 def buildTableLines(self, fields, nStart=1, nEnd=0): 00081 """ 00082 builds a table of the form: 00083 'DESVAR' DVID1 DVID2 DVID3 DVID4 DVID5 DVID6 DVID7 00084 DVID8 -etc.- 00085 'UM' VAL1 VAL2 -etc.- 00086 and then pads the rest of the fields with None's 00087 @param fields the fields to enter, including DESVAR 00088 @param nStart the number of blank fields at the start of the line (default=1) 00089 @param nEnd the number of blank fields at the end of the line (default=0) 00090 00091 @note will be used for DVPREL2, RBE1, RBE3 00092 @warning only works for small field format??? 00093 """ 00094 fieldsOut = [] 00095 n = 8-nStart-nEnd 00096 00097 # pack all the fields into a list. Only consider an entry as isolated 00098 for (i,field) in enumerate(fields): 00099 fieldsOut.append(field) 00100 if i>0 and i%n==0: # beginning of line 00101 #print "i=%s" %(i) 00102 #pad = [None]*(i+j) 00103 #fieldsOut += pad 00104 fieldsOut += [None]*(nStart+nEnd) 00105 ### 00106 ### 00107 # make sure they're aren't any trailing None's (from a new line) 00108 fieldsOut = self._wipeEmptyFields(fieldsOut) 00109 #print "fieldsOut = ",fieldsOut,len(fieldsOut) 00110 00111 # push the next key (aka next fields[0]) onto the next line 00112 nSpaces = 8-(len(fieldsOut))%8 # puts UM onto next line 00113 #print "nSpaces[%s] = %s max=%s" %(fields[0],nSpaces,nSpaceMax) 00114 if nSpaces<8: 00115 fieldsOut += [None]*(nSpaces) 00116 #print "" 00117 return fieldsOut 00118 00119 def isSameCard(self, card, debug=False): 00120 fields1 = self.rawFields() 00121 fields2 = card.rawFields() 00122 if debug: 00123 print("fields1=%s fields2=%s" %(fields1, fields2)) 00124 return self.isSameFields(fields1, fields2) 00125 00126 def printRawFields(self): 00127 """A card's raw fields include all defaults for all fields""" 00128 fields = self.rawFields() 00129 return self.printCard(fields) 00130 00131 def reprFields(self): 00132 return self.rawFields() 00133 00134 def __repr__(self): # ,tol=1e-8 00135 """ 00136 Prints a card in the simplest way possible 00137 (default values are left blank). 00138 """ 00139 #print "tol = ",tol 00140 fields = self.reprFields() 00141 try: 00142 return self.printCard(fields) 00143 except: 00144 print('problem printing %s card' %(self.type)) 00145 print("fields = ",fields) 00146 raise 00147 00148 def Mid(self): 00149 #print str(self) 00150 if isinstance(self.mid,int): 00151 return self.mid 00152 else: 00153 return self.mid.mid 00154 ### 00155 00156 class Property(BaseCard): 00157 def __init__(self, card, data): 00158 assert card is None or data is None 00159 pass 00160 00161 def Mid(self): 00162 return Mid(self) 00163 00164 def isSameCard(self, prop, debug=False): 00165 if self.type!=prop.type: return False 00166 fields1 = self.rawFields() 00167 fields2 = prop.rawFields() 00168 if debug: 00169 print("fields1=%s fields2=%s" %(fields1, fields2)) 00170 return self.isSameFields(fields1, fields2) 00171 00172 def crossReference(self, model): 00173 self.mid = model.Material(self.mid) 00174 00175 class Material(BaseCard): 00176 """Base Material Class""" 00177 def __init__(self, card, data): 00178 pass 00179 #self.type = card[0] 00180 00181 def isSameCard(self, mat, debug=False): 00182 if self.type!=mat.type: return False 00183 fields1 = self.rawFields() 00184 fields2 = mat.rawFields() 00185 if debug: 00186 print("fields1=%s fields2=%s" %(fields1, fields2)) 00187 return self.isSameFields(fields1, fields2) 00188 00189 def crossReference(self, model): 00190 pass 00191 00192 def Mid(self): 00193 return self.mid 00194 00195 class Element(BaseCard): 00196 pid = 0 # CONM2, rigid 00197 def __init__(self, card, data): 00198 assert card is None or data is None 00199 ## the list of node IDs for an element (default=None) 00200 self.nodes = None 00201 #self.nids = [] 00202 pass 00203 00204 def isSameCard(self, element, debug=False): 00205 if self.type!=element.type: return False 00206 fields1 = self.rawFields() 00207 fields2 = element.rawFields() 00208 if debug: 00209 print("fields1=%s fields2=%s" %(fields1, fields2)) 00210 return self.isSameFields(fields1, fields2) 00211 00212 def Pid(self): 00213 """returns the property ID of an element""" 00214 if isinstance(self.pid, int): 00215 return self.pid 00216 else: 00217 return self.pid.pid 00218 ### 00219 00220 def nodePositions(self, nodes=None): 00221 """returns the positions of multiple node objects""" 00222 if not nodes: 00223 nodes = self.nodes 00224 return [node.Position() for node in nodes] 00225 00226 def nodeIDs(self,nodes=None, allowEmptyNodes=False, msg=''): 00227 """returns nodeIDs for repr functions""" 00228 try: 00229 if not nodes: 00230 nodes = self.nodes 00231 00232 if allowEmptyNodes: 00233 nodes2 = [] 00234 for i,node in enumerate(nodes): 00235 if node==0 or node is None: 00236 nodes2.append(None) 00237 elif isinstance(node, int): 00238 nodes2.append(node) 00239 else: 00240 nodes2.append(node.nid) 00241 return nodes2 00242 else: 00243 if isinstance(nodes[0], int): 00244 nodeIDs = [node for node in nodes] 00245 else: 00246 nodeIDs = [node.nid for node in nodes] 00247 ### 00248 assert 0 not in nodeIDs, 'nodeIDs = %s' %(nodeIDs) 00249 return nodeIDs 00250 except: 00251 print("nodes=%s allowEmptyNodes=%s\nmsg=%s" %(nodes, allowEmptyNodes, msg)) 00252 raise 00253 00254 def prepareNodeIDs(self, nids, allowEmptyNodes=False): 00255 """verifies all node IDs exist and that they're integers""" 00256 self.nodes = [] 00257 for nid in nids: 00258 if isinstance(nid, int): 00259 self.nodes.append(int(nid)) 00260 elif nid==None and allowEmptyNodes: 00261 self.nodes.append(nid) 00262 else: # string??? 00263 self.nodes.append(int(nid)) 00264 #raise RuntimeError('this element may not have missing nodes...nids=%s allowEmptyNodes=False' %(nids)) 00265 ### 00266 00267 #def Normal(self,a,b): 00268 # """finds the unit normal vector of 2 vectors""" 00269 # return Normal(a,b) 00270 00271 def CentroidTriangle(self, n1, n2, n3, debug=False): 00272 if debug: 00273 print("n1=%s \nn2=%s \nn3=%s" %(n1, n2, n3)) 00274 centroid = (n1+n2+n3)/3. 00275 return centroid 00276 00277 def Centroid(self): 00278 msg = 'Centroid not implemented in the %s class' %(self.__class__.__name__) 00279 raise NotImplementedError(msg) 00280 def Length(self): 00281 msg = 'Length not implemented in the %s class' %(self.__class__.__name__) 00282 raise NotImplementedError(msg) 00283 def Area(self): 00284 msg = 'Area not implemented in the %s class' %(self.__class__.__name__) 00285 raise NotImplementedError(msg) 00286 def Volume(self): 00287 msg = 'Volume not implemented in the %s class' %(self.__class__.__name__) 00288 raise NotImplementedError(msg) 00289 def Mass(self): 00290 msg = 'Mass not implemented in the %s class' %(self.__class__.__name__) 00291 raise NotImplementedError(msg) 00292 00293 def B(self): 00294 msg = 'B matrix not implemented in the %s class' %(self.__class__.__name__) 00295 raise NotImplementedError(msg) 00296 def D(self): 00297 msg = 'D matrix not implemented in the %s class' %(self.__class__.__name__) 00298 raise NotImplementedError(msg) 00299 def Jacobian(self): 00300 msg = 'Jacobian not implemented for %s' %(self.self.__class__.__name__) 00301 raise NotImplementedError(msg) 00302 00303 def stiffnessMatrix(self): 00304 msg = 'stiffnessMatrix not implemented in the %s class' %(self.__class__.__name__) 00305 raise NotImplementedError(msg) 00306 def massMatrix(self): 00307 msg = 'massMatrix not implemented in the %s class' %(self.__class__.__name__) 00308 raise NotImplementedError(msg) 00309 00310 00311 def expandThru(fields): 00312 """ 00313 expands a list of values of the form [1,5,THRU,9,13] 00314 to be [1,5,6,7,8,9,13] 00315 """ 00316 if len(fields) == 1: 00317 return fields 00318 #print("expandThru") 00319 #print("fields = ", fields) 00320 out = [] 00321 nFields = len(fields) 00322 i=0 00323 while(i<nFields): 00324 if fields[i] == 'THRU': 00325 for j in xrange(fields[i-1], fields[i+1]+1): 00326 out.append(j) 00327 ### 00328 i+=2 00329 else: 00330 out.append(fields[i]) 00331 i+=1 00332 ### 00333 ### 00334 #print "out = ",out,'\n' 00335 return list(set(out)) 00336 00337 def expandThruBy(fields): 00338 """ 00339 expands a list of values of the form [1,5,THRU,9,BY,2,13] 00340 to be [1,5,7,9,13] 00341 @todo not tested 00342 @note used for QBDY3, ??? 00343 """ 00344 if len(fields) == 1: 00345 return fields 00346 #print "expandThruBy" 00347 #print "fields = ",fields 00348 out = [] 00349 nFields = len(fields) 00350 i=0 00351 by = 1 00352 while(i<nFields): 00353 if fields[i] == 'THRU': 00354 by = 1 00355 if i+2<nFields and fields[i+2] == 'BY': 00356 by = fields[i+3] 00357 #sys.stderr.write("BY was found...untested...") 00358 #raise NotImplementedError('implement BY support\nfields=%s' %(fields)) 00359 else: 00360 by = 1 00361 minValue = fields[i-1] 00362 maxValue = fields[i+1] 00363 #print "minValue=%s maxValue=%s by=%s" %(minValue,maxValue,by) 00364 for j in xrange(0,(maxValue-minValue)//by+1): # +1 is to include final point 00365 value = minValue+by*j 00366 out.append(value) 00367 ### 00368 if by==1: # null/standard case 00369 i+=2 00370 else: # BY case 00371 i+=3 00372 ### 00373 else: 00374 out.append(fields[i]) 00375 i+=1 00376 ### 00377 ### 00378 #out = list(set(out)) 00379 #out.sort() 00380 #print "out = ",out,'\n' 00381 return list(set(out)) 00382 00383 def expandThruExclude(self, fields): 00384 """ 00385 expands a list of values of the form [1,5,THRU,11,EXCEPT,7,8,13] 00386 to be [1,5,6,9,10,11,13] 00387 @todo not tested 00388 """ 00389 fieldsOut = [] 00390 nFields = len(fields) 00391 for i in xrange(nFields): 00392 if fields[i] == 'THRU': 00393 storedList = [] 00394 for j in xrange(fields[i-1], fields[i+1]): 00395 storedList.append(fields[j]) 00396 ### 00397 elif fields[i] == 'EXCLUDE': 00398 storedSet = set(storedList) 00399 while fields[i] < max(storedList): 00400 storedSet.remove(fields[i]) 00401 storedList = list(storedSet) 00402 else: 00403 if storedList: 00404 fieldsOut += storedList 00405 fieldsOut.append(fields[i]) 00406 ### 00407 ### 00408 00409 def collapseThru(fields): 00410 return fields 00411 00412 def collapseThruBy(fields): 00413 return fields 00414 00415 def _collapseThru(fields): 00416 """ 00417 1,THRU,10 00418 1,3,THRU,19,15 00419 @warning doesnt work 00420 """ 00421 fields = list(set(fields)) 00422 00423 #assumes sorted... 00424 00425 dnMax = 1 00426 (pre, i) = _preCollapse(fields, dnMax=dnMax) 00427 mid = _midCollapse(pre, dnMax=dnMax) 00428 #out = self._postCollapse(mid) 00429 00430 out = [] 00431 print("running post...") 00432 for data in mid: 00433 print("data = %s" %(data)) 00434 nData = len(data) 00435 if nData == 1: 00436 out.append(data[0]) # 1 field only 00437 else: 00438 assert data[2] == 1 # dn 00439 out += [data[0], 'THRU', data[1]] 00440 ### 00441 ### 00442 print("dataOut = ",out) 00443 return out 00444 00445 def _midCollapse(preCollapse, dnMax=10000000): 00446 """ 00447 input is lists of [[1,3,5,7],2] dn=2 00448 dNmax = 2 00449 output is [1,7,2] 00450 """ 00451 out = [] 00452 print(preCollapse) 00453 for collapse in preCollapse: 00454 print("collapse = ", collapse) 00455 (data,dn) = collapse 00456 print("data = ",data) 00457 print("dn = ",dn) 00458 if len(data)>1: 00459 if dn<=dnMax: # 1:11:2 - patran syntax 00460 fields = [data[0], data[-1], dn] 00461 out.append(fields) 00462 ### 00463 else: # bigger than dn 00464 for field in data: 00465 out.append(field) 00466 ### 00467 ### 00468 else: # 1 item 00469 out.append([data[0]]) 00470 ### 00471 return out 00472 00473 def _preCollapse(fields, dnMax=10000000): # assumes sorted 00474 out = [] 00475 nFields = len(fields)-1 00476 i=0 00477 while(i<nFields): 00478 dn = fields[i+1]-fields[i] 00479 print("preFields = %s" %(fields[i:])) 00480 (outFields,j) = _subCollapse(fields[i:], dn, dnMax) 00481 print("outFields = %s" %(outFields)) 00482 out.append([outFields, dn]) 00483 i+=j 00484 ### 00485 #if i==nFields+1: 00486 # out.append([[fields[i-1]],1]) 00487 # print("lastOut = ",out[-1]) 00488 ### 00489 ### 00490 00491 print("i=%s out=%s" %(i,out)) 00492 print("--end of preCollapse") 00493 return (out, i) 00494 00495 def _subCollapse(fields, dn, dnMax=10000000): 00496 """ 00497 in = [1,2,3, 7] 00498 out = [1,2,3] 00499 """ 00500 # dn=1 00501 print("subIn = %s" %(fields)) 00502 out = [fields[0]] 00503 nFields = len(fields) 00504 00505 for i in xrange(1, nFields): 00506 dn = fields[i]-fields[i-1] 00507 print("i=%s field[%s]=%s fields[%s]=%s dn=%s dnMax=%s" % (i, i, fields[i], i-1, fields[i-1], dn, dnMax)) 00508 if dn != dnMax: 00509 #i += 1 00510 #out.append(fields[i]) 00511 break 00512 out.append(fields[i]) 00513 #i -= 1 00514 print("subOut = %s" %(out)) 00515 #i += 1 00516 print("iSubEnd = %s\n" %(i)) 00517 return (out,i) 00518 ### 00519 00520 00521 #dnMax = 2 00522 if __name__=='__main__': 00523 card = BaseCard() 00524 00525 """ 00526 1,THRU,10 00527 1,3,THRU,19,15 00528 """ 00529 card.collapseThru([1,2,3,4,5,10]) 00530 card.collapseThru([1,3,4,5,6,17]) 00531