Skip to content

Commit ec88ce6

Browse files
tylerflexcopybara-bot
authored andcommitted
chore(tidy3d-client): add practical tips to docstrings and consolidate agentic workflow guide (#3780)
Compute-RevId: 5157340979fd74af3dfa66186c2e1a0e421d0322 Flex-RevId: fe5ca928d71c11b04a846a2a89c1a11239dd664d
1 parent e54b830 commit ec88ce6

File tree

12 files changed

+550
-7
lines changed

12 files changed

+550
-7
lines changed

docs/ai/index.rst

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,42 @@ Watch these demonstration videos to see Tidy3D + AI capabilities in action. Each
5454

5555
For more comprehensive tutorials and the complete video series, visit our `Tidy3D + AI Video Playlist <https://www.youtube.com/playlist?list=PL7kxN4u_N9HHb1QBPXhTlYEMjIt2SY1re>`_.
5656

57+
Using Tidy3D with AI Coding Agents
58+
-----------------------------------
59+
60+
The FlexAgent MCP works with any MCP-compatible AI coding agent — not just the Tidy3D IDE extensions. To get started:
61+
62+
1. **Set up the MCP** — follow the instructions in `FlexAgent MCP — Standalone MCP Client <flex_agent.html#standalone-mcp-client>`_ for your client (Claude Code, Codex CLI, Cursor, Gemini CLI, etc.).
63+
2. **Add a bootstrap file** — save the markdown below as ``AGENTS.md`` (or ``CLAUDE.md`` for Claude Code) in your project root directory. This tells the agent how to use the MCP effectively.
64+
3. **Review the simulation tips** — the `Simulation Tips for AI Agents <simulation_tips.html>`_ page covers workflow patterns, parameter sweeps, inverse design, and common pitfalls.
65+
66+
.. code-block:: markdown
67+
68+
# Tidy3D Agent Instructions
69+
70+
## MCP Setup
71+
72+
If the Tidy3D MCP server is not already configured, see:
73+
https://docs.flexcompute.com/projects/tidy3d/en/latest/ai/flex_agent.html#standalone-mcp-client
74+
75+
## Workflow
76+
77+
Use `search_flexcompute_docs` and `fetch_flexcompute_doc` from the Tidy3D MCP as
78+
your primary documentation source throughout the session. Before writing simulation
79+
code:
80+
81+
1. Search for "Simulation Tips for AI Agents" — workflow patterns, pitfalls,
82+
and checklists.
83+
2. Search for the relevant class docstrings — they contain selection guides,
84+
extraction patterns, and practical advice.
85+
3. Search for example notebooks related to the device or workflow you are building.
86+
5787
5888
.. toctree::
5989

6090
flex_agent
6191
3d_viewer
92+
simulation_tips
6293
cursor_extension
6394
vscode_extension
6495

docs/ai/simulation_tips.rst

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
Simulation Tips for AI Agents
2+
=============================
3+
4+
*Workflow patterns and runtime pitfalls for AI-assisted photonics simulation.*
5+
6+
This page collects practical workflow guidance that spans multiple Tidy3D classes and doesn't belong in any single docstring. For API-level guidance (which source to use, how to set up materials, grid resolution), see the class docstrings — they contain selection guides and tips under **Practical Advice** headings.
7+
8+
**Units**: length in micrometers, time in seconds, frequency in Hz.
9+
10+
.. contents:: On this page
11+
:local:
12+
:depth: 2
13+
14+
15+
Documentation Lookups with MCP
16+
------------------------------
17+
18+
The `FlexAgent MCP <flex_agent.html>`_ server is your primary source for API docs and examples. Always search it before writing simulation code.
19+
20+
.. list-table::
21+
:header-rows: 1
22+
:widths: 40 60
23+
24+
* - Tool
25+
- Purpose
26+
* - ``search_flexcompute_docs``
27+
- Search all Flexcompute docs and examples
28+
* - ``fetch_flexcompute_doc``
29+
- Fetch a specific documentation page by URL
30+
31+
**Search patterns:**
32+
33+
- Device type: ``"ring resonator tidy3d"``, ``"grating coupler SOI"``
34+
- API class: ``"ModeSource tidy3d"``, ``"FluxMonitor tidy3d"``
35+
- Workflow: ``"parameter sweep batch tidy3d"``, ``"adjoint inverse design tidy3d"``
36+
37+
**Decompose the request before searching:**
38+
39+
1. **Device** — what structure? Search ``"{device_type} tidy3d"``
40+
2. **FOM** — what to measure? Search ``"{monitor_type} tidy3d"``
41+
3. **Workflow** — single run, sweep, EME, or inverse design?
42+
43+
44+
Visualization-First Workflow
45+
----------------------------
46+
47+
Never run a simulation you haven't visually inspected. Generate plots liberally and inspect them — this is the cheapest way to catch errors.
48+
49+
1. **Plot geometry** — ``sim.plot_eps(x=0)``, ``sim.plot_eps(y=0)``, ``sim.plot_eps(z=0)``. Check: waveguides extend through PML, sources in straight sections, monitors correctly placed, dimensions look right.
50+
2. **Fix and re-plot** — if anything looks wrong, fix it and plot again. Do not skip to running the simulation.
51+
3. **After running, inspect results** — are T values physical (at most 1.0)? Do field profiles look reasonable? Any shutoff warnings? Do sweep trends make sense?
52+
4. **Stop if wrong** — do not continue to optimization or parameter sweeps on top of a broken setup.
53+
54+
55+
Parameter Sweeps
56+
----------------
57+
58+
Use ``web.run_async()`` to run multiple simulations in parallel:
59+
60+
.. code-block:: python
61+
62+
sims = {f"width_{w:.3f}": make_sim(w) for w in np.linspace(0.4, 0.6, 11)}
63+
batch_data = web.run_async(sims)
64+
for name, sim_data in batch_data.items():
65+
T = sim_data["transmission"].flux.values
66+
67+
68+
S-Parameter Extraction
69+
----------------------
70+
71+
Use the ``ModalComponentModeler`` plugin for multi-port S-parameters:
72+
73+
.. code-block:: python
74+
75+
from tidy3d.plugins.smatrix import ModalComponentModeler, Port
76+
77+
import tidy3d.web as web
78+
79+
ports = [
80+
Port(center=(-5, 1, 0), size=(0, 2, 2), mode_spec=td.ModeSpec(), direction="+", name="in1"),
81+
Port(center=(5, 1, 0), size=(0, 2, 2), mode_spec=td.ModeSpec(), direction="-", name="out1"),
82+
]
83+
modeler = ModalComponentModeler(simulation=sim, ports=ports, freqs=freqs)
84+
modeler_data = web.run(modeler)
85+
smatrix = modeler_data.smatrix()
86+
S21 = smatrix.loc[dict(port_in="in1", mode_index_in=0, port_out="out1", mode_index_out=0)]
87+
88+
89+
Resonator Q Factor
90+
------------------
91+
92+
Three methods:
93+
94+
1. **Spectral fitting** — fit a Lorentzian to the transmission dip near resonance.
95+
2. **Time-domain decay** — place a ``FieldTimeMonitor`` at the cavity center, fit exponential decay to extract ``Q = omega * tau / 2``.
96+
3. **ResonanceFinder plugin** — ``from tidy3d.plugins.resonance import ResonanceFinder``.
97+
98+
99+
EME Simulations
100+
---------------
101+
102+
EME (Eigenmode Expansion) is efficient for devices with slowly varying cross-sections — tapers, transitions, periodic structures.
103+
104+
.. code-block:: python
105+
106+
eme_sim = td.EMESimulation(
107+
size=(10, 4, 4),
108+
structures=[...], monitors=[...],
109+
grid_spec=td.GridSpec.auto(min_steps_per_wvl=20, wavelength=wavelength),
110+
axis=0,
111+
eme_grid_spec=td.EMEUniformGrid(
112+
num_cells=20, mode_spec=td.EMEModeSpec(num_modes=10)
113+
),
114+
freqs=[freq0],
115+
)
116+
eme_data = web.run(eme_sim, task_name="eme_taper")
117+
118+
EME can sweep device length without re-solving modes using ``sweep_spec=td.EMELengthSweep(scale_factors=np.linspace(0.5, 2.0, 20))``.
119+
120+
.. list-table::
121+
:header-rows: 1
122+
:widths: 50 50
123+
124+
* - Use EME when...
125+
- Use FDTD when...
126+
* - Device is long (>20 wavelengths)
127+
- Complex 3D scattering
128+
* - Cross-section changes slowly
129+
- Broadband response needed
130+
* - Need length optimization
131+
- Time-domain effects matter
132+
133+
134+
Inverse Design
135+
--------------
136+
137+
Tidy3D uses **HIPS autograd**, not JAX. The old ``tidy3d.plugins.adjoint`` plugin has been removed. Regular ``td.Simulation`` and ``web.run()`` are differentiable — no special classes needed.
138+
139+
.. code-block:: python
140+
141+
import autograd
142+
import autograd.numpy as anp
143+
# NOT: import jax
144+
# NOT: from tidy3d.plugins.adjoint import ...
145+
146+
For detailed patterns (filter and project, beta scheduling, learning rates, gradient sign conventions, traceable components, gotchas), see the autograd README in the Tidy3D source: ``tidy3d/plugins/autograd/README.md``.
147+
148+
149+
Pre-Flight Checklist
150+
--------------------
151+
152+
Run through before every simulation:
153+
154+
1. Waveguides extend through PML — use ``td.inf``
155+
2. Source/monitor in straight sections — not at bends or junctions
156+
3. Mode monitor sized 3-4x waveguide width
157+
4. Grid resolves features — at least 15-20 cells across smallest feature
158+
5. Transmission is physical — T at most 1.0
159+
6. ``RunTimeSpec`` or sufficient ``run_time`` — no shutoff warnings
160+
161+
162+
Common Failure Modes
163+
--------------------
164+
165+
.. list-table::
166+
:header-rows: 1
167+
:widths: 20 35 45
168+
169+
* - Symptom
170+
- Likely Cause
171+
- Fix
172+
* - T = 0
173+
- Source/monitor misaligned
174+
- Check geometry plot
175+
* - T > 1.0
176+
- Monitor direction wrong
177+
- Check ``direction`` parameter
178+
* - NaN
179+
- Simulation diverged
180+
- Check PML, resolution, dispersive materials
181+
* - Shutoff warning
182+
- ``run_time`` too short
183+
- Use ``RunTimeSpec`` with higher ``quality_factor``
184+
* - Wrong wavelength
185+
- ``n_eff`` guessed
186+
- Use ModeSolver to compute it
187+
* - Autograd TypeError
188+
- ``float()`` on traced param
189+
- Never cast traced params
190+
* - 0 gradient
191+
- Sim domain depends on params
192+
- Only geometry should be traced
193+
194+
195+
Coding Discipline
196+
-----------------
197+
198+
- **Never suppress warnings** — every Tidy3D warning tells you something is wrong. Read it, fix the root cause. Do not use ``warnings.filterwarnings("ignore")``.
199+
- **Never use try/except to mask failures** — if ``web.run()`` fails, the error tells you what's wrong. Catching and ignoring it means proceeding with garbage.
200+
- **Never guess physical parameters** — always compute ``n_eff``, grating periods, etc. using ModeSolver or equivalent. This is the number one source of wrong results.
201+
- **Fix warnings, don't work around them** — search the MCP for the warning text, understand the cause, fix it, verify it's gone.

tidy3d/components/boundary.py

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -802,8 +802,15 @@ class PML(AbsorberSpec):
802802
Note
803803
----
804804
805-
For best results, structures that intersect with the PML or simulation edges should extend extend all the way
806-
through. In many such cases, an “infinite” size ``td.inf`` can be used to define the size along that dimension.
805+
**Practical Advice**
806+
807+
For best results, structures that intersect with the PML or simulation edges should extend all the way
808+
through using ``td.inf``::
809+
810+
Structure(geometry=Box(size=(td.inf, width, height)), medium=core)
811+
812+
Structures that terminate inside PML can cause evanescent fields at the interface to be
813+
amplified by the absorber, potentially leading to simulation divergence.
807814
808815
Example
809816
-------
@@ -1373,6 +1380,16 @@ class BoundarySpec(Tidy3dBaseModel):
13731380
x=False, y=False, z=False)``. This will put :class:`PML` along the dimensions defined as ``True``,
13741381
and set periodic boundaries along the other dimensions.
13751382
1383+
**Practical Advice**
1384+
1385+
Common boundary configurations:
1386+
1387+
- **PML on all sides**: Default for most devices (waveguides, resonators, scatterers).
1388+
- **Periodic in x/y, PML in z**: Infinite planar arrays (gratings, metasurfaces).
1389+
- **Bloch in x/y, PML in z**: Oblique incidence on periodic structures.
1390+
1391+
Use :meth:`BoundarySpec.pml` as a convenience to set PML along selected dimensions and periodic
1392+
along the rest.
13761393
13771394
See Also
13781395
--------

tidy3d/components/grid/grid_spec.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,22 @@ def estimated_min_dl(
894894
class AutoGrid(AbstractAutoGrid):
895895
"""Specification for non-uniform grid along a given dimension.
896896
897+
Notes
898+
-----
899+
900+
**Practical Advice**
901+
902+
Recommended ``min_steps_per_wvl`` values:
903+
904+
- **10** (default): Quick estimates and sanity checks only.
905+
- **15-20**: Good accuracy for most simulations. Verify convergence by comparing results
906+
at two resolutions.
907+
- **Higher**: Only if convergence tests show the result hasn't settled. Can be expensive —
908+
use judiciously.
909+
910+
Ensure that geometric features (thin slabs, narrow gaps) are covered by at least 2 grid cells.
911+
For a typical 220 nm SOI waveguide, fewer than 10 cells in z is often sufficient.
912+
897913
Example
898914
-------
899915
>>> grid_1d = AutoGrid(min_steps_per_wvl=16, max_scale=1.4)
@@ -2422,6 +2438,17 @@ def _resolve_gaps(
24222438
class GridSpec(Tidy3dBaseModel):
24232439
"""Collective grid specification for all three dimensions.
24242440
2441+
Notes
2442+
-----
2443+
2444+
**Practical Advice**
2445+
2446+
When using :class:`AutoGrid`, the ``wavelength`` parameter determines the scale for automatic meshing.
2447+
If omitted, it is inferred from sources in the simulation. For simulations without sources or where
2448+
finer control is needed, set it explicitly::
2449+
2450+
grid_spec = GridSpec.auto(min_steps_per_wvl=20, wavelength=1.55)
2451+
24252452
Example
24262453
-------
24272454
>>> uniform = UniformGrid(dl=0.1)

tidy3d/components/medium.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1360,6 +1360,33 @@ class Medium(AbstractMedium):
13601360
13611361
D(t) = \\epsilon E(t)
13621362
1363+
The ``permittivity`` parameter is the relative permittivity (dimensionless). The ``conductivity``
1364+
parameter has units of S/μm (siemens per micrometer), consistent with Tidy3D's micrometer-based unit
1365+
system. To convert from standard S/m, divide by 1e6.
1366+
1367+
**Practical Advice**
1368+
1369+
**Choosing a Material Type**
1370+
1371+
- Material is in ``td.material_library``? → Use it directly
1372+
(e.g. ``td.material_library['cSi']['Li1993_293K']``).
1373+
- Lossless, wavelength-independent refractive index? → ``td.Medium(permittivity=n**2)``.
1374+
- Known n and k at a specific frequency? → ``td.Medium.from_nk(n=2.4, k=0.01, freq=freq0)``.
1375+
Note: when k > 0, the resulting medium has wavelength-independent n but wavelength-dependent k.
1376+
- You have n,k data vs wavelength? → Use ``FastDispersionFitter`` from
1377+
``tidy3d.plugins.dispersion`` to fit a pole-residue model.
1378+
- Permittivity varies spatially? → Use ``CustomMedium`` with a ``SpatialDataArray``.
1379+
- Need an analytical dispersive model? → Use ``Sellmeier``, ``Lorentz``, ``Drude``,
1380+
``Debye``, or ``PoleResidue`` directly.
1381+
1382+
**Common Library Materials (telecom, ~1.55 μm)**
1383+
1384+
- Silicon: ``td.material_library['cSi']['Li1993_293K']`` (n ≈ 3.48)
1385+
- SiO2: ``td.material_library['SiO2']['Palik_Lossless']`` (n ≈ 1.44)
1386+
- Si3N4: ``td.material_library['Si3N4']['Luke2015PMLStable']`` (n ≈ 2.0)
1387+
- Gold: ``td.material_library['Au']['JohnsonChristy1972']``
1388+
- Silver: ``td.material_library['Ag']['JohnsonChristy1972']``
1389+
13631390
Example
13641391
-------
13651392
>>> dielectric = Medium(permittivity=4.0, name='my_medium')
@@ -1747,6 +1774,35 @@ def _sel_custom_data_inside(self, bounds: Bound) -> Self:
17471774
class CustomMedium(AbstractCustomMedium):
17481775
""":class:`.Medium` with user-supplied permittivity distribution.
17491776
1777+
Notes
1778+
-----
1779+
1780+
**Practical Advice**
1781+
1782+
Use ``CustomMedium`` when permittivity varies spatially — for example, graded-index
1783+
(GRIN) lenses or topology-optimized design regions. Define the permittivity on a
1784+
rectangular grid using ``SpatialDataArray``::
1785+
1786+
from tidy3d import SpatialDataArray
1787+
import numpy as np
1788+
1789+
x = np.linspace(-5, 5, 100)
1790+
y = np.linspace(-5, 5, 100)
1791+
z = [0] # 2D variation
1792+
X, Y = np.meshgrid(x, y, indexing="ij")
1793+
eps_data = 1 + 3 * np.exp(-(X**2 + Y**2) / 4)
1794+
eps_data = eps_data[:, :, np.newaxis]
1795+
1796+
permittivity = SpatialDataArray(eps_data, coords=dict(x=x, y=y, z=z))
1797+
custom_medium = CustomMedium(permittivity=permittivity)
1798+
1799+
For uniform pixelated grids (e.g. topology optimization), consider the convenience method
1800+
:meth:`Structure.from_permittivity_array`, which creates a ``Structure`` with a ``CustomMedium``
1801+
directly from a 3D numpy array and a geometry.
1802+
1803+
For wavelength-independent homogeneous materials, use :class:`Medium` instead.
1804+
For dispersive materials, use :class:`FastDispersionFitter` or an analytical model.
1805+
17501806
Example
17511807
-------
17521808
>>> Nx, Ny, Nz = 10, 9, 8

0 commit comments

Comments
 (0)