Schematic-Driven Design API Reference
Components
DeviceLayout.AbstractComponent — Type
abstract 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.0UPREFERRED)} 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 (Hooks) at the end of each lead.
AbstractComponents 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): ANamedTupleofHooks andHookarrays 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 ofSymbols.- A keyword constructor that merges keyword arguments into
default_parametersnon-recursively. (That is, aNamedTuplekeyword argument will overwrite the default parameter entirely, meaning every "subparameter" in theNamedTupleneeds to be specified.) create_component(::Type{MyComponent}, name::String=default_parameters(MyComponent).name, base_parameters::NamedTuple=default_parameters(MyComponent); kwargs...): Constructor that recursively mergeskwargsintobase_parameters. (That is, aNamedTuplekeyword argument will be merged into the correspondingNamedTuplebase 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_componentusingmycompfor 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 their 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 still often helpful for designers to explicitly give important components unique names, guaranteeing that the corresponding identifiers are the same.
Implementing subtypes
Components must have a name field. Defining components with @compdef is recommended, since it creates a name field if not specified, allows specification of default parameters, creates a field to store the geometry after it is first calculated, and defines a default_parameters method.
Non-composite components must implement the following specializations:
_geometry!(cs::CoordinateSystem, comp::MyComponent): Add the geometry to cshooks(comp::MyComponent): Return aNamedTupleofHooks
For composite components (those with subcomponents), see AbstractCompositeComponent.
DeviceLayout.SchematicDrivenLayout.Component — Type
const 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.
DeviceLayout.SchematicDrivenLayout.@compdef — Macro
@compdef typedefThis 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, _schematic, and _hooks fields for composite components. If there is no name field, one will automatically be created with a default name of the type name (as a string).
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)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
...
endCreate 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.allowed_rotation_angles — Function
allowed_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.
DeviceLayout.SchematicDrivenLayout.check_rotation — Function
check_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.create_component — Function
create_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.matching_hooks — Function
matching_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 — Function
matching_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).
DeviceLayout.SchematicDrivenLayout.geometry — Function
geometry(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 plan(graph(cc)).coordinate_system.
DeviceLayout.hooks — Function
hooks(pa::Path):p0: AHandedPointHooklooking into the start of pathpa.:p1: AHandedPointHooklooking into the end of pathpa.:p0_lh: A left-handedHandedPointHooklooking into the start of pathpa.:p1_lh: A left-handedHandedPointHooklooking into the end of pathpa.
hooks(rres::ExampleClawedMeanderReadout)Hooks 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_gapfrom 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 Hooks (a set of locations and rules for attaching to other components).
Returns a NamedTuple of Hooks and/or arrays of Hooks 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)Hooks 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].
DeviceLayout.SchematicDrivenLayout.default_parameters — Function
default_parameters(::Type{T}) where T <: AbstractComponent
default_parameters(::T) where T <: AbstractComponentA NamedTuple of default parameters for component type T.
DeviceLayout.halo — Method
halo(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 Symbols, 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.
AbstractComponents may define their own halo methods.
DeviceLayout.name — Method
name(comp::AbstractComponent)The component's name.
DeviceLayout.SchematicDrivenLayout.non_default_parameters — Function
non_default_parameters(c::AbstractComponent)A NamedTuple of the parameters of c that were set to values other than their defaults.
DeviceLayout.parameters — Function
parameters(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 ofNamedTuples containing the subcomponent parameters in order
DeviceLayout.SchematicDrivenLayout.parameter_names — Function
parameter_names(::Type{T}) where T <: AbstractComponent
parameter_names(::T) where T <: AbstractComponentParameter name Symbols for component type T.
DeviceLayout.SchematicDrivenLayout.set_parameters — Function
set_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...).
DeviceLayout.SchematicDrivenLayout.base_variant — Function
base_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! — Function
flipchip!(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=nothingCreate 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=nothingCreate 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.
Built-in Components
DeviceLayout.SchematicDrivenLayout.ArrowAnnotation — Type
struct ArrowAnnotation{T} <: AbstractComponent{T}
name::String = "arrow"
length = 0.032mm
width = 0.005mm
text::String = ""
textsize = 0.025mm
meta = SemanticMeta(:annotation)
endAn 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 — Type
struct 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 — Type
struct GDSComponent{T} <: AbstractComponent{T}
GDSComponent(cell:Cell, hooks=compass(), parameters=(;))
GDSComponent([name::String=uniquename(cellname),]
filename::String,
cellname::String,
hooks=compass(),
parameters=(;))A component with geometry corresponding to an explicit Cell.
The Cell can be provided to the constructor directly, or as the path to a .gds file together with the name of a top-level cell in that file.
Hooks are supplied by the user, with a default of compass().
Users can specify their own NamedTuple of parameters. These parameters have no effect on geometry or hooks.
DeviceLayout.SchematicDrivenLayout.Spacer — Type
Spacer{T} <: AbstractComponent{T}A component with empty geometry and an 8-point compass of hooks at each of two points separated by p1.
Parameters
name: The name of the spacerp1: The endpoint of the spacer
Hooks
p0_east: Hook at the origin, within_directionpointing "east" (positive x direction)p0_northeastp0_north- ...
p0_southeastp1_east: Hook atp1, within_directionpointing "east" (positive x direction)p1_northeast- ...
p1_southeast
DeviceLayout.SchematicDrivenLayout.WeatherVane — Type
WeatherVane{T} <: AbstractComponent{T}A component with empty geometry and an 8-point compass of hooks at the origin.
Composite Components
DeviceLayout.SchematicDrivenLayout.AbstractCompositeComponent — Type
abstract type AbstractCompositeComponent{T} <: AbstractComponent{T}An AbstractComponent with geometry derived from that of a SchematicGraph of subcomponents.
The alias CompositeComponent = AbstractCompositeComponent{typeof(1.0UPREFERRED)} 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))).
Implementing subtypes
Components must have a name field. Defining components with @compdef is recommended, since it creates a name field if not specified, allows specification of default parameters, creates fields for storing the schematic, graph, and hooks after they are first calculated, and defines a default_parameters method.
A CompositeComponent must implement the following specializations:
_build_subcomponents: Returns aTupleof subcomponents_graph!(g::SchematicGraph, cc::MyComponent, subcomps::NamedTuple): Populates and connects the schematic graph corresponding tocc, wheresubcompscontains the results of_build_subcomponentskeyed by namemap_hooks(::Type{MyComponent}): ADict{Pair{Int, Symbol}, Symbolmapping subcomponent hooks to hooks presented by the composite component.
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 — Type
const 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 — Type
struct 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 ith 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 ofNamedTuples 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 NamedTuples containing each subcomponent's parameters. If it is not empty, it must have a NamedTuples 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 — Method
components(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.SchematicDrivenLayout.filter_parameters — Function
filter_parameters(subcomp::AbstractComponent, comp::AbstractComponent; prefix=name(subcomp) * "_", except=Symbol[])
filter_parameters(subcomp::Type{<:AbstractComponent}, comp::AbstractComponent; prefix="", except=Symbol[])Return the parameters of comp that match prefix * param where param is the name of a parameter of subcomp, as a collection of param => value pairs.
Ignores any parameters of comp whose name matches a Symbol in except. Also ignores the name parameter if prefix is empty.
Intended as a utility for passing parameters down to subcomponents.
subcomp can be an AbstractComponent instance or subtype. If it is an instance, the prefix defaults to the subcomponent name followed by an underscore. If it is a type, the default prefix is empty.
@compdef struct MyCompositeComponent <: CompositeComponent
templates = (;
subcomp1=MySubComponent(; name="subcomp1"),
subcomp2=MySubComponent(; name="subcomp2")
)
subcomp1_width = 2mm
length = 2mm
end
@compdef struct MySubComponent <: Component
width = 1mm
length = 1mm
end
function SchematicDrivenLayout._build_subcomponents(cc::MyCompositeComponent)
# Matching with no prefix: (; length=...)
shared_params = filter_parameters(MySubComponent, cc)
# Matching with prefix: (; width=...)
subcomp1_overrides = filter_parameters(cc.templates.subcomp1, cc)
@component subcomp1 = cc.templates.subcomp1(; subcomp1_overrides..., shared_params...)
@component subcomp2 = cc.templates.subcomp2(; shared_params...)
return (subcomp1, subcomp2)
endDeviceLayout.flatten — Method
flatten(g::SchematicGraph; depth=-1)Create a copy of g with all AbstractCompositeComponents replaced by their graphs.
For non-composite components, the identical ComponentNodes will be preserved.
Keywords
depth: How many times to iteratively flatten top-levelAbstractCompositeComponents. If negative, will repeat until noAbstractCompositeComponents remain.
DeviceLayout.SchematicDrivenLayout.graph — Function
graph(cc::AbstractCompositeComponent)The SchematicGraph represented by cc.
DeviceLayout.SchematicDrivenLayout.map_hooks — Function
map_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 Hooks 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.
Hooks
DeviceLayout.Hook — Type
abstract type Hook{T} <: AbstractGeometry{T}Contains information describing how one component can attach to others.
DeviceLayout.PointHook — Type
PointHook(p::Point, in_direction)
PointHook(x, y, in_direction)Hook defined by a point and a direction (an angle CCW from the positive x axis).
Attaching two PointHooks will match the points, oriented so the angles are opposite. By convention, hooks point inward.
DeviceLayout.HandedPointHook — Type
HandedPointHook{T} <: Hook{T}
h::PointHook{T}
right_handed::BoolA PointHook augmented with handedness.
In addition to translation and rotation, which are used to fuse one PointHook to another, a HandedPointHook being fused to another HandedPointHook will apply a reflection if necessary to match its handedness.
DeviceLayout.hooks — Method
hooks(pa::Path):p0: AHandedPointHooklooking into the start of pathpa.:p1: AHandedPointHooklooking into the end of pathpa.:p0_lh: A left-handedHandedPointHooklooking into the start of pathpa.:p1_lh: A left-handedHandedPointHooklooking into the end of pathpa.
DeviceLayout.Paths.p0_hook — Function
p0_hook(pa::Path) = PointHook(p0(pa), α0(pa))A PointHook looking into the start of path pa.
DeviceLayout.Paths.p1_hook — Function
p1_hook(pa::Path) = PointHook(p1(pa), α1(pa) + π)A PointHook looking into the end of path pa.
DeviceLayout.in_direction — Function
in_direction(h::Hook)The inward-pointing direction stored by the PointHook (angle CCW from the positive x axis)
DeviceLayout.out_direction — Function
out_direction(h::PointHook)The outward-pointing angle opposite to the direction stored by the PointHook
DeviceLayout.Paths.path_in — Function
path_in(h::PointHook)A Path starting at h, pointing along its inward direction.
DeviceLayout.Paths.path_out — Function
path_out(h::PointHook)A Path starting at h, pointing along its outward direction.
DeviceLayout.transformation — Method
transformation(h1::Hook, h2::Hook)Return a CoordinateTransformation to align h2 to h1.
Given hooks h1, h2 relative to CoordinateSystems h1cs, h2cs respectively, if you reference h2cs inside h1cs with push!(h1cs.refs, sref(h2cs, origin, rot=rotation, xrefl=xrefl)), then relative to h1cs, h2 will lie on top of h1 with its in_direction pointing opposite to that of h1 (and matching handedness, if applicable).
DeviceLayout.compass — Function
compass(;p0=Point(0μm, 0μm))An 8-point compass of PointHooks at p0.
The NamedTuple (:east = PointHook(p0, 0°), :northeast = PointHook(p0, 45°), ... for all cardinal and primary intercardinal directions (every 45°). (The in_direction of each hook points in its compass direction.)
Autofill
DeviceLayout.Autofill.autofill! — Function
autofill!(cs::AbstractCoordinateSystem,
filler_cs::AbstractCoordinateSystem,
grid_x::AbstractArray,
grid_y::AbstractArray,
exclusion)Add references to filler_cs inside cs at grid points not in any exclusion polygon.
The exclusion argument may be
- a
Coordinatedenoting an offset used to generate the excluded region from shapes incs - a
CoordinateSystemorCellcontaining the geometry of the excluded region - a
Functioncreating aCoordinateSystemorCellfromcs - an
AbstractArray{<:AbstractPolygon}
Returns the origins of references.
DeviceLayout.halo — Function
halo(pa::Path, outer_delta, inner_delta; only_layers=[], ignore_layers=[])Return a Path forming the halo of pa, equivalent to offsetting the geometry by outer_delta, then subtracting the offset by inner_delta if it is not nothing.
Extends the start and/or end by delta (for each inner/outer offset) when pa has nonzero extent at the endpoints. Each segment's style is the halo style of the original segment.
For the segments of the Path and any attached references, any entities in layers in ignore_layers will be skipped. Note that even if the segments are ignored, attachments may not be. 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 Symbols, 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.
Returns a Path.
halo(sty::Paths.Style, outer_delta, inner_delta=nothing; kwargs...)Return a Trace or CPW style covering the extent of sty from an offset of inner_delta to outer_delta.
halo(ent::GeometryEntity{T}, outer_delta, inner_delta=nothing)Return the "halo" of ent using an offset of outer_delta.
By default, the halo is a vector containing offset(footprint(ent), outer_delta), but it may contain any number of GeometryEntitys that together fully cover ent with a margin of outer_delta. It is not guaranteed to cover footprint(ent).
If inner_delta is provided, then the offset at inner_delta is subtracted from the result.
A GeometryEntity should implement a specialized halo if there is an efficient non-Polygon representation of the halo. If it does not, then by default offset will be used, which first resolves the entity into Polygons using to_polygons.
halo(cs::CoordinateSystem, outer_delta, inner_delta=nothing; only_layers=[],
ignore_layers=[])
halo(cs::Cell, outer_delta, inner_delta=nothing; only_layers=[], ignore_layers=[])A coordinate system of type typeof(cs) with halos for all entities in cs, tracing through cs.refs.
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 Symbols, 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.
The orientations of polygons must be consistent, such that outer polygons share the same orientation, and any holes have the opposite orientation. Additionally, any holes should be contained within outer polygons; offsetting hole edges may create positive artifacts at corners.
halo(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 Symbols, 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.
AbstractComponents may define their own halo methods.
DeviceLayout.Autofill.make_halo — Function
make_halo(delta, inner_delta=nothing; only_layers=[], ignore_layers=[])Returns a function (c)->halo(c, delta, inner_delta; only_layers, ignore_layers) for generating GeometryStructure halos.
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 Symbols, 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.
Schematics
Schematic Graph
DeviceLayout.SchematicDrivenLayout.SchematicGraph — Type
SchematicGraph <: AbstractMetaGraph{Int}Graph describing the components and connectivity of a device schematic.
DeviceLayout.SchematicDrivenLayout.ComponentNode — Type
mutable struct ComponentNode
id::String
component::AbstractComponent
endRepresents an instance of a component in the context of a schematic graph.
DeviceLayout.SchematicDrivenLayout.add_node! — Function
add_node!(g::SchematicGraph, comp::AbstractComponent; base_id=name(comp), kwargs...)Create and return a new node for comp.
The base_id will be used as the node's id if it is not already in use by the Schematic. If it is, then a uniquename will be generated by appending _n, where n is the number of occurrences of base_id so far (including this one).
Additional keyword arguments will become vertex properties.
DeviceLayout.SchematicDrivenLayout.fuse! — Function
fuse!(g::SchematicGraph,
nodehook1::Pair{Int, Symbol},
nodehook2::Pair{Int, Symbol}; kwargs...)
fuse!(g::SchematicGraph,
nodehook1::Pair{ComponentNode, <:Union{Symbol, Hook}},
nodehook2::Pair{<:Union{ComponentNode, AbstractComponent}, <:Union{Symbol, Hook}}; kwargs...)Usage:
fuse!(g, node1=>:hook1, node2=>:hook2)fuse!(g, idx1=>:hook1, idx2=>:hook2)fuse!(g, node1=>:hook1, comp2=>:hook2)fuse!(g, node1=>:hook1, comp2=>:hook2; PLAN_SKIPS_EDGE=>true)
Add an edge (node1, node2) connecting their hooks (:hook1, :hook2) to g.
Returns the second ComponentNode.
If fuse! is passed a bare component comp2, it makes a new node, even if that component is referenced in another node.
If no hook or only one hook is specified, fuse! will try to determine the correct hook using matching_hooks or matching_hook.
You can make an attachment anywhere, not just to an existing named hook, by providing a Hook object with the desired point and orientation rather than a Symbol (example: fuse!(g, node1=>PointHook(1mm, 1mm, 90°), comp2=>:hook2)). This option is provided for convenience in situations that call for ad-hoc or case-by-case relative positioning; one example use case might be placing labels near components. On the other hand, if you find yourself needing a consistent hook that doesn't already exist for MyComponent, then it's generally better to update the component definition so that the hook is available through hooks(::MyComponent). Alternatively, if you want to use an existing hook with an additional offset, consider using the Spacer component.
Cycles: Sometimes we need to avoid adding edges in the graph to avoid cycles that'd force the plan function to throw an error. Solution: Pass a keyword argument plan_skips_edge=true. This allows us to encode all the edges in the graph, while informing the plan function that the edge should be skipped for rendering purposes.
DeviceLayout.Paths.route! — Method
route!(g::SchematicGraph, rule::RouteRule,
nodehook1::Pair{ComponentNode,Symbol}, nodehook2::Pair{ComponentNode,Symbol},
sty, meta;
waypoints=[], waydirs=[], global_waypoints=false,
name=uniquename("r_$(component(nodehook1.first).name)_$(component(nodehook2.first).name)"),
kwargs...)
route!(g::SchematicGraph, rule::RouteRule, node1::ComponentNode, nodehook2::Pair{ComponentNode,Symbol}, sty, meta; kwargs...)
route!(g::SchematicGraph, rule::RouteRule, nodehook1::Pair{ComponentNode,Symbol}, node2::ComponentNode, sty, meta; kwargs...)
route!(g::SchematicGraph, rule::RouteRule, node1::ComponentNode, node2::ComponentNode, sty, meta; kwargs...)Creates a RouteComponent with given style sty and metadata meta, and fuses it between the specified nodes and hooks in g.
Returns the resulting ComponentNode in g.
Example usage: route!(g, BSplineRouting(), zline_node=>:feedline, z_launcher_node=>:line, Paths.CPW(10μm, 6μm), GDSMeta(1, 2))
If one or both hook symbols are not specified, then matching_hook or matching_hooks will be used to attempt to automatically find the correct hook or hooks.
The route will have start and endpoints at the origin until a method like plan! is called. waypoints and waydirs are in component-local coordinates (unless global_waypoints is true), and rule determines how they will be used.
Additional keyword arguments will become vertex properties for the RouteComponent's node.
name should be unique.
DeviceLayout.SchematicDrivenLayout.RouteComponent — Type
struct RouteComponent{T} <: AbstractComponent{T}
name::String
r::Paths.Route{T}
global_waypoints::Bool
sty::Paths.Style
meta::MetaWraps a Route in a Component type for use with schematics.
name should be unique. If global_waypoints is false, then the waypoints and waydirs are taken to be relative to the component coordinate system. Otherwise, they will be relative to the schematic global coordinate system.
DeviceLayout.Paths.attach! — Method
attach!(g::SchematicGraph,
pathnode::S,
nodehook2::Pair{T, Symbol},
position;
i=lastindex(component(pathnode)),
location=0) where {S <: ComponentNode, T <: ComponentNode}
Usage: attach!(g, pathnode, node2=>:hook2, position; i=segment_idx, location=0)Adds an edge to g between pathnode and node2, attaching :hook2 to a specified point along pathnode.
A new HandedPointHook is generated a distance position along the segment specified by i. If location is +1 or -1, the generated hook is offset to the right or left of the segment by the extent defined by the path's style, with the hook's inward direction pointing back to the segment.
DeviceLayout.SchematicDrivenLayout.plan — Function
plan(g::SchematicGraph, hooks_fn=hooks; strict=:error, log_dir="build", log_level=Logging.Info)
plan(g::SchematicGraph, t::Target; strict=:error, log_dir="build", log_level=Logging.Info)Constructs a Schematic floorplan from g without rendering Components.
Iterates through the nodes in g depth-first to build a tree (or more than one, if the graph is disconnected) of CoordinateSystems satisfying the translations, reflections, and rotations required by the hooks corresponding to each edge. Each CoordinateSystem holds a StructureReference to the Component in the corresponding node. A new Schematic is created containing references to the roots of these trees (sometimes called "rendering trees") as well as a dictionary mapping each ComponentNode in g to the reference to that node's CoordinateSystem in its rendering tree. (This is the reference stored by its parent.)
plan will ignore edges with the property SchematicDrivenLayout.PLAN_SKIPS_EDGE=>true.
The strict keyword should be :error, :warn, or :no.
The strict=:error keyword option causes plan to throw an error if any errors were logged during planning. This is enabled by default, but can be disabled with strict=:no, in which case any node that was not successfully placed relative to an existing node will simply appear at the origin. Using strict=:no is recommended only for debugging purposes.
The strict=:warn keyword option causes plan to throw an error if any warnings were logged during planning. This is disabled by default. Using strict=:warn is suggested for use in automated pipelines, where warnings may require human review.
Log messages with level of at least log_level will be written to joinpath(log_dir, name(g) * ".log"). If logdir is nothing, then no file will be written. The same log file will be used for build! and render! stages of the schematic workflow.
Schematic
DeviceLayout.SchematicDrivenLayout.Schematic — Type
struct Schematic{S} <: AbstractCoordinateSystem{S}Spatial representation of a layout in terms of AbstractComponents rather than polygons.
Can be constructed from a SchematicGraph g using plan(g).
DeviceLayout.bounds — Method
bounds(sch::Schematic, node::ComponentNode)
bounds(sch::Schematic, node_idx::Int)The Rectangle bounding the component in node in the global coordinate system of sch.
DeviceLayout.center — Method
center(sch::Schematic, node::ComponentNode)
center(sch::Schematic, node_idx::Int)The center of the bounds of node's component in the global coordinate system of sch.
DeviceLayout.SchematicDrivenLayout.crossovers! — Function
crossovers!(sch::Schematic, xsty::DeviceLayout.Paths.Intersect.IntersectStyle)Splice crossovers into intersecting Paths and RouteComponents.
RouteComponents will cross over Paths, and otherwise components added to the schematic graph later will cross over those added earlier.
Example
g = SchematicGraph("crossover_example")
# ...
floorplan = plan(g)
xsty = Intersect.AirBridge(
crossing_gap=5μm,
foot_gap=2μm,
foot_length=4μm,
extent_gap=2μm,
scaffold_gap=5μm,
scaffold_meta=GDSMeta(5),
air_bridge_meta=GDSMeta(3)
)
SchematicDrivenLayout.crossovers!(floorplan, xsty)crossovers!(sch::Schematic, xsty::DeviceLayout.Paths.Intersect.IntersectStyle,
nodes_1, nodes_2)Splice crossovers into intersecting Paths and RouteComponents.
Components in nodes_2 will cross over any components in nodes_1, and otherwise components added to the schematic graph later will cross over those added earlier.
DeviceLayout.SchematicDrivenLayout.find_components — Function
find_components(f::Function, g::SchematicGraph; depth=-1)Return the indices of the nodes in sch or g for which f(component(node)) is true.
Keyword depth is the number of layers of the graph to search within, where the subgraph of each CompositeComponent is a layer beyond the layer holding the ComponentNode for that CompositeComponent. To search only top-level components, use depth=1. To search all subgraphs recursively, use a negative depth (the default).
Indices for nodes in subgraphs are integers or Tuples of integer indices, one for each additional layer. For example, if a node containing a CompositeComponent has index 2 in the top layer, then the third subcomponent in the CompositeComponent's graph has index (2,3).
find_components(comptype::Type{T}, sch::Schematic; depth=-1) where {T <: AbstractComponent}
find_components(comptype::Type{T}, g::SchematicGraph; depth=-1) where {T <: AbstractComponent}Return the indices of nodes containing components of type T.
DeviceLayout.SchematicDrivenLayout.find_nodes — Function
find_nodes(f::Function, g::SchematicGraph; depth=-1)Return the indices of the nodes in sch or g for which f(node) is true.
Keyword depth is the number of layers of the graph to search within, where the subgraph of each CompositeComponent is a layer beyond the layer holding the ComponentNode for that CompositeComponent. To search only top-level components, use depth=1. To search all subgraphs recursively, use a negative depth (the default).
Indices for nodes in subgraphs are integers or Tuples of integer indices, one for each additional layer. For example, if a node containing a CompositeComponent has index 2 in the top layer, then the third subcomponent in the CompositeComponent's graph has index (2,3).
See also find_components, which checks f(component(node)).
DeviceLayout.hooks — Method
hooks(sch::Schematic, node::ComponentNode)The hooks belonging to the component in node in the global coordinate system of sch.
DeviceLayout.SchematicDrivenLayout.indexof — Method
indexof(n::ComponentNode, g::SchematicGraph)Finds the index of the node n in g.
DeviceLayout.Transformations.origin — Method
origin(sch::Schematic, node::ComponentNode)
origin(sch::Schematic, node_idx::Int)The origin of node in the global coordinate system of sch.
DeviceLayout.SchematicDrivenLayout.position_dependent_replace! — Function
position_dependent_replace!(sch::Schematic{S}, node_index::Int, replacement::Function;
schematic_origin_globalcoords=zero(Point{S})) where {S}Replaces the component c at node_index in sch with replacement.
The replacement function should have a signature matching replacement(c<:AbstractComponent, c_origin_globalcoords::Point) where c is the component to be replaced and c_origin_globalcoords is the component's origin in global coordinates. Here "global" coordinates are defined as those in which the schematic's origin is the keyword argument schematic_origin_globalcoords in position_dependent_replace!.
replacement should return a new AbstractComponent with a unique name.
position_dependent_replace!(sch::Schematic{S}, comptype::Type{<:AbstractComponent},
replacement::Function;
schematic_origin_globalcoords=zero(Point{S})) where {S}Replaces each component instance of type comptype in sch using position_dependent_replace! with the replacement function replacement(c<:AbstractComponent, c_origin_globalcoords::Point).
DeviceLayout.SchematicDrivenLayout.replace_component! — Function
replace_component!(sch::Schematic, node_index::Int, replacement::Function)Replaces the component c at node_index in sch with replacement(c).
replacement should return a new AbstractComponent with a unique name.
DeviceLayout.SchematicDrivenLayout.rotations_valid — Function
rotations_valid(sch::Schematic)Verifies that all checkable components in sch have the right orientation in the global coordinate system (returns true if successful and throws an error otherwise). A component of type T is checkable if check_rotation(::T) = true.
DeviceLayout.transformation — Method
transformation(sch::Schematic, node::ComponentNode)Given a Schematic sch containing ComponentNode node in its SchematicGraph, this function returns a CoordinateTransformations.Transformation object that lets you translate from the coordinate system of the node to the global coordinate system of sch.
Effectively a wrapper around DeviceLayout.transformation(::CoordinateSystem, ::CoordSysRef).
DeviceLayout.SchematicDrivenLayout.check! — Function
check!(sch::Schematic; rules=[rotations_valid])Verifies that sch satisfies rule(sch) == true for each rule in rules, and sets sch.checked = true if it does.
By default, checks that all checkable components have an allowed global orientation (rotations_valid(::Schematic)).
DeviceLayout.SchematicDrivenLayout.build! — Function
build!(sch::Schematic, geometry_fn=geometry; strict=:error)
build!(sch::Schematic, t::Target; strict=:error)Replace the AbstractComponents in sch with their geometry. Usually there's no reason to do this, since render! will still render the geometry but won't modify sch.
Users must run check!(sch) before calling this method; otherwise, it will throw an error.
The strict keyword should be :error, :warn, or :no.
The strict=:error keyword option causes build! to throw an error if any errors were logged while building component geometries. This is enabled by default, but can be disabled with strict=:no, in which case any component which was not successfully built will have an empty geometry. Using strict=:no is recommended only for debugging purposes.
The strict=:warn keyword option causes build! to throw an error if any warnings were logged. This is disabled by default. Using strict=:warn is suggested for use in automated pipelines, where warnings may require human review.
DeviceLayout.render! — Method
render!(
cs::AbstractCoordinateSystem,
sch::Schematic,
target::LayoutTarget;
strict=:error,
kwargs...
)Render the schematic sch to cs using target's rendering options, without modifying sch.
Users must run check!(sch) before calling this method; otherwise, it will throw an error.
The strict keyword should be :error, :warn, or :no.
The strict=:error keyword option causes render! to throw an error if any errors were logged while building component geometries or while rendering geometries to cs. This is enabled by default, but can be disabled with strict=:no, in which case any component which was not successfully built will have an empty geometry, and any non-fatal rendering errors will be ignored as usual. Using strict=:no is recommended only for debugging purposes.
The strict=:warn keyword option causes render! to throw an error if any warnings were logged. This is disabled by default. Using strict=:warn is suggested for use in automated pipelines, where warnings may require human review.
DeviceLayout.render! — Method
render!(sm::SolidModel, sch::Schematic, target::Target; strict=:error, kwargs...)Render sch to sm, using rendering settings from target.
The strict keyword should be :error, :warn, or :no.
The strict=:error keyword option causes render! to throw an error if any errors were logged while building component geometries or while rendering geometries to cs. This is enabled by default, but can be disabled with strict=:no, in which case any component which was not successfully built will have an empty geometry, and any non-fatal rendering errors will be ignored as usual. Using strict=:no is recommended only for debugging purposes.
The strict=:warn keyword option causes render! to throw an error if any warnings were logged. This is disabled by default. Using strict=:warn is suggested for use in automated pipelines, where warnings may require human review.
Additional keyword arguments may be used for certain entity types for controlling how geometry entities are converted to primitives and added to sm.
Technologies
DeviceLayout.SchematicDrivenLayout.ProcessTechnology — Type
struct ProcessTechnology
layer_record::NamedTuple
parameters::NamedTuple
endSpecifies process-specific parameters and mapping of semantic layers (e.g., to GDS layers).
The interface for process parameters includes the functions layer_thickness, layer_height, chip_thicknesses, and flipchip_gaps, which use the following parameters entries if they are defined:
thickness: ANamedTupleassociating layerSymbols with a thickness or list of thicknesses indexed by level.height: ANamedTupleassociating layerSymbols with "height" or list of heights indexed by level. Height is the distance of a layer from the substrate surface at a given level, measured away from the substrate.chip_thicknesses: AVectorof thicknesses of chips from bottom to top, used for calculating the z coordinate of the surface for each level.flipchip_gaps: AVectorof gaps between chips in a flipchip stack, used withchip_thicknessesfor calculating the z coordinate of the surface for each level.
Note that other tools often have their own related concepts of a "Technology", but these may not directly correspond to our ProcessTechnology.
DeviceLayout.SchematicDrivenLayout.chip_thicknesses — Function
chip_thicknesses(tech::ProcessTechnology)A Vector of thicknesses of chips (default [525μm, 525μm]).
DeviceLayout.SchematicDrivenLayout.flipchip_gaps — Function
flipchip_gaps(tech::ProcessTechnology)A Vector of flipchip gaps (default [5μm]).
DeviceLayout.SchematicDrivenLayout.layer_thickness — Function
layer_thickness(tech::ProcessTechnology, m::DeviceLayout.Meta)The thickness of m in tech.parameters.thickness (0μm if not specified).
DeviceLayout.SchematicDrivenLayout.layer_height — Function
layer_height(tech::ProcessTechnology, m::DeviceLayout.Meta)The height of m in tech.parameters.height (0μm if not specified).
Height is measured outward from the substrate surface corresponding to level(m).
DeviceLayout.SchematicDrivenLayout.layer_z — Function
layer_z(tech::ProcessTechnology, m::DeviceLayout.Meta)The z position corresponding to metadata m.
Uses chip_thicknesses(tech) and flipchip_gaps(tech) to determine the z coordinate corresponding to level(m), then adds or subtracts layer_height(tech, m) for odd or even level(m), respectively. (That is, the layer height is measured outward from the substrate surface using the flipchip level convention.)
DeviceLayout.SchematicDrivenLayout.level_z — Function
level_z(l::Integer; t_chips=[525μm, 525μm], t_gaps=[5μm])Return the z position corresponding to level l.
Uses the flip-chip level convention (see facing, backing).
Keywords
t_chips: A list of chip thicknesses from bottom to topt_gaps: A list of gap thicknesses between substrates
t_gaps should start at the same index as t_chips and have length length(t_chips) - 1. Level 1 is at z = 0.
Targets
DeviceLayout.SchematicDrivenLayout.Target — Type
abstract type TargetA Target can customize behavior during plan, build!, and/or render!.
Given a target::Target, you would use it like this:
g = SchematicGraph("example")
# ... build up schematic graph here
floorplan = plan(g, target)
check!(floorplan)
build!(floorplan, target)
output = Cell(floorplan, target)DeviceLayout.SchematicDrivenLayout.LayoutTarget — Type
struct LayoutTarget <: Target
technology::ProcessTechnology
rendering_options::NamedTuple
levels::Vector{Int}
level_increment::GDSMeta
indexed_layers::Vector{Symbol}
map_meta_dict::Dict{DeviceLayout.Meta, Union{GDSMeta,Nothing}}
endContains information about how to render schematics, typically to a Cell (for the GDSII backend).
A LayoutTarget contains:
technology::ProcessTechnology: used to map semantic layers to output layers whenrendering to an output format (usinglayer_record(technology)).rendering_options: aNamedTupleof keyword arguments to be supplied torender!levels::Vector{Int}: a list of metadata levels to be renderedlevel_increment::GDSMeta: if there are multiple levels in the list, each successive level in the list will have its GDSMeta remapped by this incrementindexed_layers::Vector{Symbol}: a list of layer symbols whose entities should have GDS datatype incremented by their metadata index, for example to give distinct layers to different port boundaries for simulationmap_meta_dict::Dict{SemanticMeta, Union{GDSMeta,Nothing}}: used for memoization of theSemanticMeta -> GDSMetamap; it can also be populated manually to customize behavior
Rendering options might include tolerance (atol) or keyword flags like simulation=true that determine how or whether entities with an OptionalStyle with the corresponding flag are rendered.
When rendering ent::GeometryEntity with target::LayoutTarget, its metadata m is handled as follows:
- If
target.map_meta_dict[m]exists (as aGDSMetainstance ornothing), use that. This can be manually assigned before rendering, overriding the default behavior for any metadata type, includingGDSMeta. Otherwise, the result of the steps below will be stored intarget.map_meta_dict[m]. - If
mis already aGDSMetaand not inmap_meta_dict, use as is. - If
layer(m) == layer(DeviceLayout.NORENDER_META)(that is,:norender), usenothing. - If
!(level(m) in target.levels), usenothing. - If
layer(m)is not present as a key inlayer_record(target.technology)and is not of the form:GDS<layer>_<datatype>, then emit a warning and useGDSMeta(0,0), ignoring level and layer index. - If
layer(m)is not present as a key inlayer_record(target.technology)but is of the form:GDS<layer>_<datatype>, then takeGDSMeta(layer, datatype)and add any increments according tolevel(m)andlayerindex(m)as below. - If
layer(m)is present as a key inlayer_record(target.technology), then maplayer(m)to aGDSMetaornothingusinglayer_record(target.technology)[layer(m)]. If the result isnothing, use that. Otherwise, also considerlevel(m)andlayerindex(m)as below. - If
target.levelshas more than one element andlevel(m)is thenth element, increment the result by(n-1)times the GDS layer and datatype oftarget.level_increment. - If
layer(m) in target.indexed_layers, then increment the GDS datatype of the result bylayerindex(m).
If the result is nothing, then ent is not rendered. Here are some examples:
julia> using DeviceLayout, DeviceLayout.SchematicDrivenLayout, DeviceLayout.PreferredUnits
julia> tech = ProcessTechnology((; base_negative=GDSMeta()), (;));
julia> meta = SemanticMeta(:base_negative);
julia> cs = CoordinateSystem("test", nm);
julia> render!.(cs, Ref(Rectangle(10μm, 10μm)), [
meta,
facing(meta),
SemanticMeta(:GDS2_2, index=2, level=2),
DeviceLayout.UNDEF_META,
DeviceLayout.NORENDER_META,
GDSMeta(2, 2)
]);
julia> cell = Cell("test", nm);
julia> render!(cell, cs, ArtworkTarget(tech; levels=[1, 2], indexed_layers=[:GDS2_2]));
│ ┌ Warning: Target technology does not have a mapping for layer `:undefined`; mapping to GDS layer/datatype 0/0
│ [...]
julia> cell.element_metadata == [
GDSMeta(), # :base_negative => GDSMeta()
GDSMeta(300), # :base_negative => GDSMeta() => GDSMeta(300) [level increment]
GDSMeta(302, 4), # :GDS2_2 => GDSMeta(2, 2) => GDSMeta(302, 2) [level] => GDSMeta(302, 4) [index]
GDSMeta(), # UNDEF_META is not in the layer record, so it's mapped to GDSMeta(0, 0)
# NORENDER_META is skipped
GDSMeta(2, 2) # GDSMeta(2, 2) is passed through without modification
]
trueDeviceLayout.SchematicDrivenLayout.ArtworkTarget — Function
ArtworkTarget(technology::ProcessTechnology;
rendering_options = (; simulation=false, artwork=true),
levels = [1,2],
level_increment = GDSMeta(300,0),
indexed_layers = Symbol[],
map_meta_dict = Dict{SemanticMeta, Union{GDSMeta,Nothing}}()
)A LayoutTarget with defaults set for artwork.
DeviceLayout.SchematicDrivenLayout.SimulationTarget — Function
SimulationTarget(technology::ProcessTechnology;
rendering_options = (; simulation=true, artwork=false),
levels = [1,2],
level_increment = GDSMeta(300,0),
indexed_layers = Symbol[],
map_meta_dict = Dict{SemanticMeta, Union{GDSMeta,Nothing}}()
)A LayoutTarget with defaults set for simulation.
DeviceLayout.SchematicDrivenLayout.SolidModelTarget — Type
struct SolidModelTarget <: Target
technology::ProcessTechnology
bounding_layers::Vector{Symbol}
levelwise_layers::Vector{Symbol}
indexed_layers::Vector{Symbol}
substrate_layers::Vector{Symbol}
ignored_layers::Vector{Symbol}
rendering_options::NamedTuple
postrenderer
endContains information about how to render a Schematic to a 3D SolidModel.
The technology contains parameters like layer heights and thicknesses that are used to position and extrude 2D geometry elements.
Metadata Mapping
When rendering entities, metadata is mapped to physical group names as follows:
- If
layer(m) == layer(DeviceLayout.NORENDER_META)(i.e.,:norender), the entity is skipped and not rendered to the solid model. - If
layer(m)is inignored_layers, the entity is skipped and not rendered to the solid model. - The base name is taken from
layername(m). - If
layer(m)is inlevelwise_layers,"_L$(level(m))"is appended. - If
layer(m)is inindexed_layersandlayerindex(m) != 0,"_$(layerindex(m))"is appended.
Rendering Options
The rendering_options include any keyword arguments to be passed down to the lower-level render!(::SolidModel, ::CoordinateSystem; kwargs...). The target also includes some 3D-specific options:
bounding_layers: A list of layerSymbols. These layers are extruded according totechnologyto define the rendered volume, and then all other layers andtechnology-based extrusions are replaced with their intersection with the rendered volume.levelwise_layers: A list of layerSymbols to be turned intoPhysicalGroups "levelwise". That is, rather than create a singlePhysicalGroupfor all entities with the given layer symbol, a group is created for eachlevelvaluelwith"_L$l"appended.indexed_layers: A list of layerSymbols to be turned into separatePhysicalGroups with"_$i"appended for each indexi. These layers will be automatically indexed if not already present in aSchematic'sindex_dict.substrate_layers: A list of layerSymbols for layers that are extruded by theirtechnologyinto the substrate, rather than away from it.wave_port_layers: A list of layerSymbols for layers that are 1D line segments extruded to define wave port boundary conditions.ignored_layers: A list of layerSymbols for layers that should be ignored during rendering (mapped tonothing). This provides an alternative to usingNORENDER_METAfor layers that should be conditionally ignored in solid model rendering but may be needed for other rendering targets.retained_physical_groups: Vector of(name, dimension)tuples specifying which physical groups to keep after rendering. All other groups are removed.
The postrenderer is a list of geometry kernel commands that create new named groups of entities from other groups, for example by geometric Boolean operations like intersection. These follow the extrusions and bounding_layers intersections generated according to the technology and rendering_options.
DeviceLayout.SchematicDrivenLayout.backing — Function
backing(l::Int)
backing(s::SemanticMeta)
backing(m::Meta)The level backing l or metadata like s in the level backing level(s).
For example, level 3 backs level 2, so backing(2) == 3 and backing(SemanticMeta("lyr"; level=2)) == SemanticMeta(lyr; level=3)
If a metadata object m has no layer attribute, then backing(m) == m.
DeviceLayout.SchematicDrivenLayout.facing — Function
facing(l::Int)
facing(s::SemanticMeta)
facing(m::Meta)The level facing l or metadata like s in the level facing level(s).
For example, level 2 faces level 1, so facing(2) == 1 and facing(SemanticMeta("lyr"; level=1)) == SemanticMeta(lyr; level=2)
If a metadata object m has no layer attribute, then facing(m) == m.
DeviceLayout.SchematicDrivenLayout.not_simulated — Function
not_simulated(ent::GeometryEntity)Return a version of ent that is rendered unless simulation=true in the rendering options.
The simulation option can be set as a keyword argument to render! or as an element in rendering_options in the Target provided to render!.
DeviceLayout.SchematicDrivenLayout.only_simulated — Function
only_simulated(ent::GeometryEntity)Return a GeometryEntity that is rendered if and only if simulation=true in the rendering options.
The simulation option can be set as a keyword argument to render! or as an element in rendering_options in the Target provided to render!.
DeviceLayout.SchematicDrivenLayout.not_solidmodel — Function
not_solidmodel(ent::GeometryEntity)Return a version of ent that is rendered unless solidmodel=true in the rendering options.
The solidmodel option can be set as a keyword argument to render! or as an element in rendering_options in the Target provided to render!.
DeviceLayout.SchematicDrivenLayout.only_solidmodel — Function
only_solidmodel(ent::GeometryEntity)Return a GeometryEntity that is rendered if and only if solidmodel=true in the rendering options.
The solidmodel option can be set as a keyword argument to render! or as an element in rendering_options in the Target provided to render!.
PDKs
DeviceLayout.SchematicDrivenLayout.generate_component_definition — Function
generate_component_definition(compname, pdk::Module, filepath; composite=false,
template=get_template(composite ? "CompositeComponent.jlt" : "Component.jlt", pdk=pdk))Generates a file defining the component type compname at filepath based on template.
Uses a template at the file path template for standard components or for composite components depending on the keyword argument composite. If the template keyword is not explicitly used, then if the PDK defines a Component.jlt or CompositeComponent.jlt template in a templates folder at the package root, that will be used; otherwise, the built-in DeviceLayout templates are used.
For generating a new component package, see generate_component_package. Closely related components that should always be versioned together can be defined in the same package, in which case this method can be used to generate only the file defining a component. That file can then be included from the file defining the root package module.
The built-in template defines a module because it's also used for package generation, but it is not necessary for every component in a package to be in its own module.
DeviceLayout.SchematicDrivenLayout.generate_component_package — Function
generate_component_package(name::AbstractString, pdk::Module, compname="MyComp";
composite=false,
template=get_template(composite ? "CompositeComponent.jlt" : "Component.jlt", pdk=pdk)
docs_template=get_template("Component.mdt", pdk=pdk),
kwargs...
)Generates a new component package named name in the components directory of pdk.
Adds pdk and DeviceLayout as dependencies and sets non-inclusive upper bounds of the next major versions. Creates a definition for a Component type named compname in the main module file, using a template for standard components or for composite components depending on the keyword argument composite. If the template keyword is not explicitly used, then if the PDK defines a Component.jlt or CompositeComponent.jlt template in a templates folder at the package root, that will be used; otherwise, the built-in DeviceLayout templates are used. The source file generated in this way should not be included from the PDK source files, since it is an independent package even if it is tracked in the same Git repository.
Also generates documentation based on docs_template.
The component package can be registered in your private registry MyRegistry as follows using the LocalRegistry package. First, make sure you are on a branch of the MyRegistry registry in ~/.julia/registries/MyRegistry. Then add the LocalRegistry package to your active environment and run:
using LocalRegistry
register(
name;
registry="MyRegistry",
push=false,
repo="git@ssh.example.com:path/to/MyPDK.jl.git" # or however you usually get your repo
)You will need to push the changes and make a pull request for your branch.
For more information about creating and using a local registry, see the LocalRegistry README.
DeviceLayout.SchematicDrivenLayout.generate_pdk — Function
generate_pdk(name="MyPDK"; dir=pwd(), template=get_template("PDK.jlt"), kwargs...)Generates a PDK package named name in the parent directory dir based on template.
Additional keyword arguments are forwarded to PkgTemplates.Template.
The PDK package can be registered in your private registry MyRegistry as follows using the LocalRegistry package. First, make sure you are on a branch of the MyRegistry registry in ~/.julia/registries/MyRegistry. Then add the LocalRegistry package to your active environment and run:
using LocalRegistry
register(
"MyPDK";
registry="MyRegistry",
push=false,
repo="git@ssh.example.com:path/to/MyPDK.jl.git" # or however you usually get your repo
)You will need to push the changes and make a pull request for your branch.
For more information about creating and using a local registry, see the LocalRegistry README.