pyroll-core 3.1.0__tar.gz → 3.1.2__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.
Files changed (85) hide show
  1. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/PKG-INFO +1 -1
  2. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/__init__.py +2 -2
  3. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/disk_elements/hookimpls.py +8 -0
  4. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/engine/engine.py +13 -4
  5. pyroll_core-3.1.2/pyroll/core/engine/hookimpls.py +29 -0
  6. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/generic_elongation.py +2 -0
  7. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/ovals/constricted_swedish_oval.py +1 -1
  8. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/profile/profile.py +31 -25
  9. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/hookimpls/base_roll_pass.py +4 -0
  10. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/hookimpls/engine.py +7 -0
  11. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/hookimpls/profile.py +4 -0
  12. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/sequence/sequence.py +108 -12
  13. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/unit/hookimpls.py +5 -0
  14. pyroll_core-3.1.0/pyroll/core/engine/hookimpls.py +0 -19
  15. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/.gitignore +0 -0
  16. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/LICENSE +0 -0
  17. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/README.md +0 -0
  18. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/hatch.toml +0 -0
  19. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyproject.toml +0 -0
  20. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/config.py +0 -0
  21. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/disk_elements/__init__.py +0 -0
  22. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/disk_elements/disk_element_unit.py +0 -0
  23. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/engine/__init__.py +0 -0
  24. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/__init__.py +0 -0
  25. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/base.py +0 -0
  26. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/boxes/__init__.py +0 -0
  27. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/boxes/box.py +0 -0
  28. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/boxes/constricted_box.py +0 -0
  29. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/boxes/upset_box.py +0 -0
  30. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/diamonds/__init__.py +0 -0
  31. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/diamonds/diamond.py +0 -0
  32. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/diamonds/gothic.py +0 -0
  33. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/diamonds/square.py +0 -0
  34. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/equivalent_ripped_groove.py +0 -0
  35. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/flat.py +0 -0
  36. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/generic_elongation_solvers.py +0 -0
  37. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/hexagonal.py +0 -0
  38. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/ovals/__init__.py +0 -0
  39. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/ovals/circular_oval.py +0 -0
  40. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/ovals/constricted_circular_oval.py +0 -0
  41. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/ovals/flat_oval.py +0 -0
  42. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/ovals/oval_3radii.py +0 -0
  43. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/ovals/oval_3radii_flanked.py +0 -0
  44. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/ovals/swedish_oval.py +0 -0
  45. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/ovals/upset_oval.py +0 -0
  46. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/rounds/__init__.py +0 -0
  47. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/rounds/false_round.py +0 -0
  48. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/rounds/round.py +0 -0
  49. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/grooves/spline.py +0 -0
  50. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/hooks.py +0 -0
  51. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/log.py +0 -0
  52. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/profile/__init__.py +0 -0
  53. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/profile/hookimpls.py +0 -0
  54. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/repr.py +0 -0
  55. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll/__init__.py +0 -0
  56. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll/hookimpls.py +0 -0
  57. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll/roll.py +0 -0
  58. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/__init__.py +0 -0
  59. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/base.py +0 -0
  60. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/deformation_unit.py +0 -0
  61. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/hookimpls/__init__.py +0 -0
  62. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/hookimpls/deformation_unit.py +0 -0
  63. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/hookimpls/disk_element.py +0 -0
  64. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/hookimpls/helpers.py +0 -0
  65. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/hookimpls/roll.py +0 -0
  66. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/hookimpls/symmetric_roll_pass.py +0 -0
  67. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/hookimpls/three_roll_pass.py +0 -0
  68. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/hookimpls/two_roll_pass.py +0 -0
  69. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/symmetric_roll_pass.py +0 -0
  70. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/three_roll_pass.py +0 -0
  71. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/roll_pass/two_roll_pass.py +0 -0
  72. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/rotator/__init__.py +0 -0
  73. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/rotator/hookimpls.py +0 -0
  74. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/rotator/rotator.py +0 -0
  75. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/sequence/__init__.py +0 -0
  76. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/sequence/hookimpls.py +0 -0
  77. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/shapes.py +0 -0
  78. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/transport/__init__.py +0 -0
  79. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/transport/cooling_pipe.py +0 -0
  80. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/transport/hookimpls/__init__.py +0 -0
  81. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/transport/hookimpls/cooling_pipe.py +0 -0
  82. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/transport/hookimpls/transport.py +0 -0
  83. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/transport/transport.py +0 -0
  84. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/unit/__init__.py +0 -0
  85. {pyroll_core-3.1.0 → pyroll_core-3.1.2}/pyroll/core/unit/unit.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyroll-core
3
- Version: 3.1.0
3
+ Version: 3.1.2
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
@@ -45,7 +45,7 @@ from .hooks import Hook, HookHost, HookFunction, root_hooks
45
45
  from .disk_elements import DiskElementUnit
46
46
  from .config import Config, config, PlottingBackend, ConfigValue, ConfigMeta
47
47
 
48
- VERSION = "3.1.0"
48
+ VERSION = "3.1.2"
49
49
 
50
50
  __all__ = [
51
51
  # grooves
@@ -134,6 +134,6 @@ root_hooks.extend(
134
134
  BaseRollPass.InProfile.velocity,
135
135
  PassSequence.log_elongation,
136
136
  BaseRollPass.OutProfile.technologically_orientated_cross_section,
137
- BaseRollPass.technologically_orientated_contour_lines
137
+ BaseRollPass.technologically_orientated_contour_lines,
138
138
  ]
139
139
  )
@@ -24,3 +24,11 @@ def in_x(self: DiskElementUnit.DiskElement.InProfile):
24
24
  except IndexError:
25
25
  # is first disk: get from parent in_profile
26
26
  return self.disk_element.parent.in_profile.x
27
+
28
+
29
+ @DiskElementUnit.DiskElement.InProfile.position
30
+ def in_position(self: DiskElementUnit.DiskElement.InProfile):
31
+ try:
32
+ return self.disk_element.prev.out_profile.position
33
+ except IndexError:
34
+ return self.disk_element.parent.in_profile.position
@@ -10,9 +10,18 @@ __all__ = ["Engine"]
10
10
  class Engine(HookHost):
11
11
  """Represents an engine of a roll pass."""
12
12
 
13
+ label = Hook[str]()
14
+ """Type of the engine (DC or AC)"""
15
+
13
16
  rotational_frequency = Hook[float]()
14
17
  """Rotational frequency (revolutions per time)."""
15
18
 
19
+ base_rotational_frequency = Hook[float]()
20
+ """Knee point were a DC engine switches from anchor range to field weakening range."""
21
+
22
+ maximum_rotational_frequency = Hook[float]()
23
+ """Maximum rotational frequency of the engine."""
24
+
16
25
  torque = Hook[float]()
17
26
  """Engine torque."""
18
27
 
@@ -34,11 +43,11 @@ class Engine(HookHost):
34
43
  maximum_power = Hook[float]()
35
44
  """Maximum power of the engine."""
36
45
 
37
- maximum_rotational_frequency = Hook[float]()
38
- """Maximum rotational frequency of the engine."""
46
+ available_power = Hook[float]()
47
+ """Available power of the engine only relevant for DC engines."""
39
48
 
40
- base_speed = Hook[float]()
41
- """Knee point were a DC engine switches from anchor range to field weakening range."""
49
+ mean_power = Hook[float]()
50
+ """Mean power of the engine incorporating roll pass and transport durations."""
42
51
 
43
52
 
44
53
  def __init__(self, **kwargs):
@@ -0,0 +1,29 @@
1
+ import numpy as np
2
+
3
+ from .engine import Engine
4
+ from ..transport import Transport
5
+
6
+ @Engine.gear_box_efficiency
7
+ def gear_box_efficiency_becker(self: Engine):
8
+ return 0.95
9
+
10
+ @Engine.spindle_efficiency
11
+ def spindle_efficiency_becker(self: Engine):
12
+ return 0.98
13
+
14
+ @Engine.idle_torque
15
+ def default_idle_torque(self: Engine):
16
+ return 0
17
+
18
+ @Engine.power
19
+ def roll_power(self: Engine):
20
+ return self.torque * self.rotational_frequency * 2 * np.pi
21
+
22
+ @Engine.mean_power
23
+ def mean_power(self: Engine):
24
+ try:
25
+ next_transport = self.roll_pass.next_of(Transport)
26
+ except (IndexError, ValueError):
27
+ return np.sqrt(self.torque ** 2 * self.roll_pass.duration / self.roll_pass.duration)
28
+
29
+ return np.sqrt(self.torque ** 2 * self.roll_pass.duration / (next_transport.duration + self.roll_pass.duration))
@@ -31,6 +31,7 @@ class GenericElongationGroove(GrooveBase, ReprMixin):
31
31
  rel_pad: float = Config.GROOVE_PADDING,
32
32
  pad_angle: float = 0,
33
33
  classifiers: Sequence[str] = (),
34
+ label: str = ""
34
35
  ):
35
36
  """
36
37
  Give any three of ``usable_width``, ``ground_width``, ``flank_angle`` and ``depth``.
@@ -99,6 +100,7 @@ class GenericElongationGroove(GrooveBase, ReprMixin):
99
100
  except TypeError:
100
101
  raise TypeError("Exactly three of usable_width, ground_width, flank_angle and depth must be given.")
101
102
 
103
+ self.label = label
102
104
  self.r1 = r1
103
105
  self.r2 = r2
104
106
  self.r3 = r3
@@ -62,4 +62,4 @@ class ConstrictedSwedishOvalGroove(GenericElongationGroove):
62
62
 
63
63
  @property
64
64
  def classifiers(self):
65
- return {"oval", "swedish_oval", "constricted_swedish_oval"} | super().classifiers
65
+ return {"oval", "swedish_oval", "constricted"} | super().classifiers
@@ -32,11 +32,17 @@ class Profile(HookHost):
32
32
  classifiers = Hook[Set[str]]()
33
33
  """Classifiers of the profile's shape's type."""
34
34
 
35
+ position = Hook[float]()
36
+ """Global spacial coordinate in rolling direction."""
37
+
38
+ time = Hook[float]()
39
+ """Global time in rolling direction."""
40
+
35
41
  x = Hook[float]()
36
- """Spacial coordinate in rolling direction."""
42
+ """Local spacial coordinate in rolling direction."""
37
43
 
38
44
  t = Hook[float]()
39
- """Temporal coordinate."""
45
+ """Local temporal coordinate."""
40
46
 
41
47
  velocity = Hook[float]()
42
48
  """Mean material flow velocity."""
@@ -191,13 +197,13 @@ class Profile(HookHost):
191
197
 
192
198
  @classmethod
193
199
  def from_groove(
194
- cls,
195
- groove: GrooveBase,
196
- width: Optional[float] = None,
197
- filling: Optional[float] = None,
198
- height: Optional[float] = None,
199
- gap: Optional[float] = None,
200
- **kwargs,
200
+ cls,
201
+ groove: GrooveBase,
202
+ width: Optional[float] = None,
203
+ filling: Optional[float] = None,
204
+ height: Optional[float] = None,
205
+ gap: Optional[float] = None,
206
+ **kwargs,
201
207
  ) -> "Profile":
202
208
  """
203
209
  Create a profile instance based on a given groove.
@@ -242,8 +248,8 @@ class Profile(HookHost):
242
248
  poly = Polygon(np.concatenate([upper_contour_line.coords, lower_contour_line.coords]))
243
249
 
244
250
  if (
245
- # one percent tolerance to bypass discretization issues
246
- -width / 2 < poly.bounds[0] * 1.01 or width / 2 > poly.bounds[2] * 1.01
251
+ # one percent tolerance to bypass discretization issues
252
+ -width / 2 < poly.bounds[0] * 1.01 or width / 2 > poly.bounds[2] * 1.01
247
253
  ):
248
254
  raise ValueError(
249
255
  "Profile's width can not be larger than its contour lines." "May be caused by critical overfilling."
@@ -294,7 +300,7 @@ class Profile(HookHost):
294
300
 
295
301
  @classmethod
296
302
  def square(
297
- cls, side: Optional[float] = None, diagonal: Optional[float] = None, corner_radius: float = 0, **kwargs
303
+ cls, side: Optional[float] = None, diagonal: Optional[float] = None, corner_radius: float = 0, **kwargs
298
304
  ) -> "SquareProfile":
299
305
  """
300
306
  Creates a square shaped profile (a real square with rounded corners, without imperfections of square grooves).
@@ -347,12 +353,12 @@ class Profile(HookHost):
347
353
 
348
354
  @classmethod
349
355
  def hexagon(
350
- cls,
351
- side: Optional[float] = None,
352
- height: Optional[float] = None,
353
- diagonal: Optional[float] = None,
354
- corner_radius: float = 0,
355
- **kwargs,
356
+ cls,
357
+ side: Optional[float] = None,
358
+ height: Optional[float] = None,
359
+ diagonal: Optional[float] = None,
360
+ corner_radius: float = 0,
361
+ **kwargs,
356
362
  ) -> "HexagonProfile":
357
363
  """
358
364
  Creates a hexagonal shaped profile (a real hexagonal shape with rounded corners,
@@ -567,7 +573,7 @@ class DiamondProfile(Profile):
567
573
 
568
574
  class SquareProfile(Profile):
569
575
  def __init__(
570
- self, side: Optional[float] = None, diagonal: Optional[float] = None, corner_radius: float = 0, **kwargs
576
+ self, side: Optional[float] = None, diagonal: Optional[float] = None, corner_radius: float = 0, **kwargs
571
577
  ):
572
578
  """
573
579
  Creates a square shaped profile (a real square with rounded corners, without imperfections of square grooves).
@@ -626,12 +632,12 @@ class SquareProfile(Profile):
626
632
 
627
633
  class HexagonProfile(Profile):
628
634
  def __init__(
629
- self,
630
- side: Optional[float] = None,
631
- height: Optional[float] = None,
632
- diagonal: Optional[float] = None,
633
- corner_radius: float = 0,
634
- **kwargs,
635
+ self,
636
+ side: Optional[float] = None,
637
+ height: Optional[float] = None,
638
+ diagonal: Optional[float] = None,
639
+ corner_radius: float = 0,
640
+ **kwargs,
635
641
  ):
636
642
  """
637
643
  Creates a hexagonal shaped profile (a real hexagonal shape with rounded corners,
@@ -135,6 +135,10 @@ def default_back_tension(self: BaseRollPass):
135
135
  def technologically_correctly_orientated_contour_lines(self: BaseRollPass):
136
136
  return MultiLineString([self._get_oriented_geom(cl) for cl in self.contour_lines.geoms])
137
137
 
138
+ @BaseRollPass.InProfile.technologically_orientated_cross_section
139
+ def technologically_correctly_orientated_cross_section(self: BaseRollPass.InProfile):
140
+ return self.roll_pass._get_oriented_geom(self.cross_section, orientation=self.roll_pass.orientation)
141
+
138
142
 
139
143
  @BaseRollPass.OutProfile.technologically_orientated_cross_section
140
144
  def technologically_correctly_orientated_cross_section(self: BaseRollPass.OutProfile):
@@ -19,3 +19,10 @@ def engine_rotational_frequency(self: BaseRollPass.Engine):
19
19
  return self.roll_pass.roll.rotational_frequency * self.gear_ratio
20
20
  else:
21
21
  return self.roll_pass.roll.rotational_frequency
22
+
23
+ @BaseRollPass.Engine.available_power
24
+ def available_power(self: BaseRollPass.Engine):
25
+ if self.has_set_or_cached("base_rotational_frequency"):
26
+ return self.maximum_power * (self.rotational_frequency / self.base_rotational_frequency)
27
+ else:
28
+ return self.maximum_power
@@ -75,6 +75,10 @@ def cross_section(self: BaseRollPass.OutProfile) -> Polygon:
75
75
  if width_at_y0 < in_profile_width:
76
76
  in_profile_width = width_at_y0
77
77
 
78
+ if "constricted" in self.roll_pass.classifiers:
79
+ cs = helpers.out_cross_section(self.roll_pass, self.width)
80
+ return cs
81
+
78
82
  half_abs_spread = (self.width - in_profile_width) / 2
79
83
  half_in_profile = clip_by_rect(self.roll_pass.in_profile.cross_section, 0, -np.inf, np.inf, np.inf)
80
84
  out_profile_side_contour_translated = translate(half_in_profile, xoff=half_abs_spread)
@@ -4,7 +4,9 @@ import numpy as np
4
4
 
5
5
  from collections.abc import Sequence
6
6
  from typing import overload, List, cast
7
+ from scipy.interpolate import interp1d
7
8
 
9
+ from .. import CoolingPipe
8
10
  from ..unit import Unit
9
11
  from ..roll_pass import BaseRollPass
10
12
  from ..transport import Transport
@@ -130,6 +132,11 @@ class PassSequence(Unit, Sequence[Unit]):
130
132
  """Returns a list of all transports in this sequence."""
131
133
  return list(u for u in self._subunits if isinstance(u, Transport))
132
134
 
135
+ @property
136
+ def cooling_pipes(self) -> List[CoolingPipe]:
137
+ """Returns a list of all cooling pipes in this sequence."""
138
+ return list(u for u in self._subunits if isinstance(u, CoolingPipe))
139
+
133
140
  @property
134
141
  def __attrs__(self):
135
142
  return super().__attrs__ | {"units": self.units}
@@ -137,6 +144,32 @@ class PassSequence(Unit, Sequence[Unit]):
137
144
  def _ipython_key_completions_(self):
138
145
  return [u.label for u in self._subunits]
139
146
 
147
+ def solve_velocities_from_elongations(self, in_profile: Profile, final_speed: float, elongations: np.ndarray):
148
+ """
149
+ Solve method, that calculates all velocities of the roll pass starting from the very last roll_pass of the sequence using given elongations.
150
+
151
+ :param in_profile: incoming profile
152
+ :param final_speed: speed of the last stand
153
+ :param elongations: array of the elongations per pass, starting from the first roll pass
154
+ """
155
+
156
+ if len(elongations) != len(self.roll_passes) - 1:
157
+ raise ValueError("Number of Elongations doesn't match the number of roll passes.")
158
+
159
+ speed = final_speed
160
+ speeds = [final_speed]
161
+
162
+ for e in elongations:
163
+ speed = speed / e
164
+ speeds.append(speed)
165
+
166
+ speeds = np.array(speeds)
167
+
168
+ for rp, speed in zip(self.roll_passes, speeds[::-1]):
169
+ rp.velocity = speed
170
+
171
+ self.solve(in_profile)
172
+
140
173
  def solve_velocities_backward(self, in_profile: Profile, final_speed: float, final_cross_section_area: float):
141
174
  """
142
175
  Solve method, that calculates all velocities of the roll pass starting from the very last roll_pass of the sequence.
@@ -156,13 +189,17 @@ class PassSequence(Unit, Sequence[Unit]):
156
189
  for roll_pass, velocity in zip(roll_passes, velocities):
157
190
  roll_pass.velocity = velocity
158
191
 
159
- usable_cross_section_areas = np.asarray([roll_pass.usable_cross_section.area for roll_pass in copied_sequence.roll_passes])
192
+ usable_cross_section_areas = np.asarray(
193
+ [roll_pass.usable_cross_section.area for roll_pass in copied_sequence.roll_passes]
194
+ )
160
195
  initial_roll_pass_velocities = np.zeros_like(usable_cross_section_areas, dtype=float)
161
196
 
162
197
  initial_roll_pass_velocities[-1] = final_speed
163
198
  usable_cross_section_areas[-1] = final_cross_section_area
164
199
 
165
- calculate_velocities_array(velocities=initial_roll_pass_velocities, cross_sections_areas=usable_cross_section_areas)
200
+ calculate_velocities_array(
201
+ velocities=initial_roll_pass_velocities, cross_sections_areas=usable_cross_section_areas
202
+ )
166
203
  set_velocities_to_roll_passes(roll_passes=copied_sequence.roll_passes, velocities=initial_roll_pass_velocities)
167
204
 
168
205
  copied_sequence.solve(in_profile)
@@ -173,7 +210,9 @@ class PassSequence(Unit, Sequence[Unit]):
173
210
  profile_areas = [roll_pass.out_profile.cross_section.area for roll_pass in copied_sequence.roll_passes]
174
211
 
175
212
  calculate_velocities_array(velocities=current_roll_pass_velocities, cross_sections_areas=profile_areas)
176
- set_velocities_to_roll_passes(roll_passes=copied_sequence.roll_passes, velocities=current_roll_pass_velocities)
213
+ set_velocities_to_roll_passes(
214
+ roll_passes=copied_sequence.roll_passes, velocities=current_roll_pass_velocities
215
+ )
177
216
 
178
217
  copied_sequence.solve(in_profile)
179
218
 
@@ -203,12 +242,18 @@ class PassSequence(Unit, Sequence[Unit]):
203
242
  for roll_pass, velocity in zip(roll_passes, velocities):
204
243
  roll_pass.velocity = velocity
205
244
 
206
- usable_cross_section_areas = np.asarray([roll_pass.usable_cross_section.area for roll_pass in copied_sequence.roll_passes])
245
+ usable_cross_section_areas = np.asarray(
246
+ [roll_pass.usable_cross_section.area for roll_pass in copied_sequence.roll_passes]
247
+ )
207
248
  initial_roll_pass_velocities = np.zeros_like(usable_cross_section_areas, dtype=float)
208
249
 
209
- initial_roll_pass_velocities[0] = initial_speed * in_profile.cross_section.area / copied_sequence.roll_passes[0].usable_cross_section.area
250
+ initial_roll_pass_velocities[0] = (
251
+ initial_speed * in_profile.cross_section.area / copied_sequence.roll_passes[0].usable_cross_section.area
252
+ )
210
253
 
211
- calculate_velocities_array(velocities=initial_roll_pass_velocities, cross_sections_areas=usable_cross_section_areas)
254
+ calculate_velocities_array(
255
+ velocities=initial_roll_pass_velocities, cross_sections_areas=usable_cross_section_areas
256
+ )
212
257
  set_velocities_to_roll_passes(roll_passes=copied_sequence.roll_passes, velocities=initial_roll_pass_velocities)
213
258
 
214
259
  copied_sequence.solve(in_profile)
@@ -231,8 +276,9 @@ class PassSequence(Unit, Sequence[Unit]):
231
276
  self.solve(in_profile)
232
277
  break
233
278
 
234
- def solve_interstand_tensions_with_given_velocity_ratios(self, in_profile: Profile,
235
- velocity_ratios: np.ndarray[float], final_speed: float):
279
+ def solve_interstand_tensions_with_given_velocity_ratios(
280
+ self, in_profile: Profile, velocity_ratios: np.ndarray[float], final_speed: float
281
+ ):
236
282
  """
237
283
  Solve method, that calculates the resulting tensions for given reductions.
238
284
  Further, it sets the velocities according to these reductions and the finishing speed.
@@ -260,10 +306,12 @@ class PassSequence(Unit, Sequence[Unit]):
260
306
  for index in range(1, len(self.roll_passes)):
261
307
  engineering_strain = engineering_strains[index - 1]
262
308
 
263
- tensions[2 * index - 1] = copied_sequence.roll_passes[
264
- index - 1].in_profile.elastic_modulus * engineering_strain
265
- tensions[2 * index] -= copied_sequence.roll_passes[
266
- index - 1].out_profile.elastic_modulus * engineering_strain
309
+ tensions[2 * index - 1] = (
310
+ copied_sequence.roll_passes[index - 1].in_profile.elastic_modulus * engineering_strain
311
+ )
312
+ tensions[2 * index] -= (
313
+ copied_sequence.roll_passes[index - 1].out_profile.elastic_modulus * engineering_strain
314
+ )
267
315
 
268
316
  tensions[0] = 0
269
317
  tensions[-1] = 0
@@ -273,3 +321,51 @@ class PassSequence(Unit, Sequence[Unit]):
273
321
  roll_pass.front_tension = tensions[2 * index + 1]
274
322
 
275
323
  self.solve(in_profile=in_profile)
324
+
325
+ def find_value_by_position_or_time(self, hook_name: str, position: float = None, time: float = None):
326
+ if position is None:
327
+ search_coordinate = "t"
328
+ else:
329
+ search_coordinate = "position"
330
+
331
+ coordinates = []
332
+ values = []
333
+
334
+ for su in self.subunits:
335
+ if len(su.subunits) > 0:
336
+ for ssu in su.subunits:
337
+ coordinates.append(getattr(ssu.in_profile, search_coordinate, None))
338
+ values.append(getattr(ssu.in_profile, hook_name, None))
339
+
340
+ coordinates.append(getattr(ssu.out_profile, search_coordinate, None))
341
+ values.append(getattr(ssu.out_profile, hook_name, None))
342
+ else:
343
+ coordinates.append(getattr(su.in_profile, search_coordinate, None))
344
+ values.append(getattr(su.in_profile, hook_name, None))
345
+
346
+ coordinates.append(getattr(su.out_profile, search_coordinate, None))
347
+ values.append(getattr(su.out_profile, hook_name, None))
348
+
349
+ if all(v is None for v in values):
350
+ raise ValueError(f"No hook with name {hook_name} found.")
351
+
352
+ closest_pos = min(coordinates, key=lambda p: abs(p - position))
353
+ closest_index = coordinates.index(closest_pos)
354
+
355
+ postion_array = np.array(coordinates)
356
+ values_array = np.array(values)
357
+
358
+ next_element = 1
359
+ while True:
360
+ if (closest_index + next_element) >= len(coordinates):
361
+ raise ValueError(f"Coordinate index {closest_index + next_element} out of sequence range.")
362
+
363
+ if coordinates[closest_index + next_element] >= position:
364
+ break
365
+ next_element += 1
366
+
367
+ val = values_array[closest_index : closest_index + next_element + 1]
368
+ pos = postion_array[closest_index : closest_index + next_element + 1]
369
+ interpolation = interp1d(pos, val)
370
+
371
+ return interpolation(position)
@@ -56,6 +56,11 @@ def out_velocity(self: Unit.OutProfile):
56
56
  return self.unit.in_profile.velocity * self.unit.in_profile.cross_section.area / self.cross_section.area
57
57
 
58
58
 
59
+ @Unit.OutProfile.position
60
+ def out_position(self: Unit.OutProfile):
61
+ return self.unit.in_profile.position + self.unit.length
62
+
63
+
59
64
  @Unit.OutProfile.x
60
65
  def out_x(self: Unit.OutProfile):
61
66
  return self.unit.in_profile.x + self.unit.length
@@ -1,19 +0,0 @@
1
- import numpy as np
2
-
3
- from .engine import Engine
4
-
5
- @Engine.gear_box_efficiency
6
- def gear_box_efficiency_becker(self: Engine):
7
- return 0.95
8
-
9
- @Engine.spindle_efficiency
10
- def spindle_efficiency_becker(self: Engine):
11
- return 0.98
12
-
13
- @Engine.idle_torque
14
- def default_idle_torque(self: Engine):
15
- return 0
16
-
17
- @Engine.power
18
- def roll_power(self: Engine):
19
- return self.torque * self.rotational_frequency * 2 * np.pi
File without changes
File without changes
File without changes
File without changes
File without changes