qtcad.device.epr module

EPR analysis of Maxwell eigenmodes.

This analysis starts from Maxwell eigenmodes computed with linearized Josephson junctions represented as lumped inductors. It extracts the linear quantities needed for EPR quantization, including energy participations, mode energies, and phase-aligned currents.

The extracted linear data are then used to compute superconducting-circuit Hamiltonian parameters, including dressed mode frequencies, Kerr coefficients, and zero-point fluctuations.

Reference:

Minev, Zlatko K., et al. “Energy-participation quantization of Josephson circuits.” npj Quantum Information 7.1 (2021): 131.

class DielectricSpec(region: str, loss_tangent: float | None = None)

Bases: DielectricSpec

Describe a volume dielectric region for EPR loss analysis.

Attributes:
  • region – Name of the QTCAD volume region. The region must be available to qtcad.device.Device.energy_e().

  • loss_tangent – Optional dielectric loss tangent. If provided, the analysis computes \(Q = 1 / (p\,\mathrm{loss\_tangent})\) for each mode.

loss_tangent: float | None = None
region: str
class EPRAnalysis(device: Device, junctions: Sequence[JunctionSpec] | JunctionSpec, modes: Sequence[int] | None = None, renormalize: bool = True, phase_reference: str | int = 'largest_current', balance_warn_threshold: float = 0.05, dielectrics: Sequence[DielectricSpec] | DielectricSpec | None = None, surface_dielectrics: Sequence[SurfaceDielectricSpec] | SurfaceDielectricSpec | None = None)

Bases: EPRAnalysis

Compute the EPR result from the solved Maxwell eigenmodes.

solve computes the linear EPR quantities (mode energies, junction energies, participation, signs, currents, and voltages) and the Hamiltonian quantities (dressed frequencies, Kerr coefficients, and zero-point fluctuations).

The key formulas are:

  • \(E_j = 0.25 L_j |I_{mj}|^2\)

  • \(E_\mathrm{ind} = E_b + \sum_j E_j\)

  • \(E_\mathrm{cap} = E_e\)

  • \(p^\mathrm{raw}_{mj} = E_j / E_\mathrm{ind}\)

Here, \(E_j\) is the lumped inductive energy stored in junction \(j\) by mode \(m\), \(L_j\) is the junction inductance, \(I_{mj}\) is the phase-aligned junction current, \(E_b\) is the distributed magnetic-field energy, and \(E_e\) is the distributed electric-field energy.

Attributes:
  • device – Device containing a solved Maxwell eigenmode result.

  • junctions – Junction specifications included in the EPR analysis.

  • modes – Maxwell mode indices included in the analysis. If None, all solved modes are used.

  • renormalize – Whether to rescale each mode’s junction participation row to match the expected total junction inductive participation.

  • phase_reference – Junction used to fix each mode’s arbitrary global phase. This may be "largest_current", a junction boundary name, or a junction index.

  • balance_warn_threshold – Relative capacitive/inductive energy-imbalance threshold above which a warning is emitted. The default 0.05 means a 5% mismatch. It is a warning-only diagnostic chosen to flag modes whose energy balance is visibly poor.

  • dielectrics – Volume dielectric regions included in the electric participation and quality-factor analysis.

  • surface_dielectrics – Thin surface dielectric specifications included in the electric-field participation and dielectric-loss quality-factor analysis.

  • result – Complete EPRResult produced by solve(), or None before the analysis is solved.

__init__(device: Device, junctions: Sequence[JunctionSpec] | JunctionSpec, modes: Sequence[int] | None = None, renormalize: bool = True, phase_reference: str | int = 'largest_current', balance_warn_threshold: float = 0.05, dielectrics: Sequence[DielectricSpec] | DielectricSpec | None = None, surface_dielectrics: Sequence[SurfaceDielectricSpec] | SurfaceDielectricSpec | None = None)

Instantiate an EPR analysis.

Parameters:
  • device – Device for which the Maxwell eigenmode solver has already been run.

  • junctions – One junction specification or a sequence of them.

  • modes – Maxwell mode indices to analyze. If None, all solved modes are used.

  • renormalize – Whether to rescale each mode’s junction participation row to match the expected total junction inductive participation.

  • phase_reference – Rule used to fix the current phase before extracting signs. Default: "largest_current".

  • balance_warn_threshold – Relative capacitive/inductive energy-imbalance threshold above which a warning is emitted. The default 0.05 means a 5% mismatch. It is a warning-only diagnostic chosen to flag modes whose energy balance is visibly poor.

  • dielectrics – Optional volume dielectric region specifications for electric participation and quality-factor analysis.

  • surface_dielectrics – Optional thin surface dielectric layer specifications for electric participation and quality-factor analysis.

solve() EPRResult

Compute the EPR result from the solved Maxwell eigenmodes.

Returns:

The complete EPRResult, including energy participations, mode energies, phase-aligned currents, dressed mode frequencies, Kerr coefficients, and zero-point fluctuations.

Raises:
  • RuntimeError – If required Maxwell frequencies, fields, or inductor currents are missing from the device.

  • ValueError – If the perturbation order, requested modes, or junction data are invalid.

class EPRResult(junctions: tuple[~qtcad.device.epr.JunctionSpec, ...], junction_inductances: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]], mode_indices: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.int64]], mode_frequencies: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], participation_raw: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], participation: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], signs: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.int64]], currents: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], voltages: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], electric_energy: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], magnetic_energy: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], junction_energy: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], inductive_energy: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], capacitive_energy: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], energy_imbalance: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]], current_phases: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], bare_frequencies: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], detuning: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], lamb_shift: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], dressed_frequencies: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], chi_raw: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], chi: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], anharmonicity: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], cross_kerr: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], phi_zpf: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]], ej_over: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]], dielectrics: tuple[~qtcad.device.epr.DielectricSpec, ...] = <factory>, dielectric_energy: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]] = <factory>, dielectric_participation: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]] = <factory>, dielectric_quality_factors: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] = <factory>, surface_dielectrics: tuple[~qtcad.device.epr.SurfaceDielectricSpec, ...] = <factory>, surface_dielectric_energy: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]] = <factory>, surface_dielectric_participation: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] | ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.complex128]] = <factory>, surface_dielectric_quality_factors: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] = <factory>, mode_quality_factors: ~numpy.ndarray[tuple[~typing.Any, ...], ~numpy.dtype[~numpy.float64]] = <factory>, metadata: dict[str, ~typing.Any] = <factory>)

Bases: EPRResult

Store complete EPR and Hamiltonian data extracted from Maxwell modes.

Shape convention:

M, J, D, and S are shape symbols used in this docstring, not class attributes. M is the number of selected Maxwell modes, J is the number of junction boundaries, D is the number of volume dielectric regions, and S is the number of thin surface dielectric layers included in the EPR analysis.

Index m selects a mode entry in 1D arrays such as mode_indices[m] and selects row m in mode-indexed matrices such as participation[m, :]. The Maxwell mode number represented by row m is mode_indices[m].

Index j selects a junction entry in 1D arrays such as junctions[j] and selects column j in mode-by-junction matrices such as participation[:, j]. Index d similarly selects a volume dielectric entry and column in (M, D) arrays, while index s selects a surface dielectric entry and column in (M, S) arrays.

Attributes:
  • junctions – Junction specifications used in the analysis.

  • junction_inductances – Junction inductances resolved from the device, with shape (J,).

  • mode_indices – Indices of the Maxwell modes included in the result, with shape (M,).

  • mode_frequencies – Bare Maxwell eigenfrequencies in hertz, with shape (M,).

  • participation_raw – Raw current-based participation, \(E_j / E_\mathrm{ind}\), where \(E_j\) is the lumped inductive energy stored in junction \(j\) and \(E_\mathrm{ind}\) is the total inductive energy of the mode, with shape (M, J).

  • participation – Participation matrix used for Hamiltonian analysis, with the same shape as participation_raw.

  • signs – Lossless-compatibility sign matrix extracted from phase-aligned junction currents, with the same shape as participation_raw.

  • currents – Complex junction currents for each mode and junction, with the same shape as participation_raw.

  • voltages – Junction voltages, computed from \(V_j = i\,2\pi f_m L_j I_j\), with the same shape as participation_raw. Here, \(f_m\) is the mode frequency, \(L_j\) is the junction inductance, and \(I_j\) is the junction current.

    This is the frequency-domain inductor phasor relation \(V = i\omega L I\), matching QTCAD’s inductor-current extraction convention \(I = V / (i\omega L)\).

    In lossless workflows that use voltage magnitudes, the factor of i only changes the stored phase.

  • electric_energy – Electric energy in joules for each mode (i.e. the energy stored in the electric field, excluding any lumped elements), with shape (M,). This can be complex for lossy eigenmodes.

  • magnetic_energy – Magnetic energy in joules for each mode (i.e. the energy stored in the magnetic field, excluding any lumped elements), with shape (M,). This can be complex for lossy eigenmodes.

  • junction_energy – Junction inductive energy in joules for each mode (i.e. the kinetic energy stored in the lumped inductor representing the Josephson junction) and junction, with the same shape as participation_raw.

  • inductive_energy – Total inductive energy in joules, \(E_b + \sum_j E_j\), where \(E_b\) is the distributed magnetic-field energy and \(E_j\) is the lumped junction inductive energy, with shape (M,).

  • capacitive_energy – Total capacitive energy \(E_e\) in joules, with shape (M,). It is computed from the distributed electric-field energy; lumped capacitive contributions are not considered.

  • dielectrics – Volume dielectric region specifications used for electric participation and quality-factor analysis. Column d in the dielectric arrays corresponds to dielectrics[d].

  • dielectric_energy – Electric energy in joules stored in each requested volume dielectric region, with shape (M, D).

  • dielectric_participation – Electric-energy participation ratio for each requested volume dielectric region, \(E_\mathrm{dielectric} / E_\mathrm{cap}\), with shape (M, D).

  • dielectric_quality_factors – Per-region dielectric-loss-limited quality factors, with shape (M, D). Entries are np.inf when a dielectric has no loss tangent or zero loss participation.

  • surface_dielectrics – Thin surface dielectric specifications used for electric participation and quality-factor analysis. For a surface dielectric index s, column s in arrays with shape (M, S) corresponds to surface_dielectrics[s].

  • surface_dielectric_energy – Electric energy in joules stored in each requested thin surface dielectric layer, with shape (M, S). This uses the thin-film approximation \(0.25\,\epsilon_0\epsilon_r t\int_\mathrm{surface} \vec{E}^*\cdot\vec{E}\,dA\), where the integrand reduces to \(|\vec{E}|^2\) for real fields.

  • surface_dielectric_participation – Electric-energy participation ratio for each requested thin surface dielectric layer, with shape (M, S).

  • surface_dielectric_quality_factors – Per-surface dielectric-loss-limited quality factors, with shape (M, S). Entries are np.inf when a surface dielectric has no loss tangent or zero loss participation.

  • mode_quality_factors – Total dielectric-loss-limited quality factor for each mode, combining all provided volume and surface dielectric losses. Entries are np.inf when no lossy dielectric specifications are supplied.

  • energy_imbalance – Absolute relative mismatch between capacitive and inductive energies, \(|E_\mathrm{cap} - E_\mathrm{ind}| / [0.5(|E_\mathrm{cap}| + |E_\mathrm{ind}|)]\), with shape (M,).

  • current_phases – Complex relative current phase factors extracted after mode-level phase alignment, with the same shape as participation_raw. For lossless modes this reduces to signs.

  • bare_frequencies – Bare linear mode frequencies in hertz, with shape (M,). This is equal to mode_frequencies and is stored under the Hamiltonian naming convention for readability.

  • detuning – Pairwise bare-mode frequency differences in hertz, with shape (M, M). Entry detuning[m, n] is \(\Delta_{mn} = f_m - f_n\), where \(f_m\) is the frequency of row mode m and \(f_n\) is the frequency of column mode n. The matrix is antisymmetric: detuning[m, n] = -detuning[n, m].

  • lamb_shift – Leading-order Lamb shift \(\Delta_m\) in hertz, with shape (M,).

    From Eq. (B.9) of [1]’s supplementary material, \(\Delta_m = 0.5\sum_n \chi^\mathrm{raw}_{mn}\). The effective Hamiltonian subtracts this shift from the bare frequency.

  • dressed_frequencies – First-order dressed mode frequencies in hertz, with shape (M,).

    These are the bare frequencies shifted by the leading-order Lamb shift from Eq. (B.9) of [1]’s supplementary material. In other words, dressed_frequencies equals bare_frequencies minus \(0.5\sum_n \chi^\mathrm{raw}_{mn}\). The minus sign comes from the effective Hamiltonian in Eq. (B.2), where the Lamb-shift term appears as \(-h\Delta_m a_m^\dagger a_m\).

  • chi_raw – Leading-order Kerr matrix in hertz units before any post-processing of the diagonal, with shape (M, M).

    This is the matrix that corresponds most directly to Eq. (B.6) of [1]’s supplementary material, rewritten from angular frequencies to hertz. Its full row sum is what enters Eq. (B.9).

  • chi – Kerr matrix, with shape (M, M).

  • anharmonicity – Anharmonicity magnitude, \(-\mathrm{diag}(\chi)\), with shape (M,).

  • cross_kerr – Off-diagonal part of \(\chi\), with shape (M, M).

  • phi_zpf – Reduced-flux zero-point fluctuations, with shape (M, J).

  • ej_over – Josephson energies divided by Planck’s constant, with shape (J,).

  • metadata – Cached intermediate matrices. The current implementation stores frequency_matrix with shape (M, M) and inverse_ej_over with shape (J, J).

anharmonicity: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
bare_frequencies: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
capacitive_energy: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
chi: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
chi_raw: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
cross_kerr: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
current_phases: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
currents: ndarray[tuple[Any, ...], dtype[complex128]]
detuning: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
dielectric_energy: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
dielectric_participation: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
dielectric_quality_factors: ndarray[tuple[Any, ...], dtype[float64]]
dielectrics: tuple[DielectricSpec, ...]
dressed_frequencies: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
ej_over: ndarray[tuple[Any, ...], dtype[float64]]
electric_energy: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
energy_imbalance: ndarray[tuple[Any, ...], dtype[float64]]
inductive_energy: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
junction_energy: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
junction_inductances: ndarray[tuple[Any, ...], dtype[float64]]
junctions: tuple[JunctionSpec, ...]
lamb_shift: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
magnetic_energy: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
metadata: dict[str, Any]
mode_frequencies: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
mode_indices: ndarray[tuple[Any, ...], dtype[int64]]
mode_quality_factors: ndarray[tuple[Any, ...], dtype[float64]]
participation: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
participation_raw: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
phi_zpf: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
signs: ndarray[tuple[Any, ...], dtype[int64]]
surface_dielectric_energy: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
surface_dielectric_participation: ndarray[tuple[Any, ...], dtype[float64]] | ndarray[tuple[Any, ...], dtype[complex128]]
surface_dielectric_quality_factors: ndarray[tuple[Any, ...], dtype[float64]]
surface_dielectrics: tuple[SurfaceDielectricSpec, ...]
voltages: ndarray[tuple[Any, ...], dtype[complex128]]
class JunctionSpec(boundary: str)

Bases: JunctionSpec

Identify a Josephson junction boundary.

Attributes:

boundary – Name of the QTCAD inductor boundary. This should match the corresponding physical-group label in the mesh file.

boundary: str
class SurfaceDielectricSpec(surface: str, thickness: float, relative_permittivity: float, loss_tangent: float | None = None)

Bases: SurfaceDielectricSpec

Describe one thin dielectric loss layer on a QTCAD surface.

Attributes:
  • surface – Name of the QTCAD boundary/surface over which \(|\vec{E}|^2\) is integrated.

  • thickness – Physical thickness of the thin dielectric layer in meters.

  • relative_permittivity – Relative permittivity of the thin dielectric layer.

  • loss_tangent – Optional surface dielectric loss tangent. If provided, the analysis computes \(Q = 1 / (p\,\mathrm{loss\_tangent})\) for each mode.

loss_tangent: float | None = None
relative_permittivity: float
surface: str
thickness: float