Coverage for src/beamme/four_c/element_beam.py: 100%
60 statements
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-11 12:17 +0000
« prev ^ index » next coverage.py v7.10.3, created at 2025-08-11 12:17 +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 file implements beam elements for 4C."""
24import warnings as _warnings
26import numpy as _np
28from beamme.core.conf import bme as _bme
29from beamme.core.element_beam import Beam as _Beam
30from beamme.four_c.four_c_types import BeamType as _BeamType
31from beamme.four_c.material import MaterialEulerBernoulli as _MaterialEulerBernoulli
32from beamme.four_c.material import MaterialKirchhoff as _MaterialKirchhoff
33from beamme.four_c.material import MaterialReissner as _MaterialReissner
34from beamme.four_c.material import (
35 MaterialReissnerElastoplastic as _MaterialReissnerElastoplastic,
36)
39class Beam3rHerm2Line3(_Beam):
40 """Represents a Simo-Reissner beam element with third order Hermitian
41 interpolation of the centerline and second order Lagrangian interpolation
42 of the rotations."""
44 nodes_create = [-1, 0, 1]
45 beam_type = _BeamType.reissner
46 valid_material = [_MaterialReissner, _MaterialReissnerElastoplastic]
48 coupling_fix_dict = {"NUMDOF": 9, "ONOFF": [1, 1, 1, 1, 1, 1, 0, 0, 0]}
49 coupling_joint_dict = {"NUMDOF": 9, "ONOFF": [1, 1, 1, 0, 0, 0, 0, 0, 0]}
51 def dump_to_list(self):
52 """Return a list with the (single) item representing this element."""
54 # Check the material.
55 self._check_material()
57 return {
58 "id": self.i_global + 1,
59 "cell": {
60 "type": "LINE3",
61 "connectivity": [self.nodes[i] for i in [0, 2, 1]],
62 },
63 "data": {
64 "type": "BEAM3R",
65 "MAT": self.material,
66 "TRIADS": [
67 item
68 for i in [0, 2, 1]
69 for item in self.nodes[i].rotation.get_rotation_vector()
70 ],
71 "HERMITE_CENTERLINE": True,
72 },
73 }
76class Beam3rLine2Line2(_Beam):
77 """Represents a Reissner beam with linear shapefunctions in the rotations
78 as well as the displacements."""
80 nodes_create = [-1, 1]
81 beam_type = _BeamType.reissner
82 valid_material = [_MaterialReissner]
84 coupling_fix_dict = {"NUMDOF": 6, "ONOFF": [1, 1, 1, 1, 1, 1]}
85 coupling_joint_dict = {"NUMDOF": 6, "ONOFF": [1, 1, 1, 0, 0, 0]}
87 def dump_to_list(self):
88 """Return a list with the (single) item representing this element."""
90 # Check the material.
91 self._check_material()
93 return {
94 "id": self.i_global + 1,
95 "cell": {
96 "type": "LINE2",
97 "connectivity": self.nodes,
98 },
99 "data": {
100 "type": "BEAM3R",
101 "MAT": self.material,
102 "TRIADS": [
103 item
104 for i in [0, 1]
105 for item in self.nodes[i].rotation.get_rotation_vector()
106 ],
107 },
108 }
111class Beam3kClass(_Beam):
112 """Represents a Kirchhoff beam element."""
114 nodes_create = [-1, 0, 1]
115 beam_type = _BeamType.kirchhoff
116 valid_material = [_MaterialKirchhoff]
118 coupling_fix_dict = {"NUMDOF": 7, "ONOFF": [1, 1, 1, 1, 1, 1, 0]}
119 coupling_joint_dict = {"NUMDOF": 7, "ONOFF": [1, 1, 1, 0, 0, 0, 0]}
121 def __init__(self, *, weak=True, rotvec=True, is_fad=True, **kwargs):
122 _Beam.__init__(self, **kwargs)
124 # Set the parameters for this beam.
125 self.weak = weak
126 self.rotvec = rotvec
127 self.is_fad = is_fad
129 # Show warning when not using rotvec.
130 if not rotvec:
131 _warnings.warn(
132 "Use rotvec=False with caution, especially when applying the boundary conditions "
133 "and couplings."
134 )
136 def dump_to_list(self):
137 """Return a list with the (single) item representing this element."""
139 # Check the material.
140 self._check_material()
142 return {
143 "id": self.i_global + 1,
144 "cell": {
145 "type": "LINE3",
146 "connectivity": [self.nodes[i] for i in [0, 2, 1]],
147 },
148 "data": {
149 "type": "BEAM3K",
150 "WK": 1 if self.weak else 0,
151 "ROTVEC": 1 if self.rotvec else 0,
152 "MAT": self.material,
153 "TRIADS": [
154 item
155 for i in [0, 2, 1]
156 for item in self.nodes[i].rotation.get_rotation_vector()
157 ],
158 **({"USE_FAD": True} if self.is_fad else {}),
159 },
160 }
163def Beam3k(**kwargs_class):
164 """This factory returns a function that creates a new Beam3kClass object
165 with certain attributes defined.
167 The returned function behaves like a call to the object.
168 """
170 def create_class(**kwargs):
171 """The function that will be returned.
173 This function should behave like the call to the __init__
174 function of the class.
175 """
176 return Beam3kClass(**kwargs_class, **kwargs)
178 return create_class
181class Beam3eb(_Beam):
182 """Represents a Euler Bernoulli beam element."""
184 nodes_create = [-1, 1]
185 beam_type = _BeamType.euler_bernoulli
186 valid_material = [_MaterialEulerBernoulli]
188 def dump_to_list(self):
189 """Return a list with the (single) item representing this element."""
191 # Check the material.
192 self._check_material()
194 # The two rotations must be the same and the x1 vector must point from
195 # the start point to the end point.
196 if not self.nodes[0].rotation == self.nodes[1].rotation:
197 raise ValueError(
198 "The two nodal rotations in Euler Bernoulli beams must be the same, i.e. the beam "
199 "has to be straight!"
200 )
201 direction = self.nodes[1].coordinates - self.nodes[0].coordinates
202 t1 = self.nodes[0].rotation * [1, 0, 0]
203 if _np.linalg.norm(direction / _np.linalg.norm(direction) - t1) >= _bme.eps_pos:
204 raise ValueError(
205 "The rotations do not match the direction of the Euler Bernoulli beam!"
206 )
208 return {
209 "id": self.i_global + 1,
210 "cell": {
211 "type": "LINE2",
212 "connectivity": self.nodes,
213 },
214 "data": {
215 "type": "BEAM3EB",
216 "MAT": self.material,
217 },
218 }