Reputation: 269
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.
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()
Upvotes: 0
Views: 654
Reputation: 26
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!
Upvotes: 1
Reputation: 269
As a temporary solution, I decided to use switches.
I used switches to connect all nodes in the upper rail to an ncc
node that is kept at Vcc
and all nodes in the bottom rail to an ndd
node that is kept at Vdd
node. The switches are initially closed and which forces all nodes at the desired values. They open at some time after which I assume the simulation starts.
Codewise, I remove the following line.
simulator.initial_condition(n1=ic,n2=ic,n3=ic,n4=ic,n_2=0,n_3=0,n_4=0)
And add the following function.
def switch_generator(circuit, number_of_branches, switchingtime):
circuit.PulseVoltageSource('SV', 'SVN', circuit.gnd,initial_value=1, pulsed_value=-1,
pulse_width=finaltime, period=finaltime, delay_time=switchingtime)
circuit.R('SR1', 'SVN', circuit.gnd, 1@u_kΩ)
circuit.V('Vcc', 'ncc', circuit.gnd, 100@u_mV)
circuit.V('Vdd', 'ndd', circuit.gnd, 0@u_mV)
for i in range(1, number_of_branches - 1):
circuit.VoltageControlledSwitch('S'+str(i), 'n'+str(i), 'ncc','SVN', circuit.gnd, model='switch')
for i in range(2, number_of_branches - 1):
circuit.VoltageControlledSwitch('S_'+str(i), 'n_'+str(i), 'ndd','SVN', circuit.gnd, model='switch')
return circuit
The values I am looking for are almost identical between the two simulation. The difference is at the 7th significant figure after the decimal point.
There is some associated computational burden with this approach. The switching process seems to bring a one time extra burden but dissipates in ratio for long simulations.
Upvotes: 0