Allow writing a list of patterns to gds (multiple topcells)

This commit is contained in:
jan 2017-11-04 12:18:42 -07:00
parent c451e93df0
commit b7b0da7432
2 changed files with 40 additions and 29 deletions

View File

@ -19,14 +19,14 @@ from ..utils import rotation_matrix_2d, get_bit, vector2
__author__ = 'Jan Petykiewicz' __author__ = 'Jan Petykiewicz'
def write(pattern: Pattern, def write(patterns: Pattern or List[Pattern],
filename: str, filename: str,
meters_per_unit: float, meters_per_unit: float,
logical_units_per_unit: float = 1): logical_units_per_unit: float = 1):
""" """
Write a Pattern to a GDSII file, by first calling .polygonize() on it Write a Pattern or list of patterns to a GDSII file, by first calling
to change the shapes into polygons, and then writing patterns as GDSII .polygonize() to change the shapes into polygons, and then writing patterns
structures, polygons as boundary elements, and subpatterns as structure as GDSII structures, polygons as boundary elements, and subpatterns as structure
references (sref). references (sref).
For each shape, For each shape,
@ -43,7 +43,7 @@ def write(pattern: Pattern,
If you want pattern polygonized with non-default arguments, just call pattern.polygonize() If you want pattern polygonized with non-default arguments, just call pattern.polygonize()
prior to calling this function. prior to calling this function.
:param pattern: A Pattern to write to file. Modified by this function. :param patterns: A Pattern or list of patterns to write to file. Modified by this function.
:param filename: Filename to write to. :param filename: Filename to write to.
:param meters_per_unit: Written into the GDSII file, meters per (database) length unit. :param meters_per_unit: Written into the GDSII file, meters per (database) length unit.
All distances are assumed to be an integer multiple of this unit, and are stored as such. All distances are assumed to be an integer multiple of this unit, and are stored as such.
@ -57,12 +57,17 @@ def write(pattern: Pattern,
logical_unit=logical_units_per_unit, logical_unit=logical_units_per_unit,
physical_unit=meters_per_unit) physical_unit=meters_per_unit)
if isinstance(patterns, Pattern):
patterns = [patterns]
# Get a dict of id(pattern) -> pattern # Get a dict of id(pattern) -> pattern
patterns_by_id = {**(pattern.referenced_patterns_by_id()), id(pattern): pattern} patterns_by_id = {id(pattern): pattern for pattern in patterns}
for pattern in patterns:
patterns_by_id.update(pattern.referenced_patterns_by_id())
# Now create a structure for each pattern, and add in any Boundary and SREF elements # Now create a structure for each pattern, and add in any Boundary and SREF elements
for pat in patterns_by_id.values(): for pat in patterns_by_id.values():
sanitized_name = re.compile('[^A-Za-z0-9_\?\$]').sub('_', pattern.name) sanitized_name = re.compile('[^A-Za-z0-9_\?\$]').sub('_', pat.name)
structure = gdsii.structure.Structure(name=sanitized_name.encode('ASCII')) structure = gdsii.structure.Structure(name=sanitized_name.encode('ASCII'))
lib.append(structure) lib.append(structure)
@ -86,7 +91,7 @@ def write(pattern: Pattern,
# Add an SREF for each subpattern entry # Add an SREF for each subpattern entry
# strans must be set for angle and mag to take effect # strans must be set for angle and mag to take effect
for subpat in pat.subpatterns: for subpat in pat.subpatterns:
sanitized_name = re.compile('[^A-Za-z0-9_\?\$]').sub('_', subpat.name) sanitized_name = re.compile('[^A-Za-z0-9_\?\$]').sub('_', subpat.pattern.name)
sref = gdsii.elements.SRef(struct_name=sanitized_name.encode('ASCII'), sref = gdsii.elements.SRef(struct_name=sanitized_name.encode('ASCII'),
xy=numpy.round([subpat.offset]).astype(int)) xy=numpy.round([subpat.offset]).astype(int))
sref.strans = 0 sref.strans = 0
@ -98,15 +103,15 @@ def write(pattern: Pattern,
lib.save(stream) lib.save(stream)
def write_dose2dtype(pattern: Pattern, def write_dose2dtype(patterns: Pattern or List[Pattern],
filename: str, filename: str,
meters_per_unit: float, meters_per_unit: float,
logical_units_per_unit: float = 1 logical_units_per_unit: float = 1
) -> List[float]: ) -> List[float]:
""" """
Write a Pattern to a GDSII file, by first calling .polygonize() on it Write a Pattern or list of patterns to a GDSII file, by first calling
to change the shapes into polygons, and then writing patterns as GDSII .polygonize() to change the shapes into polygons, and then writing patterns
structures, polygons as boundary elements, and subpatterns as structure as GDSII structures, polygons as boundary elements, and subpatterns as structure
references (sref). references (sref).
For each shape, For each shape,
@ -117,7 +122,7 @@ def write_dose2dtype(pattern: Pattern,
A list of doses is retured, providing a mapping between datatype A list of doses is retured, providing a mapping between datatype
(list index) and dose (list entry). (list index) and dose (list entry).
Note that this function modifies the Pattern. Note that this function modifies the Pattern(s).
It is often a good idea to run pattern.subpatternize() prior to calling this function, It is often a good idea to run pattern.subpatternize() prior to calling this function,
especially if calling .polygonize() will result in very many vertices. especially if calling .polygonize() will result in very many vertices.
@ -125,7 +130,7 @@ def write_dose2dtype(pattern: Pattern,
If you want pattern polygonized with non-default arguments, just call pattern.polygonize() If you want pattern polygonized with non-default arguments, just call pattern.polygonize()
prior to calling this function. prior to calling this function.
:param pattern: A Pattern to write to file. Modified by this function. :param patterns: A Pattern or list of patterns to write to file. Modified by this function.
:param filename: Filename to write to. :param filename: Filename to write to.
:param meters_per_unit: Written into the GDSII file, meters per (database) length unit. :param meters_per_unit: Written into the GDSII file, meters per (database) length unit.
All distances are assumed to be an integer multiple of this unit, and are stored as such. All distances are assumed to be an integer multiple of this unit, and are stored as such.
@ -141,11 +146,16 @@ def write_dose2dtype(pattern: Pattern,
logical_unit=logical_units_per_unit, logical_unit=logical_units_per_unit,
physical_unit=meters_per_unit) physical_unit=meters_per_unit)
# Get a dict of id(pattern) -> pattern if isinstance(patterns, Pattern):
patterns_by_id = {**(pattern.referenced_patterns_by_id()), id(pattern): pattern} patterns = [patterns]
# Get a table of (id(subpat.pattern), written_dose) for each subpattern # Get a dict of id(pattern) -> pattern
sd_table = make_dose_table(pattern) patterns_by_id = {id(pattern): pattern for pattern in patterns}
for pattern in patterns:
patterns_by_id.update(pattern.referenced_patterns_by_id())
# Get a table of (id(pat), written_dose) for each pattern and subpattern
sd_table = make_dose_table(patterns)
# Figure out all the unique doses necessary to write this pattern # Figure out all the unique doses necessary to write this pattern
# This means going through each row in sd_table and adding the dose values needed to write # This means going through each row in sd_table and adding the dose values needed to write

View File

@ -2,7 +2,7 @@
Helper functions for file reading and writing Helper functions for file reading and writing
""" """
import re import re
from typing import Set, Tuple from typing import Set, Tuple, List
from masque.pattern import Pattern from masque.pattern import Pattern
@ -24,18 +24,19 @@ def mangle_name(pattern: Pattern, dose_multiplier: float=1.0) -> str:
return sanitized_name return sanitized_name
def make_dose_table(pattern: Pattern, dose_multiplier: float=1.0) -> Set[Tuple[int, float]]: def make_dose_table(patterns: List[Pattern], dose_multiplier: float=1.0) -> Set[Tuple[int, float]]:
""" """
Create a set containing (id(subpat.pattern), written_dose) for each subpattern Create a set containing (id(pat), written_dose) for each pattern (including subpatterns)
:param pattern: Source Pattern. :param pattern: Source Patterns.
:param dose_multiplier: Multiplier for all written_dose entries. :param dose_multiplier: Multiplier for all written_dose entries.
:return: {(id(subpat.pattern), written_dose), ...} :return: {(id(subpat.pattern), written_dose), ...}
""" """
dose_table = {(id(pattern), dose_multiplier)} dose_table = {(id(pattern), dose_multiplier) for pattern in patterns}
for pattern in patterns:
for subpat in pattern.subpatterns: for subpat in pattern.subpatterns:
subpat_dose_entry = (id(subpat.pattern), subpat.dose * dose_multiplier) subpat_dose_entry = (id(subpat.pattern), subpat.dose * dose_multiplier)
if subpat_dose_entry not in dose_table: if subpat_dose_entry not in dose_table:
subpat_dose_table = make_dose_table(subpat.pattern, subpat.dose * dose_multiplier) subpat_dose_table = make_dose_table([subpat.pattern], subpat.dose * dose_multiplier)
dose_table = dose_table.union(subpat_dose_table) dose_table = dose_table.union(subpat_dose_table)
return dose_table return dose_table