Wednesday, May 22, 2013

Controlled Voltage Source

Think I got a basic code working for a controlled voltage source. To begin with here is the code for the class (click on "view raw" below the code box to see the code in a new window):


class Controlled_Voltage_Source:
""" Controlled Voltage Source class. Takes the instantaneous
voltage as input from the user file. """
def __init__(self, volt_index, volt_pos, volt_tag):
""" Constructor to initialize value.
Also, takes in the identifiers -
index (serial number), cell position and tag. """
self.type="ControlledVoltageSource"
self.volt_number=volt_index
self.volt_pos=volt_pos
self.pos=volt_pos
self.volt_tag=volt_tag
self.tag=volt_tag
self.voltage=0.0
self.current=0.0
self.op_value=0.0
self.v_polrty=[-1, -1]
self.control_tag=["Control"]
self.control_values=[0.0]
def display(self):
""" Displays info about the component."""
print "Controlled Voltage Source is ",
print self.volt_tag,
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, sys_branch):
""" Writes the values needed to the spreadsheet."""
volt_params=["ControlledVoltageSource"]
volt_params.append(self.volt_tag)
volt_params.append(self.volt_pos)
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]
else:
for c1 in range(len(sys_branch)):
if csv_tuple(self.volt_pos) in sys_branch[c1]:
if not self.v_polrty in sys_branch[c1]:
print
print "!"*50
print "ERROR!!! Voltage source polarity should be in the same branch as the voltage source. Check source at %s" %self.volt_pos
print "!"*50
print
volt_params.append("Positive polarity towards (cell) = %s" %csv_element(self.v_polrty))
volt_params.append("Name of control signal = %s" %self.control_tag[0])
x_list.append(volt_params)
def get_values(self, x_list, ckt_mat):
""" Takes the parameter from the spreadsheet."""
volt_polrty=x_list[0].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)
self.control_tag[0]=x_list[1].split("=")[1]
while self.control_tag[0][0]==" ":
self.control_tag[0]=self.control_tag[0][1:]
return
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)):
for c2 in range(len(sys_loops[c1][c1])):
if csv_tuple(self.volt_pos) in sys_loops[c1][c1][c2]:
# 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][c2].index(self.v_polrty)<sys_loops[c1][c1][c2].index(csv_tuple(self.volt_pos)):
if sys_loops[c1][c1][c2][-1]=="forward":
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
else:
if sys_loops[c1][c1][c2][-1]=="forward":
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 transfer_to_branch(self, sys_branch, source_list):
""" Transfers parameters to system branch if voltage
source exists in the branch. """
if csv_tuple(self.volt_pos) in sys_branch:
if sys_branch.index(self.v_polrty)<sys_branch.index(csv_tuple(self.volt_pos)):
sys_branch[-1][1][source_list.index(self.volt_pos)]=-1.0
else:
sys_branch[-1][1][source_list.index(self.volt_pos)]=1.0
def generate_val(self, source_lst, sys_loops, mat_e, mat_a, mat_b, mat_u, t, dt):
""" The source voltage is updated in the matrix u in
E.dx/dt=Ax+Bu ."""
mat_u.data[source_lst.index(self.volt_pos)][0]=self.control_values[0]
self.op_value=self.control_values[0]
def update_val(self, sys_loops, lbyr_ratio, mat_e, mat_a, mat_b, state_vec, mat_u):
pass
The only difference between a normal voltage source is that it has two lists control_tag and control_values. The control tag is the name of the control input and the corresponding index in the other list is its value. These are lists because there can be multiple control inputs to any controllable device.

Next in the main program "circuit_solver.py". The first stage is to get the names of the control codes from the user. Then generate "descriptor" files for each of these control codes. Check if they exist. If they don't create blank templates (check the previous blog entry). This is the code (click on "view raw" below the code box to see it in a new window):


# This is the main control program
# It will contain the individual control
# codes as functions.
complete_control=open("__control.py","w")
# Check if there exists a controlled element
if controlled_elements:
user_input=raw_input("Enter the control files. Omit the .py extension and just leave spaces between files --> ")
control_files=user_input.split()
control_descs=[]
for c1 in range(len(control_files)):
control_descs.append(control_files[c1]+"_desc.csv")
control_functions=[]
for c1 in range(len(control_files)):
control_functions.append(control_files[c1]+"_func")
for c1 in range(len(control_files)):
control_files[c1]=control_files[c1]+".py"
# These lists will contain separate dictionaries
# for every control file.
control_file_inputs=[]
control_file_outputs=[]
control_file_staticvars=[]
control_file_timeevents=[]
control_desc_handles=[]
for c1 in range(len(control_files)):
# Adding an empty dictionary for a control file.
control_file_inputs.append({})
control_file_outputs.append({})
control_file_staticvars.append({})
control_file_timeevents.append({})
# Check if the descriptor exists.
try:
control_desc_handles.append(open(control_descs[c1],"r"))
except:
# If it doesn't create a blank template.
control_desc_handles.append(open(control_descs[c1],"w"))
# Input template
control_desc_handles[c1].write("Input")
control_desc_handles[c1].write(", ")
control_desc_handles[c1].write("Element name in circuit spreadsheet = %s" %(component_objects[meter_list[0]].type+"_"+component_objects[meter_list[0]].tag))
control_desc_handles[c1].write(", ")
control_desc_handles[c1].write("Desired variable name in control code = %s" %(component_objects[meter_list[0]].type+"_"+component_objects[meter_list[0]].tag))
control_desc_handles[c1].write(", ")
control_desc_handles[c1].write("\n")
# Output template. Create a line for every
# control input a particular controlled
# element has.
for c2 in range(len(component_objects[controlled_elements[0]].control_tag)):
control_desc_handles[c1].write("Output")
control_desc_handles[c1].write(", ")
control_desc_handles[c1].write("Element name in circuit spreadsheet = %s" %(component_objects[controlled_elements[0]].type+"_"+component_objects[controlled_elements[0]].tag))
control_desc_handles[c1].write(", ")
control_desc_handles[c1].write("Control tag defined in parameters spreadhseet = %s" %(component_objects[controlled_elements[0]].control_tag[c2]))
control_desc_handles[c1].write(", ")
control_desc_handles[c1].write("Desired variable name in control code = %s" %(component_objects[controlled_elements[0]].control_tag[c2]))
control_desc_handles[c1].write(", ")
control_desc_handles[c1].write("\n")
# Static variable template
control_desc_handles[c1].write("StaticVariable")
control_desc_handles[c1].write(", ")
control_desc_handles[c1].write("Desired variable name in control code = Var1")
control_desc_handles[c1].write(", ")
control_desc_handles[c1].write("Initial value of variable = 0.0")
control_desc_handles[c1].write(", ")
control_desc_handles[c1].write("\n")
# Time event template
control_desc_handles[c1].write("TimeEvent")
control_desc_handles[c1].write(", ")
control_desc_handles[c1].write("Desired variable name in control code = t1")
control_desc_handles[c1].write(", ")
control_desc_handles[c1].write("First time event = 0.0")
control_desc_handles[c1].write(", ")
control_desc_handles[c1].write("\n")
control_desc_handles[c1].close()
The next step is to take in the descriptor parameters. These will be used to update the dictionaries for the inputs, outputs, staticvariables and time events. Here is the code (click on "view raw" below the code box to see it in a new window):


# Wait for the user to enter parameters before
# reading the *_desc.csv file.
cont_ans="n"
while cont_ans.lower()!="y":
print "Enter control parameters in the following files --> "
for c1 in range(len(control_descs)):
print "%s " %control_descs[c1]
cont_ans=raw_input("When ready press y and enter to continue -> ")
# Read the parameters from the descriptor spreadsheet.
control_desc_handles=[]
for c1 in range(len(control_files)):
control_desc_handles.append(open(control_descs[c1],"r"))
params_from_file=reading_params(control_desc_handles[c1])
for c2 in range(len(params_from_file)):
# Scrubbing blank spaces from the beginning
# and the end of the first cell.
while params_from_file[c2][0][0]==" ":
params_from_file[c2][0]=params_from_file[c2][0][1:]
while params_from_file[c2][0][-1]==" ":
params_from_file[c2][0]=params_from_file[c2][0][:-1]
if params_from_file[c2][0].lower()=="input":
# If it is an input, it will be a meter.
meter_type=params_from_file[c2][1].split("=")[1]
while meter_type[0]==" ":
meter_type=meter_type[1:]
while meter_type[-1]==" ":
meter_type=meter_type[:-1]
# Look for the meter in components_found
# and get the cell position from the meter tag.
# The cell position which is unique will be the
# dictionary key for control_file_inputs.
for c3 in range(len(components_found[meter_type.split("_")[0].lower()])):
if components_found[meter_type.split("_")[0].lower()][c3][1]==meter_type.split("_")[1]:
meter_type_ref=meter_type.split("_")[0].lower()
control_file_inputs[c1][components_found[meter_type_ref][c3][0]]=[components_found[meter_type_ref][c3][1]]
var_name=params_from_file[c2][2].split("=")[1]
while var_name[0]==" ":
var_name=var_name[1:]
while var_name[-1]==" ":
var_name=var_name[:-1]
control_file_inputs[c1][components_found[meter_type_ref][c3][0]].append(var_name)
if params_from_file[c2][0].lower()=="output":
# If it is an output, it is a controlled element
element_type=params_from_file[c2][1].split("=")[1]
while element_type[0]==" ":
element_type=element_type[1:]
while element_type[-1]==" ":
element_type=element_type[:-1]
# Look for the controlled element in components_found
# and get the cell position from the device tag.
# The cell position will be the unique dictionary key
for c3 in range(len(components_found[element_type.split("_")[0].lower()])):
if components_found[element_type.split("_")[0].lower()][c3][1]==element_type.split("_")[1]:
element_type_ref=element_type.split("_")[0].lower()
# Since a controlled element can have more than one control input
# Check if it has been found before.
if not components_found[element_type_ref][c3][0] in control_file_outputs[c1].keys():
control_file_outputs[c1][components_found[element_type_ref][c3][0]]=[components_found[element_type_ref][c3][1]]
control_tag_name=params_from_file[c2][2].split("=")[1]
control_var_name=params_from_file[c2][3].split("=")[1]
while control_tag_name[0]==" ":
control_tag_name=control_tag_name[1:]
while control_tag_name[-1]==" ":
control_tag_name=control_tag_name[:-1]
while control_var_name[0]==" ":
control_var_name=control_var_name[1:]
while control_var_name[-1]==" ":
control_var_name=control_var_name[:-1]
control_file_outputs[c1][components_found[element_type_ref][c3][0]].append([control_tag_name, control_var_name, 0.0])
if params_from_file[c2][0].lower()=="staticvariable":
# If it is a staticvariable, the dictionary key
# will be the variable name.
staticvar_type=params_from_file[c2][1].split("=")[1]
while staticvar_type[0]==" ":
staticvar_type=staticvar_type[1:]
while staticvar_type[-1]==" ":
staticvar_type=staticvar_type[:-1]
staticvar_val=params_from_file[c2][2].split("=")[1]
while staticvar_val[0]==" ":
staticvar_val=staticvar_val[1:]
while staticvar_val[-1]==" ":
staticvar_val=staticvar_val[:-1]
control_file_staticvars[c1][staticvar_type]=float(staticvar_val)
if params_from_file[c2][0].lower()=="timeevent":
# If it is a timeevent, the dictionary key
# will be the variable name.
timeevent_type=params_from_file[c2][1].split("=")[1]
while timeevent_type[0]==" ":
timeevent_type=timeevent_type[1:]
while timeevent_type[-1]==" ":
timeevent_type=timeevent_type[:-1]
timeevent_val=params_from_file[c2][2].split("=")[1]
while timeevent_val[0]==" ":
timeevent_val=timeevent_val[1:]
while timeevent_val[-1]==" ":
timeevent_val=timeevent_val[:-1]
control_file_timeevents[c1][timeevent_type]=float(timeevent_val)
control_desc_handles[c1].close()
The next step was a bit tricky. The idea is to write all these different control codes into one main program called __control.py and import this file. Each control code will be written as a function.

So basically, define the function, assign the input to variables, assign the static variables to local variables, assign the time events to local variables. And then finally embed the control code. Then assign the local variables to outputs, reassign local variables to static variables and time events as applicable to take these back to the main program.

Here is the code (click on "view raw" below the code box to see it in a new window):


# Wait for use to update the control code.
cont_ans="n"
while cont_ans.lower()!="y":
print "Enter control code in the following files --> "
for c1 in range(len(control_files)):
print "%s " %control_files[c1]
cont_ans=raw_input("When ready press y and enter to continue -> ")
# This list will contain the control
# code as lists of strings.
control_code=[]
for c1 in range(len(control_files)):
control_code.append([])
control_handles=[]
for c1 in range(len(control_files)):
control_handles.append(open(control_files[c1],"r"))
# Add the lines in the control codes
# to the lists.
for line in control_handles[c1]:
control_code[c1].append(line)
control_handles[c1].close()
# Check for any import statements.
# If any of the control codes have import statements
# add them to the main control program.
for c1 in range(len(control_code)):
for c2 in range(len(control_code[c1])):
if "import" in control_code[c1][c2].split():
complete_control.write(control_code[c1][c2])
complete_control.write("\n")
for c1 in range(len(control_code)):
# For each control code, define a function
# Name of function has been defined in
# control_functions.
complete_control.write("def %s(interface_inputs, interface_outputs, interface_static, interface_time, circuit_components, pos, t_clock):" %control_functions[c1])
complete_control.write("\n")
# The remaining statements have a tab \t for indentation
# Assign the input variables to the meter outputs
for ip_keys in control_file_inputs[c1].keys():
complete_control.write("\t")
complete_control.write("%s=circuit_components['%s'].op_value" %(control_file_inputs[c1][ip_keys][1], ip_keys))
complete_control.write("\n")
# Assign the static variables to their latest values
for static_keys in control_file_staticvars[c1].keys():
complete_control.write("\t")
complete_control.write("%s=interface_static[pos]['%s']" %(static_keys, static_keys))
complete_control.write("\n")
# Assign the time events variables to their latest values
for time_keys in control_file_timeevents[c1].keys():
complete_control.write("\t")
complete_control.write("%s=interface_time[pos]['%s']" %(time_keys, time_keys))
complete_control.write("\n")
# Include the control code.
for c2 in range(len(control_code[c1])):
complete_control.write("\t")
complete_control.write(control_code[c1][c2])
# Assign the output controlled elements to the
# variables. Additionally, check each control element
# for multiple control inputs.
for op_keys in control_file_outputs[c1].keys():
for c3 in range(1, len(control_file_outputs[c1][op_keys])):
control_pos=component_objects[op_keys].control_tag.index(control_file_outputs[c1][op_keys][c3][0])
complete_control.write("\t")
# Update the object control values
complete_control.write("circuit_components['%s'].control_values[%d]=%s" %(op_keys, control_pos, control_file_outputs[c1][op_keys][c3][1]))
complete_control.write("\n")
complete_control.write("\t")
# Update the dictionary values
complete_control.write("interface_outputs[pos]['%s'][%d][2]=%s" %(op_keys, c3, control_file_outputs[c1][op_keys][c3][1]))
complete_control.write("\n")
# Store the static variables in the dictionary
for static_keys in control_file_staticvars[c1].keys():
complete_control.write("\t")
complete_control.write("interface_static[pos]['%s']=%s" %(static_keys, static_keys))
complete_control.write("\n")
# Store the time events in the dictionary
for time_keys in control_file_timeevents[c1].keys():
complete_control.write("\t")
complete_control.write("interface_time[pos]['%s']=%s" %(time_keys, time_keys))
complete_control.write("\n")
# end the function
complete_control.write("\t")
complete_control.write("return")
complete_control.write("\n")
complete_control.write("\n")
complete_control.close()
# Import the main control program
from __control import *
Anyway, a basic circuit with a controlled voltage source works. So I'll just release this as the next minor version.

No comments:

Post a Comment