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
« 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."""
24import numpy as _np
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
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.
51 The structure will be created in the x-y plane.
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.
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 """
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 )
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
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
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
112 for i_width in range(n_width + 1):
113 base_zig_zag = base_row + direction * zig_zag_y + width * i_width * nx
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 )
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
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)
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]
141 # Add connections for the nodes with same positions.
142 if create_couplings:
143 mesh_honeycomb.couple_nodes(nodes=honeycomb_nodes)
145 # Get min and max nodes of the honeycomb.
146 min_max_nodes = _get_min_max_nodes(honeycomb_nodes)
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)
156 mesh.add(mesh_honeycomb)
158 return return_set
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.
175 The cylinder axis will be the z-axis.
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.
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
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 )
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()
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)
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"]
265 # Add to this mesh
266 mesh.add_mesh(mesh_temp)
268 return return_set