ordec.core.arrange — Arrangement groups
Arrangement groups arrange schematic elements (instances, ports, nested groups) relative to each other without explicit coordinates. A group records its children in declaration order and emits constraints for the view’s Solver during postprocessing, so children of different sizes are placed without user intervention.
Row and Col place their children geometrically. Series and Parallel additionally connect them electrically, so circuit structures can be described by nesting alone. Groups are context managers:
with Col(gap=2):
Pmos pu
Nmos pd
with Col(gap=2) as stack: names the group, e.g. for use in
constraints. Group blocks are naming-transparent: children declared
inside the body are added to the view root as usual and only
additionally recorded in the group.
Example (ORD):
viewgen schematic -> Schematic:
port a : .align=East
port y : .align=West
net vss # forward declaration, port statement follows in the stack
with Series(gap=4):
port vdd: .align=South
Pmos pu:
.g -- a
.b -- vdd
.d -- y
Nmos pd:
.g -- a
.b -- vss
port vss: .align=North
Choosing a group
Row and Col are purely geometric: they place their
children and leave all wiring to explicit -- connections.
Series and Parallel additionally connect their children
electrically, modeling a current path:
Group |
Placement |
Electrical connection |
|---|---|---|
side by side, left to right |
none |
|
stacked, top to bottom |
none |
|
along the current path |
each pair of neighbors, pin to pin |
|
across the current path |
all children between two rail nets |
Use Series/Parallel when the placement follows the circuit topology and let the connections fall out of the structure. Fall back to Col/Row with explicit wiring when the structure is irregular (heterogeneous pin selections, connections that do not follow the stack).
Attributes
All groups:
gapDistance between adjacent children along the main axis. The default (2) leaves room for
auto_wire()routing. Tighter gaps can exceed the router’s capacity.alignCross-axis placement policy for children of different sizes:
'center'(default for Row, Col and Parallel) centers the children’s bounding boxes, snapped down to the unit grid so that on-grid pins stay routable.'start'/'end'align the bottom/left or top/right edges.'pins'(Series only, its default) shifts each child so that each pair’s facing pins line up, giving straight junction wires. Where a child has no single junction point (a nested Parallel rail), its center is used.
With children of equal size, all policies coincide.
anchorPosition of the group’s southwest corner. The default
'auto'anchors a top-level group at (0, 0). Several auto-anchored groups in one view line up side by side. A group with a member that appears in a user constraint or has a directly assigned position follows that member instead. Pass an (x, y) tuple to anchor explicitly, or None to never emit an anchor.
Series and Parallel only:
horizontalDirection of the current path through each child: vertical (False, the default) connects through downward-/upward-facing pins, horizontal (True) through right-/left-facing pins. The placement axis follows from it: Series places along the path, Parallel across it, so a vertical Series stacks like Col while a vertical Parallel places side by side like Row. The group never rotates children. Orient instances so that their current-carrying pins face along the path.
top,bottom(vertical),left,right(horizontal)Pin name overrides for pin detection: when an instance child has several pins on a facing side, these select the connecting pin, uniformly for all instance children.
Connectivity
Series connects the pin of each child facing the next child to the next child’s pin facing back. Port children connect through their net. Parallel ties all pins facing one way to one rail net and all pins facing the other way to a second rail net. It does not accept port children (wire the rail net to the port explicitly).
A junction or rail net is adopted from whichever involved pin is
already connected, so wiring one pin explicitly (.d -- y) names the
net. Without any, an anonymous net is created. Pins that are already on
two different nets are a connection conflict and raise an error.
Since a port’s statement position inside a group determines its place in
the stack, a port that earlier statements reference can be
forward-declared with net (see the example above): the later
port statement attaches the symbol pin to the declared net.
Nesting
Any group nests in any group geometrically. The nested group is placed as a rigid block. Series and Parallel additionally connect nested Series/Parallel children through their boundary: a nested Series exposes the outward-facing pins of its first and last child, a nested Parallel exposes its two rails. Series/parallel circuit structures can therefore be described by nesting alone, e.g. a NAND as a Series of vdd, a Parallel pull-up pair, the pull-down transistors and vss. Tutorial section 4.9 Arrangement groups shows this NAND as a complete example with its rendered schematic.
Two restrictions apply: a Series/Parallel cannot electrically connect a nested Col/Row child (nest Series/Parallel instead, or wire explicitly), and a nested connecting group must share the enclosing group’s orientation (a horizontal Parallel has no upward/downward rails to connect in a vertical Series).
Placement precedence
Positions of schematic elements are determined in this order:
Directly assigned positions (
.pos = (x, y)) and explicit constraints (! ...) always win. A port or instance referenced in any constraint is not placed automatically.Members of an arrangement group are placed by the group. Top-level groups anchor at (0, 0). Several auto-anchored groups line up side by side. A group with a constrained or directly positioned member follows that member instead of being anchored.
Remaining ports are auto-placed on the edge of the content bounding box based on their align (see
schem_place_ports()inordec.schematic.helpers): the align is the direction the port arrow points, into the drawing. A port withalign=Eastis placed on the left edge,Weston the right,Northon the bottom andSouthon the top edge. Along the edge, the port lines up with the connected pin nearest to its edge, so that the wire towards it can run straight.Instances that end up with no position at all raise a
ordec.core.constraints.SolverError.
Classes
Groups are configured entirely through their constructor arguments, described in the class docstrings and under Attributes above. Their methods are internal machinery (see below).
- class ordec.core.arrange.ArrangementGroup(gap: int = 2, align: str | None = None, anchor='auto')
Base class of all arrangement groups, see module docstring. Use Row or Col (or Series/Parallel for electrical connection).
- Parameters:
gap – Distance between adjacent children along the main axis. The default leaves room for auto_wire() routing.
align – Cross-axis alignment of the children: ‘center’ (snapped down to the unit grid), ‘start’ (bottom/left edge) or ‘end’ (top/right edge). None picks the group’s default_align.
anchor – Position of the group’s southwest corner: an (x, y) tuple, None to never emit an anchor, or ‘auto’ (default) to anchor a top-level group at (0, 0) unless a member appears in a user constraint or has a directly assigned position.
- class ordec.core.arrange.Row(gap: int = 2, align: str | None = None, anchor='auto')
Bases:
ArrangementGroupPlaces children side by side, left to right along the x axis.
- class ordec.core.arrange.Col(gap: int = 2, align: str | None = None, anchor='auto')
Bases:
ArrangementGroupPlaces children in a stack, top to bottom along the y axis.
- class ordec.core.arrange.ConnectingGroup(gap: int = 2, align: str | None = None, anchor='auto', horizontal: bool = False, top: str | None = None, bottom: str | None = None, left: str | None = None, right: str | None = None)
Bases:
ArrangementGroupCommon base of the electrically connecting groups Series and Parallel: pin detection by facing side and shared net handling.
Pin detection relies on the symbol convention that the pins carrying the current path face along its direction. It must be unambiguous: with several pins on a facing side, the top=/bottom= (vertical) or left=/right= (horizontal) pin names select the pin, uniformly for all instance children. horizontal chooses the orientation of the current path. The placement axis follows from it and flow_along_axis. gap, align and anchor are inherited from ArrangementGroup.
- class ordec.core.arrange.Series(gap: int = 2, align: str | None = None, anchor='auto', horizontal: bool = False, top: str | None = None, bottom: str | None = None, left: str | None = None, right: str | None = None)
Bases:
ConnectingGroupPlaces children in a stack like Col (side by side like Row with horizontal=True) and connects them in series, modeling a current path: the pin of each child facing the next child is connected to the next child’s pin facing back. Port children connect through their net. A junction’s net is adopted from whichever facing pin is already connected, so it can be named by wiring one pin explicitly (e.g.
.d -- y). Otherwise an anonymous net is created.The default alignment ‘pins’ aligns each pair’s facing pins on one line, giving straight junction wires. Pass align=’center’ to center the children’s bounding boxes instead.
- class ordec.core.arrange.Parallel(gap: int = 2, align: str | None = None, anchor='auto', horizontal: bool = False, top: str | None = None, bottom: str | None = None, left: str | None = None, right: str | None = None)
Bases:
ConnectingGroupPlaces children side by side like Row (stacked like Col with horizontal=True) and connects them in parallel: all upward-facing pins are tied to one rail net and all downward-facing pins to another (left-/right-facing with horizontal=True). To name a rail net, wire one child pin explicitly (e.g.
.d -- y). Port children are not supported. Wire ports to the rail nets explicitly.
Internals
While the viewgen body runs, a group only records its children in
declaration order. The view context emits all top-level groups during
postprocessing: ArrangementGroup.emit() resolves connectivity
(parent before child, since ORDB cannot merge nets), computes the rigid
relative arrangement() of the children and
constrains each child’s position to the first child by a constant
offset, anchoring top-level groups. User code only calls these methods
when using groups outside a viewgen.
- class ordec.core.arrange.Endpoint(net: Net = None, attach: Callable = None)
Connectable boundary of an arrangement group child: an instance pin, a port’s net or a nested group’s boundary/rail.
- attach: Callable = None
Callable that connects the endpoint to a given net, or None for endpoints that are themselves nets (ports).
- class ordec.core.arrange.ArrangedRects(rects: list, offsets: list, width: float, height: float)
Rigid relative layout of a group’s children, see ArrangementGroup.arrangement().
- ArrangementGroup.add(child)
Appends a child (an instance, a port/net or a nested group) and returns it. Raises TypeError if the group is sealed because its outline was already used (see rect()), and ValueError if the child was already added.
- ArrangementGroup.child_rect(child) Rect4LinearTerm
Returns a child’s bounding box in view coordinates. A net is placed through its port. Ports are zero-size points at their position.
- ArrangementGroup.arrangement() ArrangedRects
Computes the relative arrangement of the children. Since child sizes are constants, the arrangement is rigid. Center alignment is snapped down to the unit grid so that on-grid children stay on grid regardless of size differences.
- ArrangementGroup.rect() Rect4LinearTerm
Returns the group’s bounding box, anchored on the first child. Reading it seals the group against further children (terms derived from it would not follow).
- ArrangementGroup.resolve_connectivity()
Establishes the group’s electrical connections, a no-op for the purely geometric groups. emit() resolves a group’s connectivity before its children’s: a nested group receives its boundary nets from the enclosing group here and ties its internals to them when its own turn comes (ORDB cannot merge nets afterwards).
- ArrangementGroup.emit(solver: Solver, toplevel: bool = True, auto_anchor: tuple = (0, 0)) bool
Resolves the group’s connectivity and emits its placement constraints into solver. Called by the view context during postprocessing. Call manually when using groups outside a viewgen. Nested groups are emitted with toplevel=False and never anchored. auto_anchor is the position used for anchor=’auto’. Returns True if the automatic anchor was applied.
- ConnectingGroup.facing_pin(inst, side: D4) str
Returns the name of the single pin of an instance child that faces side, honoring the group’s pin name overrides. Raises ValueError if no unambiguous, plainly named pin is found.