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; } } } }