""" Tutorial: using `LazyLibrary` and `Builder.interface()`. 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. """ from typing import Any from pprint import pformat from masque import Builder, LazyLibrary from masque.file.gdsii import writefile, load_libraryfile import basic_shapes import devices from devices import data_to_ports from basic_shapes import GDS_OPTS def main() -> None: # A `LazyLibrary` delays work until a pattern is actually needed. # That applies both to GDS cells we load from disk and to python callables # that generate patterns on demand. lib = LazyLibrary() # # Load some devices from a GDS file # # Scan circuit.gds and prepare to lazy-load its contents gds_lib, _properties = load_libraryfile('circuit.gds', postprocess=data_to_ports) # Add those cells into our lazy library. # Nothing is read yet; we are only registering how to fetch and postprocess # each pattern when it is first requested. 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 # lib['triangle'] = lambda: basic_shapes.triangle(devices.RADIUS) opts: dict[str, Any] = dict( lattice_constant = devices.LATTICE_CONSTANT, hole = 'triangle', ) # Triangle-based variants. These lambdas are only recipes for building the # patterns; they do not execute until someone asks for the cell. 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 # # Start a new design by copying the ports from an existing library cell. # This gives `circ2` the same external interface as `tri_l3cav`. circ2 = Builder(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 `Builder`. 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 `Builder` 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 # # Build a second device that is explicitly designed to mate with `circ2`. # # `Builder.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 = Builder.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 # # Write all devices into a GDS file # print('Writing library to file...') writefile(lib, '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 #