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

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.""" 

23 

24import warnings as _warnings 

25 

26import numpy as _np 

27 

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) 

37 

38 

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.""" 

43 

44 nodes_create = [-1, 0, 1] 

45 beam_type = _BeamType.reissner 

46 valid_material = [_MaterialReissner, _MaterialReissnerElastoplastic] 

47 

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]} 

50 

51 def dump_to_list(self): 

52 """Return a list with the (single) item representing this element.""" 

53 

54 # Check the material. 

55 self._check_material() 

56 

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 } 

74 

75 

76class Beam3rLine2Line2(_Beam): 

77 """Represents a Reissner beam with linear shapefunctions in the rotations 

78 as well as the displacements.""" 

79 

80 nodes_create = [-1, 1] 

81 beam_type = _BeamType.reissner 

82 valid_material = [_MaterialReissner] 

83 

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]} 

86 

87 def dump_to_list(self): 

88 """Return a list with the (single) item representing this element.""" 

89 

90 # Check the material. 

91 self._check_material() 

92 

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 } 

109 

110 

111class Beam3kClass(_Beam): 

112 """Represents a Kirchhoff beam element.""" 

113 

114 nodes_create = [-1, 0, 1] 

115 beam_type = _BeamType.kirchhoff 

116 valid_material = [_MaterialKirchhoff] 

117 

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]} 

120 

121 def __init__(self, *, weak=True, rotvec=True, is_fad=True, **kwargs): 

122 _Beam.__init__(self, **kwargs) 

123 

124 # Set the parameters for this beam. 

125 self.weak = weak 

126 self.rotvec = rotvec 

127 self.is_fad = is_fad 

128 

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 ) 

135 

136 def dump_to_list(self): 

137 """Return a list with the (single) item representing this element.""" 

138 

139 # Check the material. 

140 self._check_material() 

141 

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 } 

161 

162 

163def Beam3k(**kwargs_class): 

164 """This factory returns a function that creates a new Beam3kClass object 

165 with certain attributes defined. 

166 

167 The returned function behaves like a call to the object. 

168 """ 

169 

170 def create_class(**kwargs): 

171 """The function that will be returned. 

172 

173 This function should behave like the call to the __init__ 

174 function of the class. 

175 """ 

176 return Beam3kClass(**kwargs_class, **kwargs) 

177 

178 return create_class 

179 

180 

181class Beam3eb(_Beam): 

182 """Represents a Euler Bernoulli beam element.""" 

183 

184 nodes_create = [-1, 1] 

185 beam_type = _BeamType.euler_bernoulli 

186 valid_material = [_MaterialEulerBernoulli] 

187 

188 def dump_to_list(self): 

189 """Return a list with the (single) item representing this element.""" 

190 

191 # Check the material. 

192 self._check_material() 

193 

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 ) 

207 

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 }