1. Capacitance extraction of a coaxial cable
1.1. Requirements
1.1.1. Software components
QTCAD
Gmsh
1.1.2. Geometry file
qtcad/examples/tutorials/meshes/coaxial_cable.py
1.1.3. Python script
qtcad/examples/tutorials/cap_coaxial_cable.py
1.1.4. References
1.2. Briefing
In this tutorial, we explain how to perform capacitance extraction
for a coaxial cable. It is assumed that the user is familiar with basic
Python. It is also assumed that the user has access to the script that uses Gmsh to generate
the mesh for the coaxial cable device. Such files for all the QTCAD tutorials are stored in
the folder examples/tutorials/meshes
. Some of the files are written in Gmsh’s scripting
language and have an extension .geo
, while the .py`
files use Gmsh’s Python interface.
The outer surface of the cable is a signal conductor (labeled by the physical group "signal"
).
The ground conductor
("gnd"
) is on the inside. The medium between the two conductors is called "domain"
.
The full code may be found at the bottom of this page,
or in qtcad/examples/tutorials/cap_coaxial_cable.py
.
Do not hesitate to look into the API reference for detailed descriptions of the inputs and outputs of each class, method, or function used in the tutorials. The API reference also contains documentation for many other useful features not described here.
1.3. Mesh generation
The mesh is generated by executing the
script qtcad/examples/tutorials/meshes/coaxial_cable.py
.
This wil produce a .msh
file in the same directory as the mesh generation
script. See Arbitrary geometry definition with Gmsh for more information about
interfacing with Gmsh.
1.4. Setting up the device and extracting the capacitance
1.4.1. Header
A header is first written to import relevant modules from the QTCAD Device simulator.
import qtcad.device.capacitance as cap
from qtcad.device.mesh3d import Mesh
from qtcad.device import Device
from qtcad.device import materials as mt
from qtcad.device import constants as ct
from pathlib import Path
import os
import math
This imports modules (or classes within modules) that are necessary to the execution of the code that follows. Here is a brief description of what each of these modules is responsible for.
capacitance
: Capacitance solver which will be used below.mesh3d
: Definition of the 3D Mesh class, which stores information about the position of mesh points, their connectivity (definition of elements, physical regions and boundaries).device
: Definition of the Device class, which is central to all simulations done with QTCAD. A device object stores the value of all physical parameters (for example, permittivity) and variables (for example, electric potential) at each element or mesh point.materials
: Objects describing each material supported by QTCAD. Here, a material is an object whose attributes are the material parameters such as permittivity.constants
: Universal physical constants (elementary charge, permittivity of free space, etc.).
The capacitance
module contains Solver
and
SolverParams
classes. While the Solver
class contains algorithms needed
to solve the corresponding equations, the SolverParams
class may be used to
control parameters of the solver.
1.4.2. Input parameters
# input parameters
eps_rel = 10 # relative permittivity of the dielectric
length = 50e-3 # length of the cable (in meters)
r_inner = 2e-3 # inner radius of the cable
r_outer = 5e-3 # outer radius of the cable
The first parameter is the relative permittivity of the dielectric that will be used later
to define the medium in the domain
region. The next three parameters describe the geometry
of the dielectric and are the same ones in qtcad/examples/tutorials/meshes/coaxial_cable.py
,
except written in SI units as opposed to millimeters. These values are used later to compute
the theoretical value of the capacitance.
1.4.3. Directories and file paths
The code below defines paths to the input mesh file and the folder where solver results are to be saved. It also checks that the mesh file exists.
# directories and file paths
script_dir = Path(__file__).parent.resolve()
mesh_dir = script_dir / "meshes" # for the mesh file
result_dir = script_dir / "output" / Path(__file__).stem # for results
fpath_mesh = mesh_dir / "coaxial_cable.msh" # mesh file
# code to check that the mesh file exists
if not os.path.isfile(fpath_mesh):
raise Exception(
"Please run %s/coaxial_cable.py to generate the mesh and raw geometry files."
% (mesh_dir)
)
1.4.4. Defining the mesh
The mesh is defined by loading the mesh file produced by Gmsh and setting units of length.
# scale in the Gmsh files
scale = 1e-3 # Gmsh files are in mm
# parse the mesh
mesh = Mesh(scale, fpath_mesh)
The two arguments of the constructor of the Mesh
class are scaling
and fpath_mesh
. Argument fpath_mesh
is the string or pathlib.Path
object pointing to the mesh file to use. Argument scaling
indicates a number by
which all coordinates in the mesh file should be multiplied. Since QTCAD
uses SI units, the value chosen here (1e-3
) indicates that lengths
in the mesh file are millimeters. It is recommended to choose the units
in the mesh file that avoid very small or very large values for the dimensions.
The mesh may also be displayed using:
# Plot the mesh
mesh.show()
This will call the Visualization ToolKit (VTK) through its Python wrapper (PyVista) to produce a 3D representation of the mesh, like this:

Fig. 1.4.11 Mesh illustration
1.4.5. Creating the device
The device to simulate is then initialized on the mesh defined above:
# Create device from mesh.
dvc = Device(mesh)
1.4.6. Specifying physical regions and boundaries
It is now time to specify the value of device parameters (permittivity for the case of capacitance solver) at each point of the mesh over which the device is build. This may be done explicitly for each element or node of the mesh; however this approach is tedious and produces code that is prone to bugs and difficult to read. A better way is to leverage physical volumes and surfaces defined in the geometry file to define regions and boundaries.
Regions are a collection of elements that belong to a given 3D physical group. A physical group is a set of elements associated with an integer or string label in the geometry file. It is possible to define regions based on physical groups, and specify a material for such a region. In the present example, this is done as follows.
First, we define a material with relative permittivity of 10
.
# define the medium in the problem
material_properties = dict(name="arbitrary_medium", eps=eps_rel * ct.eps0)
material = mt.Material(material_properties)
The materials
module contains material properties
for many commonly used media, such as vacuum, silicon, silicon dioxide, and others.
A table of materials with their properties is available in the
Material list section.
For example, in order to use silicon in place of the arbitrary medium above, one can
replace the second line above with material = mt.Si
.
The next step is to assign material
to the "domain"
region.
# Create regions first (here only one region),
# Make sure each element is assigned to a region
dvc.new_region("domain", material)
Remark: When writing geometry files, it is important to make sure that all elements in a given mesh belong to one and only one region. If an element belongs to two or more regions at the same time, an exception will be raised.
In addition to 3D physical groups (physical volumes), Gmsh supports 2D physical groups (physical surfaces), which also associate certain two-dimensional elements to a label. These may be leveraged to define boundaries as follows:
# Set potential to zero on the surfaces of each conductor (including ground)
dvc.new_dirichlet_bnd("gnd", 0.0)
dvc.new_dirichlet_bnd("signal", 0.0)
In the code above, we associate the Gmsh physical surfaces gnd
and signal
with
homogeneous Dirichlet boundary conditions. In the capacitance solver, boundaries with this boundary
condition are treated as perfect electric conductors.
Note
The two flat annular surfaces on both ends of the coaxial cable have no boundary conditions or Gmsh physical surfaces associated with them. These boundaries will be treated as per natural boundary conditions explained in the capacitance solver theory and linear Poisson equation solver theory.
1.4.7. Creating and running the capacitance solver
Next, we define the signal conductors (see Signal and ground conductors). In this case, we have a single
signal conductor signal
and a single ground conductor gnd
. Hence, the capacitance matrix will be a
1 × 1 matrix, containing the capacitance between the two conductors. See Capacitance Matrix
for more information.
# Set up signal conductors
signal_conductors = ["signal"]
Signal conductors can also include multiple surfaces (see Signal and ground conductors for details).
Next, we create the solver object and run the solver:
# setup and run the solver
cap_solver = cap.Solver(dvc, signal_conductors)
c = cap_solver.solve()
As the arguments to the Solver
constructor
,
we specify the device and signal conductor created earlier.
Here, the solver is created with default parameters, which is why the
SolverParams
class is not used. In the next tutorial
you will find details on using this class to specify parameters of the solver.
The output of the solver is a capacitance matrix, represented as a dictionary. The following code displays the solver result, along with the theoretical value.
# print the capacitance
print(
"Capacitance between the signal and ground lines: %.2f pF"
% (c["signal", "signal"] * 1e12)
)
# compute and print the theoretical value
# See Pozar, David M. "Microwave engineering." Fourth Edition, John Wiley & Sons, Inc (2012).
theor_value = 2 * math.pi * material.eps * length / math.log(r_outer / r_inner)
print("Theoretical value of capacitance is : %.2f pF" % (theor_value * 1e12))
The resulting values are shown below:
Capacitance between the signal and ground lines: 30.06 pF
Theoretical value of capacitance is : 30.36 pF
1.5. Full code
__copyright__ = "Copyright 2022-2025, Nanoacademic Technologies Inc."
import qtcad.device.capacitance as cap
from qtcad.device.mesh3d import Mesh
from qtcad.device import Device
from qtcad.device import materials as mt
from qtcad.device import constants as ct
from pathlib import Path
import os
import math
# input parameters
eps_rel = 10 # relative permittivity of the dielectric
length = 50e-3 # length of the cable (in meters)
r_inner = 2e-3 # inner radius of the cable
r_outer = 5e-3 # outer radius of the cable
# directories and file paths
script_dir = Path(__file__).parent.resolve()
mesh_dir = script_dir / "meshes" # for the mesh file
result_dir = script_dir / "output" / Path(__file__).stem # for results
fpath_mesh = mesh_dir / "coaxial_cable.msh" # mesh file
# code to check that the mesh file exists
if not os.path.isfile(fpath_mesh):
raise Exception(
"Please run %s/coaxial_cable.py to generate the mesh and raw geometry files."
% (mesh_dir)
)
# scale in the Gmsh files
scale = 1e-3 # Gmsh files are in mm
# parse the mesh
mesh = Mesh(scale, fpath_mesh)
# Plot the mesh
mesh.show()
# Create device from mesh.
dvc = Device(mesh)
# define the medium in the problem
material_properties = dict(name="arbitrary_medium", eps=eps_rel * ct.eps0)
material = mt.Material(material_properties)
# Create regions first (here only one region),
# Make sure each element is assigned to a region
dvc.new_region("domain", material)
# Set potential to zero on the surfaces of each conductor (including ground)
dvc.new_dirichlet_bnd("gnd", 0.0)
dvc.new_dirichlet_bnd("signal", 0.0)
# Set up signal conductors
signal_conductors = ["signal"]
# setup and run the solver
cap_solver = cap.Solver(dvc, signal_conductors)
c = cap_solver.solve()
# print the capacitance
print(
"Capacitance between the signal and ground lines: %.2f pF"
% (c["signal", "signal"] * 1e12)
)
# compute and print the theoretical value
# See Pozar, David M. "Microwave engineering." Fourth Edition, John Wiley & Sons, Inc (2012).
theor_value = 2 * math.pi * material.eps * length / math.log(r_outer / r_inner)
print("Theoretical value of capacitance is : %.2f pF" % (theor_value * 1e12))