allow more freedom in pattern names (e.g. names which violate spec, longer suffixes, filter warning, etc)

This commit is contained in:
Jan Petykiewicz 2019-12-12 01:19:07 -08:00
parent 0118bf0eb3
commit 09711116a7

View File

@ -6,7 +6,7 @@ import gdsii.library
import gdsii.structure import gdsii.structure
import gdsii.elements import gdsii.elements
from typing import List, Any, Dict, Tuple from typing import List, Any, Dict, Tuple, Callable
import re import re
import io import io
import copy import copy
@ -46,7 +46,8 @@ def write(patterns: Pattern or List[Pattern],
meters_per_unit: float, meters_per_unit: float,
logical_units_per_unit: float = 1, logical_units_per_unit: float = 1,
library_name: str = 'masque-gdsii-write', library_name: str = 'masque-gdsii-write',
modify_originals: bool = False): modify_originals: bool = False,
disambiguate_func: Callable[[List[Pattern]], None] = None):
""" """
Write a Pattern or list of patterns to a GDSII file, by first calling Write a Pattern or list of patterns to a GDSII file, by first calling
.polygonize() to change the shapes into polygons, and then writing patterns .polygonize() to change the shapes into polygons, and then writing patterns
@ -77,13 +78,20 @@ def write(patterns: Pattern or List[Pattern],
:param modify_originals: If True, the original pattern is modified as part of the writing :param modify_originals: If True, the original pattern is modified as part of the writing
process. Otherwise, a copy is made. process. Otherwise, a copy is made.
Default False. Default False.
:param disambiguate_func: Function which takes a list of patterns and alters them
to make their names valid and unique. Default is `disambiguate_pattern_names`, which
attempts to adhere to the GDSII standard as well as possible.
WARNING: No additional error checking is performed on the results.
""" """
if not modify_originals:
patterns = copy.deepcopy(patterns)
if isinstance(patterns, Pattern): if isinstance(patterns, Pattern):
patterns = [patterns] patterns = [patterns]
if disambiguate_func is None:
disambiguate_func = disambiguate_pattern_names
if not modify_originals:
patterns = copy.deepcopy(patterns)
# Create library # Create library
lib = gdsii.library.Library(version=600, lib = gdsii.library.Library(version=600,
name=library_name.encode('ASCII'), name=library_name.encode('ASCII'),
@ -95,7 +103,7 @@ def write(patterns: Pattern or List[Pattern],
for pattern in patterns: for pattern in patterns:
patterns_by_id.update(pattern.referenced_patterns_by_id()) patterns_by_id.update(pattern.referenced_patterns_by_id())
_disambiguate_pattern_names(patterns_by_id.values()) disambiguate_func(patterns_by_id.values())
# 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():
@ -506,12 +514,16 @@ def _labels_to_texts(labels: List[Label]) -> List[gdsii.elements.Text]:
return texts return texts
def _disambiguate_pattern_names(patterns): def disambiguate_pattern_names(patterns,
max_name_length: int = 32,
suffix_length: int = 6,
dup_warn_filter: Callable[[str,], bool] = None, # If returns False, don't warn about this name
):
used_names = [] used_names = []
for pat in patterns: for pat in patterns:
if len(pat.name) > 32: if len(pat.name) > max_name_length:
shortened_name = pat.name[:26] shortened_name = pat.name[:max_name_length - suffix_length]
logger.warning('Pattern name "{}" is too long ({}/32 chars),\n'.format(pat.name, len(pat.name)) + logger.warning('Pattern name "{}" is too long ({}/{} chars),\n'.format(pat.name, len(pat.name), max_name_length) +
' shortening to "{}" before generating suffix'.format(shortened_name)) ' shortening to "{}" before generating suffix'.format(shortened_name))
else: else:
shortened_name = pat.name shortened_name = pat.name
@ -529,15 +541,16 @@ def _disambiguate_pattern_names(patterns):
if sanitized_name == '': if sanitized_name == '':
logger.warning('Empty pattern name saved as "{}"'.format(suffixed_name)) logger.warning('Empty pattern name saved as "{}"'.format(suffixed_name))
elif suffixed_name != sanitized_name: elif suffixed_name != sanitized_name:
logger.warning('Pattern name "{}" ({}) appears multiple times;\n renaming to "{}"'.format( if dup_warn_filter is None or dup_warn_filter(pat.name):
pat.name, sanitized_name, suffixed_name)) logger.warning('Pattern name "{}" ({}) appears multiple times;\n renaming to "{}"'.format(
pat.name, sanitized_name, suffixed_name))
encoded_name = suffixed_name.encode('ASCII') encoded_name = suffixed_name.encode('ASCII')
if len(encoded_name) == 0: if len(encoded_name) == 0:
# Should never happen since zero-length names are replaced # Should never happen since zero-length names are replaced
raise PatternError('Zero-length name after sanitize+encode,\n originally "{}"'.format(pat.name)) raise PatternError('Zero-length name after sanitize+encode,\n originally "{}"'.format(pat.name))
if len(encoded_name) > 32: if len(encoded_name) > max_name_length:
raise PatternError('Pattern name "{}" length > 32 after encode,\n originally "{}"'.format(encoded_name, pat.name)) raise PatternError('Pattern name "{}" length > {} after encode,\n originally "{}"'.format(encoded_name, max_name_length, pat.name))
pat.name = encoded_name pat.name = encoded_name
used_names.append(suffixed_name) used_names.append(suffixed_name)