Package: masque
Masque READMEMasque is a Python module for designing lithography masks.The general idea is to implement something resembling the GDSII file-format, but
with some vectorized element types (eg. circles, not just polygons) and the ability
to output to multiple formats.Source repositoryPyPIInstallationRequirements:python >= 3.11numpyklamath (used for GDSII i/o)Optional requirements:ezdxf(DXF i/o): ezdxfoasis(OASIS i/o): fatamorganasvg(SVG output): svgwritevisualization(shape plotting): matplotlibtext(Textshape): matplotlib, freetypeInstall with pip:pipinstall'masque[oasis,dxf,svg,visualization,text]'OverviewA layout consists of a hierarchy ofPatterns stored in a singleLibrary.
EachPatterncan containRefs pointing at other patterns,Shapes,Labels, andPorts.masquedeparts from several "classic" GDSII paradigms:APatternobject does not store its own name. A name is only assigned when the pattern is placed
into aLibrary, which is effectively a name->Patternmapping.Layer info forShapess andLabels is not stored in the individual shape and label objects.
Instead, the layer is determined by the key for the container dict (e.g.pattern.shapes[layer]).This simplifies many common tasks: filteringShapes by layer, remapping layers, and checking if
a layer is empty.Technically, this allows reusing the same shape or label object across multiple layers. This isn't
part of the standard workflow since a mixture of single-use and multi-use shapes could be confusing.This is similar to the approach used inKLayoutReftarget names are also determined in the key of the container dict (e.g.pattern.refs[target_name]).This similarly simplifies filteringRefs by target name, updating to a new target, and checking
if a givenPatternis referenced.Patternnames are set by their containingLibraryand are not stored in thePatternobjects.This guarantees that there are no duplicate pattern names within any givenLibrary.Likewise, enumerating all the names (and all thePatterns) in aLibraryis straightforward.EachRef,Shape, orLabelcan be repeated multiple times by attaching arepetitionobject to it.This is similar to how OASIS reptitions are handled, and provides extra flexibility over the GDSII
approach of only allowing arrays through AREF (Ref+repetition).Labels do not have an orientation or presentationThis is in line with how they are used in practice, and how they are represented in OASIS.Non-polygonalShapes are allowed. For example, elliptical arcs are a basic shape type.This enables compatibility with OASIS (e.g. circles) and other formats.Shapes provide a.to_polygons()method for GDSII compatibility.Most coordinate values are stored as 64-bit floats internally.1 earth radii in nanometers (6e15) is still represented without approximation (53 bit mantissa -> 2^53 > 9e15)Operations that would otherwise clip/round on are still represented approximately.Memory usage is usually dominated by other Python overhead.Patternobjects also containPortinformation, which can be used to "snap" together
multiple sub-components by matching up the requested port offsets and rotations.Port rotations are defined as counter-clockwise angles from the +x axis.Ports point into the interior of their associated device.Port rotations may beNonein the case of non-oriented ports.Ports have aptypestring which is compared in order to catch mismatched connections at build time.Ports can be exported into/imported fromLabels stored directly in the layout,
editable from standard tools (e.g. KLayout). A default format is provided.In one important way,masquestays very orthodox:
References are accomplished by listing the target's name, not itsPatternobject.The main downside of this is that any operations that traverse the hierarchy require
both thePatternand theLibrarywhich is contains its reference targets.This guarantees that names within aLibraryremain unique at all times.Since this can be tedious in cases where you don't actually care about the name of a
pattern, patterns whose names start withSINGLE_USE_PREFIX(default: an underscore)
may be silently renamed in order to maintain uniqueness.
Seemasque.library.SINGLE_USE_PREFIX,masque.library._rename_patterns(),
andILibrary.add()for more details.Having all patterns accessible through theLibraryavoids having to perform a
tree traversal for every operation which needs to touch allPatternobjects
(e.g. deleting a layer everywhere or scaling all patterns).SincePatterndoesn't know its own name, you can't create a reference by passing in
aPatternobject -- you need to know its name.Youcanreference aPatternbefore it is created, so long as you have already decided
on its name.Functions likePattern.place()andPattern.plug()need to receive a pattern's name
in order to create a reference, but they also need to access the pattern's ports.One way to provide this data is through anAbstract, generated viaLibrary.abstract()or through aLibrary.abstract_view().Another way is useBuilder.place()orBuilder.plug(), which automatically creates
anAbstractfrom its internally-referencedLibrary.GlossaryLibrary: A collection of named cells. OASIS or GDS "library" or file."tree": Any Library which has only one topcell.Pattern: A collection of geometry, text labels, and reference to other patterns.
OASIS or GDS "Cell", DXF "Block".Ref: A reference to another pattern. GDS "AREF/SREF", OASIS "Placement".Shape: Individual geometric entity. OASIS or GDS "Geometry element", DXF "LWPolyline" or "Polyline".repetition: Repetition operation. OASIS "repetition".
GDS "AREF" is aRefcombined with aGridrepetition.Label: Text label. Not rendered into geometry. OASIS, GDS, DXF "Text".annotation: Additional metadata. OASIS or GDS "property".Syntax, shorthand, and design patternsMost syntax and behavior should follow normal python conventions.
There are a few exceptions, either meant to catch common mistakes or to provide a shorthand for common operations:Libraryobjects don't allow overwriting already-existing patternslibrary['mycell']=pattern0library['mycell']=pattern1# Error! 'mycell' already exists and can't be overwrittendellibrary['mycell']# We can explicitly delete itlibrary['mycell']=pattern1# And now it's ok to assign a new valuelibrary.delete('mycell')# This also deletes all refs pointing to 'mycell' by defaultInsert a newly-made hierarchical pattern (with children) into a layout# Let's say we have a function which returns a new library containing one topcell (and possibly children)tree=make_tree(...)# To reference this cell in our layout, we have to add all its children to our `library` first:top_name=tree.top()# get the name of the topcellname_mapping=library.add(tree)# add all patterns from `tree`, renaming elgible conflicting patternsnew_name=name_mapping.get(top_name,top_name)# get the new name for the cell (in case it was auto-renamed)my_pattern.ref(new_name,...)# instantiate the cell# This can be accomplished as followsnew_name=library<<tree# Add `tree` into `library` and return the top cell's new namemy_pattern.ref(new_name,...)# instantiate the cell# In practice, you may do lots ofmy_pattern.ref(lib<<make_tree(...),...)We can also use this shorthand to quickly add and reference a single flat (as yet un-named) pattern:anonymous_pattern=Pattern(...)my_pattern.ref(lib<<{'_tentative_name':anonymous_pattern},...)Place a hierarchical pattern into a layout, preserving its port info# As above, we have a function that makes a new library containing one topcell (and possibly children)tree=make_tree(...)# We need to go get its port info to `place()` it into our existing layout,new_name=library<<tree# Add the tree to the library and return its name (see `<<` above)abstract=library.abstract(tree)# An `Abstract` stores a pattern's name and its ports (but no geometry)my_pattern.place(abstract,...)# With shorthand,abstract=library<=treemy_pattern.place(abstract,...)# ormy_pattern.place(library<<make_tree(...),...)### Quickly add geometry, labels, or refs:Thelongformforaddingelementscanbeoverlyverbose:```python3my_pattern.shapes[layer].append(Polygon(vertices,...))my_pattern.labels[layer]+=[Label('my text')]my_pattern.refs[target_name].append(Ref(offset=...,...))There is shorthand for the most common elements:my_pattern.polygon(layer=layer,vertices=vertices,...)my_pattern.rect(layer=layer,xctr=...,xmin=...,ymax=...,ly=...)# rectangle; pick 4 of 6 constraintsmy_pattern.rect(layer=layer,ymin=...,ymax=...,xctr=...,lx=...)my_pattern.path(...)my_pattern.label(layer,'my_text')my_pattern.ref(target_name,offset=...,...)Accessing ports# Square brackets pull from the underlying `.ports` dict:assertpattern['input']ispattern.ports['input']# And you can use them to read multiple ports at once:assertpattern[('input','output')]=={'input':pattern.ports['input'],'output':pattern.ports['output'],}# But you shouldn't use them for anything except readingpattern['input']=Port(...)# Error!has_input=('input'inpattern)# Error!Building patternslibrary=Library(...)my_pattern_name,my_pattern=library.mkpat(some_name_generator())...def_make_my_subpattern()->str:#   This function can draw from the outer scope (e.g. `library`) but will not pollute the outer scope# (e.g. the variable `subpattern` will not be accessible from outside the function; you must load it# from within `library`).subpattern_name,subpattern=library.mkpat(...)subpattern.rect(...)...returnsubpattern_namemy_pattern.ref(_make_my_subpattern(),offset=...,...)TODOBetter interface for polygon operations (e.g. withpyclipper)de-embeddingboolean opsTests tests testscheck renderpatherpather and renderpather examples