Electrostatic simulations with Elmer#

Here, we show how Elmer may be used to perform electrostatic simulations. For a given geometry, one needs to specify the terminals where to apply potential. This effectively solves the mutual capacitance matrix for the terminals and the capacitance to ground. For details on the physics, see [1].

Installation#

See Elmer FEM – Installation for installation or compilation instructions. Gplugins assumes ElmerSolver, ElmerSolver_mpi, and ElmerGrid are available in your PATH environment variable.

Alternatively, Singularity / Apptainer containers may be used. An example definition file is found at CSCfi/singularity-recipes and can be built with:

singularity build elmer.sif <DEFINITION_FILE>.def

Afterwards, an easy install method is to add scripts to ~/.local/bin (or elsewhere in PATH) calling the Singularity container for each of the necessary executables. For example, one may create a ElmerSolver_mpi file containing

#!/bin/bash
singularity exec <CONTAINER_LOCATION>/elmer.sif ElmerSolver_mpi $@

Geometry, layer config and materials#

Hide code cell source
import os
from math import inf
from pathlib import Path

import gdsfactory as gf
import pyvista as pv
from gdsfactory.components.interdigital_capacitor_enclosed import (
    interdigital_capacitor_enclosed,
)
from gdsfactory.generic_tech import LAYER, get_generic_pdk
from gdsfactory.technology import LayerStack
from gdsfactory.technology.layer_stack import LayerLevel
from IPython.display import display

from gplugins.elmer import run_capacitive_simulation_elmer

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

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,
        ),
    )
)
material_spec = {
    "Si": {"relative_permittivity": 11.45},
    "Nb": {"relative_permittivity": inf},
    "vacuum": {"relative_permittivity": 1},
}
simulation_box = [[-200, -200], [200, 200]]
c = gf.Component("capacitance_elmer")
cap = c << interdigital_capacitor_enclosed(
    metal_layer=LAYER.WG, gap_layer=LAYER.DEEPTRENCH, enclosure_box=simulation_box
)
c.add_ports(cap.ports)
substrate = gf.components.bbox(bbox=simulation_box, layer=LAYER.WAFER)
c << substrate
c.plot()

Running the simulation#

We use the function run_capacitive_simulation_elmer(). This runs the simulation and returns an instance of ElectrostaticResults containing the capacitance matrix and a path to the mesh and the field solution.

help(run_capacitive_simulation_elmer)

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_elmer(
#     c,
#     layer_stack=layer_stack,
#     material_spec=material_spec,
#     n_processes=1,
#     element_order=1,
#     simulation_folder=Path(os.getcwd()) / "temporary",
#     mesh_parameters=dict(
#         background_tag="vacuum",
#         background_padding=(0,) * 5 + (700,),
#         port_names=c.ports.keys(),
#         default_characteristic_length=200,
#         resolutions={
#             "bw": {
#                 "resolution": 15,
#             },
#             "substrate": {
#                 "resolution": 40,
#             },
#             "vacuum": {
#                 "resolution": 40,
#             },
#             **{
#                 f"bw__{port}": {  # `__` is used as the layer–port delimiter for Elmer
#                     "resolution": 20,
#                     "DistMax": 30,
#                     "DistMin": 10,
#                     "SizeMax": 14,
#                     "SizeMin": 3,
#                 }
#                 for port in c.ports
#             },
#         },
#     ),
# )
# display(results)
if results.field_file_location:
    pv.start_xvfb()
    pv.set_jupyter_backend("trame")
    field = pv.read(results.field_file_location)
    field_slice = field.slice_orthogonal(z=layer_stack.layers["bw"].zmin * 1e-6)

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

Bibliography#

[1]

Ivica Smolić and Bruno Klajn. Capacitance matrix revisited. Progress In Electromagnetics Research B, 92:1–18, 2021. URL: http://www.jpier.org/PIERB/pier.php?paper=21011501 (visited on 2023-08-17), doi:10.2528/PIERB21011501.

[2]

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.