v2.10 Refactor Migration Guide#

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:

  1. Path Integral Class Renames - Classes were renamed for consistency

  2. WavePort API Changes - WavePort was refactored to support multiple modes with cleaner impedance specification

This guide helps you update your scripts to work with v2.10+.

1. Path Integral Class Renames#

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.

Key changes:

  • Renamed Classes: Path integral classes have been renamed to follow a consistent naming pattern.

  • 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.

Class Name Changes#

The following classes have been renamed for consistency:

Voltage Integrals:

  • VoltageIntegralAxisAligned β†’ AxisAlignedVoltageIntegral

  • CustomVoltageIntegral2D β†’ Custom2DVoltageIntegral

Current Integrals:

  • CurrentIntegralAxisAligned β†’ AxisAlignedCurrentIntegral

  • CustomCurrentIntegral2D β†’ Custom2DCurrentIntegral

Path Integrals:

  • CustomPathIntegral2D β†’ Custom2DPathIntegral

Migration Examples#

Before (v2.9.x):

from tidy3d.plugins.microwave import (
    VoltageIntegralAxisAligned,
    CurrentIntegralAxisAligned,
)

voltage_path = VoltageIntegralAxisAligned(
    center=(0, 0, 0),
    size=(0, 0, 1),
    sign="+",
)

current_path = CurrentIntegralAxisAligned(
    center=(0, 0, 0),
    size=(2, 1, 0),
    sign="+",
)

After (v2.10+):

from tidy3d import (
    AxisAlignedVoltageIntegral,
    AxisAlignedCurrentIntegral,
)

voltage_path = AxisAlignedVoltageIntegral(
    center=(0, 0, 0),
    size=(0, 0, 1),
    sign="+",
)

current_path = AxisAlignedCurrentIntegral(
    center=(0, 0, 0),
    size=(2, 1, 0),
    sign="+",
)

Custom 2D Path Integrals#

Before:

from tidy3d.plugins.microwave import (
    CustomVoltageIntegral2D,
    CustomCurrentIntegral2D,
)

vertices = [[0, 0], [1, 0], [1, 1], [0, 1]]

voltage_path = CustomVoltageIntegral2D(
    axis=2,
    position=0.0,
    vertices=vertices,
)

current_path = CustomCurrentIntegral2D(
    axis=2,
    position=0.0,
    vertices=vertices,
)

After:

from tidy3d import (
    Custom2DVoltageIntegral,
    Custom2DCurrentIntegral,
)

vertices = [[0, 0], [1, 0], [1, 1], [0, 1]]

voltage_path = Custom2DVoltageIntegral(
    axis=2,
    position=0.0,
    vertices=vertices,
)

current_path = Custom2DCurrentIntegral(
    axis=2,
    position=0.0,
    vertices=vertices,
)

Summary#

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.

2. WavePort API Changes for Multimodal Support#

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.

Overview of Breaking Changes#

The WavePort refactor introduces the following breaking changes:

  • Removed fields: voltage_integral and current_integral fields are removed from WavePort

  • Impedance specification moved: Voltage and current path integrals are now specified via MicrowaveModeSpec.impedance_specs instead of directly on the port

  • ModeSpec type changed: mode_spec field now requires MicrowaveModeSpec instead of generic ModeSpec

  • Method renamed: compute_port_impedance() renamed to get_port_impedance() with new signature

  • Return shapes changed: compute_voltage() and compute_current() now return data with a mode_index dimension

  • Deprecated field: mode_index field is deprecated (still works but triggers warning)

Removed Fields#

The following fields have been removed from WavePort:

  • voltage_integral: Optional[VoltageIntegralType] - Path integral for voltage calculation

  • current_integral: Optional[CurrentIntegralType] - Path integral for current calculation

Deprecated Fields#

  • mode_index: Optional[int] - Deprecated field that previously specified which single mode to use (default was 0 in v2.9). Still works for backward compatibility but triggers a deprecation warning. Will be removed in a future version. Migration: For backwards compatible code, omit this field entirely. For forward compatible code, use mode_selection instead if you need to select specific modes.

New MicrowaveModeSpec Integration#

The mode_spec field now requires a MicrowaveModeSpec (instead of the generic ModeSpec). This new class includes:

  • impedance_specs: Defines how to compute voltage, current, and characteristic impedance for each mode

Migration Examples#

Single-Mode WavePort#

Before (v2.9.x):

import tidy3d as td
from tidy3d.plugins.smatrix import WavePort

# Define path integrals
voltage_path = td.AxisAlignedVoltageIntegralSpec(
    center=(0, 0, 0),
    size=(1.0, 0, 0),
    sign="+",
)

current_path = td.Custom2DCurrentIntegralSpec.from_circular_path(
    center=(0, 0, 0),
    radius=0.5,
    num_points=21,
    normal_axis=2,
    clockwise=False
)

# Create WavePort - path integrals attached directly to port
port = WavePort(
    center=(0, 0, -5),
    size=(2, 2, 0),
    direction="+",
    mode_spec=td.ModeSpec(num_modes=1),  # Generic ModeSpec
    mode_index=0,  # Which mode to excite
    voltage_integral=voltage_path,  # Attached to port
    current_integral=current_path,  # Attached to port
    name="port1"
)

After (v2.10+):

import tidy3d as td
from tidy3d.plugins.smatrix import WavePort

# Define path integrals (same as before)
voltage_path = td.AxisAlignedVoltageIntegralSpec(
    center=(0, 0, 0),
    size=(1.0, 0, 0),
    sign="+",
)

current_path = td.Custom2DCurrentIntegralSpec.from_circular_path(
    center=(0, 0, 0),
    radius=0.5,
    num_points=21,
    normal_axis=2,
    clockwise=False
)

# Create WavePort - path integrals now in MicrowaveModeSpec
port = WavePort(
    center=(0, 0, -5),
    size=(2, 2, 0),
    direction="+",
    mode_spec=td.MicrowaveModeSpec(  # Use MicrowaveModeSpec
        num_modes=1,
        impedance_specs=td.CustomImpedanceSpec(
            voltage_spec=voltage_path,  # Moved to impedance_specs
            current_spec=current_path
        )
    ),
    name="port1"
    # Note: mode_index, voltage_integral, current_integral removed
)

# Mode selection now happens at source creation
source_time = td.GaussianPulse(freq0=10e9, fwidth=1e9)
source = port.to_source(source_time, mode_index=0)  # Mode selected here

Multi-Mode WavePort (New Feature!)#

The new API enables WavePorts to support multiple modes simultaneously:

import tidy3d as td
from tidy3d.plugins.smatrix import WavePort

# Create a 3-mode WavePort
port = WavePort(
    center=(0, 0, -5),
    size=(4, 4, 0),
    direction="+",
    mode_spec=td.MicrowaveModeSpec(
        num_modes=3,  # Solve for 3 modes
        impedance_specs=td.AutoImpedanceSpec()  # Auto-compute impedance for all modes
    ),
    name="multimode_port"
)

# Create sources for different modes
source_time = td.GaussianPulse(freq0=10e9, fwidth=1e9)
source_mode0 = port.to_source(source_time, mode_index=0)  # Excite mode 0
source_mode1 = port.to_source(source_time, mode_index=1)  # Excite mode 1
source_mode2 = port.to_source(source_time, mode_index=2)  # Excite mode 2

Per-Mode Impedance Specifications#

For advanced use cases, you can specify different impedance calculation methods for each mode:

# Define custom specs for mode 0, auto for modes 1 and 2
port = WavePort(
    center=(0, 0, -5),
    size=(4, 4, 0),
    direction="+",
    mode_spec=td.MicrowaveModeSpec(
        num_modes=3,
        impedance_specs=(
            td.CustomImpedanceSpec(
                voltage_spec=custom_voltage_path,
                current_spec=custom_current_path
            ),  # Mode 0 uses custom specs
            td.AutoImpedanceSpec(),  # Mode 1 uses auto
            td.AutoImpedanceSpec(),  # Mode 2 uses auto
        )
    ),
    name="mixed_impedance_port"
)

Method Changes#

compute_port_impedance() β†’ get_port_impedance()#

The method for retrieving port impedance has been renamed and now requires a mode_index parameter:

Before (v2.9.x):

# Get impedance - implicitly used port.mode_index
Z0 = port.compute_port_impedance(sim_data)

After (v2.10+):

# Get impedance for mode 0
Z0_mode0 = port.get_port_impedance(sim_data, mode_index=0)

# Get impedance for mode 1 (multimodal port)
Z0_mode1 = port.get_port_impedance(sim_data, mode_index=1)

Voltage and Current Computation Shape Changes#

For multimodal ports, compute_voltage() and compute_current() now return data for all modes with a mode_index dimension:

Before (v2.9.x):

# Returns shape: (n_freqs,) - single mode only
voltage = port.compute_voltage(sim_data)
current = port.compute_current(sim_data)

After (v2.10+):

# For single-mode port: Returns shape: (n_freqs, 1)
# For multi-mode port: Returns shape: (n_freqs, n_modes)
voltage = port.compute_voltage(sim_data)
current = port.compute_current(sim_data)

# Select specific mode using xarray selection
voltage_mode0 = voltage.sel(mode_index=0)
voltage_mode1 = voltage.sel(mode_index=1)

Using AutoImpedanceSpec for Simplified Setup#

For most cases, you can use AutoImpedanceSpec which automatically computes voltage, current, and impedance from the electromagnetic fields:

# Simplest form - let Tidy3D auto-compute everything
port = WavePort(
    center=(0, 0, -5),
    size=(2, 2, 0),
    direction="+",
    mode_spec=td.MicrowaveModeSpec(
        num_modes=2,
        impedance_specs=td.AutoImpedanceSpec()  # Works for all modes
    ),
    name="simple_port"
)

Breaking Changes Summary#

The following table summarizes all breaking changes to the WavePort API:

Change

Old (v2.9.x)

New (v2.10+)

Port field: mode_index

Required int (default: 0)

Deprecated Optional[int]. Omit this field.

Port field: mode_selection

Did not exist

NEW Optional[tuple[int, ...]] to select specific modes (default: None = all modes)

Port field: voltage_integral

voltage_integral=path

Removed. Use mode_spec.impedance_specs

Port field: current_integral

current_integral=path

Removed. Use mode_spec.impedance_specs

mode_spec type

ModeSpec

MicrowaveModeSpec

Impedance method

compute_port_impedance(sim_data)

get_port_impedance(sim_data, mode_index)

Monitor type

Returns ModeMonitor

Returns MicrowaveModeMonitor

Voltage/current shape

(n_freqs,)

(n_freqs, n_modes) - added mode_index dimension

Multimodal support

Not supported

Fully supported via mode_spec.num_modes

Benefits of the New API#

The refactored API provides several advantages:

  • Multimodal ports: Support for multiple modes enables more accurate modeling of multimode waveguides and transmission lines

  • Cleaner separation of concerns: Mode selection happens at excitation time, not port definition time

  • Type safety: MicrowaveModeSpec and MicrowaveModeMonitor make RF-specific behavior explicit

  • Flexibility: Per-mode impedance specifications allow fine-grained control

  • Consistency: Aligns with the general pattern of ModeSource where mode_index is a source parameter

Backward Compatibility#

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.

The mode_index field is deprecated but still functional for backward compatibility:

  • In v2.9: Required int field (default: 0) specifying which single mode to use

  • In v2.10+: Optional int field (still accepts single integer only) but triggers a deprecation warning

  • Migration: Simply omit the mode_index field from your WavePort definitions

The new mode_selection field replaces mode_index for selecting modes:

  • mode_selection: Optional[tuple[int, ...]] - New field that accepts a tuple of mode indices (e.g., (0, 2) to use modes 0 and 2)

  • When None (default), all modes from mode_spec.num_modes are used

  • Note: mode_index accepts only a single int, while mode_selection accepts a tuple for multiple modes