When must nodal analysis take place?
- When there are inductors with sufficient energy and that energy might be abruptly interrupted.
- When does an inductor have sufficient energy? When it has been in at least one non stiff loop in the previous iteration. This way, the circuit is power independent.
The code is below (click on "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
new_branch_events="yes" | |
while new_branch_events=="yes": | |
# Initialize currents in branches | |
# This is used to determine whether any | |
# inductor current change event occurs. | |
for c1 in range(len(branch_currents)): | |
branch_currents[c1]=0.0 | |
# Set the branch currents equal to the inductor current | |
# and equal to any node with zero impedance | |
# The concept is that currents through resistance may need | |
# to be calculated to determine if their state will change | |
# This is specifically for freewheeling | |
for c1 in range(len(branch_params)): | |
# Check if branch has inductance and is not stiff | |
# In this case it behaves like a current source for | |
# the nodal analysis. | |
# Check if the branch is stiff | |
if stiff_ratio[c1]=="no": | |
# Check if the branch has inductance | |
if branch_params[c1][-1][0][1]: | |
#if branch_params[c1][-1][2]>0.001: | |
branch_currents[c1]=branch_params[c1][-1][2] | |
# Check if it is a zero impedance branch with a voltage | |
elif ((branch_params[c1][-1][0][0]==0.0) and (1.0 in branch_params[c1][-1][1] or -1.0 in branch_params[c1][-1][1])): | |
branch_currents[c1]=branch_params[c1][-1][2] | |
# Making a list of all the branches that have inductors | |
# but are not stiff. | |
inductor_list=[] | |
for c1 in range(len(branch_params)): | |
if branch_params[c1][-1][0][1]: | |
if stiff_ratio[c1]=="no": | |
inductor_list.append(c1) | |
# Determining is nodal analysis is required. | |
# Check if the inductor appears in a loop | |
# Check if any other branch of the loop is stiff. | |
# If that branch has become stiff in the previous iteration | |
# it is not counted as a stiff branch as that branch has | |
# experienced an event that is making the loop stiff. | |
# If at least one non stiff loop exists with the inductor | |
# the inductor has been in a non stiff loop and a nodal | |
# analysis needs to be performed | |
nodal_analysis_reqd="no" | |
if inductor_list: | |
for c1 in range(len(inductor_list)): | |
inductor_loop_stiff="yes" | |
for c2 in range(len(system_loop_map)): | |
if system_loop_map[c2][inductor_list[c1]]=="forward" or system_loop_map[c2][inductor_list[c1]]=="reverse": | |
loop_stiff="no" | |
for c3 in range(len(system_loop_map[c2])): | |
if system_loop_map[c2][c3]=="stiff_forward" or system_loop_map[c2][c3]=="stiff_reverse": | |
if branch_events[c3]=="no": | |
loop_stiff="yes" | |
if loop_stiff=="no": | |
inductor_loop_stiff="no" | |
if inductor_loop_stiff=="no": | |
nodal_analysis_reqd="yes" | |
# This function is to determine if there is a an event. | |
# In this case the event is - does the current through any inductor | |
# change instantaneously. | |
if nodal_analysis_reqd=="yes": | |
current_continuity(node_list, branch_params, stiff_ratio, node_voltage, branch_currents, admittance_matrix, source_vector, sys_mat_u, branch_events, "det_state") | |
# The branch currents from the above function | |
# contain the currents calculated from nodal analysis | |
# This is compared from the currents from loop analysis | |
# If there is a difference, there is an event. | |
continuity_event="no" | |
for c1 in range(len(branch_params)): | |
if branch_params[c1][-1][2]: | |
if (abs(branch_params[c1][-1][2]-branch_currents[c1])/branch_params[c1][-1][2]>0.01): | |
continuity_event="yes" | |
# Determine the state of nonlinear devices | |
# Using the currents from nodal analysis, | |
# it will check if any of the devices will start | |
# or stop conducting. | |
if continuity_event=="yes": | |
for comps in component_objects.keys(): | |
component_objects[comps].determine_state(branch_currents, branch_params, branch_events) | |
# Check if there is a difference in the branch events as opposed | |
# to the previous nodal analysis. Keep performing nodal analysis | |
# until there are no new branch events | |
new_branch_events="no" | |
for c1 in range(len(branch_params)): | |
if not branch_events[c1]==branch_events_prev[c1]: | |
new_branch_events="yes" | |
# If there has been a new branch event, update the branch parameters | |
if new_branch_events=="yes": | |
for c1 in range(len(branch_params)): | |
branch_params[c1][-1][0][0]=0.0 | |
branch_params[c1][-1][0][1]=0.0 | |
for c2 in range(len(source_list)): | |
branch_params[c1][-1][1][c2]=0.0 | |
for c1 in range(len(branch_params)): | |
for c2 in range(len(branch_params[c1][:-1])): | |
try: | |
comp_pos=csv_element(branch_params[c1][c2]) | |
component_objects[comp_pos] | |
except: | |
pass | |
else: | |
component_objects[comp_pos].transfer_to_branch(branch_params[c1], source_list) | |
# Store the branch events | |
for c1 in range(len(branch_params)): | |
branch_events_prev[c1]=branch_events[c1] |
The process is as follows:
- In the beginning, a new branch event is assumed by default whenever a branch event occurs.
- Look if there are any inductors that have previously been in non stiff loops.
- If so, nodal analysis is required.
- Following nodal analysis, check if there is a continuity event with branch currents from nodal analysis being different from the branch currents from previous iteration of loop analysis.
- If so, execute the determine_state function to check if any of the elements will change their state. If any of the elements do change state, the respective branches will have branch events.
- Check if there are any branch events that had not occurred in the previous iteration. If so, go back to step 2.
- This will repeat until no new branch events occur.
In nodal analysis, there has been one significant change. In order to perform nodal analysis, it is essential to calculate the node voltages. So the entire circuit has to be solved. However, the branch currents will be calculated only if:
- At a node, check if there has been an branch which has a "hard" event.
- A hard event is when a switch turns on or when it turns off while carrying a non negligible current. When a switch or a diode turns off when the current goes negative, that is a soft event. When a diode turns on when it is forward biased, it is a soft event.
- If a single branch incident at a node has a "hard" event, the currents of all the branches incident at the node will have to calculated. If not, the branch currents remain at their initial values - either zero or a current it is an inductor.
The code is below (click on "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
if func_purpose=="det_state": | |
# Need to differentiate between branches that are hard switched and | |
# those that are soft switched. If a node does not have a single branch | |
# that is hard switched, the branch currents of branches | |
# incident at that node do not get recalculated | |
freewheel_branches=[] | |
for c1 in range(len(list_of_nodes)): | |
hard_switched_branch="no" | |
for c2 in range(len(branch_info)): | |
if branch_info[c2][0]==list_of_nodes[c1] or branch_info[c2][-2]==list_of_nodes[c1]: | |
if br_events[c2]=="hard": | |
hard_switched_branch="yes" | |
# If the node is a short node, perform the | |
# same check with branches incident on all | |
# the short nodes of the same group | |
if hard_switched_branch=="no": | |
for c2 in range(len(shortnode_list)): | |
if c1 in shortnode_list[c2]: | |
for c3 in range(len(branch_info)): | |
for c4 in range(len(shortnode_list[c2])): | |
if branch_info[c3][0]==list_of_nodes[shortnode_list[c2][c4]] or branch_info[c3][-2]==list_of_nodes[shortnode_list[c2][c4]]: | |
if br_events[c3]=="hard": | |
hard_switched_branch="yes" | |
if hard_switched_branch=="yes": | |
for c2 in range(len(branch_info)): | |
if branch_info[c2][0]==list_of_nodes[c1] or branch_info[c2][-2]==list_of_nodes[c1]: | |
if c2 not in freewheel_branches: | |
freewheel_branches.append(c2) | |
else: | |
if func_purpose=="calc_currents": | |
freewheel_branches=[] | |
for c1 in range(len(branch_info)): | |
freewheel_branches.append(c1) |
There has been another tag created called function_purpose. When determining state, function purpose will only calculate branch currents at nodes where there has been a "hard" event. But while calculating currents before the next loop analysis, all branch currents will be recalculated.