[wip] introduce BuildLibrary and make overlay first-class
This commit is contained in:
parent
e108199bcd
commit
6d494142fe
7 changed files with 1335 additions and 98 deletions
|
|
@ -20,10 +20,10 @@ Contents
|
|||
* Use `Pather` to snap ports together into a circuit
|
||||
* Check for dangling references
|
||||
- [library](library.py)
|
||||
* Continue from `devices.py` using a lazy library
|
||||
* Create a `LazyLibrary`, which loads / generates patterns only when they are first used
|
||||
* Continue from `devices.py` by declaring a mixed library with `BuildLibrary`
|
||||
* Import source-backed GDS cells and register python-generated recipes together
|
||||
* Call `build()` to produce a normal library for downstream `Pather` usage and writing
|
||||
* Explore alternate ways of specifying a pattern for `.plug()` and `.place()`
|
||||
* Design a pattern which is meant to plug into an existing pattern (via `.interface()`)
|
||||
- [pather](pather.py)
|
||||
* Use `Pather` to route individual wires and wire bundles
|
||||
* Use `AutoTool` to generate paths
|
||||
|
|
|
|||
|
|
@ -1,136 +1,114 @@
|
|||
"""
|
||||
Tutorial: using a source-backed lazy GDS library and `Pather.interface()`.
|
||||
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 mix lazily loaded GDS content with
|
||||
python-generated devices inside one library.
|
||||
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 Pather
|
||||
from masque import BuildLibrary, Pather, Pattern, cell
|
||||
from masque.file.gdsii import writefile
|
||||
from masque.file.gdsii_lazy import OverlayLibrary, readfile
|
||||
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:
|
||||
# `OverlayLibrary` lets us mix source-backed GDS cells with python-generated
|
||||
# patterns behind the same library interface.
|
||||
lib = OverlayLibrary()
|
||||
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.
|
||||
# imported on first materialization, but the raw source remains untouched
|
||||
# until we build the final library.
|
||||
gds_lib, _properties = readfile('circuit.gds')
|
||||
lib.add_source(gds_lib.with_ports_from_data(layers=[(3, 0)], max_depth=1))
|
||||
builder.add_source(gds_lib.with_ports_from_data(layers=[(3, 0)], max_depth=1))
|
||||
|
||||
print('Patterns loaded from GDS into library:\n' + pformat(list(lib.keys())))
|
||||
print('Registered imported cells:\n' + pformat(list(gds_lib.keys())))
|
||||
|
||||
#
|
||||
# Add some new devices to the library, this time from python code rather than GDS
|
||||
# Register some new devices, this time from python code rather than GDS.
|
||||
#
|
||||
|
||||
lib['triangle'] = basic_shapes.triangle(devices.RADIUS)
|
||||
cells.triangle = basic_shapes.triangle(devices.RADIUS)
|
||||
opts: dict[str, Any] = dict(
|
||||
lattice_constant = devices.LATTICE_CONSTANT,
|
||||
hole = 'triangle',
|
||||
)
|
||||
lattice_constant=devices.LATTICE_CONSTANT,
|
||||
hole='triangle',
|
||||
)
|
||||
|
||||
lib['tri_wg10'] = devices.waveguide(length=10, mirror_periods=5, **opts)
|
||||
lib['tri_wg05'] = devices.waveguide(length=5, mirror_periods=5, **opts)
|
||||
lib['tri_wg28'] = devices.waveguide(length=28, mirror_periods=5, **opts)
|
||||
lib['tri_bend0'] = devices.bend(mirror_periods=5, **opts)
|
||||
lib['tri_ysplit'] = devices.y_splitter(mirror_periods=5, **opts)
|
||||
lib['tri_l3cav'] = devices.perturbed_l3(xy_size=(4, 10), **opts, hole_lib=lib)
|
||||
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 a mixed waveguide with an L3 cavity in the middle
|
||||
# Build the declaration set into a normal library.
|
||||
#
|
||||
|
||||
# Start a new design by copying the ports from an existing library cell.
|
||||
# This gives `circ2` the same external interface as `tri_l3cav`.
|
||||
circ2 = Pather(library=lib, ports='tri_l3cav')
|
||||
|
||||
# First way to specify what we are plugging in: request an explicit abstract.
|
||||
# This works with `Pattern` methods directly as well as with `Pather`.
|
||||
circ2.plug(lib.abstract('wg10'), {'input': 'right'})
|
||||
|
||||
# Second way: use an `AbstractView`, which behaves like a mapping of names
|
||||
# to abstracts.
|
||||
abstracts = lib.abstract_view()
|
||||
circ2.plug(abstracts['wg10'], {'output': 'left'})
|
||||
|
||||
# Third way: let `Pather` resolve a pattern name through its own library.
|
||||
# This shorthand is convenient, but it is specific to helpers that already
|
||||
# carry a library reference.
|
||||
circ2.plug('tri_wg10', {'input': 'right'})
|
||||
circ2.plug('tri_wg10', {'output': 'left'})
|
||||
|
||||
# Add the circuit to the device library.
|
||||
lib['mixed_wg_cav'] = circ2.pattern
|
||||
|
||||
built = builder.build()
|
||||
print('Built library contains:\n' + pformat(list(built.keys())))
|
||||
|
||||
#
|
||||
# Build a second device that is explicitly designed to mate with `circ2`.
|
||||
# Continue designing against the built library.
|
||||
#
|
||||
|
||||
# `Pather.interface()` makes a new pattern whose ports mirror an existing
|
||||
# design's external interface. That is useful when you want to design an
|
||||
# adapter, continuation, or mating structure.
|
||||
circ3 = Pather.interface(source=circ2)
|
||||
|
||||
# Continue routing outward from those inherited ports.
|
||||
circ3.plug('tri_bend0', {'input': 'right'})
|
||||
circ3.plug('tri_bend0', {'input': 'left'}, mirrored=True) # mirror since no tri y-symmetry
|
||||
circ3.plug('tri_bend0', {'input': 'right'})
|
||||
circ3.plug('bend0', {'output': 'left'})
|
||||
circ3.plug('bend0', {'output': 'left'})
|
||||
circ3.plug('bend0', {'output': 'left'})
|
||||
circ3.plug('tri_wg10', {'input': 'right'})
|
||||
circ3.plug('tri_wg28', {'input': 'right'})
|
||||
circ3.plug('tri_wg10', {'input': 'right', 'output': 'left'})
|
||||
|
||||
lib['loop_segment'] = circ3.pattern
|
||||
# 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
|
||||
# Write all devices into a GDS file.
|
||||
#
|
||||
print('Writing library to file...')
|
||||
writefile(lib, 'library.gds', **GDS_OPTS)
|
||||
writefile(built, 'library.gds', **GDS_OPTS)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
||||
#
|
||||
#class prout:
|
||||
# def place(
|
||||
# self,
|
||||
# other: Pattern,
|
||||
# label_layer: layer_t = 'WATLAYER',
|
||||
# *,
|
||||
# port_map: Dict[str, str | None] | None = None,
|
||||
# **kwargs,
|
||||
# ) -> 'prout':
|
||||
#
|
||||
# Pattern.place(self, other, port_map=port_map, **kwargs)
|
||||
# name: str | None
|
||||
# for name in other.ports:
|
||||
# if port_map:
|
||||
# assert(name is not None)
|
||||
# name = port_map.get(name, name)
|
||||
# if name is None:
|
||||
# continue
|
||||
# self.pattern.label(string=name, offset=self.ports[name].offset, layer=label_layer)
|
||||
# return self
|
||||
#
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue