The Tidy3D Web GUI provides a straightforward and user-friendly interface to build simulation models and visualize results.
However, for more advanced tasks—such as custom post-processing, batch analysis, or integrating results across multiple simulations—Python offers a powerful and flexible approach. With Python, the possibilities for post-processing are virtually limitless.
Tidy3D users need not be Python experts to do this. The Tidy3D Web GUI generates pre-built Python code snippets for most simulations, and you can run them directly in our Web Notebook environment. No local installation required.
In this notebook, we’ll walk through tips and examples for post-processing parametric sweep results from the Web GUI using Python. Most code snippets presented here can be easily adapted to your own projects.
We will cover the following topics:
1: Load results from the GUI.
2: Plot flux results on the same canvas.
3: Work with field data from a FieldMonitor for multiple simulations.
4: Work with data from a ModeMonitor for multiple simulations.
5: Work with data from a ModeSolver sweep.
We won’t focus on the simulation setup itself. Instead, we’ll use data from our All-dielectric Structural Colors example as a reference.
Let’s get started!
Loading Results From GUI ¶
Once you run a parametric sweep, you will see the Download Using Python button. This button generates a script as shown below.
import tidy3d as td
import tidy3d.web as web
design_task_id = [
'sw-5f2a3905-a835-46ea-9a5b-b33923925b454',
'sw-a05473ab-f17e-4b03-a6c4-53ae3df47f211',
'sw-9494afda-cb41-4c85-815f-a767d86a20805',
'sw-7126922d-64cd-4366-b6fe-1dde9055b4b63',
'sw-a07f0f54-b5f6-4b4b-804b-f5f8b8b3a6940',
'sw-b0d69553-4621-43cc-9f7c-6a4bd856f7c32',
]
for id, task_id in enumerate(design_task_id):
# Load and save the design optimization simulations
sim_data = web.load(task_id, path=f"./sim_{id}.hdf5", verbose=False)
# Your code to process sim_data
The logic is straightforward: it will automatically generate a list with of the task_id of all tasks in the sweep and load a SimulationData object for each one of them. The only thing left is to specify what should be done with each SimulationData object.
In the next sections, we will provide some common examples.
Plot Flux Results Together ¶
The flux spectrum is obtained from the SimulationData object by simply calling sim_data[monitor_name].flux
. To plot all the results on a single canvas, just call the flux.plot
method with the same ax
object.
The ax
variable refers to the Matplotlib axes object. Think of it as our canvas, where we will plot the data. This object lets us fully configure the graphic. For example, we can add a title, labels, and more, as we will demonstrate in this tutorial.
# variables to adapt to your simulation
monitor_name = "ref_spec"
# ------------------------------------
import matplotlib.pyplot as plt # Import matplotlib for plotting
import tidy3d as td
import tidy3d.web as web
design_task_id = [
"sw-5f2a3905-a835-46ea-9a5b-b33923925b454",
"sw-a05473ab-f17e-4b03-a6c4-53ae3df47f211",
"sw-9494afda-cb41-4c85-815f-a767d86a20805",
"sw-7126922d-64cd-4366-b6fe-1dde9055b4b63",
"sw-a07f0f54-b5f6-4b4b-804b-f5f8b8b3a6940",
"sw-b0d69553-4621-43cc-9f7c-6a4bd856f7c32",
]
ax = None # Create the ax variable
for id, task_id in enumerate(design_task_id):
# Load and save the design optimization simulations
sim_data = web.load(task_id, path=f"./sim_{id}.hdf5", verbose=False)
# Call the plot method with the ax variable
sim_data[monitor_name].flux.plot(ax=ax)
plt.show()

Customizing the Plot¶
For a more customizable plot, you can directly create the canvas using Matplotlib, and plot the frequencies (which can be converted to wavelength) on the x-axis and the flux on the y-axis:
# variables to adapt to your simulation
monitor_name = "ref_spec"
# ------------------------------------
import matplotlib.pyplot as plt # Import matplotlib for plotting
import tidy3d.web as web
design_task_id = [
"sw-5f2a3905-a835-46ea-9a5b-b33923925b454",
"sw-a05473ab-f17e-4b03-a6c4-53ae3df47f211",
"sw-9494afda-cb41-4c85-815f-a767d86a20805",
"sw-7126922d-64cd-4366-b6fe-1dde9055b4b63",
"sw-a07f0f54-b5f6-4b4b-804b-f5f8b8b3a6940",
"sw-b0d69553-4621-43cc-9f7c-6a4bd856f7c32",
]
for id, task_id in enumerate(design_task_id):
# Load and save the design optimization simulations
sim_data = web.load(task_id, path=f"./sim_{id}.hdf5", verbose=False)
# Process the simulation data
freqs = sim_data[monitor_name].flux.f # Get the frequencies
flux = sim_data[monitor_name].flux.values # Get the flux values
# Plot the flux spectrum as a function of wavelength
plt.plot(td.C_0 / freqs, flux, "-o") # Changing the line style to '-o'
# Add labels and title
plt.xlabel("Wavelength (µm)")
plt.ylabel("Flux (W/m$^2$)")
plt.title("Flux Spectrum for Different Designs")
plt.show()

Processing Field Data ¶
Another common use is to process field data from 2D field monitors. To plot the fields, you can simply call the SimulationData.plot_field method, specifying the monitor name, field component or field modulus to plot, and the type of data (real part, imaginary part, absolute value, etc.).
In the following example, we will plot the real part of the Ez field component for each monitor, side by side.
# Variables to adapt to your simulation
monitor_name = "field_R"
field_component = "Ez"
field_type = "real"
frequency = 475861056012720.6
# ------------------------------------
import matplotlib.pyplot as plt # Import matplotlib for plotting
import tidy3d as td
import tidy3d.web as web
design_task_id = [
"sw-5f2a3905-a835-46ea-9a5b-b33923925b454",
"sw-a05473ab-f17e-4b03-a6c4-53ae3df47f211",
"sw-9494afda-cb41-4c85-815f-a767d86a20805",
"sw-7126922d-64cd-4366-b6fe-1dde9055b4b63",
"sw-a07f0f54-b5f6-4b4b-804b-f5f8b8b3a6940",
"sw-b0d69553-4621-43cc-9f7c-6a4bd856f7c32",
]
fig, Ax = plt.subplots(ncols=6, figsize=(18, 3)) # width=18, height=3 in inches
for id, task_id in enumerate(design_task_id):
# Load and save the design optimization simulations
sim_data = web.load(task_id, path=f"./sim_{id}.hdf5", verbose=False)
# Plot the fields
sim_data.plot_field(monitor_name, field_component, field_type, f=frequency, ax=Ax[id])
plt.show()

Working With Components¶
To access field components from the field monitor, you just need to call sim_data[monitor_name].field_component
. With that, you have the freedom to perform any kind of operation with the field components.
In the next example, we will calculate |E|, use the sel
method to select the desired frequency and coordinate, and plot the x=0
phase profile of the $E_x$ component.
For this, we will use NumPy, a popular Python library for numerical computing, especially with arrays and matrices.
np.angle
returns the angle (or phase) of complex numbers, in radians.
np.unwrap
corrects phase angles to remove jumps greater than $\pi$, making the phase continuous.
# Variables to adapt to your simulation
monitor_name = "field_R"
field_component = "Ex"
field_type = "real"
frequency = 475861056012720.6
x_coordinate = 0
y_coordinate = 0
# ------------------------------------
import matplotlib.pyplot as plt # Import matplotlib for plotting
import numpy as np # Import numpy for numerical operations
import tidy3d as td
import tidy3d.web as web
design_task_id = [
"sw-5f2a3905-a835-46ea-9a5b-b33923925b454",
"sw-a05473ab-f17e-4b03-a6c4-53ae3df47f211",
"sw-9494afda-cb41-4c85-815f-a767d86a20805",
"sw-7126922d-64cd-4366-b6fe-1dde9055b4b63",
"sw-a07f0f54-b5f6-4b4b-804b-f5f8b8b3a6940",
"sw-b0d69553-4621-43cc-9f7c-6a4bd856f7c32",
]
fig, ax = plt.subplots() # Create a single axis for plotting
for id, task_id in enumerate(design_task_id):
# Load and save the design optimization simulations
sim_data = web.load(task_id, path=f"./sim_{id}.hdf5", verbose=False)
# Calculate E
field = sim_data[monitor_name].field_components[field_component]
selected_field = field.sel(f=frequency, x=x_coordinate, y=y_coordinate)
angle = np.angle(selected_field.values) # Calculate the phase angle
ax.plot(selected_field.z, np.unwrap(angle))
ax.set_xlabel("z (µm)")
ax.set_ylabel("Phase (radians)")
ax.set_title(f"Phase Profile of {field_component} at x={x_coordinate} µm, y={y_coordinate} µm")
plt.show()

Note that the field component can also be accessed as field = sim_data[monitor_name].Ex
.
Processing Mode Monitor Data ¶
Now, we demonstrate how to process mode data from a ModeMonitor. The ModeMonitor records a complete mode analysis for each mode, including amplitude, phase, effective index, etc.
In the following example, we will use data from our Edge Coupler example, where we analyze the modes as a function of the taper length.
Let's iterate through each design and plot the transmittance, which is simply the absolute square of the mode amplitude.
# Variables to adapt to your simulation
monitor_name = "mode_monitor"
mode_index = 0 # Index of the mode to analyze
direction = "+" # The direction of the mode, can be '+' or '-'
# ------------------------------------
import matplotlib.pyplot as plt # Import matplotlib for plotting
import tidy3d as td
import tidy3d.web as web
design_task_id = [
"sw-8738b266-4733-48da-aa13-11b4397872bf1",
"sw-e490dc15-217f-49b3-a4f5-0675c92d85b80",
"sw-b91352e9-fb81-4880-9394-3399fa724fc13",
"sw-f48edeeb-5372-4bfb-acbf-c7f3199d0fc52",
]
ax = None # Create the ax variable
for id, task_id in enumerate(design_task_id):
# Load and save the design optimization simulations
sim_data = web.load(task_id, path=f"./sim_{id}.hdf5", verbose=False)
# Plot the |amp|^2
mode_amplitudes = sim_data[monitor_name].amps.sel(mode_index=mode_index, direction=direction)
transmittance = mode_amplitudes.abs**2
ax = transmittance.plot(ax=ax)[0].axes # Get the axes from the plot
ax.set_ylabel("Transmittance")
plt.show()

Process Mode Solver Data ¶
We can also run sweeps using the ModeSolver.
In this case, it is useful to use the sim_data[monitor_name].to_dataframe()
method to create a Pandas DataFrame object containing full mode information, and easily access the real ("n eff") and imaginary ("k eff") parts of the effective index, as well as the mode area and group index if calculated in the simulation.
# Variables to adapt to your simulation
mode_index = 0 # Index of the mode to analyze
quantity_of_interest = "n eff" # Can be 'n eff', 'k eff' or 'mode area'
# ------------------------------------
import matplotlib.pyplot as plt # Import matplotlib for plotting
import tidy3d as td
import tidy3d.web as web
design_task_id = [
"sw-db60f8aa-3196-4361-b694-48f6100a4b474",
"sw-3340fb2a-9627-40f0-a1d0-b5d67128f78914",
"sw-8e22ae40-3b92-4221-9bf6-25c9104cd8ec11",
"sw-608fd3e1-ad5c-41ca-a382-247268204ffb3",
"sw-809cc1e1-0fd5-4f44-8454-fc1aea75df2e1",
"sw-0fb58701-c820-4090-a309-ebbda46a86ca12",
"sw-6dc7d32b-7ab6-49c3-8f64-168c9a54c8127",
"sw-82abae19-4978-47ee-aac8-cb8da40c9e5e13",
"sw-6d84d662-542c-46b6-91a6-718f84c12de12",
"sw-5402252e-ccf1-46c1-8a89-789f18cffbe910",
"sw-dc16e0a9-26c1-4e75-9564-36e6c7bdb05415",
"sw-3b550f02-3fa4-46d2-9a75-8f0aec1a3a5e6",
"sw-36823176-e570-4ebc-b339-f20ef0ef4b525",
"sw-d5bc6181-9e34-4212-a665-87c7be630e718",
"sw-723945f8-b3c0-4af6-a902-f204895bdc839",
"sw-0c6e28fa-c2bd-49a7-aca9-cb1946b27f210",
]
ax = None # Create the ax variable
for id, task_id in enumerate(design_task_id):
# Load and save the design optimization simulations
sim_data = web.load(task_id, path=f"./sim_{id}.hdf5", verbose=False)
# Create the DataFrame
dataframe = sim_data.to_dataframe()
# Select and plot the quantity of interest
ax = (
dataframe.xs(key=mode_index, level="mode_index")
.set_index("wavelength")[quantity_of_interest]
.plot(ax=ax)
)
ax.set_ylabel(quantity_of_interest)
plt.show()

The code snippet above can be easily adapted to extract and visualize different modal properties by simply modifying the quantity_of_interest
variable. Available quantities include:
Description |
quantity_of_interest value |
---|---|
Effective index (real part) | 'n eff' |
Effective index (imaginary part) | 'k eff' |
TE (Ey) polarization fraction | 'TE (Ey) fraction' |
Waveguide TE fraction | 'wg TE fraction' |
Waveguide TM fraction | 'wg TM fraction' |
Mode area | 'mode area' |
Group index | 'group index' |
Dispersion (ps/(nm·km)) | 'dispersion (ps/(nm km))' |
Just set quantity_of_interest
to any of the values above to obtain the corresponding data.