Components
DeviceLayout.AbstractComponent
— Typeabstract type AbstractComponent{T} <: GeometryStructure{T}
A parameterized layout element, to be used as a building block in schematic-driven design.
The alias Component = AbstractComponent{typeof(1.0nm)}
is provided for convenience.
Each AbstractComponent
comes with the necessary parameters and methods to render its geometry and to attach it to other components in a schematic. Something similar to this concept may be familiar from other electronic design automation tools as a PCell ("parameterized cell").
You might call something a component if you would include it in a schematic or diagram rather than abstracting it away, if it can be described as a black box with ports, or if you might want to simulate it independently. For example, a series interdigital capacitor could be defined as a AbstractComponent
, with attachment points (Hook
s) at the end of each lead.
AbstractComponent
s provide a common interface:
name(c::MyComponent)
: The name of the component.parameters(c::MyComponent)
: The parameters of the component as aNamedTuple
.geometry(c::MyComponent)
: A coordinate system containingc
's rendered geometry.hooks(c::MyComponent)
: ANamedTuple
ofHook
s andHook
arrays that specify how connections are made withc
.hooks(c::MyComponent, h::Symbol)
: The specific hook identified byh
.default_parameters(c::Union{MyComponent, Type{MyComponent}})
: Default parameters as aNamedTuple
.parameter_names(c::Union{MyComponent, Type{MyComponent}})
: Parameter names as a collection ofSymbol
s.- A keyword constructor that merges keyword arguments into
default_parameters
non-recursively. (That is, aNamedTuple
keyword argument will overwrite the default parameter entirely, meaning every "subparameter" in theNamedTuple
needs to be specified.) create_component(::Type{MyComponent}, name::String=default_parameters(MyComponent).name, base_parameters::NamedTuple=default_parameters(MyComponent); kwargs...)
: Constructor that recursively mergeskwargs
intobase_parameters
. (That is, aNamedTuple
keyword argument will be merged into the correspondingNamedTuple
base parameter, meaning not every "subparameter" needs to be fully specified.)set_parameters(mycomp::MyComponent, name::String=name(mycomp), params::NamedTuple=parameters(c); kwargs...)
: Shorthand forcreate_component
usingmycomp
for base parameters(mycomp::MyComponent)(name::String=name(mycomp), params::NamedTuple=parameters(mycomp)=; kwargs...)
: Shorter shorthand for the above
Since AbstractComponent
is a subtype of GeometryStructure
, they can also be referenced by a StructureReference
. Other GeometryStructure
interface methods, including elements
, element_metadata
, refs
, flatten
, footprint
, halo
, and transform
operate on geometry(mycomp)
.
The name
of a component is not guaranteed to be unique. Instances of components within schematics as well as coordinate systems within a layout will always have unique identifiers, which are automatically constructed from a component's name using uniquename
if it is not already unique. For this reason, it is often best for designers to give important components unique names, guaranteeing that the corresponding identifiers are the same.
DeviceLayout.SchematicDrivenLayout.Component
— Typeconst Component = AbstractComponent{typeof(1.0UPREFERRED)}
Component
is an alias for AbstractComponent
with the coordinate type typeof(1.0UPREFERRED)
.
DeviceLayout.UPREFERRED
is a constant set according to the unit
preference in Project.toml
or LocalPreferences.toml
. The default ("PreferNanometers"
) gives const UPREFERRED = DeviceLayout.nm
, with mixed-unit operations preferring conversion to nm
.
Defining components
The recommended way to define a component is the @compdef
macro, which specifies the parameters names and defaults as part of the struct definition. It creates the keyword constructor (like Base.@kwdef
does) as well as the default_parameters
method that are required to allow the above interface to work.
DeviceLayout.SchematicDrivenLayout.@compdef
— Macro@compdef typedef
This is a helper macro that extends Base.@kwdef
, which automatically defines a keyword-based constructor for the type declared in the expression typedef
, which must be a struct
or mutable struct
expression. The default argument is supplied by declaring fields of the form field::T = default
or field = default
. If no default is provided then the keyword argument becomes a required keyword argument in the resulting type constructor. Inner constructors can still be defined, but at least one should accept arguments in the same form as the default inner constructor (i.e. one positional argument per field) in order to function correctly with the keyword outer constructor.
This generates a default_parameters
method for the defined type, which returns the default parameters as a NamedTuple
. If any parameters are required (have no default), they do not appear in default_parameters
.
The new type will also have a _geometry
field for caching geometry, or _graph
and _schematic
fields for composite components. Note that if you want to use your own abstract composite supertype you should define SchematicDrivenLayout.iscomposite(::Val{:MyAbstractCompositeComponent}) = true
.
Examples
julia> @compdef struct MyComp <: Component
name::String = "mycomp"
a::Int = 1 # specified default
b::String # required keyword
end
julia> default_parameters(MyComp)
(name = "mycomp", a = 1)
An AbstractComponent
should include a name
field, and also implement the following specializations:
_geometry!(cs::CoordinateSystem, comp::MyComponent)
: Render the component's geometry to aCoordinateSystem
cs
.hooks(::MyComponent)
: aNamedTuple
ofHook
s and/orVector{Hook}
s that specify where and how attachments are made
Validation or reconciling of parameters should be done in an inner constructor. At least one inner constructor should accept arguments in the same form as the default inner constructor (i.e. one positional argument per field) in order to function correctly with the keyword outer constructor.
An AbstractComponent
may also override GeometryStructure
methods like footprint
and halo
.
The methods check_rotation
and allowed_rotation_angles
can be implemented to enforce a requirement for the orientation of the component in the global coordinate system:
DeviceLayout.SchematicDrivenLayout.check_rotation
— Functioncheck_rotation(::AbstractComponent)
Determines whether the global orientation of a component will be checked by check!(::Schematic)
. check_rotation(::AbstractComponent
) returns false
, so any components of type T
requiring rotation checks must overload this method as check_rotation(::T) = true
. Checkable components must also overload the method allow_rotation_angles(::T)
.
DeviceLayout.SchematicDrivenLayout.allowed_rotation_angles
— Functionallowed_rotation_angles(::AbstractComponent)
Return a vector of allowed rotation angles. If the net rotation of a component in a planned Schematic
(the rotation of its native axes relative to the axes of the global coordinate system) matches a number in this list, the component passes the check.
Finally, an AbstractComponent
may define methods to specify default hooks for fusion with other components in a schematic:
matching_hooks(::MyComponent1, ::MyComponent2)
matching_hooks(::MyComponent2, ::MyComponent1)
matching_hook(::MyComponent1, hook1::Symbol, ::MyComponent2)
- ...
Methods for both argument orders in matching_hooks
should generally be defined. Although SchematicGraph
s are undirected, certain components may treat the different argument orders differently. For example, matching_hooks(::Path, <:AbstractComponent)
will attach the second argument to the endpoint hook :p1
on the Path
, while the reverse order will attach the start point hook :p0
to the first argument.
DeviceLayout.SchematicDrivenLayout.matching_hooks
— Functionmatching_hooks(c1::S, c2::T) where {S <: AbstractComponent, T <: AbstractComponent}
Return the hooks on c1
and c2
that should be fused together (as a Tuple{Symbol,Symbol}
).
DeviceLayout.SchematicDrivenLayout.matching_hook
— Functionmatching_hook(c1::S, h1::Symbol, c2::T) where {S <: AbstractComponent, T <: AbstractComponent}
Return the hook on c2
that goes with h1
on c1
(as a Symbol
).
Components support the following API:
DeviceLayout.SchematicDrivenLayout.geometry
— Functiongeometry(comp::AbstractComponent)
A CoordinateSystem
containing the AbstractComponent
's geometry with metadata.
The result for each unique comp
(by ===
) is memoized.
The result has result.name == uniquename(name(comp))
.
geometry(cc:AbstractCompositeComponent)
Return the CoordinateSystem
resulting from build!(plan(graph(cc)))
.
DeviceLayout.hooks
— Functionhooks(pa::Path)
:p0
: AHandedPointHook
looking into the start of pathpa
.:p1
: AHandedPointHook
looking into the end of pathpa
.:p0_lh
: A left-handedHandedPointHook
looking into the start of pathpa
.:p1_lh
: A left-handedHandedPointHook
looking into the end of pathpa
.
hooks(rres::ExampleClawedMeanderReadout)
Hook
s for attaching a readout resonator claw to a qubit and coupling section to a feedline.
qubit
: The "palm" of the claw on the outside edge of the "shield". Matches(eq::ExampleRectangleTransmon) => :rres
.feedline
: A distancecoupling_gap
from the edge of the ground plane, vertically aligned with the claw.
hooks(sch::Schematic, node::ComponentNode)
The hooks belonging to the component in node
in the global coordinate system of sch
.
hooks(comp::AbstractComponent)
A component's Hook
s (a set of locations and rules for attaching to other components).
Returns a NamedTuple
of Hook
s and/or arrays of Hook
s for a AbstractComponent instance. To access a Hook
directly whether or not it's in an array, use hooks(::AbstractComponent, ::Symbol)
.
hooks(comp::AbstractComponent, h::Symbol)
A component's Hook
identified by h
.
Preferred way to retrieve a hook over accessing the NamedTuple
directly (hooks(comp).h
). Allows access to hooks in arrays by interpreting :hookname_i
as hooks(comp).hookname[i]
if :hookname_i
is not itself a key in hooks(comp)
.
hooks(cc::AbstractCompositeComponent)
Hook
s for the composite geometry, placed at corresponding hooks of the subcomponents.
Hooks
A hook hcc
is returned for each hook (name h
) of every subcomponent node (index i
). If keys(map_hooks(cc))
contains i => h
, then the corresponding composite hook is map_hooks(cc)[h]
. Otherwise, it is _$(i)_$h
.
hooks(cc::AbstractCompositeComponent, subcompname::String, h::Symbol)
Attempts to retrieve the composite hook corresponding to hook h
of a Component
with name subcompname
(either its unique name or its name parameters). Will emit an error if the name is ambiguous.
hooks(cc::AbstractCompositeComponent, i::Int, h::Symbol)
hooks(cc::AbstractCompositeComponent, (i=>h)::Pair{Int, Symbol})
The composite hook corresponding to hook h
of components(cc)[i]
.
hooks(::ExampleRectangleTransmon)
Hook
s for attaching control lines and readout to the transmon.
readout
: The center edge of the ground plane on the opposite side of the island from the SQUID.xy
: The left side of the capacitor gap.z
: The center edge of the ground plane in the SQUID loop.
DeviceLayout.SchematicDrivenLayout.default_parameters
— Functiondefault_parameters(::Type{T}) where T <: AbstractComponent
default_parameters(::T) where T <: AbstractComponent
A NamedTuple
of default parameters for component type T
.
DeviceLayout.halo
— Methodhalo(c::AbstractComponent, delta, inner_delta=nothing; only_layers=[], ignore_layers=[])
A component's halo, intended for use as an exclusion zone parameterized by a bias delta
.
By default, this applies a delta
halo to all geometry
elements whose metadata matches the inclusion/exclusion requirements. For example, polygons are offset by delta
(enlarged by growing delta
away from each original edge). Any entities in layers in ignore_layers
will be skipped. If only_layers
is not empty, only those layers will be used to generate the halo. Layers for inclusion and exclusion can be provided as layer name Symbol
s, in which case only the layer name needs to be matched, or as full DeviceLayout.Meta
objects, in which case all metadata fields (e.g., index and level for SemanticMeta
) must match.
An inner_delta
may be specified to subtract the halo at that bias from the result.
AbstractComponent
s may define their own halo
methods.
DeviceLayout.name
— Methodname(comp::AbstractComponent)
The component's name.
DeviceLayout.SchematicDrivenLayout.non_default_parameters
— Functionnon_default_parameters(c::AbstractComponent)
A NamedTuple
of the parameters of c
that were set to values other than their defaults.
DeviceLayout.parameters
— Functionparameters(comp::AbstractComponent)
The component's NamedTuple
of parameters.
parameters(cc::BasicCompositeComponent)
Retrieve the parameters set indirectly by cc
's internal SchematicGraph
:
name
:g.name
, from which the component's unique name is generatedsub_parameters
: A tuple ofNamedTuple
s containing the subcomponent parameters in order
DeviceLayout.SchematicDrivenLayout.parameter_names
— Functionparameter_names(::Type{T}) where T <: AbstractComponent
parameter_names(::T) where T <: AbstractComponent
Parameter name Symbol
s for component type T
.
Components can be created by several methods. In addition to the keyword constructor, the create_component
and set_parameters
method allow the use of a set of base parameters or a prototype component to effectively provide a new set of defaults. The @component
macro can also be used with either a component type or an instance.
DeviceLayout.SchematicDrivenLayout.@component
— Macro@component comp = MyComponent param1=val1 param2=val2 ...
@component comp = MyComponent begin
param1 = val1
param2 = val2
...
end
@component comp[1:10] = MyComponent begin
param1 .= vals1_vec
param2 = val2
...
end
@component comp[1:10, 1:10] = MyComponent begin
param1 .= vals1_arr
param2 = val2
...
end
Create a Component
or vector of components with specified name and parameters.
For a single component, the symbol on the left-hand side is passed as the name
of the component. Parameters can be provided like keyword arguments on the same line or in a block (multiple lines enclosed by begin
and end
).
If the left-hand side is written as comp[1:n]
, then comp
will be an array of n
components with names comp1
, comp2
, ..., comp$n
. A parameter can be passed to all instances using the same syntax as for a single component, or each component can be passed its parameter out of a vector of parameters values vals_vec
by using broadcast assignment (param .= vals_vec
).
Similarly, multidimensional arrays of components can be created using @component comp[1:m, 1:n]
.
A component instance can also be used in place of the component type, in which case the "default" values for unspecified parameters will be those of that component.
DeviceLayout.SchematicDrivenLayout.create_component
— Functioncreate_component(
::Type{T},
name::String=default_parameters(T).name,
base_parameters::NamedTuple=default_parameters(T);
kwargs...
) where {T <: AbstractComponent}
Create an instance of type T
with name name
and parameters derived by merging kwargs
into base_parameters
.
The parameter merge is recursive, meaning that a NamedTuple
keyword argument will be merged into the corresponding NamedTuple
base parameter. This can be convenient because not every "subparameter" within that NamedTuple
needs to be specified. This is in contrast to the default component keyword constructor, which does not merge recursively.
DeviceLayout.SchematicDrivenLayout.set_parameters
— Functionset_parameters(
c::AbstractComponent,
name::String=name(c),
params::NamedTuple=parameters(c);
kwargs...
)
Create an instance of type typeof(c)
with name name
and parameters derived by merging kwargs
into `params.
The parameter merge is recursive, meaning that a NamedTuple
keyword argument will be merged into the corresponding NamedTuple
base parameter. This can be convenient because not every "subparameter" within that NamedTuple
needs to be specified. This is in contrast to the default component keyword constructor, which does not merge recursively.
This can also be written by calling the component instance c
like a function: c(name, params; kwargs...)
.
Built-in components
We provide some other predefined components that may be generally useful.
DeviceLayout.SchematicDrivenLayout.ArrowAnnotation
— Typestruct ArrowAnnotation{T} <: AbstractComponent{T}
name::String = "arrow"
length = 0.032mm
width = 0.005mm
text::String = ""
textsize = 0.025mm
meta = SemanticMeta(:annotation)
end
An arrow with a given length and width along with a text annotation in the layer given by meta
.
Hooks
nock
: The base of the arrow, with inward direction along the arrowtip
: The tip of the arrow, with inward direction opposite the arrow
DeviceLayout.SchematicDrivenLayout.BasicComponent
— Typestruct BasicComponent{T} <: AbstractComponent{T}
BasicComponent(cs::CoordinateSystem{T}, hooks=(;))
A simple AbstractComponent
that acts as a lightweight wrapper for a CoordinateSystem
.
The component geometry
is a fixed CoordinateSystem
, provided to the constructor along with hooks
.
DeviceLayout.SchematicDrivenLayout.GDSComponent
— Typestruct GDSComponent{T} <: AbstractComponent{T}
name::String
cell::Cell{T}
hooks::NamedTuple
meta::DeviceLayout.Meta
end
A component with geometry corresponding to an explicit Cell
.
The meta
field does not affect metadata inside the Cell
, but can still be used by a LayoutTarget
to decide whether the component should be rendered or not.
Hooks are supplied by the user, with a default of compass()
.
DeviceLayout.SchematicDrivenLayout.Spacer
— TypeSpacer{T} <: AbstractComponent{T}
Provides an 8-point compass of hooks at each of two points separated by p1
.
Parameters
p1
: The endpoint of the spacer
Hooks
p0_east
: Hook at the origin, within_direction
pointing "east" (positive x direction)p0_northeast
p0_north
- ...
p0_southeast
p1_east
: Hook atp1
, within_direction
pointing "east" (positive x direction)p1_northeast
- ...
p1_southeast
DeviceLayout.SchematicDrivenLayout.WeatherVane
— TypeWeatherVane{T} <: AbstractComponent{T}
A component with empty geometry and an 8-point compass of hooks at the origin.
Paths as components
Another component already showed up in geometry-level layout: DeviceLayout.Path
is an AbstractComponent
with hooks p0
and p1
corresponding to its start and end. Path
supports the interface described above and can be added directly to a schematic just like any other component.
DeviceLayout.hooks
— Methodhooks(pa::Path)
:p0
: AHandedPointHook
looking into the start of pathpa
.:p1
: AHandedPointHook
looking into the end of pathpa
.:p0_lh
: A left-handedHandedPointHook
looking into the start of pathpa
.:p1_lh
: A left-handedHandedPointHook
looking into the end of pathpa
.
Recall that Path
supports a geometry-level attach!
method that can place references to other structures along it. It also supports a schematic-level attach!
with a similar syntax, which positions components along the path by defining an edge in the schematic graph.
Composite components
A "composite" component is one that's at least partly made up of other components. Nothing stops you from "baking in" subcomponents in your component's geometry
function by deriving parameters and getting geometries for those subcomponents yourself. You could even construct a SchematicGraph
then plan
, check!
, and build!
it to get a coordinate system of subcomponents fused together. But in that case, your top-level schematic won't know anything about what's inside your composite component.
As an alternative, we define abstract type AbstractCompositeComponent <: AbstractComponent
, which is minimally a Component
with a graph(cc::MyCompositeComponent)
method that produces a SchematicGraph
of the subcomponents it's made from, such that the composite component's geometry is just build!(plan(graph(cc)))
.
SchematicDrivenLayout provides a concrete composite component: BasicCompositeComponent
, which is really just a graph defined by the user and wrapped in a Component
. It maps parameters and hook names exposed by the composite component by prefixing their names with _i_
, where i
is the index of the subcomponent node in graph(cc)
.
A new subtype of CompositeComponent
can be defined to use a custom reparameterization and mapping of hook names in the subgraph.
DeviceLayout.SchematicDrivenLayout.AbstractCompositeComponent
— Typeabstract type AbstractCompositeComponent{T} <: AbstractComponent{T}
A Component
with geometry derived from that of a SchematicGraph
of subcomponents.
The alias CompositeComponent = AbstractCompositeComponent{typeof(1.0nm)}
is provided for convenience.
A standard Component
c
is described by its geometry(c)
, parameters(c)
, and hooks(c)
. In contrast, a CompositeComponent
cc
also has parameters
, but is otherwise described at the level of graph(cc)
and map_hooks(cc)
, which define a relationship between the CompositeComponent
and the geometry and hooks of its subcomponents.
If a SchematicGraph
g
contains a node with a CompositeComponent
, then the subgraph graph(cc)
will be accessible to inspection tools for g
. For example, find_components
can return nodes in the subgraph. You can also flatten!(g)
to simply replace cc
's node with graph(cc)
.
The list of subcomponents in the graph can be obtained with components(cc)
(equivalent to components(graph(cc))
).
In order to take advantage of this interface, the implementation of a CompositeComponent
should contain the following fields, in order:
parameters::NamedTuple
: A collection of parameters
A CompositeComponent
must also implement the following specializations:
_build_subcomponents
: Returns aTuple
of subcomponents_graph!(g::SchematicGraph, cc::MyComponent, subcomps::NamedTuple)
: Populates and connects the schematic graph corresponding tocc
, wheresubcomps
contains the results of_build_subcomponents
keyed by namemap_hooks(::Type{MyComponent})
: ADict{Pair{Int, Symbol}, Symbol
mapping subcomponent hooks to hooks presented by the composite component.
Validation or reconciling of parameters should be done in an inner constructor that replaces the default inner constructor.
If you define your own abstract composite component subtype you should define SchematicDrivenLayout.iscomposite(::Val{:MyAbstractCompositeComponent}) = true
to allow the @compdef
macro to recognize that the component is composite.
DeviceLayout.SchematicDrivenLayout.CompositeComponent
— Typeconst CompositeComponent = AbstractCompositeComponent{typeof(1.0UPREFERRED)}
CompositeComponent
is an alias for AbstractCompositeComponent
with the coordinate type typeof(1.0UPREFERRED)
.
DeviceLayout.UPREFERRED
is a constant set according to the unit
preference in Project.toml
or LocalPreferences.toml
. The default ("PreferNanometers"
) gives const UPREFERRED = DeviceLayout.nm
, with mixed-unit operations preferring conversion to nm
.
DeviceLayout.SchematicDrivenLayout.BasicCompositeComponent
— Typestruct BasicCompositeComponent{T} <: AbstractCompositeComponent{T}
A simple AbstractCompositeComponent
that acts as a lightweight wrapper for a SchematicGraph
.
The component scc = BasicCompositeComponent(g::SchematicGraph)
copies g
and generates its geometry as build!(check!(plan(g)))
.
hooks(scc)
returns a NamedTuple
with the hook h
of the i
th subcomponent as _i_h
.
Parameters are set indirectly by the internal SchematicGraph
:
name
:g.name
, from which the component's unique name is generatedsub_parameters
: A tuple ofNamedTuple
s containing the subcomponent parameters in order
A BasicCompositeComponent
instance can also be used as a constructor, taking the argument param_sets
(a tuple of parameters for each subcomponent, in order), along with keyword arguments _i_param
for a parameter named param
in subcomponent i
. Default values are provided by the components in g
.
(cc::BasicCompositeComponent)(
param_sets::Tuple = ();
kwargs...)
Create a version of cc
with different subcomponent parameters.
Argument param_sets
is a tuple of NamedTuple
s containing each subcomponent's parameters. If it is not empty, it must have a NamedTuple
s for each subcomponent, even an empty one.
Keyword arguments are _i_param
for a parameter named param
in subcomponent i
. Default values are provided by the components in g
.
DeviceLayout.SchematicDrivenLayout.components
— Methodcomponents(cc::AbstractCompositeComponent)
A list of the components in the subgraph of cc
. Equivalent to components(graph(cc))
Unlike subcomponents
, components
will return a component for every node, even if multiple nodes use the same component.
DeviceLayout.flatten
— Methodflatten(g::SchematicGraph; depth=-1)
Create a copy of g
with all AbstractCompositeComponent
s replaced by their graphs.
For non-composite components, the identical ComponentNode
s will be preserved.
Keywords
depth
: How many times to iteratively flatten top-levelAbstractCompositeComponent
s. If negative, will repeat until noAbstractCompositeComponent
s remain.
DeviceLayout.SchematicDrivenLayout.graph
— Functiongraph(cc::AbstractCompositeComponent)
The SchematicGraph
represented by cc
.
DeviceLayout.SchematicDrivenLayout.map_hooks
— Functionmap_hooks(cc::AbstractCompositeComponent)
map_hooks(cc::Type{<:AbstractCompositeComponent})
A Dict{Pair{Int, Symbol}, Symbol}
mapping subcomponent hooks to composite hooks.
For example, the entry (2 => :readout) => :readout_2
means the readout
hook for the subcomponent in graph(cc)
's node 2 will be available as hooks(cc).readout_2
in the composite geometry.
Subcomponent Hook
s that are not mapped will still be available as :_$(i)_$h
, where i
is the subcomponent's node index in graph(cc)
and h
is the hook name. In other words, the above example would have the fallback default :_2_readout
.
Modifying components
Sometimes we want to use components in ways the component designer didn't predict. For example, we might want to draw a Qubit
component on the top chip in a flipchip assembly, but the metadata in the generated geometry corresponds to the bottom chip.
There are a couple ways to do this concisely. One is to take a Qubit
and run map_metadata!
on it:
q = Qubit()
map_metadata!(q, facing)
This applies facing
to each piece of metadata in geometry(q)
. Any function of metadata that returns metadata can be used as the second argument, so if you only wanted to flip the :jj
layer, you could write map_metadata!(q, m -> layer(m) == :jj ? facing(m) : m)
.
Note that geometry(q)
is associated only with q
, and changes to it are not tracked elsewhere. If, in a later step, you were to apply junction width corrections by replacing q
with q2 = typeof(q)(; parameters(q)..., jj_width=new_width)
, then q2
would not be on the flip chip.
For larger changes, we can make a new Component
type. Rather than copy the Qubit
definition and find-and-replace its metadata, we can use the @variant
macro.
@variant FlipchipQubit Qubit map_meta = facing
fq = FlipchipQubit()
all(level.(element_metadata(geometry(fq))) .== 2)
This creates a new type, FlipchipQubit
, and automatically defines the required methods for Component
implementation using the Qubit
implementation. It has the same default_parameters
, hooks
, and almost the same _geometry!
. Since we supplied the optional keyword map_meta
, the geometry method for the new type creates a Qubit
and then applies facing
to each element (and referenced structure, recursively), as in the map_metadata!
example above. But unlike with using map_metadata!
on a single Qubit
instance, you can replace fq
with another typeof(fq)
without losing the level information.
You can also go further with manual modifications. You could add a cutout on the bottom chip, along with a parameter to control the size of the cutout:
@variant CutoutFlipchipQubit Qubit map_meta = facing new_defaults = (; cutout_margin=20μm)
function SchematicDrivenLayout._geometry!(cs::CoordinateSystem, fq::CutoutFlipchipQubit)
_geometry!(cs, base_variant(fq)) # base_variant gives you the equivalent `Qubit`
map_metadata!(cs, facing)
# The above lines are what we would get from @variant Qubit map_meta=facing
# Below, we add a cutout on the bottom chip
return place!(cs, offset(bounds(cs), parameters(fq).cutout_margin)[1], BASE_NEGATIVE)
end
Note that if Qubit
were a CompositeComponent
, we would have to use the macro @composite_variant
instead.
DeviceLayout.SchematicDrivenLayout.base_variant
— Functionbase_variant(comp::AbstractComponent)
If comp
is a @variant
of some other component type T <: AbstractComponent
, return an instance of T
with the same name as comp
; otherwise, return comp
.
When the base component shares a parameter with comp
, then in the returned component, that parameter will take the value it has in comp
.
base_variant(comptype::Type{<:AbstractComponent})
If comptype
is a @variant
of some other component type T <: AbstractComponent
, return T
; otherwise, return comptype
.
DeviceLayout.SchematicDrivenLayout.flipchip!
— Functionflipchip!(geom::GeometryStructure)
Map all metadata in geom
to facing
copies. Recursive on referenced structures.
DeviceLayout.SchematicDrivenLayout.@variant
— Macro@variant NewType BaseType new_defaults=(;) map_meta=nothing
Create NewType <: AbstractComponent
based on BaseType
, with optional new_defaults
and map_meta
.
Default parameters for the new type will be new_defaults
merged into default_parameters(T)
. You can override the original defaults or add entirely new parameters this way.
If provided, map_meta
should be a function of DeviceLayout.Meta
that returns another DeviceLayout.Meta
. It will be applied recursively to the geometry of the base component using map_metadata!
.
Individual methods like hooks
and _geometry!
can then be overridden to create variant behavior.
DeviceLayout.SchematicDrivenLayout.@composite_variant
— Macro@composite_variant NewType BaseType new_defaults=(;) map_meta=nothing
Create NewType <: AbstractCompositeComponent
based on BaseType
, with optional new_defaults
and map_meta
.
Default parameters for the new type will be new_defaults
merged into default_parameters(T)
.
If provided, map_meta
should be a function of DeviceLayout.Meta
that returns another DeviceLayout.Meta
. It will be applied recursively to the geometry of the base component using map_metadata!
.
Individual methods like hooks
and _geometry!
can then be overridden to create variant behavior.