Lots of progress on tutorials

This commit is contained in:
Jan Petykiewicz 2023-01-24 23:25:10 -08:00 committed by jan
commit f4537a0feb
16 changed files with 579 additions and 510 deletions

View file

@ -4,8 +4,7 @@ from pprint import pformat
import numpy
from numpy import pi
from masque.builder import Device
from masque.library import Library, LibDeviceLibrary
from masque import Pattern, Builder, WrapLibrary, LazyLibrary, Library
from masque.file.gdsii import writefile, load_libraryfile
import pcgen
@ -16,66 +15,62 @@ from basic_shapes import GDS_OPTS
def main() -> None:
# Define a `Library`-backed `DeviceLibrary`, which provides lazy evaluation
# for device generation code and lazy-loading of GDS contents.
device_lib = LibDeviceLibrary()
# Define a `LazyLibrary`, which provides lazy evaluation for generating
# patterns and lazy-loading of GDS contents.
lib = LazyLibrary()
#
# Load some devices from a GDS file
#
# Scan circuit.gds and prepare to lazy-load its contents
pattern_lib, _properties = load_libraryfile('circuit.gds', tag='mycirc01')
gds_lib, _properties = load_libraryfile('circuit.gds', postprocess=pat2dev)
# Add it into the device library by providing a way to read port info
# This maintains the lazy evaluation from above, so no patterns
# are actually read yet.
device_lib.add_library(pattern_lib, pat2dev=pat2dev)
print('Devices loaded from GDS into library:\n' + pformat(list(device_lib.keys())))
lib.add(gds_lib)
print('Patterns loaded from GDS into library:\n' + pformat(list(lib.keys())))
#
# Add some new devices to the library, this time from python code rather than GDS
#
a = devices.LATTICE_CONSTANT
tri = basic_shapes.triangle(devices.RADIUS)
# Convenience function for adding devices
# This is roughly equivalent to
# `device_lib[name] = lambda: dev2pat(fn())`
# but it also guarantees that the resulting pattern is named `name`.
def add(name: str, fn: Callable[[], Device]) -> None:
device_lib.add_device(name=name, fn=fn, dev2pat=dev2pat)
lib['triangle'] = lambda: basic_shapes.triangle(devices.RADIUS)
opts = dict(
lattice_constant = devices.LATTICE_CONSTANT,
hole = 'triangle',
)
# Triangle-based variants. These are defined here, but they won't run until they're
# retrieved from the library.
add('tri_wg10', lambda: devices.waveguide(lattice_constant=a, hole=tri, length=10, mirror_periods=5))
add('tri_wg05', lambda: devices.waveguide(lattice_constant=a, hole=tri, length=5, mirror_periods=5))
add('tri_wg28', lambda: devices.waveguide(lattice_constant=a, hole=tri, length=28, mirror_periods=5))
add('tri_bend0', lambda: devices.bend(lattice_constant=a, hole=tri, mirror_periods=5))
add('tri_ysplit', lambda: devices.y_splitter(lattice_constant=a, hole=tri, mirror_periods=5))
add('tri_l3cav', lambda: devices.perturbed_l3(lattice_constant=a, hole=tri, xy_size=(4, 10)))
lib['tri_wg10'] = lambda: devices.waveguide(length=10, mirror_periods=5, **opts)
lib['tri_wg05'] = lambda: devices.waveguide(length=5, mirror_periods=5, **opts)
lib['tri_wg28'] = lambda: devices.waveguide(length=28, mirror_periods=5, **opts)
lib['tri_bend0'] = lambda: devices.bend(mirror_periods=5, **opts)
lib['tri_ysplit'] = lambda: devices.y_splitter(mirror_periods=5, **opts)
lib['tri_l3cav'] = lambda: devices.perturbed_l3(xy_size=(4, 10), **opts, hole_lib=lib)
#
# Build a mixed waveguide with an L3 cavity in the middle
#
# Immediately start building from an instance of the L3 cavity
circ2 = device_lib['tri_l3cav'].build('mixed_wg_cav')
circ2 = Builder(library=lib, ports='tri_l3cav')
print(device_lib['wg10'].ports)
circ2.plug(device_lib['wg10'], {'input': 'right'})
circ2.plug(device_lib['wg10'], {'output': 'left'})
circ2.plug(device_lib['tri_wg10'], {'input': 'right'})
circ2.plug(device_lib['tri_wg10'], {'output': 'left'})
print(lib['wg10'].ports)
circ2.plug(lib.abstract('wg10'), {'input': 'right'})
abstracts = lib.abstract_view() # Alternate way to get abstracts
circ2.plug(abstracts['wg10'], {'output': 'left'})
circ2.plug(abstracts['tri_wg10'], {'input': 'right'})
circ2.plug(abstracts['tri_wg10'], {'output': 'left'})
# Add the circuit to the device library.
# It has already been generated, so we can use `set_const` as a shorthand for
# `device_lib['mixed_wg_cav'] = lambda: circ2`
device_lib.set_const(circ2)
# `lib['mixed_wg_cav'] = lambda: circ2.pattern`
lib.set_const('mixed_wg_cav', circ2.pattern)
#
@ -83,29 +78,26 @@ def main() -> None:
#
# We'll be designing against an existing device's interface...
circ3 = circ2.as_interface('loop_segment')
# ... that lets us continue from where we left off.
circ3.plug(device_lib['tri_bend0'], {'input': 'right'})
circ3.plug(device_lib['tri_bend0'], {'input': 'left'}, mirrored=(True, False)) # mirror since no tri y-symmetry
circ3.plug(device_lib['tri_bend0'], {'input': 'right'})
circ3.plug(device_lib['bend0'], {'output': 'left'})
circ3.plug(device_lib['bend0'], {'output': 'left'})
circ3.plug(device_lib['bend0'], {'output': 'left'})
circ3.plug(device_lib['tri_wg10'], {'input': 'right'})
circ3.plug(device_lib['tri_wg28'], {'input': 'right'})
circ3.plug(device_lib['tri_wg10'], {'input': 'right', 'output': 'left'})
circ3 = Builder.interface(source=circ2)
device_lib.set_const(circ3)
# ... that lets us continue from where we left off.
circ3.plug(abstracts['tri_bend0'], {'input': 'right'})
circ3.plug(abstracts['tri_bend0'], {'input': 'left'}, mirrored=(True, False)) # mirror since no tri y-symmetry
circ3.plug(abstracts['tri_bend0'], {'input': 'right'})
circ3.plug(abstracts['bend0'], {'output': 'left'})
circ3.plug(abstracts['bend0'], {'output': 'left'})
circ3.plug(abstracts['bend0'], {'output': 'left'})
circ3.plug(abstracts['tri_wg10'], {'input': 'right'})
circ3.plug(abstracts['tri_wg28'], {'input': 'right'})
circ3.plug(abstracts['tri_wg10'], {'input': 'right', 'output': 'left'})
lib.set_const('loop_segment', circ3.pattern)
#
# Write all devices into a GDS file
#
# This line could be slow, since it generates or loads many of the devices
# since they were not all accessed above.
all_device_pats = [dev.pattern for dev in device_lib.values()]
writefile(all_device_pats, 'library.gds', **GDS_OPTS)
print('Writing library to file...')
writefile(lib, 'library.gds', **GDS_OPTS)
if __name__ == '__main__':
@ -116,14 +108,14 @@ if __name__ == '__main__':
#class prout:
# def place(
# self,
# other: Device,
# other: Pattern,
# label_layer: layer_t = 'WATLAYER',
# *,
# port_map: Optional[Dict[str, Optional[str]]] = None,
# **kwargs,
# ) -> 'prout':
#
# Device.place(self, other, port_map=port_map, **kwargs)
# Pattern.place(self, other, port_map=port_map, **kwargs)
# name: Optional[str]
# for name in other.ports:
# if port_map: