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.structure
|
||||||
import gdsii.elements
|
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 .. import Pattern, SubPattern, GridRepetition, PatternError, Label, Shape, subpattern_t
|
||||||
from ..shapes import Polygon, Path
|
from ..shapes import Polygon, Path
|
||||||
from ..utils import rotation_matrix_2d, get_bit, set_bit, vector2, is_scalar, layer_t
|
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
|
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],
|
def readfile(filename: Union[str, pathlib.Path],
|
||||||
*args,
|
*args,
|
||||||
**kwargs,
|
**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)`.
|
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`.
|
If `True`, set the layer to `gds_layer` and the dose to `gds_datatype`.
|
||||||
Default `False`.
|
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.
|
clean_vertices: If `True`, remove any redundant vertices when loading polygons.
|
||||||
The cleaning process removes any polygons with zero area or <3 vertices.
|
The cleaning process removes any polygons with zero area or <3 vertices.
|
||||||
Default `True`.
|
Default `True`.
|
||||||
@ -325,14 +243,9 @@ def read(stream: io.BufferedIOBase,
|
|||||||
# Switch based on element type:
|
# Switch based on element type:
|
||||||
if isinstance(element, gdsii.elements.Boundary):
|
if isinstance(element, gdsii.elements.Boundary):
|
||||||
args = {'vertices': element.xy[:-1],
|
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)
|
poly = Polygon(**args)
|
||||||
|
|
||||||
if clean_vertices:
|
if clean_vertices:
|
||||||
@ -350,6 +263,7 @@ def read(stream: io.BufferedIOBase,
|
|||||||
raise PatternError('Unrecognized path type: {}'.format(element.path_type))
|
raise PatternError('Unrecognized path type: {}'.format(element.path_type))
|
||||||
|
|
||||||
args = {'vertices': element.xy,
|
args = {'vertices': element.xy,
|
||||||
|
'layer': (element.layer, element.data_type),
|
||||||
'width': element.width if element.width is not None else 0.0,
|
'width': element.width if element.width is not None else 0.0,
|
||||||
'cap': cap,
|
'cap': cap,
|
||||||
}
|
}
|
||||||
@ -361,12 +275,6 @@ def read(stream: io.BufferedIOBase,
|
|||||||
if element.end_extn is not None:
|
if element.end_extn is not None:
|
||||||
args['cap_extensions'][1] = element.end_extn
|
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)
|
path = Path(**args)
|
||||||
|
|
||||||
if clean_vertices:
|
if clean_vertices:
|
||||||
@ -389,6 +297,10 @@ def read(stream: io.BufferedIOBase,
|
|||||||
elif isinstance(element, gdsii.elements.ARef):
|
elif isinstance(element, gdsii.elements.ARef):
|
||||||
pat.subpatterns.append(_aref_to_gridrep(element))
|
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)
|
patterns.append(pat)
|
||||||
|
|
||||||
# Create a dict of {pattern.name: pattern, ...}, then fix up all subpattern.pattern entries
|
# 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
|
Helper functions for file reading and writing
|
||||||
"""
|
"""
|
||||||
import re
|
|
||||||
from typing import Set, Tuple, List
|
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:
|
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)
|
subpat_dose_table = make_dose_table([subpat.pattern], subpat.dose * dose_multiplier)
|
||||||
dose_table = dose_table.union(subpat_dose_table)
|
dose_table = dose_table.union(subpat_dose_table)
|
||||||
return 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