Skip to content

Commit 62feb7e

Browse files
author
James Nightingale
authored
Merge pull request #519 from PyAutoLabs/feature/numpy-deflections-p3
perf: rotation-matrix grid transform + one Grid2D per VectorYX2D (numpy deflections phase 3)
2 parents c9f67e7 + 755e43d commit 62feb7e

2 files changed

Lines changed: 23 additions & 10 deletions

File tree

‎autoarray/geometry/geometry_util.py‎

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -519,15 +519,20 @@ def transform_grid_2d_to_reference_frame(
519519

520520
shifted_grid_2d = grid_2d - xp.array(centre)
521521

522-
radius = xp.sqrt(xp.sum(xp.square(shifted_grid_2d), axis=1))
523-
theta_coordinate_to_profile = xp.arctan2(
524-
shifted_grid_2d[:, 0], shifted_grid_2d[:, 1]
525-
) - xp.radians(angle)
522+
# A clockwise rotation by `angle` is the rotation matrix applied to the shifted (y, x)
523+
# coordinates, built from one scalar cos / sin of the angle. This is algebraically
524+
# identical to the polar form (r, theta - angle) -> (r sin, r cos) but costs two
525+
# multiply-adds per coordinate instead of a per-pixel sqrt, arctan2, sin and cos.
526+
cos_angle = xp.cos(xp.radians(angle))
527+
sin_angle = xp.sin(xp.radians(angle))
528+
529+
y = shifted_grid_2d[:, 0]
530+
x = shifted_grid_2d[:, 1]
526531

527532
return xp.vstack(
528533
[
529-
radius * xp.sin(theta_coordinate_to_profile),
530-
radius * xp.cos(theta_coordinate_to_profile),
534+
y * cos_angle - x * sin_angle,
535+
x * cos_angle + y * sin_angle,
531536
]
532537
).T
533538

‎autoarray/structures/vectors/uniform.py‎

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,11 +140,19 @@ def __init__(
140140
grid_2d=values, mask_2d=mask, store_native=store_native
141141
)
142142

143-
grid = grid_2d_util.convert_grid_2d(
144-
grid_2d=grid, mask_2d=mask, store_native=store_native
145-
)
143+
# A `Grid2D` already paired with this mask is the grid the vectors are located on: the
144+
# `@to_vector_yx` decorator hands the input grid straight through, so re-converting its
145+
# values and rebuilding a second `Grid2D` on every profile evaluation would only pay the
146+
# constructor twice for the same object.
147+
if isinstance(grid, Grid2D) and grid.mask is mask and not store_native:
148+
self.grid = grid
149+
else:
150+
grid = grid_2d_util.convert_grid_2d(
151+
grid_2d=grid, mask_2d=mask, store_native=store_native
152+
)
153+
154+
self.grid = Grid2D(values=grid, mask=mask)
146155

147-
self.grid = Grid2D(values=grid, mask=mask)
148156
self.mask = mask
149157

150158
super().__init__(values)

0 commit comments

Comments
 (0)