Coverage for src/beamme/four_c/element_beam.py: 100%
59 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-30 18:48 +0000
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-30 18:48 +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 mpy as _mpy
29from beamme.core.element_beam import Beam as _Beam
30from beamme.four_c.material import MaterialEulerBernoulli as _MaterialEulerBernoulli
31from beamme.four_c.material import MaterialKirchhoff as _MaterialKirchhoff
32from beamme.four_c.material import MaterialReissner as _MaterialReissner
33from beamme.four_c.material import (
34 MaterialReissnerElastoplastic as _MaterialReissnerElastoplastic,
35)
38class Beam3rHerm2Line3(_Beam):
39 """Represents a Simo-Reissner beam element with third order Hermitian
40 interpolation of the centerline and second order Lagrangian interpolation
41 of the rotations."""
43 nodes_create = [-1, 0, 1]
44 beam_type = _mpy.beam.reissner
45 valid_material = [_MaterialReissner, _MaterialReissnerElastoplastic]
47 coupling_fix_dict = {"NUMDOF": 9, "ONOFF": [1, 1, 1, 1, 1, 1, 0, 0, 0]}
48 coupling_joint_dict = {"NUMDOF": 9, "ONOFF": [1, 1, 1, 0, 0, 0, 0, 0, 0]}
50 def dump_to_list(self):
51 """Return a list with the (single) item representing this element."""
53 # Check the material.
54 self._check_material()
56 return {
57 "id": self.i_global,
58 "cell": {
59 "type": "LINE3",
60 "connectivity": [self.nodes[i] for i in [0, 2, 1]],
61 },
62 "data": {
63 "type": "BEAM3R",
64 "MAT": self.material,
65 "TRIADS": [
66 item
67 for i in [0, 2, 1]
68 for item in self.nodes[i].rotation.get_rotation_vector()
69 ],
70 "HERMITE_CENTERLINE": True,
71 },
72 }
75class Beam3rLine2Line2(_Beam):
76 """Represents a Reissner beam with linear shapefunctions in the rotations
77 as well as the displacements."""
79 nodes_create = [-1, 1]
80 beam_type = _mpy.beam.reissner
81 valid_material = [_MaterialReissner]
83 coupling_fix_dict = {"NUMDOF": 6, "ONOFF": [1, 1, 1, 1, 1, 1]}
84 coupling_joint_dict = {"NUMDOF": 6, "ONOFF": [1, 1, 1, 0, 0, 0]}
86 def dump_to_list(self):
87 """Return a list with the (single) item representing this element."""
89 # Check the material.
90 self._check_material()
92 return {
93 "id": self.i_global,
94 "cell": {
95 "type": "LINE2",
96 "connectivity": self.nodes,
97 },
98 "data": {
99 "type": "BEAM3R",
100 "MAT": self.material,
101 "TRIADS": [
102 item
103 for i in [0, 1]
104 for item in self.nodes[i].rotation.get_rotation_vector()
105 ],
106 },
107 }
110class Beam3kClass(_Beam):
111 """Represents a Kirchhoff beam element."""
113 nodes_create = [-1, 0, 1]
114 beam_type = _mpy.beam.kirchhoff
115 valid_material = [_MaterialKirchhoff]
117 coupling_fix_dict = {"NUMDOF": 7, "ONOFF": [1, 1, 1, 1, 1, 1, 0]}
118 coupling_joint_dict = {"NUMDOF": 7, "ONOFF": [1, 1, 1, 0, 0, 0, 0]}
120 def __init__(self, *, weak=True, rotvec=True, is_fad=True, **kwargs):
121 _Beam.__init__(self, **kwargs)
123 # Set the parameters for this beam.
124 self.weak = weak
125 self.rotvec = rotvec
126 self.is_fad = is_fad
128 # Show warning when not using rotvec.
129 if not rotvec:
130 _warnings.warn(
131 "Use rotvec=False with caution, especially when applying the boundary conditions "
132 "and couplings."
133 )
135 def dump_to_list(self):
136 """Return a list with the (single) item representing this element."""
138 # Check the material.
139 self._check_material()
141 return {
142 "id": self.i_global,
143 "cell": {
144 "type": "LINE3",
145 "connectivity": [self.nodes[i] for i in [0, 2, 1]],
146 },
147 "data": {
148 "type": "BEAM3K",
149 "WK": 1 if self.weak else 0,
150 "ROTVEC": 1 if self.rotvec else 0,
151 "MAT": self.material,
152 "TRIADS": [
153 item
154 for i in [0, 2, 1]
155 for item in self.nodes[i].rotation.get_rotation_vector()
156 ],
157 **({"USE_FAD": True} if self.is_fad else {}),
158 },
159 }
162def Beam3k(**kwargs_class):
163 """This factory returns a function that creates a new Beam3kClass object
164 with certain attributes defined.
166 The returned function behaves like a call to the object.
167 """
169 def create_class(**kwargs):
170 """The function that will be returned.
172 This function should behave like the call to the __init__
173 function of the class.
174 """
175 return Beam3kClass(**kwargs_class, **kwargs)
177 return create_class
180class Beam3eb(_Beam):
181 """Represents a Euler Bernoulli beam element."""
183 nodes_create = [-1, 1]
184 beam_type = _mpy.beam.euler_bernoulli
185 valid_material = [_MaterialEulerBernoulli]
187 def dump_to_list(self):
188 """Return a list with the (single) item representing this element."""
190 # Check the material.
191 self._check_material()
193 # The two rotations must be the same and the x1 vector must point from
194 # the start point to the end point.
195 if not self.nodes[0].rotation == self.nodes[1].rotation:
196 raise ValueError(
197 "The two nodal rotations in Euler Bernoulli beams must be the same, i.e. the beam "
198 "has to be straight!"
199 )
200 direction = self.nodes[1].coordinates - self.nodes[0].coordinates
201 t1 = self.nodes[0].rotation * [1, 0, 0]
202 if _np.linalg.norm(direction / _np.linalg.norm(direction) - t1) >= _mpy.eps_pos:
203 raise ValueError(
204 "The rotations do not match the direction of the Euler Bernoulli beam!"
205 )
207 return {
208 "id": self.i_global,
209 "cell": {
210 "type": "LINE2",
211 "connectivity": self.nodes,
212 },
213 "data": {
214 "type": "BEAM3EB",
215 "MAT": self.material,
216 },
217 }