%load_ext autoreload
%autoreload 2
Tutorial 7. EPR through PyAEDT (gRPC) — no COM#
The same energy-participation extraction, over Ansys’s official Python API#
Author: Joey Yaker
Introduction#
pyEPR has always driven HFSS through a hand-rolled COM layer (pyEPR.ansys), which is Windows-only and tied to a private scripting interface. This tutorial runs the same Energy-Participation-Ratio extraction through PyAEDT (pyaedt) — Ansys’s official, maintained Python API — entirely over gRPC, with no COM, using pyEPR.ansys_pyaedt.PyaedtDistributedAnalysis.
The physics is unchanged. The extracted participations feed pyEPR’s own epr_to_zpf and epr_numerical_diagonalization exactly as the COM path does, and the results match the COM path digit-for-digit (p_mj = 0.9755 on this demo transmon).
What actually differs is small and mechanical:
COM ( |
PyAEDT ( |
|
|---|---|---|
transport |
COM (.NET, Windows-only) |
gRPC (default since AEDT 2022 R2) |
connect |
Running-Object-Table search |
attach to the session that owns the |
field-calculator handle |
|
|
read a result back |
|
|
The field-calculator token stack (EnterQty / ClcMaterial / EnterVol / EnterLine / Integrate) and the eigenmode normalization (Solutions.EditSources) are identical between the two — they both run fine over gRPC. The single thing that does not survive gRPC is the stateful ClcEval read-back, which is why this backend writes results to a file with CalculatorWrite instead.
Requirements#
This backend needs PyAEDT, which pyEPR declares as an optional extra so that import pyEPR never requires it:
pip install "pyEPR-quantum[pyaedt]"
You also need a solved HFSS eigenmode design open in AEDT (the backend attaches to your running session). This tutorial uses a single-junction transmon solved for four eigenmodes.
Load pyEPR and the PyAEDT backend#
import numpy as np
import pyEPR as epr
from pyEPR.ansys_pyaedt import PyaedtDistributedAnalysis
Describe the project and its junctions#
A ProjectInfo describes where the design is and what the junctions are — exactly as in the COM workflow. The one difference: pass do_connect=False, because PyAEDT does the connecting, not pyEPR’s COM layer.
Each junction is declared with the same keys pyEPR uses:
Lj_variable — the HFSS design variable holding the junction inductance (e.g. "Lj_Transmon" = "12.9201nH"); read and parsed over gRPC. (You may instead give Lj_henries directly.)
line — the polyline across the junction used for the line-voltage integral.
pinfo = epr.ProjectInfo(
project_path=r"C:\path\to\your\HFSS_Projects", # change to your project folder
project_name="EPR_Demo_Project",
design_name="EPR_Sample_Demo",
setup_name="EPR_Scan",
do_connect=False, # PyAEDT does the connecting, not COM
)
pinfo.junctions["j1"] = {
"Lj_variable": "Lj_Transmon", # HFSS variable, e.g. '12.9201nH'
"line": "Junction_line", # polyline across the junction
}
Extract the EPR over gRPC#
PyaedtDistributedAnalysis attaches to the running AEDT session (over gRPC, via the project’s .aedt.lock), then for every eigenmode it:
normalizes the fields to that mode with Solutions.EditSources (so the line voltage and the energy share one field scaling),
integrates the material-weighted electric energy \(U_E \propto \mathrm{Re}\!\int \mathbf{E}\cdot\varepsilon\,\mathbf{E}^* \, dV\) over all objects, and
integrates the junction line voltage \(V\) along each junction line,
then forms the participation \(p_{mj} = \tfrac{1}{2}L_J (V/\omega L_J)^2 \,/\, (U_E/2)\). All of it is pure gRPC; nothing here touches COM.
eprd = PyaedtDistributedAnalysis(pinfo, aedt_version="2026.1")
eprd.do_EPR_analysis()
for m, f in enumerate(eprd.freqs_GHz):
print(f"mode {m+1}: f = {f:.6f} GHz "
f"p_mj = {eprd.PJ[m, 0]:.4f} sign = {eprd.SJ[m, 0]:+d}")
mode 1: f = 4.815509 GHz p_mj = 0.9755 sign = -1
mode 2: f = 5.060004 GHz p_mj = 0.0061 sign = +1
mode 3: f = 6.484727 GHz p_mj = 0.0000 sign = -1
mode 4: f = 6.489703 GHz p_mj = 0.0000 sign = +1
The results live on the analysis object as plain NumPy arrays — the same shapes pyEPR’s diagonalizer expects (M modes × J junctions):
eprd.freqs_GHz — eigenfrequencies (GHz)
eprd.PJ — participations \(p_{mj}\)
eprd.SJ — signs \(s_{mj}\)
eprd.Ljs — junction inductances (H), parsed from the HFSS variables
print("freqs_GHz :", np.round(eprd.freqs_GHz, 6))
print("PJ :", np.round(eprd.PJ.ravel(), 4))
print("SJ :", eprd.SJ.ravel())
print("Ljs (nH) :", np.round(eprd.Ljs * 1e9, 4))
freqs_GHz : [4.815509 5.060004 6.484727 6.489703]
PJ : [0.9755 0.0061 0. 0. ]
SJ : [-1 1 -1 1]
Ljs (nH) : [12.9201]
Hand off to pyEPR’s quantum analysis#
The extracted participations now go straight into pyEPR’s own physics. eprd.analyze() is a thin wrapper over pyEPR.calcs.back_box_numeric.epr_numerical_diagonalization — the very function the COM DistributedAnalysis uses — so the quantum result is produced by unchanged pyEPR code.
It returns the dressed mode frequencies and the cross-Kerr / anharmonicity matrix (MHz); the diagonal is each mode’s anharmonicity \(\alpha\). Mode 1 is the qubit (it carries essentially all of the junction participation).
f_ND, chi_ND = eprd.analyze(cos_trunc=8, fock_trunc=9)
alpha = np.abs(np.asarray(chi_ND))[0, 0]
print(f"qubit-mode anharmonicity alpha = {alpha:.3f} MHz")
qubit-mode anharmonicity alpha = 248.415 MHz
Same physics, no COM#
The qubit-mode participation p_mj = 0.9755 is identical to what pyEPR’s COM path extracts from the same solved design — digit-for-digit — and everything downstream of it (here, a 248.415 MHz anharmonicity from the four-mode diagonalization) follows from the same unchanged pyEPR physics. The only thing that changed is the road taken to HFSS: gRPC through Ansys’s maintained PyAEDT API instead of COM.
Practical upsides of the PyAEDT path:
No COM / pywin32 — the transport is gRPC, the default since AEDT 2022 R2.
Attaches to your running session via the project’s .aedt.lock, avoiding the stale-session and project-locked errors common with COM’s Running-Object-Table lookup.
Maintenance moves to Ansys — pyaedt is officially versioned and supported, rather than a private COM interface pyEPR has to track by hand across AEDT releases.
For COM users nothing changes: pyEPR.ansys and DistributedAnalysis are untouched, and PyAEDT stays an optional extra.
This backend needs PyAEDT, which pyEPR declares as an optional extra so that import pyEPR never requires it:
pip install "pyEPR-quantum[pyaedt]"
You also need a solved HFSS eigenmode design open in AEDT (the backend attaches to your running session). This tutorial uses a single-junction transmon solved for four eigenmodes.
Load pyEPR and the PyAEDT backend#
import numpy as np
import pyEPR as epr
from pyEPR.ansys_pyaedt import PyaedtDistributedAnalysis
Describe the project and its junctions#
A ProjectInfo describes where the design is and what the junctions are — exactly as in the COM workflow. The one difference: pass do_connect=False, because PyAEDT does the connecting, not pyEPR’s COM layer.
Each junction is declared with the same keys pyEPR uses:
Lj_variable — the HFSS design variable holding the junction inductance (e.g. "Lj_Transmon" = "12.9201nH"); read and parsed over gRPC. (You may instead give Lj_henries directly.)
line — the polyline across the junction used for the line-voltage integral.
pinfo = epr.ProjectInfo(
project_path=r"C:\path\to\your\HFSS_Projects", # change to your project folder
project_name="EPR_Demo_Project",
design_name="EPR_Sample_Demo",
setup_name="EPR_Scan",
do_connect=False, # PyAEDT does the connecting, not COM
)
pinfo.junctions["j1"] = {
"Lj_variable": "Lj_Transmon", # HFSS variable, e.g. '12.9201nH'
"line": "Junction_line", # polyline across the junction
}
Extract the EPR over gRPC#
PyaedtDistributedAnalysis attaches to the running AEDT session (over gRPC, via the project’s .aedt.lock), then for every eigenmode it:
normalizes the fields to that mode with Solutions.EditSources (so the line voltage and the energy share one field scaling),
integrates the material-weighted electric energy \(U_E \propto \mathrm{Re}\!\int \mathbf{E}\cdot\varepsilon\,\mathbf{E}^* \, dV\) over all objects, and
integrates the junction line voltage \(V\) along each junction line,
then forms the participation \(p_{mj} = \tfrac{1}{2}L_J (V/\omega L_J)^2 \,/\, (U_E/2)\). All of it is pure gRPC; nothing here touches COM.
eprd = PyaedtDistributedAnalysis(pinfo, aedt_version="2026.1")
eprd.do_EPR_analysis()
for m, f in enumerate(eprd.freqs_GHz):
print(f"mode {m+1}: f = {f:.6f} GHz "
f"p_mj = {eprd.PJ[m, 0]:.4f} sign = {eprd.SJ[m, 0]:+d}")
mode 1: f = 4.815509 GHz p_mj = 0.9755 sign = -1
mode 2: f = 5.060004 GHz p_mj = 0.0061 sign = +1
mode 3: f = 6.484727 GHz p_mj = 0.0000 sign = -1
mode 4: f = 6.489703 GHz p_mj = 0.0000 sign = +1
The results live on the analysis object as plain NumPy arrays — the same shapes pyEPR’s diagonalizer expects (M modes × J junctions):
eprd.freqs_GHz — eigenfrequencies (GHz)
eprd.PJ — participations \(p_{mj}\)
eprd.SJ — signs \(s_{mj}\)
eprd.Ljs — junction inductances (H), parsed from the HFSS variables
print("freqs_GHz :", np.round(eprd.freqs_GHz, 6))
print("PJ :", np.round(eprd.PJ.ravel(), 4))
print("SJ :", eprd.SJ.ravel())
print("Ljs (nH) :", np.round(eprd.Ljs * 1e9, 4))
freqs_GHz : [4.815509 5.060004 6.484727 6.489703]
PJ : [0.9755 0.0061 0. 0. ]
SJ : [-1 1 -1 1]
Ljs (nH) : [12.9201]
Hand off to pyEPR’s quantum analysis#
The extracted participations now go straight into pyEPR’s own physics. eprd.analyze() is a thin wrapper over pyEPR.calcs.back_box_numeric.epr_numerical_diagonalization — the very function the COM DistributedAnalysis uses — so the quantum result is produced by unchanged pyEPR code.
It returns the dressed mode frequencies and the cross-Kerr / anharmonicity matrix (MHz); the diagonal is each mode’s anharmonicity \(\alpha\). Mode 1 is the qubit (it carries essentially all of the junction participation).
f_ND, chi_ND = eprd.analyze(cos_trunc=8, fock_trunc=9)
alpha = np.abs(np.asarray(chi_ND))[0, 0]
print(f"qubit-mode anharmonicity alpha = {alpha:.3f} MHz")
qubit-mode anharmonicity alpha = 248.415 MHz
Same physics, no COM#
The qubit-mode participation p_mj = 0.9755 is identical to what pyEPR’s COM path extracts from the same solved design — digit-for-digit — and everything downstream of it (here, a 248.415 MHz anharmonicity from the four-mode diagonalization) follows from the same unchanged pyEPR physics. The only thing that changed is the road taken to HFSS: gRPC through Ansys’s maintained PyAEDT API instead of COM.
Practical upsides of the PyAEDT path:
No COM / pywin32 — the transport is gRPC, the default since AEDT 2022 R2.
Attaches to your running session via the project’s .aedt.lock, avoiding the stale-session and project-locked errors common with COM’s Running-Object-Table lookup.
Maintenance moves to Ansys — pyaedt is officially versioned and supported, rather than a private COM interface pyEPR has to track by hand across AEDT releases.
For COM users nothing changes: pyEPR.ansys and DistributedAnalysis are untouched, and PyAEDT stays an optional extra.
import numpy as np
import pyEPR as epr
from pyEPR.ansys_pyaedt import PyaedtDistributedAnalysis
Describe the project and its junctions#
A ProjectInfo describes where the design is and what the junctions are — exactly as in the COM workflow. The one difference: pass do_connect=False, because PyAEDT does the connecting, not pyEPR’s COM layer.
Each junction is declared with the same keys pyEPR uses:
Lj_variable — the HFSS design variable holding the junction inductance (e.g. "Lj_Transmon" = "12.9201nH"); read and parsed over gRPC. (You may instead give Lj_henries directly.)
line — the polyline across the junction used for the line-voltage integral.
pinfo = epr.ProjectInfo(
project_path=r"C:\path\to\your\HFSS_Projects", # change to your project folder
project_name="EPR_Demo_Project",
design_name="EPR_Sample_Demo",
setup_name="EPR_Scan",
do_connect=False, # PyAEDT does the connecting, not COM
)
pinfo.junctions["j1"] = {
"Lj_variable": "Lj_Transmon", # HFSS variable, e.g. '12.9201nH'
"line": "Junction_line", # polyline across the junction
}
Extract the EPR over gRPC#
PyaedtDistributedAnalysis attaches to the running AEDT session (over gRPC, via the project’s .aedt.lock), then for every eigenmode it:
normalizes the fields to that mode with Solutions.EditSources (so the line voltage and the energy share one field scaling),
integrates the material-weighted electric energy \(U_E \propto \mathrm{Re}\!\int \mathbf{E}\cdot\varepsilon\,\mathbf{E}^* \, dV\) over all objects, and
integrates the junction line voltage \(V\) along each junction line,
then forms the participation \(p_{mj} = \tfrac{1}{2}L_J (V/\omega L_J)^2 \,/\, (U_E/2)\). All of it is pure gRPC; nothing here touches COM.
eprd = PyaedtDistributedAnalysis(pinfo, aedt_version="2026.1")
eprd.do_EPR_analysis()
for m, f in enumerate(eprd.freqs_GHz):
print(f"mode {m+1}: f = {f:.6f} GHz "
f"p_mj = {eprd.PJ[m, 0]:.4f} sign = {eprd.SJ[m, 0]:+d}")
mode 1: f = 4.815509 GHz p_mj = 0.9755 sign = -1
mode 2: f = 5.060004 GHz p_mj = 0.0061 sign = +1
mode 3: f = 6.484727 GHz p_mj = 0.0000 sign = -1
mode 4: f = 6.489703 GHz p_mj = 0.0000 sign = +1
The results live on the analysis object as plain NumPy arrays — the same shapes pyEPR’s diagonalizer expects (M modes × J junctions):
eprd.freqs_GHz — eigenfrequencies (GHz)
eprd.PJ — participations \(p_{mj}\)
eprd.SJ — signs \(s_{mj}\)
eprd.Ljs — junction inductances (H), parsed from the HFSS variables
print("freqs_GHz :", np.round(eprd.freqs_GHz, 6))
print("PJ :", np.round(eprd.PJ.ravel(), 4))
print("SJ :", eprd.SJ.ravel())
print("Ljs (nH) :", np.round(eprd.Ljs * 1e9, 4))
freqs_GHz : [4.815509 5.060004 6.484727 6.489703]
PJ : [0.9755 0.0061 0. 0. ]
SJ : [-1 1 -1 1]
Ljs (nH) : [12.9201]
Hand off to pyEPR’s quantum analysis#
The extracted participations now go straight into pyEPR’s own physics. eprd.analyze() is a thin wrapper over pyEPR.calcs.back_box_numeric.epr_numerical_diagonalization — the very function the COM DistributedAnalysis uses — so the quantum result is produced by unchanged pyEPR code.
It returns the dressed mode frequencies and the cross-Kerr / anharmonicity matrix (MHz); the diagonal is each mode’s anharmonicity \(\alpha\). Mode 1 is the qubit (it carries essentially all of the junction participation).
f_ND, chi_ND = eprd.analyze(cos_trunc=8, fock_trunc=9)
alpha = np.abs(np.asarray(chi_ND))[0, 0]
print(f"qubit-mode anharmonicity alpha = {alpha:.3f} MHz")
qubit-mode anharmonicity alpha = 248.415 MHz
Same physics, no COM#
The qubit-mode participation p_mj = 0.9755 is identical to what pyEPR’s COM path extracts from the same solved design — digit-for-digit — and everything downstream of it (here, a 248.415 MHz anharmonicity from the four-mode diagonalization) follows from the same unchanged pyEPR physics. The only thing that changed is the road taken to HFSS: gRPC through Ansys’s maintained PyAEDT API instead of COM.
Practical upsides of the PyAEDT path:
No COM / pywin32 — the transport is gRPC, the default since AEDT 2022 R2.
Attaches to your running session via the project’s .aedt.lock, avoiding the stale-session and project-locked errors common with COM’s Running-Object-Table lookup.
Maintenance moves to Ansys — pyaedt is officially versioned and supported, rather than a private COM interface pyEPR has to track by hand across AEDT releases.
For COM users nothing changes: pyEPR.ansys and DistributedAnalysis are untouched, and PyAEDT stays an optional extra.
A ProjectInfo describes where the design is and what the junctions are — exactly as in the COM workflow. The one difference: pass do_connect=False, because PyAEDT does the connecting, not pyEPR’s COM layer.
Each junction is declared with the same keys pyEPR uses:
Lj_variable— the HFSS design variable holding the junction inductance (e.g."Lj_Transmon"="12.9201nH"); read and parsed over gRPC. (You may instead giveLj_henriesdirectly.)line— the polyline across the junction used for the line-voltage integral.
pinfo = epr.ProjectInfo(
project_path=r"C:\path\to\your\HFSS_Projects", # change to your project folder
project_name="EPR_Demo_Project",
design_name="EPR_Sample_Demo",
setup_name="EPR_Scan",
do_connect=False, # PyAEDT does the connecting, not COM
)
pinfo.junctions["j1"] = {
"Lj_variable": "Lj_Transmon", # HFSS variable, e.g. '12.9201nH'
"line": "Junction_line", # polyline across the junction
}
Extract the EPR over gRPC#
PyaedtDistributedAnalysis attaches to the running AEDT session (over gRPC, via the project’s .aedt.lock), then for every eigenmode it:
normalizes the fields to that mode with Solutions.EditSources (so the line voltage and the energy share one field scaling),
integrates the material-weighted electric energy \(U_E \propto \mathrm{Re}\!\int \mathbf{E}\cdot\varepsilon\,\mathbf{E}^* \, dV\) over all objects, and
integrates the junction line voltage \(V\) along each junction line,
then forms the participation \(p_{mj} = \tfrac{1}{2}L_J (V/\omega L_J)^2 \,/\, (U_E/2)\). All of it is pure gRPC; nothing here touches COM.
eprd = PyaedtDistributedAnalysis(pinfo, aedt_version="2026.1")
eprd.do_EPR_analysis()
for m, f in enumerate(eprd.freqs_GHz):
print(f"mode {m+1}: f = {f:.6f} GHz "
f"p_mj = {eprd.PJ[m, 0]:.4f} sign = {eprd.SJ[m, 0]:+d}")
mode 1: f = 4.815509 GHz p_mj = 0.9755 sign = -1
mode 2: f = 5.060004 GHz p_mj = 0.0061 sign = +1
mode 3: f = 6.484727 GHz p_mj = 0.0000 sign = -1
mode 4: f = 6.489703 GHz p_mj = 0.0000 sign = +1
The results live on the analysis object as plain NumPy arrays — the same shapes pyEPR’s diagonalizer expects (M modes × J junctions):
eprd.freqs_GHz — eigenfrequencies (GHz)
eprd.PJ — participations \(p_{mj}\)
eprd.SJ — signs \(s_{mj}\)
eprd.Ljs — junction inductances (H), parsed from the HFSS variables
print("freqs_GHz :", np.round(eprd.freqs_GHz, 6))
print("PJ :", np.round(eprd.PJ.ravel(), 4))
print("SJ :", eprd.SJ.ravel())
print("Ljs (nH) :", np.round(eprd.Ljs * 1e9, 4))
freqs_GHz : [4.815509 5.060004 6.484727 6.489703]
PJ : [0.9755 0.0061 0. 0. ]
SJ : [-1 1 -1 1]
Ljs (nH) : [12.9201]
Hand off to pyEPR’s quantum analysis#
The extracted participations now go straight into pyEPR’s own physics. eprd.analyze() is a thin wrapper over pyEPR.calcs.back_box_numeric.epr_numerical_diagonalization — the very function the COM DistributedAnalysis uses — so the quantum result is produced by unchanged pyEPR code.
It returns the dressed mode frequencies and the cross-Kerr / anharmonicity matrix (MHz); the diagonal is each mode’s anharmonicity \(\alpha\). Mode 1 is the qubit (it carries essentially all of the junction participation).
f_ND, chi_ND = eprd.analyze(cos_trunc=8, fock_trunc=9)
alpha = np.abs(np.asarray(chi_ND))[0, 0]
print(f"qubit-mode anharmonicity alpha = {alpha:.3f} MHz")
qubit-mode anharmonicity alpha = 248.415 MHz
Same physics, no COM#
The qubit-mode participation p_mj = 0.9755 is identical to what pyEPR’s COM path extracts from the same solved design — digit-for-digit — and everything downstream of it (here, a 248.415 MHz anharmonicity from the four-mode diagonalization) follows from the same unchanged pyEPR physics. The only thing that changed is the road taken to HFSS: gRPC through Ansys’s maintained PyAEDT API instead of COM.
Practical upsides of the PyAEDT path:
No COM / pywin32 — the transport is gRPC, the default since AEDT 2022 R2.
Attaches to your running session via the project’s .aedt.lock, avoiding the stale-session and project-locked errors common with COM’s Running-Object-Table lookup.
Maintenance moves to Ansys — pyaedt is officially versioned and supported, rather than a private COM interface pyEPR has to track by hand across AEDT releases.
For COM users nothing changes: pyEPR.ansys and DistributedAnalysis are untouched, and PyAEDT stays an optional extra.
PyaedtDistributedAnalysis attaches to the running AEDT session (over gRPC, via the project’s .aedt.lock), then for every eigenmode it:
normalizes the fields to that mode with
Solutions.EditSources(so the line voltage and the energy share one field scaling),integrates the material-weighted electric energy \(U_E \propto \mathrm{Re}\!\int \mathbf{E}\cdot\varepsilon\,\mathbf{E}^* \, dV\) over all objects, and
integrates the junction line voltage \(V\) along each junction line,
then forms the participation \(p_{mj} = \tfrac{1}{2}L_J (V/\omega L_J)^2 \,/\, (U_E/2)\). All of it is pure gRPC; nothing here touches COM.
eprd = PyaedtDistributedAnalysis(pinfo, aedt_version="2026.1")
eprd.do_EPR_analysis()
for m, f in enumerate(eprd.freqs_GHz):
print(f"mode {m+1}: f = {f:.6f} GHz "
f"p_mj = {eprd.PJ[m, 0]:.4f} sign = {eprd.SJ[m, 0]:+d}")
mode 1: f = 4.815509 GHz p_mj = 0.9755 sign = -1
mode 2: f = 5.060004 GHz p_mj = 0.0061 sign = +1
mode 3: f = 6.484727 GHz p_mj = 0.0000 sign = -1
mode 4: f = 6.489703 GHz p_mj = 0.0000 sign = +1
The results live on the analysis object as plain NumPy arrays — the same shapes pyEPR’s diagonalizer expects (M modes × J junctions):
eprd.freqs_GHz— eigenfrequencies (GHz)eprd.PJ— participations \(p_{mj}\)eprd.SJ— signs \(s_{mj}\)eprd.Ljs— junction inductances (H), parsed from the HFSS variables
print("freqs_GHz :", np.round(eprd.freqs_GHz, 6))
print("PJ :", np.round(eprd.PJ.ravel(), 4))
print("SJ :", eprd.SJ.ravel())
print("Ljs (nH) :", np.round(eprd.Ljs * 1e9, 4))
freqs_GHz : [4.815509 5.060004 6.484727 6.489703]
PJ : [0.9755 0.0061 0. 0. ]
SJ : [-1 1 -1 1]
Ljs (nH) : [12.9201]
Hand off to pyEPR’s quantum analysis#
The extracted participations now go straight into pyEPR’s own physics. eprd.analyze() is a thin wrapper over pyEPR.calcs.back_box_numeric.epr_numerical_diagonalization — the very function the COM DistributedAnalysis uses — so the quantum result is produced by unchanged pyEPR code.
It returns the dressed mode frequencies and the cross-Kerr / anharmonicity matrix (MHz); the diagonal is each mode’s anharmonicity \(\alpha\). Mode 1 is the qubit (it carries essentially all of the junction participation).
f_ND, chi_ND = eprd.analyze(cos_trunc=8, fock_trunc=9)
alpha = np.abs(np.asarray(chi_ND))[0, 0]
print(f"qubit-mode anharmonicity alpha = {alpha:.3f} MHz")
qubit-mode anharmonicity alpha = 248.415 MHz
Same physics, no COM#
The qubit-mode participation p_mj = 0.9755 is identical to what pyEPR’s COM path extracts from the same solved design — digit-for-digit — and everything downstream of it (here, a 248.415 MHz anharmonicity from the four-mode diagonalization) follows from the same unchanged pyEPR physics. The only thing that changed is the road taken to HFSS: gRPC through Ansys’s maintained PyAEDT API instead of COM.
Practical upsides of the PyAEDT path:
No COM / pywin32 — the transport is gRPC, the default since AEDT 2022 R2.
Attaches to your running session via the project’s .aedt.lock, avoiding the stale-session and project-locked errors common with COM’s Running-Object-Table lookup.
Maintenance moves to Ansys — pyaedt is officially versioned and supported, rather than a private COM interface pyEPR has to track by hand across AEDT releases.
For COM users nothing changes: pyEPR.ansys and DistributedAnalysis are untouched, and PyAEDT stays an optional extra.
The extracted participations now go straight into pyEPR’s own physics. eprd.analyze() is a thin wrapper over pyEPR.calcs.back_box_numeric.epr_numerical_diagonalization — the very function the COM DistributedAnalysis uses — so the quantum result is produced by unchanged pyEPR code.
It returns the dressed mode frequencies and the cross-Kerr / anharmonicity matrix (MHz); the diagonal is each mode’s anharmonicity \(\alpha\). Mode 1 is the qubit (it carries essentially all of the junction participation).
f_ND, chi_ND = eprd.analyze(cos_trunc=8, fock_trunc=9)
alpha = np.abs(np.asarray(chi_ND))[0, 0]
print(f"qubit-mode anharmonicity alpha = {alpha:.3f} MHz")
qubit-mode anharmonicity alpha = 248.415 MHz
Same physics, no COM#
The qubit-mode participation p_mj = 0.9755 is identical to what pyEPR’s COM path extracts from the same solved design — digit-for-digit — and everything downstream of it (here, a 248.415 MHz anharmonicity from the four-mode diagonalization) follows from the same unchanged pyEPR physics. The only thing that changed is the road taken to HFSS: gRPC through Ansys’s maintained PyAEDT API instead of COM.
Practical upsides of the PyAEDT path:
No COM /
pywin32— the transport is gRPC, the default since AEDT 2022 R2.Attaches to your running session via the project’s
.aedt.lock, avoiding the stale-session and project-locked errors common with COM’s Running-Object-Table lookup.Maintenance moves to Ansys —
pyaedtis officially versioned and supported, rather than a private COM interface pyEPR has to track by hand across AEDT releases.
For COM users nothing changes: pyEPR.ansys and DistributedAnalysis are untouched, and PyAEDT stays an optional extra.