Coverage for src/meshpy/mesh_creation_functions/beam_honeycomb.py: 96%

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

23 

24import numpy as _np 

25 

26from meshpy.core.geometry_set import GeometryName as _GeometryName 

27from meshpy.core.geometry_set import GeometrySet as _GeometrySet 

28from meshpy.core.mesh import Mesh as _Mesh 

29from meshpy.core.rotation import Rotation as _Rotation 

30from meshpy.mesh_creation_functions.beam_basic_geometry import ( 

31 create_beam_mesh_line as _create_beam_mesh_line, 

32) 

33from meshpy.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 add_sets=False, 

49): 

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

51 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 add_sets: bool 

76 If this is true the sets are added to the mesh and then displayed 

77 in eventual VTK output, even if they are not used for a boundary 

78 condition or coupling. 

79 

80 Return 

81 ---- 

82 return_set: GeometryName 

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

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

85 'all' contains all nodes. 

86 """ 

87 

88 def add_line(pointa, pointb): 

89 """Shortcut to add line.""" 

90 return _create_beam_mesh_line( 

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

92 ) 

93 

94 # Geometrical shortcuts. 

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

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

97 a = width * 0.5 / cos30 

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

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

100 zig_zag_x = nx * width * 0.5 

101 zig_zag_y = ny * a * sin30 * 0.5 

102 

103 # Create the honeycomb structure 

104 mesh_honeycomb = _Mesh() 

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

106 for i_height in range(n_height + 1): 

107 # Start point for this zig-zag line. 

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

109 

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

111 if i_height % 2 == 0: 

112 direction = 1 

113 else: 

114 direction = -1 

115 

116 for i_width in range(n_width + 1): 

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

118 

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

120 # for the remaining vertical lines). 

121 if i_width < n_width: 

122 add_line( 

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

124 ) 

125 add_line( 

126 base_zig_zag + zig_zag_x - 2 * direction * zig_zag_y, 

127 base_zig_zag + nx * width, 

128 ) 

129 

130 # Check where the vertical lines start. 

131 if i_height % 2 == 0: 

132 base_vert = base_zig_zag 

133 else: 

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

135 

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

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

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

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

140 add_line(base_vert, base_vert + ny * a) 

141 

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

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

144 

145 # Add connections for the nodes with same positions. 

146 if create_couplings: 

147 mesh_honeycomb.couple_nodes(nodes=honeycomb_nodes) 

148 

149 # Get min and max nodes of the honeycomb. 

150 min_max_nodes = _get_min_max_nodes(honeycomb_nodes) 

151 

152 # Return the geometry set. 

153 return_set = _GeometryName() 

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

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

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

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

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

159 

160 mesh.add(mesh_honeycomb) 

161 if add_sets: 

162 mesh.add(return_set) 

163 return return_set 

164 

165 

166def create_beam_mesh_honeycomb( 

167 mesh, 

168 beam_class, 

169 material, 

170 diameter, 

171 n_circumference, 

172 n_axis, 

173 *, 

174 n_el=1, 

175 closed_top=True, 

176 vertical=True, 

177 add_sets=False, 

178): 

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

180 the z-axis. 

181 

182 Args 

183 ---- 

184 mesh: Mesh 

185 Mesh that the honeycomb will be added to. 

186 beam_class: Beam 

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

188 material: Material 

189 Material for the beam. 

190 diameter: float 

191 Diameter of the cylinder. 

192 n_circumference: int 

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

194 has to be an odd number. 

195 n_axis: int 

196 Number of honeycombs in axial-direction. 

197 n_el: int 

198 Number of elements per beam line. 

199 closed_top: bool 

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

201 vertical: bool 

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

203 add_sets: bool 

204 If this is true the sets are added to the mesh and then displayed 

205 in eventual VTK output, even if they are not used for a boundary 

206 condition or coupling. 

207 

208 Return 

209 ---- 

210 return_set: GeometryName 

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

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

213 contains all nodes. 

214 """ 

215 

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

217 if vertical: 

218 width = diameter * _np.pi / n_circumference 

219 closed_width = False 

220 closed_height = closed_top 

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

222 n_height = n_axis 

223 n_width = n_circumference 

224 else: 

225 if not n_circumference % 2 == 0: 

226 raise ValueError( 

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

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

229 ) 

230 H = diameter * _np.pi / n_circumference 

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

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

233 closed_width = closed_top 

234 closed_height = False 

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

236 n_height = n_circumference - 1 

237 n_width = n_axis 

238 

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

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

241 # connected. 

242 mesh_temp = _Mesh() 

243 honeycomb_sets = create_beam_mesh_honeycomb_flat( 

244 mesh_temp, 

245 beam_class, 

246 material, 

247 width, 

248 n_width, 

249 n_height, 

250 n_el=n_el, 

251 closed_width=closed_width, 

252 closed_height=closed_height, 

253 create_couplings=False, 

254 ) 

255 

256 # Move the mesh to the correct position. 

257 mesh_temp.rotate(rotation) 

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

259 mesh_temp.wrap_around_cylinder() 

260 

261 # Add connections for the nodes with same positions. 

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

263 mesh_temp.couple_nodes(nodes=honeycomb_nodes) 

264 

265 # Return the geometry set' 

266 return_set = _GeometryName() 

267 return_set["all"] = honeycomb_sets["all"] 

268 if vertical: 

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

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

271 else: 

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

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

274 if add_sets: 

275 mesh_temp.add(return_set) 

276 

277 # Add to this mesh 

278 mesh.add_mesh(mesh_temp) 

279 

280 return return_set