Remove support for dose
Since there isn't GDS/OASIS level support for dose, this can be mostly handled by using arbitrary layers/dtypes directly. Dose scaling isn't handled as nicely that way, but it corresponds more directly to what gets written to file.
This commit is contained in:
parent
f7a2edfe23
commit
c7f3e7ee52
18 changed files with 57 additions and 340 deletions
|
|
@ -26,8 +26,8 @@ def writefile(
|
|||
|
||||
Note that this function modifies the Pattern.
|
||||
|
||||
If `custom_attributes` is `True`, non-standard `pattern_layer` and `pattern_dose` attributes
|
||||
are written to the relevant elements.
|
||||
If `custom_attributes` is `True`, a non-standard `pattern_layer` attribute
|
||||
is written to the relevant elements.
|
||||
|
||||
It is often a good idea to run `pattern.subpatternize()` on pattern prior to
|
||||
calling this function, especially if calling `.polygonize()` will result in very
|
||||
|
|
@ -39,8 +39,8 @@ def writefile(
|
|||
Args:
|
||||
pattern: Pattern to write to file. Modified by this function.
|
||||
filename: Filename to write to.
|
||||
custom_attributes: Whether to write non-standard `pattern_layer` and
|
||||
`pattern_dose` attributes to the SVG elements.
|
||||
custom_attributes: Whether to write non-standard `pattern_layer` attribute to the
|
||||
SVG elements.
|
||||
"""
|
||||
pattern = library[top]
|
||||
|
||||
|
|
@ -61,8 +61,7 @@ def writefile(
|
|||
svg = svgwrite.Drawing(filename, profile='full', viewBox=viewbox_string,
|
||||
debug=(not custom_attributes))
|
||||
|
||||
# Now create a group for each row in sd_table (ie, each pattern + dose combination)
|
||||
# and add in any Boundary and Use elements
|
||||
# Now create a group for each pattern and add in any Boundary and Use elements
|
||||
for name, pat in library.items():
|
||||
svg_group = svg.g(id=mangle_name(name), fill='blue', stroke='red')
|
||||
|
||||
|
|
@ -73,7 +72,6 @@ def writefile(
|
|||
path = svg.path(d=path_spec)
|
||||
if custom_attributes:
|
||||
path['pattern_layer'] = polygon.layer
|
||||
path['pattern_dose'] = polygon.dose
|
||||
|
||||
svg_group.add(path)
|
||||
|
||||
|
|
@ -82,8 +80,6 @@ def writefile(
|
|||
continue
|
||||
transform = f'scale({subpat.scale:g}) rotate({subpat.rotation:g}) translate({subpat.offset[0]:g},{subpat.offset[1]:g})'
|
||||
use = svg.use(href='#' + mangle_name(subpat.target), transform=transform)
|
||||
if custom_attributes:
|
||||
use['pattern_dose'] = subpat.dose
|
||||
svg_group.add(use)
|
||||
|
||||
svg.defs.add(svg_group)
|
||||
|
|
|
|||
|
|
@ -15,23 +15,18 @@ from ..shapes import Polygon, Path
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def mangle_name(name: str, dose_multiplier: float = 1.0) -> str:
|
||||
def mangle_name(name: str) -> str:
|
||||
"""
|
||||
Create a new name using `name` and the `dose_multiplier`.
|
||||
Sanitize a name.
|
||||
|
||||
Args:
|
||||
name: Name we want to mangle.
|
||||
dose_multiplier: Dose multiplier to mangle with.
|
||||
|
||||
Returns:
|
||||
Mangled name.
|
||||
"""
|
||||
if dose_multiplier == 1:
|
||||
full_name = name
|
||||
else:
|
||||
full_name = f'{name}_dm{dose_multiplier}'
|
||||
expression = re.compile(r'[^A-Za-z0-9_\?\$]')
|
||||
sanitized_name = expression.sub('_', full_name)
|
||||
sanitized_name = expression.sub('_', name)
|
||||
return sanitized_name
|
||||
|
||||
|
||||
|
|
@ -59,134 +54,6 @@ def clean_pattern_vertices(pat: Pattern) -> Pattern:
|
|||
return pat
|
||||
|
||||
|
||||
def make_dose_table(
|
||||
top_names: Iterable[str],
|
||||
library: Mapping[str, Pattern],
|
||||
dose_multiplier: float = 1.0,
|
||||
) -> Set[Tuple[str, float]]:
|
||||
"""
|
||||
Create a set containing `(name, written_dose)` for each pattern (including subpatterns)
|
||||
|
||||
Args:
|
||||
top_names: Names of all topcells
|
||||
pattern: Source Patterns.
|
||||
dose_multiplier: Multiplier for all written_dose entries.
|
||||
|
||||
Returns:
|
||||
`{(name, written_dose), ...}`
|
||||
"""
|
||||
dose_table = {(top_name, dose_multiplier) for top_name in top_names}
|
||||
for name, pattern in library.items():
|
||||
for subpat in pattern.subpatterns:
|
||||
if subpat.target is None:
|
||||
continue
|
||||
subpat_dose_entry = (subpat.target, subpat.dose * dose_multiplier)
|
||||
if subpat_dose_entry not in dose_table:
|
||||
subpat_dose_table = make_dose_table(subpat.target, library, subpat.dose * dose_multiplier)
|
||||
dose_table = dose_table.union(subpat_dose_table)
|
||||
return dose_table
|
||||
|
||||
|
||||
def dtype2dose(pattern: Pattern) -> Pattern:
|
||||
"""
|
||||
For each shape in the pattern, if the layer is a tuple, set the
|
||||
layer to the tuple's first element and set the dose to the
|
||||
tuple's second element.
|
||||
|
||||
Generally intended for use with `Pattern.apply()`.
|
||||
|
||||
Args:
|
||||
pattern: Pattern to modify
|
||||
|
||||
Returns:
|
||||
pattern
|
||||
"""
|
||||
for shape in pattern.shapes:
|
||||
if isinstance(shape.layer, tuple):
|
||||
shape.dose = shape.layer[1]
|
||||
shape.layer = shape.layer[0]
|
||||
return pattern
|
||||
|
||||
|
||||
def dose2dtype(
|
||||
library: Mapping[str, Pattern],
|
||||
) -> Tuple[List[Pattern], List[float]]:
|
||||
"""
|
||||
For each shape in each pattern, set shape.layer to the tuple
|
||||
(base_layer, datatype), where:
|
||||
layer is chosen to be equal to the original shape.layer if it is an int,
|
||||
or shape.layer[0] if it is a tuple. `str` layers raise a PatterError.
|
||||
datatype is chosen arbitrarily, based on calcualted dose for each shape.
|
||||
Shapes with equal calcualted dose will have the same datatype.
|
||||
A list of doses is retured, providing a mapping between datatype
|
||||
(list index) and dose (list entry).
|
||||
|
||||
Note that this function modifies the input Pattern(s).
|
||||
|
||||
Args:
|
||||
patterns: A `Pattern` or list of patterns to write to file. Modified by this function.
|
||||
|
||||
Returns:
|
||||
(patterns, dose_list)
|
||||
patterns: modified input patterns
|
||||
dose_list: A list of doses, providing a mapping between datatype (int, list index)
|
||||
and dose (float, list entry).
|
||||
"""
|
||||
logger.warning('TODO: dose2dtype() needs to be tested!')
|
||||
|
||||
if not isinstance(library, Library):
|
||||
library = WrapROLibrary(library)
|
||||
|
||||
# Get a table of (id(pat), written_dose) for each pattern and subpattern
|
||||
sd_table = make_dose_table(library.find_toplevel(), library)
|
||||
|
||||
# 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
|
||||
# that subpattern at that dose level
|
||||
dose_vals = set()
|
||||
for name, pat_dose in sd_table:
|
||||
pat = library[name]
|
||||
for shape in pat.shapes:
|
||||
dose_vals.add(shape.dose * pat_dose)
|
||||
|
||||
if len(dose_vals) > 256:
|
||||
raise PatternError('Too many dose values: {}, maximum 256 when using dtypes.'.format(len(dose_vals)))
|
||||
|
||||
dose_vals_list = list(dose_vals)
|
||||
|
||||
# Create a new pattern for each non-1-dose entry in the dose table
|
||||
# and update the shapes to reflect their new dose
|
||||
new_names = {} # {(old name, dose): new name} mapping
|
||||
new_lib = {} # {new_name: new_pattern} mapping
|
||||
for name, pat_dose in sd_table:
|
||||
mangled_name = mangle_name(name, pat_dose)
|
||||
new_names[(name, pat_dose)] = mangled_name
|
||||
|
||||
old_pat = library[name]
|
||||
|
||||
if pat_dose == 1:
|
||||
new_lib[mangled_name] = old_pat
|
||||
continue
|
||||
|
||||
pat = old_pat.deepcopy()
|
||||
|
||||
if len(mangled_name) == 0:
|
||||
raise PatternError(f'Zero-length name after mangle, originally "{name}"')
|
||||
|
||||
for shape in pat.shapes:
|
||||
data_type = dose_vals_list.index(shape.dose * pat_dose)
|
||||
if isinstance(shape.layer, int):
|
||||
shape.layer = (shape.layer, data_type)
|
||||
elif isinstance(shape.layer, tuple):
|
||||
shape.layer = (shape.layer[0], data_type)
|
||||
else:
|
||||
raise PatternError(f'Invalid layer for gdsii: {shape.layer}')
|
||||
|
||||
new_lib[mangled_name] = pat
|
||||
|
||||
return new_lib, dose_vals_list
|
||||
|
||||
|
||||
def is_gzipped(path: pathlib.Path) -> bool:
|
||||
with open(path, 'rb') as stream:
|
||||
magic_bytes = stream.read(2)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue