more fixes and improvements
This commit is contained in:
parent
6eb4af3203
commit
c2ce9ed547
@ -3,11 +3,13 @@ from typing import Dict, TypeVar
|
|||||||
import copy
|
import copy
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
import numpy
|
||||||
from numpy.typing import ArrayLike
|
from numpy.typing import ArrayLike
|
||||||
|
|
||||||
#from .pattern import Pattern
|
#from .pattern import Pattern
|
||||||
from .ref import Ref
|
from .ref import Ref
|
||||||
from .ports import PortList, Port
|
from .ports import PortList, Port
|
||||||
|
from .utils import rotation_matrix_2d, normalize_mirror
|
||||||
|
|
||||||
#if TYPE_CHECKING:
|
#if TYPE_CHECKING:
|
||||||
# from .builder import Builder, Tool
|
# from .builder import Builder, Tool
|
||||||
@ -203,7 +205,7 @@ class Abstract(PortList):
|
|||||||
Returns:
|
Returns:
|
||||||
self
|
self
|
||||||
"""
|
"""
|
||||||
mirror_across_x, angle = normalize_mirror(ref.mirrored)
|
mirrored_across_x, angle = normalize_mirror(ref.mirrored)
|
||||||
if mirrored_across_x:
|
if mirrored_across_x:
|
||||||
self.mirror(across_axis=0)
|
self.mirror(across_axis=0)
|
||||||
self.rotate_ports(angle + ref.rotation)
|
self.rotate_ports(angle + ref.rotation)
|
||||||
@ -224,7 +226,7 @@ class Abstract(PortList):
|
|||||||
|
|
||||||
# TODO test undo_ref_transform
|
# 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.translate_ports(-ref.offset)
|
||||||
self.rotate_port_offsets(-angle - ref.rotation)
|
self.rotate_port_offsets(-angle - ref.rotation)
|
||||||
self.rotate_ports(-angle - ref.rotation)
|
self.rotate_ports(-angle - ref.rotation)
|
||||||
|
@ -13,6 +13,7 @@ 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 ..abstract import Abstract
|
||||||
|
from ..utils import SupportsBool
|
||||||
from .tools import Tool
|
from .tools import Tool
|
||||||
from .utils import ell
|
from .utils import ell
|
||||||
|
|
||||||
@ -148,7 +149,7 @@ class Builder(PortList):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def interface(
|
def interface(
|
||||||
cls,
|
cls,
|
||||||
source: Union[PortList, Mapping[str, Port]],
|
source: Union[PortList, Mapping[str, Port], str],
|
||||||
*,
|
*,
|
||||||
library: Optional[MutableLibrary] = None,
|
library: Optional[MutableLibrary] = None,
|
||||||
tools: Union[None, Tool, MutableMapping[Optional[str], Tool]] = 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):
|
if tools is None and hasattr(source, 'tools') and isinstance(source.tools, dict):
|
||||||
tools = source.tools
|
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
|
orig_ports = source.ports
|
||||||
elif isinstance(source, dict):
|
elif isinstance(source, dict):
|
||||||
orig_ports = source
|
orig_ports = source
|
||||||
@ -250,7 +253,7 @@ class Builder(PortList):
|
|||||||
|
|
||||||
def plug(
|
def plug(
|
||||||
self: BB,
|
self: BB,
|
||||||
other: Abstract,
|
other: Union[Abstract, str],
|
||||||
map_in: Dict[str, str],
|
map_in: Dict[str, str],
|
||||||
map_out: Optional[Dict[str, Optional[str]]] = None,
|
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.
|
having to provide `map_out` each time `plug` is called.
|
||||||
|
|
||||||
Args:
|
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
|
map_in: Dict of `{'self_port': 'other_port'}` mappings, specifying
|
||||||
port connections between the two devices.
|
port connections between the two devices.
|
||||||
map_out: Dict of `{'old_name': 'new_name'}` mappings, specifying
|
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')
|
logger.error('Skipping plug() since device is dead')
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
if isinstance(other, str):
|
||||||
|
other = self.library.abstract(other)
|
||||||
|
|
||||||
if (inherit_name
|
if (inherit_name
|
||||||
and not map_out
|
and not map_out
|
||||||
and len(map_in) == 1
|
and len(map_in) == 1
|
||||||
@ -345,7 +351,7 @@ class Builder(PortList):
|
|||||||
|
|
||||||
def place(
|
def place(
|
||||||
self: BB,
|
self: BB,
|
||||||
other: Abstract,
|
other: Union[Abstract, str],
|
||||||
*,
|
*,
|
||||||
offset: ArrayLike = (0, 0),
|
offset: ArrayLike = (0, 0),
|
||||||
rotation: float = 0,
|
rotation: float = 0,
|
||||||
@ -369,7 +375,7 @@ class Builder(PortList):
|
|||||||
rather than the port name on the original `pad` device.
|
rather than the port name on the original `pad` device.
|
||||||
|
|
||||||
Args:
|
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).
|
offset: Offset at which to place the instance. Default (0, 0).
|
||||||
rotation: Rotation applied to the instance before placement. Default 0.
|
rotation: Rotation applied to the instance before placement. Default 0.
|
||||||
pivot: Rotation is applied around this pivot point (default (0, 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')
|
logger.error('Skipping place() since device is dead')
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
if isinstance(other, str):
|
||||||
|
other = self.library.abstract(other)
|
||||||
|
|
||||||
if port_map is None:
|
if port_map is None:
|
||||||
port_map = {}
|
port_map = {}
|
||||||
|
|
||||||
@ -508,7 +517,7 @@ class Builder(PortList):
|
|||||||
def path(
|
def path(
|
||||||
self: BB,
|
self: BB,
|
||||||
portspec: str,
|
portspec: str,
|
||||||
ccw: Optional[bool],
|
ccw: Optional[SupportsBool],
|
||||||
length: float,
|
length: float,
|
||||||
*,
|
*,
|
||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||||
@ -529,7 +538,7 @@ class Builder(PortList):
|
|||||||
def path_to(
|
def path_to(
|
||||||
self: BB,
|
self: BB,
|
||||||
portspec: str,
|
portspec: str,
|
||||||
ccw: Optional[bool],
|
ccw: Optional[SupportsBool],
|
||||||
position: float,
|
position: float,
|
||||||
*,
|
*,
|
||||||
tool_port_names: Sequence[str] = ('A', 'B'),
|
tool_port_names: Sequence[str] = ('A', 'B'),
|
||||||
@ -563,7 +572,7 @@ class Builder(PortList):
|
|||||||
def mpath(
|
def mpath(
|
||||||
self: BB,
|
self: BB,
|
||||||
portspec: Union[str, Sequence[str]],
|
portspec: Union[str, Sequence[str]],
|
||||||
ccw: Optional[bool],
|
ccw: Optional[SupportsBool],
|
||||||
*,
|
*,
|
||||||
spacing: Optional[Union[float, ArrayLike]] = None,
|
spacing: Optional[Union[float, ArrayLike]] = None,
|
||||||
set_rotation: Optional[float] = None,
|
set_rotation: Optional[float] = None,
|
||||||
@ -611,4 +620,13 @@ class Builder(PortList):
|
|||||||
|
|
||||||
# TODO def path_join() and def bus_join()?
|
# 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
|
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.
|
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 logging
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import NDArray
|
|
||||||
|
|
||||||
from ..pattern import Pattern
|
from ..pattern import Pattern
|
||||||
from ..label import Label
|
from ..label import Label
|
||||||
from ..utils import rotation_matrix_2d, layer_t
|
from ..utils import layer_t
|
||||||
from ..ports import Port
|
from ..ports import Port
|
||||||
from ..error import PatternError
|
from ..error import PatternError
|
||||||
from ..library import Library, WrapROLibrary
|
from ..library import Library, WrapROLibrary
|
||||||
@ -79,7 +78,6 @@ def pat2dev(
|
|||||||
to contain their own port info without interfering with supercells'
|
to contain their own port info without interfering with supercells'
|
||||||
port data.
|
port data.
|
||||||
Default True.
|
Default True.
|
||||||
blacklist: If a cell name appears in the blacklist, do not ea
|
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
The updated `pattern`. Port labels are not removed.
|
The updated `pattern`. Port labels are not removed.
|
||||||
@ -99,6 +97,8 @@ def pat2dev(
|
|||||||
# Load ports for all subpatterns, and use any we find
|
# Load ports for all subpatterns, and use any we find
|
||||||
found_ports = False
|
found_ports = False
|
||||||
for target in set(rr.target for rr in pattern.refs):
|
for target in set(rr.target for rr in pattern.refs):
|
||||||
|
if target is None:
|
||||||
|
continue
|
||||||
pp = pat2dev(
|
pp = pat2dev(
|
||||||
layers=layers,
|
layers=layers,
|
||||||
library=library,
|
library=library,
|
||||||
@ -106,7 +106,6 @@ def pat2dev(
|
|||||||
name=target,
|
name=target,
|
||||||
max_depth=max_depth - 1,
|
max_depth=max_depth - 1,
|
||||||
skip_subcells=skip_subcells,
|
skip_subcells=skip_subcells,
|
||||||
blacklist=blacklist + {name},
|
|
||||||
)
|
)
|
||||||
found_ports |= bool(pp.ports)
|
found_ports |= bool(pp.ports)
|
||||||
|
|
||||||
@ -114,6 +113,8 @@ def pat2dev(
|
|||||||
return pattern
|
return pattern
|
||||||
|
|
||||||
for ref in pattern.refs:
|
for ref in pattern.refs:
|
||||||
|
if ref.target is None:
|
||||||
|
continue
|
||||||
aa = library.abstract(ref.target)
|
aa = library.abstract(ref.target)
|
||||||
if not aa.ports:
|
if not aa.ports:
|
||||||
continue
|
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 typing import TYPE_CHECKING, Optional, Sequence
|
||||||
|
|
||||||
|
from ..utils import SupportsBool
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ..pattern import Pattern
|
from ..pattern import Pattern
|
||||||
|
|
||||||
@ -10,7 +12,7 @@ if TYPE_CHECKING:
|
|||||||
class Tool:
|
class Tool:
|
||||||
def path(
|
def path(
|
||||||
self,
|
self,
|
||||||
ccw: Optional[bool],
|
ccw: Optional[SupportsBool],
|
||||||
length: float,
|
length: float,
|
||||||
*,
|
*,
|
||||||
in_ptype: Optional[str] = None,
|
in_ptype: Optional[str] = None,
|
||||||
|
@ -6,7 +6,7 @@ import numpy
|
|||||||
from numpy import pi
|
from numpy import pi
|
||||||
from numpy.typing import ArrayLike, NDArray
|
from numpy.typing import ArrayLike, NDArray
|
||||||
|
|
||||||
from ..utils import rotation_matrix_2d
|
from ..utils import rotation_matrix_2d, SupportsBool
|
||||||
from ..error import BuildError
|
from ..error import BuildError
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
@ -15,7 +15,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
def ell(
|
def ell(
|
||||||
ports: Mapping[str, 'Port'],
|
ports: Mapping[str, 'Port'],
|
||||||
ccw: Optional[bool],
|
ccw: Optional[SupportsBool],
|
||||||
bound_type: str,
|
bound_type: str,
|
||||||
bound: Union[float, ArrayLike],
|
bound: Union[float, ArrayLike],
|
||||||
*,
|
*,
|
||||||
|
@ -571,7 +571,7 @@ def load_libraryfile(
|
|||||||
*,
|
*,
|
||||||
use_mmap: bool = True,
|
use_mmap: bool = True,
|
||||||
full_load: bool = False,
|
full_load: bool = False,
|
||||||
postprocess: Optional[Callable[[Library, str], Pattern]] = None
|
postprocess: Optional[Callable[[Library, str, Pattern], Pattern]] = None
|
||||||
) -> Tuple[LazyLibrary, Dict[str, Any]]:
|
) -> Tuple[LazyLibrary, Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Wrapper for `load_library()` that takes a filename or path instead of a stream.
|
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 documentn all library classes
|
||||||
# TODO toplevel documentation of library, classes, and abstracts
|
# 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
|
from typing import Tuple, Union, Iterator, Mapping, MutableMapping, Set, Optional, Sequence
|
||||||
import logging
|
import logging
|
||||||
import base64
|
import base64
|
||||||
@ -396,14 +396,14 @@ class Library(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
if pattern is not original_pattern:
|
if pattern is not original_pattern:
|
||||||
name = hierarchy[-1]
|
name = hierarchy[-1]
|
||||||
if not isintance(self, MutableLibrary):
|
if not isinstance(self, MutableLibrary):
|
||||||
raise LibraryError('visit_* functions returned a new `Pattern` object'
|
raise LibraryError('visit_* functions returned a new `Pattern` object'
|
||||||
' but the library is immutable')
|
' but the library is immutable')
|
||||||
if name is None:
|
if name is None:
|
||||||
raise LibraryError('visit_* functions returned a new `Pattern` object'
|
raise LibraryError('visit_* functions returned a new `Pattern` object'
|
||||||
' but no top-level name was provided in `hierarchy`')
|
' but no top-level name was provided in `hierarchy`')
|
||||||
|
|
||||||
self.set_const(name, pattern)
|
cast(MutableLibrary, self).set_const(name, pattern)
|
||||||
|
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
@ -13,9 +13,9 @@ from numpy.typing import NDArray, ArrayLike
|
|||||||
# .visualize imports matplotlib and matplotlib.collections
|
# .visualize imports matplotlib and matplotlib.collections
|
||||||
|
|
||||||
from .ref import Ref
|
from .ref import Ref
|
||||||
from .shapes import Shape
|
from .shapes import Shape, Polygon
|
||||||
from .label import Label
|
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 .error import PatternError
|
||||||
from .traits import AnnotatableImpl, Scalable, Mirrorable, Rotatable, Positionable, Repeatable
|
from .traits import AnnotatableImpl, Scalable, Mirrorable, Rotatable, Positionable, Repeatable
|
||||||
from .ports import Port, PortList
|
from .ports import Port, PortList
|
||||||
@ -24,7 +24,7 @@ from .ports import Port, PortList
|
|||||||
P = TypeVar('P', bound='Pattern')
|
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
|
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.
|
(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))
|
self.refs.append(Ref(*args, **kwargs))
|
||||||
return self
|
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(
|
def flatten(
|
||||||
self: P,
|
self: P,
|
||||||
library: Mapping[str, P],
|
library: Mapping[str, P],
|
||||||
|
@ -11,7 +11,7 @@ from numpy import pi
|
|||||||
from numpy.typing import ArrayLike, NDArray
|
from numpy.typing import ArrayLike, NDArray
|
||||||
|
|
||||||
from .traits import PositionableImpl, Rotatable, PivotableImpl, Copyable, Mirrorable
|
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
|
from .error import PortError
|
||||||
|
|
||||||
|
|
||||||
@ -23,7 +23,7 @@ PL = TypeVar('PL', bound='PortList')
|
|||||||
PL2 = TypeVar('PL2', 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`.
|
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).
|
The `ptype` is an arbitrary string, default of `unk` (unknown).
|
||||||
"""
|
"""
|
||||||
__slots__ = ('ptype', '_rotation')
|
__slots__ = (
|
||||||
|
'ptype', '_rotation',
|
||||||
|
# inherited:
|
||||||
|
'_offset',
|
||||||
|
)
|
||||||
|
|
||||||
_rotation: Optional[float]
|
_rotation: Optional[float]
|
||||||
""" radians counterclockwise from +x, pointing into device body.
|
""" radians counterclockwise from +x, pointing into device body.
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
"""
|
"""
|
||||||
Various helper functions, type definitions, etc.
|
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 .array import is_scalar
|
||||||
from .autoslots import AutoSlots
|
from .autoslots import AutoSlots
|
||||||
|
@ -1,8 +1,13 @@
|
|||||||
"""
|
"""
|
||||||
Type definitions
|
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]
|
layer_t = Union[int, Tuple[int, int], str]
|
||||||
annotations_t = Dict[str, List[Union[int, float, str]]]
|
annotations_t = Dict[str, List[Union[int, float, str]]]
|
||||||
|
|
||||||
|
|
||||||
|
class SupportsBool(Protocol):
|
||||||
|
def __bool__(self) -> bool:
|
||||||
|
...
|
||||||
|
Loading…
Reference in New Issue
Block a user