Allow writing a list of patterns to gds (multiple topcells)
This commit is contained in:
parent
c451e93df0
commit
b7b0da7432
@ -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
|
||||||
@ -185,7 +195,7 @@ def write_dose2dtype(pattern: Pattern,
|
|||||||
# 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:
|
||||||
dose_mult = subpat.dose * pat_dose
|
dose_mult = subpat.dose * pat_dose
|
||||||
sref = gdsii.elements.SRef(struct_name=mangle_name(subpat.pattern, dose_mult).encode('ASCII'),
|
sref = gdsii.elements.SRef(struct_name=mangle_name(subpat.pattern, dose_mult).encode('ASCII'),
|
||||||
xy=numpy.round([subpat.offset]).astype(int))
|
xy=numpy.round([subpat.offset]).astype(int))
|
||||||
sref.strans = 0
|
sref.strans = 0
|
||||||
sref.angle = subpat.rotation
|
sref.angle = subpat.rotation
|
||||||
|
@ -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 subpat in pattern.subpatterns:
|
for pattern in patterns:
|
||||||
subpat_dose_entry = (id(subpat.pattern), subpat.dose * dose_multiplier)
|
for subpat in pattern.subpatterns:
|
||||||
if subpat_dose_entry not in dose_table:
|
subpat_dose_entry = (id(subpat.pattern), subpat.dose * dose_multiplier)
|
||||||
subpat_dose_table = make_dose_table(subpat.pattern, subpat.dose * dose_multiplier)
|
if subpat_dose_entry not in dose_table:
|
||||||
dose_table = dose_table.union(subpat_dose_table)
|
subpat_dose_table = make_dose_table([subpat.pattern], subpat.dose * dose_multiplier)
|
||||||
|
dose_table = dose_table.union(subpat_dose_table)
|
||||||
return dose_table
|
return dose_table
|
||||||
|
Loading…
Reference in New Issue
Block a user