Full-wave driven simulations with Palace#
Warning
The full-wave driven plugin for Palace is experimental and currently supports only lumped ports that have to be placed manually as rectangles in the geometry.
Here, we show how Palace may be used to perform full-wave driven simulations in the frequency domain. See Palace – Crosstalk Between Coplanar Waveguides for more details.
For a given geometry, one needs to specify the terminals where to apply an excitation similar to Ansys HFSS. To this end, lumped ports (or wave ports) have to be added to the geometry to simulate. This effectively solves the scattering parameters for the terminals.
In this notebook, we the same interdigital capacitor as in palace_01_electrostatic.ipynb but add lumped ports to the geometry. Afterwards, the capacitance matrix can be computed from the scattering parameters as described in Eq. (1).
Installation#
See Palace – Installation for installation or compilation instructions. Gplugins assumes palace
is available in your PATH environment variable.
Alternatively, Singularity / Apptainer containers may be used. Instructions for building and an example definition file are found at Palace – Build using Singularity/Apptainer.
Afterwards, an easy install method is to add a script to ~/.local/bin
(or elsewhere in PATH
) calling the Singularity container. For example, one may create a palace
file containing
#!/bin/bash
singularity exec ~/palace.sif /opt/palace/bin/palace "$@"
Geometry, layer config and materials#
We employ an example LayerStack used in superconducting circuits similar to [1].
layer_stack = LayerStack(
layers=dict(
substrate=LayerLevel(
layer=LAYER.WAFER,
thickness=500,
zmin=0,
material="Si",
mesh_order=99,
),
bw=LayerLevel(
layer=LAYER.WG,
thickness=200e-3,
zmin=500,
material="Nb",
mesh_order=2,
),
bw_port=LayerLevel(
layer=LAYER.PORT,
thickness=200e-3,
zmin=500,
material="Nb",
mesh_order=2,
),
)
)
material_spec: RFMaterialSpec = {
"Si": {"relative_permittivity": 11.45, "relative_permeability": 1},
"Nb": {"relative_permittivity": inf, "relative_permeability": 1},
"vacuum": {"relative_permittivity": 1, "relative_permeability": 1},
}
c = gf.Component()
cap = c << interdigital_capacitor()
c
c = gf.Component()
cap = c << interdigital_capacitor()
# Add lumped port rectangles manually, see examples for https://awslabs.github.io/palace/stable/examples/cpw/
c.add_polygon([(-40, +11), (-46, +11), (-46, +5), (-40, +5)], layer=LAYER.PORT)
c.add_polygon([(40, -11), (46, -11), (46, -5), (40, -5)], layer=LAYER.WAFER)
c.add_port(
"o1_1", center=(-40, (11 + 5) / 2), layer=LAYER.PORT, width=1, orientation=180
)
c.add_port("o1_2", center=(+46, -11), layer=LAYER.PORT, width=1, orientation=0)
c.add_polygon([(-200, -200), (200, -200), (200, 200), (-200, 200)], layer=LAYER.WAFER)
c.draw_ports()
c.show()
c
Running the simulation#
We use the function run_scattering_simulation_palace()
. This runs the simulation and returns an instance of DrivenFullWaveResults
containing the capacitance matrix and a path to the mesh and the field solutions.
help(run_scattering_simulation_palace)
results = run_scattering_simulation_palace(
c,
layer_stack=layer_stack,
material_spec=material_spec,
only_one_port=True,
simulation_folder=Path(os.getcwd()) / "temporary",
driven_settings={
"MinFreq": 0.1,
"MaxFreq": 5,
"FreqStep": 5,
},
mesh_parameters=dict(
background_tag="vacuum",
background_padding=(0,) * 5 + (700,),
port_names=c.ports,
verbosity=1,
default_characteristic_length=200,
resolutions={
"bw": {
"resolution": 14,
},
"substrate": {
"resolution": 50,
},
"vacuum": {
"resolution": 120,
},
**{
f"bw_port{port}_vacuum": {
"resolution": 8,
"DistMax": 30,
"DistMin": 10,
"SizeMax": 14,
"SizeMin": 3,
}
for port in c.ports
},
},
),
)
display(results)
The capacitance matrix can be solved from the admittance matrix \(Y\) as
df = results.scattering_matrix
df.columns = df.columns.str.strip()
s_complex = 10 ** df["|S[2][1]| (dB)"].values * np.exp(
1j * skrf.degree_2_radian(df["arg(S[2][1]) (deg.)"].values)
)
ntw = skrf.Network(f=df["f (GHz)"].values, s=s_complex, z0=50)
cap = np.imag(ntw.y.flatten()) / (ntw.f * 2 * np.pi)
display(cap)
plt.plot(ntw.f, cap * 1e15)
plt.xlabel("Freq (GHz)")
plt.ylabel("C (fF)")
if results.field_file_locations:
pv.start_xvfb()
pv.set_jupyter_backend("trame")
field = pv.read(results.field_file_locations[0])
slice = field.slice_orthogonal(z=layer_stack.layers["bw"].zmin * 1e-6)
p = pv.Plotter()
p.add_mesh(slice, scalars="Ue", cmap="turbo")
p.show_grid()
p.camera_position = "xy"
p.enable_parallel_projection()
p.show()
Bibliography#
Fabian Marxer, Antti Vepsäläinen, Shan W. Jolin, Jani Tuorila, Alessandro Landra, Caspar Ockeloen-Korppi, Wei Liu, Olli Ahonen, Adrian Auer, Lucien Belzane, Ville Bergholm, Chun Fai Chan, Kok Wai Chan, Tuukka Hiltunen, Juho Hotari, Eric Hyyppä, Joni Ikonen, David Janzso, Miikka Koistinen, Janne Kotilahti, Tianyi Li, Jyrgen Luus, Miha Papic, Matti Partanen, Jukka Räbinä, Jari Rosti, Mykhailo Savytskyi, Marko Seppälä, Vasilii Sevriuk, Eelis Takala, Brian Tarasinski, Manish J. Thapa, Francesca Tosto, Natalia Vorobeva, Liuqi Yu, Kuan Yen Tan, Juha Hassel, Mikko Möttönen, and Johannes Heinsoo. Long-distance transmon coupler with cz-gate fidelity above 99.8 %. PRX Quantum, 4(1):010314, 2023. URL: https://link.aps.org/doi/10.1103/PRXQuantum.4.010314 (visited on 2023-08-17), doi:10.1103/PRXQuantum.4.010314.