{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "**TODO**\n", "- create a better control stuc for internal parameters to, look as SKiDl's lib file that does the conversion from SKiDl to pyspice for inspiration" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "WARNING: KICAD_SYMBOL_DIR environment variable is missing, so the default KiCad symbol libraries won't be searched.\n" ] } ], "source": [ "#Library import statements\n", "\n", "from skidl.pyspice import *\n", "#can you say cheeky \n", "import PySpice as pspice\n", "#becouse it's written by a kiwi you know\n", "import lcapy as kiwi\n", "\n", "import numpy as np\n", "import pandas as pd\n", "import matplotlib.pyplot as plt\n", "import warnings\n", "\n", "from IPython.display import YouTubeVideo, display\n", "\n", "import traceback" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "data": { "application/json": { "Software versions": [ { "module": "Python", "version": "3.7.6 64bit [GCC 7.3.0]" }, { "module": "IPython", "version": "7.12.0" }, { "module": "OS", "version": "Linux 4.19.104 microsoft standard x86_64 with debian bullseye sid" }, { "module": "skidl", "version": "0.0.31.dev0" }, { "module": "PySpice", "version": "1.4.3" }, { "module": "lcapy", "version": "0.75.dev0" }, { "module": "sympy", "version": "1.6.2" }, { "module": "numpy", "version": "1.18.1" }, { "module": "matplotlib", "version": "3.3.0" }, { "module": "pandas", "version": "1.1.4" }, { "module": "scipy", "version": "1.4.1" } ] }, "text/html": [ "
SoftwareVersion
Python3.7.6 64bit [GCC 7.3.0]
IPython7.12.0
OSLinux 4.19.104 microsoft standard x86_64 with debian bullseye sid
skidl0.0.31.dev0
PySpice1.4.3
lcapy0.75.dev0
sympy1.6.2
numpy1.18.1
matplotlib3.3.0
pandas1.1.4
scipy1.4.1
Mon Jan 18 15:30:41 2021 MST
" ], "text/latex": [ "\\begin{tabular}{|l|l|}\\hline\n", "{\\bf Software} & {\\bf Version} \\\\ \\hline\\hline\n", "Python & 3.7.6 64bit [GCC 7.3.0] \\\\ \\hline\n", "IPython & 7.12.0 \\\\ \\hline\n", "OS & Linux 4.19.104 microsoft standard x86\\_64 with debian bullseye sid \\\\ \\hline\n", "skidl & 0.0.31.dev0 \\\\ \\hline\n", "PySpice & 1.4.3 \\\\ \\hline\n", "lcapy & 0.75.dev0 \\\\ \\hline\n", "sympy & 1.6.2 \\\\ \\hline\n", "numpy & 1.18.1 \\\\ \\hline\n", "matplotlib & 3.3.0 \\\\ \\hline\n", "pandas & 1.1.4 \\\\ \\hline\n", "scipy & 1.4.1 \\\\ \\hline\n", "\\hline \\multicolumn{2}{|l|}{Mon Jan 18 15:30:41 2021 MST} \\\\ \\hline\n", "\\end{tabular}\n" ], "text/plain": [ "Software versions\n", "Python 3.7.6 64bit [GCC 7.3.0]\n", "IPython 7.12.0\n", "OS Linux 4.19.104 microsoft standard x86_64 with debian bullseye sid\n", "skidl 0.0.31.dev0\n", "PySpice 1.4.3\n", "lcapy 0.75.dev0\n", "sympy 1.6.2\n", "numpy 1.18.1\n", "matplotlib 3.3.0\n", "pandas 1.1.4\n", "scipy 1.4.1\n", "Mon Jan 18 15:30:41 2021 MST" ] }, "execution_count": 2, "metadata": {}, "output_type": "execute_result" } ], "source": [ "#notebook specific loading control statements \n", "%matplotlib inline\n", "#tool to log notebook internals\n", "#https://github.com/jrjohansson/version_information\n", "%load_ext version_information\n", "%version_information skidl, PySpice,lcapy, sympy, numpy, matplotlib, pandas, scipy" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "#import the op read tool from last subchapter\n", "from DC_1_Codes import op_results_collect" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Working with SKiDl elements" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The following example, to be honest, is pedantic; but it serves to introduce the current source in SPICE which can be a real headache. It also shows how to let Python do the work of interacting with elements that we will readily make great use of down the road.\n", "\n", "So why is thus so pedantic? source transformations are mostly an analytic simplification tool. And while yes there is some practicality in being able to find a matching current source to a voltage source and vice versa with equivalent power from the standpoint of Thevenin and Norton's theorem. There are, however, serious limation with how the real circuits handle inputs of voltage and current differently. And frankly, from SPICE’s point of view for this exercise, it doesn't care, it's going to solve regardless. So if you need a refresher on source transformations with an illustration of why there a really great analytical tool please watch ALL ABOUT ELECTRONICS YT video on \"Source Transformations\" where this example is pulled from.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Example 1 from \"Source Transformation\" @ ~6:30 min " ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "image/jpeg": "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n6EXVf21eUs/Qi6r+2g2sqD6VqnKSfoRdV/bTlJP0Iuq/toNrQLVOUk/Qi6r+2nKSfoRdV/bQbYqtS5ST9CLqv7avKWfoRdV/bQbWoVqvKWfoQ9V/bUOUk/Qi6r+2g2z6EWp8pJ+hF1X9tOUk/Qi6r+2g2zmQrU+Uk/Qi6r+2nKWfoxdV/bQbYotU5ST9CLqv7acpJ+hF1X9tBtYQblqnKSfoRdV/bTlJP0Yuq/toNrb9Ki1TlHP0Yuq/tq8o5+jF1X9tBtZQLVOUc/Qi6r+2nKOfoRdV/bQbWFVqfKSfoxdV/bTlJP0Ieq/toNtCjlqfKSfoRdV/bV5ST9GLqv7aDannz8/KqtTOUk/Qi6r+2nKSfoxdV/bQbYqVqfKWfoRdV/bTlLP0Ieq/toNsRanyln6EPVf205Sz9CLqv7aDbAh+hanyln6EXVf20OUk/Qh6r+2g4RERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREBERAREQEREH/2Q==\n", "text/html": [ "\n", " \n", " " ], "text/plain": [ "" ] }, "execution_count": 4, "metadata": {}, "output_type": "execute_result" } ], "source": [ "YouTubeVideo('FtEU5ZoO-fg', width=500, height=400, start=390)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### A very important word about current source in SPICE" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Before building the circuit above a word about any current sources in SPICE. Recalling the discussion around the surefire method of measuring currents in SPICE using a 0V Liner Voltage Souce (aka the SPICE ammeter trick) in SPICE current flow is in the positive direction from a positive voltage to a negative voltage. So by convention, we draw independent and dependent sources with an arrow in the direction of how current is being added. While that means that all current sources are polarized such that the positive terminal is at the tail of the drawn current arrow and the head is the negative terminal. When you use a schematic editor with built-in SPICE it does all the terminal work in the background. But when we are indirectly working with netlist, via SKiDL, you will have to make sure you remember this. Or else this will bite you in the butt and keep costing you time till have this arrow and terminal connection for current ingrained into you." ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ".title \n", "Vs_4 0 N_1 4V\n", "Vs_8 N_2 N_3 8V\n", "Is_2 0 N_3 2A\n", "R1 N_1 N_2 6Ohm\n", "R2 0 N_2 12Ohm\n", "R3 0 N_3 12Ohm\n", "\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\n", "No errors or warnings found during netlist generation.\n", "\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Item_TypeValueUnit
Circ_Item
N_1Node_Voltage-4V
N_2Node_Voltage6V
N_3Node_Voltage-2V
Vs_4Branch_Curr1.66667A
Vs_8Branch_Curr2.16667A
\n", "
" ], "text/plain": [ " Item_Type Value Unit\n", "Circ_Item \n", "N_1 Node_Voltage -4 V\n", "N_2 Node_Voltage 6 V\n", "N_3 Node_Voltage -2 V\n", "Vs_4 Branch_Curr 1.66667 A\n", "Vs_8 Branch_Curr 2.16667 A" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "reset()\n", "vs_4=V(ref='s_4', dc_value=4@u_V)\n", "vs_8=V(ref='s_8', dc_value=8@u_V)\n", "cs_2=I(ref='s_2', dc_value=2@u_A)\n", "r1=R(ref='1', value=6@u_Ohm)\n", "r2=R(ref='2', value=12@u_Ohm)\n", "r3=R(ref='3', value=12@u_Ohm)\n", "\n", "(gnd&vs_4['p', 'n']&r1) |r2 \n", "\n", "vs_8['p', 'n']+=r2[2], r3[2]\n", "\n", "(gnd & cs_2 | r3)\n", "\n", "circ=generate_netlist()\n", "print(circ)\n", "\n", "preop_sim=op_results_collect(circ)\n", "#store the results for comperasion to post souce transfromations\n", "pre_tf_res=preop_sim.results_df\n", "pre_tf_res" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### SKiDl's Diabetic Syntax `&`, `|`" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Notice above that usage of Pythons logical AND operator & and logical OR operator | in creating the circuit. Since & and | are just operators in python we can do what is called operator extensions to them to make them act in special ways in a certain context. In SKiDls particular case the logical and (&) is a shorthand for putting two elements in series. And the logical OR (|) is shorthand for putting two elements in parral. Furthermore, these operators and parentheses sensitive, and are not polarized sensitive and so polarized elements need to have their terminals called out when using the. There called Diabetic Syntical Suger in light of their release announcement entitled [\"SWEETENING SKIDL\"](http://xess.com/skidl/docs/_site/blog/sweetening-skidl). Using is up to your codding style and that of your colleagues. All joking aside they are extremely useful operators to know how to use, and we will use them in this book." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Crafting the transfomation tool" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We are not going into that much detial about these tool. The important thing is that we can take advante that all our elements (SKiDl part) and nets are objects in Python. And therefore have methods and attriputs that are accesable and therefore more usable then helping produce part of a line of a SPICE netlist. For instiance Voltage and Current souce store there dcvalue in `.dc_value` where resitors store there resistince in `R.value`.This then alows us to use the elements to perform calculation outside of SPICE and even better assisit in creating new elements as we have done below." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "code_folding": [] }, "outputs": [], "source": [ "#%%writefile -a DC_1_Codes.py\n", "#chapter 1 section 2 get_skidl_spice_ref function\n", "#used for getting the name of the element as it would appear in a\n", "#generated netlist\n", "\n", "def get_skidl_spice_ref(skidle_element):\n", " \"\"\"\n", " Helper function to retrieve SKiDL element name as appears in the final netlist\n", " \n", " Args:\n", " skidle_element (skidl.part.Part): SKiDl part to get the netlist name from\n", " \n", " Returns:\n", " returns a string with the netlist name of `skidle_element`, or throws an\n", " error if `skidle_element` is not a SKiDl part\n", " \n", " \"\"\"\n", " assert repr(type(skidle_element))==\"\", '`skidle_element` must be a SKiDl part'\n", " \n", " if skidle_element.ref_prefix!=skidle_element.ref[0]:\n", " return skidle_element.ref_prefix+skidle_element.ref\n", " else:\n", " return skidle_element.ref" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "#%%writefile -a DC_1_Codes.py\n", "#chapter 1 section 2 dc_cs2vs function\n", "#creates a voltage source element to the current source based on the \n", "#value if the input DC current element and it's parallel resistor\n", "#via the source transformation rules\n", "\n", "def dc_cs2vs(dc_cs, cs_par_res):\n", " \"\"\"\n", " Create a new equivalent voltage source to the current source with parallel resistance\n", " Args:\n", " dc_cs (SKiDl current source): the current source to transform to a voltage source\n", " cs_par_res (SKiDl resistor): the parrel resistor to the current source to be transformed\n", " \n", " Returns:\n", " returns an equivalent DC voltage source element to the current source based on the \n", " value if the input DC current element and it's parallel resistor via the source transformation rules \n", " \n", " TODO:\n", " -figure out how to do assertion check that cs_par_res is in parallel to dc_cs\n", " \n", " Future:\n", " -make into subcircuit with net inputs to automatically add the new source and resistance to the circuit\n", " \"\"\"\n", " \n", " #do assertion checks to insure that passed in objects are what they are supposed to be\n", " assert dc_cs.ref_prefix=='I', ' was not a current source'\n", " assert cs_par_res.ref_prefix=='R', ' was not a resistor'\n", " \n", " \n", " old_maxpower=float((dc_cs.dc_value**2)*cs_par_res.value)\n", " new_vs_val=float(dc_cs.dc_value*cs_par_res.value)\n", " assert np.around(float(new_vs_val*dc_cs.dc_value), 6)==np.around(old_maxpower, 6), \"Um, something wrong since before and after max power not equal\"\n", " \n", " new_vs_ref=dc_cs.ref\n", " if new_vs_ref[0]!='I':\n", " new_vs_ref='I'+new_vs_ref\n", " \n", " \n", " new_vs_ref=f\"V{new_vs_ref[1:]}_f_{new_vs_ref}\"\n", " print(new_vs_ref)\n", " \n", " \n", " eq_vs=V(dc_value=new_vs_val@u_V, ref=new_vs_ref)\n", " warnings.warn(f\"\"\"New voltage source values: {new_vs_val} [V] with max aviabel power {old_maxpower} [W] \\n transformed creation statment will be like: \\n`(gnd & ['n', 'p'] & )`\"\"\")\n", " \n", " return eq_vs" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [], "source": [ "#%%writefile -a DC_1_Codes.py\n", "#chapter 1 section 2 dc_vs2cs function\n", "#creats current source element to the voltage source based on the \n", "#value if the input DC current element and it's series resistor\n", "#via the source transformation rules\n", "\n", "def dc_vs2cs(dc_vs, vs_ser_res):\n", " \"\"\"\n", " Create a new equivalent current source to voltage source with series resistance\n", " Args:\n", " dc_vs (SKiDl voltage source): the voltage source to transform to a current source\n", " vs_ser_res (SKiDl resistor): the serries resistor to the voltage source to be transformed\n", " \n", " Returns:\n", " \n", " TODO:\n", " -figure out how to do assertion check that vs_ser_res is in serries to dc_vs\n", " \n", " Future:\n", " -make into subcircuit with net inputs to automatically add the new source and resistance to the circuit\n", " \"\"\"\n", " \n", " #do assertion checks to insure that passed in objects are what they are supposed to be\n", " assert dc_vs.ref_prefix=='V', ' was not a voltage source'\n", " assert vs_ser_res.ref_prefix=='R', ' was not a resistor'\n", " \n", " old_maxpower=float((dc_vs.dc_value**2)/vs_ser_res.value)\n", " new_cs_val=float(dc_vs.dc_value/vs_ser_res.value)\n", " assert np.around(float(new_cs_val*dc_vs.dc_value), 6)==np.around(old_maxpower, 6), \"Um, something wrong since before and after max power not equal\"\n", "\n", "\n", " new_cs_ref=dc_vs.ref\n", " if new_cs_ref[0]!='V':\n", " new_cs_ref='V'+new_cs_ref\n", " \n", " \n", " new_cs_ref=f\"I{new_cs_ref[1:]}_f_{new_cs_ref}\"\n", " #print(new_cs_ref)\n", " \n", " \n", " eq_cs=I(dc_value=new_cs_val@u_A, ref=new_cs_ref)# might still need this: , circuit=vs_ser_res.circuit)\n", "\n", " warnings.warn(f\"\"\"New current source values: {new_cs_val} [A] with max aviabel power {old_maxpower} [W] \\n transformed creation statment will be like:\\n `(gnd & ['n', 'p'] | )` \\n\"\"\")\n", " \n", " return eq_cs\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### validate the transform¶\n", "\n", "For this, we are to transform the left voltage source and series resistors and the right current source and parral resistor simultaneously which halfway deviates from what ALL ABOUT ELECTRONICS did working the example analytically. We will have the center parallel resistor and voltage source as a reference network." ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Vs_2_f_Is_2\n", ".title \n", "R1 0 N_1 6Ohm\n", "R2 0 N_1 12Ohm\n", "R3 N_3 N_2 12Ohm\n", "Vs_8 N_1 N_2 8V\n", "Is_4_f_Vs_4 N_1 0 0.6666666666666666A\n", "Vs_2_f_Is_2 N_3 0 24.0V\n", "\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/home/iridium/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:43: UserWarning: New current source values: 0.6666666666666666 [A] with max aviabel power 2.6666666666666665 [W] \n", " transformed creation statment will be like:\n", " `(gnd & ['n', 'p'] | )` \n", "\n", "/home/iridium/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:44: UserWarning: New voltage source values: 24.0 [V] with max aviabel power 48.0 [W] \n", " transformed creation statment will be like: \n", "`(gnd & ['n', 'p'] & )`\n", "\n", "No errors or warnings found during netlist generation.\n", "\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Item_TypeValueUnit
Circ_Item
N_1Node_Voltage6V
N_3Node_Voltage24V
N_2Node_Voltage-2V
Vs_8Branch_Curr2.16667A
Vs_2_f_Is_2Branch_Curr2.16667A
\n", "
" ], "text/plain": [ " Item_Type Value Unit\n", "Circ_Item \n", "N_1 Node_Voltage 6 V\n", "N_3 Node_Voltage 24 V\n", "N_2 Node_Voltage -2 V\n", "Vs_8 Branch_Curr 2.16667 A\n", "Vs_2_f_Is_2 Branch_Curr 2.16667 A" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "reset()\n", "r1=R(ref='1', value=6@u_Ohm)\n", "r2=R(ref='2', value=12@u_Ohm)\n", "r3=R(ref='3', value=12@u_Ohm)\n", "\n", "vs_8=V(ref='s_8', dc_value=8@u_V)\n", "\n", "cs_4_f_cs_4=dc_vs2cs(vs_4, r1)\n", "\n", "vs_2_f_cs_2=dc_cs2vs(cs_2, r3)\n", "\n", "\n", "(gnd&cs_4_f_cs_4['n', 'p']|r1) |r2 \n", "\n", "vs_8['p', 'n']+=r2[2], r3[2]\n", "\n", "(gnd & vs_2_f_cs_2['n', 'p'] & r3)\n", "\n", "circ=generate_netlist()\n", "print(circ)\n", "\n", "\n", "postop_sim=op_results_collect(circ)\n", "#store the results for comperaion to pre souce transfromations\n", "post_tf_res=postop_sim.results_df\n", "post_tf_res" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Since we stored the results from the pre transformed circuit we can try to do an eyeball compersion between the two dataframes, however, since the net names are no longer the same we only can look at the branch current of vs_8 which remained constant" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Item_TypeValueUnit
Circ_Item
N_1Node_Voltage-4V
N_2Node_Voltage6V
N_3Node_Voltage-2V
Vs_4Branch_Curr1.66667A
Vs_8Branch_Curr2.16667A
\n", "
" ], "text/plain": [ " Item_Type Value Unit\n", "Circ_Item \n", "N_1 Node_Voltage -4 V\n", "N_2 Node_Voltage 6 V\n", "N_3 Node_Voltage -2 V\n", "Vs_4 Branch_Curr 1.66667 A\n", "Vs_8 Branch_Curr 2.16667 A" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pre_tf_res" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "True" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "(pre_tf_res.loc[get_skidl_spice_ref(vs_8)]==post_tf_res.loc[get_skidl_spice_ref(vs_8)]).all()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Thus we can assume that the circuits are source equivalents of each other, but this book is about cultivating analog design verification. And assuming can yield performance hits and even worse the need for a SPIN. Therefore DONT ASSUME, figure out a way to VERIFY via Quantifiable answers." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## internal parameters:\n", "\n", "ngspice (in fact most SPICE engines) elements have what are called internal parameters. Most of the setup parameters like dc_value, resistance, ect along with nitty-gritty parameters for more advanced SPICE simulations that will get too. What we are after of now are the internal values that store results of simulations as we have alluded to in the non surefire way to save internal parameters. For instance, resistors have a way of measuring the internal current flowing through them the but the caveat is that it only returns real values in ngspice, which will be an issue when doing AC simulations. But for DC simulations is a tool we should utilize. Also at the time of writing this PySPICE has a quark that internal values are not retrieved at the same time the branch currents and net voltages are. So to get both the simulation has to be run twice and the author is not sure if this is quark of ngspice or PySPICE but the author will look into it at a later time.\n", "\n", "For now, just know internal parameters have a string calls of `@[]` that is passed to a PySPICE simulation objects `.save_internal_parameters` method and then are returned in the results as the original string call to the results super dictionary.\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [], "source": [ "#%%writefile -a DC_1_Codes.py\n", "#chapter 1 section 2 op_internal_ivp class\n", "# class to get both the branch currents and node voltages,\n", "# along with the internal parameters values for \n", "# DC operating point analysis\n", "\n", "class op_internal_ivp():\n", " \"\"\"\n", " Class for creating a SPICE simulation on the whole circuits internal parameters for current, voltage, and power\n", " for dc operating point (.op) simulations. Will only collect internal parameters and not global voltage and currents \n", " of the circuit\n", " TODO:\n", " Make this inheritable from op_results_collect \n", " \"\"\"\n", " def __init__(self, op_sim_circ, display_results=False):\n", " \"\"\"\n", " Basic class to get pyspice operating_point (ngspice `.op`) simulation results\n", " for internal parameters for Resistors, Current Source, Voltage Source current, voltage, power\n", " respectively\n", " \n", " \n", " Args:\n", " op_sim_circ (pspice.Spice.Netlist.Circuit): the Netlist circuit produced \n", " from SKiDl's `generate_netlist()`\n", " \n", " display_results (bool; False): option to have the simulation results\n", " stored in `self.results_df` automatically displayed from a jupyter notebook\n", " ell\n", " \n", " Returns:\n", " will create a simulation in `self.op_sim`, raw results of dc operating \n", " point simulation will be stored in `self.op_sim_results`, the tablized\n", " results will be stored in pandas dataframe `self.results_df`\n", " \n", " TODO: \n", " - add kwargs to the simulator\n", " - add an assertion that only a pyspice netlist circuit obj can\n", " be passed into op_sim_circ\n", " \n", " \"\"\"\n", " #need to add assertions for op_sim_circ ==pspice.Spice.Netlist.Circuit\n", " \n", " #store the circuit internally\n", " self.op_sim_circ=op_sim_circ\n", " #create the sim obj\n", " self.op_sim=self.op_sim_circ.simulator()\n", " \n", " #store bool to display results dataframe\n", " self.display_results=display_results\n", " \n", " #create the internal parameters to save\n", " self.create_savable_items()\n", " #run the sim for .op for internal parameters and record results\n", " self.sim_and_record()\n", " \n", " def create_savable_items(self):\n", " \"\"\"\n", " Helper method to create a listing of internal parameters and the table of the results.\n", " Right now only creates savable internal parameters for:\n", " Linear Dependent Voltage Sources: current, power\n", " Linear Dependent Current Sources: current, voltage, power\n", " Standard Resistor: current, voltage, power\n", " Linear Dependent Current Sources: current, voltage, power\n", " VCCS: current, voltage, power\n", " VCVS: current, voltage, power\n", " CCVS: current, voltage, power\n", " CCCS:currrent, voltage, power\n", " \n", " See ngspice manual typically chapter 31 \"Model and Device Parameters\"\n", " for more deitals about deice intiernal parmaters\n", "\n", " \"\"\"\n", " self.results_df=pd.DataFrame(columns=['Circ_Item', 'Item_Type', 'Value', 'Unit'])\n", " self.results_df.set_index('Circ_Item', drop=True, append=False, inplace=True, verify_integrity=False)\n", " \n", " #helper row creation statement\n", " def add_row(index, unit): \n", " self.results_df.at[index, ['Item_Type', 'Unit']]=['internal', unit]\n", " \n", " \n", " for e in self.op_sim_circ.element_names:\n", " \"\"\"\n", " Ref: ngspice documentation chapter 31 (typically): Model and Device Parameters\n", " \n", " \"\"\"\n", " #resistors\n", " if e[0]=='R':\n", " add_row(f'@{e}[i]', 'A')\n", " add_row(f'@{e}[p]', 'W')\n", " \n", " #independnt current source\n", " elif e[0]=='I':\n", " add_row(f'@{e}[c]', 'A')\n", " add_row(f'@{e}[v]', 'V')\n", " add_row(f'@{e}[p]', 'W')\n", " \n", " #independ Voltage source\n", " elif e[0]=='V':\n", " add_row(f'@{e}[i]', 'A')\n", " add_row(f'@{e}[p]', 'W')\n", " \n", " #controlled sources\n", " elif e[0] in ['F', 'H', 'G', 'E']:\n", " add_row(f'@{e}[i]', 'A')\n", " add_row(f'@{e}[v]', 'V')\n", " add_row(f'@{e}[p]', 'W')\n", " \n", " else:\n", " warnings.warn(f\"Circ Element {e} is not setup to have internals read, skiping\")\n", " \n", " def sim_and_record(self):\n", " \"\"\"\n", " run the .op simulation and get the internal values \n", " \n", " Args: None\n", " Returns:\n", " `self.internal_opsim_res` store the raw results of the .op for internal pamtyers\n", " whereas `self.results_df` stores the pandas dataframe of internal parameters results\n", " \n", " TODO: figure out how to do this at the same time as the main node branch sim\n", " this doing separately is getting old\n", " \"\"\"\n", " save_items=list(self.results_df.index)\n", " self.op_sim.save_internal_parameters(*save_items)\n", " self.internal_opsim_res=self.op_sim.operating_point()\n", " \n", " for save in save_items:\n", " self.results_df.at[save, 'Value']=self.internal_opsim_res[save].as_ndarray()[0]\n", " \n", " if self.display_results:\n", " print('.op sim internal parmter results')\n", " display(self.results_df)\n", "\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### pre transform_internals" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ ".title \n", "Vs_4 0 N_1 4V\n", "Vs_8 N_2 N_3 8V\n", "Is_2 0 N_3 2A\n", "R1 N_1 N_2 6Ohm\n", "R2 0 N_2 12Ohm\n", "R3 0 N_3 12Ohm\n", "\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "\n", "No errors or warnings found during netlist generation.\n", "\n" ] } ], "source": [ "reset()\n", "vs_4=V(ref='s_4', dc_value=4@u_V)\n", "vs_8=V(ref='s_8', dc_value=8@u_V)\n", "cs_2=I(ref='s_2', dc_value=2@u_A)\n", "r1=R(ref='1', value=6@u_Ohm)\n", "r2=R(ref='2', value=12@u_Ohm)\n", "r3=R(ref='3', value=12@u_Ohm)\n", "\n", "(gnd&vs_4['p', 'n']&r1) |r2 \n", "\n", "vs_8['p', 'n']+=r2[2], r3[2]\n", "\n", "(gnd & cs_2 | r3)\n", "\n", "circ=generate_netlist()\n", "print(circ)\n", "\n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Unit is None for @r1[p] power\n", "Unit is None for @r2[p] power\n", "Unit is None for @is_2[p] power\n", "Unit is None for @vs_4[p] power\n", "Unit is None for @vs_8[p] power\n", "Unit is None for @r3[p] power\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Item_TypeValueUnit
Circ_Item
@Vs_4[i]internal-1.66667A
@Vs_4[p]internal6.66667W
@Vs_8[i]internal-2.16667A
@Vs_8[p]internal17.3333W
@Is_2[c]internal2A
@Is_2[v]internal-2V
@Is_2[p]internal4W
@R1[i]internal-1.66667A
@R1[p]internal16.6667W
@R2[i]internal-0.5A
@R2[p]internal3W
@R3[i]internal0.166667A
@R3[p]internal0.333333W
\n", "
" ], "text/plain": [ " Item_Type Value Unit\n", "Circ_Item \n", "@Vs_4[i] internal -1.66667 A\n", "@Vs_4[p] internal 6.66667 W\n", "@Vs_8[i] internal -2.16667 A\n", "@Vs_8[p] internal 17.3333 W\n", "@Is_2[c] internal 2 A\n", "@Is_2[v] internal -2 V\n", "@Is_2[p] internal 4 W\n", "@R1[i] internal -1.66667 A\n", "@R1[p] internal 16.6667 W\n", "@R2[i] internal -0.5 A\n", "@R2[p] internal 3 W\n", "@R3[i] internal 0.166667 A\n", "@R3[p] internal 0.333333 W" ] }, "execution_count": 14, "metadata": {}, "output_type": "execute_result" } ], "source": [ "preop_ivp_sim=op_internal_ivp(circ)\n", "pre_ivp_res=preop_ivp_sim.results_df\n", "pre_ivp_res" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### post transform internals" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Vs_2_f_Is_2\n", ".title \n", "R1 0 N_1 6Ohm\n", "R2 0 N_1 12Ohm\n", "R3 N_3 N_2 12Ohm\n", "Vs_8 N_1 N_2 8V\n", "Is_4_f_Vs_4 N_1 0 0.6666666666666666A\n", "Vs_2_f_Is_2 N_3 0 24.0V\n", "\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/home/iridium/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:43: UserWarning: New current source values: 0.6666666666666666 [A] with max aviabel power 2.6666666666666665 [W] \n", " transformed creation statment will be like:\n", " `(gnd & ['n', 'p'] | )` \n", "\n", "/home/iridium/anaconda3/lib/python3.7/site-packages/ipykernel_launcher.py:44: UserWarning: New voltage source values: 24.0 [V] with max aviabel power 48.0 [W] \n", " transformed creation statment will be like: \n", "`(gnd & ['n', 'p'] & )`\n", "\n", "No errors or warnings found during netlist generation.\n", "\n" ] } ], "source": [ "reset()\n", "r1=R(ref='1', value=6@u_Ohm)\n", "r2=R(ref='2', value=12@u_Ohm)\n", "r3=R(ref='3', value=12@u_Ohm)\n", "\n", "vs_8=V(ref='s_8', dc_value=8@u_V)\n", "\n", "cs_f_vs_4=dc_vs2cs(vs_4, r1)\n", "\n", "vs_f_cs_2=dc_cs2vs(cs_2, r3)\n", "\n", "\n", "(gnd&cs_f_vs_4['n', 'p']|r1) |r2 \n", "\n", "vs_8['p', 'n']+=r2[2], r3[2]\n", "\n", "(gnd & vs_f_cs_2['n', 'p'] & r3)\n", "\n", "circ=generate_netlist()\n", "print(circ)" ] }, { "cell_type": "code", "execution_count": 16, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "Unit is None for @r1[p] power\n", "Unit is None for @r2[p] power\n", "Unit is None for @is_4_f_vs_4[p] power\n", "Unit is None for @vs_2_f_is_2[p] power\n", "Unit is None for @vs_8[p] power\n", "Unit is None for @r3[p] power\n" ] }, { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Item_TypeValueUnit
Circ_Item
@R1[i]internal-1A
@R1[p]internal6W
@R2[i]internal-0.5A
@R2[p]internal3W
@R3[i]internal2.16667A
@R3[p]internal56.3333W
@Vs_8[i]internal-2.16667A
@Vs_8[p]internal17.3333W
@Is_4_f_Vs_4[c]internal0.666667A
@Is_4_f_Vs_4[v]internal-6V
@Is_4_f_Vs_4[p]internal4W
@Vs_2_f_Is_2[i]internal-2.16667A
@Vs_2_f_Is_2[p]internal52W
\n", "
" ], "text/plain": [ " Item_Type Value Unit\n", "Circ_Item \n", "@R1[i] internal -1 A\n", "@R1[p] internal 6 W\n", "@R2[i] internal -0.5 A\n", "@R2[p] internal 3 W\n", "@R3[i] internal 2.16667 A\n", "@R3[p] internal 56.3333 W\n", "@Vs_8[i] internal -2.16667 A\n", "@Vs_8[p] internal 17.3333 W\n", "@Is_4_f_Vs_4[c] internal 0.666667 A\n", "@Is_4_f_Vs_4[v] internal -6 V\n", "@Is_4_f_Vs_4[p] internal 4 W\n", "@Vs_2_f_Is_2[i] internal -2.16667 A\n", "@Vs_2_f_Is_2[p] internal 52 W" ] }, "execution_count": 16, "metadata": {}, "output_type": "execute_result" } ], "source": [ "postop_ivp_sim=op_internal_ivp(circ)\n", "post_ivp_res=postop_ivp_sim.results_df\n", "post_ivp_res" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Quantitive comparison ¶\n", "Since our results are stored in Pandas dataframes we can make use of the power of Pandas to do data analysis to get insight into what is going on. Where below we get a merger of the two dataframes side by side for all the elements that remained the same in the circuit pre and post-transformation. And we then follow that up with color-coding of what values remained the same between the pre and post-transformation of the circuit\n" ] }, { "cell_type": "code", "execution_count": 17, "metadata": {}, "outputs": [ { "data": { "text/html": [ "
\n", "\n", "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
PrePost
Item_TypeValueUnitItem_TypeValueUnit
Circ_Item
@Vs_8[i]internal-2.16667Ainternal-2.16667A
@Vs_8[p]internal17.3333Winternal17.3333W
@R1[i]internal-1.66667Ainternal-1A
@R1[p]internal16.6667Winternal6W
@R2[i]internal-0.5Ainternal-0.5A
@R2[p]internal3Winternal3W
@R3[i]internal0.166667Ainternal2.16667A
@R3[p]internal0.333333Winternal56.3333W
\n", "
" ], "text/plain": [ " Pre Post \n", " Item_Type Value Unit Item_Type Value Unit\n", "Circ_Item \n", "@Vs_8[i] internal -2.16667 A internal -2.16667 A\n", "@Vs_8[p] internal 17.3333 W internal 17.3333 W\n", "@R1[i] internal -1.66667 A internal -1 A\n", "@R1[p] internal 16.6667 W internal 6 W\n", "@R2[i] internal -0.5 A internal -0.5 A\n", "@R2[p] internal 3 W internal 3 W\n", "@R3[i] internal 0.166667 A internal 2.16667 A\n", "@R3[p] internal 0.333333 W internal 56.3333 W" ] }, "execution_count": 17, "metadata": {}, "output_type": "execute_result" } ], "source": [ "pre_post_comp=pd.concat([pre_ivp_res, post_ivp_res], join='inner', axis='columns', keys=['Pre', 'Post'])\n", "pre_post_comp" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "text/html": [ "\n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", " \n", "
Pre Post
Item_Type Value Unit Item_Type Value Unit
Circ_Item
@Vs_8[i]internal-2.166667Ainternal-2.166667A
@Vs_8[p]internal17.333333Winternal17.333333W
@R1[i]internal-1.666667Ainternal-1.000000A
@R1[p]internal16.666667Winternal6.000000W
@R2[i]internal-0.500000Ainternal-0.500000A
@R2[p]internal3.000000Winternal3.000000W
@R3[i]internal0.166667Ainternal2.166667A
@R3[p]internal0.333333Winternal56.333333W
" ], "text/plain": [ "" ] }, "execution_count": 18, "metadata": {}, "output_type": "execute_result" } ], "source": [ "def color_results(row): \n", " is_equal=(row['Pre']==row['Post']).all() \n", " if is_equal:\n", " return ['background-color: lightgreen']*len(row)\n", " else:\n", " return ['background-color: yellow']*len(row)\n", "\n", "pre_post_comp.style.apply(color_results, axis=1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The results show that the part of the network that remained the same had identical currents and power through their elements. While the resistors that we transformed in accordance with the source transformations. This is due to source efficiency since the full network presents different equivalent circuits to each of the transforms and therefore different voltage, current, and power draws on each source." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Citations:\n", "[1] ALL ABOUT ELECTRONICS. \"Source transformation in network analysis,\" YouTube, Dec 24, 2016. [Video file]. Available: https://youtu.be/FtEU5ZoO-fg. [Accessed: Nov 30, 2020].\n", "\n" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.7.6" }, "latex_envs": { "LaTeX_envs_menu_present": true, "autoclose": false, "autocomplete": true, "bibliofile": "biblio.bib", "cite_by": "apalike", "current_citInitial": 1, "eqLabelWithNumbers": true, "eqNumInitial": 1, "hotkeys": { "equation": "Ctrl-E", "itemize": "Ctrl-I" }, "labels_anchors": false, "latex_user_defs": false, "report_style_numbering": false, "user_envs_cfg": false }, "toc": { "base_numbering": 1, "nav_menu": {}, "number_sections": true, "sideBar": true, "skip_h1_title": false, "title_cell": "Table of Contents", "title_sidebar": "Contents", "toc_cell": false, "toc_position": {}, "toc_section_display": true, "toc_window_display": true } }, "nbformat": 4, "nbformat_minor": 4 }