Compare commits
No commits in common. "f28c31fe2917f480584a9ebb921b7690cf63acdc" and "b4d31903c10fe8bea48a92ad3f344b76c7ef9e80" have entirely different histories.
f28c31fe29
...
b4d31903c1
@ -101,7 +101,7 @@ References are accomplished by listing the target's name, not its `Pattern` obje
|
|||||||
|
|
||||||
## Glossary
|
## Glossary
|
||||||
- `Library`: A collection of named cells. OASIS or GDS "library" or file.
|
- `Library`: A collection of named cells. OASIS or GDS "library" or file.
|
||||||
- `Tree`: Any `{name: pattern}` mapping which has only one topcell.
|
- "tree": Any Library which has only one topcell.
|
||||||
- `Pattern`: A collection of geometry, text labels, and reference to other patterns.
|
- `Pattern`: A collection of geometry, text labels, and reference to other patterns.
|
||||||
OASIS or GDS "Cell", DXF "Block".
|
OASIS or GDS "Cell", DXF "Block".
|
||||||
- `Ref`: A reference to another pattern. GDS "AREF/SREF", OASIS "Placement".
|
- `Ref`: A reference to another pattern. GDS "AREF/SREF", OASIS "Placement".
|
||||||
@ -142,11 +142,6 @@ my_pattern.ref(new_name, ...) # instantiate the cell
|
|||||||
|
|
||||||
# In practice, you may do lots of
|
# In practice, you may do lots of
|
||||||
my_pattern.ref(lib << make_tree(...), ...)
|
my_pattern.ref(lib << make_tree(...), ...)
|
||||||
|
|
||||||
# With a `Builder` and `place()`/`plug()` the `lib <<` portion can be implicit:
|
|
||||||
my_builder = Builder(library=lib, ...)
|
|
||||||
...
|
|
||||||
my_builder.place(make_tree(...))
|
|
||||||
```
|
```
|
||||||
|
|
||||||
We can also use this shorthand to quickly add and reference a single flat (as yet un-named) pattern:
|
We can also use this shorthand to quickly add and reference a single flat (as yet un-named) pattern:
|
||||||
|
@ -38,7 +38,7 @@ from .pattern import Pattern, map_layers, map_targets, chain_elements
|
|||||||
from .library import (
|
from .library import (
|
||||||
ILibraryView, ILibrary,
|
ILibraryView, ILibrary,
|
||||||
LibraryView, Library, LazyLibrary,
|
LibraryView, Library, LazyLibrary,
|
||||||
AbstractView, TreeView, Tree,
|
AbstractView,
|
||||||
)
|
)
|
||||||
from .ports import Port, PortList
|
from .ports import Port, PortList
|
||||||
from .abstract import Abstract
|
from .abstract import Abstract
|
||||||
|
@ -8,7 +8,7 @@ import logging
|
|||||||
from numpy.typing import ArrayLike
|
from numpy.typing import ArrayLike
|
||||||
|
|
||||||
from ..pattern import Pattern
|
from ..pattern import Pattern
|
||||||
from ..library import ILibrary, TreeView
|
from ..library import ILibrary
|
||||||
from ..error import BuildError
|
from ..error import BuildError
|
||||||
from ..ports import PortList, Port
|
from ..ports import PortList, Port
|
||||||
from ..abstract import Abstract
|
from ..abstract import Abstract
|
||||||
@ -190,7 +190,7 @@ class Builder(PortList):
|
|||||||
|
|
||||||
def plug(
|
def plug(
|
||||||
self,
|
self,
|
||||||
other: Abstract | str | Pattern | TreeView,
|
other: Abstract | str | Pattern,
|
||||||
map_in: dict[str, str],
|
map_in: dict[str, str],
|
||||||
map_out: dict[str, str | None] | None = None,
|
map_out: dict[str, str | None] | None = None,
|
||||||
*,
|
*,
|
||||||
@ -201,16 +201,11 @@ class Builder(PortList):
|
|||||||
) -> Self:
|
) -> Self:
|
||||||
"""
|
"""
|
||||||
Wrapper around `Pattern.plug` which allows a string for `other`.
|
Wrapper around `Pattern.plug` which allows a string for `other`.
|
||||||
|
|
||||||
The `Builder`'s library is used to dereference the string (or `Abstract`, if
|
The `Builder`'s library is used to dereference the string (or `Abstract`, if
|
||||||
one is passed with `append=True`). If a `TreeView` is passed, it is first
|
one is passed with `append=True`).
|
||||||
added into `self.library`.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
other: An `Abstract`, string, `Pattern`, or `TreeView` describing the
|
other: An `Abstract`, string, or `Pattern` describing the device to be instatiated.
|
||||||
device to be instatiated. If it is a `TreeView`, it is first
|
|
||||||
added into `self.library`, after which the topcell is plugged;
|
|
||||||
an equivalent statement is `self.plug(self.library << other, ...)`.
|
|
||||||
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
|
||||||
@ -248,10 +243,6 @@ class Builder(PortList):
|
|||||||
logger.error('Skipping plug() since device is dead')
|
logger.error('Skipping plug() since device is dead')
|
||||||
return self
|
return self
|
||||||
|
|
||||||
if not isinstance(other, (str, Abstract, Pattern)):
|
|
||||||
# We got a Tree; add it into self.library and grab an Abstract for it
|
|
||||||
other = self.library << other
|
|
||||||
|
|
||||||
if isinstance(other, str):
|
if isinstance(other, str):
|
||||||
other = self.library.abstract(other)
|
other = self.library.abstract(other)
|
||||||
if append and isinstance(other, Abstract):
|
if append and isinstance(other, Abstract):
|
||||||
@ -270,7 +261,7 @@ class Builder(PortList):
|
|||||||
|
|
||||||
def place(
|
def place(
|
||||||
self,
|
self,
|
||||||
other: Abstract | str | Pattern | TreeView,
|
other: Abstract | str | Pattern,
|
||||||
*,
|
*,
|
||||||
offset: ArrayLike = (0, 0),
|
offset: ArrayLike = (0, 0),
|
||||||
rotation: float = 0,
|
rotation: float = 0,
|
||||||
@ -281,17 +272,12 @@ class Builder(PortList):
|
|||||||
append: bool = False,
|
append: bool = False,
|
||||||
) -> Self:
|
) -> Self:
|
||||||
"""
|
"""
|
||||||
Wrapper around `Pattern.place` which allows a string or `TreeView` for `other`.
|
Wrapper around `Pattern.place` which allows a string for `other`.
|
||||||
|
|
||||||
The `Builder`'s library is used to dereference the string (or `Abstract`, if
|
The `Builder`'s library is used to dereference the string (or `Abstract`, if
|
||||||
one is passed with `append=True`). If a `TreeView` is passed, it is first
|
one is passed with `append=True`).
|
||||||
added into `self.library`.
|
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
other: An `Abstract`, string, `Pattern`, or `TreeView` describing the
|
other: An `Abstract`, string, or `Pattern` describing the device to be instatiated.
|
||||||
device to be instatiated. If it is a `TreeView`, it is first
|
|
||||||
added into `self.library`, after which the topcell is plugged;
|
|
||||||
an equivalent statement is `self.plug(self.library << other, ...)`.
|
|
||||||
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)).
|
||||||
@ -320,10 +306,6 @@ class Builder(PortList):
|
|||||||
logger.error('Skipping place() since device is dead')
|
logger.error('Skipping place() since device is dead')
|
||||||
return self
|
return self
|
||||||
|
|
||||||
if not isinstance(other, (str, Abstract, Pattern)):
|
|
||||||
# We got a Tree; add it into self.library and grab an Abstract for it
|
|
||||||
other = self.library << other
|
|
||||||
|
|
||||||
if isinstance(other, str):
|
if isinstance(other, str):
|
||||||
other = self.library.abstract(other)
|
other = self.library.abstract(other)
|
||||||
if append and isinstance(other, Abstract):
|
if append and isinstance(other, Abstract):
|
||||||
|
@ -14,7 +14,7 @@ Classes include:
|
|||||||
- `AbstractView`: Provides a way to use []-indexing to generate abstracts for patterns in the linked
|
- `AbstractView`: Provides a way to use []-indexing to generate abstracts for patterns in the linked
|
||||||
library. Generated with `ILibraryView.abstract_view()`.
|
library. Generated with `ILibraryView.abstract_view()`.
|
||||||
"""
|
"""
|
||||||
from typing import Callable, Self, Type, TYPE_CHECKING, cast, TypeAlias, Protocol, Literal
|
from typing import Callable, Self, Type, TYPE_CHECKING, cast
|
||||||
from typing import Iterator, Mapping, MutableMapping, Sequence
|
from typing import Iterator, Mapping, MutableMapping, Sequence
|
||||||
import logging
|
import logging
|
||||||
import base64
|
import base64
|
||||||
@ -26,7 +26,7 @@ from collections import defaultdict
|
|||||||
from abc import ABCMeta, abstractmethod
|
from abc import ABCMeta, abstractmethod
|
||||||
|
|
||||||
import numpy
|
import numpy
|
||||||
from numpy.typing import ArrayLike, NDArray
|
from numpy.typing import ArrayLike
|
||||||
|
|
||||||
from .error import LibraryError, PatternError
|
from .error import LibraryError, PatternError
|
||||||
from .utils import rotation_matrix_2d, layer_t
|
from .utils import rotation_matrix_2d, layer_t
|
||||||
@ -42,24 +42,7 @@ if TYPE_CHECKING:
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
class visitor_function_t(Protocol):
|
visitor_function_t = Callable[..., 'Pattern']
|
||||||
""" Signature for `Library.dfs()` visitor functions. """
|
|
||||||
def __call__(
|
|
||||||
self,
|
|
||||||
pattern: 'Pattern',
|
|
||||||
hierarchy: tuple[str | None, ...],
|
|
||||||
memo: dict,
|
|
||||||
transform: NDArray[numpy.float64] | Literal[False],
|
|
||||||
) -> Pattern:
|
|
||||||
...
|
|
||||||
|
|
||||||
|
|
||||||
TreeView: TypeAlias = Mapping[str, 'Pattern']
|
|
||||||
""" A name-to-`Pattern` mapping which is expected to have only one top-level cell """
|
|
||||||
|
|
||||||
Tree: TypeAlias = MutableMapping[str, 'Pattern']
|
|
||||||
""" A mutable name-to-`Pattern` mapping which is expected to have only one top-level cell """
|
|
||||||
|
|
||||||
|
|
||||||
SINGLE_USE_PREFIX = '_'
|
SINGLE_USE_PREFIX = '_'
|
||||||
"""
|
"""
|
||||||
@ -387,9 +370,6 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
def top(self) -> str:
|
def top(self) -> str:
|
||||||
"""
|
"""
|
||||||
Return the name of the topcell, or raise an exception if there isn't a single topcell
|
Return the name of the topcell, or raise an exception if there isn't a single topcell
|
||||||
|
|
||||||
Raises:
|
|
||||||
LibraryError if there is not exactly one topcell.
|
|
||||||
"""
|
"""
|
||||||
tops = self.tops()
|
tops = self.tops()
|
||||||
if len(tops) != 1:
|
if len(tops) != 1:
|
||||||
@ -399,9 +379,6 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
def top_pattern(self) -> 'Pattern':
|
def top_pattern(self) -> 'Pattern':
|
||||||
"""
|
"""
|
||||||
Shorthand for self[self.top()]
|
Shorthand for self[self.top()]
|
||||||
|
|
||||||
Raises:
|
|
||||||
LibraryError if there is not exactly one topcell.
|
|
||||||
"""
|
"""
|
||||||
return self[self.top()]
|
return self[self.top()]
|
||||||
|
|
||||||
@ -461,7 +438,7 @@ class ILibraryView(Mapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
if transform is None or transform is True:
|
if transform is None or transform is True:
|
||||||
transform = numpy.zeros(4)
|
transform = numpy.zeros(4)
|
||||||
elif transform is not False:
|
elif transform is not False:
|
||||||
transform = numpy.array(transform, dtype=float, copy=False)
|
transform = numpy.array(transform)
|
||||||
|
|
||||||
original_pattern = pattern
|
original_pattern = pattern
|
||||||
|
|
||||||
@ -697,15 +674,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
return rename_map
|
return rename_map
|
||||||
|
|
||||||
def __lshift__(self, other: TreeView) -> str:
|
def __lshift__(self, other: Mapping[str, 'Pattern']) -> str:
|
||||||
"""
|
|
||||||
`add()` items from a tree (single-topcell name: pattern mapping) into this one,
|
|
||||||
and return the name of the tree's topcell (in this library; it may have changed
|
|
||||||
based on `add()`'s default `rename_theirs` argument).
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
LibraryError if there is more than one topcell in `other`.
|
|
||||||
"""
|
|
||||||
if len(other) == 1:
|
if len(other) == 1:
|
||||||
name = next(iter(other))
|
name = next(iter(other))
|
||||||
else:
|
else:
|
||||||
@ -723,13 +692,6 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
return new_name
|
return new_name
|
||||||
|
|
||||||
def __le__(self, other: Mapping[str, 'Pattern']) -> Abstract:
|
def __le__(self, other: Mapping[str, 'Pattern']) -> Abstract:
|
||||||
"""
|
|
||||||
Perform the same operation as `__lshift__` / `<<`, but return an `Abstract` instead
|
|
||||||
of just the pattern's name.
|
|
||||||
|
|
||||||
Raises:
|
|
||||||
LibraryError if there is more than one topcell in `other`.
|
|
||||||
"""
|
|
||||||
new_name = self << other
|
new_name = self << other
|
||||||
return self.abstract(new_name)
|
return self.abstract(new_name)
|
||||||
|
|
||||||
@ -865,7 +827,7 @@ class ILibrary(ILibraryView, MutableMapping[str, 'Pattern'], metaclass=ABCMeta):
|
|||||||
|
|
||||||
if name_func is None:
|
if name_func is None:
|
||||||
def name_func(_pat, _shape):
|
def name_func(_pat, _shape):
|
||||||
return self.get_name(SINGLE_USE_PREFIX + 'rep')
|
return self.get_name(SINGLE_USE_PREFIX = 'rep')
|
||||||
|
|
||||||
for pat in tuple(self.values()):
|
for pat in tuple(self.values()):
|
||||||
for layer in pat.shapes:
|
for layer in pat.shapes:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user