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

81 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-28 15:20 +0000

1# The MIT License (MIT) 

2# 

3# Copyright (c) 2018-2026 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. 

50 

51 The structure will be created in the x-y plane. 

52 

53 Args 

54 ---- 

55 mesh: Mesh 

56 Mesh that the honeycomb will be added to. 

57 beam_class: Beam 

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

59 material: Material 

60 Material for the beam. 

61 width: float 

62 Width of one honeycomb. 

63 n_width: int 

64 Number of honeycombs in x-direction. 

65 n_height: int 

66 Number of honeycombs in y-direction. 

67 n_el: int 

68 Number of elements per beam line. 

69 closed_width: bool 

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

71 closed_height: bool 

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

73 create_couplings: bool 

74 If the nodes will be connected in this function. 

75 

76 Return 

77 ---- 

78 return_set: GeometryName 

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

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

81 'all' contains all nodes. 

82 """ 

83 

84 def add_line(pointa, pointb): 

85 """Shortcut to add line.""" 

86 return _create_beam_mesh_line( 

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

88 ) 

89 

90 # Geometrical shortcuts. 

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

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

93 a = width * 0.5 / cos30 

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

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

96 zig_zag_x = nx * width * 0.5 

97 zig_zag_y = ny * a * sin30 * 0.5 

98 

99 # Create the honeycomb structure 

100 mesh_honeycomb = _Mesh() 

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

102 for i_height in range(n_height + 1): 

103 # Start point for this zig-zag line. 

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

105 

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

107 if i_height % 2 == 0: 

108 direction = 1 

109 else: 

110 direction = -1 

111 

112 for i_width in range(n_width + 1): 

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

114 

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

116 # for the remaining vertical lines). 

117 if i_width < n_width: 

118 add_line( 

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

120 ) 

121 add_line( 

122 base_zig_zag + zig_zag_x - 2 * direction * zig_zag_y, 

123 base_zig_zag + nx * width, 

124 ) 

125 

126 # Check where the vertical lines start. 

127 if i_height % 2 == 0: 

128 base_vert = base_zig_zag 

129 else: 

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

131 

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

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

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

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

136 add_line(base_vert, base_vert + ny * a) 

137 

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

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

140 

141 # Add connections for the nodes with same positions. 

142 if create_couplings: 

143 mesh_honeycomb.couple_nodes(nodes=honeycomb_nodes) 

144 

145 # Get min and max nodes of the honeycomb. 

146 min_max_nodes = _get_min_max_nodes(honeycomb_nodes) 

147 

148 # Return the geometry set. 

149 return_set = _GeometryName() 

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

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

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

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

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

155 

156 mesh.add(mesh_honeycomb) 

157 

158 return return_set 

159 

160 

161def create_beam_mesh_honeycomb( 

162 mesh, 

163 beam_class, 

164 material, 

165 diameter, 

166 n_circumference, 

167 n_axis, 

168 *, 

169 n_el=1, 

170 closed_top=True, 

171 vertical=True, 

172): 

173 """Wrap a honeycomb structure around a cylinder. 

174 

175 The cylinder axis will be the z-axis. 

176 

177 Args 

178 ---- 

179 mesh: Mesh 

180 Mesh that the honeycomb will be added to. 

181 beam_class: Beam 

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

183 material: Material 

184 Material for the beam. 

185 diameter: float 

186 Diameter of the cylinder. 

187 n_circumference: int 

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

189 has to be an odd number. 

190 n_axis: int 

191 Number of honeycombs in axial-direction. 

192 n_el: int 

193 Number of elements per beam line. 

194 closed_top: bool 

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

196 vertical: bool 

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

198 

199 Return 

200 ---- 

201 return_set: GeometryName 

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

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

204 contains all nodes. 

205 """ 

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

207 if vertical: 

208 width = diameter * _np.pi / n_circumference 

209 closed_width = False 

210 closed_height = closed_top 

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

212 n_height = n_axis 

213 n_width = n_circumference 

214 else: 

215 if not n_circumference % 2 == 0: 

216 raise ValueError( 

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

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

219 ) 

220 H = diameter * _np.pi / n_circumference 

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

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

223 closed_width = closed_top 

224 closed_height = False 

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

226 n_height = n_circumference - 1 

227 n_width = n_axis 

228 

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

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

231 # connected. 

232 mesh_temp = _Mesh() 

233 honeycomb_sets = create_beam_mesh_honeycomb_flat( 

234 mesh_temp, 

235 beam_class, 

236 material, 

237 width, 

238 n_width, 

239 n_height, 

240 n_el=n_el, 

241 closed_width=closed_width, 

242 closed_height=closed_height, 

243 create_couplings=False, 

244 ) 

245 

246 # Move the mesh to the correct position. 

247 mesh_temp.rotate(rotation) 

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

249 mesh_temp.wrap_around_cylinder() 

250 

251 # Add connections for the nodes with same positions. 

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

253 mesh_temp.couple_nodes(nodes=honeycomb_nodes) 

254 

255 # Return the geometry set' 

256 return_set = _GeometryName() 

257 return_set["all"] = honeycomb_sets["all"] 

258 if vertical: 

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

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

261 else: 

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

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

264 

265 # Add to this mesh 

266 mesh.add_mesh(mesh_temp) 

267 

268 return return_set