more fixes and improvements

libcentric
Jan Petykiewicz 1 year ago
parent 6eb4af3203
commit c2ce9ed547

@ -3,11 +3,13 @@ from typing import Dict, TypeVar
import copy
import logging
import numpy
from numpy.typing import ArrayLike
#from .pattern import Pattern
from .ref import Ref
from .ports import PortList, Port
from .utils import rotation_matrix_2d, normalize_mirror
#if TYPE_CHECKING:
# from .builder import Builder, Tool
@ -203,7 +205,7 @@ class Abstract(PortList):
Returns:
self
"""
mirror_across_x, angle = normalize_mirror(ref.mirrored)
mirrored_across_x, angle = normalize_mirror(ref.mirrored)
if mirrored_across_x:
self.mirror(across_axis=0)
self.rotate_ports(angle + ref.rotation)
@ -224,7 +226,7 @@ class Abstract(PortList):
# TODO test undo_ref_transform
"""
mirror_across_x, angle = normalize_mirror(ref.mirrored)
mirrored_across_x, angle = normalize_mirror(ref.mirrored)
self.translate_ports(-ref.offset)
self.rotate_port_offsets(-angle - ref.rotation)
self.rotate_ports(-angle - ref.rotation)

@ -13,6 +13,7 @@ from ..library import MutableLibrary
from ..error import PortError, BuildError
from ..ports import PortList, Port
from ..abstract import Abstract
from ..utils import SupportsBool
from .tools import Tool
from .utils import ell
@ -148,7 +149,7 @@ class Builder(PortList):
@classmethod
def interface(
cls,
source: Union[PortList, Mapping[str, Port]],
source: Union[PortList, Mapping[str, Port], str],
*,
library: Optional[MutableLibrary] = None,
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = None,
@ -215,7 +216,9 @@ class Builder(PortList):
if tools is None and hasattr(source, 'tools') and isinstance(source.tools, dict):
tools = source.tools
if isinstance(source, PortList):
if isinstance(source, str):
orig_ports = library.abstract(source).ports
elif isinstance(source, PortList):
orig_ports = source.ports
elif isinstance(source, dict):
orig_ports = source
@ -250,7 +253,7 @@ class Builder(PortList):
def plug(
self: BB,
other: Abstract,
other: Union[Abstract, str],
map_in: Dict[str, str],
map_out: Optional[Dict[str, Optional[str]]] = None,
*,
@ -280,7 +283,7 @@ class Builder(PortList):
having to provide `map_out` each time `plug` is called.
Args:
other: A `DeviceRef` describing the device to be instatiated.
other: An `Abstract` describing the device to be instatiated.
map_in: Dict of `{'self_port': 'other_port'}` mappings, specifying
port connections between the two devices.
map_out: Dict of `{'old_name': 'new_name'}` mappings, specifying
@ -315,6 +318,9 @@ class Builder(PortList):
logger.error('Skipping plug() since device is dead')
return self
if isinstance(other, str):
other = self.library.abstract(other)
if (inherit_name
and not map_out
and len(map_in) == 1
@ -345,7 +351,7 @@ class Builder(PortList):
def place(
self: BB,
other: Abstract,
other: Union[Abstract, str],
*,
offset: ArrayLike = (0, 0),
rotation: float = 0,
@ -369,7 +375,7 @@ class Builder(PortList):
rather than the port name on the original `pad` device.
Args:
other: A `DeviceRef` describing the device to be instatiated.
other: An `Abstract` describing the device to be instatiated.
offset: Offset at which to place the instance. Default (0, 0).
rotation: Rotation applied to the instance before placement. Default 0.
pivot: Rotation is applied around this pivot point (default (0, 0)).
@ -395,6 +401,9 @@ class Builder(PortList):
logger.error('Skipping place() since device is dead')
return self
if isinstance(other, str):
other = self.library.abstract(other)
if port_map is None:
port_map = {}
@ -508,7 +517,7 @@ class Builder(PortList):
def path(
self: BB,
portspec: str,
ccw: Optional[bool],
ccw: Optional[SupportsBool],
length: float,
*,
tool_port_names: Sequence[str] = ('A', 'B'),
@ -529,7 +538,7 @@ class Builder(PortList):
def path_to(
self: BB,
portspec: str,
ccw: Optional[bool],
ccw: Optional[SupportsBool],
position: float,
*,
tool_port_names: Sequence[str] = ('A', 'B'),
@ -563,7 +572,7 @@ class Builder(PortList):
def mpath(
self: BB,
portspec: Union[str, Sequence[str]],
ccw: Optional[bool],
ccw: Optional[SupportsBool],
*,
spacing: Optional[Union[float, ArrayLike]] = None,
set_rotation: Optional[float] = None,
@ -611,4 +620,13 @@ class Builder(PortList):
# TODO def path_join() and def bus_join()?
def flatten(self: BB) -> BB:
"""
Flatten the contained pattern, using the contained library to resolve references.
Returns:
self
"""
self.pattern.flatten(self.library)
return self

@ -5,15 +5,14 @@ Functions for writing port data into a Pattern (`dev2pat`) and retrieving it (`p
the port locations. This particular approach is just a sensible default; feel free to
to write equivalent functions for your own format or alternate storage methods.
"""
from typing import Sequence, Optional, Mapping, Tuple, Dict
from typing import Sequence, Optional, Mapping
import logging
import numpy
from numpy.typing import NDArray
from ..pattern import Pattern
from ..label import Label
from ..utils import rotation_matrix_2d, layer_t
from ..utils import layer_t
from ..ports import Port
from ..error import PatternError
from ..library import Library, WrapROLibrary
@ -79,7 +78,6 @@ def pat2dev(
to contain their own port info without interfering with supercells'
port data.
Default True.
blacklist: If a cell name appears in the blacklist, do not ea
Returns:
The updated `pattern`. Port labels are not removed.
@ -99,6 +97,8 @@ def pat2dev(
# Load ports for all subpatterns, and use any we find
found_ports = False
for target in set(rr.target for rr in pattern.refs):
if target is None:
continue
pp = pat2dev(
layers=layers,
library=library,
@ -106,7 +106,6 @@ def pat2dev(
name=target,
max_depth=max_depth - 1,
skip_subcells=skip_subcells,
blacklist=blacklist + {name},
)
found_ports |= bool(pp.ports)
@ -114,6 +113,8 @@ def pat2dev(
return pattern
for ref in pattern.refs:
if ref.target is None:
continue
aa = library.abstract(ref.target)
if not aa.ports:
continue

@ -3,6 +3,8 @@ Tools are objects which dynamically generate simple single-use devices (e.g. wir
"""
from typing import TYPE_CHECKING, Optional, Sequence
from ..utils import SupportsBool
if TYPE_CHECKING:
from ..pattern import Pattern
@ -10,7 +12,7 @@ if TYPE_CHECKING:
class Tool:
def path(
self,
ccw: Optional[bool],
ccw: Optional[SupportsBool],
length: float,
*,
in_ptype: Optional[str] = None,

@ -6,7 +6,7 @@ import numpy
from numpy import pi
from numpy.typing import ArrayLike, NDArray
from ..utils import rotation_matrix_2d
from ..utils import rotation_matrix_2d, SupportsBool
from ..error import BuildError
if TYPE_CHECKING:
@ -15,7 +15,7 @@ if TYPE_CHECKING:
def ell(
ports: Mapping[str, 'Port'],
ccw: Optional[bool],
ccw: Optional[SupportsBool],
bound_type: str,
bound: Union[float, ArrayLike],
*,

@ -571,7 +571,7 @@ def load_libraryfile(
*,
use_mmap: bool = True,
full_load: bool = False,
postprocess: Optional[Callable[[Library, str], Pattern]] = None
postprocess: Optional[Callable[[Library, str, Pattern], Pattern]] = None
) -> Tuple[LazyLibrary, Dict[str, Any]]:
"""
Wrapper for `load_library()` that takes a filename or path instead of a stream.

@ -5,7 +5,7 @@ Library classes for managing unique name->pattern mappings and
# TODO documentn all library classes
# TODO toplevel documentation of library, classes, and abstracts
"""
from typing import List, Dict, Callable, TypeVar, Generic, Type, TYPE_CHECKING
from typing import List, Dict, Callable, TypeVar, Generic, Type, TYPE_CHECKING, cast
from typing import Tuple, Union, Iterator, Mapping, MutableMapping, Set, Optional, Sequence
import logging
import base64
@ -396,14 +396,14 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
if pattern is not original_pattern:
name = hierarchy[-1]
if not isintance(self, MutableLibrary):
if not isinstance(self, MutableLibrary):
raise LibraryError('visit_* functions returned a new `Pattern` object'
' but the library is immutable')
if name is None:
raise LibraryError('visit_* functions returned a new `Pattern` object'
' but no top-level name was provided in `hierarchy`')
self.set_const(name, pattern)
cast(MutableLibrary, self).set_const(name, pattern)
return self

@ -13,9 +13,9 @@ from numpy.typing import NDArray, ArrayLike
# .visualize imports matplotlib and matplotlib.collections
from .ref import Ref
from .shapes import Shape
from .shapes import Shape, Polygon
from .label import Label
from .utils import rotation_matrix_2d, AutoSlots, annotations_t
from .utils import rotation_matrix_2d, annotations_t
from .error import PatternError
from .traits import AnnotatableImpl, Scalable, Mirrorable, Rotatable, Positionable, Repeatable
from .ports import Port, PortList
@ -24,7 +24,7 @@ from .ports import Port, PortList
P = TypeVar('P', bound='Pattern')
class Pattern(PortList, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
class Pattern(PortList, AnnotatableImpl, Mirrorable):
"""
2D layout consisting of some set of shapes, labels, and references to other Pattern objects
(via Ref). Shapes are assumed to inherit from masque.shapes.Shape or provide equivalent functions.
@ -534,6 +534,21 @@ class Pattern(PortList, AnnotatableImpl, Mirrorable, metaclass=AutoSlots):
self.refs.append(Ref(*args, **kwargs))
return self
def rect(self: P, *args: Any, **kwargs: Any) -> P:
"""
Convenience function which calls `Polygon.rect` to construct a
rectangle and adds it to this pattern.
Args:
*args: Passed to `Polygon.rect()`
**kwargs: Passed to `Polygon.rect()`
Returns:
self
"""
self.shapes.append(Polygon.rect(*args, **kwargs))
return self
def flatten(
self: P,
library: Mapping[str, P],

@ -11,7 +11,7 @@ from numpy import pi
from numpy.typing import ArrayLike, NDArray
from .traits import PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable
from .utils import AutoSlots, rotate_offsets_around
from .utils import rotate_offsets_around
from .error import PortError
@ -23,7 +23,7 @@ PL = TypeVar('PL', bound='PortList')
PL2 = TypeVar('PL2', bound='PortList')
class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable, metaclass=AutoSlots):
class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable):
"""
A point at which a `Device` can be snapped to another `Device`.
@ -37,7 +37,11 @@ class Port(PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable, met
The `ptype` is an arbitrary string, default of `unk` (unknown).
"""
__slots__ = ('ptype', '_rotation')
__slots__ = (
'ptype', '_rotation',
# inherited:
'_offset',
)
_rotation: Optional[float]
""" radians counterclockwise from +x, pointing into device body.

@ -1,7 +1,7 @@
"""
Various helper functions, type definitions, etc.
"""
from .types import layer_t, annotations_t
from .types import layer_t, annotations_t, SupportsBool
from .array import is_scalar
from .autoslots import AutoSlots

@ -1,8 +1,13 @@
"""
Type definitions
"""
from typing import Union, Tuple, Dict, List
from typing import Union, Tuple, Dict, List, Protocol
layer_t = Union[int, Tuple[int, int], str]
annotations_t = Dict[str, List[Union[int, float, str]]]
class SupportsBool(Protocol):
def __bool__(self) -> bool:
...

Loading…
Cancel
Save