masque 3.2

Creator: railscoder56

Last updated:

Add to Cart

Description:

masque 3.2

Masque README
Masque 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 repository
PyPI
Github mirror

Installation
Requirements:

python >= 3.11
numpy
klamath (used for GDSII i/o)

Optional requirements:

ezdxf (DXF i/o): ezdxf
oasis (OASIS i/o): fatamorgana
svg (SVG output): svgwrite
visualization (shape plotting): matplotlib
text (Text shape): matplotlib, freetype

Install with pip:
pip install 'masque[oasis,dxf,svg,visualization,text]'

Overview
A layout consists of a hierarchy of Patterns stored in a single Library.
Each Pattern can contain Refs pointing at other patterns, Shapes, Labels, and Ports.
masque departs from several "classic" GDSII paradigms:

A Pattern object does not store its own name. A name is only assigned when the pattern is placed
into a Library, which is effectively a name->Pattern mapping.
Layer info for Shapess and Labels 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: filtering Shapes 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 in KLayout


Ref target names are also determined in the key of the container dict (e.g. pattern.refs[target_name]).

This similarly simplifies filtering Refs by target name, updating to a new target, and checking
if a given Pattern is referenced.


Pattern names are set by their containing Library and are not stored in the Pattern objects.

This guarantees that there are no duplicate pattern names within any given Library.
Likewise, enumerating all the names (and all the Patterns) in a Library is straightforward.


Each Ref, Shape, or Label can be repeated multiple times by attaching a repetition object 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 presentation

This is in line with how they are used in practice, and how they are represented in OASIS.


Non-polygonal Shapes 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.


Pattern objects also contain Port information, 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 be None in the case of non-oriented ports.
Ports have a ptype string which is compared in order to catch mismatched connections at build time.
Ports can be exported into/imported from Labels stored directly in the layout,
editable from standard tools (e.g. KLayout). A default format is provided.



In one important way, masque stays very orthodox:
References are accomplished by listing the target's name, not its Pattern object.

The main downside of this is that any operations that traverse the hierarchy require
both the Pattern and the Library which is contains its reference targets.
This guarantees that names within a Library remain 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 with SINGLE_USE_PREFIX (default: an underscore)
may be silently renamed in order to maintain uniqueness.
See masque.library.SINGLE_USE_PREFIX, masque.library._rename_patterns(),
and ILibrary.add() for more details.


Having all patterns accessible through the Library avoids having to perform a
tree traversal for every operation which needs to touch all Pattern objects
(e.g. deleting a layer everywhere or scaling all patterns).
Since Pattern doesn't know its own name, you can't create a reference by passing in
a Pattern object -- you need to know its name.
You can reference a Pattern before it is created, so long as you have already decided
on its name.
Functions like Pattern.place() and Pattern.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 an Abstract, generated via
Library.abstract() or through a Library.abstract_view().
Another way is use Builder.place() or Builder.plug(), which automatically creates
an Abstract from its internally-referenced Library.



Glossary

Library: A collection of named cells. OASIS or GDS "library" or file.
Tree: Any {name: pattern} mapping 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 a Ref combined with a Grid repetition.
Label: Text label. Not rendered into geometry. OASIS, GDS, DXF "Text".
annotation: Additional metadata. OASIS or GDS "property".

Syntax, shorthand, and design patterns
Most 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:
Library objects don't allow overwriting already-existing patterns
library['mycell'] = pattern0
library['mycell'] = pattern1 # Error! 'mycell' already exists and can't be overwritten
del library['mycell'] # We can explicitly delete it
library['mycell'] = pattern1 # And now it's ok to assign a new value
library.delete('mycell') # This also deletes all refs pointing to 'mycell' by default

Insert 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 topcell
name_mapping = library.add(tree) # add all patterns from `tree`, renaming elgible conflicting patterns
new_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 follows
new_name = library << tree # Add `tree` into `library` and return the top cell's new name
my_pattern.ref(new_name, ...) # instantiate the cell

# In practice, you may do lots of
my_pattern.ref(lib << make_tree(...), ...)

# With a `Builder` and `place()`/`plug()` the `lib <<` portion can be implicit:
my_builder = Builder(library=lib, ...)
...
my_builder.place(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 <= tree
my_pattern.place(abstract, ...)

# or
my_pattern.place(library << make_tree(...), ...)


### Quickly add geometry, labels, or refs:
The long form for adding elements can be overly verbose:
```python3
my_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 constraints
my_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:
assert pattern['input'] is pattern.ports['input']

# And you can use them to read multiple ports at once:
assert pattern[('input', 'output')] == {
'input': pattern.ports['input'],
'output': pattern.ports['output'],
}

# But you shouldn't use them for anything except reading
pattern['input'] = Port(...) # Error!
has_input = ('input' in pattern) # Error!

Building patterns
library = 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(...)
...
return subpattern_name
my_pattern.ref(_make_my_subpattern(), offset=..., ...)

TODO

Better interface for polygon operations (e.g. with pyclipper)

de-embedding
boolean ops


Tests tests tests
check renderpather
pather and renderpather examples
context manager for retool
allow a specific mismatch when connecting ports

License

For personal and professional use. You cannot resell or redistribute these repositories in their original state.

Customer Reviews

There are no reviews.