move dose2dtype() into masque.file.utils, add dtype2dose(), and add a note that use_dtype_as_dose
This commit is contained in:
parent
1b0b056bf9
commit
8082743e17
@ -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
|
||||
|
@ -1,10 +1,11 @@
|
||||
"""
|
||||
Helper functions for file reading and writing
|
||||
"""
|
||||
import re
|
||||
from typing import Set, Tuple, List
|
||||
import re
|
||||
import copy
|
||||
|
||||
from masque.pattern import Pattern
|
||||
from .. import Pattern, PatternError
|
||||
|
||||
|
||||
def mangle_name(pattern: Pattern, dose_multiplier: float=1.0) -> str:
|
||||
@ -45,3 +46,108 @@ def make_dose_table(patterns: List[Pattern], dose_multiplier: float=1.0) -> Set[
|
||||
subpat_dose_table = make_dose_table([subpat.pattern], 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(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
|
||||
|
Loading…
Reference in New Issue
Block a user