Coverage for cards/properties.py: 79%

1"""

2 NASTRAN property Cards collection

3"""

4 import logging

5 import re

6 from collections import defaultdict

7 from pprint import pprint

8

9 import numpy as np

10

11 from nastranio.cardslib import (

12 ComplexCard,

13 RepeatedRowsCard,

14 SimpleCard,

15 SimpleCyclingCard,

16)

17 from nastranio.decorators import cached_property, fem_property

18

19

20@fem_property

21 class PCOMP(SimpleCyclingCard):

22 """

23 Layered Composite Element Property.

24 Defines the properties of an n-ply composite material laminate.

25

26 ref: NX Nastran 12 Quick Reference Guide 16-97 (p.2327)

27

28

29 >>> pcomp = PCOMP()

30 >>> pcomp.append_fields_list(

31 ... [ 1001, None, 0.00361, None, None, None, None, None, '+',

32 ... '+', 1002, 0.018, 0.0, 'YES', 1003, 0.339, 0.0, 'YES', '+',

33 ... '+', 1009, 0.018, 5.0, 'YES'])

34 >>> pcomp.append_fields_list(

35 ... [ 1002, None, 0.005, None, None, None, None, None, '+',

36 ... '+', 1002, 0.018, 0.0, 'YES', 1003, 0.339, None, 'YES', '+',

37 ... '+', 1009, 0.018, 5.0, 'YES'])

38 >>> # check main data:

39 >>> pprint(pcomp.carddata['main']) # doctest: +NORMALIZE_WHITESPACE

40 defaultdict(<class 'list'>,

41 {'FT': [None, None],

42 'GE': [None, None],

43 'LAM': [None, None],

44 'NSM': [0.00361, 0.005],

45 'PID': [1001, 1002],

46 'SB': [None, None],

47 'TREF': [None, None],

48 'Z0': [None, None],

49 'pcomp_layupID': [0, 0]})

50 >>> # check LAYUPs creation. Both cards come with the same layout values. Therefore

51 >>> # only one LAYUP (made of three plies) is created:

52 >>> pprint(pcomp.carddata['pcomp_layup']) # doctest: +NORMALIZE_WHITESPACE

53 [[{'MID': 1002, 'SOUT': 'YES', 'T': 0.018, 'THETA': 0.0},

54 {'MID': 1003, 'SOUT': 'YES', 'T': 0.339, 'THETA': 0.0},

55 {'MID': 1009, 'SOUT': 'YES', 'T': 0.018, 'THETA': 5.0}]]

56 """

57

58 DIM = "2d"

59 REPEATED_DATA_NAME = "layup"

60 TABLE = """

61| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

62|-------+------+----+--------+-------+--------+------+--------+-------+----|

63| PCOMP | PID | Z0 | NSM | SB | FT | TREF | GE | LAM | |

64| "" | MID1 | T1 | THETA1 | SOUT1 | MID2 | T2 | THETA2 | SOUT2 | |

65| "" | MID3 | T3 | THETA3 | SOUT3 | -etc.- | | | | |

66"""

67 DEFAULTS = {

68 "NSM": 0.0,

69 "Z0": None,

70 "SB": None,

71 "FT": None,

72 "TREF": None,

73 "GE": None,

74 "LAM": None,

75 }

76 REPEATED_DEFAULTS = {"THETA": 0.0, "SOUT": "NO"}

77 COMMENTS_KEY = "Property"

78

79 @cached_property

80 def thk(self):

81 """return a dict of thicknesses"""

82 # calculate thicknesses for all layups

83 layup_thks = {}

84 for layID, data in enumerate(self.carddata["pcomp_layup"]):

85 ts = [ply["T"] for ply in data]

86 layup_thks[layID] = sum(ts)

87 # map to PIDS

88 thks = {

89 pid: layup_thks[lay]

90 for pid, lay in zip(

91 self.carddata["main"]["PID"], self.carddata["main"]["pcomp_layupID"]

92 )

93 }

94 return {

95 "data": np.array(list(thks.values())),

96 "index": np.array(list(thks.keys())),

97 "name": "pid2thk",

98 }

99

100 @cached_property

101 def pid2mids(self):

102 """This ovverides the default "pid2mids" defined in `cardslib` module"""

103 ret = {}

104

105 for ix, pid in enumerate(self.carddata["main"]["PID"]):

106 ret[pid] = set()

107 layup_id = self.carddata["main"]["pcomp_layupID"][ix]

108 layups = self.carddata["pcomp_layup"][layup_id]

109 for layup in layups:

110 ret[pid].add(layup["MID"])

111 ret[pid] = frozenset(ret[pid])

112 return ret

113

114

115@fem_property

116 class PBAR(SimpleCard):

117 """

118 Simple Beam Property.

119 Defines the properties of a simple beam element (CBAR entry).

120

121 ref: NX Nastran 12 Quick Reference Guide 16-32 (p.2262)

122 """

123

124 DIM = "1d"

125 COMMENTS_KEY = "Property"

126 TABLE = """

127| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

128|------+-----+-----+-----+----+----+----+-----+----+----|

129| PBAR | PID | MID | A | I1 | I2 | J | NSM | |

130| "" | C1 | C2 | D1 | D2 | E1 | E2 | F1 | F2 |

131| "" | K1 | K2 | I12 | | | | | |

132"""

133

134 DEFAULTS = {

135 "K1": 0.0,

136 "K2": 0.0,

137 "A": 0.0,

138 "I1": 0.0,

139 "I2": 0.0,

140 "I12": 0.0,

141 "J": 0.0,

142 "NSM": 0.0,

143 "C1": 0.0,

144 "D1": 0.0,

145 "E1": 0.0,

146 "F1": 0.0,

147 "C2": 0.0,

148 "D2": 0.0,

149 "E2": 0.0,

150 "F2": 0.0,

151 }

152

153

154@fem_property

155 class PSHEAR(SimpleCard):

156 """

157 SHEAR Panel Property

158 Defines the propertires of a shear panel (CSHEAR entry).

159

160 ref: NX Nastran 12 Quick Reference Guide 16-208 (p.2438)

161

162

163 """

164

165 DIM = "2d"

166 COMMENTS_KEY = "Property"

167 TABLE = """

168| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

169|--------+-----+------+------+------+----+-----+----+----+----|

170| PSHEAR | PID | MID | T | NSM | F1 | F2 | | | |

171"""

172 DEFAULTS = {

173 "NSM": 0.0,

174 "F1": 0.0,

175 "F2": 0.0,

176 }

177

178 @cached_property

179 def thk(self):

180 """return a dict of thicknesses"""

181 thks = {

182 pid: thk

183 for pid, thk in zip(

184 self.carddata["main"]["PID"], self.carddata["main"]["T"]

185 )

186 }

187 # thks -> {1: 0.09, 203: 1.9}

188 return {

189 "data": np.array(list(thks.values())),

190 "index": np.array(list(thks.keys())),

191 "name": "pid2thk",

192 }

193

194

195@fem_property

196 class PSHELL(SimpleCard):

197 """

198 Shell Element Property

199 Defines the membrane, bending, transverse shear, and coupling properties of thin

200 shell elements.

201

202 ref: NX Nastran 12 Quick Reference Guide 16-210 (p.2440)

203 """

204

205 DIM = "2d"

206 COMMENTS_KEY = "Property"

207 TABLE = """

208| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

209|--------+-----+------+------+------+----------+------+------+-----+----|

210| PSHELL | PID | MID1 | T | MID2 | 12I/T**3 | MID3 | TS/T | NSM | |

211| | Z1 | Z2 | MID4 | | | | | | |

212"""

213 DEFAULTS = {

214 "12I/T**3": None,

215 "TS/T": None,

216 "NSM": 0.0,

217 "Z1": None,

218 "Z2": None,

219 "MID4": None,

220 }

221

222 @cached_property

223 def thk(self):

224 """return a dict of thicknesses"""

225 thks = {

226 pid: thk

227 for pid, thk in zip(

228 self.carddata["main"]["PID"], self.carddata["main"]["T"]

229 )

230 }

231 # thks -> {1: 0.09, 203: 1.9}

232 return {

233 "data": np.array(list(thks.values())),

234 "index": np.array(list(thks.keys())),

235 "name": "pid2thk",

236 }

237

238

239@fem_property

240 class PROD(SimpleCard):

241 """

242 Rod Property.

243

244 Defines the properties of a rod element (CROD entry).

245

246 ref: NX Nastran 12 Quick Reference Guide 16-206 (p.2436)

247 """

248

249 DIM = "1d"

250 COMMENTS_KEY = "Property"

251 TABLE = """

252 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

253 |------+-----+-----+---+---+---+-----+---+---+----|

254 | PROD | PID | MID | A | J | C | NSM | | | |

255 """

256

257 DEFAULTS = {"C": 0, "NSM": 0}

258

259

260@fem_property

261 class PBUSH(ComplexCard):

262 """

263 Generalized Spring-and-Damper Property

264 Defines the nominal property values for a generalized spring-and-damper structural

265 element.

266

267 ref: NX Nastran 12 Quick Reference Guide 16-83 (p.2800)

268 """

269

270 DIM = "1d"

271 COMMENTS_KEY = "Property"

272 TABLE = """

273 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

274 |-------+-----+-------+-----+-----+-----+-----+-----+-----+----|

275 | PBUSH | PID | "K" | K1 | K2 | K3 | K4 | K5 | K6 | |

276 | | | "B" | B1 | B2 | B3 | B4 | B5 | B6 | |

277 | | | "GE" | GE1 | GE2 | GE3 | GE4 | GE5 | GE6 | |

278 | | | "RCV" | SA | ST | EA | ET | | | |

279 """

280

281 DEFAULTS = {

282 "C": 0,

283 "NSM": 0,

284 '"K"': "K",

285 "K1": None,

286 "K2": None,

287 "K3": None,

288 "K4": None,

289 "K5": None,

290 "K6": None,

291 '"B"': "B",

292 "B1": None,

293 "B2": None,

294 "B3": None,

295 "B4": None,

296 "B5": None,

297 "B6": None,

298 '"GE"': "GE",

299 "GE1": None,

300 "GE2": None,

301 "GE3": None,

302 "GE4": None,

303 "GE5": None,

304 "GE6": None,

305 '"RCV"': "RCV",

306 "SA": None,

307 "ST": None,

308 "EA": None,

309 "ET": None,

310 }

311

312 def clean_sparams(self, sparams):

313 """remove"""

314 Kfields = set((f"K{i}" for i in range(1, 6)))

315 Ks = set((sparams[Ki] for Ki in Kfields))

316 if Ks == {None}:

317 Kfields.add('"K"')

318 for field in Kfields:

319 sparams.pop(field)

320 Bfields = set((f"B{i}" for i in range(1, 6)))

321 Bs = set((sparams[Bi] for Bi in Bfields))

322 if Bs == {None}:

323 Bfields.add('"B"')

324 for field in Bfields:

325 sparams.pop(field)

326 GEfields = set((f"GE{i}" for i in range(1, 6)))

327 GEs = set((sparams[GEi] for GEi in GEfields))

328 if GEs == {None}:

329 GEfields.add('"GE"')

330 for field in GEfields:

331 sparams.pop(field)

332

333

334@fem_property

335 class PBEAM(RepeatedRowsCard):

336 """

337 Beam Property

338 Defines the properties of a beam element (CBEAM entry). This element may be

339 used to model tapered beams.

340

341 ref: NX Nastran 12 Quick Reference Guide 16-48 (p.2278)

342

343 > If SO is “YESA” or “NO”, the third continuation entry, which contains the fields

344 > C1 through F2, must be omitted. If SO is “YES”, the continuation for Ci, Di, Ei,

345 > and Fi must be the next entry.

346

347 > ...

348

349 > The fourth and fifth continuation entries, which contain fields K1 through

350 > N2(B), are optional and may be omitted if the default values are appropriate.

351 """

352

353 DIM = "1d"

354 COMMENTS_KEY = "Property"

355 TABLE = """

356 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

357 |-------+-------+--------+-------+-------+-------+--------+-------+--------+----|

358 | PBEAM | PID | MID | A(A) | I1(A) | I2(A) | I12(A) | J(A) | NSM(A) | |

359 | | C1(A) | C2(A) | D1(A) | D2(A) | E1(A) | E2(A) | F1(A) | F2(A) | |

360 """

361

362 REPEATED_ROWS_TABLE = """

363 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

364 |---+----+------+----+----+----+-----+----+-----+----|

365 | | SO | X/XB | A | I1 | I2 | I12 | J | NSM | |

366 | | C1 | C2 | D1 | D2 | E1 | E2 | F1 | F2 | |

367 """

368

369 TRAILING_ROWS_TABLE = """

370 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |

371 |---+-------+-------+-------+-------+--------+--------+-------+-------+----|

372 | | K1 | K2 | S1 | S2 | NSI(A) | NSI(B) | CW(A) | CW(B) | |

373 | | M1(A) | M2(A) | M1(B) | M2(B) | N1(A) | N2(A) | N1(B) | N2(B) |

374 """

375

376 REPEATED_ROWS_NAME = "stations"

377 TRIGGER_REPEATED_ON = str

378 SKIP_NEXT_ROW_ON = ("SO", ("YESA", "NO"))

379 DEFAULTS = {"I12(A)": 0.0, "J(A)": 0.0, "NSM(A)": 0.0}

380

381

382 if __name__ == "__main__":

383 import doctest

384

385 doctest.testmod(optionflags=doctest.ELLIPSIS)