-
Notifications
You must be signed in to change notification settings - Fork 65
feat(rf): FXC-3725 add voltage-based selection of modes to the ModeSolver #2948
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
c6b771b to
fac5487
Compare
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.
7 files reviewed, 8 comments
tidy3d/components/microwave/path_integrals/mode_plane_analyzer.py
Outdated
Show resolved
Hide resolved
tidy3d/components/microwave/path_integrals/mode_plane_analyzer.py
Outdated
Show resolved
Hide resolved
tidy3d/components/microwave/path_integrals/mode_plane_analyzer.py
Outdated
Show resolved
Hide resolved
fac5487 to
90a1ac0
Compare
Diff CoverageDiff: origin/develop...HEAD, staged and unstaged changes
Summary
|
70ed291 to
0349e69
Compare
0349e69 to
a26413b
Compare
|
another review @greptile |
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.
15 files reviewed, no comments
weiliangjin2021
left a comment
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.
_get_isolated_conductors_as_shapely in ModePlaneAnalyzer can be slow as it seems to take all structures, and interior_disjoint_geometries defaults to False. Could you filter structures similar to
tidy3d/tidy3d/components/grid/grid_spec.py
Line 1456 in 4d5efb9
| structures_intersect = [s for s in structure_list if self.intersects(s.geometry)] |
variable that defaults toTrue`?
| "been used to set up the monitor or mode solver.", | ||
| ) | ||
|
|
||
| def _is_transmission_line_mode(self, mode_index: int) -> bool: |
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 we name it _is_quasi_tem_mode to be more specific?
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.
Yes, and since I need to add this mode_type categorization anyways in the other PR. I will try to quickly generalize to determine TEM/quasiTEM,TE,TM.
| ) | ||
|
|
||
| def _is_transmission_line_mode(self, mode_index: int) -> bool: | ||
| """Check if a mode qualifies as a quasi-TEM transmission line mode. |
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.
add here that it's checked at lowest frequency
| """Validate conductor identification inputs.""" | ||
|
|
||
| # If it's a string or tuple, pass through | ||
| if isinstance(val, (str, tuple)): |
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.
could you elaborate on why tuple is always valid?
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 see, it's supposed to be a 2d point. that needs validation as well.
| description="Optional tuple of terminal specifications for mode selection and ordering in " | ||
| "transmission line systems. Each 'TerminalSpec' defines the desired voltage pattern (which conductors " | ||
| "should be positive vs. negative) for a mode. When provided, the mode solver automatically reorders " | ||
| "computed modes to match the terminal specification order and applies phase corrections to ensure " |
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.
sometimes it's called phase alignment
| raise SetupError( | ||
| f"No conductor found intersecting with the {terminal_type}_terminal. " | ||
| "Please ensure that your terminal specification (coordinate, line, or polygon) " | ||
| "intersects with at least one conductive structure in the mode plane. " |
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.
"intersects with exactly one conductive structure"
| plus_indices = [ | ||
| i for i, geom in enumerate(conductor_shapely) if geom.intersects(plus_terminal) | ||
| ] | ||
| validate_conductor_intersection(plus_indices, "plus") |
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.
what about multiple terminal_specs intersect the same conductor?
dbochkov-flexcompute
left a comment
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.
Looks very good to me, some minor comments
| "of vertices defining a geometric region (point for N=1, line for N=2, polygon for N>2). " | ||
| "The coordinate or region should lie within the desired conductor in the mode plane.", | ||
| ) | ||
|
|
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 think it is checked at some point, but would it make sense to add a cheap validator here that checks that when plus and minus terminals are converted to sets their intersection is empty? I think that might require to convert numpy array into list or tuples. Just to invalidate grossly incorrectly setups like TerminalSpec(plus_terminals=["ground"], minus_terminals=["ground"])
| ... ) | ||
| """ | ||
|
|
||
| plus_terminals: tuple[ConductorIdentifierType, ...] = pd.Field( |
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.
Definitely optional, but I think plus and minus would also be acceptable here instead of plus_terminals and minus_terminals
| "ignored for the associated mode.", | ||
| ) | ||
|
|
||
| terminal_specs: Optional[tuple[TerminalSpec, ...]] = pd.Field( |
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.
is there any restrictions on the length of this tuple, like < num_modes? It would also be good to clarify in description whether each terminal_spec is for a separate mode
From understanding, I need that |
My understanding is that we'll follow standard conventions that metal structure always override dielectrics. For mask, users need to manually apply boolean operations to metal structures. |
| mode_plane_analyzer = ModePlaneAnalyzer( | ||
| center=plane.center, | ||
| size=plane.size, | ||
| field_data_colocated=colocate, | ||
| structures=sim.volumetric_structures, | ||
| grid=sim.grid, | ||
| symmetry=sim.symmetry, | ||
| sim_box=sim.simulation_geometry, | ||
| ) | ||
| _ = mode_plane_analyzer.conductor_bounding_boxes |
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.
What benefit do we get by having ModePlaneAnalyzer being a class rather than a function? Just how you're envisioning this to grow
| voltage_sets = mode_plane_analyzer._identify_conductor_voltage_sets( | ||
| mode_spec.terminal_specs | ||
| ) | ||
| mode_plane_analyzer._validate_conductor_voltage_configurations(voltage_sets) |
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 thing is that if it were a function it does not require schema changes really? And don't see the class itself bring much value?
| # where the first set contains positive terminals and the second set contains negative terminals. | ||
| VoltageSets = tuple[set[int], set[int]] | ||
| # Conversion of user-supplied TerminalSpec into shapely geometries which are used to find intersecting conductors | ||
| TerminalSpecShapes = tuple[list[Shapely], list[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.
So this is what you want to extend to more inputs ultimately?
|
|
||
| # Type for holding sets of indices associated with conductors, | ||
| # where the first set contains positive terminals and the second set contains negative terminals. | ||
| VoltageSets = tuple[set[int], set[int]] |
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.
Do we feel this is going to be extensible fully?
| def mode_symmetry(self) -> tuple[Symmetry, Symmetry, Symmetry]: | ||
| """Mode symmetry considering simulation box and simulation symmetry.""" | ||
| return self._get_mode_symmetry(self.sim_box, self.symmetry) | ||
|
|
||
| @cached_property | ||
| def mode_limits(self) -> Bound: | ||
| """Mode plane bounds restricted to final grid positions. | ||
| Mode profiles are calculated on a grid which is expanded from the monitor size | ||
| to the closest grid boundaries, taking into account symmetry conditions. | ||
| """ | ||
| return self._get_mode_limits(self.grid, self.mode_symmetry) |
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.
So this type of code structure gives me an impression of trying to store a state but then we have the initialization latency ultimately, and it depends on the applications
backend: https://github.com/flexcompute/compute/pull/2764
Greptile Overview
Greptile Summary
This PR adds voltage-based mode selection capability to the
ModeSolverthrough a newTerminalSpecclass that allows users to specify which conductors should be at positive or negative voltage for transmission line modes.Key changes:
TerminalSpecclass inmode_spec.pyto specify voltage patterns across conductors using coordinate points, structure names, or geometric regions (arrays)terminal_specsfield toMicrowaveModeSpecfor mode selection and orderingModePlaneAnalyzer:_convert_terminal_specifications_to_candidate_geometry(),_identify_conductor_voltage_sets(), and_validate_conductor_voltage_configurations()ModeSolver._validate_mode_plane_analysis()to catch configuration errors before submissionSimulation._validate_microwave_mode_specs()to_validate_microwave_mode_plane_analysis()for consistency_post_process_modes_with_terminal_specs()placeholder that raisesSetupErrorfor local mode solver (backend implementation pending)Implementation notes:
Confidence Score: 4/5
Important Files Changed
File Analysis
TerminalSpecclass andterminal_specsfield toMicrowaveModeSpecfor voltage-based mode selection. Clean implementation with good validation logic._validate_mode_plane_analysis()and_post_process_modes_with_terminal_specs()methods for early validation. Also fixed return type annotation from list to tuple.Sequence Diagram
sequenceDiagram participant User participant ModeSolver participant MicrowaveModeSpec participant ModePlaneAnalyzer participant TerminalSpec User->>ModeSolver: Create with terminal_specs ModeSolver->>ModeSolver: _post_init_validators() ModeSolver->>ModeSolver: _validate_mode_plane_analysis() alt terminal_specs is not None ModeSolver->>ModePlaneAnalyzer: Create analyzer instance ModePlaneAnalyzer->>ModePlaneAnalyzer: conductor_bounding_boxes ModePlaneAnalyzer->>ModePlaneAnalyzer: conductor_shapes (cached) ModePlaneAnalyzer-->>ModeSolver: conductor info ModeSolver->>ModePlaneAnalyzer: _identify_conductor_voltage_sets() ModePlaneAnalyzer->>ModePlaneAnalyzer: _convert_terminal_specifications_to_candidate_geometry() ModePlaneAnalyzer->>TerminalSpec: Read plus/minus terminals TerminalSpec-->>ModePlaneAnalyzer: terminal identifiers alt terminal is tuple ModePlaneAnalyzer->>ModePlaneAnalyzer: Create Point else terminal is string ModePlaneAnalyzer->>ModePlaneAnalyzer: Find structure by name ModePlaneAnalyzer->>ModePlaneAnalyzer: Get intersections with geometry else terminal is array ModePlaneAnalyzer->>ModePlaneAnalyzer: Create Point/LineString/Polygon end ModePlaneAnalyzer->>ModePlaneAnalyzer: _find_conductor_terminals() ModePlaneAnalyzer->>ModePlaneAnalyzer: Match terminals to conductors ModePlaneAnalyzer-->>ModeSolver: voltage_sets ModeSolver->>ModePlaneAnalyzer: _validate_conductor_voltage_configurations() ModePlaneAnalyzer->>ModePlaneAnalyzer: Check for conflicts/duplicates alt validation fails ModePlaneAnalyzer-->>ModeSolver: Raise SetupError ModeSolver-->>User: "TerminalSpec was not setup correctly" end end User->>ModeSolver: solve() / access .data ModeSolver->>ModeSolver: _add_microwave_data() alt terminal_specs is not None ModeSolver->>ModeSolver: _post_process_modes_with_terminal_specs() Note over ModeSolver: Raises SetupError<br/>(not available locally) end ModeSolver->>ModeSolver: _make_path_integrals() ModeSolver-->>User: MicrowaveModeSolverData