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=C0103,R0902,R0904,R0914 00026 00027 from __future__ import division, print_function 00028 #import sys 00029 from itertools import izip, count 00030 from numpy import array, pi 00031 00032 from pyNastran.bdf.fieldWriter import set_blank_if_default 00033 from pyNastran.bdf.cards.baseCard import BaseCard, expandThru 00034 00035 00036 class AEFACT(BaseCard): 00037 """ 00038 Defines real numbers for aeroelastic analysis. 00039 AEFACT SID D1 D2 D3 D4 D5 D6 D7 00040 D8 D9 -etc.- 00041 AEFACT 97 .3 .7 1.0 00042 """ 00043 type = 'AEFACT' 00044 def __init__(self, card=None, data=None): ## @todo doesnt support data 00045 ## Set identification number. (Unique Integer > 0) 00046 self.sid = card.field(1) 00047 ## Number (float) 00048 self.Di = card.fields(2) 00049 00050 def rawFields(self): 00051 fields = ['AEFACT', self.sid]+self.Di 00052 return fields 00053 00054 def reprFields(self): 00055 return self.rawFields() 00056 00057 class AELINK(BaseCard): 00058 """ 00059 Defines relationships between or among AESTAT and AESURF entries, such that: 00060 \f[ u^D + \Sigma_{i=1}^n C_i u_i^I = 0.0\f] 00061 AELINK ID LABLD LABL1 C1 LABL2 C2 LABL3 C3 00062 LABL4 C4 -etc.- 00063 AELINK 10 INBDA OTBDA -2.0 00064 """ 00065 type = 'AELINK' 00066 def __init__(self, card=None, data=None): ## @todo doesnt support data 00067 ## an ID=0 is applicable to the global subcase, ID=1 only subcase 1 00068 self.id = card.field(1) 00069 ## defines the dependent variable name (string) 00070 self.label = card.field(2) 00071 ## defines the independent variable name (string) 00072 self.independentLabels = [] 00073 ## linking coefficient (real) 00074 self.Cis = [] 00075 00076 fields = card.fields(3) 00077 #print "aelink fields = ",fields 00078 assert len(fields)%2==0,'fields=%s' %(fields) 00079 #print "len(fields) = ",len(fields) 00080 for i in xrange(0,len(fields),2): 00081 independentLabel = fields[i] 00082 Ci = fields[i+1] 00083 self.independentLabels.append(independentLabel) 00084 self.Cis.append(Ci) 00085 ### 00086 #print self 00087 00088 def rawFields(self): 00089 fields = ['AELINK', self.id, self.label] 00090 #print "self.independentLabels = ",self.independentLabels 00091 #print "self.Cis = ",self.Cis 00092 for (ivar, ival) in izip(self.independentLabels, self.Cis): 00093 fields += [ivar, ival] 00094 #print "AELINK fields = ",fields 00095 return fields 00096 00097 class AELIST(BaseCard): 00098 """ 00099 Defines a list of aerodynamic elements to undergo the motion prescribed 00100 with the AESURF Bulk Data entry for static aeroelasticity. 00101 AELIST SID E1 E2 E3 E4 E5 E6 E7 00102 E8... 00103 AELIST 75 1001 THRU 1075 1101 THRU 1109 1201 00104 1202 00105 00106 Remarks: 00107 1. These entries are referenced by the AESURF entry. 00108 2. When the THRU option is used, all intermediate grid points must exist. 00109 The word THRU may not appear in field 3 or 9 (2 or 9 for continuations). 00110 3. Intervening blank fields are not allowed. 00111 """ 00112 type = 'AELIST' 00113 def __init__(self, card=None, data=None): ## @todo doesnt support data 00114 ## Set identification number. (Integer > 0) 00115 self.sid = card.field(1) 00116 ## List of aerodynamic boxes generated by CAERO1 entries to define a 00117 ## surface. (Integer > 0 or 'THRU') 00118 self.elements = expandThru(card.fields(2)) 00119 self.cleanIDs() 00120 00121 def cleanIDs(self): 00122 self.elements = list(set(self.elements)) 00123 self.elements.sort() 00124 00125 def rawFields(self): 00126 fields = ['AELIST', self.sid]+self.elements 00127 return fields 00128 00129 class AEPARM(BaseCard): 00130 """ 00131 Defines a general aerodynamic trim variable degree-of-freedom (aerodynamic 00132 extra point). The forces associated with this controller will be derived 00133 from AEDW, AEFORCE and AEPRESS input data. 00134 AEPARM ID LABEL UNITS 00135 AEPARM 5 THRUST LBS 00136 """ 00137 type = 'AEPARM' 00138 def __init__(self,card=None,data=None): 00139 if card: 00140 self.id = card.field(1) 00141 self.label = card.field(2) 00142 self.units = card.fiedl(3, '') 00143 else: 00144 self.id = data[0] 00145 self.label = data[1] 00146 self.units = data[2] 00147 assert len(data)==3,'data = %s' %(data) 00148 ### 00149 00150 def rawFields(self): 00151 fields = ['AEPARM', self.id, self.label, self.units] 00152 return fields 00153 00154 class AESTAT(BaseCard): 00155 """ 00156 Specifies rigid body motions to be used as trim variables in static 00157 aeroelasticity. 00158 AESTAT ID LABEL 00159 AESTAT 5001 ANGLEA 00160 """ 00161 type = 'AESTAT' 00162 def __init__(self, card=None, data=None): 00163 if card: 00164 self.id = card.field(1) 00165 self.label = card.field(2) 00166 else: 00167 self.id = data[0] 00168 self.label = data[1] 00169 assert len(data)==2,'data = %s' %(data) 00170 ### 00171 00172 def rawFields(self): 00173 fields = ['AESTAT', self.id, self.label] 00174 return fields 00175 00176 class AESURF(BaseCard): 00177 """ 00178 Specifies an aerodynamic control surface as a member of the set of 00179 aerodynamic extra points. The forces associated with this controller will 00180 be derived from rigid rotation of the aerodynamic model about the hinge 00181 line(s) and from AEDW, AEFORCE and AEPRESS input data. The mass properties 00182 of the control surface can be specified using an AESURFS entry. 00183 00184 AESURF ID LABEL CID1 ALID1 CID2 ALID2 EFF LDW 00185 CREFC CREFS PLLIM PULIM HMLLIM HMULIM TQLLIM TQULIM 00186 """ 00187 type = 'AESURF' 00188 def __init__(self, card=None, data=None): ## @todo doesnt support data 00189 ## Set identification number. (Integer > 0) 00190 self.aesid = card.field(1) 00191 ## Controller identification number 00192 self.cntlid = card.field(2) 00193 ## Controller name. 00194 self.label = card.field(3) 00195 00196 ## Identification number of a rectangular coordinate system with a 00197 ## y-axis that defines the hinge line of the control surface 00198 ## component. 00199 self.cid1 = card.field(4) 00200 ## Identification of an AELIST Bulk Data entry that identifies all 00201 ## aerodynamic elements that make up the control surface 00202 ## component. (Integer > 0) 00203 self.alid1 = card.field(5) 00204 00205 self.cid2 = card.field(6) 00206 self.alid2 = card.field(7) 00207 00208 ## Control surface effectiveness. See Remark 4. (Real != 0.0; 00209 ## Default=1.0) 00210 self.eff = card.field(8, 1.0) 00211 ## Linear downwash flag. See Remark 2. (Character, one of LDW or NOLDW; 00212 ## Default=LDW). 00213 self.ldw = card.field(9, 'LDW') 00214 ## Reference chord length for the control surface. (Real>0.0; 00215 ## Default=1.0) 00216 self.crefc = card.field(10, 1.0) 00217 ## Reference surface area for the control surface. (Real>0.0; 00218 ## Default=1.0) 00219 self.crefs = card.field(11, 1.0) 00220 ## Lower and upper deflection limits for the control surface in 00221 ## radians. (Real, Default = +/- pi/2) 00222 self.pllim = card.field(12, -pi/2.) 00223 self.pulim = card.field(13, pi/2.) 00224 ## Lower and upper hinge moment limits for the control surface in 00225 ## force-length units. (Real, Default = no limit) -> 1e8 00226 self.hmllim = card.field(14) 00227 self.hmulim = card.field(15) 00228 ## Set identification numbers of TABLEDi entries that provide the 00229 ## lower and upper deflection limits for the control surface as a 00230 ## function of the dynamic pressure. (Integer>0, Default = no limit) 00231 self.tqllim = card.field(16) 00232 self.tqulim = card.field(17) 00233 00234 00235 def rawFields(self): 00236 fields = ['AESURF',self.aesid, self.cntlid, self.label, self.cid1, self.alid1, self.cid2, self.alid2, self.eff, self.ldw, 00237 self.crefc, self.crefs, self.pllim, self.pulim, self.hmllim, self.hmulim, self.tqllim, self.tqulim] 00238 return fields 00239 00240 def reprFields(self): 00241 eff = set_blank_if_default(self.eff, 1.0) 00242 ldw = set_blank_if_default(self.ldw, 'LDW') 00243 crefc = set_blank_if_default(self.crefc, 1.0) 00244 crefs = set_blank_if_default(self.crefs, 1.0) 00245 00246 pllim = set_blank_if_default(self.pllim, -pi/2.) 00247 pulim = set_blank_if_default(self.pulim, pi/2.) 00248 00249 fields = ['AESURF', self.aesid, self.cntlid, self.label, self.cid1, self.alid1, self.cid2, self.alid2, eff, ldw, 00250 crefc, crefs, pllim, pulim, self.hmllim, self.hmulim, self.tqllim, self.tqulim] 00251 return fields 00252 00253 class AESURFS(BaseCard): # not integrated 00254 """ 00255 Optional specification of the structural nodes associated with an 00256 aerodynamic control surface that has been defined on an AESURF entry. The 00257 mass associated with these structural nodes define the control surface 00258 moment(s) of inertia about the hinge line(s). 00259 Specifies rigid body motions to be used as trim variables in static 00260 aeroelasticity. 00261 AESURFS ID LABEL - LIST1 - LIST2 00262 AESURFS 6001 ELEV - 6002 - 6003 00263 """ 00264 type = 'AESURFS' 00265 def __init__(self, card=None, data=None): 00266 if card: 00267 self.id = card.field(1) 00268 self.label = card.field(2) 00269 self.list1 = card.field(4) 00270 self.list2 = card.field(6) 00271 else: 00272 self.id = data[0] 00273 self.label = data[1] 00274 self.list1 = data[2] 00275 self.list2 = data[3] 00276 assert len(data)==4, 'data = %s' %(data) 00277 ### 00278 00279 def rawFields(self): 00280 fields = ['AESURFS', self.id, self.label, None, self.list1, None, self.list2] 00281 return fields 00282 00283 class Aero(BaseCard): 00284 """Base class for AERO and AEROS cards.""" 00285 def __init__(self, card, data): 00286 pass 00287 00288 def IsSymmetricalXY(self): 00289 if self.symXY==1: 00290 return True 00291 return False 00292 00293 def IsSymmetricalXZ(self): 00294 if self.symXZ==1: 00295 return True 00296 return False 00297 00298 def EnableGroundEffect(self): 00299 self.symXY = -1 00300 00301 def DisableGroundEffect(self): 00302 self.symXY = 1 00303 00304 def IsAntiSymmetricalXY(self): 00305 if self.symXY==-1: 00306 return True 00307 return False 00308 00309 def IsAntiSymmetricalXZ(self): 00310 if self.symXY==-1: 00311 return True 00312 return False 00313 00314 class AERO(Aero): 00315 """ 00316 Gives basic aerodynamic parameters for unsteady aerodynamics. 00317 AERO ACSID VELOCITY REFC RHOREF SYMXZ SYMXY 00318 AERO 3 1.3+4 100. 1.-5 1 -1 00319 """ 00320 type = 'AERO' 00321 def __init__(self, card=None, data=None): 00322 Aero.__init__(self, card, data) 00323 if card: 00324 self.acsid = card.field(1, 0) 00325 self.velocity = card.field(2) 00326 self.cRef = card.field(3) 00327 self.rhoRef = card.field(4) 00328 self.symXZ = card.field(5, 0) 00329 self.symXY = card.field(6, 0) 00330 else: 00331 self.acsid = data[0] 00332 self.velocity = data[1] 00333 self.cRef = data[2] 00334 self.rhoRef = data[3] 00335 self.symXZ = data[4] 00336 self.symXY = data[5] 00337 assert len(data)==6, 'data = %s' %(data) 00338 ### 00339 # T is the tabular function 00340 #angle = self.wg*self.t*(t-(x-self.x0)/self.V) 00341 00342 def rawFields(self): 00343 fields = ['AERO', self.acsid, self.velocity, self.cRef, self.rhoRef, self.symXZ, self.symXY] 00344 return fields 00345 00346 def reprFields(self): 00347 symXZ = set_blank_if_default(self.symXZ, 0) 00348 symXY = set_blank_if_default(self.symXY, 0) 00349 fields = ['AERO', self.acsid, self.velocity, self.cRef, self.rhoRef, symXZ, symXY] 00350 return fields 00351 00352 class AEROS(Aero): 00353 """ 00354 Gives basic aerodynamic parameters for unsteady aerodynamics. 00355 AEROS ACSID RCSID REFC REFB REFS SYMXZ SYMXY 00356 AEROS 10 20 10. 100. 1000. 1 00357 """ 00358 type = 'AEROS' 00359 def __init__(self, card=None, data=None): 00360 Aero.__init__(self, card, data) 00361 if card: 00362 self.acsid = card.field(1, 0) 00363 self.rcsid = card.field(2) 00364 self.cRef = card.field(3) 00365 self.bRef = card.field(4) 00366 self.Sref = card.field(5) 00367 self.symXZ = card.field(6, 0) 00368 self.symXY = card.field(7, 0) 00369 else: 00370 self.acsid = data[0] 00371 self.rcsid = data[1] 00372 self.cRef = data[2] 00373 self.bRef = data[3] 00374 self.Sref = data[4] 00375 self.symXZ = data[5] 00376 self.symXY = data[6] 00377 assert len(data)==7, 'data = %s' %(data) 00378 ### 00379 00380 def rawFields(self): 00381 fields = ['AEROS', self.acsid, self.rcsid, self.cRef, self.bRef, self.Sref, self.symXZ, self.symXY] 00382 return fields 00383 00384 def reprFields(self): 00385 symXZ = set_blank_if_default(self.symXZ, 0) 00386 symXY = set_blank_if_default(self.symXY, 0) 00387 fields = ['AEROS', self.acsid, self.rcsid, self.cRef, self.bRef, self.Sref, symXZ, symXY] 00388 return fields 00389 00390 class CSSCHD(BaseCard): 00391 """ 00392 Defines a scheduled control surface deflection as a function of Mach number 00393 and angle of attack. 00394 CSSCHD SlD AESID LALPHA LMACH LSCHD 00395 """ 00396 type = 'ASSCHD' 00397 def __init__(self,card=None,data=None): 00398 Aero.__init__(self,card,data) 00399 if card: 00400 self.sid = card.field(1) 00401 self.aesid = card.field(2) # AESURF 00402 self.lAlpha = card.field(3) # AEFACT 00403 self.lMach = card.field(4) # AEFACT 00404 self.lSchd = card.field(5) # AEFACT 00405 else: 00406 self.sid = data[0] 00407 self.aesid = data[1] # AESURF 00408 self.lAlpha = data[2] # AEFACT 00409 self.lMach = data[3] # AEFACT 00410 self.lSchd = data[4] # AEFACT 00411 ### 00412 00413 def crossReference(self, model): 00414 self.aesid = model.AESurf(self.aesid) 00415 self.lAlpha = model.AEFact(self.lAlpha) 00416 self.lMach = model.AEFact(self.lMach) 00417 self.lSchd = model.AEFact(self.lSchd) 00418 00419 def AESid(self): 00420 if isinstance(self.aesid, int): 00421 return self.aesid 00422 return self.aesid.aesid 00423 00424 def LAlpha(self): 00425 if isinstance(self.lAlpha, int): 00426 return self.lAlpha 00427 return self.lAlpha.sid 00428 00429 def LMach(self): 00430 if isinstance(self.lMach, int): 00431 return self.lMach 00432 return self.lMach.sid 00433 00434 def LSchd(self): 00435 if isinstance(self.lSchd, int): 00436 return self.lSchd 00437 return self.lSchd.sid 00438 00439 def rawFields(self): 00440 fields = ['CSSCHD', self.sid, self.AESid(), self.LAlpha(), self.LMach(), self.LSchd()] 00441 return fields 00442 00443 def reprFields(self): 00444 return self.rawFields() 00445 00446 class CAERO1(BaseCard): 00447 """ 00448 Defines an aerodynamic macro element (panel) in terms of two leading edge 00449 locations and side chords. This is used for Doublet-Lattice theory for 00450 subsonic aerodynamics and the ZONA51 theory for supersonic aerodynamics. 00451 CAERO1 EID PID CP NSPAN NCHORD LSPAN LCHORD IGID 00452 X1 Y1 Z1 X12 X4 Y4 Z4 X43 00453 """ 00454 type = 'CAERO1' 00455 def __init__(self,card=None,data=None): 00456 """ 00457 1 00458 | \ 00459 | \ 00460 | \ 00461 | 4 00462 | | 00463 | | 00464 2------3 00465 """ 00466 #Material.__init__(self,card) 00467 self.eid = card.field(1) 00468 self.pid = card.field(2) 00469 self.cp = card.field(3, 0) 00470 self.nspan = card.field(4, 0) 00471 self.nchord = card.field(5, 0) 00472 00473 #if self.nspan==0: 00474 self.lspan = card.field(6) 00475 00476 #if self.nchord==0: 00477 self.lchord = card.field(7) 00478 00479 self.igid = card.field(8) 00480 00481 self.p1 = array([card.field(9, 0.0), card.field(10, 0.0), card.field(11, 0.0)]) 00482 self.x12 = card.field(12, 0.) 00483 #self.p2 = self.p1+array([card.field(12, 0.0), 0., 0.]) 00484 00485 self.p4 = array([card.field(13, 0.0), card.field(14, 0.0), card.field(15, 0.0)]) 00486 self.x43 = card.field(16, 0.) 00487 #self.p3 = self.p4+array([card.field(16, 0.0), 0., 0.]) 00488 00489 def Cp(self): 00490 if isinstance(self.cp, int): 00491 return self.cp 00492 return self.cp.cid 00493 00494 def Pid(self): 00495 if isinstance(self.pid, int): 00496 return self.pid 00497 return self.pid.pid 00498 00499 def crossReference(self, model): 00500 self.pid = model.PAero(self.pid) 00501 self.cp = model.Coord(self.cp) 00502 00503 def Points(self): 00504 p1,matrix = self.cp.transformToGlobal(self.p1) 00505 p4,matrix = self.cp.transformToGlobal(self.p4) 00506 00507 p2 = self.p1+array([self.x12, 0., 0.]) 00508 p3 = self.p4+array([self.x43, 0., 0.]) 00509 00510 #print "x12 = ",self.x12 00511 #print "x43 = ",self.x43 00512 #print "pcaero[%s] = %s" %(self.eid,[p1,p2,p3,p4]) 00513 return [p1,p2,p3,p4] 00514 00515 def SetPoints(self,points): 00516 self.p1 = points[0] 00517 self.p2 = points[1] 00518 self.p3 = points[2] 00519 self.p4 = points[3] 00520 x12 = self.p2-self.p1 00521 x43 = self.p4-self.p3 00522 self.x12 = x12[0] 00523 self.x43 = x43[0] 00524 00525 def rawFields(self): 00526 fields = ['CAERO1', self.eid, self.Pid(), self.Cp(), self.nspan, self.nchord, self.lspan, self.lchord,self.igid, 00527 ]+list(self.p1)+[self.x12]+list(self.p4)+[self.x43] 00528 return fields 00529 00530 def reprFields(self): 00531 cp = set_blank_if_default(self.Cp(), 0) 00532 nspan = set_blank_if_default(self.nspan, 0) 00533 nchord = set_blank_if_default(self.nchord, 0) 00534 fields = ['CAERO1', self.eid, self.Pid(), cp, nspan, nchord, self.lspan, self.lchord, self.igid, 00535 ]+list(self.p1)+[self.x12]+list(self.p4)+[self.x43] 00536 return fields 00537 00538 class CAERO2(BaseCard): 00539 """ 00540 Aerodynamic Body Connection 00541 Defines aerodynamic slender body and interference elements for 00542 Doublet-Lattice aerodynamics. 00543 """ 00544 type = 'CAERO2' 00545 def __init__(self, card=None, data=None): 00546 """ 00547 1 \ 00548 | \ 00549 | \ 00550 | 3 00551 | | 00552 | | 00553 2------4 00554 """ 00555 #Material.__init__(self,card) 00556 ## Element identification number 00557 self.eid = card.field(1) 00558 ## Property identification number of a PAERO2 entry. 00559 self.pid = card.field(2) 00560 ## Coordinate system for locating point 1. 00561 self.cp = card.field(3, 0) 00562 ## Number of slender body elements. If NSB > 0, then NSB equal divisions 00563 ## are assumed; if zero or blank, specify a list of divisions in LSB. 00564 ## (Integer >= 0) 00565 self.nsb = card.field(4) 00566 ## Number of interference elements. If NINT > 0, then NINT equal 00567 ## divisions are assumed; if zero or blank, specify a list of divisions 00568 ## in LINT. (Integer >= 0) 00569 self.nint = card.field(5) 00570 00571 ## ID of an AEFACT Bulk Data entry for slender body division points; 00572 ## used only if NSB is zero or blank. (Integer >= 0) 00573 self.lsb = card.field(6) # ID of AEFACT 00574 ## ID of an AEFACT data entry containing a list of division points for 00575 ## interference elements; used only if NINT is zero or blank. 00576 ## (Integer > 0) 00577 self.lint = card.field(7) 00578 ## Interference group identification. Aerodynamic elements with 00579 ## different IGIDs are uncoupled. (Integer >= 0) 00580 self.igid = card.field(8) 00581 ## Location of point 1 in coordinate system CP 00582 self.p1 = array([card.field(9, 0.0), card.field(10, 0.0), card.field(11, 0.0)]) 00583 ## Length of body in the x-direction of the aerodynamic coordinate 00584 ## system. (Real > 0) 00585 self.x12 = card.field(12,0.) 00586 00587 def Cp(self): 00588 if isinstance(self.cp, int): 00589 return self.cp 00590 return self.cp.cid 00591 00592 def Pid(self): 00593 if isinstance(self.pid, int): 00594 return self.pid 00595 return self.pid.pid 00596 00597 def Lsb(self): # AEFACT 00598 if isinstance(self.lsb, int): 00599 return self.lsb 00600 return self.lsb.sid 00601 00602 def crossReference(self, model): 00603 self.pid = model.PAero(self.pid) # links to PAERO2 00604 self.cp = model.Coord(self.cp) 00605 #self.lsb = model.AeFact(self.lsb) # not added 00606 00607 def Points(self): 00608 (p1, matrix) = self.cp.transformToGlobal(self.p1) 00609 p2 = self.p1+array([self.x12, 0., 0.]) 00610 #print "x12 = ",self.x12 00611 #print "pcaero[%s] = %s" %(self.eid,[p1,p2]) 00612 return [p1,p2] 00613 00614 def SetPoints(self, points): 00615 self.p1 = points[0] 00616 self.p2 = points[1] 00617 x12 = self.p2-self.p1 00618 self.x12 = x12[0] 00619 00620 def rawFields(self): 00621 fields = ['CAERO2', self.eid, self.Pid(), self.Cp(), self.nsb, self.nint, self.lsb, self.lint,self.igid, 00622 ]+list(self.p1)+[self.x12] 00623 return fields 00624 00625 def reprFields(self): 00626 cp = set_blank_if_default(self.Cp(), 0) 00627 fields = ['CAERO2', self.eid, self.Pid(), cp, self.nsb, self.nint, self.lsb, self.lint, self.igid, 00628 ]+list(self.p1)+[self.x12] 00629 return fields 00630 00631 class CAERO3(BaseCard): 00632 pass 00633 00634 class CAERO4(BaseCard): 00635 pass 00636 00637 class CAERO5(BaseCard): 00638 pass 00639 00640 class FLFACT(BaseCard): 00641 """ 00642 FLFACT SID F1 F2 F3 F4 F5 F6 F7 00643 F8 F9 -etc.- 00644 00645 FLFACT 97 .3 .7 3.5 00646 00647 FLFACT SID F1 THRU FNF NF FMID # delta quantity approach 00648 FLFACT 201 .200 THRU .100 11 .133333 00649 """ 00650 type = 'FLFACT' 00651 def __init__(self, card=None, data=None): 00652 if card: 00653 self.sid = card.field(1) 00654 self.factors = card.fields(2) 00655 00656 if len(self.factors)>1 and self.factors[1]=='THRU': 00657 msg = 'embedded THRUs not supported yet on FLFACT card\n' 00658 raise NotImplementedError(msg) 00659 #(a,thru,b,n,dn) = factors 00660 #for i in xrange( 00661 ### 00662 else: 00663 self.sid = data[0] 00664 self.factors = data[1:] 00665 ### 00666 00667 def rawFields(self): 00668 fields = ['FLFACT', self.sid]+self.factors 00669 return fields 00670 00671 def __repr__(self): 00672 fields = self.reprFields() 00673 return self.printCard(fields, tol=0.) 00674 00675 class FLUTTER(BaseCard): 00676 """ 00677 Defines data needed to perform flutter analysis. 00678 FLUTTER SID METHOD DENS MACH RFREQ IMETH NVALUE/OMAX EPS 00679 FLUTTER 19 K 119 219 319 S 5 1.-4 00680 """ 00681 type = 'FLUTTER' 00682 def __init__(self, card=None, data=None): 00683 if card: 00684 self.sid = card.field(1) 00685 self.method = card.field(2) 00686 self.density = card.field(3) 00687 self.mach = card.field(4) 00688 self.rfreqVel = card.field(5) 00689 else: 00690 assert len(data)==8, 'FLUTTER = %s' %(data) 00691 self.sid = data[0] 00692 self.method = data[1] 00693 self.density = data[2] 00694 self.mach = data[3] 00695 self.rfreqVel = data[4] 00696 self.method = data[5] 00697 self.imethod = data[6] 00698 self.nValue = data[7] 00699 self.omax = data[8] 00700 raise NotImplementedError('verify...') 00701 ### 00702 assert self.method in ['K', 'PK', 'PKNL', 'PKS', 'PKNLS', 'KE'] 00703 00704 if self.method in ['K', 'KE']: 00705 self.imethod = card.field(6, 'L') 00706 self.nValue = card.field(7) 00707 self.omax = None 00708 assert self.imethod in ['L', 'S'] 00709 elif self.method in ['PKS', 'PKNLS']: 00710 self.imethod = None 00711 self.nValue = None 00712 self.omax = card.field(7) 00713 else: 00714 self.nValue = card.field(7) 00715 self.omax = None 00716 self.imethod = None 00717 00718 self.epsilon = card.field(8) # no default listed... 00719 00720 def _rawNValueOMax(self): 00721 if self.method in ['K', 'KE']: 00722 return (self.imethod, self.nValue) 00723 assert self.imethod in ['L', 'S'] 00724 elif self.method in ['PKS', 'PKNLS']: 00725 return(self.imethod, self.omax) 00726 else: 00727 return(self.imethod, self.nValue) 00728 ### 00729 00730 def _reprNValueOMax(self): 00731 if self.method in ['K', 'KE']: 00732 imethod = set_blank_if_default(self.imethod, 'L') 00733 return (imethod,self.nValue) 00734 assert self.imethod in ['L', 'S'] 00735 elif self.method in ['PKS', 'PKNLS']: 00736 return(self.imethod, self.omax) 00737 else: 00738 return(self.imethod, self.nValue) 00739 ### 00740 00741 def rawFields(self): 00742 (imethod,nValue) = self._rawNValueOMax() 00743 fields = ['FLUTTER', self.sid, self.method, self.density, self.mach, self.rfreqVel, imethod, nValue, self.epsilon] 00744 return fields 00745 00746 #def reprFields(self): 00747 # (imethod, nValue) = self._reprNValueOMax() 00748 # fields = ['FLUTTER', self.sid, self.method, self.density, self.mach, self.rfreqVel, imethod, nValue, self.epsilon] 00749 # return fields 00750 00751 class GUST(BaseCard): 00752 """ 00753 Defines a stationary vertical gust for use in aeroelastic response analysis. 00754 GUST SID DLOAD WG X0 V 00755 GUST 133 61 1.0 0. 1.+4 00756 """ 00757 type = 'GUST' 00758 def __init__(self, card=None, data=None): 00759 if card: 00760 self.sid = card.field(1) 00761 self.dload = card.field(2) 00762 self.wg = card.field(3) 00763 self.x0 = card.field(4) 00764 self.V = card.field(5) 00765 else: 00766 self.sid = data[0] 00767 self.dload = data[1] 00768 self.wg = data[2] 00769 self.x0 = data[3] 00770 self.V = data[4] 00771 assert len(data)==5, 'data = %s' %(data) 00772 00773 ## angle = self.wg*self.t*(t-(x-self.x0)/self.V) # T is the tabular 00774 ## function 00775 00776 def rawFields(self): 00777 fields = ['GUST', self.sid, self.dload, self.wg, self.x0, self.V] 00778 return fields 00779 00780 class MKAERO1(BaseCard): 00781 """ 00782 Provides a table of Mach numbers (m) and reduced frequencies (k) for 00783 aerodynamic matrix calculation 00784 MKAERO1 m1 m2 m3 m4 m5 m6 m7 m8 00785 k1 k2 k3 k4 k5 k6 k7 k8 00786 """ 00787 type = 'MKAERO1' 00788 def __init__(self, card=None, data=None): 00789 if card: 00790 fields = card.fields(1) 00791 nFields = len(fields)-8 00792 self.machs = [] 00793 self.rFreqs = [] 00794 for i in xrange(1, 1+nFields): 00795 self.machs.append( card.field(i )) 00796 self.rFreqs.append(card.field(i+8)) 00797 else: 00798 raise NotImplementedError('MKAERO1') 00799 ### 00800 #print "machs = ",self.machs 00801 #print "rFreqs = ",self.rFreqs 00802 00803 def addFreqs(self, mkaero): 00804 self.getMach_rFreqs() 00805 for m in mkaero.machs: 00806 self.machs.append(m) 00807 for f in mkaero.rFreqs: 00808 self.rFreqs.append(f) 00809 00810 def rawFields(self): 00811 #fields = ['MKAERO2'] 00812 #for (i, mach, rfreq) in izip(count(), self.machs, self.rFreqs): 00813 # fields += [mach,rfreq] 00814 machs = [None]*8 00815 freqs = [None]*8 00816 for i,mach in enumerate(self.machs): 00817 machs[i] = mach 00818 for i,freq in enumerate(self.rFreqs): 00819 freqs[i] = freq 00820 fields = ['MKAERO1']+machs+freqs 00821 return fields 00822 00823 def getMach_rFreqs(self): 00824 return (self.machs,self.rFreqs) 00825 00826 def reprFields(self): 00827 return self.rawFields() 00828 00829 class MKAERO2(BaseCard): 00830 """ 00831 Provides a table of Mach numbers (m) and reduced frequencies (k) for 00832 aerodynamic matrix calculation 00833 MKAERO2 m1 k1 m2 k2 m3 k3 m4 k4 00834 """ 00835 type = 'MKAERO2' 00836 def __init__(self, card=None, data=None): 00837 if card: 00838 fields = card.fields(1) 00839 nFields = len(fields) 00840 self.machs = [] 00841 self.rFreqs = [] 00842 for i in xrange(1, 1+nFields, 2): 00843 self.machs.append( card.field(i )) 00844 self.rFreqs.append(card.field(i+1)) 00845 ### 00846 else: 00847 raise NotImplementedError('MKAERO2') 00848 ### 00849 00850 def addFreqs(self, mkaero): 00851 self.getMach_rFreqs() 00852 for m in mkaero.machs: 00853 self.machs.append(m) 00854 for f in mkaero.rFreqs: 00855 self.rFreqs.append(f) 00856 ### 00857 00858 def rawFields(self): 00859 fields = ['MKAERO2'] 00860 for (i, mach, rfreq) in izip(count(), self.machs, self.rFreqs): 00861 fields += [mach, rfreq] 00862 return fields 00863 00864 def getMach_rFreqs(self): 00865 return (self.machs, self.rFreqs) 00866 00867 def reprFields(self): 00868 return self.rawFields() 00869 00870 00871 class PAERO1(BaseCard): 00872 """ 00873 Defines associated bodies for the panels in the Doublet-Lattice method. 00874 PAERO1 PID B1 B2 B3 B4 B5 B6 00875 """ 00876 type = 'PAERO1' 00877 def __init__(self, card=None, data=None): 00878 self.pid = card.field(1) 00879 Bi = card.fields(2) 00880 self.Bi = [] 00881 00882 for bi in Bi: 00883 if isinstance(bi, int) and bi>=0: 00884 self.Bi.append(bi) 00885 elif bi is not None: 00886 raise RuntimeError('invalid Bi value on PAERO1 bi=|%r|' %(bi)) 00887 #else: 00888 # pass 00889 ### 00890 00891 def Bodies(self): 00892 return self.Bi 00893 00894 def rawFields(self): 00895 fields = ['PAERO1', self.pid] + self.Bi 00896 return fields 00897 00898 def reprFields(self): 00899 return self.rawFields() 00900 00901 class PAERO2(BaseCard): 00902 """ 00903 Defines the cross-sectional properties of aerodynamic bodies 00904 PAERO2 PID ORIENT WIDTH AR LRSB LRIB LTH1 LTH2 00905 THI1 THN1 THI2 THN2 THI3 THN3 00906 """ 00907 type = 'PAERO2' 00908 def __init__(self, card=None, data=None): 00909 ## Property identification number. (Integer > 0) 00910 self.pid = card.field(1) 00911 ## Orientation flag. Type of motion allowed for bodies. Refers to the 00912 ## aerodynamic coordinate system of ACSID. See AERO entry. 00913 ## (Character = 'Z', 'Y', or 'ZY') 00914 self.orient = card.field(2) 00915 ## Reference half-width of body and the width of the constant width 00916 ## interference tube. (Real > 0.0) 00917 self.width = card.field(3) 00918 ## Aspect ratio of the interference tube (height/width). float>0. 00919 self.AR = card.field(4) 00920 ## Identification number of an AEFACT entry containing a list of slender 00921 ## body half-widths at the end points of the slender body elements. If 00922 ## blank, the value of WIDTH will be used. (Integer > 0 or blank) 00923 self.lrsb = card.field(5) 00924 ## Identification number of an AEFACT entry containing a list of slender 00925 ## body half-widths at the end points of the interference elements. If 00926 ## blank, the value of WIDTH will be used. (Integer > 0 or blank) 00927 self.lrib = card.field(6) 00928 ## dentification number of AEFACT entries for defining ? arrays for 00929 ## interference calculations. (Integer >= 0) 00930 self.lth1 = card.field(7) 00931 self.lth2 = card.field(8) 00932 self.thi = [] 00933 self.thn = [] 00934 fields = card.fields(9) 00935 nFields = len(fields) 00936 for i in xrange(9, 9+nFields, 2): 00937 self.thi.append(card.field(i )) 00938 self.thi.append(card.field(i+1)) 00939 ### 00940 00941 def rawFields(self): 00942 fields = ['PAERO2', self.pid, self.orient, self.width, self.AR, self.lrsb, self.lrib, self.lth1, self.lth2] 00943 for (thi, thn) in izip(self.thi, self.thn): 00944 fields += [thi, thn] 00945 return fields 00946 00947 def reprFields(self): 00948 return self.rawFields() 00949 00950 class Spline(BaseCard): 00951 def __init__(self, card, data): 00952 pass 00953 00954 class SPLINE1(Spline): 00955 """ 00956 Surface Spline Methods 00957 Defines a surface spline for interpolating motion and/or forces for 00958 aeroelastic problems on aerodynamic geometries defined by regular arrays of 00959 aerodynamic points. 00960 SPLINE1 EID CAERO BOX1 BOX2 SETG DZ METH USAGE 00961 NELEM MELEM 00962 00963 SPLINE1 3 111 115 122 14 0. 00964 """ 00965 type = 'SPLINE1' 00966 def __init__(self, card=None, data=None): 00967 Spline.__init__(self, card, data) 00968 if card: 00969 self.eid = card.field(1) 00970 self.caero = card.field(2) 00971 self.box1 = card.field(3) 00972 self.box2 = card.field(4) 00973 self.setg = card.field(5) 00974 self.dz = card.field(6, 0.0) 00975 self.method = card.field(7, 'IPS') 00976 self.usage = card.field(8, 'BOTH') 00977 self.nelements = card.field(9, 10) 00978 self.melements = card.field(10, 10) 00979 else: 00980 self.eid = data[0] 00981 self.caero = data[1] 00982 self.box1 = data[2] 00983 self.box2 = data[3] 00984 self.setg = data[4] 00985 self.dz = data[5] 00986 self.method = data[6] 00987 self.usage = data[7] 00988 self.nelements = data[8] 00989 self.melements = data[9] 00990 assert len(data)==10, 'data = %s' %(data) 00991 ### 00992 00993 assert self.box2>=self.box1 00994 assert self.method in ['IPS', 'TPS', 'FPS'] 00995 assert self.usage in ['FORCE', 'DISP', 'BOTH'] 00996 00997 def CAero(self): 00998 if isinstance(self.caero, int): 00999 return self.caero 01000 return self.caero.eid 01001 01002 def Set(self): 01003 if isinstance(self.setg, int): 01004 return self.setg 01005 return self.setg.sid 01006 01007 def crossReference(self, model): 01008 self.caero = model.CAero(self.caero) 01009 self.setg = model.Set(self.setg) 01010 01011 def rawFields(self): 01012 fields = ['SPLINE1',self.eid, self.CAero(), self.box1, self.box2, self.Set(), self.dz, self.method, self.usage, 01013 self.nelements, self.melements] 01014 return fields 01015 01016 def reprFields(self): 01017 dz = set_blank_if_default(self.dz, 0.) 01018 method = set_blank_if_default(self.method, 'IPS') 01019 usage = set_blank_if_default(self.usage, 'BOTH') 01020 nelements = set_blank_if_default(self.nelements, 10) 01021 melements = set_blank_if_default(self.melements, 10) 01022 01023 fields = ['SPLINE1', self.eid, self.CAero(), self.box1, self.box2, self.Set(), dz, method, usage, 01024 nelements, melements] 01025 fields = self._wipeEmptyFields(fields) 01026 return fields 01027 01028 class SPLINE2(Spline): 01029 """ 01030 Linear Spline 01031 Defines a surface spline for interpolating motion and/or forces for 01032 aeroelastic problems on aerodynamic geometries defined by regular arrays of 01033 aerodynamic points. 01034 SPLINE2 EID CAERO ID1 ID2 SETG DZ DTOR CID 01035 DTHX DTHY None USAGE 01036 SPLINE2 5 8 12 24 60 0. 1.0 3 01037 1. 01038 """ 01039 type = 'SPLINE2' 01040 def __init__(self, card=None, data=None): 01041 Spline.__init__(self, card, data) 01042 if card: 01043 self.eid = card.field(1) 01044 self.caero = card.field(2) 01045 self.id1 = card.field(3) 01046 self.id2 = card.field(4) 01047 self.setg = card.field(5) 01048 self.dz = card.field(6, 0.0) 01049 self.dtor = card.field(7, 1.0) 01050 self.cid = card.field(8, 0) 01051 self.thx = card.field(9) 01052 self.thy = card.field(10) 01053 01054 self.usage = card.field(12, 'BOTH') 01055 else: 01056 raise NotImplementedError('not supported') 01057 ### 01058 01059 def crossReference(self,model): 01060 self.caero = model.CAero(self.caero) 01061 self.setg = model.Set(self.setg) 01062 01063 def Cid(self): 01064 if isinstance(self.cid, int): 01065 return self.cid 01066 return self.cid.cid 01067 01068 def CAero(self): 01069 if isinstance(self.caero, int): 01070 return self.caero 01071 return self.caero.eid 01072 01073 def Set(self): 01074 if isinstance(self.setg, int): 01075 return self.setg 01076 return self.setg.sid 01077 01078 def rawFields(self): 01079 fields = ['SPLINE2',self.eid, self.CAero(), self.id1, self.id2, self.Set(), self.dz, self.dtor, self.Cid(), 01080 self.thx, self.thy, None, self.usage] 01081 return fields 01082 01083 def reprFields(self): 01084 dz = set_blank_if_default(self.dz, 0.) 01085 usage = set_blank_if_default(self.usage, 'BOTH') 01086 fields = ['SPLINE2',self.eid, self.CAero(), self.id1, self.id2, self.Set(), dz, self.dtor, self.Cid(), 01087 self.thx, self.thy, None, usage] 01088 return fields 01089 01090 class SPLINE4(Spline): 01091 """ 01092 Surface Spline Methods 01093 Defines a curved surface spline for interpolating motion and/or forces for aeroelastic 01094 problems on general aerodynamic geometries using either the Infinite Plate, Thin 01095 Plate or Finite Plate splining method. 01096 SPLINE4 EID CAERO AELIST --- SETG DZ METH USAGE 01097 NELEM MELEM 01098 01099 SPLINE4 3 111 115 --- 14 0. IPS 01100 """ 01101 type = 'SPLINE4' 01102 def __init__(self, card=None, data=None): 01103 Spline.__init__(self, card, data) 01104 if card: 01105 self.eid = card.field(1) 01106 self.caero = card.field(2) 01107 self.aelist = card.field(3) 01108 # None 01109 self.setg = card.field(5) 01110 self.dz = card.field(6, 0.0) 01111 self.method = card.field(7, 'IPS') 01112 self.usage = card.field(8, 'BOTH') 01113 self.nelements = card.field(9, 10) 01114 self.melements = card.field(10, 10) 01115 else: 01116 self.eid = data[0] 01117 self.caero = data[1] 01118 self.aelist = data[2] 01119 self.setg = data[3] 01120 self.dz = data[4] 01121 self.method = data[5] 01122 self.usage = data[6] 01123 self.nelements = data[7] 01124 self.melements = data[8] 01125 assert len(data)==9, 'data = %s' %(data) 01126 ### 01127 01128 assert self.method in ['IPS', 'TPS', 'FPS'] 01129 assert self.usage in ['FORCE', 'DISP', 'BOTH'] 01130 01131 def CAero(self): 01132 if isinstance(self.caero, int): 01133 return self.caero 01134 return self.caero.eid 01135 01136 def AEList(self): 01137 if isinstance(self.aelist, int): 01138 return self.aelist 01139 return self.aelist.aelist 01140 01141 def Set(self): 01142 if isinstance(self.setg, int): 01143 return self.setg 01144 return self.setg.sid 01145 01146 def crossReference(self, model): 01147 self.caero = model.CAero(self.caero) 01148 self.setg = model.Set(self.setg) 01149 self.aelist = model.AEList(self.aelist) 01150 01151 def rawFields(self): 01152 fields = ['SPLINE4',self.eid, self.CAero(), self.AEList(), None, self.Set(), self.dz, self.method, self.usage, 01153 self.nelements, self.melements] 01154 return fields 01155 01156 def reprFields(self): 01157 dz = set_blank_if_default(self.dz, 0.) 01158 method = set_blank_if_default(self.method, 'IPS') 01159 usage = set_blank_if_default(self.usage, 'BOTH') 01160 nelements = set_blank_if_default(self.nelements, 10) 01161 melements = set_blank_if_default(self.melements, 10) 01162 01163 fields = ['SPLINE4', self.eid, self.CAero(), self.AEList(), None, self.Set(), dz, method, usage, 01164 nelements, melements] 01165 fields = self._wipeEmptyFields(fields) 01166 return fields 01167 01168 class SPLINE5(Spline): 01169 """ 01170 Linear Spline 01171 Defines a 1D beam spline for interpolating motion and/or forces for 01172 aeroelastic problems on aerodynamic geometries defined by irregular arrays 01173 of aerodynamic points. The interpolating beam supports axial rotation and 01174 bending in the yz-plane. 01175 01176 SPLINE5 EID CAERO AELIST --- SETG DZ DTOR CID 01177 DTHX DTHY --- USAGE 01178 """ 01179 type = 'SPLINE5' 01180 def __init__(self, card=None, data=None): 01181 Spline.__init__(self,card,data) 01182 if card: 01183 self.eid = card.field(1) 01184 self.caero = card.field(2) 01185 self.aelist = card.field(3) 01186 # None 01187 self.setg = card.field(5) 01188 self.dz = card.field(6, 0.0) 01189 self.dtor = card.field(7, 1.0) 01190 self.cid = card.field(8, 0) 01191 self.thx = card.field(9) 01192 self.thy = card.field(10) 01193 01194 self.usage = card.field(12, 'BOTH') 01195 else: 01196 raise NotImplementedError('not supported') 01197 ### 01198 01199 def Cid(self): 01200 if isinstance(self.cid, int): 01201 return self.cid 01202 return self.cid.cid 01203 01204 def CAero(self): 01205 if isinstance(self.caero, int): 01206 return self.caero 01207 return self.caero.eid 01208 01209 def AEList(self): 01210 if isinstance(self.aelist, int): 01211 return self.aelist 01212 return self.aelist.aelist 01213 01214 def Set(self): 01215 if isinstance(self.setg, int): 01216 return self.setg 01217 return self.setg.sid 01218 01219 def crossReference(self, model): 01220 self.caero = model.CAero(self.caero) 01221 self.setg = model.Set(self.setg) 01222 self.aelist = model.AEList(self.aelist) 01223 01224 def rawFields(self): 01225 fields = ['SPLINE2',self.eid, self.CAero(), self.AEList(), None, self.Set(), self.dz, self.dtor,self.Cid(), 01226 self.thx, self.thy, None, self.usage] 01227 return fields 01228 01229 def reprFields(self): 01230 dz = set_blank_if_default(self.dz, 0.) 01231 usage = set_blank_if_default(self.usage, 'BOTH') 01232 fields = ['SPLINE5', self.eid, self.CAero(), self.AEList(), None, self.Set(), dz, self.dtor, self.Cid(), 01233 self.thx, self.thy, None, usage] 01234 return fields 01235 01236 class TRIM(BaseCard): 01237 type = 'TRIM' 01238 def __init__(self, card=None, data=None): 01239 if card: 01240 ## Trim set identification number. (Integer > 0) 01241 self.sid = card.field(1) 01242 ## Mach number. (Real > 0.0 and != 1.0) 01243 self.mach = card.field(2) 01244 ## Dynamic pressure. (Real > 0.0) 01245 self.q = card.field(3) 01246 ## The label identifying aerodynamic trim variables defined on an 01247 ## AESTAT or AESURF entry. 01248 self.labels = [] 01249 ## The magnitude of the aerodynamic extra point degree-of-freedom. 01250 ## (Real) 01251 self.uxs = [] 01252 ## Flag to request a rigid trim analysis (Real > 0.0 and < 1.0; 01253 ## Default =1.0. A value of 0.0 provides a rigid trim analysis, 01254 ## not supported 01255 self.aeqr = 1.0 01256 fields = card.fields(4) 01257 01258 i=0 01259 nFields = len(fields)-1 01260 while i<nFields: ## @todo doesnt support aeqr 01261 label = fields[i] 01262 ux = fields[i+1] 01263 assert isinstance(label, str), 'TRIM card doesnt support AEQR field...iField=%s label=%s fields=%s' %(i,label,card.fields(0)) 01264 self.labels.append(label) 01265 self.uxs.append(ux) 01266 if i==2: 01267 self.aeqr = card.field(4+i+2, 1.0) 01268 i+=1 01269 i+=2 01270 ### 01271 else: 01272 raise NotImplementedError('TRIM not supported') 01273 ### 01274 01275 def rawFields(self): 01276 fields = ['TRIM', self.sid, self.mach, self.q] 01277 for (i, label, ux) in izip(count(), self.labels, self.uxs): 01278 fields += [label, ux] 01279 if i==1: 01280 fields += [self.aeqr] 01281 return fields