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

74 statements  

« prev     ^ index     » next       coverage.py v7.10.7, created at 2025-09-29 11:30 +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 BeamType as _BeamType 

32from beamme.four_c.input_file_mappings import ( 

33 INPUT_FILE_MAPPINGS as _INPUT_FILE_MAPPINGS, 

34) 

35from beamme.four_c.material import MaterialEulerBernoulli as _MaterialEulerBernoulli 

36from beamme.four_c.material import MaterialKirchhoff as _MaterialKirchhoff 

37from beamme.four_c.material import MaterialReissner as _MaterialReissner 

38from beamme.four_c.material import ( 

39 MaterialReissnerElastoplastic as _MaterialReissnerElastoplastic, 

40) 

41 

42 

43def dump_four_c_beam_to_list(self) -> dict: 

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

45 

46 Args: 

47 self: The beam element to be dumped. 

48 """ 

49 

50 # Check the material. 

51 self._check_material() 

52 

53 # Gather the element data for the input file. 

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

55 element_data["MAT"] = self.material 

56 if type(self).four_c_triads: 

57 element_data["TRIADS"] = [ 

58 item 

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

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

61 ] 

62 

63 return { 

64 "id": self.i_global + 1, 

65 "cell": { 

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

67 "connectivity": [ 

68 self.nodes[i] 

69 for i in _INPUT_FILE_MAPPINGS["n_nodes_to_node_ordering"][ 

70 len(self.nodes) 

71 ] 

72 ], 

73 }, 

74 "data": element_data, 

75 } 

76 

77 

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

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

80 

81 four_c_element_data = { 

82 "type": _INPUT_FILE_MAPPINGS["beam_types"][_BeamType.reissner] 

83 } 

84 if is_hermite_centerline: 

85 # TODO: Move this to the four_c_element_data. 

86 four_c_element_data["HERMITE_CENTERLINE"] = is_hermite_centerline 

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

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

89 else: 

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

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

92 

93 return type( 

94 "BeamFourCSimoReissner", 

95 (_generate_beam_class(n_nodes),), 

96 { 

97 "four_c_beam_type": _BeamType.reissner, 

98 "four_c_triads": True, 

99 "four_c_element_data": four_c_element_data, 

100 "valid_materials": [_MaterialReissner, _MaterialReissnerElastoplastic], 

101 "coupling_fix_dict": coupling_fix_dict, 

102 "coupling_joint_dict": coupling_joint_dict, 

103 "dump_to_list": dump_four_c_beam_to_list, 

104 }, 

105 ) 

106 

107 

108def get_four_c_kirchhoff_beam(weak=True, rotvec=True, is_fad=True) -> type[_Beam]: 

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

110 

111 # Set the parameters for this beam. 

112 four_c_element_data = { 

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

114 "WK": weak, 

115 "ROTVEC": 1 if rotvec else 0, 

116 } 

117 # TODO: Move this to the four_c_element_data. 

118 if is_fad: 

119 four_c_element_data["USE_FAD"] = True 

120 

121 # Show warning when not using rotvec. 

122 if not rotvec: 

123 _warnings.warn( 

124 "Use rotvec=False with caution, especially when applying the boundary conditions " 

125 "and couplings." 

126 ) 

127 

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

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

130 

131 return type( 

132 "BeamFourCKirchhoffLove", 

133 (_generate_beam_class(3),), 

134 { 

135 "four_c_beam_type": _BeamType.kirchhoff, 

136 "four_c_triads": True, 

137 "four_c_element_data": four_c_element_data, 

138 "valid_materials": [_MaterialKirchhoff], 

139 "coupling_fix_dict": coupling_fix_dict, 

140 "coupling_joint_dict": coupling_joint_dict, 

141 "dump_to_list": dump_four_c_beam_to_list, 

142 }, 

143 ) 

144 

145 

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

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

148 

149 four_c_beam_type = _BeamType.euler_bernoulli 

150 four_c_triads = False 

151 four_c_element_data = { 

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

153 } 

154 

155 valid_materials = [_MaterialEulerBernoulli] 

156 

157 def dump_to_list(self): 

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

159 

160 # Check the material. 

161 self._check_material() 

162 

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

164 # the start point to the end point. 

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

166 raise ValueError( 

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

168 "has to be straight!" 

169 ) 

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

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

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

173 raise ValueError( 

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

175 ) 

176 

177 return dump_four_c_beam_to_list(self) 

178 

179 

180def get_four_c_beam( 

181 beam_type: _BeamType, 

182 *, 

183 n_nodes: int | None = None, 

184 is_hermite_centerline: bool | None = None, 

185 **kwargs, 

186) -> type[_Beam]: 

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

188 

189 def _check_arguments(name, value, expected): 

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

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

192 raise ValueError( 

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

194 ) 

195 

196 match beam_type: 

197 case _BeamType.reissner: 

198 # Set default values for centerline interpolation 

199 if n_nodes is None: 

200 n_nodes = 3 

201 if is_hermite_centerline is None: 

202 is_hermite_centerline = True 

203 return get_four_c_reissner_beam( 

204 n_nodes=n_nodes, is_hermite_centerline=is_hermite_centerline, **kwargs 

205 ) 

206 case _BeamType.kirchhoff: 

207 _check_arguments("n_nodes", n_nodes, 3) 

208 _check_arguments("is_hermite_centerline", is_hermite_centerline, True) 

209 return get_four_c_kirchhoff_beam(**kwargs) 

210 case _BeamType.euler_bernoulli: 

211 _check_arguments("n_nodes", n_nodes, 2) 

212 _check_arguments("is_hermite_centerline", is_hermite_centerline, True) 

213 return BeamFourCEulerBernoulli 

214 case _: 

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

216 

217 

218# Provide shortcuts for backwards compatibility 

219Beam3rHerm2Line3 = get_four_c_beam( 

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

221) 

222Beam3rLine2Line2 = get_four_c_beam( 

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

224) 

225Beam3eb = get_four_c_beam(_BeamType.euler_bernoulli)