Coverage for src / beamme / four_c / element_beam.py: 89%

73 statements  

« prev     ^ index     » next       coverage.py v7.12.0, created at 2025-11-21 12:57 +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.core.element_beam import generate_beam_class as _generate_beam_class 

31from beamme.four_c.four_c_types import ( 

32 BeamKirchhoffConstraintType as _BeamKirchhoffConstraintType, 

33) 

34from beamme.four_c.four_c_types import ( 

35 BeamKirchhoffParametrizationType as _BeamKirchhoffParametrizationType, 

36) 

37from beamme.four_c.four_c_types import BeamType as _BeamType 

38from beamme.four_c.input_file_mappings import ( 

39 INPUT_FILE_MAPPINGS as _INPUT_FILE_MAPPINGS, 

40) 

41from beamme.four_c.material import MaterialEulerBernoulli as _MaterialEulerBernoulli 

42from beamme.four_c.material import MaterialKirchhoff as _MaterialKirchhoff 

43from beamme.four_c.material import MaterialReissner as _MaterialReissner 

44from beamme.four_c.material import ( 

45 MaterialReissnerElastoplastic as _MaterialReissnerElastoplastic, 

46) 

47 

48 

49def dump_four_c_beam_to_list(self) -> dict: 

50 """Return the dictionary representing this beam element in 4C. 

51 

52 Args: 

53 self: The beam element to be dumped. 

54 """ 

55 

56 # Check the material. 

57 self._check_material() 

58 

59 # Gather the element data for the input file. 

60 element_data = type(self).four_c_element_data | self.data 

61 element_data["MAT"] = self.material 

62 if type(self).four_c_triads: 

63 element_data["TRIADS"] = [ 

64 item 

65 for i in _INPUT_FILE_MAPPINGS["n_nodes_to_node_ordering"][len(self.nodes)] 

66 for item in self.nodes[i].rotation.get_rotation_vector() 

67 ] 

68 

69 return { 

70 "id": self.i_global + 1, 

71 "cell": { 

72 "type": _INPUT_FILE_MAPPINGS["n_nodes_to_cell_type"][len(self.nodes)], 

73 "connectivity": [ 

74 self.nodes[i] 

75 for i in _INPUT_FILE_MAPPINGS["n_nodes_to_node_ordering"][ 

76 len(self.nodes) 

77 ] 

78 ], 

79 }, 

80 "data": element_data, 

81 } 

82 

83 

84def get_four_c_reissner_beam(n_nodes: int, is_hermite_centerline: bool) -> type[_Beam]: 

85 """Return a Simo-Reissner beam for 4C.""" 

86 

87 four_c_element_data = { 

88 "type": _INPUT_FILE_MAPPINGS["beam_types"][_BeamType.reissner], 

89 "HERMITE_CENTERLINE": is_hermite_centerline, 

90 } 

91 if is_hermite_centerline: 

92 coupling_fix_dict = {"NUMDOF": 9, "ONOFF": [1, 1, 1, 1, 1, 1, 0, 0, 0]} 

93 coupling_joint_dict = {"NUMDOF": 9, "ONOFF": [1, 1, 1, 0, 0, 0, 0, 0, 0]} 

94 else: 

95 coupling_fix_dict = {"NUMDOF": 6, "ONOFF": [1, 1, 1, 1, 1, 1]} 

96 coupling_joint_dict = {"NUMDOF": 6, "ONOFF": [1, 1, 1, 0, 0, 0]} 

97 

98 return type( 

99 "BeamFourCSimoReissner", 

100 (_generate_beam_class(n_nodes),), 

101 { 

102 "four_c_beam_type": _BeamType.reissner, 

103 "four_c_triads": True, 

104 "four_c_element_data": four_c_element_data, 

105 "valid_materials": [_MaterialReissner, _MaterialReissnerElastoplastic], 

106 "coupling_fix_dict": coupling_fix_dict, 

107 "coupling_joint_dict": coupling_joint_dict, 

108 "dump_to_list": dump_four_c_beam_to_list, 

109 }, 

110 ) 

111 

112 

113def get_four_c_kirchhoff_beam( 

114 constraint: _BeamKirchhoffConstraintType = _BeamKirchhoffConstraintType.weak, 

115 parametrization: _BeamKirchhoffParametrizationType = _BeamKirchhoffParametrizationType.rot, 

116 is_fad: bool = True, 

117) -> type[_Beam]: 

118 """Return a Kirchhoff-Love beam for 4C.""" 

119 

120 # Set the parameters for this beam. 

121 four_c_element_data = { 

122 "type": _INPUT_FILE_MAPPINGS["beam_types"][_BeamType.kirchhoff], 

123 "CONSTRAINT": constraint.name, 

124 "PARAMETRIZATION": parametrization.name, 

125 "USE_FAD": is_fad, 

126 } 

127 

128 # Show warning when not using rotvec. 

129 if not parametrization == _BeamKirchhoffParametrizationType.rot: 

130 _warnings.warn( 

131 "Use tangent based parametrization with caution, especially when " 

132 " applying the boundary conditions and couplings." 

133 ) 

134 

135 coupling_fix_dict = {"NUMDOF": 7, "ONOFF": [1, 1, 1, 1, 1, 1, 0]} 

136 coupling_joint_dict = {"NUMDOF": 7, "ONOFF": [1, 1, 1, 0, 0, 0, 0]} 

137 

138 return type( 

139 "BeamFourCKirchhoffLove", 

140 (_generate_beam_class(3),), 

141 { 

142 "four_c_beam_type": _BeamType.kirchhoff, 

143 "four_c_triads": True, 

144 "four_c_element_data": four_c_element_data, 

145 "valid_materials": [_MaterialKirchhoff], 

146 "coupling_fix_dict": coupling_fix_dict, 

147 "coupling_joint_dict": coupling_joint_dict, 

148 "dump_to_list": dump_four_c_beam_to_list, 

149 }, 

150 ) 

151 

152 

153class BeamFourCEulerBernoulli(_generate_beam_class(2)): # type: ignore[misc] 

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

155 

156 four_c_beam_type = _BeamType.euler_bernoulli 

157 four_c_triads = False 

158 four_c_element_data = { 

159 "type": _INPUT_FILE_MAPPINGS["beam_types"][_BeamType.euler_bernoulli] 

160 } 

161 

162 valid_materials = [_MaterialEulerBernoulli] 

163 

164 def dump_to_list(self): 

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

166 

167 # Check the material. 

168 self._check_material() 

169 

170 # The two rotations must be the same and the x1 vector must point from 

171 # the start point to the end point. 

172 if not self.nodes[0].rotation == self.nodes[1].rotation: 

173 raise ValueError( 

174 "The two nodal rotations in Euler Bernoulli beams must be the same, i.e. the beam " 

175 "has to be straight!" 

176 ) 

177 direction = self.nodes[1].coordinates - self.nodes[0].coordinates 

178 t1 = self.nodes[0].rotation * [1, 0, 0] 

179 if _np.linalg.norm(direction / _np.linalg.norm(direction) - t1) >= _bme.eps_pos: 

180 raise ValueError( 

181 "The rotations do not match the direction of the Euler Bernoulli beam!" 

182 ) 

183 

184 return dump_four_c_beam_to_list(self) 

185 

186 

187def get_four_c_beam( 

188 beam_type: _BeamType, 

189 *, 

190 n_nodes: int | None = None, 

191 is_hermite_centerline: bool | None = None, 

192 **kwargs, 

193) -> type[_Beam]: 

194 """Return an object that can be used to create beams with 4C.""" 

195 

196 def _check_arguments(name, value, expected): 

197 """Check that if an argument is given, it has the expected value.""" 

198 if value is not None and not value == expected: 

199 raise ValueError( 

200 f"Parameter {name} with the value {value} does not match the expected value {expected}" 

201 ) 

202 

203 match beam_type: 

204 case _BeamType.reissner: 

205 # Set default values for centerline interpolation 

206 if n_nodes is None: 

207 n_nodes = 3 

208 if is_hermite_centerline is None: 

209 is_hermite_centerline = True 

210 return get_four_c_reissner_beam( 

211 n_nodes=n_nodes, is_hermite_centerline=is_hermite_centerline, **kwargs 

212 ) 

213 case _BeamType.kirchhoff: 

214 _check_arguments("n_nodes", n_nodes, 3) 

215 _check_arguments("is_hermite_centerline", is_hermite_centerline, True) 

216 return get_four_c_kirchhoff_beam(**kwargs) 

217 case _BeamType.euler_bernoulli: 

218 _check_arguments("n_nodes", n_nodes, 2) 

219 _check_arguments("is_hermite_centerline", is_hermite_centerline, True) 

220 return BeamFourCEulerBernoulli 

221 case _: 

222 raise ValueError("Got unexpected beam type.") 

223 

224 

225# Provide shortcuts for backwards compatibility 

226Beam3rHerm2Line3 = get_four_c_beam( 

227 _BeamType.reissner, n_nodes=3, is_hermite_centerline=True 

228) 

229Beam3rLine2Line2 = get_four_c_beam( 

230 _BeamType.reissner, n_nodes=2, is_hermite_centerline=False 

231) 

232Beam3eb = get_four_c_beam(_BeamType.euler_bernoulli)