-
Notifications
You must be signed in to change notification settings - Fork 66
FXC-3547-Adding mode sorting to ModeSpec #2869
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
Conversation
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.
5 files reviewed, 1 comment
861448e to
e6fbfa1
Compare
Diff CoverageDiff: origin/develop...HEAD, staged and unstaged changes
Summary
tidy3d/components/data/monitor_data.pyLines 2217-2225 2217 num_freqs, num_modes = sort_inds_2d.shape
2218 modify_data = {}
2219 for key, data in self.data_arrs.items():
2220 if "mode_index" not in data.dims or "f" not in data.dims:
! 2221 continue
2222 dims_orig = data.dims
2223 f_coord = data.coords["f"]
2224 slices = []
2225 for ifreq in range(num_freqs):Lines 2258-2266 2258 """
2259
2260 # Return the original data if no new sorting / tracking required
2261 if track_freq is None and sort_spec is None:
! 2262 return self
2263
2264 num_freqs = self.n_eff["f"].size
2265 num_modes = self.n_eff["mode_index"].size
2266 all_inds = np.arange(num_modes)tidy3d/components/mode_spec.pyLines 317-325 317 if self.track_freq is not None:
318 return self.track_freq
319 if self.sort_spec is not None:
320 return self.sort_spec.track_freq
! 321 return None
322
323
324 class ModeSpec(AbstractModeSpec):
325 """ |
|
Actually @caseyflex @yuanshen-flexcompute hold off the review, I'm going to generalize this further to include filtering. |
bd946f2 to
609cfba
Compare
|
Okay now you can review. :) I've introduced advanced filter/sort options. You can select to filter by a given property so that values e.g. over or under a reference value will be put first in the list; then separately you can sort each of the two groups by some other criterion. I think with this #1391 can be closed - probably one thing that still would be nice to add though is some built-in that computes the field intensity close to the plane boundaries (maybe not just on them, but within some region), which can help with PML mode filtering. Or similarly some metric that computes the field intensity within a given region. |
609cfba to
e0f0136
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.
Nice! Generally very clean and easy to use. I have some comments:
sort_ordershould default toascending(currentlydescending) whensort_referenceis notNone. This way, values closest to the reference are returned first.Sorting after filtering doesn't seem to work correctly in some cases. I tested filtering byThis actually might be user error. Things look good for now.mode_areaunder a certain value, then sorting byn_effclosest to a reference value. This resulted in the sorting "breaking" the filtered order in some cases. I have a test notebook if you would like to take a closer look.
Thanks!
|
Thanks!
I think that makes sense. Originally I set it to
Yeah, thanks, a minimally reproducible example would be great! I added a test for that but it might not be comprehensive enough so this will help fix and extend testing. |
caseyflex
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.
This is a great addition! It mostly looks good, just a few comments
|
The issue that @yuanshen-flexcompute thought he was seeing was related to the fact that by default, we have |
I see, that’s a good point. So this new sorting is frequency independent, while track_freq couples different frequencies. Currently, track_freq is applied second. Since they are both sorting, it could be nice to unify their api in mode_spec. Or could just validate that only one or the other is used. |
So I don't think that's a good idea, because a typical use case could very well be that you roughly want a certain filtering, but you also want the modes to be continuously ordered across frequencies, that is to say the latter is still more important than the strict filtering. Like, I want to have my TM mode come first at my central frequency, but if there's a crossing somewhere I don't want to force the reshuffling. By the way the reason I still filter all modes rather than the tracked frequency only, if So having thought about this I think indeed moving |
|
@momchil-flex this might be another case for having a list of filter_specs, so the order in which they are applied is transparent |
|
Well tracking first and then sorting doesn't really make sense as an option, right? Because that would just sort. You just need to define whether you want to track in the end or not. |
3905129 to
f672c21
Compare
|
@greptile take a look |
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.
Looking at this PR, I can see it's a comprehensive implementation of advanced mode sorting functionality for Tidy3D's mode solver. This is a significant enhancement that addresses user needs for better mode filtering and sorting capabilities.
Key Changes Analysis
1. New ModeSortSpec Class
- Provides fine-grained control over mode filtering and sorting
- Supports filtering modes into groups based on thresholds
- Allows sorting within groups by various physical properties
- Includes frequency tracking capabilities
2. Integration with Existing ModeSpec
- Adds
sort_specfield toModeSpec - Maintains backward compatibility while deprecating
filter_polandtrack_freq - Includes mutual exclusivity validation to prevent conflicting configurations
3. Enhanced Mode Data Properties
- Adds convenient aliases (
TE_fraction,TM_fraction, etc.) for easier access to polarization data - Improves the mode sorting infrastructure with better reordering methods
Areas of Attention
Mode Solver Implementation - The core sorting logic in mode_solver.py is complex and handles multiple scenarios. The _apply_mode_reorder method correctly preserves data integrity across all frequency-mode combinations.
Validation Logic - The mutual exclusivity validator properly prevents users from accidentally using both old and new filtering approaches simultaneously.
Backward Compatibility - The deprecation warnings are well-implemented, and the legacy behavior is preserved while guiding users toward the new API.
Schema Updates - All JSON schemas have been properly updated to reflect the new structure.
Strengths
- Well-structured API: The
ModeSortSpecclass provides intuitive parameters for complex sorting scenarios - Comprehensive testing: The test coverage includes various sorting combinations and edge cases
- Proper deprecation handling: Clear warnings and backward compatibility preservation
- Performance considerations: The implementation avoids unnecessary recomputation when no sorting is needed
The implementation looks solid and follows good software engineering practices. The sorting functionality will significantly improve the user experience for mode analysis workflows.
f672c21 to
d5cfb4e
Compare
|
Alright so I did some major updates based on previous discussions @caseyflex :
|
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.
Great, thanks @momchil-flex !
Probably the one remaining awkwardness that I'm not 100% happy with is that overlap_sort and sort_modes will always set the mode_data.monitor.mode_spec.track_freq to None, and set the sort_spec.track_freq to the one that matches the tracking that was done. This can lead to a situation where the mode_data.monitor.mode_spec is not the same as the one of the monitor that initially generated it if track_freq was defined there in the deprecated way. However, this resulted in the most straightforward usage for everything else.
At least this only happens if the deprecated api is used. Actually, I had a similar concern. Say the original monitor.mode_spec filters based on one key, and then the user calls sort_modes with another filter key for example. Then the updated monitor.mode_spec will only contain the second filter key, and if you use that monitor in a fresh simulation, it will give different results.
More broadly, this difficulty comes up repeatedly due to our concept of MonitorData containing a Dataset as well as a Monitor. For example, ModeData._isel is private because it results in inconsistency between the monitor and the data. Also in EMESimulationData, we have _extract_mode_solver_data, which makes an attempt to update the monitor in a reasonable way, but is not guaranteed that the Monitor would reproduce the MonitorData if used in a fresh simulation. It would be extremely useful to have isel functionality for all of our Datasets.
Probably to avoid this, we would have to shift a lot of functionality out of the MonitorData classes into the Dataset classes, so that then MonitorData is really just a Dataset together with a Monitor that gives rise to it. And the methods that process the Dataset return a new Dataset that doesn't necessarily correspond to any Monitor.
Not suggesting changing this here, just bringing it up as a recurring difficulty.
yuanshen-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.
Latest changes looks good to me. Thanks!
Good thought - actually in this case, the sorting errors, because we explicitly error if both The reason I couldn't do the same for |
d5cfb4e to
3a16916
Compare
Addresses the new suggestion in #1391 to add more advanced ways to sort modes in the mode solver (and mode sources and monitors).
Probably we should still keep the issue open as I should also implement the FilterSpec as discussed in that issue.
@yuanshen-flexcompute does the current PR help with some of your use cases that made you bring it up though?
Greptile Overview
Updated On: 2025-10-07 08:34:20 UTC
Summary
This PR adds advanced mode sorting functionality to the Tidy3D mode solver by introducing a new `ModeSortSpec` class and integrating it into the `ModeSpec` configuration. The implementation addresses user feedback from issue #1391 where users were spending significant time manually filtering and sorting modes after simulation.The core addition is the
ModeSortSpecclass which allows users to sort modes by various physical properties including effective index (n_eff), loss coefficient (k_eff), TE polarization fraction (TE_fraction), waveguide TE/TM fractions (wg_TE_fraction,wg_TM_fraction), and mode area (mode_area). Users can specify sorting direction (ascending/descending) and optionally provide a reference value to sort by proximity to that value.The implementation integrates seamlessly into the existing mode solver pipeline through a new
_sort_modes()method that operates onModeSolverDataafter polarization filtering but before frequency tracking. The sorting is applied per-frequency to handle cases where mode properties vary across the frequency spectrum. A mutual exclusivity validator ensures that the newsort_speccannot be used simultaneously with the legacyfilter_polfield to prevent conflicting behaviors.To support the sorting functionality, three new property aliases were added to the
ModeDataclass:TE_fraction,wg_TE_fraction, andwg_TM_fraction. These provide more convenient access to polarization fraction data and are used by themodes_infomethod for consistency. TheModeSortSpecclass is properly exported through the main__init__.pyfile to make it available in the public API.Important Files Changed
Changed Files
ModeSortSpecclass andsort_specfield toModeSpecwith mutual exclusivity validation_sort_modes()method and helper functions for mode reorderingModeSortSpecclass to public APIConfidence score: 4/5
Sequence Diagram
sequenceDiagram participant User participant ModeSpec participant ModeSolver participant ModeSolverData participant sort_methods as _sort_modes & _apply_mode_reorder User->>ModeSpec: "Create ModeSpec with sort_spec" ModeSpec->>ModeSpec: "Validate sort_spec parameters" Note over ModeSpec: Check key is in SORT_KEYS<br/>Ensure filter_pol and sort_spec not both used User->>ModeSolver: "Create ModeSolver with ModeSpec" ModeSolver->>ModeSolver: "Initialize and validate" User->>ModeSolver: "Call solve() or data_raw" ModeSolver->>ModeSolver: "_data_on_yee_grid()" ModeSolver->>ModeSolver: "_colocate_data() if needed" ModeSolver->>ModeSolver: "_normalize_modes()" ModeSolver->>ModeSolver: "_filter_polarization()" ModeSolver->>sort_methods: "_sort_modes(mode_solver_data)" sort_methods->>ModeSolverData: "getattr(mode_solver_data, sort_spec.key)" Note over sort_methods: Get metric values (n_eff, k_eff, etc.) sort_methods->>sort_methods: "Apply reference_value if provided" Note over sort_methods: metric = abs(metric - reference_value) sort_methods->>sort_methods: "Loop over frequencies" loop For each frequency sort_methods->>sort_methods: "np.argsort(metric values)" sort_methods->>sort_methods: "Reverse if direction='descending'" sort_methods->>sort_methods: "_apply_mode_reorder(data, freq_idx, sort_inds)" sort_methods->>ModeSolverData: "Reorder field components and n_complex" Note over ModeSolverData: data.values[..., freq_idx, :] = data.values[..., freq_idx, sort_inds] end alt If track_freq is set and multiple frequencies ModeSolver->>ModeSolverData: "mode_solver_data.overlap_sort(track_freq)" Note over ModeSolverData: Apply frequency-based mode tracking end ModeSolver-->>User: "Return sorted ModeSolverData"