AC simulation of arbitrary networks (from spice netlists)

Hello guys,

I want to create a Julia project for AC simulation of arbitrary networks (from spice netlists) using nodal or loop analysis and then calculate voltage and currents for all elements.

There are only R, L, C lumped elements and a voltage source and the nodes are named 0 and Nxxx in the netlist.

My goal is to read some netlists from spice and automatically solve the ODEs.

Are there maybe any libraries for that?

1 Like

I don’t know this general area very well, but you could start by taking a look at https://github.com/CedarEDA/CedarEDA.jl. It seems to do what you want but it might not be completely (publicly) available yet.

1 Like

Would a periodic steady state or frequency domain solution satisfy your requirements?

1 Like

A solution in the frequency domain would meet the requirements since I only need to calculate the node voltages at a fixed frequency of 100 Hz in the first step.

1 Like

In that case, you might find JosephsonCircuits.jl helpful. It is a harmonic balance solver for arbitrary netlists containing capacitors, inductors, resistors, mutual inductors, and Josephson junctions (which you don’t need). It performs nodal analysis in the node flux basis. Here is an example of simulating the scattering parameters and node voltages of a transmission line with an impedance mismatch. The input is a SPICE style netlist with a unique component name, the two nodes, and the component value (here I define them with symbolic variables but you can enter numbers if you prefer):

using JosephsonCircuits
using Plots

@variables R C L
circuit = Tuple{String,String,String,Num}[]

# port on the input side
push!(circuit,("P$(1)_$(0)","1","0",1))
push!(circuit,("R$(1)_$(0)","1","0",R))
Ncells=50
#first half unit cell
push!(circuit,("C$(1)_$(0)","1","0",C/2))
push!(circuit,("L$(1)_$(2)","1","2",L)) 

j=2
for i = 2:Ncells-1
    
    push!(circuit,("C$(j)_$(0)","$(j)","$(0)",C))
    push!(circuit,("L$(j)_$(j+1)","$(j)","$(j+1)",L))

    # increment the index
    j+=1

end

#last jj
push!(circuit,("C$(j)_$(0)","$(j)","$(0)",C/2))
push!(circuit,("R$(j)_$(0)","$(j)","$(0)",R))
# port on the output side
push!(circuit,("P$(j)_$(0)","$(j)","$(0)",2))


circuitdefs = Dict(
    L => 5e-10,
    C => 45.0e-15,
    R => 50.0,
)

w=2*pi*(1.0:0.01:80)*1e9
@time sol = hblinsolve(w, circuit, circuitdefs;returnvoltage=true)

p1=plot(sol.w/(2*pi*1e9),
    10*log10.(abs2.(sol.S(
            outputmode=(0,),
            outputport=2,
            inputmode=(0,),
            inputport=1,
            freqindex=:),
    )),
    ylim=(-40,30),label="S21",
    xlabel="Signal Frequency (GHz)",
    legend=:bottomright,
    title="Scattering Parameters",
    ylabel="dB")

plot!(sol.w/(2*pi*1e9),
    10*log10.(abs2.(sol.S((0,),1,(0,),2,:))),
    label="S12",
    )

plot!(sol.w/(2*pi*1e9),
    10*log10.(abs2.(sol.S((0,),1,(0,),1,:))),
    label="S11",
    )

plot!(sol.w/(2*pi*1e9),
    10*log10.(abs2.(sol.S((0,),2,(0,),2,:))),
    label="S22",
    )

image

and we can compare the simulated voltages at one node to those from the SPICE solver WRspice packaged in XicTools_jll.jl (or Xyce.jl, etc).

using XicTools_jll
n = JosephsonCircuits.exportnetlist(circuit,circuitdefs);
input = JosephsonCircuits.wrspice_input_ac(n.netlist,sol.w/(2*pi),(2,1),n.portcurrent); # warning: this wrapper code is very crude
@time output = JosephsonCircuits.spice_run(input,XicTools_jll.wrspice());

and plot them both:

plot(sol.w/(2*pi*1e9),real.(sol.voltage(
        outputmode=(0,),
        node="1",
        inputmode=(0,),
        inputport=1,
        freqindex=:)),label="Re, JosephsonCircuits.jl",ylabel="Voltage (V)",xlabel="Frequency (GHz)")
plot!(sol.w/(2*pi*1e9),imag.(sol.voltage(
        outputmode=(0,),
        node="1",
        inputmode=(0,),
        inputport=1,
        freqindex=:)),label="Im, JosephsonCircuits.jl")

plot!(sol.w/(2*pi*1e9),
    real.(output.values["V"][1,:]),
    linecolor=:black,
    linestyle=:dash,
    label="Re, WRspice")
plot!(sol.w/(2*pi*1e9),
    imag.(output.values["V"][1,:]),
    linecolor=:gray,
    linestyle=:dash,
    label="Im, WRspice")

image

You could also take the sparse capacitance, conductance, and inverse inductances matrices generated from the netlist and solve them yourself (a lot of the codebase is for dealing with nonlinear systems and quantum noise which you probably don’t need). Here is a snippet of code to generate those:

nm = JosephsonCircuits.numericmatrices(circuit, circuitdefs);
# capacitance matrix
nm.Cnm
# inverse inductance matrix
nm.invLnm
# conductance matrix
nm.Gnm
1 Like