Skip to content

Commit

Permalink
add common symmetric roll passes base class
Browse files Browse the repository at this point in the history
  • Loading branch information
axtimhaus committed Nov 6, 2024
1 parent 1b5cbba commit 2d82aed
Show file tree
Hide file tree
Showing 16 changed files with 533 additions and 421 deletions.
2 changes: 1 addition & 1 deletion pyroll/core/roll/hookimpls.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def contour_points(self: Roll):

@Roll.surface_x
def surface_x(self: Roll):
padded_contact_angle = np.arcsin(1.1 * self.contact_length / self.min_radius)
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
points = np.concatenate([
np.linspace(0, padded_contact_angle, Config.ROLL_SURFACE_DISCRETIZATION_COUNT, endpoint=False),
np.linspace(padded_contact_angle, np.pi / 2, Config.ROLL_SURFACE_DISCRETIZATION_COUNT),
Expand Down
4 changes: 3 additions & 1 deletion pyroll/core/roll_pass/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
from .base import BaseRollPass
from .roll_pass import RollPass
from .two_roll_pass import TwoRollPass
from .three_roll_pass import ThreeRollPass
from .deformation_unit import DeformationUnit

from . import hookimpls

RollPass = TwoRollPass



31 changes: 12 additions & 19 deletions pyroll/core/roll_pass/base.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import weakref
from abc import abstractmethod, ABC
from typing import List, Union, cast

import numpy as np
Expand All @@ -13,7 +14,7 @@
from .deformation_unit import DeformationUnit


class BaseRollPass(DiskElementUnit, DeformationUnit):
class BaseRollPass(DiskElementUnit, DeformationUnit, ABC):
"""Represents a roll pass with two symmetric working rolls."""

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

entry_angle = Hook[float]()
"""Angle at which the material enters the roll gap."""

exit_point = Hook[float]()
"""Point where the material exits the roll gap."""

exit_angle = Hook[float]()
"""Angle at which the material exits the roll gap."""

front_tension = Hook[float]()
"""Front tension acting on the current roll pass."""

Expand Down Expand Up @@ -101,7 +96,6 @@ class BaseRollPass(DiskElementUnit, DeformationUnit):

def __init__(
self,
roll: BaseRoll,
label: str = "",
**kwargs
):
Expand All @@ -113,21 +107,20 @@ def __init__(

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

self.roll = self.Roll(roll, self)
"""The working roll of this pass (equal upper and lower)."""

self._contour_lines = None

@property
@abstractmethod
def contour_lines(self):
"""List of line strings bounding the roll pass at the high point."""
raise NotImplementedError
raise NotImplementedError()

@property
@abstractmethod
def classifiers(self):
"""A tuple of keywords to specify the shape type classifiers of this roll pass.
Shortcut to ``self.groove.classifiers``."""
return set(self.roll.groove.classifiers)
raise NotImplementedError()

@property
def disk_elements(self) -> List['BaseRollPass.DiskElement']:
Expand All @@ -148,12 +141,6 @@ def init_solve(self, in_profile: BaseProfile):
super().init_solve(in_profile)
self.out_profile.cross_section = self.usable_cross_section

def get_root_hook_results(self):
super_results = super().get_root_hook_results()
roll_results = self.roll.evaluate_and_set_hooks()

return np.concatenate([super_results, roll_results], axis=0)

def reevaluate_cache(self):
super().reevaluate_cache()
self.roll.reevaluate_cache()
Expand Down Expand Up @@ -197,6 +184,12 @@ def __init__(self, template: BaseRoll, roll_pass: 'BaseRollPass'):

self._roll_pass = weakref.ref(roll_pass)

entry_angle = Hook[float]()
"""Angle at which the material enters the roll gap."""

exit_angle = Hook[float]()
"""Angle at which the material exits the roll gap."""

@property
def roll_pass(self):
"""Reference to the roll pass this roll is used in."""
Expand Down
5 changes: 4 additions & 1 deletion pyroll/core/roll_pass/hookimpls/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
from . import roll_pass
from . import base_roll_pass
from . import symmetric_roll_pass
from . import two_roll_pass
from . import three_roll_pass
from . import profile
from . import roll
from . import disk_element
Expand Down
124 changes: 124 additions & 0 deletions pyroll/core/roll_pass/hookimpls/base_roll_pass.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
from shapely import difference
from shapely.ops import linemerge

from ..base import BaseRollPass
from ...config import Config
from ...rotator import Rotator


@BaseRollPass.rotation
def auto_rotation(self: BaseRollPass):
return Config.ROLL_PASS_AUTO_ROTATION


@BaseRollPass.rotation
def detect_already_rotated(self: BaseRollPass):
if Config.ROLL_PASS_AUTO_ROTATION and self.parent is not None:
try:
prev = self.prev
except IndexError:
return True

while True:
if isinstance(prev, BaseRollPass):
return True
if isinstance(prev, Rotator):
return False
try:
prev = prev.prev
except IndexError:
return True


@BaseRollPass.orientation
def default_orientation(self: BaseRollPass):
return 0


@BaseRollPass.volume
def volume(self: BaseRollPass):
return (self.in_profile.cross_section.area + 2 * self.out_profile.cross_section.area
) / 3 * self.length


@BaseRollPass.surface_area
def surface_area(self: BaseRollPass):
return (self.in_profile.cross_section.perimeter + 2 * self.out_profile.cross_section.perimeter
) / 3 * self.length


@BaseRollPass.duration
def duration(self: BaseRollPass):
return self.length / self.velocity


@BaseRollPass.length
def length(self: BaseRollPass):
return -self.entry_point + self.exit_point


@BaseRollPass.displaced_cross_section
def displaced_cross_section(self: BaseRollPass):
return difference(self.in_profile.cross_section, self.usable_cross_section)


@BaseRollPass.reappearing_cross_section
def reappearing_cross_section(self: BaseRollPass):
return difference(self.out_profile.cross_section, self.in_profile.cross_section)


@BaseRollPass.elongation_efficiency
def elongation_efficiency(self: BaseRollPass):
return 1 - self.reappearing_cross_section.area / self.displaced_cross_section.area


@BaseRollPass.target_filling_ratio(trylast=True)
def default_target_filling(self: BaseRollPass):
return 1


@BaseRollPass.target_width
def target_width_from_target_filling_ratio(self: BaseRollPass):
if self.has_value("target_filling_ratio"):
return self.target_filling_ratio * self.usable_width


@BaseRollPass.target_filling_ratio
def target_filling_ratio_from_target_width(self: BaseRollPass):
if self.has_set_or_cached("target_width"):
return self.target_width / self.usable_width


@BaseRollPass.target_cross_section_area
def target_cross_section_area_from_target_cross_section_filling_ratio(self: BaseRollPass):
if self.has_set_or_cached("target_cross_section_filling_ratio"):
return self.target_cross_section_filling_ratio * self.usable_cross_section.area


@BaseRollPass.target_cross_section_filling_ratio
def target_cross_section_filling_ratio_from_target_cross_section_area(self: BaseRollPass):
if self.has_value("target_cross_section_area"): # important has_value for computing from target_width
return self.target_cross_section_area / self.usable_cross_section.area


@BaseRollPass.exit_point
def exit_point(self: BaseRollPass):
return 0


@BaseRollPass.Profile.contact_lines
def contact_contour_lines(self: BaseRollPass.Profile):
rp = self.roll_pass
return [linemerge(cl.intersection(self.cross_section.exterior.buffer(1e-9))) for cl in rp.contour_lines]


@BaseRollPass.front_tension
def default_front_tension(self: BaseRollPass):
return 0


@BaseRollPass.back_tension
def default_back_tension(self: BaseRollPass):
return 0


3 changes: 0 additions & 3 deletions pyroll/core/roll_pass/hookimpls/deformation_unit.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import numpy as np

from shapely.ops import linemerge
from shapely.geometry import LineString
from shapely.affinity import translate, rotate

from ..deformation_unit import DeformationUnit
from ..roll_pass import RollPass
from ...config import Config


Expand Down
8 changes: 5 additions & 3 deletions pyroll/core/roll_pass/hookimpls/helpers.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,19 @@
import math

import numpy as np
import shapely
from shapely import Polygon, clip_by_rect
from shapely.affinity import rotate

from ..roll_pass import RollPass
from ..two_roll_pass import TwoRollPass
from ..three_roll_pass import ThreeRollPass
from ...profile.profile import refine_cross_section


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


def out_cross_section3(rp: ThreeRollPass, width: float) -> Polygon:
Expand Down
28 changes: 14 additions & 14 deletions pyroll/core/roll_pass/hookimpls/roll.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import numpy as np

from ..base import BaseRollPass
from ..roll_pass import RollPass
from ..two_roll_pass import TwoRollPass
from ..three_roll_pass import ThreeRollPass


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

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


@BaseRollPass.Roll.contact_length
def contact_length_square_oval(self: BaseRollPass.Roll):
if "square" in self.roll_pass.in_profile.classifiers and "oval" in self.roll_pass.classifiers:
depth = self.groove.local_depth(self.roll_pass.in_profile.width / 2)
height_change = self.roll_pass.in_profile.height - self.roll_pass.gap - 2 * depth
radius = self.max_radius - depth
return np.sqrt(radius * height_change - height_change ** 2 / 4)


@RollPass.Roll.contact_area
def contact_area(self: RollPass.Roll):
@BaseRollPass.Roll.contact_area
def contact_area(self: TwoRollPass.Roll):
return (self.roll_pass.in_profile.width + self.roll_pass.out_profile.width) / 2 * self.contact_length


Expand Down Expand Up @@ -60,3 +50,13 @@ def surface_velocity(self: BaseRollPass.Roll):
return self.roll_pass.velocity / np.cos(self.neutral_angle)
else:
return self.roll_pass.velocity / np.cos(self.roll_pass.exit_angle)


@BaseRollPass.Roll.entry_angle
def entry_angle(self: BaseRollPass.Roll):
return np.arcsin(self.roll_pass.entry_point / self.working_radius)


@BaseRollPass.Roll.exit_angle
def exit_angle(self: BaseRollPass.Roll):
return np.arcsin(self.roll_pass.exit_point / self.working_radius)
Loading

0 comments on commit 2d82aed

Please sign in to comment.