v2.10 RF 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 three 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

  3. Component Modeler Refactor - ComponentModeler classes were refactored for improved web support

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 โ€˜tidy3d.rfโ€™ sub-package . Prefer importing directly from tidy3d.rf (e.g., from tidy3d.rf 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.rf 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.rf 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.rf import ...) and class names according to the table above, and your code will work with v2.10.

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):

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

current_path = 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=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+):

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

current_path = 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=MicrowaveModeSpec(  # Use MicrowaveModeSpec
        num_modes=1,
        impedance_specs=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 = 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:

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

# Create sources for different modes
source_time = 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=MicrowaveModeSpec(
        num_modes=3,
        impedance_specs=(
            CustomImpedanceSpec(
                voltage_spec=custom_voltage_path,
                current_spec=custom_current_path
            ),  # Mode 0 uses custom specs
            AutoImpedanceSpec(),  # Mode 1 uses auto
            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=MicrowaveModeSpec(
        num_modes=2,
        impedance_specs=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

3. Component Modeler Refactor#

The smatrix plugin classes (ComponentModeler, TerminalComponentModeler, etc.) were refactored to improve web and GUI support for RF capabilities. This section helps you update your scripts to the new, more robust API.

Note

This content is also available as a standalone guide: v2.10 Refactor Migration


  • Rename: ComponentModeler has been renamed ModalComponentModeler.

  • Web Interface: Web parameters like verbose and path_dir are now passed to tidy3d.web.run() instead of the modeler class constructor.

  • Immutable Data Structures: Modeler classes are now immutable. ComponentModelerData classes hold simulation results, separating data from the modeler definition.

  • Explicit Method Calls: Running simulations and fetching results are now done through explicit function calls like tidy3d.web.run() and tidy3d.web.load().

Updated Workflow#

The new workflow is more explicit and aligns with the general tidy3d API.

Before (Old API):

import tidy3d.plugins.smatrix as sm

# Modeler class was mutable and included web parameters
tcm = sm.TerminalComponentModeler(
    simulation=sim,
    ports=[LP1, LP2],
    freqs=freqs,
    verbose=True,
    path_dir="data",
)
# The run method was part of the modeler class
s_matrix = tcm.run()

After (New API):

import tidy3d.web as web
# Rf classes now found in tidy3d.rf
import tidy3d.rf as rf

# Modeler class is now immutable and cleaner
tcm = rf.TerminalComponentModeler(
    simulation=sim,
    ports=[LP1, LP2],
    freqs=my_freqs,
)
# Use web.run to execute the simulation
modeler_data = web.run(tcm, verbose=True, path="data/modeler_data.hdf5")
s_matrix = modeler_data.smatrix()

Note

The S-matrix is now computed from the ComponentModelerData objects, so it is accessed via the .smatrix() method.

Cost Estimation#

Cost estimation is now done by uploading the modeler to the web API.

Before:

est_flex_credits = tcm.estimate_cost()
real_flex_credits = tcm.real_cost()

After:

task_id = web.upload(tcm)
est_flex_credits = web.estimate_cost(task_id)
# After the run is complete
real_flex_credits = web.real_cost(task_id)

Data Handling#

The new API introduces immutable data containers for simulation results, ensuring that your data is more predictable and easier to manage.

These data objects contain the S-matrix, port impedance, and other relevant results.

Before:

# batch_data was a mutable property of the modeler
tcm_batch = tcm.batch_data
sim_data = tcm_batch["smatrix_LP1"]

After:

# web.run returns an immutable data object
modeler_data = web.run(tcm)
# Access simulation data for each port
sim_data = modeler_data.data["smatrix_LP1"]

Migration Utilities#

To ease the transition, we provide utilities that mimic the old workflow.

Warning

These migration helpers are temporary and will be deprecated in a future release.

  • Run a batch of simulations: If you prefer to manage the batch run yourself, you can create and run a batch explicitly.

    from tidy3d.plugins.smatrix.run import create_batch
    
    batch = create_batch(modeler=my_modeler)
    batch_data = batch.run()
    
  • Compose data from a batch: If you have a tidy3d.web.BatchData object from a manual run, you can still create the corresponding ModalComponentModelerData or TerminalComponentModelerData object.

    from tidy3d.plugins.smatrix.run import compose_modeler_data_from_batch_data
    
    modeler_data = compose_modeler_data_from_batch_data(
        modeler=my_modeler, batch_data=batch_data
    )
    

API Reference#

For more details, see the API documentation for the new classes and functions:

tidy3d.plugins.smatrix.ModalComponentModeler

A tool for modeling devices and computing scattering matrix elements.

tidy3d.plugins.smatrix.ModalComponentModelerData

A data container for the results of a ModalComponentModeler run.

tidy3d.rf.TerminalComponentModeler

Tool for modeling two-terminal multiport devices and computing port parameters with lumped and wave ports.

tidy3d.rf.TerminalComponentModelerData

Data associated with a TerminalComponentModeler simulation run.

tidy3d.SimulationMap

An immutable dictionary-like container for simulations.

tidy3d.SimulationDataMap

An immutable dictionary-like container for simulation data.

tidy3d.plugins.smatrix.run.create_batch(...)

Create a simulation Batch from a component modeler.

tidy3d.plugins.smatrix.run.compose_modeler_data_from_batch_data(...)

Select the correct composer based on modeler type and create the data object.

tidy3d.plugins.smatrix.run.compose_modeler_data(...)

Create a modeler data object from a modeler and indexed simulation data.