Adding circuit elements

There are two general categories of circuit elements. Two-terminal elements, such as Resistors and Capacitors, are subclasses of schemdraw.elements.Element2Term and have additional positioning methods that automatically extending the leads of the two terminals to fit a desired length. The standard schemdraw.elements.Element class applies to all elements regardless of the number of terminals, but the leads will not extend. These include, for example, Transistors, Opamps, and Grounds.

Placement

The position of each element can be specified in a number of ways. If no position is given, the next element will start at the current drawing position, typically where the previous element ends, and in the same drawing direction, as seen below where no position or direction parameters are provided.

d = schemdraw.Drawing()
d += elm.Capacitor()
d += elm.Resistor()
d += elm.Diode()
d.draw()
../_images/placement_1_0.svg

Remember that += is equivalent to calling d.add(). If a direction method is added to an element, the element is rotated in that direction, and future elements take the same direction:

d = schemdraw.Drawing()
d += elm.Capacitor()
d += elm.Resistor().up()
d += elm.Diode()
d.draw()
../_images/placement_2_0.svg

The theta method can be used to specify any rotation angle in degrees.

d += elm.Resistor().theta(20).label('R1')
d += elm.Resistor().label('R2')  # Takes position and direction from R1
../_images/placement_5_0.svg

Using anchors

The (x, y) position of an element can also be specified using its at method. Rather than using exact numerical coordinates, the at parameter will usually be set to an “anchor” of another element.

An anchor is simply a predefined position within an element. Two-terminal elements have anchors named start, center, and end. Three-terminal elements have other named anchors, for example an Opamp has in1, in2, and out anchors. Each element’s docstring lists the available anchors.

Once an element is added to the drawing, all its anchor positions will be added as attributes to the element object. For example, to draw an opamp and place a resistor on the output, store the Opamp instance to a variable. Then call the at method of the new element passing the out attribute of the Opamp. The current Drawing position is ignored, and is reset to the endpoint of the resistor.

opamp = d.add(elm.Opamp())
d.add(elm.Resistor().right().at(opamp.out))
../_images/placement_8_0.svg

Python’s walrus operator provides a convenient shorthand notation for adding an element using += and storing it at the same time. The above code can be written equivalently as:

d += (opamp := elm.Opamp())
d += elm.Resistor().right().at(opamp.out)

Additionally, a new element can be placed with its anchor set to the current Drawing position using the anchor method. Here, an Opamp is placed at the end of a resistor, connected to the opamp’s in1 anchor (the inverting input).

d += elm.Resistor().label('R1')
d += elm.Opamp().anchor('in1')
../_images/placement_11_0.svg

Compared to anchoring the opamp at in2 (the noninverting input):

d += elm.Resistor().label('R2')
d += elm.Opamp().anchor('in2')
../_images/placement_14_0.svg

Placing 2-Terminal Elements

Two-terminal elements hae some other placement options because their length can grow to fit a predetermined space. The length method sets an exact length for an element.

d = schemdraw.Drawing()
d += elm.Dot()
d += elm.Resistor()
d += elm.Dot()
d += elm.Diode().length(6)
d += elm.Dot()
d.draw()
../_images/placement_15_0.svg

The inner zig-zag portion of a resistor has length of 1 unit, while the default lead extensions are 1 unit on each side, making the default total resistor length 3 units. This default size can be changed using the unit parameter to the schemdraw.Drawing class.

The to method will set an exact endpoint for a 2-terminal element. The starting point is still the ending location of the previous element. Notice the Diode is longer than the standard element length in order to fill the diagonal distance.

d = schemdraw.Drawing()
R = d.add(elm.Resistor())
C = d.add(elm.Capacitor().up())
Q = d.add(elm.Diode().to(R.start))
d.draw()
../_images/placement_16_0.svg

The tox and toy methods are useful for placing 2-terminal elements to “close the loop”, without requiring an exact length. Here, the Line element does not need to specify an exact length to fill the space and connect back with the Source.

C = d.add(elm.Capacitor())
d.add(elm.Diode())
d.add(elm.Line().down())

# Now we want to close the loop, but can use `tox`
# to avoid having to know exactly how far to go.
# Note we passed the [x, y] position of capacitor C,
# but only the x value will be used.
d.add(elm.Line().left().tox(C.start))

d.add(elm.Source().up())
../_images/placement_19_0.svg

Finally, exact endpoints can also be specified using the endpoints method.

R = d.add(elm.Resistor())
Q = d.add(elm.Diode().down().length(6))
d.add(elm.Line().left().tox(R.start))
d.add(elm.Capacitor().up().toy(R.start))
d.add(elm.SourceV().endpoints(Q.end, R.start))
../_images/placement_22_0.svg

Orientation

The flip and reverse methods are useful for changing orientation of directional elements such as Diodes, but they do not affect the drawing direction.

d += elm.Zener().label('Normal')
d += elm.Zener().flip().label('Flip')
d += elm.Zener().reverse().label('Reverse')
../_images/placement_25_0.svg

Drawing State

The schemdraw.Drawing maintains a drawing state that includes the current x, y position, stored in the Drawing.here attribute as a (x, y) tuple, and drawing direction stored in the Drawing.theta attribute. A LIFO stack of drawing states can be used, via the schemdraw.Drawing.push() and schemdraw.Drawing.pop() method, for times when it’s useful to save the drawing state and come back to it later.

d += elm.Inductor()
d += elm.Dot()
print('d.here:', d.here)
d.push()  # Save this drawing position/direction for later

d += elm.Capacitor().down()  # Go off in another direction temporarily
print('d.here:', d.here)

d.pop()   # Return to the pushed position/direction
print('d.here:', d.here)
d += elm.Diode()
d.draw()
d.here: Point(3.0,0.0)
d.here: Point(2.9999999999999996,-3.0)
d.here: Point(3.0,0.0)
../_images/placement_27_1.svg

Changing the drawing position can be accomplished by calling schemdraw.Drawing.move().

Labels

Labels are added to elements using the schemdraw.elements.Element.label() method. Some unicode utf-8 characters are allowed, such as '1μF' and '1MΩ' if the character is included in your font set. Alternatively, in the Matplotlib backend, full LaTeX math expressions can be rendered when enclosed in $..$, such as r'$\tau = \frac{1}{RC}$' For full details on LaTeX math support, see Matplotlib Mathtext. The SVG backend supports a limited subset of Mathtext, including most special characters, subscripts, and superscripts.

Subscripts and superscripts are also added using LaTeX math mode, for example:

.label('$V_0$')  # subscript 0
.label('$x^2$')  # superscript 2

The label location is specified with the loc parameter to the label method. It can be left, right, up, down, or the name of a defined anchor within the element. These directions do not depend on rotation. A label with loc=’left’ is always on the left side of the element.

d.add(elm.Resistor()
      .label('Label')
      .label('Bottom', loc='bottom')
      .label('Right', loc='right')
      .label('Left', loc='left'))
../_images/placement_30_0.svg
d.add(elm.BjtNpn()
      .label('b', loc='base')
      .label('c', loc='collector')
      .label('e', loc='emitter'))
../_images/placement_33_0.svg

Alternatively, a label may be a list/tuple of strings, which will be evenly-spaced along the length of the element. This allows for labeling positive and negative along with a component name, for example:

d += elm.Resistor().label(('–','$R_1$','+'))  # Note: using endash U+2013 character
../_images/placement_36_0.svg

The schemdraw.elements.Element.label() method also takes parameters that control the label’s rotation, offset, and color.

d += elm.Resistor().label('no offset')
d += elm.Resistor().label('offset', ofst=1)
d += elm.Resistor().theta(-45).label('no rotate')
d += elm.Resistor().theta(-45).label('rotate', rotate=True)
d += elm.Resistor().theta(45).label('90°', rotate=90)
../_images/placement_39_0.svg

Current Arrow Labels

To label the current through an element, the schemdraw.elements.lines.CurrentLabel element can be added. The at method of this element can take an Element instance to label, and the arrow will be placed over the center of that Element.

R1 = d.add(elm.Resistor())
d.add(elm.CurrentLabel().at(R1).label('10 mA'))
d.draw()
../_images/placement_41_0.svg

Alternatively, current labels can be drawn inline as arrowheads on the leads of 2-terminal elements using schemdraw.elements.lines.CurrentLabelInline. Parameters direction and start control whether the arrow is shown pointing into or out of the element, and which end to place the arrowhead on.

R1 = d.add(elm.Resistor())
d.add(elm.CurrentLabelInline(direction='in').at(R1).label('10 mA'))
../_images/placement_44_0.svg
Loop currents can be added using schemdraw.elements.lines.LoopCurrent, given

a list of 4 existing elements surrounding the loop.

R1 = d.add(elm.Resistor())
C1 = d.add(elm.Capacitor().down())
D1 = d.add(elm.Diode().fill(True).left())
L1 = d.add(elm.Inductor().up())
d.add(elm.LoopCurrent([R1, C1, D1, L1], direction='cw').label('$I_1$'))
../_images/placement_47_0.svg

Alternatively, loop current arrows can be added anywhere with any size using schemdraw.elements.lines.LoopArrow.

d = schemdraw.Drawing()
d += (a:=elm.LineDot())
d += elm.LoopArrow(width=.75, height=.75).at(a.end)
d.draw()
../_images/placement_50_0.svg

Styling

Element styling methods include color, fill, linewidth, and linestyle. If a style method is not called when creating an Element, its value is obtained from from the drawing defaults.

# All elements are blue with lightgray fill unless specified otherwise
d = schemdraw.Drawing(color='blue', fill='lightgray')

d += elm.Diode()
d += elm.Diode().fill('red')        # Fill overrides drawing color here
d += elm.Resistor().fill('purple')  # Fill has no effect on non-closed elements
d += elm.RBox().linestyle('--').color('orange')
d += elm.Resistor().linewidth(5)
../_images/placement_52_0.svg

U.S. versus European Style

By default, a Resistor and related elements (variable resistor, photo resistor, etc.) appear in IEEE/U.S. style. To configure IEC/European style, use the schemdraw.elements.style() method with either elm.STYLE_IEC or elm.STYLE_IEEE parameter.

elm.style(elm.STYLE_IEC)
d += elm.Resistor()
d.draw()
../_images/placement_54_0.svg
elm.style(elm.STYLE_IEEE)
d += elm.Resistor()
d.draw()
../_images/placement_56_0.svg

Global styles

The style method schemdraw.elements.style() can also be used to configure global styles on individual elements. Its argument is a dictionary of {name: Element} class pairs. Combined with functools.partial from the standard library, parameters to elements can be set globally. For example, the following code fills all Diode elements without adding the fill() method or fill keyword argument.

from functools import partial

elm.style({'Diode': partial(elm.Diode, fill=True)})

d = schemdraw.Drawing()
d += elm.Diode()
d.draw()
../_images/placement_57_0.svg

Be careful, though, because the style method can overwrite existing elements in the namespace.

Walrus Mode

Python 3.8’s new walrus operator (:=) allows for adding elements and assigning them to a variable all in one line. The global position of an element is not calculated until the element is actually added to the drawing, however, so setting an at position based on another element’s anchor attribute won’t work. However, the at parameter also accepts a tuple of (Element, anchorname) to allow filling in the position when the element is ready to be drawn.

This mode allows creating an entire schematic in a single call to Drawing.

# R1 can't set .at(Q1.base), because base position is not defined until Drawing is created
# But it can set .at((Q1, 'base')).
schemdraw.Drawing(
    Q1 := elm.BjtNpn().label('$Q_1$'),
    elm.Resistor().left().at((Q1, 'base')).label('$R_1$').label('$V_{in}$', 'left'),
    elm.Resistor().up().at((Q1, 'collector')).label('$R_2$').label('$V_{cc}$', 'right'),
    elm.Ground().at((Q1, 'emitter'))
    )
../_images/placement_58_0.svg

Keyword Arguments

All schemdraw.elements.Element types take keyword arguments that can also be used to set element properties, partly for historical reasons but also for easy element setup via dictionary unpacking. The keyword arguments are equivalent to calling the Element setup methods. The keyword arguments are not validated or type checked, so the chained method interface described above is recommended for configuring elements.

Keyword Argument

Method Equivalent

d=’up’

.up()

d=’down’

.down()

d=’left’

.left()

d=’right’

.right()

theta=X

.theta(X)

at=X or xy=X

.at(X)

flip=True

.flip()

reverse=True

.reverse()

anchor=X

.anchor(X)

zoom=X

.scale(X)

color=X

.color(X)

fill=X

.fill(X)

ls=X

.linestyle(X)

lw=X

.linewidth(X)

zorder=X

.zorder(X)

move_cur=False

.hold()

label=X

.label(X)

botlabel=X

.label(X, loc=’bottom’)

lftlabel=X

.label(X, loc=’left’)

rgtlabel=X

.label(X, loc=’right’)

toplabel=X

.label(X, loc=’top’)

lblloc=X

.label(…, loc=X)