Skip to content

SchusterLab/multimode_linear_coupler_design

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

multimode_linear_coupler_design

Python tooling for analyzing a linearized-SQUID coupler in HFSS 2023.2. Drives the HFSS Fields Calculator from a Python process attached (via COM) to a running AEDT GUI session, registers named expressions for per-junction voltage, common/differential drive asymmetry, and SQUID-loop flux, then sweeps them across frequency and plots the result.


What this is measuring

A linearized-SQUID coupler requires the flux line to deliver a purely differential drive — the two junctions should see equal-and-opposite voltages, which is the signature of an inductive AC-flux-threading mode. Anything that drives both junctions to the same potential is parasitic capacitive coupling exciting the wrong mode. We quantify this with a single unitless number across frequency: the drive asymmetry

$$ \eta_{\text{asym}}(f) ;=; \frac{|,\hat V_1 + \hat V_2,|}{|,\hat V_1 - \hat V_2,|} ;=; \frac{|\text{common-mode amplitude}|}{|\text{differential amplitude}|}. $$

Small $\eta_{\text{asym}}$ = clean differential flux delivery. Large means capacitive parasitic mode is leaking in.

Expressions computed

All quantities are registered as HFSS Fields Calculator named expressions on the live AEDT session and evaluated per frequency via ClcEval. The formal definitions:

Conventions. HFSS stores the complex phasor $\vec{\tilde E}(\vec r)$ at every saved-field frequency. The phase-$\phi$ snapshot is $\vec E(\vec r,\phi) = \mathrm{Re}{\vec{\tilde E}(\vec r),e^{i\phi}}$. For an oriented curve $C$ with unit tangent $\hat t$,

$$ V_C(\phi) = \int_C \vec E(\vec r,\phi)\cdot d\vec\ell, \qquad \tilde V_C = \int_C \vec{\tilde E}(\vec r)\cdot d\vec\ell. $$

1. Phase-0 junction voltage   voltage_<line>:

$$ V_i^{(0)} = \int_{C_i} \vec E(\vec r, 0)\cdot d\vec\ell ;=; \mathrm{Re},\tilde V_{C_i}. $$

Real-valued snapshot; its sign captures whether the two junctions are in-phase or anti-phase at the chosen reference.

2. Sign   <line>_sign:

$$ \sigma_i = \frac{V_i^{(0)}}{|V_i^{(0)}|} \in {-1,+1}. $$

3. Signed phasor magnitude   signed_voltage_<line>:

$$ \hat V_i ;=; \sigma_i,|\tilde V_{C_i}| ;=; \sigma_i \sqrt{ \Big(!!\int_{C_i}!\mathrm{Re},\vec{\tilde E}\cdot d\vec\ell\Big)^{!2} +\Big(!!\int_{C_i}!\mathrm{Im},\vec{\tilde E}\cdot d\vec\ell\Big)^{!2}}. $$

The bare phasor magnitude $|\tilde V_{C_i}|$ is always positive, so without re-attaching the sign the differential / common-mode combinations below would never cancel.

4. Drive asymmetry   asym_voltage:

$$ \boxed{;; \eta_{\text{asym}} ;=; \frac{|,\hat V_1 + \hat V_2,|}{|,\hat V_1 - \hat V_2,|}. ;;} $$

$\eta_{\text{asym}}$ drive character
$\to 0$ pure differential ($\hat V_1 \approx -\hat V_2$) — wanted inductive flux mode
$\sim 1$ balanced — common-mode and differential equal
$\gg 1$ common-mode dominates — capacitive parasitic resonance, wrong mode excited

5. SQUID-loop flux   JJ_flux:

$$ \frac{\Phi}{\Phi_0} ;=; \frac{1}{\Phi_0}\int_{S}\bigl|,\mu(\vec r),\vec{\tilde H}_{\text{smooth}}(\vec r)\cdot\hat n,\bigr|,dA, \qquad \Phi_0 = \frac{h}{2e} \approx 2.0678 \times 10^{-15}\ \text{Wb}. $$

$S$ is a sheet object enclosed by the SQUID loop; the integrand is $|,\vec{\tilde B}\cdot\hat n,|$ written in terms of the underlying $\vec H$ the calculator returns.

See EXPRESSIONS.md for the full calculator-stack construction of each expression and longer notes on the choice of CmplxMag, Smooth, AtPhase, etc.


Layout

multimode_linear_coupler_design/
├── README.md                      this file
├── EXPRESSIONS.md                 canonical math + calculator-stack reference
├── linc/                          Python package
│   ├── __init__.py
│   ├── attach.py                  attach to running AEDT by PID
│   ├── calculator.py              register named field expressions
│   ├── evaluate.py                ClcEval -> .fld -> parse, sweep across freq
│   ├── inspection.py              design / sweep / mesh -> markdown snapshot
│   └── plotting.py                asymmetry & per-junction voltage plots
├── scripts/
│   ├── _path_setup.py             puts repo root on sys.path
│   ├── register_expressions.py    one-shot named-expression registration
│   └── scan_and_plot.py           sweep + plot + write a results/ entry
└── results/
    ├── README.md                  per-run record convention
    └── YYYY-MM-DD_<design>/       one entry per analysis run
        ├── meta.md                design + sweep + mesh + findings (markdown)
        ├── asym_voltage_vs_freq.png
        └── asym_voltage_vs_freq.csv

Prerequisites

  • AEDT 2023.2 launched as a normal GUI session (no -grpcsrv flag needed).
  • Python env with pyaedt==0.8.11, pythonnet==3.1.0, psutil, numpy, matplotlib.
  • The AEDT design must contain the geometry objects referenced by the calculator: polylines JJ1_line, JJ2_line (the $E\cdot d\vec\ell$ integration paths across each junction) and optionally a sheet SQUID_loop (the $B\cdot \hat n$ integration surface used by JJ_flux).

pyaedt >= 1.0 (renamed to ansys-aedt-core) is gRPC-only. A GUI-launched AEDT 2023.2 has no gRPC server, so the new API silently spawns a second AEDT instance instead of attaching. Stick to 0.8.11.

Typical workflow

  1. Launch AEDT, open the project, set the active design, run an analysis so saved-field data exists (Discrete sweep with SaveFields=True gives the densest curve; Interpolating only saves fields at a handful of anchor frequencies).
  2. Register the named expressions (one-time per design):
    python scripts/register_expressions.py \
        --project differential_drive_orig_coup \
        --design 06_01_diff_drive_check
    
  3. Sweep + plot:
    python scripts/scan_and_plot.py \
        --project differential_drive_orig_coup \
        --design 06_01_diff_drive_check \
        --n-points 100
    
  4. The script creates results/YYYY-MM-DD_<design>/ with auto-generated meta.md (markdown — formulas + tables + embedded plot), the PNG, and a CSV. Edit meta.md to fill in the (manual) fields (mesh tet count from AEDT Profile dialog, sweep wall time, findings).

Known gotchas

  • AddNamedExpr raises an opaque COM error on name collision. calculator._safe_add deletes-then-adds so registration is idempotent.
  • ClcEval only works at frequencies where HFSS saved a full field solution. For interpolating sweeps that's typically 5–10 anchor frequencies. For discrete sweeps, every solved point qualifies. evaluate.sweep_expression samples the sweep's grid by default so you hit saved points; evaluate.eval_any falls back across all available solutions (e.g. Setup1 : LastAdaptive for the adaptive freqs, Setup1 : Sweep for sweep freqs) so each request gets a hit if any solution has saved fields at that frequency.
  • close_on_exit=False in attach.attach() is mandatory — otherwise the Python process exiting kills the user's AEDT GUI session.
  • Discrete sweeps with SaveFields=True produce ~84 MB/freq of field data. A 401-point sweep needs ~34 GB of disk. Make sure your HFSS Temp Directory (Tools → Options → General → Directories) lives on a drive with the space — not on a cramped system drive.

About

HFSS field-calculator tooling for linearized-SQUID coupler analysis (junction voltages, differential/common-mode asymmetry, SQUID-loop flux). Python + pyaedt 0.8.11 + pythonnet, COM-attached to a running AEDT 2023.2 session.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages