//Schrodinger wave equation simulation
Source code files are:
Schrodinger.java
SwingWorker.java
ThreadsSchrodinger.java
package schrodingerappletapplication;
/*
* @Copyright (c) 2005, Lazar Kovacevic
* www.inverudio.com
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL purposes and without
* fee is hereby granted provided that this copyright notice
* appears in all copies.
*
* Numerical solution taken from: http://electron6.phys.utk.edu/qm1/numerical/program1.htm
*/
//najmanji npts koji ce da radi posao; odnos dx-L-dt!?! - normalizacija parametara.
//Known bugs: repaint when webpage is scrolled up/down
//TODO rescaling of the graph for different functions and for different window sizes (to make current fixed width flexible, and rescale the graph)
//TODO to detach applet from the webpage
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
//import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
//import java.util.ArrayList;
import java.util.LinkedList;
import javax.swing.JApplet;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* @author Lazar Kovacevic
*/
public class Schrodinger extends JApplet implements Runnable {
private static final long serialVersionUID = 1L;
// Calculation related fields
static final int MAX_NPTS = 1000; // Number of points in space (npts) cannot be larger than this.
int npts = MAX_NPTS / 2; // Initial value for npts
int nReIm = 1000; // Dimension of the phase plot vector - determines how many time steps will be plotted.
static final int xoff = 10; // Offset for simulation plot
static final int yoff = 10; // Offset for simulation plot
boolean p = false; // Determines if the simulation is paused
boolean draw = false; // Determines if the function is drown or generated
double max = 0; // Used for normalizing y-axis of the plot
int xpos; // Used for mouse draging of the npts line
int reimPos = MAX_NPTS / 2;
int delay = 100; // Determines number of frames (10 frames/second)
long T = 0; // Current time step
double time = 0; // Current time
double norm;
double phi2[] = new double[MAX_NPTS]; // Square of the wave function
double phir[] = new double[MAX_NPTS]; // Real part
double phii[] = new double[MAX_NPTS]; // Imaginary part
LinkedList reim = new LinkedList(); // Array for the Re-Im plot
LinkedList entropy = new LinkedList(); // Array for the Entropy in time plot
// arraylist -> linked list (possible speed improvement)
/* ArrayList phaseplot = new ArrayList();*///Array for the phase plot
// ArrayList var = new ArrayList(); - Tried to generate all textFields with one loop, but did not succeed. Need to learn more!
double Cnt = (double) npts;
double Minimum;
double Maximum;
int MaxBins;
double MinMax[];
// User defined parameters
// npts is density of points, L is width of region, dx = L/npts.
boolean automax = true;
int plotTimeJump = 10; // How many time steps are calculated before each plotting
int sampling = 1; // Sampling rate - by increasing this beyond 1, normalization will be broken
double energy = 5; // Energy
double dt = 0.1; // Time step
double L = 100; // Length of the bounded area.
double dx = L / npts; // Space step
double a = 25 * dx; // Width of the Gaussian wave posket - standard deviation related constant
String[] functions = { "Gauss", "Square Envelope", "Square Re Im", "Dirac",
"Sine" }; //To think about boundary conditions...
// GUI related fields and components
// URL for the refference - did not know how to put url, so I put it in a textfield
JLabel refl = new JLabel("Numerical solution taken from: ");
JTextField ref = new JTextField(
"http://electron6.phys.utk.edu/qm1/numerical/program1.htm");
// A few instruction lines
JLabel instruction1 = new JLabel(
"To change parameters (enter real numbers not text), you have to press return/enter in text box! Move mouse over text fields and buttons to see the tip.");
JLabel instruction2 = new JLabel(
"You will receive message Change Acknowledged! while simulation is running.");
//JLabel instruction3 = new JLabel("Be careful! There are no exceptions at this point so be sure to enter real numbers and not text.");
JLabel instruction4 = new JLabel(
"To see phase plot of the whole spatial series, use this trick: ");
JLabel instruction5 = new JLabel(
"Decrease dt to be small enough so that series do not evolve, and slide slowly slider from left to right.");
JTextArea explanation = new JTextArea(
"This program solves the time-dependent Schroedinger equation for "
+ "\n"
+ "a wavepacket representing an electron of average energy E(eV) confined to "
+ "\n"
+ "a region from x=0 to x=L(1E-10m). Time is stepped in units of dt(1E-16s).");
// Box that groups instructions together
JComponent instructionBox = new JPanel();
// JTextFields accepting input from the user.
// Pressing 'Enter' is required for the change to be acknowledged.
// It is much easier than testing for exceptions, and doing logic for real time change
JTextField txtSampling = new JTextField(3);
JTextField txtPlotTimeJump = new JTextField(3);
JTextField txtEnergy = new JTextField(3);
JTextField txtdt = new JTextField(3);
JTextField txtdx = new JTextField(3);
JTextField txta = new JTextField(3);
JTextField txtL = new JTextField(3);
JTextField txtnReIm = new JTextField(4);
JCheckBox cbAutoMax = new JCheckBox("max dt?");
JButton startButton;
JButton interruptButton; // Stop button
JButton printButton;
JButton drawButton;
JComboBox functionBox;
// npts related components and fields
JTextField txtNpts = new JTextField(8);
JSlider sldNpts = new JSlider(1, 50, MAX_NPTS / npts);
String str = "# of points: " + npts; // Current number of points in space that is being calculated
boolean lineMove = false; // Registers if user is moving boundary (npts line)with a mouse
// If memory true, remembers values right of npts after restarting simulation
JCheckBox mem = new JCheckBox("Memory on right");
boolean memory = false; // used with
// Box that groups text fields and buttons togethers
JComponent buttonBox = new JPanel();
// Label showing current time step
String ds = " "; // Used for right allingment of the current time value
JLabel timeStep = new JLabel("Time step: " + "0");
// Label showing status of the simulation
JLabel statusField = new JLabel("Click Start to begin", JLabel.CENTER);
// Reports if the user has changed the parameters
JLabel changeacknowledged = new JLabel("");//("Change Acknowledged!"); // something is messed up, and relevant code is not working properly.
int cnt = 0; // counts 15 time steps, then erases changeacknowledged from the screen
// Box that groups current status related fields
JComponent statusBox = new JPanel();
// Box that holds changeAcknowleged note
JComponent changeBox = new JPanel();
JPanel panel; // Panel for plotting simulation
SwingWorker worker; // Uses 3rd version of SwingWorker abstract class
SwingWorker drawing; // Uses 3rd version of SwingWorker abstract class
// Called when this applet is loaded into the browser.
public void init() {
//Execute a job on the event-dispatching thread:
//creating this applet's GUI.
try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (Exception e) {
System.err.println("createGUI didn't successfully complete");
}
}
/**
* And now for a little assembly. Put together the buttons, progress bar and
* status text field.
*/
public void createGUI() {
//new Schrodinger();
setLayout(null);
// Putting all components together
String tt = "";
// Refference
add(refl);
add(ref);
//refl.setBounds(10,1,200,20);
//ref.setBounds(191,1,330,20);
refl.setBounds(270, 430, 440, 20);
ref.setBounds(451, 430, 350, 20);
add(explanation);
explanation.setBounds(270, 460, 500, 50);
explanation.setEnabled(false);
// Initializing names for labels
String lbls[] = new String[8];
lbls[0] = "Sampling";
lbls[1] = "Plot every n-th step";
lbls[2] = "Energy";
lbls[3] = "dt";
lbls[4] = "dx";
lbls[5] = "a";
lbls[6] = "L";
lbls[7] = "nReIm";
// Initializing text fields for user changable variables
txtSampling.setText("" + sampling);
txtSampling.setToolTipText("Integers only!");
txtPlotTimeJump.setText("" + plotTimeJump);
txtEnergy.setText("" + energy);
txtdt.setText("" + dt);
txtdx.setText("" + L / npts);
// txtdx.setEnabled(false);
txta.setText("" + a / dx); // refresh all related variables & textfields when some component is changed!
txtL.setText("" + L);
txtnReIm.setText("" + nReIm);
//for (int i=0;i<6;i++) var.add(new JTextField(4));
/*var.add(txtSampling);
var.get(0).setText("1");
var.add(txtPlotTimeJump);
var.get(1).setText("1");
var.add(txtEnergy);
var.get(2).setText("5");
var.add(txtdt);
var.get(3).setText(".1");
var.add(txtdx);
var.get(4).setText("" + L / npts);
var.get(4).setEnabled(false);
var.add(txta);
var.get(5).setText("25");*/
/*addJLabel(buttonBox, lbls[0]);
buttonBox.add(txtSampling);
txtSampling.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
sampling = Integer.parseInt(txtSampling.getText());
changeBox.add(changeacknowledged);
cnt = 15;
}
}
);*/
tt = "Integers only!";
addJLabel(buttonBox, lbls[1], tt);
buttonBox.add(txtPlotTimeJump);
txtPlotTimeJump.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
plotTimeJump = Integer.parseInt(txtPlotTimeJump.getText());
changeBox.add(changeacknowledged);
cnt = 15;
}
});
txtPlotTimeJump.setToolTipText(tt);
tt = "See description below.";
addJLabel(buttonBox, lbls[2], tt);
buttonBox.add(txtEnergy);
txtEnergy.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
energy = Double.parseDouble(txtEnergy.getText());
changeBox.add(changeacknowledged);
cnt = 15;
}
});
txtEnergy.setToolTipText(tt);
cbAutoMax.setSelected(automax);
cbAutoMax
.setToolTipText("If checked, program automatically finds almost maximum dt for an accurate simulation within .001 in Phi units.");
cbAutoMax.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
automax = !automax;
changeBox.add(changeacknowledged);
cnt = 15;
}
});
buttonBox.add(cbAutoMax);
tt = "Time step increment";
addJLabel(buttonBox, lbls[3], tt);
buttonBox.add(txtdt);
txtdt.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
dt = Double.parseDouble(txtdt.getText());
changeBox.add(changeacknowledged);
cnt = 15;
}
});
txtdt.setToolTipText(tt);
tt = "See description below.";
addJLabel(buttonBox, lbls[4], tt);
buttonBox.add(txtdx);
/*txtdx.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
dx = Double.parseDouble(txtdx.getText());
//L = dx * npts; This is wrong! L is constant!
//npts = (int)(L/dx);
changeBox.add(changeacknowledged);
cnt = 15;
a = Double.parseDouble(txta.getText())*dx;
}
}
);*/
txtdx.setEnabled(false); // not user changable!
txtdx.setToolTipText(tt);
tt = "Wave width parameter.";
addJLabel(buttonBox, lbls[5], tt);
buttonBox.add(txta);
txta.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
a = Double.parseDouble(txta.getText()) * dx;
changeBox.add(changeacknowledged);
cnt = 15;
}
});
txta.setToolTipText(tt);
tt = "See description below.";
addJLabel(buttonBox, lbls[6], tt);
buttonBox.add(txtL);
txtL.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
L = Double.parseDouble(txtL.getText());
dx = L / npts;
txtdx.setText("" + dx);
a = Double.parseDouble(txta.getText()) * dx;
changeBox.add(changeacknowledged);
cnt = 15;
}
});
txtL.setToolTipText(tt);
tt = "Number of points that (blue) time series can contain. If too many, calculation will slow down.";
addJLabel(buttonBox, lbls[7], tt);
buttonBox.add(txtnReIm);
txtnReIm.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
nReIm = Integer.parseInt(txtnReIm.getText());
changeBox.add(changeacknowledged);
cnt = 15;
}
});
txtnReIm.setToolTipText(tt);
// See above defining of 'ArrayList var' for comment
/*for (int i = 0; i < lbls.length; i++)
{ addJLabel(buttonBox, lbls[i]);
buttonBox.add(var.get(i));
var.get(i).addActionListener(new ActionListener()
{ public void actionPerformed(ActionEvent e)
{ if (var.get(0).equals(e.getActionCommand())) {
source = (JTextField)e.getSource();
sampling = Integer.parseInt(txtSampling.getText());
} else if (var.get(1).equals(e.getActionCommand())) {
source = (JTextField)e.getSource();
plotTimeJump = Integer.parseInt(txtPlotTimeJump.getText());
}}});}*/
startButton = new JButton("Start");
startButton.addActionListener(startListener);
startButton.setEnabled(true);
startButton
.setToolTipText("Starts animation. You can pause/unpause animation by clicking at the plot.");
interruptButton = new JButton("Stop");
interruptButton.addActionListener(interruptListener);
interruptButton.setEnabled(false);
interruptButton
.setToolTipText("Stops animation and reinitializes parameters. Enables change of density and other parameters.");
printButton = new JButton("PrintToFile");
printButton.addActionListener(printListener);
printButton.setEnabled(true);
printButton
.setToolTipText("Creates on Desktop 'txt' file of the current plots with current parameters encoded in the filename.");
drawButton = new JButton("Gauss Wave");
drawButton.addActionListener(drawListener);
drawButton.setEnabled(true);
drawButton
.setToolTipText("Allows you to draw a function with a mouse.");
functionBox = new JComboBox(functions);
//functionBox.addActionListener(functionListener);
functionBox.setEnabled(true);
functionBox.setToolTipText("Choose a function.");
functionBox.setSelectedIndex(0);
txtNpts.setText(str);
txtNpts.setEnabled(false);
txtNpts.setToolTipText("NPTS: Number of PoinTS in space.");
mem.setSelected(memory);
mem
.setToolTipText("If checked, you can start animation with high NPTS, and then restart with smaller NPTS, and keep the old wave there!");
mem.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
memory = !memory;
changeBox.add(changeacknowledged);
cnt = 15;
}
});
buttonBox.add(startButton);
buttonBox.add(interruptButton);
buttonBox.add(printButton);
//buttonBox.add(drawButton);
buttonBox.add(functionBox);
drawButton.setEnabled(false);
//buttonBox.add(txtNpts);
//buttonBox.add(mem);
add(buttonBox);
Dimension size = buttonBox.getPreferredSize();
buttonBox.setBounds(1, 25, size.width + 30, size.height);
add(timeStep);
add(statusField);
timeStep.setBounds(25, 55, 350, 50);
statusField.setBounds(250, 55, 750, 50);
statusField.setForeground(Color.blue);
add(changeBox);
changeBox.setBounds(550, 65, 150, 50);
// Panel with plot
panel = new JPanel();
panel.addMouseListener(pauseListener);
//translate(3,3);
add(panel);
panel.setBounds(0, 100, 1011, 311);
sldNpts.addChangeListener(nptsListener);
sldNpts
.setToolTipText("Changes the number of points (NPTS) that is currently being calculated for animation. (dx=L/NPTS)");
add(sldNpts);
sldNpts.setBounds(10, 430, 50, 20);
JLabel sl = new JLabel("Change dx");
add(sl);
sl.setBounds(61, 430, 90, 20);
add(txtNpts);
txtNpts.setBounds(151, 430, 100, 20);
instructionBox.add(instruction1);
//instructionBox.add(instruction2);
//instructionBox.add(instruction4);
//instructionBox.add(instruction5);
add(instructionBox);
//instructionBox.setBounds(250 , 427 , 600, 100);
instructionBox.setBounds(10, 1, 900, 100);
}
// Used for double buffering of the picture, so that animation would be smooth
Dimension offDimension;
Image offImage;
Graphics2D offGraphics;
public void addJLabel(Container c, String title, String tooltip) {
JLabel label = new JLabel(title);
label.setToolTipText(tooltip);
c.add(label);
}
// Main class ThreadsSchrodinger is calling this method
JButton getStartButton() {
return startButton;
}
/**
* This method represents the application code that we'd like to run on a
* separate thread. It simulates Schrodinger wave equation.
*/
Object doWork() {
try {
double azr[] = new double[MAX_NPTS];
double gamr[] = new double[MAX_NPTS];
double gami[] = new double[MAX_NPTS];
double betr[] = new double[MAX_NPTS];
double beti[] = new double[MAX_NPTS];
double phi2old[] = new double[MAX_NPTS];
double phi2current[] = new double[MAX_NPTS];
int i;
//for (i=reim.size()-1; i>0; i--) reim.remove(i);
reim.clear();
entropy.clear();
//if(drawButton.getText()=="Gauss Wave")
{
initialize(dx, a, phir, phii);
for (i = 0; i < npts; i++) {
azr[i] = -2;
}
// Finding maximum dt for accurate calculation (within .001 error tolerance)
if (automax) {
boolean maxdt;
int I = 1;
double maxt = 0;
boolean maxdtP = false;
int start = 0;
while (I < 5 && dt < 1000000) {
dt = dt / Math.pow(2, I);
timeStep(1 + (int) Math.pow(2, I), azr, gamr, gami,
betr, beti, phir, phii, phi2old);
for (i = 0; i < npts; i++) {
phi2current[(Math.round(i * MAX_NPTS / npts))] = phi2[(Math
.round(i * MAX_NPTS / npts))];
}
dt = dt * (1 + Math.pow(2, I));
timeStep(1, azr, gamr, gami, betr, beti, phir, phii,
phi2old);
maxdt = true;
for (i = 0; i < npts; i++) {
if (Math.abs(phi2current[(Math.round(i * MAX_NPTS
/ npts))]
- phi2[(Math.round(i * MAX_NPTS / npts))]) > .001) {
maxdt = false;
}
}//dt proportional to error!
if (maxdt && (maxt < dt))
maxt = dt;
if (!maxdt)
dt = dt - dt * 2 / (1 + Math.pow(2, I));
if (!maxdt && maxdtP & start > 1)
I++;
maxdtP = maxdt;
start++;
}
dt = maxt;
if (maxt == 0) {
txtdt.setText("INF");
txtdt.setForeground(Color.red);//WHY IS THIS NOT WORKING????????????????????
} else
txtdt.setText("" + dt);
txtdt.setForeground(Color.black);
}
}
//Plotting initial graf
updateStatus(0);
long startTime = System.currentTimeMillis();
//Calculating and plotting simulation
for (int inf = 1; inf < 10;) {
if (!p) {
timeStep(plotTimeJump, azr, gamr, gami, betr, beti, phir,
phii, phi2old);
T = T + plotTimeJump;
time = time + dt * plotTimeJump;
updateStatus(T);
}
if (Thread.interrupted()) {
throw new InterruptedException();
}
try {
startTime += delay;
if ((startTime - System.currentTimeMillis()) < 0) {
p = true;
statusField
.setText("Paused... Please decrease 'Plot every n-th step'. Calculation is too demanding, and animation is not smooth. Click on plot to resume.");
}
Thread.sleep(Math.max(0, startTime
- System.currentTimeMillis()));
} catch (InterruptedException e) {
break;
}
}
} catch (InterruptedException e) {
updateStatus(0);
return "Interrupted"; // SwingWorker.get() returns this
}
return "Done?"; // or this
}
// Initializing the wave function
private void initialize(double dx, double a, double[] phir, double[] phii) {
double x;
double GAUSS; // this is envelope for the function
norm = 0;
double k0 = Math.sqrt(0.263 * energy);
double x0 = dx * npts / 2;
int i;
// Start with a Gaussian wavepacket of width a*dx*sqr(2).
T = 0;
time = 0;
phir[0] = 0;
phii[0] = 0;
phir[npts - 1] = 0;
phii[npts - 1] = 0;
phi2[(Math.round((npts - 1) * MAX_NPTS / npts))] = 0;
phi2[(Math.round((npts - 1) * MAX_NPTS / npts))] = 0;
switch (functionBox.getSelectedIndex()) {
case 1: //"Square Envelope"
for (i = 1; i < npts; i++) {
x = i * dx;
GAUSS = 1 / (2 * a * dx);
if (Math.abs(x - x0) < a) {
phir[i] = Math.cos(k0 * x) * GAUSS;//Math.cos(k0 * x)* GAUSS;
phii[i] = Math.cos(k0 * x) * GAUSS;//Math.sin(k0 * x)* GAUSS;
phi2[(Math.round(i * MAX_NPTS / npts))] = phir[i] * phir[i]
+ phii[i] * phii[i];
} else {
phir[i] = 0;
phii[i] = 0;
phi2[(Math.round(i * MAX_NPTS / npts))] = 0;
}
}
break;
case 2: //"Square Re Im"
for (i = 1; i < npts; i++) {
x = i * dx;
GAUSS = 1 / (2 * a * dx);
if (Math.abs(x - x0) < a) {
phir[i] = GAUSS;//Math.cos(k0 * x)* GAUSS;
phii[i] = GAUSS;//Math.sin(k0 * x)* GAUSS;
phi2[(Math.round(i * MAX_NPTS / npts))] = phir[i] * phir[i]
+ phii[i] * phii[i];
} else {
phir[i] = 0;
phii[i] = 0;
phi2[(Math.round(i * MAX_NPTS / npts))] = 0;
}
}
break;
case 3: //"Dirac"
for (i = 1; i < npts; i++) {
x = i * dx;
GAUSS = 1 / dx;
if (x == x0) {
phir[i] = Math.cos(k0 * x) * GAUSS;
phii[i] = Math.sin(k0 * x) * GAUSS;
phi2[(Math.round(i * MAX_NPTS / npts))] = phir[i] * phir[i]
+ phii[i] * phii[i];
} else {
phir[i] = 0;
phii[i] = 0;
phi2[(Math.round(i * MAX_NPTS / npts))] = 0;
}
}
break;
case 4: //"Sine"
for (i = 1; i < npts; i++) {
x = i * dx;
GAUSS = Math.sin(x / (a / dx) / (2 * Math.PI) * 360 / L);
if (i % sampling == 0 && i < npts - 1) {
phir[i] = Math.cos(k0 * x) * GAUSS;
phii[i] = Math.sin(k0 * x) * GAUSS;
phi2[(Math.round(i * MAX_NPTS / npts))] = phir[i] * phir[i]
+ phii[i] * phii[i];
} else if (i < npts - 1 || (i > npts - 1 && !memory)) {
phir[i] = 0;
phii[i] = 0;
phi2[(Math.round(i * MAX_NPTS / npts))] = 0;
}
}
break;
default: // "Gauss"
for (i = 1; i < npts; i++) {
x = i * dx;
GAUSS = (1 / a) * (1 / Math.sqrt(Math.PI))
* Math.exp(-Math.pow((x - x0), 2) / (a * a));//I added: (1/a)*(1/Math.sqrt(Math.PI))*
if (i % sampling == 0 && i < npts - 1) {
phir[i] = Math.cos(k0 * x) * GAUSS;
phii[i] = Math.sin(k0 * x) * GAUSS;
phi2[(Math.round(i * MAX_NPTS / npts))] = Math
.pow(GAUSS, 2);
} else if (i < npts - 1 || (i > npts - 1 && !memory)) {
phir[i] = 0;
phii[i] = 0;
phi2[(Math.round(i * MAX_NPTS / npts))] = 0;
}
}
}
for (i = 0; i < npts; i++)
norm = norm + phi2[(Math.round(i * MAX_NPTS / npts))] * dx;
double sqn;
sqn = Math.sqrt(norm);
double phi2max = 0;
max = 0;
for (i = 0; i < npts; i++) {
phir[i] = phir[i] / sqn;
phii[i] = phii[i] / sqn;
phi2[(Math.round(i * MAX_NPTS / npts))] = phi2[(Math.round(i
* MAX_NPTS / npts))]
/ norm;
if (phi2[(Math.round(i * MAX_NPTS / npts))] > phi2max) {
phi2max = phi2[(Math.round(i * MAX_NPTS / npts))];
max = phi2max;
}
}
}
// Calculates plotTimeJump number of time steps
private void timeStep(int plotTimeJump, double[] azr, double[] gamr,
double[] gami, double[] betr, double[] beti, double[] phir,
double[] phii, double[] phi2old) {
double azi;
double azi2;
double denom;
double chir;
double temp;
double chii;
double xdt = dx * dx / dt;
int i;
int timeJump = plotTimeJump;
if (cnt > 0) {
cnt--;
} else if (cnt == 0) {
changeBox.remove(changeacknowledged);
}
for (int tj = 0; tj < timeJump; tj++) {
// 'Calculate Gamma.
azi = 3.457 * xdt * 100;
azi2 = 2 * azi;
denom = azr[npts - 1] * azr[npts - 1] + azi * azi;
gamr[npts - 1] = -azr[npts - 1] / denom;
gami[npts - 1] = azi / denom;
for (i = npts - 2; i > 0; i--) {
denom = Math.pow((gamr[i] + azr[i - 1]), 2)
+ Math.pow((gami[i] + azi), 2);
gamr[i - 1] = -(gamr[i] + azr[i - 1]) / denom;
gami[i - 1] = (gami[i] + azi) / denom;
}
// 'Calculate beta.
betr[npts - 1] = 0;
beti[npts - 1] = 0;
for (i = npts - 2; i > 0; i--) {
betr[i - 1] = gamr[i] * (betr[i] + azi2 * phii[i]);
betr[i - 1] = betr[i - 1] - gami[i]
* (beti[i] - azi2 * phir[i]);
beti[i - 1] = gamr[i] * (beti[i] - azi2 * phir[i]);
beti[i - 1] = beti[i - 1] + gami[i]
* (betr[i] + azi2 * phii[i]);
}
// 'CALCULATE PHI
chir = 0;
chii = 0;
//double sum=0, sum2=0;
for (i = 1; i < npts - 1; i++) {
temp = chir;
chir = gamr[i] * chir - gami[i] * chii + betr[i - 1];
chii = gamr[i] * chii + gami[i] * temp + beti[i - 1];
phir[i] = chir - phir[i];
phii[i] = chii - phii[i];
phi2old[(Math.round(i * MAX_NPTS / npts))] = phi2[(Math.round(i
* MAX_NPTS / npts))];
phi2[(Math.round(i * MAX_NPTS / npts))] = phir[i] * phir[i]
+ phii[i] * phii[i];
//sum = Math.sqrt(phi2[Math.round(i*MAX_NPTS/npts)])+sum; //checks the integral value of the wave - should be constant with small variations due to rounding
//sum2 = phi2[Math.round(i*MAX_NPTS/npts)]+sum2; //checks the integral value of the wave - should be constant with small variations due to rounding
}
//System.out.println(sum+" "+sum2); //same as above
}
}
/**
* When the worker needs to update the GUI we do so by queuing a Runnable
* for the event dispatching thread with SwingUtilities.invokeLater().
* Here, buffered immage is created, and then finished picture is plotted on the screen.
*/
void updateStatus(final long i) {
Runnable doSetProgressBarValue = new Runnable() {
public void run() {
Dimension d = getSize();
if ((offGraphics == null) || (d.width != offDimension.width)
|| (d.height != offDimension.height)) {
offDimension = d;
offImage = createImage(d.width, d.height);
offGraphics = (Graphics2D) offImage.getGraphics();
}
// offGraphics.setBackground(Color.WHITE);
// offGraphics.setColor(getBackground());
offGraphics.setColor(Color.white);
offGraphics.fillRect(0, 0, d.width, d.height);
offGraphics.setColor(Color.black);
// Allignment to the right of the time step
/*if (i<10000000){ds = "";}
if (i<1000000){ds = " ";}
if (i<100000){ds = " ";}
if (i<10000){ds = " ";}
if (i<1000){ds = " ";}
if (i<100){ds = " ";}
if (i<10){ds = " ";}*/
if (i != 0)
timeStep.setText("Time step: " + i + " Time: " + Math.round(time));// + " dx*dx/dt = " + dx*dx*dt
plotting();
//TODO here somewhere is repaint method bug
panel.getGraphics().drawImage(offImage, xoff, yoff, panel);
}
};
SwingUtilities.invokeLater(doSetProgressBarValue);
}
// Plotting
public void plotting()//unfinished version of the above function with entropy
{
entropy.addLast(new bins(CalcEntropy(phi2, 0, max, 2),CalcEntropy(phi2, 0, max, 8),CalcEntropy(phi2, 0, max, 32),CalcEntropy(phi2, 0, max, 128)));
offGraphics.draw(new Line2D.Double(0, 0, MAX_NPTS, 0));
offGraphics.draw(new Line2D.Double(0, 0, 0, 300));
offGraphics.draw(new Line2D.Double(MAX_NPTS, 0, MAX_NPTS,
300));
//offGraphics.setColor(Color.blue);
offGraphics.draw(new Line2D.Double(sldNpts.getValue(), 0, sldNpts.getValue(),
300));
offGraphics.setColor(Color.blue);
offGraphics.draw(new Line2D.Double(reimPos, 0, reimPos,
300));
offGraphics.setColor(Color.black);
offGraphics.draw(new Line2D.Double(0, 300,
MAX_NPTS, 300));
for (int i = 0; i < npts - 1; i++)
{
offGraphics.draw(new Line2D.Double((Math.round(i*MAX_NPTS/npts)), 300 * (1 - phi2[(Math.round(i*MAX_NPTS/npts))] / max/1.5),
Math.round((i+1)*MAX_NPTS/npts), 300 * (1 - phi2[(Math.round((i+1)*MAX_NPTS/npts))] / max/1.5)));
}
reim.addLast(new ReIm(phir[reimPos/sldNpts.getValue()],phii[reimPos/sldNpts.getValue()]));
while (reim.size()>nReIm)reim.removeFirst();
offGraphics.setColor(Color.blue);
offGraphics.drawString("Real-Imaginary plane plot at position = " + reimPos/sldNpts.getValue() + " (drag blue line with mouse)",600,20);
for (ReIm cords : reim)
{
offGraphics.fillOval((int)(50*cords.getX()/max)+800,(int)(1-50*cords.getY()/max) + 150,1,1);
}
offGraphics.setColor(Color.black);
}
/**
* This action listener, called by the "Start" button, effectively forks the
* thread that does the work.
*/
ActionListener startListener = new ActionListener() {
public void actionPerformed(ActionEvent event) {
startButton.setEnabled(false);
interruptButton.setEnabled(true);
drawButton.setEnabled(false);
functionBox.setEnabled(false);
txtNpts.setEnabled(false);
txtSampling.setEnabled(false);
txtL.setEnabled(false);
sldNpts.setEnabled(false);
//txtdx.setEnabled(false);
txta.setEnabled(false);
txtEnergy.setEnabled(false);
statusField.setText("Working...");
cbAutoMax.setEnabled(false);
/*
* Invoking start() on the SwingWorker causes a new Thread to be
* created that will call construct(), and then finished(). Note
* that finished() is called even if the worker is interrupted
* because we catch the InterruptedException in doWork().
*/
worker = new SwingWorker() {
public Object construct() {
return doWork();
}
public void finished() {
startButton.setEnabled(true);
interruptButton.setEnabled(false);
drawButton.setEnabled(true);
functionBox.setEnabled(true);
// txtnpts.setEnabled(true);
statusField.setText(get().toString());
}
};
worker.start();
}
};
/**
* This action listener, called by the "Stop" button, interrupts the
* worker thread which is running this.doWork(). Note that the doWork()
* method handles InterruptedExceptions cleanly.
*/
ActionListener interruptListener = new ActionListener() {
public void actionPerformed(ActionEvent event) {
worker.interrupt();
interruptButton.setEnabled(false);
startButton.setEnabled(true);
drawButton.setEnabled(true);
functionBox.setEnabled(true);
txtSampling.setEnabled(true);
txtL.setEnabled(true);
sldNpts.setEnabled(true);
//txtdx.setEnabled(true);
txta.setEnabled(true);
txtEnergy.setEnabled(true);
cbAutoMax.setEnabled(true);
/*offGraphics.setColor(getBackground());
offGraphics.fillRect(0, 0, MAX_NPTS + 1, MAX_NPTS + 1);
offGraphics.setColor(Color.black);
panel.getGraphics().drawImage(offImage, xoff, yoff, panel);*/
if (p) {
p = false;
}
// drawing method needs to be modified, not working properly at this moment.
/*drawing = new SwingWorker()
{
public Object construct()
{
return progres();
}
public void finished()
{
startButton.setEnabled(true);
interruptButton.setEnabled(false);
drawButton.setEnabled(true);
// txtnpts.setEnabled(true);
statusField.setText(get().toString());
}
};
drawing.start();*/
}
};
// allows drawing - not finished/used
ActionListener drawListener = new ActionListener() {
public void actionPerformed(ActionEvent event) {
if (drawButton.getText() == "Gauss Wave") {
drawButton.setText("User Drown");
// - disejblujes parametre vezane za gausovu funkciju -
txtSampling.setEnabled(false);
//txtL.setEnabled(false);
sldNpts.setEnabled(true);
//txtdx.setEnabled(true);
txta.setEnabled(false);
txtEnergy.setEnabled(false);
} else if (drawButton.getText() == "User Drown") {
//- enablujes parametre vezane za gausovu funkciju -
drawButton.setText("Gauss Wave");
txtSampling.setEnabled(true);
//txtL.setEnabled(true);
sldNpts.setEnabled(true);
//txtdx.setEnabled(true);
txta.setEnabled(true);
txtEnergy.setEnabled(true);
/*offGraphics.setColor(getBackground());
offGraphics.fillRect(0, 0, MAX_NPTS + 1, MAX_NPTS + 1);
offGraphics.setColor(Color.black);
panel.getGraphics().drawImage(offImage, xoff, yoff, panel);*/
}
}
};
/*// exporting plot to datafile
ActionListener functionListener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
}
};*/
// exporting plot to datafile
ActionListener printListener = new ActionListener() {
public void actionPerformed(ActionEvent event) {
dataFile();
}
};
// Pausing simulation or draging npts line
MouseAdapter pauseListener = new MouseAdapter() {
// Pause
public void mouseClicked(MouseEvent arg0) {
if (statusField.getText() != "Done?") {
if (!p) {
p = true;
statusField.setText("Paused... Click on plot to resume.");
} else {
p = false;
statusField.setText("Working...");
}
}
/*else if (drawButton.getText()=="User Drown") //not used
{
if(xpos!=(getMousePosition().x-xoff))
{
xpos = getMousePosition().x-xoff;
phi2[Math.round(getMousePosition().x)]=-(getMousePosition().y/411-1)*max*norm;
//300 * (1 - phi2[(Math.round((i+1)*MAX_NPTS/npts))] / max)=-(xpos/300-1)*max
phir[xpos/sldNpts.getValue()] = Math.cos(getMousePosition().x) * Math.sqrt(phi2[xpos]);
phii[xpos/sldNpts.getValue()] = Math.sin(getMousePosition().x) * Math.sqrt(phi2[xpos]);
updateStatus(0);
}
}*/
}
// npts line pressed
public void mousePressed(MouseEvent arg0) {
if ((reimPos - 10 + xoff) < getMousePosition().x
&& getMousePosition().x < (reimPos + 10 + xoff)) {
lineMove = true;
xpos = getMousePosition().x;
}
}
// npts line released
public void mouseReleased(MouseEvent arg0) {
if (lineMove && (xpos != getMousePosition().x)) {
//for (int i=reim.size()-1; i>0; i--) reim.remove(i);
reim.clear();
entropy.clear();
reimPos = getMousePosition().x - xoff;
if (reimPos < 1)
reimPos = 1;
if (reimPos > MAX_NPTS)
reimPos = MAX_NPTS;
reimPos = (reimPos / sldNpts.getValue()) * MAX_NPTS / npts;
//sldNpts.setValue(npts - xoff);
//str = "# of points: " + npts;
//txtNpts.setText(str);
updateStatus(0);
}
lineMove = false;
}
public void mouseEntered(MouseEvent arg0) {
if (drawButton.getText() == "User Drown")
draw = true;
}
public void mouseExited(MouseEvent arg0) {
if (drawButton.getText() == "User Drown")
draw = false;
}
};
// listener for slider - changes npts
ChangeListener nptsListener = new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
npts = MAX_NPTS / sldNpts.getValue();
str = "# of points: " + npts;
txtNpts.setText(str);
dx = L / npts;
txtdx.setText("" + dx);
updateStatus(0);
}
};
// listener for slider - changes l
/*ChangeListener lListener = new ChangeListener()
{
public void stateChanged(ChangeEvent arg0)
{
L = sldL.getValue();
txtL.setText(""+L);
dx = L/npts;
txtdx.setText(""+dx);
a = Double.parseDouble(txta.getText())*dx;
changeBox.add(changeacknowledged);
updateStatus(0);
}
};*/
// Opens text file to export plot values
public void dataFile() {
try {
boolean wp = true;
if (!p) {
p = true;
wp = false;
} // pauses simulation
PrintWriter out = new PrintWriter(new FileWriter("Function-"
+ functionBox.getSelectedItem() + " npts-" + npts
+ " time-" + Math.round(time) + " T-" + T + " En-" + energy
+ " dt-" + Math.round(dt * 100) / 100 + " dx-"
+ Math.round(dx * 100) / 100 + " L-" + L + " a-" + a / dx
+ " .txt")); // encodes file name with current simulation parameters' values
writeData(out);
out.close();
if (!wp)
p = false; // unpauses simulation
} catch (IOException exception) {
exception.printStackTrace();
}
}
// Writes to text file
void writeData(PrintWriter out) throws IOException {
// spatial series at current time
out.println("Spatial series at itteration T = " + T + " and time = "
+ time);
out.println("Function: " + functionBox.getSelectedItem() + " ; npts = "
+ npts + " ; time = " + time + " ; T = " + T + " ; Energy = "
+ energy + " ; dt = " + dt + " ; dx = " + dx + " ; L = " + L
+ " ; a = " + a / dx);
for (int i = 0; i < npts; i++) {
out.println(phi2[(Math.round(i * MAX_NPTS / npts))]);
}
// time series for the last nreim time steps that were plotted
out.println("Time series in positions " + reimPos / sldNpts.getValue());
out.println("Function: " + functionBox.getSelectedItem()
+ "Number of points = " + reim.size()
+ " ; First T (scaled for current 'Plot every n-th step')= "
+ (T - plotTimeJump * (reim.size() - 1)) + " ; Last T = " + T
+ " ; time = " + time + " ; npts = " + npts + " ; Energy = "
+ energy + " ; dt = " + dt + " ; dx = " + dx + " ; L = " + L
+ " ; a = " + a / dx);
out
.println("Re Im Add their squares to calculate amplitude.");
for (int i = 0; i < reim.size(); i++) {
out.println(reim.get(i).getX() + " " + reim.get(i).getY());
}
}
// Class used for storing time series in reim array
class ReIm {
double re = 0;
double im = 0;
public ReIm(double x, double y) {
super();
re = x;
im = y;
}
public double getX() {
return re;
}
public double getY() {
return im;
}
}
void updateDrawing()//not used
{
Runnable drawing = new Runnable() {
public void run() {
/*if(draw)
{
phi2[Math.round(getMousePosition().x)]=-(getMousePosition().y/300-1)*max;
//300 * (1 - phi2[(Math.round((i+1)*MAX_NPTS/npts))] / max)=-(xpos/300-1)*max
phir[getMousePosition().x/sldNpts.getValue()] = Math.cos(getMousePosition().x) * Math.sqrt(phi2[getMousePosition().x]);
phii[getMousePosition().x/sldNpts.getValue()] = Math.sin(getMousePosition().x) * Math.sqrt(phi2[getMousePosition().x]);
}*/
}
};
SwingUtilities.invokeLater(drawing);
}
/**
* This method represents the application code that we'd like to run on a
* separate thread. It allows drawing from the user.
*/
// not being used at this version
Object progres() {
try {
while (draw) {
updateDrawing();
updateStatus(0);
if (Thread.interrupted()) {
throw new InterruptedException();
}
Thread.sleep(10);
}
} catch (InterruptedException e) {
updateStatus(0);
return "Interrupted"; // SwingWorker.get() returns this
}
return "Done?"; // or this
}
/* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
public void run() {
worker = new SwingWorker() {
public Object construct() {
return doWork();
}
public void finished() {
startButton.setEnabled(true);
interruptButton.setEnabled(false);
drawButton.setEnabled(true);
functionBox.setEnabled(true);
// txtnpts.setEnabled(true);
statusField.setText(get().toString());
}
};
worker.start();
}
public String getAppletInfo() {
return "Title: Schrodinger, 23 Jul 2005\n"
+ "Author: Lazar Kovacevic\n"
+ "Simulation of the Schrodinger wave equation.";
}
public Schrodinger() {
}
public static void main(String args[]) {
Schrodinger applet = new Schrodinger();
JFrame frame = new JFrame(
"Scrodinger wave-equation simulation for a particle confined to a region 0 to L.");
// To close the application:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(applet);
frame.setSize(1050, 550);//TODO detach applet from webpage, make it rescalable, and rescale graph
applet.init();
applet.start();
frame.setVisible(true);
}
public void start() {
if (!p) {
} else {
p = false;
}
}
public void stop() {
if (p) {
} else {
p = true;
}
}
}
//