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.
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
Small
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
1. Phase-0 junction voltage voltage_<line>:
Real-valued snapshot; its sign captures whether the two junctions are in-phase or anti-phase at the chosen reference.
2. Sign <line>_sign:
3. Signed phasor magnitude signed_voltage_<line>:
The bare phasor magnitude
4. Drive asymmetry asym_voltage:
| drive character | |
|---|---|
| pure differential ( |
|
| balanced — common-mode and differential equal | |
| common-mode dominates — capacitive parasitic resonance, wrong mode excited |
5. SQUID-loop flux JJ_flux:
See EXPRESSIONS.md for the full calculator-stack
construction of each expression and longer notes on the choice of CmplxMag,
Smooth, AtPhase, etc.
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
- AEDT 2023.2 launched as a normal GUI session (no
-grpcsrvflag 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 sheetSQUID_loop(the$B\cdot \hat n$ integration surface used byJJ_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.
- Launch AEDT, open the project, set the active design, run an analysis
so saved-field data exists (Discrete sweep with
SaveFields=Truegives the densest curve; Interpolating only saves fields at a handful of anchor frequencies). - Register the named expressions (one-time per design):
python scripts/register_expressions.py \ --project differential_drive_orig_coup \ --design 06_01_diff_drive_check - Sweep + plot:
python scripts/scan_and_plot.py \ --project differential_drive_orig_coup \ --design 06_01_diff_drive_check \ --n-points 100 - The script creates
results/YYYY-MM-DD_<design>/with auto-generatedmeta.md(markdown — formulas + tables + embedded plot), the PNG, and a CSV. Editmeta.mdto fill in the (manual) fields (mesh tet count from AEDT Profile dialog, sweep wall time, findings).
AddNamedExprraises an opaque COM error on name collision.calculator._safe_adddeletes-then-adds so registration is idempotent.ClcEvalonly 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_expressionsamples the sweep's grid by default so you hit saved points;evaluate.eval_anyfalls back across all available solutions (e.g.Setup1 : LastAdaptivefor the adaptive freqs,Setup1 : Sweepfor sweep freqs) so each request gets a hit if any solution has saved fields at that frequency.close_on_exit=Falseinattach.attach()is mandatory — otherwise the Python process exiting kills the user's AEDT GUI session.- Discrete sweeps with
SaveFields=Trueproduce ~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.