Skip to content

Commit f44909b

Browse files
committed
feat(rf): support multimodal waveports
1 parent 0ed42f3 commit f44909b

File tree

19 files changed

+1070
-717
lines changed

19 files changed

+1070
-717
lines changed

CHANGELOG.md

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2727
- Introduced a profile-based configuration manager with TOML persistence and runtime overrides exposed via `tidy3d.config`.
2828
- Added support of `os.PathLike` objects as paths like `pathlib.Path` alongside `str` paths in all path-related functions.
2929
- Added configurable local simulation result caching with checksum validation, eviction limits, and per-call overrides across `web.run`, `web.load`, and job workflows.
30+
- Added multimode support to `WavePort` in the smatrix plugin, allowing multiple modes to be analyzed per port.
31+
32+
### Breaking Changes
33+
**Note: These breaking changes only affect the microwave and smatrix plugins.**
34+
- Renamed path integral classes for improved consistency. Please see our migration guide for details on updating your code.
35+
- `VoltageIntegralAxisAligned``AxisAlignedVoltageIntegral`
36+
- `CurrentIntegralAxisAligned``AxisAlignedCurrentIntegral`
37+
- `CustomPathIntegral2D``Custom2DPathIntegral`
38+
- `CustomVoltageIntegral2D``Custom2DVoltageIntegral`
39+
- `CustomCurrentIntegral2D``Custom2DCurrentIntegral`
40+
- Path integral and impedance calculator classes have been refactored and moved from the microwave plugin into Tidy3D components. They are now publicly exported via the top-level package `__init__.py`.
41+
- `WavePort` has been refactored to use `MicrowaveModeSpec`. The fields `voltage_integral`, and `current_integral` have been removed. Impedance specifications are now defined in `MicrowaveModeSpec.impedance_specs`. Please see our migration guide for details on updating your code.
3042

3143
### Changed
3244
- Improved performance of antenna metrics calculation by utilizing cached wave amplitude calculations instead of recomputing wave amplitudes for each port excitation in the `TerminalComponentModelerData`.
3345
- Changed hashing method in `Tidy3dBaseModel` from sha256 to md5.
3446
- Allowing for more geometries in a ClipOperation geometry.
3547
- Improved the speed of computing `Box` shape derivatives when used inside a `GeometryGroup`.
3648
- All RF and microwave specific components now inherit from `MicrowaveBaseModel`.
37-
- **[BREAKING]** Renamed path integral classes in `tidy3d.plugins.microwave` for improved consistency. Please see our migration guide for details on updating your code.
38-
- `VoltageIntegralAxisAligned``AxisAlignedVoltageIntegral`
39-
- `CurrentIntegralAxisAligned``AxisAlignedCurrentIntegral`
40-
- `CustomPathIntegral2D``Custom2DPathIntegral`
41-
- `CustomVoltageIntegral2D``Custom2DVoltageIntegral`
42-
- `CustomCurrentIntegral2D``Custom2DCurrentIntegral`
43-
- Path integral and impedance calculator classes have been refactored and moved from `tidy3d.plugins.microwave` to `tidy3d.components.microwave`. They are now publicly exported via the top-level package `__init__.py`, so you can import them directly, e.g. `from tidy3d import ImpedanceCalculator, AxisAlignedVoltageIntegral, AxisAlignedCurrentIntegral, Custom2DVoltageIntegral, Custom2DCurrentIntegral, Custom2DPathIntegral`.
4449
- `DirectivityMonitor` now forces `far_field_approx` to `True`, which was previously configurable.
4550
- Unified run submission API: `web.run(...)` is now a container-aware wrapper that accepts a single simulation or arbitrarily nested containers (`list`, `tuple`, `dict` values) and returns results in the same shape.
4651
- `web.Batch(ComponentModeler)` and `web.Job(ComponentModeler)` native support

docs/api/microwave/microwave_migration.rst

Lines changed: 314 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,27 @@
11
.. _microwave_migration:
22

3-
v2.10 Refactor Migration
4-
-------------------------
3+
v2.10 Refactor Migration Guide
4+
-------------------------------
55

6-
In version ``v2.10.0``, microwave plugin path integral classes were renamed for improved consistency and clarity. Additionally, these classes (and the impedance calculator) were refactored out of the plugin and moved into ``tidy3d.components.microwave`` and re-exported at the top-level package for convenience. This guide helps you update your scripts to use the new class names and imports.
6+
In version ``v2.10.0``, the microwave and RF simulation capabilities underwent significant refactoring to improve consistency, clarity, and functionality. This guide covers two major sets of breaking changes:
77

8-
Key Changes
9-
~~~~~~~~~~~
8+
1. **Path Integral Class Renames** - Classes were renamed for consistency
9+
2. **WavePort API Changes** - WavePort was refactored to support multiple modes with cleaner impedance specification
10+
11+
This guide helps you update your scripts to work with v2.10+.
12+
13+
1. Path Integral Class Renames
14+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
15+
16+
Path integral classes were renamed for improved consistency and clarity. Additionally, these classes (and the impedance calculator) were refactored out of the plugin and re-exported at the top-level package for convenience.
17+
18+
**Key changes:**
1019

1120
* **Renamed Classes**: Path integral classes have been renamed to follow a consistent naming pattern.
1221
* **New Import Path (simplified)**: The path integral classes and impedance calculator are now exported at the top level. Prefer importing directly from ``tidy3d`` (e.g., ``from tidy3d import AxisAlignedVoltageIntegral, ImpedanceCalculator``). Existing plugin imports continue to work for backwards compatibility where applicable.
1322

1423
Class Name Changes
15-
~~~~~~~~~~~~~~~~~~
24+
^^^^^^^^^^^^^^^^^^
1625

1726
The following classes have been renamed for consistency:
1827

@@ -31,7 +40,7 @@ The following classes have been renamed for consistency:
3140
* ``CustomPathIntegral2D`` → ``Custom2DPathIntegral``
3241

3342
Migration Examples
34-
~~~~~~~~~~~~~~~~~~
43+
^^^^^^^^^^^^^^^^^^
3544

3645
**Before (v2.9.x):**
3746

@@ -76,7 +85,7 @@ Migration Examples
7685
)
7786
7887
Custom 2D Path Integrals
79-
~~~~~~~~~~~~~~~~~~~~~~~~~
88+
""""""""""""""""""""""""
8089

8190
**Before:**
8291

@@ -125,6 +134,302 @@ Custom 2D Path Integrals
125134
)
126135
127136
Summary
128-
~~~~~~~
137+
^^^^^^^
129138

130139
All functionality remains the same—only class names and preferred import paths have changed. Update your imports to the top level (``from tidy3d import ...``) and class names according to the table above, and your code will work with v2.10. For impedance calculations, import ``ImpedanceCalculator`` directly via ``from tidy3d import ImpedanceCalculator``.
140+
141+
2. WavePort API Changes for Multimodal Support
142+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
143+
144+
The ``WavePort`` class was refactored to support multiple modes and to provide cleaner integration with the microwave mode solver. This required several breaking changes to the API.
145+
146+
Overview of Changes
147+
^^^^^^^^^^^^^^^^^^^
148+
149+
The key improvements in v2.10 include:
150+
151+
* **Multimodal support**: WavePorts can now excite and monitor multiple modes simultaneously
152+
* **Cleaner impedance specification**: Voltage and current path integrals are now specified via ``MicrowaveModeSpec`` instead of directly on the port
153+
* **Mode selection at source creation**: The mode to excite is now specified when creating a source, not when defining the port
154+
* **Improved type safety**: Uses ``MicrowaveModeSpec`` and ``MicrowaveModeMonitor`` for clearer RF-specific behavior
155+
156+
Removed Fields
157+
^^^^^^^^^^^^^^
158+
159+
The following fields have been **removed** from ``WavePort``:
160+
161+
* ``voltage_integral: Optional[VoltageIntegralType]`` - Path integral for voltage calculation
162+
* ``current_integral: Optional[CurrentIntegralType]`` - Path integral for current calculation
163+
164+
Changed Fields
165+
^^^^^^^^^^^^^^
166+
167+
* ``mode_index`` - The behavior has changed. Previously a required ``int`` specifying which single mode to use (default: 0). Now an optional ``Union[int, tuple[int, ...]]`` that selects which modes to monitor and excite. When ``None`` (default), all modes from ``mode_spec`` are used.
168+
169+
New MicrowaveModeSpec Integration
170+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
171+
172+
The ``mode_spec`` field now requires a ``MicrowaveModeSpec`` (instead of the generic ``ModeSpec``). This new class includes:
173+
174+
* ``impedance_specs``: Defines how to compute voltage, current, and characteristic impedance for each mode
175+
176+
Migration Examples
177+
^^^^^^^^^^^^^^^^^^
178+
179+
Single-Mode WavePort
180+
""""""""""""""""""""
181+
182+
**Before (v2.9.x):**
183+
184+
.. code-block:: python
185+
186+
import tidy3d as td
187+
from tidy3d.plugins.smatrix import WavePort
188+
189+
# Define path integrals
190+
voltage_path = td.AxisAlignedVoltageIntegralSpec(
191+
center=(0, 0, 0),
192+
size=(1.0, 0, 0),
193+
sign="+",
194+
)
195+
196+
current_path = td.Custom2DCurrentIntegralSpec.from_circular_path(
197+
center=(0, 0, 0),
198+
radius=0.5,
199+
num_points=21,
200+
normal_axis=2,
201+
clockwise=False
202+
)
203+
204+
# Create WavePort - path integrals attached directly to port
205+
port = WavePort(
206+
center=(0, 0, -5),
207+
size=(2, 2, 0),
208+
direction="+",
209+
mode_spec=td.ModeSpec(num_modes=1), # Generic ModeSpec
210+
mode_index=0, # Which mode to excite
211+
voltage_integral=voltage_path, # Attached to port
212+
current_integral=current_path, # Attached to port
213+
name="port1"
214+
)
215+
216+
**After (v2.10+):**
217+
218+
.. code-block:: python
219+
220+
import tidy3d as td
221+
from tidy3d.plugins.smatrix import WavePort
222+
223+
# Define path integrals (same as before)
224+
voltage_path = td.AxisAlignedVoltageIntegralSpec(
225+
center=(0, 0, 0),
226+
size=(1.0, 0, 0),
227+
sign="+",
228+
)
229+
230+
current_path = td.Custom2DCurrentIntegralSpec.from_circular_path(
231+
center=(0, 0, 0),
232+
radius=0.5,
233+
num_points=21,
234+
normal_axis=2,
235+
clockwise=False
236+
)
237+
238+
# Create WavePort - path integrals now in MicrowaveModeSpec
239+
port = WavePort(
240+
center=(0, 0, -5),
241+
size=(2, 2, 0),
242+
direction="+",
243+
mode_spec=td.MicrowaveModeSpec( # Use MicrowaveModeSpec
244+
num_modes=1,
245+
impedance_specs=td.CustomImpedanceSpec(
246+
voltage_spec=voltage_path, # Moved to impedance_specs
247+
current_spec=current_path
248+
)
249+
),
250+
name="port1"
251+
# Note: mode_index, voltage_integral, current_integral removed
252+
)
253+
254+
# Mode selection now happens at source creation
255+
source_time = td.GaussianPulse(freq0=10e9, fwidth=1e9)
256+
source = port.to_source(source_time, mode_index=0) # Mode selected here
257+
258+
Multi-Mode WavePort (New Feature!)
259+
"""""""""""""""""""""""""""""""""""
260+
261+
The new API enables WavePorts to support multiple modes simultaneously:
262+
263+
.. code-block:: python
264+
265+
import tidy3d as td
266+
from tidy3d.plugins.smatrix import WavePort
267+
268+
# Create a 3-mode WavePort
269+
port = WavePort(
270+
center=(0, 0, -5),
271+
size=(4, 4, 0),
272+
direction="+",
273+
mode_spec=td.MicrowaveModeSpec(
274+
num_modes=3, # Solve for 3 modes
275+
impedance_specs=td.AutoImpedanceSpec() # Auto-compute impedance for all modes
276+
),
277+
name="multimode_port"
278+
)
279+
280+
# Create sources for different modes
281+
source_time = td.GaussianPulse(freq0=10e9, fwidth=1e9)
282+
source_mode0 = port.to_source(source_time, mode_index=0) # Excite mode 0
283+
source_mode1 = port.to_source(source_time, mode_index=1) # Excite mode 1
284+
source_mode2 = port.to_source(source_time, mode_index=2) # Excite mode 2
285+
286+
Per-Mode Impedance Specifications
287+
""""""""""""""""""""""""""""""""""
288+
289+
For advanced use cases, you can specify different impedance calculation methods for each mode:
290+
291+
.. code-block:: python
292+
293+
# Define custom specs for mode 0, auto for modes 1 and 2
294+
port = WavePort(
295+
center=(0, 0, -5),
296+
size=(4, 4, 0),
297+
direction="+",
298+
mode_spec=td.MicrowaveModeSpec(
299+
num_modes=3,
300+
impedance_specs=(
301+
td.CustomImpedanceSpec(
302+
voltage_spec=custom_voltage_path,
303+
current_spec=custom_current_path
304+
), # Mode 0 uses custom specs
305+
td.AutoImpedanceSpec(), # Mode 1 uses auto
306+
td.AutoImpedanceSpec(), # Mode 2 uses auto
307+
)
308+
),
309+
name="mixed_impedance_port"
310+
)
311+
312+
Method Changes
313+
^^^^^^^^^^^^^^
314+
315+
``compute_port_impedance()`` → ``get_port_impedance()``
316+
""""""""""""""""""""""""""""""""""""""""""""""""""""""""
317+
318+
The method for retrieving port impedance has been renamed and now requires a ``mode_index`` parameter:
319+
320+
**Before (v2.9.x):**
321+
322+
.. code-block:: python
323+
324+
# Get impedance - implicitly used port.mode_index
325+
Z0 = port.compute_port_impedance(sim_data)
326+
327+
**After (v2.10+):**
328+
329+
.. code-block:: python
330+
331+
# Get impedance for mode 0
332+
Z0_mode0 = port.get_port_impedance(sim_data, mode_index=0)
333+
334+
# Get impedance for mode 1 (multimodal port)
335+
Z0_mode1 = port.get_port_impedance(sim_data, mode_index=1)
336+
337+
Voltage and Current Computation Shape Changes
338+
""""""""""""""""""""""""""""""""""""""""""""""
339+
340+
For multimodal ports, ``compute_voltage()`` and ``compute_current()`` now return data for **all modes** with a ``mode_index`` dimension:
341+
342+
**Before (v2.9.x):**
343+
344+
.. code-block:: python
345+
346+
# Returns shape: (n_freqs,) - single mode only
347+
voltage = port.compute_voltage(sim_data)
348+
current = port.compute_current(sim_data)
349+
350+
**After (v2.10+):**
351+
352+
.. code-block:: python
353+
354+
# For single-mode port: Returns shape: (n_freqs, 1)
355+
# For multi-mode port: Returns shape: (n_freqs, n_modes)
356+
voltage = port.compute_voltage(sim_data)
357+
current = port.compute_current(sim_data)
358+
359+
# Select specific mode using xarray selection
360+
voltage_mode0 = voltage.sel(mode_index=0)
361+
voltage_mode1 = voltage.sel(mode_index=1)
362+
363+
Using AutoImpedanceSpec for Simplified Setup
364+
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
365+
366+
For most cases, you can use ``AutoImpedanceSpec`` which automatically computes voltage, current, and impedance from the electromagnetic fields:
367+
368+
.. code-block:: python
369+
370+
# Simplest form - let Tidy3D auto-compute everything
371+
port = WavePort(
372+
center=(0, 0, -5),
373+
size=(2, 2, 0),
374+
direction="+",
375+
mode_spec=td.MicrowaveModeSpec(
376+
num_modes=2,
377+
impedance_specs=td.AutoImpedanceSpec() # Works for all modes
378+
),
379+
name="simple_port"
380+
)
381+
382+
Breaking Changes Summary
383+
^^^^^^^^^^^^^^^^^^^^^^^^
384+
385+
The following table summarizes all breaking changes to the ``WavePort`` API:
386+
387+
.. list-table::
388+
:header-rows: 1
389+
:widths: 30 30 40
390+
391+
* - Change
392+
- Old (v2.9.x)
393+
- New (v2.10+)
394+
* - Port field: mode_index
395+
- Required ``int``, single mode (default: 0)
396+
- Optional ``Union[int, tuple[int, ...]]``, selects modes to monitor/excite (default: ``None`` = all modes)
397+
* - Port field: voltage_integral
398+
- ``voltage_integral=path``
399+
- Removed. Use ``mode_spec.impedance_specs``
400+
* - Port field: current_integral
401+
- ``current_integral=path``
402+
- Removed. Use ``mode_spec.impedance_specs``
403+
* - mode_spec type
404+
- ``ModeSpec``
405+
- ``MicrowaveModeSpec``
406+
* - Impedance method
407+
- ``compute_port_impedance(sim_data)``
408+
- ``get_port_impedance(sim_data, mode_index)``
409+
* - Monitor type
410+
- Returns ``ModeMonitor``
411+
- Returns ``MicrowaveModeMonitor``
412+
* - Voltage/current shape
413+
- ``(n_freqs,)`` - single mode
414+
- ``(n_freqs, n_modes)`` - all modes
415+
* - Multimodal support
416+
- Not supported
417+
- Fully supported via ``num_modes``
418+
419+
Benefits of the New API
420+
^^^^^^^^^^^^^^^^^^^^^^^^
421+
422+
The refactored API provides several advantages:
423+
424+
* **Multimodal ports**: Support for multiple modes enables more accurate modeling of multimode waveguides and transmission lines
425+
* **Cleaner separation of concerns**: Mode selection happens at excitation time, not port definition time
426+
* **Type safety**: ``MicrowaveModeSpec`` and ``MicrowaveModeMonitor`` make RF-specific behavior explicit
427+
* **Flexibility**: Per-mode impedance specifications allow fine-grained control
428+
* **Consistency**: Aligns with the general pattern of ``ModeSource`` where ``mode_index`` is a source parameter
429+
430+
Backward Compatibility
431+
^^^^^^^^^^^^^^^^^^^^^^
432+
433+
There is **no backward compatibility** for WavePort instantiation with the old field names (``voltage_integral``, ``current_integral``). Attempting to use these fields will result in a Pydantic validation error.
434+
435+
The ``mode_index`` field has **changed behavior**: In v2.9, it was a required ``int`` specifying a single mode. In v2.10, it is optional and can select multiple modes via a tuple. Code using ``mode_index=0`` will work but now means "only use mode 0" rather than being the default.

0 commit comments

Comments
 (0)