/* Copyright (C) Paul Falstad This file is part of CircuitJS1. CircuitJS1 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. CircuitJS1 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with CircuitJS1. If not, see <http://www.gnu.org/licenses/>. */ package com.lushprojects.circuitjs1.client; //import java.awt.*; //import java.util.StringTokenizer; class VCCSElm extends ChipElm { double gain; int inputCount; Expr expr; ExprState exprState; String exprString; public boolean broken; public VCCSElm(int xa, int ya, int xb, int yb, int f, StringTokenizer st) { super(xa, ya, xb, yb, f, st); inputCount = Integer.parseInt(st.nextToken()); exprString = CustomLogicModel.unescape(st.nextToken()); parseExpr(); setupPins(); } public VCCSElm(int xx, int yy) { super(xx, yy); inputCount = 2; exprString = ".1*(a-b)"; parseExpr(); setupPins(); } String dump() { return super.dump() + " " + inputCount + " " + CustomLogicModel.escape(exprString); } double lastVolts[]; void setupPins() { sizeX = 2; sizeY = inputCount > 2 ? inputCount : 2; pins = new Pin[inputCount+2]; int i; for (i = 0; i != inputCount; i++) pins[i] = new Pin(i, SIDE_W, Character.toString((char)('A'+i))); pins[inputCount] = new Pin(0, SIDE_E, "C+"); pins[inputCount+1] = new Pin(1, SIDE_E, "C-"); lastVolts = new double[inputCount]; exprState = new ExprState(inputCount); } String getChipName() { return "VCCS"; } boolean nonLinear() { return true; } void stamp() { sim.stampNonLinear(nodes[inputCount]); sim.stampNonLinear(nodes[inputCount+1]); } double lastvd; double sign(double a, double b) { return a > 0 ? b : -b; } double getLimitStep() { // get limit on changes in voltage per step. be more lenient the more iterations we do if (sim.subIterations < 4) return 10; if (sim.subIterations < 10) return 1; if (sim.subIterations < 20) return .1; if (sim.subIterations < 40) return .01; return .001; } double getConvergeLimit() { // get maximum change in voltage per step when testing for convergence. be more lenient over time if (sim.subIterations < 10) return .001; if (sim.subIterations < 200) return .01; return .1; } boolean hasCurrentOutput() { return true; } int getOutputNode(int n) { return nodes[n+inputCount]; } void doStep() { int i; // no current path? give up if (broken) { pins[inputCount].current = 0; pins[inputCount+1].current = 0; // avoid singular matrix errors sim.stampResistor(nodes[inputCount], nodes[inputCount+1], 1e8); return; } // converged yet? double limitStep = getLimitStep(); double convergeLimit = getConvergeLimit(); for (i = 0; i != inputCount; i++) { if (Math.abs(volts[i]-lastVolts[i]) > convergeLimit) sim.converged = false; if (Double.isNaN(volts[i])) volts[i] = 0; if (Math.abs(volts[i]-lastVolts[i]) > limitStep) volts[i] = lastVolts[i] + sign(volts[i]-lastVolts[i], limitStep); } if (expr != null) { // calculate output for (i = 0; i != inputCount; i++) exprState.values[i] = volts[i]; exprState.t = sim.t; double v0 = -expr.eval(exprState); // if (Math.abs(volts[inputCount]-v0) > Math.abs(v0)*.01 && sim.subIterations < 100) // sim.converged = false; double rs = v0; // calculate and stamp output derivatives for (i = 0; i != inputCount; i++) { double dv = 1e-6; exprState.values[i] = volts[i]+dv; double v = -expr.eval(exprState); exprState.values[i] = volts[i]-dv; double v2 = -expr.eval(exprState); double dx = (v-v2)/(dv*2); if (Math.abs(dx) < 1e-6) dx = sign(dx, 1e-6); sim.stampVCCurrentSource(nodes[inputCount], nodes[inputCount+1], nodes[i], 0, dx); // sim.console("ccedx " + i + " " + dx); // adjust right side rs -= dx*volts[i]; exprState.values[i] = volts[i]; } // sim.console("ccers " + rs); sim.stampCurrentSource(nodes[inputCount], nodes[inputCount+1], rs); pins[inputCount].current = -v0; pins[inputCount+1].current = v0; } for (i = 0; i != inputCount; i++) lastVolts[i] = volts[i]; } void draw(Graphics g) { drawChip(g); } int getPostCount() { return inputCount+2; } int getVoltageSourceCount() { return 0; } int getDumpType() { return 213; } boolean getConnection(int n1, int n2) { return comparePair(inputCount, inputCount+1, n1, n2); } boolean hasGroundConnection(int n1) { return false; } public EditInfo getEditInfo(int n) { if (n == 0) { EditInfo ei = new EditInfo("<a href=\"customfunction.html\" target=\"_blank\">Output Function</a>", 0, -1, -1); ei.text = exprString; return ei; } if (n == 1) return new EditInfo("# of Inputs", inputCount, 1, 8). setDimensionless(); return null; } public void setEditValue(int n, EditInfo ei) { if (n == 0) { exprString = ei.textf.getText(); parseExpr(); return; } if (n == 1) { if (ei.value < 0 || ei.value > 8) return; inputCount = (int) ei.value; setupPins(); allocNodes(); setPoints(); } } void parseExpr() { ExprParser parser = new ExprParser(exprString); expr = parser.parseExpression(); } void getInfo(String arr[]) { super.getInfo(arr); int i; for (i = 0; arr[i] != null; i++) ; arr[i] = "I = " + getCurrentText(pins[inputCount].current); } }