File indexing completed on 2024-04-28 05:43:15

0001 /***************************************************************************
0002  *   Copyright (C) 2003-2005 by David Saxton                               *
0003  *   david@bluehaze.org                                                    *
0004  *                                                                         *
0005  *   This program is free software; you can redistribute it and/or modify  *
0006  *   it under the terms of the GNU General Public License as published by  *
0007  *   the Free Software Foundation; either version 2 of the License, or     *
0008  *   (at your option) any later version.                                   *
0009  ***************************************************************************/
0010 
0011 #include "circuit.h"
0012 #include "circuitdocument.h"
0013 #include "element.h"
0014 #include "elementset.h"
0015 #include "logic.h"
0016 #include "nonlinear.h"
0017 #include "pin.h"
0018 #include "reactive.h"
0019 #include "wire.h"
0020 
0021 //#include <vector>
0022 #include <cmath>
0023 #include <map>
0024 
0025 typedef std::multimap<int, PinList> PinListMap;
0026 
0027 // BEGIN class Circuit
0028 Circuit::Circuit()
0029 {
0030     m_bCanAddChanged = true;
0031     m_pNextChanged[0] = m_pNextChanged[1] = nullptr;
0032     m_logicOutCount = 0;
0033     m_bCanCache = false;
0034     m_pLogicOut = nullptr;
0035     m_elementSet = new ElementSet(this, 0, 0); // why do we do this?
0036     m_cnodeCount = m_branchCount = -1;
0037     m_prepNLCount = 0;
0038     m_pLogicCacheBase = new LogicCacheNode;
0039 }
0040 
0041 Circuit::~Circuit()
0042 {
0043     delete m_elementSet;
0044     delete m_pLogicCacheBase;
0045     delete[] m_pLogicOut;
0046 }
0047 
0048 void Circuit::addPin(Pin *node)
0049 {
0050     if (m_pinList.contains(node))
0051         return;
0052     m_pinList.append(node);
0053 }
0054 
0055 void Circuit::addElement(Element *element)
0056 {
0057     if (m_elementList.contains(element))
0058         return;
0059     m_elementList.append(element);
0060 }
0061 
0062 bool Circuit::contains(Pin *node)
0063 {
0064     return m_pinList.contains(node);
0065 }
0066 
0067 // static function
0068 int Circuit::identifyGround(PinList nodeList, int *highest)
0069 {
0070     // What this function does:
0071     // We are given a list of pins. First, we divide them into groups of pins
0072     // that are directly connected to each other (e.g. through wires or
0073     // switches). Then, each group of connected pins is looked at to find the
0074     // pin with the highest "ground priority", and this is taken to be
0075     // the priority of the group. The highest ground priority from all the
0076     // groups is recorded. If the highest ground priority found is the maximum,
0077     // then all the pins in groups with this priority are marked as ground
0078     // (their eq-id is set to -1). Otherwise, the first group of pins with the
0079     // highest ground priority found is marked as ground, and all others are
0080     // marked as non ground (their eq-id is set to 0).
0081 
0082     int temp_highest;
0083     if (!highest)
0084         highest = &temp_highest;
0085 
0086     // Now to give all the Pins ids
0087     PinListMap eqs;
0088     while (!nodeList.isEmpty()) {
0089         PinList associated;
0090         PinList nodes;
0091         Pin *node = *nodeList.begin();
0092         recursivePinAdd(node, &nodeList, &associated, &nodes);
0093         if (nodes.size() > 0) {
0094             eqs.insert(std::make_pair(associated.size(), nodes));
0095         }
0096     }
0097 
0098     // Now, we want to look through the associated Pins,
0099     // to find the ones with the highest "Ground Priority". Anything with a lower
0100     // priority than Pin::gt_never will not be considered
0101     *highest = Pin::gt_never; // The highest priority found so far
0102     int numGround = 0;        // The number of node groups found with that priority
0103     const PinListMap::iterator eqsEnd = eqs.end();
0104     for (PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it) {
0105         int highPri = Pin::gt_never; // The highest priority found in these group of nodes
0106         const PinList::iterator send = it->second.end();
0107         for (PinList::iterator sit = it->second.begin(); sit != send; ++sit) {
0108             if ((*sit)->groundType() < highPri)
0109                 highPri = (*sit)->groundType();
0110         }
0111 
0112         if (highPri == *highest)
0113             numGround++;
0114 
0115         else if (highPri < *highest) {
0116             numGround = 1;
0117             *highest = highPri;
0118         }
0119     }
0120 
0121     if (*highest == Pin::gt_never) {
0122         (*highest)--;
0123         numGround = 0;
0124     }
0125     // If there are no Always Ground nodes, then we only want to set one of the nodes as ground
0126     else if (*highest > Pin::gt_always)
0127         numGround = 1;
0128 
0129     // Now, we can give the nodes their cnode ids, or tell them they are ground
0130     bool foundGround = false; // This is only used when we don't have a Always ground node
0131     for (PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it) {
0132         bool ground = false;
0133         const PinList::iterator send = it->second.end();
0134         for (PinList::iterator sit = it->second.begin(); sit != send; ++sit) {
0135             ground |= (*sit)->groundType() <= (*highest);
0136         }
0137         if (ground && (!foundGround || *highest == Pin::gt_always)) {
0138             for (PinList::iterator sit = it->second.begin(); sit != send; ++sit) {
0139                 (*sit)->setEqId(-1);
0140             }
0141             foundGround = true;
0142         } else {
0143             for (PinList::iterator sit = it->second.begin(); sit != send; ++sit) {
0144                 (*sit)->setEqId(0);
0145             }
0146         }
0147     }
0148 
0149     return numGround;
0150 }
0151 
0152 void Circuit::init()
0153 {
0154     m_branchCount = 0;
0155 
0156     const ElementList::iterator listEnd = m_elementList.end();
0157     for (ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it) {
0158         m_branchCount += (*it)->numCBranches();
0159     }
0160 
0161     // Now to give all the Pins ids
0162     int groundCount = 0;
0163     PinListMap eqs;
0164     PinList unassignedNodes = m_pinList;
0165     while (!unassignedNodes.isEmpty()) {
0166         PinList associated;
0167         PinList nodes;
0168         Pin *node = *unassignedNodes.begin();
0169         if (recursivePinAdd(node, &unassignedNodes, &associated, &nodes)) {
0170             groundCount++;
0171         }
0172         if (nodes.size() > 0) {
0173             eqs.insert(std::make_pair(associated.size(), nodes));
0174         }
0175     }
0176 
0177     m_cnodeCount = eqs.size() - groundCount;
0178 
0179     delete m_pLogicCacheBase;
0180     m_pLogicCacheBase = nullptr;
0181 
0182     delete m_elementSet;
0183     m_elementSet = new ElementSet(this, m_cnodeCount, m_branchCount);
0184 
0185     // Now, we can give the nodes their cnode ids, or tell them they are ground
0186     QuickVector *x = m_elementSet->x();
0187     int i = 0;
0188     const PinListMap::iterator eqsEnd = eqs.end();
0189     for (PinListMap::iterator it = eqs.begin(); it != eqsEnd; ++it) {
0190         bool foundGround = false;
0191 
0192         const PinList::iterator sEnd = it->second.end();
0193         for (PinList::iterator sit = it->second.begin(); sit != sEnd; ++sit)
0194             foundGround |= (*sit)->eqId() == -1;
0195 
0196         if (foundGround)
0197             continue;
0198 
0199         bool foundEnergyStoragePin = false;
0200 
0201         for (PinList::iterator sit = it->second.begin(); sit != sEnd; ++sit) {
0202             (*sit)->setEqId(i);
0203 
0204             bool energyStorage = false;
0205             const ElementList elements = (*sit)->elements();
0206             ElementList::const_iterator elementsEnd = elements.end();
0207             for (ElementList::const_iterator it = elements.begin(); it != elementsEnd; ++it) {
0208                 if (!*it)
0209                     continue;
0210 
0211                 if (((*it)->type() == Element::Element_Capacitance) || ((*it)->type() == Element::Element_Inductance)) {
0212                     energyStorage = true;
0213                     break;
0214                 }
0215             }
0216 
0217             // A pin attached to an energy storage pin overrides one that doesn't.
0218             // If the two pins have equal status with in this regard, we pick the
0219             // one with the highest absolute voltage on it.
0220 
0221             if (foundEnergyStoragePin && !energyStorage)
0222                 continue;
0223 
0224             double v = (*sit)->voltage();
0225 
0226             if (energyStorage && !foundEnergyStoragePin) {
0227                 foundEnergyStoragePin = true;
0228                 (*x)[i] = v;
0229                 continue;
0230             }
0231 
0232             if (std::abs(v) > std::abs((*x)[i]))
0233                 (*x)[i] = v;
0234         }
0235         i++;
0236     }
0237 
0238     // And add the elements to the elementSet
0239     for (ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it) {
0240         // We don't want the element to prematurely try to do anything,
0241         // as it doesn't know its actual cnode ids yet
0242         (*it)->setCNodes();
0243         (*it)->setCBranches();
0244         m_elementSet->addElement(*it);
0245     }
0246     // And give the branch ids to the elements
0247     i = 0;
0248     for (ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it) {
0249         switch ((*it)->numCBranches()) {
0250         case 0:
0251             break;
0252         case 1:
0253             (*it)->setCBranches(i);
0254             i += 1;
0255             break;
0256         case 2:
0257             (*it)->setCBranches(i, i + 1);
0258             i += 2;
0259             break;
0260         case 3:
0261             (*it)->setCBranches(i, i + 1, i + 2);
0262             i += 3;
0263             break;
0264         default:
0265             // What the?!
0266             break;
0267         }
0268     }
0269 }
0270 
0271 void Circuit::initCache()
0272 {
0273     m_elementSet->updateInfo();
0274 
0275     m_bCanCache = true;
0276     m_logicOutCount = 0;
0277 
0278     delete[] m_pLogicOut;
0279     m_pLogicOut = nullptr;
0280 
0281     delete m_pLogicCacheBase;
0282     m_pLogicCacheBase = nullptr;
0283 
0284     const ElementList::iterator end = m_elementList.end();
0285     for (ElementList::iterator it = m_elementList.begin(); it != end && m_bCanCache; ++it) {
0286         switch ((*it)->type()) {
0287         case Element::Element_BJT:
0288         case Element::Element_CCCS:
0289         case Element::Element_CCVS:
0290         case Element::Element_CurrentSource:
0291         case Element::Element_Diode:
0292         case Element::Element_JFET:
0293         case Element::Element_LogicIn:
0294         case Element::Element_MOSFET:
0295         case Element::Element_OpAmp:
0296         case Element::Element_Resistance:
0297         case Element::Element_VCCS:
0298         case Element::Element_VCVS:
0299         case Element::Element_VoltagePoint:
0300         case Element::Element_VoltageSource: {
0301             break;
0302         }
0303 
0304         case Element::Element_LogicOut: {
0305             m_logicOutCount++;
0306             break;
0307         }
0308 
0309         case Element::Element_CurrentSignal:
0310         case Element::Element_VoltageSignal:
0311         case Element::Element_Capacitance:
0312         case Element::Element_Inductance: {
0313             m_bCanCache = false;
0314             break;
0315         }
0316         }
0317     }
0318 
0319     if (!m_bCanCache)
0320         return;
0321 
0322     m_pLogicOut = new LogicOut *[m_logicOutCount];
0323     unsigned i = 0;
0324     for (ElementList::iterator it = m_elementList.begin(); it != end && m_bCanCache; ++it) {
0325         if ((*it)->type() == Element::Element_LogicOut)
0326             m_pLogicOut[i++] = static_cast<LogicOut *>(*it);
0327     }
0328 
0329     m_pLogicCacheBase = new LogicCacheNode;
0330 }
0331 
0332 void Circuit::setCacheInvalidated()
0333 {
0334     if (m_pLogicCacheBase) {
0335         delete m_pLogicCacheBase->high;
0336         m_pLogicCacheBase->high = nullptr;
0337 
0338         delete m_pLogicCacheBase->low;
0339         m_pLogicCacheBase->low = nullptr;
0340 
0341         delete m_pLogicCacheBase->data;
0342         m_pLogicCacheBase->data = nullptr;
0343     }
0344 }
0345 
0346 void Circuit::cacheAndUpdate()
0347 {
0348     LogicCacheNode *node = m_pLogicCacheBase;
0349     for (unsigned i = 0; i < m_logicOutCount; i++) {
0350         if (m_pLogicOut[i]->outputState()) {
0351             if (!node->high)
0352                 node->high = new LogicCacheNode;
0353 
0354             node = node->high;
0355         } else {
0356             if (!node->low)
0357                 node->low = new LogicCacheNode;
0358 
0359             node = node->low;
0360         }
0361     }
0362 
0363     if (node->data) {
0364         (*m_elementSet->x()) = *node->data;
0365         m_elementSet->updateInfo();
0366         return;
0367     }
0368 
0369     if (m_elementSet->containsNonLinear())
0370         m_elementSet->doNonLinear(150, 1e-10, 1e-13);
0371     else
0372         m_elementSet->doLinear(true);
0373 
0374     if (node->data) {
0375         node->data = m_elementSet->x();
0376     } else {
0377         node->data = new QuickVector(m_elementSet->x());
0378     }
0379 
0380     //  node->data = new Vector( m_elementSet->x()->size() );
0381     //  *node->data = *m_elementSet->x();
0382 }
0383 
0384 void Circuit::createMatrixMap()
0385 {
0386     m_elementSet->createMatrixMap();
0387 }
0388 
0389 bool Circuit::recursivePinAdd(Pin *node, PinList *unassignedNodes, PinList *associated, PinList *nodes)
0390 {
0391     if (!unassignedNodes->contains(node))
0392         return false;
0393     unassignedNodes->removeAll(node);
0394 
0395     bool foundGround = node->eqId() == -1;
0396 
0397     const PinList circuitDependentPins = node->circuitDependentPins();
0398     const PinList::const_iterator dEnd = circuitDependentPins.end();
0399     for (PinList::const_iterator it = circuitDependentPins.begin(); it != dEnd; ++it) {
0400         if (!associated->contains(*it))
0401             associated->append(*it);
0402     }
0403 
0404     nodes->append(node);
0405 
0406     const PinList localConnectedPins = node->localConnectedPins();
0407     const PinList::const_iterator end = localConnectedPins.end();
0408     for (PinList::const_iterator it = localConnectedPins.begin(); it != end; ++it)
0409         foundGround |= recursivePinAdd(*it, unassignedNodes, associated, nodes);
0410 
0411     return foundGround;
0412 }
0413 
0414 void Circuit::doNonLogic()
0415 {
0416     if (!m_elementSet || m_cnodeCount + m_branchCount <= 0)
0417         return;
0418 
0419     if (m_bCanCache) {
0420         if (!m_elementSet->b()->isChanged() && !m_elementSet->matrix()->isChanged())
0421             return;
0422         cacheAndUpdate();
0423         updateNodalVoltages();
0424         m_elementSet->b()->setUnchanged();
0425         return;
0426     }
0427 
0428     stepReactive();
0429     if (m_elementSet->containsNonLinear()) {
0430         m_elementSet->doNonLinear(10, 1e-9, 1e-12);
0431         updateNodalVoltages();
0432     } else {
0433         if (m_elementSet->doLinear(true))
0434             updateNodalVoltages();
0435     }
0436 }
0437 
0438 void Circuit::stepReactive()
0439 {
0440     ElementList::iterator listEnd = m_elementList.end();
0441     for (ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it) {
0442         Element *const e = *it;
0443         if (e && e->isReactive())
0444             (static_cast<Reactive *>(e))->time_step();
0445     }
0446 }
0447 
0448 void Circuit::updateNodalVoltages()
0449 {
0450     CNode **_cnodes = m_elementSet->cnodes();
0451 
0452     const PinList::iterator endIt = m_pinList.end();
0453     for (PinList::iterator it = m_pinList.begin(); it != endIt; ++it) {
0454         Pin *const node = *it;
0455         int i = node->eqId();
0456         if (i == -1)
0457             node->setVoltage(0.);
0458         else {
0459             const double v = _cnodes[i]->v;
0460             node->setVoltage(std::isfinite(v) ? v : 0.);
0461         }
0462     }
0463 }
0464 
0465 void Circuit::updateCurrents()
0466 {
0467     ElementList::iterator listEnd = m_elementList.end();
0468     for (ElementList::iterator it = m_elementList.begin(); it != listEnd; ++it) {
0469         (*it)->updateCurrents();
0470     }
0471 }
0472 
0473 void Circuit::displayEquations()
0474 {
0475     m_elementSet->displayEquations();
0476 }
0477 // END class Circuit
0478 
0479 // BEGIN class LogicCacheNode
0480 
0481 LogicCacheNode::LogicCacheNode()
0482 {
0483     low = nullptr;
0484     high = nullptr;
0485     data = nullptr;
0486 }
0487 
0488 LogicCacheNode::~LogicCacheNode()
0489 {
0490     delete low;
0491     delete high;
0492     if (data)
0493         delete data;
0494 }
0495 // END class LogicCacheNode