/* Quantum Harmonic Oscillator originally by Ondrej Psencik Modified 2000 by L. Kocbach - and many times afterwords Modified most recently by L. Kocbach, July 2010 now on the occasion of prof. Glauber's visit to Bergen 2010 changes: 1. removed the bugs caused by changed jave scrollbars ('visible') (this was not bringing the zero amplitudes) 2. normalization is completely consistent in all states 3. The applet now starts in active state - and can be stopped Earlier changes (last in 2005): Modified and extended 2002 by L. Kocbach and Nazila Yavari Modified and reorganized 2003 by L. Kocbach Modified on the occasion of Glauber's Nobel Prize by L. Kocbach, October 2005 This is a new documented version with many states Indicator of Glauber/Superposed added October 2005 There are 4 classes: class Glauber2010 extends Applet class SimPanel extends Panel class ScrollerArea extends Panel class ThreadOsc extends Thread */ import java.applet.Applet; import java.awt.*; import java.awt.event.*; import java.lang.System.*; import java.lang.Math.*; import java.lang.*; import java.lang.Thread; import java.text.NumberFormat; import java.math.*; // ////////////////////////// /////////////////////////////////////////////// // // class Glauber // public class Glauber2010 extends Applet implements ActionListener, AdjustmentListener { int width=640; int height=400; SimPanel PERFORM; ScrollerArea CONTROLS; Button RunHO; Button plus,minus,back,ahead, ResetHO; Label descrip1,descrip2, descrip3; ThreadOsc threadd; int Running=0; Scrollbar speed, scrldt; // Color simColor=new Color(220,220,100); // Animation Panel NEWCOLOR Color BckgMainColor=new Color(255,255,192); public void init() { resize(width,height); setLayout(null); setBackground(BckgMainColor); Font f=new Font("Helvetica",Font.PLAIN,12); setFont(f); CONTROLS=new ScrollerArea(10,10,this); add(CONTROLS); PERFORM=new SimPanel(380,10,createImage(250,300),CONTROLS); add(PERFORM); back=new Button("<"); add(back); back.setSize(50,30); back.setLocation(360+40,320); back.addActionListener(this); RunHO=new Button("Run"); add(RunHO); RunHO.setSize(50,30); RunHO.setLocation(420+40,320); RunHO.addActionListener(this); ResetHO=new Button("t=0"); // Reset add(ResetHO); ResetHO.setSize(50,30); ResetHO.setLocation(480+40,320); ResetHO.addActionListener(this); ahead=new Button(">"); add(ahead); ahead.setSize(50,30); ahead.setLocation(540+40,320); ahead.addActionListener(this); descrip1=new Label("Time Step:"); add(descrip1); descrip1.setSize(descrip1.getMinimumSize()); descrip1.setLocation(20,316); speed=new Scrollbar(Scrollbar.HORIZONTAL,90,1,1,100); speed.setSize(100,15); speed.setLocation(130,340); add(speed); speed.addAdjustmentListener(this); scrldt=new Scrollbar(Scrollbar.HORIZONTAL,30,1,1,50); scrldt.setSize(100,15); scrldt.setLocation(20,340); add(scrldt); PERFORM.dt=0.001*30; scrldt.addAdjustmentListener(new AdjustmentListener(){ public void adjustmentValueChanged(AdjustmentEvent e) { PERFORM.dt=0.001*e.getValue(); } }); descrip3=new Label("Speed of animation"); add(descrip3); descrip3.setSize(descrip3.getMinimumSize()); descrip3.setLocation(130,316); PERFORM.CALCULATE(); RunHO.setVisible(true); // The applet was originally stopped at the start; // This is now changed by using the old callback of the start button threadd=new ThreadOsc(PERFORM); threadd.start(); Running=1; back.setVisible(false); ahead.setVisible(false); RunHO.setLabel("Stop"); repaint(); } public void adjustmentValueChanged(AdjustmentEvent e) { PERFORM.thrdsleep=105-e.getValue(); } public void paint(Graphics g) { g.drawRoundRect(1,1,width-2,height-2,30,30); } public void actionPerformed(ActionEvent e) { if(e.getActionCommand()=="Run"){ if(Running==0){ threadd=new ThreadOsc(PERFORM); threadd.start(); Running=1; back.setVisible(false); ahead.setVisible(false); RunHO.setLabel("Stop"); } } else if(e.getActionCommand()=="t=0") // Reset { PERFORM.t=0.0; repaint(); // if(RunHO.getLabel()=="Run") PERFORM.CALCULATE(); } else if(e.getActionCommand()=="Stop") { if(Running==1){ if (threadd!=null) threadd.stop(); threadd=null; back.setVisible(true); ahead.setVisible(true); RunHO.setLabel("Run"); repaint(); Running=0; if (threadd!=null) while(threadd.isAlive()); PERFORM.CALCULATE(); } } else if(e.getActionCommand()==">"){ PERFORM.t+=PERFORM.dt; repaint(); PERFORM.CALCULATE(); } else if(e.getActionCommand()=="<"){ PERFORM.t-=PERFORM.dt; repaint(); PERFORM.CALCULATE(); } } } // ////////////////////////// /////////////////////////////////////////////// // // Class Simulation panel // class SimPanel extends Panel { Image MyBuffer; Graphics MyGr,OldGr; MediaTracker mt; // Class Simulation panel // It contains all the mathematical functions // It knows as well about the selection area sel Color simColor=new Color(255,255,212); // Animation Panel NEWCOLOR Color grafCOLOR=Color.black; int thrdsleep=10; int GlauberFlag=1; int Nstate; int width=250; int height=300; int mult=20; //multiplicative factor for the axis double Xdiff=210; double dt=0.001; double d=1; double t=1; double fixt,wtx; double AverageEnerg; ScrollerArea CONTROLS; int [] aryN=new int[11]; double [] aryCn=new double[31]; // MOVED double [] rawCn=new double[11]; double [] alfan=new double[31]; double [] nfact=new double[31]; int [][] StraightLines=new int[3][11]; double [][]PsiArray=new double[212][31]; int []parab=new int[212]; double Scale=104.0;//21.0; double ScaleParab=21;//1.25*21; double HALFd=5; int HalfSize=21; double NormFact; public SimPanel(int x, int y, Image i, ScrollerArea sel) // Simulation Panel Creator { // This is the creator of simulation panel CONTROLS=sel; setSize(width,height); setLocation(x,y); mt=new MediaTracker(this); MyBuffer = i; mt.addImage(MyBuffer,1); try {mt.waitForAll();} catch(Exception e) {}; MyGr=MyBuffer.getGraphics(); MyGr.setColor(simColor); MyGr.fillRoundRect(0,0,250,300,20,20); // // Fill the coefficien array used in functions PSI_n_form() // Nstate=25; for(int j=1;j<31;j++){ alfan[j]=fan(j-1); } nfact[1] = 1.0; for(int j=2;j<31;j++){ nfact[j]=nfact[j-1]*(double)(j-1); } double HALFd=5; int HalfSize=21; double py; // // Filling the wavefunction vectors // for all the points // // Note: point -> x (lpoint-1.0)/HalfSize-HALFd // for(int lpoint=1; lpoint<212; lpoint+=1) { for(int jquant=1; jquant0.0){ Re+=Math.cos((i-1+0.5)*t)*Cn*PsiArray[xx+1][i]; // PSI_n_form(x,i-1); // *PsiArray[xx+1][i]; Im+=Math.sin((i-1+0.5)*t)*Cn*PsiArray[xx+1][i]; // PSI_n_form(x,i-1); // *PsiArray[xx+1][i]; } } wtx=Scale*(Re*Re+Im*Im); } // Amplitude controls start // Normalize amplitudes in ConvertAmplitudes() // and in singleConvertAmplitudes() public void Normalize() { double arySum; double aryFact; if (GlauberFlag==0) { CONTROLS.L_Superp.setText(" Superposed State"); // GlauberFlag == 0 CONTROLS.L_Glaub.setText("--"); // // } if (GlauberFlag==1) { CONTROLS.L_Glaub.setText(" Glauber State "); // GlauberFlag == 1 CONTROLS.L_Superp.setText("--"); // } if (GlauberFlag==-1) { CONTROLS.L_Superp.setText(" Single State"); // GlauberFlag == 0 CONTROLS.L_Glaub.setText("--"); // } if (GlauberFlag<1) { for(int i=1;i<=Nstate;i++) { aryCn[i]=0.0; } } arySum=(double) 0.000000000001; for(int i=1;i<=10;i++) { arySum=arySum+rawCn[i]*rawCn[i]; } aryFact= (double)(1.00)/Math.sqrt(arySum); for(int i=1;i<=10;i++) { aryCn[i]=rawCn[i]*aryFact; rawCn[i]=rawCn[i]*aryFact; } // Evaluate AverageEnerg AverageEnerg=(double) 0.5; for(int i=1;i<=10;i++) { AverageEnerg=AverageEnerg + (double)(i-1)*aryCn[i]*aryCn[i]; } } // Setting of amplitudes by scrollers public void ConvertAmplitudes() { double arySum; double aryFact; GlauberFlag=0; // CONTROLS.L_Superp.setText(" Superposed State"); // GlauberFlag == 0 // CONTROLS.L_Glaub.setText("--"); // // CONTROLS.L_Glaub.setText(" Glauber State "); // GlauberFlag == 1 // CONTROLS.L_Superp.setText("--"); // for(int whichstate=1;whichstate<=10;whichstate++) { if (CONTROLS.AmplitScroll[whichstate].getValue()>95) rawCn[whichstate]=0; else rawCn[whichstate]=(double)(100-CONTROLS.AmplitScroll[whichstate].getValue())/100; } Normalize(); for(int whichstate=1;whichstate<=10;whichstate++) { rawCn[whichstate]=100*aryCn[whichstate]; CONTROLS.AmplitScroll[whichstate].setValue(100-(int)rawCn[whichstate]); } CONTROLS.EnergScroll.setValue( (int)( 100-(100/10)*(AverageEnerg-0.5) ) ); // error here, paranthesis } // Setting of amplitudes for a single state by buttons public void singleConvertAmplitudes(int Nvalue) // Single State Button Function { double arySum; double aryFact; GlauberFlag=-1; // CONTROLS.L_Superp.setText(" Single State"); // GlauberFlag == 0 // CONTROLS.L_Glaub.setText("--"); // // CONTROLS.L_Glaub.setText(" Glauber State "); // GlauberFlag == 1 // CONTROLS.L_Superp.setText("--"); // for(int whichstate=1;whichstate<=10;whichstate++) { rawCn[whichstate]=0.0; } rawCn[Nvalue]=1.0; Normalize(); for(int whichstate=1;whichstate<=10;whichstate++) { rawCn[whichstate]=100*aryCn[whichstate]; CONTROLS.AmplitScroll[whichstate].setValue(100-(int)rawCn[whichstate]); } CONTROLS.EnergScroll.setValue( (int)( CONTROLS.t_maximum -(100/10)*(AverageEnerg-0.5) ) ); } // Setting of amplitudes for Glauber State by energy selector scroller public void CoherentAmplitudes(int Nvalue) { double arySum; double aryFact; double Xx; double Xx2; GlauberFlag=1; // CONTROLS.L_Superp.setText(" Superposed State"); // GlauberFlag == 0 // CONTROLS.L_Glaub.setText("--"); // CONTROLS.L_Glaub.setText(" Glauber State "); // GlauberFlag == 1 CONTROLS.L_Superp.setText("--"); // // This must be reversed here: CONTROLS.EnergScroll.setValue( (int)( 100-(100/10)*(AverageEnerg-0.5) ) ); AverageEnerg=0.5+0.0000000001+(10.0-0.1*(double)(Nvalue) ); // SETENERGY Xx=Math.sqrt(AverageEnerg-0.5); for(int ist=1;ist<=Nstate;ist++) { // Matlab values // for N=2:10,fac(N)=fac(N-1)*(N-1);end // for N=0:9,coef(N+1)=x^N/sqrt(fac(N+1))*exp(-0.5*x^2);end Xx2=0.5*Math.pow(Xx,2); aryCn[ist]=Math.pow(Xx,ist-1)/Math.sqrt(nfact[ist]) *Math.exp(-Xx2); } for(int i=1;i<=10;i++) { rawCn[i]=aryCn[i]; } for(int whichstate=1;whichstate<=10;whichstate++) { rawCn[whichstate]=100*aryCn[whichstate]; CONTROLS.AmplitScroll[whichstate].setValue(CONTROLS.t_maximum -(int)rawCn[whichstate]); } } // Amplitude controls end public void CALCULATE() { int x,y,sx,sy, EnerPos; Color ParabolaColor=new Color(198,198,20); // Harmonic Oscillator NEWCOLOR MyGr.setColor(simColor); MyGr.fillRoundRect(0,0,250,300,20,20); MyGr.setColor(Color.black); MyGr.drawRoundRect(1,1,247,297,20,20); // rectangle ANIMATION EnerPos = (int)(AverageEnerg*ScaleParab); // Convert energy to position PSI(t,0,0); x=20; y=(int)(wtx); // // Drawing the line of the wavefunction in 210 points // for(int i=21; i<230; i+=1) { sx=x; sy=y; PSI(t,(i-21.0)/HalfSize-HALFd,i-21); x=i; y=(int)(wtx);//*mult); MyGr.drawLine(sx,280-EnerPos-sy,x,280-EnerPos-y); } // // Drawing the oscillator levels // MyGr.setColor(ParabolaColor); for(int j=10;j<201;j++) { MyGr.drawLine(j+19,280-parab[j],j+20,280-parab[j+1]); } MyGr.setColor(ParabolaColor); for(int j=0;j<10;j++) { int yy=(int)((j+0.5)*ScaleParab); MyGr.drawLine(StraightLines[1][j+1]+1,280-yy,StraightLines[2][j+1]-1,280-yy); MyGr.drawString(Integer.toString(j),230,280-yy); } repaint(); } public void update(Graphics g) { paint(g); } public void paint(Graphics g) { g.drawImage(MyBuffer,0,0,this); } } // ////////////////////////// /////////////////////////////////////////////// // // Class ScrollerArea // class ScrollerArea extends Panel implements AdjustmentListener, ActionListener { int width=320+40; int height=300; int []Coherent=new int[11]; // ScrollerArea.L_Glaub.SetText("--"); //changing strings // ScrollerArea.L_Superp.SetText("--"); //changing strings // Scrollbar(int orientation, int value, int visible, int minimum, int maximum) int t_visible = 30; int t_minimum= 0; int t_maximum= 100; Button singleStat; // This is a mechanism Scrollbar s, EnergScroll; TextField t; Font f; Glauber2010 mparent; Label l, L_Ampl, L_Energ, L_Expla, L_Glaub, L_Superp; // Color simColor=new Color(220,220,100); // Animation Panel NEWCOLOR Color BgrdColor=new Color(255,255,212); // Scrolbar area Panel NEWCOLOR // double []aryCn=new double[11]; // COPIED (useless?) Scrollbar[] AmplitScroll=new Scrollbar[11]; TextField[] ArN=new TextField[11]; Button[] singleSt=new Button[11]; // This is a mechanism int z; public ScrollerArea(int x, int y, Glauber2010 parent) { mparent=parent; setSize(width,height); setLocation(x,y); setLayout(null); Coherent[ 1 ] = (int) 20 ; // Used only for start Coherent[ 2 ] = (int) 36 ; Coherent[ 3 ] = (int) 45 ; Coherent[ 4 ] = (int) 47 ; Coherent[ 5 ] = (int) 42 ; Coherent[ 6 ] = (int) 34 ; Coherent[ 7 ] = (int) 25 ; Coherent[ 8 ] = (int) 17 ; Coherent[ 9 ] = (int) 11 ; Coherent[ 10] = (int) 6 ; f=new Font("Helvetica",Font.PLAIN,14); // L_Ampl, L_Energ, L_Expla L_Ampl=new Label("Amplitudes of states:"); L_Ampl.setLocation(12,20); // Position of label Amplitudes L_Ampl.setFont(f); L_Ampl.setBackground(BgrdColor); L_Ampl.setSize(170,30); add(L_Ampl); L_Energ=new Label("Energy"); L_Energ.setLocation(295,20); // Position of label energy L_Energ.setFont(f); L_Energ.setBackground(BgrdColor); L_Energ.setSize(63,30); add(L_Energ); L_Superp=new Label(" Superposed State"); L_Superp.setLocation(12,230); // Position of Superposition L_Superp.setFont(f); L_Superp.setBackground(BgrdColor); L_Superp.setSize(125,30); add(L_Superp); L_Glaub=new Label(" Glauber State "); L_Glaub.setLocation(180,230); // Position of Glauber Label L_Glaub.setFont(f); L_Glaub.setBackground(BgrdColor); L_Glaub.setSize(120,30); add(L_Glaub); f=new Font("Helvetica",Font.PLAIN,10); setFont(f); L_Expla=new Label(" Glauber state: Energy slider [on]; Choice of amplitudes [off]"); L_Expla.setLocation(12,265); // Position of Explanation L_Expla.setFont(f); L_Expla.setBackground(BgrdColor); L_Expla.setSize(334,30); add(L_Expla); f=new Font("Helvetica",Font.PLAIN,10); setFont(f); for(int i=1;i<=10;i++){ // Scrollbar(int orientation, int value, int visible, int minimum, int maximum) // int t_visible = 20; int t_minimum= 0; int t_maximum= 100; s=new Scrollbar(Scrollbar.VERTICAL,0,t_visible,t_minimum,t_maximum+t_visible); s.setSize(15,100); s.setLocation(i*30-15,100); add(s); s.setValue( t_maximum -Coherent[i] ); z=i; s.addAdjustmentListener( new AdjustmentListener() { int whichstate=z; public void adjustmentValueChanged(AdjustmentEvent e) { mparent.PERFORM.ConvertAmplitudes(); if(mparent.RunHO.getLabel()=="Run") mparent.PERFORM.CALCULATE(); } } ); AmplitScroll[i]=s; // assign each scrollbar to parent l=new Label(Integer.toString(i-1)); l.setSize(20,20); l.setLocation(i*30-18,50); l.setBackground(BgrdColor); add(l); l.setAlignment(Label.CENTER); //ArN[i]=l; } // buttons to set individual states for(int i=1;i<=10;i++) { singleStat=new Button(Integer.toString(i-1)); singleStat.setSize(20,20); singleStat.setLocation(i*30-20,75); add(singleStat); z=i; singleSt[i]=singleStat; singleSt[i].setVisible(true); } repaint(); singleSt[1].addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { mparent.PERFORM.singleConvertAmplitudes(1); mparent.PERFORM.CALCULATE(); } } ); singleSt[2].addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { mparent.PERFORM.singleConvertAmplitudes(2); mparent.PERFORM.CALCULATE(); } } ); singleSt[3].addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { mparent.PERFORM.singleConvertAmplitudes(3); mparent.PERFORM.CALCULATE(); } } ); singleSt[4].addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { mparent.PERFORM.singleConvertAmplitudes(4); mparent.PERFORM.CALCULATE(); } } ); singleSt[5].addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { mparent.PERFORM.singleConvertAmplitudes(5); mparent.PERFORM.CALCULATE(); } } ); singleSt[6].addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { mparent.PERFORM.singleConvertAmplitudes(6); mparent.PERFORM.CALCULATE(); } } ); singleSt[7].addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { mparent.PERFORM.singleConvertAmplitudes(7); mparent.PERFORM.CALCULATE(); } } ); singleSt[8].addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { mparent.PERFORM.singleConvertAmplitudes(8); mparent.PERFORM.CALCULATE(); } } ); singleSt[9].addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { mparent.PERFORM.singleConvertAmplitudes(9); mparent.PERFORM.CALCULATE(); } } ); singleSt[10].addActionListener( new ActionListener() { public void actionPerformed(ActionEvent e) { mparent.PERFORM.singleConvertAmplitudes(10); mparent.PERFORM.CALCULATE(); } } ); // Coherent State scroller Energy value // Scrollbar(int orientation, int value, int visible, int minimum, int maximum) // int t_visible = 20; int t_minimum= 0; int t_maximum= 100; EnergScroll=new Scrollbar(Scrollbar.VERTICAL,0,t_visible,t_minimum,t_maximum+t_visible); EnergScroll.setSize(15,200); EnergScroll.setLocation(11*30,60); // Position of Energy slider add(EnergScroll); EnergScroll.setValue( (int)( (100/10)*(1.8*1.8 ) )); // the original value EnergScroll.addAdjustmentListener( new AdjustmentListener() { public void adjustmentValueChanged(AdjustmentEvent e) { int Nval; Nval=e.getValue(); mparent.PERFORM.CoherentAmplitudes(Nval); // if(mparent.RunHO.getLabel()=="Run") mparent.PERFORM.CALCULATE(); } } ); // ending Coherent State scroller } public void actionPerformed(ActionEvent e) { if(mparent.threadd!=null) mparent.threadd.suspend(); mparent.PERFORM.ConvertAmplitudes(); if(mparent.threadd!=null) mparent.threadd.resume(); mparent.PERFORM.CALCULATE(); } public void adjustmentValueChanged(AdjustmentEvent e) { } public void paint(Graphics g) { g.setColor(BgrdColor); g.fillRoundRect(1,1,width-2,height-2,15,15); g.setColor(Color.black); g.drawRoundRect(1,1,width-2,height-2,15,15); } } // ////////////////////////// /////////////////////////////////////////////// // // Class ThreadOsc // class ThreadOsc extends Thread { SimPanel HOprocess; public ThreadOsc(SimPanel TheProcess) { this.HOprocess=TheProcess; } public void run() { //panel.t=0; while(true){ HOprocess.t+=HOprocess.dt; HOprocess.CALCULATE(); try{sleep(HOprocess.thrdsleep);} catch(InterruptedException e){}; } } }