Example #1
0
/***********************************************************************//**
 * @brief Read model from XML element
 *
 * @param[in] xml XML element.
 *
 * @exception GException::model_invalid_parnum
 *            Invalid number of model parameters found in XML element.
 * @exception GException::model_invalid_parnames
 *            Invalid model parameter names found in XML element.
 *
 * Read the map cube information from an XML element. The XML element is
 * required to have 1 parameter named either "Normalization" or "Value".
 ***************************************************************************/
void GModelSpatialCube::read(const GXmlElement& xml)
{
    // Verify that XML element has exactly 1 parameters
    if (xml.elements() != 1 || xml.elements("parameter") != 1) {
        throw GException::model_invalid_parnum(G_READ, xml,
              "Map cube spatial model requires exactly 1 parameter.");
    }

    // Get pointer on model parameter
    GXmlElement* par = static_cast<GXmlElement*>(xml.element("parameter", 0));

    // Get value
    if (par->attribute("name") == "Normalization" ||
        par->attribute("name") == "Value") {
        m_value.read(*par);
    }
    else {
        throw GException::model_invalid_parnames(G_READ, xml,
              "Map cube spatial model requires either \"Value\" or"
              " \"Normalization\" parameter.");
    }

    // Load map cube from file
    load_cube(xml.attribute("file"));

    // Return
    return;
}
Example #2
0
void Cube::load(FILE *file){
	load_cube(file);
}
int main(int argc, const char *argv[]) {
  printf("Running %s version %s\n", argv[0], version_identifier());
  for (int i=0; argv[i]; i++) {
    printf("%s ", argv[i]);
  }
  printf("\n");
  took("Starting program");
  // ----------------------------------------------------------------------------
  // Define "Constants" -- set from arguments then unchanged
  // ----------------------------------------------------------------------------

  // NOTE: debug can slow things down VERY much
  int debug = false;

  sw_simulation sw;

  sw.sticky_wall = 0;
  sw.len[0] = sw.len[1] = sw.len[2] = 1;
  sw.walls = 0;
  sw.N = 10;
  sw.translation_scale = 0.05;

  unsigned long int seed = 0;

  char *data_dir = new char[1024];
  sprintf(data_dir, "free-energy-data");
  char *filename = new char[1024];
  sprintf(filename, "default_filename");
  char *filename_suffix = new char[1024];
  sprintf(filename_suffix, "default_filename_suffix");
  long simulation_runs = 1000000;
  double acceptance_goal = .4;
  double R = 1;
  const double well_width = 1;
  double ff = 0.3;
  double ff_small = -1;
  bool force_cube=false;
  bool pack_and_save_cube=false;
  double neighbor_scale = 2;
  double de_g = 0.05;
  double max_rdf_radius = 10;
  int totime = 0;
  double scaling_factor = 1.0;
  int small_cell_check_period = (1 * sw.N * sw.N)/10;

  poptContext optCon;
  // ----------------------------------------------------------------------------
  // Set values from parameters
  // ----------------------------------------------------------------------------
  poptOption optionsTable[] = {
    {"N", '\0', POPT_ARG_INT, &sw.N, 0, "Number of balls to simulate", "INT"},
    {"ff", '\0', POPT_ARG_DOUBLE, &ff, 0, "Filling fraction. If specified, the "
     "cell dimensions are adjusted accordingly without changing the shape of "
     "the cell."},
    {"ff_small", '\0', POPT_ARG_DOUBLE, &ff_small, 0, "Small filling fraction. If specified, "
     "This sets the desired filling fraction of the shrunk cell. Otherwise it defaults to ff."},
    {"force_cube", '\0', POPT_ARG_NONE, &force_cube, 0, "forces box to be a cube", "BOOLEAN"},
    {"pack_and_save_cube", '\0', POPT_ARG_NONE, &pack_and_save_cube, 0, "packs cube, save positions, then returns", "BOOLEAN"},
    {"walls", '\0', POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &sw.walls, 0,
     "Number of walled dimensions (dimension order: x,y,z)", "INT"},
    {"runs", '\0', POPT_ARG_LONG | POPT_ARGFLAG_SHOW_DEFAULT, &simulation_runs,
     0, "Number of \"runs\" for which to run the simulation", "INT"},
    {"de_g", '\0', POPT_ARG_DOUBLE | POPT_ARGFLAG_SHOW_DEFAULT, &de_g, 0,
     "Resolution of distribution functions", "DOUBLE"},
    {"max_rdf_radius", '\0', POPT_ARG_DOUBLE | POPT_ARGFLAG_SHOW_DEFAULT,
     &max_rdf_radius, 0, "Set maximum radius for RDF data collection", "DOUBLE"},
    {"lenx", '\0', POPT_ARG_DOUBLE, &sw.len[x], 0,
     "Relative cell size in x dimension", "DOUBLE"},
    {"leny", '\0', POPT_ARG_DOUBLE, &sw.len[y], 0,
     "Relative cell size in y dimension", "DOUBLE"},
    {"lenz", '\0', POPT_ARG_DOUBLE, &sw.len[z], 0,
     "Relative cell size in z dimension", "DOUBLE"},
    {"filename", '\0', POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &filename, 0,
     "Base of output file names", "STRING"},
    {"filename_suffix", '\0', POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT,
     &filename_suffix, 0, "Output file name suffix", "STRING"},
    {"data_dir", '\0', POPT_ARG_STRING | POPT_ARGFLAG_SHOW_DEFAULT, &data_dir, 0,
     "Directory in which to save data", "data_dir"},
    {"neighbor_scale", '\0', POPT_ARG_DOUBLE | POPT_ARGFLAG_SHOW_DEFAULT,
     &neighbor_scale, 0, "Ratio of neighbor sphere radius to interaction scale "
     "times ball radius. Drastically reduces collision detections","DOUBLE"},
    {"translation_scale", '\0', POPT_ARG_DOUBLE | POPT_ARGFLAG_SHOW_DEFAULT,
     &sw.translation_scale, 0, "Standard deviation for translations of balls, "
     "relative to ball radius", "DOUBLE"},
    {"seed", '\0', POPT_ARG_INT | POPT_ARGFLAG_SHOW_DEFAULT, &seed, 0,
     "Seed for the random number generator", "INT"},
    {"acceptance_goal", '\0', POPT_ARG_DOUBLE | POPT_ARGFLAG_SHOW_DEFAULT,
     &acceptance_goal, 0, "Goal to set the acceptance rate", "DOUBLE"},
    {"time", '\0', POPT_ARG_INT, &totime, 0,
     "Timing of display information (seconds)", "INT"},
    {"R", '\0', POPT_ARG_DOUBLE | POPT_ARGFLAG_SHOW_DEFAULT,
     &R, 0, "Ball radius (for testing purposes; should always be 1)", "DOUBLE"},
    {"debug", '\0', POPT_ARG_NONE, &debug, 0, "Debug mode", "BOOLEAN"},
    {"sf", '\0', POPT_ARG_DOUBLE, &scaling_factor, 0,
      "Factor by which to scale the small cell", "DOUBLE"},
    {"sc_period", '\0', POPT_ARG_INT, &small_cell_check_period, 0,
     "Check the small cell every P iterations", "INT"},
    POPT_AUTOHELP
    POPT_TABLEEND
  };
  optCon = poptGetContext(NULL, argc, argv, optionsTable, 0);
  poptSetOtherOptionHelp(optCon, "[OPTION...]\nNumber of balls and filling "
                         "fraction or cell dimensions are required arguments.");

  int c = 0;
  // go through arguments, set them based on optionsTable
  while((c = poptGetNextOpt(optCon)) >= 0);
  if (c < -1) {
    fprintf(stderr, "\n%s: %s\n", poptBadOption(optCon, 0), poptStrerror(c));
    return 1;
  }
  poptFreeContext(optCon);

  // ----------------------------------------------------------------------------
  // Verify we have reasonable arguments and set secondary parameters
  // ----------------------------------------------------------------------------

  if(sw.walls >= 2){
    printf("Code cannot currently handle walls in more than one dimension.\n");
    return 254;
  }
  if(sw.walls > 3){
    printf("You cannot have walls in more than three dimensions.\n");
    return 254;
  }
  if(well_width < 1){
    printf("Interaction scale should be greater than (or equal to) 1.\n");
    return 254;
  }

  if(ff_small != -1 && scaling_factor != 1.0){
    printf("You can't specify both the small filling fraction and the scaling factor.");  
    return 1;
  }


  if (ff != 0) {
    // The user specified a filling fraction, so we must make it so!
    const double volume = 4*M_PI/3*R*R*R*sw.N/ff;
    const double min_cell_width = 2*sqrt(2)*R; // minimum cell width
    const int numcells = (sw.N+3)/4; // number of unit cells we need
    const int max_cubic_width
      = pow(volume/min_cell_width/min_cell_width/min_cell_width, 1.0/3);
    if (max_cubic_width*max_cubic_width*max_cubic_width >= numcells) {
      // We can get away with a cubic cell, so let's do so.  Cubic
      // cells are nice and comfortable!
      sw.len[x] = sw.len[y] = sw.len[z] = pow(volume, 1.0/3);
    } else {
      printf("N = %d\n", sw.N);
      printf("ff = %g\n", ff);
      printf("min_cell_width = %g\n", min_cell_width);
      printf("volume**1/3 = %g\n", pow(volume, 1.0/3));
      printf("max_cubic_width**3 = %d, numcells = %d\n",
             max_cubic_width*max_cubic_width*max_cubic_width, numcells);
      // A cubic cell won't work with our initialization routine, so
      // let's go with a lopsided cell that should give us something
      // that will work.
      int xcells = int( pow(numcells, 1.0/3) );
      int cellsleft = (numcells + xcells - 1)/xcells;
      int ycells = int( sqrt(cellsleft) );
      int zcells = (cellsleft + ycells - 1)/ycells;

      // The above should give a zcells that is largest, followed by
      // ycells and then xcells.  Thus we make the lenz just small
      // enough to fit these cells, and so on, to make the cell as
      // close to cubic as possible.
      sw.len[z] = zcells*min_cell_width;
      if (xcells == ycells) {
        sw.len[x] = sw.len[y] = sqrt(volume/sw.len[z]);
      } else {
        sw.len[y] = min_cell_width*ycells;
        sw.len[x] = volume/sw.len[y]/sw.len[z];
      }
      printf("Using lopsided %d x %d x %d cell (total goal %d)\n",
             xcells, ycells, zcells, numcells);
    }
  }

  // if the user didn't specifiy a small filling fraction, then we should get it.
  if(ff_small == -1){
      ff_small = (4*M_PI/3*R*R*R*sw.N)/
        (sw.len[x]*sw.len[y]*sw.len[z]*scaling_factor*scaling_factor*scaling_factor);
  }
  // if they did, then we need to get the scale factor
  else{
    scaling_factor = std::cbrt(ff/ff_small);
  }


  printf("\nSetting cell dimensions to (%g, %g, %g).\n",
         sw.len[x], sw.len[y], sw.len[z]);
  printf("\nFilling fraction of small cell is %g", ff_small);
  if (sw.N <= 0 || simulation_runs < 0 || R <= 0 ||
      neighbor_scale <= 0 || sw.translation_scale < 0 ||
      sw.len[x] < 0 || sw.len[y] < 0 || sw.len[z] < 0) {
    fprintf(stderr, "\nAll parameters must be positive.\n");
    return 1;
  }

  const double eta = (double)sw.N*4.0/3.0*M_PI*R*R*R/(sw.len[x]*sw.len[y]*sw.len[z]);
  if (eta > 1) {
    fprintf(stderr, "\nYou're trying to cram too many balls into the cell. "
            "They will never fit. Filling fraction: %g\n", eta);
    return 7;
  }

  // If a filename was not selected, make a default
  if (strcmp(filename, "default_filename") == 0) {
    char *wall_tag = new char[100];
    if(sw.walls == 0) sprintf(wall_tag,"periodic");
    else if(sw.walls == 1) sprintf(wall_tag,"wall");
    else if(sw.walls == 2) sprintf(wall_tag,"tube");
    else if(sw.walls == 3) sprintf(wall_tag,"box");
    sprintf(filename, "%s-ww%04.2f-ff%04.2f-N%i-sf%f",
            wall_tag, well_width, eta, sw.N, scaling_factor);
    printf("\nUsing default file name: ");
    delete[] wall_tag;
  }
  else
    printf("\nUsing given file name: ");
  // If a filename suffix was specified, add it
  if (strcmp(filename_suffix, "default_filename_suffix") != 0)
    sprintf(filename, "%s-%s", filename, filename_suffix);
  printf("%s\n",filename);

  printf("------------------------------------------------------------------\n");
  printf("Running %s with parameters:\n", argv[0]);
  for(int i = 1; i < argc; i++) {
    if(argv[i][0] == '-') printf("\n");
    printf("%s ", argv[i]);
  }
  printf("\nUsing scaling factor of %f", scaling_factor);
  printf("\n");
  if (totime > 0) printf("Timing information will be displayed.\n");
  if (debug) printf("DEBUG MODE IS ENABLED!\n");
  else printf("Debug mode disabled\n");
  printf("------------------------------------------------------------------\n\n");

  // ----------------------------------------------------------------------------
  // Define sw_simulation variables
  // ----------------------------------------------------------------------------

  sw.iteration = 0; // start at zeroeth iteration
  sw.max_entropy_state = 0;
  sw.min_energy_state = 0;
  sw.energy = 0;
  sw.min_important_energy = 0;

  // translation distance should scale with ball radius
  sw.translation_scale *= R;

  // neighbor radius should scale with radius and interaction scale
  sw.neighbor_R = neighbor_scale*R*well_width;

  // Find the upper limit to the maximum number of neighbors a ball could have
  sw.max_neighbors = max_balls_within(2+neighbor_scale*well_width);

  // Energy histogram and weights
  sw.interaction_distance = 2*R*well_width;
  sw.energy_levels = 1; // hard spheres can only have one energy!
  sw.energy_histogram = new long[sw.energy_levels]();
  sw.ln_energy_weights = new double[sw.energy_levels]();

  // Observed and sampled energies
  sw.pessimistic_observation = new bool[sw.energy_levels]();
  sw.pessimistic_samples = new long[sw.energy_levels]();
  sw.optimistic_samples = new long[sw.energy_levels]();

  // Transitions from one energy to another
  sw.biggest_energy_transition = max_balls_within(sw.interaction_distance);
  sw.transitions_table =
    new long[sw.energy_levels*(2*sw.biggest_energy_transition+1)]();

  // Walker histograms
  sw.walkers_up = new long[sw.energy_levels]();


  printf("memory use estimate = %.2g G\n\n",
         8*double(6*sw.energy_levels)/1024/1024/1024);

  sw.balls = new ball[sw.N];

  if(totime < 0) totime = 10*sw.N;

  // Initialize the random number generator with our seed
  random::seed(seed);

  // ----------------------------------------------------------------------------
  // Set up the initial grid of balls
  // ----------------------------------------------------------------------------

  for(int i = 0; i < sw.N; i++) // initialize ball radii
    sw.balls[i].R = R;

    //###########################  pack and save  ####################
  if(pack_and_save_cube==true){
	  INITBOX *box=load_cube(data_dir);
	  if(box!=NULL && box->numAtoms<sw.N) {delete box; box=NULL;}
	  if(box!=NULL){//---------loaded baseAtoms up to max of sw.N
		  printf("loading from baseAtoms.txt\n");
		  for(int count=0;count<(100*box->numAtoms);count++){
			  //mixes indices so the culled atoms are random
			  //atoms are only culled from the end...
			  int n1=floor(random::ran()*(box->numAtoms));
			  int n2=floor(random::ran()*(box->numAtoms));
			  double xTemp=box->list[n1].x;
			  double yTemp=box->list[n1].y;
			  double zTemp=box->list[n1].z;
			  box->list[n1].x=box->list[n2].x;
			  box->list[n1].y=box->list[n2].y;
			  box->list[n1].z=box->list[n2].z;
			  box->list[n2].x=xTemp;
			  box->list[n2].y=yTemp;
			  box->list[n2].z=zTemp;
		  }
		  //box2 is used to cull atoms from the end (if needed)
		  //box2 is simulated to mix the atoms up after culling
		  INITBOX *box2=new INITBOX(box->lx);
		  for(int i=0;i<sw.N;i++) box2->addAtom(box->list[i].x,box->list[i].y,box->list[i].z);
		  delete box;
		  box2->temperature=1000.0;
		  for(int i=0;i<100;i++) box2->simulate(sw.N*100);//mix box
		  sw.len[x] = sw.len[y] = sw.len[z] = box2->lx;
		  for(int i=0;i<sw.N;i++) {
			  sw.balls[i].pos=vector3d(box2->list[i].x,box2->list[i].y,box2->list[i].z);
		  }
		  delete box2;
	  }
	  else{//if no baseAtoms.txt then try to pack the box
		if(ff==0){printf("must initialize using --ff\n"); return 254;}
		printf("building baseAtoms.txt\n");
		const double volume = 4*M_PI/3*R*R*R*sw.N/ff;
		sw.len[x] = sw.len[y] = sw.len[z] = pow(volume, 1.0/3);
		if(pack_cube(sw)==false) {printf("could not fill box\n"); return 254;}
	}//done initializing positions
	  mkdir(data_dir, 0777); // create save directory
	  
	  char *posData = new char[4096];
	  sprintf(posData,"%s/baseAtoms.txt",data_dir);
	  FILE *pos_out = fopen((const char *)posData, "w");
	  sprintf(posData,"L = %g\nN = %i\n",sw.len[x],sw.N);
	  fprintf(pos_out,"%s",posData);
	  for(int i=0;i<sw.N;i++){
		sprintf(posData,"%g %g %g",
				sw.balls[i].pos.x,sw.balls[i].pos.y,sw.balls[i].pos.z);
		fprintf(pos_out,"%s",posData);
		if(i+1<sw.N) fprintf(pos_out,"\n");
	  }
      fclose(pos_out);
      delete[] posData;
      took("Placement");
      return 0;
  }
  //###########################  DONE pack and save  #################
  
  if(force_cube==true){//try to load baseAtoms.txt
	  if(ff==0){printf("must initialize using --ff\n"); return 254;}
	  printf("attempting to load baseAtoms.txt\n");
	  const double volume = 4*M_PI/3*R*R*R*sw.N/ff;
	  sw.len[x] = sw.len[y] = sw.len[z] = pow(volume, 1.0/3);
	  INITBOX *box=load_cube(data_dir);
	  if(box==NULL) {
		  printf("requires initialization on the smallest box\nuse the --pack_and_save --ff -N   flags\n");
		  return 254; }
	  if(box->numAtoms<sw.N){
		  printf("boxAtoms.txt contains less atoms than expected\n");
		  delete box;
		  return 254; }
	  if(box->lx>sw.len[x]){
		  printf("baseAtoms must be in a box smaller than current simulation\n");
		  delete box;
		  return 254; }
	  box->temperature=1000.0;
	  for(int i=0;i<100;i++) box->simulate(sw.N*100);//mix box
      double scaleFactor=sw.len[x]/box->lx;
	  for(int i=0;i<sw.N;i++){
          double x=(box->list[i].x)*scaleFactor;
          double y=(box->list[i].y)*scaleFactor;
          double z=(box->list[i].z)*scaleFactor;
		  sw.balls[i].pos=vector3d(x,y,z);
	  }
	  delete box;
  }
  else {
	  // Balls will be initially placed on a face centered cubic (fcc) grid
	  // Note that the unit cells need not be actually "cubic", but the fcc grid will
      //   be stretched to cell dimensions
	  const double min_cell_width = 2*sqrt(2)*R; // minimum cell width
	  const int spots_per_cell = 4; // spots in each fcc periodic unit cell
	  int cells[3]; // array to contain number of cells in x, y, and z dimensions
	  for(int i = 0; i < 3; i++){
	    cells[i] = int(sw.len[i]/min_cell_width); // max number of cells that will fit
	  }
	
	  // It is usefull to know our cell dimensions
	  double cell_width[3];
	  for(int i = 0; i < 3; i++) cell_width[i] = sw.len[i]/cells[i];
	
	  // If we made our cells to small, return with error
	  for(int i = 0; i < 3; i++){
	    if(cell_width[i] < min_cell_width){
	      printf("Placement cell size too small: (%g,  %g,  %g) coming from (%g, %g, %g)\n",
	             cell_width[0],cell_width[1],cell_width[2],
	             sw.len[0], sw.len[1], sw.len[2]);
	      printf("Minimum allowed placement cell width: %g\n",min_cell_width);
	      printf("Total simulation cell dimensions: (%g,  %g,  %g)\n",
	             sw.len[0],sw.len[1],sw.len[2]);
	      printf("Fixing the chosen ball number, filling fractoin, and relative\n"
	             "  simulation cell dimensions simultaneously does not appear to be possible\n");
	      return 176;
	    }
	  }
	
	  // Define ball positions relative to cell position
	  vector3d* offset = new vector3d[4]();
	  offset[x] = vector3d(0,cell_width[y],cell_width[z])/2;
	  offset[y] = vector3d(cell_width[x],0,cell_width[z])/2;
	  offset[z] = vector3d(cell_width[x],cell_width[y],0)/2;
	
	  // Reserve some spots at random to be vacant
	  const int total_spots = spots_per_cell*cells[x]*cells[y]*cells[z];
	  bool *spot_reserved = new bool[total_spots]();
	  int p; // Index of reserved spot
	  for(int i = 0; i < total_spots-sw.N; i++) {
	    p = floor(random::ran()*total_spots); // Pick a random spot index
	    if(spot_reserved[p] == false) // If it's not already reserved, reserve it
	      spot_reserved[p] = true;
	    else // Otherwise redo this index (look for a new spot)
	      i--;
	  }
	
	  // Place all balls in remaining spots
	  int b = 0;
	  for(int i = 0; i < cells[x]; i++) {
	    for(int j = 0; j < cells[y]; j++) {
	      for(int k = 0; k < cells[z]; k++) {
	        for(int l = 0; l < 4; l++) {
	          if(!spot_reserved[i*(4*cells[z]*cells[y])+j*(4*cells[z])+k*4+l]) {
	            sw.balls[b].pos = vector3d(i*cell_width[x],j*cell_width[y],
	                                       k*cell_width[z]) + offset[l];
	            b++;
	          }
	        }
	      }
	    }
	  }
	  delete[] offset;
	  delete[] spot_reserved;
  }
  took("Placement");

  // ----------------------------------------------------------------------------
  // Print info about the initial configuration for troubleshooting
  // ----------------------------------------------------------------------------

  {
    int most_neighbors =
      initialize_neighbor_tables(sw.balls, sw.N, sw.neighbor_R,
                                 sw.max_neighbors, sw.len, sw.walls);
    if (most_neighbors < 0) {
      fprintf(stderr, "The guess of %i max neighbors was too low. Exiting.\n",
              sw.max_neighbors);
      return 1;
    }
    printf("Neighbor tables initialized.\n");
    printf("The most neighbors is %i, whereas the max allowed is %i.\n",
           most_neighbors, sw.max_neighbors);
  }

  // ----------------------------------------------------------------------------
  // Make sure initial placement is valid
  // ----------------------------------------------------------------------------

  for(int i = 0; i < sw.N; i++) {
    for(int j = 0; j < i; j++) {
      if (overlap(sw.balls[i], sw.balls[j], sw.len, sw.walls)) {
        print_bad(sw.balls, sw.N, sw.len, sw.walls);
        printf("Error in initial placement: balls are overlapping.\n");
        return 253;
      }
    }
  }

  fflush(stdout);

  // ----------------------------------------------------------------------------
  // Initialization of cell
  // ----------------------------------------------------------------------------

  sw.initialize_translation_distance();

  // --------------------------------------------------------------------------
  // end initilization routine.
  // --------------------------------------------------------------------------

  // ----------------------------------------------------------------------------
  // Generate info to put in save files
  // ----------------------------------------------------------------------------

  mkdir(data_dir, 0777); // create save directory

  char *headerinfo = new char[4096];
  sprintf(headerinfo,
          "# version: %s\n"
          "# cell dimensions: (%g, %g, %g)\n"
          "# walls: %i\n"
          "# de_g: %g\n"
          "# seed: %li\n"
          "# N: %i\n"
          "# R: %f\n"
          "# well_width: %g\n"
          "# translation_scale: %g\n"
          "# neighbor_scale: %g\n"
          "# scaling factor: %g\n"
          "# ff: %g\n"
          "# ff_small: %g\n",
          version_identifier(),
          sw.len[0], sw.len[1], sw.len[2], sw.walls, de_g, seed, sw.N, R,
          well_width, sw.translation_scale, neighbor_scale, scaling_factor, ff, ff_small);

  char *g_fname = new char[1024];
  sprintf(g_fname, "%s/%s.dat", data_dir, filename);

  took("Initialization");

  // ----------------------------------------------------------------------------
  // MAIN PROGRAM LOOP
  // ----------------------------------------------------------------------------

  sw.moves.total = 0;
  sw.moves.working = 0;
  sw.iteration = 0;

  // tracking for free energy
  int current_valid_run = 0;
  int current_failed_run = 0;
  int longest_valid_run = 0;
  int longest_failed_run = 0;
  int total_checks_of_small_cell = 0;
  int total_valid_small_checks = 0;
  int total_failed_small_checks = 0;
  int valid_runs = 0;
  int failed_runs = 0;
  double average_valid_run = 0;
  double average_failed_run = 0;

  // Reset energy histogram and sample counts
  for(int i = 0; i < sw.energy_levels; i++){
    sw.energy_histogram[i] = 0;
    sw.pessimistic_samples[i] = 0;
    sw.optimistic_samples[i] = 0;
  }

  do {
    // ---------------------------------------------------------------
    // Move each ball once, add to energy histogram
    // ---------------------------------------------------------------
    for(int i = 0; i < sw.N; i++){
      sw.move_a_ball();
    }

    // just hacking stuff in to see what works
    // do the small bit every 100 n^2 iterations for now
    if (sw.iteration % small_cell_check_period == 0) {
      total_checks_of_small_cell++;

      if(overlap_in_small_cell(sw,  scaling_factor)){
        total_failed_small_checks++;
        
        if (current_failed_run == 0){
          // then valid run just ended so record it
          valid_runs++;
          if(current_valid_run > longest_valid_run)
            longest_valid_run = current_valid_run;
          average_valid_run = average_valid_run + (current_valid_run - average_valid_run)/valid_runs;
          current_valid_run = 0;
        }

        current_failed_run++;

        if(debug){printf("%i - false\n", current_failed_run);}
      }
      else{
        total_valid_small_checks++;

        if (current_valid_run == 0){
          // then failed run just ended so record it
          failed_runs++;
          if(current_failed_run > longest_failed_run)
            longest_failed_run = current_failed_run;
          average_failed_run = average_failed_run + (current_failed_run - average_failed_run)/failed_runs;
          current_failed_run = 0;
        }

        current_valid_run++;

        if(debug){printf("%i - true\n", current_valid_run);}
      }
    }

    // ---------------------------------------------------------------
    // Save to file
    // ---------------------------------------------------------------

    if (time_to_save() || valid_runs == simulation_runs) {
      const clock_t now = clock();
      const double secs_done = double(now)/CLOCKS_PER_SEC;
      const int seconds = int(secs_done) % 60;
      const int minutes = int(secs_done / 60) % 60;
      const int hours = int(secs_done / 3600) % 24;
      const int days = int(secs_done / 86400);
      const long percent_done = 100*valid_runs/simulation_runs;
      printf("Saving data after %i days, %02i:%02i:%02i, %li iterations (%ld%%) "
             "complete.\n", days, hours, minutes, seconds, sw.iteration,
             percent_done);
      fflush(stdout);

      char *countinfo = new char[4096];
      sprintf(countinfo,
              "# iterations: %li\n"
              "# working moves: %li\n"
              "# total moves: %li\n"
              "# acceptance rate: %g\n"
              "# longest failed run: %i\n"
              "# longest valid run: %i\n"
              "# total checks of small cell: %i\n"
              "# total failed small checks: %i\n"
              "# total valid small checks: %i\n"
              "# valid runs: %i\n"
              "# failed runs: %i\n"
              "# average valid run: %g\n"
              "# average failed run: %g\n\n",
              sw.iteration, sw.moves.working, sw.moves.total,
              double(sw.moves.working)/sw.moves.total, longest_failed_run,
              longest_valid_run, total_checks_of_small_cell, total_failed_small_checks,
              total_valid_small_checks, valid_runs, failed_runs, average_valid_run, average_failed_run);

      // Save data
      if(!sw.walls){
        FILE *g_out = fopen((const char *)g_fname, "w");
        fprintf(g_out, "%s", headerinfo);
        fprintf(g_out, "%s", countinfo);
        fclose(g_out);
      }

      delete[] countinfo;
    }
  } while (valid_runs < simulation_runs);
  // ----------------------------------------------------------------------------
  // END OF MAIN PROGRAM LOOP
  // ----------------------------------------------------------------------------

  for (int i=0; i<sw.N; i++) {
    delete[] sw.balls[i].neighbors;
  }
  delete[] sw.balls;
  delete[] sw.ln_energy_weights;
  delete[] sw.energy_histogram;

  delete[] sw.transitions_table;

  delete[] sw.walkers_up;

  delete[] sw.pessimistic_observation;
  delete[] sw.pessimistic_samples;
  delete[] sw.optimistic_samples;

  delete[] headerinfo;
  delete[] g_fname;

  delete[] data_dir;
  delete[] filename;
  delete[] filename_suffix;

  return 0;
}