move dose2dtype() into masque.file.utils, add dtype2dose(), and add a note that use_dtype_as_dose

This commit is contained in:
Jan Petykiewicz 2020-05-19 00:13:50 -07:00
commit 8082743e17
2 changed files with 117 additions and 99 deletions

View file

@ -27,7 +27,7 @@ import gdsii.library
import gdsii.structure
import gdsii.elements
from .utils import mangle_name, make_dose_table
from .utils import mangle_name, make_dose_table, dose2dtype, dtype2dose
from .. import Pattern, SubPattern, GridRepetition, PatternError, Label, Shape, subpattern_t
from ..shapes import Polygon, Path
from ..utils import rotation_matrix_2d, get_bit, set_bit, vector2, is_scalar, layer_t
@ -173,90 +173,6 @@ def writefile(patterns: Union[List[Pattern], Pattern],
return results
def dose2dtype(patterns: List[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).
"""
# Get a dict of id(pattern) -> pattern
patterns_by_id = {id(pattern): pattern for pattern in patterns}
for pattern in patterns:
for i, p in pattern.referenced_patterns_by_id().items():
patterns_by_id[i] = p
# 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
# 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 pat_id, pat_dose in sd_table:
pat = patterns_by_id[pat_id]
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_pats = {} # (id, dose) -> new_pattern mapping
for pat_id, pat_dose in sd_table:
if pat_dose == 1:
new_pats[(pat_id, pat_dose)] = patterns_by_id[pat_id]
continue
old_pat = patterns_by_id[pat_id]
pat = old_pat.copy() # keep old subpatterns
pat.shapes = copy.deepcopy(old_pat.shapes)
pat.labels = copy.deepcopy(old_pat.labels)
encoded_name = mangle_name(pat, pat_dose)
if len(encoded_name) == 0:
raise PatternError('Zero-length name after mangle+encode, originally "{}"'.format(pat.name))
pat.name = encoded_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_pats[(pat_id, pat_dose)] = pat
# Go back through all the dose-specific patterns and fix up their subpattern entries
for (pat_id, pat_dose), pat in new_pats.items():
for subpat in pat.subpatterns:
dose_mult = subpat.dose * pat_dose
subpat.pattern = new_pats[(id(subpat.pattern), dose_mult)]
return patterns, dose_vals_list
def readfile(filename: Union[str, pathlib.Path],
*args,
**kwargs,
@ -302,6 +218,8 @@ def read(stream: io.BufferedIOBase,
use_dtype_as_dose: If `False`, set each polygon's layer to `(gds_layer, gds_datatype)`.
If `True`, set the layer to `gds_layer` and the dose to `gds_datatype`.
Default `False`.
NOTE: This will be deprecated in the future in favor of
`pattern.apply(masque.file.utils.dtype2dose)`.
clean_vertices: If `True`, remove any redundant vertices when loading polygons.
The cleaning process removes any polygons with zero area or <3 vertices.
Default `True`.
@ -325,14 +243,9 @@ def read(stream: io.BufferedIOBase,
# Switch based on element type:
if isinstance(element, gdsii.elements.Boundary):
args = {'vertices': element.xy[:-1],
'layer': (element.layer, element.data_type),
}
if use_dtype_as_dose:
args['dose'] = element.data_type
args['layer'] = element.layer
else:
args['layer'] = (element.layer, element.data_type)
poly = Polygon(**args)
if clean_vertices:
@ -350,6 +263,7 @@ def read(stream: io.BufferedIOBase,
raise PatternError('Unrecognized path type: {}'.format(element.path_type))
args = {'vertices': element.xy,
'layer': (element.layer, element.data_type),
'width': element.width if element.width is not None else 0.0,
'cap': cap,
}
@ -361,12 +275,6 @@ def read(stream: io.BufferedIOBase,
if element.end_extn is not None:
args['cap_extensions'][1] = element.end_extn
if use_dtype_as_dose:
args['dose'] = element.data_type
args['layer'] = element.layer
else:
args['layer'] = (element.layer, element.data_type)
path = Path(**args)
if clean_vertices:
@ -389,6 +297,10 @@ def read(stream: io.BufferedIOBase,
elif isinstance(element, gdsii.elements.ARef):
pat.subpatterns.append(_aref_to_gridrep(element))
if use_dtype_as_dose:
logger.warning('use_dtype_as_dose will be removed in the future!')
pat = dtype2dose(pat)
patterns.append(pat)
# Create a dict of {pattern.name: pattern, ...}, then fix up all subpattern.pattern entries