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 OscillatorStopSpecial extends SimpleStop { float [] values = new float[3]; float [] valuescell2 = new float[3]; int numTroughs, numPeaks, checkCount; float sumTroughs, sumSqrTroughs, sumPeaks, sumSqrPeaks, sumPeakWavelength, sumSqrPeakWavelength; float lastPeakTime, lastHeight, lastPeakHeight, lastTroughHeight; int numTroughs2, numPeaks2; float sumTroughs2, sumSqrTroughs2, sumPeaks2, sumSqrPeaks2, sumPeakWavelength2, sumSqrPeakWavelength2; float lastPeakTime2, lastHeight2, lastPeakHeight2, lastTroughHeight2; 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 [] peakHeights2 = new float[maxNumPeaks]; float [] troughHeights = new float[maxNumPeaks]; float [] troughHeights2 = new float[maxNumPeaks]; boolean printPeaks = false; // No arg constructor for instantiating by name public OscillatorStopSpecial() { stopTime = 1; } public OscillatorStopSpecial(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); float nodeval2 = model.cells[pos+1].getValue(nodeNum); if(checkCount == 0) { values[1] = nodeval; valuescell2[1] = nodeval2; checkCount++; return false; } else if(checkCount == 1) { values[2] = nodeval; valuescell2[2] = nodeval2; checkCount++; return false; } values[0] = values[1]; values[1] = values[2]; values[2] = nodeval; valuescell2[0] = valuescell2[1]; valuescell2[1] = valuescell2[2]; valuescell2[2] = nodeval2; 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 { if(printPeaks) 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 { if(printPeaks) 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(valuescell2[0] > valuescell2[1] && valuescell2[2] > valuescell2[1]) // Hit a trough { if(Math.abs(valuescell2[1] - lastHeight2) > minAmplitude) // Make sure the oscillation is big enough { if(printPeaks) System.out.println("Trough2 " + valuescell2[1] + " @ time=" + time); numTroughs2++; sumTroughs2 += valuescell2[1]; sumSqrTroughs2 += valuescell2[1] * valuescell2[1]; lastTroughHeight2 = valuescell2[1]; troughHeights2[numTroughs2 - 1] = valuescell2[1]; } lastHeight2 = valuescell2[1]; } else if(valuescell2[0] < valuescell2[1] && valuescell2[2] < valuescell2[1]) // Hit a peak { if(valuescell2[1] - lastHeight2 > minAmplitude) // Make sure the oscillation is big enough { if(printPeaks) System.out.println("Peak2 " + valuescell2[1] + " @ time=" + time); numPeaks2++; sumPeaks2 += valuescell2[1]; sumSqrPeaks2 += valuescell2[1] * valuescell2[1]; if(numPeaks2 > 1) { sumPeakWavelength2 += time - lastPeakTime2; sumSqrPeakWavelength2 += (time - lastPeakTime2) * (time - lastPeakTime2); } lastPeakTime2 = time; peakHeights2[numPeaks2 - 1] = valuescell2[1]; } lastHeight2 = valuescell2[1]; } else if(valuescell2[0] == valuescell2[1] && valuescell2[1] == valuescell2[2]) // stable state? not oscillating so quit out { numPeaks2 = 0; time = stopTime + 1; // force quit below - kind of kludgy } if(numPeaks < 2 || numTroughs < 2) score = 10000; else { score = MoreMath.getStdDev(sumPeaks, sumSqrPeaks, numPeaks) / (sumPeaks / numPeaks); if(numPeaks > 2) score -= (sumPeakWavelength / (numPeaks - 1) - sumPeakWavelength2 / (numPeaks2 - 1)) / stopTime; } if(noDamping && numPeaks > 2 && !checkDamping(peakHeights, numPeaks, troughHeights, numTroughs, lastTroughHeight)) { if(peakHeights[numPeaks - 2] > peakHeights[numPeaks - 1]) score += 10 * ( peakHeights[numPeaks - 2] - peakHeights[numPeaks - 1]); else score += 1; } if(noDamping && numTroughs > 2 && !checkDamping(peakHeights, numPeaks, troughHeights, numTroughs, lastTroughHeight)) { if(troughHeights[numTroughs - 2] < troughHeights[numTroughs - 1]) score += 10 * ( troughHeights[numTroughs - 2] - troughHeights[numTroughs - 1]); else score += 1; } if(noDamping && numPeaks2 > 2 && !checkDamping(peakHeights2, numPeaks2, troughHeights2, numTroughs2, lastTroughHeight2)) { if(peakHeights2[numPeaks2 - 2] > peakHeights2[numPeaks2 - 1]) score += 10 * ( peakHeights2[numPeaks2 - 2] - peakHeights2[numPeaks2 - 1]); else score += 1; } if(noDamping && numTroughs2 > 2 && !checkDamping(peakHeights2, numPeaks2, troughHeights2, numTroughs2, lastTroughHeight2)) { if(troughHeights2[numTroughs2 - 2] < troughHeights2[numTroughs2 - 1]) score += 10 * ( troughHeights2[numTroughs2 - 2] - troughHeights2[numTroughs2 - 1]); else score += 1; } // Check that didn't run out of peaks (indicating damping) if(numPeaks >= 2 && time - lastPeakTime > sumPeakWavelength / (numPeaks - 1) * 1.1) score += time - lastPeakTime - sumPeakWavelength / (numPeaks - 1); if(numPeaks2 >= 2 && time - lastPeakTime2 > sumPeakWavelength2 / (numPeaks2 - 1) * 1.1) score += time - lastPeakTime2 - sumPeakWavelength2 / (numPeaks2 - 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 && !checkDamping(peakHeights, numPeaks, troughHeights, numTroughs, lastTroughHeight)) return false; if(noDamping && !checkDamping(peakHeights2, numPeaks2, troughHeights2, numTroughs2, lastTroughHeight2)) return false; 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(float [] peakheights, int numpeaks, float [] troughheights, int numtroughs, float lasttroughheight) { // 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; numPeaks2 = numTroughs2 = 0; sumTroughs2 = sumSqrTroughs2 = 0; sumPeaks2 = sumSqrPeaks2 = 0; sumPeakWavelength2 = sumSqrPeakWavelength2 = 0; checkCount = 0; lastTime = -100; lastPeakTime = 0; lastHeight = -100; lastPeakHeight = -100; peakHeights = new float[maxNumPeaks]; lastPeakTime2 = 0; lastHeight2 = -100; lastPeakHeight2 = -100; peakHeights2 = 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("PrintPeaks")) { GeneralInput.nextToken(tokenizer); if(tokenizer.sval.toUpperCase().equals("Y")) printPeaks = true; else printPeaks = false; } else if(info.equals("Interval")) { GeneralInput.nextToken(tokenizer); interval = (float)tokenizer.nval; } else super.loadParameter(info,tokenizer); } }