Skip to content
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

Phase 1: self-intersecting polyslab #678

Merged
merged 1 commit into from
Feb 9, 2023

Conversation

weiliangjin2021
Copy link
Collaborator

The support of self-intersecting polyslabs is scheduled to roll out in two phases. In phase 1, we consider the case where the length of some edges can approach 0 (or neighboring vertices become degenerate) at some point during extrusion. In phase 2, more general types of edge events will be treated. The main idea in this PR is to divide a complex polyslab into many simple polyslabs.

  • A class ComplexPolySlab is defined in plugin. It takes the same fields as PolySlab. We can consider move this class to geometry.py. The current design is to leave space for phase 2.
  • An example on how to use this class can be found here. In short, one can use the property method .sub_polyslabs to obtain a list of simple polyslabs, or .geometry_group that wraps up the simple polyslabs in a geometry group.
  • Constructing from GDS file is also supported. The usage is the same as PolySlab. Here is an example.
  • There some updates to the visualization part in PolySlab: previously we cache the polygon on the base, and always use base polygon to construct polygons at other extrusion distance. However, now as we construct simple polyslabs from a complex polyslab, the degenerate vertices of the simple polyslab are removed on the top or the base. So the polygon on the base or the top can be different from the polygon in the between. An appropriate way is to use the polygon in the middle to construct polygons at other extrusion distance.

Copy link
Collaborator

@tylerflex tylerflex left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! I'm not able to follow everything but I think this general approach makes sense. So if a user wants to use a ComplexPolySlab in their Simulation, would the standard way be to

cps = ComplexPolySlab(...)
geo = cps.geometry_group
structure = td.Structure(geometry=geo, medium=medium)

? I worry a little that it won't be so clear to the new user how to do this, so maybe there can be some mention of this in the Docstring of ComplexPolySlab. For example

Class ComplexPolySlab(PolySlab):
    """ Interface for dividing a complex polyslab where self-intersecting polygon can occur during extrusion. 
    To generate a :class:`.GeometryGroup` from a `ComplexPolySlab`` instance for use in :class:`Simulation`, 
    pass `complex_polyslab.geometry_group` to the `geometry` field of :class:`.Structure`.
    """

tidy3d/components/geometry.py Outdated Show resolved Hide resolved
tidy3d/plugins/__init__.py Show resolved Hide resolved
tidy3d/plugins/polyslab/polyslab.py Outdated Show resolved Hide resolved
tidy3d/plugins/polyslab/polyslab.py Outdated Show resolved Hide resolved
tidy3d/plugins/polyslab/polyslab.py Outdated Show resolved Hide resolved
tidy3d/plugins/polyslab/polyslab.py Show resolved Hide resolved
tidy3d/plugins/polyslab/polyslab.py Outdated Show resolved Hide resolved
tidy3d/plugins/polyslab/polyslab.py Outdated Show resolved Hide resolved
tidy3d/plugins/polyslab/polyslab.py Outdated Show resolved Hide resolved
@weiliangjin2021
Copy link
Collaborator Author

So if a user wants to use a ComplexPolySlab in their Simulation, would the standard way be to

cps = ComplexPolySlab(...)
geo = cps.geometry_group
structure = td.Structure(geometry=geo, medium=medium)

Right, and the other approach is

cps = ComplexPolySlab(...)
geo_list = cps.sub_polyslabs
structure_list = [td.Structure(geometry=geo, medium=medium) for geo in geo_list]

So how about a docstring like this?

Class ComplexPolySlab(PolySlab):
    """ Interface for dividing a complex polyslab where self-intersecting polygon can occur during extrusion. 
    The list of divided polyslabs can be accessed from a `ComplexPolySlab`` instance with 
    `complex_polyslab.sub_polyslabs`, or wrapped in a :class:`.GeometryGroup` with
    `complex_polyslab.geometry_group`.
    """

@lucas-flexcompute
Copy link
Collaborator

So if a user wants to use a ComplexPolySlab in their Simulation, would the standard way be to

cps = ComplexPolySlab(...)
geo = cps.geometry_group
structure = td.Structure(geometry=geo, medium=medium)

Right, and the other approach is

cps = ComplexPolySlab(...)
geo_list = cps.sub_polyslabs
structure_list = [td.Structure(geometry=geo, medium=medium) for geo in geo_list]

So how about a docstring like this?

Class ComplexPolySlab(PolySlab):
    """ Interface for dividing a complex polyslab where self-intersecting polygon can occur during extrusion. 
    The list of divided polyslabs can be accessed from a `ComplexPolySlab`` instance with 
    `complex_polyslab.sub_polyslabs`, or wrapped in a :class:`.GeometryGroup` with
    `complex_polyslab.geometry_group`.
    """

I usually find it most useful to have a small example straight into the documentation, much like numpy and scipy do. It's easy to understand and quicker to find than going through a complete example notebook (which should, of course, exist as a full reference and tutorial)

@tylerflex
Copy link
Collaborator

Related: It might be nice to add

def to_structure(self, medium: MediumType) -> td.Structure:
    """Construct a structure containing a user-specified medium and a GeometryGroup made of all the PolySlabs from this object."""

just as one other alternative to both of those.

@weiliangjin2021
Copy link
Collaborator Author

I usually find it most useful to have a small example straight into the documentation, much like numpy and scipy do. It's easy to understand and quicker to find than going through a complete example notebook (which should, of course, exist as a full reference and tutorial)

Good point. It has been added to the doc.

Copy link
Collaborator

@lucas-flexcompute lucas-flexcompute left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can tell, this should work for edge collapsing (neighboring vertices merging), but I'm not sure it will be straightforward to implement the general case. That's because the subdivision algorithm explicitly uses the offset at which the collapsing event happens, which is much harder for edges. Anyway, for now it looks great!

tidy3d/plugins/polyslab/polyslab.py Show resolved Hide resolved
vertices = self._proper_vertices(self.vertices)
if isclose(self.dilation, 0):
return vertices
return self._shift_vertices(vertices, self.dilation)[0]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This dilation/erosion might itself result in self-intersecting polygons. Should we do some clean-up to remove those? Here the problem is easier than with the extrusion, since it doesn't have to be compared to any other slabs. @weiliangjin2021 I believe you mentioned that shapely had already a function that could be used for this, right?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about doing this as well, but then quickly realized that if we allow self-intersecting polygon at the reference plane and perform clean-up, there are situations where the healed polygon can split and have islands/holes. So right now, we simply error in this case:

@pydantic.validator("vertices", always=True)
def no_self_intersecting_polygon_at_reference_plane(cls, val, values):

Another possibility is to error only when the healed polygon splits or generates holes/islands, but allow the change of vertices number. What do you think?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think its usually best to make it easier for the user, as long as we don't overstep on what their intentions might be. In this case, we should gracefully handle both cases, but I can see that the current API might make it difficult to return multiple islands. Maybe continue to error in this case, but handle the easier one, where we just clean-up the polygon and remain with a single proper polygon?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shall we error or just warning for polygon splitting/islands? I'm thinking about: 1) error for polygon splitting/islands; 2) warning for the change of the number of vertices.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part has been added. Quite significant change in the two validators.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, what I have now doesn't really address your comments, since I still offset the vertices first and then heal. I cannot think of any self-intersecting polygon that can really pass the validator here. Maybe let's plan on the next release.

Copy link
Contributor

@tomflexcompute tomflexcompute left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an useful implementation. With the regular PolySlab, it seems like as long as the user knows where the vertices will meet during the extrusion, they can always manually define multiple polyslabs to achieve the same final geometry (group) as the ComplexPolySlab, but it could take a lot of trigometry to figure everything out.

My concern is that polyslabs that will result in self-intersection vertices usually have a more complex geometry. After extrusion, it might be difficult to examine if the result is correct only by examining the crossing section using the plot method. At this point, probably there is no better way than submitting the job to the server and going to web GUI for visualization.

tidy3d/plugins/polyslab/polyslab.py Outdated Show resolved Hide resolved
Visualization and GDS import
@weiliangjin2021 weiliangjin2021 merged commit 88cdcbc into pre/1.9.0 Feb 9, 2023
@tylerflex tylerflex deleted the weiliang/self-intersecting branch May 16, 2023 14:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rc2 2nd pre-release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants