public Connection Connect(CircuitElement left, int leftLeadNdx, CircuitElement right, int rightLeadNdx) { Lead leftlead = left.getLead(leftLeadNdx); Lead rightlead = right.getLead(rightLeadNdx); return(Connect(leftlead, rightlead)); }
public void panic(String why, CircuitElement elem) { circuitMatrix = null; _analyze = true; Debug.Log(why); throw new CircuitException(why, elem); }
public void AddElement(CircuitElement elm) { if (!elements.Contains(elm)) { elements.Add(elm); } }
public FindPathInfo(Circuit r, PathType t, CircuitElement e, int d) { sim = r; dest = d; type = t; firstElm = e; used = new bool[sim.nodeList.Count]; }
public List <ScopeFrame> Watch(CircuitElement component) { if (!scopeMap.ContainsKey(component)) { List <ScopeFrame> scope = new List <ScopeFrame>(); scopeMap.Add(component, scope); return(scope); } else { return(scopeMap[component]); } }
internal Lead(CircuitElement e, int i) { elem = e; ndx = i; Id = Circuit.snowflake.NextId(); }
public void analyze() { if (elements.Count == 0) { return; } nodeList.Clear(); #region 查找电源和接地元素 // Search the circuit for a Ground, or Voltage sourcre CircuitElement voltageElm = null; bool gotGround = false; bool gotRail = false; for (int i = 0; i != elements.Count; i++) { CircuitElement elem = elements[i]; if (elem is Ground) { gotGround = true; break; } if (elem is VoltageInput) //RailElm in Java { gotRail = true; } if (voltageElm == null && elem is Voltage) { voltageElm = elem; } } // If no ground and no rails, then the voltage elm's first terminal is ground. // 没有电源(单),没有接地,但是有电源(双),那么电源的零点为接地点 if (!gotGround && !gotRail && voltageElm != null) { CircuitNode cn = new CircuitNode(); cn.lead = voltageElm.getLead(0); nodeList.Add(cn); } else { // If the circuit contains a ground, rail, or voltage // element, push a temporary node to the node list. CircuitNode cn = new CircuitNode(); cn.lead = null; //Bin: 特殊节点,零点 nodeList.Add(cn); } #endregion // At this point, there is 1 node in the list, the special `global ground` node. #region 电路节点和电源节点 int vscount = 0; // Number of voltage sources for (int i = 0; i != elements.Count; i++) { CircuitElement ce = elements[i]; int leads = ce.getLeadCount(); for (int j = 0; j != leads; j++) { Lead lead = ce.getLead(j); int k; for (k = 0; k != nodeList.Count; k++) //查找现有的每个节点,判断是否相连 { //原Java版,这里用元器件的端点的坐标(XY)判断是否相连 //我们这里没有坐标,因此连接关系由Lead自己维护 CircuitNode cn = nodeList[k]; if (cn.lead != null) { FindConnection fc = new FindConnection(this, lead.Id); if (fc.IsConnected(cn.lead.Id)) { break; } } } if (k == nodeList.Count) //没有找到现有的节点 { CircuitNode cn = new CircuitNode(); cn.lead = lead; CircuitNodeLink cnl = new CircuitNodeLink(); cnl.lead_ndx = j; cnl.element = ce; cn.links.Add(cnl); ce.setLeadNode(j, nodeList.Count); nodeList.Add(cn); //这两行顺序不能颠倒 } else { CircuitNodeLink cnl = new CircuitNodeLink(); cnl.lead_ndx = j; cnl.element = ce; nodeList[k].links.Add(cnl); ce.setLeadNode(j, k); // if it's the ground node, make sure the node voltage is 0, // cause it may not get set later if (k == 0) { ce.setLeadVoltage(j, 0); } } } // Push an internal node onto the list for // each internal lead on the element. int internalLeads = ce.getInternalLeadCount(); for (int j = 0; j != internalLeads; j++) { CircuitNode cn = new CircuitNode(); cn.lead = null; cn.Internal = true; CircuitNodeLink cnl = new CircuitNodeLink(); cnl.lead_ndx = j + leads; cnl.element = ce; cn.links.Add(cnl); ce.setLeadNode(cnl.lead_ndx, nodeList.Count); nodeList.Add(cn); //这两行顺序不能颠倒 } vscount += ce.getVoltageSourceCount(); } #endregion // 创建电源节点数组 // 同时判断电路是否线性 // VoltageSourceId -> CircuitElement map voltageSources = new CircuitElement[vscount]; vscount = 0; circuitNonLinear = false; for (int i = 0; i != elements.Count; i++) { CircuitElement ce = elements[i]; if (ce.nonLinear()) { circuitNonLinear = true; } // Assign each votage source in the element a globally unique id, // (the index of the next open slot in voltageSources) for (int j = 0; j != ce.getVoltageSourceCount(); j++) { voltageSources[vscount] = ce; ce.setVoltageSource(j, vscount); vscount++; } } #region 创建矩阵 int matrixSize = nodeList.Count - 1 + vscount; // setup circuitMatrix circuitMatrix = new double[matrixSize][]; for (int z = 0; z < matrixSize; z++) { circuitMatrix[z] = new double[matrixSize]; } circuitRightSide = new double[matrixSize]; // setup origMatrix origMatrix = new double[matrixSize][]; for (int z = 0; z < matrixSize; z++) { origMatrix[z] = new double[matrixSize]; } origRightSide = new double[matrixSize]; // setup circuitRowInfo circuitRowInfo = new RowInfo[matrixSize]; for (int i = 0; i != matrixSize; i++) { circuitRowInfo[i] = new RowInfo(); } circuitPermute = new int[matrixSize]; circuitMatrixSize = circuitMatrixFullSize = matrixSize; circuitNeedsMap = false; #endregion // Stamp linear circuit elements. for (int i = 0; i != elements.Count; i++) { elements[i].stamp(this); } #region 查找连接不正确的节点 bool[] closure = new bool[nodeList.Count]; bool changed = true; closure[0] = true; while (changed) { changed = false; for (int i = 0; i != elements.Count; i++) { CircuitElement ce = elements[i]; // loop through all ce's nodes to see if they are connected // to other nodes not in closure for (int leadX = 0; leadX < ce.getLeadCount(); leadX++) { if (!closure[ce.getLeadNode(leadX)]) { if (ce.leadIsGround(leadX)) { closure[ce.getLeadNode(leadX)] = changed = true; } continue; } for (int k = 0; k != ce.getLeadCount(); k++) { if (leadX == k) { continue; } int kn = ce.getLeadNode(k); if (ce.leadsAreConnected(leadX, k) && !closure[kn]) { closure[kn] = true; changed = true; } } } } if (changed) { continue; } // connect unconnected nodes for (int i = 0; i != nodeList.Count; i++) { if (!closure[i] && !nodeList[i].Internal) { //System.out.println("node " + i + " unconnected"); stampResistor(0, i, 1E8); closure[i] = true; changed = true; break; } } } #endregion #region 电路正确性检查 for (int i = 0; i != elements.Count; i++) { CircuitElement ce = elements[i]; // look for inductors with no current path if (ce is InductorElm) { FindPathInfo fpi = new FindPathInfo(this, FindPathInfo.PathType.INDUCT, ce, ce.getLeadNode(1)); // first try findPath with maximum depth of 5, to avoid slowdowns if (!fpi.findPath(ce.getLeadNode(0), 5) && !fpi.findPath(ce.getLeadNode(0))) { //System.out.println(ce + " no path"); ce.reset(); } } // look for current sources with no current path if (ce is CurrentSource) { FindPathInfo fpi = new FindPathInfo(this, FindPathInfo.PathType.INDUCT, ce, ce.getLeadNode(1)); if (!fpi.findPath(ce.getLeadNode(0))) { panic("No path for current source!", ce); } } // look for voltage source loops if ((ce is Voltage && ce.getLeadCount() == 2) || ce is Wire) { FindPathInfo fpi = new FindPathInfo(this, FindPathInfo.PathType.VOLTAGE, ce, ce.getLeadNode(1)); if (fpi.findPath(ce.getLeadNode(0))) { panic("Voltage source/wire loop with no resistance!", ce); } } // look for shorted caps, or caps w/ voltage but no R if (ce is CapacitorElm) { FindPathInfo fpi = new FindPathInfo(this, FindPathInfo.PathType.SHORT, ce, ce.getLeadNode(1)); if (fpi.findPath(ce.getLeadNode(0))) { //System.out.println(ce + " shorted"); ce.reset(); } else { fpi = new FindPathInfo(this, FindPathInfo.PathType.CAP_V, ce, ce.getLeadNode(1)); if (fpi.findPath(ce.getLeadNode(0))) { panic("Capacitor loop with no resistance!", ce); } } } } #endregion #region 简化矩阵 这块会导致电池无法并联,先去掉 //for(int i = 0; i != matrixSize; i++) { // int qm = -1, qp = -1; // double qv = 0; // RowInfo re = circuitRowInfo[i]; // if(re.lsChanges || re.dropRow || re.rsChanges) // continue; // double rsadd = 0; // // look for rows that can be removed // int leadX = 0; // for(; leadX != matrixSize; leadX++) { // double q = circuitMatrix[i][leadX]; // if(circuitRowInfo[leadX].type == RowInfo.ROW_CONST) { // // keep a running total of const values that have been removed already // rsadd -= circuitRowInfo[leadX].value * q; // continue; // } // if(q == 0) // continue; // if(qp == -1) { // qp = leadX; // qv = q; // continue; // } // if(qm == -1 && q == -qv) { // qm = leadX; // continue; // } // break; // } // if(leadX == matrixSize) { // if(qp == -1) // panic("Matrix error", null); // RowInfo elt = circuitRowInfo[qp]; // if(qm == -1) { // // we found a row with only one nonzero entry; // // that value is a constant // for(int k = 0; elt.type == RowInfo.ROW_EQUAL && k < 100; k++) { // // follow the chain // // System.out.println("following equal chain from " + i + " " + qp + " to " + elt.nodeEq); // qp = elt.nodeEq; // elt = circuitRowInfo[qp]; // } // if(elt.type == RowInfo.ROW_EQUAL) { // // break equal chains // // System.out.println("Break equal chain"); // elt.type = RowInfo.ROW_NORMAL; // continue; // } // if(elt.type != RowInfo.ROW_NORMAL) { // //System.out.println("type already " + elt.type + " for " + qp + "!"); // continue; // } // elt.type = RowInfo.ROW_CONST; // elt.value = (circuitRightSide[i] + rsadd) / qv; // circuitRowInfo[i].dropRow = true; // // System.out.println(qp + " * " + qv + " = const " + elt.value); // i = -1; // start over from scratch // } else if(circuitRightSide[i] + rsadd == 0) { // // we found a row with only two nonzero entries, and one // // is the negative of the other; the values are equal // if(elt.type != RowInfo.ROW_NORMAL) { // // System.out.println("swapping"); // int qq = qm; // qm = qp; // qp = qq; // elt = circuitRowInfo[qp]; // if(elt.type != RowInfo.ROW_NORMAL) { // // we should follow the chain here, but this hardly // // ever happens so it's not worth worrying about // //System.out.println("swap failed"); // continue; // } // } // elt.type = RowInfo.ROW_EQUAL; // elt.nodeEq = qm; // circuitRowInfo[i].dropRow = true; // // System.out.println(qp + " = " + qm); // } // } //} // == Find size of new matrix int nn = 0; for (int i = 0; i != matrixSize; i++) { RowInfo elt = circuitRowInfo[i]; if (elt.type == RowInfo.ROW_NORMAL) { elt.mapCol = nn++; // System.out.println("col " + i + " maps to " + elt.mapCol); continue; } if (elt.type == RowInfo.ROW_EQUAL) { RowInfo e2 = null; // resolve chains of equality; 100 max steps to avoid loops for (int leadX = 0; leadX != 100; leadX++) { e2 = circuitRowInfo[elt.nodeEq]; if (e2.type != RowInfo.ROW_EQUAL) { break; } if (i == e2.nodeEq) { break; } elt.nodeEq = e2.nodeEq; } } if (elt.type == RowInfo.ROW_CONST) { elt.mapCol = -1; } } for (int i = 0; i != matrixSize; i++) { RowInfo elt = circuitRowInfo[i]; if (elt.type == RowInfo.ROW_EQUAL) { RowInfo e2 = circuitRowInfo[elt.nodeEq]; if (e2.type == RowInfo.ROW_CONST) { // if something is equal to a const, it's a const elt.type = e2.type; elt.value = e2.value; elt.mapCol = -1; } else { elt.mapCol = e2.mapCol; } } } // == Make the new, simplified matrix. int newsize = nn; double[][] newmatx = new double[newsize][]; for (int z = 0; z < newsize; z++) { newmatx[z] = new double[newsize]; } double[] newrs = new double[newsize]; int ii = 0; for (int i = 0; i != matrixSize; i++) { RowInfo rri = circuitRowInfo[i]; if (rri.dropRow) { rri.mapRow = -1; continue; } newrs[ii] = circuitRightSide[i]; rri.mapRow = ii; // System.out.println("Row " + i + " maps to " + ii); for (int leadX = 0; leadX != matrixSize; leadX++) { RowInfo ri = circuitRowInfo[leadX]; if (ri.type == RowInfo.ROW_CONST) { newrs[ii] -= ri.value * circuitMatrix[i][leadX]; } else { newmatx[ii][ri.mapCol] += circuitMatrix[i][leadX]; } } ii++; } #endregion #region //// Copy matrix to orig //// circuitMatrix = newmatx; circuitRightSide = newrs; matrixSize = circuitMatrixSize = newsize; // copy `rightSide` to `origRightSide` for (int i = 0; i != matrixSize; i++) { origRightSide[i] = circuitRightSide[i]; } // copy `matrix` to `origMatrix` for (int i = 0; i != matrixSize; i++) { for (int leadX = 0; leadX != matrixSize; leadX++) { origMatrix[i][leadX] = circuitMatrix[i][leadX]; } } #endregion circuitNeedsMap = true; _analyze = false; // If the matrix is linear, we can do the lu_factor // here instead of needing to do it every frame. if (!circuitNonLinear) { if (!lu_factor(circuitMatrix, circuitMatrixSize, circuitPermute)) { panic("Singular matrix!", null); } } }
public CircuitException(string why, CircuitElement elem) : base(why) { element = elem; }
public bool findPath(int n1, int depth) { if (n1 == dest) { return(true); } if (depth-- == 0) { return(false); } if (used[n1]) { return(false); } used[n1] = true; for (int i = 0; i != sim.elements.Count; i++) { CircuitElement ce = sim.elements[i]; if (ce == firstElm) { continue; } if (type == PathType.INDUCT) { if (ce is CurrentSource) { continue; } } if (type == PathType.VOLTAGE) { if (!(ce.isWire() || ce is Voltage)) { continue; } } if (type == PathType.SHORT && !ce.isWire()) { continue; } if (type == PathType.CAP_V) { if (!(ce.isWire() || ce is CapacitorElm || ce is Voltage)) { continue; } } if (n1 == 0) { // look for posts which have a ground connection; // our path can go through ground for (int z = 0; z != ce.getLeadCount(); z++) { if (ce.leadIsGround(z) && findPath(ce.getLeadNode(z), depth)) { used[n1] = false; return(true); } } } int j; for (j = 0; j != ce.getLeadCount(); j++) { if (ce.getLeadNode(j) == n1) { break; } } if (j == ce.getLeadCount()) { continue; } if (ce.leadIsGround(j) && findPath(0, depth)) { // System.out.println(ce + " has ground"); used[n1] = false; return(true); } if (type == PathType.INDUCT && ce is InductorElm) { double c = ce.getCurrent(); if (j == 0) { c = -c; } // System.out.println("matching " + c + " to " + firstElm.getCurrent()); // System.out.println(ce + " " + firstElm); if (Math.Abs(c - firstElm.getCurrent()) > 1e-10) { continue; } } for (int k = 0; k != ce.getLeadCount(); k++) { if (j == k) { continue; } // System.out.println(ce + " " + ce.getNode(j) + "-" + ce.getNode(k)); if (ce.leadsAreConnected(j, k) && findPath(ce.getLeadNode(k), depth)) { // System.out.println("got findpath " + n1); used[n1] = false; return(true); } // System.out.println("back on findpath " + n1); } } used[n1] = false; // System.out.println(n1 + " failed"); return(false); }