Coverage for src / beamme / four_c / header_functions.py: 90%

103 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-21 12:57 +0000

1# The MIT License (MIT) 

2# 

3# Copyright (c) 2018-2025 BeamMe Authors 

4# 

5# Permission is hereby granted, free of charge, to any person obtaining a copy 

6# of this software and associated documentation files (the "Software"), to deal 

7# in the Software without restriction, including without limitation the rights 

8# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 

9# copies of the Software, and to permit persons to whom the Software is 

10# furnished to do so, subject to the following conditions: 

11# 

12# The above copyright notice and this permission notice shall be included in 

13# all copies or substantial portions of the Software. 

14# 

15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 

16# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 

17# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 

18# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 

19# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 

20# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 

21# THE SOFTWARE. 

22"""This module defines functions that can be used to add header information to 

23an input file.""" 

24 

25from typing import Any as _Any 

26from typing import List as _List 

27from typing import Union as _Union 

28 

29from beamme.core.conf import bme as _bme 

30from beamme.four_c.input_file import InputFile as _InputFile 

31 

32 

33def _get_segmentation_strategy(segmentation): 

34 """Get the 4C string for a geometry pair strategy.""" 

35 if segmentation: 

36 return "segmentation" 

37 else: 

38 return "gauss_point_projection_without_boundary_segmentation" 

39 

40 

41def set_runtime_output( 

42 input_file, 

43 *, 

44 output_solid=True, 

45 output_stress_strain=False, 

46 btsvmt_output=True, 

47 btss_output=True, 

48 output_triad=True, 

49 every_iteration=False, 

50 absolute_beam_positions=True, 

51 element_owner=True, 

52 element_gid=True, 

53 element_mat_id=True, 

54 output_energy=False, 

55 output_strains=True, 

56): 

57 """Set the basic runtime output options. 

58 

59 Args 

60 ---- 

61 input_file: 

62 Input file that the options will be added to. 

63 output_solid: bool 

64 If the solid output should be written at runtime. 

65 output_stress_strain: bool 

66 If stress and strain output should be written for the solid. 

67 btsvmt_output: bool 

68 If the output for btsvmt should be written. 

69 btss_output: bool 

70 If the output for beam-to-surface coupling should be written. 

71 output_triad: bool 

72 If the triads along the beam should be written. 

73 every_iteration: int 

74 If output at every Newton iteration should be written. 

75 absolute_beam_positions: bool 

76 If the beams should be written at the current position or always at 

77 the reference position. 

78 element_owner: bool 

79 If the owing rank of each element should be output (currently 

80 only affects the solid elements in 4C, beam element owners are 

81 written by default). 

82 element_gid: bool 

83 If the 4C internal GID of each element should be output. 

84 element_mat_id: bool 

85 If the 4C internal material ID of each element should be output. 

86 output_energy: bool 

87 If the energy output from 4C should be activated. 

88 output_strains: bool 

89 If the strains in the Gauss points should be output. 

90 """ 

91 

92 # Set the basic runtime output options. 

93 input_file.add( 

94 { 

95 "IO/RUNTIME VTK OUTPUT": { 

96 "OUTPUT_DATA_FORMAT": "binary", 

97 "INTERVAL_STEPS": 1, 

98 "EVERY_ITERATION": every_iteration, 

99 } 

100 } 

101 ) 

102 

103 # Set the structure runtime output options 

104 input_file.add( 

105 { 

106 "IO/RUNTIME VTK OUTPUT/STRUCTURE": { 

107 "OUTPUT_STRUCTURE": output_solid, 

108 "DISPLACEMENT": True, 

109 "STRESS_STRAIN": output_stress_strain, 

110 "ELEMENT_OWNER": element_owner, 

111 "ELEMENT_GID": element_gid, 

112 "ELEMENT_MAT_ID": element_mat_id, 

113 } 

114 } 

115 ) 

116 

117 # Set the beam runtime output options 

118 input_file.add( 

119 { 

120 "IO/RUNTIME VTK OUTPUT/BEAMS": { 

121 "OUTPUT_BEAMS": True, 

122 "DISPLACEMENT": True, 

123 "USE_ABSOLUTE_POSITIONS": absolute_beam_positions, 

124 "TRIAD_VISUALIZATIONPOINT": output_triad, 

125 "STRAINS_GAUSSPOINT": output_strains, 

126 "ELEMENT_GID": element_gid, 

127 } 

128 } 

129 ) 

130 

131 if btsvmt_output: 

132 # Set the beam to solid volume mesh tying runtime output options. 

133 input_file.add( 

134 { 

135 "BEAM INTERACTION/BEAM TO SOLID VOLUME MESHTYING/RUNTIME VTK OUTPUT": { 

136 "WRITE_OUTPUT": True, 

137 "NODAL_FORCES": True, 

138 "MORTAR_LAMBDA_DISCRET": True, 

139 "MORTAR_LAMBDA_CONTINUOUS": True, 

140 "MORTAR_LAMBDA_CONTINUOUS_SEGMENTS": 5, 

141 "SEGMENTATION": True, 

142 "INTEGRATION_POINTS": True, 

143 } 

144 } 

145 ) 

146 

147 if btss_output: 

148 # Set the beam to solid surface coupling runtime output options. 

149 input_file.add( 

150 { 

151 "BEAM INTERACTION/BEAM TO SOLID SURFACE/RUNTIME VTK OUTPUT": { 

152 "WRITE_OUTPUT": True, 

153 "NODAL_FORCES": True, 

154 "MORTAR_LAMBDA_DISCRET": True, 

155 "MORTAR_LAMBDA_CONTINUOUS": True, 

156 "MORTAR_LAMBDA_CONTINUOUS_SEGMENTS": 5, 

157 "SEGMENTATION": True, 

158 "INTEGRATION_POINTS": True, 

159 "AVERAGED_NORMALS": True, 

160 } 

161 } 

162 ) 

163 

164 if output_energy: 

165 input_file["STRUCTURAL DYNAMIC"]["RESEVERYERGY"] = 1 

166 

167 

168def set_beam_to_solid_meshtying( 

169 input_file, 

170 interaction_type, 

171 *, 

172 contact_discretization=None, 

173 segmentation=True, 

174 segmentation_search_points=2, 

175 couple_restart=False, 

176 mortar_shape="none", 

177 n_gauss_points=6, 

178 n_integration_points_circ=None, 

179 penalty_parameter=None, 

180 coupling_type=None, 

181 binning_parameters: dict = {}, 

182): 

183 """Set the beam to solid meshtying options. 

184 

185 Args 

186 ---- 

187 input_file: 

188 Input file that the options will be added to. 

189 interaction_type: BoundaryCondition 

190 Type of beam-to-solid interaction. 

191 contact_discretization: str 

192 Type of contact (mortar, Gauss point, ...) 

193 segmentation: bool 

194 If segmentation should be used in the numerical integration. 

195 segmentation_search_points: int 

196 Number of search points for segmentation. 

197 couple_restart: bool 

198 If the restart configuration should be used for the coupling 

199 mortar_shape: str 

200 Type of shape function for mortar discretization. 

201 n_gauss_points: int 

202 Number of Gauss points for numerical integration. 

203 n_integration_points_circ: int 

204 Number of integration points along the circumference of the cross 

205 section. 

206 penalty_parameter: float 

207 Penalty parameter for contact enforcement. 

208 coupling_type: str 

209 Type of coupling for beam-to-surface coupling. 

210 binning_parameters: 

211 Keyword parameters for the binning section 

212 """ 

213 

214 # Set the beam contact options. 

215 # check if these keys are already set, otherwise set them 

216 if ( 

217 "BEAM INTERACTION" not in input_file 

218 or input_file["BEAM INTERACTION"].get("REPARTITIONSTRATEGY") != "everydt" 

219 ): 

220 input_file.add({"BEAM INTERACTION": {"REPARTITIONSTRATEGY": "everydt"}}) 

221 

222 if ( 

223 "BEAM CONTACT" not in input_file 

224 or input_file["BEAM CONTACT"].get("MODELEVALUATOR") != "Standard" 

225 ): 

226 input_file.add({"BEAM CONTACT": {"MODELEVALUATOR": "Standard"}}) 

227 

228 set_binning_strategy_section( 

229 input_file, 

230 **binning_parameters, 

231 ) 

232 

233 # Add the beam to solid volume mesh tying options. 

234 bts_parameters = {} 

235 if interaction_type == _bme.bc.beam_to_solid_volume_meshtying: 

236 bts_section_name = "BEAM INTERACTION/BEAM TO SOLID VOLUME MESHTYING" 

237 elif interaction_type == _bme.bc.beam_to_solid_surface_meshtying: 

238 bts_section_name = "BEAM INTERACTION/BEAM TO SOLID SURFACE MESHTYING" 

239 if coupling_type is not None: 

240 bts_parameters["COUPLING_TYPE"] = coupling_type 

241 else: 

242 raise ValueError( 

243 "Got wrong beam-to-solid mesh tying type. " 

244 f"Got {interaction_type} of type {type(interaction_type)}." 

245 ) 

246 bts_parameters["CONSTRAINT_STRATEGY"] = "penalty" 

247 if penalty_parameter is not None: 

248 bts_parameters["PENALTY_PARAMETER"] = penalty_parameter 

249 bts_parameters["GAUSS_POINTS"] = n_gauss_points 

250 

251 if contact_discretization == "mortar": 

252 bts_parameters["CONTACT_DISCRETIZATION"] = "mortar" 

253 bts_parameters["MORTAR_SHAPE_FUNCTION"] = mortar_shape 

254 segmentation_strategy = _get_segmentation_strategy(segmentation) 

255 elif contact_discretization == "gp": 

256 bts_parameters["CONTACT_DISCRETIZATION"] = "gauss_point_to_segment" 

257 segmentation_strategy = _get_segmentation_strategy(segmentation) 

258 elif contact_discretization == "circ": 

259 bts_parameters["CONTACT_DISCRETIZATION"] = "gauss_point_cross_section" 

260 bts_parameters["INTEGRATION_POINTS_CIRCUMFERENCE"] = n_integration_points_circ 

261 segmentation_strategy = "gauss_point_projection_cross_section" 

262 else: 

263 raise ValueError( 

264 f'Wrong contact_discretization "{contact_discretization}" given!' 

265 ) 

266 

267 bts_parameters["GEOMETRY_PAIR_STRATEGY"] = segmentation_strategy 

268 bts_parameters["GEOMETRY_PAIR_SEGMENTATION_SEARCH_POINTS"] = ( 

269 segmentation_search_points 

270 ) 

271 if interaction_type == _bme.bc.beam_to_solid_volume_meshtying: 

272 bts_parameters["COUPLE_RESTART_STATE"] = couple_restart 

273 

274 input_file.add({bts_section_name: bts_parameters}) 

275 

276 

277def set_header_static( 

278 input_file: _InputFile, 

279 *, 

280 time_step: float | None = None, 

281 n_steps: int | None = None, 

282 total_time: float | None = None, 

283 max_iter: int = 20, 

284 tol_residuum: float = 1e-8, 

285 tol_increment: float = 1e-10, 

286 load_lin: bool = False, 

287 write_bin: bool = False, 

288 write_stress: str = "no", 

289 write_strain: str = "no", 

290 prestress: str = "None", 

291 prestress_time: float = 0, 

292 create_nox_file: bool = True, 

293): 

294 """Set the default parameters for a static structure analysis. 

295 

296 At least two of the three time stepping keyword arguments ["time_step", 

297 "n_steps", "total_time"] have to be set. 

298 

299 Args: 

300 input_file: 

301 Input file that the options will be added to. 

302 time_step: 

303 Time increment per step. 

304 n_steps: 

305 Number of time steps. 

306 total_time: 

307 Total time of simulation 

308 max_iter: 

309 Maximal number of Newton iterations. 

310 tol_residuum: 

311 Tolerance for the convergence of the residuum. 

312 tol_increment: 

313 Tolerance for the convergence of the displacement increment. 

314 load_lin: 

315 If the load_lin option should be set. 

316 write_bin: 

317 If binary output should be written. 

318 write_stress: 

319 If and which stress output to write 

320 write_strain: 

321 If and which strain output to write 

322 prestress: 

323 Type of prestressing strategy to be used 

324 prestress_time: 

325 Prestress Time 

326 create_nox_file: 

327 If the nonlinear solver parameters should be set via a NOX xml file or 

328 directly in the input file. 

329 """ 

330 

331 input_file_parameters: dict[str, _Any] = {} 

332 

333 # Set the parameters for a static analysis. 

334 input_file_parameters["PROBLEM TYPE"] = {"PROBLEMTYPE": "Structure"} 

335 input_file_parameters["IO"] = { 

336 "OUTPUT_BIN": write_bin, 

337 "STRUCT_DISP": False, 

338 "STRUCT_STRESS": write_stress, 

339 "STRUCT_STRAIN": write_strain, 

340 "VERBOSITY": "Standard", 

341 } 

342 

343 # Set the time step parameters 

344 given_time_arguments = sum( 

345 1 for arg in (time_step, n_steps, total_time) if arg is not None 

346 ) 

347 if given_time_arguments < 2: 

348 raise ValueError( 

349 'At least two of the following arguments "time_step", "n_steps" or ' 

350 '"total_time" are required' 

351 ) 

352 elif time_step is None and total_time is not None and n_steps is not None: 

353 time_step = total_time / n_steps 

354 elif n_steps is None and total_time is not None and time_step is not None: 

355 n_steps = round(total_time / time_step) 

356 elif total_time is None and time_step is not None and n_steps is not None: 

357 total_time = time_step * n_steps 

358 

359 input_file_parameters["STRUCTURAL DYNAMIC"] = { 

360 "LINEAR_SOLVER": 1, 

361 "INT_STRATEGY": "Standard", 

362 "DYNAMICTYPE": "Statics", 

363 "PREDICT": "TangDis", 

364 "PRESTRESS": prestress, 

365 "PRESTRESSTIME": prestress_time, 

366 "TIMESTEP": time_step, 

367 "NUMSTEP": n_steps, 

368 "MAXTIME": total_time, 

369 "LOADLIN": load_lin, 

370 } 

371 input_file_parameters["SOLVER 1"] = { 

372 "NAME": "Structure_Solver", 

373 "SOLVER": "Superlu", 

374 } 

375 

376 # Set the solver parameters. 

377 if create_nox_file: 

378 # Set the contents of the NOX xml file. 

379 nox_xml_contents = f""" 

380 <ParameterList name="Status Test"> 

381 <!-- Outer Status Test: This test is an OR combination of the structural convergence and the maximum number of iterations --> 

382 <ParameterList name="Outer Status Test"> 

383 <Parameter name="Test Type" type="string" value="Combo"/> 

384 <Parameter name="Combo Type" type="string" value="OR" /> 

385 <!-- Structural convergence is an AND combination of the residuum and step update --> 

386 <ParameterList name="Test 0"> 

387 <Parameter name="Test Type" type="string" value="Combo" /> 

388 <Parameter name="Combo Type" type="string" value="AND" /> 

389 <!-- BEGIN: Combo AND - Test 0: "NormF" --> 

390 <ParameterList name="Test 0"> 

391 <Parameter name="Test Type" type="string" value="NormF" /> 

392 <!-- NormF - Quantity 0: Check the right-hand-side norm of the structural quantities --> 

393 <ParameterList name="Quantity 0"> 

394 <Parameter name="Quantity Type" type="string" value="Structure" /> 

395 <Parameter name="Tolerance Type" type="string" value="Absolute" /> 

396 <Parameter name="Tolerance" type="double" value="{tol_residuum}" /> 

397 <Parameter name="Norm Type" type="string" value="Two Norm" /> 

398 <Parameter name="Scale Type" type="string" value="Scaled" /> 

399 </ParameterList> 

400 </ParameterList> 

401 <!-- END: Combo AND - Test 0: "NormF" --> 

402 <!-- BEGIN: Combo AND - Test 1: "NormWRMS" --> 

403 <ParameterList name="Test 1"> 

404 <Parameter name="Test Type" type="string" value="NormUpdate" /> 

405 <!-- NormWRMS - Quantity 0: Check the increment of the structural displacements --> 

406 <ParameterList name="Quantity 0"> 

407 <Parameter name="Quantity Type" type="string" value="Structure" /> 

408 <Parameter name="Tolerance Type" type="string" value="Absolute" /> 

409 <Parameter name="Tolerance" type="double" value="{tol_increment}" /> 

410 <Parameter name="Norm Type" type="string" value="Two Norm" /> 

411 <Parameter name="Scale Type" type="string" value="Scaled" /> 

412 </ParameterList> 

413 </ParameterList> 

414 <!-- END: Combo AND - Test 1: "NormWRMS" --> 

415 </ParameterList> 

416 <!-- END: Combo 0 - Test 0: "Combo" --> 

417 <!-- BEGIN: Combo OR - Test 1: "MaxIters" --> 

418 <ParameterList name="Test 1"> 

419 <Parameter name="Test Type" type="string" value="MaxIters" /> 

420 <Parameter name="Maximum Iterations" type="int" value="{max_iter}" /> 

421 </ParameterList> <!--END: "MaxIters" --> 

422 </ParameterList> 

423 </ParameterList> 

424 """ 

425 

426 input_file_parameters["STRUCT NOX/Printing"] = { 

427 "Error": True, 

428 "Inner Iteration": False, 

429 "Details": True, 

430 "Linear Solver Details": True, 

431 "Test Details": True, 

432 } 

433 

434 # Set the xml content in the input file. 

435 input_file.nox_xml_contents = nox_xml_contents 

436 

437 else: 

438 input_file_parameters["STRUCTURAL DYNAMIC"]["MAXITER"] = max_iter 

439 input_file_parameters["STRUCTURAL DYNAMIC"]["TOLRES"] = tol_residuum 

440 input_file_parameters["STRUCTURAL DYNAMIC"]["TOLDISP"] = tol_increment 

441 

442 input_file.add(input_file_parameters) 

443 

444 

445def set_binning_strategy_section( 

446 input_file: _InputFile, 

447 binning_bounding_box: _Union[_List[int], None] = None, 

448 binning_cutoff_radius: _Union[float, None] = None, 

449): 

450 """Set binning strategy in section of the input file. 

451 

452 Args 

453 ---- 

454 input_file: 

455 Input file that the options will be added to. 

456 binning_bounding_box: 

457 List with the limits of the bounding box. 

458 binning_cutoff_radius: 

459 Maximal influence radius of pair elements. 

460 """ 

461 

462 if binning_bounding_box is not None and binning_cutoff_radius is not None: 

463 binning_bounding_box_string = " ".join( 

464 [str(val) for val in binning_bounding_box] 

465 ) 

466 

467 input_file.add( 

468 { 

469 "BINNING STRATEGY": { 

470 "BIN_SIZE_LOWER_BOUND": binning_cutoff_radius, 

471 "DOMAINBOUNDINGBOX": binning_bounding_box_string, 

472 } 

473 } 

474 ) 

475 elif [binning_bounding_box, binning_cutoff_radius].count(None) == 2: 

476 return 

477 else: 

478 raise ValueError( 

479 f"The variables binning_bounding_box {binning_bounding_box} and binning_cutoff_radius {binning_cutoff_radius} must both be set." 

480 ) 

481 

482 

483def set_beam_interaction_section( 

484 input_file: _InputFile, 

485 *, 

486 repartition_strategy: str = "everydt", 

487 search_strategy: str = "bounding_volume_hierarchy", 

488): 

489 """Set beam interaction section in input file. 

490 

491 Args 

492 ---- 

493 input_file: 

494 Input file that the options will be added to. 

495 repartition_strategy: 

496 Type of employed repartitioning strategy 

497 Options: "adaptive" or "everydt" 

498 search_strategy: 

499 Type of search strategy used for finding coupling pairs. 

500 Options: "bruteforce_with_binning", "bounding_volume_hierarchy" 

501 """ 

502 

503 input_file.add( 

504 { 

505 "BEAM INTERACTION": { 

506 "REPARTITIONSTRATEGY": repartition_strategy, 

507 "SEARCH_STRATEGY": search_strategy, 

508 } 

509 } 

510 ) 

511 

512 

513def set_beam_contact_runtime_output( 

514 input_file: _InputFile, *, every_iteration: bool = False 

515): 

516 """Output the beam-to-beam contact forces and gaps with runtime output. 

517 

518 input_file: 

519 Input file that the options will be added to. 

520 every_iteration: 

521 If output at every Newton iteration should be written. 

522 """ 

523 

524 input_file.add( 

525 { 

526 "BEAM CONTACT/RUNTIME VTK OUTPUT": { 

527 "VTK_OUTPUT_BEAM_CONTACT": True, 

528 "EVERY_ITERATION": every_iteration, 

529 "INTERVAL_STEPS": 1, 

530 "CONTACT_FORCES": True, 

531 "GAPS": True, 

532 } 

533 } 

534 ) 

535 

536 

537def set_beam_contact_section( 

538 input_file: _InputFile, 

539 *, 

540 interaction_strategy: str = "penalty", 

541 btb_penalty: float = 0, 

542 btb_line_penalty: float = 0, 

543 per_shift_angle: list[float] = [70, 80], 

544 par_shift_angle: list[float] = [70, 80], 

545 b_seg_angle: float = 12, 

546 num_integration: int = 5, 

547 penalty_law: str = "LinPosQuadPen", 

548 penalty_regularization_g0: float = 0, 

549 penalty_regularization_f0: float = 0, 

550 penalty_regularization_c0: float = 0, 

551 binning_parameters: dict = {}, 

552 beam_interaction_parameters: dict = {}, 

553): 

554 """Set default beam contact section, for more and updated details see 

555 respective input file within 4C. Parameters for set_binning_strategy and 

556 set_beam_interaction may be forwarded as keyword arguments. 

557 

558 Args 

559 ---- 

560 input_file: 

561 Input file that the options will be added to. 

562 interaction_strategy: 

563 Type of employed solving strategy 

564 Options: "none", "penalty" or "gmshonly" 

565 btb_penalty: double 

566 Penalty parameter for beam-to-beam point contact 

567 btb_line_penalty: 

568 Penalty parameter per unit length for beam-to-beam line contact 

569 per_shift_angle: 

570 Lower and upper shift angle (in degrees) for penalty scaling of large-angle-contact 

571 par_shift_angle: 

572 Lower and upper shift angle (in degrees) for penalty scaling of small-angle-contact 

573 b_seg_angle: 

574 Maximal angle deviation allowed for contact search segmentation 

575 num_integration: 

576 Number of integration intervals per element 

577 penalty_law: 

578 Penalty Law Options: "LinPen", "QuadPen", "LinNegQuadPen", "LinPosQuadPen", "LinPosCubPen", "LinPosDoubleQuadPen", "LinPosExpPen" 

579 penalty_regularization_g0: 

580 First penalty regularization parameter G0 

581 penalty_regularization_f0: 

582 Second penalty regularization parameter F0 

583 penalty_regularization_c0: 

584 Third penalty regularization parameter C0 

585 binning_parameters: 

586 Keyword parameters for the binning section 

587 beam_interaction_parameters: 

588 Keyword parameters for the beam-contact section 

589 """ 

590 

591 if len(per_shift_angle) != 2: 

592 raise ValueError( 

593 "Please provide lower and upper value of BEAMS_PERPSHIFTANGLE." 

594 ) 

595 

596 if len(par_shift_angle) != 2: 

597 raise ValueError("Please provide lower and upper value of BEAMS_PARSHIFTANGLE.") 

598 

599 input_file.add( 

600 { 

601 "BEAM INTERACTION/BEAM TO BEAM CONTACT": { 

602 "STRATEGY": interaction_strategy, 

603 } 

604 } 

605 ) 

606 

607 input_file.add( 

608 { 

609 "BEAM CONTACT": { 

610 "MODELEVALUATOR": "standard", 

611 "BEAMS_STRATEGY": "penalty", 

612 "BEAMS_BTBPENALTYPARAM": btb_penalty, 

613 "BEAMS_BTBLINEPENALTYPARAM": btb_line_penalty, 

614 "BEAMS_SEGCON": True, 

615 "BEAMS_PERPSHIFTANGLE1": per_shift_angle[0], 

616 "BEAMS_PERPSHIFTANGLE2": per_shift_angle[1], 

617 "BEAMS_PARSHIFTANGLE1": par_shift_angle[0], 

618 "BEAMS_PARSHIFTANGLE2": par_shift_angle[1], 

619 "BEAMS_SEGANGLE": b_seg_angle, 

620 "BEAMS_NUMINTEGRATIONINTERVAL": num_integration, 

621 "BEAMS_PENALTYLAW": penalty_law, 

622 "BEAMS_PENREGPARAM_G0": penalty_regularization_g0, 

623 "BEAMS_PENREGPARAM_F0": penalty_regularization_f0, 

624 "BEAMS_PENREGPARAM_C0": penalty_regularization_c0, 

625 "BEAMS_MAXDELTADISSCALEFAC": -1.0, 

626 } 

627 } 

628 ) 

629 

630 # beam contact needs a binning strategy 

631 set_binning_strategy_section(input_file, **binning_parameters) 

632 

633 # beam contact needs interaction strategy 

634 set_beam_interaction_section(input_file, **beam_interaction_parameters) 

635 

636 

637def add_result_description( 

638 input_file: _InputFile, 

639 displacements: _List, 

640 node_ids: _List[int], 

641 *, 

642 tol: float = 1e-10, 

643): 

644 """Add result descriptions for structure problems to the input file. 

645 

646 Args: 

647 input_file: Input file to add the result description to 

648 displacements: Array with the displacements (n_nodes x 3) 

649 node_ids: List with the IDs of the nodes to check 

650 tol: Tolerance 

651 """ 

652 result_descriptions = [] 

653 

654 for i_node, node in enumerate(node_ids): 

655 for i_dir, direction in enumerate(["x", "y", "z"]): 

656 result_descriptions.append( 

657 { 

658 "STRUCTURE": { 

659 "DIS": "structure", 

660 "NODE": node, 

661 "QUANTITY": f"disp{direction}", 

662 "VALUE": displacements[i_node][i_dir], 

663 "TOLERANCE": tol, 

664 }, 

665 } 

666 ) 

667 

668 input_file.add({"RESULT DESCRIPTION": result_descriptions})