This section explains some things about the KQCircuits code architecture.
Every KQCircuits object that can be placed in a KLayout layout is derived from
Element, that in turn is derived from PCellDeclarationHelper.
There exist also more specific base classes such as
All base classes (black) are shown, abstracts are ovals, concretes are rectangles. Only one example “leaf” class (green) is shown for most base classes. The class hierarchy is relatively flat as PCells make it more natural to use composition than inheritance. For simplicity we don’t use multiple inheritance in this project, if it is ever used keep the Element as the first parent.
Elements in KQCircuits are PCells, see KLayout documentation for more information on PCells. Due to how KLayout handles PCells, the elements (i.e. classes inheriting from Element) in KQCircuits should not be treated as normal Python classes. The following things should be taken into account when writing new elements:
PCells in Python code have corresponding objects living in the C++-side of KLayout. This means that you should not instantiate any elements like a normal class, but instead use the
createmethod of the element, which are wrappers for KLayout’s
layout.create_cell. These wrappers are used to validate the parameters using the
Validatorin parameter_helper.py. The C++-object is created properly only if you use these wrappers (or if a new PCell is added to a layout in KLayout GUI).
insert_cellis the preferred method of adding a new (sub)cell. The
createmethod is a
@classmethodthat is useful when adding a top level PCell or when not calling from another PCell.
When a new PCell instance is created, or when the parameters are changed in KLayout GUI, the
produce_implmethod of the PCell is called. For KQC elements
buildmethod, which is where you “build” the element. When
produce_implis called, the instance variables of the PCell are set to new values based on the given parameters. The PCell instance is then created or updated based on these new parameter values.
The PCell parameters for KQCircuits elements are plain class attributes defined
Param descriptor class. These can be used like normal instance
variables. The values of these parameters can be set from the KLayout GUI, or in
add_element methods in code. The parameters of a class
are automatically merged with its parent’s parameters (see element.py), so
an instance will contain the parameters of all its ancestors in the inheritance
hierarchy. When building hierarchical elements the parameter values appropriate
for a sub-element are transparently passed to it from the caller with
It is possible to change inherited parameters default values in a per class
basis using the
default_parameter_values section of the defaults.py
configuration file. Technically this creates a copy of the Param object with
different default value.
add_parameter decorator functions add some other class’
parameter(s) to the decorated class so there is no need to re-define the same
parameter in multiple places. They are like normal parameters to all intents and
purposes. Note that these parameters will be inherited by descendants of the
decorated class. Technically these are like references to the same Param object.
add_parameters_from it is also possible to add some other class’
parameter with a changed default value. This is practically identical to setting
a default value for the decorated class using
the configuration file. Technically, new parameter(s) are created with the
# Get two parameters form 'OtherClass' @add_parameters_from(OtherClass, "param_a", "param_b") class MyClass(): # Get all parameters form 'OtherClass' @add_parameters_from(OtherClass, "*") # or just @add_parameters_from(OtherClass) class MyClass(): # Get all parameters form 'OtherClass' except one @add_parameters_from(OtherClass, "*", "param_a") class MyClass(): # Get and change default values of several parameters at once @add_parameters_from(OtherClass, "param_a", "param_b", param_c=42, param_d=43) class MyClass(): # Get all parameters form 'OtherClass' but override one @add_parameters_from(OtherClass, "*", param_b=41) class MyClass():
add_parameter decorator is less practical as it only allows adding
parameters one-by-one, but it is possible to change all the properties of a
parameter with it, not only the
default value but also
unit or even
description. The syntax is also a bit
@add_parameter(OtherClass, "param_a", hidden=True, choices=[...], ...)
Note that decorators are applied in “reverse-order”, i.e. first the class is defined and then from bottom-up the decorators are called. This is also the reason why decorators may override Param definitions in the class’ body but not the other way around.
The PCells in KQCircuits are divided into libraries such as
Chip Library or
Junction Library. Each library contains a base
class for all other classes in the library, for example all classes in the
Chip Library inherit from the
Chip base class. Each base class contains
LIBRARY_DESCRIPTION constants, so that these are
available for all derived classes.
Libraries have a strict dependency order defined in
defaults.py. KLayout loads them in this order. Classes can not use other
classes from other libraries downstream in the dependency graph.
The elements in these libraries are automatically discovered and registered to
KLayout by library_helper.py. It finds all classes in KQCircuits
directory which inherit from
PCellDeclarationHelper, and uses their
LIBRARY_NAME attribute to register them to the correct library. Note
that this requires all element classes to follow PascalCase naming
convention, as required by PEP-8.
Any KLayout functions/classes/etc. should be imported using
(see pya_resolver.py). For example, you should write
from kqcircuits.pya_resolver import pya, and not
import pya or
import klayout.db. This ensures that KQCircuits works both with KLayout
GUI and the standalone module.