Skip to content

Commit

Permalink
Merge pull request #42 from nikosavola/capacitive-elmer-simulations
Browse files Browse the repository at this point in the history
Support electrostatic (capacitive) simulations with Elmer
  • Loading branch information
joamatab authored Aug 19, 2023
2 parents f7f3bbb + 0e180e1 commit d5aecd0
Show file tree
Hide file tree
Showing 13 changed files with 1,028 additions and 8 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/test_code.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,34 @@ jobs:
run: |
make meep
pytest gplugins/gmeep gplugins/modes
test_non_pip:
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
max-parallel: 2
matrix:
os: [ubuntu-latest]
plugin: [elmer]
name: Test ${{ matrix.plugin }} on ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: actions/cache@v3
with:
path: |
~/.cache/pip
key: ${{ hashFiles('pyproject.toml') }}
- uses: actions/setup-python@v4
with:
python-version: 3.11
- name: Install dependencies
run: |
make gmsh
make ${{ matrix.plugin }}
pip install -e .[dev,gmsh]
- name: Test with pytest
env:
GDSFACTORY_DISPLAY_TYPE: klayout
run: pytest gplugins/${{ matrix.plugin }}
test_code_coverage:
runs-on: ubuntu-latest
steps:
Expand Down
8 changes: 4 additions & 4 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ repos:
hooks:
- id: black

- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.9.0.5
hooks:
- id: shellcheck
# - repo: https://github.com/shellcheck-py/shellcheck-py
# rev: v0.9.0.5
# hooks:
# - id: shellcheck

- repo: https://github.com/PyCQA/bandit
rev: 1.7.5
Expand Down
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ install:
pip install -e .[dev,docs,database,devsim,femwell,gmsh,meow,meshwell,ray,sax,schematic,tidy3d,web]
pre-commit install

dev: test-data meep gmsh install
dev: test-data meep gmsh elmer install

gmsh:
sudo apt-get install -y python3-gmsh gmsh
Expand All @@ -12,6 +12,11 @@ gmsh:
meep:
mamba install -c conda-forge pymeep=*=mpi_mpich_* nlopt -y

elmer:
sudo apt-add-repository ppa:elmer-csc-ubuntu/elmer-csc-ppa
sudo apt-get update
sudo apt-get install -y elmerfem-csc mpich

test:
pytest

Expand Down
4 changes: 2 additions & 2 deletions docs/_config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,6 @@ latex:
latex_engine: pdflatex # one of 'pdflatex', 'xelatex' (recommended for unicode), 'luatex', 'platex', 'uplatex'
use_jupyterbook_latex: true # use sphinx-jupyterbook-latex for pdf builds as default

# Add a bibtex file so that we can create citations

html:
home_page_in_navbar: true
use_edit_page_button: true
Expand Down Expand Up @@ -68,3 +66,5 @@ sphinx:
.py:
- jupytext.reads
- fmt: py
bibtex_reference_style: author_year
bibtex_bibfiles: "bibliography.bib"
1 change: 1 addition & 0 deletions docs/_toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ parts:
- file: notebooks/devsim_01_pin_waveguide
- file: notebooks/tcad_02_analytical_process
- file: notebooks/tcad_03_numerical_implantation
- file: notebooks/elmer_01_electrostatic
- file: plugins_mode_solver
sections:
- file: notebooks/femwell_01_modes
Expand Down
31 changes: 31 additions & 0 deletions docs/bibliography.bib
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

@article{smolic_capacitance_2021,
title = {Capacitance matrix revisited},
volume = {92},
issn = {1937-6472},
url = {http://www.jpier.org/PIERB/pier.php?paper=21011501},
doi = {10.2528/PIERB21011501},
pages = {1--18},
journaltitle = {Progress In Electromagnetics Research B},
shortjournal = {{PIER} B},
author = {Smolić, Ivica and Klajn, Bruno},
urldate = {2023-08-17},
date = {2021},
langid = {english},
}

@article{marxer_long-distance_2023,
title = {Long-Distance Transmon Coupler with cz-Gate Fidelity above 99.8 \%},
volume = {4},
issn = {2691-3399},
url = {https://link.aps.org/doi/10.1103/PRXQuantum.4.010314},
doi = {10.1103/PRXQuantum.4.010314},
pages = {010314},
number = {1},
journaltitle = {{PRX} Quantum},
shortjournal = {{PRX} Quantum},
author = {Marxer, Fabian and Vepsäläinen, Antti and Jolin, Shan W. and Tuorila, Jani and Landra, Alessandro and Ockeloen-Korppi, Caspar and Liu, Wei and Ahonen, Olli and Auer, Adrian and Belzane, Lucien and Bergholm, Ville and Chan, Chun Fai and Chan, Kok Wai and Hiltunen, Tuukka and Hotari, Juho and Hyyppä, Eric and Ikonen, Joni and Janzso, David and Koistinen, Miikka and Kotilahti, Janne and Li, Tianyi and Luus, Jyrgen and Papic, Miha and Partanen, Matti and Räbinä, Jukka and Rosti, Jari and Savytskyi, Mykhailo and Seppälä, Marko and Sevriuk, Vasilii and Takala, Eelis and Tarasinski, Brian and Thapa, Manish J. and Tosto, Francesca and Vorobeva, Natalia and Yu, Liuqi and Tan, Kuan Yen and Hassel, Juha and Möttönen, Mikko and Heinsoo, Johannes},
urldate = {2023-08-17},
date = {2023-02-06},
langid = {english},
}
250 changes: 250 additions & 0 deletions docs/notebooks/elmer_01_electrostatic.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
# ---
# jupyter:
# jupytext:
# cell_metadata_filter: -all
# custom_cell_magics: kql
# text_representation:
# extension: .py
# format_name: percent
# format_version: '1.3'
# jupytext_version: 1.11.2
# kernelspec:
# display_name: base
# language: python
# name: python3
# ---

# %% [markdown]
# # 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 {cite:p}`smolic_capacitance_2021`.
#
# ## Installation
# See [Elmer FEM – Installation](https://www.elmerfem.org/blog/binaries/) for installation or compilation instructions. Gplugins assumes `ElmerSolver`, `ElmerSolver_mpi`, and `ElmerGrid` are available in your PATH environment variable.
#
# Alternatively, [Singularity / Apptainer](https://apptainer.org/) containers may be used. An example definition file is found at [CSCfi/singularity-recipes](https://github.com/CSCfi/singularity-recipes/blob/main/elmer/elmer_9.0_csc.def) and can be built with:
# ```console
# 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
# ```console
# #!/bin/bash
# singularity exec <CONTAINER_LOCATION>/elmer.sif ElmerSolver_mpi $@
# ```

# %%

import inspect
import os
from collections.abc import Sequence
from math import inf
from pathlib import Path

import gdsfactory as gf
import numpy as np
import pyvista as pv
from gdsfactory.component import Component
from gdsfactory.components.interdigital_capacitor import interdigital_capacitor
from gdsfactory.generic_tech import LAYER, get_generic_pdk
from gdsfactory.technology import LayerStack
from gdsfactory.technology.layer_stack import LayerLevel
from gdsfactory.typings import LayerSpec

from gplugins.elmer import run_capacitive_simulation_elmer

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

# LAYER_STACK = PDK.layer_stack

# %% [markdown]
# We employ an example LayerStack used in superconducting circuits similar to {cite:p}`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,
),
)
)
material_spec = {
"Si": {"relative_permittivity": 11.45},
"Nb": {"relative_permittivity": inf},
"vacuum": {"relative_permittivity": 1},
}

# %%

INTERDIGITAL_DEFAULTS = {
k: v.default
for k, v in inspect.signature(interdigital_capacitor).parameters.items()
}


@gf.cell
def interdigital_capacitor_enclosed(
enclosure_box: Sequence[Sequence[float | int]] = [[-200, -200], [200, 200]],
fingers: int = INTERDIGITAL_DEFAULTS["fingers"],
finger_length: float | int = INTERDIGITAL_DEFAULTS["finger_length"],
finger_gap: float | int = INTERDIGITAL_DEFAULTS["finger_gap"],
thickness: float | int = INTERDIGITAL_DEFAULTS["thickness"],
cpw_dimensions: Sequence[float | int] = (10, 6),
gap_to_ground: float | int = 5,
metal_layer: LayerSpec = INTERDIGITAL_DEFAULTS["layer"],
gap_layer: LayerSpec = "DEEPTRENCH",
) -> Component:
"""Generates an interdigital capacitor surrounded by a ground plane and coplanar waveguides with ports on both ends.
See for :func:`~interdigital_capacitor` for details.
Note:
``finger_length=0`` effectively provides a plate capacitor.
Args:
enclosure_box: Bounding box dimensions for a ground metal enclosure.
fingers: total fingers of the capacitor.
finger_length: length of the probing fingers.
finger_gap: length of gap between the fingers.
thickness: Thickness of fingers and section before the fingers.
gap_to_ground: Size of gap from capacitor to ground metal.
cpw_dimensions: Dimensions for the trace width and gap width of connecting coplanar waveguides.
metal_layer: layer for metalization.
gap_layer: layer for trenching.
"""
c = Component()
cap = interdigital_capacitor(
fingers, finger_length, finger_gap, thickness, metal_layer
).ref_center()
c.add(cap)

gap = Component()
for port in cap.get_ports_list():
port2 = port.copy()
direction = -1 if port.orientation > 0 else 1
port2.move((30 * direction, 0))
port2 = port2.flip()

cpw_a, cpw_b = cpw_dimensions
s1 = gf.Section(width=cpw_b, offset=(cpw_a + cpw_b) / 2, layer=gap_layer)
s2 = gf.Section(width=cpw_b, offset=-(cpw_a + cpw_b) / 2, layer=gap_layer)
x = gf.CrossSection(
width=cpw_a,
offset=0,
layer=metal_layer,
port_names=("in", "out"),
sections=[s1, s2],
)
route = gf.routing.get_route(
port,
port2,
cross_section=x,
)
c.add(route.references)

term = c << gf.components.bbox(
[[0, 0], [cpw_b, cpw_a + 2 * cpw_b]], layer=gap_layer
)
if direction < 0:
term.movex(-cpw_b)
term.move(
destination=route.ports[-1].move_copy(-1 * np.array([0, cpw_a / 2 + cpw_b]))
)

c.add_port(route.ports[-1])
c.auto_rename_ports()

gap.add_polygon(cap.get_polygon_enclosure(), layer=gap_layer)
gap = gap.offset(gap_to_ground, layer=gap_layer)
gap = gf.geometry.boolean(A=gap, B=c, operation="A-B", layer=gap_layer)

ground = gf.components.bbox(bbox=enclosure_box, layer=metal_layer)
ground = gf.geometry.boolean(
A=ground, B=[c, gap], operation="A-B", layer=metal_layer
)

c << ground

return c.flatten()


# %%
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.flatten()

# %%
results = run_capacitive_simulation_elmer(
c,
layer_stack=layer_stack,
material_spec=material_spec,
n_processes=4,
element_order=1,
simulation_folder=Path(os.getcwd()) / "tmp",
mesh_parameters=dict(
background_tag="vacuum",
background_padding=(0,) * 5 + (700,),
portnames=c.ports,
default_characteristic_length=200,
layer_portname_delimiter=(delimiter := "__"),
resolutions={
"bw": {
"resolution": 15,
},
"substrate": {
"resolution": 40,
},
"vacuum": {
"resolution": 40,
},
**{
f"bw{delimiter}{port}": {
"resolution": 20,
"DistMax": 30,
"DistMin": 10,
"SizeMax": 14,
"SizeMin": 3,
}
for port in c.ports
},
},
),
)
print(results)

# %%
if results.field_file_location:
field = pv.read(results.field_file_location)
slice = field.slice_orthogonal(z=layer_stack.layers["bw"].zmin * 1e-6)

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


# %% [markdown]
# ## Bibliography
# ```{bibliography}
# :style: unsrt
# ```
5 changes: 5 additions & 0 deletions gplugins/elmer/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from gplugins.elmer.get_capacitance import run_capacitive_simulation_elmer

__all__ = [
"run_capacitive_simulation_elmer",
]
Loading

0 comments on commit d5aecd0

Please sign in to comment.