χ⁽²⁾ nonlinear optics (SHG / SFG / DFG)¶
Coupled-wave solvers, quasi-phase-matching and the χ⁽²⁾ coupling constants
(rk4ip-integrated solve_shg, solve_sfg, solve_dfg; delta_k_shg,
Lambda_qpm). Docstrings are the source of truth, rendered with
mkdocstrings (numpydoc style).
Modal (overlap) SHG coupling¶
For waveguide χ⁽²⁾ devices (x-cut LNOI, PGLN-style grooves) use the
mode-overlap entry points instead of the scalar A_eff:
import numpy as np
from photonics_helper.base import Wavelength
from photonics_helper.chi2 import shg_coupling_overlap, pgln_overlap
# transverse mode fields (nx, nz) from a FEM solver, arbitrary units
wl = Wavelength(1550, "nm")
g = shg_coupling_overlap(
E_pump, E_sh, dx, dz, wavelength=wl, d=27e-12, n_pump=2.14, n_sh=2.19
)
# g in 1/(√W·m) — for a uniform mode this equals shg_coupling(A_eff)
res = pgln_overlap(
E_pump,
E_sh,
dx,
dz,
wavelength=wl,
d0=27e-12,
d1=d1_profile, # d^(m) harmonic profiles
delta_eps1_pump=de1,
delta_eps1_sh=de1,
delta_k=2 * np.pi / 2.77e-6,
) # QPM residual mismatch
res["g_eff"] # the quasi-phase-matched overlap g' (Wang et al. 2017 Eq. 5)
photonics_helper.chi2 ¶
χ⁽²⁾ (second-order) nonlinear optics: coupled SHG / SFG / DFG solver.
Where is phase matching? (χ³ vs χ² map)
- photonics_helper.chi2 (this module): χ⁽²⁾ — SHG / SFG / DFG phase
matching (delta_k_shg, Lambda_qpm, QPM grating) and the coupling
constants (shg_coupling, shg_coupling_overlap, pgln_overlap).
- photonics_helper.phase_matching: χ⁽³⁾ — FWM / modulation instability /
dispersive-wave roots.
Scalar, long-pulse (CW-like) three-wave mixing in a waveguide. The envelopes
are normalized so that |A|² is an optical power in watts; the coupled
equations are integrated in z with a classical fourth-order Runge–Kutta
step in the interaction picture (RK4IP), which removes the fast
exp(±iΔk z) phase from the state vector.
Conventions
For a non-degenerate three-wave process ω₃ = ω₁ + ω₂ with phase mismatch
Δk = β(ω₃) − β(ω₁) − β(ω₂) and a single effective coupling σ
[1/(√W·m)] the physical envelopes obey
.. math::
\frac{dA_1}{dz} &= i\sigma A_3 A_2^* e^{-i\Delta k z} \\
\frac{dA_2}{dz} &= i\sigma A_3 A_1^* e^{-i\Delta k z} \\
\frac{dA_3}{dz} &= i\sigma A_1 A_2 e^{+i\Delta k z}
Substituting A_3 = a_3 e^{iΔk z} gives the autonomous interaction-picture
system that is actually integrated::
da₁/dz = iσ a₃ a₂*
da₂/dz = iσ a₃ a₁*
da₃/dz = iσ a₁ a₂ − iΔk a₃
SHG is the degenerate case A₁ = A₂ = A_f, A₃ = A_sh. With
Δk = 0 the undepleted-pump solution is exact:
.. math::
\eta(z) = \frac{|A_\mathrm{sh}(z)|^2}{|A_f(0)|^2}
= \tanh^2\!\left(\sigma \sqrt{P_0}\, z\right),
\qquad \kappa = \sigma\sqrt{P_0}
which is the regression ground truth used by the reproductions and tests.
The physical coupling for SHG in a waveguide of effective area A_eff and
index n is (Boyd convention, fields power-normalised, Z₀ = 1/(c ε₀))::
σ = (ω d_eff / (n c)) · sqrt( 2 Z₀ / (n³ A_eff) )
(v0.1.1: this is 2× smaller than the pre-0.1.1 σ — the earlier
formula double-counted the SHG degeneracy factor; conversion
efficiencies change by 4×.)
Quasi-phase-matching
Periodic poling flips the sign of d_eff every half period. The square-wave
grating g(z) = sign[cos(2πz/Λ)] is applied to σ directly. The
first-order QPM period that compensates a mismatch Δk is
Λ = 2π/|Δk| (see :func:Lambda_qpm); the effective coupling is reduced by
2/π.
Scope
Scalar envelopes, no dispersion / group-velocity mismatch / walk-off, no loss. Suitable for CW or long-pulse conversion-efficiency estimates and for validating χ⁽²⁾ bookkeeping; broadband ultrafast χ⁽²⁾+χ⁽³⁾ coupling is out of scope.
Public API
delta_k_shg — Δk = β(2ω) − 2β(ω) for SHG Lambda_qpm — first-order QPM poling period from Δk qpm_grating — square-wave poling sign g(z) shg_coupling — σ from d_eff, n, A_eff, λ Chi2Result — z-resolved envelopes and powers solve_shg — degenerate SHG solve_three_wave — generic ω₃ = ω₁ + ω₂ system solve_sfg — sum-frequency generation wrapper solve_dfg — difference-frequency generation wrapper
Chi2Result
dataclass
¶
Chi2Result(z: NDArray, A: NDArray, labels: tuple[str, ...], sigma: float, delta_k: float, qpm_period: float | None = None, loss_alpha: tuple[float, float] = (0.0, 0.0))
Z-resolved χ⁽²⁾ mixing result.
Attributes:
| Name | Type | Description |
|---|---|---|
z |
1-D array — propagation coordinate (m), ``n_steps + 1`` points.
|
|
A |
2-D complex array — shape ``(n_fields, n_z)``; ``|A|²`` is power (W).
|
|
labels |
tuple[str, ...] — field names, aligned with ``A`` rows.
|
|
sigma |
float — coupling used, ``1/(√W·m)``.
|
|
delta_k |
float — phase mismatch used, 1/m.
|
|
qpm_period |
float or None — poling period if QPM was enabled.
|
|
loss_alpha |
tuple (pump, sh) — Napierian power-loss coefficients (1/m)
|
used by the solver; |
efficiency ¶
efficiency(signal: str = 'sh', pump: str | None = None) -> NDArray
Conversion efficiency P_signal(z) / P_pump(0).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
signal
|
str — label of the generated field (default ``"sh"``).
|
|
'sh'
|
pump
|
str or None — label of the reference pump; defaults to the first
|
field ( |
None
|
delta_k_shg ¶
delta_k_shg(beta_fn: Callable[[float], float], omega: float) -> float
Phase mismatch for SHG: Δk = β(2ω) − 2β(ω).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
beta_fn
|
callable — ``β(ω)`` in 1/m (e.g. a
|
:class: |
required |
omega
|
float — fundamental angular frequency in rad/s.
|
|
required |
Returns:
| Type | Description |
|---|---|
float — Δk in 1/m.
|
|
Lambda_qpm ¶
Lambda_qpm(delta_k: float, order: int = 1) -> float
Quasi-phase-matching poling period Λ = 2π·order / |Δk|.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
delta_k
|
float — phase mismatch in 1/m (non-zero).
|
|
required |
order
|
int — QPM order (default 1). Odd orders are the useful ones for a
|
50 % duty-cycle square grating. |
1
|
Returns:
| Type | Description |
|---|---|
float — poling period in metres.
|
|
qpm_grating ¶
qpm_grating(z: float | NDArray, period: float, duty_cycle: float = 0.5) -> NDArray
Square-wave poling sign g(z) = ±1 with period period.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
z
|
float or array — propagation coordinate(s) in metres.
|
|
required |
period
|
float — poling period Λ in metres (positive).
|
|
required |
duty_cycle
|
float — fraction of the period with ``+1`` (default 0.5).
|
|
0.5
|
Returns:
| Type | Description |
|---|---|
NDArray — ``+1`` or ``-1``, same shape as ``z``.
|
|
shg_coupling ¶
shg_coupling(wavelength: Wavelength, d_eff: float, *, n: float = 1.0, A_eff: Area | None = None) -> float
SHG coupling coefficient κ = σ in 1/(√W·m) (Boyd convention).
Uses the Maxwell/Boyd plane-wave normalisation with the field amplitudes
scaled so that |A|² is power:
.. math::
σ = κ/√P₀ = (ω d_eff / (n c)) · sqrt( 2 Z₀ / (n³ A_eff) ),
giving κ = σ√P₀ and the undepleted-pump limit η = tanh²(σ√P₀ z)
at perfect phase matching. This is the coupling obtained directly from
Boyd's Nonlinear Optics (3rd ed., Ch. 2) coupled-wave equations and the
Maxwell derivation with I = ½ n c ε₀ |E|² and P = I·A_eff; the
degeneracy (factor 2) of the SHG process is already carried by d_eff
(d_eff = χ⁽²⁾/2), so no extra factor of 2 belongs here.
Breaking change (v0.1.1): earlier releases used
σ = (2ω d_eff/(n c))·√(2/(n c ε₀ A_eff)), which is exactly 2× this
value (efficiencies 4× too large). Re-derive any reference values from
before this change.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
wavelength
|
Wavelength — fundamental (pump) vacuum wavelength.
|
|
required |
d_eff
|
float — effective second-order coefficient in m/V (i.e.
|
|
required |
n
|
float — refractive index at the fundamental (default 1.0). The
|
second-harmonic index is taken equal (non-dispersive approximation). |
1.0
|
A_eff
|
Area — effective mode area; defaults to 1 µm².
|
|
None
|
Returns:
| Type | Description |
|---|---|
float — σ in ``1/(√W·m)``.
|
|
solve_shg ¶
solve_shg(*, length: float, P0: float, sigma: float, n_steps: int = 2000, delta_k: float = 0.0, qpm_period: float | None = None, qpm_duty_cycle: float = 0.5, loss_db_per_cm: tuple[float, float] | None = None) -> Chi2Result
Integrate degenerate SHG ω + ω → 2ω with pump depletion.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
length
|
float — interaction length L in metres.
|
|
required |
P0
|
float — input fundamental power in watts (``|A_f(0)|²``).
|
|
required |
sigma
|
float — coupling coefficient ``σ`` in ``1/(√W·m)`` (see
|
:func: |
required |
n_steps
|
int — number of RK4 steps (default 2000).
|
|
2000
|
delta_k
|
float — phase mismatch ``Δk = β(2ω) − 2β(ω)`` in 1/m.
|
|
0.0
|
qpm_period
|
float or None — poling period Λ for QPM; ``None`` disables it.
|
|
None
|
qpm_duty_cycle
|
float — QPM duty cycle (default 0.5).
|
|
0.5
|
loss_db_per_cm
|
tuple (pump, sh) or None — power-loss coefficients in
|
dB/cm; converted internally to Napierian per metre via
|
None
|
Returns:
| Type | Description |
|---|---|
Chi2Result — fields labelled ``("fundamental", "sh")``.
|
|
solve_three_wave ¶
solve_three_wave(*, length: float, sigma: float, A1_0: complex, A2_0: complex, A3_0: complex = 0j, labels: tuple[str, str, str] = ('field1', 'field2', 'field3'), n_steps: int = 2000, delta_k: float = 0.0, qpm_period: float | None = None, qpm_duty_cycle: float = 0.5) -> Chi2Result
Integrate a generic ω₃ = ω₁ + ω₂ three-wave mixing process.
Covers SFG (two inputs, sum output) and DFG (pump + signal → idler) by
choosing the input amplitudes and Δk = β(ω₃) − β(ω₁) − β(ω₂).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
length
|
float — interaction length in metres.
|
|
required |
sigma
|
float — coupling coefficient in ``1/(√W·m)``.
|
|
required |
A1_0
|
complex — input envelopes (``√W``).
|
|
required |
A2_0
|
complex — input envelopes (``√W``).
|
|
required |
A3_0
|
complex — input of the generated field (default 0).
|
|
0j
|
labels
|
tuple[str, str, str] — names for the three fields.
|
|
('field1', 'field2', 'field3')
|
n_steps
|
int
|
|
2000
|
delta_k
|
int
|
|
2000
|
qpm_period
|
int
|
|
2000
|
qpm_duty_cycle
|
int
|
|
2000
|
Returns:
| Type | Description |
|---|---|
Chi2Result — three labelled fields.
|
|
solve_sfg ¶
solve_sfg(*, length: float, P1: float, P2: float, sigma: float, n_steps: int = 2000, delta_k: float = 0.0, qpm_period: float | None = None, qpm_duty_cycle: float = 0.5) -> Chi2Result
Sum-frequency generation ω₁ + ω₂ → ω₃ from two real inputs.
Returns a :class:Chi2Result labelled ("signal", "pump", "sum").
solve_dfg ¶
solve_dfg(*, length: float, Ppump: float, Psignal: float, sigma: float, n_steps: int = 2000, delta_k: float = 0.0, qpm_period: float | None = None, qpm_duty_cycle: float = 0.5) -> Chi2Result
Difference-frequency generation ω_pump − ω_signal → ω_idler.
Returns a :class:Chi2Result labelled ("pump", "signal", "idler").
solve_cascaded_shg ¶
solve_cascaded_shg(*, length: float, P0: float, sigma: float, gamma_f: float, gamma_sh: float | None = None, gamma_cross: float | None = None, n_steps: int = 2000, delta_k: float = 0.0, qpm_period: float | None = None, qpm_duty_cycle: float = 0.5, loss_db_per_cm: tuple[float, float] | None = None) -> Chi2Result
Integrate degenerate SHG plus the bulk χ⁽³⁾ Kerr effects.
The two coupled equations (fundamental ω, second harmonic 2ω) in the
interacting-frame convention of :func:solve_shg:
::
dA_f/dz = i σ A_SH A_f* + iγ_f |A_f|² A_f + iγ_cross |A_SH|² A_f
− (α_f/2) A_f
dA_SH/dz = i σ A_f² − iΔk A_SH + iγ_sh |A_SH|² A_SH
+ iγ_cross |A_f|² A_SH − (α_sh/2) A_SH
i.e. the second-order (quadratic) coupling of :func:solve_shg
combined with the cubic Kerr self-/cross-phase modulation. At large
phase mismatch (|Δk| ≫ σ√P₀) the quadratic coupling acts on the
fundamental like an effective Kerr coefficient γ_φ = σ²P₀/Δk —
the cascaded-Kerr limit (Epstein; Saltiel et al.; Agrawal §10.5) —
validated in the test suite as the large-mismatch limit of this exact
integrator.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
length
|
float — interaction length L in metres.
|
|
required |
P0
|
float — input fundamental power in watts (``|A_f(0)|²``).
|
|
required |
sigma
|
float — quadratic coupling ``σ`` in ``1/(√W·m)``
|
(:func: |
required |
gamma_f
|
float — Kerr SPM coefficient on the fundamental (W⁻¹m⁻¹,
|
|
required |
gamma_sh
|
float or None — Kerr SPM coefficient on the SH. ``None``
|
(default) uses |
None
|
gamma_cross
|
float or None — Kerr XPM coefficient between the two
|
waves. |
None
|
n_steps
|
int
|
:func: |
2000
|
delta_k
|
int
|
:func: |
2000
|
qpm_period
|
int
|
:func: |
2000
|
qpm_duty_cycle
|
int
|
:func: |
2000
|
loss_db_per_cm
|
int
|
:func: |
2000
|
Returns:
| Type | Description |
|---|---|
Chi2Result — fields labelled ``("fundamental", "sh")``.
|
|
Limit contracts (regression-tested)
gamma_f = 0(pure quadratic) equals :func:solve_shgexactly;sigma = 0(pure Kerr): the fundamental solves the scalar SPM equation with the SH held empty — analyticexp(iγ_f P₀ L)phase;- large-Δk cascaded limit: fundamental nonlinear phase ≈
(γ_f + σ²P₀/Δk) · P₀ · Lwithin the undepleted-pump approximation.
shg_coupling_overlap ¶
shg_coupling_overlap(E_pump: ndarray, E_sh: ndarray, dx: float, dz: float, *, wavelength: Wavelength, d: float | ndarray = 2.7e-11, n_pump: float | None = None, n_sh: float | None = None) -> float
Modal nonlinear overlap coupling g in 1/(√W·m).
Implements the Wang et al. 2017 (Eq. 2) overlap integral with the scale-invariant, units-safe shape-factor form::
g = (ω d₀/c)·√(2 Z₀/(n_p² n_s)) · O / (I_p √I_s)
where I_p = ∬|E_ω|²dx dz, I_s = ∬|E_2ω|²dx dz, and
O = ∬ (E_2ω)*·(d(x,z)/d₀)·(E_ω)² dx dz. For a uniform field over
area A_eff this reduces exactly to the Boyd plane-wave coupling of
:func:shg_coupling; for higher-order / sign-alternating (e.g. TE₃) SH
modes the partial cancelation shrinks g by the modal overlap factor,
which a scalar A_eff cannot represent.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
E_pump
|
(nx, nz) complex arrays — transverse mode fields at ω and
|
2ω; arbitrary absolute scaling (all ratios are scale-invariant). |
required |
E_sh
|
(nx, nz) complex arrays — transverse mode fields at ω and
|
2ω; arbitrary absolute scaling (all ratios are scale-invariant). |
required |
dx
|
float — transverse grid spacings (m).
|
|
required |
dz
|
float — transverse grid spacings (m).
|
|
required |
wavelength
|
Wavelength — fundamental (pump) vacuum wavelength.
|
|
required |
d
|
float or (nx, nz) array — χ⁽²⁾ coefficient d(x,z) (m/V); a scalar
|
broadcasts (e.g. LiNbO₃ d33 = 27 pm/V); spatial profiles carry groove/poling modulation. |
2.7e-11
|
n_pump
|
float or None — effective indices at ω / 2ω
|
(default 2.0). |
None
|
n_sh
|
float or None — effective indices at ω / 2ω
|
(default 2.0). |
None
|
Returns:
| Type | Description |
|---|---|
float — modal coupling ``g`` in ``1/(√W·m)``, same σ convention as
|
|
func:`shg_coupling`; directly usable in :func:`solve_shg`.
|
|
pgln_overlap ¶
pgln_overlap(E_pump: ndarray, E_sh: ndarray, dx: float, dz: float, *, wavelength: Wavelength, d0: float | ndarray, d1: float | ndarray, delta_eps1_pump: float | ndarray, delta_eps1_sh: float | ndarray, delta_k: float, n_pump: float | None = None, n_sh: float | None = None) -> dict
Quasi-phase-matched overlap of a periodically-grooved (PGLN) guide.
Implements Wang et al. 2017 (Opt. Express 25(6), 6963), Eqs (3)-(5),
for the first Fourier order of a geometric (groove) modulation of period
Λ. All values use the same proven overg convention as
:func:shg_coupling_overlap::
g_NL^(m) = (2ω d₀/c)·√(2 Z₀/(n_p² n_s)) · D_m
g_L^ω = (ω ε₀/4)·∫Δε₁|E_ω|² modulated shape (1-W fields)
g_L^2ω = (2ω ε₀/4)·∫Δε₁|E_2ω|² modulated shape
φ = 2 (g_L^2ω − g_L^ω)/Δk
g' = g_NL^(1)·(J₀(φ) + J₂(φ)) − g_NL^(0)·J₁(φ)
D_m here are Fourier-harmonic-overlap shape factors, computed with
the same scale-invariant ratio as :func:shg_coupling_overlap: the
caller supplies (nx, nz) profiles of the harmonic mean coefficient
d^(m)(x, z) (grooves set d = 0 there) and the index-perturbation
profile Δε₁(x, z).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
E_pump
|
(nx, nz) complex arrays — transverse mode fields at ω and
|
2ω; arbitrary absolute scaling. |
required |
E_sh
|
(nx, nz) complex arrays — transverse mode fields at ω and
|
2ω; arbitrary absolute scaling. |
required |
dx
|
float — transverse grid spacings (m).
|
|
required |
dz
|
float — transverse grid spacings (m).
|
|
required |
wavelength
|
Wavelength — pump vacuum wavelength.
|
|
required |
d0
|
float or (nx, nz) array — mean χ⁽²⁾ coefficient (m/V), e.g. 27 pm/V.
|
|
required |
d1
|
float or (nx, nz) array — first-harmonic χ⁽²⁾ profile (0 for a pure
|
groove-modulated waveguide with grooves that remove the LN). |
required |
delta_eps1_pump
|
float or (nx, nz) arrays — first-harmonic
|
permittivity perturbation Δε₁ seen by each harmonic (dimensionless
e.g. |
required |
delta_eps1_sh
|
float or (nx, nz) arrays — first-harmonic
|
permittivity perturbation Δε₁ seen by each harmonic (dimensionless
e.g. |
required |
delta_k
|
float — residual mismatch ``β(2ω) − 2β(ω) − 2π/Λ`` (1/m).
|
|
required |
n_pump
|
float or None — effective indices; default 2.0/2.0.
|
|
None
|
n_sh
|
float or None — effective indices; default 2.0/2.0.
|
|
None
|
Returns:
| Type | Description |
|---|---|
dict with ``"g_nl_0"``, ``"g_nl_1"``, ``"g_L_w"``, ``"g_L_2w"``,
|
|
``"phi"``, ``"g_eff"`` (floats; coupling values in ``1/(√W·m)``,
|
|
``φ`` dimensionless).
|
|
Notes
For a zero-modulation check, d1 = 0 gives g_NL^(1) = 0 →
g' = j1(φ)·g_NL^(0)·(∓1); the uniform-guide limit
(Δε₁ = 0, helper) does NOT reduce to :func:shg_coupling_overlap
because a groove with zero depth is not the uniform waveguide — callers
verifying divisibility should set (Δε₁ = 0, d1 = d0·shape) (the
article's duty-cycle square wave) and check g_eff → g there.