Schematic-Driven Design API Reference

Components

DeviceLayout.AbstractComponentType
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 a NamedTuple.
  • geometry(c::MyComponent): A coordinate system containing c's rendered geometry.
  • hooks(c::MyComponent): A NamedTuple of Hooks and Hook arrays that specify how connections are made with c.
  • hooks(c::MyComponent, h::Symbol): The specific hook identified by h.
  • default_parameters(c::Union{MyComponent, Type{MyComponent}}): Default parameters as a NamedTuple.
  • parameter_names(c::Union{MyComponent, Type{MyComponent}}): Parameter names as a collection of Symbols.
  • A keyword constructor that merges keyword arguments into default_parameters non-recursively. (That is, a NamedTuple keyword argument will overwrite the default parameter entirely, meaning every "subparameter" in the NamedTuple needs to be specified.)
  • create_component(::Type{MyComponent}, name::String=default_parameters(MyComponent).name, base_parameters::NamedTuple=default_parameters(MyComponent); kwargs...): Constructor that recursively merges kwargs into base_parameters. (That is, a NamedTuple keyword argument will be merged into the corresponding NamedTuple 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 for create_component using mycomp 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 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 cs
  • hooks(comp::MyComponent): Return a NamedTuple of Hooks

For composite components (those with subcomponents), see AbstractCompositeComponent.

source
DeviceLayout.SchematicDrivenLayout.ComponentType
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.

source
DeviceLayout.SchematicDrivenLayout.@compdefMacro
@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, _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)
source
DeviceLayout.SchematicDrivenLayout.@componentMacro
@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.

source
DeviceLayout.SchematicDrivenLayout.allowed_rotation_anglesFunction
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.

source
DeviceLayout.SchematicDrivenLayout.check_rotationFunction
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).

source
DeviceLayout.SchematicDrivenLayout.create_componentFunction
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.

source
DeviceLayout.SchematicDrivenLayout.geometryFunction
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)).

source
geometry(cc:AbstractCompositeComponent)

Return the CoordinateSystem resulting from plan(graph(cc)).coordinate_system.

source
DeviceLayout.hooksFunction
hooks(pa::Path)
  • :p0: A HandedPointHook looking into the start of path pa.
  • :p1: A HandedPointHook looking into the end of path pa.
  • :p0_lh: A left-handed HandedPointHook looking into the start of path pa.
  • :p1_lh: A left-handed HandedPointHook looking into the end of path pa.
source
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 distance coupling_gap from the edge of the ground plane, vertically aligned with the claw.
source
hooks(sch::Schematic, node::ComponentNode)

The hooks belonging to the component in node in the global coordinate system of sch.

source
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).

source
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).

source
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.

source
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.

source
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].

source
DeviceLayout.haloMethod
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.

source
DeviceLayout.parametersFunction
parameters(comp::AbstractComponent)

The component's NamedTuple of parameters.

source
parameters(cc::BasicCompositeComponent)

Retrieve the parameters set indirectly by cc's internal SchematicGraph:

  • name: g.name, from which the component's unique name is generated
  • sub_parameters: A tuple of NamedTuples containing the subcomponent parameters in order
source
DeviceLayout.SchematicDrivenLayout.set_parametersFunction
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...).

source
DeviceLayout.SchematicDrivenLayout.base_variantFunction
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.

source
base_variant(comptype::Type{<:AbstractComponent})

If comptype is a @variant of some other component type T <: AbstractComponent, return T; otherwise, return comptype.

source
DeviceLayout.SchematicDrivenLayout.@variantMacro
@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.

source
DeviceLayout.SchematicDrivenLayout.@composite_variantMacro
@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.

source

Built-in Components

DeviceLayout.SchematicDrivenLayout.ArrowAnnotationType
struct 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 arrow
  • tip: The tip of the arrow, with inward direction opposite the arrow
source
DeviceLayout.SchematicDrivenLayout.BasicComponentType
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.

source
DeviceLayout.SchematicDrivenLayout.GDSComponentType
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.

source
DeviceLayout.SchematicDrivenLayout.SpacerType
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 spacer
  • p1: The endpoint of the spacer

Hooks

  • p0_east: Hook at the origin, with in_direction pointing "east" (positive x direction)
  • p0_northeast
  • p0_north
  • ...
  • p0_southeast
  • p1_east: Hook at p1, with in_direction pointing "east" (positive x direction)
  • p1_northeast
  • ...
  • p1_southeast
source

Composite Components

DeviceLayout.SchematicDrivenLayout.AbstractCompositeComponentType
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 a Tuple of subcomponents
  • _graph!(g::SchematicGraph, cc::MyComponent, subcomps::NamedTuple): Populates and connects the schematic graph corresponding to cc, where subcomps contains the results of _build_subcomponents keyed by name
  • map_hooks(::Type{MyComponent}): A Dict{Pair{Int, Symbol}, Symbol mapping 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.

source
DeviceLayout.SchematicDrivenLayout.CompositeComponentType
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.

source
DeviceLayout.SchematicDrivenLayout.BasicCompositeComponentType
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 generated
  • sub_parameters: A tuple of NamedTuples 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.

source
DeviceLayout.SchematicDrivenLayout.componentsMethod
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.

source
DeviceLayout.SchematicDrivenLayout.filter_parametersFunction
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)
end
source
DeviceLayout.flattenMethod
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-level AbstractCompositeComponents. If negative, will repeat until no AbstractCompositeComponents remain.
source
DeviceLayout.SchematicDrivenLayout.map_hooksFunction
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.

source

Hooks

DeviceLayout.HookType
abstract type Hook{T} <: AbstractGeometry{T}

Contains information describing how one component can attach to others.

source
DeviceLayout.PointHookType
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.

source
DeviceLayout.HandedPointHookType
HandedPointHook{T} <: Hook{T}
    h::PointHook{T}
    right_handed::Bool

A 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.

source
DeviceLayout.hooksMethod
hooks(pa::Path)
  • :p0: A HandedPointHook looking into the start of path pa.
  • :p1: A HandedPointHook looking into the end of path pa.
  • :p0_lh: A left-handed HandedPointHook looking into the start of path pa.
  • :p1_lh: A left-handed HandedPointHook looking into the end of path pa.
source
DeviceLayout.in_directionFunction
in_direction(h::Hook)

The inward-pointing direction stored by the PointHook (angle CCW from the positive x axis)

source
DeviceLayout.transformationMethod
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).

source
DeviceLayout.compassFunction
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.)

source

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 Coordinate denoting an offset used to generate the excluded region from shapes in cs
  • a CoordinateSystem or Cell containing the geometry of the excluded region
  • a Function creating a CoordinateSystem or Cell from cs
  • an AbstractArray{<:AbstractPolygon}

Returns the origins of references.

source
DeviceLayout.haloFunction
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.

source
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.

source
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.

source
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.

source
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.

source
DeviceLayout.Autofill.make_haloFunction
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.

source

Schematics

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.

source
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.

source
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.

source
DeviceLayout.SchematicDrivenLayout.RouteComponentType
struct RouteComponent{T} <: AbstractComponent{T}
    name::String
    r::Paths.Route{T}
    global_waypoints::Bool
    sty::Paths.Style
    meta::Meta

Wraps 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.

source
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.

source
DeviceLayout.SchematicDrivenLayout.planFunction
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.

source

Schematic

DeviceLayout.SchematicDrivenLayout.SchematicType
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).

source
DeviceLayout.boundsMethod
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.

source
DeviceLayout.centerMethod
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.

source
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)
source
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.

source
DeviceLayout.SchematicDrivenLayout.find_componentsFunction
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).

source
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.

See find_components(::Function, ::SchematicGraph).

source
DeviceLayout.SchematicDrivenLayout.find_nodesFunction
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)).

source
DeviceLayout.hooksMethod
hooks(sch::Schematic, node::ComponentNode)

The hooks belonging to the component in node in the global coordinate system of sch.

source
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.

source
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).

source
DeviceLayout.SchematicDrivenLayout.rotations_validFunction
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.

source
DeviceLayout.transformationMethod
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).

source
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.

source
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.

source
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.

source

Technologies

DeviceLayout.SchematicDrivenLayout.ProcessTechnologyType
struct ProcessTechnology
    layer_record::NamedTuple
    parameters::NamedTuple
end

Specifies 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: A NamedTuple associating layer Symbols with a thickness or list of thicknesses indexed by level.
  • height: A NamedTuple associating layer Symbols 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: A Vector of thicknesses of chips from bottom to top, used for calculating the z coordinate of the surface for each level.
  • flipchip_gaps: A Vector of gaps between chips in a flipchip stack, used with chip_thicknesses for 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.

source
DeviceLayout.SchematicDrivenLayout.layer_heightFunction
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).

source
DeviceLayout.SchematicDrivenLayout.layer_zFunction
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.)

source
DeviceLayout.SchematicDrivenLayout.level_zFunction
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 top
  • t_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.

source

Targets

DeviceLayout.SchematicDrivenLayout.TargetType
abstract type Target

A 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)
source
DeviceLayout.SchematicDrivenLayout.LayoutTargetType
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}}
end

Contains 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 when rendering to an output format (using layer_record(technology)).
  • rendering_options: a NamedTuple of keyword arguments to be supplied to render!
  • levels::Vector{Int}: a list of metadata levels to be rendered
  • level_increment::GDSMeta: if there are multiple levels in the list, each successive level in the list will have its GDSMeta remapped by this increment
  • indexed_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 simulation
  • map_meta_dict::Dict{SemanticMeta, Union{GDSMeta,Nothing}}: used for memoization of the SemanticMeta -> GDSMeta map; 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:

  1. If target.map_meta_dict[m] exists (as a GDSMeta instance or nothing), use that. This can be manually assigned before rendering, overriding the default behavior for any metadata type, including GDSMeta. Otherwise, the result of the steps below will be stored in target.map_meta_dict[m].
  2. If m is already a GDSMeta and not in map_meta_dict, use as is.
  3. If layer(m) == layer(DeviceLayout.NORENDER_META) (that is, :norender), use nothing.
  4. If !(level(m) in target.levels), use nothing.
  5. If layer(m) is not present as a key in layer_record(target.technology) and is not of the form :GDS<layer>_<datatype>, then emit a warning and use GDSMeta(0,0), ignoring level and layer index.
  6. If layer(m) is not present as a key in layer_record(target.technology) but is of the form :GDS<layer>_<datatype>, then take GDSMeta(layer, datatype) and add any increments according to level(m) and layerindex(m) as below.
  7. If layer(m) is present as a key in layer_record(target.technology), then map layer(m) to a GDSMeta or nothing using layer_record(target.technology)[layer(m)]. If the result is nothing, use that. Otherwise, also consider level(m) and layerindex(m) as below.
  8. If target.levels has more than one element and level(m) is the nth element, increment the result by (n-1) times the GDS layer and datatype of target.level_increment.
  9. If layer(m) in target.indexed_layers, then increment the GDS datatype of the result by layerindex(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
    ]
true
source
DeviceLayout.SchematicDrivenLayout.ArtworkTargetFunction
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.

source
DeviceLayout.SchematicDrivenLayout.SimulationTargetFunction
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.

source
DeviceLayout.SchematicDrivenLayout.SolidModelTargetType
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
end

Contains 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:

  1. If layer(m) == layer(DeviceLayout.NORENDER_META) (i.e., :norender), the entity is skipped and not rendered to the solid model.
  2. If layer(m) is in ignored_layers, the entity is skipped and not rendered to the solid model.
  3. The base name is taken from layername(m).
  4. If layer(m) is in levelwise_layers, "_L$(level(m))" is appended.
  5. If layer(m) is in indexed_layers and layerindex(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 layer Symbols. These layers are extruded according to technology to define the rendered volume, and then all other layers and technology-based extrusions are replaced with their intersection with the rendered volume.
  • levelwise_layers: A list of layer Symbols to be turned into PhysicalGroups "levelwise". That is, rather than create a single PhysicalGroup for all entities with the given layer symbol, a group is created for each level value l with "_L$l" appended.
  • indexed_layers: A list of layer Symbols to be turned into separate PhysicalGroups with "_$i" appended for each index i. These layers will be automatically indexed if not already present in a Schematic's index_dict.
  • substrate_layers: A list of layer Symbols for layers that are extruded by their technology into the substrate, rather than away from it.
  • wave_port_layers: A list of layer Symbols for layers that are 1D line segments extruded to define wave port boundary conditions.
  • ignored_layers: A list of layer Symbols for layers that should be ignored during rendering (mapped to nothing). This provides an alternative to using NORENDER_META for 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.

source
DeviceLayout.SchematicDrivenLayout.backingFunction
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.

source
DeviceLayout.SchematicDrivenLayout.facingFunction
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.

source
DeviceLayout.SchematicDrivenLayout.not_simulatedFunction
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!.

source
DeviceLayout.SchematicDrivenLayout.only_simulatedFunction
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!.

source
DeviceLayout.SchematicDrivenLayout.not_solidmodelFunction
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!.

source
DeviceLayout.SchematicDrivenLayout.only_solidmodelFunction
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!.

source

PDKs

DeviceLayout.SchematicDrivenLayout.generate_component_definitionFunction
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.

source
DeviceLayout.SchematicDrivenLayout.generate_component_packageFunction
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.

source
DeviceLayout.SchematicDrivenLayout.generate_pdkFunction
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.

source