Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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]()
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you remove the entry, resp. exit angles?

"""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 @@ -104,7 +99,6 @@ class BaseRollPass(DiskElementUnit, DeformationUnit):

def __init__(
self,
roll: BaseRoll,
label: str = "",
**kwargs
):
Expand All @@ -116,21 +110,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 @@ -151,12 +144,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 @@ -200,6 +187,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
141 changes: 141 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,141 @@
from shapely import difference, line_merge, MultiLineString

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
contact_contur_lines_possible_multilinestring = [cl.intersection(self.cross_section.exterior.buffer(1e-9)) for cl in rp.contour_lines]

contact_contour_lines_linestring = []
for ccl in contact_contur_lines_possible_multilinestring:
if isinstance(ccl, MultiLineString):
merged_ccl = line_merge(ccl)
contact_contour_lines_linestring.append(merged_ccl)
else:
contact_contour_lines_linestring.append(ccl)

return contact_contour_lines_linestring


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


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


@BaseRollPass.technologically_orientated_contour_lines
def technologically_correctly_orientated_contour_lines(self: BaseRollPass):
return MultiLineString([self._get_oriented_geom(cl) for cl in self.contour_lines])


@BaseRollPass.OutProfile.technologically_orientated_cross_section
def technologically_correctly_orientated_cross_section(self: BaseRollPass.OutProfile):
return self.roll_pass._get_oriented_geom(self.cross_section, orientation=self.roll_pass.orientation)
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
4 changes: 2 additions & 2 deletions pyroll/core/roll_pass/hookimpls/profile.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ def exit_point(self: BaseRollPass.OutProfile):

@BaseRollPass.InProfile.longitudinal_angle
def longitudinal_angle(self: BaseRollPass.InProfile):
return self.roll_pass.entry_angle
return self.roll_pass.roll.entry_angle


@BaseRollPass.OutProfile.longitudinal_angle
def longitudinal_angle(self: BaseRollPass.OutProfile):
return self.roll_pass.exit_angle
return self.roll_pass.roll.exit_angle


@BaseRollPass.OutProfile.strain
Expand Down
30 changes: 15 additions & 15 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 @@ -59,4 +49,14 @@ def surface_velocity(self: BaseRollPass.Roll):
if self.has_value("neutral_angle"):
return self.roll_pass.velocity / np.cos(self.neutral_angle)
else:
return self.roll_pass.velocity / np.cos(self.roll_pass.exit_angle)
return self.roll_pass.velocity / np.cos(self.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