pyroll-core 2.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyroll/core/__init__.py +38 -0
- pyroll/core/config.py +155 -0
- pyroll/core/disk_elements/__init__.py +2 -0
- pyroll/core/disk_elements/disk_element_unit.py +73 -0
- pyroll/core/disk_elements/hookimpls.py +26 -0
- pyroll/core/grooves/__init__.py +10 -0
- pyroll/core/grooves/base.py +59 -0
- pyroll/core/grooves/boxes/__init__.py +3 -0
- pyroll/core/grooves/boxes/box.py +57 -0
- pyroll/core/grooves/boxes/constricted_box.py +63 -0
- pyroll/core/grooves/boxes/upset_box.py +18 -0
- pyroll/core/grooves/diamonds/__init__.py +3 -0
- pyroll/core/grooves/diamonds/diamond.py +60 -0
- pyroll/core/grooves/diamonds/gothic.py +41 -0
- pyroll/core/grooves/diamonds/square.py +48 -0
- pyroll/core/grooves/flat.py +31 -0
- pyroll/core/grooves/generic_elongation.py +258 -0
- pyroll/core/grooves/generic_elongation_solvers.py +422 -0
- pyroll/core/grooves/hexagonal.py +57 -0
- pyroll/core/grooves/ovals/__init__.py +8 -0
- pyroll/core/grooves/ovals/circular_oval.py +43 -0
- pyroll/core/grooves/ovals/constricted_circular_oval.py +53 -0
- pyroll/core/grooves/ovals/constricted_swedish_oval.py +61 -0
- pyroll/core/grooves/ovals/flat_oval.py +56 -0
- pyroll/core/grooves/ovals/oval_3radii.py +47 -0
- pyroll/core/grooves/ovals/oval_3radii_flanked.py +65 -0
- pyroll/core/grooves/ovals/swedish_oval.py +57 -0
- pyroll/core/grooves/ovals/upset_oval.py +47 -0
- pyroll/core/grooves/rounds/__init__.py +2 -0
- pyroll/core/grooves/rounds/false_round.py +65 -0
- pyroll/core/grooves/rounds/round.py +44 -0
- pyroll/core/grooves/spline.py +92 -0
- pyroll/core/hooks.py +420 -0
- pyroll/core/log.py +7 -0
- pyroll/core/profile/__init__.py +3 -0
- pyroll/core/profile/hookimpls.py +58 -0
- pyroll/core/profile/profile.py +380 -0
- pyroll/core/repr.py +49 -0
- pyroll/core/roll/__init__.py +3 -0
- pyroll/core/roll/hookimpls.py +65 -0
- pyroll/core/roll/roll.py +145 -0
- pyroll/core/roll_pass/__init__.py +8 -0
- pyroll/core/roll_pass/deformation_unit.py +61 -0
- pyroll/core/roll_pass/hookimpls/__init__.py +5 -0
- pyroll/core/roll_pass/hookimpls/deformation_unit.py +78 -0
- pyroll/core/roll_pass/hookimpls/disk_element.py +16 -0
- pyroll/core/roll_pass/hookimpls/profile.py +94 -0
- pyroll/core/roll_pass/hookimpls/roll.py +43 -0
- pyroll/core/roll_pass/hookimpls/roll_pass.py +107 -0
- pyroll/core/roll_pass/roll_pass.py +168 -0
- pyroll/core/roll_pass/three_roll_pass.py +84 -0
- pyroll/core/rotator/__init__.py +3 -0
- pyroll/core/rotator/hookimpls.py +71 -0
- pyroll/core/rotator/rotator.py +65 -0
- pyroll/core/sequence/__init__.py +3 -0
- pyroll/core/sequence/hookimpls.py +18 -0
- pyroll/core/sequence/sequence.py +68 -0
- pyroll/core/shapes.py +104 -0
- pyroll/core/transport/__init__.py +3 -0
- pyroll/core/transport/hookimpls.py +17 -0
- pyroll/core/transport/transport.py +57 -0
- pyroll/core/unit/__init__.py +3 -0
- pyroll/core/unit/hookimpls.py +67 -0
- pyroll/core/unit/unit.py +271 -0
- pyroll_core-2.0.0.dist-info/METADATA +109 -0
- pyroll_core-2.0.0.dist-info/RECORD +68 -0
- pyroll_core-2.0.0.dist-info/WHEEL +4 -0
- pyroll_core-2.0.0.dist-info/licenses/LICENSE +11 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from numpy import deg2rad
|
|
3
|
+
|
|
4
|
+
from .generic_elongation import GenericElongationGroove
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class FlatGroove(GenericElongationGroove):
|
|
8
|
+
"""Represents a flat groove aka no groove."""
|
|
9
|
+
|
|
10
|
+
def __init__(
|
|
11
|
+
self,
|
|
12
|
+
usable_width: float,
|
|
13
|
+
r1: float = 0,
|
|
14
|
+
pad_angle: float = 0,
|
|
15
|
+
**kwargs
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
:param usable_width: usable width of the roll face
|
|
19
|
+
:param kwargs: more keyword arguments passed to the GenericElongationGroove constructor
|
|
20
|
+
"""
|
|
21
|
+
pad_angle = deg2rad(pad_angle)
|
|
22
|
+
|
|
23
|
+
super().__init__(
|
|
24
|
+
usable_width=usable_width, r1=r1, r2=0, depth=0, flank_angle=0,
|
|
25
|
+
pad_angle=pad_angle,
|
|
26
|
+
**kwargs
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
@property
|
|
30
|
+
def classifiers(self):
|
|
31
|
+
return {"flat",} | super().classifiers
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
from typing import Union, Tuple, Sequence, Optional
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from shapely.geometry import LineString, Polygon
|
|
5
|
+
|
|
6
|
+
from pyroll.core.grooves import GrooveBase
|
|
7
|
+
from pyroll.core.repr import ReprMixin
|
|
8
|
+
from ..config import Config
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class GenericElongationGroove(GrooveBase, ReprMixin):
|
|
12
|
+
"""Represents a groove defined by the generic elongation groove geometry."""
|
|
13
|
+
|
|
14
|
+
def __init__(
|
|
15
|
+
self,
|
|
16
|
+
|
|
17
|
+
r1: float,
|
|
18
|
+
r2: float,
|
|
19
|
+
|
|
20
|
+
flank_angle: Optional[float] = None,
|
|
21
|
+
usable_width: Optional[float] = None,
|
|
22
|
+
ground_width: Optional[float] = None,
|
|
23
|
+
depth: Optional[float] = None,
|
|
24
|
+
|
|
25
|
+
r3: float = 0,
|
|
26
|
+
alpha3: float = 0,
|
|
27
|
+
|
|
28
|
+
r4: float = 0,
|
|
29
|
+
alpha4: float = 0,
|
|
30
|
+
|
|
31
|
+
indent: float = 0,
|
|
32
|
+
even_ground_width: float = 0,
|
|
33
|
+
|
|
34
|
+
pad: float = 0,
|
|
35
|
+
rel_pad: float = Config.GROOVE_PADDING,
|
|
36
|
+
pad_angle: float = 0,
|
|
37
|
+
|
|
38
|
+
classifiers: Sequence[str] = ()
|
|
39
|
+
):
|
|
40
|
+
"""
|
|
41
|
+
Give any three of ``usable_width``, ``ground_width``, ``flank_angle`` and ``depth``.
|
|
42
|
+
All angles are measured in radians.
|
|
43
|
+
All measures must be non-negative.
|
|
44
|
+
|
|
45
|
+
:param r1: radius 1 (face/flank)
|
|
46
|
+
:param r2: radius 2 (flank/radius 3)
|
|
47
|
+
|
|
48
|
+
:param usable_width: width of flank/face intersections
|
|
49
|
+
:param ground_width: width of flank/ground-line intersections
|
|
50
|
+
:param flank_angle: angle of the flanks to the z-axis
|
|
51
|
+
:param depth: maximum depth
|
|
52
|
+
|
|
53
|
+
:param r3: radius 3 (radius 2/radius 4)
|
|
54
|
+
:param r4: radius 4 (radius 3/ground)
|
|
55
|
+
:param alpha3: angle corresponding to ``r3``
|
|
56
|
+
:param alpha4: angle corresponding to ``r4``
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
:param indent: indent of the ground
|
|
60
|
+
|
|
61
|
+
:param pad: absolute padding at roll face
|
|
62
|
+
:param rel_pad: padding at roll face relative to ``usable_width``
|
|
63
|
+
:param pad_angle: angle of the face padding from horizontal line
|
|
64
|
+
(commonly 0 for two-roll, π/6 for three-roll and π/4 for four-roll)
|
|
65
|
+
|
|
66
|
+
:param classifiers: sequence of additional type classifiers
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
try:
|
|
70
|
+
if usable_width is None:
|
|
71
|
+
usable_width = ground_width + 2 * depth / np.tan(flank_angle)
|
|
72
|
+
elif ground_width is None:
|
|
73
|
+
ground_width = usable_width - 2 * depth / np.tan(flank_angle)
|
|
74
|
+
elif flank_angle is None:
|
|
75
|
+
flank_angle = np.arctan(depth / (usable_width - ground_width) * 2)
|
|
76
|
+
elif depth is None:
|
|
77
|
+
depth = (usable_width - ground_width) / 2 * np.tan(flank_angle)
|
|
78
|
+
else:
|
|
79
|
+
raise TypeError("Too many arguments given.")
|
|
80
|
+
|
|
81
|
+
except TypeError:
|
|
82
|
+
raise TypeError("Exactly three of usable_width, ground_width, flank_angle and depth must be given.")
|
|
83
|
+
|
|
84
|
+
self.r1 = r1
|
|
85
|
+
self.r2 = r2
|
|
86
|
+
self.r3 = r3
|
|
87
|
+
self.r4 = r4
|
|
88
|
+
|
|
89
|
+
self.alpha1 = alpha1 = flank_angle + pad_angle
|
|
90
|
+
self.alpha3 = alpha3
|
|
91
|
+
self.alpha4 = alpha4
|
|
92
|
+
self.alpha2 = alpha2 = flank_angle + self.alpha4 - self.alpha3
|
|
93
|
+
|
|
94
|
+
self.indent = indent
|
|
95
|
+
self.even_ground_width = even_ground_width
|
|
96
|
+
self._usable_width = usable_width
|
|
97
|
+
self.ground_width = ground_width
|
|
98
|
+
self.flank_angle = flank_angle
|
|
99
|
+
self._depth = depth
|
|
100
|
+
self._classifiers = set(classifiers)
|
|
101
|
+
pad = pad if pad else usable_width * rel_pad
|
|
102
|
+
self.pad_angle = pad_angle
|
|
103
|
+
|
|
104
|
+
self.z2 = usable_width / 2
|
|
105
|
+
self.y2 = 0
|
|
106
|
+
|
|
107
|
+
l12 = r1 * np.tan(alpha1 / 2)
|
|
108
|
+
self.z1 = self.z2 + l12 * np.cos(pad_angle)
|
|
109
|
+
self.y1 = l12 * np.sin(pad_angle)
|
|
110
|
+
|
|
111
|
+
self.z0 = self.z1 + pad * np.cos(pad_angle)
|
|
112
|
+
self.y0 = self.y1 + pad * np.sin(pad_angle)
|
|
113
|
+
|
|
114
|
+
self.z12 = self.z1 - r1 * np.sin(pad_angle)
|
|
115
|
+
self.y12 = self.y1 + r1 * np.cos(pad_angle)
|
|
116
|
+
|
|
117
|
+
self.z3 = self.z12 - r1 * np.sin(flank_angle)
|
|
118
|
+
self.y3 = self.y12 - r1 * np.cos(flank_angle)
|
|
119
|
+
|
|
120
|
+
self.z9 = 0
|
|
121
|
+
self.y9 = depth - indent
|
|
122
|
+
|
|
123
|
+
self.z7 = self.even_ground_width / 2
|
|
124
|
+
self.y7 = self.y9
|
|
125
|
+
|
|
126
|
+
self.z8 = self.z7
|
|
127
|
+
self.y8 = self.y9 + r4
|
|
128
|
+
|
|
129
|
+
self.z6 = self.z8 + r4 * np.sin(alpha4)
|
|
130
|
+
self.y6 = self.y8 - r4 * np.cos(alpha4)
|
|
131
|
+
|
|
132
|
+
self.beta = beta = alpha4 - alpha3 / 2
|
|
133
|
+
|
|
134
|
+
self.z10 = self.z6 + r3 * np.sin(alpha3 / 2 + beta)
|
|
135
|
+
self.y10 = self.y6 - r3 * np.cos(alpha3 / 2 + beta)
|
|
136
|
+
|
|
137
|
+
self.z5 = self.z10 + r3 * np.sin(alpha3 / 2 - beta)
|
|
138
|
+
self.y5 = self.y10 + r3 * np.cos(alpha3 / 2 - beta)
|
|
139
|
+
|
|
140
|
+
self.z11 = self.z10 + (r3 - r2) * np.sin(alpha3 / 2 - beta)
|
|
141
|
+
self.y11 = self.y10 + (r3 - r2) * np.cos(alpha3 / 2 - beta)
|
|
142
|
+
|
|
143
|
+
self.gamma = gamma = np.pi / 2 - alpha2 - alpha3 + alpha4
|
|
144
|
+
|
|
145
|
+
self.z4 = self.z11 + r2 * np.cos(gamma)
|
|
146
|
+
self.y4 = self.y11 + r2 * np.sin(gamma)
|
|
147
|
+
|
|
148
|
+
right_side = np.array(list(self._enumerate_contour_points()))
|
|
149
|
+
left_side = right_side[:-1].copy()
|
|
150
|
+
left_side[:, 0] *= -1
|
|
151
|
+
|
|
152
|
+
self._contour_points = np.concatenate([left_side, right_side[::-1]])
|
|
153
|
+
self._contour_line = LineString(self._contour_points)
|
|
154
|
+
self._cross_section = Polygon(self._contour_line)
|
|
155
|
+
|
|
156
|
+
self.test_plausibility()
|
|
157
|
+
|
|
158
|
+
def _r1_contour_line(self, z):
|
|
159
|
+
return self.y12 - np.sqrt(self.r1 ** 2 - (z - self.z12) ** 2)
|
|
160
|
+
|
|
161
|
+
def _r2_contour_line(self, z):
|
|
162
|
+
return self.y11 + np.sqrt(self.r2 ** 2 - (z - self.z11) ** 2)
|
|
163
|
+
|
|
164
|
+
def _r3_contour_line(self, z):
|
|
165
|
+
return self.y10 + np.sqrt(self.r3 ** 2 - (z - self.z10) ** 2)
|
|
166
|
+
|
|
167
|
+
def _r4_contour_line(self, z):
|
|
168
|
+
return self.y8 - np.sqrt(self.r4 ** 2 - (z - self.z8) ** 2)
|
|
169
|
+
|
|
170
|
+
def _flank_contour_line(self, z):
|
|
171
|
+
return self.y3 - np.tan(self.flank_angle) * (z - self.z3)
|
|
172
|
+
|
|
173
|
+
def _ground_contour_line(self, z):
|
|
174
|
+
return np.ones_like(z) * (self.depth - self.indent)
|
|
175
|
+
|
|
176
|
+
@staticmethod
|
|
177
|
+
def _face_contour_line(z):
|
|
178
|
+
return np.zeros_like(z)
|
|
179
|
+
|
|
180
|
+
def _enumerate_contour_points(self):
|
|
181
|
+
yield self.z0, self.y0
|
|
182
|
+
|
|
183
|
+
if not np.isclose(self.z1, self.z3):
|
|
184
|
+
for z in np.linspace(self.z1, self.z3, 20, endpoint=False):
|
|
185
|
+
yield z, self._r1_contour_line(z)
|
|
186
|
+
|
|
187
|
+
if not np.isclose(self.z3, self.z4):
|
|
188
|
+
yield self.z3, self.y3
|
|
189
|
+
|
|
190
|
+
if not np.isclose(self.z4, self.z5):
|
|
191
|
+
for z in np.linspace(self.z4, self.z5, 20, endpoint=False):
|
|
192
|
+
yield z, self._r2_contour_line(z)
|
|
193
|
+
|
|
194
|
+
if not np.isclose(self.z5, self.z6):
|
|
195
|
+
for z in np.linspace(self.z5, self.z6, 20, endpoint=False):
|
|
196
|
+
yield z, self._r3_contour_line(z)
|
|
197
|
+
|
|
198
|
+
if not np.isclose(self.z6, self.z7):
|
|
199
|
+
for z in np.linspace(self.z6, self.z7, 20, endpoint=False):
|
|
200
|
+
yield z, self._r4_contour_line(z)
|
|
201
|
+
|
|
202
|
+
yield self.z9, self.y9
|
|
203
|
+
|
|
204
|
+
@property
|
|
205
|
+
def contour_points(self):
|
|
206
|
+
return self._contour_points
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def contour_line(self) -> LineString:
|
|
210
|
+
return self._contour_line
|
|
211
|
+
|
|
212
|
+
@property
|
|
213
|
+
def cross_section(self) -> Polygon:
|
|
214
|
+
return self._cross_section
|
|
215
|
+
|
|
216
|
+
def local_depth(self, z) -> Union[float, np.ndarray]:
|
|
217
|
+
z = np.abs(z)
|
|
218
|
+
|
|
219
|
+
return np.piecewise(
|
|
220
|
+
z,
|
|
221
|
+
[z < self.z7, (self.z7 <= z) & (z < self.z6), (self.z6 <= z) & (z < self.z5),
|
|
222
|
+
(self.z5 <= z) & (z < self.z4), (self.z4 <= z) & (z < self.z3), (self.z3 <= z) & (z < self.z1)],
|
|
223
|
+
[self._ground_contour_line, self._r4_contour_line, self._r3_contour_line, self._r2_contour_line,
|
|
224
|
+
self._flank_contour_line, self._r1_contour_line, self._face_contour_line]
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
@property
|
|
228
|
+
def classifiers(self):
|
|
229
|
+
return {"generic_elongation"} | self._classifiers
|
|
230
|
+
|
|
231
|
+
@property
|
|
232
|
+
def usable_width(self) -> float:
|
|
233
|
+
return self._usable_width
|
|
234
|
+
|
|
235
|
+
@property
|
|
236
|
+
def width(self) -> float:
|
|
237
|
+
return 2 * self.z1
|
|
238
|
+
|
|
239
|
+
@property
|
|
240
|
+
def depth(self) -> float:
|
|
241
|
+
return self._depth
|
|
242
|
+
|
|
243
|
+
def test_plausibility(self):
|
|
244
|
+
if (self.flank_angle + self.alpha4 - self.alpha2 - self.alpha3) > 0.01:
|
|
245
|
+
raise ValueError("given angles should fulfill α1 + α4 = α2 + α3 to be geometrically plausible")
|
|
246
|
+
|
|
247
|
+
if self.y4 - self._flank_contour_line(self.z4) > 0.001 * self.depth:
|
|
248
|
+
raise ValueError("under given conditions a step appears in z4")
|
|
249
|
+
|
|
250
|
+
@property
|
|
251
|
+
def __attrs__(self):
|
|
252
|
+
return {
|
|
253
|
+
n: v for n in ["r1", "r2", "r3", "r4", "alpha1", "alpha2", "alpha3", "alpha4", "depth", "indent",
|
|
254
|
+
"even_ground_width", "usable_width", "classifiers", "flank_angle", "pad_angle",
|
|
255
|
+
"ground_width",
|
|
256
|
+
"contour_line"]
|
|
257
|
+
if (v := getattr(self, n))
|
|
258
|
+
}
|
|
@@ -0,0 +1,422 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
from scipy.optimize import root_scalar, root, fixed_point
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def solve_r124(
|
|
8
|
+
r1: float,
|
|
9
|
+
|
|
10
|
+
r2: Optional[float],
|
|
11
|
+
depth: Optional[float],
|
|
12
|
+
width: Optional[float],
|
|
13
|
+
|
|
14
|
+
pad_angle: float,
|
|
15
|
+
|
|
16
|
+
flank_angle: Optional[float] = None,
|
|
17
|
+
flank_width: Optional[float] = None,
|
|
18
|
+
flank_height: Optional[float] = None,
|
|
19
|
+
flank_length: Optional[float] = None,
|
|
20
|
+
|
|
21
|
+
r4: float = 0,
|
|
22
|
+
indent: float = 0,
|
|
23
|
+
):
|
|
24
|
+
def l23(_alpha):
|
|
25
|
+
return r1 * np.tan((_alpha + pad_angle) / 2)
|
|
26
|
+
|
|
27
|
+
if flank_angle is None:
|
|
28
|
+
if flank_length is not None:
|
|
29
|
+
def fw(_alpha):
|
|
30
|
+
return flank_length * np.cos(_alpha)
|
|
31
|
+
|
|
32
|
+
def fh(_alpha):
|
|
33
|
+
return flank_length * np.sin(_alpha)
|
|
34
|
+
|
|
35
|
+
elif flank_width is not None:
|
|
36
|
+
def fw(_alpha):
|
|
37
|
+
return flank_width
|
|
38
|
+
|
|
39
|
+
def fh(_alpha):
|
|
40
|
+
return flank_width * np.tan(_alpha)
|
|
41
|
+
|
|
42
|
+
elif flank_height is not None:
|
|
43
|
+
def fw(_alpha):
|
|
44
|
+
return flank_height / np.tan(_alpha)
|
|
45
|
+
|
|
46
|
+
def fh(_alpha):
|
|
47
|
+
return flank_height
|
|
48
|
+
|
|
49
|
+
else:
|
|
50
|
+
def fw(_alpha):
|
|
51
|
+
return 0
|
|
52
|
+
|
|
53
|
+
def fh(_alpha):
|
|
54
|
+
return 0
|
|
55
|
+
|
|
56
|
+
if width is None:
|
|
57
|
+
def f(_alpha):
|
|
58
|
+
return depth - r2 * (1 - np.cos(_alpha)) - l23(_alpha) * np.sin(_alpha) - fh(_alpha)
|
|
59
|
+
|
|
60
|
+
flank_angle = root_scalar(f, bracket=(0, np.pi / 2)).root
|
|
61
|
+
|
|
62
|
+
elif depth is None:
|
|
63
|
+
alpha4 = np.arccos(1 - indent / (r2 + r4))
|
|
64
|
+
|
|
65
|
+
def f(_alpha):
|
|
66
|
+
return (
|
|
67
|
+
width / 2 - r2 * np.sin(_alpha) - l23(_alpha) * np.cos(_alpha)
|
|
68
|
+
- fw(_alpha) - (r2 + r4) * np.sin(alpha4)
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
raster = np.linspace(0, np.pi / 2, 100)
|
|
72
|
+
values = f(raster)
|
|
73
|
+
lower = raster[values > 0][0]
|
|
74
|
+
upper = raster[values < 0][-1]
|
|
75
|
+
flank_angle = root_scalar(f, bracket=(lower, upper)).root
|
|
76
|
+
|
|
77
|
+
elif r2 is None:
|
|
78
|
+
def f(_alpha):
|
|
79
|
+
_r2 = (depth - l23(_alpha) * np.sin(_alpha) - fh(_alpha)) / (1 - np.cos(_alpha))
|
|
80
|
+
_alpha4 = np.arccos(1 - indent / (_r2 + r4))
|
|
81
|
+
|
|
82
|
+
return (
|
|
83
|
+
width / 2 - _r2 * np.sin(_alpha) - l23(_alpha) * np.cos(_alpha)
|
|
84
|
+
- fw(_alpha) - (_r2 + r4) * np.sin(_alpha4)
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
flank_angle = root_scalar(f, bracket=(0, np.pi / 2)).root
|
|
88
|
+
|
|
89
|
+
else:
|
|
90
|
+
raise TypeError("Give either usable_width or depth.")
|
|
91
|
+
|
|
92
|
+
if r2 is not None:
|
|
93
|
+
alpha4 = np.arccos(1 - indent / (r2 + r4))
|
|
94
|
+
|
|
95
|
+
if width is None:
|
|
96
|
+
width = 2 * (
|
|
97
|
+
r2 * np.sin(flank_angle)
|
|
98
|
+
+ (depth - r2 * (1 - np.cos(flank_angle))) / np.tan(flank_angle)
|
|
99
|
+
+ (r2 + r4) * np.sin(alpha4)
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
elif depth is None:
|
|
103
|
+
depth = (
|
|
104
|
+
r2 * (1 - np.cos(flank_angle))
|
|
105
|
+
+ (width / 2 - r2 * np.sin(flank_angle) - (r2 + r4) * np.sin(alpha4)) * np.tan(flank_angle)
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
else:
|
|
109
|
+
def f(_r2):
|
|
110
|
+
_alpha4 = np.arccos(1 - indent / (_r2 + r4))
|
|
111
|
+
return (
|
|
112
|
+
(depth - width / 2 * np.tan(flank_angle) + r4 * np.sin(_alpha4))
|
|
113
|
+
/ (1 - np.cos(flank_angle) - np.sin(flank_angle) * np.tan(flank_angle) - np.sin(_alpha4))
|
|
114
|
+
)
|
|
115
|
+
r2 = fixed_point(f, width if width > depth else depth)
|
|
116
|
+
alpha4 = np.arccos(1 - indent / (r2 + r4))
|
|
117
|
+
|
|
118
|
+
return dict(
|
|
119
|
+
width=width,
|
|
120
|
+
depth=depth,
|
|
121
|
+
r2=r2,
|
|
122
|
+
alpha=flank_angle,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def solve_r123(
|
|
127
|
+
r1: float,
|
|
128
|
+
r2: float,
|
|
129
|
+
r3: float,
|
|
130
|
+
depth: float,
|
|
131
|
+
width: float,
|
|
132
|
+
|
|
133
|
+
pad_angle: float,
|
|
134
|
+
|
|
135
|
+
flank_angle: Optional[float] = None,
|
|
136
|
+
flank_width: Optional[float] = None,
|
|
137
|
+
flank_height: Optional[float] = None,
|
|
138
|
+
flank_length: Optional[float] = None,
|
|
139
|
+
):
|
|
140
|
+
def l23(_alpha):
|
|
141
|
+
return r1 * np.tan((_alpha + pad_angle) / 2)
|
|
142
|
+
|
|
143
|
+
r32 = r3 - r2
|
|
144
|
+
|
|
145
|
+
if flank_angle is None:
|
|
146
|
+
if flank_length is not None:
|
|
147
|
+
def fw(_alpha):
|
|
148
|
+
return flank_length * np.cos(_alpha)
|
|
149
|
+
|
|
150
|
+
def fh(_alpha):
|
|
151
|
+
return flank_length * np.sin(_alpha)
|
|
152
|
+
|
|
153
|
+
elif flank_width is not None:
|
|
154
|
+
def fw(_alpha):
|
|
155
|
+
return flank_width
|
|
156
|
+
|
|
157
|
+
def fh(_alpha):
|
|
158
|
+
return flank_width * np.tan(_alpha)
|
|
159
|
+
|
|
160
|
+
elif flank_height is not None:
|
|
161
|
+
def fw(_alpha):
|
|
162
|
+
return flank_height / np.tan(_alpha)
|
|
163
|
+
|
|
164
|
+
def fh(_alpha):
|
|
165
|
+
return flank_height
|
|
166
|
+
|
|
167
|
+
else:
|
|
168
|
+
def fw(_alpha):
|
|
169
|
+
return 0
|
|
170
|
+
|
|
171
|
+
def fh(_alpha):
|
|
172
|
+
return 0
|
|
173
|
+
|
|
174
|
+
def f(_alpha):
|
|
175
|
+
_alpha2 = _alpha[0]
|
|
176
|
+
_alpha3 = _alpha[1]
|
|
177
|
+
_flank_angle = _alpha2 + _alpha3
|
|
178
|
+
_gamma = np.pi / 2 - _flank_angle
|
|
179
|
+
|
|
180
|
+
return np.array(
|
|
181
|
+
[
|
|
182
|
+
(
|
|
183
|
+
depth - r3 + r32 * np.cos(_alpha3) + r2 * np.sin(_gamma)
|
|
184
|
+
- fh(_flank_angle) - l23(_flank_angle) * np.sin(_flank_angle)
|
|
185
|
+
),
|
|
186
|
+
(
|
|
187
|
+
width / 2 - r32 * np.sin(_alpha3) - r2 * np.cos(_gamma)
|
|
188
|
+
- fw(_flank_angle) - l23(_flank_angle) * np.cos(_flank_angle)
|
|
189
|
+
),
|
|
190
|
+
]
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
sol = root(
|
|
194
|
+
f, np.array([np.pi / 4, np.pi / 4]),
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
if not sol.success:
|
|
198
|
+
raise RuntimeError("Could not determine geometric values with given input.")
|
|
199
|
+
|
|
200
|
+
alpha2 = sol.x[0]
|
|
201
|
+
alpha3 = sol.x[1]
|
|
202
|
+
flank_angle = alpha2 + alpha3
|
|
203
|
+
|
|
204
|
+
else:
|
|
205
|
+
def f(_alpha):
|
|
206
|
+
_alpha2 = _alpha
|
|
207
|
+
_alpha3 = flank_angle - _alpha
|
|
208
|
+
_gamma = np.pi / 2 - flank_angle
|
|
209
|
+
|
|
210
|
+
_fw = width / 2 - r32 * np.sin(_alpha3) - r2 * np.cos(_gamma) - l23(flank_angle) * np.cos(flank_angle)
|
|
211
|
+
|
|
212
|
+
return (
|
|
213
|
+
depth - r3 + r32 * np.cos(_alpha3) + r2 * np.sin(_gamma)
|
|
214
|
+
- _fw * np.tan(flank_angle) - l23(flank_angle) * np.sin(flank_angle)
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
sol = root_scalar(f, bracket=(0, np.pi / 2)).root
|
|
218
|
+
alpha2 = sol
|
|
219
|
+
alpha3 = flank_angle - sol
|
|
220
|
+
|
|
221
|
+
return dict(
|
|
222
|
+
flank_angle=flank_angle,
|
|
223
|
+
alpha2=alpha2,
|
|
224
|
+
alpha3=alpha3,
|
|
225
|
+
)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def solve_r1234(
|
|
229
|
+
r1: float,
|
|
230
|
+
r2: float,
|
|
231
|
+
r3: float,
|
|
232
|
+
r4: float,
|
|
233
|
+
depth: float,
|
|
234
|
+
width: float,
|
|
235
|
+
indent: float,
|
|
236
|
+
|
|
237
|
+
pad_angle: float,
|
|
238
|
+
|
|
239
|
+
flank_angle: Optional[float] = None,
|
|
240
|
+
flank_width: Optional[float] = None,
|
|
241
|
+
flank_height: Optional[float] = None,
|
|
242
|
+
flank_length: Optional[float] = None,
|
|
243
|
+
):
|
|
244
|
+
def l23(_alpha):
|
|
245
|
+
return r1 * np.tan((_alpha + pad_angle) / 2)
|
|
246
|
+
|
|
247
|
+
if flank_angle is None:
|
|
248
|
+
if flank_length is not None:
|
|
249
|
+
def fw(_alpha):
|
|
250
|
+
return flank_length * np.cos(_alpha)
|
|
251
|
+
|
|
252
|
+
def fh(_alpha):
|
|
253
|
+
return flank_length * np.sin(_alpha)
|
|
254
|
+
|
|
255
|
+
elif flank_width is not None:
|
|
256
|
+
def fw(_alpha):
|
|
257
|
+
return flank_width
|
|
258
|
+
|
|
259
|
+
def fh(_alpha):
|
|
260
|
+
return flank_width * np.tan(_alpha)
|
|
261
|
+
|
|
262
|
+
elif flank_height is not None:
|
|
263
|
+
def fw(_alpha):
|
|
264
|
+
return flank_height / np.tan(_alpha)
|
|
265
|
+
|
|
266
|
+
def fh(_alpha):
|
|
267
|
+
return flank_height
|
|
268
|
+
|
|
269
|
+
else:
|
|
270
|
+
def fw(_alpha):
|
|
271
|
+
return 0
|
|
272
|
+
|
|
273
|
+
def fh(_alpha):
|
|
274
|
+
return 0
|
|
275
|
+
|
|
276
|
+
def f(_alpha):
|
|
277
|
+
_alpha2 = _alpha[0]
|
|
278
|
+
_alpha3 = _alpha[1]
|
|
279
|
+
_alpha4 = _alpha[2]
|
|
280
|
+
_flank_angle = _alpha2 + _alpha3 - _alpha4
|
|
281
|
+
|
|
282
|
+
if _alpha3 > _alpha4:
|
|
283
|
+
_gamma = np.pi / 2 - _flank_angle
|
|
284
|
+
return np.array(
|
|
285
|
+
[
|
|
286
|
+
(
|
|
287
|
+
depth - r3 + (r3 - r2) * np.cos(_alpha3 - _alpha4) + r2 * np.sin(_gamma)
|
|
288
|
+
- fh(_flank_angle) - l23(_flank_angle) * np.sin(_flank_angle)
|
|
289
|
+
),
|
|
290
|
+
(
|
|
291
|
+
width / 2 - (r3 - r2) * np.sin(_alpha3 - _alpha4) - r2 * np.cos(_gamma)
|
|
292
|
+
- fw(_flank_angle) - l23(_flank_angle) * np.cos(_flank_angle)
|
|
293
|
+
- (r3 + r4) * np.sin(_alpha4)
|
|
294
|
+
),
|
|
295
|
+
(
|
|
296
|
+
indent - (r3 + r4) * (1 - np.cos(_alpha4))
|
|
297
|
+
)
|
|
298
|
+
]
|
|
299
|
+
)
|
|
300
|
+
else:
|
|
301
|
+
_gamma = np.pi / 2 - _alpha4
|
|
302
|
+
return np.array(
|
|
303
|
+
[
|
|
304
|
+
(
|
|
305
|
+
depth - r2 * (1 - np.cos(_flank_angle))
|
|
306
|
+
- fh(_flank_angle) - l23(_flank_angle) * np.sin(_flank_angle)
|
|
307
|
+
),
|
|
308
|
+
(
|
|
309
|
+
width / 2 - r2 * np.sin(_flank_angle)
|
|
310
|
+
- fw(_flank_angle) - l23(_flank_angle) * np.cos(_flank_angle)
|
|
311
|
+
- r4 * np.sin(_alpha4) - r3 * np.cos(_gamma)
|
|
312
|
+
- (r2 - r3) * np.sin(_alpha2 - _flank_angle)
|
|
313
|
+
),
|
|
314
|
+
(
|
|
315
|
+
indent - r4 * (1 - np.cos(_alpha4)) + r3 * np.sin(_gamma)
|
|
316
|
+
+ (r2 - r3) * np.cos(_alpha4 - _alpha3) - r2
|
|
317
|
+
)
|
|
318
|
+
]
|
|
319
|
+
)
|
|
320
|
+
|
|
321
|
+
sol = root(
|
|
322
|
+
f, np.array([np.pi / 4, np.pi / 4, np.pi / 4]),
|
|
323
|
+
)
|
|
324
|
+
|
|
325
|
+
if not sol.success:
|
|
326
|
+
raise RuntimeError("Could not determine geometric values with given input.")
|
|
327
|
+
|
|
328
|
+
alpha2 = sol.x[0]
|
|
329
|
+
alpha3 = sol.x[1]
|
|
330
|
+
alpha4 = sol.x[2]
|
|
331
|
+
flank_angle = alpha2 + alpha3 - alpha4
|
|
332
|
+
|
|
333
|
+
else:
|
|
334
|
+
def f(_alpha):
|
|
335
|
+
_alpha2 = _alpha[0]
|
|
336
|
+
_alpha3 = _alpha[1]
|
|
337
|
+
_alpha4 = _alpha2 + _alpha3 - flank_angle
|
|
338
|
+
_gamma = np.pi / 2 - flank_angle
|
|
339
|
+
|
|
340
|
+
_fw = width / 2 - (r3 - r2) * np.sin(_alpha3) - r2 * np.cos(_gamma) - l23(flank_angle) * np.cos(flank_angle)
|
|
341
|
+
|
|
342
|
+
return np.array(
|
|
343
|
+
[
|
|
344
|
+
(
|
|
345
|
+
depth - r3 + (r3 - r2) * np.cos(_alpha3 - _alpha4) + r2 * np.sin(_gamma)
|
|
346
|
+
- _fw * np.tan(flank_angle) - l23(flank_angle) * np.sin(flank_angle)
|
|
347
|
+
),
|
|
348
|
+
(
|
|
349
|
+
indent - (r3 + r4) * (1 - np.cos(_alpha4))
|
|
350
|
+
)
|
|
351
|
+
]
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
sol = root(
|
|
355
|
+
f, np.array([np.pi / 4, np.pi / 4]),
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
if not sol.success:
|
|
359
|
+
raise RuntimeError("Could not determine geometric values with given input.")
|
|
360
|
+
|
|
361
|
+
alpha2 = sol.x[0]
|
|
362
|
+
alpha3 = sol.x[1]
|
|
363
|
+
alpha4 = alpha2 + alpha3 - flank_angle
|
|
364
|
+
|
|
365
|
+
return dict(
|
|
366
|
+
flank_angle=flank_angle,
|
|
367
|
+
alpha2=alpha2,
|
|
368
|
+
alpha3=alpha3,
|
|
369
|
+
alpha4=alpha4,
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def solve_box_like(
|
|
374
|
+
r2: float,
|
|
375
|
+
r4: float,
|
|
376
|
+
depth: float,
|
|
377
|
+
indent: float,
|
|
378
|
+
ground_width: Optional[float],
|
|
379
|
+
even_ground_width: Optional[float],
|
|
380
|
+
usable_width: Optional[float],
|
|
381
|
+
flank_angle: Optional[float],
|
|
382
|
+
):
|
|
383
|
+
alpha4 = np.arccos(1 - indent / (r2 + r4))
|
|
384
|
+
|
|
385
|
+
if flank_angle is None:
|
|
386
|
+
if usable_width is not None:
|
|
387
|
+
if ground_width is not None:
|
|
388
|
+
flank_angle = np.arctan(depth / (usable_width - ground_width) * 2)
|
|
389
|
+
elif even_ground_width is not None:
|
|
390
|
+
def f(_alpha):
|
|
391
|
+
_ground_width = even_ground_width + 2 * ((r4 + r2) * np.sin(alpha4) + r2 * np.tan(_alpha / 2))
|
|
392
|
+
return depth - (usable_width - _ground_width) / 2 * np.tan(_alpha)
|
|
393
|
+
|
|
394
|
+
flank_angle = root_scalar(f, bracket=(0, np.pi / 2)).root
|
|
395
|
+
ground_width = even_ground_width + 2 * ((r4 + r2) * np.sin(alpha4) + r2 * np.tan(flank_angle / 2))
|
|
396
|
+
else:
|
|
397
|
+
raise TypeError("either ground_width or even_ground_width must not be None")
|
|
398
|
+
else:
|
|
399
|
+
raise TypeError("usable_width must not be None if flank_angle is None")
|
|
400
|
+
elif ground_width is None and even_ground_width is None:
|
|
401
|
+
if usable_width is not None:
|
|
402
|
+
ground_width = usable_width - 2 * depth / np.tan(flank_angle)
|
|
403
|
+
else:
|
|
404
|
+
raise TypeError("usable_width must not be None if ground_width and even_ground_width are None")
|
|
405
|
+
elif usable_width is None:
|
|
406
|
+
if ground_width is None:
|
|
407
|
+
ground_width = even_ground_width + 2 * ((r4 + r2) * np.sin(alpha4) + r2 * np.tan(flank_angle / 2))
|
|
408
|
+
|
|
409
|
+
usable_width = ground_width + 2 * depth / np.tan(flank_angle)
|
|
410
|
+
else:
|
|
411
|
+
raise TypeError("either flank_angle, ground_width or usable_width must be None")
|
|
412
|
+
|
|
413
|
+
if even_ground_width is None:
|
|
414
|
+
even_ground_width = ground_width - 2 * ((r4 + r2) * np.sin(alpha4) + r2 * np.tan(flank_angle / 2))
|
|
415
|
+
|
|
416
|
+
return dict(
|
|
417
|
+
ground_width=ground_width,
|
|
418
|
+
usable_width=usable_width,
|
|
419
|
+
flank_angle=flank_angle,
|
|
420
|
+
even_ground_width=even_ground_width,
|
|
421
|
+
alpha4=alpha4
|
|
422
|
+
)
|