Skip to content

Commit 769b6c6

Browse files
committed
add common symmetric roll passes base class
1 parent 381282d commit 769b6c6

16 files changed

Lines changed: 542 additions & 437 deletions

pyroll/core/roll/hookimpls.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ def contour_points(self: Roll):
5151

5252
@Roll.surface_x
5353
def surface_x(self: Roll):
54-
padded_contact_angle = np.arcsin(1.1 * self.contact_length / self.min_radius)
54+
padded_contact_angle = np.arcsin(1.1 * self.contact_length / self.min_radius) if self.has_set_or_cached("contact_length") else np.pi / 4
5555
points = np.concatenate([
5656
np.linspace(0, padded_contact_angle, Config.ROLL_SURFACE_DISCRETIZATION_COUNT, endpoint=False),
5757
np.linspace(padded_contact_angle, np.pi / 2, Config.ROLL_SURFACE_DISCRETIZATION_COUNT),

pyroll/core/roll_pass/__init__.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from .base import BaseRollPass
2-
from .roll_pass import RollPass
2+
from .two_roll_pass import TwoRollPass
33
from .three_roll_pass import ThreeRollPass
44
from .deformation_unit import DeformationUnit
55

66
from . import hookimpls
77

8+
RollPass = TwoRollPass
9+
810

911

pyroll/core/roll_pass/base.py

Lines changed: 12 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import weakref
2+
from abc import abstractmethod, ABC
23
from typing import List, Union, cast
34

45
import numpy as np
@@ -13,7 +14,7 @@
1314
from .deformation_unit import DeformationUnit
1415

1516

16-
class BaseRollPass(DiskElementUnit, DeformationUnit):
17+
class BaseRollPass(DiskElementUnit, DeformationUnit, ABC):
1718
"""Represents a roll pass with two symmetric working rolls."""
1819

1920
rotation = Hook[Union[bool, float]]()
@@ -63,15 +64,9 @@ class BaseRollPass(DiskElementUnit, DeformationUnit):
6364
entry_point = Hook[float]()
6465
"""Point where the material enters the roll gap."""
6566

66-
entry_angle = Hook[float]()
67-
"""Angle at which the material enters the roll gap."""
68-
6967
exit_point = Hook[float]()
7068
"""Point where the material exits the roll gap."""
7169

72-
exit_angle = Hook[float]()
73-
"""Angle at which the material exits the roll gap."""
74-
7570
front_tension = Hook[float]()
7671
"""Front tension acting on the current roll pass."""
7772

@@ -104,7 +99,6 @@ class BaseRollPass(DiskElementUnit, DeformationUnit):
10499

105100
def __init__(
106101
self,
107-
roll: BaseRoll,
108102
label: str = "",
109103
**kwargs
110104
):
@@ -116,21 +110,20 @@ def __init__(
116110

117111
super().__init__(label=label, **kwargs)
118112

119-
self.roll = self.Roll(roll, self)
120-
"""The working roll of this pass (equal upper and lower)."""
121-
122113
self._contour_lines = None
123114

124115
@property
116+
@abstractmethod
125117
def contour_lines(self):
126118
"""List of line strings bounding the roll pass at the high point."""
127-
raise NotImplementedError
119+
raise NotImplementedError()
128120

129121
@property
122+
@abstractmethod
130123
def classifiers(self):
131124
"""A tuple of keywords to specify the shape type classifiers of this roll pass.
132125
Shortcut to ``self.groove.classifiers``."""
133-
return set(self.roll.groove.classifiers)
126+
raise NotImplementedError()
134127

135128
@property
136129
def disk_elements(self) -> List['BaseRollPass.DiskElement']:
@@ -151,12 +144,6 @@ def init_solve(self, in_profile: BaseProfile):
151144
super().init_solve(in_profile)
152145
self.out_profile.cross_section = self.usable_cross_section
153146

154-
def get_root_hook_results(self):
155-
super_results = super().get_root_hook_results()
156-
roll_results = self.roll.evaluate_and_set_hooks()
157-
158-
return np.concatenate([super_results, roll_results], axis=0)
159-
160147
def reevaluate_cache(self):
161148
super().reevaluate_cache()
162149
self.roll.reevaluate_cache()
@@ -200,6 +187,12 @@ def __init__(self, template: BaseRoll, roll_pass: 'BaseRollPass'):
200187

201188
self._roll_pass = weakref.ref(roll_pass)
202189

190+
entry_angle = Hook[float]()
191+
"""Angle at which the material enters the roll gap."""
192+
193+
exit_angle = Hook[float]()
194+
"""Angle at which the material exits the roll gap."""
195+
203196
@property
204197
def roll_pass(self):
205198
"""Reference to the roll pass this roll is used in."""

pyroll/core/roll_pass/hookimpls/__init__.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
from . import roll_pass
1+
from . import base_roll_pass
2+
from . import symmetric_roll_pass
3+
from . import two_roll_pass
4+
from . import three_roll_pass
25
from . import profile
36
from . import roll
47
from . import disk_element
Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
from shapely import difference, line_merge, MultiLineString
2+
3+
from ..base import BaseRollPass
4+
from ...config import Config
5+
from ...rotator import Rotator
6+
7+
8+
@BaseRollPass.rotation
9+
def auto_rotation(self: BaseRollPass):
10+
return Config.ROLL_PASS_AUTO_ROTATION
11+
12+
13+
@BaseRollPass.rotation
14+
def detect_already_rotated(self: BaseRollPass):
15+
if Config.ROLL_PASS_AUTO_ROTATION and self.parent is not None:
16+
try:
17+
prev = self.prev
18+
except IndexError:
19+
return True
20+
21+
while True:
22+
if isinstance(prev, BaseRollPass):
23+
return True
24+
if isinstance(prev, Rotator):
25+
return False
26+
try:
27+
prev = prev.prev
28+
except IndexError:
29+
return True
30+
31+
32+
@BaseRollPass.orientation
33+
def default_orientation(self: BaseRollPass):
34+
return 0
35+
36+
37+
@BaseRollPass.volume
38+
def volume(self: BaseRollPass):
39+
return (self.in_profile.cross_section.area + 2 * self.out_profile.cross_section.area
40+
) / 3 * self.length
41+
42+
43+
@BaseRollPass.surface_area
44+
def surface_area(self: BaseRollPass):
45+
return (self.in_profile.cross_section.perimeter + 2 * self.out_profile.cross_section.perimeter
46+
) / 3 * self.length
47+
48+
49+
@BaseRollPass.duration
50+
def duration(self: BaseRollPass):
51+
return self.length / self.velocity
52+
53+
54+
@BaseRollPass.length
55+
def length(self: BaseRollPass):
56+
return -self.entry_point + self.exit_point
57+
58+
59+
@BaseRollPass.displaced_cross_section
60+
def displaced_cross_section(self: BaseRollPass):
61+
return difference(self.in_profile.cross_section, self.usable_cross_section)
62+
63+
64+
@BaseRollPass.reappearing_cross_section
65+
def reappearing_cross_section(self: BaseRollPass):
66+
return difference(self.out_profile.cross_section, self.in_profile.cross_section)
67+
68+
69+
@BaseRollPass.elongation_efficiency
70+
def elongation_efficiency(self: BaseRollPass):
71+
return 1 - self.reappearing_cross_section.area / self.displaced_cross_section.area
72+
73+
74+
@BaseRollPass.target_filling_ratio(trylast=True)
75+
def default_target_filling(self: BaseRollPass):
76+
return 1
77+
78+
79+
@BaseRollPass.target_width
80+
def target_width_from_target_filling_ratio(self: BaseRollPass):
81+
if self.has_value("target_filling_ratio"):
82+
return self.target_filling_ratio * self.usable_width
83+
84+
85+
@BaseRollPass.target_filling_ratio
86+
def target_filling_ratio_from_target_width(self: BaseRollPass):
87+
if self.has_set_or_cached("target_width"):
88+
return self.target_width / self.usable_width
89+
90+
91+
@BaseRollPass.target_cross_section_area
92+
def target_cross_section_area_from_target_cross_section_filling_ratio(self: BaseRollPass):
93+
if self.has_set_or_cached("target_cross_section_filling_ratio"):
94+
return self.target_cross_section_filling_ratio * self.usable_cross_section.area
95+
96+
97+
@BaseRollPass.target_cross_section_filling_ratio
98+
def target_cross_section_filling_ratio_from_target_cross_section_area(self: BaseRollPass):
99+
if self.has_value("target_cross_section_area"): # important has_value for computing from target_width
100+
return self.target_cross_section_area / self.usable_cross_section.area
101+
102+
103+
@BaseRollPass.exit_point
104+
def exit_point(self: BaseRollPass):
105+
return 0
106+
107+
108+
@BaseRollPass.Profile.contact_lines
109+
def contact_contour_lines(self: BaseRollPass.Profile):
110+
rp = self.roll_pass
111+
contact_contur_lines_possible_multilinestring = [cl.intersection(self.cross_section.exterior.buffer(1e-9)) for cl in rp.contour_lines]
112+
113+
contact_contour_lines_linestring = []
114+
for ccl in contact_contur_lines_possible_multilinestring:
115+
if isinstance(ccl, MultiLineString):
116+
merged_ccl = line_merge(ccl)
117+
contact_contour_lines_linestring.append(merged_ccl)
118+
else:
119+
contact_contour_lines_linestring.append(ccl)
120+
121+
return contact_contour_lines_linestring
122+
123+
124+
@BaseRollPass.front_tension
125+
def default_front_tension(self: BaseRollPass):
126+
return 0
127+
128+
129+
@BaseRollPass.back_tension
130+
def default_back_tension(self: BaseRollPass):
131+
return 0
132+
133+

pyroll/core/roll_pass/hookimpls/deformation_unit.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
import numpy as np
22

3-
from shapely.ops import linemerge
43
from shapely.geometry import LineString
5-
from shapely.affinity import translate, rotate
64

75
from ..deformation_unit import DeformationUnit
8-
from ..roll_pass import RollPass
96
from ...config import Config
107

118

pyroll/core/roll_pass/hookimpls/helpers.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
import math
22

33
import numpy as np
4+
import shapely
45
from shapely import Polygon, clip_by_rect
56
from shapely.affinity import rotate
67

7-
from ..roll_pass import RollPass
8+
from ..two_roll_pass import TwoRollPass
89
from ..three_roll_pass import ThreeRollPass
910
from ...profile.profile import refine_cross_section
1011

1112

12-
def out_cross_section(rp: RollPass, width: float) -> Polygon:
13+
def out_cross_section(rp: TwoRollPass, width: float) -> Polygon:
1314
poly = Polygon(np.concatenate([cl.coords for cl in rp.contour_lines]))
14-
return refine_cross_section(clip_by_rect(poly, -width / 2, -math.inf, width / 2, math.inf))
15+
poly = clip_by_rect(poly, -width / 2, -math.inf, width / 2, math.inf)
16+
return refine_cross_section(poly)
1517

1618

1719
def out_cross_section3(rp: ThreeRollPass, width: float) -> Polygon:

pyroll/core/roll_pass/hookimpls/roll.py

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import numpy as np
22

33
from ..base import BaseRollPass
4-
from ..roll_pass import RollPass
4+
from ..two_roll_pass import TwoRollPass
55
from ..three_roll_pass import ThreeRollPass
66

77

@@ -12,21 +12,11 @@ def roll_torque(self: BaseRollPass.Roll):
1212

1313
@BaseRollPass.Roll.contact_length
1414
def contact_length(self: BaseRollPass.Roll):
15-
height_change = self.roll_pass.in_profile.height - self.roll_pass.height
16-
return np.sqrt(self.min_radius * height_change - height_change ** 2 / 4)
15+
return self.roll_pass.exit_point - self.roll_pass.entry_point
1716

1817

19-
@BaseRollPass.Roll.contact_length
20-
def contact_length_square_oval(self: BaseRollPass.Roll):
21-
if "square" in self.roll_pass.in_profile.classifiers and "oval" in self.roll_pass.classifiers:
22-
depth = self.groove.local_depth(self.roll_pass.in_profile.width / 2)
23-
height_change = self.roll_pass.in_profile.height - self.roll_pass.gap - 2 * depth
24-
radius = self.max_radius - depth
25-
return np.sqrt(radius * height_change - height_change ** 2 / 4)
26-
27-
28-
@RollPass.Roll.contact_area
29-
def contact_area(self: RollPass.Roll):
18+
@BaseRollPass.Roll.contact_area
19+
def contact_area(self: TwoRollPass.Roll):
3020
return (self.roll_pass.in_profile.width + self.roll_pass.out_profile.width) / 2 * self.contact_length
3121

3222

@@ -60,3 +50,13 @@ def surface_velocity(self: BaseRollPass.Roll):
6050
return self.roll_pass.velocity / np.cos(self.neutral_angle)
6151
else:
6252
return self.roll_pass.velocity / np.cos(self.roll_pass.exit_angle)
53+
54+
55+
@BaseRollPass.Roll.entry_angle
56+
def entry_angle(self: BaseRollPass.Roll):
57+
return np.arcsin(self.roll_pass.entry_point / self.working_radius)
58+
59+
60+
@BaseRollPass.Roll.exit_angle
61+
def exit_angle(self: BaseRollPass.Roll):
62+
return np.arcsin(self.roll_pass.exit_point / self.working_radius)

0 commit comments

Comments
 (0)