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
« 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."""
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. The structure will be created in the x-y
50 plane.
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.
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 """
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 )
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
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
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
111 for i_width in range(n_width + 1):
112 base_zig_zag = base_row + direction * zig_zag_y + width * i_width * nx
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 )
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
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)
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]
140 # Add connections for the nodes with same positions.
141 if create_couplings:
142 mesh_honeycomb.couple_nodes(nodes=honeycomb_nodes)
144 # Get min and max nodes of the honeycomb.
145 min_max_nodes = _get_min_max_nodes(honeycomb_nodes)
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)
155 mesh.add(mesh_honeycomb)
157 return return_set
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.
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.
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 """
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
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 )
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()
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)
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"]
264 # Add to this mesh
265 mesh.add_mesh(mesh_temp)
267 return return_set