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

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.""" 

23 

24import warnings as _warnings 

25 

26import numpy as _np 

27 

28from meshpy.core.mesh import Mesh as _Mesh 

29from meshpy.core.rotation import Rotation as _Rotation 

30 

31from .beam_line import create_beam_mesh_line as _create_beam_mesh_line 

32 

33 

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]. 

52 

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. 

78 

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. 

88 

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 """ 

95 

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 ) 

101 

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 ) 

107 

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 ) 

112 

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) 

117 

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) 

124 

125 # create temporary mesh to not alter original mesh 

126 mesh_temp = _Mesh() 

127 

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 ) 

141 

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 ) 

147 

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 

154 

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 ) 

163 

164 # add line to mesh 

165 mesh.add_mesh(mesh_temp) 

166 

167 return line_sets 

168 

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 ) 

199 

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 ) 

208 

209 mesh_temp.wrap_around_cylinder() 

210 

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) 

217 

218 # add helix to mesh 

219 mesh.add_mesh(mesh_temp) 

220 

221 return helix_sets