Skip to content

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 {doc}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. {eq}s_to_y_to_c.

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

import os
from math import inf
from pathlib import Path

import gdsfactory as gf
import numpy as np
import pyvista as pv
import skrf
from gdsfactory.components.interdigital_capacitor import interdigital_capacitor
from gdsfactory.gpdk import LAYER, get_generic_pdk
from gdsfactory.technology import LayerStack
from gdsfactory.technology.layer_stack import LayerLevel
from IPython.display import display
from matplotlib import pyplot as plt

from gplugins.common.types import RFMaterialSpec
from gplugins.palace import run_scattering_simulation_palace

gf.config.rich_output()
PDK = get_generic_pdk()
PDK.activate()

We employ an example LayerStack used in superconducting circuits similar to [@marxer_long-distance_2023].

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 :func:`~run_scattering_simulation_palace`. This runs the simulation and returns an instance of :class:`~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

Math

:label: s_to_y_to_c C_{\text{i,j}} = \frac{\mathrm{Im}\,Y_{\text{i,j}}}{\mathrm{i} \omega} .

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)")
TODO the results don't seem good, something must be wrong in the setup…
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

Bibliography

:style: unsrt :filter: docname in docnames