1. Only voltage source, resistor and inductor have been included. Capacitor has not yet been tested.
2. Ammeters can be introduced in the circuit wherever needed.
So here is the code (click "view raw" below the code box to see the code in a new window):
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env python | |
import sys, math, matrix | |
from network_reader import * | |
from solver import * | |
def csv_tuple(csv_elem): | |
""" Convert a cell position from spreadsheet form | |
to [row, tuple] form. """ | |
csv_elem.upper() | |
# Create a dictionary of alphabets | |
csv_col="A B C D E F G H I J K L M N O P Q R S T U V W X Y Z" | |
csv_dict={} | |
csv_col_list=csv_col.split(" ") | |
# Make the alphabets correspond to integers | |
for c1 in range(1, 27): | |
csv_dict[csv_col_list[c1-1]]=c1 | |
# The cell position starts with a number | |
flag="number" | |
c1=0 | |
while flag=="number": | |
# When conversion to int fails | |
# it means the element is an alphabet | |
try: | |
int(csv_elem[c1]) | |
except ValueError: | |
flag="alphabet" | |
else: | |
c1+=1 | |
# Split them up into numbers and alphabets | |
pol_row=int(csv_elem[0:c1]) | |
pol_col=csv_elem[c1:] | |
elem_tuple=[pol_row-1, 0] | |
# Convert the alphabets to number | |
# Similar to converting binary to decimal | |
for c1 in range(len(pol_col)-1, -1, -1): | |
if len(pol_col)-1-c1>0: | |
elem_tuple[1]+=26*(len(pol_col)-1-c1)*csv_dict[pol_col[c1]] | |
else: | |
elem_tuple[1]+=csv_dict[pol_col[c1]]-1 | |
return elem_tuple | |
def reading_params(param_file): | |
""" Read a file. Ramove additional quotes and | |
carriage returns. Remove leading spaces. """ | |
from_file=[] | |
for line in param_file: | |
from_file.append(line.split(",")) | |
for c1 in range(len(from_file)): | |
for c2 in range(len(from_file[c1])-1, -1, -1): | |
# Remove additional quotes and carriage returns | |
if from_file[c1][c2]: | |
scrub_elements(from_file, c1, c2) | |
# Remove blank spaces and null elements | |
if from_file[c1][c2]==" " or from_file[c1][c2]=="": | |
del from_file[c1][c2] | |
return from_file | |
class Resistor: | |
def __init__(self, res_index, res_pos, res_tag): | |
self.res_number=res_index | |
self.res_pos=res_pos | |
self.res_tag=res_tag | |
self.resistor=100.0 | |
self.current=0.0 | |
self.voltage=0.0 | |
def display(self): | |
print "Resistor is ", | |
print self.res_tag, | |
print "= %f" %self.resistor, | |
print " located at ", | |
print self.res_pos | |
def ask_values(self, x_list, ckt_mat): | |
res_params=["Resistor"] | |
res_params.append(self.res_tag) | |
res_params.append(self.res_pos) | |
res_params.append(self.resistor) | |
x_list.append(res_params) | |
def get_values(self, x_list, ckt_mat): | |
self.resistor=float(x_list[0]) | |
def transfer_to_sys(self, sys_loops, mat_e, mat_a, mat_b, mat_u, source_list): | |
""" The matrix A in E.dx/dt=Ax+Bu will be updated by the | |
resistor value.""" | |
for c1 in range(len(sys_loops)): | |
for c2 in range(c1, len(sys_loops)): | |
# Updating the diagonal element corresponding | |
# to a loop. | |
if c1==c2: | |
if csv_tuple(self.res_pos) in sys_loops[c1][c2]: | |
mat_a.data[c1][c2]+=self.resistor | |
else: | |
# Updating the offdiagonal element depending | |
# on the sense of the loops (aiding or opposing) | |
for c3 in range(len(sys_loops[c1][c2])): | |
if csv_tuple(self.res_pos) in sys_loops[c1][c2][c3]: | |
if sys_loops[c1][c2][c3][-1]=="forward": | |
mat_a.data[c1][c2]+=self.resistor | |
else: | |
mat_a.data[c1][c2]-=self.resistor | |
# Because the matrices are symmetric | |
mat_a.data[c2][c1]=mat_a.data[c1][c2] | |
return | |
def generate_val(self, source_lst, sys_loops, mat_u, t, dt): | |
pass | |
def update_val(self, sys_loops, mat_e, mat_a, mat_b, state_vec, mat_u): | |
pass | |
class Inductor: | |
def __init__(self, ind_index, ind_pos, ind_tag): | |
self.ind_number=ind_index | |
self.ind_pos=ind_pos | |
self.ind_tag=ind_tag | |
self.inductor=0.001 | |
self.current=0.0 | |
self.i_dbydt=0.0 | |
self.voltage=0.0 | |
def display(self): | |
print "Inductor is ", | |
print self.ind_tag, | |
print "=%f" %self.inductor, | |
print " located at ", | |
print self.ind_pos | |
def ask_values(self, x_list, ckt_mat): | |
ind_params=["Inductor"] | |
ind_params.append(self.ind_tag) | |
ind_params.append(self.ind_pos) | |
ind_params.append(self.inductor) | |
x_list.append(ind_params) | |
def get_values(self, x_list, ckt_mat): | |
self.inductor=float(x_list[0]) | |
def transfer_to_sys(self, sys_loops, mat_e, mat_a, mat_b, mat_u, source_list): | |
""" The matrix E in E.dx/dt=Ax+Bu will be updated by the | |
inductor value.""" | |
for c1 in range(len(sys_loops)): | |
for c2 in range(c1, len(sys_loops)): | |
# Updating the diagonal element corresponding | |
# to a loop. | |
if c1==c2: | |
if csv_tuple(self.ind_pos) in sys_loops[c1][c2]: | |
mat_e.data[c1][c2]+=self.inductor | |
else: | |
# Updating the offdiagonal element depending | |
# on the sense of the loops (aiding or opposing) | |
for c3 in range(len(sys_loops[c1][c2])): | |
if csv_tuple(self.ind_pos) in sys_loops[c1][c2][c3]: | |
if sys_loops[c1][c2][c3][-1]=="forward": | |
mat_e.data[c1][c2]+=self.inductor | |
else: | |
mat_e.data[c1][c2]-=self.inductor | |
# Because the matrices are symmetric | |
mat_e.data[c2][c1]=mat_e.data[c1][c2] | |
return | |
def generate_val(self, source_lst, sys_loops, mat_u, t, dt): | |
pass | |
def update_val(self, sys_loops, mat_e, mat_a, mat_b, state_vec, mat_u): | |
pass | |
class Capacitor: | |
def __init__(self, cap_index, cap_pos, cap_tag): | |
self.cap_number=cap_index | |
self.cap_pos=cap_pos | |
self.cap_tag=cap_tag | |
self.capacitor=10.0e-6 | |
self.current=0.0 | |
self.voltage=0.0 | |
self.v_dbydt=0.0 | |
def display(self): | |
print "Capacitor is ", | |
print self.cap_tag, | |
print "= %f" %self.capacitor, | |
print " located at ", | |
print self.cap_pos, | |
print " with positive polarity towards %s" %(csv_element(self.cap_polrty)) | |
def ask_values(self, x_list, ckt_mat): | |
cap_params=["Capacitor"] | |
cap_params.append(self.cap_tag) | |
cap_params.append(self.cap_pos) | |
cap_params.append(self.capacitor) | |
# Looking for a default value of polarity | |
# in the neighbouring cells | |
self.cap_elem=csv_tuple(self.cap_pos) | |
if self.cap_elem[0]>0: | |
if ckt_mat[self.cap_elem[0]-1][self.cap_elem[1]]: | |
self.cap_polrty=[self.cap_elem[0]-1, self.cap_elem[1]] | |
if self.cap_elem[1]>0: | |
if ckt_mat[self.cap_elem[0]][self.cap_elem[1]-1]: | |
self.cap_polrty=[self.cap_elem[0], self.cap_elem[1]-1] | |
if self.cap_elem[0]<len(ckt_mat)-1: | |
if ckt_mat[self.cap_elem[0]+1][self.cap_elem[1]]: | |
self.cap_polrty=[self.cap_elem[0]+1, self.cap_elem[1]] | |
if self.cap_elem[1]<len(ckt_mat)-1: | |
if ckt_mat[self.cap_elem[0]][self.cap_elem[1]+1]: | |
self.cap_polrty=[self.cap_elem[0], self.cap_elem[1]+1] | |
cap_params.append("Positive polarity towards (cell) = %s" %csv_element(self.cap_polrty)) | |
x_list.append(cap_params) | |
def get_values(self, x_list, ckt_mat): | |
self.capacitor=float(x_list[0]) | |
cap_polrty=x_list[1].split("=")[1] | |
# Convert the human readable form of cell | |
# to [row, column] form | |
while cap_polrty[0]==" ": | |
cap_polrty=cap_polrty[1:] | |
self.cap_polrty=csv_tuple(cap_polrty) | |
if not ckt_mat[self.cap_polrty[0]][self.cap_polrty[1]]: | |
print "Polarity incorrect. Branch does not exist at %s" %csv_element(self.cap_polrty) | |
def transfer_to_sys(self, sys_loops, mat_e, mat_a, mat_b, mat_u, source_list): | |
""" The matrix B in E.dx/dt=Ax+Bu will be updated by the | |
polarity of the capacitor.""" | |
for c1 in range(len(sys_loops)): | |
if csv_tuple(self.cap_pos) in sys_loops[c1][c1]: | |
# If the positive polarity appears before the capacitor position | |
# it means as per KVL, we are moving from +ve to -ve | |
# and so the capacitor voltage will be taken negative | |
if sys_loops[c1][c1].index(self.cap_polrty)<sys_loops[c1][c1].index(csv_tuple(self.cap_pos)): | |
mat_b.data[c1][source_list.index(self.cap_pos)]=-1.0 | |
else: | |
mat_b.data[c1][source_list.index(self.cap_pos)]=1.0 | |
return | |
def generate_val(self, source_lst, sys_loops, mat_u, t, dt): | |
""" The capacitor voltage is updated in the matrix u in | |
E.dx/dt=Ax+Bu .""" | |
self.v_dbydt=self.current/self.capacitor | |
self.voltage+=self.v_dbydt*dt | |
mat_u.data[source_lst.index(self.cap_pos)][0]=self.voltage | |
def update_val(self, sys_loops, mat_e, mat_a, mat_b, state_vec, mat_u): | |
""" The capacitor current is calculated as a result of the KVL.""" | |
c1=0 | |
while c1<len(sys_loops): | |
# The first occurance of the capacitor | |
if csv_tuple(self.cap_pos) in sys_loops[c1][c1]: | |
# Check for polarity. If positive polarity is before the capacitor | |
# it means the current is entering the positive terminal and so | |
# current is positive. | |
if sys_loops[c1][c1].index(self.cap_polrty)<sys_loops[c1][c1].index(csv_tuple(self.cap_pos)): | |
self.current=state_vec.data[c1][0] | |
else: | |
self.current=-state_vec.data[c1][0] | |
# Check for capacitor in other loops with the capacitor | |
# as common branch | |
for c2 in range(c1+1, len(sys_loops)): | |
# These lists are nested lists as two loops | |
# may have separate branches in common | |
for c3 in range(len(sys_loops[c1][c2])): | |
# If capacitor is found in one of the branches. | |
# Can only be in one branch at most. | |
if csv_tuple(self.cap_pos) in sys_loops[c1][c2][c3]: | |
# Check for polarity again | |
if sys_loops[c1][c2][c3].index(self.cap_polrty)<sys_loops[c1][c2][c3].index(csv_tuple(self.cap_pos)): | |
# Then check is the loop is aiding or opposing | |
# the main loop. | |
if sys_loops[c1][c2][c3][-1]=="forward": | |
self.current+=state_vec.data[c2][0] | |
else: | |
self.current-=state_vec.data[c2][0] | |
else: | |
if sys_loops[c1][c2][c3][-1]=="forward": | |
self.current-=state_vec.data[c2][0] | |
else: | |
self.current+=state_vec.data[c2][0] | |
# This is to jump out of the while | |
# because looking along a row of the | |
# time capacitor is found is enough. | |
c1=len(sys_loops) | |
# Increment the counter if capacitor | |
# is not found. | |
c1+=1 | |
class Voltage_Source: | |
def __init__(self, volt_index, volt_pos, volt_tag): | |
self.volt_number=volt_index | |
self.volt_pos=volt_pos | |
self.volt_tag=volt_tag | |
self.v_peak=120.0 | |
self.v_freq=60.0 | |
self.v_phase=0.0 | |
self.voltage=0.0 | |
self.current=0.0 | |
self.op_value=0.0 | |
self.v_polrty=[-1, -1] | |
def display(self): | |
print "Voltage Source is ", | |
print self.volt_tag, | |
print "of %f V(peak), %f Hz(frequency) and %f (degrees phase shift)" %(self.v_peak, self.v_freq, self.v_phase), | |
print " located at ", | |
print self.volt_pos, | |
print " with positive polarity towards %s" %(csv_element(self.v_polrty)) | |
def ask_values(self, x_list, ckt_mat): | |
volt_params=["VoltageSource"] | |
volt_params.append(self.volt_tag) | |
volt_params.append(self.volt_pos) | |
volt_params.append("Peak (Volts) = %f" %self.v_peak) | |
volt_params.append("Frequency (Hertz) = %f" %self.v_freq) | |
volt_params.append("Phase (degrees) = %f" %self.v_phase) | |
if self.v_polrty==[-1, -1]: | |
# Looking for a default value of polarity | |
# in the neighbouring cells | |
self.volt_elem=csv_tuple(self.volt_pos) | |
if self.volt_elem[0]>0: | |
if ckt_mat[self.volt_elem[0]-1][self.volt_elem[1]]: | |
self.v_polrty=[self.volt_elem[0]-1, self.volt_elem[1]] | |
if self.volt_elem[1]>0: | |
if ckt_mat[self.volt_elem[0]][self.volt_elem[1]-1]: | |
self.v_polrty=[self.volt_elem[0], self.volt_elem[1]-1] | |
if self.volt_elem[0]<len(ckt_mat)-1: | |
if ckt_mat[self.volt_elem[0]+1][self.volt_elem[1]]: | |
self.v_polrty=[self.volt_elem[0]+1, self.volt_elem[1]] | |
if self.volt_elem[1]<len(ckt_mat)-1: | |
if ckt_mat[self.volt_elem[0]][self.volt_elem[1]+1]: | |
self.v_polrty=[self.volt_elem[0], self.volt_elem[1]+1] | |
volt_params.append("Positive polarity towards (cell) = %s" %csv_element(self.v_polrty)) | |
x_list.append(volt_params) | |
def get_values(self, x_list, ckt_mat): | |
self.v_peak=float(x_list[0].split("=")[1]) | |
self.v_freq=float(x_list[1].split("=")[1]) | |
self.v_phase=float(x_list[2].split("=")[1]) | |
volt_polrty=x_list[3].split("=")[1] | |
# Convert the human readable form of cell | |
# to [row, column] form | |
while volt_polrty[0]==" ": | |
volt_polrty=volt_polrty[1:] | |
self.v_polrty=csv_tuple(volt_polrty) | |
if not ckt_mat[self.v_polrty[0]][self.v_polrty[1]]: | |
print "Polarity incorrect. Branch does not exist at %s" %csv_element(self.v_polrty) | |
def transfer_to_sys(self, sys_loops, mat_e, mat_a, mat_b, mat_u, source_list): | |
""" The matrix B in E.dx/dt=Ax+Bu will be updated by the | |
polarity of the voltage source.""" | |
for c1 in range(len(sys_loops)): | |
if csv_tuple(self.volt_pos) in sys_loops[c1][c1]: | |
# If the positive polarity appears before the voltage position | |
# it means as per KVL, we are moving from +ve to -ve | |
# and so the voltage will be taken negative | |
if sys_loops[c1][c1].index(self.v_polrty)<sys_loops[c1][c1].index(csv_tuple(self.volt_pos)): | |
mat_b.data[c1][source_list.index(self.volt_pos)]=-1.0 | |
else: | |
mat_b.data[c1][source_list.index(self.volt_pos)]=1.0 | |
return | |
def generate_val(self, source_lst, sys_loops, mat_u, t, dt): | |
""" The source voltage is updated in the matrix u in | |
E.dx/dt=Ax+Bu .""" | |
self.voltage=self.v_peak*math.sin(2*math.pi*self.v_freq*t+self.v_phase) | |
mat_u.data[source_lst.index(self.volt_pos)][0]=self.voltage | |
self.op_value=self.voltage | |
def update_val(self, sys_loops, mat_e, mat_a, mat_b, state_vec, mat_u): | |
pass | |
class Ammeter: | |
def __init__(self, amm_index, amm_pos, amm_tag): | |
self.amm_number=amm_index | |
self.amm_pos=amm_pos | |
self.amm_tag=amm_tag | |
self.current=0.0 | |
self.op_value=0.0 | |
self.amm_polrty=[-1, -1] | |
def display(self): | |
print "Ammeter is ", | |
print self.amm_tag, | |
print " located at ", | |
print self.amm_pos, | |
print " with positive polarity towards %s" %(csv_element(self.amm_polrty)) | |
def ask_values(self, x_list, ckt_mat): | |
amm_params=["Ammeter"] | |
amm_params.append(self.amm_tag) | |
amm_params.append(self.amm_pos) | |
if self.amm_polrty==[-1, -1]: | |
# Looking for a default value of polarity | |
# in the neighbouring cells | |
self.amm_elem=csv_tuple(self.amm_pos) | |
if self.amm_elem[0]>0: | |
if ckt_mat[self.amm_elem[0]-1][self.amm_elem[1]]: | |
self.amm_polrty=[self.amm_elem[0]-1, self.amm_elem[1]] | |
if self.amm_elem[1]>0: | |
if ckt_mat[self.amm_elem[0]][self.amm_elem[1]-1]: | |
self.amm_polrty=[self.amm_elem[0], self.amm_elem[1]-1] | |
if self.amm_elem[0]<len(ckt_mat)-1: | |
if ckt_mat[self.amm_elem[0]+1][self.amm_elem[1]]: | |
self.amm_polrty=[self.amm_elem[0]+1, self.amm_elem[1]] | |
if self.amm_elem[1]<len(ckt_mat)-1: | |
if ckt_mat[self.amm_elem[0]][self.amm_elem[1]+1]: | |
self.amm_polrty=[self.amm_elem[0], self.amm_elem[1]+1] | |
amm_params.append("Positive polarity towards (cell) = %s" %csv_element(self.amm_polrty)) | |
x_list.append(amm_params) | |
def get_values(self, x_list, ckt_mat): | |
amm_polrty=x_list[0].split("=")[1] | |
# Convert the human readable form of cell | |
# to [row, column] form | |
while amm_polrty[0]==" ": | |
amm_polrty=amm_polrty[1:] | |
self.amm_polrty=csv_tuple(amm_polrty) | |
if not ckt_mat[self.amm_polrty[0]][self.amm_polrty[1]]: | |
print "Polarity incorrect. Branch does not exist at %s" %csv_element(self.amm_polrty) | |
def transfer_to_sys(self, sys_loops, mat_e, mat_a, mat_b, mat_u, source_list): | |
pass | |
def generate_val(self, source_lst, sys_loops, mat_u, t, dt): | |
pass | |
def update_val(self, sys_loops, mat_e, mat_a, mat_b, state_vec, mat_u): | |
""" The ammeter current is calculated as a result of the KVL.""" | |
c1=0 | |
while c1<len(sys_loops): | |
# The first occurance of the ammeter | |
if csv_tuple(self.amm_pos) in sys_loops[c1][c1]: | |
# Check for polarity. If positive polarity is after the ammeter | |
# it means the current is in the direction of ammeter current and so | |
# current is positive. | |
if sys_loops[c1][c1].index(self.amm_polrty)>sys_loops[c1][c1].index(csv_tuple(self.amm_pos)): | |
self.current=state_vec.data[c1][0] | |
else: | |
self.current=-state_vec.data[c1][0] | |
# Check for ammeter in other loops with the ammeter | |
# as common branch | |
for c2 in range(c1+1, len(sys_loops)): | |
# These lists are nested lists as two loops | |
# may have separate branches in common | |
for c3 in range(len(sys_loops[c1][c2])): | |
if csv_tuple(self.amm_pos) in sys_loops[c1][c2][c3]: | |
# Check for polarity again | |
if sys_loops[c1][c2][c3].index(self.amm_polrty)>sys_loops[c1][c2][c3].index(csv_tuple(self.amm_pos)): | |
# Then check is the loop is aiding or opposing | |
# the main loop. | |
if sys_loops[c1][c2][c3][-1]=="forward": | |
self.current+=state_vec.data[c2][0] | |
else: | |
self.current-=state_vec.data[c2][0] | |
else: | |
if sys_loops[c1][c2][c3][-1]=="forward": | |
self.current-=state_vec.data[c2][0] | |
else: | |
self.current+=state_vec.data[c2][0] | |
# This is to jump out of the while | |
# because looking along a row of the | |
# time ammeter is found is enough. | |
c1=len(sys_loops) | |
# Increment the counter if ammeter | |
# is not found. | |
c1+=1 | |
# Since it is a meter, this is its output value | |
self.op_value=self.current | |
component_list={"resistor":Resistor, "inductor":Inductor, "capacitor":Capacitor, "voltagesource":Voltage_Source, "ammeter":Ammeter} | |
nw_input=raw_input("CSV file containing the network layout --> ") | |
nw_layout=nw_input+".csv" | |
test_ckt=open(nw_layout,"r") | |
# Read the circuit into tst_mat | |
# Also performs a scrubbing of tst_mat | |
tst_mat=csv_reader(test_ckt) | |
components_found={} | |
for c1 in range(len(tst_mat)): | |
for c2 in range(len(tst_mat[0])): | |
elem=tst_mat[c1][c2] | |
if elem: | |
# wire is a zero resistance connection | |
if elem.lower()!="wire": | |
if len(elem.split("_"))==1: | |
jump_det=elem.split("_")[0] | |
if len(jump_det)>3: | |
if jump_det.lower()[0:4]=="jump": | |
pass | |
else: | |
print "Error! Component at %s does not have a unique name/tag." %csv_element([c1, c2]) | |
else: | |
print "Error! Component at %s does not have a unique name/tag." %csv_element([c1, c2]) | |
else: | |
[elem_name, elem_tag]=elem.split("_") | |
elem_type=elem_name.lower() | |
if elem_type[0]==" ": | |
elem_type=elem_type[1:] | |
if elem_tag[0]==" ": | |
elem_tag=elem_tag[1:] | |
# Check if component exists | |
if elem_type in component_list.keys(): | |
# If found for the first time | |
# Create that dictionary element with key | |
# as component type | |
if elem_type not in components_found: | |
components_found[elem_type]=[[csv_element([c1, c2]), elem_tag]] | |
else: | |
# If already found, append it to | |
# dictionary item with that key. | |
components_found[elem_type].append([csv_element([c1, c2]), elem_tag]) | |
else: | |
print "Error! Component at %s doesn't exist." %csv_element([c1, c2]) | |
# Check if a component of the same type has the same tag. | |
for items in components_found.keys(): | |
for c1 in range(len(components_found[items])): | |
for c2 in range(len(components_found[items])): | |
if c1!=c2: | |
if components_found[items][c1][1]==components_found[items][c2][1]: | |
print "Duplicate labels found for components of type %s at %s and %s" %(items, components_found[items][c1][0], components_found[items][c2][0]) | |
component_objects={} | |
for items in components_found.keys(): | |
# Take every type of component found | |
# item -> resistor, inductor etc | |
for c1 in range(len(components_found[items])): | |
# Each component type will be occurring | |
# multiple times. Iterate through every find. | |
# The list corresponding to each component is | |
# the unique cell position in the spreadsheet | |
component_objects[components_found[items][c1][0]] = \ | |
component_list[items](c1+1, components_found[items][c1][0], components_found[items][c1][1]) | |
parameters_file=nw_input+"_params.csv" | |
# Check if the *_params.csv file exists. | |
try: | |
csv_check_values=open(parameters_file,"r") | |
# If not, it has to be created and filled | |
# with default values. | |
except: | |
#param_flag="no" | |
pass | |
# Check if any of the components with the same | |
# tags are present in nw_params.csv. If so, take | |
# those parameters from nw_params.csv and replace | |
# the default parameters in the component objects. | |
else: | |
params_from_file=reading_params(csv_check_values) | |
for c1 in range(len(params_from_file)): | |
# Remove leading spaces if any | |
# The first column is the type of element | |
if params_from_file[c1][0][0]==" ": | |
params_from_file[c1][0]=params_from_file[c1][0][1:] | |
name_from_file=params_from_file[c1][0].lower() | |
# Check if the component type | |
# exists in the new circuit | |
try: | |
components_found[name_from_file] | |
except: | |
# If the component doesn't exist, just don't | |
# try to read the parameters. | |
pass | |
else: | |
# If it exists, check for the tag. | |
for c2 in range(len(components_found[name_from_file])): | |
# Remove leading spaces if any | |
if params_from_file[c1][1][0]==" ": | |
params_from_file[c1][1]=params_from_file[c1][1][1:] | |
# Check if the component tag exists in | |
# components found so far | |
if params_from_file[c1][1]==components_found[name_from_file][c2][1]: | |
# If so take the parameters and move them into the object | |
# having of that type and having the new cell position | |
component_objects[components_found[name_from_file][c2][0]].get_values(params_from_file[c1][3:], tst_mat) | |
csv_check_values.close() | |
values_to_file=[] | |
for items in component_objects.keys(): | |
# Each component object has a method | |
# ask_values that prints in the csv file | |
# default values for parameters. | |
component_objects[items].ask_values(values_to_file, tst_mat) | |
csv_ask_values=open(parameters_file,"w") | |
for c1 in range(len(values_to_file)): | |
for c2 in range(len(values_to_file[c1])): | |
csv_ask_values.write("%s" %values_to_file[c1][c2]) | |
csv_ask_values.write(", ") | |
csv_ask_values.write("\n") | |
csv_ask_values.close() | |
# Wait for the user to enter parameters before | |
# reading the nw_params.csv file. | |
cont_ans="n" | |
while cont_ans.lower()!="y": | |
cont_ans=raw_input("Enter parameters in file %s. When ready press y and enter to continue -> " %parameters_file) | |
csv_get_values=open(parameters_file,"r") | |
params_from_file=reading_params(csv_get_values) | |
csv_get_values.close() | |
for c1 in range(len(params_from_file)): | |
# Getting rid of the beginning spaces | |
# in the component keys | |
if params_from_file[c1][2][0]==" ": | |
params_from_file[c1][2]=params_from_file[c1][2][1:] | |
component_objects[params_from_file[c1][2]].get_values(params_from_file[c1][3:], tst_mat) | |
# Just checking the objects | |
for items in component_objects.keys(): | |
component_objects[items].display() | |
node_list, branch_map, loop_list, loop_branches, conn_matrix, \ | |
[number_of_nodes, number_of_branches, loop_count] = network_solver(nw_layout) | |
loop_count=len(loop_branches) | |
print "*"*50 | |
print "Number of nodes", | |
print number_of_nodes | |
print "Number of branches", | |
print number_of_branches | |
print "Number of loops", | |
print loop_count | |
print "*"*50 | |
def human_loop(loop): | |
""" Takes a loop as a list of tupes. | |
And prints a series of elements in spreadsheet format. """ | |
for c1 in range(len(loop)): | |
print csv_element(loop[c1]), | |
return | |
for c1 in range(len(loop_branches)): | |
human_loop(loop_branches[c1]) | |
print "*"*50 | |
def comm_elem_in_loop(loop1, loop2): | |
""" Takes two loops and returns a list | |
which has all the elements (tuples) that are | |
common between the loops. """ | |
loop_comm=[] | |
# Check every element of loop1 | |
# w.r.t to every element of loop2 | |
for c1 in range(len(loop1)): | |
for c2 in range(len(loop2)): | |
# Check if elements are equal | |
if loop1[c1]==loop2[c2]: | |
# Check if either of the elements | |
# are the last of the loops | |
if c1<len(loop1)-1 and c2<len(loop2)-1: | |
# Check if they have already been | |
# identified as common elements | |
if loop1[c1] not in loop_comm: | |
loop_comm.append(loop1[c1]) | |
elif loop2[c2-1]==loop_comm[-1]: | |
# This is a special condition. | |
# The first and last element of | |
# every loop are the same. | |
# Therefore, it will fail the condition that | |
# the element should not be found before. | |
# But, it may be possible that the segment of the | |
# loops that is common does not contain the last | |
# element. So, the check is: | |
# If the latest element to be found is loop2[c2-1] | |
# i.e is the second last element of loop2, in that | |
# case, the last element i.e loop2[c2] should | |
# also be appended to the common segment | |
loop_comm.append(loop2[c2]) | |
return loop_comm | |
def comm_branch_in_loop(loop1, loop2): | |
""" Takes the common elements (loop1) found between | |
two loops (out of which one is loop2) and break these | |
elements up into separate branches.""" | |
# The collection of branches | |
loop_comm=[] | |
# Each branch | |
loop_segment=[] | |
# starting element | |
prev_elem=loop1[0] | |
# Iterate from second to last element | |
for c1 in range(1, len(loop1)): | |
# Check if the index between this current element | |
# and the previous element is less than or equal to 1 | |
# This means it is a continuation of a branch | |
if abs(loop2.index(loop1[c1])-loop2.index(prev_elem))<=1: | |
loop_segment.append(prev_elem) | |
# If not, it means it is a new branch | |
else: | |
# Complete the branch with the previous element | |
loop_segment.append(prev_elem) | |
# If it is the final element of the loop | |
# Don't leave it out but add that too | |
if c1==len(loop1)-1: | |
loop_segment.append(loop1[c1]) | |
# Add that to the collection of branches | |
loop_comm.append(loop_segment) | |
loop_segment=[] | |
# Refresh the previous element | |
prev_elem=loop1[c1] | |
# This is a special condition. | |
# If there is only one common branch, the main | |
# condition will fail because a new branch will not be found | |
# In that case, the addition function needs to be repeated. | |
if not loop_comm: | |
loop_segment.append(prev_elem) | |
loop_comm.append(loop_segment) | |
return loop_comm | |
# A temporary list that stores the nodes | |
# common to two loops | |
nodes_in_loop=[] | |
# A array of all the loops of the system | |
# including common branches between loops. | |
system_loops=[] | |
for c1 in range(loop_count): | |
row_vector=[] | |
for c2 in range(loop_count): | |
row_vector.append([]) | |
system_loops.append(row_vector) | |
for c1 in range(len(loop_branches)): | |
# The diagonal elements of system_loops | |
# will be the loops themselves. | |
for c2 in range(len(loop_branches[c1])): | |
system_loops[c1][c1].append(loop_branches[c1][c2]) | |
# The system_loops array will be symmetric | |
for c2 in range(c1+1, len(loop_branches)): | |
# Find the nodes in loop1 | |
for c3 in range(len(node_list)): | |
if node_list[c3] in loop_branches[c1]: | |
nodes_in_loop.append(node_list[c3]) | |
# Find out the nodes common to loop1 | |
# and loop2. | |
for c3 in range(len(nodes_in_loop)-1, -1, -1): | |
if nodes_in_loop[c3] not in loop_branches[c2]: | |
del nodes_in_loop[c3] | |
# If there are two or more nodes common | |
# between loop1 and loop2, there are | |
# common elements. | |
if len(nodes_in_loop)>1: | |
comm_seg_in_loop=comm_elem_in_loop(loop_branches[c1], loop_branches[c2]) | |
# The list of common branches between | |
# loop c1 and loop c2 | |
sys_loop_off_diag=comm_branch_in_loop(comm_seg_in_loop, loop_branches[c1]) | |
for c3 in range(len(sys_loop_off_diag)): | |
#for c4 in range(len(sys_loop_off_diag[c3])): | |
system_loops[c1][c2].append(sys_loop_off_diag[c3]) | |
system_loops[c2][c1].append(sys_loop_off_diag[c3]) | |
# Determining the direction of common | |
# branches between loops c1 and c2 | |
for c3 in range(len(sys_loop_off_diag)): | |
st_node=sys_loop_off_diag[c3][0] | |
end_node=sys_loop_off_diag[c3][-1] | |
# Map the start and end nodes of common | |
# branch c3 to loops c1 and c2 | |
loop1_st_node=loop_branches[c1].index(st_node) | |
loop1_end_node=loop_branches[c1].index(end_node) | |
loop2_st_node=loop_branches[c2].index(st_node) | |
loop2_end_node=loop_branches[c2].index(end_node) | |
# This check is because the first node of a | |
# loop is the same as the last node. | |
# So this is to make sure, the correct start | |
# and end nodes are mapped, the difference between | |
# the indices is compared to the length of the common | |
# branch. If not equal, this means, the last node of | |
# a loop needs to be taken instead of the first node. | |
if (abs(loop1_end_node-loop1_st_node)!= len(sys_loop_off_diag[c3])): | |
if (len(loop_branches[c1])-loop1_st_node==len(sys_loop_off_diag[c3])): | |
loop1_end_node=len(loop_branches[c1])-1 | |
if (len(loop_branches[c1])-loop1_end_node==len(sys_loop_off_diag[c3])): | |
loop1_st_node=len(loop_branches[c1])-1 | |
if (abs(loop2_end_node-loop2_st_node)!= len(sys_loop_off_diag[c3])): | |
if (len(loop_branches[c2])-loop2_st_node==len(sys_loop_off_diag[c3])): | |
loop2_end_node=len(loop_branches[c2])-1 | |
if (len(loop_branches[c2])-loop2_end_node==len(sys_loop_off_diag[c3])): | |
loop2_st_node=len(loop_branches[c2])-1 | |
if (loop1_st_node<loop1_end_node): | |
loop1_dir="ahead" | |
else: | |
loop1_dir="back" | |
if (loop2_st_node<loop2_end_node): | |
loop2_dir="ahead" | |
else: | |
loop2_dir="back" | |
if loop1_dir=="ahead" and loop2_dir=="ahead": | |
sys_loop_off_diag[c3].append("forward") | |
if loop1_dir=="back" and loop2_dir=="back": | |
sys_loop_off_diag[c3].append("forward") | |
if loop1_dir=="ahead" and loop2_dir=="back": | |
sys_loop_off_diag[c3].append("reverse") | |
if loop1_dir=="back" and loop2_dir=="ahead": | |
sys_loop_off_diag[c3].append("reverse") | |
#print c1, c2 | |
#human_loop(comm_seg_in_loop) | |
#for item in sys_loop_off_diag: | |
#print "[", | |
#human_loop(item[:-1]), | |
#print item[-1], | |
#print "]", | |
nodes_in_loop=[] | |
system_size=len(loop_branches) | |
sys_mat_a=matrix.Matrix(system_size, system_size) | |
sys_mat_e=matrix.Matrix(system_size, system_size) | |
curr_state_vec=matrix.Matrix(system_size) | |
next_state_vec=matrix.Matrix(system_size) | |
source_list=[] | |
try: | |
components_found["voltagesource"] | |
except: | |
pass | |
else: | |
for c1 in range(len(components_found["voltagesource"])): | |
source_list.append(components_found["voltagesource"][c1][0]) | |
try: | |
components_found["capacitor"] | |
except: | |
pass | |
else: | |
for c1 in range(len(components_found["capacitor"])): | |
source_list.append(components_found["capacitor"][c1][0]) | |
if source_list: | |
sys_mat_b=matrix.Matrix(system_size, len(source_list)) | |
sys_mat_u=matrix.Matrix(len(source_list)) | |
else: | |
sys_mat_b=matrix.Matrix(system_size) | |
sys_mat_u=0.0 | |
meter_list=[] | |
try: | |
components_found["ammeter"] | |
except: | |
pass | |
else: | |
for c1 in range(len(components_found["ammeter"])): | |
meter_list.append(components_found["ammeter"][c1][0]) | |
for comps in components_found.keys(): | |
for c1 in range(len(components_found[comps])): | |
comp_pos=components_found[comps][c1][0] | |
component_objects[comp_pos].transfer_to_sys(system_loops, sys_mat_e, sys_mat_a, sys_mat_b, sys_mat_u, source_list) | |
##mat_ode_reduce(e, a, b) | |
validity_flag="no" | |
while validity_flag=="no": | |
dt=raw_input("Simulation time step in seconds --> ") | |
dt=float(dt) | |
if dt>0.0: | |
validity_flag="yes" | |
else: | |
print "Error. Invalid time step." | |
validity_flag="no" | |
validity_flag="no" | |
while validity_flag=="no": | |
t_limit=raw_input("Duration of simulation in seconds --> ") | |
t_limit=float(t_limit) | |
if t_limit>0.0: | |
validity_flag="yes" | |
else: | |
print "Error. Invalid time duration." | |
validity_flag="no" | |
op_dat_file=raw_input("Output data file name (.dat extension will be added) --> ") | |
op_dat_file=op_dat_file+".dat" | |
f=open(op_dat_file,"w") | |
ode_k1=matrix.Matrix(system_size) | |
ode_k2=matrix.Matrix(system_size) | |
ode_k3=matrix.Matrix(system_size) | |
ode_k4=matrix.Matrix(system_size) | |
ode_dbydt=matrix.Matrix(system_size) | |
ode_var=[ode_k1, ode_k2, ode_k3, ode_k4, ode_dbydt] | |
t=0.0 | |
while t<t_limit: | |
for c1 in range(len(source_list)): | |
component_objects[source_list[c1]].generate_val(source_list, system_loops, sys_mat_u, t, dt) | |
mat_ode(sys_mat_e, sys_mat_a, sys_mat_b, [curr_state_vec, next_state_vec], sys_mat_u, dt, ode_var) | |
for comps in component_objects.keys(): | |
component_objects[comps].update_val(system_loops, sys_mat_e, sys_mat_a, sys_mat_b, next_state_vec, sys_mat_u) | |
curr_state_vec=next_state_vec | |
#f.write("%s \t %s \t %s \t %s \n" %(str(t), str(sys_mat_u.data[0][0]), str(next_state_vec.data[0][0]), str(next_state_vec.data[1][0]))) | |
f.write("%s " %str(t),) | |
for c1 in range(len(source_list)): | |
f.write("%s " %component_objects[source_list[c1]].op_value,) | |
for c1 in range(len(meter_list)): | |
f.write("%s " %component_objects[meter_list[c1]].op_value,) | |
f.write("\n") | |
t=t+dt | |
print sys_mat_a | |
print sys_mat_e | |
print sys_mat_b | |
f.close() |
The main comments are:
1. The loop currents are calculated at every simulation time step. Currents in every branch and every element will not be calculated unless necessary. For example, at a later stage, saturation in the inductor or heating in the resistor can be included. So then, current will be calculated and used.
2. If you want to know the currents, you connect an ammeter. Voltmeters need to be coded. Ammeter was simple as I need to manipulate the loop currents. But voltmeters, I need to think how to use them. If I connect a voltmeter across two nodes, it will be seen as a branch. So it will appear in loop equations. This means, to make sure the currents don't change a large resistance will have to be chosen. However, a simpler method will be to look for the voltmeter tag and exclude that branch from the system matrix. I can then back calculate later.
A sample circuit:
No comments:
Post a Comment