Coverage for src/meshpy/four_c/element_beam.py: 95%

76 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-04-28 04:21 +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.""" 

23 

24import warnings as _warnings 

25 

26import numpy as _np 

27 

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) 

36from meshpy.utils.environment import fourcipp_is_available as _fourcipp_is_available 

37 

38 

39class Beam3rHerm2Line3(_Beam): 

40 """Represents a BEAM3R HERM2LINE3 element.""" 

41 

42 nodes_create = [-1, 0, 1] 

43 beam_type = _mpy.beam.reissner 

44 valid_material = [_MaterialReissner, _MaterialReissnerElastoplastic] 

45 

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

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

48 

49 def dump_to_list(self): 

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

51 

52 if _fourcipp_is_available(): 

53 raise ValueError( 

54 "Port this functionality to not use the legacy string any more" 

55 ) 

56 

57 string_nodes = " ".join(str(self.nodes[i].i_global) for i in [0, 2, 1]) 

58 string_triads = " ".join( 

59 str(item) 

60 for i in [0, 2, 1] 

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

62 ) 

63 

64 # Check the material. 

65 self._check_material() 

66 

67 return [ 

68 f"{self.i_global} BEAM3R HERM2LINE3 {string_nodes} MAT {self.material.i_global} " 

69 f"TRIADS {string_triads}" 

70 ] 

71 

72 

73class Beam3rLine2Line2(_Beam): 

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

75 as well as the displacements.""" 

76 

77 nodes_create = [-1, 1] 

78 beam_type = _mpy.beam.reissner 

79 valid_material = [_MaterialReissner] 

80 

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

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

83 

84 def dump_to_list(self): 

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

86 

87 if _fourcipp_is_available(): 

88 raise ValueError( 

89 "Port this functionality to not use the legacy string any more" 

90 ) 

91 

92 string_nodes = " ".join(str(self.nodes[i].i_global) for i in [0, 1]) 

93 string_triads = " ".join( 

94 str(item) 

95 for i in [0, 1] 

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

97 ) 

98 

99 # Check the material. 

100 self._check_material() 

101 

102 return [ 

103 f"{self.i_global} BEAM3R LINE2 {string_nodes} MAT {self.material.i_global} " 

104 f"TRIADS {string_triads}" 

105 ] 

106 

107 

108class Beam3kClass(_Beam): 

109 """Represents a Kirchhoff beam element.""" 

110 

111 nodes_create = [-1, 0, 1] 

112 beam_type = _mpy.beam.kirchhoff 

113 valid_material = [_MaterialKirchhoff] 

114 

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

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

117 

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

119 _Beam.__init__(self, **kwargs) 

120 

121 # Set the parameters for this beam. 

122 self.weak = weak 

123 self.rotvec = rotvec 

124 self.is_fad = is_fad 

125 

126 # Show warning when not using rotvec. 

127 if not rotvec: 

128 _warnings.warn( 

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

130 "and couplings." 

131 ) 

132 

133 def dump_to_list(self): 

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

135 

136 if _fourcipp_is_available(): 

137 raise ValueError( 

138 "Port this functionality to not use the legacy string any more" 

139 ) 

140 

141 string_nodes = " ".join(str(self.nodes[i].i_global) for i in [0, 2, 1]) 

142 string_triads = " ".join( 

143 str(item) 

144 for i in [0, 2, 1] 

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

146 ) 

147 

148 # Check the material. 

149 self._check_material() 

150 

151 string_dat = ("{} BEAM3K LINE3 {} WK {} ROTVEC {} MAT {} TRIADS {}{}").format( 

152 self.i_global, 

153 string_nodes, 

154 "1" if self.weak else "0", 

155 "1" if self.rotvec else "0", 

156 self.material.i_global, 

157 string_triads, 

158 " FAD" if self.is_fad else "", 

159 ) 

160 

161 return [string_dat] 

162 

163 

164def Beam3k(**kwargs_class): 

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

166 with certain attributes defined. 

167 

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

169 """ 

170 

171 def create_class(**kwargs): 

172 """The function that will be returned. 

173 

174 This function should behave like the call to the __init__ 

175 function of the class. 

176 """ 

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

178 

179 return create_class 

180 

181 

182class Beam3eb(_Beam): 

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

184 

185 nodes_create = [-1, 1] 

186 beam_type = _mpy.beam.euler_bernoulli 

187 valid_material = [_MaterialEulerBernoulli] 

188 

189 def dump_to_list(self): 

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

191 

192 if _fourcipp_is_available(): 

193 raise ValueError( 

194 "Port this functionality to not use the legacy string any more" 

195 ) 

196 

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

198 # the start point to the end point. 

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

200 raise ValueError( 

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

202 "has to be straight!" 

203 ) 

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

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

206 if _np.linalg.norm(direction / _np.linalg.norm(direction) - t1) >= _mpy.eps_pos: 

207 raise ValueError( 

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

209 ) 

210 

211 string_nodes = " ".join(str(self.nodes[i].i_global) for i in [0, 1]) 

212 

213 # Check the material. 

214 self._check_material() 

215 

216 return [ 

217 f"{self.i_global} BEAM3EB LINE2 {string_nodes} MAT {self.material.i_global}" 

218 ]