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