/*************************************************************************** * Copyright (C) 2003-2005 by David Saxton * * david@bluehaze.org * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * ***************************************************************************/ #include #include "circuit.h" #include "circuitdocument.h" #include "element.h" #include "elementset.h" #include "logic.h" #include "matrix.h" #include "nonlinear.h" #include "pin.h" #include "reactive.h" #include "wire.h" #include #include typedef std::multimap PinListMap; //BEGIN class Circuit Circuit::Circuit() { m_bCanAddChanged = true; m_pNextChanged[0] = m_pNextChanged[1] = 0l; m_logicOutCount = 0; m_bCanCache = false; m_pLogicOut = 0l; m_elementSet = new ElementSet( this, 0, 0 ); m_cnodeCount = m_branchCount = -1; m_prepNLCount = 0; m_pLogicCacheBase = new LogicCacheNode; } Circuit::~Circuit() { delete m_elementSet; delete m_pLogicCacheBase; delete[] m_pLogicOut; } void Circuit::addPin( Pin *node ) { if ( m_pinList.tqcontains(node) ) return; m_pinList.append(node); } void Circuit::addElement( Element *element ) { if ( m_elementList.tqcontains(element) ) return; m_elementList.append(element); } bool Circuit::tqcontains( Pin *node ) { return m_pinList.tqcontains(node); } // static function int Circuit::identifyGround( PinList nodeList, int *highest ) { // What this function does: // We are given a list of pins. First, we divide them into groups of pins // that are directly connected to each other (e.g. through wires or // switches). Then, each group of connected pins is looked at to find the // pin with the highest "ground priority", and this is taken to be // the priority of the group. The highest ground priority from all the // groups is recorded. If the highest ground priority found is the maximum, // then all the pins in groups with this priority are marked as ground // (their eq-id is set to -1). Otherwise, the first group of pins with the // highest ground priority found is marked as ground, and all others are // marked as non ground (their eq-id is set to 0). int temp_highest; if (!highest) highest = &temp_highest; // Now to give all the Pins ids PinListMap eqs; while ( !nodeList.isEmpty() ) { PinList associated; PinList nodes; Pin *node = *nodeList.begin(); recursivePinAdd( node, &nodeList, &associated, &nodes ); if ( nodes.size() > 0 ) { eqs.insert( std::make_pair( associated.size(), nodes ) ); } } // Now, we want to look through the associated Pins, // to find the ones with the highest "Ground Priority". Anything with a lower // priority than Pin::gt_never will not be considered *highest = Pin::gt_never; // The highest priority found so far int numGround = 0; // The number of node groups found with that priority const PinListMap::iterator eqsEnd = eqs.end(); for ( PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it ) { int highPri = Pin::gt_never; // The highest priority found in these group of nodes const PinList::iterator send = it->second.end(); for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit ) { if ( (*sit)->groundType() < highPri ) highPri = (*sit)->groundType(); } if ( highPri == *highest ) numGround++; else if ( highPri < *highest ) { numGround = 1; *highest = highPri; } } if ( *highest == Pin::gt_never ) { (*highest)--; numGround=0; } // If there are no Always Ground nodes, then we only want to set one of the nodes as ground else if ( *highest > Pin::gt_always ) numGround = 1; // Now, we can give the nodes their cnode ids, or tell them they are ground bool foundGround = false; // This is only used when we don't have a Always ground node for ( PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it ) { bool ground = false; const PinList::iterator send = it->second.end(); for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit ) { ground |= (*sit)->groundType() <= (*highest); } if ( ground && (!foundGround || *highest == Pin::gt_always ) ) { for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit ) { (*sit)->setEqId(-1); } foundGround = true; } else { for ( PinList::iterator sit = it->second.begin(); sit != send; ++sit ) { (*sit)->setEqId(0); } } } return numGround; } void Circuit::init() { m_branchCount = 0; const ElementList::iterator listEnd = m_elementList.end(); for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) { m_branchCount += (*it)->numCBranches(); } // Now to give all the Pins ids int groundCount = 0; PinListMap eqs; PinList unassignedNodes = m_pinList; while ( !unassignedNodes.isEmpty() ) { PinList associated; PinList nodes; Pin *node = *unassignedNodes.begin(); if ( recursivePinAdd( node, &unassignedNodes, &associated, &nodes ) ) { groundCount++; } if ( nodes.size() > 0 ) { eqs.insert( std::make_pair( associated.size(), nodes ) ); } } m_cnodeCount = eqs.size() - groundCount; delete m_pLogicCacheBase; m_pLogicCacheBase = 0l; delete m_elementSet; m_elementSet = new ElementSet( this, m_cnodeCount, m_branchCount ); // Now, we can give the nodes their cnode ids, or tell them they are ground Vector *x = m_elementSet->x(); int i=0; const PinListMap::iterator eqsEnd = eqs.end(); for ( PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it ) { bool foundGround = false; const PinList::iterator sEnd = it->second.end(); for ( PinList::iterator sit = it->second.begin(); sit != sEnd; ++sit ) foundGround |= (*sit)->eqId() == -1; if ( foundGround ) continue; bool foundEnergyStoragePin = false; for ( PinList::iterator sit = it->second.begin(); sit != sEnd; ++sit ) { (*sit)->setEqId(i); bool energyStorage = false; const ElementList elements = (*sit)->elements(); ElementList::const_iterator elementsEnd = elements.end(); for ( ElementList::const_iterator it = elements.begin(); it != elementsEnd; ++it ) { if ( !*it ) continue; if ( ((*it)->type() == Element::Element_Capacitance) || ((*it)->type() == Element::Element_Inductance) ) { energyStorage = true; break; } } // A pin attached to an energy storage pin overrides one that doesn't. // If the two pins have equal status with in this regard, we pick the // one with the highest absolute voltage on it. if ( foundEnergyStoragePin && !energyStorage ) continue; double v = (*sit)->voltage(); if ( energyStorage && !foundEnergyStoragePin ) { foundEnergyStoragePin = true; (*x)[i] = v; continue; } if ( std::abs(v) > std::abs( (*x)[i] ) ) (*x)[i] = v; } i++; } // And add the elements to the elementSet for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) { // We don't want the element to prematurely try to do anything, // as it doesn't know its actual cnode ids yet (*it)->setCNodes(); (*it)->setCBranches(); m_elementSet->addElement(*it); } // And give the branch ids to the elements i=0; for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) { switch ( (*it)->numCBranches() ) { case 0: break; case 1: (*it)->setCBranches( i ); i += 1; break; case 2: (*it)->setCBranches( i, i+1 ); i += 2; break; case 3: (*it)->setCBranches( i, i+1, i+2 ); i += 3; break; default: // What the?! break; } } } void Circuit::initCache() { m_elementSet->updateInfo(); m_bCanCache = true; m_logicOutCount = 0; delete[] m_pLogicOut; m_pLogicOut = 0l; delete m_pLogicCacheBase; m_pLogicCacheBase = 0l; const ElementList::iterator end = m_elementList.end(); for ( ElementList::iterator it = m_elementList.begin(); it != end && m_bCanCache; ++it ) { switch ( (*it)->type() ) { case Element::Element_BJT: case Element::Element_CCCS: case Element::Element_CCVS: case Element::Element_CurrentSource: case Element::Element_Diode: case Element::Element_LogicIn: case Element::Element_OpAmp: case Element::Element_Resistance: case Element::Element_VCCS: case Element::Element_VCVS: case Element::Element_VoltagePoint: case Element::Element_VoltageSource: { break; } case Element::Element_LogicOut: { m_logicOutCount++; break; } case Element::Element_CurrentSignal: case Element::Element_VoltageSignal: case Element::Element_Capacitance: case Element::Element_Inductance: { m_bCanCache = false; break; } } } if ( !m_bCanCache ) return; m_pLogicOut = new LogicOut*[m_logicOutCount]; unsigned i = 0; for ( ElementList::iterator it = m_elementList.begin(); it != end && m_bCanCache; ++it ) { if ( (*it)->type() == Element::Element_LogicOut ) m_pLogicOut[i++] = static_cast(*it); } m_pLogicCacheBase = new LogicCacheNode; } void Circuit::setCacheInvalidated() { if (m_pLogicCacheBase) { delete m_pLogicCacheBase->high; m_pLogicCacheBase->high = 0l; delete m_pLogicCacheBase->low; m_pLogicCacheBase->low = 0l; delete m_pLogicCacheBase->data; m_pLogicCacheBase->data = 0l; } } void Circuit::cacheAndUpdate() { LogicCacheNode * node = m_pLogicCacheBase; for ( unsigned i = 0; i < m_logicOutCount; i++ ) { if ( m_pLogicOut[i]->outputState() ) { if (!node->high) node->high = new LogicCacheNode; node = node->high; } else { if (!node->low) node->low = new LogicCacheNode; node = node->low; } } if ( node->data ) { (*m_elementSet->x()) = *node->data; m_elementSet->updateInfo(); return; } if ( m_elementSet->containsNonLinear() ) m_elementSet->doNonLinear( 150, 1e-10, 1e-13 ); else m_elementSet->doLinear(true); node->data = new Vector( m_elementSet->x()->size() ); *node->data = *m_elementSet->x(); } void Circuit::createMatrixMap() { m_elementSet->createMatrixMap(); } bool Circuit::recursivePinAdd( Pin *node, PinList *unassignedNodes, PinList *associated, PinList *nodes ) { if ( !unassignedNodes->tqcontains(node) ) return false; unassignedNodes->remove(node); bool foundGround = node->eqId() == -1; const PinList circuitDependentPins = node->circuitDependentPins(); const PinList::const_iterator dEnd = circuitDependentPins.end(); for ( PinList::const_iterator it = circuitDependentPins.begin(); it != dEnd; ++it ) { if ( !associated->tqcontains(*it) ) associated->append(*it); } nodes->append(node); const PinList localConnectedPins = node->localConnectedPins(); const PinList::const_iterator end = localConnectedPins.end(); for ( PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it ) foundGround |= recursivePinAdd( *it, unassignedNodes, associated, nodes ); return foundGround; } void Circuit::doNonLogic() { if ( !m_elementSet || m_cnodeCount+m_branchCount <= 0 ) return; if (m_bCanCache) { if ( !m_elementSet->b()->isChanged() && !m_elementSet->matrix()->isChanged() ) return; cacheAndUpdate(); updateNodalVoltages(); m_elementSet->b()->setUnchanged(); return; } stepReactive(); if ( m_elementSet->containsNonLinear() ) { m_elementSet->doNonLinear( 10, 1e-9, 1e-12 ); updateNodalVoltages(); } else { if ( m_elementSet->doLinear(true) ) updateNodalVoltages(); } } void Circuit::stepReactive() { ElementList::iterator listEnd = m_elementList.end(); for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) { Element * const e = *it; if ( e && e->isReactive() ) (static_cast(e))->time_step(); } } void Circuit::updateNodalVoltages() { CNode **_cnodes = m_elementSet->cnodes(); const PinList::iterator endIt = m_pinList.end(); for ( PinList::iterator it = m_pinList.begin(); it != endIt; ++it ) { Pin * const node = *it; int i = node->eqId(); if ( i == -1 ) node->setVoltage(0.); else { const double v = _cnodes[i]->v; node->setVoltage( std::isfinite(v)?v:0. ); } } } void Circuit::updateCurrents() { ElementList::iterator listEnd = m_elementList.end(); for ( ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it ) { (*it)->updateCurrents(); } } void Circuit::displayEquations() { m_elementSet->displayEquations(); } //END class Circuit //BEGIN class LogicCacheNode LogicCacheNode::LogicCacheNode() { low = 0l; high = 0l; data = 0l; } LogicCacheNode::~LogicCacheNode() { delete low; delete high; delete data; } //END class LogicCacheNode