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
from numpy import pi
import masque
import masque.file.gdsii
import masque.file.klamath
import masque.file.dxf
import masque.file.oasis
from masque import shapes, Pattern, SubPattern
from masque import Pattern, Ref, Arc, WrapLibrary
from masque.repetition import Grid
from masque.file import gdsii, dxf, oasis
from pprint import pprint
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):
pat.shapes.append(shapes.Arc(
pat.shapes.append(Arc(
radii=(rmin, rmin),
width=0.1,
angles=(0*-numpy.pi/4, numpy.pi/4),
angles=(0 * -pi/4, pi/4),
annotations={'1': ['blah']},
))
))
pat.scale_by(1000)
# pat.visualize()
pat2 = pat.copy()
pat2.name = 'grating2'
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)
lib[cell_name] = pat
print(f'\nAdded {cell_name}:')
pprint(pat.shapes)
rep = Grid(a_vector=[1e4, 0],
b_vector=[0, 1.5e4],
a_count=3,
b_count=2,)
pat4 = Pattern('aref_test')
pat4.subpatterns = [
SubPattern(pat, repetition=rep, offset=(1e5, 3e5)),
SubPattern(pat, repetition=rep, offset=(2e5, 3e5), rotation=pi/3),
SubPattern(pat, repetition=rep, offset=(3e5, 3e5), rotation=pi/2),
SubPattern(pat, repetition=rep, offset=(4e5, 3e5), rotation=pi),
SubPattern(pat, repetition=rep, offset=(5e5, 3e5), rotation=3*pi/2),
SubPattern(pat, repetition=rep, mirrored=(True, False), offset=(1e5, 4e5)),
SubPattern(pat, repetition=rep, mirrored=(True, False), offset=(2e5, 4e5), rotation=pi/3),
SubPattern(pat, repetition=rep, mirrored=(True, False), offset=(3e5, 4e5), rotation=pi/2),
SubPattern(pat, repetition=rep, mirrored=(True, False), offset=(4e5, 4e5), rotation=pi),
SubPattern(pat, repetition=rep, mirrored=(True, False), offset=(5e5, 4e5), rotation=3*pi/2),
SubPattern(pat, repetition=rep, mirrored=(False, True), offset=(1e5, 5e5)),
SubPattern(pat, repetition=rep, mirrored=(False, True), offset=(2e5, 5e5), rotation=pi/3),
SubPattern(pat, repetition=rep, mirrored=(False, True), offset=(3e5, 5e5), rotation=pi/2),
SubPattern(pat, repetition=rep, mirrored=(False, True), offset=(4e5, 5e5), rotation=pi),
SubPattern(pat, repetition=rep, mirrored=(False, True), offset=(5e5, 5e5), rotation=3*pi/2),
SubPattern(pat, repetition=rep, mirrored=(True, True), offset=(1e5, 6e5)),
SubPattern(pat, repetition=rep, mirrored=(True, True), offset=(2e5, 6e5), rotation=pi/3),
SubPattern(pat, repetition=rep, mirrored=(True, True), offset=(3e5, 6e5), rotation=pi/2),
SubPattern(pat, repetition=rep, mirrored=(True, True), offset=(4e5, 6e5), rotation=pi),
SubPattern(pat, repetition=rep, mirrored=(True, True), offset=(5e5, 6e5), rotation=3*pi/2),
new_name = lib.get_name(cell_name)
lib[new_name] = pat.copy()
print(f'\nAdded a copy of {cell_name} as {new_name}')
pat3 = Pattern()
pat3.refs = [
Ref(cell_name, offset=(1e5, 3e5), annotations={'4': ['Hello I am the base Ref']}),
Ref(cell_name, offset=(2e5, 3e5), rotation=pi/3),
Ref(cell_name, offset=(3e5, 3e5), rotation=pi/2),
Ref(cell_name, offset=(4e5, 3e5), rotation=pi),
Ref(cell_name, offset=(5e5, 3e5), rotation=3*pi/2),
Ref(cell_name, mirrored=(True, False), offset=(1e5, 4e5)),
Ref(cell_name, mirrored=(True, False), offset=(2e5, 4e5), rotation=pi/3),
Ref(cell_name, mirrored=(True, False), offset=(3e5, 4e5), rotation=pi/2),
Ref(cell_name, mirrored=(True, False), offset=(4e5, 4e5), rotation=pi),
Ref(cell_name, mirrored=(True, False), offset=(5e5, 4e5), rotation=3*pi/2),
Ref(cell_name, mirrored=(False, True), offset=(1e5, 5e5)),
Ref(cell_name, mirrored=(False, True), offset=(2e5, 5e5), rotation=pi/3),
Ref(cell_name, mirrored=(False, True), offset=(3e5, 5e5), rotation=pi/2),
Ref(cell_name, mirrored=(False, True), offset=(4e5, 5e5), rotation=pi),
Ref(cell_name, mirrored=(False, True), offset=(5e5, 5e5), rotation=3*pi/2),
Ref(cell_name, mirrored=(True, True), offset=(1e5, 6e5)),
Ref(cell_name, mirrored=(True, True), offset=(2e5, 6e5), rotation=pi/3),
Ref(cell_name, mirrored=(True, True), offset=(3e5, 6e5), rotation=pi/2),
Ref(cell_name, mirrored=(True, True), offset=(4e5, 6e5), rotation=pi),
Ref(cell_name, mirrored=(True, True), offset=(5e5, 6e5), rotation=3*pi/2),
]
folder = 'layouts/'
masque.file.klamath.writefile((pat, pat2, pat3, pat4), folder + 'rep.gds.gz', 1e-9, 1e-3)
lib['sref_test'] = pat3
print('\nAdded sref_test:')
pprint(pat3)
pprint(pat3.refs)
cells = list(masque.file.klamath.readfile(folder + 'rep.gds.gz')[0].values())
masque.file.klamath.writefile(cells, folder + 'rerep.gds.gz', 1e-9, 1e-3)
rep = Grid(
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')
dxf, info = masque.file.dxf.readfile(folder + 'rep.dxf.gz')
masque.file.dxf.writefile(dxf, folder + 'rerep.dxf.gz')
lib['aref_test'] = pat4
print('\nAdded aref_test')
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)}
masque.file.oasis.writefile((pat, pat2, pat3, pat4), folder + 'rep.oas.gz', 1000, layer_map=layer_map)
oas, info = masque.file.oasis.readfile(folder + 'rep.oas.gz')
masque.file.oasis.writefile(list(oas.values()), folder + 'rerep.oas.gz', 1000, layer_map=layer_map)
print(info)
oas1 = folder / 'rep.oas'
oas2 = folder / 'rerep.oas'
print(f'Write lib to {oas1}')
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__':

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -39,7 +39,7 @@ from ..utils import layer_t, normalize_mirror, annotations_t
logger = logging.getLogger(__name__)
logger.warning('OASIS support is experimental and mostly untested!')
logger.warning('OASIS support is experimental!')
path_cap_map = {
@ -51,7 +51,7 @@ path_cap_map = {
#TODO implement more shape types?
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(

View File

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

View File

@ -88,7 +88,9 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
self.refs = list(refs)
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 {}

View File

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