/*
// double well BifurcationPlot.java
/*
applet to construct bifurcation diagram for motion of particle
in double-well potential
*/
import java.awt.*;
import java.awt.event.*;
import java.lang.*;
import java.applet.Applet;
public class DWBifurcationPlot extends Applet implements ActionListener
{
DoubleWell dw1;
VariableSet tvars;
plotCanvas b; // canvas that draws bifurcation diagram
plotCanvas pp; //canvas for phase plot
calcThread calculate1; // suspendable thread for calculation loop.
double fmin, fmax, xmin, xmax; //bounding values of the bifurcation map
double pmax; //estimate of maximum momentum.
int transienttime = 100; // number of periods discarded
// before plotting
int width = 630, height = 440; // width and height of drawing canvas
Button startButton, stopButton, resetButton, switchPlot; // buttons to control flow
TextField forceTF; // text fields for user input of force,
TextField transienttimeTF; // number of drive periods to discard,
TextField fminTF, fmaxTF; // range of force plotted on canvas
TextField xminTF, xmaxTF; // range of x plotted on canvas
boolean frozen = false; // true if user has stopped the motion
public void init () {
dw1 = new DoubleWell(); // instantiate dynamical system object
//width = getWidth() - 20; height=getHeight() - 80; // set plot size
// set initial values of fmin, fmax, xmin, xmax:
fmin = 0.65; fmax = 0.69; xmin = -.7 ; xmax = -.3 ;
dw1.setforce(fmin + .01*(fmax-fmin));
b = new plotCanvas(createImage(width, height), fmin, fmax, xmin, xmax );
b.setBackground( Color.white ); // set white background
b.setHLabel("force"); b.setVLabel("x");
b.drawbox();
dw1.setdt(0.20); // set time step
dw1.setomega(2.*Math.PI/5.); // set drive frequency
b.ghostGraphics.drawString("Double well dynamics: bifurcation diagram and phase plot", 100, 70);
b.ghostGraphics.drawString("Equations of motion: "+dw1.getmass()+" dx/dt = p; ", 100, 110);
b.ghostGraphics.drawString("dp/dt = d/dx (4 x^3 - 2 ("+(dw1.getA()*dw1.getA())+") x) - "
+dw1.getdamping()+ " p/"+ dw1.getmass()+ " + " + dw1.getforce()+
" cos(" + (dw1.getomega()/Math.PI) + "pi t)", 100, 140);
b.ghostGraphics.drawString("Bifurcation plot makes a dot every driving cycle after waiting", 100, 170);
b.ghostGraphics.drawString("the number of cycles indicated below.", 100, 200);
b.ghostGraphics.drawString(" Fill in blanks, then hit 'new plot'; to shift force"+
"amplitude fill in new force and hit RETURN", 100, 230);
b.ghostGraphics.drawString("To add more dots, hit RETURN (with the force blank highlighted)", 100, 260);
b.ghostGraphics.drawString("To see phase plot, click the 'phase' button", 100, 290);
b.ghostGraphics.drawString("force = .7258 is especially interesting", 100, 320);
// make canvas for phase plot
pmax = 1.3*Math.sqrt(xmax*xmax + xmin*xmin) * dw1.getomega()* dw1.getmass();
pp = new plotCanvas(createImage(width, height), xmin, xmax, -pmax, pmax);
pp.setHLabel("x"); pp.setVLabel("p"); pp.drawbox();
// put the user interface components on the applet
Panel p = new Panel();
startButton = new Button("new plot");
startButton.addActionListener( this );
stopButton = new Button("Stop");
stopButton.addActionListener( this );
switchPlot = new Button("phase");
switchPlot.addActionListener(this);
resetButton = new Button("reset");
resetButton.addActionListener( this );
p.add( startButton );
p.add( stopButton );
p.add( switchPlot );
p.add( resetButton );
Panel p2 = new Panel();
Label transienttimeprompt = new Label("number of periods before plotting:");
transienttimeTF = new TextField (Integer.toString(transienttime), 5);
transienttimeTF.addActionListener( this );
p2.add( transienttimeprompt );
p2.add( transienttimeTF );
Panel p3 = new Panel();
p3.setLayout(new FlowLayout());
Label fprompt = new Label ("force, hit RETURN:");
forceTF = new TextField (6);
forceTF.setText(Double.toString(dw1.getforce()));
forceTF.addActionListener( this );
p3.add( fprompt );
p3.add( forceTF );
Panel p4 = new Panel();
Label fminprompt = new Label("fmin:");
fminTF = new TextField (5 );
fminTF.setText(Double.toString(fmin));
Label fmaxprompt = new Label("fmax:");
fmaxTF = new TextField (5 );
fmaxTF.setText(Double.toString(fmax));
p4.add( fminprompt );
p4.add( fminTF );
p4.add( fmaxprompt );
p4.add( fmaxTF );
Panel p5 = new Panel();
Label xminprompt = new Label("xmin:");
xminTF = new TextField (5 );
xminTF.setText(Double.toString(xmin));
Label xmaxprompt = new Label("xmax:");
xmaxTF = new TextField (5);
xmaxTF.setText(Double.toString(xmax));
p5.add( xminprompt );
p5.add( xminTF );
p5.add( xmaxprompt );
p5.add( xmaxTF );
add(b);
add(p2);
add(p3);
add(p4);
add(p5);
add(p);
} // end of init
public class calcThread extends Thread {
// thread to do the calculations and make the canvas image.
VariableSet vars, nextvars;
boolean die=false;
calcThread() { //constructor sets up initial values and dw1 parameters
setPriority(Thread.MIN_PRIORITY); die=false;
}
public void run() {
int n_var = 2, kount;
// double xvec[] = new double[n_var]; // xvec[0]=position, xvec[1]=velocity
double time;
showStatus("starting calcThread. frozen = " + frozen);
b.ghostGraphics.setColor(Color.white);
b.ghostGraphics.drawRect(0,0,width,1);
pp.ghostGraphics.setColor(Color.yellow);
for (kount=1; kount<200*25 && !die ; kount++) { //calculation loop
vars = dw1.getvars(); // get DoubleWell variables
nextvars = vars;
// Get new position
nextvars = dw1.nextvars();
time = nextvars.gettime();
b.ghostGraphics.drawRect(kount/10,0,0,1); b.repaint();
dw1.setvars(nextvars);
pp.addLine(vars.getx()[0], vars.getx()[1], nextvars.getx()[0], nextvars.getx()[1]);
pp.repaint();
vars = nextvars;
// discard first transienttime periods at new parameter values, and
// use fact that period is exactly 5 time units:
if( (time > 5*transienttime-.001) &&
( Math.abs( (time/5.)-Math.round(time/5.) ) < 0.01) ) {
b.ghostGraphics.setColor(Color.blue);
pp.ghostGraphics.setColor(Color.blue);
b.addPoint(dw1.getforce(), vars.getx()[0]); // put point (f,x) on canvas
b.repaint();
// showStatus("plotting...");
} //end of if
// try{sleep(5);} catch(InterruptedException e) {showStatus("interrupted sleep"); }
while(frozen && !die) {try {sleep(100); } catch(InterruptedException e) {showStatus("interrupted sleep");} }
} //end of calculation loop
pp.repaint();
showStatus("calcThread ends");
System.out.println("time, force, x: "+ vars.gettime() + ", "+ '\t'+
dw1.getforce()+", "+ '\t' + vars.getx()[0]);
} // end of run
} //end of calcThread
public void actionPerformed(ActionEvent evt) // handle user instructions
{ System.out.println(evt.paramString());
if(evt.getSource() == switchPlot) {
if(switchPlot.getLabel() == "phase") {
remove(b); add(pp, 0); validate(); switchPlot.setLabel("bifurc."); return;
}
else {remove(pp); add(b, 0); validate(); switchPlot.setLabel("phase"); return;}
}
if(calculate1 != null) {calculate1.die = true;
try {calculate1.join();} catch(InterruptedException e) {showStatus("dieing interrupted\007");}
//System.out.print(" threads= "+calculate1.activeCount() );
} // kill the thread.
if (evt.getSource() == stopButton) { frozen = true;} //suspend
else if (evt.getSource() == resetButton) { // clear plotting canvas
fmin = 0.65; fmax = 0.69; xmin = -.7 ; xmax = -.3 ; transienttime=100;
dw1.setforce(fmin + .01*(fmax-fmin));
fminTF.setText(Double.toString(fmin));
fmaxTF.setText(Double.toString(fmax));
xminTF.setText(Double.toString(xmin));
xmaxTF.setText(Double.toString(xmax));
forceTF.setText(Double.toString(dw1.getforce()));
transienttimeTF.setText(Integer.toString(transienttime));
b = new plotCanvas(createImage(width, height), fmin, fmax, xmin, xmax );
b.setBackground( Color.white ); // set white background
b.setHLabel("force"); b.setVLabel("x");
b.drawbox();
pmax = 1.3*Math.sqrt(xmax*xmax + xmin*xmin) * dw1.getomega()* dw1.getmass();
pp = new plotCanvas(createImage(width, height), xmin, xmax, -pmax, pmax);
pp.setHLabel("x"); pp.setVLabel("p"); pp.drawbox();
remove(0); add(b,0); switchPlot.setLabel("phase"); validate();
dw1.setvars( dw1.resetvars ); // reset variables
frozen=true;
}
else if(evt.getSource() == startButton) { //remake plotCanvas with new parameters
System.out.println( "getting new parameters..." );
String s = fminTF.getText(); // read string for fmin
fmin = new Double(s).doubleValue() ;
s = fmaxTF.getText(); // read string for fmax
fmax = new Double(s).doubleValue() ;
s = xminTF.getText(); // read string for xmin
xmin = new Double(s).doubleValue() ;
s = xmaxTF.getText(); // read string for xmax
xmax = new Double(s).doubleValue() ;
frozen=false;
b = new plotCanvas(createImage(width, height), fmin, fmax, xmin, xmax );
b.setBackground( Color.white ); // set white background
b.setHLabel("force"); b.setVLabel("x");
b.drawbox();
pmax = 1.3*Math.sqrt(xmax*xmax + xmin*xmin) * dw1.getomega()* dw1.getmass();
pp = new plotCanvas(createImage(width, height), xmin, xmax, -pmax, pmax);
pp.setHLabel("x"); pp.setVLabel("p"); pp.drawbox();
remove(0); add(b,0); switchPlot.setLabel("phase"); validate();
}
else frozen=false; // end of if else
// any event requires restarting new calculation
String s = forceTF.getText(); // read string for force
dw1.setforce(new Double(s).doubleValue() ); // convert to double
s = transienttimeTF.getText(); // read string for transienttime
transienttime = new Integer(s).intValue(); // convert to double
showStatus("setting up new calculation thread");
b.repaint();
calculate1 = new calcThread();
dw1.settime(0.);
calculate1.start();
} // end of action method
class plotCanvas extends Canvas { // canvas for plotting data points.
//set xmin, xmax, ymin, ymax to control range of plot.
// Draw on my ghostGraphics object or call addPoint(double x, double y) to add a point.
Graphics ghostGraphics; Image ghostImage;
double xwidth, yheight; // ranges of x and y on graph
double xmin, xmax, ymin, ymax; //bounding values of the graphed variables
int imin, iwidth, jmax, jheight; // pixel values
String HLabel = "x", VLabel = "y";
// constructor for plotCanvas:
plotCanvas(Image image, double xmin, double xmax, double ymin, double ymax) {
super(); // first call Canvas constructor
setSize(image.getWidth(null), image.getHeight(null));
setBackground( Color.white );
this.xmin=xmin; this.xmax=xmax; this.ymin=ymin; this.ymax=ymax;
ghostImage = image;
ghostGraphics = image.getGraphics();
xwidth = xmax-xmin; // horizontal width
yheight = ymax-ymin; //vertical height
// set imin, iwidth, jmin, jheight based on size of the canvas:
imin = 3*(getSize().width/20);
iwidth = getSize().width - (3*imin/2);
jmax = getSize().height - 2*(getSize().height/20);
jheight = getSize().height - 3*(getSize().height/20);
drawbox(ghostGraphics); // draw the box, numbers, and labels
} // end of plotCanvas constructor
int xtoi(double x) { // converts x to screen coord i
return Math.round( (float) (imin + iwidth*(x-xmin)/xwidth) );
}
int ytoj(double y) { // converts x to screen coord j
return Math.round( (float) (jmax - jheight*(y-ymin)/yheight) );
}
public void setHLabel(String HLabel) {this.HLabel = HLabel;}
public void setVLabel(String VLabel) {this.VLabel = VLabel;}
public void drawbox() {drawbox(ghostGraphics);}
public void drawbox(Graphics g) { // draw box, numbers, and labels for graph
g.setColor(Color.white);
g.fillRect(0,0,width,height);
g.setColor(Color.black);
g.drawRect(imin, jmax-jheight, iwidth, jheight);
g.drawString(Double.toString(xmin), imin-10, jmax+15);
g.drawString(Double.toString(xmax), imin+iwidth-10, jmax+15);
g.drawString(HLabel, imin+iwidth/2-20, jmax+15);
g.drawString(Double.toString(ymin), imin-30, jmax+5);
g.drawString(Double.toString(ymax), imin-30, jmax-jheight+5);
g.drawString(VLabel, imin-30, jmax-jheight/2+10);
} //end of drawbox
public void addPoint(double x, double y) {
ghostGraphics.drawRect(xtoi(x), ytoj( y ), 0, 0);
}
public void addLine(double x1, double y1, double x2, double y2) {
ghostGraphics.drawLine(xtoi(x1), ytoj(y1), xtoi(x2), ytoj(y2));
}
public void update(Graphics g) {paint(g);}
public void paint( Graphics g ) {
g.drawImage(ghostImage, 0, 0, null); //display the buffer image
}
} // end of Canvas
} // end of applet