Lithography mask library
Project description
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.
Installation
Requirements:
- python >= 3.11
- numpy
- klamath (used for GDSII i/o)
Optional requirements:
ezdxf
(DXF i/o): ezdxfoasis
(OASIS i/o): fatamorganasvg
(SVG output): svgwritevisualization
(shape plotting): matplotlibtext
(Text
shape): matplotlib, freetype
Install with pip:
pip install 'masque[oasis,dxf,svg,visualization,text]'
Overview
A layout consists of a hierarchy of Pattern
s stored in a single Library
.
Each Pattern
can contain Ref
s pointing at other patterns, Shape
s, Label
s, and Port
s.
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 aLibrary
, which is effectively a name->Pattern
mapping. - Layer info for
Shape
ss andLabel
s 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
Shape
s 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
- This simplifies many common tasks: filtering
Ref
target names are also determined in the key of the container dict (e.g.pattern.refs[target_name]
).- This similarly simplifies filtering
Ref
s by target name, updating to a new target, and checking if a givenPattern
is referenced.
- This similarly simplifies filtering
Pattern
names are set by their containingLibrary
and are not stored in thePattern
objects.- This guarantees that there are no duplicate pattern names within any given
Library
. - Likewise, enumerating all the names (and all the
Pattern
s) in aLibrary
is straightforward.
- This guarantees that there are no duplicate pattern names within any given
- Each
Ref
,Shape
, orLabel
can be repeated multiple times by attaching arepetition
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
).
- This is similar to how OASIS reptitions are handled, and provides extra flexibility over the GDSII
approach of only allowing arrays through AREF (
Label
s 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
Shape
s are allowed. For example, elliptical arcs are a basic shape type.- This enables compatibility with OASIS (e.g. circles) and other formats.
Shape
s 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 containPort
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
Label
s 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 theLibrary
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. Seemasque.library.SINGLE_USE_PREFIX
,masque.library._rename_patterns()
, andILibrary.add()
for more details.
- Since this can be tedious in cases where you don't actually care about the name of a
pattern, patterns whose names start with
- Having all patterns accessible through the
Library
avoids having to perform a tree traversal for every operation which needs to touch allPattern
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 aPattern
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()
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 an
Abstract
, generated viaLibrary.abstract()
or through aLibrary.abstract_view()
. - Another way is use
Builder.place()
orBuilder.plug()
, which automatically creates anAbstract
from its internally-referencedLibrary
.
- One way to provide this data is through an
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 aRef
combined with aGrid
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
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
File details
Details for the file masque-3.2.tar.gz
.
File metadata
- Download URL: masque-3.2.tar.gz
- Upload date:
- Size: 151.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | c7f57fe4d39d109a0e6c20687bbf323f88088eb1528ed65754d91627b3e70038 |
|
MD5 | 98779e3f52ce71d7cea704c47ae1aba8 |
|
BLAKE2b-256 | df5f048f711658f5cd7e911359d8f6826394687bbf2b36346d7557c9271ce63d |
File details
Details for the file masque-3.2-py3-none-any.whl
.
File metadata
- Download URL: masque-3.2-py3-none-any.whl
- Upload date:
- Size: 179.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/5.1.1 CPython/3.12.4
File hashes
Algorithm | Hash digest | |
---|---|---|
SHA256 | a9e1b62d8cc8926fece2f4ad464dcfe8485a4cb2ba925fa3ef102f188569238f |
|
MD5 | 65cceb2f1dfdad466b26b0617d8f8e24 |
|
BLAKE2b-256 | d5b5509ee727e788101e18df44846f99be76d57551a0666f8595787ddd1cf610 |