feat(rf): FXC-3725 add voltage-based selection of modes to the ModeSolver#2948
feat(rf): FXC-3725 add voltage-based selection of modes to the ModeSolver#2948dmarek-flex wants to merge 1 commit intodevelopfrom
Conversation
c6b771b to
fac5487
Compare
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 |
weiliangjin2021
left a comment
There was a problem hiding this comment.
_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
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.
should we name it _is_quasi_tem_mode to be more specific?
There was a problem hiding this comment.
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.
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.
could you elaborate on why tuple is always valid?
There was a problem hiding this comment.
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.
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.
"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.
what about multiple terminal_specs intersect the same conductor?
dbochkov-flexcompute
left a comment
There was a problem hiding this comment.
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.
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.
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.
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.
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.
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.
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.
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.
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