pyroll-core 2.1.3__tar.gz → 2.1.4__tar.gz
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-2.1.3 → pyroll_core-2.1.4}/PKG-INFO +8 -1
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/README.md +1 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/hatch.toml +3 -4
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyproject.toml +8 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/__init__.py +14 -1
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/config.py +22 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/base.py +36 -14
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/generic_elongation.py +4 -4
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/spline.py +4 -4
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/profile/profile.py +74 -22
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/repr.py +31 -16
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/roll_pass/deformation_unit.py +3 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/roll_pass/hookimpls/deformation_unit.py +8 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/roll_pass/hookimpls/helpers.py +3 -2
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/roll_pass/roll_pass.py +128 -54
- pyroll_core-2.1.4/pyroll/core/transport/hookimpls.py +52 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/unit/hookimpls.py +5 -5
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/unit/unit.py +14 -0
- pyroll_core-2.1.3/pyroll/core/transport/hookimpls.py +0 -26
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/.gitignore +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/LICENSE +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/disk_elements/__init__.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/disk_elements/disk_element_unit.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/disk_elements/hookimpls.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/__init__.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/boxes/__init__.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/boxes/box.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/boxes/constricted_box.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/boxes/upset_box.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/diamonds/__init__.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/diamonds/diamond.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/diamonds/gothic.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/diamonds/square.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/equivalent_ripped_groove.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/flat.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/generic_elongation_solvers.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/hexagonal.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/ovals/__init__.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/ovals/circular_oval.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/ovals/constricted_circular_oval.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/ovals/constricted_swedish_oval.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/ovals/flat_oval.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/ovals/oval_3radii.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/ovals/oval_3radii_flanked.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/ovals/swedish_oval.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/ovals/upset_oval.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/rounds/__init__.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/rounds/false_round.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/rounds/round.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/hooks.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/log.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/profile/__init__.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/profile/hookimpls.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/roll/__init__.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/roll/hookimpls.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/roll/roll.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/roll_pass/__init__.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/roll_pass/hookimpls/__init__.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/roll_pass/hookimpls/disk_element.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/roll_pass/hookimpls/profile.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/roll_pass/hookimpls/roll.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/roll_pass/hookimpls/roll_pass.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/roll_pass/three_roll_pass.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/rotator/__init__.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/rotator/hookimpls.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/rotator/rotator.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/sequence/__init__.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/sequence/hookimpls.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/sequence/sequence.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/shapes.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/transport/__init__.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/transport/cooling_pipe.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/transport/transport.py +0 -0
- {pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/unit/__init__.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: pyroll-core
|
|
3
|
-
Version: 2.1.
|
|
3
|
+
Version: 2.1.4
|
|
4
4
|
Summary: PyRoll rolling simulation framework - core library.
|
|
5
5
|
Project-URL: Homepage, https://pyroll-project.github.io
|
|
6
6
|
Project-URL: Repository, https://github.com/pyroll-project/pyroll-core
|
|
@@ -29,8 +29,14 @@ Requires-Dist: ezdxf
|
|
|
29
29
|
Requires-Dist: numpy~=1.19
|
|
30
30
|
Requires-Dist: scipy~=1.9
|
|
31
31
|
Requires-Dist: shapely~=2.0
|
|
32
|
+
Provides-Extra: matplotlib
|
|
33
|
+
Requires-Dist: matplotlib~=3.7; extra == 'matplotlib'
|
|
32
34
|
Provides-Extra: plot
|
|
33
35
|
Requires-Dist: matplotlib~=3.7; extra == 'plot'
|
|
36
|
+
Provides-Extra: plotly
|
|
37
|
+
Requires-Dist: kaleido; extra == 'plotly'
|
|
38
|
+
Requires-Dist: pandas~=2.0; extra == 'plotly'
|
|
39
|
+
Requires-Dist: plotly~=5.18; extra == 'plotly'
|
|
34
40
|
Description-Content-Type: text/markdown
|
|
35
41
|
|
|
36
42
|
# PyRolL Rolling Simulation Framework
|
|
@@ -38,6 +44,7 @@ Description-Content-Type: text/markdown
|
|
|
38
44
|
[](https://pypi.org/project/pyroll-core/)
|
|
39
45
|

|
|
40
46
|
[](LICENSE)
|
|
47
|
+
[](https://doi.org/10.21105/joss.06200)
|
|
41
48
|

|
|
42
49
|
[](https://www.bestpractices.dev/projects/7971)
|
|
43
50
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
[](https://pypi.org/project/pyroll-core/)
|
|
4
4
|

|
|
5
5
|
[](LICENSE)
|
|
6
|
+
[](https://doi.org/10.21105/joss.06200)
|
|
6
7
|

|
|
7
8
|
[](https://www.bestpractices.dev/projects/7971)
|
|
8
9
|
|
|
@@ -11,20 +11,19 @@ path = ".venv"
|
|
|
11
11
|
dependencies = [
|
|
12
12
|
"ipython ~= 8.0",
|
|
13
13
|
"pytest ~= 7.0",
|
|
14
|
-
"matplotlib ~= 3.6",
|
|
15
14
|
"pyroll-report ~= 2.0.0",
|
|
16
|
-
"jupyter"
|
|
15
|
+
"jupyter",
|
|
17
16
|
]
|
|
17
|
+
features = ["plotly", "matplotlib"]
|
|
18
18
|
|
|
19
19
|
[envs.test]
|
|
20
20
|
path = ""
|
|
21
21
|
dependencies = [
|
|
22
22
|
"pytest ~= 7.0",
|
|
23
|
-
"matplotlib ~= 3.6",
|
|
24
23
|
]
|
|
25
24
|
|
|
26
25
|
[[envs.test.matrix]]
|
|
27
|
-
|
|
26
|
+
features = ["matplotlib", "plotly"]
|
|
28
27
|
|
|
29
28
|
[envs.test.scripts]
|
|
30
29
|
all = "pytest tests"
|
|
@@ -46,6 +46,14 @@ dynamic = ["version"]
|
|
|
46
46
|
plot = [
|
|
47
47
|
"matplotlib ~= 3.7"
|
|
48
48
|
]
|
|
49
|
+
matplotlib = [
|
|
50
|
+
"matplotlib ~= 3.7"
|
|
51
|
+
]
|
|
52
|
+
plotly = [
|
|
53
|
+
"plotly ~= 5.18",
|
|
54
|
+
"pandas ~= 2.0",
|
|
55
|
+
"kaleido"
|
|
56
|
+
]
|
|
49
57
|
|
|
50
58
|
[project.urls]
|
|
51
59
|
Homepage = "https://pyroll-project.github.io"
|
|
@@ -12,7 +12,7 @@ from .hooks import Hook, HookHost, HookFunction, root_hooks
|
|
|
12
12
|
from .disk_elements import DiskElementUnit
|
|
13
13
|
from .config import Config, config
|
|
14
14
|
|
|
15
|
-
VERSION = "2.1.
|
|
15
|
+
VERSION = "2.1.4"
|
|
16
16
|
|
|
17
17
|
root_hooks.extend(
|
|
18
18
|
[
|
|
@@ -32,3 +32,16 @@ root_hooks.extend(
|
|
|
32
32
|
PassSequence.log_elongation,
|
|
33
33
|
]
|
|
34
34
|
)
|
|
35
|
+
|
|
36
|
+
# determine available plotting backend, plotly is preferred
|
|
37
|
+
try:
|
|
38
|
+
import plotly as _
|
|
39
|
+
PLOTTING_BACKEND = "plotly"
|
|
40
|
+
|
|
41
|
+
except ImportError:
|
|
42
|
+
try:
|
|
43
|
+
import matplotlib as _
|
|
44
|
+
PLOTTING_BACKEND = "matplotlib"
|
|
45
|
+
|
|
46
|
+
except ImportError:
|
|
47
|
+
PLOTTING_BACKEND = None
|
|
@@ -168,3 +168,25 @@ class Config:
|
|
|
168
168
|
|
|
169
169
|
ROLL_SURFACE_DISCRETIZATION_COUNT = 100
|
|
170
170
|
"""Count of discrete points used to describe the roll surface."""
|
|
171
|
+
|
|
172
|
+
UNIVERSAL_GAS_CONSTANT = 8.31446261815324
|
|
173
|
+
"""Universal gas constant R."""
|
|
174
|
+
|
|
175
|
+
BOLTZMANN_CONSTANT = 1.380649e-23
|
|
176
|
+
"""Boltzmann constant kB."""
|
|
177
|
+
|
|
178
|
+
STEFAN_BOLTZMANN_CONSTANT = 5.670374419e-8
|
|
179
|
+
"""Stefan-Boltzmann radiation constant σ."""
|
|
180
|
+
|
|
181
|
+
AVOGADRO_CONSTANT = 6.02214076e23
|
|
182
|
+
"""Avogadro constant NA."""
|
|
183
|
+
|
|
184
|
+
STANDARD_GRAVITY = 9.80665
|
|
185
|
+
"""Standard acceleration of gravity g0."""
|
|
186
|
+
|
|
187
|
+
PROFILE_CONTOUR_REFINEMENT = 0
|
|
188
|
+
"""Refine the line string of profile contours with more intermediate points.
|
|
189
|
+
Higher integers mean finer. Values < 1 disable this feature."""
|
|
190
|
+
|
|
191
|
+
GROOVE_RADIUS_POINT_COUNT = 20
|
|
192
|
+
"""Count of points used to represent the radii in a generic elongation groove as line string."""
|
|
@@ -65,22 +65,44 @@ class GrooveBase(ReprMixin):
|
|
|
65
65
|
}
|
|
66
66
|
|
|
67
67
|
def plot(self, **kwargs):
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
except ImportError as e:
|
|
68
|
+
from pyroll.core import PLOTTING_BACKEND
|
|
69
|
+
if PLOTTING_BACKEND is None:
|
|
71
70
|
raise RuntimeError(
|
|
72
|
-
"This method is only available if matplotlib is installed in the environment. "
|
|
73
|
-
"You may install
|
|
74
|
-
)
|
|
71
|
+
"This method is only available if matplotlib or plotly is installed in the environment. "
|
|
72
|
+
"You may install one of them using the 'plot', 'matplotlib' or 'plotly' extras of pyroll-core."
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
if PLOTTING_BACKEND == "matplotlib":
|
|
76
|
+
import matplotlib.pyplot as plt
|
|
77
|
+
|
|
78
|
+
fig: plt.Figure = plt.figure(**kwargs)
|
|
79
|
+
ax: plt.Axes = fig.subplots()
|
|
80
|
+
|
|
81
|
+
ax.set_ylabel("y")
|
|
82
|
+
ax.set_xlabel("z")
|
|
83
|
+
|
|
84
|
+
ax.set_aspect("equal", "datalim")
|
|
85
|
+
ax.grid(lw=0.5)
|
|
86
|
+
|
|
87
|
+
ax.plot(*self.contour_line.xy, color="k")
|
|
88
|
+
return fig
|
|
89
|
+
|
|
90
|
+
if PLOTTING_BACKEND == "plotly":
|
|
91
|
+
import plotly.express as px
|
|
92
|
+
|
|
93
|
+
fig = px.line(
|
|
94
|
+
x=self.contour_line.xy[0],
|
|
95
|
+
y=self.contour_line.xy[1],
|
|
96
|
+
labels={"y": "y", "x": "z"},
|
|
97
|
+
template="simple_white",
|
|
98
|
+
)
|
|
75
99
|
|
|
76
|
-
|
|
77
|
-
ax: plt.Axes = fig.subplots()
|
|
100
|
+
fig.update_traces(line_color="black")
|
|
78
101
|
|
|
79
|
-
|
|
80
|
-
|
|
102
|
+
fig.update_yaxes(
|
|
103
|
+
scaleanchor="x",
|
|
104
|
+
scaleratio=1
|
|
105
|
+
)
|
|
81
106
|
|
|
82
|
-
|
|
83
|
-
ax.grid(lw=0.5)
|
|
107
|
+
return fig
|
|
84
108
|
|
|
85
|
-
ax.plot(*self.contour_line.xy, color="k")
|
|
86
|
-
return fig
|
|
@@ -193,22 +193,22 @@ class GenericElongationGroove(GrooveBase, ReprMixin):
|
|
|
193
193
|
yield self.z0, self.y0
|
|
194
194
|
|
|
195
195
|
if not np.isclose(self.z1, self.z3):
|
|
196
|
-
for z in np.linspace(self.z1, self.z3,
|
|
196
|
+
for z in np.linspace(self.z1, self.z3, Config.GROOVE_RADIUS_POINT_COUNT, endpoint=False):
|
|
197
197
|
yield z, self._r1_contour_line(z)
|
|
198
198
|
|
|
199
199
|
if not np.isclose(self.z3, self.z4):
|
|
200
200
|
yield self.z3, self.y3
|
|
201
201
|
|
|
202
202
|
if not np.isclose(self.z4, self.z5):
|
|
203
|
-
for z in np.linspace(self.z4, self.z5,
|
|
203
|
+
for z in np.linspace(self.z4, self.z5, Config.GROOVE_RADIUS_POINT_COUNT, endpoint=False):
|
|
204
204
|
yield z, self._r2_contour_line(z)
|
|
205
205
|
|
|
206
206
|
if not np.isclose(self.z5, self.z6):
|
|
207
|
-
for z in np.linspace(self.z5, self.z6,
|
|
207
|
+
for z in np.linspace(self.z5, self.z6, Config.GROOVE_RADIUS_POINT_COUNT, endpoint=False):
|
|
208
208
|
yield z, self._r3_contour_line(z)
|
|
209
209
|
|
|
210
210
|
if not np.isclose(self.z6, self.z7):
|
|
211
|
-
for z in np.linspace(self.z6, self.z7,
|
|
211
|
+
for z in np.linspace(self.z6, self.z7, Config.GROOVE_RADIUS_POINT_COUNT, endpoint=False):
|
|
212
212
|
yield z, self._r4_contour_line(z)
|
|
213
213
|
|
|
214
214
|
yield self.z9, self.y9
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
from typing import Union, Tuple, Iterable, Optional
|
|
2
|
-
|
|
3
1
|
import numpy as np
|
|
4
2
|
import scipy.interpolate
|
|
3
|
+
from pathlib import Path
|
|
5
4
|
from ezdxf import readfile
|
|
6
|
-
from shapely.geometry import Polygon, LineString
|
|
5
|
+
from shapely.geometry import Polygon, LineString
|
|
6
|
+
from typing import Union, Tuple, Iterable, Optional
|
|
7
7
|
|
|
8
8
|
from pyroll.core.grooves import GrooveBase
|
|
9
9
|
|
|
@@ -93,7 +93,7 @@ class SplineGroove(GrooveBase):
|
|
|
93
93
|
return self._local_depth(z)
|
|
94
94
|
|
|
95
95
|
@classmethod
|
|
96
|
-
def from_dxf_drawing(cls, filepath:
|
|
96
|
+
def from_dxf_drawing(cls, filepath: Path, classifiers: Iterable[str], usable_width: Optional[float] = None,
|
|
97
97
|
arc_discretization: int = 1000) -> 'SplineGroove':
|
|
98
98
|
"""
|
|
99
99
|
Creates a spline groove with a given contour line as defined inside the .dxf drawing.
|
|
@@ -7,6 +7,7 @@ from shapely.affinity import translate, rotate
|
|
|
7
7
|
from shapely.geometry import Point, LinearRing, Polygon, LineString
|
|
8
8
|
from shapely.ops import clip_by_rect, unary_union
|
|
9
9
|
|
|
10
|
+
from ..config import Config
|
|
10
11
|
from ..grooves import GrooveBase
|
|
11
12
|
from ..hooks import HookHost, Hook
|
|
12
13
|
|
|
@@ -106,6 +107,30 @@ class Profile(HookHost):
|
|
|
106
107
|
scale_thickness = Hook[float]()
|
|
107
108
|
"""Thickness of the scale covering the profile."""
|
|
108
109
|
|
|
110
|
+
deformation_activation_energy = Hook[float]()
|
|
111
|
+
"""Activation energy of deformation especially for calculation of Zener-Holomon-Parameter."""
|
|
112
|
+
|
|
113
|
+
martensite_start_temperature = Hook[float]()
|
|
114
|
+
"""Martensite start temperature for the given chemical composition of the profile."""
|
|
115
|
+
|
|
116
|
+
martensite_finish_temperature = Hook[float]()
|
|
117
|
+
"""Martensite finish temperature for the given chemical composition of the profile."""
|
|
118
|
+
|
|
119
|
+
bainite_start_temperature = Hook[float]()
|
|
120
|
+
"""Bainite start temperature for the given chemical composition of the profile."""
|
|
121
|
+
|
|
122
|
+
bainite_finish_temperature = Hook[float]()
|
|
123
|
+
"""Bainite finish temperature for the given chemical composition of the profile."""
|
|
124
|
+
|
|
125
|
+
ae1_temperature = Hook[float]()
|
|
126
|
+
"""Temperature at which austenite starts to transform into a mixture of ferrite and cementite during cooling."""
|
|
127
|
+
|
|
128
|
+
ae3_temperature = Hook[float]()
|
|
129
|
+
"""Temperature above which the transformation of austenite into a mixture of ferrite and cementite is complete during cooling."""
|
|
130
|
+
|
|
131
|
+
vickers_hardness = Hook[float]()
|
|
132
|
+
"""Vickers hardness of the cold profile material."""
|
|
133
|
+
|
|
109
134
|
def __init__(self, **kwargs):
|
|
110
135
|
"""Using the ``__init__`` is not recommended, use one of the factory class methods instead."""
|
|
111
136
|
self.t = 0
|
|
@@ -190,7 +215,7 @@ class Profile(HookHost):
|
|
|
190
215
|
polygon = clip_by_rect(poly, -width / 2, -math.inf, width / 2, math.inf)
|
|
191
216
|
|
|
192
217
|
return cls(
|
|
193
|
-
cross_section=polygon,
|
|
218
|
+
cross_section=refine_cross_section(polygon),
|
|
194
219
|
classifiers=set(groove.classifiers),
|
|
195
220
|
**kwargs
|
|
196
221
|
)
|
|
@@ -340,7 +365,6 @@ class Profile(HookHost):
|
|
|
340
365
|
:raises ValueError: if arguments are out of range
|
|
341
366
|
"""
|
|
342
367
|
|
|
343
|
-
|
|
344
368
|
return HexagonProfile(side, height, diagonal, corner_radius, **kwargs)
|
|
345
369
|
|
|
346
370
|
def local_height(self, z: float) -> float:
|
|
@@ -390,26 +414,47 @@ class Profile(HookHost):
|
|
|
390
414
|
raise ValueError("Value of self.material is neither a string or a collection of strings.")
|
|
391
415
|
|
|
392
416
|
def plot(self, **kwargs):
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
except ImportError as e:
|
|
417
|
+
from pyroll.core import PLOTTING_BACKEND
|
|
418
|
+
if PLOTTING_BACKEND is None:
|
|
396
419
|
raise RuntimeError(
|
|
397
|
-
"This method is only available if matplotlib is installed in the environment. "
|
|
398
|
-
"You may install
|
|
399
|
-
)
|
|
420
|
+
"This method is only available if matplotlib or plotly is installed in the environment. "
|
|
421
|
+
"You may install one of them using the 'plot', 'matplotlib' or 'plotly' extras of pyroll-core."
|
|
422
|
+
)
|
|
400
423
|
|
|
401
|
-
|
|
402
|
-
|
|
424
|
+
if PLOTTING_BACKEND == "matplotlib":
|
|
425
|
+
import matplotlib.pyplot as plt
|
|
426
|
+
|
|
427
|
+
fig: plt.Figure = plt.figure(**kwargs)
|
|
428
|
+
ax: plt.Axes = fig.subplots()
|
|
429
|
+
|
|
430
|
+
ax.set_ylabel("y")
|
|
431
|
+
ax.set_xlabel("z")
|
|
432
|
+
|
|
433
|
+
ax.set_aspect("equal", "datalim")
|
|
434
|
+
ax.grid(lw=0.5)
|
|
435
|
+
|
|
436
|
+
ax.plot(*self.cross_section.boundary.xy, color="k")
|
|
437
|
+
ax.fill(*self.cross_section.boundary.xy, color="k", alpha=0.5)
|
|
438
|
+
return fig
|
|
439
|
+
|
|
440
|
+
if PLOTTING_BACKEND == "plotly":
|
|
441
|
+
import plotly.express as px
|
|
403
442
|
|
|
404
|
-
|
|
405
|
-
|
|
443
|
+
fig = px.line(
|
|
444
|
+
x=self.cross_section.boundary.xy[0],
|
|
445
|
+
y=self.cross_section.boundary.xy[1],
|
|
446
|
+
labels={"y": "y", "x": "z"},
|
|
447
|
+
template="simple_white",
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
fig.data[0].fill = "toself"
|
|
406
451
|
|
|
407
|
-
|
|
408
|
-
|
|
452
|
+
fig.update_yaxes(
|
|
453
|
+
scaleanchor="x",
|
|
454
|
+
scaleratio=1
|
|
455
|
+
)
|
|
409
456
|
|
|
410
|
-
|
|
411
|
-
ax.fill(*self.cross_section.boundary.xy, color="k", alpha=0.5)
|
|
412
|
-
return fig
|
|
457
|
+
return fig
|
|
413
458
|
|
|
414
459
|
|
|
415
460
|
class RoundProfile(Profile):
|
|
@@ -447,7 +492,7 @@ class RoundProfile(Profile):
|
|
|
447
492
|
circle = center.buffer(radius)
|
|
448
493
|
|
|
449
494
|
super().__init__(
|
|
450
|
-
cross_section=circle,
|
|
495
|
+
cross_section=refine_cross_section(circle),
|
|
451
496
|
classifiers={"round"},
|
|
452
497
|
**kwargs
|
|
453
498
|
)
|
|
@@ -506,7 +551,7 @@ class BoxProfile(Profile):
|
|
|
506
551
|
polygon = polygon.buffer(corner_radius)
|
|
507
552
|
|
|
508
553
|
super().__init__(
|
|
509
|
-
cross_section=polygon,
|
|
554
|
+
cross_section=refine_cross_section(polygon),
|
|
510
555
|
classifiers={"box"},
|
|
511
556
|
**kwargs
|
|
512
557
|
)
|
|
@@ -560,7 +605,7 @@ class DiamondProfile(Profile):
|
|
|
560
605
|
polygon = polygon.buffer(corner_radius)
|
|
561
606
|
|
|
562
607
|
super().__init__(
|
|
563
|
-
cross_section=polygon,
|
|
608
|
+
cross_section=refine_cross_section(polygon),
|
|
564
609
|
classifiers={"diamond"},
|
|
565
610
|
**kwargs
|
|
566
611
|
)
|
|
@@ -621,7 +666,7 @@ class SquareProfile(Profile):
|
|
|
621
666
|
polygon = polygon.buffer(corner_radius)
|
|
622
667
|
|
|
623
668
|
super().__init__(
|
|
624
|
-
cross_section=polygon,
|
|
669
|
+
cross_section=refine_cross_section(polygon),
|
|
625
670
|
classifiers={"square", "diamond"},
|
|
626
671
|
**kwargs
|
|
627
672
|
)
|
|
@@ -713,7 +758,7 @@ class HexagonProfile(Profile):
|
|
|
713
758
|
polygon = polygon.buffer(corner_radius)
|
|
714
759
|
|
|
715
760
|
super().__init__(
|
|
716
|
-
cross_section=polygon,
|
|
761
|
+
cross_section=refine_cross_section(polygon),
|
|
717
762
|
classifiers={"hexagon"},
|
|
718
763
|
**kwargs
|
|
719
764
|
)
|
|
@@ -739,3 +784,10 @@ class HexagonProfile(Profile):
|
|
|
739
784
|
side=self._side,
|
|
740
785
|
diagonal=self._diagonal,
|
|
741
786
|
corner_radius=self._corner_radius)
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
def refine_cross_section(cross_section: Polygon):
|
|
790
|
+
if Config.PROFILE_CONTOUR_REFINEMENT < 1:
|
|
791
|
+
return cross_section
|
|
792
|
+
|
|
793
|
+
return cross_section.segmentize(cross_section.boundary.length / Config.PROFILE_CONTOUR_REFINEMENT)
|
|
@@ -48,38 +48,53 @@ class ReprMixin(ABC):
|
|
|
48
48
|
def _repr_html_(self):
|
|
49
49
|
"""HTML repr for IPython."""
|
|
50
50
|
|
|
51
|
-
buf = [f"<
|
|
51
|
+
buf = [f"<details><summary style='font-weight:bold'>{html.escape(str(self), True)}</summary><table>"]
|
|
52
52
|
|
|
53
53
|
for name, value in sorted(self.__attrs__.items()):
|
|
54
54
|
if hasattr(value, "_repr_html_"):
|
|
55
55
|
r = value._repr_html_()
|
|
56
|
+
elif isinstance(value, list) or isinstance(value, set):
|
|
57
|
+
item_reprs = (
|
|
58
|
+
item._repr_html_() if hasattr(item, "_repr_html_") else html.escape(repr(item), True)
|
|
59
|
+
for item in value
|
|
60
|
+
)
|
|
61
|
+
r = f"<table style='margin:0'>{''.join(f'<tr><td>{item}</td></tr>' for item in item_reprs)}</table>"
|
|
56
62
|
else:
|
|
57
63
|
r = html.escape(repr(value), True)
|
|
58
64
|
buf.append(f"<tr><td style='text-align:left'>{html.escape(name, True)}</td><td>{r}</td></tr>")
|
|
59
65
|
|
|
60
|
-
buf.append("</table>")
|
|
66
|
+
buf.append("</table></details>")
|
|
61
67
|
|
|
62
68
|
table = ''.join(buf)
|
|
63
69
|
|
|
64
70
|
try:
|
|
65
71
|
plot = self.plot()
|
|
66
72
|
|
|
67
|
-
|
|
68
|
-
import re
|
|
73
|
+
from pyroll.core import PLOTTING_BACKEND
|
|
69
74
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
75
|
+
if PLOTTING_BACKEND == "matplotlib":
|
|
76
|
+
import matplotlib.pyplot as plt
|
|
77
|
+
with StringIO() as sio:
|
|
78
|
+
plot.savefig(sio, format="svg")
|
|
73
79
|
svg = sio.getvalue()
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
plt.close(plot)
|
|
81
|
+
if PLOTTING_BACKEND == "plotly":
|
|
82
|
+
from plotly.io import to_image
|
|
83
|
+
svg = to_image(
|
|
84
|
+
plot,
|
|
85
|
+
format="svg",
|
|
86
|
+
width=600,
|
|
87
|
+
height=400
|
|
88
|
+
).decode("utf-8")
|
|
89
|
+
|
|
90
|
+
return (
|
|
91
|
+
"<table>"
|
|
92
|
+
+ "<tr><td style='text-align: center'>" + svg + "</td></tr>"
|
|
93
|
+
+ "<tr><td style='text-align: left'>" + table + "</td></tr>"
|
|
94
|
+
+ "</table>"
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
except (NotImplementedError, ImportError, AttributeError, TypeError) as e:
|
|
83
98
|
return table
|
|
84
99
|
|
|
85
100
|
def __rich_repr__(self):
|
|
@@ -54,6 +54,9 @@ class DeformationUnit(Unit):
|
|
|
54
54
|
deformation_resistance = Hook[float]()
|
|
55
55
|
"""Equivalent deformation resistance (mean flow stress increased by deformation efficiency)."""
|
|
56
56
|
|
|
57
|
+
zener_holomon_parameter = Hook[float]()
|
|
58
|
+
"""Temperature corrected strain rate acc. to Zener and Holomon."""
|
|
59
|
+
|
|
57
60
|
class Profile(Unit.Profile):
|
|
58
61
|
"""Represents a profile in context of a deformation unit."""
|
|
59
62
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import numpy as np
|
|
2
2
|
|
|
3
3
|
from ..roll_pass import DeformationUnit
|
|
4
|
+
from ...config import Config
|
|
4
5
|
|
|
5
6
|
|
|
6
7
|
@DeformationUnit.draught
|
|
@@ -76,3 +77,10 @@ def strain_rate(self: DeformationUnit):
|
|
|
76
77
|
@DeformationUnit.free_surface_area
|
|
77
78
|
def free_surface_area(self: DeformationUnit):
|
|
78
79
|
return self.surface_area - self.contact_area
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@DeformationUnit.zener_holomon_parameter
|
|
83
|
+
def zener_holomon_parameter(self: DeformationUnit):
|
|
84
|
+
return self.strain_rate * np.exp(self.in_profile.deformation_activation_energy / (
|
|
85
|
+
Config.UNIVERSAL_GAS_CONSTANT * self.in_profile.temperature
|
|
86
|
+
))
|
|
@@ -6,11 +6,12 @@ from shapely.affinity import rotate
|
|
|
6
6
|
|
|
7
7
|
from ..roll_pass import RollPass
|
|
8
8
|
from ..three_roll_pass import ThreeRollPass
|
|
9
|
+
from ...profile.profile import refine_cross_section
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def out_cross_section(rp: RollPass, width: float) -> Polygon:
|
|
12
13
|
poly = Polygon(np.concatenate([cl.coords for cl in rp.contour_lines]))
|
|
13
|
-
return clip_by_rect(poly, -width / 2, -math.inf, width / 2, math.inf)
|
|
14
|
+
return refine_cross_section(clip_by_rect(poly, -width / 2, -math.inf, width / 2, math.inf))
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
def out_cross_section3(rp: ThreeRollPass, width: float) -> Polygon:
|
|
@@ -20,4 +21,4 @@ def out_cross_section3(rp: ThreeRollPass, width: float) -> Polygon:
|
|
|
20
21
|
poly = clip_by_rect(poly, -math.inf, -width / 2, math.inf, math.inf)
|
|
21
22
|
poly = rotate(poly, angle=120, origin=(0, 0))
|
|
22
23
|
|
|
23
|
-
return poly
|
|
24
|
+
return refine_cross_section(poly)
|
|
@@ -96,6 +96,9 @@ class RollPass(DiskElementUnit, DeformationUnit):
|
|
|
96
96
|
forward_slip_ratio = Hook[float]()
|
|
97
97
|
"""Ratio of forward slip of the roll gap."""
|
|
98
98
|
|
|
99
|
+
location = Hook[float]()
|
|
100
|
+
"""Coordinate of the passes high point in rolling direction."""
|
|
101
|
+
|
|
99
102
|
def __init__(
|
|
100
103
|
self,
|
|
101
104
|
roll: BaseRoll,
|
|
@@ -234,27 +237,12 @@ class RollPass(DiskElementUnit, DeformationUnit):
|
|
|
234
237
|
"""Represents an outgoing profile of a disk element unit."""
|
|
235
238
|
|
|
236
239
|
def plot(self, **kwargs):
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
except ImportError as e:
|
|
240
|
+
from pyroll.core import PLOTTING_BACKEND
|
|
241
|
+
if PLOTTING_BACKEND is None:
|
|
240
242
|
raise RuntimeError(
|
|
241
|
-
"This method is only available if matplotlib is installed in the environment. "
|
|
242
|
-
"You may install
|
|
243
|
-
)
|
|
244
|
-
|
|
245
|
-
fig: plt.Figure = plt.figure(**kwargs)
|
|
246
|
-
ax: plt.Axes
|
|
247
|
-
axl: plt.Axes
|
|
248
|
-
ax = fig.subplots()
|
|
249
|
-
|
|
250
|
-
if self.label:
|
|
251
|
-
ax.set_title(f"Roll Pass '{self.label}'")
|
|
252
|
-
|
|
253
|
-
ax.set_ylabel("y")
|
|
254
|
-
ax.set_xlabel("z")
|
|
255
|
-
|
|
256
|
-
ax.set_aspect("equal", "datalim")
|
|
257
|
-
ax.grid(lw=0.5)
|
|
243
|
+
"This method is only available if matplotlib or plotly is installed in the environment. "
|
|
244
|
+
"You may install one of them using the 'plot', 'matplotlib' or 'plotly' extras of pyroll-core."
|
|
245
|
+
)
|
|
258
246
|
|
|
259
247
|
def oriented(geom):
|
|
260
248
|
orientation = self.orientation
|
|
@@ -271,38 +259,124 @@ class RollPass(DiskElementUnit, DeformationUnit):
|
|
|
271
259
|
return rotate(geom, orientation, (0, 0))
|
|
272
260
|
return geom
|
|
273
261
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if self.in_profile:
|
|
277
|
-
artists += ax.fill(
|
|
278
|
-
*oriented(self.in_profile.cross_section.boundary).xy,
|
|
279
|
-
alpha=0.5, color="red", label="in profile"
|
|
280
|
-
)
|
|
281
|
-
artists += ax.fill(
|
|
282
|
-
*oriented(self.in_profile.equivalent_rectangle.boundary).xy,
|
|
283
|
-
fill=False, color="red", ls="--", label="in eq. rectangle"
|
|
284
|
-
)
|
|
285
|
-
|
|
286
|
-
if self.out_profile:
|
|
287
|
-
artists += ax.fill(
|
|
288
|
-
*oriented(self.out_profile.cross_section.boundary).xy,
|
|
289
|
-
alpha=0.5, color="blue", label="out profile"
|
|
290
|
-
)
|
|
291
|
-
artists += ax.fill(
|
|
292
|
-
*oriented(self.out_profile.equivalent_rectangle.boundary).xy,
|
|
293
|
-
fill=False, color="blue", ls="--", label="out eq. rectangle"
|
|
294
|
-
)
|
|
295
|
-
|
|
296
|
-
c = None
|
|
297
|
-
for cl in self.contour_lines:
|
|
298
|
-
c = ax.plot(*oriented(cl).xy, color="k", label="roll surface")
|
|
299
|
-
|
|
300
|
-
if c is not None:
|
|
301
|
-
artists += c
|
|
302
|
-
|
|
303
|
-
ncols = len(artists) // 2 + 1
|
|
304
|
-
|
|
305
|
-
ax.legend(handles=artists, ncols=ncols, loc='lower center')
|
|
306
|
-
fig.set_layout_engine('constrained')
|
|
262
|
+
if PLOTTING_BACKEND == "matplotlib":
|
|
263
|
+
import matplotlib.pyplot as plt
|
|
307
264
|
|
|
308
|
-
|
|
265
|
+
fig: plt.Figure = plt.figure(**kwargs)
|
|
266
|
+
ax: plt.Axes
|
|
267
|
+
axl: plt.Axes
|
|
268
|
+
ax = fig.subplots()
|
|
269
|
+
|
|
270
|
+
if self.label:
|
|
271
|
+
ax.set_title(f"Roll Pass '{self.label}'")
|
|
272
|
+
|
|
273
|
+
ax.set_ylabel("y")
|
|
274
|
+
ax.set_xlabel("z")
|
|
275
|
+
|
|
276
|
+
ax.set_aspect("equal", "datalim")
|
|
277
|
+
ax.grid(lw=0.5)
|
|
278
|
+
|
|
279
|
+
artists = []
|
|
280
|
+
|
|
281
|
+
if self.in_profile:
|
|
282
|
+
artists += ax.fill(
|
|
283
|
+
*oriented(self.in_profile.cross_section.boundary).xy,
|
|
284
|
+
alpha=0.5, color="red", label="in profile"
|
|
285
|
+
)
|
|
286
|
+
artists += ax.fill(
|
|
287
|
+
*oriented(self.in_profile.equivalent_rectangle.boundary).xy,
|
|
288
|
+
fill=False, color="red", ls="--", label="in eq. rectangle"
|
|
289
|
+
)
|
|
290
|
+
|
|
291
|
+
if self.out_profile:
|
|
292
|
+
artists += ax.fill(
|
|
293
|
+
*oriented(self.out_profile.cross_section.boundary).xy,
|
|
294
|
+
alpha=0.5, color="blue", label="out profile"
|
|
295
|
+
)
|
|
296
|
+
artists += ax.fill(
|
|
297
|
+
*oriented(self.out_profile.equivalent_rectangle.boundary).xy,
|
|
298
|
+
fill=False, color="blue", ls="--", label="out eq. rectangle"
|
|
299
|
+
)
|
|
300
|
+
|
|
301
|
+
c = None
|
|
302
|
+
for cl in self.contour_lines:
|
|
303
|
+
c = ax.plot(*oriented(cl).xy, color="k", label="roll surface")
|
|
304
|
+
|
|
305
|
+
if c is not None:
|
|
306
|
+
artists += c
|
|
307
|
+
|
|
308
|
+
ncols = len(artists) // 2 + 1
|
|
309
|
+
|
|
310
|
+
ax.legend(handles=artists, ncols=ncols, loc='lower center')
|
|
311
|
+
fig.set_layout_engine('constrained')
|
|
312
|
+
|
|
313
|
+
return fig
|
|
314
|
+
|
|
315
|
+
if PLOTTING_BACKEND == "plotly":
|
|
316
|
+
import plotly.graph_objects as go
|
|
317
|
+
|
|
318
|
+
fig = go.Figure(layout=go.Layout(
|
|
319
|
+
xaxis=dict(
|
|
320
|
+
title="z",
|
|
321
|
+
),
|
|
322
|
+
yaxis=dict(
|
|
323
|
+
title="y",
|
|
324
|
+
scaleanchor="x",
|
|
325
|
+
scaleratio=1
|
|
326
|
+
),
|
|
327
|
+
title=f"Roll Pass '{self.label}'" if self.label else None,
|
|
328
|
+
template="simple_white"
|
|
329
|
+
))
|
|
330
|
+
|
|
331
|
+
if self.in_profile:
|
|
332
|
+
coords = oriented(self.in_profile.cross_section.boundary).xy
|
|
333
|
+
fig.add_trace(go.Scatter(
|
|
334
|
+
x=np.array(coords[0]),
|
|
335
|
+
y=np.array(coords[1]),
|
|
336
|
+
mode="lines",
|
|
337
|
+
fill="toself",
|
|
338
|
+
line=dict(color="red"),
|
|
339
|
+
name="in profile"
|
|
340
|
+
))
|
|
341
|
+
coords = oriented(self.in_profile.equivalent_rectangle.boundary).xy
|
|
342
|
+
fig.add_trace(go.Scatter(
|
|
343
|
+
x=np.array(coords[0]),
|
|
344
|
+
y=np.array(coords[1]),
|
|
345
|
+
mode="lines",
|
|
346
|
+
line=dict(color="red", dash="dash"),
|
|
347
|
+
name="in eq. rectangle"
|
|
348
|
+
))
|
|
349
|
+
|
|
350
|
+
if self.out_profile:
|
|
351
|
+
coords = oriented(self.out_profile.cross_section.boundary).xy
|
|
352
|
+
fig.add_trace(go.Scatter(
|
|
353
|
+
x=np.array(coords[0]),
|
|
354
|
+
y=np.array(coords[1]),
|
|
355
|
+
mode="lines",
|
|
356
|
+
fill="toself",
|
|
357
|
+
line=dict(color="blue"),
|
|
358
|
+
name="out profile"
|
|
359
|
+
))
|
|
360
|
+
coords = oriented(self.out_profile.equivalent_rectangle.boundary).xy
|
|
361
|
+
fig.add_trace(go.Scatter(
|
|
362
|
+
x=np.array(coords[0]),
|
|
363
|
+
y=np.array(coords[1]),
|
|
364
|
+
mode="lines",
|
|
365
|
+
line=dict(color="blue", dash="dash"),
|
|
366
|
+
name="out eq. rectangle"
|
|
367
|
+
))
|
|
368
|
+
|
|
369
|
+
show_in_legend = True
|
|
370
|
+
for cl in self.contour_lines:
|
|
371
|
+
coords = oriented(cl).xy
|
|
372
|
+
fig.add_trace(go.Scatter(
|
|
373
|
+
x=np.array(coords[0]),
|
|
374
|
+
y=np.array(coords[1]),
|
|
375
|
+
mode="lines",
|
|
376
|
+
line=dict(color="black"),
|
|
377
|
+
name="roll surface",
|
|
378
|
+
showlegend=show_in_legend
|
|
379
|
+
))
|
|
380
|
+
show_in_legend = False
|
|
381
|
+
|
|
382
|
+
return fig
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from .transport import Transport
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@Transport.OutProfile.strain
|
|
5
|
+
def out_strain(self: Transport.OutProfile):
|
|
6
|
+
"""Assume total recrystallization during transport."""
|
|
7
|
+
return 0
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@Transport.duration
|
|
11
|
+
def duration(self: Transport):
|
|
12
|
+
if self.has_set_or_cached("length"):
|
|
13
|
+
return self.length / self.velocity
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@Transport.velocity(
|
|
17
|
+
trylast=True # do not override getting from in_profile
|
|
18
|
+
)
|
|
19
|
+
def conti_velocity(self: Transport):
|
|
20
|
+
if self.has_set_or_cached("length"): # probably indicates conti process
|
|
21
|
+
return self.prev.velocity
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
@Transport.environment_temperature
|
|
25
|
+
def environment_temperature(self):
|
|
26
|
+
return 293
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@Transport.length
|
|
30
|
+
def length_from_roll_pass_positions(self: Transport, cycle):
|
|
31
|
+
if cycle:
|
|
32
|
+
return None
|
|
33
|
+
|
|
34
|
+
from ..roll_pass import RollPass
|
|
35
|
+
next_pass = self.next_of(RollPass)
|
|
36
|
+
prev_pass = self.prev_of(RollPass)
|
|
37
|
+
|
|
38
|
+
if next_pass.has_value("location") and prev_pass.has_value("location"):
|
|
39
|
+
entry = next_pass.entry_point if next_pass.has_value("entry_point") else 0
|
|
40
|
+
length = 0
|
|
41
|
+
|
|
42
|
+
current = self
|
|
43
|
+
while not (current.next is next_pass):
|
|
44
|
+
length += current.next.length
|
|
45
|
+
current = current.next
|
|
46
|
+
|
|
47
|
+
current = self
|
|
48
|
+
while not (current.prev is prev_pass):
|
|
49
|
+
length += current.prev.length
|
|
50
|
+
current = current.prev
|
|
51
|
+
|
|
52
|
+
return next_pass.location - prev_pass.location + entry - length
|
|
@@ -23,20 +23,20 @@ def surface_area(self: Unit):
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
@Unit.length
|
|
26
|
-
def length(self: Unit):
|
|
27
|
-
if self.
|
|
26
|
+
def length(self: Unit, cycle):
|
|
27
|
+
if not cycle and self.has_value("duration"):
|
|
28
28
|
return self.velocity * self.duration
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
@Unit.duration
|
|
32
|
-
def duration(self: Unit):
|
|
33
|
-
if self.
|
|
32
|
+
def duration(self: Unit, cycle):
|
|
33
|
+
if not cycle and self.has_value("length"):
|
|
34
34
|
return self.length / self.velocity
|
|
35
35
|
|
|
36
36
|
|
|
37
37
|
@Unit.velocity
|
|
38
38
|
def velocity(self: Unit):
|
|
39
|
-
if self.in_profile.
|
|
39
|
+
if self.in_profile.has_value("velocity"):
|
|
40
40
|
return self.in_profile.velocity
|
|
41
41
|
|
|
42
42
|
|
|
@@ -218,6 +218,20 @@ class Unit(HookHost):
|
|
|
218
218
|
"""List of the subunits."""
|
|
219
219
|
return self._subunits
|
|
220
220
|
|
|
221
|
+
@property
|
|
222
|
+
def profiles(self) -> List[BaseProfile]:
|
|
223
|
+
"""Returns a list of all profiles appearing in this unit.
|
|
224
|
+
The list contains in and out profiles of all subunits and this unit itself in the order of rolling direction.
|
|
225
|
+
This property recurses into all subunits."""
|
|
226
|
+
|
|
227
|
+
def _yield_profiles():
|
|
228
|
+
yield self.in_profile
|
|
229
|
+
for u in self._subunits:
|
|
230
|
+
yield from u.profiles
|
|
231
|
+
yield self.out_profile
|
|
232
|
+
|
|
233
|
+
return list(_yield_profiles())
|
|
234
|
+
|
|
221
235
|
class Profile(BaseProfile):
|
|
222
236
|
"""Represents a profile in context of a unit."""
|
|
223
237
|
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
from .transport import Transport
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
@Transport.OutProfile.strain
|
|
5
|
-
def out_strain(self: Transport.OutProfile):
|
|
6
|
-
"""Assume total recrystallization during transport."""
|
|
7
|
-
return 0
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
@Transport.duration
|
|
11
|
-
def duration(self: Transport):
|
|
12
|
-
if self.has_set_or_cached("length"):
|
|
13
|
-
return self.length / self.velocity
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
@Transport.velocity(
|
|
17
|
-
trylast=True # do not override getting from in_profile
|
|
18
|
-
)
|
|
19
|
-
def conti_velocity(self: Transport):
|
|
20
|
-
if self.has_set_or_cached("length"): # probably indicates conti process
|
|
21
|
-
return self.prev.velocity
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
@Transport.environment_temperature
|
|
25
|
-
def environment_temperature(self):
|
|
26
|
-
return 293
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/ovals/constricted_circular_oval.py
RENAMED
|
File without changes
|
{pyroll_core-2.1.3 → pyroll_core-2.1.4}/pyroll/core/grooves/ovals/constricted_swedish_oval.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|