Пример #1
0
        public void AnalyzeCircuit()
        {
            bool debug   = false;
            var  elmList = mSim.ElmList;

            if (elmList.Count == 0)
            {
                PostDrawList      = new List <Point>();
                BadConnectionList = new List <Point>();
                return;
            }

            StopMessage = null;
            StopElm     = null;

            int vscount = 0;

            NodeList      = new List <CircuitNode>();
            mPostCountMap = new Dictionary <Point, int>();
            bool       gotGround = false;
            bool       gotRail   = false;
            CircuitElm volt      = null;

            calculateWireClosure();

            if (debug)
            {
                Console.WriteLine("ac1");
            }
            /* look for voltage or ground element */
            for (int i = 0; i != elmList.Count; i++)
            {
                var ce = mSim.getElm(i);
                if (ce is GroundElm)
                {
                    gotGround = true;
                    break;
                }

                if (ce is RailElm)
                {
                    gotRail = true;
                }
                if (volt == null && (ce is VoltageElm))
                {
                    volt = ce;
                }
            }

            /* if no ground, and no rails, then the voltage elm's first terminal
             * /* is ground */
            if (!gotGround && volt != null && !gotRail)
            {
                var cn = new CircuitNode();
                var pt = volt.GetPost(0);
                NodeList.Add(cn);
                /* update node map */
                if (mNodeMap.ContainsKey(pt))
                {
                    mNodeMap[pt].Node = 0;
                }
                else
                {
                    mNodeMap.Add(pt, new NodeMapEntry(0));
                }
            }
            else
            {
                /* otherwise allocate extra node for ground */
                var cn = new CircuitNode();
                NodeList.Add(cn);
            }
            if (debug)
            {
                Console.WriteLine("ac2");
            }

            /* allocate nodes and voltage sources */
            LabeledNodeElm.ResetNodeList();
            for (int i = 0; i != elmList.Count; i++)
            {
                var ce     = mSim.getElm(i);
                int inodes = ce.InternalNodeCount;
                int ivs    = ce.VoltageSourceCount;
                int posts  = ce.PostCount;

                /* allocate a node for each post and match posts to nodes */
                for (int j = 0; j != posts; j++)
                {
                    var pt = ce.GetPost(j);
                    if (mPostCountMap.ContainsKey(pt))
                    {
                        int g = mPostCountMap[pt];
                        mPostCountMap[pt] = g + 1;
                    }
                    else
                    {
                        mPostCountMap.Add(pt, 1);
                    }

                    NodeMapEntry cln  = null;
                    var          ccln = mNodeMap.ContainsKey(pt);
                    if (ccln)
                    {
                        cln = mNodeMap[pt];
                    }

                    /* is this node not in map yet?  or is the node number unallocated?
                     * /* (we don't allocate nodes before this because changing the allocation order
                     * /* of nodes changes circuit behavior and breaks backward compatibility;
                     * /* the code below to connect unconnected nodes may connect a different node to ground) */
                    if (!ccln || cln.Node == -1)
                    {
                        var cn  = new CircuitNode();
                        var cnl = new CircuitNodeLink();
                        cnl.Num = j;
                        cnl.Elm = ce;
                        cn.Links.Add(cnl);
                        ce.SetNode(j, NodeList.Count);
                        if (ccln)
                        {
                            cln.Node = NodeList.Count;
                        }
                        else
                        {
                            mNodeMap.Add(pt, new NodeMapEntry(NodeList.Count));
                        }
                        NodeList.Add(cn);
                    }
                    else
                    {
                        int n   = cln.Node;
                        var cnl = new CircuitNodeLink();
                        cnl.Num = j;
                        cnl.Elm = ce;
                        getCircuitNode(n).Links.Add(cnl);
                        ce.SetNode(j, n);

                        /* if it's the ground node, make sure the node voltage is 0,
                         * /* cause it may not get set later */
                        if (n == 0)
                        {
                            ce.SetNodeVoltage(j, 0);
                        }
                    }
                }
                for (int j = 0; j != inodes; j++)
                {
                    var cn = new CircuitNode();
                    cn.Internal = true;
                    var cnl = new CircuitNodeLink();
                    cnl.Num = j + posts;
                    cnl.Elm = ce;
                    cn.Links.Add(cnl);
                    ce.SetNode(cnl.Num, NodeList.Count);
                    NodeList.Add(cn);
                }
                vscount += ivs;
            }

            makePostDrawList();
            if (!calcWireInfo())
            {
                return;
            }
            mNodeMap = null; /* done with this */

            VoltageSources   = new CircuitElm[vscount];
            vscount          = 0;
            CircuitNonLinear = false;
            if (debug)
            {
                Console.WriteLine("ac3");
            }

            /* determine if circuit is nonlinear */
            for (int i = 0; i != elmList.Count; i++)
            {
                var ce = mSim.getElm(i);
                if (ce.NonLinear)
                {
                    CircuitNonLinear = true;
                }
                int ivs = ce.VoltageSourceCount;
                for (int j = 0; j != ivs; j++)
                {
                    VoltageSources[vscount] = ce;
                    ce.SetVoltageSource(j, vscount++);
                }
            }
            VoltageSourceCount = vscount;

            int matrixSize = NodeList.Count - 1 + vscount;

            Matrix         = new double[matrixSize, matrixSize];
            mRightSide     = new double[matrixSize];
            mOrigMatrix    = new double[matrixSize, matrixSize];
            mOrigRightSide = new double[matrixSize];
            mMatrixSize    = mMatrixFullSize = matrixSize;
            mRowInfo       = new RowInfo[matrixSize];
            mPermute       = new int[matrixSize];
            for (int i = 0; i != matrixSize; i++)
            {
                mRowInfo[i] = new RowInfo();
            }
            mCircuitNeedsMap = false;

            /* stamp linear circuit elements */
            for (int i = 0; i != elmList.Count; i++)
            {
                var ce = mSim.getElm(i);
                ce.Stamp();
            }
            if (debug)
            {
                Console.WriteLine("ac4");
            }

            /* determine nodes that are not connected indirectly to ground */
            var  closure = new bool[NodeList.Count];
            bool changed = true;

            closure[0] = true;
            while (changed)
            {
                changed = false;
                for (int i = 0; i != elmList.Count; i++)
                {
                    var ce = mSim.getElm(i);
                    if (ce is WireElm)
                    {
                        continue;
                    }

                    /* loop through all ce's nodes to see if they are connected
                     * /* to other nodes not in closure */
                    for (int j = 0; j < ce.ConnectionNodeCount; j++)
                    {
                        if (!closure[ce.GetConnectionNode(j)])
                        {
                            if (ce.HasGroundConnection(j))
                            {
                                closure[ce.GetConnectionNode(j)] = changed = true;
                            }
                            continue;
                        }
                        int k;
                        for (k = 0; k != ce.ConnectionNodeCount; k++)
                        {
                            if (j == k)
                            {
                                continue;
                            }
                            int kn = ce.GetConnectionNode(k);
                            if (ce.GetConnection(j, k) && !closure[kn])
                            {
                                closure[kn] = true;
                                changed     = true;
                            }
                        }
                    }
                }
                if (changed)
                {
                    continue;
                }

                /* connect one of the unconnected nodes to ground with a big resistor, then try again */
                for (int i = 0; i != NodeList.Count; i++)
                {
                    if (!closure[i] && !getCircuitNode(i).Internal)
                    {
                        /* Console.WriteLine("node " + i + " unconnected"); */
                        StampResistor(0, i, 1e8);
                        closure[i] = true;
                        changed    = true;
                        break;
                    }
                }
            }
            if (debug)
            {
                Console.WriteLine("ac5");
            }

            for (int i = 0; i != elmList.Count; i++)
            {
                var ce = mSim.getElm(i);

                /* look for inductors with no current path */
                if (ce is InductorElm)
                {
                    var fpi = new PathInfo(PathType.INDUCTOR, ce, ce.Nodes[1], elmList, NodeList.Count);
                    if (!fpi.FindPath(ce.Nodes[0]))
                    {
                        if (debug)
                        {
                            Console.WriteLine(ce + " no path");
                        }
                        ce.Reset();
                    }
                }

                /* look for current sources with no current path */
                if (ce is CurrentElm)
                {
                    var cur = (CurrentElm)ce;
                    var fpi = new PathInfo(PathType.INDUCTOR, ce, ce.Nodes[1], elmList, NodeList.Count);
                    if (!fpi.FindPath(ce.Nodes[0]))
                    {
                        cur.stampCurrentSource(true);
                    }
                    else
                    {
                        cur.stampCurrentSource(false);
                    }
                }

                if (ce is VCCSElm)
                {
                    var cur = (VCCSElm)ce;
                    var fpi = new PathInfo(PathType.INDUCTOR, ce, cur.getOutputNode(0), elmList, NodeList.Count);
                    if (cur.hasCurrentOutput() && !fpi.FindPath(cur.getOutputNode(1)))
                    {
                        cur.mBroken = true;
                    }
                    else
                    {
                        cur.mBroken = false;
                    }
                }

                /* look for voltage source or wire loops.  we do this for voltage sources or wire-like elements (not actual wires
                 * /* because those are optimized out, so the findPath won't work) */
                if (2 == ce.PostCount)
                {
                    if ((ce is VoltageElm) || (ce.IsWire && !(ce is WireElm)))
                    {
                        var fpi = new PathInfo(PathType.VOLTAGE, ce, ce.Nodes[1], elmList, NodeList.Count);
                        if (fpi.FindPath(ce.Nodes[0]))
                        {
                            Stop("Voltage source/wire loop with no resistance!", ce);
                            return;
                        }
                    }
                }
                else if (ce is Switch2Elm)
                {
                    /* for Switch2Elms we need to do extra work to look for wire loops */
                    var fpi = new PathInfo(PathType.VOLTAGE, ce, ce.Nodes[0], elmList, NodeList.Count);
                    for (int j = 1; j < ce.PostCount; j++)
                    {
                        if (ce.GetConnection(0, j) && fpi.FindPath(ce.Nodes[j]))
                        {
                            Stop("Voltage source/wire loop with no resistance!", ce);
                            return;
                        }
                    }
                }

                /* look for path from rail to ground */
                if ((ce is RailElm) || (ce is LogicInputElm))
                {
                    var fpi = new PathInfo(PathType.VOLTAGE, ce, ce.Nodes[0], elmList, NodeList.Count);
                    if (fpi.FindPath(0))
                    {
                        Stop("Path to ground with no resistance!", ce);
                        return;
                    }
                }

                /* look for shorted caps, or caps w/ voltage but no R */
                if (ce is CapacitorElm)
                {
                    var fpi = new PathInfo(PathType.SHORT, ce, ce.Nodes[1], elmList, NodeList.Count);
                    if (fpi.FindPath(ce.Nodes[0]))
                    {
                        Console.WriteLine(ce + " shorted");
                        ((CapacitorElm)ce).Shorted();
                    }
                    else
                    {
                        /* a capacitor loop used to cause a matrix error. but we changed the capacitor model
                         * /* so it works fine now. The only issue is if a capacitor is added in parallel with
                         * /* another capacitor with a nonzero voltage; in that case we will get oscillation unless
                         * /* we reset both capacitors to have the same voltage. Rather than check for that, we just
                         * /* give an error. */
                        fpi = new PathInfo(PathType.CAPACITOR_V, ce, ce.Nodes[1], elmList, NodeList.Count);
                        if (fpi.FindPath(ce.Nodes[0]))
                        {
                            Stop("Capacitor loop with no resistance!", ce);
                            return;
                        }
                    }
                }
            }
            if (debug)
            {
                Console.WriteLine("ac6");
            }

            if (!simplifyMatrix(matrixSize))
            {
                return;
            }

            if (debug)
            {
                Console.WriteLine("matrixSize = " + matrixSize + " " + CircuitNonLinear);
                for (int j = 0; j != mMatrixSize; j++)
                {
                    Console.WriteLine("RightSide[{0}]:{1}", j, mRightSide[j]);
                    for (int i = 0; i != mMatrixSize; i++)
                    {
                        Console.WriteLine("  Matrix[{0},{1}]:{2}", j, i, Matrix[j, i]);
                    }
                }
            }

            /* check if we called stop() */
            if (Matrix == null)
            {
                return;
            }

            /* if a matrix is linear, we can do the lu_factor here instead of
             * /* needing to do it every frame */
            if (!CircuitNonLinear)
            {
                if (!luFactor(Matrix, mMatrixSize, mPermute))
                {
                    Stop("Singular matrix!", null);
                    return;
                }
            }

            /* show resistance in voltage sources if there's only one */
            bool gotVoltageSource = false;

            ShowResistanceInVoltageSources = true;
            for (int i = 0; i != elmList.Count; i++)
            {
                var ce = mSim.getElm(i);
                if (ce is VoltageElm)
                {
                    if (gotVoltageSource)
                    {
                        ShowResistanceInVoltageSources = false;
                    }
                    else
                    {
                        gotVoltageSource = true;
                    }
                }
            }
        }