ipkiss3.all.PlaceRelative

class ipkiss3.all.PlaceRelative(pos_selector1, pos_selector2, offset, angle=None)

Specifies that an instance (inst1) or a port (inst1:port) should be placed relative to another instance (inst2) or a port (inst2:port) with a given offset (x, y) and an angle (optional).

If angle is not specified (i.e. angle=None), then the instance will keep its original transformations.

If angle is specified, the original transformations will be overridden and the instance will be rotated.

If you specify an angle and choose to place a port that has an angle, then the instance is rotated such that the port’s angle will equal this desired angle.

If you place a port with an undefined port angle (angle=None), and you specify an angle in Place, then the port’s angle will stay ‘None’ but its instance will be rotated.

Examples

>>> # Place 'inst1' relative to 'inst2' with an offset (10, 15). Original transformations are kept.
>>> i3.PlaceRelative('inst1', 'inst2', (10, 15))
>>> # Place 'inst1' relative to 'inst2' with an offset (10, 15). Rotation of 'inst1' is 45.
>>> i3.PlaceRelative('inst1', 'inst2', (10, 15), angle=45)
>>> # Place port 'out' of 'inst1' (with port angle 180 degrees) relative to port 'in' from 'inst2' with an offset (10, 0).
>>> i3.PlaceRelative('inst1:out', 'inst2:in', (10, 0), angle=180)
>>> # Place port 'out' of 'inst1' (with a rotation of 180 degrees if the original port angle is None) relative to port 'in' from 'inst2' with an offset (10, 0).
>>> i3.PlaceRelative('inst1:out', 'inst2:in', (10, 0), angle=180)
>>> # Place port 'out' of 'inst1' relative to port 'in' from 'inst2' with an offset (-10, -10). Original transformations are kept.
>>> i3.PlaceRelative('inst1:out', 'inst2:in', (-10, -10))

Below a few examples that illustrate how to use the PlaceRelative spec to put instances or ports relative to other instances. In the examples we use LayoutCell to quickly visualize the results.

Optical components

import ipkiss3.all as i3
from pylab import plt

wg1 = i3.RoundedWaveguide().Layout(shape=[
  (0, 0),
  (10, 0),
])

wg2 = i3.RoundedWaveguide().Layout(shape=[
  (-5, 0),
  (0, 0),
  (10, 10),
])

insts = i3.place_insts({
  'wg1': wg1,
  'wg2': wg2
}, [
  # this specifies that the origin '(0, 0)' of 'wg1' should be at (10, 0) and that 'wg2' should be placed 10 micron
  # further in both X and Y direction, relative to 'wg1'.
  i3.Place('wg1', (10, 0)),
  i3.PlaceRelative('wg2', 'wg1', (10, 10))
])

i3.LayoutCell().Layout(instances=insts, ports=insts['wg1'].ports + insts['wg2'].ports).visualize(annotate=True)
ax = plt.gca()
ax.hlines(xmin=-20, xmax=20, y=0, linestyle='dashed')
ax.vlines(ymin=-20, ymax=20, x=0, linestyle='dashed')
../../../_images/ipkiss3-all-PlaceRelative-1.png

Instead of specifying the position of the origin of an instance, we can also specify that a port should be placed relative to another instance or its port.

insts = i3.place_insts({
  'wg1': wg1,
  'wg2': wg2
}, [
  # We specify that the 'in' port of 'wg2' should be placed 10 micron further in both X and Y direction.
  # (This corresponds to (20, 10)).
  i3.Place('wg1', (10, 0)),
  i3.PlaceRelative('wg2:in', 'wg1', (10, 10) )
])

lv = i3.LayoutCell().Layout(instances=insts, ports=insts['wg1'].ports + insts['wg2'].ports)
lv.visualize(annotate=True)
ax = plt.gca()
ax.hlines(xmin=-20, xmax=20, y=0, linestyle='dashed')
ax.vlines(ymin=-20, ymax=20, x=0, linestyle='dashed')
../../../_images/ipkiss3-all-PlaceRelative-2.png

By default the transformation of the instance or the port remains untouched. But by specifying the angle attribute, we can place the instance or port with a different rotation. In this case the original transformation of the instance is discarded.

insts = i3.place_insts({
  'wg1': wg1,
  'wg2': wg2
}, [
  # origin of 'wg1' must be at (0, 0) and 'wg2' must be placed relative to 'wg1' and have an angle of 45.
  i3.Place('wg1', (0, 0)),
  i3.PlaceRelative('wg2', 'wg1', (10, 10), angle=45)
])

lv = i3.LayoutCell().Layout(instances=insts, ports=insts['wg1'].ports + insts['wg2'].ports)
lv.visualize(annotate=True)
ax = plt.gca()
ax.hlines(xmin=-20, xmax=20, y=0, linestyle='dashed')
ax.vlines(ymin=-20, ymax=20, x=0, linestyle='dashed')
../../../_images/ipkiss3-all-PlaceRelative-3.png

This works as well for ports:

insts = i3.place_insts({
  'wg1': wg1,
  'wg2': wg2
}, [
  # port 'wg2:out' must be at (15, 10) further than 'wg1' with an angle of 90 deg.
  i3.Place('wg1', (0, 0)),
  i3.PlaceRelative('wg2:in', 'wg1', (15, 10), angle=90)
])

lv = i3.LayoutCell().Layout(instances=insts, ports=insts['wg1'].ports + insts['wg2'].ports)
lv.visualize(annotate=True)
ax = plt.gca()
ax.hlines(xmin=-20, xmax=20, y=0, linestyle='dashed')
ax.vlines(ymin=-20, ymax=20, x=0, linestyle='dashed')
../../../_images/ipkiss3-all-PlaceRelative-4.png

Use the FlipH/FlipV specs to declare that you want to place a mirrored version of your component.

insts = i3.place_insts({
  'wg1': wg1,
  'wg2': wg2
}, [
  # Place a horizontally mirrored instance of 'wg2' instead of the normal one.
  i3.FlipH('wg2'),
  i3.Place('wg1', (0, 0)),
  i3.PlaceRelative('wg2', 'wg1', (10, 10))
])

lv = i3.LayoutCell().Layout(instances=insts, ports=insts['wg1'].ports + insts['wg2'].ports)
lv.visualize(annotate=True)
ax = plt.gca()
ax.hlines(xmin=-20, xmax=20, y=0, linestyle='dashed')
ax.vlines(ymin=-20, ymax=20, x=0, linestyle='dashed')
../../../_images/ipkiss3-all-PlaceRelative-5.png

When you’re providing an already rotated instance as input to place_insts and don’t specify the angle attribute of PlaceRelative, the transformation (in this case a rotation) will be kept.

insts = i3.place_insts(i3.InstanceDict([
  i3.SRef(name='wg1',
          reference=wg1,
          transformation=i3.Rotation(rotation=45.)),
  i3.SRef(name='wg2',
          reference=wg2,
          transformation=i3.Rotation(rotation=45.))
]), [
  i3.Place('wg1', (-10, 0)),
  i3.PlaceRelative('wg2', 'wg1', (20, 10))
])

lv = i3.LayoutCell().Layout(instances=insts, ports=insts['wg1'].ports + insts['wg2'].ports)
lv.visualize(annotate=True)
ax.hlines(xmin=-20, xmax=20, y=0, linestyle='dashed')
ax.vlines(ymin=-20, ymax=20, x=0, linestyle='dashed')
../../../_images/ipkiss3-all-PlaceRelative-6.png

Electrical components

The examples above are easily replicated in the electrical domain by changing i3.RoundedWaveguide to i3.ElectricalWire. For example:

import ipkiss3.all as i3
from pylab import plt

w1 = i3.ElectricalWire().Layout(shape=[
  (0, 0),
  (10, 0),
])

w2 = i3.ElectricalWire().Layout(shape=[
  (-5, 0),
  (0, 0),
  (10, 10),
])

insts = i3.place_insts({
  'w1': w1,
  'w2': w2
}, [
  # this specifies that the origin '(0, 0)' of 'w1' should be at (10, 0) and that 'w2' should be placed 10 micron
  # further in both X and Y direction, relative to 'w1'.
  i3.Place('w1', (10, 0)),
  i3.PlaceRelative('w2', 'w1', (10, 10))
])

i3.LayoutCell().Layout(instances=insts, ports=insts['w1'].ports + insts['w2'].ports).visualize(annotate=True)
ax = plt.gca()
ax.hlines(xmin=-20, xmax=20, y=0, linestyle='dashed')
ax.vlines(ymin=-20, ymax=20, x=0, linestyle='dashed')
../../../_images/ipkiss3-all-PlaceRelative-7.png

The results will mostly be the same, but there is a difference when placing electrical ports and specifiying an angle. If an angle is undefined (None), then its instance is rotated instead:

insts = i3.place_insts({
  'w1': w1,
  'w2': w2
}, [
  # port 'w2:out' must be at (15, 10) further than 'w1' and its instance rotated 90 degrees.
  i3.Place('w1', (0, 0)),
  i3.PlaceRelative('w2:in', 'w1', (15, 10), angle=90)
])

lv = i3.LayoutCell().Layout(instances=insts, ports=insts['w1'].ports + insts['w2'].ports)
lv.visualize(annotate=True)
ax = plt.gca()
ax.hlines(xmin=-20, xmax=20, y=0, linestyle='dashed')
ax.vlines(ymin=-20, ymax=20, x=0, linestyle='dashed')
../../../_images/ipkiss3-all-PlaceRelative-8.png

Compare this to the same situation using optical ports:

insts = i3.place_insts({
  'wg1': wg1,
  'wg2': wg2
}, [
  # port 'wg2:out' must be at (15, 10) further than 'wg1' with an angle of 90 deg.
  i3.Place('wg1', (0, 0)),
  i3.PlaceRelative('wg2:in', 'wg1', (15, 10), angle=90)
])

lv = i3.LayoutCell().Layout(instances=insts, ports=insts['wg1'].ports + insts['wg2'].ports)
lv.visualize(annotate=True)
ax = plt.gca()
ax.hlines(xmin=-20, xmax=20, y=0, linestyle='dashed')
ax.vlines(ymin=-20, ymax=20, x=0, linestyle='dashed')
../../../_images/ipkiss3-all-PlaceRelative-9.png

Note

If the electrical ports have defined port angles (just like optical ports), then the placement specifications of those ports will work exactly the same as with optical ports.