Last active
August 29, 2015 13:58
-
-
Save Spacerat/9959756 to your computer and use it in GitHub Desktop.
Circuit tree calculator
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"metadata": { | |
"name": "Circuit Tree" | |
}, | |
"nbformat": 3, | |
"nbformat_minor": 0, | |
"worksheets": [ | |
{ | |
"cells": [ | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": "%pylab inline", | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": "Populating the interactive namespace from numpy and matplotlib\n" | |
} | |
], | |
"prompt_number": 13 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": "# Circuit tree calculator\n\n## Basics\n\nCircuits are created from Impedence Havers. An Impedence Haver is anything which returns a value of **get_impedence(*frequency*)**.\n\nResistors, Capacitors and Inductors are simple Impedence Havers which return a value based on their value.\n\n## Blocks\n\nBlocks are a special kind of impedence haver. Blocks have a *branches* variable, which is a list of branches. Each branch is a list of Impedence Havers.\n\nA Block's **get_impedence()** function performs calculates:\n\n$$\n\\frac{1}{\\sum_{b=1}^{branches} (\\frac{1}{\\sum_{c=1}^{components} Z_c})}\n$$\n\nFor each component $c$ of each branch $b$ of the block. In the case that there is only one branch, the block is a serial circuit instead of parallel. The impedence calculation formula does *not* need to change, since it simplifies to\n\n$$\n\\frac{1}{\\frac{1}{\\sum_{c=1}^{components} Z}} = \\sum_{c=1}^{components} Z\n$$\n\n## Possible improvements:\n\n- A faster string parser which does not use recursion.\n- Graphical representation (graphviz would be good for this - you can generat a *dot* file and feed it to Graphviz.\n- Better treatment of infinite/zero impedences" | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": "\n\nclass Impedence_Haver(object):\n \"\"\"Anything which can have an impedence.\"\"\"\n def get_impedence(frequency):\n raise \"Not Implimented\"\n \n def __str__(self):\n \"\"\"The default function for representing a component, looks like:\n Kind[Value]\n \"\"\"\n return \"{}{}\".format(self.__class__.__name__, self.value)\n \n def __repr__(self):\n return \"{}({})\".format(self.__class__.__name__, self.value)\n\nclass Block(Impedence_Haver):\n \"\"\"A block is an impedence haver.\n It contains a list of branches, and each branch is a list of impedence-havers\"\"\"\n \n \n def __init__(self, contents, debug=False):\n \"\"\"Define the contents of this block\"\"\"\n # If the block is passed a string, use init_from_string()\n if isinstance(contents, basestring):\n self.init_from_string(contents, debug=debug)\n\n #Otherwise, asssume that the conents variable is a list of lists (a list of branches)\n elif contents is not None:\n self.branches = contents\n else:\n raise \"Error\"\n \n \n def add_component_from_string(self, branch, text):\n \"\"\"Create a new component based on some text, and add it to a list\"\"\"\n\n # Do nothing for empty text\n if len(text) == 0:\n return\n \n # Otherwise, create LRC objects. \n # This just uses the text after L/R/C as the component parameter, but\n # it could be replaced with a lookup table, map, or some other scheme.\n if text[0] == \"R\":\n branch.append(R(float(text[1:])))\n elif text[0] == \"C\":\n branch.append(C(float(text[1:])))\n elif text[0] == \"L\":\n branch.append(L(float(text[1:])))\n else:\n branch.append(R(1))\n \n def init_from_string(self, s, debug=False):\n \"\"\"Recursively parse a string to create a block\n \n Note: This is not the fastest way of doing this, but it is fairly easy to undersand. \n With this recursive strategy, for a string such as {{{{{{{}}}}}}}, each character would\n be processed one additonal time for each level down it goes. \n \n It would be possible (and not too hard) to write a function which only needs one pass\n over the string. It would need to keep track of a *stack* of Block() objects. Each time\n you enter a curly-brace, you would make a new block and push it on to the stack. \n Each time you exit a curly brace, you would pop the current block from the stack, and\n then add it as a component to its parent.\n \n See: https://github.com/Spacerat/JoeBot2/blob/master/modules/dynamic_core.py#L213\n for an example of doing it without recursion. Instead of using a stack, each 'node'\n knows who its parent is:\n \n At line 225, when an opening tag \"<...>\" is seen, the current_node is given a new child\n and that child is set as the current_node.\n At line 221, When a closing tag \"</...>\" is seen, the current_node's parent is set as\n the current_node.\n \"\"\"\n \n self.branches = [] # Initialise the internal list of branches\n text = \"\" # All of the text seen so far for the current component\n bracket_count = 0 # The current 'level' of curly brackets\n current_branch = [] # The current branch being built\n \n if debug:\n print \"Block text > \",s\n \n # Iterate through each character of the string\n for i, char in enumerate(s):\n # Ignore whitespace (this must come first)\n if char == \" \" or char == \"\\t\":\n continue\n \n # At the lowest level, add components to this block's branches\n if bracket_count == 0:\n # When we see one of \",{-\", the previous component is finished\n # which means it needs to be added to the current branch *before\n # doing anything else*. \n # This is done with self.add_component_from_string()\n \n # Go down a level if we see {\n if char == \"{\":\n self.add_component_from_string(current_branch, text)\n text = \"\"\n bracket_count +=1\n \n # } at the bottom level means that the string is invalid\n elif char == \"}\":\n raise Exception(\"Error processing string \\\"{}\\\" at character {} - mismatched bracket\".format(s, i))\n \n # , means a new branch. Push the current branch to the list of branches.\n elif char == \",\":\n self.add_component_from_string(current_branch, text)\n text = \"\"\n self.branches.append(current_branch)\n current_branch = []\n \n # - means a new component in serial, so just push the current component\n # to the current branch.\n elif char == \"-\":\n self.add_component_from_string(current_branch, text)\n text = \"\"\n \n #Anything other text is part of the current component\n else:\n text+=char\n \n # Inside curly brackets, just wait for closing curly brackets ...\n elif bracket_count > 0:\n # (Counting brackets)\n if char == \"{\":\n bracket_count +=1\n text+=char\n elif char == \"}\":\n bracket_count -= 1\n # Until getting back to the top level.\n if bracket_count == 0:\n # Then *RECURSE*. Make a new block with the 'text' string,\n # which contains the entirety of the {curly {bracket} text}\n current_branch.append(Block(text, debug=debug))\n text = \"\"\n else:\n text+=char\n else:\n text+=char\n\n # At the end, make sure we're back to 0 brackets.\n if bracket_count != 0:\n raise Exception(\"Error processing string \\\"{}\\\" at character {} - mismatched bracket\".format(s, i))\n \n # Then add the last component seen to the current branch\n self.add_component_from_string(current_branch, text)\n # and add the current branch to the list of branches\n self.branches.append(current_branch)\n\n def get_impedence(self, frequency = 1.0):\n \"\"\"Get the impedence of this entire block\"\"\"\n # We could check if there is only a single branch and then do something different here,\n # But there isn't any point.\n \n # This iterates over every branch, getting the reciprocal of the sum of the impedence of each object in the branch \n def branch_imp():\n for b in self.branches:\n yield 1.0/sum((x.get_impedence(frequency) for x in b))\n \n # This sums the branches and takes the reciprocal\n return 1.0 / sum(branch_imp())\n \n def __str__(self):\n \"\"\"Get a string representation of the block,\n which can be be used by init_from_string() to create\n an identical block\"\"\"\n innerstr = \",\".join((\"-\".join((str(x) for x in b))) for b in self.branches)\n \n # Onlt use {...} if the block has multiple branches\n if len(innerstr) == 0:\n return \"{ }\"\n elif len(self.branches) == 1:\n return innerstr\n else:\n return \"{\"+innerstr+\"}\"\n \n def __repr__(self):\n \"\"\"Get a Python-code representation of the block,\n which can be executed to create an identical block\"\"\"\n innerstr = \",\".join(\"[\"+(\",\".join((repr(x) for x in b)))+\"]\" for b in self.branches)\n return \"Block([\"+innerstr+\"])\"\n\n\n \n# These are the component classes, defining resistors, capacitors and inductors.\n\n# They derive their __str__ function from Impedence_Haver\n\n# They convert their inputs to numpy classes float64 and complex_, so that division by 0\n# doesn't cause crashes.\n\nclass R(Impedence_Haver):\n \"\"\"Resistor\"\"\"\n def __init__(self, resistance):\n self.value = float64(resistance)\n \n def get_impedence(self, frequency):\n \"\"\"impedence of R + 0j\"\"\"\n return complex_(complex(self.value, 0))\n \nclass C(Impedence_Haver):\n \"\"\"Capacitor\"\"\"\n def __init__(self, capacitance):\n self.value = float64(capacitance)\n \n def get_impedence(self, frequency):\n \"\"\"impedence of 0 + -1/(f*C)j\"\"\"\n return complex_(complex(0, -1.0/(self.value * frequency)))\n\nclass L(Impedence_Haver):\n \"\"\"Inductor\"\"\"\n def __init__(self, inductance):\n self.value = float64(inductance)\n def get_impedence(self, frequency):\n \"\"\"impedence of 0 + (f*L)j\"\"\"\n return complex_(complex(0, frequency * self.value))\n ", | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 303 | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": "## Tests\n\n### " | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": "serial = Block([[R(1), R(2)]])\nprint serial\nprint serial.get_impedence()\n", | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": "R1.0-R2.0\n(3+0j)\n" | |
} | |
], | |
"prompt_number": 304 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": "", | |
"language": "python", | |
"metadata": {}, | |
"outputs": [], | |
"prompt_number": 304 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": "parallel = Block([[serial], [serial]])\n\nprint parallel\nprint parallel.get_impedence()", | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": "{R1.0-R2.0,R1.0-R2.0}\n(1.5+0j)\n" | |
} | |
], | |
"prompt_number": 305 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": "complicated = Block([[C(2), R(1)], [parallel], [serial]])\nprint complicated\nprint complicated.get_impedence(1)\nprint complicated.get_impedence(5)\nprint complicated.get_impedence(10)\nprint\nprint repr(complicated)", | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": "{C2.0-R1.0,{R1.0-R2.0,R1.0-R2.0},R1.0-R2.0}\n(0.529411764706-0.117647058824j)\n(0.501246882793-0.0249376558603j)\n(0.500312304809-0.0124921923798j)\n\nBlock([[C(2.0),R(1.0)],[Block([[Block([[R(1.0),R(2.0)]])],[Block([[R(1.0),R(2.0)]])]])],[Block([[R(1.0),R(2.0)]])]])\n" | |
} | |
], | |
"prompt_number": 306 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": "Block([[C(2.0),R(1.0)],[Block([[Block([[R(1.0),R(2.0)]])],[Block([[R(1.0),R(2.0)]])]])],[Block([[R(1.0),R(2.0)]])]])", | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"metadata": {}, | |
"output_type": "pyout", | |
"prompt_number": 307, | |
"text": "Block([[C(2.0),R(1.0)],[Block([[Block([[R(1.0),R(2.0)]])],[Block([[R(1.0),R(2.0)]])]])],[Block([[R(1.0),R(2.0)]])]])" | |
} | |
], | |
"prompt_number": 307 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": "# Test some examples of string inputs\ntest_strings = [\n \"R1 - R2\",\n \"{{{R1 - C2}}}\",\n \"R1 - {R2 - R2, C2 - C2} - R1\",\n \"{R1, L1 {R1, L1 { R1, L1 {R1}} R2} R2}\",\n \"- R5 - -\"\n]\n\nfor s in test_strings:\n block = Block(s, debug=True)\n print \"Generated string:\",block\n print \"Z =\", block.get_impedence()\n print\n", | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": "Block text > R1 - R2\nGenerated string: R1.0-R2.0\nZ = (3+0j)\n\nBlock text > {{{R1 - C2}}}\nBlock text > {{R1-C2}}\nBlock text > {R1-C2}\nBlock text > R1-C2\nGenerated string: R1.0-C2.0\nZ = (1-0.5j)\n\nBlock text > R1 - {R2 - R2, C2 - C2} - R1\nBlock text > R2-R2,C2-C2\nGenerated string: R1.0-{R2.0-R2.0,C2.0-C2.0}-R1.0\nZ = (2.23529411765-0.941176470588j)\n\nBlock text > {R1, L1 {R1, L1 { R1, L1 {R1}} R2} R2}\nBlock text > R1,L1{R1,L1{R1,L1{R1}}R2}R2\nBlock text > R1,L1{R1,L1{R1}}R2\nBlock text > R1,L1{R1}\nBlock text > R1\nGenerated string: {R1.0,L1.0-{R1.0,L1.0-{R1.0,L1.0-R1.0}-R2.0}-R2.0}\nZ = (0.75387420237+0.0711030082042j)\n\nBlock text > - R5 - -\nGenerated string: R5.0\nZ = (5+0j)\n\n" | |
} | |
], | |
"prompt_number": 315 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": "# Test that error checking works\ntry:\n Block(\"{R1}}\")\nexcept Exception as e:\n print e\n\ntry:\n Block(\"{R1{}\")\nexcept Exception as e:\n print e ", | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": "Error processing string \"{R1}}\" at character 4 - mismatched bracket\nError processing string \"{R1{}\" at character 4 - mismatched bracket\n" | |
} | |
], | |
"prompt_number": 309 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": "# Show the conversion between an original string, generated string output, and generated code output\noriginal_string = \"{R1,R2{{R3,C1-C2,{L1-L2,L3-L4}},R4{R5,R6-R7}}}\"\nblock = Block(original_string)\nblock_to_string = str(block)\nstring_to_block = Block(block_to_string)\n\nprint \"Original string:\"\nprint original_string\nprint\nprint \"str(Block(original_string)):\"\nprint block_to_string\nprint\nprint \"str(Block(str(Block(original_string)))):\"\nprint string_to_block.__str__()\nprint\nprint \"Generated Python-code representation\"\nprint repr(string_to_block)\nprint\nprint \"Testing the generated code\"\nprint eval(repr(string_to_block))", | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": "Original string:\n{R1,R2{{R3,C1-C2,{L1-L2,L3-L4}},R4{R5,R6-R7}}}\n\nstr(Block(original_string)):\n{R1.0,R2.0-{{R3.0,C1.0-C2.0,{L1.0-L2.0,L3.0-L4.0}},R4.0-{R5.0,R6.0-R7.0}}}\n\nstr(Block(str(Block(original_string)))):\n{R1.0,R2.0-{{R3.0,C1.0-C2.0,{L1.0-L2.0,L3.0-L4.0}},R4.0-{R5.0,R6.0-R7.0}}}\n\nGenerated Python-code representation\nBlock([[Block([[R(1.0)],[R(2.0),Block([[Block([[R(3.0)],[C(1.0),C(2.0)],[Block([[L(1.0),L(2.0)],[L(3.0),L(4.0)]])]])],[R(4.0),Block([[R(5.0)],[R(6.0),R(7.0)]])]])]])]])\n\nTesting the generated code\n{R1.0,R2.0-{{R3.0,C1.0-C2.0,{L1.0-L2.0,L3.0-L4.0}},R4.0-{R5.0,R6.0-R7.0}}}\n" | |
} | |
], | |
"prompt_number": 319 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": "# Demonstrate the effect of a large number of levels\nb = Block(\"{{{{{{{{{{{{{{{{}}}}}}}}}}}}}}}}\", debug=True)\ns = str(b)\nr = repr(b)\nprint \nprint s\nprint\nprint r", | |
"language": "python", | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"output_type": "stream", | |
"stream": "stdout", | |
"text": "Block text > {{{{{{{{{{{{{{{{}}}}}}}}}}}}}}}}\nBlock text > {{{{{{{{{{{{{{{}}}}}}}}}}}}}}}\nBlock text > {{{{{{{{{{{{{{}}}}}}}}}}}}}}\nBlock text > {{{{{{{{{{{{{}}}}}}}}}}}}}\nBlock text > {{{{{{{{{{{{}}}}}}}}}}}}\nBlock text > {{{{{{{{{{{}}}}}}}}}}}\nBlock text > {{{{{{{{{{}}}}}}}}}}\nBlock text > {{{{{{{{{}}}}}}}}}\nBlock text > {{{{{{{{}}}}}}}}\nBlock text > {{{{{{{}}}}}}}\nBlock text > {{{{{{}}}}}}\nBlock text > {{{{{}}}}}\nBlock text > {{{{}}}}\nBlock text > {{{}}}\nBlock text > {{}}\nBlock text > {}\nBlock text > \n\n{ }\n\nBlock([[Block([[Block([[Block([[Block([[Block([[Block([[Block([[Block([[Block([[Block([[Block([[Block([[Block([[Block([[Block([[Block([[]])]])]])]])]])]])]])]])]])]])]])]])]])]])]])]])]])\n" | |
} | |
], | |
"prompt_number": 327 | |
}, | |
{ | |
"cell_type": "code", | |
"collapsed": false, | |
"input": "", | |
"language": "python", | |
"metadata": {}, | |
"outputs": [] | |
} | |
], | |
"metadata": {} | |
} | |
] | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment