masque/examples/tutorial/library.py

115 lines
4 KiB
Python

"""
Tutorial: authoring a mixed library with `BuildLibrary`.
This example assumes you have already read `devices.py` and generated the
`circuit.gds` file it writes. The goal here is not the photonic-crystal geometry
itself, but rather how Masque lets you combine imported GDS cells with
python-generated recipes, then turn that declaration set into a normal library
for downstream assembly and writing.
"""
from typing import Any
from pprint import pformat
from masque import BuildLibrary, Pather, Pattern, cell
from masque.file.gdsii import writefile
from masque.file.gdsii.lazy import readfile
import basic_shapes
import devices
from basic_shapes import GDS_OPTS
def make_mixed_waveguide(lib: BuildLibrary) -> Pattern:
"""
Recipe which assembles imported and generated cells behind the builder API.
"""
circ = Pather(library=lib, ports='tri_l3cav')
# First way to specify what we are plugging in: request an explicit abstract.
circ.plug(lib.abstract('wg10'), {'input': 'right'})
# Second way: use an AbstractView, which behaves like a mapping of names
# to abstracts.
abstracts = lib.abstract_view()
circ.plug(abstracts['wg10'], {'output': 'left'})
# Third way: let Pather resolve a pattern name through its own library.
circ.plug('tri_wg10', {'input': 'right'})
circ.plug('tri_wg10', {'output': 'left'})
return circ.pattern
def main() -> None:
builder = BuildLibrary()
cells = builder.cells
#
# Load some devices from a GDS file
#
# Scan circuit.gds and prepare to lazy-load its contents. Port labels are
# imported on first materialization, but the raw source remains untouched
# until we build the final library.
gds_lib, _properties = readfile('circuit.gds')
builder.add_source(gds_lib.with_ports_from_data(layers=[(3, 0)], max_depth=1))
print('Registered imported cells:\n' + pformat(list(gds_lib.keys())))
#
# Register some new devices, this time from python code rather than GDS.
#
cells.triangle = basic_shapes.triangle(devices.RADIUS)
opts: dict[str, Any] = dict(
lattice_constant=devices.LATTICE_CONSTANT,
hole='triangle',
)
cells.tri_wg10 = cell(devices.waveguide)(length=10, mirror_periods=5, **opts)
cells.tri_wg05 = cell(devices.waveguide)(length=5, mirror_periods=5, **opts)
cells.tri_wg28 = cell(devices.waveguide)(length=28, mirror_periods=5, **opts)
cells.tri_bend0 = cell(devices.bend)(mirror_periods=5, **opts)
cells.tri_ysplit = cell(devices.y_splitter)(mirror_periods=5, **opts)
cells.tri_l3cav = cell(devices.perturbed_l3)(xy_size=(4, 10), **opts, hole_lib=builder)
cells.mixed_wg_cav = cell(make_mixed_waveguide)(builder)
print('Declared cells waiting to be built:\n' + pformat(list(builder.keys())))
#
# Build the declaration set into a normal library.
#
built, report = builder.build()
print('Built library contains:\n' + pformat(list(built.keys())))
print('Build dependency graph:\n' + pformat(report.dependency_graph))
#
# Continue designing against the built library.
#
# The built result behaves like a normal mutable library, so downstream code
# can use Pather, abstract views, and writing without going back through the
# builder interface.
circ = Pather.interface(source='mixed_wg_cav', library=built)
circ.plug('tri_bend0', {'input': 'right'})
circ.plug('tri_bend0', {'input': 'left'}, mirrored=True) # mirror since no tri y-symmetry
circ.plug('tri_bend0', {'input': 'right'})
circ.plug('bend0', {'output': 'left'})
circ.plug('bend0', {'output': 'left'})
circ.plug('bend0', {'output': 'left'})
circ.plug('tri_wg10', {'input': 'right'})
circ.plug('tri_wg28', {'input': 'right'})
circ.plug('tri_wg10', {'input': 'right', 'output': 'left'})
built['loop_segment'] = circ.pattern
#
# Write all devices into a GDS file.
#
print('Writing library to file...')
writefile(built, 'library.gds', **GDS_OPTS)
if __name__ == '__main__':
main()