Skip to content

VCVS constraint stamp has phase-dependent back-reflection (S11 ≠ 0) #31

Description

@cdaunt

Problem

The standard VCVS (voltage-controlled voltage source) stamp used for unidirectional optical components has an implicit phase-dependent input reflection coefficient.

A VCVS with V_out = T · V_in is stamped as:

return {
    "o1": s.i_fwd,      # shared branch current at input
    "o2": -s.i_fwd,     # shared branch current at output
    "i_fwd": V_o2 - T * V_o1,  # constraint
}

Converting to S-parameters, this device has:

S11 = (1 - T) / (1 + T)        S21 = 2T / (1 + T)

For |T| ≈ 1, |S11| = |tan(φ/2)| — the reflection sweeps from 0 to ∞ as phase rotates. This is not physical for a waveguide or phase shifter, which should have S11 = 0 and S21 = T.

Impact

In a thermal MZI circuit (mmi1x2 → heater → mmi2x2 → detectors), the artificial back-reflection from the heater VCVS propagates backward through the mmi1x2's Y-matrix. Because the mmi1x2 is near-lossless, its Y-matrix is ill-conditioned (det(S+I) = 1 - 2t² ≈ 0.067 for 0.3 dB loss), amplifying the reflected current ~15×. This causes:

  • Non-physical power oscillation at intermediate nodes (between mmi1x2 output and heater input)
  • Node voltage V(W1) depends on heater phase: V(W1) ∝ 1 / (T(I) + T_fixed + 2) instead of being constant
  • Visible amplitude modulation on what should be a pure phase rotation

Fix

Replace the shared branch current at the input port with a matched input admittance (Y_in = 1/z₀ = 1):

return {
    "o1": signals.o1,    # matched input: Y_in = 1, gives S11 = 0
    "o2": -s.i_fwd,      # output current from constraint
    "i_fwd": V_o2 - T * V_o1,
}

This decouples the input from the output loading. The KCL at the input node no longer contains T(I), so the node voltage is constant across the sweep. Power conservation holds: input power |V|² equals output power |T|² · |V|².

Scope

This affects any VCVS-based component where T varies during a simulation (e.g., thermal phase shifters, tunable couplers). Components with fixed T are less affected because the reflection coefficient is constant, but still present incorrect S11 values.

The TunableBeamSplitter in circulax/components/photonic.py uses the same shared-branch-current pattern and would have the same issue.

The near-lossless SAX component Y-matrices (via s_to_y) amplify the problem but are not the root cause — the VCVS stamp itself is the source of the artificial reflection.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions