Skip to content

Commit b463cd6

Browse files
authored
Performance improvements based on Seattle script (#184)
1 parent 06ea1ee commit b463cd6

File tree

5 files changed

+75
-51
lines changed

5 files changed

+75
-51
lines changed

.github/workflows/nightly_docker_test.yml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ jobs:
2929
uses: actions/setup-python@v4
3030
with:
3131
python-version: ${{ env.MAIN_PYTHON_VERSION }}
32-
cache: 'pip'
33-
cache-dependency-path: 'pyproject.toml'
32+
# TODO : This should be activated once the private dependencies are removed
33+
# pip install process installs them again even if they are on cache
34+
# cache: 'pip'
35+
# cache-dependency-path: 'pyproject.toml'
3436

3537
- name: Create Python venv
3638
run: |

src/ansys/geometry/core/math/frame.py

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -160,12 +160,7 @@ def transform_point2d_local_to_global(self, point: Point2D) -> Point3D:
160160
Point3D
161161
The global coordinates ``Point3D`` object.
162162
"""
163-
local_point_as_3d = Point3D(
164-
[point.x.to_base_units().m, point.y.to_base_units().m, 0], point.base_unit
165-
)
166-
return self.origin + Point3D(
167-
self.local_to_global_rotation @ local_point_as_3d, point.base_unit
168-
)
163+
return self.origin + Vector3D(self.local_to_global_rotation @ [point[0], point[1], 0])
169164

170165
def __eq__(self, other: "Frame") -> bool:
171166
"""Equals operator for ``Frame``."""

src/ansys/geometry/core/math/point.py

Lines changed: 57 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
if TYPE_CHECKING: # pragma: no cover
2525
from ansys.geometry.core.math.vector import Vector2D, Vector3D
2626

27+
BASE_UNIT_LENGTH = UNITS.get_base_units(UNIT_LENGTH)[1]
28+
2729

2830
class Point2D(np.ndarray, PhysicalQuantity):
2931
"""
@@ -66,7 +68,8 @@ def __init__(
6668
raise ValueError("Point2D class must receive 2 arguments.") # noqa: E501
6769

6870
# Store values
69-
self.flat = [(elem * self.unit).to_base_units().magnitude for elem in input]
71+
self._quantities = [Quantity(elem, units=unit) for elem in input]
72+
self.flat = [elem.to_base_units().m for elem in self._quantities]
7073

7174
def __eq__(self, other: "Point2D") -> bool:
7275
"""Equals operator for ``Point2D``."""
@@ -80,29 +83,36 @@ def __ne__(self, other: "Point2D") -> bool:
8083
def __set_value(self, input: Quantity, idx: int) -> None:
8184
"""General setter method for ``Point2D`` class."""
8285
self[idx] = self._base_units_magnitude(input)
86+
self._quantities[idx] = input.to(self.unit)
8387

8488
def __add__(self, other: Union["Point2D", "Vector2D"]) -> "Point2D":
8589
"""Add operation for ``Point2D``."""
8690
from ansys.geometry.core.math.vector import Vector2D
8791

8892
check_type(other, (Point2D, Vector2D))
89-
point = Point2D(np.add(self, other), self.base_unit)
90-
point.unit = self.unit
93+
point = np.add(self, other).view(Point2D)
94+
point._unit = self.unit
95+
point._base_unit = self.base_unit
96+
point._quantities = [np.nan, np.nan]
9197
return point
9298

9399
def __sub__(self, other: "Point2D") -> "Point2D":
94100
"""Subtraction operation for ``Point2D``."""
95101
from ansys.geometry.core.math.vector import Vector2D
96102

97103
check_type(other, (Point2D, Vector2D))
98-
point = Point2D(np.subtract(self, other), self.base_unit)
99-
point.unit = self.unit
104+
point = np.subtract(self, other).view(Point2D)
105+
point._unit = self.unit
106+
point._base_unit = self.base_unit
107+
point._quantities = [np.nan, np.nan]
100108
return point
101109

102110
@property
103111
def x(self) -> Quantity:
104112
"""Returns the X plane component value."""
105-
return self._get_quantity(self[0])
113+
if self._quantities[0] is np.nan:
114+
self._quantities[0] = Quantity(self[0], units=self.base_unit).to(self.unit)
115+
return self._quantities[0]
106116

107117
@x.setter
108118
def x(self, x: Quantity) -> None:
@@ -112,7 +122,9 @@ def x(self, x: Quantity) -> None:
112122
@property
113123
def y(self) -> Quantity:
114124
"""Returns the Y plane component value."""
115-
return self._get_quantity(self[1])
125+
if self._quantities[1] is np.nan:
126+
self._quantities[1] = Quantity(self[1], units=self.base_unit).to(self.unit)
127+
return self._quantities[1]
116128

117129
@y.setter
118130
def y(self, y: Quantity) -> None:
@@ -122,20 +134,20 @@ def y(self, y: Quantity) -> None:
122134
@PhysicalQuantity.unit.getter
123135
def unit(self) -> Unit:
124136
"""Returns the unit of the object."""
125-
try:
126-
return PhysicalQuantity.unit.fget(self)
127-
except AttributeError:
137+
if hasattr(self, "_unit"):
138+
return self._unit
139+
else:
128140
self._unit = UNIT_LENGTH
129-
return self.unit
141+
return self._unit
130142

131143
@PhysicalQuantity.base_unit.getter
132144
def base_unit(self) -> Unit:
133145
"""Returns the base unit of the object."""
134-
try:
135-
return PhysicalQuantity.base_unit.fget(self)
136-
except AttributeError:
137-
self._base_unit = UNITS.get_base_units(UNIT_LENGTH)[1]
138-
return self.base_unit
146+
if hasattr(self, "_base_unit"):
147+
return self._base_unit
148+
else:
149+
self._base_unit = BASE_UNIT_LENGTH
150+
return self._base_unit
139151

140152

141153
class Point3D(np.ndarray, PhysicalQuantity):
@@ -179,7 +191,8 @@ def __init__(
179191
raise ValueError("Point3D class must receive 3 arguments.") # noqa: E501
180192

181193
# Store values
182-
self.flat = [(elem * self.unit).to_base_units().magnitude for elem in input]
194+
self._quantities = [Quantity(elem, units=unit) for elem in input]
195+
self.flat = [elem.to_base_units().m for elem in self._quantities]
183196

184197
def __eq__(self, other: "Point3D") -> bool:
185198
"""Equals operator for ``Point3D``."""
@@ -195,27 +208,34 @@ def __add__(self, other: Union["Point3D", "Vector3D"]) -> "Point3D":
195208
from ansys.geometry.core.math.vector import Vector3D
196209

197210
check_type(other, (Point3D, Vector3D))
198-
point = Point3D(np.add(self, other), self.base_unit)
199-
point.unit = self.unit
211+
point = np.add(self, other).view(Point3D)
212+
point._unit = self.unit
213+
point._base_unit = self.base_unit
214+
point._quantities = [np.nan, np.nan, np.nan]
200215
return point
201216

202217
def __sub__(self, other: Union["Point3D", "Vector3D"]) -> "Point3D":
203218
"""Subtraction operation for ``Point3D``."""
204219
from ansys.geometry.core.math.vector import Vector3D
205220

206221
check_type(other, (Point3D, Vector3D))
207-
point = Point3D(np.subtract(self, other), self.base_unit)
208-
point.unit = self.unit
222+
point = np.subtract(self, other).view(Point3D)
223+
point._unit = self.unit
224+
point._base_unit = self.base_unit
225+
point._quantities = [np.nan, np.nan, np.nan]
209226
return point
210227

211228
def __set_value(self, input: Quantity, idx: int) -> None:
212229
"""General setter method for ``Point3D`` class."""
213230
self[idx] = self._base_units_magnitude(input)
231+
self._quantities[idx] = input.to(self.unit)
214232

215233
@property
216234
def x(self) -> Quantity:
217235
"""Returns the X plane component value."""
218-
return self._get_quantity(self[0])
236+
if self._quantities[0] is np.nan:
237+
self._quantities[0] = Quantity(self[0], units=self.base_unit).to(self.unit)
238+
return self._quantities[0]
219239

220240
@x.setter
221241
def x(self, x: Quantity) -> None:
@@ -225,7 +245,9 @@ def x(self, x: Quantity) -> None:
225245
@property
226246
def y(self) -> Quantity:
227247
"""Returns the Y plane component value."""
228-
return self._get_quantity(self[1])
248+
if self._quantities[1] is np.nan:
249+
self._quantities[1] = Quantity(self[1], units=self.base_unit).to(self.unit)
250+
return self._quantities[1]
229251

230252
@y.setter
231253
def y(self, y: Quantity) -> None:
@@ -235,7 +257,9 @@ def y(self, y: Quantity) -> None:
235257
@property
236258
def z(self) -> Quantity:
237259
"""Returns the Z plane component value."""
238-
return self._get_quantity(self[2])
260+
if self._quantities[2] is np.nan:
261+
self._quantities[2] = Quantity(self[2], units=self.base_unit).to(self.unit)
262+
return self._quantities[2]
239263

240264
@z.setter
241265
def z(self, z: Quantity) -> None:
@@ -245,17 +269,17 @@ def z(self, z: Quantity) -> None:
245269
@PhysicalQuantity.unit.getter
246270
def unit(self) -> Unit:
247271
"""Returns the unit of the object."""
248-
try:
249-
return PhysicalQuantity.unit.fget(self)
250-
except AttributeError:
272+
if hasattr(self, "_unit"):
273+
return self._unit
274+
else:
251275
self._unit = UNIT_LENGTH
252-
return self.unit
276+
return self._unit
253277

254278
@PhysicalQuantity.base_unit.getter
255279
def base_unit(self) -> Unit:
256280
"""Returns the base unit of the object."""
257-
try:
258-
return PhysicalQuantity.base_unit.fget(self)
259-
except AttributeError:
260-
self._base_unit = UNITS.get_base_units(UNIT_LENGTH)[1]
261-
return self.base_unit
281+
if hasattr(self, "_base_unit"):
282+
return self._base_unit
283+
else:
284+
self._base_unit = BASE_UNIT_LENGTH
285+
return self._base_unit

src/ansys/geometry/core/math/vector.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def normalize(self) -> "Vector3D":
101101
"""Return a normalized version of the ``Vector3D``."""
102102
norm = self.norm
103103
if norm > 0:
104-
return Vector3D(self / norm)
104+
return (self / norm).view(Vector3D)
105105
else:
106106
raise ValueError("The norm of the Vector3D is not valid.")
107107

@@ -135,7 +135,7 @@ def get_angle_between(self, v: "Vector3D") -> Quantity:
135135
def cross(self, v: "Vector3D") -> "Vector3D":
136136
"""Return cross product of Vector3D objects"""
137137
check_type_equivalence(v, self)
138-
return Vector3D(np.cross(self, v))
138+
return np.cross(self, v).view(Vector3D)
139139

140140
def __eq__(self, other: "Vector3D") -> bool:
141141
"""Equals operator for ``Vector3D``."""
@@ -154,7 +154,7 @@ def __mul__(self, other: Union["Vector3D", Real]) -> Union["Vector3D", Real]:
154154
Also admits scalar multiplication.
155155
"""
156156
if isinstance(other, (int, float)):
157-
return Vector3D(np.multiply(self, other))
157+
return np.multiply(self, other).view(Vector3D)
158158
else:
159159
check_type_equivalence(other, self)
160160
return self.dot(other)
@@ -179,7 +179,7 @@ def __add__(self, other: Union["Vector3D", Point3D]) -> Union["Vector3D", Point3
179179
def __sub__(self, other: "Vector3D") -> "Vector3D":
180180
"""Subtraction operation overload for ``Vector3D`` objects."""
181181
check_type_equivalence(other, self)
182-
return Vector3D(np.subtract(self, other))
182+
return np.subtract(self, other).view(Vector3D)
183183

184184
@classmethod
185185
def from_points(
@@ -285,7 +285,7 @@ def normalize(self) -> "Vector2D":
285285
"""Return a normalized version of the ``Vector2D``."""
286286
norm = self.norm
287287
if norm > 0:
288-
return Vector2D(self / norm)
288+
return (self / norm).view(Vector2D)
289289
else:
290290
raise ValueError("The norm of the Vector2D is not valid.")
291291

@@ -329,7 +329,7 @@ def __mul__(self, other: Union["Vector2D", Real]) -> Union["Vector2D", Real]:
329329
Also admits scalar multiplication.
330330
"""
331331
if isinstance(other, (int, float)):
332-
return Vector2D(np.multiply(self, other))
332+
return np.multiply(self, other).view(Vector2D)
333333
else:
334334
check_type_equivalence(other, self)
335335
return self.dot(other)
@@ -350,7 +350,7 @@ def __add__(self, other: Union["Vector2D", Point2D]) -> Union["Vector2D", Point2
350350
def __sub__(self, other: "Vector2D") -> "Vector2D":
351351
"""Subtraction operation overload for ``Vector2D`` objects."""
352352
check_type_equivalence(other, self)
353-
return Vector2D(np.subtract(self, other))
353+
return np.subtract(self, other).view(Vector2D)
354354

355355
@classmethod
356356
def from_points(

src/ansys/geometry/core/misc/units.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22

33
from typing import Optional, Union
44

5-
from pint import Quantity, Unit, UnitRegistry
5+
from pint import Quantity, Unit, UnitRegistry, set_application_registry
66

77
from ansys.geometry.core.misc.checks import check_pint_unit_compatibility, check_type
88
from ansys.geometry.core.typing import Real
99

1010
UNITS = UnitRegistry()
1111
"""Unit manager."""
1212

13+
# This forces pint to set the previous UnitRegistry as the one to be used
14+
set_application_registry(UNITS)
15+
1316

1417
class PhysicalQuantity:
1518
"""Base class to handle units homogeneously throughout PyGeometry.
@@ -63,7 +66,7 @@ def _get_quantity(self, input: Real) -> Quantity:
6366
~pint.Quantity
6467
The physical quantity the number represents.
6568
"""
66-
return (input * self.base_unit).to(self.unit)
69+
return Quantity(input, units=self.base_unit).to(self.unit)
6770

6871
def _base_units_magnitude(self, input: Quantity) -> Real:
6972
"""Returns input's :class:`pint.Quantity` magnitude
@@ -81,4 +84,4 @@ def _base_units_magnitude(self, input: Quantity) -> Real:
8184
"""
8285
check_type(input, Quantity)
8386
check_pint_unit_compatibility(input.units, self._base_unit)
84-
return input.m_as(self._base_unit)
87+
return input.to_base_units().m

0 commit comments

Comments
 (0)