Allow raw refractive index input (no manual conjugation required)#101
Allow raw refractive index input (no manual conjugation required)#101jhpark94 wants to merge 5 commits intokc-ml2:mainfrom
Conversation
Three changes for correct lossy material support: 1. convolution_matrix.py: eps = conj(n)^2 internally, so users pass raw n (standard exp(-iwt) convention, Im(n)>0 = loss). 2. transfer_method.py (transfer_1d_1): Substrate boundary uses eigenvalue-consistent q = sqrt(kx^2 - eps) instead of 1j*conj(kz)/n^2. Fixes incorrect Fresnel for lossy substrates. 3. transfer_method.py (transfer_1d_3): Detect same-material interfaces (V_i @ G ≈ -I) and skip boundary matching, applying propagation only. Fixes uniform lossy layer on same lossy substrate (e.g., Au on Au). Verified against analytical results: - Air -> Au, TM: R=0.981936 (Fresnel exact) - Au(20nm)/Glass, TM: R=0.911680, T=0.050702 (thin-film exact) - Au(20nm)/Glass, TE: R=0.911680, T=0.050702 (exact) - Si(200nm)/Glass, TM: R=0.602732, T=0.397268 (no regression) - Au on Au, all thicknesses: R=0.981936 (Fresnel exact) - Au grating on Au: R+T=0.945 (physical, T>0) Breaking change: users should pass raw n in ucell (remove conj()). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
d98feee to
9a2a994
Compare
Same three changes as numpy backend: 1. convolution_matrix: eps = conj(n)^2 internally 2. transfer_*_1: eigenvalue-consistent substrate boundary 3. transfer_*_3: same-material interface detection JAX uses jax.lax.cond for JIT compatibility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…hold Replace the heuristic V_i@G ≈ -I check (atol=0.1) with explicit eps comparison: compare the layer's diagonal eps (from convolution matrix) with the eps below (substrate or previous layer). This is more robust, interpretable, and doesn't require tuning a threshold. Applied to all three backends (numpy, torch, JAX). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- transfer_1d_conical_3, transfer_2d_3: add same_material parameter - _base.py solve_1d_conical, solve_2d: add eps comparison logic - Fix torch transfer_1d_1 type_complex casting (eps_bot) - Applied to all three backends (numpy, torch, JAX) Verified (numpy backend): - Oblique incidence (0-60°, TE/TM): all match Fresnel - 1D conical: Si/Glass R+T=1, Au/Glass R+T<1 - 2D grating: Si/Glass R+T=1, Au patch on Au T>0 - Au uniform on Au (2D): R=0.981936 = Fresnel Note: torch backend has a pre-existing issue with 1D dielectric (R=1.659 for Si/Glass) that exists in the original meent code and is not caused by this PR. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three bugs fixed:
1. sqrt branch cut: torch loses -0j imaginary sign from kx.conj(),
causing sqrt(-real+0j) to select wrong branch (+1.5j instead of
-1.5j). This flips the G matrix sign for propagating orders,
producing R=1.659 instead of 0.603 for Si/Glass. Fixed by adding
-1e-20j perturbation to force correct branch selection.
2. same_material T calculation: eigenvalue ordering from torch.linalg.eig
differs from numpy, so T = T @ X applied propagation phases to wrong
diffraction orders. Fixed by transforming X to physical basis:
T = T @ W @ X @ W^{-1}.
3. TM de_ti in torch transfer_1d_4: used kz_bot instead of kz_bot.conj(),
giving wrong/negative transmittance for lossy substrates.
All three fixes applied consistently across numpy, torch, and jax backends
for 1D, conical, and 2D solvers.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update: Three additional torch backend bugs fixedBug 1: sqrt branch cut (critical)
Impact: Si/Glass TM gave R=1.659 instead of correct R=0.603. Bug 2: same_material eigenvalue ordering
Impact: Si/Si gave T=0.000 instead of correct T=0.694. Bug 3: TM de_ti missing
|
Summary
Users can now pass standard refractive indices directly — no manual
conj(n)needed. Meent handles the exp(+iωt) convention internally.Previously, users had to:
Now:
Changes
convolution_matrix.py:eps = np.conj(layer)**2— applies conjugation internally instead of expecting pre-conjugated inputtransfer_method.py: Substrate boundary uses eigenvalue-consistentq = sqrt(kx² - eps)instead of1j * conj(kz) / n², fixing incorrect results for lossy substratesVerification
Breaking change
Users who previously passed
conj(n)inucellwill now get double conjugation. They should remove the manualconj()call.Scope
Closes #100
🤖 Generated with Claude Code