From 59a23c6beb037703c317e0865fd43c955e8318e6 Mon Sep 17 00:00:00 2001 From: Andrew Jewett Date: Tue, 3 Jun 2025 22:35:27 -0400 Subject: [PATCH 1/5] added "transpose" argument to plot_shape() from geometry/base.py --- tidy3d/components/geometry/base.py | 45 ++++++++++++++++++++++++++---- 1 file changed, 39 insertions(+), 6 deletions(-) diff --git a/tidy3d/components/geometry/base.py b/tidy3d/components/geometry/base.py index 4e6e7d2b2..6ebcfafc0 100644 --- a/tidy3d/components/geometry/base.py +++ b/tidy3d/components/geometry/base.py @@ -13,7 +13,7 @@ import xarray as xr try: - from matplotlib import patches + from matplotlib import patches, transforms except ImportError: pass @@ -545,8 +545,18 @@ def plot( ax = Box.add_ax_labels_and_title(ax=ax, x=x, y=y, z=z, plot_length_units=plot_length_units) return ax - def plot_shape(self, shape: Shapely, plot_params: PlotParams, ax: Ax) -> Ax: - """Defines how a shape is plotted on a matplotlib axes.""" + def plot_shape( + self, + shape: Shapely, + plot_params: PlotParams, + ax: Ax, + transpose: bool = False, + ) -> Ax: + """ + Defines how a shape is plotted on a matplotlib axes. + If transpose==True, the horizontal and vertical axes are swapped. + """ + if shape.geom_type in ( "MultiPoint", "MultiLineString", @@ -555,19 +565,42 @@ def plot_shape(self, shape: Shapely, plot_params: PlotParams, ax: Ax) -> Ax: ): for sub_shape in shape.geoms: ax = self.plot_shape(shape=sub_shape, plot_params=plot_params, ax=ax) - return ax _shape = Geometry.evaluate_inf_shape(shape) if _shape.geom_type == "LineString": xs, ys = zip(*_shape.coords) - ax.plot(xs, ys, color=plot_params.facecolor, linewidth=plot_params.linewidth) + if transpose: + ax.plot(ys, xs, color=plot_params.facecolor, linewidth=plot_params.linewidth) + else: + ax.plot(xs, ys, color=plot_params.facecolor, linewidth=plot_params.linewidth) elif _shape.geom_type == "Point": - ax.scatter(shape.x, shape.y, color=plot_params.facecolor) + if transpose: + ax.scatter(shape.y, shape.x, color=plot_params.facecolor) + else: + ax.scatter(shape.x, shape.y, color=plot_params.facecolor) else: patch = polygon_patch(_shape, **plot_params.to_kwargs()) + if transpose: + # Define a transformation which swaps horizal<-->vertical coordinates. + transpose_xy = transforms.Affine2D().set_matrix( + np.array( + [[0, 1, 0], # Swap the X and Y axes. + [1, 0, 0], + [0, 0, 1]] + ) + ) + # Apply this transformation to the coordinates of the patch. + patch.set_transform(transpose_xy + ax.transData) ax.add_artist(patch) + if transpose: + # I also update the axis labels. In practice, this seems to have no + # effect (since they get overwritten elsewhere), but I do it anway. + xlabel_orig = ax.get_xlabel() + ylabel_orig = ax.get_ylabel() + ax.set_xlabel(ylabel_orig) + ax.set_ylabel(xlabel_orig) return ax @staticmethod From 89930f8638f0a448ba34bc3c67adf5cd990527ab Mon Sep 17 00:00:00 2001 From: Andrew Jewett Date: Tue, 3 Jun 2025 23:20:16 -0400 Subject: [PATCH 2/5] added "transpose" argument to plot_structures() from scene.py and plot(), plot_eps() from simulation.py --- tidy3d/components/geometry/base.py | 35 +++++++++++++++++++++++------- tidy3d/components/scene.py | 20 ++++++++++++++--- tidy3d/components/simulation.py | 5 ++++- 3 files changed, 48 insertions(+), 12 deletions(-) diff --git a/tidy3d/components/geometry/base.py b/tidy3d/components/geometry/base.py index 6ebcfafc0..60c764d6b 100644 --- a/tidy3d/components/geometry/base.py +++ b/tidy3d/components/geometry/base.py @@ -485,6 +485,29 @@ def _update_from_bounds(self, bounds: tuple[float, float], axis: Axis) -> Geomet raise NotImplementedError( "'_update_from_bounds' is not compatible with this geometry class." ) + + @staticmethod + def transpose_axis_info( + ax: Ax = None, + swap_axis_labels: bool = False, + swap_axis_limits: bool = False, + ) -> None: + """Swaps matplotlib axis-labels and limits, but does not alter geometry (contents) of a plot.""" + if swap_axis_labels: + # Swap the axis labels? + xlabel_orig = ax.get_xlabel() + ylabel_orig = ax.get_ylabel() + ax.set_xlabel(ylabel_orig) + ax.set_ylabel(xlabel_orig) + if swap_axis_limits: + # Do we want to swap the graph boundaries/limits? + xlim_orig = ax.get_xlim() + ylim_orig = ax.get_ylim() + ax.set_xlim(ylim_orig) + ax.set_ylim(xlim_orig) + # Now recalculate automatic tick locations based on new limits: + ax.relim() + ax.autoscale_view() @equal_aspect @add_ax_if_none @@ -554,7 +577,7 @@ def plot_shape( ) -> Ax: """ Defines how a shape is plotted on a matplotlib axes. - If transpose==True, the horizontal and vertical axes are swapped. + If transpose==True, the horizontal and vertical coordinates are swapped. """ if shape.geom_type in ( @@ -594,13 +617,9 @@ def plot_shape( # Apply this transformation to the coordinates of the patch. patch.set_transform(transpose_xy + ax.transData) ax.add_artist(patch) - if transpose: - # I also update the axis labels. In practice, this seems to have no - # effect (since they get overwritten elsewhere), but I do it anway. - xlabel_orig = ax.get_xlabel() - ylabel_orig = ax.get_ylabel() - ax.set_xlabel(ylabel_orig) - ax.set_ylabel(xlabel_orig) + # I transpose==True, I also update the axis labels. In practice, this seems to + # have no effect (since they get overwritten elsewhere), but I do it anway. + Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) return ax @staticmethod diff --git a/tidy3d/components/scene.py b/tidy3d/components/scene.py index 17752120a..26878ccf6 100644 --- a/tidy3d/components/scene.py +++ b/tidy3d/components/scene.py @@ -34,7 +34,7 @@ TriangularGridDataset, UnstructuredGridDataset, ) -from .geometry.base import Box, ClipOperation, GeometryGroup +from .geometry.base import Box, ClipOperation, GeometryGroup, Geometry from .geometry.utils import flatten_groups, merging_geometries_on_plane, traverse_geometries from .grid.grid import Coords, Grid from .material.multi_physics import MultiPhysicsMedium @@ -396,6 +396,7 @@ def plot( hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, fill_structures: bool = True, + transpose: bool = False, **patch_kwargs, ) -> Ax: """Plot each of scene's components on a plane defined by one nonzero x,y,z coordinate. @@ -425,7 +426,16 @@ def plot( hlim, vlim = Scene._get_plot_lims(bounds=self.bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim) - ax = self.plot_structures(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, fill=fill_structures) + ax = self.plot_structures( + ax=ax, + x=x, + y=y, + z=z, + hlim=hlim, + vlim=vlim, + fill=fill_structures, + transpose=transpose, + ) ax = self._set_plot_bounds(bounds=self.bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) return ax @@ -440,6 +450,7 @@ def plot_structures( hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, fill: bool = True, + transpose: bool = False, ) -> Ax: """Plot each of scene's structures on a plane defined by one nonzero x,y,z coordinate. @@ -478,6 +489,7 @@ def plot_structures( shape=shape, ax=ax, fill=fill, + transpose=transpose, ) # clean up the axis display @@ -488,6 +500,7 @@ def plot_structures( ax = Box.add_ax_labels_and_title( ax=ax, x=x, y=y, z=z, plot_length_units=self.plot_length_units ) + Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) # Swap axis labels if needed return ax def _plot_shape_structure( @@ -497,6 +510,7 @@ def _plot_shape_structure( shape: Shapely, ax: Ax, fill: bool = True, + transpose: bool = False, ) -> Ax: """Plot a structure's cross section shape for a given medium.""" plot_params_struct = self._get_structure_plot_params( @@ -504,7 +518,7 @@ def _plot_shape_structure( mat_index=mat_index, fill=fill, ) - ax = self.box.plot_shape(shape=shape, plot_params=plot_params_struct, ax=ax) + ax = self.box.plot_shape(shape=shape, plot_params=plot_params_struct, ax=ax, transpose=transpose) return ax def _get_structure_plot_params( diff --git a/tidy3d/components/simulation.py b/tidy3d/components/simulation.py index 3bf9f477c..903fa83ab 100644 --- a/tidy3d/components/simulation.py +++ b/tidy3d/components/simulation.py @@ -451,6 +451,7 @@ def plot( hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, fill_structures: bool = True, + transpose: bool = False, **patch_kwargs, ) -> Ax: """Plot each of simulation's components on a plane defined by one nonzero x,y,z coordinate. @@ -515,7 +516,7 @@ def plot( bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim ) ax = self.plot_boundaries(ax=ax, x=x, y=y, z=z) - + Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) # Swap axis labels if needed return ax @equal_aspect @@ -930,6 +931,7 @@ def plot_grid( vlim: Optional[tuple[float, float]] = None, override_structures_alpha: float = 1, snapping_points_alpha: float = 1, + transpose: bool = False, **kwargs, ) -> Ax: """Plot the cell boundaries as lines on a plane defined by one nonzero x,y,z coordinate. @@ -1064,6 +1066,7 @@ def plot_grid( ax = Box.add_ax_labels_and_title( ax=ax, x=x, y=y, z=z, plot_length_units=self.plot_length_units ) + Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) # Swap axis labels if needed return ax @equal_aspect From f80806abad872d27940c8df0ed4a6c9309fb5e94 Mon Sep 17 00:00:00 2001 From: Andrew Jewett Date: Wed, 4 Jun 2025 00:22:12 -0400 Subject: [PATCH 3/5] bug fix --- tidy3d/components/geometry/base.py | 4 ++-- tidy3d/components/scene.py | 2 -- tidy3d/components/simulation.py | 1 + 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tidy3d/components/geometry/base.py b/tidy3d/components/geometry/base.py index 60c764d6b..c6cb33c93 100644 --- a/tidy3d/components/geometry/base.py +++ b/tidy3d/components/geometry/base.py @@ -579,7 +579,6 @@ def plot_shape( Defines how a shape is plotted on a matplotlib axes. If transpose==True, the horizontal and vertical coordinates are swapped. """ - if shape.geom_type in ( "MultiPoint", "MultiLineString", @@ -607,7 +606,8 @@ def plot_shape( patch = polygon_patch(_shape, **plot_params.to_kwargs()) if transpose: # Define a transformation which swaps horizal<-->vertical coordinates. - transpose_xy = transforms.Affine2D().set_matrix( + transpose_xy = transforms.Affine2D() + transpose_xy.set_matrix( np.array( [[0, 1, 0], # Swap the X and Y axes. [1, 0, 0], diff --git a/tidy3d/components/scene.py b/tidy3d/components/scene.py index 26878ccf6..fa07c009a 100644 --- a/tidy3d/components/scene.py +++ b/tidy3d/components/scene.py @@ -423,9 +423,7 @@ def plot( matplotlib.axes._subplots.Axes The supplied or created matplotlib axes. """ - hlim, vlim = Scene._get_plot_lims(bounds=self.bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim) - ax = self.plot_structures( ax=ax, x=x, diff --git a/tidy3d/components/simulation.py b/tidy3d/components/simulation.py index 903fa83ab..c3c8e104f 100644 --- a/tidy3d/components/simulation.py +++ b/tidy3d/components/simulation.py @@ -503,6 +503,7 @@ def plot( hlim=hlim, vlim=vlim, fill_structures=fill_structures, + transpose=transpose, ) ax = self.plot_sources(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=source_alpha) From 2596ff2a8080f69816ad2184b324628ad34085dc Mon Sep 17 00:00:00 2001 From: Andrew Jewett Date: Wed, 4 Jun 2025 11:14:11 -0400 Subject: [PATCH 4/5] corrected a (possible) oversight in plot_grid(). Needs testing --- tidy3d/components/simulation.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tidy3d/components/simulation.py b/tidy3d/components/simulation.py index c3c8e104f..abad2920f 100644 --- a/tidy3d/components/simulation.py +++ b/tidy3d/components/simulation.py @@ -1033,6 +1033,8 @@ def plot_grid( ): for point in points: _, (x_point, y_point) = Geometry.pop_axis(point, axis=axis) + if transpose: + x_point, y_point = y_point, x_point # swap X and Y if x_point is None and y_point is None: continue if x_point is None: From 9694eaa4f0dcfc2adab5d04eafaf38d93f9f5d37 Mon Sep 17 00:00:00 2001 From: Andrew Jewett Date: Wed, 4 Jun 2025 18:30:20 -0400 Subject: [PATCH 5/5] attempting to add the "transpose" argument to these functions: plot_sources(), plot_monitors(), plot_lumped_elements(), plot_symmetries(), plot_pml(), _get_plot_bounds(), _set_plot_bounds(), plot_arrow(). This code runs without crashing, but there are quite a few weird quirks (things that aren't quite working correctly). --- tidy3d/components/base_sim/simulation.py | 55 +++++++++++----- tidy3d/components/geometry/base.py | 17 ++++- tidy3d/components/monitor.py | 4 +- tidy3d/components/scene.py | 49 ++++++++++---- tidy3d/components/simulation.py | 66 +++++++++++++------ tidy3d/components/source/base.py | 5 +- tidy3d/components/structure.py | 5 +- .../components/tcad/simulation/heat_charge.py | 18 +++-- 8 files changed, 157 insertions(+), 62 deletions(-) diff --git a/tidy3d/components/base_sim/simulation.py b/tidy3d/components/base_sim/simulation.py index f6f0e8c90..38bf795f1 100644 --- a/tidy3d/components/base_sim/simulation.py +++ b/tidy3d/components/base_sim/simulation.py @@ -9,7 +9,7 @@ import pydantic.v1 as pd from tidy3d.components.base import cached_property, skip_if_fields_missing -from tidy3d.components.geometry.base import Box +from tidy3d.components.geometry.base import Box, Geometry from tidy3d.components.medium import Medium, MediumType3D from tidy3d.components.scene import Scene from tidy3d.components.structure import Structure @@ -251,6 +251,7 @@ def plot( hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, fill_structures: bool = True, + transpose: bool = False, **patch_kwargs, ) -> Ax: """Plot each of simulation's components on a plane defined by one nonzero x,y,z coordinate. @@ -275,6 +276,9 @@ def plot( The z range if plotting on xz or yz planes, y plane if plotting on xy plane. fill_structures : bool = True Whether to fill structures with color or just draw outlines. + transpose: bool = False + Optional: Swap the horizontal and vertical axes. + Returns ------- matplotlib.axes._subplots.Axes @@ -282,25 +286,25 @@ def plot( """ hlim, vlim = Scene._get_plot_lims( - bounds=self.simulation_bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose ) ax = self.scene.plot_structures( - ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, fill=fill_structures + ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, fill=fill_structures, transpose=transpose ) - ax = self.plot_sources(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=source_alpha) - ax = self.plot_monitors(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=monitor_alpha) + ax = self.plot_sources(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=source_alpha, transpose=transpose) + ax = self.plot_monitors(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=monitor_alpha, transpose=transpose) ax = Scene._set_plot_bounds( - bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose ) - ax = self.plot_boundaries(ax=ax, x=x, y=y, z=z) - ax = self.plot_symmetries(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) + ax = self.plot_boundaries(ax=ax, x=x, y=y, z=z, transpose=transpose) + ax = self.plot_symmetries(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose) # Add the default axis labels, tick labels, and title ax = Box.add_ax_labels_and_title( ax=ax, x=x, y=y, z=z, plot_length_units=self.plot_length_units ) - + Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) # Swap axis labels if NEEDED return ax @equal_aspect @@ -314,6 +318,7 @@ def plot_sources( vlim: Optional[tuple[float, float]] = None, alpha: Optional[float] = None, ax: Ax = None, + transpose: bool = False, ) -> Ax: """Plot each of simulation's sources on a plane defined by one nonzero x,y,z coordinate. @@ -333,6 +338,8 @@ def plot_sources( Opacity of the sources, If ``None`` uses Tidy3d default. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. + transpose: bool = False + Optional: Swap the horizontal and vertical axes. Returns ------- @@ -343,12 +350,13 @@ def plot_sources( for source in self.sources: ax = source.plot(x=x, y=y, z=z, alpha=alpha, ax=ax, sim_bounds=bounds) ax = Scene._set_plot_bounds( - bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose ) # Add the default axis labels, tick labels, and title ax = Box.add_ax_labels_and_title( ax=ax, x=x, y=y, z=z, plot_length_units=self.plot_length_units ) + Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) # Swap axis labels if needed return ax @equal_aspect @@ -362,6 +370,7 @@ def plot_monitors( vlim: Optional[tuple[float, float]] = None, alpha: Optional[float] = None, ax: Ax = None, + transpose: bool = False, ) -> Ax: """Plot each of simulation's monitors on a plane defined by one nonzero x,y,z coordinate. @@ -381,6 +390,8 @@ def plot_monitors( Opacity of the sources, If ``None`` uses Tidy3d default. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. + transpose: bool = False + Optional: Swap the horizontal and vertical axes. Returns ------- @@ -389,14 +400,15 @@ def plot_monitors( """ bounds = self.bounds for monitor in self.monitors: - ax = monitor.plot(x=x, y=y, z=z, alpha=alpha, ax=ax, sim_bounds=bounds) + ax = monitor.plot(x=x, y=y, z=z, alpha=alpha, ax=ax, sim_bounds=bounds, transpose=transpose) ax = Scene._set_plot_bounds( - bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose ) # Add the default axis labels, tick labels, and title ax = Box.add_ax_labels_and_title( ax=ax, x=x, y=y, z=z, plot_length_units=self.plot_length_units ) + Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) # Swap axis labels if needed return ax @equal_aspect @@ -409,6 +421,7 @@ def plot_symmetries( hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, ax: Ax = None, + transpose: bool = False, ) -> Ax: """Plot each of simulation's symmetries on a plane defined by one nonzero x,y,z coordinate. @@ -426,6 +439,8 @@ def plot_symmetries( The z range if plotting on xz or yz planes, y plane if plotting on xy plane. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. + transpose: bool = False + Optional: Swap the horizontal and vertical axes. Returns ------- @@ -442,12 +457,13 @@ def plot_symmetries( plot_params = self._make_symmetry_plot_params(sym_value=sym_value) ax = sym_box.plot(x=x, y=y, z=z, ax=ax, **plot_params.to_kwargs()) ax = Scene._set_plot_bounds( - bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose ) # Add the default axis labels, tick labels, and title ax = Box.add_ax_labels_and_title( ax=ax, x=x, y=y, z=z, plot_length_units=self.plot_length_units ) + Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) # Swap axis labels if needed return ax def _make_symmetry_plot_params(self, sym_value: Symmetry) -> PlotParams: @@ -519,6 +535,7 @@ def plot_structures( hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, fill: bool = True, + # transpose: bool = False, CONTINUEHERE ) -> Ax: """Plot each of simulation's structures on a plane defined by one nonzero x,y,z coordinate. @@ -545,11 +562,11 @@ def plot_structures( """ hlim_new, vlim_new = Scene._get_plot_lims( - bounds=self.simulation_bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim #, transpose=transpose CONTINUEHERE ) return self.scene.plot_structures( - x=x, y=y, z=z, ax=ax, hlim=hlim_new, vlim=vlim_new, fill=fill + x=x, y=y, z=z, ax=ax, hlim=hlim_new, vlim=vlim_new, fill=fill #, transpose=transpose CONTINUEHERE ) @equal_aspect @@ -566,6 +583,7 @@ def plot_structures_eps( ax: Ax = None, hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, + # transpose: bool = False, CONTINUEHERE ) -> Ax: """Plot each of simulation's structures on a plane defined by one nonzero x,y,z coordinate. The permittivity is plotted in grayscale based on its value at the specified frequency. @@ -603,7 +621,7 @@ def plot_structures_eps( """ hlim, vlim = Scene._get_plot_lims( - bounds=self.simulation_bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim #, transpose=transpose CONTINUEHERE ) return self.scene.plot_structures_eps( @@ -617,6 +635,7 @@ def plot_structures_eps( hlim=hlim, vlim=vlim, reverse=reverse, + # transpose=transpose CONTINUEHERE ) @equal_aspect @@ -632,6 +651,7 @@ def plot_structures_heat_conductivity( ax: Ax = None, hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, + # transpose: bool, CONTINUEHERE ) -> Ax: """Plot each of simulation's structures on a plane defined by one nonzero x,y,z coordinate. The permittivity is plotted in grayscale based on its value at the specified frequency. @@ -669,7 +689,7 @@ def plot_structures_heat_conductivity( """ hlim, vlim = Scene._get_plot_lims( - bounds=self.simulation_bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim #, transpose=transpose CONTINUEHERE ) return self.scene.plot_structures_heat_conductivity( @@ -682,6 +702,7 @@ def plot_structures_heat_conductivity( hlim=hlim, vlim=vlim, reverse=reverse, + # transpose=transpose CONTINUEHERE ) @classmethod diff --git a/tidy3d/components/geometry/base.py b/tidy3d/components/geometry/base.py index c6cb33c93..8ef4a1149 100644 --- a/tidy3d/components/geometry/base.py +++ b/tidy3d/components/geometry/base.py @@ -519,6 +519,7 @@ def plot( ax: Ax = None, plot_length_units: LengthUnit = None, viz_spec: VisualizationSpec = None, + transpose: bool = False, **patch_kwargs, ) -> Ax: """Plot geometry cross section at single (x,y,z) coordinate. @@ -537,6 +538,8 @@ def plot( Specify units to use for axis labels, tick labels, and the title. viz_spec : VisualizationSpec = None Plotting parameters associated with a medium to use instead of defaults. + transpose: bool = False + Optional: Swap the horizontal and vertical axes. **patch_kwargs Optional keyword arguments passed to the matplotlib patch plotting of structure. For details on accepted values, refer to @@ -559,13 +562,14 @@ def plot( # for each intersection, plot the shape for shape in shapes_intersect: - ax = self.plot_shape(shape, plot_params=plot_params, ax=ax) + ax = self.plot_shape(shape, plot_params=plot_params, ax=ax, transpose=transpose) # clean up the axis display ax = self.add_ax_lims(axis=axis, ax=ax) ax.set_aspect("equal") # Add the default axis labels, tick labels, and title ax = Box.add_ax_labels_and_title(ax=ax, x=x, y=y, z=z, plot_length_units=plot_length_units) + Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) # Swap axis labels if needed return ax def plot_shape( @@ -617,8 +621,8 @@ def plot_shape( # Apply this transformation to the coordinates of the patch. patch.set_transform(transpose_xy + ax.transData) ax.add_artist(patch) - # I transpose==True, I also update the axis labels. In practice, this seems to - # have no effect (since they get overwritten elsewhere), but I do it anway. + # If transpose==True, I also update the axis labels (if any). In practice, this + # seems to have no effect (since they get overwritten elsewhere), but I do it anway. Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) return ax @@ -2252,6 +2256,7 @@ def _plot_arrow( both_dirs: bool = False, ax: Ax = None, arrow_base: Coordinate = None, + transpose: bool = False, ) -> Ax: """Adds an arrow to the axis if with options if certain conditions met. @@ -2277,6 +2282,8 @@ def _plot_arrow( If True, plots an arrow pointing in direction and one in -direction. arrow_base : :class:`.Coordinate` = None Custom base of the arrow. Uses the geometry's center if not provided. + transpose: bool = False + Optional: Swap the horizontal and vertical axes. Returns ------- @@ -2312,6 +2319,10 @@ def _plot_arrow( v_x = (xmax - xmin) / 10 v_y = (ymax - ymin) / 10 + if transpose: + x0, y0 = y0, x0 + v_x, v_y = v_y, v_x + directions = (1.0, -1.0) if both_dirs else (1.0,) for sign in directions: arrow = patches.FancyArrowPatch( diff --git a/tidy3d/components/monitor.py b/tidy3d/components/monitor.py index 1ad5cef33..31d9d9730 100644 --- a/tidy3d/components/monitor.py +++ b/tidy3d/components/monitor.py @@ -358,11 +358,12 @@ def plot( y: Optional[float] = None, z: Optional[float] = None, ax: Ax = None, + transpose: bool = False, **patch_kwargs, ) -> Ax: """Plot this monitor.""" # call the monitor.plot() function first - ax = super().plot(x=x, y=y, z=z, ax=ax, **patch_kwargs) + ax = super().plot(x=x, y=y, z=z, ax=ax, transpose=transpose, **patch_kwargs) kwargs_alpha = patch_kwargs.get("alpha") arrow_alpha = ARROW_ALPHA if kwargs_alpha is None else kwargs_alpha @@ -384,6 +385,7 @@ def plot( color=ARROW_COLOR_MONITOR, alpha=arrow_alpha, both_dirs=True, + transpose=transpose, ) return ax diff --git a/tidy3d/components/scene.py b/tidy3d/components/scene.py index fa07c009a..cf9744904 100644 --- a/tidy3d/components/scene.py +++ b/tidy3d/components/scene.py @@ -366,11 +366,17 @@ def _get_plot_lims( z: Optional[float] = None, hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, + transpose: bool = False, ) -> tuple[tuple[float, float], tuple[float, float]]: # if no hlim and/or vlim given, the bounds will then be the usual pml bounds axis, _ = Box.parse_xyz_kwargs(x=x, y=y, z=z) _, (hmin, vmin) = Box.pop_axis(bounds[0], axis=axis) _, (hmax, vmax) = Box.pop_axis(bounds[1], axis=axis) + if transpose: + # Swap the default horizontal and vertical plot limits. (Note: If + # the user manually specified hlim, vlim, then we do not swap them.) + hmin, vmin = vmin, hmin + hmax, vmax = vmax, hmax # account for unordered limits if hlim is None: @@ -417,6 +423,8 @@ def plot( The z range if plotting on xz or yz planes, y plane if plotting on xy plane. fill_structures : bool = True Whether to fill structures with color or just draw outlines. + transpose: bool = False + Optional: Swap the horizontal and vertical axes. Returns ------- @@ -434,7 +442,7 @@ def plot( fill=fill_structures, transpose=transpose, ) - ax = self._set_plot_bounds(bounds=self.bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) + ax = self._set_plot_bounds(bounds=self.bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose) return ax @equal_aspect @@ -493,7 +501,7 @@ def plot_structures( # clean up the axis display axis, _ = Box.parse_xyz_kwargs(x=x, y=y, z=z) ax = self.box.add_ax_lims(axis=axis, ax=ax) - ax = self._set_plot_bounds(bounds=self.bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) + ax = self._set_plot_bounds(bounds=self.bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose) # Add the default axis labels, tick labels, and title ax = Box.add_ax_labels_and_title( ax=ax, x=x, y=y, z=z, plot_length_units=self.plot_length_units @@ -592,6 +600,7 @@ def _set_plot_bounds( z: Optional[float] = None, hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, + transpose: bool = False, ) -> Ax: """Sets the xy limits of the scene at a plane, useful after plotting. @@ -609,13 +618,16 @@ def _set_plot_bounds( The x range if plotting on xy or xz planes, y range if plotting on yz plane. vlim : Tuple[float, float] = None The z range if plotting on xz or yz planes, y plane if plotting on xy plane. + transpose: bool = False + Optional: Swap the default horizontal and vertical axes (when hlim==None or vlim==None). + Returns ------- matplotlib.axes._subplots.Axes The axes after setting the boundaries. """ - hlim, vlim = Scene._get_plot_lims(bounds=bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim) + hlim, vlim = Scene._get_plot_lims(bounds=bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose) ax.set_xlim(hlim) ax.set_ylim(vlim) return ax @@ -628,6 +640,7 @@ def _get_structures_2dbox( z: Optional[float] = None, hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, + # transpose: bool = False, CONTINUEHERE ) -> list[tuple[Medium, Shapely]]: """Compute list of shapes to plot on 2d box specified by (x_min, x_max), (y_min, y_max). @@ -778,12 +791,12 @@ def plot_eps( The supplied or created matplotlib axes. """ - hlim, vlim = Scene._get_plot_lims(bounds=self.bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim) + hlim, vlim = Scene._get_plot_lims(bounds=self.bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim) #, transpose=transpose CONTINUEHERE ax = self.plot_structures_eps( - freq=freq, cbar=True, alpha=alpha, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim + freq=freq, cbar=True, alpha=alpha, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim #, transpose=transpose CONTINUEHERE ) - ax = self._set_plot_bounds(bounds=self.bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) + ax = self._set_plot_bounds(bounds=self.bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) #, transpose=transpose CONTINUEHERE return ax @equal_aspect @@ -803,6 +816,7 @@ def plot_structures_eps( vlim: Optional[tuple[float, float]] = None, grid: Grid = None, eps_component: Optional[PermittivityComponent] = None, + # transpose: bool = False, CONTINUEHERE ) -> Ax: """Plot each of scene's structures on a plane defined by one nonzero x,y,z coordinate. The permittivity is plotted in grayscale based on its value at the specified frequency. @@ -860,6 +874,7 @@ def plot_structures_eps( grid=grid, property="eps", eps_component=eps_component, + # transpose=transpose, CONTINUEHERE ) @equal_aspect @@ -880,6 +895,7 @@ def plot_structures_property( grid: Grid = None, property: Literal["eps", "doping", "N_a", "N_d"] = "eps", eps_component: Optional[PermittivityComponent] = None, + # transpose: bool = False, CONTINUEHERE ) -> Ax: """Plot each of scene's structures on a plane defined by one nonzero x,y,z coordinate. The permittivity is plotted in grayscale based on its value at the specified frequency. @@ -953,7 +969,7 @@ def plot_structures_property( else: structures = [self.background_structure, *list(structures)] medium_shapes = self._get_structures_2dbox( - structures=structures, x=x, y=y, z=z, hlim=hlim, vlim=vlim + structures=structures, x=x, y=y, z=z, hlim=hlim, vlim=vlim #, transpose=transpose CONTINUEHERE ) property_min, property_max = limits @@ -990,11 +1006,11 @@ def plot_structures_property( reverse=reverse, shape=shape, ax=ax, - property="doping", + property="doping", #, transpose=transpose CONTINUEHERE ) else: self._pcolormesh_shape_doping_box( - x, y, z, alpha, medium, property_min, property_max, shape, ax, property + x, y, z, alpha, medium, property_min, property_max, shape, ax, property #, transpose=transpose CONTINUEHERE ) else: # if the background medium is custom medium, it needs to be rendered separately @@ -1011,7 +1027,7 @@ def plot_structures_property( reverse=reverse, shape=shape, ax=ax, - eps_component=eps_component, + eps_component=eps_component, #, transpose=transpose CONTINUEHERE ) else: # For custom medium, apply pcolormesh clipped by the shape. @@ -1028,7 +1044,7 @@ def plot_structures_property( shape, ax, grid, - eps_component=eps_component, + eps_component=eps_component, #, transpose=transpose CONTINUEHERE ) if cbar: @@ -1048,11 +1064,12 @@ def plot_structures_property( # clean up the axis display axis, _ = Box.parse_xyz_kwargs(x=x, y=y, z=z) ax = self.box.add_ax_lims(axis=axis, ax=ax) - ax = self._set_plot_bounds(bounds=self.bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) + ax = self._set_plot_bounds(bounds=self.bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) #, transpose=transpose CONTINUEHERE # Add the default axis labels, tick labels, and title ax = Box.add_ax_labels_and_title( ax=ax, x=x, y=y, z=z, plot_length_units=self.plot_length_units ) + # CONTINUEHERE Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) # Swap axis labels if NEEDED return ax @staticmethod @@ -1125,6 +1142,7 @@ def _pcolormesh_shape_custom_medium_structure_eps( ax: Ax, grid: Grid, eps_component: Optional[PermittivityComponent] = None, + # transpose: bool = False, CONTINUEHERE ): """ Plot shape made of custom medium with ``pcolormesh``. @@ -1260,6 +1278,7 @@ def _pcolormesh_shape_custom_medium_structure_eps( eps_shape = eps_min + eps_max - eps_shape # pcolormesh + # CONTINUEHERE: Do I need to swap xp<-->yp if transpose==True? plane_xp, plane_yp = np.meshgrid(plane_coord[0], plane_coord[1], indexing="ij") ax.pcolormesh( plane_xp, @@ -1351,6 +1370,7 @@ def plot_heat_charge_property( ax: Ax = None, hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, + # transpose: bool = False, CONTINUEHERE ) -> Ax: """Plot each of scebe's components on a plane defined by one nonzero x,y,z coordinate. The thermal conductivity is plotted in grayscale based on its value. @@ -1405,6 +1425,7 @@ def plot_structures_heat_conductivity( ax: Ax = None, hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, + # transpose: bool = False, CONTINUEHERE ) -> Ax: """Plot each of scene's structures on a plane defined by one nonzero x,y,z coordinate. The thermal conductivity is plotted in grayscale based on its value. @@ -1455,6 +1476,7 @@ def plot_structures_heat_conductivity( ax=ax, hlim=hlim, vlim=vlim, + # transpose=transpose, CONTINUEHERE ) @equal_aspect @@ -1471,6 +1493,7 @@ def plot_structures_heat_charge_property( ax: Ax = None, hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, + # transpose: bool = False, CONTINUEHERE ) -> Ax: """Plot each of scene's structures on a plane defined by one nonzero x,y,z coordinate. The thermal conductivity is plotted in grayscale based on its value. @@ -1536,6 +1559,7 @@ def plot_structures_heat_charge_property( shape=shape, ax=ax, property=property, + # transpose=transpose, CONTINUEHERE ) if cbar: @@ -1560,6 +1584,7 @@ def plot_structures_heat_charge_property( ax = Box.add_ax_labels_and_title( ax=ax, x=x, y=y, z=z, plot_length_units=self.plot_length_units ) + # CONTINUEHERE: Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) # Swap axis labels if needed return ax def heat_charge_property_bounds(self, property) -> tuple[float, float]: diff --git a/tidy3d/components/simulation.py b/tidy3d/components/simulation.py index abad2920f..8ccd75d1f 100644 --- a/tidy3d/components/simulation.py +++ b/tidy3d/components/simulation.py @@ -478,6 +478,8 @@ def plot( The x range if plotting on xy or xz planes, y range if plotting on yz plane. vlim : Tuple[float, float] = None The z range if plotting on xz or yz planes, y plane if plotting on xy plane. + transpose: bool = False + Optional: Swap the horizontal and vertical axes. Returns ------- @@ -492,7 +494,7 @@ def plot( """ hlim, vlim = Scene._get_plot_lims( - bounds=self.simulation_bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose, ) ax = self.scene.plot( @@ -506,17 +508,17 @@ def plot( transpose=transpose, ) - ax = self.plot_sources(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=source_alpha) - ax = self.plot_monitors(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=monitor_alpha) + ax = self.plot_sources(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=source_alpha, transpose=transpose) + ax = self.plot_monitors(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=monitor_alpha, transpose=transpose) ax = self.plot_lumped_elements( - ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=lumped_element_alpha + ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=lumped_element_alpha, transpose=transpose ) - ax = self.plot_symmetries(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) - ax = self.plot_pml(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) + ax = self.plot_symmetries(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose) + ax = self.plot_pml(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose) ax = Scene._set_plot_bounds( - bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose ) - ax = self.plot_boundaries(ax=ax, x=x, y=y, z=z) + ax = self.plot_boundaries(ax=ax, x=x, y=y, z=z, transpose=transpose) Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) # Swap axis labels if needed return ax @@ -537,6 +539,7 @@ def plot_eps( ax: Ax = None, eps_component: Optional[PermittivityComponent] = None, eps_lim: tuple[Union[float, None], Union[float, None]] = (None, None), + # tuple: bool = False, CONTINUEHERE ) -> Ax: """Plot each of simulation's components on a plane defined by one nonzero x,y,z coordinate. The permittivity is plotted in grayscale based on its value at the specified frequency. @@ -598,7 +601,7 @@ def plot_eps( ) hlim, vlim = Scene._get_plot_lims( - bounds=self.simulation_bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim #, transpose=transpose CONTINUEHERE ) ax = self.plot_structures_eps( @@ -613,18 +616,19 @@ def plot_eps( vlim=vlim, eps_component=eps_component, eps_lim=eps_lim, + #, transpose=transpose CONTINUEHERE ) - ax = self.plot_sources(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=source_alpha) - ax = self.plot_monitors(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=monitor_alpha) + ax = self.plot_sources(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=source_alpha) #, transpose=transpose CONTINUEHERE + ax = self.plot_monitors(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=monitor_alpha) #, transpose=transpose CONTINUEHERE ax = self.plot_lumped_elements( - ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=lumped_element_alpha + ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, alpha=lumped_element_alpha #, transpose=transpose CONTINUEHERE ) - ax = self.plot_symmetries(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) - ax = self.plot_pml(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) + ax = self.plot_symmetries(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) #, transpose=transpose CONTINUEHERE + ax = self.plot_pml(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) #, transpose=transpose CONTINUEHERE ax = Scene._set_plot_bounds( - bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim #, transpose=transpose CONTINUEHERE ) - ax = self.plot_boundaries(ax=ax, x=x, y=y, z=z) + ax = self.plot_boundaries(ax=ax, x=x, y=y, z=z) #, transpose=transpose CONTINUEHERE return ax @equal_aspect @@ -643,6 +647,7 @@ def plot_structures_eps( vlim: Optional[tuple[float, float]] = None, eps_component: Optional[PermittivityComponent] = None, eps_lim: tuple[Union[float, None], Union[float, None]] = (None, None), + # transpose: bool = False, CONTINUEHERE ) -> Ax: """Plot each of simulation's structures on a plane defined by one nonzero x,y,z coordinate. The permittivity is plotted in grayscale based on its value at the specified frequency. @@ -716,6 +721,7 @@ def plot_structures_eps( reverse=reverse, eps_component=eps_component, eps_lim=eps_lim, + # transpose=transpose, CONTINUEHERE ) @equal_aspect @@ -728,6 +734,7 @@ def plot_pml( hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, ax: Ax = None, + transpose: bool = False, ) -> Ax: """Plot each of simulation's absorbing boundaries on a plane defined by one nonzero x,y,z coordinate. @@ -746,6 +753,8 @@ def plot_pml( The z range if plotting on xz or yz planes, y plane if plotting on xy plane. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. + transpose: bool = False + Optional: Swap the horizontal and vertical axes. Returns ------- @@ -757,12 +766,13 @@ def plot_pml( for pml_box in pml_boxes: pml_box.plot(x=x, y=y, z=z, ax=ax, **plot_params_pml.to_kwargs()) ax = Scene._set_plot_bounds( - bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose ) # Add the default axis labels, tick labels, and title ax = Box.add_ax_labels_and_title( ax=ax, x=x, y=y, z=z, plot_length_units=self.plot_length_units ) + Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) return ax # candidate for removal in 3.0 @@ -886,6 +896,7 @@ def plot_lumped_elements( vlim: Optional[tuple[float, float]] = None, alpha: Optional[float] = None, ax: Ax = None, + transpose: bool = False, ) -> Ax: """Plot each of simulation's lumped elements on a plane defined by one nonzero x,y,z coordinate. @@ -906,6 +917,8 @@ def plot_lumped_elements( Opacity of the lumped element, If ``None`` uses Tidy3d default. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. + transpose: bool = False + Optional: Swap the horizontal and vertical axes. Returns ------- @@ -915,10 +928,11 @@ def plot_lumped_elements( bounds = self.bounds for element in self.lumped_elements: kwargs = element.plot_params.include_kwargs(alpha=alpha).to_kwargs() - ax = element.to_geometry().plot(x=x, y=y, z=z, ax=ax, sim_bounds=bounds, **kwargs) + ax = element.to_geometry().plot(x=x, y=y, z=z, ax=ax, sim_bounds=bounds, transpose=transpose, **kwargs) ax = Scene._set_plot_bounds( - bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose ) + Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) return ax @add_ax_if_none @@ -955,6 +969,8 @@ def plot_grid( Opacity of the snapping points. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. + transpose: bool = False + Optional: Swap the horizontal and vertical axes. **kwargs Optional keyword arguments passed to the matplotlib ``LineCollection``. For details on accepted values, refer to @@ -1013,6 +1029,8 @@ def plot_grid( xmin, xmax, ymin, ymax = ( self._evaluate_inf(v) for v in (xmin, xmax, ymin, ymax) ) + if transpose: + xmin, xmax, ymin, ymax = ymin, ymax, xmin, xmax rect = mpl.patches.Rectangle( xy=(xmin, ymin), width=(xmax - xmin), @@ -1063,7 +1081,7 @@ def plot_grid( ) ax = Scene._set_plot_bounds( - bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim, transpose=transpose ) # Add the default axis labels, tick labels, and title ax = Box.add_ax_labels_and_title( @@ -1080,6 +1098,7 @@ def plot_boundaries( y: Optional[float] = None, z: Optional[float] = None, ax: Ax = None, + transpose: bool = False, **kwargs, ) -> Ax: """Plot the simulation boundary conditions as lines on a plane @@ -1095,6 +1114,8 @@ def plot_boundaries( position of plane in z direction, only one of x, y, z must be specified to define plane. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. + transpose: bool = False + Optional: Swap the horizontal and vertical axes. **kwargs Optional keyword arguments passed to the matplotlib ``LineCollection``. For details on accepted values, refer to @@ -1190,6 +1211,11 @@ def set_plot_params(boundary_edge, lim, side, thickness): ax = Box.add_ax_labels_and_title( ax=ax, x=x, y=y, z=z, plot_length_units=self.plot_length_units ) + if transpose: + import sys + sys.stderr.write( + "WARNING: Simulation.plot_boundaries() does not yet implement transpose=True!" + ) return ax # TODO: not yet supported diff --git a/tidy3d/components/source/base.py b/tidy3d/components/source/base.py index 388896fb5..02fa98603 100644 --- a/tidy3d/components/source/base.py +++ b/tidy3d/components/source/base.py @@ -74,6 +74,7 @@ def plot( y: Optional[float] = None, z: Optional[float] = None, ax: Ax = None, + transpose: bool = None, **patch_kwargs, ) -> Ax: """Plot this source.""" @@ -81,7 +82,7 @@ def plot( kwargs_arrow_base = patch_kwargs.pop("arrow_base", None) # call the `Source.plot()` function first. - ax = Box.plot(self, x=x, y=y, z=z, ax=ax, **patch_kwargs) + ax = Box.plot(self, x=x, y=y, z=z, ax=ax, transpose=transpose, **patch_kwargs) kwargs_alpha = patch_kwargs.get("alpha") arrow_alpha = ARROW_ALPHA if kwargs_alpha is None else kwargs_alpha @@ -112,6 +113,7 @@ def plot( alpha=arrow_alpha, both_dirs=False, arrow_base=kwargs_arrow_base, + transpose=transpose, ) if self._pol_vector is not None: @@ -125,6 +127,7 @@ def plot( alpha=arrow_alpha, both_dirs=False, arrow_base=kwargs_arrow_base, + transpose=transpose, ) return ax diff --git a/tidy3d/components/structure.py b/tidy3d/components/structure.py index 17f175a8b..9ef068d2e 100644 --- a/tidy3d/components/structure.py +++ b/tidy3d/components/structure.py @@ -149,6 +149,7 @@ def plot( y: Optional[float] = None, z: Optional[float] = None, ax: Ax = None, + transpose: bool = False, **patch_kwargs, ) -> Ax: """Plot structure's geometric cross section at single (x,y,z) coordinate. @@ -163,6 +164,8 @@ def plot( Position of plane in z direction, only one of x,y,z can be specified to define plane. ax : matplotlib.axes._subplots.Axes = None Matplotlib axes to plot on, if not specified, one is created. + transpose: bool = False + Optional: Swap the horizontal and vertical axes. **patch_kwargs Optional keyword arguments passed to the matplotlib patch plotting of structure. For details on accepted values, refer to @@ -173,7 +176,7 @@ def plot( matplotlib.axes._subplots.Axes The supplied or created matplotlib axes. """ - return self.geometry.plot(x=x, y=y, z=z, ax=ax, viz_spec=self.viz_spec, **patch_kwargs) + return self.geometry.plot(x=x, y=y, z=z, ax=ax, viz_spec=self.viz_spec, transpose=transpose, **patch_kwargs) class Structure(AbstractStructure): diff --git a/tidy3d/components/tcad/simulation/heat_charge.py b/tidy3d/components/tcad/simulation/heat_charge.py index 3dd1d34f4..4ddd4d168 100644 --- a/tidy3d/components/tcad/simulation/heat_charge.py +++ b/tidy3d/components/tcad/simulation/heat_charge.py @@ -916,6 +916,7 @@ def plot_property( property: str = "heat_conductivity", hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, + # transpose: bool = False, CONTINUEHERE ) -> Ax: """Plot each of simulation's components on a plane defined by one nonzero x,y,z coordinate. @@ -951,7 +952,7 @@ def plot_property( """ hlim, vlim = Scene._get_plot_lims( - bounds=self.simulation_bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, x=x, y=y, z=z, hlim=hlim, vlim=vlim #, transpose=transpose CONTINUEHERE ) cbar_cond = True @@ -986,16 +987,17 @@ def plot_property( hlim=hlim, vlim=vlim, property=property, + # transpose=transpose CONTINUEHERE ) ax = self.plot_sources( - ax=ax, x=x, y=y, z=z, property=property, alpha=source_alpha, hlim=hlim, vlim=vlim + ax=ax, x=x, y=y, z=z, property=property, alpha=source_alpha, hlim=hlim, vlim=vlim #, transpose=transpose CONTINUEHERE ) - ax = self.plot_monitors(ax=ax, x=x, y=y, z=z, alpha=monitor_alpha, hlim=hlim, vlim=vlim) - ax = self.plot_boundaries(ax=ax, x=x, y=y, z=z, property=property) + ax = self.plot_monitors(ax=ax, x=x, y=y, z=z, alpha=monitor_alpha, hlim=hlim, vlim=vlim) #, transpose=transpose CONTINUEHERE + ax = self.plot_boundaries(ax=ax, x=x, y=y, z=z, property=property) #, transpose=transpose CONTINUEHERE ax = Scene._set_plot_bounds( - bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim + bounds=self.simulation_bounds, ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim #, transpose=transpose CONTINUEHERE ) - ax = self.plot_symmetries(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) + ax = self.plot_symmetries(ax=ax, x=x, y=y, z=z, hlim=hlim, vlim=vlim) #, transpose=transpose CONTINUEHERE if property == "source": self._add_source_cbar(ax=ax, property=property) @@ -1015,6 +1017,7 @@ def plot_heat_conductivity( colorbar: str = "conductivity", hlim: Optional[tuple[float, float]] = None, vlim: Optional[tuple[float, float]] = None, + # transpose: bool = False, CONTINUEHERE **kwargs, ) -> Ax: """ @@ -1074,6 +1077,7 @@ def plot_heat_conductivity( property=plot_type, hlim=hlim, vlim=vlim, + #, transpose=transpose CONTINUEHERE ) @equal_aspect @@ -1144,7 +1148,7 @@ def plot_boundaries( ax = Box.add_ax_labels_and_title( ax=ax, x=x, y=y, z=z, plot_length_units=self.plot_length_units ) - + # CONTINUEHERE: Geometry.transpose_axis_info(ax, swap_axis_labels=transpose) # Swap axis labels if needed return ax def _get_bc_plot_params(self, boundary_spec: HeatChargeBoundarySpec) -> PlotParams: