Grid / pack / align / distribute

Grid / pack / align / distribute#

Grid#

The gf.components.grid() function can take a list (or 2D array) of objects and arrange them along a grid. This is often useful for making parameter sweeps. If the separation argument is true, grid is arranged such that the elements are guaranteed not to touch, with a spacing distance between them. If separation is false, elements are spaced evenly along a grid. The align_x/align_y arguments specify intra-row/intra-column alignment. The edge_x/edge_y arguments specify inter-row/inter-column alignment (unused if separation = True).

import gdsfactory as gf

components_list = []
for width1 in [1, 6, 9]:
    for width2 in [1, 2, 4, 8]:
        D = gf.components.taper(length=10, width1=width1, width2=width2, layer=(1, 0))
        components_list.append(D)

c = gf.grid(
    components_list,
    spacing=(5, 1),
    separation=True,
    shape=(3, 4),
    align_x="x",
    align_y="y",
    edge_x="x",
    edge_y="ymax",
)
c.plot()
../_images/cdbe75405a4fc0ffb88652009e337b707b079da7d3d8d2aac3c226f264910de0.png

Pack#

The gf.pack() function packs geometries together into rectangular bins. If a max_size is specified, the function will create as many bins as is necessary to pack all the geometries and then return a list of the filled-bin Components.

Here we generate several random shapes then pack them together automatically. We allow the bin to be as large as needed to fit all the Components by specifying max_size = (None, None). By setting aspect_ratio = (2,1), we specify the rectangular bin it tries to pack them into should be twice as wide as it is tall:

import numpy as np

import gdsfactory as gf

np.random.seed(5)
D_list = [gf.components.rectangle(size=(i, i)) for i in range(1, 10)]

D_packed_list = gf.pack(
    D_list,  # Must be a list or tuple of Components
    spacing=1.25,  # Minimum distance between adjacent shapes
    aspect_ratio=(2, 1),  # (width, height) ratio of the rectangular bin
    max_size=(None, None),  # Limits the size into which the shapes will be packed
    density=1.05,  # Values closer to 1 pack tighter but require more computation
    sort_by_area=True,  # Pre-sorts the shapes by area
)
D = D_packed_list[0]  # Only one bin was created, so we plot that
D.plot()
../_images/e5ae6808f7e65a4430416e0a3afce3dd9cf3168cefc564ed424b083eef944ff3.png

Say we need to pack many shapes into multiple 500x500 unit die. If we set max_size = (500,500) the shapes will be packed into as many 500x500 unit die as required to fit them all:

np.random.seed(1)
D_list = [
    gf.components.ellipse(radii=tuple(np.random.rand(2) * n + 2)) for n in range(120)
]
D_packed_list = gf.pack(
    D_list,  # Must be a list or tuple of Components
    spacing=4,  # Minimum distance between adjacent shapes
    aspect_ratio=(1, 1),  # Shape of the box
    max_size=(500, 500),  # Limits the size into which the shapes will be packed
    density=1.05,  # Values closer to 1 pack tighter but require more computation
    sort_by_area=True,  # Pre-sorts the shapes by area
)

# Put all packed bins into a single device and spread them out with distribute()
F = gf.Component("packed")
[F.add_ref(D) for D in D_packed_list]
F.distribute(elements="all", direction="x", spacing=100, separation=True)
F.plot()
2024-04-27 01:04:49.521 | WARNING  | gdsfactory.pack:pack:249 - UserWarning: unable to pack in one component, creating 4 components
../_images/bdcb80521ba72f7286d30c2ea799b0d98eeeb8262c5707009ca623b24b4e207f.png

Note that the packing problem is an NP-complete problem, so gf.components.packer() may be slow if there are more than a few hundred Components to pack (in that case, try pre-packing a few dozen at a time then packing the resulting bins). Requires the rectpack python package.

Distribute#

The distribute() function allows you to space out elements within a Component evenly in the x or y direction. It is meant to duplicate the distribute functionality present in Inkscape / Adobe Illustrator:

Say we start out with a few random-sized rectangles we want to space out:

c = gf.Component("rectangles")
# Create different-sized rectangles and add them to D
[
    c.add_ref(
        gf.components.rectangle(size=[n * 15 + 20, n * 15 + 20], layer=(2, 0))
    ).move([n, n * 4])
    for n in [0, 2, 3, 1, 2]
]
c.plot()
../_images/a001e473fac36bcd1c94705ace6200a468a065cae8d6e760b036fc4ef59f0869.png

Oftentimes, we want to guarantee some distance between the objects. By setting separation = True we move each object such that there is spacing distance between them:

D = gf.Component("rectangles_separated")
# Create different-sized rectangles and add them to D
[
    D.add_ref(gf.components.rectangle(size=[n * 15 + 20, n * 15 + 20])).move((n, n * 4))
    for n in [0, 2, 3, 1, 2]
]
# Distribute all the rectangles in D along the x-direction with a separation of 5
D.distribute(
    elements="all",  # either 'all' or a list of objects
    direction="x",  # 'x' or 'y'
    spacing=5,
    separation=True,
)
D.plot()
../_images/e9e9f7cc0c73f2e9e2d78a9a4b5b317c1d414dfa8277b74991d10cb102f2502a.png

Alternatively, we can spread them out on a fixed grid by setting separation = False. Here we align the left edge (edge = 'min') of each object along a grid spacing of 100:

D = gf.Component("spacing100")
[
    D.add_ref(gf.components.rectangle(size=[n * 15 + 20, n * 15 + 20])).move((n, n * 4))
    for n in [0, 2, 3, 1, 2]
]
D.distribute(
    elements="all", direction="x", spacing=100, separation=False, edge="xmin"
)  # edge must be either 'xmin' (left), 'xmax' (right), or 'x' (center)
D.plot()
../_images/3ffc58e703042da8a02dbe68fac6a533d48a01706ac02c92dec6a5d2212d171c.png

The alignment can be done along the right edge as well by setting edge = 'max', or along the center by setting edge = 'center' like in the following:

D = gf.Component("alignment")
[
    D.add_ref(gf.components.rectangle(size=[n * 15 + 20, n * 15 + 20])).move(
        (n - 10, n * 4)
    )
    for n in [0, 2, 3, 1, 2]
]
D.distribute(
    elements="all", direction="x", spacing=100, separation=False, edge="x"
)  # edge must be either 'xmin' (left), 'xmax' (right), or 'x' (center)
D.plot()
../_images/a52151865d39ad120bc01541c536810f5d6b48db853715852b785c313301e5f2.png

Align#

The align() function allows you to elements within a Component horizontally or vertically. It is meant to duplicate the alignment functionality present in Inkscape / Adobe Illustrator:

Say we distribute() a few objects, but they’re all misaligned:

D = gf.Component("distribute")
# Create different-sized rectangles and add them to D then distribute them
[
    D.add_ref(gf.components.rectangle(size=[n * 15 + 20, n * 15 + 20])).move((n, n * 4))
    for n in [0, 2, 3, 1, 2]
]
D.distribute(elements="all", direction="x", spacing=5, separation=True)
D.plot()
../_images/e9e9f7cc0c73f2e9e2d78a9a4b5b317c1d414dfa8277b74991d10cb102f2502a.png

we can use the align() function to align their top edges (``alignment = ‘ymax’):

D = gf.Component("align")
# Create different-sized rectangles and add them to D then distribute them
[
    D.add_ref(gf.components.rectangle(size=[n * 15 + 20, n * 15 + 20])).move((n, n * 4))
    for n in [0, 2, 3, 1, 2]
]
D.distribute(elements="all", direction="x", spacing=5, separation=True)

# Align top edges
D.align(elements="all", alignment="ymax")
D.plot()
../_images/6728630405b0451aed5722ebecde669b1938d4c634bb7a9bf5fdc2020bd9a5c4.png

or align their centers (``alignment = ‘y’):

D = gf.Component("distribute_align_y")
# Create different-sized rectangles and add them to D then distribute them
[
    D.add_ref(gf.components.rectangle(size=[n * 15 + 20, n * 15 + 20])).move((n, n * 4))
    for n in [0, 2, 3, 1, 2]
]
D.distribute(elements="all", direction="x", spacing=5, separation=True)

# Align top edges
D.align(elements="all", alignment="y")
D.plot()
../_images/fca7bb2cc1a0a78ef060359525193eb1bfc209eee449e0e0607159a3a4468403.png

other valid alignment options include 'xmin', 'x', 'xmax', 'ymin', 'y', and 'ymax'