You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ktechlab/src/electronics/simulation/circuit.cpp

551 lines
13 KiB

/***************************************************************************
* 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 <vector>
#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 <cmath>
#include <map>
typedef std::multimap<int, PinList> 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.contains(node) ) return;
m_pinList.append(node);
}
void Circuit::addElement( Element *element )
{
if ( m_elementList.contains(element) ) return;
m_elementList.append(element);
}
bool Circuit::contains( Pin *node )
{
return m_pinList.contains(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<LogicOut*>(*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->contains(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->contains(*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<Reactive*>(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