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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user