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

99 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-08 11:03 +0000

1# The MIT License (MIT) 

2# 

3# Copyright (c) 2018-2026 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 

27 

28from beamme.core.conf import bme as _bme 

29from beamme.four_c.input_file import InputFile as _InputFile 

30 

31 

32def _get_segmentation_strategy(segmentation): 

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

34 if segmentation: 

35 return "segmentation" 

36 else: 

37 return "gauss_point_projection_without_boundary_segmentation" 

38 

39 

40def set_runtime_output( 

41 input_file, 

42 *, 

43 output_solid=True, 

44 output_stress_strain=False, 

45 btsvmt_output=True, 

46 btss_output=True, 

47 output_triad=True, 

48 every_iteration=False, 

49 absolute_beam_positions=True, 

50 element_owner=True, 

51 element_gid=True, 

52 element_mat_id=True, 

53 output_energy=False, 

54 output_strains=True, 

55): 

56 """Set the basic runtime output options. 

57 

58 Args 

59 ---- 

60 input_file: 

61 Input file that the options will be added to. 

62 output_solid: bool 

63 If the solid output should be written at runtime. 

64 output_stress_strain: bool 

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

66 btsvmt_output: bool 

67 If the output for btsvmt should be written. 

68 btss_output: bool 

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

70 output_triad: bool 

71 If the triads along the beam should be written. 

72 every_iteration: int 

73 If output at every Newton iteration should be written. 

74 absolute_beam_positions: bool 

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

76 the reference position. 

77 element_owner: bool 

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

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

80 written by default). 

81 element_gid: bool 

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

83 element_mat_id: bool 

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

85 output_energy: bool 

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

87 output_strains: bool 

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

89 """ 

90 

91 # Set the basic runtime output options. 

92 input_file.add( 

93 { 

94 "IO/RUNTIME VTK OUTPUT": { 

95 "OUTPUT_DATA_FORMAT": "binary", 

96 "INTERVAL_STEPS": 1, 

97 "EVERY_ITERATION": every_iteration, 

98 } 

99 } 

100 ) 

101 

102 # Set the structure runtime output options 

103 input_file.add( 

104 { 

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

106 "OUTPUT_STRUCTURE": output_solid, 

107 "DISPLACEMENT": True, 

108 "STRESS_STRAIN": output_stress_strain, 

109 "ELEMENT_OWNER": element_owner, 

110 "ELEMENT_GID": element_gid, 

111 "ELEMENT_MAT_ID": element_mat_id, 

112 } 

113 } 

114 ) 

115 

116 # Set the beam runtime output options 

117 input_file.add( 

118 { 

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

120 "OUTPUT_BEAMS": True, 

121 "DISPLACEMENT": True, 

122 "USE_ABSOLUTE_POSITIONS": absolute_beam_positions, 

123 "TRIAD_VISUALIZATIONPOINT": output_triad, 

124 "STRAINS_GAUSSPOINT": output_strains, 

125 "ELEMENT_GID": element_gid, 

126 } 

127 } 

128 ) 

129 

130 if btsvmt_output: 

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

132 input_file.add( 

133 { 

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

135 "WRITE_OUTPUT": True, 

136 "NODAL_FORCES": True, 

137 "MORTAR_LAMBDA_DISCRET": True, 

138 "MORTAR_LAMBDA_CONTINUOUS": True, 

139 "MORTAR_LAMBDA_CONTINUOUS_SEGMENTS": 5, 

140 "SEGMENTATION": True, 

141 "INTEGRATION_POINTS": True, 

142 } 

143 } 

144 ) 

145 

146 if btss_output: 

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

148 input_file.add( 

149 { 

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

151 "WRITE_OUTPUT": True, 

152 "NODAL_FORCES": True, 

153 "MORTAR_LAMBDA_DISCRET": True, 

154 "MORTAR_LAMBDA_CONTINUOUS": True, 

155 "MORTAR_LAMBDA_CONTINUOUS_SEGMENTS": 5, 

156 "SEGMENTATION": True, 

157 "INTEGRATION_POINTS": True, 

158 "AVERAGED_NORMALS": True, 

159 } 

160 } 

161 ) 

162 

163 if output_energy: 

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

165 

166 

167def set_beam_to_solid_meshtying( 

168 input_file, 

169 interaction_type, 

170 *, 

171 contact_discretization=None, 

172 segmentation=True, 

173 segmentation_search_points=2, 

174 couple_restart=False, 

175 mortar_shape="none", 

176 n_gauss_points=6, 

177 n_integration_points_circ=None, 

178 penalty_parameter=None, 

179 coupling_type=None, 

180 binning_parameters: dict = {}, 

181): 

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

183 

184 Args 

185 ---- 

186 input_file: 

187 Input file that the options will be added to. 

188 interaction_type: BoundaryCondition 

189 Type of beam-to-solid interaction. 

190 contact_discretization: str 

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

192 segmentation: bool 

193 If segmentation should be used in the numerical integration. 

194 segmentation_search_points: int 

195 Number of search points for segmentation. 

196 couple_restart: bool 

197 If the restart configuration should be used for the coupling 

198 mortar_shape: str 

199 Type of shape function for mortar discretization. 

200 n_gauss_points: int 

201 Number of Gauss points for numerical integration. 

202 n_integration_points_circ: int 

203 Number of integration points along the circumference of the cross 

204 section. 

205 penalty_parameter: float 

206 Penalty parameter for contact enforcement. 

207 coupling_type: str 

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

209 binning_parameters: 

210 Keyword parameters for the binning section 

211 """ 

212 

213 # Set the beam contact options. 

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

215 if ( 

216 "BEAM INTERACTION" not in input_file 

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

218 ): 

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

220 

221 set_binning_strategy_section( 

222 input_file, 

223 **binning_parameters, 

224 ) 

225 

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

227 bts_parameters = {} 

228 if interaction_type == _bme.bc.beam_to_solid_volume_meshtying: 

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

230 elif interaction_type == _bme.bc.beam_to_solid_surface_meshtying: 

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

232 if coupling_type is not None: 

233 bts_parameters["COUPLING_TYPE"] = coupling_type 

234 else: 

235 raise ValueError( 

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

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

238 ) 

239 bts_parameters["CONSTRAINT_STRATEGY"] = "penalty" 

240 if penalty_parameter is not None: 

241 bts_parameters["PENALTY_PARAMETER"] = penalty_parameter 

242 bts_parameters["GAUSS_POINTS"] = n_gauss_points 

243 

244 if contact_discretization == "mortar": 

245 bts_parameters["CONTACT_DISCRETIZATION"] = "mortar" 

246 bts_parameters["MORTAR_SHAPE_FUNCTION"] = mortar_shape 

247 segmentation_strategy = _get_segmentation_strategy(segmentation) 

248 elif contact_discretization == "gp": 

249 bts_parameters["CONTACT_DISCRETIZATION"] = "gauss_point_to_segment" 

250 segmentation_strategy = _get_segmentation_strategy(segmentation) 

251 elif contact_discretization == "circ": 

252 bts_parameters["CONTACT_DISCRETIZATION"] = "gauss_point_cross_section" 

253 bts_parameters["INTEGRATION_POINTS_CIRCUMFERENCE"] = n_integration_points_circ 

254 segmentation_strategy = "gauss_point_projection_cross_section" 

255 else: 

256 raise ValueError( 

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

258 ) 

259 

260 bts_parameters["GEOMETRY_PAIR_STRATEGY"] = segmentation_strategy 

261 bts_parameters["GEOMETRY_PAIR_SEGMENTATION_SEARCH_POINTS"] = ( 

262 segmentation_search_points 

263 ) 

264 if interaction_type == _bme.bc.beam_to_solid_volume_meshtying: 

265 bts_parameters["COUPLE_RESTART_STATE"] = couple_restart 

266 

267 input_file.add({bts_section_name: bts_parameters}) 

268 

269 

270def set_header_static( 

271 input_file: _InputFile, 

272 *, 

273 time_step: float | None = None, 

274 n_steps: int | None = None, 

275 total_time: float | None = None, 

276 max_iter: int = 20, 

277 tol_residuum: float = 1e-8, 

278 tol_increment: float = 1e-10, 

279 restart_every: int = 1, 

280 load_lin: bool = False, 

281 write_bin: bool = False, 

282 write_stress: str = "no", 

283 write_strain: str = "no", 

284 predictor: str = "TangDis", 

285 prestress: str = "None", 

286 prestress_time: float = 0, 

287 create_nox_file: bool = True, 

288): 

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

290 

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

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

293 

294 Args: 

295 input_file: 

296 Input file that the options will be added to. 

297 time_step: 

298 Time increment per step. 

299 n_steps: 

300 Number of time steps. 

301 total_time: 

302 Total time of simulation 

303 max_iter: 

304 Maximal number of Newton iterations. 

305 tol_residuum: 

306 Tolerance for the convergence of the residuum. 

307 tol_increment: 

308 Tolerance for the convergence of the displacement increment. 

309 load_lin: 

310 If the load_lin option should be set. 

311 write_bin: 

312 If binary output should be written. 

313 restart_every: 

314 Frequency for writing restart output. 

315 write_stress: 

316 If and which stress output to write 

317 write_strain: 

318 If and which strain output to write 

319 predictor: 

320 Type of predictor to be used 

321 prestress: 

322 Type of prestressing strategy to be used 

323 prestress_time: 

324 Prestress Time 

325 create_nox_file: 

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

327 directly in the input file. 

328 """ 

329 

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

331 

332 # Set the parameters for a static analysis. 

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

334 input_file_parameters["IO"] = { 

335 "OUTPUT_BIN": write_bin, 

336 "STRUCT_DISP": False, 

337 "STRUCT_STRESS": write_stress, 

338 "STRUCT_STRAIN": write_strain, 

339 "VERBOSITY": "Standard", 

340 } 

341 

342 # Set the time step parameters 

343 given_time_arguments = sum( 

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

345 ) 

346 if given_time_arguments < 2: 

347 raise ValueError( 

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

349 '"total_time" are required' 

350 ) 

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

352 time_step = total_time / n_steps 

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

354 n_steps = round(total_time / time_step) 

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

356 total_time = time_step * n_steps 

357 

358 input_file_parameters["STRUCTURAL DYNAMIC"] = { 

359 "LINEAR_SOLVER": 1, 

360 "INT_STRATEGY": "Standard", 

361 "DYNAMICTYPE": "Statics", 

362 "PREDICT": predictor, 

363 "PRESTRESS": prestress, 

364 "PRESTRESSTIME": prestress_time, 

365 "TIMESTEP": time_step, 

366 "NUMSTEP": n_steps, 

367 "MAXTIME": total_time, 

368 "LOADLIN": load_lin, 

369 "RESTARTEVERY": restart_every, 

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: list[int] | None = None, 

448 binning_cutoff_radius: 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, *, repartition_strategy: str = "everydt" 

485): 

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

487 

488 Args 

489 ---- 

490 input_file: 

491 Input file that the options will be added to. 

492 repartition_strategy: 

493 Type of employed repartitioning strategy 

494 Options: "adaptive" or "everydt" 

495 search_strategy: 

496 Type of search strategy used for finding coupling pairs. 

497 """ 

498 

499 input_file.add({"BEAM INTERACTION": {"REPARTITIONSTRATEGY": repartition_strategy}}) 

500 

501 

502def set_beam_contact_runtime_output( 

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

504): 

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

506 

507 input_file: 

508 Input file that the options will be added to. 

509 every_iteration: 

510 If output at every Newton iteration should be written. 

511 """ 

512 

513 input_file.add( 

514 { 

515 "BEAM INTERACTION/BEAM TO BEAM CONTACT/RUNTIME VTK OUTPUT": { 

516 "VTK_OUTPUT_BEAM_CONTACT": True, 

517 "EVERY_ITERATION": every_iteration, 

518 "INTERVAL_STEPS": 1, 

519 "CONTACT_FORCES": True, 

520 "GAPS": True, 

521 } 

522 } 

523 ) 

524 

525 

526def set_beam_contact_section( 

527 input_file: _InputFile, 

528 *, 

529 interaction_strategy: str = "penalty", 

530 btb_penalty: float = 0, 

531 btb_line_penalty: float = 0, 

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

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

534 b_seg_angle: float = 12, 

535 num_integration: int = 5, 

536 penalty_law: str = "LinPosQuadPen", 

537 penalty_regularization_g0: float = 0, 

538 penalty_regularization_f0: float = 0, 

539 penalty_regularization_c0: float = 0, 

540 binning_parameters: dict = {}, 

541 beam_interaction_parameters: dict = {}, 

542): 

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

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

545 set_beam_interaction may be forwarded as keyword arguments. 

546 

547 Args 

548 ---- 

549 input_file: 

550 Input file that the options will be added to. 

551 interaction_strategy: 

552 Type of employed solving strategy 

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

554 btb_penalty: double 

555 Penalty parameter for beam-to-beam point contact 

556 btb_line_penalty: 

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

558 per_shift_angle: 

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

560 par_shift_angle: 

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

562 b_seg_angle: 

563 Maximal angle deviation allowed for contact search segmentation 

564 num_integration: 

565 Number of integration intervals per element 

566 penalty_law: 

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

568 penalty_regularization_g0: 

569 First penalty regularization parameter G0 

570 penalty_regularization_f0: 

571 Second penalty regularization parameter F0 

572 penalty_regularization_c0: 

573 Third penalty regularization parameter C0 

574 binning_parameters: 

575 Keyword parameters for the binning section 

576 beam_interaction_parameters: 

577 Keyword parameters for the beam-contact section 

578 """ 

579 

580 if len(per_shift_angle) != 2: 

581 raise ValueError( 

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

583 ) 

584 

585 if len(par_shift_angle) != 2: 

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

587 

588 input_file.add( 

589 { 

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

591 "BEAMS_BTBPENALTYPARAM": btb_penalty, 

592 "BEAMS_BTBLINEPENALTYPARAM": btb_line_penalty, 

593 "BEAMS_SEGCON": True, 

594 "BEAMS_PERPSHIFTANGLE1": per_shift_angle[0], 

595 "BEAMS_PERPSHIFTANGLE2": per_shift_angle[1], 

596 "BEAMS_PARSHIFTANGLE1": par_shift_angle[0], 

597 "BEAMS_PARSHIFTANGLE2": par_shift_angle[1], 

598 "BEAMS_SEGANGLE": b_seg_angle, 

599 "BEAMS_NUMINTEGRATIONINTERVAL": num_integration, 

600 "BEAMS_PENALTYLAW": penalty_law, 

601 "BEAMS_PENREGPARAM_G0": penalty_regularization_g0, 

602 "BEAMS_PENREGPARAM_F0": penalty_regularization_f0, 

603 "BEAMS_PENREGPARAM_C0": penalty_regularization_c0, 

604 } 

605 } 

606 ) 

607 

608 # beam contact needs a binning strategy 

609 set_binning_strategy_section(input_file, **binning_parameters) 

610 

611 # beam contact needs interaction strategy 

612 set_beam_interaction_section(input_file, **beam_interaction_parameters) 

613 

614 

615def add_result_description( 

616 input_file: _InputFile, 

617 displacements: _List, 

618 node_ids: _List[int], 

619 *, 

620 tol: float = 1e-10, 

621): 

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

623 

624 Args: 

625 input_file: Input file to add the result description to 

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

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

628 tol: Tolerance 

629 """ 

630 result_descriptions = [] 

631 

632 for i_node, node in enumerate(node_ids): 

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

634 result_descriptions.append( 

635 { 

636 "STRUCTURE": { 

637 "DIS": "structure", 

638 "NODE": node, 

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

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

641 "TOLERANCE": tol, 

642 }, 

643 } 

644 ) 

645 

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