Skip to content

Use effective indices in the 2D meep notebook, not bulk ones - #254

Merged
vvahidd merged 1 commit into
gdsfactory:mainfrom
Alisama20:fix/meep-2d-notebook-effective-index
Sep 18, 2026
Merged

vvahidd merged 1 commit into
gdsfactory:mainfrom
Alisama20:fix/meep-2d-notebook-effective-index

Conversation

@Alisama20

@Alisama20 Alisama20 commented Sep 8, 2026 •

Copy link
Copy Markdown
Collaborator

What

nbs/meep_2d.ipynb calls itself "the maintained reference for a true 2D
effective-index simulation" and then sets bulk indices:

sim.materials = {
    "si": Material(refractive_index=3.47),
    "SiO2": Material(refractive_index=1.44),
}

Collapsing z means the silicon region has to carry the effective index of
the vertical slab. Nothing in gsim computes that for you: the XY 2D path builds
no geometry from the layer stack (script.py, if not is_3d and plane == "xy": return []) and _resolve_xy_background_material takes the bulk
dielectric at the cut plane. So the notebook is silently running a 500 nm wide
strip of bulk silicon rather than a 220 nm SOI waveguide.

How much it costs

Measured against a full-vector 2D FEM solve of the real x-z cross-section
(femwell), on the 220 nm core the notebook's own PDK provides:

truth notebook today
n_eff, 500 nm guide 2.43813 3.14721 (+0.709)
L_c of coupler(gap=0.5) 496.3 um 2965.8 um (5.98x)

The example predicts a coupling length about six times too long.

Those come from a mode calculation, so I also checked them with MEEP itself -
the engine this notebook runs on. Handed the notebook's current materials,
MEEP's own mode solver puts a 500 nm guide at 3.13647 against the FEM truth
of 2.43813; with the effective index this PR derives, at 2.47875. And a 2D FDTD
of the coupler, changing nothing but the index map, recovers a coupling length
of 93.66 um for bulk against a truth of 37.44 um at a 200 nm gap - 2.50x - and
24.53 um with effective indices. In both cases the FDTD reproduces the mode
algebra to within 4.4% and 1.8%, so the error is in the index map rather than
in the reduction used to estimate it.

(The 200 nm gap is used for the FDTD because the notebook's own 500 nm gap
gives a beat about half a millimetre long, which is not a simulation anyone
would run. The 5.98x figure above is at the notebook's actual gap.)

Change

The cell now solves the symmetric-slab dispersion relation for the core
thickness read from the active stack, instead of hardcoding a number, and
prints what it derived:

core thickness 220 nm -> 2D silicon index 2.84146 (bulk is 3.47)

With that, the mode index lands at 2.484 against a truth of 2.438 (1.9%) and
the coupling length at 297 um against 496 um.

The markdown is corrected too: the claim that sim.solver(mode="2d", ...) is
"the only difference from a 3D simulation" is what makes this easy to get
wrong, since supplying effective indices is a second, mandatory difference.

Honest scope

Classical effective indices do not fix the coupling length completely -
0.6x rather than 6x - because L_c depends on a difference between supermodes
and is therefore far more sensitive than n_eff. Doing better than that is the
subject of #156, where I have now posted the full measurements, including a
negative result about the variational method proposed there and a 2D FDTD
cross-check of all three index maps. This PR is only about the notebook no
longer using values it explicitly claims not to use.

Verification

Every cell except the cloud sim.run() executes locally against this branch:

core thickness 220 nm -> 2D silicon index 2.84146 (bulk is 3.47)
Stack validation: PASSED

I do not have GDSFactory+ access, so the sim.run() dispatch itself is
unexercised - though it is unaffected by a material value. Someone with access
should confirm the cloud run before merging.

The reference numbers come from a solver chain validated first: the 1D solve
matches the exact transcendental slab relation to 4e-7 (TE) and 5e-8 (TM) at
second order, and the FEM reference is converged to 2.5e-5 in mesh and 7.2e-5
in window.

meep_2d.ipynb describes itself as "the maintained reference for a true 2D
effective-index simulation" and then sets si=3.47 and SiO2=1.44, which are the
bulk indices. Collapsing z means the silicon region has to carry the effective
index of the vertical slab instead, and nothing in gsim computes that: the XY
2D path builds no geometry from the layer stack (script.py returns [] for
plane="xy") and takes the bulk dielectric at the cut plane.

Measured against a full-vector 2D FEM solve of the real cross-section, on the
220 nm SOI core this notebook's own PDK provides:

  n_eff of a 500 nm wide guide   truth 2.43813   bulk 3.14721   (+0.709)
  L_c of coupler(gap=0.5)        truth 496.3 um  bulk 2965.8 um (5.98x)

so the example predicts a coupling length about six times too long.

The cell now solves the symmetric-slab dispersion relation for the core
thickness read from the active stack, rather than hardcoding a number, and
prints what it derived. For the generic PDK that is 2.84146, which is where the
mode index lands at 2.484 against the FEM truth of 2.438 (1.9%) and the
coupling length at 297 um against 496 um.

Classical effective indices do not fix the coupling length completely, because
L_c depends on a difference between supermodes. That is a separate discussion
in gdsfactory#156; this change is just about the notebook no longer using
values it explicitly claims not to use.

Verified locally: every cell except the cloud sim.run() executes, printing
"core thickness 220 nm -> 2D silicon index 2.84146 (bulk is 3.47)" and
"Stack validation: PASSED". The cloud run itself needs GDSFactory+ access,
which I do not have, so that one call is unexercised.
@codecov

codecov Bot commented Sep 8, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 61.63%. Comparing base (ead7c4f) to head (5278394).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #254      +/-   ##
==========================================
+ Coverage   61.62%   61.63%   +0.01%     
==========================================
  Files         105      105              
  Lines       15304    15304              
  Branches     3029     3029              
==========================================
+ Hits         9431     9433       +2     
+ Misses       4882     4879       -3     
- Partials      991      992       +1     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@vvahidd
vvahidd merged commit 47f7de3 into gdsfactory:main Sep 18, 2026
22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants