Skip to content

RF Simulation Overview

This page introduces the basic concepts of running an RF simulation in Flexcompute RF (also Flex RF).

The following are the default units used by the RF solver, largely based on the SI convention. The key difference is that the default length unit is the micrometer (10610^{-6} meters).

Physical quantityDefault unit
Lengthmicrometer (um)
Timesecond (s)
CurrentAmpere (A)

The following constants are available, defined based on the default unit system.

ConstantDescriptionUnit
C_0Speed of lightum/s
EPSILON_0Permittivity of free spaceF/um
MU_0Permeability of free spaceH/um
ETA_0Impedance of free spaceOhm
infInfinity (effective)None

Finally, note that the RF solver uses the physics phase convention eiωte^{-i\omega t}, rather than the engineering phase convention eiωte^{i\omega t}. This means that complex quantities like S-, Z-, Y-parameters, mode propagation constant, as well as certain lossy material coefficients may require complex conjugation when interfacing with external data.

The TerminalComponentModeler is the core simulation object for 3D microwave analysis. Its main function is to perform a sweep over N ports/modes across M frequency points, then return an M x N x N S-parameter matrix.

TerminalComponentModeler
├── simulation # Base simulation
│ ├── center, size # Simulation domain center and size
│ ├── structures # List of physical structures (geometry + medium)
│ ├── monitors # List of user-specified monitors
│ ├── grid_spec # Specification for FDTD grid
│ ├── boundary_spec # Specification for external boundary conditions
│ ├── lumped_elements # List of lumped elements (if present)
│ ├── run_time # Length of simulated time (unit: s)
│ └── ...
├── ports # List of ports to sweep
├── freqs # List of frequency points
├── radiation_monitors # Monitor(s) for antenna FoM calculation
└── ...

The TerminalComponentModeler contains a base Simulation object along with the port and frequency list information. The base Simulation contains the physical structures, monitors, lumped elements, grid and boundary settings, as well as various other detailed settings.

The typical workflow is to first construct the base Simulation object. Some key parameters include:

# Define the base simulation
my_sim = Simulation(
center=(0,0,0),
size=(1e5, 1e5, 1e4),
structures=my_structure_list,
monitors=my_monitor_list,
grid_spec=my_grid_spec,
run_time=1e-9,
)

The TerminalComponentModeler is then constructed from the base Simulation. Key parameters at this stage include:

  • the base Simulation
  • list of ports
  • list of frequency points
# Defining the TerminalComponentModeler
my_tcm = TerminalComponentModeler(
simulation=my_sim,
ports=[port_1, port_2],
freqs=np.linspace(f_min, f_max, 401)
)

Use the plot_sim() method in TerminalComponentModeler to visualize the simulation domain in a 2D cross-section along the x, y or z axis. Use the plot_grid() method in the base Simulation to visualize the simulation grid.

fig, ax = plt.subplots(figsize=(8,6))
# Plot the simulation grid at z = 30um
my_tcm.simulation.plot_grid(z=30, ax=ax)
# Also plot the simulation domain (structures, ports, monitors etc.) at z = 30um
my_tcm.plot_sim(z=30, ax=ax)
plt.show()

Use the plot_3d() method in the base Simulation to open a 3D viewer in a Jupyter notebook.

# Launch inline 3D viewer in the Jupyter notebook
my_tcm.simulation.plot_3d()

The default output of a TerminalComponentModeler simulation is the full S-matrix.

# Submit a TCM job
my_tcm_data = web.run(my_tcm, task_name='my tcm job')
# After job completion, access the S-matrix
s_matrix = my_tcm_data.smatrix()

The S-matrix is stored in an xarray-based format. Individual S_ij entries can be accessed using the sel or isel methods. Broadcasting with numpy math methods is supported.

# Access S11 using port index (zero-indexed)
S11 = s_matrix.data.isel(port_in=0, port_out=0)
# Access S11 using port names
S11 = s_matrix.data.sel(port_in='my port 1', port_out='my port 1')
# Calculate the dB value of S11
S11dB = 20*np.log10(np.abs(S11))

If the base Simulation contains user-specified monitors, the monitor data is recorded for every instance of the port/mode sweep. In other words, if there are two single-mode ports in a TerminalComponentModeler instance, there will be two sets of recorded monitor data corresponding to each port excitation. These sets of data are stored in a dictionary format.

# Access the monitors dataset corresponding to 'port_1' excitation
my_sim_data = my_tcm_data['port_1']
# Plot the field from a user-defined FieldMonitor 'my_monitor_1'
my_sim_data.plot_field(field_monitor_name="my_monitor_1", field_name="E", val="abs", f=f_min, ...)
# Access data from a user-defined FieldMonitor 'my_monitor_1'
Ex = my_sim_data['my_monitor_1'].Ex
Ey = my_sim_data['my_monitor_1'].Ey
Ez = my_sim_data['my_monitor_1'].Ez

The example code above shows how to access the data from a specific user-defined FieldMonitor corresponding to a specific port excitation. Note that different monitor types may have different data access patterns.

The ModeSolver is the core simulation object for 2D mode analysis workflows.

ModeSolver
├── simulation # Base simulation
│ ├── center, size # Simulation domain center and size
│ ├── structures # List of physical structures (geometry + medium)
│ ├── grid_spec # Specification for FDTD grid
│ └── ...
├── mode_spec # Specification for the mode calculation
│ ├── num_modes # Number of modes to find
│ ├── target_neff # Initial search value for effective mode index
│ ├── sort_spec # Specification for mode sorting and filtering
│ └── ...
├── plane # 2D Box object that defines the mode solver plane
├── freqs # List of frequency points
├── direction # Direction of propagation along axis ('+' or '-')
└── ...

Like the TerminalComponentModeler, the ModeSolver also contains a base Simulation object.

While the user can manually define a ModeSolver object, a more common workflow is to create the ModeSolver from a pre-defined wave port object and base Simulation. A wave port (also known as a waveguide port in other literature) represents a modal- or terminal-based excitation in a 3D simulation. More information on wave ports can be found in this article.

# Define a Simulation
my_sim = Simulation(...)
# Define a wave port
my_port = WavePort(...)
# Define a mode solver simulation from a wave port
my_mode_study = my_port.to_mode_solver(
simulation=my_sim,
freqs=np.linspace(f_min, f_max, 21)
)

Use the plot() and plot_grid() methods of ModeSolver to visualize the 2D mode solver domain and simulation grid, respectively.

fig, ax = plt.subplots(figsize=(8,6))
# Plot the simulation grid in the mode solver plane
my_mode_study.plot_grid(ax=ax)
# Also plot the mode solver domain
my_mode_study.plot(ax=ax)
plt.show()

The default output of a ModeSolver simulation is the found eigenmodes (field profiles) and eigenvalues (complex propagation constants) of the 2D problem.

# Submit a mode solver job
my_mode_data = web.run(my_mode_study, task_name='my mode study')
# Plot the resulting field profile(s)
my_mode_study.plot_field(field_name="E", val="abs", f=f_max, mode_index=0)
# Access mode field components
mode_Ex = my_mode_data.Ex
mode_Ey = my_mode_data.Ey
mode_Ez = my_mode_data.Ez

The mode data contains datasets organized as xarray-type data structures. This means that utility functions like sel and isel can be used to filter based on coordinates. Common coordinates include

  • Mode index mode_index
  • Frequency f
  • Spatial coordinates x, y, z (where applicable)
# Get Ex component of mode 0 at f=f_min and x=10 micron
Ex = my_mode_data.Ex.sel(mode_index=0, f=f_min, x=10)

Additional physical quantities such as effective mode index, attenuation, and mode impedance are also pre-calculated.

# Access common transmission line quantities
n_eff = my_mode_data.n_eff # Effective index
gamma = my_mode_data.gamma # Complex propagation constant
Z0 = my_mode_data.transmission_line_data.Z0 # Characteristic impedance