package stoppers; import java.lang.*; import java.io.*; import main.BetterTokenizer; import main.Cell; import main.GeneralInput; import main.Model; import main.MoreMath; public class OscillatorStop extends SimpleStop { float [] values = new float[3]; int numTroughs, numPeaks, checkCount; float sumTroughs, sumSqrTroughs, sumPeaks, sumSqrPeaks, sumPeakWavelength, sumSqrPeakWavelength; float lastPeakTime, lastHeight, lastPeakHeight, lastTroughHeight; int maxNumPeaks = 10; int nodeNum; int pos=0; // matches array index float interval=1f; float transientPeriod = 10; float minAmplitude = 0.05f; float tolerance = 0.001f; boolean noDamping = false, damping = false; float [] peakHeights = new float[maxNumPeaks]; float [] troughHeights = new float[maxNumPeaks]; // No arg constructor for instantiating by name public OscillatorStop() { stopTime = 1; } public OscillatorStop(float stop_time) { stopTime = stop_time; } public boolean stop(float time) { if(time < lastTime + interval) { return false; } lastTime = time; if(time < transientPeriod) return false; float nodeval = model.cells[pos].getValue(nodeNum); if(checkCount == 0) { values[1] = nodeval; checkCount++; return false; } else if(checkCount == 1) { values[2] = nodeval; checkCount++; return false; } values[0] = values[1]; values[1] = values[2]; values[2] = nodeval; if(values[0] > values[1] && values[2] > values[1]) // Hit a trough { if(Math.abs(values[1] - lastHeight) > minAmplitude) // Make sure the oscillation is big enough { System.out.println("Trough " + values[1] + " @ time=" + time); numTroughs++; sumTroughs += values[1]; sumSqrTroughs += values[1] * values[1]; lastTroughHeight = values[1]; troughHeights[numTroughs - 1] = values[1]; } lastHeight = values[1]; } else if(values[0] < values[1] && values[2] < values[1]) // Hit a peak { if(values[1] - lastHeight > minAmplitude) // Make sure the oscillation is big enough { System.out.println("Peak " + values[1] + " @ time=" + time); numPeaks++; sumPeaks += values[1]; sumSqrPeaks += values[1] * values[1]; if(numPeaks > 1) { sumPeakWavelength += time - lastPeakTime; sumSqrPeakWavelength += (time - lastPeakTime) * (time - lastPeakTime); } lastPeakTime = time; peakHeights[numPeaks - 1] = values[1]; } lastHeight = values[1]; } else if(values[0] == values[1] && values[1] == values[2]) // stable state? not oscillating so quit out { numPeaks = 0; time = stopTime + 1; // force quit below - kind of kludgy } if(numPeaks < 2 || numTroughs < 2) score = 10000; else { // Score is combination of how variable peak and trough heights are, and // how variable frequency is score = MoreMath.getStdDev(sumTroughs, sumSqrTroughs, numTroughs) / (sumTroughs / numTroughs); score += MoreMath.getStdDev(sumPeaks, sumSqrPeaks, numPeaks) / (sumPeaks / numPeaks); if(numPeaks > 2) score += MoreMath.getStdDev(sumPeakWavelength, sumSqrPeakWavelength, numPeaks - 1) / (sumPeakWavelength / (numPeaks - 1)); score *= 1 / (sumPeaks / numPeaks - sumTroughs / numTroughs); } if(noDamping && numPeaks > 2 && !checkDamping()) score += 10 * ( peakHeights[numPeaks - 2] - peakHeights[numPeaks - 1]); if(noDamping && numTroughs > 2 && !checkDamping()) score += 10 * ( troughHeights[numTroughs - 2] - troughHeights[numTroughs - 1]); if(numPeaks >= maxNumPeaks) return true; // Stop if we hit max peaks we want if(time > stopTime) return true; // Stop if passed max time return false; } public boolean didPass(float s) { return didPass(); } public boolean didPass() { if(score < Cutoff) return true; if(numPeaks < 2 || numTroughs < 2) return false; if(noDamping) return checkDamping(); return true; } /* Returns true if there is no damping (peaks reach same height), false if there is damping (peak heights decline over time). */ public boolean checkDamping() { // First check that the last peaks and trough are far enough apart if(peakHeights[numPeaks - 1] - lastTroughHeight < minAmplitude) return false; boolean good = false; // See if the last or second to last peak is higher than one of the previous ones // This hopefully, if there are several peaks, will work despite the integrator // reaching a different point on each peak for(int i = numPeaks - 2; i >= 0; i--) if(peakHeights[numPeaks - 1] > peakHeights[i] * (1 - tolerance)) good = true; for(int i = numPeaks - 3; i >= 0; i--) if(peakHeights[numPeaks - 2] > peakHeights[i] * (1 - tolerance)) good = true; if( !good ) return false; // Do same for troughs good = false; for(int i = numTroughs - 2; i >= 0; i--) if(troughHeights[numTroughs - 1] < troughHeights[i] * (1 + tolerance)) good = true; for(int i = numTroughs - 3; i >= 0; i--) if(troughHeights[numTroughs - 2] < troughHeights[i] * (1 + tolerance)) good = true; return good; } public void reset() { super.reset(); score = 0; numPeaks = numTroughs = 0; sumTroughs = sumSqrTroughs = 0; sumPeaks = sumSqrPeaks = 0; sumPeakWavelength = sumSqrPeakWavelength = 0; checkCount = 0; lastTime = -100; lastPeakTime = 0; lastHeight = -100; lastPeakHeight = -100; peakHeights = new float[maxNumPeaks]; } protected void loadParameter(String info, BetterTokenizer tokenizer) throws Exception { if(info.equals("Node")) { GeneralInput.nextToken(tokenizer); nodeNum = model.cells[0].getNodeNum(tokenizer.sval); } else if(info.equals("Position")) { GeneralInput.nextToken(tokenizer); pos = ((int)tokenizer.nval-1); } // subtract 1 to make pos an array index else if(info.equals("MaxNumPeaks")) { GeneralInput.nextToken(tokenizer); maxNumPeaks = (int)tokenizer.nval; } else if(info.equals("TransientPeriod")) { GeneralInput.nextToken(tokenizer); transientPeriod = (float)tokenizer.nval; } else if(info.equals("MinAmplitude")) { GeneralInput.nextToken(tokenizer); minAmplitude = (float)tokenizer.nval; } else if(info.equals("Tolerance")) { GeneralInput.nextToken(tokenizer); tolerance = (float)tokenizer.nval; } else if(info.equals("NoDamping")) { GeneralInput.nextToken(tokenizer); if(tokenizer.sval.toUpperCase().equals("Y")) noDamping = true; else noDamping = false; } else if(info.equals("Interval")) { GeneralInput.nextToken(tokenizer); interval = (float)tokenizer.nval; } else super.loadParameter(info,tokenizer); } }