-
Notifications
You must be signed in to change notification settings - Fork 63
XY transposable plots V3 part 1: Geometry and Structure (issue #1072) #2687
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Conversation
@@ -23,6 +23,7 @@ | |||
SpatialDataArray, | |||
) | |||
from tidy3d.components.types import ArrayLike, Ax, Axis, Bound | |||
from tidy3d.components.utils import pop_axis_and_swap |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: The modifications to the triangular.py
were probably not necessary for Geometry
or Structure
plotting. But I'd like to include these changes in this PR anyway. The ability to plot triangle meshes is needed by several other PRs which are coming soon, so I want to land them in this PR first.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those subsequent changes are currently bundled into a single PR (PR2544), which is based on top of this PR.
Regarding this PR:
I verified this function is working by running the plot_mesh()
function in the ChargeSolver.ipynb example with transpose=True
, and verifying that the horizontal and vertical axes were swapped. However you will need the other changes in PR2544 for that particular example to work, because it does not invoke this function directly. Perhaps the changes to this file should be moved into that PR, since the other code modified in this PR does not depend on this file.
ax.scatter(shape.x, shape.y, color=plot_params.facecolor) | ||
xcrds, ycrds = shape.x, shape.y | ||
if transpose: # shape.x and shape.y might be infinite, so shape_swap_xy(shape) won't | ||
xcrds, ycrds = ycrds, xcrds # work. Instead we must swap coordinates manually. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you're curious why shape_swap_xy(shape)
doesn't work when the coordinates are infinite, it's because it multiplies the coordinates by a matrix that swaps the X and Y axes. (The shapely
library has a function to apply transformations to coordinates. I used it to implement shape_swap_xy()
.) Unfortunately, if any of the coordinates are inf
, the resulting coordinates are nan
after matrix multiplication.
log.warning( | ||
f"UNTESTED! The `{prefix}{func_name}()` function has not yet been tested with `{arg}={val}`.", | ||
log_once=True, | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The warn_untested_argument()
function is not used in this PR. I am not sure if I will need it. If not (after I've submitted all the PRs in this series), I will delete this function. But let's keep it here for now. (I need it to debug the other PRs in the series.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
12 files reviewed, 3 comments
transpose: bool = False, | ||
) -> tuple[Any, Any, Any]: | ||
""" | ||
``unpop_axis_and_swap()`` is identical to ``Geompetry.unpop_axis()``, except that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syntax: Typo: 'Geompetry' should be 'Geometry'
``unpop_axis_and_swap()`` is identical to ``Geompetry.unpop_axis()``, except that | |
``unpop_axis_and_swap()`` is identical to ``Geometry.unpop_axis()``, except that |
@@ -570,9 +571,10 @@ def does_cover(self, bounds: Bound) -> bool: | |||
|
|||
""" Plotting """ | |||
|
|||
@property | |||
def _triangulation_obj(self) -> Triangulation: | |||
def _triangulation_obj(self, transpose: bool = False) -> Triangulation: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: Converting this from a @property
to a method breaks backward compatibility for any code that accessed _triangulation_obj
as a property
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agree, should be added back
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry. That was bad. I should have defined a new function here.
@pytest.mark.parametrize("component", GEO_TYPES) | ||
def test_plot(component): | ||
_ = component.plot(z=0, ax=AX) | ||
@pytest.mark.parametrize("component, transpose", zip(GEO_TYPES, [True, False])) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
logic: The parametrize decorator uses zip(GEO_TYPES, [True, False])
which will only test each geometry type with one transpose value. This should be product(GEO_TYPES, [True, False])
to test all combinations.
@pytest.mark.parametrize("component, transpose", zip(GEO_TYPES, [True, False])) | |
@pytest.mark.parametrize("component, transpose", list(itertools.product(GEO_TYPES, [True, False]))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd suggest simply doubling up on pytest.mark.parameterize
eg.
@pytest.mark.parameterize("component" , ...)
@pytest.mark.parameterize("transpose" , ...)
Diff CoverageDiff: origin/develop...HEAD, staged and unstaged changes
Summary
tidy3d/components/data/unstructured/triangular.py
tidy3d/components/geometry/base.py
tidy3d/components/geometry/utils.py
tidy3d/components/utils.py
|
…t which swaps the horizontal and vertical axes"
4165b47
to
d64d088
Compare
|
||
|
||
@pytest.mark.parametrize("transpose", [True, False]) | ||
def test_pop_axis_and_swap(transpose): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should also check if the swapping is done properly
@@ -263,15 +264,19 @@ def intersections_plane( | |||
origin = self.unpop_axis(position, (0, 0), axis=axis) | |||
normal = self.unpop_axis(1, (0, 0), axis=axis) | |||
to_2D = np.eye(4) | |||
if axis != 2: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
need to understand why this was changed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi Tyler. Please revert this change back to the way it was.
If you're curious, I removed axis != 2
during a time when I was making big modifications to all of the intersections_...()
functions in Tidy3D (enabling them to support transpose=True
). The axis != 2
conditional got in the way of making that change.
Later, I figured out a way to avoid making any changes to any of the intersections_...()
functions. (This was a big simplification.) I tried to revert all of the older changes I made to these functions, but I guess I forgot to undo this change. Sorry about that. Please change it back.
(If I remember correctly, the next two lines have no effect if axis==2
. That's why Lucas added the if axis != 2
there when he wrote the original code. Before I made this change, I discussed it with him over slack to confirm I could safely remove the if axis != 2
, and he said yes. Either way, feel free to change it back.)
@@ -1567,7 +1610,6 @@ def intersections_tilted_plane( | |||
For more details refer to | |||
`Shapely's Documentation <https://shapely.readthedocs.io/en/stable/project.html>`_. | |||
""" | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will remove newline changes
from tidy3d.log import log | ||
|
||
|
||
def pop_axis_and_swap( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would rather this live in the same place as pop_axis_and_swap
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean pop_axis()
?
That was my plan originally. pop_axis_and_swap()
was originally defined in components/geometry/base.py
(immediately after pop_axis()
). (And it was originally a wrapper function that extended the functionality of pop_axis()
.)
But I discovered later that there are plotting functions in plugins/
which also need to support transpose=True
. (Example here.) That's why I moved those functions out of the components/geometry/
folder (paving the way for changes introduced later in PR2544).
Either way, I agree that it's better for them to be defined in the same file, and better if pop_axis_and_swap()
wraps/extends pop_axis()
. Feel free to move these functions back (or alternatively, move the pop_axis()
and unpop_axis()
functions here).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no changes needed
from tidy3d.log import log | ||
|
||
|
||
def pop_axis_and_swap( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
these functions should wrap pop_axis
and unpop_axis
to reuse code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comment above.
return tuple(coords) | ||
|
||
|
||
def shape_swap_xy(shape: Shapely) -> Shapely: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can go into Geometry too
return shape_new | ||
|
||
|
||
def warn_untested_argument(cls_name: Optional[str], func_name: str, arg: str, val: str): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
will remove
transpose
argument to all plots forGeometry
objects andStructure
objects.Scene.plot()
.)Test instructions
To test this PR, follow the instructions in this file (after unzipping):
tidy3d_issue1072_v3_part1_geom.ipynb.zip
Notes
Geometry
andStructure
objects. However they will soon be needed in subsequent PRs in the coming days.Greptile Summary
This PR implements the
transpose
parameter for geometry and structure plotting methods as part of a larger effort to add XY transposable plots functionality (issue #1072). The changes allow users to swap horizontal and vertical axes in 2D plots by settingtranspose=True
, overriding the default ascending axis order.The implementation introduces several key components:
New utility functions in
tidy3d/components/utils.py
:pop_axis_and_swap()
andunpop_axis_and_swap()
extend existing geometry axis manipulation with transpose capabilityshape_swap_xy()
handles coordinate swapping for Shapely objects using affine transformationswarn_untested_argument()
provides standardized warnings for untested argument combinationsCore geometry plotting updates in
tidy3d/components/geometry/base.py
:transpose
parameter (defaulting toFalse
)self.pop_axis()
calls withpop_axis_and_swap()
for coordinate handlingExtended support across geometry types:
Structure plotting integration:
AbstractStructure.plot()
method updated to accept and forward the transpose parameter to underlying geometry plottingComprehensive test coverage:
The changes maintain full backward compatibility and follow established patterns in the codebase. The implementation is systematic and ensures consistent behavior across all geometry and structure plotting methods.
Confidence score: 4/5
• This PR introduces well-structured functionality with comprehensive test coverage, but has a few minor issues that should be addressed.
• The score reflects one potential test reproducibility issue with random coordinate generation and a minor typo in documentation, but the core transpose functionality appears solid and well-implemented.
• Files needing more attention:
tests/test_components/test_utils.py
(random test data),tidy3d/components/utils.py
(docstring typo)