//package org.opensourcephysics.sip.ch08.md;
import org.opensourcephysics.controls.*;
import org.opensourcephysics.frames.*;
import org.opensourcephysics.display.GUIUtils;

/**
 * LJParticlesApp simulates a creates either an LJParticles (granular particles interacting via a Lennard-Jones potential)
 * or a SiloParticles object (granular particles inside of a silo with a hole of variable size in the base)
 *
 * Daniel Costantino, based on LJParticlesApp by
 * @author Jan Tobochnik, Wolfgang Christian, Harvey Gould
 * @version 1.0 revised 03/28/05, 3/29/05
 */

public class LJParticlesApp extends AbstractSimulation {
   LJParticles md;	// the type of object will be determined by a user input
   PlotFrame temperatureData = new PlotFrame("time", "temperature", "Mean Temperature");
			// plots the temperature (average kinetic energy) of the system)
   PlotFrame numberPlot = new PlotFrame("time", "Number of Particles", "Number of Particles");
			// plots the number of particles as a function of time (only for a SiloParticles object though
   //PlotFrame pressureData = new PlotFrame("time", "PA/NkT", "Mean Pressure");
			// this shouldn't be necessary but in case I decide to play with it later, I left it in

   HistogramFrame xVelocityHistogram = new HistogramFrame("vx", "H(vx)", "X-Velocity Histogram");
   HistogramFrame yVelocityHistogram = new HistogramFrame("vy", "H(vy)", "Y-Velocity Histogram");
   HistogramFrame speedHistogram = new HistogramFrame("speed", "H(speed)", "Speed Histogram");
			// plot histograms of the x and y components of velocity as well as its magnitude
   DisplayFrame display = new DisplayFrame("x", "y", "Lennard-Jones Particles");
			// plot the position of all the particles (as well as the hole in a silo if we're in one)
   double temperature = 0;	// the temperature of the system
   boolean silo;		// keeps track of if the simulation is for a silo or not

//initialize() reads in the user inputs and then initializes md according to the method in LJParticles()
//--------------------------------------------------------------------------------------------------------------------------
   public void initialize() {
	display.clearDrawables();// each time, the "LJ Particles" frame should be cleared

      silo = control.getBoolean("Silo Set-Up?");
		// check to see if the user wants to simulate particles in a silo, and create md accordingly
	if(!silo) {
		md = new LJParticles();
	} else {
		md = new SiloParticles();
	}
      md.isSilo = silo;

      md.N = control.getInt("Number of Particles");	// the number of particles in the simulation
      md.initialKineticEnergy = control.getDouble("initial kinetic energy per particle");
							// the initial kinetic energy per particle
      md.Lx = control.getDouble("Lx");			// the width and
      md.Ly = control.getDouble("Ly");			// height of the grain's container
      md.rCutOff = control.getDouble("CutOff Factor / 2^(1/6)");
							// how far away particles can interact with each other
      md.rCutOff = md.rCutOff * Math.pow(2, 1/6);	// default is the minimum of the Lennard-Jones potential

      md.gamma = control.getDouble("Damping Factor");	// coefficient of the damping force to remove energy from collisions
      md.giveRandom = control.getBoolean("Give Random Kicks?");
      md.desiredKE = control.getDouble("Desired KE per particle");	// a kinetic energy to be added if the system
									// allows kicks of energy to be added
      md.stepsBetween = control.getInt("Find Neighbor Time");	// how often the system should look for which particles are
								// close to one another
      md.dt = control.getDouble("dt");			// size of the time step
      md.holeDiam = control.getDouble("Aperture Size");	// how large the silo's aperture should be, if the simulation is
							// modeling a silo
      md.holeDiam = md.holeDiam * 2 * md.rCutOff;	// convert the aperture into grain diameters

      md.nuclear = control.getBoolean("Have Close CutOff?");	// artificial controls to make sure the temperature of the
      md.bootstrap = control.getBoolean("Artificial Temperature Reduction?");	// system does not explode
      md.theMatrix = control.getBoolean("Allow Time Slow Down?");
      md.initialize();

      display.addDrawable(md);
      display.setPreferredMinMax(0, md.Lx, 0, md.Ly);

      xVelocityHistogram.setBinWidth(2*md.initialKineticEnergy/md.N);
      yVelocityHistogram.setBinWidth(2*md.initialKineticEnergy/md.N);
      speedHistogram.setBinWidth(2*md.initialKineticEnergy/md.N);	// this makes the bin-size for the histograms
									// 2 * initialKE / N (allows for resolution of
									// vAveInitial^2 / N
   }

//doStep() advances the time step by calling step() in LJParticles and appends the new information onto the appropriate
//plots---------------------------------------------------------------------------------------------------------------------
   public void doStep() {
      control.clearMessages();
      md.step(xVelocityHistogram, yVelocityHistogram, speedHistogram);
      
      temperature = md.getMeanTemperature();
      temperatureData.append(0, md.t, temperature);	// graph the temperature at this time
      temperature = 1000 * temperature;
      temperature = temperature + 0.5;
      temperature = (int) temperature;
      temperature = temperature / 1000;
      control.println("Temperature = "+temperature);	// output the temperature with only the first few digits after
							// the decimal point

      control.println("Time = "+md.t);

      if(silo) {
	numberPlot.append(0, md.t, md.nPart);		// if it's a silo problem, graph the number of particles in the
      }							// container

   }

//stop() takes care of printing statistics about the run once it is done
//------------------------------------------------------------------------------------------------------------------------
   public void stop() {
      control.println("Density = "+decimalFormat.format(md.rho));
      control.println("Number of time steps = "+md.steps);
      control.println("Time step dt = "+decimalFormat.format(md.dt));
      control.println("<T>= "+decimalFormat.format(md.getMeanTemperature()));
      control.println("<E> = "+decimalFormat.format(md.getMeanEnergy()));

   }

//startRunning() reads in the adjustable parameters and checks to see if they've changed
//-----------------------------------------------------------------------------------------------------------------------
   public void startRunning() {
      md.dt = control.getDouble("dt");
      double Lx = control.getDouble("Lx");
      double Ly = control.getDouble("Ly");
      double desiredKE = control.getDouble("Desired KE per particle");

      if((Lx!=md.Lx)||(Ly!=md.Ly) || (desiredKE != md.desiredKE)) {
					// if any of the adjustables have changed, read in the new values and calculate
					// the possibly changed neighbors list and acceleration
         md.Lx = Lx;
         md.Ly = Ly;
	 md.desiredKE = desiredKE;
	 control.println("desired KE"+desiredKE);

	 md.findNeighbors();
         md.computeAcceleration();
         display.setPreferredMinMax(0, Lx, 0, Ly);
         resetData();
      }
   }

//reset() establishes the default values for the various inputs read in during initialize()
//-----------------------------------------------------------------------------------------------------------------------
   public void reset() {
      control.setValue("Number of Particles", 64);
      control.setAdjustableValue("Lx", 20.0);
      control.setAdjustableValue("Ly", 20.0);
      control.setValue("initial kinetic energy per particle", 10.0);

      control.setValue("CutOff Factor / 2^(1/6)", 1.0);
      control.setValue("Damping Factor", 100);
      control.setAdjustableValue("dt", 0.001);

      control.setValue("Find Neighbor Time", 20);

      control.setValue("Give Random Kicks?", false);
      control.setAdjustableValue("Desired KE per particle", 5.0);

      control.setValue("Silo Set-Up?", false);
      control.setValue("Aperture Size", 5);

      control.setValue("Have Close CutOff?", false);
      control.setValue("Artificial Temperature Reduction?", false);
      control.setValue("Allow Time Slow Down?", false);

      enableStepsPerDisplay(true);
      display.setSquareAspect(true); // so particles will appear as circular disks
   }

//resetData() clears all the drawing frames and current averages-----------------------------------------------------------
   public void resetData() {
      md.resetAverages();
      GUIUtils.clearDrawingFrameData(false);  // clears old data from the plot frames
   }
 

//main starts the program running by creating the control object and adding the button resetData
//-------------------------------------------------------------------------------------------------------------------------
   public static void main(String[] args) {
      SimulationControl control = SimulationControl.createApp(new LJParticlesApp());
      control.addButton("resetData", "Reset Data");
   }
} // ends LJParticlesApp