Directional coupler quick reference for quantum engineers

The orientation of directional couplers always confuses me, so I wrote some notes for myself to help me figure it out. Recently, I found out that I’m not alone—many of my colleagues are struggling with them as well. This motivated me to polish my notes and put them online. I hope you find them useful!

Often, directional couplers are drawn like this:

Symbol of a directional coupler with ports labeled "In / P1", "Out / P2", and "Coupled / P3".

Or sometimes like this, with the coupled port confusingly on the other side:

Alternative symbol of a directional coupler with ports labeled "In / P1", "Out / P2", and "Coupled / P3", and a terminated port.

Apart from the two symbols that are easy to mix up, I think the main source of confusion is that couplers have ports explicitly labeled “in” and “out”, but there are three different ways to use them, and the “in” and “out” labels only really apply in one configuration. When used for combining signals, the roles are actually completely reversed! Sometimes, the ports are labeled just P1, P2 and P3, but that’s even less meaningful in my opinion.

So here’s how to orient a directional coupler in all configurations. I’ve added the usual labels in parenthesis, so that you can compare to what’s written on your coupler.

Splitting a signal

Diagram showing directional couplers used for splitting a signal: The input signal goes to In / P1, and it comes out of the Out / P2 port, as well as the coupled port with some attenuation.

The signal coming out of the coupled port is attenuated by the specified coupling of the coupler, usually 10 or 20 dB. For 10 dB coupling, 90% or about −0.46 dB of the power goes through port P2, and for 20 dB, 99% or about −0.044 dB goes through P2.

Combining two signals

Diagram showing directional couplers used for combining signals: Signals coming from Out / P2 and Coupled / P3 are combined to a signal that is output from In / P1.

Note the reversed roles of “input” and “output”! The signal from the coupled port will be attenuated by the specified coupling. The rest of the power is absorbed in the terminated port.

Reflection measurement

Diagram showing directional couplers used for a reflection measurement: The input signal comes in through the coupled port / P3, goes out of the In / P1 port, gets reflected from the device under test, and goes out of the Out / P2 port.

Here also the input signal from the coupled port will be attenuated by the specified coupling. Usually we don’t care about that much, because the input power can be adjusted relatively freely in our measurements.


Here are just the couplers themselves if you want to use them in your own diagrams:

Symbol of directional coupler Symbol of directional coupler with terminated port

And here are classes for Schemdraw that I used to generate these diagrams:
import schemdraw.elements as elm
from schemdraw.segments import (
    Segment,
    SegmentPoly,
    SegmentArc,
)


class DirectionalCoupler1(elm.Element):
    """
    Basic symbol
    """
    _element_defaults = {
        'lblloc': 'bottom',
    }
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        # width and height of box
        w = 1.5
        h = 1.0
        wire_x = w - 0.3 # x position of coupled wire at the top of the box
        wire_y = 0.3 # y position of horizontal wire
        r = 0.7  # radius of the rounded corner of the coupling

        self.segments.append(SegmentPoly([(0, 0), (0, h), (w, h), (w, 0)]))
        self.segments.append(Segment([(0, wire_y), (w, wire_y)]))

        self.segments.append(
            SegmentArc(
                center=(wire_x - r, wire_y + r),
                width=2 * r,
                height=2 * r,
                theta1=-90,
                theta2=0,
            )
        )

        self.anchors["input"] = (0, wire_y)
        self.anchors["output"] = (w, wire_y)
        self.anchors["cpl"] = (wire_x, h)


class DirectionalCoupler2(elm.Element):
    """
    Terminated coupler
    """
    _element_defaults = {
        'lblloc': 'bottom',
    }
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

        ws = 0.25 # space between wires and edge of box
        inner_w = 1.0  # space between vertical wires
        inner_h = 0.3  # space between horizontal wire and top of the box
        r = 0.2  # radius of the rounded corner of the coupling
        term_size = 0.3  # size of the capping terminal
        coupler_spacing = 0.25  # spacing between in/out wire and coupled wire

        # total width of the box
        w = 2 * ws + inner_w
        # height of the box, excluding the terminator
        h = ws + coupler_spacing + r + inner_h
        # x coordinates of left and right wire
        lx = ws
        rx = w - ws
        # y-coordinate of bottom endpoint of vertical wires
        cpl_wire_y = coupler_spacing + ws + r

        self.segments.append(SegmentPoly([(0, 0), (0, h), (w, h), (w, 0)]))
        self.segments.append(Segment([(0, ws), (w, ws)]))

        # terminator
        self.segments.append(Segment([
            (rx - 0.5 * term_size, h),
            (rx - 0.5 * term_size, h + term_size),
            (rx + 0.5 * term_size, h + term_size),
            (rx + 0.5 * term_size, h),
        ]))

        self.segments.append(Segment([(lx, cpl_wire_y), (lx, h)]))
        self.segments.append(Segment([(rx, cpl_wire_y), (rx, h)]))
        self.segments.append(
            Segment([
                (lx + r, coupler_spacing + ws),
                (rx - r, coupler_spacing + ws),
            ])
        )
        self.segments.append(
            SegmentArc(
                center=(lx + r, cpl_wire_y),
                width=2 * r,
                height=2 * r,
                theta1=180,
                theta2=-90,
            )
        )
        self.segments.append(
            SegmentArc(
                center=(rx - r, cpl_wire_y),
                width=2 * r,
                height=2 * r,
                theta1=-90,
                theta2=0,
            )
        )

        self.anchors["input"] = (0, ws)
        self.anchors["output"] = (w, ws)
        self.anchors["cpl"] = (lx, h)
Here’s an example Schemdraw snippet showing a coupler with labels and arrows:
from schemdraw.util import Point

def P(x, y):
    return Point((x, y))

with schemdraw.Drawing(show=False) as d:
    dc = DirectionalCoupler1()

    l = 0.75  # arrow length
    s = 0.15  # spacing between arrow and coupler
    oy = 0.1  # label offsets for in/out

    (
        elm.Annotate(arrow="<-")
        .at(dc.input + P(-s, 0))
        .label("In / P1", valign="center", halign="right", ofst=(-0.1, oy))
        .delta(dx=-l, dy=0)
    )
    (
        elm.Annotate(arrow="->", th2=270)
        .at(dc.output + P(s, 0))
        .label("Out / P2", valign="center", halign="left", ofst=(0.05, oy))
        .delta(dx=l, dy=0)
    )
    (
        elm.Annotate(arrow="->", th2=270)
        .at(dc.cpl + P(0, s))
        .label("Coupled / P3", valign="bottom", ofst=(0, 0.2))
        .delta(dx=0, dy=l)
    )

d.save("my-coupler.svg")

The diagrams and code are licensed under CC-BY-SA 4.0 (the same license as Stack Overflow), so go ahead and grab a copy!


Further reading

Thanks to Aashish Sah and Heikki Suominen for helpful comments.