General overhaul of gdsii read/write functions
- read() and write() now take streams instead of filenames - readfile() and writefile() were added to handle filenames and can detect and handle '.gz' suffixed/compressed files. - write_dose2dtype() and and read_dtype2dose() were removed in favor of read(use_dtype_as_dose=True) and dose2dtype()
This commit is contained in:
parent
d6d26b4e46
commit
485a7bc29d
@ -8,11 +8,14 @@ import gdsii.elements
|
|||||||
|
|
||||||
from typing import List, Any, Dict, Tuple
|
from typing import List, Any, Dict, Tuple
|
||||||
import re
|
import re
|
||||||
|
import io
|
||||||
import copy
|
import copy
|
||||||
import numpy
|
import numpy
|
||||||
import base64
|
import base64
|
||||||
import struct
|
import struct
|
||||||
import logging
|
import logging
|
||||||
|
import pathlib
|
||||||
|
import gzip
|
||||||
|
|
||||||
from .utils import mangle_name, make_dose_table
|
from .utils import mangle_name, make_dose_table
|
||||||
from .. import Pattern, SubPattern, GridRepetition, PatternError, Label, Shape
|
from .. import Pattern, SubPattern, GridRepetition, PatternError, Label, Shape
|
||||||
@ -35,7 +38,7 @@ path_cap_map = {0: Path.Cap.Flush,
|
|||||||
|
|
||||||
|
|
||||||
def write(patterns: Pattern or List[Pattern],
|
def write(patterns: Pattern or List[Pattern],
|
||||||
filename: str,
|
stream: io.BufferedIOBase,
|
||||||
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',
|
||||||
@ -58,8 +61,8 @@ def write(patterns: Pattern or List[Pattern],
|
|||||||
If you want pattern polygonized with non-default arguments, just call pattern.polygonize()
|
If you want pattern polygonized with non-default arguments, just call pattern.polygonize()
|
||||||
prior to calling this function.
|
prior to calling this function.
|
||||||
|
|
||||||
:param patterns: A Pattern or list of patterns to write to file. Modified by this function.
|
:param patterns: A Pattern or list of patterns to write to file.
|
||||||
:param filename: Filename to write to.
|
:param file: Filename or stream object to write to.
|
||||||
:param meters_per_unit: Written into the GDSII file, meters per (database) length unit.
|
:param meters_per_unit: Written into the GDSII file, meters per (database) length unit.
|
||||||
All distances are assumed to be an integer multiple of this unit, and are stored as such.
|
All distances are assumed to be an integer multiple of this unit, and are stored as such.
|
||||||
:param logical_units_per_unit: Written into the GDSII file. Allows the GDSII to specify a
|
:param logical_units_per_unit: Written into the GDSII file. Allows the GDSII to specify a
|
||||||
@ -99,50 +102,29 @@ def write(patterns: Pattern or List[Pattern],
|
|||||||
structure += _labels_to_texts(pat.labels)
|
structure += _labels_to_texts(pat.labels)
|
||||||
structure += _subpatterns_to_refs(pat.subpatterns)
|
structure += _subpatterns_to_refs(pat.subpatterns)
|
||||||
|
|
||||||
with open(filename, mode='wb') as stream:
|
lib.save(stream)
|
||||||
lib.save(stream)
|
return
|
||||||
|
|
||||||
|
|
||||||
def write_dose2dtype(patterns: Pattern or List[Pattern],
|
def writefile(patterns: List[Pattern] or Pattern,
|
||||||
filename: str,
|
filename: str or pathlib.Path,
|
||||||
meters_per_unit: float,
|
*args,
|
||||||
*args,
|
**kwargs,
|
||||||
**kwargs,
|
):
|
||||||
) -> List[float]:
|
|
||||||
"""
|
"""
|
||||||
Write a Pattern or list of patterns to a GDSII file, by first calling
|
Wrapper for gdsii.write() that takes a filename or path instead of a stream.
|
||||||
.polygonize() to change the shapes into polygons, and then writing patterns
|
|
||||||
as GDSII structures, polygons as boundary elements, and subpatterns as structure
|
|
||||||
references (sref).
|
|
||||||
|
|
||||||
For each shape,
|
Will automatically compress the file if it has a .gz suffix.
|
||||||
layer is chosen to be equal to shape.layer if it is an int,
|
|
||||||
or shape.layer[0] if it is a tuple
|
|
||||||
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 Pattern(s).
|
|
||||||
|
|
||||||
It is often a good idea to run pattern.subpatternize() prior to calling this function,
|
|
||||||
especially if calling .polygonize() will result in very many vertices.
|
|
||||||
|
|
||||||
If you want pattern polygonized with non-default arguments, just call pattern.polygonize()
|
|
||||||
prior to calling this function.
|
|
||||||
|
|
||||||
:param patterns: A Pattern or list of patterns to write to file. Modified by this function.
|
|
||||||
:param filename: Filename to write to.
|
|
||||||
:param meters_per_unit: Written into the GDSII file, meters per (database) length unit.
|
|
||||||
All distances are assumed to be an integer multiple of this unit, and are stored as such.
|
|
||||||
:param args: passed to masque.file.gdsii.write().
|
|
||||||
:param kwargs: passed to masque.file.gdsii.write().
|
|
||||||
:returns: A list of doses, providing a mapping between datatype (int, list index)
|
|
||||||
and dose (float, list entry).
|
|
||||||
"""
|
"""
|
||||||
patterns, dose_vals = dose2dtype(patterns)
|
path = pathlib.Path(filename)
|
||||||
write(patterns, filename, meters_per_unit, *args, **kwargs)
|
if path.suffix == 'gz':
|
||||||
return dose_vals
|
open_func = gzip.open
|
||||||
|
else:
|
||||||
|
open_func = open
|
||||||
|
|
||||||
|
with open_func(path, mode='wb') as stream:
|
||||||
|
results = write(patterns, stream, *args, **kwargs)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
def dose2dtype(patterns: Pattern or List[Pattern],
|
def dose2dtype(patterns: Pattern or List[Pattern],
|
||||||
@ -219,14 +201,27 @@ def dose2dtype(patterns: Pattern or List[Pattern],
|
|||||||
return patterns, list(dose_vals)
|
return patterns, list(dose_vals)
|
||||||
|
|
||||||
|
|
||||||
def read_dtype2dose(filename: str) -> (List[Pattern], Dict[str, Any]):
|
def readfile(filename: str or pathlib.Path,
|
||||||
|
*args,
|
||||||
|
**kwargs,
|
||||||
|
) -> (Dict[str, Pattern], Dict[str, Any]):
|
||||||
"""
|
"""
|
||||||
Alias for read(filename, use_dtype_as_dose=True)
|
Wrapper for gdsii.read() that takes a filename or path instead of a stream.
|
||||||
|
|
||||||
|
Tries to autodetermine file type based on suffixes
|
||||||
"""
|
"""
|
||||||
return read(filename, use_dtype_as_dose=True)
|
path = pathlib.Path(filename)
|
||||||
|
if path.suffix == 'gz':
|
||||||
|
open_func = gzip.open
|
||||||
|
else:
|
||||||
|
open_func = open
|
||||||
|
|
||||||
|
with open_func(path, mode='rb') as stream:
|
||||||
|
results = read(stream, *args, **kwargs)
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
def read(filename: str,
|
def read(stream: io.BufferedIOBase,
|
||||||
use_dtype_as_dose: bool = False,
|
use_dtype_as_dose: bool = False,
|
||||||
clean_vertices: bool = True,
|
clean_vertices: bool = True,
|
||||||
) -> (Dict[str, Pattern], Dict[str, Any]):
|
) -> (Dict[str, Pattern], Dict[str, Any]):
|
||||||
@ -251,8 +246,7 @@ def read(filename: str,
|
|||||||
:return: Tuple: (Dict of pattern_name:Patterns generated from GDSII structures, Dict of GDSII library info)
|
:return: Tuple: (Dict of pattern_name:Patterns generated from GDSII structures, Dict of GDSII library info)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with open(filename, mode='rb') as stream:
|
lib = gdsii.library.Library.load(stream)
|
||||||
lib = gdsii.library.Library.load(stream)
|
|
||||||
|
|
||||||
library_info = {'name': lib.name.decode('ASCII'),
|
library_info = {'name': lib.name.decode('ASCII'),
|
||||||
'meters_per_unit': lib.physical_unit,
|
'meters_per_unit': lib.physical_unit,
|
||||||
@ -532,3 +526,4 @@ def _disambiguate_pattern_names(patterns):
|
|||||||
|
|
||||||
pat.name = encoded_name
|
pat.name = encoded_name
|
||||||
used_names.append(suffixed_name)
|
used_names.append(suffixed_name)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user