Coverage for src / sdynpy / fileio / sdynpy_uff_datasets / sdynpy_uff_dataset_55.py: 19%

351 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-11 16:22 +0000

1# -*- coding: utf-8 -*- 

2""" 

3Data at Nodes (Shapes) 

4 

5This dataset defines mode shapes, which are defined as data at nodes. 

6""" 

7""" 

8Copyright 2022 National Technology & Engineering Solutions of Sandia, 

9LLC (NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. 

10Government retains certain rights in this software. 

11 

12This program is free software: you can redistribute it and/or modify 

13it under the terms of the GNU General Public License as published by 

14the Free Software Foundation, either version 3 of the License, or 

15(at your option) any later version. 

16 

17This program is distributed in the hope that it will be useful, 

18but WITHOUT ANY WARRANTY; without even the implied warranty of 

19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

20GNU General Public License for more details. 

21 

22You should have received a copy of the GNU General Public License 

23along with this program. If not, see <https://www.gnu.org/licenses/>. 

24""" 

25 

26from ..sdynpy_uff import UFFReadError, parse_uff_line, parse_uff_lines, write_uff_line 

27import numpy as np 

28 

29_analysis_types = {0:'Unknown', 

30 1:'Static', 

31 2:'Normal Mode', 

32 3:'Complex Mode', 

33 4:'Transient', 

34 5:'Frequency Response', 

35 6:'Buckling', 

36 7:'Complex Mode Second Order'} 

37 

38class Sdynpy_UFF_Dataset_55: 

39 def __init__(self, idline1, idline2, idline3, idline4, idline5, model_type, 

40 analysis_type, data_characteristic, specific_data_type, data_type, 

41 integer_data, real_data, node_data_dictionary): 

42 self.idline1 = idline1 

43 self.idline2 = idline2 

44 self.idline3 = idline3 

45 self.idline4 = idline4 

46 self.idline5 = idline5 

47 self.model_type = model_type 

48 self.analysis_type = analysis_type 

49 self.data_characteristic = data_characteristic 

50 self.specific_data_type = specific_data_type 

51 self.data_type = data_type 

52 self.integer_data = integer_data 

53 self.real_data = real_data 

54 self.node_data_dictionary = node_data_dictionary 

55 

56 @property 

57 def dataset_number(self): 

58 return 55 

59 

60 # For Analysis Type = 0, Unknown 

61 # 

62 # RECORD 7: 

63 #  

64 # FIELD 1: 1 

65 # FIELD 2: 1 

66 # FIELD 3: ID Number - Done 

67 #  

68 # RECORD 8: 

69 #  

70 # FIELD 1: 0.0 

71 # 

72 # For Analysis Type = 1, Static 

73 # 

74 # RECORD 7: 

75 # FIELD 1: 1 

76 # FIELD 2: 1 

77 # FIELD 3: Load Case Number - Done 

78 # 

79 # RECORD 8: 

80 # FIELD 11: 0.0 

81 # 

82 # For Analysis Type = 2, Normal Mode 

83 # 

84 # RECORD 7: 

85 # 

86 # FIELD 1: 2 

87 # FIELD 2: 4 

88 # FIELD 3: Load Case Number - Done 

89 # FIELD 4: Mode Number - Done 

90 # 

91 # RECORD 8: 

92 # FIELD 1: Frequency (Hertz) - Done 

93 # FIELD 2: Modal - Done 

94 # FIELD 3: Modal Viscous Damping Ratio - Done 

95 # FIELD 4: Modal Hysteretic Damping Ratio - Done 

96 # 

97 # For Analysis Type = 3, Complex Eigenvalue 

98 # 

99 # RECORD 7: 

100 # FIELD 1: 2 

101 # FIELD 2: 6 

102 # FIELD 3: Load Case Number - Done 

103 # FIELD 4: Mode Number - Done 

104 # 

105 # RECORD 8: 

106 # 

107 # FIELD 1: Real Part Eigenvalue - Done 

108 # FIELD 2: Imaginary Part Eigenvalue - Done 

109 # FIELD 3: Real Part Of Modal A - Done 

110 # FIELD 4: Imaginary Part Of Modal A - Done 

111 # FIELD 5: Real Part Of Modal B 

112 # FIELD 6: Imaginary Part Of Modal B 

113 # 

114 # 

115 # For Analysis Type = 4, Transient 

116 # 

117 # RECORD 7: 

118 # 

119 # FIELD 1: 2 

120 # FIELD 2: 1 

121 # FIELD 3: Load Case Number - Done 

122 # FIELD 4: Time Step Number - Done 

123 # 

124 # RECORD 8: 

125 # FIELD 1: Time (Seconds) 

126 # 

127 # For Analysis Type = 5, Frequency Response 

128 # 

129 # RECORD 7: 

130 # 

131 # FIELD 1: 2 

132 # FIELD 2: 1 

133 # FIELD 3: Load Case Number - Done 

134 # FIELD 4: Frequency Step Number - Done 

135 # 

136 # RECORD 8: 

137 # FIELD 1: Frequency (Hertz) - Done 

138 # 

139 # For Analysis Type = 6, Buckling 

140 # 

141 # RECORD 7: 

142 # 

143 # FIELD 1: 1 

144 # FIELD 2: 1 

145 # FIELD 3: Load Case Number - Done 

146 # 

147 # RECORD 8: 

148 # 

149 # FIELD 1: Eigenvalue - Done 

150 

151 @property 

152 def id_number(self): 

153 valid_types = [0] 

154 index = 0 

155 if self.analysis_type in valid_types: 

156 return self.integer_data[index] 

157 else: 

158 raise AttributeError( 

159 'id_number field only exists for analysis_types {:}'.format( 

160 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

161 

162 @id_number.setter 

163 def id_number(self, value): 

164 valid_types = [0] 

165 index = 0 

166 if self.analysis_type in valid_types: 

167 try: 

168 self.integer_data[index] = value 

169 except IndexError: 

170 print('Integer data currently only has {:} indices, tried to index {:}, expanding to {:}'.format( 

171 len(self.integer_data), index, index + 1)) 

172 self.integer_data += ((index + 1) - (len(self.integer_data))) * [0] 

173 self.integer_data[index] = value 

174 else: 

175 raise AttributeError( 

176 'id_number field only exists for analysis_types {:}'.format( 

177 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

178 

179 @property 

180 def load_case_number(self): 

181 valid_types = [1,2,3,4,5,6] 

182 index = 0 

183 if self.analysis_type in valid_types: 

184 return self.integer_data[index] 

185 else: 

186 raise AttributeError( 

187 'load_case_number field only exists for analysis_types {:}'.format( 

188 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

189 

190 

191 @load_case_number.setter 

192 def load_case_number(self, value): 

193 valid_types = [1,2,3,4,5,6] 

194 index = 0 

195 if self.analysis_type in valid_types: 

196 try: 

197 self.integer_data[index] = value 

198 except IndexError: 

199 print('Integer data currently only has {:} indices, tried to index {:}, expanding to {:}'.format( 

200 len(self.integer_data), index, index + 1)) 

201 self.integer_data += ((index + 1) - (len(self.integer_data))) * [0] 

202 self.integer_data[index] = value 

203 else: 

204 raise AttributeError( 

205 'load_case_number field only exists for analysis_types {:}'.format( 

206 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

207 

208 @property 

209 def mode_number(self): 

210 valid_types = [2,3] 

211 index = 1 

212 if self.analysis_type in valid_types: 

213 return self.integer_data[index] 

214 else: 

215 raise AttributeError( 

216 'mode_number field only exists for analysis_types {:}'.format( 

217 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

218 

219 

220 @mode_number.setter 

221 def mode_number(self, value): 

222 valid_types = [2,3] 

223 index = 1 

224 if self.analysis_type in valid_types: 

225 try: 

226 self.integer_data[index] = value 

227 except IndexError: 

228 print('Integer data currently only has {:} indices, tried to index {:}, expanding to {:}'.format( 

229 len(self.integer_data), index, index + 1)) 

230 self.integer_data += ((index + 1) - (len(self.integer_data))) * [0] 

231 self.integer_data[index] = value 

232 else: 

233 raise AttributeError( 

234 'mode_number field only exists for analysis_types {:}'.format( 

235 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

236 

237 @property 

238 def time_step_number(self): 

239 valid_types = [4] 

240 index = 1 

241 if self.analysis_type in valid_types: 

242 return self.integer_data[index] 

243 else: 

244 raise AttributeError( 

245 'time_step_number field only exists for analysis_types {:}'.format( 

246 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

247 

248 

249 @time_step_number.setter 

250 def time_step_number(self, value): 

251 valid_types = [4] 

252 index = 1 

253 if self.analysis_type in valid_types: 

254 try: 

255 self.integer_data[index] = value 

256 except IndexError: 

257 print('Integer data currently only has {:} indices, tried to index {:}, expanding to {:}'.format( 

258 len(self.integer_data), index, index + 1)) 

259 self.integer_data += ((index + 1) - (len(self.integer_data))) * [0] 

260 self.integer_data[index] = value 

261 else: 

262 raise AttributeError( 

263 'time_step_number field only exists for analysis_types {:}'.format( 

264 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

265 

266 @property 

267 def frequency_step_number(self): 

268 valid_types = [4] 

269 index = 1 

270 if self.analysis_type in valid_types: 

271 return self.integer_data[index] 

272 else: 

273 raise AttributeError( 

274 'frequency_step_number field only exists for analysis_types {:}'.format( 

275 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

276 

277 

278 @frequency_step_number.setter 

279 def frequency_step_number(self, value): 

280 valid_types = [5] 

281 index = 1 

282 if self.analysis_type in valid_types: 

283 try: 

284 self.integer_data[index] = value 

285 except IndexError: 

286 print('Integer data currently only has {:} indices, tried to index {:}, expanding to {:}'.format( 

287 len(self.integer_data), index, index + 1)) 

288 self.integer_data += ((index + 1) - (len(self.integer_data))) * [0] 

289 self.integer_data[index] = value 

290 else: 

291 raise AttributeError( 

292 'frequency_step_number field only exists for analysis_types {:}'.format( 

293 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

294 

295 @property 

296 def frequency(self): 

297 valid_types = [2,5] 

298 if self.analysis_type in valid_types: 

299 index = 0 

300 return self.real_data[index] 

301 else: 

302 raise AttributeError( 

303 'frequency field only exists for analysis_types {:}'.format( 

304 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

305 

306 

307 @frequency.setter 

308 def frequency(self, value): 

309 valid_types = [2,5] 

310 index = 0 

311 if self.analysis_type in valid_types: 

312 try: 

313 self.real_data[index] = value 

314 except IndexError: 

315 print('Real data currently only has {:} indices, tried to index {:}, expanding to {:}'.format( 

316 len(self.real_data), index, index + 1)) 

317 self.real_data += ((index + 1) - (len(self.real_data))) * [0] 

318 self.real_data[index] = value 

319 else: 

320 raise AttributeError( 

321 'frequency field only exists for analysis_types {:}'.format( 

322 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

323 

324 @property 

325 def modal_mass(self): 

326 valid_types = [2] 

327 index = 1 

328 if self.analysis_type in valid_types: 

329 return self.real_data[index] 

330 else: 

331 raise AttributeError( 

332 'modal_mass field only exists for analysis_types {:}'.format( 

333 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

334 

335 

336 @modal_mass.setter 

337 def modal_mass(self, value): 

338 valid_types = [2] 

339 index = 1 

340 if self.analysis_type in valid_types: 

341 try: 

342 self.real_data[index] = value 

343 except IndexError: 

344 print('Real data currently only has {:} indices, tried to index {:}, expanding to {:}'.format( 

345 len(self.real_data), index, index + 1)) 

346 self.real_data += ((index + 1) - (len(self.real_data))) * [0] 

347 self.real_data[index] = value 

348 else: 

349 raise AttributeError( 

350 'modal_mass field only exists for analysis_types {:}'.format( 

351 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

352 

353 

354 @property 

355 def modal_viscous_damping(self): 

356 valid_types = [2] 

357 index = 2 

358 if self.analysis_type in valid_types: 

359 return self.real_data[index] 

360 else: 

361 raise AttributeError( 

362 'modal_viscous_damping field only exists for analysis_types {:}'.format( 

363 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

364 

365 

366 @modal_viscous_damping.setter 

367 def modal_viscous_damping(self, value): 

368 valid_types = [2] 

369 index = 2 

370 if self.analysis_type in valid_types: 

371 try: 

372 self.real_data[index] = value 

373 except IndexError: 

374 print('Real data currently only has {:} indices, tried to index {:}, expanding to {:}'.format( 

375 len(self.real_data), index, index + 1)) 

376 self.real_data += ((index + 1) - (len(self.real_data))) * [0] 

377 self.real_data[index] = value 

378 else: 

379 raise AttributeError( 

380 'modal_viscous_damping field only exists for analysis_types {:}'.format( 

381 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

382 

383 @property 

384 def modal_hysteretic_damping(self): 

385 valid_types = [2] 

386 index = 3 

387 if self.analysis_type in valid_types: 

388 return self.real_data[index] 

389 else: 

390 raise AttributeError( 

391 'modal_hysteretic_damping field only exists for analysis_types {:}'.format( 

392 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

393 

394 

395 @modal_hysteretic_damping.setter 

396 def modal_hysteretic_damping(self, value): 

397 valid_types = [2] 

398 index = 3 

399 if self.analysis_type in valid_types: 

400 try: 

401 self.real_data[index] = value 

402 except IndexError: 

403 print('Real data currently only has {:} indices, tried to index {:}, expanding to {:}'.format( 

404 len(self.real_data), index, index + 1)) 

405 self.real_data += ((index + 1) - (len(self.real_data))) * [0] 

406 self.real_data[index] = value 

407 else: 

408 raise AttributeError( 

409 'modal_hysteretic_damping field only exists for analysis_types {:}'.format( 

410 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

411 

412 @property 

413 def eigenvalue(self): 

414 valid_types = [3,6] 

415 if self.analysis_type in valid_types: 

416 if self.analysis_type == 3: 

417 return self.real_data[0] + 1j*self.real_data[1] 

418 elif self.analysis_type == 6: 

419 return self.real_data[0] 

420 else: 

421 raise AttributeError( 

422 'eigenvalue field only exists for analysis_types {:}'.format( 

423 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

424 

425 

426 @eigenvalue.setter 

427 def eigenvalue(self, value): 

428 valid_types = [3,6] 

429 if self.analysis_type in valid_types: 

430 if self.analysis_type == 3: 

431 try: 

432 self.real_data[0] = np.real(value) 

433 self.real_data[1] = np.imag(value) 

434 except IndexError: 

435 print('Real data currently only has {:} indices, tried to index {:}, expanding to {:}'.format( 

436 len(self.real_data), 1, 1 + 1)) 

437 self.real_data += ((1 + 1) - (len(self.real_data))) * [0] 

438 self.real_data[0] = np.real(value) 

439 self.real_data[1] = np.imag(value) 

440 if self.analysis_type == 6: 

441 index = 0 

442 try: 

443 self.real_data[index] = value 

444 except IndexError: 

445 print('Real data currently only has {:} indices, tried to index {:}, expanding to {:}'.format( 

446 len(self.real_data), index, index + 1)) 

447 self.real_data += ((index + 1) - (len(self.real_data))) * [0] 

448 self.real_data[index] = value 

449 else: 

450 raise AttributeError( 

451 'eigenvalue field only exists for analysis_types {:}'.format( 

452 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

453 

454 

455 @property 

456 def modal_a(self): 

457 valid_types = [3] 

458 if self.analysis_type in valid_types: 

459 return self.real_data[2] + 1j*self.real_data[3] 

460 else: 

461 raise AttributeError( 

462 'modal_a field only exists for analysis_types {:}'.format( 

463 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

464 

465 

466 @modal_a.setter 

467 def modal_a(self, value): 

468 valid_types = [3] 

469 if self.analysis_type in valid_types: 

470 try: 

471 self.real_data[2] = np.real(value) 

472 self.real_data[3] = np.imag(value) 

473 except IndexError: 

474 print('Real data currently only has {:} indices, tried to index {:}, expanding to {:}'.format( 

475 len(self.real_data), 3, 3 + 1)) 

476 self.real_data += ((3 + 1) - (len(self.real_data))) * [0] 

477 self.real_data[2] = np.real(value) 

478 self.real_data[3] = np.imag(value) 

479 else: 

480 raise AttributeError( 

481 'modal_a field only exists for analysis_types {:}'.format( 

482 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

483 

484 @property 

485 def modal_b(self): 

486 valid_types = [3] 

487 if self.analysis_type in valid_types: 

488 return self.real_data[2] + 1j*self.real_data[3] 

489 else: 

490 raise AttributeError( 

491 'modal_b field only exists for analysis_types {:}'.format( 

492 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

493 

494 

495 @modal_b.setter 

496 def modal_b(self, value): 

497 valid_types = [3] 

498 if self.analysis_type in valid_types: 

499 try: 

500 self.real_data[4] = np.real(value) 

501 self.real_data[5] = np.imag(value) 

502 except IndexError: 

503 print('Real data currently only has {:} indices, tried to index {:}, expanding to {:}'.format( 

504 len(self.real_data), 5, 5 + 1)) 

505 self.real_data += ((5 + 1) - (len(self.real_data))) * [0] 

506 self.real_data[4] = np.real(value) 

507 self.real_data[5] = np.imag(value) 

508 else: 

509 raise AttributeError( 

510 'modal_b field only exists for analysis_types {:}'.format( 

511 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

512 

513 @property 

514 def time(self): 

515 valid_types = [4] 

516 index = 0 

517 if self.analysis_type in valid_types: 

518 return self.real_data[index] 

519 else: 

520 raise AttributeError( 

521 'time field only exists for analysis_types {:}'.format( 

522 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

523 

524 

525 @time.setter 

526 def time(self, value): 

527 valid_types = [4] 

528 index = 0 

529 if self.analysis_type in valid_types: 

530 try: 

531 self.real_data[index] = value 

532 except IndexError: 

533 print('Real data currently only has {:} indices, tried to index {:}, expanding to {:}'.format( 

534 len(self.real_data), index, index + 1)) 

535 self.real_data += ((index + 1) - (len(self.real_data))) * [0] 

536 self.real_data[index] = value 

537 else: 

538 raise AttributeError( 

539 'time field only exists for analysis_types {:}'.format( 

540 ', '.join(['{:} ({:})'.format(t,_analysis_types[t]) for t in valid_types]))) 

541 

542 @classmethod 

543 def from_uff_data_array(cls, data): 

544 # Transform from binary to ascii 

545 data = [line.decode() for line in data] 

546 # RECORD 1: Format (40A2) 

547 # FIELD 1: ID Line 1 

548 idline1, = parse_uff_line(data[0], ['A80']) 

549# RECORD 2: Format (40A2) 

550# FIELD 1: ID Line 2 

551 idline2, = parse_uff_line(data[1], ['A80']) 

552# RECORD 3: Format (40A2) 

553# 

554# FIELD 1: ID Line 3 

555 idline3, = parse_uff_line(data[2], ['A80']) 

556# RECORD 4: Format (40A2) 

557# FIELD 1: ID Line 4 

558 idline4, = parse_uff_line(data[3], ['A80']) 

559# RECORD 5: Format (40A2) 

560# FIELD 1: ID Line 5 

561 idline5, = parse_uff_line(data[4], ['A80']) 

562# RECORD 6: Format (6I10) 

563# 

564# Data Definition Parameters 

565# 

566# FIELD 1: Model Type 

567# 0: Unknown 

568# 1: Structural 

569# 2: Heat Transfer 

570# 3: Fluid Flow 

571# 

572# FIELD 2: Analysis Type 

573# 0: Unknown 

574# 1: Static 

575# 2: Normal Mode 

576# 3: Complex eigenvalue first order 

577# 4: Transient 

578# 5: Frequency Response 

579# 6: Buckling 

580# 7: Complex eigenvalue second order 

581# 

582# FIELD 3: Data Characteristic 

583# 0: Unknown 

584# 1: Scalar 

585# 2: 3 DOF Global Translation 

586# Vector 

587# 3: 6 DOF Global Translation 

588# & Rotation Vector 

589# 4: Symmetric Global Tensor 

590# 5: General Global Tensor 

591# 

592# 

593# 

594# FIELD 4: Specific Data Type 

595# 0: Unknown 

596# 1: General 

597# 2: Stress 

598# 3: Strain (Engineering) 

599# 4: Element Force 

600# 5: Temperature 

601# 6: Heat Flux 

602# 7: Strain Energy 

603# 8: Displacement 

604# 9: Reaction Force 

605# 10: Kinetic Energy 

606# 11: Velocity 

607# 12: Acceleration 

608# 13: Strain Energy Density 

609# 14: Kinetic Energy Density 

610# 15: Hydro-Static Pressure 

611# 16: Heat Gradient 

612# 17: Code Checking Value 

613# 18: Coefficient Of Pressure 

614# 

615# FIELD 5: Data Type 

616# 2: Real 

617# 5: Complex 

618# 

619# FIELD 6: Number Of Data Values Per Node (NDV) 

620 model_type, analysis_type, data_characteristic, specific_data_type, data_type, num_data_per_node = ( 

621 parse_uff_line(data[5], 6 * ['I10'])) 

622 # Correct the number of values per node 

623 if data_characteristic == 2: 

624 required_data_values = 3 

625 if num_data_per_node != required_data_values: 

626 print('Warning: Dataset 55 has data characteristic "3 DOF Global Translation" but has {:} data values per node (should be {:})\nUniversal File is formatted incorrectly.'.format(num_data_per_node,required_data_values)) 

627 num_data_per_node = required_data_values 

628 elif data_characteristic == 3: 

629 required_data_values = 6 

630 if num_data_per_node != required_data_values: 

631 print('Warning: Dataset 55 has data characteristic "3 DOF Global Translation & Rotation Vector" but has {:} data values per node (should be {:})\nUniversal File is formatted incorrectly.'.format(num_data_per_node,required_data_values)) 

632 num_data_per_node = required_data_values 

633 elif data_characteristic == 4: 

634 required_data_values = 6 

635 if num_data_per_node != required_data_values: 

636 print('Warning: Dataset 55 has data characteristic "Symmetric GLobal Tensor" but has {:} data values per node (should be {:})\nUniversal File is formatted incorrectly.'.format(num_data_per_node,required_data_values)) 

637 num_data_per_node = required_data_values 

638 elif data_characteristic == 5: 

639 required_data_values = 9 

640 if num_data_per_node != required_data_values: 

641 print('Warning: Dataset 55 has data characteristic "General GLobal Tensor" but has {:} data values per node (should be {:})\nUniversal File is formatted incorrectly.'.format(num_data_per_node,required_data_values)) 

642 num_data_per_node = required_data_values 

643 data_is_complex = data_type == 5 

644# Records 7 And 8 Are Analysis Type Specific 

645# 

646# General Form 

647# 

648# RECORD 7: Format (8I10) 

649# 

650# FIELD 1: Number Of Integer Data Values 

651# 1 < Or = Nint < Or = 10 

652# FIELD 2: Number Of Real Data Values 

653# 1 < Or = Nrval < Or = 12 

654# FIELDS 3-N: Type Specific Integer Parameters 

655# 

656# 

657# RECORD 8: Format (6E13.5) 

658# FIELDS 1-N: Type Specific Real Parameters 

659 # Initially just read the number of data that we will get 

660 nints, nreals = parse_uff_line(data[6], 2 * ['I10']) 

661 # Now read the whole of record 7 

662 int_data, lines_read = parse_uff_lines(data[6:], 8 * ['I10'], 2 + nints) 

663 next_line = 6 + lines_read 

664 integer_data = int_data[2:] 

665 real_data, lines_read = parse_uff_lines(data[next_line:], 6 * ['E13.5'], nreals) 

666 next_line += lines_read 

667# RECORD 9: Format (I10) 

668# 

669# FIELD 1: Node Number 

670# 

671# RECORD 10: Format (6E13.5) 

672# FIELDS 1-N: Data At This Node (NDV Real Or 

673# Complex Values) 

674# 

675# Records 9 And 10 Are Repeated For Each Node. 

676 node_data_dictionary = {} 

677 while next_line < len(data): 

678 node, = parse_uff_line(data[next_line], ['I10']) 

679 next_line += 1 

680 node_data, read_lines = parse_uff_lines( 

681 data[next_line:], 6 * ['E13.5'], num_data_per_node * (2 if data_is_complex else 1)) 

682 next_line += read_lines 

683 if data_is_complex: 

684 node_data_dictionary[node] = [real + 1j * 

685 imag for real, imag in zip(node_data[::2], node_data[1::2])] 

686 else: 

687 node_data_dictionary[node] = node_data 

688 

689 ds_55 = cls(idline1, idline2, idline3, idline4, idline5, model_type, 

690 analysis_type, data_characteristic, specific_data_type, data_type, 

691 integer_data, real_data, node_data_dictionary) 

692 return ds_55 

693 

694 def __repr__(self): 

695 return 'Sdynpy_UFF_Dataset_55<Shape with {:} Nodes>'.format(len(self.node_data_dictionary)) 

696 

697 def write_string(self): 

698 return_string = '' 

699 # See how many values are in each node 

700 nodes = [key for key in self.node_data_dictionary.keys()] 

701 if self.data_type == 5: 

702 num_data = int(len(self.node_data_dictionary[nodes[0]]) // 2) 

703 else: 

704 num_data = int(len(self.node_data_dictionary[nodes[0]])) 

705 return_string += write_uff_line(['NONE' if self.idline1 == '' else self.idline1, 

706 'NONE' if self.idline2 == '' else self.idline2, 

707 'NONE' if self.idline3 == '' else self.idline3, 

708 'NONE' if self.idline4 == '' else self.idline4, 

709 'NONE' if self.idline5 == '' else self.idline5, 

710 ], ['A80']) 

711 return_string += write_uff_line([self.model_type, 

712 self.analysis_type, 

713 self.data_characteristic, 

714 self.specific_data_type, 

715 self.data_type, 

716 num_data], 6 * ['I10']) 

717 nint = len(self.integer_data) 

718 nfloat = len(self.real_data) 

719 return_string += write_uff_line([nint, nfloat] + self.integer_data, 

720 8 * ['I10']) 

721 return_string += write_uff_line(self.real_data, 6 * ['E13.5']) 

722 for node in sorted(self.node_data_dictionary.keys()): 

723 return_string += write_uff_line([node], ['I10']) 

724 return_string += write_uff_line( 

725 [fn(val) for val in self.node_data_dictionary[node] for fn in (np.real, np.imag)] 

726 if np.iscomplexobj(self.node_data_dictionary[node]) 

727 else self.node_data_dictionary[node], 6 * ['E13.5']) 

728 return return_string 

729 

730 def __str__(self): 

731 lines = self.write_string().split('\n') 

732 if len(lines) > 8: 

733 return 'Dataset 55: Shapes\n ' + '\n '.join(lines[0:5] + ['.', '.', '.']) 

734 else: 

735 return 'Dataset 55: Shapes\n ' + '\n '.join(lines) 

736 

737 

738def read(data): 

739 return Sdynpy_UFF_Dataset_55.from_uff_data_array(data)