Coverage for src/beamme/four_c/header_functions.py: 90%
89 statements
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-28 15:20 +0000
« prev ^ index » next coverage.py v7.15.2, created at 2026-07-28 15:20 +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 an input
23file."""
25from typing import Any as _Any
27from beamme.core.conf import bme as _bme
28from beamme.four_c.input_file import InputFile as _InputFile
31def _get_segmentation_strategy(segmentation):
32 """Get the 4C string for a geometry pair strategy."""
33 if segmentation:
34 return "segmentation"
35 else:
36 return "gauss_point_projection_without_boundary_segmentation"
39def set_runtime_output(
40 input_file,
41 *,
42 output_solid=True,
43 output_stress_strain=False,
44 btsvmt_output=True,
45 btss_output=True,
46 output_triad=True,
47 every_iteration=False,
48 absolute_beam_positions=True,
49 element_owner=True,
50 element_gid=True,
51 element_mat_id=True,
52 output_energy=False,
53 output_strains=True,
54):
55 """Set the basic runtime output options.
57 Args
58 ----
59 input_file:
60 Input file that the options will be added to.
61 output_solid: bool
62 If the solid output should be written at runtime.
63 output_stress_strain: bool
64 If stress and strain output should be written for the solid.
65 btsvmt_output: bool
66 If the output for btsvmt should be written.
67 btss_output: bool
68 If the output for beam-to-surface coupling should be written.
69 output_triad: bool
70 If the triads along the beam should be written.
71 every_iteration: int
72 If output at every Newton iteration should be written.
73 absolute_beam_positions: bool
74 If the beams should be written at the current position or always at
75 the reference position.
76 element_owner: bool
77 If the owing rank of each element should be output (currently
78 only affects the solid elements in 4C, beam element owners are
79 written by default).
80 element_gid: bool
81 If the 4C internal GID of each element should be output.
82 element_mat_id: bool
83 If the 4C internal material ID of each element should be output.
84 output_energy: bool
85 If the energy output from 4C should be activated.
86 output_strains: bool
87 If the strains in the Gauss points should be output.
88 """
89 # Set the basic runtime output options.
90 input_file.add(
91 {
92 "IO/RUNTIME VTK OUTPUT": {
93 "OUTPUT_DATA_FORMAT": "binary",
94 "INTERVAL_STEPS": 1,
95 "EVERY_ITERATION": every_iteration,
96 }
97 }
98 )
100 # Set the structure runtime output options
101 input_file.add(
102 {
103 "IO/RUNTIME VTK OUTPUT/STRUCTURE": {
104 "OUTPUT_STRUCTURE": output_solid,
105 "DISPLACEMENT": True,
106 "STRESS_STRAIN": output_stress_strain,
107 "ELEMENT_OWNER": element_owner,
108 "ELEMENT_GID": element_gid,
109 "ELEMENT_MAT_ID": element_mat_id,
110 }
111 }
112 )
114 # Set the beam runtime output options
115 input_file.add(
116 {
117 "IO/RUNTIME VTK OUTPUT/BEAMS": {
118 "OUTPUT_BEAMS": True,
119 "DISPLACEMENT": True,
120 "USE_ABSOLUTE_POSITIONS": absolute_beam_positions,
121 "TRIAD_VISUALIZATIONPOINT": output_triad,
122 "STRAINS_GAUSSPOINT": output_strains,
123 "ELEMENT_GID": element_gid,
124 }
125 }
126 )
128 if btsvmt_output:
129 # Set the beam to solid volume mesh tying runtime output options.
130 input_file.add(
131 {
132 "BEAM INTERACTION/BEAM TO SOLID VOLUME MESHTYING/RUNTIME VTK OUTPUT": {
133 "WRITE_OUTPUT": True,
134 "NODAL_FORCES": True,
135 "MORTAR_LAMBDA_DISCRET": True,
136 "MORTAR_LAMBDA_CONTINUOUS": True,
137 "MORTAR_LAMBDA_CONTINUOUS_SEGMENTS": 5,
138 "SEGMENTATION": True,
139 "INTEGRATION_POINTS": True,
140 }
141 }
142 )
144 if btss_output:
145 # Set the beam to solid surface coupling runtime output options.
146 input_file.add(
147 {
148 "BEAM INTERACTION/BEAM TO SOLID SURFACE/RUNTIME VTK OUTPUT": {
149 "WRITE_OUTPUT": True,
150 "NODAL_FORCES": True,
151 "MORTAR_LAMBDA_DISCRET": True,
152 "MORTAR_LAMBDA_CONTINUOUS": True,
153 "MORTAR_LAMBDA_CONTINUOUS_SEGMENTS": 5,
154 "SEGMENTATION": True,
155 "INTEGRATION_POINTS": True,
156 "AVERAGED_NORMALS": True,
157 }
158 }
159 )
161 if output_energy:
162 input_file["STRUCTURAL DYNAMIC"]["RESEVERYERGY"] = 1
165def set_beam_to_solid_meshtying(
166 input_file,
167 interaction_type,
168 *,
169 contact_discretization=None,
170 segmentation=True,
171 segmentation_search_points=2,
172 couple_restart=False,
173 mortar_shape="none",
174 n_gauss_points=6,
175 n_integration_points_circ=None,
176 penalty_parameter=None,
177 coupling_type=None,
178):
179 """Set the beam to solid meshtying options.
181 Args
182 ----
183 input_file:
184 Input file that the options will be added to.
185 interaction_type: BoundaryCondition
186 Type of beam-to-solid interaction.
187 contact_discretization: str
188 Type of contact (mortar, Gauss point, ...)
189 segmentation: bool
190 If segmentation should be used in the numerical integration.
191 segmentation_search_points: int
192 Number of search points for segmentation.
193 couple_restart: bool
194 If the restart configuration should be used for the coupling
195 mortar_shape: str
196 Type of shape function for mortar discretization.
197 n_gauss_points: int
198 Number of Gauss points for numerical integration.
199 n_integration_points_circ: int
200 Number of integration points along the circumference of the cross
201 section.
202 penalty_parameter: float
203 Penalty parameter for contact enforcement.
204 coupling_type: str
205 Type of coupling for beam-to-surface coupling.
206 """
207 # Set the beam contact options.
208 # check if these keys are already set, otherwise set them
209 if (
210 "BEAM INTERACTION" not in input_file
211 or input_file["BEAM INTERACTION"].get("REPARTITIONSTRATEGY") != "everydt"
212 ):
213 input_file.add({"BEAM INTERACTION": {"REPARTITIONSTRATEGY": "everydt"}})
215 # Add the beam to solid volume mesh tying options.
216 bts_parameters = {}
217 if interaction_type == _bme.bc.beam_to_solid_volume_meshtying:
218 bts_section_name = "BEAM INTERACTION/BEAM TO SOLID VOLUME MESHTYING"
219 elif interaction_type == _bme.bc.beam_to_solid_surface_meshtying:
220 bts_section_name = "BEAM INTERACTION/BEAM TO SOLID SURFACE MESHTYING"
221 if coupling_type is not None:
222 bts_parameters["COUPLING_TYPE"] = coupling_type
223 else:
224 raise ValueError(
225 "Got wrong beam-to-solid mesh tying type. "
226 f"Got {interaction_type} of type {type(interaction_type)}."
227 )
228 bts_parameters["CONSTRAINT_STRATEGY"] = "penalty"
229 if penalty_parameter is not None:
230 bts_parameters["PENALTY_PARAMETER"] = penalty_parameter
231 bts_parameters["GAUSS_POINTS"] = n_gauss_points
233 if contact_discretization == "mortar":
234 bts_parameters["CONTACT_DISCRETIZATION"] = "mortar"
235 bts_parameters["MORTAR_SHAPE_FUNCTION"] = mortar_shape
236 segmentation_strategy = _get_segmentation_strategy(segmentation)
237 elif contact_discretization == "gp":
238 bts_parameters["CONTACT_DISCRETIZATION"] = "gauss_point_to_segment"
239 segmentation_strategy = _get_segmentation_strategy(segmentation)
240 elif contact_discretization == "circ":
241 bts_parameters["CONTACT_DISCRETIZATION"] = "gauss_point_cross_section"
242 bts_parameters["INTEGRATION_POINTS_CIRCUMFERENCE"] = n_integration_points_circ
243 segmentation_strategy = "gauss_point_projection_cross_section"
244 else:
245 raise ValueError(
246 f'Wrong contact_discretization "{contact_discretization}" given!'
247 )
249 bts_parameters["GEOMETRY_PAIR_STRATEGY"] = segmentation_strategy
250 bts_parameters["GEOMETRY_PAIR_SEGMENTATION_SEARCH_POINTS"] = (
251 segmentation_search_points
252 )
253 if interaction_type == _bme.bc.beam_to_solid_volume_meshtying:
254 bts_parameters["COUPLE_RESTART_STATE"] = couple_restart
256 input_file.add({bts_section_name: bts_parameters})
259def set_header_static(
260 input_file: _InputFile,
261 *,
262 time_step: float | None = None,
263 n_steps: int | None = None,
264 total_time: float | None = None,
265 max_iter: int = 20,
266 tol_residuum: float = 1e-8,
267 tol_increment: float = 1e-10,
268 restart_every: int = 1,
269 load_lin: bool = False,
270 write_bin: bool = False,
271 write_stress: str = "no",
272 write_strain: str = "no",
273 predictor: str = "TangDis",
274 prestress: str = "None",
275 prestress_time: float = 0,
276 create_nox_file: bool = True,
277):
278 """Set the default parameters for a static structure analysis.
280 At least two of the three time stepping keyword arguments ["time_step",
281 "n_steps", "total_time"] have to be set.
283 Args:
284 input_file:
285 Input file that the options will be added to.
286 time_step:
287 Time increment per step.
288 n_steps:
289 Number of time steps.
290 total_time:
291 Total time of simulation
292 max_iter:
293 Maximal number of Newton iterations.
294 tol_residuum:
295 Tolerance for the convergence of the residuum.
296 tol_increment:
297 Tolerance for the convergence of the displacement increment.
298 load_lin:
299 If the load_lin option should be set.
300 write_bin:
301 If binary output should be written.
302 restart_every:
303 Frequency for writing restart output.
304 write_stress:
305 If and which stress output to write
306 write_strain:
307 If and which strain output to write
308 predictor:
309 Type of predictor to be used
310 prestress:
311 Type of prestressing strategy to be used
312 prestress_time:
313 Prestress Time
314 create_nox_file:
315 If the nonlinear solver parameters should be set via a NOX xml file or
316 directly in the input file.
317 """
318 input_file_parameters: dict[str, _Any] = {}
320 # Set the parameters for a static analysis.
321 input_file_parameters["PROBLEM TYPE"] = {"PROBLEMTYPE": "Structure"}
322 input_file_parameters["IO"] = {
323 "OUTPUT_BIN": write_bin,
324 "STRUCT_DISP": False,
325 "STRUCT_STRESS": write_stress,
326 "STRUCT_STRAIN": write_strain,
327 "VERBOSITY": "Standard",
328 }
330 # Set the time step parameters
331 given_time_arguments = sum(
332 1 for arg in (time_step, n_steps, total_time) if arg is not None
333 )
334 if given_time_arguments < 2:
335 raise ValueError(
336 'At least two of the following arguments "time_step", "n_steps" or '
337 '"total_time" are required'
338 )
339 elif time_step is None and total_time is not None and n_steps is not None:
340 time_step = total_time / n_steps
341 elif n_steps is None and total_time is not None and time_step is not None:
342 n_steps = round(total_time / time_step)
343 elif total_time is None and time_step is not None and n_steps is not None:
344 total_time = time_step * n_steps
346 input_file_parameters["STRUCTURAL DYNAMIC"] = {
347 "LINEAR_SOLVER": 1,
348 "INT_STRATEGY": "Standard",
349 "DYNAMICTYPE": "Statics",
350 "PREDICT": predictor,
351 "PRESTRESS": prestress,
352 "PRESTRESSTIME": prestress_time,
353 "TIMESTEP": time_step,
354 "NUMSTEP": n_steps,
355 "MAXTIME": total_time,
356 "LOADLIN": load_lin,
357 "RESTARTEVERY": restart_every,
358 }
359 input_file_parameters["SOLVER 1"] = {
360 "NAME": "Structure_Solver",
361 "SOLVER": "Superlu",
362 }
364 # Set the solver parameters.
365 if create_nox_file:
366 # Set the contents of the NOX xml file.
367 nox_xml_contents = f"""
368 <ParameterList name="Status Test">
369 <!-- Outer Status Test: This test is an OR combination of the structural convergence and the maximum number of iterations -->
370 <ParameterList name="Outer Status Test">
371 <Parameter name="Test Type" type="string" value="Combo"/>
372 <Parameter name="Combo Type" type="string" value="OR" />
373 <!-- Structural convergence is an AND combination of the residuum and step update -->
374 <ParameterList name="Test 0">
375 <Parameter name="Test Type" type="string" value="Combo" />
376 <Parameter name="Combo Type" type="string" value="AND" />
377 <!-- BEGIN: Combo AND - Test 0: "NormF" -->
378 <ParameterList name="Test 0">
379 <Parameter name="Test Type" type="string" value="NormF" />
380 <!-- NormF - Quantity 0: Check the right-hand-side norm of the structural quantities -->
381 <ParameterList name="Quantity 0">
382 <Parameter name="Quantity Type" type="string" value="Structure" />
383 <Parameter name="Tolerance Type" type="string" value="Absolute" />
384 <Parameter name="Tolerance" type="double" value="{tol_residuum}" />
385 <Parameter name="Norm Type" type="string" value="Two Norm" />
386 <Parameter name="Scale Type" type="string" value="Scaled" />
387 </ParameterList>
388 </ParameterList>
389 <!-- END: Combo AND - Test 0: "NormF" -->
390 <!-- BEGIN: Combo AND - Test 1: "NormWRMS" -->
391 <ParameterList name="Test 1">
392 <Parameter name="Test Type" type="string" value="NormUpdate" />
393 <!-- NormWRMS - Quantity 0: Check the increment of the structural displacements -->
394 <ParameterList name="Quantity 0">
395 <Parameter name="Quantity Type" type="string" value="Structure" />
396 <Parameter name="Tolerance Type" type="string" value="Absolute" />
397 <Parameter name="Tolerance" type="double" value="{tol_increment}" />
398 <Parameter name="Norm Type" type="string" value="Two Norm" />
399 <Parameter name="Scale Type" type="string" value="Scaled" />
400 </ParameterList>
401 </ParameterList>
402 <!-- END: Combo AND - Test 1: "NormWRMS" -->
403 </ParameterList>
404 <!-- END: Combo 0 - Test 0: "Combo" -->
405 <!-- BEGIN: Combo OR - Test 1: "MaxIters" -->
406 <ParameterList name="Test 1">
407 <Parameter name="Test Type" type="string" value="MaxIters" />
408 <Parameter name="Maximum Iterations" type="int" value="{max_iter}" />
409 </ParameterList> <!--END: "MaxIters" -->
410 </ParameterList>
411 </ParameterList>
412 """
414 input_file_parameters["STRUCT NOX/Printing"] = {
415 "Error": True,
416 "Inner Iteration": False,
417 "Details": True,
418 "Linear Solver Details": True,
419 "Test Details": True,
420 }
422 # Set the xml content in the input file.
423 input_file.nox_xml_contents = nox_xml_contents
425 else:
426 input_file_parameters["STRUCTURAL DYNAMIC"]["MAXITER"] = max_iter
427 input_file_parameters["STRUCTURAL DYNAMIC"]["TOLRES"] = tol_residuum
428 input_file_parameters["STRUCTURAL DYNAMIC"]["TOLDISP"] = tol_increment
430 input_file.add(input_file_parameters)
433def set_beam_interaction_section(
434 input_file: _InputFile, *, repartition_strategy: str = "everydt"
435):
436 """Set beam interaction section in input file.
438 Args
439 ----
440 input_file:
441 Input file that the options will be added to.
442 repartition_strategy:
443 Type of employed repartitioning strategy
444 Options: "adaptive" or "everydt"
445 search_strategy:
446 Type of search strategy used for finding coupling pairs.
447 """
448 input_file.add({"BEAM INTERACTION": {"REPARTITIONSTRATEGY": repartition_strategy}})
451def set_beam_contact_runtime_output(
452 input_file: _InputFile, *, every_iteration: bool = False
453):
454 """Output the beam-to-beam contact forces and gaps with runtime output.
456 input_file:
457 Input file that the options will be added to.
458 every_iteration:
459 If output at every Newton iteration should be written.
460 """
461 input_file.add(
462 {
463 "BEAM INTERACTION/BEAM TO BEAM CONTACT/RUNTIME VTK OUTPUT": {
464 "VTK_OUTPUT_BEAM_CONTACT": True,
465 "EVERY_ITERATION": every_iteration,
466 "INTERVAL_STEPS": 1,
467 "CONTACT_FORCES": True,
468 "GAPS": True,
469 }
470 }
471 )
474def set_beam_contact_section(
475 input_file: _InputFile,
476 *,
477 btb_penalty: float = 0,
478 btb_line_penalty: float = 0,
479 per_shift_angle: list[float] = [70, 80],
480 par_shift_angle: list[float] = [70, 80],
481 b_seg_angle: float = 12,
482 num_integration: int = 5,
483 penalty_law: str = "LinPosQuadPen",
484 penalty_regularization_g0: float = 0,
485 penalty_regularization_f0: float = 0,
486 penalty_regularization_c0: float = 0,
487 beam_interaction_parameters: dict = {},
488):
489 """Set default beam contact section, for more and updated details see respective
490 input file within 4C.
492 Parameters for `set_beam_interaction_section` are forwarded as keyword arguments.
494 Args
495 ----
496 input_file:
497 Input file that the options will be added to.
498 btb_penalty: double
499 Penalty parameter for beam-to-beam point contact
500 btb_line_penalty:
501 Penalty parameter per unit length for beam-to-beam line contact
502 per_shift_angle:
503 Lower and upper shift angle (in degrees) for penalty scaling of large-angle-contact
504 par_shift_angle:
505 Lower and upper shift angle (in degrees) for penalty scaling of small-angle-contact
506 b_seg_angle:
507 Maximal angle deviation allowed for contact search segmentation
508 num_integration:
509 Number of integration intervals per element
510 penalty_law:
511 Penalty Law Options: "LinPen", "QuadPen", "LinNegQuadPen", "LinPosQuadPen", "LinPosCubPen", "LinPosDoubleQuadPen", "LinPosExpPen"
512 penalty_regularization_g0:
513 First penalty regularization parameter G0
514 penalty_regularization_f0:
515 Second penalty regularization parameter F0
516 penalty_regularization_c0:
517 Third penalty regularization parameter C0
518 beam_interaction_parameters:
519 Keyword parameters for `set_beam_interaction_section`
520 """
521 if len(per_shift_angle) != 2:
522 raise ValueError(
523 "Please provide lower and upper value of BEAMS_PERPSHIFTANGLE."
524 )
526 if len(par_shift_angle) != 2:
527 raise ValueError("Please provide lower and upper value of BEAMS_PARSHIFTANGLE.")
529 input_file.add(
530 {
531 "BEAM INTERACTION/BEAM TO BEAM CONTACT": {
532 "BEAMS_BTBPENALTYPARAM": btb_penalty,
533 "BEAMS_BTBLINEPENALTYPARAM": btb_line_penalty,
534 "BEAMS_SEGCON": True,
535 "BEAMS_PERPSHIFTANGLE1": per_shift_angle[0],
536 "BEAMS_PERPSHIFTANGLE2": per_shift_angle[1],
537 "BEAMS_PARSHIFTANGLE1": par_shift_angle[0],
538 "BEAMS_PARSHIFTANGLE2": par_shift_angle[1],
539 "BEAMS_SEGANGLE": b_seg_angle,
540 "BEAMS_NUMINTEGRATIONINTERVAL": num_integration,
541 "BEAMS_PENALTYLAW": penalty_law,
542 "BEAMS_PENREGPARAM_G0": penalty_regularization_g0,
543 "BEAMS_PENREGPARAM_F0": penalty_regularization_f0,
544 "BEAMS_PENREGPARAM_C0": penalty_regularization_c0,
545 }
546 }
547 )
549 # beam contact needs interaction strategy
550 set_beam_interaction_section(input_file, **beam_interaction_parameters)
553def add_result_description(
554 input_file: _InputFile,
555 displacements: list,
556 node_ids: list[int],
557 *,
558 tol: float = 1e-10,
559):
560 """Add result descriptions for structure problems to the input file.
562 Args:
563 input_file: Input file to add the result description to
564 displacements: Array with the displacements (n_nodes x 3)
565 node_ids: List with the IDs of the nodes to check
566 tol: Tolerance
567 """
568 result_descriptions = []
570 for i_node, node in enumerate(node_ids):
571 for i_dir, direction in enumerate(["x", "y", "z"]):
572 result_descriptions.append(
573 {
574 "STRUCTURE": {
575 "DIS": "structure",
576 "NODE": node,
577 "QUANTITY": f"disp{direction}",
578 "VALUE": displacements[i_node][i_dir],
579 "TOLERANCE": tol,
580 },
581 }
582 )
584 input_file.add({"RESULT DESCRIPTION": result_descriptions})