SPICE is an electrical circuit simulation program originating from UC Berkeley. Your goal is to implement a minimalistic version which can solve for the nodal voltages and branch currents of a circuit composed of resistors and DC current sources.
Input Format
The input may come from a file, io stream, or the nearest equivalent in your language of choice. However, it cannot be a function input or hard-coded string. The format should be a subset of what the original SPICE used. A quick summary of what is required for the input format:
- First line is a title line
- Following the title is the description of circuit components and their connectivity. This may be in any order (i.e. you can't assume all resistors come before current sources/vice-versa). See component descriptors below for how component descriptions are formatted.
- Following the circuit components is the command control block. The only solver you need to implement is the DC operating point solver (
.OP
) - Following the command control block are the values to output (nodal voltages/branch currents). See output descriptors below for how these should be formatted.
- The last line is
.END
(you may choose whether an ending carriage return is required or optional) - Any line beginning with
*
denotes a comment and must be skipped by your parser.
Component Descriptors
All component descriptors are in the form:
XN A B C
Where:
X
isR
for resistor orI
for a current sourceXN
is the name of the component.N
may be any alphanumeric sequence.A
is a non-negative integer indicating which node the positive terminal for the component is connected.B
is a non-negative integer indicating which node the negative terminal for the component is connected.C
is a decimal value (for a resistor this is the resistance, and for a current source this is the current from this source)- By convention, node
0
is considered ground and is at0V
. All other nodal voltages are referenced from ground. - By convention, positive current flows from the positive terminal to the negative terminal.
Output descriptors
The only output format you need to support is .PRINT
. This may output to a file, output stream, or the nearest equivalent in your language of choice. It may not be just a function return string.
Format of .PRINT statements:
.PRINT X(N)
X
isV
for printing a nodal voltage orI
for printing the current through a component.N
is the name of the quantity being outputted. For a nodal voltage, this is any non-negative number. For a current, this is the component name.
For each print statement, you should output the argument and the appropriate value v
:
X(N) v
You may print the output in any order so long as there's one output per line.
Things you don't have to support
- Subcircuits
- "Line continuations" (i.e. any lines starting with a
+
) - "SI" suffixes (e.g.
1k
instead of 1000). However, your code should be able to handle decimal input (you may specify the format) - Error handling (i.e. you may assume the input is properly formatted and has a unique solution)
- You may specify any case requirements (all upper case, all lower case, case insensitive, case sensitive, etc.)
- You may choose to require that the nodes may be ordered sequentially without skipping any integers.
- You may use any solver algorithm you want.
Scoring and Misc.
- This is code golf, shortest code in bytes wins.
- You may use any libraries you wish provided it wasn't designed specifically for handling circuits. Ex.: Using BLAS/LAPACK for matrix math is ok, but you can't use Ngspice because it is a SPICE circuit simulator already.
- Any other standard loop holes are prohibited.
- For guidance on how to implement a solver, here's a quick guide on modified nodal analysis. Note that because there are no voltage sources you can implement only the basic nodal analysis part (I link this resource because it has some good insights on how to implement such a solver). Additional resources on MNA and another another approach, sparse tableau analysis, can be found here.
Example inputs and expected outputs
Ex. 1
Example 1
* this is a comment
R1 1 0 1
I1 0 1 1
.OP
.PRINT I(R1)
.PRINT V(1)
.END
Output:
I(R1) 1
V(1) 1
Ex. 2
Example 2
I1 1 0 1
* this is a comment
R1 1 0 1
.OP
.PRINT V(0)
.PRINT I(R1)
.PRINT I(I1)
.PRINT V(1)
.END
Output:
V(0) 0
I(R1) -1
I(I1) 1
V(1) -1
Ex. 3
Example 2
ISINK 1 0 1.0
ISOURCE 0 2 2
R1 1 0 10
R2 1 2 1
.OP
.PRINT V(1)
.PRINT V(2)
.PRINT I(R1)
.PRINT I(ISOURCE)
.PRINT I(R2)
.END
Output:
V(1) 10
V(2) 12
I(R1) 1
I(ISOURCE) 2
I(R2) -2