Coverage for src/meshpy/mesh_creation_functions/beam_helix.py: 84%
44 statements
« prev ^ index » next coverage.py v7.9.0, created at 2025-06-13 04:26 +0000
« prev ^ index » next coverage.py v7.9.0, created at 2025-06-13 04:26 +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"""Functions to create beam meshes along helical paths."""
24import warnings as _warnings
26import numpy as _np
28from meshpy.core.mesh import Mesh as _Mesh
29from meshpy.core.rotation import Rotation as _Rotation
31from .beam_line import create_beam_mesh_line as _create_beam_mesh_line
34def create_beam_mesh_helix(
35 mesh,
36 beam_class,
37 material,
38 axis_vector,
39 axis_point,
40 start_point,
41 *,
42 helix_angle=None,
43 height_helix=None,
44 turns=None,
45 warning_straight_line=True,
46 **kwargs,
47):
48 """Generate a helical segment starting at a given start point around a
49 predefined axis (defined by axis_vector and axis_point). The helical
50 segment is defined by a start_point and exactly two of the basic helical
51 quantities [helix_angle, height_helix, turns].
53 Args
54 ----
55 mesh: Mesh
56 Mesh that the helical segment will be added to.
57 beam_class: Beam
58 Class of beam that will be used for this line.
59 material: Material
60 Material for this segment.
61 axis_vector: _np.array, list
62 Vector for the orientation of the helical center axis.
63 axis_point: _np.array, list
64 Point lying on the helical center axis. Does not need to align with
65 bottom plane of helix.
66 start_point: _np.array, list
67 Start point of the helix. Defines the radius.
68 helix_angle: float
69 Angle of the helix (synonyms in literature: twist angle or pitch
70 angle).
71 height_helix: float
72 Height of helix.
73 turns: float
74 Number of turns.
75 warning_straight_line: bool
76 Warn if radius of helix is zero or helix angle is 90 degrees and
77 simple line is returned.
79 **kwargs (for all of them look into create_beam_mesh_function)
80 ----
81 n_el: int
82 Number of equally spaced beam elements along the line. Defaults to 1.
83 Mutually exclusive with l_el.
84 l_el: float
85 Desired length of beam elements. Mutually exclusive with n_el.
86 Be aware, that this length might not be achieved, if the elements are
87 warped after they are created.
89 Return
90 ----
91 return_set: GeometryName
92 Set with the 'start' and 'end' node of the line. Also a 'line' set
93 with all nodes of the line.
94 """
96 if [helix_angle, height_helix, turns].count(None) != 1:
97 raise ValueError(
98 "Exactly two arguments of [helix_angle, height_helix, turns]"
99 " must be provided!"
100 )
102 if helix_angle is not None and _np.isclose(_np.sin(helix_angle), 0.0):
103 raise ValueError(
104 "Helix angle of helix is 0 degrees! "
105 + "Change angle for feasible helix geometry!"
106 )
108 if height_helix is not None and _np.isclose(height_helix, 0.0):
109 raise ValueError(
110 "Height of helix is 0! Change height for feasible helix geometry!"
111 )
113 # determine radius of helix
114 axis_vector = _np.asarray(axis_vector)
115 axis_point = _np.asarray(axis_point)
116 start_point = _np.asarray(start_point)
118 axis_vector = axis_vector / _np.linalg.norm(axis_vector)
119 origin = axis_point + _np.dot(
120 _np.dot(start_point - axis_point, axis_vector), axis_vector
121 )
122 start_point_origin_vec = start_point - origin
123 radius = _np.linalg.norm(start_point_origin_vec)
125 # create temporary mesh to not alter original mesh
126 mesh_temp = _Mesh()
128 # return line if radius of helix is 0, helix angle is pi/2 or turns is 0
129 if (
130 _np.isclose(radius, 0)
131 or (helix_angle is not None and _np.isclose(_np.cos(helix_angle), 0.0))
132 or (turns is not None and _np.isclose(turns, 0.0))
133 ):
134 if height_helix is None:
135 raise ValueError(
136 "Radius of helix is 0, helix angle is 90 degrees or turns is 0! "
137 + "Fallback to simple line geometry but height cannot be "
138 + "determined based on helix angle and turns! Either switch one "
139 + "helix parameter to height of helix or change radius!"
140 )
142 if warning_straight_line:
143 _warnings.warn(
144 "Radius of helix is 0, helix angle is 90 degrees or turns is 0! "
145 + "Simple line geometry is returned!"
146 )
148 if helix_angle is not None and height_helix is not None:
149 end_point = start_point + height_helix * axis_vector * _np.sign(
150 _np.sin(helix_angle)
151 )
152 elif height_helix is not None and turns is not None:
153 end_point = start_point + height_helix * axis_vector
155 line_sets = _create_beam_mesh_line(
156 mesh_temp,
157 beam_class,
158 material,
159 start_point=start_point,
160 end_point=end_point,
161 **kwargs,
162 )
164 # add line to mesh
165 mesh.add_mesh(mesh_temp)
167 return line_sets
169 # generate simple helix
170 if helix_angle and height_helix:
171 end_point = _np.array(
172 [
173 radius,
174 _np.sign(_np.sin(helix_angle)) * height_helix / _np.tan(helix_angle),
175 _np.sign(_np.sin(helix_angle)) * height_helix,
176 ]
177 )
178 elif helix_angle and turns:
179 end_point = _np.array(
180 [
181 radius,
182 _np.sign(_np.cos(helix_angle)) * 2 * _np.pi * radius * turns,
183 _np.sign(_np.cos(helix_angle))
184 * 2
185 * _np.pi
186 * radius
187 * _np.abs(turns)
188 * _np.tan(helix_angle),
189 ]
190 )
191 elif height_helix and turns:
192 end_point = _np.array(
193 [
194 radius,
195 2 * _np.pi * radius * turns,
196 height_helix,
197 ]
198 )
200 helix_sets = _create_beam_mesh_line(
201 mesh_temp,
202 beam_class,
203 material,
204 start_point=[radius, 0, 0],
205 end_point=end_point,
206 **kwargs,
207 )
209 mesh_temp.wrap_around_cylinder()
211 # rotate and translate simple helix to align with necessary axis and starting point
212 mesh_temp.rotate(
213 _Rotation.from_basis(start_point_origin_vec, axis_vector)
214 * _Rotation([1, 0, 0], -_np.pi * 0.5)
215 )
216 mesh_temp.translate(-mesh_temp.nodes[0].coordinates + start_point)
218 # add helix to mesh
219 mesh.add_mesh(mesh_temp)
221 return helix_sets