Search code examples
pythoninitializationsimulationcircuitpyspice

Simulating a circuit with initial conditions given in a loop with PySpice


I try to simulate the following simple RC circuit, to use finite element method to verify my solution to a slightly altered transmission line problem.

enter image description here

While it is not directly related to the question, R(i), R(0i) or C(i) are not necessarily equal to each other.

All nodes in the bottom rail have the same potential with each other at the start of the simulation. Similarly, all nodes in the top rail have the same potential at the start.

I can generate this circuit with PySpice in a loop, i.e.,

for i in range(1, number_of_branches - 1):
    circuit.R('R'+str(i), 'n'+str(i), 'n'+str(i+1), 1000@u_Ω)
for i in range(1, number_of_branches - 1):
    circuit.R('R_'+str(i), 'n_'+str(i), 'n_'+str(i+1), 1000@u_Ω)   
for i in range(1, number_of_branches-1):
    circuit.C('C'+str(i), 'n'+str(i), 'n_'+str(i), 100@u_μF)

However, I cannot generate the initial conditions in a loop. If I type all nodes individually, i.e.,

simulator.initial_condition(n1=ic,n2=ic,n3=ic,n4=ic,n_2=0,n_3=0,n_4=0)

the simulation results give me what I want. But this is just impossible for large number of branches. The main issue is that n1, n2, n_1, n_2... are variables recognised only by the circuit methods. These nodes are created with the string names passed when circuit elements are created if they don't already exist. I looked deep into the code. I tried to check all objects created by the circuit method to get a list of nodes which I can use to pass initial conditions but it seems above my python skills.

How can I provide initial conditions in a loop? This could be by passing a dictionary as well. I have the minimum working example below.

import PySpice.Logging.Logging as Logging
logger = Logging.setup_logging()
import matplotlib.pyplot as plt
from PySpice.Probe.Plot import plot
from PySpice.Spice.Netlist import Circuit
from PySpice.Unit import *

circuit = Circuit('Test')
steptime=.1@u_us
finaltime = 1000@u_ms
number_of_branches = 6
ic=100@u_mV


for i in range(1, number_of_branches - 1):
    circuit.R('R'+str(i), 'n'+str(i), 'n'+str(i+1), 1000@u_Ω)
for i in range(1, number_of_branches - 1):
    circuit.R('R_'+str(i), 'n_'+str(i), 'n_'+str(i+1), 1000@u_Ω)   
for i in range(1, number_of_branches-1):
    circuit.C('C'+str(i), 'n'+str(i), 'n_'+str(i), 100@u_μF)
circuit.C('C'+str(number_of_branches-1), 'n'+str(number_of_branches-1), circuit.gnd, 100@u_μF)
ra = circuit.R('Ra', 'n1', 'n_1', 10@u_Ω)


simulator = circuit.simulator(temperature=25, nominal_temperature=25)
simulator.initial_condition(n1=ic,n2=ic,n3=ic,n4=ic,n_2=0,n_3=0,n_4=0)
analysis = simulator.transient(step_time=steptime, end_time=finaltime)


figure = plt.subplots(figsize=(11, 6))
axe = plt.subplot(111)
plot(analysis['n3']-analysis['n_3'], axis=axe)
plt.show()

Solution

  • I apologize if I haven't formatted this correctly, this is my first answer to a SO post. Below is what I believe you are looking for:

    kwargDict = {}
    for n in netlist.node_names:
        kwargDict[n] = IC
    simulator.initial_conditions(**kwargDict)
    

    Pyspice's source code shows that it will accept any kwarg dictionary with the string name of the node, and a value, so you can modify kwargDict however you like to change initial conditions as you please. when you are done pass it with the double asterisk (**) to simulator.initial_conditions in place of the arg list. So from your example: (n1=ic,n2=ic,n3=ic,n4=ic,n_2=0,n_3=0,n_4=0) becomes:

    {'n1':ic,'n2':ic,'n3':ic,'n4':ic,'n_2':0,'n_3':0,'n_4':0}, the dict with string names as keys and the IC values as the values

    hope this helps!