Skip to content

PN Junction model from Sze + Ports - #242

Open
mdmaas wants to merge 10 commits into
gdsfactory:mainfrom
EpsilonForge:pn_junction
Open

mdmaas wants to merge 10 commits into
gdsfactory:mainfrom
EpsilonForge:pn_junction

Conversation

@mdmaas

@mdmaas mdmaas commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements a PN-junction depletion model for the 2D TW-MZM workflow, replacing the hand-picked junction capacitance with textbook physics. Builds on #233 (contains its commits; this PR shrinks to the feature commit once #233 merges).

Formulas (Sze & Ng, Physics of Semiconductor Devices, 3rd ed., Wiley, ch. 2)

Built-in voltage:

$$V_{bi} = \frac{k_B T}{q},\ln!\left(\frac{N_A N_D}{n_i^2}\right)$$

Abrupt-junction depletion width under reverse bias $V_R$:

$$W = \sqrt{\frac{2,\varepsilon_s,(V_{bi}+V_R)}{q}\cdot\frac{N_A+N_D}{N_A,N_D}}$$

Asymmetric split of the depletion into the two sides:

$$x_p = W,\frac{N_D}{N_A+N_D} \quad \text{(into P)}, \qquad x_n = W,\frac{N_A}{N_A+N_D} \quad \text{(into N)}$$

Linearly graded junction (grade constant $a = |dN/dx|$):

$$W = \left[\frac{12,\varepsilon_s,(V_{bi}+V_R)}{q,a}\right]^{1/3}$$

Depletion capacitance:

$$C_j = \frac{\varepsilon_s}{W} ;;\text{(per unit area)}, \qquad C = \frac{\varepsilon_s A}{W}$$

Doping concentrations use the industry-standard $\mathrm{cm^{-3}}$; lengths in µm.

User API

from gsim.common.stack.junction import PNJunctionConfig

junc = PNJunctionConfig(na_cm3=1e19, nd_cm3=1e19, v_reverse=0.0)
junc.w_um, junc.xp_um, junc.xn_um, junc.capacitance(length_um, height_um)

Two representation modes, auto-selected from $W$ vs the flanking doped sections — high-res when $W \ge \tfrac{1}{5}\min(\text{P flank},\text{N flank})$, capacitance otherwise (manual override via mode=):

  • capacitance: geometry unchanged (adjacent P/N); computed $C$ applied as a lumped Impedance boundary — one call:
    sim.set_pn_junction(junc, layer_p="p_rib", layer_n="n_rib",
                        length_um=10.0, height_um=0.22)
  • high_res: contiguous depleted-Si dielectric strip of width $W$ drawn between P and N on its own GDS layer and resolved on the actual mesh (pure real $\varepsilon_s$, no carriers); no lumped boundary

The profile builder returns the doped regions, the P/N regions, and the junction metadata (widths/capacitance/chosen mode):

pn = make_pn_junction_profile(comp, ..., junction=junc,
                              p_region=("p_rib", (21, 0), sigma),
                              n_region=("n_rib", (20, 0), sigma),
                              junction_region=("junction", (22, 0)),
                              mode="auto")
pn["junction"]["mode"], pn["junction"]["c_f"]

Demo

The 2d_twmzm notebook now illustrates this: the default junction ($N_A=N_D=10^{18},\mathrm{cm^{-3}}$, $V_R=0$, with $T$ and $n_i$ exposed as user parameters) gives $W \approx 50$ nm and auto-selects high-res mode — the depletion strip is drawn on the actual mesh. A doping sweep in the demo shows $W$ crossing back below the threshold into capacitance mode at heavier doping.

Also fixed

  • build_doped_cross_section() never registered the merged doping/rib materials on stack.materials, so doped domains silently resolved to eps=1.0 without conductivity in generated Palace configs (visible in prior demo artifacts). Now registered, with regression test.

Tests

41 new tests: physics values/limits/scalings and error paths, profile geometry/materials/auto-mode boundary cases, and end-to-end Palace checks (Impedance $C_s$ value in capacitance mode; junction dielectric domain group with $\varepsilon = 11.9$/no conductivity in high-res mode).

@codecov

codecov Bot commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 82.09877% with 116 lines in your changes missing coverage. Please review.
✅ Project coverage is 62.94%. Comparing base (ead7c4f) to head (97cf1e8).
⚠️ Report is 5 commits behind head on main.

Files with missing lines Patch % Lines
src/gsim/palace/runtime.py 67.37% 34 Missing and 12 partials ⚠️
src/gsim/palace/base.py 67.24% 24 Missing and 14 partials ⚠️
src/gsim/common/stack/pn_junction.py 90.19% 15 Missing and 15 partials ⚠️
src/gsim/palace/results.py 96.61% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #242      +/-   ##
==========================================
+ Coverage   61.62%   62.94%   +1.32%     
==========================================
  Files         105      106       +1     
  Lines       15304    16002     +698     
  Branches     3029     3144     +115     
==========================================
+ Hits         9431    10073     +642     
- Misses       4882     4903      +21     
- Partials      991     1026      +35     

☔ 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.

mdmaas added 6 commits August 26, 2026 00:52
Consolidate stack/junction.py + stack/doping.py into stack/pn_junction.py
and the three pn-junction test files into tests/common/test_pn_junction.py.

Add the 1D free-carrier plasma-dispersion model (carrier_profile_1d,
epsilon_eff_relative, optical_params, junction_epsilon_profile) with
SI-correct units, plus make_segmented_junction_profile (8+8 uniform
p_1..n_8 strips sampling the Sze permittivity at strip centres).

The palace_2d_twmzm optical run now uses the segmented rib with per-strip
free-carrier materials (Re(eps)->Permittivity, Im(eps)->Conductivity);
build_optical_cross_section() accepts per-region device_materials and
extra_materials to support it.
Add postprocessing-only lumped/CPW ports to BoundaryModeSim. These do not
load the 2D eigenproblem; Palace evaluates the port voltage paths after the
mode is found and writes mode-V.csv (complex integrated voltage) and
mode-Z.csv (characteristic impedance Z_PV/L_PV/C_PV, plus Z_VI/L_VI/C_VI
when a CurrentPath is given).

- PortConfig/CPWPortConfig gain voltage_path(s)/current_path/nsamples plus
  center/orientation/width/order; add_port/add_cpw_port thread them through.
- Derive cross-section voltage paths from port + stack geometry, or accept
  explicit 2D/3D paths; emit Boundaries.Postprocessing for boundarymode.
- Allow lumped/CPW ports in BoundaryModeSim (wave ports still rejected).
- Parse mode-Z.csv/mode-V.csv in PalaceTextResults via
  characteristic_impedance() and mode_voltage().
- TW-MZM notebook: add gdsfactory rf_in (CPW/GSG on metal1) and junction
  (lumped, WG at the PN junction) ports, and report the RF line impedance
  and junction V_bias.
@mdmaas mdmaas changed the title PN Junction model from Sze PN Junction model from Sze + Ports Sep 12, 2026
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.

1 participant