Coverage for src/beamme/mesh_creation_functions/applications/beam_honeycomb.py: 98%

81 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 has functions to create a honeycomb beam mesh.""" 

23 

24import numpy as _np 

25 

26from beamme.core.geometry_set import GeometryName as _GeometryName 

27from beamme.core.geometry_set import GeometrySet as _GeometrySet 

28from beamme.core.mesh import Mesh as _Mesh 

29from beamme.core.rotation import Rotation as _Rotation 

30from beamme.mesh_creation_functions.beam_line import ( 

31 create_beam_mesh_line as _create_beam_mesh_line, 

32) 

33from beamme.utils.nodes import get_min_max_nodes as _get_min_max_nodes 

34 

35 

36def create_beam_mesh_honeycomb_flat( 

37 mesh, 

38 beam_class, 

39 material, 

40 width, 

41 n_width, 

42 n_height, 

43 *, 

44 n_el=1, 

45 closed_width=True, 

46 closed_height=True, 

47 create_couplings=True, 

48): 

49 """Add a flat honeycomb structure. The structure will be created in the x-y 

50 plane. 

51 

52 Args 

53 ---- 

54 mesh: Mesh 

55 Mesh that the honeycomb will be added to. 

56 beam_class: Beam 

57 Class that will be used to create the beam elements. 

58 material: Material 

59 Material for the beam. 

60 width: float 

61 Width of one honeycomb. 

62 n_width: int 

63 Number of honeycombs in x-direction. 

64 n_height: int 

65 Number of honeycombs in y-direction. 

66 n_el: int 

67 Number of elements per beam line. 

68 closed_width: bool 

69 If the last honeycombs in x-direction will be closed. 

70 closed_height: bool 

71 If the last vertical lines in y-direction will be created. 

72 create_couplings: bool 

73 If the nodes will be connected in this function. 

74 

75 Return 

76 ---- 

77 return_set: GeometryName 

78 Set with nodes on the north, south, east and west boundaries. Those 

79 sets only contains end nodes of lines, not the middle ones. The set 

80 'all' contains all nodes. 

81 """ 

82 

83 def add_line(pointa, pointb): 

84 """Shortcut to add line.""" 

85 return _create_beam_mesh_line( 

86 mesh_honeycomb, beam_class, material, pointa, pointb, n_el=n_el 

87 ) 

88 

89 # Geometrical shortcuts. 

90 sin30 = _np.sin(_np.pi / 6) 

91 cos30 = _np.sin(2 * _np.pi / 6) 

92 a = width * 0.5 / cos30 

93 nx = _np.array([1.0, 0.0, 0.0]) 

94 ny = _np.array([0.0, 1.0, 0.0]) 

95 zig_zag_x = nx * width * 0.5 

96 zig_zag_y = ny * a * sin30 * 0.5 

97 

98 # Create the honeycomb structure 

99 mesh_honeycomb = _Mesh() 

100 origin = _np.array([0, a * 0.5 * sin30, 0]) 

101 for i_height in range(n_height + 1): 

102 # Start point for this zig-zag line. 

103 base_row = origin + (2 * zig_zag_y + a * ny) * i_height 

104 

105 # If the first node is up or down of base_row. 

106 if i_height % 2 == 0: 

107 direction = 1 

108 else: 

109 direction = -1 

110 

111 for i_width in range(n_width + 1): 

112 base_zig_zag = base_row + direction * zig_zag_y + width * i_width * nx 

113 

114 # Do not add a zig-zag line on the last run (that one is only 

115 # for the remaining vertical lines). 

116 if i_width < n_width: 

117 add_line( 

118 base_zig_zag, base_zig_zag + zig_zag_x - 2 * direction * zig_zag_y 

119 ) 

120 add_line( 

121 base_zig_zag + zig_zag_x - 2 * direction * zig_zag_y, 

122 base_zig_zag + nx * width, 

123 ) 

124 

125 # Check where the vertical lines start. 

126 if i_height % 2 == 0: 

127 base_vert = base_zig_zag 

128 else: 

129 base_vert = base_zig_zag + zig_zag_x - 2 * direction * zig_zag_y 

130 

131 # Only add vertical lines at the end if closed_width. 

132 if (i_width < n_width) or (direction == 1 and closed_width): 

133 # Check if the vertical lines at the top should be added. 

134 if not (i_height == n_height) or (not closed_height): 

135 add_line(base_vert, base_vert + ny * a) 

136 

137 # List of nodes from the honeycomb that are candidates for connections. 

138 honeycomb_nodes = [node for node in mesh_honeycomb.nodes if node.is_end_node] 

139 

140 # Add connections for the nodes with same positions. 

141 if create_couplings: 

142 mesh_honeycomb.couple_nodes(nodes=honeycomb_nodes) 

143 

144 # Get min and max nodes of the honeycomb. 

145 min_max_nodes = _get_min_max_nodes(honeycomb_nodes) 

146 

147 # Return the geometry set. 

148 return_set = _GeometryName() 

149 return_set["north"] = min_max_nodes["y_max"] 

150 return_set["east"] = min_max_nodes["x_max"] 

151 return_set["south"] = min_max_nodes["y_min"] 

152 return_set["west"] = min_max_nodes["x_min"] 

153 return_set["all"] = _GeometrySet(mesh_honeycomb.elements) 

154 

155 mesh.add(mesh_honeycomb) 

156 

157 return return_set 

158 

159 

160def create_beam_mesh_honeycomb( 

161 mesh, 

162 beam_class, 

163 material, 

164 diameter, 

165 n_circumference, 

166 n_axis, 

167 *, 

168 n_el=1, 

169 closed_top=True, 

170 vertical=True, 

171): 

172 """Wrap a honeycomb structure around a cylinder. The cylinder axis will be 

173 the z-axis. 

174 

175 Args 

176 ---- 

177 mesh: Mesh 

178 Mesh that the honeycomb will be added to. 

179 beam_class: Beam 

180 Class that will be used to create the beam elements. 

181 material: Material 

182 Material for the beam. 

183 diameter: float 

184 Diameter of the cylinder. 

185 n_circumference: int 

186 Number of honeycombs around the diameter. If vertical is False this 

187 has to be an odd number. 

188 n_axis: int 

189 Number of honeycombs in axial-direction. 

190 n_el: int 

191 Number of elements per beam line. 

192 closed_top: bool 

193 If the last honeycombs in axial-direction will be closed. 

194 vertical: bool 

195 If there are vertical lines in the honeycomb or horizontal. 

196 

197 Return 

198 ---- 

199 return_set: GeometryName 

200 Set with nodes on the top and bottom boundaries. Those sets only 

201 contains end nodes of lines, not the middle ones. The set "all" 

202 contains all nodes. 

203 """ 

204 

205 # Calculate the input values for the flat honeycomb mesh. 

206 if vertical: 

207 width = diameter * _np.pi / n_circumference 

208 closed_width = False 

209 closed_height = closed_top 

210 rotation = _Rotation([0, 0, 1], _np.pi / 2) * _Rotation([1, 0, 0], _np.pi / 2) 

211 n_height = n_axis 

212 n_width = n_circumference 

213 else: 

214 if not n_circumference % 2 == 0: 

215 raise ValueError( 

216 "There has to be an even number of elements along the diameter in horizontal mode. " 

217 "Given: {}!".format(n_circumference) 

218 ) 

219 H = diameter * _np.pi / n_circumference 

220 r = H / (1 + _np.sin(_np.pi / 6)) 

221 width = 2 * r * _np.cos(_np.pi / 6) 

222 closed_width = closed_top 

223 closed_height = False 

224 rotation = _Rotation([0, 1, 0], -0.5 * _np.pi) 

225 n_height = n_circumference - 1 

226 n_width = n_axis 

227 

228 # Create the flat mesh, do not create couplings, as they will be added 

229 # later in this function, where also the diameter nodes will be 

230 # connected. 

231 mesh_temp = _Mesh() 

232 honeycomb_sets = create_beam_mesh_honeycomb_flat( 

233 mesh_temp, 

234 beam_class, 

235 material, 

236 width, 

237 n_width, 

238 n_height, 

239 n_el=n_el, 

240 closed_width=closed_width, 

241 closed_height=closed_height, 

242 create_couplings=False, 

243 ) 

244 

245 # Move the mesh to the correct position. 

246 mesh_temp.rotate(rotation) 

247 mesh_temp.translate([diameter / 2, 0, 0]) 

248 mesh_temp.wrap_around_cylinder() 

249 

250 # Add connections for the nodes with same positions. 

251 honeycomb_nodes = [node for node in mesh_temp.nodes if node.is_end_node] 

252 mesh_temp.couple_nodes(nodes=honeycomb_nodes) 

253 

254 # Return the geometry set' 

255 return_set = _GeometryName() 

256 return_set["all"] = honeycomb_sets["all"] 

257 if vertical: 

258 return_set["top"] = honeycomb_sets["north"] 

259 return_set["bottom"] = honeycomb_sets["south"] 

260 else: 

261 return_set["top"] = honeycomb_sets["east"] 

262 return_set["bottom"] = honeycomb_sets["west"] 

263 

264 # Add to this mesh 

265 mesh.add_mesh(mesh_temp) 

266 

267 return return_set