Skip to content

Electrostatic simulations with Palace

Here, we show how Palace may be used to perform electrostatic simulations. For a given geometry, one needs to specify the terminals where to apply potential, similar to {doc}./elmer_01_electrostatic.py. This effectively solves the mutual capacitance matrix for the terminals and the capacitance to ground. For details on the physics, see [@smolic_capacitance_2021].

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 pyvista as pv
from gdsfactory.components.analog.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 gplugins.common.types import RFMaterialSpec
from gplugins.palace import run_capacitive_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=10,
            zmin=0,
            material="Si",
            mesh_order=99,
        ),
        bw=LayerLevel(
            layer=LAYER.WG,
            thickness=200e-3,
            zmin=10,
            material="Nb",
            mesh_order=2,
        ),
    )
)
material_spec: RFMaterialSpec = {
    "Si": {"relative_permittivity": 11.45},
    "Nb": {"relative_permittivity": inf},
    "vacuum": {"relative_permittivity": 1},
}
simulation_box = [[-200, -200], [200, 200]]
c = gf.Component()
cap = c << interdigital_capacitor()
c.add_ports(cap.ports)
substrate = gf.components.bbox(c, layer=LAYER.WAFER)
c << substrate
c.plot()

Running the simulation

We use the function :func:`~run_capacitive_simulation_palace`. This runs the simulation and returns an instance of :class:`~ElectrostaticResults` containing the capacitance matrix and a path to the mesh and the field solutions.
help(run_capacitive_simulation_palace)
.. note::
   The meshing parameters and element order shown here are very lax. As such, the computed capacitances are not very accurate.
results = run_capacitive_simulation_palace(
    c,
    layer_stack=layer_stack,
    material_spec=material_spec,
    n_processes=1,
    element_order=2,
    simulation_folder=Path(os.getcwd()) / "temporary",
    mesh_parameters=dict(
        background_tag="vacuum",
        background_padding=(0,) * 5 + (10,),
        port_names=[port.name for port in c.ports],
        default_characteristic_length=2,
        resolutions={
            "bw": {
                "resolution": 2,
            },
            "substrate": {
                "resolution": 2,
            },
            "vacuum": {
                "resolution": 2,
            },
            **{
                f"bw__{port.name}": {  # `__` is used as the layer to port delimiter for Palace
                    "resolution": 10,
                    "DistMax": 30,
                    "DistMin": 10,
                    "SizeMax": 2,
                    "SizeMin": 1,
                }
                for port in c.ports
            },
        },
    ),
)
display(results)
if results.field_file_location:
    field = pv.read(results.field_file_location)
    field_slice = field.slice_orthogonal(z=layer_stack.layers["bw"].zmin)

    p = pv.Plotter()
    p.add_mesh(field_slice, scalars="E", cmap="turbo")
    p.show_grid()
    p.camera_position = "xy"
    p.enable_parallel_projection()
    p.show()

Bibliography

Bibliography

:style: unsrt :filter: docname in docnames