Monday, December 30, 2013

Releasing version 0.2.2

Finally, releasing the next version 0.2.2. The example of the buck converter is there in testckt16.csv with the control code in control.py. And the description of the circuit parameters in testckt16_params.csv and the controls in control_desc.csv.

http://sourceforge.net/projects/pythonpowerelec/

Well, well, well. It is the 31st of December 2013. I started this project a little more than a year ago and frankly I never thought I would get this far. So far everything has been coded to be automatic and without hardcoding any particular circuits which I feared I might have to. If all goes well, I will add other equipment like transformers, motors. But first I need to simulate power electronic converters of every type which just the library that I have. I suppose a lot of errors will creep up but I think the main concept is there and should work out.


And finally a big thank you to my blog readers. I see a lot of hits to the blog and also received some very nice comments. Thank you so much for reading my blog and I hope that soon I'll have a real simulator that every one of you can use on any OS. This has been a great year 2013 and I hope 2014 is just as good if not better.

Happy New Year to all of you and I wish you all the success for an awesome 2014!

I'll take a break for a few days and post again when I recover from my hangover :)

Diode freewheeling - nodal analysis

Where do I begin? Been coding continuously for the past three days and now have to find where I am.

As posted before, the only way to solve the problem of getting the diode to freewheel is by a nodal analysis. Essentially, the current through an inductor should not change instantaneously. If it does, it is bad design and I'll have to figure that out later. But in standard converter circuits, the inductor is expected to freewheel through a diode. As in the example of the buck converter.


So what I have coded is a function to perform nodal analysis take inductors as current sources. So I perform a dc analysis on a snapshot of the circuit at an instant and find out where the currents would flow if the inductor current were to remain constant.

Take the snapshot when the switch turns off with the inductor La having a non-zero current. So this current will flow through the switch and the diode almost equally since these are two high resistance paths.

With these currents calculated through nodal analysis, I try to figure out if any of the devices in the circuit has the capability to change its state. Since only nonlinear devices can do so, the remaining devices will have empty functions. The diode and switch will have the function called determine_state. Here is the code for the switch (click on "View Raw" at the bottom of the code box to see code in another window)



And here is the code for the nodal analysis (click on "View Raw" at the bottom of the code box to see code in another window)



Yes, it is a monster. Anyway, the basic concept is:

1. If a branch is not stiff and has an inductor, it becomes a current source.
2. If it has no impedance, the nodes on either end of the branch become super nodes.
3. If it only a resistance or is a stiff branch, it is expressed in KCL
(Vnode1-Vnode2)/Rbranch=Ibranch

4. Also if a branch has zero impedance, the current through it is treated as a current source.
5. Because of this, the only thing to be calculated are the resistive branches which is what needs to be done because that is where the non-linear devices will be.

6. The super node was a little tough to handle. When two nodes are connected by a branch with zero impedance, the current through that branch is tough to define. Therefore, the KCLs at the two nodes can be added up to form one KCL equation. And the second equation will be the voltage of the branch connecting these nodes.

7. In general, several nodes can be connected together to form a super node. Even worse, several groups of super nodes can exist.

8. Once the KCL equations are established, they need to reduced to upper triangular form and solved backwards. One node will become the reference node and will have zero voltage.

9. The branch currents will then be calculated from these node voltages. and there comes the next part of the story.


Because the nodal analysis could have changed the nature of the circuit by changing the state of nonlinear devices, it may so happen that the loop currents calculated before are not correct. So we need to recalculate the loop currents from the branch currents that the nodal analysis has given. The code for that is in compute_loop_currents (click on "View Raw" at the bottom of the code box to see code in another window)



This function looks to isolate branches to single loops and once that happens, the current of that branch becomes the loop current. If you are lucky, and in the case of the buck converter I was, the loops will be such that every non stiff loop will have a non stiff branch in that loop only and no other. Otherwise, a row operation is performed to isolate branches.

Anyway, long post but it is long overdue. The buck converter is now working. I am not sure if this will work in its present form for all power electronic converters. Need to test it thoroughly. But I'll release the next version soon.


Saturday, December 21, 2013

Getting the diode to freewheel - II

To begin with, today marks the completion of one year of blogging on this project. The project has come pretty far though not as far as I had hoped.

The problem with the approach that I have been using is that it is based exclusively on loop analysis. This breaks almost every time with every modification to the logic I try to make for every variation of the circuit. Nothing universal seems to be coming out. I have been spending the entire week drawing circuits, writing loops, scratching them out and repeating over and over again.

So need to change my approach. How about I mix in some nodal analysis as well? The loop analysis gives me all the loop currents for any one iteration. The control changes the circuit. Now, to determine how the currents will be rearranged, nodal analysis seems to be the only solution.

I am thinking about it this way:

1. At the end of an iteration, mark which loops were non-stiff and therefore may have had inductors carrying a non-negligible current.

2. When an event occurs, we need to rearrange the circuit, the loops, the loop currents and also decide any freewheeling effects.

3. At this point, the one circuit law that should be obeyed is - the current through an inductor cannot change instantaneously. Using this as a starting point, apply nodal analysis to calculate the current through all the branches.

4. Based on the preliminary calculations, check if the branch currents result in any changes in circuit topology - diodes freewheeling or IGBTs conducting. This seems a bit complicated - will need to expand on this.

5. Now that circuit topology is updated, another nodal analysis will update the branch currents. Using these updated branch currents, the loop currents will need to be calculated for the next round of loop analysis.


Only worry is that these tasks seem computationally heavy. Even if they are executed only when events occur, every effort will need to be made to make them simpler.

Monday, December 16, 2013

Getting the diode to freewheel

The diode must freewheel when:

1. An inductor (for now, but in general an energy storage element) contains non-negligible current because a negligible current could simply mean it is in a loop that is stiff but finds itself only in stiff loops.

2. For this current to break would be either bad design which means undue voltage stress on devices as stored energy has nowhere to go or the current freewheels through a diode (or any other device such as an IGBT with an anti-parallel diode).

And this is essentially how the algorithm develops:

1. After one iteration is over, suppose the ideal switch in the buck converter turns off.

2. The next iteration will throw up an event. So new loop manipulations will be done, an effort will be made to remove stiffness, and this effort will fail because there are two parallel stiff branches.

3. The check to be done - is there any inductor in all the loops which has a non-negligible current but now finds itself only in stiff loops? Right now I will restrict the search to inductors but I will expand the criteria later if needed.

4. If so, check if it can freewheel through a device capable of freewheeling. Now this is the tough part.

5. Start with the loop that contains the inductor but has a non-negligible current. This is because the inductor could have been in multiple loops to begin with and some of them may have been stiff in the first place.

6. Check if any of the devices in the loop are capable of freewheeling. This will be done by running a function within all devices but will be empty for all devices that can't freewheel and will contain code for those than can freewheel. The function will check if the loop current and direction are such that the device will start conducting if the current were to flow through it. If so, the device state changes.

7. Now check again if loop containing the inductor is stiff. Why? Because there may be two diodes connected in series. So repeat the above procedure.

8. There could be another possibility. Suppose there are two diodes connected in series in the buck converter and they have high resistance snubbers connected in parallel with them. So now we won't know how the loops are formed. It could be that the inductor appears in a loop with the snubbers and not the diodes. The diodes appear in a loop with the snubbers. So the above algorithm will give up because the inductor loop never encounters a diode.

9. So if upon encountering a stiff branch, it so turns out that the stiff branch does not contain an element that can freewheel, check if that branch exists in other loops. If so, perform a loop manipulation and bring those other loops into the inductor loop. Now repeat the above process.

10. When will the algorithm give up? Not sure how this will work. I will have to run different cases and check. There may be times when it goes into an infinite loop.

Now I will code this. Not sure if I can finish it tonight. Probably will post again tomorrow.
 

Ideal switch

It is winter again and I am buried in snow in Toronto. I am not much into winter activities and this means only coding can stop me for going crazy. So I am back again to this project. After weeks of dithering, pretending to review code and thinking of future strategies, I finally gotten around to taking this project further.

Like I said before, the diode model is not complete because the diode doesn't know when and where to freewheel. To highlight this, I made the ideal switch model and tried to simulate a buck converter. Here's the code for the ideal switch (click on view raw below the code box to see the code in another window):



This is the circuit for the buck converter.



And this is the control code - actually just an open loop with a constant 50% duty ratio  (click on view raw below the code box to see the code in another window):



And on execution, the current (measured by Ammeter_La) through the inductor La is:

And as can be seen, the diode does not free wheel at all. The current rises when the switch turns on and drops to zero when it is turned off.

The strategy to overcome this will be my next blog entry as it is a fairly detailed algorithm.

Friday, October 18, 2013

Next step - getting back

Been a long time since my last post. Been a while so I will spend some maybe a week reviewing code before starting up again.

Some updates from outside the project. My aim is to build a hardware prototype of a microgrid and I have managed to finish building one inverter with LCL filter. Attached is the pic.

Happy to say that it works and I am able to do a current control in grid connected mode. I have revised the control PCBs and once that comes back after may be a week, I'll build the second and then the third inverter. The objective is to have an isolated microgrid and try out some control techniques on it.

I did give a lightening talk in Pycon 2013 held in Toronto. They put the talk on YouTube. Here's the link:
http://www.youtube.com/watch?v=WGliZOtQZmU
Yes, that's me :) It's just a 5 minute lightening talk so not much I could talk about other than tell people what I am trying to do. Met some other interesting people who were trying to something in the electronics field that showed interest so the conference was definitely productive.

Tuesday, July 16, 2013

Next step

Been a while since I updated this blog. I have been incredibly busy with my day job and so not much progress since the last update. I haven't given up on this project. I'll be back soon once my main project goes into autopilot.

The diode model is not complete yet. There is no special function for the diode to freewheel. So that would be the next step.

I will be giving a lightening talk (5 minutes only) in Pycon Canada to be held in Toronto next month. So if any of you are planning to attend, do send me an email.

Wednesday, June 19, 2013

Version 0.2.1 released

Released the next version of the simulator with diode model:
http://sourceforge.net/projects/pythonpowerelec/

As always, for questions or comments, please send me an email at pythonpowerelectronics@gmail.com

Tuesday, June 18, 2013

Diode model - loops and manipulations

For any circuit, the number of independent loops is Branches-Nodes+1. The loop finder function returns excess loops for all circuits except maybe the simplest circuits. Initially, I had removed the excess loops and assumed they were linear combinations of the previous loops. But when the circuit has a ladder structure, it may so happen that some of the essential loops appear right at the end and get thrown out.

This is what I found when I was trying to figure out why the three-phase diode bridge rectifier was not working. The only way to ensure that essential loops are not deleted is to let all of them be to begin with. With row operations, some loops will be eliminated. As and how loops are eliminated, they should be completely deleted and the system should be reduced.

So, first the loop finder function (click on "View Raw" below the code box to see the code in a new window):



The difference is in the terminating condition:
if loop_iter:
As long as loop_iter is searching, let it search. So let it add as many valid loops as possible.
The number of excess loops can be pretty huge (x6).

Next comes the main circuit_solver.py. In this, another matrix has been conceived called the system_loop_map. This is to indicate which branches are stiff so as to eliminate stiff branches from as many loops as possible. The code for this has been put together in one block (click on "View Raw" below the code box to see the code in a new window):



So essentially, for every loop there is minimal information about every branch in the circuit - if it exists and if it does, is the branch stiff.



So essentially, I use system_loops_map to make the system upper triangular as far as stiff branches are concerned. The next block I am not sure if it is needed. Whether it is necessary to make sure a stiff loop is connected to the input. I'll test it and try to get rid of it. For some reason, it looks like an ugly code block.

The last part of to reduce the size of the system by getting rid of redundant loops (click on "View Raw" below the code box to see the code in a new window):


Diode model - part I

Where do I begin? Massive number of changes. So first, the diode class (clink on "View Raw" below the code box to see the code in a new window):


Similar to many of the others. In the parameter specification, the user needs to enter the voltage level of the diode and the polarity in terms of where the cathode is.

The diode resistance changes with the status whether "ON" or "OFF". The diode is ON when it is forward biased beyond a certain threshold voltage. It turns "OFF" when current becomes negative. The ON resistance, forward bias threshold voltage and the ON drop voltage can be made user defined parameters.

Thursday, May 23, 2013

Testing the circuit solver

Now that a basic control interface is ready, I can begin with the power electronics library. Main focus would be on the diode and IGBT (or simply an ideal switch). With these, the intent would be to start rigorously testing the circuit solver. Something which I put off before because I wanted to get to the power electronics library ASAP.

A basic problem has arisen particularly in the solving of stiff equations. A single phase diode bridge rectifier threw this error up. It it in the way the loops are rewritten whenever a diode turns on and off.

Found out the problem this afternoon and will figure it out tomorrow.

Wednesday, May 22, 2013

Version 0.2.0 released

Releasing version 0.2.0 with controlled voltage source as library element.
http://sourceforge.net/projects/pythonpowerelec/

Just realized while moving version 0.1.5 to the archives that the zip archive was empty. Added another zip file in the archives. In case of doubts, email to pythonpowerelectronics@gmail.com.


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):


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):


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):


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):


Anyway, a basic circuit with a controlled voltage source works. So I'll just release this as the next minor version.

Control interface - code

Here's the code for just the basic design of the control interface (click on "view raw" to view it in another window):



Tuesday, May 21, 2013

Control interface design

I have been thinking of how the control interface would be like for the user. A user needs a few essentials:

1. An easy way to define inputs, outputs. So, in the same was you would join an ammeter output to an in-port, you need to be able to define such a connection and use the variable inside the code. Also, the output of a block should be connected to a controllable device. In case the controllable device has multiple controls, the user should be able to define which controls get what variables.

2. Time events: The simplest thing to do would be to define a constant sampling frequency but this would make multi-rate sampling impossible. Also, ideally, the user should be able to decide when the code will run next. As a reference, the user will be given the current simulation time as t_clock. Really, that is also not needed, because the user needs to define "after" how much time the control code will run again and not "at" what time the control code will run again.

3. The nature of the code: One of the reasons for using Python was to be able to combine some of the other efforts in signal processing, linear algebra etc that are happening and give the user the chance to write advanced control code. So the user can write anything that is valid code. For that matter I am not sure if it is possible to embed a C program/function within a Python program. But if that is possible, why not? Essentially, anything that you can do in Python in general.


So this is what the code I have written has designed so far.

1. Take from the user the name of the control files that he wants to use. For every control file, there will be a "descriptor" file with a _desc. This will be a CSV file. This file contains four keywords - "Input", "Output", "StaticVariable" and "TimeEvent".

2. As a framework, a sample of all these will be provided in every descriptor file. So there will be one of each to tell the user how to do the rest.

3. An "Input" will be from a meter. So the user will need to specify the name of the meter as in the circuit spreadsheet and the variable name by which they want to refer to it in the control code.
An "Output" will be at a controllable device and additionally a control tag to account for devices that have multiple control inputs and finally the variable name in the control code.
"StaticVariables" will be variables that will continue to be available with their latest values in the control code. So these will passed back and forth with the main program. All other variables used in the code will be temporary and will exist only in the control code (local variables).
"TimeEvent" will be when the code runs next. Here not fully sure what to do. Supposing there are multiple blocks of code the user wants to execute at different rates - say 100 microseconds and 145 microseconds. The user can define two time events. A simple way would be let the user take the simulation time in the form of "t_clock" and compare t_clock with the time events. A more elaborate way would be define any tevent as tevent=tevent+x. x could be constant or variable. And tevent can be initialized by the user.

4. With every control file, there will be a descriptor file. So there can be multiple control codes.

5. Once this is defined, the user writes the control code. The program takes the control code, embeds it inside a function and maps the inputs, outputs etc to variables passed from the main program. All this will be written automatically into another program with any import statements the user includes. This program will be imported by the main program and the functions will be executed automatically.


So this is the plan. Steps 1, 2, 3 and 4 are done. The code is almost ready and I need to think over it. Step 5 will then follow.

Thursday, May 9, 2013

Next step - controlled sources and power electronics

The thought of spending the next few weeks sorting out the exception handling put me off completely. So I decided to skip that stage and move on to expanding the library instead. Whats the point of having my own project if I can't do what I want?

So, now designing the interface for a controllable object for example a controllable voltage source. The primary objective with this software is that an advanced user should be able to code whatever he wants as the control code. Taking my requirements specifically, I would like the control code that I write to be able to translate into a C program that I can compile into a DSP.

So, with any controllable object, what are the major interface requirements?
1. Inputs - preferrably in the form of meaurements from meters
2. Control outputs - can be defined in the parameter specification sheet.
3. An event generator - when must the control values be updated.

With no. 1 and no. 2 the objective will be that the user should have convenient interface to access the outputs of measurement meters and  update the controls without getting too much into the way objects are defined. So this means, I would have to add another layer between the "component_objects" dictionary in the main circuit_solver.py and the control code. So the next question, how will this interface be decided? In most simulators, input ports are defined and these are connected to signals in the outer layer. This might be one way to design an interface. Ask the user to choose what are the inputs to the control code.

With no. 3 the purpose of an event generator is that different objects have to be upated at different rates - i.e multi-rate sampling. So the control code must execute when the object with the nearest update time is called. Also, how to ensure that objects are updated only when necessary?

Sunday, May 5, 2013

Version 0.1.5 released

Version 0.1.5 released.

http://sourceforge.net/projects/pythonpowerelec/?source=navbar
Also, just in case, since some of the blog may be written in a cryptic manner, specific questions can be directed to pythonpowerelectronics@gmail.com

Current Source

This current source turned out to be a little more tricky that I thought because I was looking for an elegant solution, something with cool network tricks or that uses object oriented programming inheritance techniques. Ended up using the easiest way - model the current source as a voltage source in series with a resistance. The end result is a little dirty - calculate one temporary value to update another temporary value. This is evident from the glitches in the first quarter cycle. Will have to test how this works particularly in an inductive-capacitive circuit. So nothing final yet.

Been a while since I released a version, so I am going to release it soon. Also, the next step will be to move on to version 0.2.0 which will look at structural changes to the code that will bring in error messages and directions of usage.

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



Wednesday, May 1, 2013

User defined objects - II

Spent a couple of days thinking about ways in which a user can define a "block" that could be connected repeatedly in the circuit. But something similar has made that half-possible already - the "jump" labels.

A crude way of defining blocks would be to simply describe a part of a circuit (for example an RLC circuit or a three-phase inverter) and connect it to the rest of the circuit using jump labels. The reason this would be crude is that the entire sub-circuit would have to be copied that many number of times and for each object, the constituent components would have to be given separate labels.

A much more elegant manner would be to define a single sub-circuit as the base class. The main circuit will then contain references to this class and the connections could be made by jump labels with additional unique identifiers. For example a RL sub-circuit could have a jump1 and jump2 labels as connectors. The main circuit can have jump labels as jump1_RL1 and jump2_RL1 to signify a component RL1. The extention "RL1" will result in labels RL1 automatically created that will be appended to the resistor and inductor within the sub-circuit.

The problem with this method is that in a very large circuit with several sub-components repeated, it may be necessary to change the parameters of some of them to simulate special conditions such as nonideal parameters. So by having a single block, additional changes to the structure won't be possible. So, by copying the sub-circuit again and again, this flexibility is provided. The only drawback is that the user will have to edit all the component labels. But that may not be such a bad thing because the program does the error checking for duplicate labels. So a circuit will not run with multiple labels if the user accidentally forgets to edit some of the labels.

So this option of copying a circuit as many times as you need in the circuit is not such a bad idea because even for the sake of appearance, you can have these blocks anywhere in the spreadhseet, not necesarily bang in the middle of the main circuit.

So, now all I have to do is define a current source and I'll be done with the first set of elements.

Friday, April 26, 2013

User defined objects

While examining current sources, I figured that since the basis for the circuit solver is mesh analysis, the current source will have to be converted to a voltage source in series with a resistance. This means a voltage source and a resistance in series connected in parallel with a voltmeter. So essentially the voltage source has to be adjusted with respect to the voltage measured so as to produce the necessary current.

So this starts another task that I was planning to do just before I released a major version. That is of user defined objects. Suppose, a user wants to create an object which is a voltage source in series with an RLC network and make a dozen copies of it in the circuit, how would that be done? As of now, the entire solver works on the basis of cell positions on the spreadsheet. But now these cell positions won't exist in real but fictitious cell positions will have to be created.

Suppose such an object is to be defined. How would the details be internally? Suppose it is placed at position 7E in the spreadsheet. How would each element be defined as cell positions in the loop as it may be more than one loop if there is a parallel element. One possibility is to go ahead with just one cell position but replicate the loop by the number of parallel elements in the loop (essentially the number of branches in parallel). So every time the object is called, an identifier needs to be created to know which parallel element to consider.

So essentially a third dimension needs to be added. This makes sense as we are adding layers to the spreadsheet. But this might need a complete rework of the entire solver! Need to think over this.

Monday, April 22, 2013

Capacitor class

Been a long time since I updated the blog.

To add another element to the library - the capacitor. Anyway, the code is just an extension/combination of the voltmeter/voltage source classes. Here is the code (click on "view raw" below the code box to see it in a new window):



I'll release another version after completing the current source class which I hope will be in a few days.

For the current source class, my guess it would be best to exclude it from the branch list so that it doesn't appear in any of the loops. For every current source a simple nodal equation can then be added.

Friday, April 5, 2013

Version 0.1.4 released

Released another version:
http://sourceforge.net/projects/pythonpowerelec/files/

Next step:
Complete the passive library - include capacitor and current source. Current source is extremely important for us power electronics guys and that might be a bit of a challenge as the loops will be refined every time a current source is found.

Simulation speed

Before the topic of simulation speed, a minor check to be performed on components that have polarity like voltage sources, ammeters and voltmeters.

Until now, the only check on the polarity of these elements is that it is within the physical circuit map. But now that branches have been defined, another check can be enforced about whether the polarity element is in the same branch as the component. This is the code (click on "view raw" below the box to see the code in a new window):



For this to happen, the main program "circuit_solver.py" has been rearranged a bit. No changes to the code as such. Just that the network interpreter is called immediately after the circuit layout file is interpreted and checked for basic errors like componenents not found in library or duplicate components. The idea is that polarity errors should be picked up at the design stage before the simulation starts.

Now the other change of simulation speed. My concept has been similar to an interrupt driver program. Instead of an interrupt vector, generate an "event vector". This event vector will be "yes" corresponding to a branch is an element has changed in the branch. Otherwise, it will remain "no". If there is a single "yes", this would mean the all the system matrices A, B and E would be calculated completely. Currently, this might be necessary. At a later stage, I could think of ways to recalculate only the parts that are affected.



So, the branch_events vector is reset to "no" at the end of the code block. This vector is initialized to "yes" at the beginning to ensure that at least one computation takes place. With passive circuits, there won't be any event generation and therefore, there will be no recalculation.

This has speeded up the execution quite a bit but still not as much as I would have liked. Need to figure which of the code blocks can be done away with. A fairly simple circuit like "testckt7.csv" in the example list which results in a 4x4 system takes around 2 minutes to solve for a run time of 0.5s and a time step of 10 microseconds. Which is quite slow. At later stages, this might be a problem when simulating an inverter with a time step of 1 microsecond. If the inverter has a switching frequency of 5 kHz, an event would be generated around every 30 microseconds. No idea how it is going to be.

Tuesday, April 2, 2013

Version 0.1.3 released

Been incredibly busy with my official research project. Will continue to be busy for another week until my PCBs are sent out for fabrication. Thought I would release another version just so that I don't loose track of what I have done.

http://sourceforge.net/projects/pythonpowerelec/

Created a voltmeter model that you can use to measure voltage. The concept is it is a large resistance is parallel with the nodes across which voltage is to be measured. The parameter taken as input is the rated voltage of the system. This is just an approximate value so as to choose the voltmeter resistance large enough that a negligible current of 1 microampere flows through the voltmeter.

Thursday, March 21, 2013

Version 0.1.2 released.

Released the next minor patch:
http://sourceforge.net/projects/pythonpowerelec/files/

Stiff Systems - III

Several changes made to the entire codes. In order to solve every type of stiff system effectively, the only was seems to be to completely recalculate not only the A, B and E matrices but the KVL loops and their interactions.

At the highest level, I defined another layer called the branch. Currently, it is another list, but it may be an object depending on the later use. I have defined the branch as follows (click on "view raw" to see the code in a new window):



There was a problem where some branches were the reverse of the other and these were difficult to handle when loops were being manipulated. So I added the code to delete the redundant branches and also remove them from the system_loops matrix.

Another concept planned for the next stage was that each branch can have an "event" flag. That is if there is a change in the parameters like a diode starting or stopping conduction, there will be an event generated. When an event is generated on a branch, an event is generated to the entire system and only then will the system matrices A, B and E be recalculated. Otherwise, leave them as it is and just solve the equations for new inputs. Even in a power electronics circuit, this may result in significant savings as a change may happen once in 20-50 microseconds so that with a time step of 1 microsecond, the computation can be 20-50x faster if all the recalculation are left out.

The branches are updated as follows (click on "view raw" to see the code in a new window):




Also, each class has another method called "transfer_to_branch".

The really huge change has been in the solver.py file. The basic concept is described here. The file has comments as far as possible.

1. The way to find out if a loop is stiff is to check the L/R ratio. If the L/R ratio of the diagonal element is low, the offdiagonal elements are checked. If they are also stiff, a row manipulation is done with the lower loops so that the stiff elements are removed from the lower loops.

2. Once the stiff elements have been isolated to individual loops by row operations, the loops are now recalculated.

3. Once again the system matrices A, B and E are then calculated from the new loops and now the ODE is solved.


Sunday, March 10, 2013

Stiff systems - II

Stupid mistake. The algorithm wasn't totally wrong. Still is wrong but not completely wrong. The problem was in the loop interaction.

Take this sample circuit:


The large resistors are Resistor_V2 (at 4D and of 500000.0 ohm) and Resistor_V1 (at 12 J and of 10000000.0 ohm).

The matrices turned out to be:
-----------------------------------------------------------------------------------------------------------
500000.0  500000.0  0.0  500000.0  500000.0
500000.0  500010.1  0.0  -500000.1  500000.1
0.0  0.0  10000010.0  0.0  -10000000.0
500000.0  -500000.1  0.0  500020.1  -499990.1
500000.0  500000.1  -10000000.0  -499990.1  10500010.1

0.0  0.0  0.0  0.0  0.0
0.0  0.0001  0.0  -0.0001  0.0001
0.0  0.0  0.1  0.0  0.0
0.0  -0.0001  0.0  0.2001  0.0999
0.0  0.0001  0.0  0.0999  0.1001

1.0
0.0
0.0
0.0
0.0
-----------------------------------------------------------------------------------------------------------

For loops defined as:
 -----------------------------------------------------------------------------------------------------------
1D 2D 3D 4D 5D 6D 6C 6B 6A 5A 4A 3A 2A 1A 1B 1C 1D

1H 1G 1F 1E 1D 2D 3D 4D 5D 6D 6E 6F 6G 6H 5H 4H 3H 2H 1H

9L 10L 11L 12L 12K 12J 12I 12H 11H 10H 9H 9I 9J 9K 9L

1H 1G 1F 1E 1D 2D 3D 4D 5D 6D 6E 6F 6G 6H 6I 6J 6K 6L 5L 4L 3L 2L 1L 1K 1J 1I 1H

1H 1G 1F 1E 1D 2D 3D 4D 5D 6D 6E 6F 6G 6H 7H 8H 9H 10H 11H 12H 12I 12J 12K 12L 11L 10L 9L 8L 7L 6L 5L 4L 3L 2L 1L 1K 1J 1I 1H
-----------------------------------------------------------------------------------------------------------

Which is wrong. Because, the interaction between loop 2 and loop 4 is -500000.1 in the matrix A but should be positive from the sense of the loops above. The problem is in the code that defines the "forward" and "reverse" directions.

This is the new code (click on "view raw" to see the code in a new window):



The problem is with the judgement of length of branch with respect to the start and end nodes. The length of the branch will the number of elements in the branch. Which will be (end_node_st_node+1). This is exact block where there was the error:
-----------------------------------------------------------------------------------------------------------
if (abs(loop1_end_node-loop1_st_node)!= (len(sys_loop_off_diag[c3])-1)):
    if (len(loop_branches[c1])-1-loop1_st_node==(len(sys_loop_off_diag[c3])-1)):
        loop1_end_node=len(loop_branches[c1])-1
    if (len(loop_branches[c1])-1-loop1_end_node==(len(sys_loop_off_diag[c3])-1)):
        loop1_st_node=len(loop_branches[c1])-1
if (abs(loop2_end_node-loop2_st_node)!= (len(sys_loop_off_diag[c3])-1)):
    if (len(loop_branches[c2])-1-loop2_st_node==(len(sys_loop_off_diag[c3])-1)):
        loop2_end_node=len(loop_branches[c2])-1
    if (len(loop_branches[c2])-1-loop2_end_node==(len(sys_loop_off_diag[c3])-1)):
        loop2_st_node=len(loop_branches[c2])-1
-----------------------------------------------------------------------------------------------------------


So, the row manipulations to remove the stiff elements from the matrix produce the following matrices:
-----------------------------------------------------------------------------------------------------------
a=
500000.0  0.0  0.0  0.0  0.0
0.0  10.1  0.0  0.0999999999767  0.0999999999767
0.0  0.0  10500010.0  0.0  0.0
0.0  0.0999999999767  0.0  20.1  10.1
0.0  0.0999999999767  10.0  10.1  10.0999999996

e=
0.0  0.0  0.0  0.0  0.0
0.0  0.0001  0.0  0.0001  0.0001
0.0  0.0  0.0  0.0  0.0
0.0  0.0001  0.0  0.2001  0.1001
0.0  0.0001  0.1  0.1001  0.1001

b=
1.0
-1.0
1.0
-1.0
-1.0
-----------------------------------------------------------------------------------------------------------


The isolation has taken place. Because the two large resistors have been isolated to two separate rows. But the loop resistance between the 10000000.0 ohm resistor and the source is wrong. It should be 10000010.1 instead of 10500010.0.

Well, it doesn't blow up like before. So an improvement.

Releasing this as a new patch anyway (v0.1.2). Check on the SourceForge site soon.

Friday, March 8, 2013

Stiff systems

Changing the title at least to reflect the focus of the problem.

Change in strategy. Make a collection of branches and for each branch have the entries for A, E and B matrices. So this will give an indication of the L, R and V values in every branch.

What is a stiff branch?

  1. It is not a branch with negligible or zero inductance. If the resistance is small enough, it could be a load resistance or even a branch resistance.
  2. It is a branch with a large resistance and negligible or zero inductance.
So, how do know this? It is relative. If all the resistances in the circuit are in a narrow range - say 0.1 to 1000, the system is not stiff. How large a resistance is again depends on the voltage level - for a 120 V system, 100 kiloohm would be large, for a 120 kV system, it would be nothing. But to avoid a per unit system and still classify a system as stiff will mean I need to arrange them in an order according to the resistances. Which is not a difficult task.

After this, I need to calculate the L/R ratio just in case though a very large inductance accompanying a large resistance is not very practical. A large resistance simply means an open circuit or a non-conducting medium but a large inductance means a huge core and a large number of turns in the winding. The worst I can think of in a huge transformer that has a large magnetising inductance but this will have a low winding resistance and a large core loss resistor across it. So it won't be a stiff system. On the contrary it will be a system with a large time constant which is actually the case because when you energize one of these monsters, the magnetizing current can take a long time to decay.

The smallest resistance and inductance would really be the measure of a circuit because they would be the parasitics. So suppose I take these as a base and try to calculate ratios of other elements with respect to it. So a parasitic resistance of 0.005 ohm for a 120 V circuit would be acceptable wire resistance. And a 10 ohm load resistor would result in  a ratio of 10/0.005=2000 which is quite OK. But a 1 megaohm resistor would result in a ratio 2.0e+8 which should raise an alarm.

Taking such a ratio is itself a form of per unit, just that the per unit is arbitrary and if a user puts ridiculous values and an equally impractical simulation time step, a circuit will classified as stiff.

Thursday, March 7, 2013

Circuit Solver - IX

Too quick to celebrate. One of the reason why I thought things were working was because the system was small and it wasn't too easy to miss out branches. The row operations to get rid of the large resistances and isolate the stiff equations was correct. But the subsequent matrix manipulation to make the matrix symmetric was wrong.

The consider the diagonal element of a manipuled row to be the sum of the offdiagonal elements is no way right. Simply because the diaognal element may already contain a branch present in any of the off diagonal elements and so the effective resistance/inductance may be larger.

I was trying to do this because I didn't want to do another parameter read from the objects. But looks like that may be the only way. Or at least can't be completely avoided. Need to figure out how to minimize the object read.

Wednesday, March 6, 2013

Circuit Solver - VIII

Documenting what I have been doing for the past two weeks won't be easy. Anyway, the main focus of my problem has been to be able to solve any type of differential equations. When I say any type, this means a combination of differential equations that are stiff and non-stiff. Basically a stiff equation is one that has a small time constant and therefore needs a small simulation time step to capture the dynamics effectively. Something I don't want to do because reducing the time step too much will cause the simulation to be very slow.

So to describe the process from the outer layers of the code to the inner layers.

First major change. The way the current loops of the system and their interaction is decribed. A loop will have the beginning and the ending node as the same and will be a continuous list. The problem is if loop manipulations have to be done - that is differences between loops have to be found in terms of branch segments, it is a little difficult.
So all loops and common branch segments are defined in terms of branches that are elements between two nodes. This is the smallest denomination. Also, every one of them has a direction. If a branch is in a loop and is the diagonal element in system_loops, it has the default direction as "forward". If it shows the interaction between loops, it was have the necessary direction.

So before the ODE solver, a pre-processing has been done to break up all elements in system_loops into lists of branches. This is the code (click on "View Raw" below the code box to see it in a new window):



The next major change is in the ODE solver. Three steps here:

  1. Check for stiff equations - L/R ratio and isolate every stiff branch to a single equation by row operations.
  2. With each row operation on the matrices A, B and E, perform the same row operations on the system_loops. So remove elements in common and add new elements.
  3. Since, the objective has been to isolate a stiff loop and approximate it to a static equation without a d/dt, the interactions with other loops has to be eliminated and these interactions will have to be readjusted.

Now in detail. Here is the code (click on "View Raw" below the code box to see it in a new window):



Check if a diagonal element has a small L/R ratio (time constant). Now this judgement is purely a guess and I have chosen it as 0.1*SimulationStep. But may have to be changed. If that is the case, this equation has to be isolated. So, the interaction between this loop and other loops, must be eliminated and first we start with the branch that is causing the problems. So look for off diagonal elements in the same row with small time constants.

Suppose an off diagonal element [c1, c2] has a small time constant. This means the branch between loops c1 and c2 has a small time constant. And this therefore means that loop c2 will also have this branch and will also have a small time constant. So, the way to go is to check which loop has a smaller time constant - c1 or c2 by looking at the diagonal elements c1,c1 and c2,c2. The smaller one remains as it is and the larger one is manipulated to remove this stiff branch. This is by changing row c2 to c2-c1.

Look through the entire matrix this way. The objective is then that only the stiff branches will be in their own isolated loops and the stiff elements removed from all other loops.


Next - loop manipulations by changing the elements.



The reason for doing this is that when the branch currents are measured, they are measured with respect to the branches present in the loops. So with the above row operations, the loops have changed, so the actual loop descriptions must also change. So when we do c2=c2-c1, we need to find out which elements in c1 are present in c2 and delete them from c2 and which elements are present in c1 but not in c2 and add them reversing their direction.


Last step - readjust the matrices. Here is the code (click on "View Raw" below the code box to see it in a new window):



This was actually a bit tricky. The first part is the stiff equation must be connected to an input source because interactions with all other loops has been eliminated. To eliminate the interaction with other loops, the off diagonal elements in matrix A are set to zero in the stiff loop.

Then check if any of the elements in the row of matrix B is nonzero. If yes, leave it as it is. If not, add the stiff loop with another nonstiff loop that is connected to the input. This will cause one of the elements of B matrix in the stiff row as non zero. At the same time, some of the off diagonal elements in the stiff row will now be nonzero as these are the resistances in the nonstiff loop with which the addition occurred. These resistances are essentially the resistances between the source and the stiff branch. So add all those to the diagonal element in A and then get rid of them. So now the diagonal element of A contains the entire resistance as a loop between the source and the stiff branch and is also linked to the source. So the stiff dynamical equation has been approximated to a static isolated equation.

Next, remove all the elements in matrix E - eliminating all d/dt terms and making it a static equation. The last part is to account for the interaction elements that still linger between the stiff loops and non stiff loops. So if Z is the stiff loop and X and Y are nonstiff loops that interact with Z in that they have nonzero elements in the same column as Z, this means that X and Y will interact with each other. This interaction needs to be captured as we want to isolated loop Z. So look for the elements and move them to the offdiagonal entries between X and Y with the appropriate sign.

Tuesday, March 5, 2013

Circuit Solver - VII

I think the problem has been solved. Updated the latest code on my Source Forge site:
https://sourceforge.net/projects/pythonpowerelec/files/

Need to close my eyes now. So details tomorrow.

Friday, March 1, 2013

Circuit Solver - VI

Last post had a mistake in the logic.

By performing row operations, the solution set does not change. Also, the way the ammeter currents are calculated is dependent on the loops. And the loops are described in system_loops and have not changed.

So essentially, the current through the large resistor will be a sum of loops i1, i3 and i4. And the accuracy of this will depend on the simulation time step. Which is why it is wrong. Currently, the current through the large resistor has a peak of 0.04Amps whereas it should be in microAmps.

So now what? This means any change I make to the resistors has to be reflected back to the system_loops. And finally, when the component currents are calculated they have to know that the system has been approximated. This is going to take some time.

Thursday, February 28, 2013

Circuit Solver - V

A little progress. At least the ODE is solvable now. I used the strategy of row operations to get rid of the elements with low (or zero) time constants from as many rows as possible. This is the code (click on "view raw" below the code box to see it in a new window):



So for a circuit:

The matrices are transformed as follows:

Original matrices are:
------------------------------------------------------------------------------------------------------------------
e=
0.0  0.0  0.0  0.0
0.0  0.2  0.0  0.1
0.0  0.0  0.0001  0.0001
0.0  0.1  0.0001  0.2001

a=
10000000.0  0.0  10000000.0  10000000.0
0.0  20.0  0.0  10.0
10000000.0  0.0  10000010.1  10000000.1
10000000.0  10.0  10000000.1  10000020.1

b=
1.0
0.0
0.0
0.0
------------------------------------------------------------------------------------------------------------------

These are manipulated to:
------------------------------------------------------------------------------------------------------------------
e=
0.0  0.0  0.0  0.0
0.0  0.2  0.0  0.1
0.0  0.0  0.0001  0.0001
0.0  0.1  0.0001  0.2001

a=
10000000.0  0.0  10000000.0  10000000.0
0.0  20.0  0.0  10.0
0.0  0.0  10.0999999996  0.0999999996275
0.0  10.0  0.0999999996275  20.0999999996

b=
1.0
0.0
-1.0
-1.0
------------------------------------------------------------------------------------------------------------------

With this the ODE solves with a time step of 10.0e-6 seconds. However, the stiff equation still is not solved satifsfactorily because of the entire row of large resistances. The way, loop current i1 would be calculated would be:

i1=(-10000000.0*i3-10000000.0*i4)/10000000.0

This would cause i1 to be of the same order as the other loop currents as this reduces to:

i1=-i3-i4

Which is wrong.

So this makes me wonder, why are the off-diagonal terms present in that case? Why not just do:

i1=u/10000000.0

Seems like that might be the solution. Because all I need is the sum total of all the resistance between the input signal in the loop to give me the approximate current. But this is then eventually ONLY the approximate current.

Monday, February 25, 2013

Circuit Solver - IV

Spent the weekend reading on different integration techniques and got only more confused.

So need to investigate a little more. Take the simple circuit below:

These are the parameters and the loops found:
--------------------------------------------------------------------------------------------------------------------
Resistor is  R4 = 10.000000  located at  9I
Inductor is  L4 =0.100000  located at  9K
Ammeter is  A5  located at  9J  with positive polarity towards 9K
Ammeter is  A2  located at  3H  with positive polarity towards 4H
Resistor is  R3 = 10.000000  located at  6I
Ammeter is  A4  located at  6J  with positive polarity towards 6K
Inductor is  L3 =0.100000  located at  6K
Ammeter is  A1  located at  1B  with positive polarity towards 1C
Resistor is  R1 = 0.100000  located at  1E
Resistor is  C1 = 10.000000  located at  4H
Inductor is  L1 =0.000100  located at  1F
Resistor is  R2 = 10.000000  located at  1I
Inductor is  L2 =0.100000  located at  1K
Ammeter is  A3  located at  1J  with positive polarity towards 1K
Resistor is  V1 = 100000.000000  located at  4D
Voltage Source is  V1 of 120.000000 V(peak), 60.000000 Hz(frequency) and 0.000000 (degrees phase shift)  located at  4A  with positive polarity towards 3A
**************************************************
Number of nodes 5
Number of branches 8
Number of loops 4
**************************************************
1D 2D 3D 4D 5D 6D 6C 6B 6A 5A 4A 3A 2A 1A 1B 1C 1D
6H 7H 8H 9H 9I 9J 9K 9L 8L 7L 6L 6K 6J 6I 6H
1H 1G 1F 1E 1D 2D 3D 4D 5D 6D 6E 6F 6G 6H 5H 4H 3H 2H 1H
1H 1G 1F 1E 1D 2D 3D 4D 5D 6D 6E 6F 6G 6H 7H 8H 9H 9I 9J 9K 9L 8L 7L 6L 5L 4L 3L 2L 1L 1K 1J 1I 1H
**************************************************
--------------------------------------------------------------------------------------------------------------------

Which is correct. However, these are the system matrices that result:
--------------------------------------------------------------------------------------------------------------------
a=
100000.0  0.0  100000.0  100000.0
0.0  20.0  0.0  10.0
100000.0  0.0  100010.1  100000.1
100000.0  10.0  100000.1  100020.1

e=
0.0  0.0  0.0  0.0
0.0  0.2  0.0  0.1
0.0  0.0  0.0001  0.0001
0.0  0.1  0.0001  0.2001

b=
1.0
0.0
0.0
0.0
--------------------------------------------------------------------------------------------------------------------

I chose resistor V1 as a large resistor. And that has caused all the problems. It appears in three out of four loops. And the entire ODE becomes stiff. One glaring element is in row 4, column 2 of matrix a. This is 10.0 while all other elements are 100000 or greater. This can be one way of reducing the system.

The resistor V1 should appear in at least one loop. Suppose it appears in a loop and we neglect the dynamics of that loop and consider a static equation. This may be OK because if you have a ridiculously low time constant, why solve it unless you want to. So can we reduce the time constants of the other equations to values that we can integrate peacefully? In the above example, resistor V1 appears quite conveniently in a static equation. So we can get rid of it from the other equations.

Performing row operations:

R3=R3-R1
R4=R4-R1

will get rid of these small time constants.

So this makes we wonder, if I need to do a preprocessing to isolate the time constants as far as possible. And if that needs to be done, what should be logic?

Thursday, February 21, 2013

Circuit Solver - III

With the SourceForge (SF from now on) project up and going, I will dedicate this blog to description rather than posting code.

One major issue. Choosing the simulation time step. The simulation time step can be chosen larger for speed but choosing a time step too large will result in instability. The concept is as follows.

Each loop has a time constant that is set by the ratio of total inductance to total resistance (L/R) in the loop. In order to simulate the system accurately, you need to choose a time step that is smaller than the smaller L/R ratio in the system. This has the potential of creating a few problems because if you choose a small parasitic inductance in series and a large resistance in parallel, a loop containing these will have a ridiculously low time constants that will need a simulation time step of nanoseconds.

So this means, maybe I need to choose the loops with the largest inductances instead. Or inform the user that the lowest time constant is "x" and a decent value of simulation time step is "y".

Monday, February 18, 2013

Circuit Solver - II

Since the number of programs is increasing and will continue to do so, I guess it is time to create a SourceForge project. Here is the link:
http://sourceforge.net/projects/pythonpowerelec/?source=directory
The programs can be found in the "Files" section.

I will keep posting program updates on the blog anyway.

Sunday, February 17, 2013

Circuit Solver

I finally got a basic circuit solver going! What has been done so far:

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):


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:

Tuesday, February 12, 2013

Circuit Parameters - VII

A few changes to the code for taking circuit parameters.

  1. I forgot completely "jump" labels. They do not need an object, but the code should ignore them while reading the cells or they will generate "Component not found" errors.
  2. Take the circuit layour file name as input from the user and generate the parameters files uniquely for the circuit.

The next stage is to generate the loop matrix for the circuit. The loops have been identified. However, every loop will interact with other loops. So, this has been coded.

Next stage is to read the parameters of every component and generate the ODE for solving the circuit KVL laws.

Anyway, here is the code (click on "view raw" below the code box for code in a new window)



Friday, February 8, 2013

Circuit Parameters - VI

There is a couple of major design flaw in the previous approach.

  1. The component names are automatically generated. It is always better to let the user name the components as they may correspond to PCB designs etc.
  2. Any change in the circuit topology, even an extension of a branch would be seen a new circuit with default values. Better to check if any of the components in the new circuit have parameters in the parameter file and start with those parameters.

So, a change in the code. The components will now be names followed by an underscore ("_") and then the name of the component. Example: Resistor_R1, voltagesource_v1. Case is not important in the component names though it is in the tags.

The unique identifier continues to be cell position. However, for a particular component type, the same tag can't be used. For example: resistor_r1 and resistor_r1 would be illegal. However, two different components could have the same tag. For example: Resistor_br and inductor_br are OK. If the user want to name the components according to function, this might be a good option.

Here is the code (click on "view raw" below the box to see the entire code):


Thursday, February 7, 2013

Circuit Parameters - V

Couple of updates to the previous code:

1. Some elements will also have a polarity. In this case, the voltage source definitely will have one. The capacitor could also have but I'll consider an ac capacitor for now.

2. There will have to be provision to check if everytime the program is run, whether the circuit has changed. If the circuit is the same but the parameters need to be updated, then nw_params.csv should not be filled with default values as the user will have to update everything all over again or will have to remember to copy nw_params.csv to another backup file. A more elaborate method can be thought of later where the actual topology can be read - nodes, branches, loops etc and if that remains the same along with elements in every branch, then circuit can be considered the same. Because otherwise a small change to a branch such as lengthening with more "wire" elements will be seen as a new circuit. For that matter, even if major changes are to be made, only the changed elements can be considered. All this is user interface and will come later.


So anyway, here is the updated code (click on "view raw" below the code box for code going out of the window):




Wednesday, February 6, 2013

Circuit Paramaters - IV

Now that the basic concept of entering parameters is established, this is the code for it.

Depending on the components found from the circuit spreadsheet, another spreadsheet is created with rows corresponding to every device found. Each device will have default parameters. So the user will have to change those. After all changes, the user can save it as another CSV file.

Each row in the spreadsheet will have the first three elements as - Component type (Resistor, Inductor etc), Component Reference (R1, L2 etc), Cell Position.

As before, the Cell position can be used to access the component object from the component object dictionary. The component classes will have a function to take the parameters which will be column 4 onwards.

Here is the code (click on "view raw" to see the part going out of the box):


Tuesday, February 5, 2013

Circuit Parameters - III

The next step is to take a sample circuit with a voltage source, resistors, inductors and capacitors and to automate the naming of the elements.

The unique  identification all these elements have is their position in the spreadsheet - only one element can occupy a cell. So the first part is to write a small function that will translate the cell information from [row, column] form to the form that can be read of from the spreadsheet.

That function is here:


So the string that marks the cell position being an immutable element can be the key in a dictionary. A dictionary can be made of all elements found in the circuit and these can be accessed at any time by the position.

Now an identification is chosen, what will be the value of this dictionary item? Ideally, it would be great if it could be the object itself. Then every element will be an object uniquely identified by its position and with the same methods for transferring data to and from the main solver.

This program seems to do this (click on "View Raw" below the code box to see the code going out of the window):


In this file, network_reader is the Python code that contains the circuit interpreter done so far. Each class so far contains only the information of its index (or serial number) and the position in the spreadsheet.
--------------------------------------------------------------------------------------
class Resistor:
    def __init__(self, res_index, res_pos):
        self.res_number=res_index
        self.res_pos=res_pos
    def display(self):
        print "Resistor is ",
        print "R"+str(self.res_number),
        print " located at ",
        print self.res_pos
--------------------------------------------------------------------------------------

All the class references are added to the dictionary:
--------------------------------------------------------------------------------------
component_list={"Resistor":Resistor, "Inductor":Inductor, "Capacitor":Capacitor, "Voltage_Source":Voltage_Source}
--------------------------------------------------------------------------------------

This is what component list looks like:
--------------------------------------------------------------------------------------
>>> component_list
{'Voltage_Source': <class __main__.Voltage_Source at 0x02C9F810>, 'Resistor': <class __main__.Resistor at 0x02C9F768>, 'Inductor': <class __main__.Inductor at 0x02C9F7A0>, 'Capacitor': <class __main__.Capacitor at 0x02C9F7D8>}
--------------------------------------------------------------------------------------


The next step is to generate a dictionary of all the components found in the spreadsheet:
--------------------------------------------------------------------------------------
components_found={}
for c1 in range(len(tst_mat)):
    for c2 in range(len(tst_mat[0])):
        if tst_mat[c1][c2]:
            if tst_mat[c1][c2].lower()!="wire":
                if tst_mat[c1][c2] in component_list.keys():
                    if tst_mat[c1][c2] not in components_found:
                        components_found[tst_mat[c1][c2]]=[nw_rd.csv_element([c1, c2])]
                    else:
                        components_found[tst_mat[c1][c2]].append(nw_rd.csv_element([c1, c2]))
                else:
                    print "Error! Component at %s doesn't exist." %nw_rd.csv_element([c1, c2])
--------------------------------------------------------------------------------------

And this is what the dictionary looks like:
--------------------------------------------------------------------------------------
>>> components_found
{'Voltage_Source': ['4A'], 'Resistor': ['1B', '1F'], 'Inductor': ['1C', '1H'], 'Capacitor': ['4E']}
--------------------------------------------------------------------------------------

From this list, each component can be given a name as they have a specific index in a list.

Finally, to create a dictionary of all the components found. Here the unique identification will be the position in the spreadsheet:
--------------------------------------------------------------------------------------
component_objects={}
for items in components_found.keys():
    for c1 in range(len(components_found[items])):
        component_objects[components_found[items][c1]]=component_list[items](c1+1, components_found[items][c1])
--------------------------------------------------------------------------------------

A quick explanation to what I have done:

1. components_found[items] are the different types of components - voltage source, resistors etc.
2. components_found[items][c1] are the positions of each of these components. So these are the keys of the dictionary component_objects.
3. component_list[items] is the value of the dictionary item corresponding to the component type - and this is a class constructor. This contructor takes the values of the index and the position.
This is the final result:
--------------------------------------------------------------------------------------
>>>
Inductor is  L1  located at  1C
Resistor is  R1  located at  1B
Resistor is  R2  located at  1F
Inductor is  L2  located at  1H
Capacitor is  C1  located at  4E
Voltage Source is  V1  located at  4A
--------------------------------------------------------------------------------------

For the circuit below

Monday, February 4, 2013

Circuit Parameters - II

The next step is to write the code such that all elements are identified and parameters are obtained. In C++ the concept that I followed was that every element was an object - a resistor, an inverter etc. There was three levels of data transfer to these objects. The first was when they were initialized. The second was when the parameters and initial conditions they contained was transferred to the system matrices for solving the ODEs and third was when the results of the ODE was transferred back to the objects to update the values of voltage/current etc.

In C++, the objects were created manually. In Python, I would like the objects to be created automatically from the network layout in the CSV file.

So, testing how to do this first step. A little piece of code to see how automatically the objects can be created and stored. This code is too ridiculously simple to put on Git, so this is it:

-----------------------------------------------------------------------------------------------------------------
#! /usr/bin/env python
import sys, math

class Resistor:
    def __init__(self, res_index):
        self.res_number=res_index
    def display(self):
        print "Resistor is ",
        print "R"+str(self.res_number)

res_list=[]
for c1 in range(1, 5):
    res_name="R"+str(c1)
    res_name=Resistor(c1)
    res_list.append(res_name)

print res_list
for res in res_list:
    res.display()
-----------------------------------------------------------------------------------------------------------------

Here, I am creating a class "Resistor" which has only one data element - its index. So the resistors will be called R1, R2, etc.

I create a list of resistors res_list and create resistors R1 to R4. Also, objects are created with that tag and added to res_list.

Here is the output:
-----------------------------------------------------------------------------------------------------------------
>>>
[<__main__.Resistor instance at 0x02A1F4E0>, <__main__.Resistor instance at 0x02A1F508>, <__main__.Resistor instance at 0x02A1F530>, <__main__.Resistor instance at 0x02A1F558>]
Resistor is  R1
Resistor is  R2
Resistor is  R3
Resistor is  R4
-----------------------------------------------------------------------------------------------------------------

res_list is a list with instances of the class Resistor. But importantly, when I access the method display of the class Resistor by res.display(), it does give me the correct resistor labels.

So now let me put everything in the spreadsheet into objects.

Friday, February 1, 2013

Circuit Parameters

After loop identification, now comes the time to input the parameters of the circuit.

One very nice thing about most circuit simulators is the dialog box that opens up with each element with all the parameters. So essentially, I need to put these features into my circuit simulator:

1. The user should only indicate the element at a given position.
2. We need to then generate a list of all the elements found, names for them, their positions in the spreadsheet and finally their parameters with some to be defaults.
3. With the case of elements like diodes, IGBTs and others that have polarities or certain ways in which they are connected, the polarity can be specified in the separate dialog.

For multiport elements like machines, transformers, there will have to be more than one tag and these will have to be translated. Things would have been much more convenient with a GUI.

This will be my first approach:

1. Create an index of names - "Resistor", "Inductor", "Capacitor", "Voltage_Source" etc.
2. The network interpreter will divide the circuit into three types of elements - "wire", "jump" and elements such as the above.
3. Each time an element is found, it will be assigned a name that creates an object of it - like R1, R2, C1, etc.
4. The program will create another spreadsheet with all the element names, their positions and parameter values in separate columns.
5. The user has to modify these values and continue with the simulation.

For passive circuits with only resistors, inductors, capacitors and voltage sources, I don't see much of a problem. However, there would be a problem with machines for example, a three-phase synchronous generator. So the stator will have three connections and the rotor will have two. How do I map these connections on a spreadsheet?

Tuesday, January 29, 2013

Loop identification - XIV

Time for some testing.

Circuit 1 - Single phase diode rectifier



Result:

>>>
Number of nodes 6
Number of branches 9
Number of loops 5
**************************************************
1K 2K 3K 4K 5K 6K 7K 8K 9K 10K 11K 11L 11M 10M 9M 8M 7M 6M 5M 4M 3M 2M 1M 1L 1K
1K 1J 1I 2I 3I 4I 5I 6I 7I 8I 9I 10I 11I 11J 11K 10K 9K 8K 7K 6K 5K 4K 3K 2K 1K
1K 1J 1I 1H 1G 1F 1E 2E 3E 4E 5E 6E 7E 8E 9E 10E 11E 11F 11G 11H 11I 11J 11K 10K 9K 8K 7K 6K 5K 4K 3K 2K 1K
1K 1J 1I 2I 3I 4I 5I 6I 6H 7B 7A 6A 5A 5B 5C 5D 5E 6E 7E 8E 9E 10E 11E 11F 11G 11H 11I 11J 11K 11L 11M 10M 9M 8M 7M 6M 5M 4M 3M 2M 1M 1L 1K
1K 1J 1I 1H 1G 1F 1E 2E 3E 4E 5E 5D 5C 5B 5A 6A 7A 7B 6H 6I 7I 8I 9I 10I 11I 11J 11K 11L 11M 10M 9M 8M 7M 6M 5M 4M 3M 2M 1M 1L 1K


One additional loop generated but result is OK.


Circuit 2 - Three phase diode rectifier

Result:

>>>
Number of nodes 10
Number of branches 15
Number of loops 9
**************************************************
1M 1L 1K 1J 1I 2I 3I 4I 5I 6I 6H 7B 7A 8A 9A 9B 6L 6M 5M 4M 3M 2M 1M
1M 1L 1K 1J 1I 1H 1G 1F 1E 2E 3E 4E 5E 5D 5C 5B 5A 6A 7A 8A 9A 9B 6L 6M 5M 4M 3M 2M 1M
11O 11P 11Q 10Q 9Q 8Q 7Q 6Q 5Q 4Q 3Q 2Q 1Q 1P 1O 2O 3O 4O 5O 6O 7O 8O 9O 10O 11O
1M 1L 1K 1J 1I 2I 3I 4I 5I 6I 7I 8I 9I 10I 11I 11J 11K 11L 11M 10M 9M 8M 7M 6M 5M 4M 3M 2M 1M
1M 1L 1K 1J 1I 1H 1G 1F 1E 2E 3E 4E 5E 6E 7E 8E 9E 10E 11E 11F 11G 11H 11I 11J 11K 11L 11M 10M 9M 8M 7M 6M 5M 4M 3M 2M 1M
1M 1L 1K 1J 1I 2I 3I 4I 5I 6I 7I 8I 9I 10I 11I 11J 11K 11L 11M 11N 11O 11P 11Q 10Q 9Q 8Q 7Q 6Q 5Q 4Q 3Q 2Q 1Q 1P 1O 1N 1M
1M 1L 1K 1J 1I 2I 3I 4I 5I 6I 7I 8I 9I 10I 11I 11H 11G 11F 11E 10E 9E 8E 7E 6E 5E 5D 5C 5B 5A 6A 7A 8A 9A 9B 6L 6M 5M 4M 3M 2M 1M
1M 1L 1K 1J 1I 1H 1G 1F 1E 2E 3E 4E 5E 6E 7E 8E 9E 10E 11E 11F 11G 11H 11I 11J 11K 11L 11M 11N 11O 11P 11Q 10Q 9Q 8Q 7Q 6Q 5Q 4Q 3Q 2Q 1Q 1P 1O 1N 1M
1M 1L 1K 1J 1I 1H 1G 1F 1E 2E 3E 4E 5E 6E 7E 8E 9E 10E 11E 11F 11G 11H 11I 10I 9I 8I 7I 6I 6H 7B 7A 8A 9A 9B 6L 6M 5M 4M 3M 2M 1M


Additional 3 loops generated but result is OK.



Circuit 3 - Dc-dc boost converter

Result:

>>>
Number of nodes 4
Number of branches 6
Number of loops 3
**************************************************
1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 11C 11B 11A 10A 9A 8A 7A 6A 5A 4A 3A 2A 1A 1B 1C 1D
11F 11G 11H 10H 9H 8H 7H 6H 5H 4H 3H 2H 1H 1G 1F 2F 3F 4F 5F 6F 7F 8F 9F 10F 11F
1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 11E 11F 11G 11H 10H 9H 8H 7H 6H 5H 4H 3H 2H 1H 1G 1F


The exact number of loops! This happens quite rarely though.


Circuit 4 - Dc-dc boost converter feeding a single-phase converter without FWDs

Result:
>>>
Number of nodes 14
Number of branches 21
Number of loops 12
**************************************************
1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 11C 11B 11A 10A 9A 8A 7A 6A 5A 4A 3A 2A 1A 1B 1C 1D
1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 11E 11F 10F 9F 8F 7F 6F 5F 4F 3F 2F 1F
1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 11E 11F 11G 11H 10H 9H 8H 7H 6H 5H 4H 3H 2H 1H 1G 1F
1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 11E 11F 11G 11H 11I 11J 10J 9J 8J 7J 6J 5J 4J 3J 2J 1J 1I 1H 1G 1F
1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 11E 11F 11G 11H 11I 11J 11K 11L 11M 11N 10N 9N 8N 7N 6N 5N 4N 3N 2N 1N 1M 1L 1K 1J 1I 1H 1G 1F
1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 11E 11F 11G 11H 11I 11J 11K 11L 11M 11N 11O 11P 11Q 11R 10R 9R 8R 7R 6R 5R 4R 3R 2R 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F
1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 11E 11F 11G 11H 11I 11J 11K 11L 11M 11N 10N 9N 8N 7N 6N 6O 7U 7V 8V 9V 9U 6K 6J 5J 4J 3J 2J 1J 1I 1H 1G 1F
1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 11E 11F 11G 11H 11I 11J 11K 11L 11M 11N 11O 11P 11Q 11R 10R 9R 8R 7R 6R 5R 5S 5T 5U 5V 6V 7V 8V 9V 9U 6K 6J 5J 4J 3J 2J 1J 1I 1H 1G 1F
1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 11E 11F 11G 11H 11I 11J 10J 9J 8J 7J 6J 6K 9U 9V 8V 7V 7U 6O 6N 5N 4N 3N 2N 1N 1M 1L 1K 1J 1I 1H 1G 1F
1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 11E 11F 11G 11H 11I 11J 10J 9J 8J 7J 6J 6K 9U 9V 8V 7V 6V 5V 5U 5T 5S 5R 4R 3R 2R 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F
1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 11E 11F 11G 11H 11I 11J 11K 11L 11M 11N 10N 9N 8N 7N 6N 6O 7U 7V 6V 5V 5U 5T 5S 5R 4R 3R 2R 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F
1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 11E 11F 11G 11H 11I 11J 11K 11L 11M 11N 11O 11P 11Q 11R 10R 9R 8R 7R 6R 5R 5S 5T 5U 5V 6V 7V 7U 6O 6N 5N 4N 3N 2N 1N 1M 1L 1K 1J 1I 1H 1G 1F


Three additional loops generated but result is OK.



Circuit 5 - Dc-dc boost converter feeding a three phase three level diode clamped inverter connected to grid

Result (I am not going to check this!):


>>>

Number of nodes 48

Number of branches 72

Number of loops 70

**************************************************

1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25C 25B 25A 24A 23A 22A 21A 20A 19A 18A 17A 16A 15A 14A 13A 12A 11A 10A 9A 8A 7A 6A 5A 4A 3A 2A 1A 1B 1C 1D

23J 23K 23L 22L 21L 21K 21J 22J 23J

21R 22R 23R 23S 23T 22T 21T 21S 21R

21Z 22Z 23Z 23AA 23AB 22AB 21AB 21AA 21Z

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 14F 13F 12F 11F 10F 9F 8F 7F 6F 5F 4F 3F 2F 1F

5J 5K 5L 4L 3L 3K 3J 4J 5J

9J 10J 11J 11K 11L 10L 9L 9K 9J

17J 17K 17L 16L 15L 15K 15J 16J 17J

3R 4R 5R 5S 5T 4T 3T 3S 3R

11R 11S 11T 10T 9T 9S 9R 10R 11R

15R 16R 17R 17S 17T 16T 15T 15S 15R

5Z 5AA 5AB 4AB 3AB 3AA 3Z 4Z 5Z

9Z 10Z 11Z 11AA 11AB 10AB 9AB 9AA 9Z

17Z 17AA 17AB 16AB 15AB 15AA 15Z 16Z 17Z

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 12N 11N 10N 9N 8N 7N 7M 7L 7K 7J 6J 5J 5K 5L 4L 3L 3K 3J 2J 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 25S 25T 25U 25V 25W 25X 25Y 25Z 24Z 23Z 22Z 21Z 20Z 19Z 19AA 19AB 19AC 19AD 18AD 17AD 16AD 15AD 14AD 13AD 12AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 24R 23R 22R 21R 20R 19R 19S 19T 19U 19V 18V 17V 16V 15V 14V 13V 13W 12G 12F 11F 10F 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 24J 23J 23K 23L 22L 21L 21K 21J 20J 19J 19K 19L 19M 19N 18N 17N 16N 15N 14N 13N 13O 15G 15F 14F 13F 12F 11F 10F 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 14F 13F 12F 12G 13W 13V 12V 11V 10V 9V 8V 7V 7U 7T 7S 7R 6R 5R 4R 3R 2R 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F



1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 24J 23J 23K 23L 22L 21L 21K 21J 20J 19J 19K 19L 19M 19N 18N 17N 16N 15N 14N 13N 12N 11N 10N 9N 8N 7N 7M 7L 7K 7J 6J 5J 4J 3J 2J 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 14F 13F 12F 11F 10F 9F 9G 11AE 11AD 10AD 9AD 8AD 7AD 7AC 7AB 7AA 7Z 6Z 5Z 5AA 5AB 4AB 3AB 3AA 3Z 2Z 1Z 1Y 1X 1W 1V 1U 1T 1S 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 25S 25T 25U 25V 25W 25X 25Y 25Z 24Z 23Z 22Z 21Z 20Z 19Z 19AA 19AB 19AC 19AD 18AD 17AD 16AD 15AD 14AD 13AD 12AD 11AD 10AD 9AD 8AD 7AD 7AC 7AB 7AA 7Z 6Z 5Z 5AA 5AB 4AB 3AB 3AA 3Z 2Z 1Z 1Y 1X 1W 1V 1U 1T 1S 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 24R 23R 22R 21R 20R 19R 19S 19T 19U 19V 18V 17V 16V 15V 14V 13V 12V 11V 10V 9V 8V 7V 7U 7T 7S 7R 6R 5R 5S 5T 4T 3T 3S 3R 2R 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 14N 15N 16N 17N 18N 19N 19M 19L 19K 19J 20J 21J 22J 23J 24J 25J 25K 25L 25M 25N 25O 25P 25Q 25R 25S 25T 25U 25V 25W 25X 25Y 25Z 24Z 23Z 23AA 23AB 22AB 21AB 21AA 21Z 20Z 19Z 19AA 19AB 19AC 19AD 18AD 17AD 16AD 15AD 14AD 13AD 12AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 14N 15N 16N 17N 18N 19N 19M 19L 19K 19J 18J 17J 17K 17L 16L 15L 15K 15J 14J 13J 12J 11J 10J 9J 8J 7J 6J 5J 5K 5L 4L 3L 3K 3J 2J 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 12N 11N 10N 9N 8N 7N 7M 7L 7K 7J 6J 5J 5K 5L 4L 3L 3K 3J 2J 1J 1K 1L 1M 1N 1O 1P 1Q 1R 1S 1T 1U 1V 1W 1X 1Y 1Z 2Z 3Z 4Z 5Z 6Z 7Z 7AA 7AB 7AC 7AD 8AD 9AD 10AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 14F 13F 12F 12G 13W 13V 14V 15V 16V 17V 18V 19V 19U 19T 19S 19R 20R 21R 21S 21T 22T 23T 23S 23R 24R 25R 25S 25T 25U 25V 25W 25X 25Y 25Z 24Z 23Z 23AA 23AB 22AB 21AB 21AA 21Z 20Z 19Z 19AA 19AB 19AC 19AD 18AD 17AD 16AD 15AD 14AD 13AD 12AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 14F 13F 12F 12G 13W 13V 12V 11V 10V 9V 8V 7V 7U 7T 7S 7R 6R 5R 4R 3R 2R 1R 1S 1T 1U 1V 1W 1X 1Y 1Z 2Z 3Z 4Z 5Z 6Z 7Z 7AA 7AB 7AC 7AD 8AD 9AD 10AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 25S 25T 25U 25V 25W 25X 25Y 25Z 24Z 23Z 22Z 21Z 20Z 19Z 18Z 17Z 17AA 17AB 16AB 15AB 15AA 15Z 14Z 13Z 12Z 11Z 10Z 9Z 8Z 7Z 7AA 7AB 7AC 7AD 8AD 9AD 10AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 24R 23R 22R 21R 20R 19R 19S 19T 19U 19V 18V 17V 16V 15V 14V 13V 13W 12G 12F 13F 14F 15F 15G 13O 13N 12N 11N 10N 9N 8N 7N 7M 7L 7K 7J 6J 5J 4J 3J 2J 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 24J 23J 23K 23L 22L 21L 21K 21J 20J 19J 18J 17J 16J 15J 14J 13J 12J 11J 11K 11L 10L 9L 9K 9J 8J 7J 6J 5J 4J 3J 2J 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 24J 23J 23K 23L 22L 21L 21K 21J 20J 19J 19K 19L 19M 19N 18N 17N 16N 15N 14N 13N 13O 15G 15F 14F 13F 12F 12G 13W 13V 12V 11V 10V 9V 8V 7V 7U 7T 7S 7R 6R 5R 5S 5T 4T 3T 3S 3R 2R 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 14N 15N 16N 17N 18N 19N 19M 19L 19K 19J 20J 21J 22J 23J 24J 25J 25K 25L 25M 25N 25O 25P 25Q 25R 24R 23R 23S 23T 22T 21T 21S 21R 20R 19R 19S 19T 19U 19V 18V 17V 16V 15V 14V 13V 13W 12G 12F 11F 10F 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 14N 15N 16N 17N 18N 19N 19M 19L 19K 19J 18J 17J 17K 17L 16L 15L 15K 15J 14J 13J 13K 17AH 17AI 16AI 15AI 14AI 13AI 13AH 13AA 13Z 14Z 15Z 15AA 15AB 16AB 17AB 17AA 17Z 18Z 19Z 19AA 19AB 19AC 19AD 18AD 17AD 16AD 15AD 14AD 13AD 12AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 14N 15N 16N 17N 18N 19N 19M 19L 19K 19J 18J 17J 17K 17L 16L 15L 15K 15J 14J 13J 13K 17AH 17AI 16AI 15AI 14AI 13AI 13AH 13AA 13Z 12Z 11Z 10Z 9Z 8Z 7Z 7AA 7AB 7AC 7AD 8AD 9AD 10AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 12N 11N 10N 9N 8N 7N 7M 7L 7K 7J 8J 9J 10J 11J 12J 13J 13K 17AH 17AI 16AI 15AI 14AI 13AI 13AH 13AA 13Z 14Z 15Z 15AA 15AB 16AB 17AB 17AA 17Z 18Z 19Z 19AA 19AB 19AC 19AD 18AD 17AD 16AD 15AD 14AD 13AD 12AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 12N 11N 10N 9N 8N 7N 7M 7L 7K 7J 8J 9J 10J 11J 12J 13J 13K 17AH 17AI 16AI 15AI 14AI 13AI 13AH 13AA 13Z 12Z 11Z 10Z 9Z 8Z 7Z 7AA 7AB 7AC 7AD 8AD 9AD 10AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 12N 11N 10N 9N 8N 7N 7M 7L 7K 7J 6J 5J 5K 5L 4L 3L 3K 3J 2J 1J 1K 1L 1M 1N 1O 1P 1Q 1R 2R 3R 4R 5R 6R 7R 7S 7T 7U 7V 8V 9V 10V 11V 12V 13V 13W 12G 12F 11F 10F 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 25S 25T 25U 25V 25W 25X 25Y 25Z 24Z 23Z 22Z 21Z 20Z 19Z 19AA 19AB 19AC 19AD 18AD 17AD 16AD 15AD 14AD 13AD 12AD 11AD 11AE 9G 9F 10F 11F 12F 13F 14F 15F 15G 13O 13N 12N 11N 10N 9N 8N 7N 7M 7L 7K 7J 6J 5J 5K 5L 4L 3L 3K 3J 2J 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 25S 25T 25U 25V 25W 25X 25Y 25Z 24Z 23Z 22Z 21Z 20Z 19Z 19AA 19AB 19AC 19AD 18AD 17AD 16AD 15AD 14AD 13AD 12AD 11AD 11AE 9G 9F 10F 11F 12F 12G 13W 13V 12V 11V 10V 9V 8V 7V 7U 7T 7S 7R 6R 5R 4R 3R 2R 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 24R 23R 22R 21R 20R 19R 18R 17R 17S 17T 16T 15T 15S 15R 14R 13R 12R 11R 10R 9R 8R 7R 7S 7T 7U 7V 8V 9V 10V 11V 12V 13V 13W 12G 12F 11F 10F 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 24R 23R 22R 21R 20R 19R 19S 19T 19U 19V 18V 17V 16V 15V 14V 13V 13W 12G 12F 11F 10F 9F 9G 11AE 11AD 10AD 9AD 8AD 7AD 7AC 7AB 7AA 7Z 6Z 5Z 4Z 3Z 2Z 1Z 1Y 1X 1W 1V 1U 1T 1S 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 24J 23J 23K 23L 22L 21L 21K 21J 20J 19J 18J 17J 16J 15J 14J 13J 13K 17AH 17AI 16AI 15AI 14AI 13AI 13AH 13AA 13Z 14Z 15Z 16Z 17Z 18Z 19Z 19AA 19AB 19AC 19AD 18AD 17AD 16AD 15AD 14AD 13AD 12AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 24J 23J 23K 23L 22L 21L 21K 21J 20J 19J 18J 17J 16J 15J 14J 13J 13K 17AH 17AI 16AI 15AI 14AI 13AI 13AH 13AA 13Z 12Z 11Z 11AA 11AB 10AB 9AB 9AA 9Z 8Z 7Z 7AA 7AB 7AC 7AD 8AD 9AD 10AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 24J 23J 23K 23L 22L 21L 21K 21J 20J 19J 18J 17J 16J 15J 14J 13J 12J 11J 11K 11L 10L 9L 9K 9J 8J 7J 7K 7L 7M 7N 8N 9N 10N 11N 12N 13N 13O 15G 15F 14F 13F 12F 11F 10F 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 24J 23J 23K 23L 22L 21L 21K 21J 20J 19J 19K 19L 19M 19N 18N 17N 16N 15N 14N 13N 13O 15G 15F 14F 13F 12F 11F 10F 9F 9G 11AE 11AD 10AD 9AD 8AD 7AD 7AC 7AB 7AA 7Z 6Z 5Z 4Z 3Z 2Z 1Z 1Y 1X 1W 1V 1U 1T 1S 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 14N 15N 16N 17N 18N 19N 19M 19L 19K 19J 18J 17J 17K 17L 16L 15L 15K 15J 14J 13J 13K 17AH 17AI 16AI 15AI 15AH 13S 13R 14R 15R 15S 15T 16T 17T 17S 17R 18R 19R 19S 19T 19U 19V 18V 17V 16V 15V 14V 13V 13W 12G 12F 11F 10F 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 14N 15N 16N 17N 18N 19N 19M 19L 19K 19J 18J 17J 17K 17L 16L 15L 15K 15J 14J 13J 13K 17AH 17AI 16AI 15AI 15AH 13S 13R 12R 11R 10R 9R 8R 7R 7S 7T 7U 7V 8V 9V 10V 11V 12V 13V 13W 12G 12F 11F 10F 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 12N 11N 10N 9N 8N 7N 7M 7L 7K 7J 8J 9J 10J 11J 12J 13J 13K 17AH 17AI 16AI 15AI 15AH 13S 13R 14R 15R 15S 15T 16T 17T 17S 17R 18R 19R 19S 19T 19U 19V 18V 17V 16V 15V 14V 13V 13W 12G 12F 11F 10F 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 12N 11N 10N 9N 8N 7N 7M 7L 7K 7J 8J 9J 10J 11J 12J 13J 13K 17AH 17AI 16AI 15AI 15AH 13S 13R 12R 11R 10R 9R 8R 7R 7S 7T 7U 7V 8V 9V 10V 11V 12V 13V 13W 12G 12F 11F 10F 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 14F 13F 12F 12G 13W 13V 14V 15V 16V 17V 18V 19V 19U 19T 19S 19R 18R 17R 16R 15R 14R 13R 13S 15AH 15AI 14AI 13AI 13AH 13AA 13Z 14Z 15Z 16Z 17Z 18Z 19Z 19AA 19AB 19AC 19AD 18AD 17AD 16AD 15AD 14AD 13AD 12AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 14F 13F 12F 12G 13W 13V 14V 15V 16V 17V 18V 19V 19U 19T 19S 19R 18R 17R 16R 15R 14R 13R 13S 15AH 15AI 14AI 13AI 13AH 13AA 13Z 12Z 11Z 11AA 11AB 10AB 9AB 9AA 9Z 8Z 7Z 7AA 7AB 7AC 7AD 8AD 9AD 10AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 14F 13F 12F 12G 13W 13V 14V 15V 16V 17V 18V 19V 19U 19T 19S 19R 18R 17R 16R 15R 14R 13R 12R 11R 11S 11T 10T 9T 9S 9R 8R 7R 6R 5R 4R 3R 2R 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 14F 13F 12F 12G 13W 13V 12V 11V 10V 9V 8V 7V 7U 7T 7S 7R 8R 9R 9S 9T 10T 11T 11S 11R 12R 13R 13S 15AH 15AI 14AI 13AI 13AH 13AA 13Z 14Z 15Z 16Z 17Z 18Z 19Z 19AA 19AB 19AC 19AD 18AD 17AD 16AD 15AD 14AD 13AD 12AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 14F 13F 12F 12G 13W 13V 12V 11V 10V 9V 8V 7V 7U 7T 7S 7R 8R 9R 9S 9T 10T 11T 11S 11R 12R 13R 13S 15AH 15AI 14AI 13AI 13AH 13AA 13Z 12Z 11Z 11AA 11AB 10AB 9AB 9AA 9Z 8Z 7Z 7AA 7AB 7AC 7AD 8AD 9AD 10AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 25S 25T 25U 25V 25W 25X 25Y 25Z 24Z 23Z 22Z 21Z 20Z 19Z 18Z 17Z 17AA 17AB 16AB 15AB 15AA 15Z 14Z 13Z 12Z 11Z 10Z 9Z 8Z 7Z 6Z 5Z 5AA 5AB 4AB 3AB 3AA 3Z 2Z 1Z 1Y 1X 1W 1V 1U 1T 1S 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 24R 23R 22R 21R 20R 19R 18R 17R 17S 17T 16T 15T 15S 15R 14R 13R 13S 15AH 15AI 14AI 13AI 13AH 13AA 13Z 14Z 15Z 15AA 15AB 16AB 17AB 17AA 17Z 18Z 19Z 19AA 19AB 19AC 19AD 18AD 17AD 16AD 15AD 14AD 13AD 12AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 24R 23R 22R 21R 20R 19R 18R 17R 17S 17T 16T 15T 15S 15R 14R 13R 13S 15AH 15AI 14AI 13AI 13AH 13AA 13Z 12Z 11Z 10Z 9Z 8Z 7Z 7AA 7AB 7AC 7AD 8AD 9AD 10AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 24R 23R 22R 21R 20R 19R 18R 17R 17S 17T 16T 15T 15S 15R 14R 13R 12R 11R 10R 9R 8R 7R 6R 5R 5S 5T 4T 3T 3S 3R 2R 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 25K 25L 25M 25N 25O 25P 25Q 25R 24R 23R 22R 21R 20R 19R 19S 19T 19U 19V 18V 17V 16V 15V 14V 13V 12V 11V 10V 9V 8V 7V 7U 7T 7S 7R 6R 5R 5S 5T 4T 3T 3S 3R 2R 1R 1S 1T 1U 1V 1W 1X 1Y 1Z 2Z 3Z 3AA 3AB 4AB 5AB 5AA 5Z 6Z 7Z 7AA 7AB 7AC 7AD 8AD 9AD 10AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 24J 23J 23K 23L 22L 21L 21K 21J 20J 19J 18J 17J 16J 15J 14J 13J 13K 17AH 17AI 16AI 15AI 15AH 13S 13R 14R 15R 16R 17R 18R 19R 19S 19T 19U 19V 18V 17V 16V 15V 14V 13V 13W 12G 12F 11F 10F 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 24J 23J 23K 23L 22L 21L 21K 21J 20J 19J 18J 17J 16J 15J 14J 13J 13K 17AH 17AI 16AI 15AI 15AH 13S 13R 12R 11R 11S 11T 10T 9T 9S 9R 8R 7R 7S 7T 7U 7V 8V 9V 10V 11V 12V 13V 13W 12G 12F 11F 10F 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 25G 25H 25I 25J 24J 23J 23K 23L 22L 21L 21K 21J 20J 19J 19K 19L 19M 19N 18N 17N 16N 15N 14N 13N 12N 11N 10N 9N 8N 7N 7M 7L 7K 7J 6J 5J 4J 3J 2J 1J 1K 1L 1M 1N 1O 1P 1Q 1R 1S 1T 1U 1V 1W 1X 1Y 1Z 2Z 3Z 3AA 3AB 4AB 5AB 5AA 5Z 6Z 7Z 7AA 7AB 7AC 7AD 8AD 9AD 10AD 11AD 11AE 9G 9F 8F 7F 6F 5F 4F 3F 2F 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 14N 15N 16N 17N 18N 19N 19M 19L 19K 19J 18J 17J 17K 17L 16L 15L 15K 15J 14J 13J 13K 17AH 17AI 16AI 15AI 14AI 13AI 13AH 13AA 13Z 12Z 11Z 10Z 9Z 8Z 7Z 6Z 5Z 5AA 5AB 4AB 3AB 3AA 3Z 2Z 1Z 1Y 1X 1W 1V 1U 1T 1S 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 14N 15N 16N 17N 18N 19N 19M 19L 19K 19J 18J 17J 17K 17L 16L 15L 15K 15J 14J 13J 13K 17AH 17AI 16AI 15AI 15AH 13S 13R 12R 11R 10R 9R 8R 7R 6R 5R 5S 5T 4T 3T 3S 3R 2R 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 12N 11N 10N 9N 8N 7N 7M 7L 7K 7J 8J 9J 10J 11J 12J 13J 13K 17AH 17AI 16AI 15AI 14AI 13AI 13AH 13AA 13Z 12Z 11Z 10Z 9Z 8Z 7Z 6Z 5Z 5AA 5AB 4AB 3AB 3AA 3Z 2Z 1Z 1Y 1X 1W 1V 1U 1T 1S 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 15G 13O 13N 12N 11N 10N 9N 8N 7N 7M 7L 7K 7J 8J 9J 10J 11J 12J 13J 13K 17AH 17AI 16AI 15AI 15AH 13S 13R 12R 11R 10R 9R 8R 7R 6R 5R 5S 5T 4T 3T 3S 3R 2R 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 14F 13F 12F 12G 13W 13V 14V 15V 16V 17V 18V 19V 19U 19T 19S 19R 18R 17R 16R 15R 14R 13R 13S 15AH 15AI 16AI 17AI 17AH 13K 13J 12J 11J 11K 11L 10L 9L 9K 9J 8J 7J 6J 5J 4J 3J 2J 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 14F 13F 12F 12G 13W 13V 12V 11V 10V 9V 8V 7V 7U 7T 7S 7R 8R 9R 9S 9T 10T 11T 11S 11R 12R 13R 13S 15AH 15AI 16AI 17AI 17AH 13K 13J 12J 11J 11K 11L 10L 9L 9K 9J 8J 7J 6J 5J 4J 3J 2J 1J 1I 1H 1G 1F

1F 1E 1D 2D 3D 4D 5D 6D 7D 8D 9D 10D 11D 12D 13D 14D 15D 16D 17D 18D 19D 20D 21D 22D 23D 24D 25D 25E 25F 24F 23F 22F 21F 20F 19F 18F 17F 16F 15F 14F 13F 12F 11F 10F 9F 9G 11AE 11AD 12AD 13AD 14AD 15AD 16AD 17AD 18AD 19AD 19AC 19AB 19AA 19Z 18Z 17Z 17AA 17AB 16AB 15AB 15AA 15Z 14Z 13Z 12Z 11Z 10Z 9Z 8Z 7Z 6Z 5Z 5AA 5AB 4AB 3AB 3AA 3Z 2Z 1Z 1Y 1X 1W 1V 1U 1T 1S 1R 1Q 1P 1O 1N 1M 1L 1K 1J 1I 1H 1G 1F