lots of fixes to get test_rep running

This commit is contained in:
Jan Petykiewicz 2023-01-24 12:45:44 -08:00 committed by jan
parent 92f7fce6ff
commit b75c8de0c4
11 changed files with 199 additions and 147 deletions

View File

@ -1,103 +1,135 @@
from pprint import pprint
from pathlib import Path
import numpy import numpy
from numpy import pi from numpy import pi
import masque import masque
import masque.file.gdsii from masque import Pattern, Ref, Arc, WrapLibrary
import masque.file.klamath
import masque.file.dxf
import masque.file.oasis
from masque import shapes, Pattern, SubPattern
from masque.repetition import Grid from masque.repetition import Grid
from masque.file import gdsii, dxf, oasis
from pprint import pprint
def main(): def main():
pat = masque.Pattern(name='ellip_grating') lib = WrapLibrary()
cell_name = 'ellip_grating'
pat = masque.Pattern()
for rmin in numpy.arange(10, 15, 0.5): for rmin in numpy.arange(10, 15, 0.5):
pat.shapes.append(shapes.Arc( pat.shapes.append(Arc(
radii=(rmin, rmin), radii=(rmin, rmin),
width=0.1, width=0.1,
angles=(0*-numpy.pi/4, numpy.pi/4), angles=(0 * -pi/4, pi/4),
annotations={'1': ['blah']}, annotations={'1': ['blah']},
)) ))
pat.scale_by(1000) pat.scale_by(1000)
# pat.visualize() # pat.visualize()
pat2 = pat.copy() lib[cell_name] = pat
pat2.name = 'grating2' print(f'\nAdded {cell_name}:')
pat3 = Pattern('sref_test')
pat3.subpatterns = [
SubPattern(pat, offset=(1e5, 3e5), annotations={'4': ['Hello I am the base subpattern']}),
SubPattern(pat, offset=(2e5, 3e5), rotation=pi/3),
SubPattern(pat, offset=(3e5, 3e5), rotation=pi/2),
SubPattern(pat, offset=(4e5, 3e5), rotation=pi),
SubPattern(pat, offset=(5e5, 3e5), rotation=3*pi/2),
SubPattern(pat, mirrored=(True, False), offset=(1e5, 4e5)),
SubPattern(pat, mirrored=(True, False), offset=(2e5, 4e5), rotation=pi/3),
SubPattern(pat, mirrored=(True, False), offset=(3e5, 4e5), rotation=pi/2),
SubPattern(pat, mirrored=(True, False), offset=(4e5, 4e5), rotation=pi),
SubPattern(pat, mirrored=(True, False), offset=(5e5, 4e5), rotation=3*pi/2),
SubPattern(pat, mirrored=(False, True), offset=(1e5, 5e5)),
SubPattern(pat, mirrored=(False, True), offset=(2e5, 5e5), rotation=pi/3),
SubPattern(pat, mirrored=(False, True), offset=(3e5, 5e5), rotation=pi/2),
SubPattern(pat, mirrored=(False, True), offset=(4e5, 5e5), rotation=pi),
SubPattern(pat, mirrored=(False, True), offset=(5e5, 5e5), rotation=3*pi/2),
SubPattern(pat, mirrored=(True, True), offset=(1e5, 6e5)),
SubPattern(pat, mirrored=(True, True), offset=(2e5, 6e5), rotation=pi/3),
SubPattern(pat, mirrored=(True, True), offset=(3e5, 6e5), rotation=pi/2),
SubPattern(pat, mirrored=(True, True), offset=(4e5, 6e5), rotation=pi),
SubPattern(pat, mirrored=(True, True), offset=(5e5, 6e5), rotation=3*pi/2),
]
pprint(pat3)
pprint(pat3.subpatterns)
pprint(pat.shapes) pprint(pat.shapes)
rep = Grid(a_vector=[1e4, 0], new_name = lib.get_name(cell_name)
b_vector=[0, 1.5e4], lib[new_name] = pat.copy()
a_count=3, print(f'\nAdded a copy of {cell_name} as {new_name}')
b_count=2,)
pat4 = Pattern('aref_test') pat3 = Pattern()
pat4.subpatterns = [ pat3.refs = [
SubPattern(pat, repetition=rep, offset=(1e5, 3e5)), Ref(cell_name, offset=(1e5, 3e5), annotations={'4': ['Hello I am the base Ref']}),
SubPattern(pat, repetition=rep, offset=(2e5, 3e5), rotation=pi/3), Ref(cell_name, offset=(2e5, 3e5), rotation=pi/3),
SubPattern(pat, repetition=rep, offset=(3e5, 3e5), rotation=pi/2), Ref(cell_name, offset=(3e5, 3e5), rotation=pi/2),
SubPattern(pat, repetition=rep, offset=(4e5, 3e5), rotation=pi), Ref(cell_name, offset=(4e5, 3e5), rotation=pi),
SubPattern(pat, repetition=rep, offset=(5e5, 3e5), rotation=3*pi/2), Ref(cell_name, offset=(5e5, 3e5), rotation=3*pi/2),
SubPattern(pat, repetition=rep, mirrored=(True, False), offset=(1e5, 4e5)), Ref(cell_name, mirrored=(True, False), offset=(1e5, 4e5)),
SubPattern(pat, repetition=rep, mirrored=(True, False), offset=(2e5, 4e5), rotation=pi/3), Ref(cell_name, mirrored=(True, False), offset=(2e5, 4e5), rotation=pi/3),
SubPattern(pat, repetition=rep, mirrored=(True, False), offset=(3e5, 4e5), rotation=pi/2), Ref(cell_name, mirrored=(True, False), offset=(3e5, 4e5), rotation=pi/2),
SubPattern(pat, repetition=rep, mirrored=(True, False), offset=(4e5, 4e5), rotation=pi), Ref(cell_name, mirrored=(True, False), offset=(4e5, 4e5), rotation=pi),
SubPattern(pat, repetition=rep, mirrored=(True, False), offset=(5e5, 4e5), rotation=3*pi/2), Ref(cell_name, mirrored=(True, False), offset=(5e5, 4e5), rotation=3*pi/2),
SubPattern(pat, repetition=rep, mirrored=(False, True), offset=(1e5, 5e5)), Ref(cell_name, mirrored=(False, True), offset=(1e5, 5e5)),
SubPattern(pat, repetition=rep, mirrored=(False, True), offset=(2e5, 5e5), rotation=pi/3), Ref(cell_name, mirrored=(False, True), offset=(2e5, 5e5), rotation=pi/3),
SubPattern(pat, repetition=rep, mirrored=(False, True), offset=(3e5, 5e5), rotation=pi/2), Ref(cell_name, mirrored=(False, True), offset=(3e5, 5e5), rotation=pi/2),
SubPattern(pat, repetition=rep, mirrored=(False, True), offset=(4e5, 5e5), rotation=pi), Ref(cell_name, mirrored=(False, True), offset=(4e5, 5e5), rotation=pi),
SubPattern(pat, repetition=rep, mirrored=(False, True), offset=(5e5, 5e5), rotation=3*pi/2), Ref(cell_name, mirrored=(False, True), offset=(5e5, 5e5), rotation=3*pi/2),
SubPattern(pat, repetition=rep, mirrored=(True, True), offset=(1e5, 6e5)), Ref(cell_name, mirrored=(True, True), offset=(1e5, 6e5)),
SubPattern(pat, repetition=rep, mirrored=(True, True), offset=(2e5, 6e5), rotation=pi/3), Ref(cell_name, mirrored=(True, True), offset=(2e5, 6e5), rotation=pi/3),
SubPattern(pat, repetition=rep, mirrored=(True, True), offset=(3e5, 6e5), rotation=pi/2), Ref(cell_name, mirrored=(True, True), offset=(3e5, 6e5), rotation=pi/2),
SubPattern(pat, repetition=rep, mirrored=(True, True), offset=(4e5, 6e5), rotation=pi), Ref(cell_name, mirrored=(True, True), offset=(4e5, 6e5), rotation=pi),
SubPattern(pat, repetition=rep, mirrored=(True, True), offset=(5e5, 6e5), rotation=3*pi/2), Ref(cell_name, mirrored=(True, True), offset=(5e5, 6e5), rotation=3*pi/2),
] ]
folder = 'layouts/' lib['sref_test'] = pat3
masque.file.klamath.writefile((pat, pat2, pat3, pat4), folder + 'rep.gds.gz', 1e-9, 1e-3) print('\nAdded sref_test:')
pprint(pat3)
pprint(pat3.refs)
cells = list(masque.file.klamath.readfile(folder + 'rep.gds.gz')[0].values()) rep = Grid(
masque.file.klamath.writefile(cells, folder + 'rerep.gds.gz', 1e-9, 1e-3) a_vector=[1e4, 0],
b_vector=[0, 1.5e4],
a_count=3,
b_count=2,
)
pat4 = Pattern()
pat4.refs = [
Ref(cell_name, repetition=rep, offset=(1e5, 3e5)),
Ref(cell_name, repetition=rep, offset=(2e5, 3e5), rotation=pi/3),
Ref(cell_name, repetition=rep, offset=(3e5, 3e5), rotation=pi/2),
Ref(cell_name, repetition=rep, offset=(4e5, 3e5), rotation=pi),
Ref(cell_name, repetition=rep, offset=(5e5, 3e5), rotation=3*pi/2),
Ref(cell_name, repetition=rep, mirrored=(True, False), offset=(1e5, 4e5)),
Ref(cell_name, repetition=rep, mirrored=(True, False), offset=(2e5, 4e5), rotation=pi/3),
Ref(cell_name, repetition=rep, mirrored=(True, False), offset=(3e5, 4e5), rotation=pi/2),
Ref(cell_name, repetition=rep, mirrored=(True, False), offset=(4e5, 4e5), rotation=pi),
Ref(cell_name, repetition=rep, mirrored=(True, False), offset=(5e5, 4e5), rotation=3*pi/2),
Ref(cell_name, repetition=rep, mirrored=(False, True), offset=(1e5, 5e5)),
Ref(cell_name, repetition=rep, mirrored=(False, True), offset=(2e5, 5e5), rotation=pi/3),
Ref(cell_name, repetition=rep, mirrored=(False, True), offset=(3e5, 5e5), rotation=pi/2),
Ref(cell_name, repetition=rep, mirrored=(False, True), offset=(4e5, 5e5), rotation=pi),
Ref(cell_name, repetition=rep, mirrored=(False, True), offset=(5e5, 5e5), rotation=3*pi/2),
Ref(cell_name, repetition=rep, mirrored=(True, True), offset=(1e5, 6e5)),
Ref(cell_name, repetition=rep, mirrored=(True, True), offset=(2e5, 6e5), rotation=pi/3),
Ref(cell_name, repetition=rep, mirrored=(True, True), offset=(3e5, 6e5), rotation=pi/2),
Ref(cell_name, repetition=rep, mirrored=(True, True), offset=(4e5, 6e5), rotation=pi),
Ref(cell_name, repetition=rep, mirrored=(True, True), offset=(5e5, 6e5), rotation=3*pi/2),
]
masque.file.dxf.writefile(pat4, folder + 'rep.dxf.gz') lib['aref_test'] = pat4
dxf, info = masque.file.dxf.readfile(folder + 'rep.dxf.gz') print('\nAdded aref_test')
masque.file.dxf.writefile(dxf, folder + 'rerep.dxf.gz')
folder = Path('./layouts/')
print(f'...writing files to {folder}...')
gds1 = folder / 'rep.gds.gz'
gds2 = folder / 'rerep.gds.gz'
print(f'Initial write to {gds1}')
gdsii.writefile(lib, gds1, 1e-9, 1e-3)
print(f'Read back and rewrite to {gds2}')
readback_lib, _info = gdsii.readfile(gds1)
gdsii.writefile(readback_lib, gds2, 1e-9, 1e-3)
dxf1 = folder / 'rep.dxf.gz'
dxf2 = folder / 'rerep.dxf.gz'
print(f'Write aref_test to {dxf1}')
dxf.writefile(lib, 'aref_test', dxf1)
print(f'Read back and rewrite to {dxf2}')
dxf_lib, _info = dxf.readfile(dxf1)
print(WrapLibrary(dxf_lib))
dxf.writefile(dxf_lib, 'Model', dxf2)
layer_map = {'base': (0,0), 'mylabel': (1,2)} layer_map = {'base': (0,0), 'mylabel': (1,2)}
masque.file.oasis.writefile((pat, pat2, pat3, pat4), folder + 'rep.oas.gz', 1000, layer_map=layer_map) oas1 = folder / 'rep.oas'
oas, info = masque.file.oasis.readfile(folder + 'rep.oas.gz') oas2 = folder / 'rerep.oas'
masque.file.oasis.writefile(list(oas.values()), folder + 'rerep.oas.gz', 1000, layer_map=layer_map) print(f'Write lib to {oas1}')
print(info) oasis.writefile(lib, oas1, 1000, layer_map=layer_map)
print(f'Read back and rewrite to {oas2}')
oas_lib, oas_info = oasis.readfile(oas1)
oasis.writefile(oas_lib, oas2, 1000, layer_map=layer_map)
print('OASIS info:')
pprint(oas_info)
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -35,7 +35,9 @@ from .pattern import Pattern
from .utils import layer_t, annotations_t from .utils import layer_t, annotations_t
from .library import Library, MutableLibrary, WrapROLibrary, WrapLibrary, LazyLibrary, AbstractView from .library import Library, MutableLibrary, WrapROLibrary, WrapLibrary, LazyLibrary, AbstractView
from .ports import Port, PortList from .ports import Port, PortList
from .builder import Builder, Abstract, Tool from .abstract import Abstract
from .builder import Builder, Tool
__author__ = 'Jan Petykiewicz' __author__ = 'Jan Petykiewicz'

View File

@ -3,13 +3,12 @@ from typing import MutableMapping, TYPE_CHECKING
import copy import copy
import logging import logging
from ..pattern import Pattern from .pattern import Pattern
from ..library import MutableLibrary from .ports import PortList, Port
from ..ports import PortList, Port
from .tools import Tool
if TYPE_CHECKING: if TYPE_CHECKING:
from .builder import Builder from .builder import Builder, Tool
from .library import MutableLibrary
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -34,8 +33,8 @@ class Abstract(PortList):
def build( def build(
self, self,
library: MutableLibrary, library: 'MutableLibrary',
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None, tools: Union[None, 'Tool', MutableMapping[Optional[str], 'Tool']] = None,
) -> 'Builder': ) -> 'Builder':
""" """
Begin building a new device around an instance of the current device Begin building a new device around an instance of the current device

View File

@ -1,4 +1,3 @@
from .builder import Builder from .builder import Builder
from .abstract import Abstract
from .utils import ell from .utils import ell
from .tools import Tool from .tools import Tool

View File

@ -12,9 +12,9 @@ from ..ref import Ref
from ..library import MutableLibrary from ..library import MutableLibrary
from ..error import PortError, BuildError from ..error import PortError, BuildError
from ..ports import PortList, Port from ..ports import PortList, Port
from ..abstract import Abstract
from .tools import Tool from .tools import Tool
from .utils import ell from .utils import ell
from .abstract import Abstract
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)

View File

@ -14,27 +14,29 @@ import pathlib
import gzip import gzip
import numpy import numpy
import ezdxf # type: ignore import ezdxf
from .. import Pattern, Ref, PatternError, Label, Shape from .. import Pattern, Ref, PatternError, Label
from ..shapes import Polygon, Path from ..library import Library, WrapROLibrary
from ..shapes import Shape, Polygon, Path
from ..repetition import Grid from ..repetition import Grid
from ..utils import rotation_matrix_2d, layer_t from ..utils import rotation_matrix_2d, layer_t
from .utils import is_gzipped
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logger.warning('DXF support is experimental and only slightly tested!') logger.warning('DXF support is experimental!')
DEFAULT_LAYER = 'DEFAULT' DEFAULT_LAYER = 'DEFAULT'
def write( def write(
library: Mapping[str, Pattern], # TODO could allow library=None for flat DXF
top_name: str, top_name: str,
library: Mapping[str, Pattern], stream: TextIO,
stream: io.TextIOBase,
*, *,
dxf_version='AC1024', dxf_version='AC1024',
) -> None: ) -> None:
@ -65,17 +67,23 @@ def write(
array with rotated instances must be manhattan _after_ having a compensating rotation applied. array with rotated instances must be manhattan _after_ having a compensating rotation applied.
Args: Args:
top_name: Name of the top-level pattern to write.
library: A {name: Pattern} mapping of patterns. Only `top_name` and patterns referenced library: A {name: Pattern} mapping of patterns. Only `top_name` and patterns referenced
by it are written. by it are written.
top_name: Name of the top-level pattern to write.
stream: Stream object to write to. stream: Stream object to write to.
disambiguate_func: Function which takes a list of patterns and alters them disambiguate_func: Function which takes a list of patterns and alters them
to make their names valid and unique. Default is `disambiguate_pattern_names`. to make their names valid and unique. Default is `disambiguate_pattern_names`.
WARNING: No additional error checking is performed on the results. WARNING: No additional error checking is performed on the results.
""" """
#TODO consider supporting DXF arcs? #TODO consider supporting DXF arcs?
if not isinstance(library, Library):
if isinstance(library, dict):
library = WrapROLibrary(library)
else:
library = WrapROLibrary(dict(library))
pattern = library[top_name] pattern = library[top_name]
subtree = library.subtree(top_name)
# Create library # Create library
lib = ezdxf.new(dxf_version, setup=True) lib = ezdxf.new(dxf_version, setup=True)
@ -85,8 +93,11 @@ def write(
_mrefs_to_drefs(msp, pattern.refs) _mrefs_to_drefs(msp, pattern.refs)
# Now create a block for each referenced pattern, and add in any shapes # Now create a block for each referenced pattern, and add in any shapes
for name, pat in library.items(): for name, pat in subtree.items():
assert pat is not None assert pat is not None
if name == top_name:
continue
block = lib.blocks.new(name=name) block = lib.blocks.new(name=name)
_shapes_to_elements(block, pat.shapes) _shapes_to_elements(block, pat.shapes)
@ -97,8 +108,8 @@ def write(
def writefile( def writefile(
top_name: str,
library: Mapping[str, Pattern], library: Mapping[str, Pattern],
top_name: str,
filename: Union[str, pathlib.Path], filename: Union[str, pathlib.Path],
*args, *args,
**kwargs, **kwargs,
@ -109,9 +120,9 @@ def writefile(
Will automatically compress the file if it has a .gz suffix. Will automatically compress the file if it has a .gz suffix.
Args: Args:
top_name: Name of the top-level pattern to write.
library: A {name: Pattern} mapping of patterns. Only `top_name` and patterns referenced library: A {name: Pattern} mapping of patterns. Only `top_name` and patterns referenced
by it are written. by it are written.
top_name: Name of the top-level pattern to write.
filename: Filename to save to. filename: Filename to save to.
*args: passed to `dxf.write` *args: passed to `dxf.write`
**kwargs: passed to `dxf.write` **kwargs: passed to `dxf.write`
@ -181,19 +192,19 @@ def read(
lib = ezdxf.read(stream) lib = ezdxf.read(stream)
msp = lib.modelspace() msp = lib.modelspace()
npat = _read_block(msp, clean_vertices) npat = _read_block(msp)
patterns_dict = dict( patterns_dict = dict(
[npat] + [_read_block(bb, clean_vertices) for bb in lib.blocks if bb.name != '*Model_Space'] [npat] + [_read_block(bb) for bb in lib.blocks if bb.name != '*Model_Space']
) )
library_info = { library_info = dict(
'layers': [ll.dxfattribs() for ll in lib.layers] layers=[ll.dxfattribs() for ll in lib.layers],
} )
return patterns_dict, library_info return patterns_dict, library_info
def _read_block(block, clean_vertices: bool) -> Tuple[str, Pattern]: def _read_block(block) -> Tuple[str, Pattern]:
name = block.name name = block.name
pat = Pattern() pat = Pattern()
for element in block: for element in block:
@ -225,18 +236,13 @@ def _read_block(block, clean_vertices: bool) -> Tuple[str, Pattern]:
else: else:
shape = Path(layer=layer, width=width, vertices=points[:, :2]) shape = Path(layer=layer, width=width, vertices=points[:, :2])
if clean_vertices:
try:
shape.clean_vertices()
except PatternError:
continue
pat.shapes.append(shape) pat.shapes.append(shape)
elif eltype in ('TEXT',): elif eltype in ('TEXT',):
args = {'offset': numpy.array(element.get_pos()[1])[:2], args = dict(
'layer': element.dxfattribs().get('layer', DEFAULT_LAYER), offset=numpy.array(element.get_pos()[1])[:2],
} layer=element.dxfattribs().get('layer', DEFAULT_LAYER),
)
string = element.dxfattribs().get('text', '') string = element.dxfattribs().get('text', '')
# height = element.dxfattribs().get('height', 0) # height = element.dxfattribs().get('height', 0)
# if height != 0: # if height != 0:
@ -257,20 +263,21 @@ def _read_block(block, clean_vertices: bool) -> Tuple[str, Pattern]:
offset = numpy.array(attr.get('insert', (0, 0, 0)))[:2] offset = numpy.array(attr.get('insert', (0, 0, 0)))[:2]
args = { args = dict(
'target': (attr.get('name', None),), target=attr.get('name', None),
'offset': offset, offset=offset,
'scale': scale, scale=scale,
'mirrored': mirrored, mirrored=mirrored,
'rotation': rotation, rotation=rotation,
'pattern': None, )
}
if 'column_count' in attr: if 'column_count' in attr:
args['repetition'] = Grid(a_vector=(attr['column_spacing'], 0), args['repetition'] = Grid(
b_vector=(0, attr['row_spacing']), a_vector=(attr['column_spacing'], 0),
a_count=attr['column_count'], b_vector=(0, attr['row_spacing']),
b_count=attr['row_count']) a_count=attr['column_count'],
b_count=attr['row_count'],
)
pat.ref(**args) pat.ref(**args)
else: else:
logger.warning(f'Ignoring DXF element {element.dxftype()} (not implemented).') logger.warning(f'Ignoring DXF element {element.dxftype()} (not implemented).')
@ -286,12 +293,12 @@ def _mrefs_to_drefs(
continue continue
encoded_name = ref.target encoded_name = ref.target
rotation = (ref.rotation * 180 / numpy.pi) % 360 rotation = numpy.rad2deg(ref.rotation) % 360
attribs = { attribs = dict(
'xscale': ref.scale * (-1 if ref.mirrored[1] else 1), xscale=ref.scale * (-1 if ref.mirrored[1] else 1),
'yscale': ref.scale * (-1 if ref.mirrored[0] else 1), yscale=ref.scale * (-1 if ref.mirrored[0] else 1),
'rotation': rotation, rotation=rotation,
} )
rep = ref.repetition rep = ref.repetition
if rep is None: if rep is None:
@ -338,7 +345,7 @@ def _shapes_to_elements(
' Please call library.wrap_repeated_shapes() before writing to file.' ' Please call library.wrap_repeated_shapes() before writing to file.'
) )
attribs = {'layer': _mlayer2dxf(shape.layer)} attribs = dict(layer=_mlayer2dxf(shape.layer))
for polygon in shape.to_polygons(): for polygon in shape.to_polygons():
xy_open = polygon.vertices + polygon.offset xy_open = polygon.vertices + polygon.offset
xy_closed = numpy.vstack((xy_open, xy_open[0, :])) xy_closed = numpy.vstack((xy_open, xy_open[0, :]))
@ -350,7 +357,7 @@ def _labels_to_texts(
labels: List[Label], labels: List[Label],
) -> None: ) -> None:
for label in labels: for label in labels:
attribs = {'layer': _mlayer2dxf(label.layer)} attribs = dict(layer=_mlayer2dxf(label.layer))
xy = label.offset xy = label.offset
block.add_text(label.string, dxfattribs=attribs).set_pos(xy, align='BOTTOM_LEFT') block.add_text(label.string, dxfattribs=attribs).set_pos(xy, align='BOTTOM_LEFT')

View File

@ -30,7 +30,7 @@ import string
from pprint import pformat from pprint import pformat
import numpy import numpy
from numpy.typing import NDArray, ArrayLike from numpy.typing import ArrayLike, NDArray
import klamath import klamath
from klamath import records from klamath import records
@ -54,7 +54,7 @@ path_cap_map = {
def rint_cast(val: ArrayLike) -> NDArray[numpy.int32]: def rint_cast(val: ArrayLike) -> NDArray[numpy.int32]:
return numpy.rint(val, dtype=numpy.int32, casting='unsafe') return numpy.rint(val).astype(numpy.int32)
def write( def write(

View File

@ -39,7 +39,7 @@ from ..utils import layer_t, normalize_mirror, annotations_t
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
logger.warning('OASIS support is experimental and mostly untested!') logger.warning('OASIS support is experimental!')
path_cap_map = { path_cap_map = {
@ -51,7 +51,7 @@ path_cap_map = {
#TODO implement more shape types? #TODO implement more shape types?
def rint_cast(val: ArrayLike) -> NDArray[numpy.int64]: def rint_cast(val: ArrayLike) -> NDArray[numpy.int64]:
return numpy.rint(val, dtype=numpy.int64, casting='unsafe') return numpy.rint(val).astype(numpy.int64)
def build( def build(

View File

@ -22,7 +22,7 @@ from .error import LibraryError, PatternError
from .utils import rotation_matrix_2d, normalize_mirror from .utils import rotation_matrix_2d, normalize_mirror
from .shapes import Shape, Polygon from .shapes import Shape, Polygon
from .label import Label from .label import Label
from .builder.abstract import Abstract from .abstract import Abstract
if TYPE_CHECKING: if TYPE_CHECKING:
from .pattern import Pattern from .pattern import Pattern
@ -37,7 +37,7 @@ ML = TypeVar('ML', bound='MutableLibrary')
LL = TypeVar('LL', bound='LazyLibrary') LL = TypeVar('LL', bound='LazyLibrary')
class Library(Mapping[str, Pattern], metaclass=ABCMeta): class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
# inherited abstract functions # inherited abstract functions
#def __getitem__(self, key: str) -> 'Pattern': #def __getitem__(self, key: str) -> 'Pattern':
#def __iter__(self) -> Iterator[str]: #def __iter__(self) -> Iterator[str]:
@ -61,7 +61,7 @@ class Library(Mapping[str, Pattern], metaclass=ABCMeta):
return Abstract(name=name, ports=self[name].ports) return Abstract(name=name, ports=self[name].ports)
def __repr__(self) -> str: def __repr__(self) -> str:
return '<Library with keys ' + repr(list(self.keys())) + '>' return '<Library with keys\n' + pformat(list(self.keys())) + '>'
def dangling_references( def dangling_references(
self, self,
@ -142,7 +142,11 @@ class Library(Mapping[str, Pattern], metaclass=ABCMeta):
Returns: Returns:
A `WrapROLibrary` containing only `tops` and the patterns they reference. A `WrapROLibrary` containing only `tops` and the patterns they reference.
""" """
if isinstance(tops, str):
tops = (tops,)
keep: Set[str] = self.referenced_patterns(tops) - set((None,)) # type: ignore keep: Set[str] = self.referenced_patterns(tops) - set((None,)) # type: ignore
keep |= set(tops)
filtered = {kk: vv for kk, vv in self.items() if kk in keep} filtered = {kk: vv for kk, vv in self.items() if kk in keep}
new = WrapROLibrary(filtered) new = WrapROLibrary(filtered)
@ -405,7 +409,7 @@ class Library(Mapping[str, Pattern], metaclass=ABCMeta):
VVV = TypeVar('VVV') VVV = TypeVar('VVV')
class MutableLibrary(Generic[VVV], Library, metaclass=ABCMeta): class MutableLibrary(Library, Generic[VVV], metaclass=ABCMeta):
# inherited abstract functions # inherited abstract functions
#def __getitem__(self, key: str) -> 'Pattern': #def __getitem__(self, key: str) -> 'Pattern':
#def __iter__(self) -> Iterator[str]: #def __iter__(self) -> Iterator[str]:
@ -627,7 +631,11 @@ class MutableLibrary(Generic[VVV], Library, metaclass=ABCMeta):
Returns: Returns:
A `Library` containing only `tops` and the patterns they reference. A `Library` containing only `tops` and the patterns they reference.
""" """
if isinstance(tops, str):
tops = (tops,)
keep: Set[str] = self.referenced_patterns(tops) - set((None,)) # type: ignore keep: Set[str] = self.referenced_patterns(tops) - set((None,)) # type: ignore
keep |= set(tops)
new = type(self)() new = type(self)()
for key in keep: for key in keep:
@ -654,7 +662,7 @@ class WrapROLibrary(Library):
return len(self.mapping) return len(self.mapping)
def __repr__(self) -> str: def __repr__(self) -> str:
return f'<WrapROLibrary ({type(self.mapping)}) with keys ' + repr(list(self.keys())) + '>' return f'<WrapROLibrary ({type(self.mapping)}) with keys\n' + pformat(list(self.keys())) + '>'
class WrapLibrary(MutableLibrary): class WrapLibrary(MutableLibrary):
@ -662,9 +670,12 @@ class WrapLibrary(MutableLibrary):
def __init__( def __init__(
self, self,
mapping: MutableMapping[str, 'Pattern'], mapping: Optional[MutableMapping[str, 'Pattern']] = None,
) -> None: ) -> None:
self.mapping = mapping if mapping is None:
self.mapping = {}
else:
self.mapping = mapping
def __getitem__(self, key: str) -> 'Pattern': def __getitem__(self, key: str) -> 'Pattern':
return self.mapping[key] return self.mapping[key]
@ -688,7 +699,7 @@ class WrapLibrary(MutableLibrary):
self[key] = other[key] self[key] = other[key]
def __repr__(self) -> str: def __repr__(self) -> str:
return f'<WrapLibrary ({type(self.mapping)}) with keys ' + repr(list(self.keys())) + '>' return f'<WrapLibrary ({type(self.mapping)}) with keys\n' + pformat(list(self.keys())) + '>'
class LazyLibrary(MutableLibrary): class LazyLibrary(MutableLibrary):
@ -745,7 +756,7 @@ class LazyLibrary(MutableLibrary):
self._set(key, other[key]) self._set(key, other[key])
def __repr__(self) -> str: def __repr__(self) -> str:
return '<LazyLibrary with keys ' + repr(list(self.keys())) + '>' return '<LazyLibrary with keys\n' + pformat(list(self.keys())) + '>'
def precache(self: LL) -> LL: def precache(self: LL) -> LL:
""" """

View File

@ -88,7 +88,9 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
self.refs = list(refs) self.refs = list(refs)
if ports is not None: if ports is not None:
ports = dict(copy.deepcopy(ports)) self.ports = dict(copy.deepcopy(ports))
else:
self.ports = {}
self.annotations = annotations if annotations is not None else {} self.annotations = annotations if annotations is not None else {}

View File

@ -178,8 +178,8 @@ class Ref(
def get_bounds( def get_bounds(
self, self,
*, *,
pattern: Optional[Pattern] = None, pattern: Optional['Pattern'] = None,
library: Optional[Mapping[str, Pattern]] = None, library: Optional[Mapping[str, 'Pattern']] = None,
) -> Optional[NDArray[numpy.float64]]: ) -> Optional[NDArray[numpy.float64]]:
""" """
Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the Return a `numpy.ndarray` containing `[[x_min, y_min], [x_max, y_max]]`, corresponding to the