-
Notifications
You must be signed in to change notification settings - Fork 59
add target_neff parameter to mode solver #716
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
Changes from all commits
27b6243
3928144
ed6a276
31e50fb
503aeac
f80388a
9f7fa52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -92,6 +92,8 @@ class Waveguide(BaseModel, extra="forbid", arbitrary_types_allowed=True): | |
| surface_k: absorption coefficient added to the core material | ||
| index on the top-surface layer. | ||
| bend_radius: radius to simulate circular bend. | ||
| target_neff: target effective index for the mode solver. Defaults | ||
| to the real part of the core refractive index if not specified. | ||
| num_modes: number of modes to compute. | ||
| group_index_step: if set to `True`, indicates that the group | ||
| index must also be calculated. If set to a positive float | ||
|
|
@@ -147,6 +149,7 @@ class Waveguide(BaseModel, extra="forbid", arbitrary_types_allowed=True): | |
| surface_thickness: float = 0.0 | ||
| surface_k: float = 0.0 | ||
| bend_radius: float | None = None | ||
| target_neff: float | None = None | ||
| num_modes: int = 2 | ||
| group_index_step: bool | float = False | ||
| precision: Precision = "double" | ||
|
|
@@ -178,6 +181,10 @@ def filepath(self) -> pathlib.Path | None: | |
| h = hashlib.md5(named_args_string.encode()).hexdigest()[:16] | ||
| return cache_path / f"{self.__class__.__name__}_{h}.npz" | ||
|
|
||
| def _resolve_target_neff(self, n_core: complex) -> float: | ||
| """Return target_neff if set, otherwise fall back to n_core.real.""" | ||
| return self.target_neff if self.target_neff is not None else n_core.real | ||
|
|
||
| @property | ||
| def waveguide(self): | ||
| """Tidy3D waveguide used by this instance.""" | ||
|
|
@@ -222,9 +229,11 @@ def waveguide(self): | |
| else None | ||
| ) | ||
|
|
||
| target_neff = self._resolve_target_neff(n_core) | ||
|
|
||
| mode_spec = td.ModeSpec( | ||
| num_modes=self.num_modes, | ||
| target_neff=n_core.real, | ||
| target_neff=target_neff, | ||
| bend_radius=self.bend_radius, | ||
| bend_axis=1, | ||
| num_pml=(12, 12) if self.bend_radius else (0, 0), | ||
|
|
@@ -502,6 +511,8 @@ class WaveguideCoupler(Waveguide): | |
| index must also be calculated. If set to a positive float | ||
| it defines the fractional frequency step used for the | ||
| numerical differentiation of the effective index. | ||
| target_neff: target effective index for the mode solver. Defaults | ||
| to the real part of the core refractive index if not specified. | ||
| precision: computation precision. | ||
| grid_resolution: wavelength resolution of the computation grid. | ||
| max_grid_scaling: grid scaling factor in cladding regions. | ||
|
|
@@ -554,9 +565,11 @@ def waveguide(self): | |
| else None | ||
| ) | ||
|
|
||
| target_neff = self._resolve_target_neff(n_core) | ||
|
|
||
| mode_spec = td.ModeSpec( | ||
| num_modes=self.num_modes, | ||
| target_neff=n_core.real, | ||
| target_neff=target_neff, | ||
| bend_radius=self.bend_radius, | ||
| bend_axis=1, | ||
| num_pml=(12, 12) if self.bend_radius else (0, 0), | ||
|
|
@@ -863,7 +876,10 @@ def sweep_mode_area(waveguide: Waveguide, **sweep_kwargs) -> np.ndarray: | |
|
|
||
|
|
||
| def sweep_bend_mismatch( | ||
| waveguide: Waveguide, bend_radii: tuple[float, ...] | ||
| waveguide: Waveguide, | ||
| bend_radii: tuple[float, ...], | ||
| track_modes: bool = False, | ||
| modes_to_track: Sequence[int] = (0,), | ||
| ) -> np.ndarray: | ||
| """Overlap integral squared for the bend mode mismatch loss. | ||
|
|
||
|
|
@@ -873,7 +889,22 @@ def sweep_bend_mismatch( | |
| Args: | ||
| waveguide: base waveguide geometry. | ||
| bend_radii: radii values to sweep. | ||
| track_modes: if True, for each radius select the bend mode with | ||
| the best overlap for each tracked straight mode. | ||
| modes_to_track: straight mode indices to track. Required when | ||
| track_modes is True. | ||
| """ | ||
| if track_modes: | ||
| if len(modes_to_track) == 0: | ||
| raise ValueError("modes_to_track must be provided when track_modes is True") | ||
| if waveguide.num_modes < max(modes_to_track) + 1: | ||
| raise ValueError( | ||
| f"num_modes ({waveguide.num_modes}) must be >= " | ||
| f"{max(modes_to_track) + 1} to track modes {modes_to_track}" | ||
| ) | ||
| if waveguide.num_modes < 2: | ||
| raise ValueError("Track modes requires num_modes >= 2") | ||
|
Comment on lines
+905
to
+906
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
|
||
| kwargs = dict(waveguide) | ||
| kwargs.pop("bend_radius") | ||
| straight = Waveguide(**kwargs) | ||
|
|
@@ -882,9 +913,14 @@ def sweep_bend_mismatch( | |
| for radius in tqdm(bend_radii): | ||
| bend = Waveguide(bend_radius=radius, **kwargs) | ||
| overlap = bend.overlap(straight) | ||
| results.append( | ||
| np.diagonal(overlap) ** 2 if straight.num_modes > 1 else overlap**2 | ||
| ) | ||
|
|
||
| if track_modes: | ||
| best = [np.max(np.abs(overlap[:, m]) ** 2) for m in modes_to_track] | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is a potential logic issue with double squaring when Line 918 calculates the power overlap as While the docstring mentions the loss is squared for a round trip, the naming 'Overlap integral squared' usually implies |
||
| results.append(best) | ||
| else: | ||
| results.append( | ||
| np.diagonal(overlap) ** 2 if straight.num_modes > 1 else overlap**2 | ||
| ) | ||
|
|
||
| return np.abs(results) ** 2 | ||
|
|
||
|
|
||
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.
suggestion (bug_risk): Revisit squaring strategy to avoid double-squaring and align with the documented quantity.
In the
track_modesbranch you computebest = [np.max(np.abs(overlap[:, m]) ** 2) ...]and later returnnp.abs(results) ** 2, so the output is effectively|overlap|**4. This matches the non-tracking branch, but the docstring says it returns the “overlap integral squared”. Please decide whether the intended quantity is|overlap|**2or|overlap|**4and update both branches consistently. If only the squared magnitude is intended, remove the inner** 2(and possibly the final** 2) to avoid redundant work and mismatch with the documentation.Suggested implementation:
These edits assume:
best = [np.max(np.abs(overlap[:, m]) ** 2, axis=1) for m in modes_to_track]results = np.max(np.abs(overlap) ** 2, axis=1)return np.abs(results) ** 2.If the actual code uses slightly different variable names or shapes (e.g.
axis=0vsaxis=1, or buildsresultsfrombestbefore returning), you should:** 2that is applied directly tonp.abs(overlap...)in both the tracking and non-tracking branches.|overlap|**2, matching the docstring “Overlap integral squared”.|overlap|**4.