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 arguments for automatically extending the leads of the two terminals to fit the 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.add(elm.Capacitor())
d.add(elm.Resistor())
d.add(elm.Diode())
d.draw()
../_images/placement_1_0.svg

If a direction parameter is provided, the element is rotated in that direction, and future elements take the same direction:

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

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

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

Using anchors

The (x, y) position can also be specified using the at keyword. Rather than using exact numerical coordinates, the at keyword 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.

Once an element is added to the drawing, it contains attributes defining the coordinates of all the element’s anchors. For example, to draw an opamp and place a resistor on the output, store the return from add to a variable. Then set the at parameter of the new element as the out attribute of the existing element. The current Drawing position is ignored, and 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

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

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

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

d.add(elm.Resistor(label='R2'))
d.add(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 l parameter sets an exact length for an element.

d = schemdraw.Drawing()
d.add(elm.Dot())
d.add(elm.Resistor())
d.add(elm.Dot())
d.add(elm.Diode(l=6))
d.add(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 parameter will set an exact endpoint for a 2-terminal 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 parameters 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 endpts parameter.

R = d.add(elm.Resistor())
Q = d.add(elm.Diode('down', l=6))
d.add(elm.Line('left', tox=R.start))
d.add(elm.Capacitor('up', toy=R.start))
d.add(elm.SourceV(endpts=[Q.end, R.start]))
../_images/placement_22_0.svg

Orientation

The flip and reverse keywords are useful for changing direction of directional elements such as Diodes, but they do not affect the d or theta parameters.

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

Drawing State

The schemdraw.Drawing maintains a drawing state that includes the current x, y position and drawing direction. 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.add(elm.Inductor)
d.add(elm.Dot)
d.push()  # Save this drawing position/direction for later

d.add(elm.Capacitor(d='down'))
d.pop()   # Return to the pushed position/direction
d.add(elm.Diode)
../_images/placement_28_0.svg

Labels

Labels are added to elements using other keyword arguments to the schemdraw.elements.Element class. Each label is a string, but LaTeX math is rendered when enclosed in $..$.

  • label: add a label in the default location for this element
  • toplabel: add a label above the top of the element
  • botlabel: add a label below the bottom of the element
  • rgtlabel: add a label to the right of the element
  • lftlabel: add a label to the left of the element

These directions do not depend on rotation. A lftlabel is always on the left side of the element.

d.add(elm.Resistor(label='Label', botlabel='Bottom', rgtlabel='Right', lftlabel='Left'))
../_images/placement_31_0.svg

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

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

See the schemdraw.elements.Element definition for parameters that control label offest, locaiton, rotation and size.

d.add(elm.Resistor(label='no offset'))
d.add(elm.Resistor(label='offset', lblofst=1))
d.add(elm.Resistor(theta=-45, label='no rotate'))
d.add(elm.Resistor(theta=-45, label='rotate', lblrotate=True))
../_images/placement_37_0.svg

For more control over label behavior, use the schemdraw.elements.Element.add_label() method. Using this method, labels can be added at arbitrary positions with any alignment.

Current Labels

To label the current through an element, the CurrentLabel element is defined. Typically, it is easier to add this element alongside an existing element using the schemdraw.Drawing.labelI() method.

R1 = d.add(elm.Resistor)
d.labelI(R1, '10 mA')
../_images/placement_40_0.svg

Alternatively, current labels can be drawn inline as arrowheads on the leads of 2-terminal elements using schemdraw.Drawing.labelI_inline().

R1 = d.add(elm.Resistor)
d.labelI_inline(R1, '$i_1$', d='in')
../_images/placement_43_0.svg

Loop currents can be added using schemdraw.Drawing.loopI().

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

Styling

Styling parameters include color, fill, lw (linewidth), ls (linestyle), fontsize and font. If a style parameter is not provided when creating an Element, its value is obtained from the element class definition or from the drawing defaults, in that order.

d = schemdraw.Drawing(color='blue', fill='lightgray')  # All elements are blue with lightgray fill unless specified otherwise
d.add(elm.Diode())
d.add(elm.Diode(fill='red'))   # Fill overrides drawing value here
d.add(elm.Resistor(fill='purple'))  # Fill has no effect on this non-closed element
d.add(elm.RBox(color='orange', ls='--'))
d.add(elm.Resistor(lw=5))
../_images/placement_48_0.svg

Walrus Mode

Python 3.8’s new walrus operator (:=) allows for adding elements and referencing them directly to the Drawing initialization. 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
schemdraw.Drawing(
    Q1 := elm.BjtNpn(label='$Q_1$'),
    elm.Resistor('left', at=(Q1, 'base'), label='$R_1$', lftlabel='$V_{in}$'),
    elm.Resistor('up', at=(Q1, 'collector'), label='$R_2$', rgtlabel='$V_{cc}$'),
    elm.Ground(at=(Q1, 'emitter'))
    )
../_images/placement_49_0.svg

Legacy Mode

Before version 0.7, schemdraw defined elements using dictionaries. In 0.7 elements were upgraded to classes, but a translation lookup still exists so that most old-style schematics are still supported.

d = schemdraw.Drawing()
d.add(elm.RES, d='right', label='1$\Omega$')
d.add(elm.CAP, d='down', label='10$\mu$F')
d.add(elm.LINE, d='left')
d.add(elm.SOURCE_SIN, d='up', label='10V')
d.draw()
../_images/placement_50_0.svg