circuit.cpp

00001 /***************************************************************************
00002  *   Copyright (C) 2003-2005 by David Saxton                               *
00003  *   david@bluehaze.org                                                    *
00004  *                                                                         *
00005  *   This program is free software; you can redistribute it and/or modify  *
00006  *   it under the terms of the GNU General Public License as published by  *
00007  *   the Free Software Foundation; either version 2 of the License, or     *
00008  *   (at your option) any later version.                                   *
00009  ***************************************************************************/
00010 
00011 #include <vector>
00012 #include "circuit.h"
00013 #include "circuitdocument.h"
00014 #include "element.h"
00015 #include "elementset.h"
00016 #include "logic.h"
00017 #include "nonlinear.h"
00018 #include "pin.h"
00019 #include "reactive.h"
00020 #include "wire.h"
00021 
00022 #include <kdebug.h>
00023 #include <cassert>
00024 #include <cmath>
00025 #include <map>
00026 
00027 typedef std::multimap<int, PinList> PinListMap;
00028 
00029 //BEGIN class Circuit
00030 Circuit::Circuit()
00031 {
00032         m_bCanAddChanged = true;
00033         m_pNextChanged[0] = m_pNextChanged[1] = 0;
00034         m_logicOutCount = 0;
00035         m_bCanCache = false;
00036         m_pLogicOut = 0;
00037         m_elementSet = new ElementSet( this, 0, 0 );
00038         m_cnodeCount = m_branchCount = -1;
00039         m_prepNLCount = 0;
00040         m_pLogicCacheBase = new LogicCacheNode;
00041 }
00042 
00043 Circuit::~Circuit()
00044 {
00045         delete m_elementSet;
00046         delete m_pLogicCacheBase;
00047         delete[] m_pLogicOut;
00048 }
00049 
00050 
00051 void Circuit::addPin( Pin *node )
00052 {
00053         if ( m_pinList.contains(node) ) return;
00054         m_pinList.append(node);
00055 }
00056 
00057 void Circuit::addElement( Element *element )
00058 {
00059         if ( m_elementList.contains(element) ) return;
00060         m_elementList.append(element);
00061 }
00062 
00063 bool Circuit::contains( Pin *node )
00064 {
00065         return m_pinList.contains(node);
00066 }
00067 
00068 // static function
00069 int Circuit::identifyGround( PinList nodeList, int *highest )
00070 {
00071         // What this function does:
00072         // We are given a list of pins. First, we divide them into groups of pins
00073         // that are directly connected to each other (e.g. through wires or
00074         // switches). Then, each group of connected pins is looked at to find the
00075         // pin with the highest "ground priority", and this is taken to be
00076         // the priority of the group. The highest ground priority from all the
00077         // groups is recorded. If the highest ground priority found is the maximum,
00078         // then all the pins in groups with this priority are marked as ground
00079         // (their eq-id is set to -1). Otherwise, the first group of pins with the
00080         // highest ground priority found is marked as ground, and all others are
00081         // marked as non ground (their eq-id is set to 0).
00082         
00083         int temp_highest;
00084         if (!highest) highest = &temp_highest;
00085 
00086         // Now to give all the Pins ids
00087         PinListMap eqs;
00088         while ( !nodeList.isEmpty() )
00089         {
00090                 PinList associated;
00091                 PinList nodes;
00092                 Pin *node = *nodeList.begin();
00093                 recursivePinAdd( node, &nodeList, &associated, &nodes );
00094                 if ( nodes.size() > 0 )
00095                 {
00096                         eqs.insert( std::make_pair( associated.size(), nodes ) );
00097                 }
00098         }
00099 
00100         // Now, we want to look through the associated Pins, 
00101         // to find the ones with the highest "Ground Priority". Anything with a lower
00102         // priority than Pin::gt_never will not be considered
00103         *highest = Pin::gt_never; // The highest priority found so far
00104         int numGround = 0; // The number of node groups found with that priority
00105         const PinListMap::iterator eqsEnd = eqs.end();
00106         for ( PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it )
00107         {
00108                 int highPri = Pin::gt_never; // The highest priority found in these group of nodes
00109                 const PinList::iterator send = it->second.end();
00110                 for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit )
00111                 {
00112                         if ( (*sit)->groundType() < highPri )
00113                                 highPri = (*sit)->groundType();
00114                 }
00115 
00116                 if(highPri == *highest ) numGround++;
00117                 else if ( highPri < *highest ) {
00118                         numGround = 1;
00119                         *highest = highPri;
00120                 }
00121         }
00122 
00123         if ( *highest == Pin::gt_never ) {
00124                 (*highest)--;
00125                 numGround=0;
00126         }
00127         // If there are no Always Ground nodes, then we only want to set one of the nodes as ground
00128         else if ( *highest > Pin::gt_always ) numGround = 1;
00129 
00130         // Now, we can give the nodes their cnode ids, or tell them they are ground
00131         bool foundGround = false; // This is only used when we don't have a Always ground node
00132         for ( PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it )
00133         {
00134                 bool ground = false;
00135                 const PinList::iterator send = it->second.end();
00136                 for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit )
00137                 {
00138                         ground |= (*sit)->groundType() <= (*highest);
00139                 }
00140 
00141                 if ( ground && (!foundGround || *highest == Pin::gt_always ) )
00142                 {
00143                         for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit )
00144                         {
00145                                 (*sit)->setEqId(-1);
00146                         }
00147                         foundGround = true;
00148                 } else {
00149                         for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit )
00150                         {
00151                                 (*sit)->setEqId(0);
00152                         }
00153                 }
00154         }
00155 
00156         return numGround;
00157 }
00158 
00159 void Circuit::init()
00160 {
00161         m_branchCount = 0;
00162 
00163         const ElementList::iterator listEnd = m_elementList.end();
00164         for(ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it) {
00165                 m_branchCount += (*it)->numCBranches();
00166         }
00167 
00168         // Now to give all the Pins ids
00169         int groundCount = 0;
00170         PinListMap eqs;
00171         PinList unassignedNodes = m_pinList;
00172         while(!unassignedNodes.isEmpty()) {
00173                 PinList associated;
00174                 PinList nodes;
00175                 Pin *node = *unassignedNodes.begin();
00176                 if(recursivePinAdd(node, &unassignedNodes, &associated, &nodes)) {
00177                         groundCount++;
00178                 }
00179 
00180                 if(nodes.size() > 0) {
00181                         eqs.insert( std::make_pair( associated.size(), nodes ) );
00182                 }
00183         }
00184 
00185         m_cnodeCount = eqs.size() - groundCount;
00186 
00187         delete m_pLogicCacheBase;
00188         m_pLogicCacheBase = 0;
00189 
00190         delete m_elementSet;
00191         m_elementSet = new ElementSet(this, m_cnodeCount, m_branchCount);
00192 
00193         // Now, we can give the nodes their cnode ids, or tell them they are ground
00194         QuickVector *x = m_elementSet->x();
00195         int i=0;
00196         const PinListMap::iterator eqsEnd = eqs.end();
00197         for(PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it) {
00198                 bool foundGround = false;
00199 
00200                 const PinList::iterator sEnd = it->second.end();
00201                 for ( PinList::iterator sit = it->second.begin(); sit != sEnd; ++sit )
00202                         foundGround |= (*sit)->eqId() == -1;
00203 
00204                 if(foundGround) continue;
00205 
00206                 bool foundEnergyStoragePin = false;
00207 
00208                 for(PinList::iterator sit = it->second.begin(); sit != sEnd; ++sit) {
00209                         (*sit)->setEqId(i);
00210                         bool energyStorage = false;
00211                         const ElementList elements = (*sit)->elements();
00212                         ElementList::const_iterator elementsEnd = elements.end();
00213                         for ( ElementList::const_iterator it = elements.begin(); it != elementsEnd; ++it ) {
00214                                 if(!*it) continue;
00215 
00216                                 if(((*it)->type() == Element::Element_Capacitance) ||
00217                                         ((*it)->type() == Element::Element_Inductance)) {
00218                                         energyStorage = true;
00219                                         break;
00220                                 }
00221                         }
00222 
00223                         // A pin attached to an energy storage pin overrides one that doesn't.
00224                         // If the two pins have equal status with in this regard, we pick the
00225                         // one with the highest absolute voltage on it.
00226 
00227                         if(foundEnergyStoragePin && !energyStorage) continue;
00228 
00229                         double v = (*sit)->voltage();
00230 
00231                         if(energyStorage && !foundEnergyStoragePin) {
00232                                 foundEnergyStoragePin = true;
00233                                 (*x)[i] = v;
00234                                 continue;
00235                         }
00236                         
00237                         if(std::abs(v) > std::abs((*x)[i])) (*x)[i] = v;
00238                 }
00239                 i++;
00240         }
00241 
00242         // And add the elements to the elementSet
00243         for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it )
00244         {
00245                 // We don't want the element to prematurely try to do anything,
00246                 // as it doesn't know its actual cnode ids yet
00247                 (*it)->setCNodes();
00248                 (*it)->setCBranches();
00249                 m_elementSet->addElement(*it);
00250         }
00251 
00252         // And give the branch ids to the elements
00253         i=0;
00254         for(ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it) {
00255                 switch((*it)->numCBranches()) {
00256                         case 0:
00257                                 break;
00258                         case 1:
00259                                 (*it)->setCBranches(i);
00260                                 i += 1;
00261                                 break;
00262                         case 2:
00263                                 (*it)->setCBranches(i, i+1);
00264                                 i += 2;
00265                                 break;
00266                         case 3:
00267                                 (*it)->setCBranches(i, i+1, i+2);
00268                                 i += 3;
00269                                 break;
00270                         default:
00271                                 // What the?!
00272                                 break;
00273                 }
00274         }
00275 }
00276 
00277 void Circuit::initCache()
00278 {
00279         m_elementSet->updateInfo();
00280 
00281         m_bCanCache = true;
00282         m_logicOutCount = 0;
00283 
00284         delete[] m_pLogicOut;
00285         m_pLogicOut = 0;
00286 
00287         delete m_pLogicCacheBase;
00288         m_pLogicCacheBase = 0;
00289 
00290         const ElementList::iterator end = m_elementList.end();
00291         for(ElementList::iterator it = m_elementList.begin(); it != end && m_bCanCache; ++it) {
00292                 switch((*it)->type()) {
00293                         case Element::Element_BJT:
00294                         case Element::Element_CCCS:
00295                         case Element::Element_CCVS:
00296                         case Element::Element_CurrentSource:
00297                         case Element::Element_Diode:
00298                         case Element::Element_LogicIn:
00299                         case Element::Element_OpAmp:
00300                         case Element::Element_Resistance:
00301                         case Element::Element_VCCS:
00302                         case Element::Element_VCVS:
00303                         case Element::Element_VoltagePoint:
00304                         case Element::Element_VoltageSource:
00305                                 break;
00306                         case Element::Element_LogicOut:
00307                                 m_logicOutCount++;
00308                                 break;
00309                         case Element::Element_CurrentSignal:
00310                         case Element::Element_VoltageSignal:
00311                         case Element::Element_Capacitance:
00312                         case Element::Element_Inductance:
00313                                 m_bCanCache = false;
00314                                 break;
00315                 }
00316         }
00317 
00318         if(!m_bCanCache) return;
00319 
00320         m_pLogicOut = new LogicOut*[m_logicOutCount];
00321         unsigned i = 0;
00322         for(ElementList::iterator it = m_elementList.begin(); it != end && m_bCanCache; ++it) {
00323                 if((*it)->type() == Element::Element_LogicOut)
00324                         m_pLogicOut[i++] = static_cast<LogicOut*>(*it);
00325         }
00326 
00327         m_pLogicCacheBase = new LogicCacheNode;
00328 }
00329 
00330 void Circuit::setCacheInvalidated()
00331 {
00332         if (m_pLogicCacheBase) {
00333                 delete m_pLogicCacheBase->high;
00334                 m_pLogicCacheBase->high = 0;
00335 
00336                 delete m_pLogicCacheBase->low;
00337                 m_pLogicCacheBase->low = 0;
00338 
00339                 delete m_pLogicCacheBase->data;
00340                 m_pLogicCacheBase->data = 0;
00341         }
00342 }
00343 
00344 void Circuit::cacheAndUpdate()
00345 {
00346         LogicCacheNode *node = m_pLogicCacheBase;
00347         for(unsigned i = 0; i < m_logicOutCount; i++) {
00348                 if(m_pLogicOut[i]->outputState()) {
00349                         if (!node->high)
00350                                 node->high = new LogicCacheNode;
00351                         node = node->high;
00352                 } else {
00353                         if (!node->low)
00354                                 node->low = new LogicCacheNode;
00355                         node = node->low;
00356                 }
00357         }
00358 
00359         if(node->data ) {
00360                 m_elementSet->set_x(new QuickVector(node->data));
00361                 m_elementSet->updateInfo();
00362                 return;
00363         }
00364 
00365         if(m_elementSet->containsNonLinear())
00366                 m_elementSet->doNonLinear(150, 1e-10, 1e-13);
00367         else    m_elementSet->doLinear(true);
00368 
00369         node->data = new QuickVector(m_elementSet->x());
00370 }
00371 
00372 void Circuit::createMatrixMap()
00373 {
00374         m_elementSet->createMatrixMap();
00375 }
00376 
00377 bool Circuit::recursivePinAdd( Pin *node, PinList *unassignedNodes, PinList *associated, PinList *nodes )
00378 {
00379         if(!unassignedNodes->contains(node)) return false;
00380         unassignedNodes->remove(node);
00381 
00382         bool foundGround = node->eqId() == -1;
00383 
00384         const PinList circuitDependentPins = node->circuitDependentPins();
00385         const PinList::const_iterator dEnd = circuitDependentPins.end();
00386         for ( PinList::const_iterator it = circuitDependentPins.begin(); it != dEnd; ++it ) {
00387                 if(!associated->contains(*it))
00388                         associated->append(*it);
00389         }
00390 
00391         nodes->append(node);
00392 
00393         const PinList localConnectedPins = node->localConnectedPins();
00394         const PinList::const_iterator end = localConnectedPins.end();
00395         for ( PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it )
00396                 foundGround |= recursivePinAdd( *it, unassignedNodes, associated, nodes );
00397 
00398         return foundGround;
00399 }
00400 
00401 //FIXME: why do we have both this and doLinear()?
00402 
00403 void Circuit::doNonLogic()
00404 {
00405         if(!m_elementSet || m_cnodeCount+m_branchCount <= 0) return;
00406 
00407         if (m_bCanCache) {
00408                 if ( !m_elementSet->b()->isChanged() && !m_elementSet->matrix()->isChanged() )
00409                         return;
00410                 cacheAndUpdate();
00411                 updateNodalVoltages();
00412                 m_elementSet->b()->setUnchanged();
00413                 return;
00414         }
00415 
00416         stepReactive();
00417         if ( m_elementSet->containsNonLinear() ) {
00418                 m_elementSet->doNonLinear( 10, 1e-9, 1e-12 );
00419                 updateNodalVoltages();
00420         } else {
00421                 if ( m_elementSet->doLinear(true) )
00422                         updateNodalVoltages();
00423         }
00424 }
00425 
00426 void Circuit::stepReactive()
00427 {
00428         ElementList::iterator listEnd = m_elementList.end();
00429         for(ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) {
00430                 Element *const e = *it;
00431                 if ( e && e->isReactive() )
00432                         (static_cast<Reactive*>(e))->time_step();
00433         }
00434 }
00435 
00436 void Circuit::updateNodalVoltages()
00437 {
00438         const PinList::iterator endIt = m_pinList.end();
00439         for ( PinList::iterator it = m_pinList.begin(); it != endIt; ++it ) {
00440                 Pin * const node = *it;
00441                 int i = node->eqId();
00442 
00443                 if ( i == -1 ) node->setVoltage(0.);
00444                 else {
00445                         const double v = m_elementSet->cnodes(i)->v;
00446                         node->setVoltage( std::isfinite(v)?v:0. );
00447                 }
00448         }
00449 }
00450 
00451 void Circuit::updateCurrents()
00452 {
00453         ElementList::iterator listEnd = m_elementList.end();
00454         double error = 0;
00455         for(ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) {
00456                 (*it)->updateCurrents();
00457                 error += (*it)->checkCurrents();
00458         }
00459 
00460         if(error>1e-9) {
00461 // you'll see this alot because ground isn't implemented right.
00462 // if you have a ground-battery-load-ground circuit, it'll work.
00463 // if you have a voltage pt->load->voltage pt circuit, it'll work.
00464 // if you have voltage pt->load->ground, expect to see alot of this error.
00465                 kdError() << "currents don't sum, residual = " << error << endl;
00466         }
00467 }
00468 
00469 void Circuit::displayEquations()
00470 {
00471         m_elementSet->displayEquations();
00472 }
00473 //END class Circuit
00474 
00475 //BEGIN class LogicCacheNode
00476 LogicCacheNode::LogicCacheNode()
00477 {
00478         low = 0;
00479         high = 0;
00480         data = 0;
00481 }
00482 
00483 LogicCacheNode::~LogicCacheNode()
00484 {
00485         delete low;
00486         delete high;
00487         delete data;
00488 }
00489 //END class LogicCacheNode
00490 

Generated on Tue May 8 17:05:28 2007 for KTechLab by  doxygen 1.5.1