Beispiel #1
0
   // zexit with error message
   void zexit(std::string message){

      // check calling of routine if error checking is activated
      if(err::check==true){std::cout << "err::zexit(std::string) has been called" << std::endl;}

      // Abort MPI processes for parallel execution
      #ifdef MPICF
      std::cerr << "Fatal error on rank " << vmpi::my_rank << ": " << message << std::endl;
      std::cerr << "Aborting program." << std::endl;
      zlog << zTs() << "Fatal error on rank " << vmpi::my_rank << ": " << message << std::endl;
      zlog << zTs() << "Aborting program." << std::endl;

      // concatenate log and sort
      #ifdef WIN_COMPILE
         system("type log.* 2>NUL | sort > log");
      #else
         system("ls log.* | xargs cat | sort -n > log");
      #endif
      MPI::COMM_WORLD.Abort(EXIT_FAILURE);
      // MPI program dies ungracefully here
      #else
         // Print Error message to screen and log
         std::cerr << "Fatal error: " << message << std::endl;
         zlog << zTs() << "Fatal error: " << message << std::endl;

      #endif

      // Print error message to screen and log
      zlog << zTs() << "Aborting program." << std::endl;
      std::cout << "Aborting program. See log file for details." << std::endl;

      // Now exit program disgracefully
      exit(EXIT_FAILURE);
   }
//------------------------------------------------------------------------------------------------------
// Function to initialize data structures
//------------------------------------------------------------------------------------------------------
void susceptibility_statistic_t::initialize(stats::magnetization_statistic_t& mag_stat) {

   // Check that magnetization statistic is properly initialized
   if(!mag_stat.is_initialized()){
      terminaltextcolor(RED);
      std::cerr << "Programmer Error - Uninitialized magnetization statistic passed to susceptibility statistic - please initialize first." << std::endl;
      terminaltextcolor(WHITE);
      zlog << zTs() << "Programmer Error - Uninitialized magnetization statistic passed to susceptibility statistic - please initialize first." << std::endl;
      err::vexit();
   }

   // Determine number of magnetization statistics*4
   std::vector<double> temp = mag_stat.get_magnetization();
   num_elements = temp.size()/4;

   // Now set number of susceptibility values to match
   mean_susceptibility.resize(4*num_elements,0.0);
   mean_susceptibility_squared.resize(4*num_elements,0.0);
   mean_absolute_susceptibility.resize(4*num_elements,0.0);
   mean_absolute_susceptibility_squared.resize(4*num_elements,0.0);

   // copy saturation data
   saturation = mag_stat.saturation;

   // initialize mean counter
   mean_counter = 0.0;

   // Set flag indicating correct initialization
   initialized=true;

}
Beispiel #3
0
   //---------------------------------------------------------------------------
   // Function to process material parameters
   //---------------------------------------------------------------------------
   bool match_material_parameter(std::string const word, std::string const value, std::string const unit, int const line, int const super_index, const int sub_index, const int max_materials){

      // add prefix string
      std::string prefix="material:";

      // Check for empty material parameter array and resize to avoid segmentation fault
      if(internal::mp.size() == 0){
         internal::mp.resize(max_materials);
      }

      //------------------------------------------------------------
      // Check for material properties
      //------------------------------------------------------------
      std::string test = "dmi-constant"; // short form
      std::string test2 = "dzyaloshinskii-moriya-interaction-constant"; // long form
      if( (word == test) || (word == test2) ){
         double dmi = atof(value.c_str());
         vin::check_for_valid_value(dmi, word, line, prefix, unit, "energy", -1e-17, 1e-17,"material"," < +/- 1.0e17");
         internal::mp[super_index].dmi[sub_index] = dmi;
         internal::enable_dmi = true; // Switch on dmi calculation and fully unrolled tensorial anisotropy
         return true;
      }
      test = "exchange-matrix";
      if(word==test){
         // extract comma separated values from string
         std::vector<double> Jij = vin::doubles_from_string(value);
         if(Jij.size() == 1){
            vin::check_for_valid_value(Jij[0], word, line, prefix, unit, "energy", -1e-18, 1e-18,"material"," < +/- 1.0e18");
            // set all components in case vectorial form is needed later
            vin::read_material[super_index].Jij_matrix_SI[sub_index][0] = Jij[0]; // Import exchange as field
            vin::read_material[super_index].Jij_matrix_SI[sub_index][1] = Jij[0];
            vin::read_material[super_index].Jij_matrix_SI[sub_index][2] = Jij[0];
            return true;
         }
         else if(Jij.size() == 3){
            vin::check_for_valid_vector(Jij, word, line, prefix, unit, "energy", -1e-18, 1e-18,"material"," < +/- 1.0e18");
            vin::read_material[super_index].Jij_matrix_SI[sub_index][0] = Jij[0]; // Import exchange as field
            vin::read_material[super_index].Jij_matrix_SI[sub_index][1] = Jij[1];
            vin::read_material[super_index].Jij_matrix_SI[sub_index][2] = Jij[2];
            // set vectorial anisotropy
            internal::exchange_type = internal::vectorial;
            return true;
         }
         else{
            terminaltextcolor(RED);
            std::cerr << "Error in input file - material[" << super_index << "]:exchange_matrix[" << sub_index << "] must have one or three values." << std::endl;
            terminaltextcolor(WHITE);
            zlog << zTs() << "Error in input file - material[" << super_index << "]:exchange_matrix[" << sub_index << "] must have one or three values." << std::endl;
            err::vexit();
            return false;
         }
      }

      //--------------------------------------------------------------------
      // Keyword not found
      //--------------------------------------------------------------------
      return false;

   }
Beispiel #4
0
   //-------------------------------------------------------------------------------
   // Function to initialize create module
   //-------------------------------------------------------------------------------
   void initialize(){

      // If create material parameters uninitialised then initialise with default parameters
      if(create::internal::mp.size() == 0){
         create::internal::mp.resize(mp::num_materials);
      }

      // Loop over materials to check for invalid input and warn appropriately
		for(int mat=0;mat<mp::num_materials;mat++){
			const double lmin=create::internal::mp[mat].min;
			const double lmax=create::internal::mp[mat].max;
			for(int nmat=0;nmat<mp::num_materials;nmat++){
				if(nmat!=mat){
					double min=create::internal::mp[nmat].min;
					double max=create::internal::mp[nmat].max;
					if(((lmin>min) && (lmin<max)) || ((lmax>min) && (lmax<max))){
						terminaltextcolor(RED);
						std::cerr << "Warning: Overlapping material heights found. Check log for details." << std::endl;
						terminaltextcolor(WHITE);
						zlog << zTs() << "Warning: material " << mat+1 << " overlaps material " << nmat+1 << "." << std::endl;
						zlog << zTs() << "If you have defined geometry then this may be OK, or possibly you meant to specify alloy keyword instead." << std::endl;
						zlog << zTs() << "----------------------------------------------------" << std::endl;
						zlog << zTs() << "  Material "<< mat+1 << ":minimum-height = " << lmin << std::endl;
						zlog << zTs() << "  Material "<< mat+1 << ":maximum-height = " << lmax << std::endl;
						zlog << zTs() << "  Material "<< nmat+1 << ":minimum-height = " << min << std::endl;
						zlog << zTs() << "  Material "<< nmat+1 << ":maximum-height = " << max << std::endl;
					}
				}
			}
		}

      return;
   }
//--------------------------------------------------
// Add point to list of input points
//
void lattice_anis_t::add_point(double temperature, double anisotropy){

   // check for valid temperature value
   if( temperature < 0.0 && temperature > 10000.0 ){
      std::cerr << "Error: Temperature value " << temperature << " in lattice anisotropy file is invalid. Temperature values must be in the range 0 - 10000 K. Exiting." << std::endl;
      zlog << zTs() << "Error: Temperature value " << temperature << " in lattice anisotropy file is invalid. Temperature values must be in the range 0 - 10000 K. Exiting." << std::endl;
      err::vexit();
   }

   // add values to list
   T.push_back(temperature);
   k.push_back(anisotropy);

   return;
}
Beispiel #6
0
/// Main function for vampire
/// Prints out program header and calls main program routines
int main(int argc, char* argv[]){

   //=============================================================
   // Check for valid command-line arguments
   //=============================================================
   std::string infile="input";

   for(int arg = 1; arg < argc; arg++){
      std::string sw=argv[arg];
      // input file
      if(sw=="-f"){
         // check number of args not exceeded
         if(arg+1 < argc){
            arg++;
            infile=string(argv[arg]);
         }
         else{
            std::cerr << "Error - no file specified for \'-f\' command line option" << std::endl;
            return EXIT_FAILURE;
         }
      }
      else{
         std::cerr << "Error - unknown command line parameter \'" << sw << "\'" << std::endl;
         return EXIT_FAILURE;
      }
   }

   // For parallel execution intialise MPI
   #ifdef MPICF
      vmpi::initialise();
   #endif

   // Initialise log file
   vout::zLogTsInit(std::string(argv[0]));

   // Output Program Header
   if(vmpi::my_rank==0){
      std::cout << "                                                _          " << std::endl;
      std::cout << "                                               (_)         " << std::endl;
      std::cout << "                    __   ____ _ _ __ ___  _ __  _ _ __ ___ " << std::endl;
      std::cout << "                    \\ \\ / / _` | '_ ` _ \\| '_ \\| | '__/ _ \\" << std::endl;
      std::cout << "                     \\ V / (_| | | | | | | |_) | | | |  __/" << std::endl;
      std::cout << "                      \\_/ \\__,_|_| |_| |_| .__/|_|_|  \\___|" << std::endl;
      std::cout << "                                         | |               " << std::endl;
      std::cout << "                                         |_|               " << std::endl;
      std::cout << std::endl;
      std::cout << "                      Version 3.0.3 " << __DATE__ << " " << __TIME__ << std::endl;
      std::cout << std::endl;

      std::cout << "  Licensed under the GNU Public License(v2). See licence file for details." << std::endl;
      std::cout << std::endl;
      std::cout << "  Lead Developer: Richard F L Evans <*****@*****.**>" << std::endl;
      std::cout << std::endl;
      std::cout << "  Contributors: Weijia Fan, Phanwadee Chureemart, Joe Barker, " << std::endl;
      std::cout << "                Thomas Ostler, Andreas Biternas, Roy W Chantrell" << std::endl;
      std::cout << " " << std::endl;
      #ifdef COMP
      std::cout << "                Compiled with:  " << COMP << std::endl;
      #endif
      std::cout << "                Compiler Flags: ";
      #ifdef CUDA
      std::cout << "CUDA ";
      #endif
      #ifdef MPICF
      std::cout << "MPI ";
      #endif
      std::cout << std::endl;
      std::cout << std::endl;
      std::cout << "================================================================================" << std::endl;
      time_t rawtime = time(NULL);
      struct tm * timeinfo = localtime(&rawtime);
      std::cout<<asctime(timeinfo);
   }


   #ifdef MPICF
      vmpi::hosts();
   #endif
  
   #ifdef MPICF
      // nullify non root cout stream
      if(vmpi::my_rank!=0){
         vout::nullify(std::cout);
      }
   #endif

   // Initialise system
   mp::initialise(infile);

   // Create system
   cs::create();

   // Simulate system
   sim::run();

   // Finalise MPI
   #ifdef MPICF
      vmpi::finalise();
      // concatenate log, sort, and append departure message.
      #ifdef WIN_COMPILE
         if(vmpi::num_processors!=1 && vmpi::my_rank==0) system("type log.* 2>NUL | sort > log");
      #else
         if(vmpi::num_processors!=1 && vmpi::my_rank==0) system("ls log.* | xargs cat | sort -n > log");
      #endif
   #endif

   zlog << zTs() << "Simulation ended gracefully." << std::endl;
   std::cout << "Simulation ended gracefully." << std::endl;

   return EXIT_SUCCESS;

}
Beispiel #7
0
int voronoi_film(std::vector<cs::catom_t> & catom_array){

	// check calling of routine if error checking is activated
	if(err::check==true){
      terminaltextcolor(RED);
      std::cerr << "cs::voronoi_film has been called" << std::endl;
      terminaltextcolor(WHITE);
   }
	//====================================================================================
	//
	//														voronoi
	//
	//				Subroutine to create granular system using qhull voronoi generator
	//
	//							Version 1.0 R Evans 16/07/2009
	//
	//====================================================================================
	//
	//		Locally allocated variables: 	init_grain_coord_array
	//												init_grain_pointx_array
	//												init_grain_pointy_array
	//												init_num_assoc_vertices_array
	//
	//=====================================================================================


	//---------------------------------------------------
	// Local constants
	//---------------------------------------------------
	const int max_vertices=50;
	double grain_sd=create_voronoi::voronoi_sd;

	// Set number of particles in x and y directions
	double size = create::internal::voronoi_grain_size + create::internal::voronoi_grain_spacing;
	double grain_cell_size_x = size;
	double grain_cell_size_y = sqrt(3.0)*size;

	int num_x_particle = 4+2*vmath::iround(cs::system_dimensions[0]/(grain_cell_size_x));
	int num_y_particle = 4+2*vmath::iround(cs::system_dimensions[1]/(grain_cell_size_y));

	int init_num_grains = num_x_particle*num_y_particle*2;

	// Define initial grain arrays
	std::vector <std::vector <double> > grain_coord_array;
	std::vector <std::vector <std::vector <double> > > grain_vertices_array;

	// Reserve space for pointers
	grain_coord_array.reserve(init_num_grains);
	grain_vertices_array.reserve(init_num_grains);

	// Calculate pointers
	for(int grain=0;grain<init_num_grains;grain++){
		grain_coord_array.push_back(std::vector <double>());
		grain_coord_array[grain].reserve(2);
		grain_vertices_array.push_back(std::vector <std::vector <double> >());
		//for(int vertex=0;vertex<max_vertices;vertex++){
		//	grain_vertices_array[grain].push_back(std::vector <double>());
		//	grain_vertices_array[grain][vertex].reserve(2);
		//}
		//std::cout << grain_vertices_array[grain].size() << " " << grain_vertices_array[grain].capacity() << std::endl;
	}
	//std::cin.get();
	double delta_particle_x = grain_cell_size_x;
	double delta_particle_y = grain_cell_size_y;
	double delta_particle_x_parity = delta_particle_x*0.5;
	double delta_particle_y_parity = delta_particle_y*0.5;

	// Set voronoi seed;
	mtrandom::grnd.seed(mtrandom::voronoi_seed);

	// Loop to generate hexagonal lattice points
	double particle_coords[2];

	int vp=int(create_voronoi::parity);
	int grain=0;

	for (int x_particle=0;x_particle < num_x_particle;x_particle++){
		for (int y_particle=0;y_particle < num_y_particle;y_particle++){
			for (int particle_parity=0;particle_parity<2;particle_parity++){

				//particle_coords[0] = (particle_parity)*delta_particle_x_parity + delta_particle_x*x_particle-size + vp*double(1-2*particle_parity)*delta_particle_x_parity;
				//particle_coords[1] = (particle_parity)*delta_particle_y_parity + delta_particle_y*y_particle-size;
				particle_coords[0] = (particle_parity)*delta_particle_x_parity + delta_particle_x*x_particle + vp*double(1-2*particle_parity)*delta_particle_x_parity;
				particle_coords[1] = (particle_parity)*delta_particle_y_parity + delta_particle_y*y_particle;

				grain_coord_array[grain].push_back(particle_coords[0]+grain_sd*mtrandom::gaussian()*delta_particle_x);
				grain_coord_array[grain].push_back(particle_coords[1]+grain_sd*mtrandom::gaussian()*delta_particle_y);

				grain++;
			}
		}
	}
	//-----------------------
	// Check for grains >=1
	//-----------------------
	if(grain<1){
		terminaltextcolor(RED);
		std::cerr << "Error! - No grains found in structure - Increase system dimensions" << std::endl;
		terminaltextcolor(WHITE);
		zlog << zTs() << "Error! - No grains found in structure - Increase system dimensions" << std::endl;
		err::vexit();
	}

   // Calculate Voronoi construction using qhull removing boundary grains (false)
	create::internal::populate_vertex_points(grain_coord_array, grain_vertices_array, false);

	// Shrink Voronoi vertices in reduced coordinates to get spacing
	double shrink_factor = create::internal::voronoi_grain_size/(create::internal::voronoi_grain_size+create::internal::voronoi_grain_spacing);

	// Reduce vertices to relative coordinates
	for(unsigned int grain=0;grain<grain_coord_array.size();grain++){
		//grain_coord_array[grain][0]=0.0;
		//grain_coord_array[grain][1]=0.0;
		const int nv = grain_vertices_array[grain].size();
		// Exclude grains with zero vertices
      if(nv!=0){
			for(int vertex=0;vertex<nv;vertex++){
				double vc[2]; // vertex coordinates
				vc[0]=shrink_factor*(grain_vertices_array[grain][vertex][0]-grain_coord_array[grain][0]);
				vc[1]=shrink_factor*(grain_vertices_array[grain][vertex][1]-grain_coord_array[grain][1]);
				grain_vertices_array[grain][vertex][0]=vc[0];
				grain_vertices_array[grain][vertex][1]=vc[1];
			}
		}
	}

   // round grains if necessary
	if(create_voronoi::rounded) create::internal::voronoi_grain_rounding(grain_coord_array, grain_vertices_array);

	// Create a 2D supercell array of atom numbers to improve performance for systems with many grains
	std::vector < std::vector < std::vector < int > > > supercell_array;

	int min_bounds[3];
	int max_bounds[3];

	min_bounds[0]=0;
	min_bounds[1]=0;
	min_bounds[2]=0;
	max_bounds[0]=cs::total_num_unit_cells[0];
	max_bounds[1]=cs::total_num_unit_cells[1];
	max_bounds[2]=cs::total_num_unit_cells[2];

	// allocate supercell array
	int dx = max_bounds[0]-min_bounds[0];
	int dy = max_bounds[1]-min_bounds[1];

	supercell_array.resize(dx);
	for(int i=0;i<dx;i++) supercell_array[i].resize(dy);

	// loop over atoms and populate supercell array
	for(unsigned int atom=0;atom<catom_array.size();atom++){
		int cx = int (catom_array[atom].x/unit_cell.dimensions[0]);
		int cy = int (catom_array[atom].y/unit_cell.dimensions[1]);
		supercell_array.at(cx).at(cy).push_back(atom);
	}

	// Determine order for core-shell grains
   std::list<create::internal::core_radius_t> material_order(0);
   for(int mat=0;mat<mp::num_materials;mat++){
      create::internal::core_radius_t tmp;
      tmp.mat=mat;
      tmp.radius=mp::material[mat].core_shell_size;
      material_order.push_back(tmp);
   }
   // sort by increasing radius
   material_order.sort(create::internal::compare_radius);

	std::cout <<"Generating Voronoi Grains";
	zlog << zTs() << "Generating Voronoi Grains";

   // arrays to store list of grain vertices
   double tmp_grain_pointx_array[max_vertices];
	double tmp_grain_pointy_array[max_vertices];

	// loop over all grains with vertices
	for(unsigned int grain=0;grain<grain_coord_array.size();grain++){
		// Exclude grains with zero vertices

		if((grain%(grain_coord_array.size()/10))==0){
		  std::cout << "." << std::flush;
		  zlog << "." << std::flush;
		}
		if(grain_vertices_array[grain].size()!=0){

			// initialise minimum and max supercell coordinates for grain
			int minx=10000000;
			int maxx=0;
			int miny=10000000;
			int maxy=0;

			// Set temporary vertex coordinates (real) and compute cell ranges
			int num_vertices = grain_vertices_array[grain].size();
			for(int vertex=0;vertex<num_vertices;vertex++){
				// determine vertex coordinates
				tmp_grain_pointx_array[vertex]=grain_vertices_array[grain][vertex][0];
				tmp_grain_pointy_array[vertex]=grain_vertices_array[grain][vertex][1];
				// determine unit cell coordinates encompassed by grain
				int x = int((tmp_grain_pointx_array[vertex]+grain_coord_array[grain][0])/unit_cell.dimensions[0]);
				int y = int((tmp_grain_pointy_array[vertex]+grain_coord_array[grain][1])/unit_cell.dimensions[1]);
				if(x < minx) minx = x;
				if(x > maxx) maxx = x;
				if(y < miny) miny = y;
				if(y > maxy) maxy = y;
			}

			// determine coordinate offset for grains
			const double x0 = grain_coord_array[grain][0];
			const double y0 = grain_coord_array[grain][1];

			// loopover cells
			for(int i=minx;i<=maxx;i++){
				for(int j=miny;j<=maxy;j++){

					// loop over atoms in cells;
					for(unsigned int id=0;id<supercell_array[i][j].size();id++){
						int atom = supercell_array[i][j][id];

						// Get atomic position
						double x = catom_array[atom].x;
						double y = catom_array[atom].y;

						if(mp::material[catom_array[atom].material].core_shell_size>0.0){
							// Iterate over materials
							for(std::list<create::internal::core_radius_t>::iterator it = material_order.begin(); it !=  material_order.end(); it++){
								int mat = (it)->mat;
								double factor = mp::material[mat].core_shell_size;
								double maxz=create::internal::mp[mat].max*cs::system_dimensions[2];
								double minz=create::internal::mp[mat].min*cs::system_dimensions[2];
								double cz=catom_array[atom].z;
                        const int atom_uc_cat = catom_array[atom].uc_category;
                        const int mat_uc_cat = create::internal::mp[mat].unit_cell_category;
								// check for within core shell range
								if(vmath::point_in_polygon_factor(x-x0,y-y0,factor, tmp_grain_pointx_array,tmp_grain_pointy_array,num_vertices)==true){
									if((cz>=minz) && (cz<maxz) && (atom_uc_cat == mat_uc_cat) ){
										catom_array[atom].include=true;
										catom_array[atom].material=mat;
										catom_array[atom].grain=grain;
									}
									// if set to clear atoms then remove atoms within radius
									else if(cs::fill_core_shell==false){
										catom_array[atom].include=false;
									}
								}
							}
						}
						// Check to see if site is within polygon
						else if(vmath::point_in_polygon_factor(x-x0,y-y0,1.0,tmp_grain_pointx_array,tmp_grain_pointy_array,num_vertices)==true){
							catom_array[atom].include=true;
							catom_array[atom].grain=grain;
						}
					}
				}
			}
		}
	}
	terminaltextcolor(GREEN);
	std::cout << "done!" << std::endl;
	terminaltextcolor(WHITE);
	zlog << "done!" << std::endl;

	// add final grain for continuous layer
	grain_coord_array.push_back(std::vector <double>());
	grain_coord_array[grain_coord_array.size()-1].push_back(0.0); // x
	grain_coord_array[grain_coord_array.size()-1].push_back(0.0); // y

	// check for continuous layer
	for(unsigned int atom=0; atom < catom_array.size(); atom++){
	  if(mp::material[catom_array[atom].material].continuous==true && catom_array[atom].include == false ){
	    catom_array[atom].include=true;
	    catom_array[atom].grain=int(grain_coord_array.size()-1);
	  }
	}

	// set number of grains
	grains::num_grains = int(grain_coord_array.size());

	// sort atoms by grain number
	sort_atoms_by_grain(catom_array);

	return EXIT_SUCCESS;
}
//--------------------------------------------------
// Creates a lookup table of interpolated functions
// to calculate lattice anisotropy
//
void lattice_anis_t::set_interpolation_table(){

   // Output informative message to log
   zlog << zTs() << "Determining interpolation variables for tabulated lattice anisotropy." << std::endl;

   // Check for undefined lattice anisotropy
   if(T.size()==0){
      Tmax=0;
      k_Tmax=0.0;
      return;
   }

   // check T(i+1) > T(i)
   for(unsigned int i=1; i<T.size(); i++){
      if(T[i]<T[i-1]){
         std::cerr << "Error: temperature value "<< T[i] <<" on line " << i+2 << " is less than the previous value " << T[i-1] << " and must be given in ascending order. Exiting" << std::endl;
         zlog << zTs() << "Error: temperature value "<< T[i] <<" on line " << i+2 << " is less than the previous value " << T[i-1] << " and must be given in ascending order. Exiting" << std::endl;
         err::vexit();
      }
   }

   // loop over all temperatures up to Tmax and create temporary list of min, max, m and c values
   std::vector<double> tmin;
   std::vector<double> tmax;
   std::vector<double> tm;
   std::vector<double> tc;

   // start from i+1
   for(unsigned int i=1; i<T.size(); i++){
      tmin.push_back(T[i-1]);
      tmax.push_back(T[i]);
      tm.push_back(vmath::interpolate_m(T[i-1],k[i-1],T[i],k[i]));
      tc.push_back(vmath::interpolate_c(T[i-1],k[i-1],T[i],k[i]));
   }

   // determine maximum temperature specified in interpolation table
   Tmax=int(T[T.size()-1]);

   // resize interpolation array to size Tmax
   m.resize(Tmax,0.0);
   c.resize(Tmax,0.0);

   // determine last value of k at Tmax
   k_Tmax = k[k.size()-1];

   // determine first value of k
   double k_Tmin = k[0];
   double Tmin = T[0];

   // loop over all values up to Tmax and substitute m and c interpolation
   for(unsigned int Ti=0; Ti<Tmax; Ti++){ // 1 Kelvin resolution

      bool found_mc=false;

      // Check for T<Tmin
      if(double(Ti)<double(Tmin)){

         m[Ti] = 0;
         c[Ti] = k_Tmin;

         //mark valid value found
         found_mc = true;

         // move to next temperature value
         continue;

      }

      // loop over all possible values
      for(unsigned int mm=0; mm<tmin.size(); mm++){

         double min = tmin[mm];
         double max = tmax[mm];

         // check if Ti is in temperature range
         if( double(Ti) >= min && double(Ti) < max ){

            // copy interpolation values
            m[Ti] = tm[mm];
            c[Ti] = tc[mm];

            // mark valid function found
            found_mc = true;

            // move to next temperature value Ti
            break;

         }
      }

      // check for value not in range
      if(found_mc==false){
         std::cerr << "Code error: Temperature value " << Ti << " in interpolation function is not within range specified in lattice-anisotropy-file. Exiting" << std::endl;
         zlog << zTs() << "Code error: Temperature value " << Ti << " in interpolation function is not within range specified in lattice-anisotropy-file. Exiting" << std::endl;
         err::vexit();
      }

   }

   return;

}
Beispiel #9
0
   //---------------------------------------------------------------------------------------------------------------------
   // Setting algorithm for exchange bias in IrMn3
   //---------------------------------------------------------------------------------------------------------------------
   void setting_process(){

      //Arrays to determine the 8 possible ground state spin orientations configurations
      double Configurations[8][3] = {
         {0,     0,      -1},
         {-0.94, 0,      0.3},
         {0.44,  -0.845, 0.3},
         {0.44,  0.845,  0.3},
         {0,     0,      1},
         {0.94,  0,      -0.3},
         {-0.44, 0.845,  -0.3},
         {-0.44, -0.845, -0.3}
      };
      // List the 8 possible combinations of spin orientation
      double Possible_Arrays[8][4] = {
         {0,1,2,3},
         {1,0,3,2},
         {2,3,0,1},
         {3,2,1,0},
         {4,5,6,7},
         {5,4,7,6},
         {6,7,4,5},
         {7,6,5,4}
      };

      std::vector< std::vector <int> > No_in_Sublattice;

      // Number sublattices in each grain
      No_in_Sublattice.resize(4);

      // resize array for correct number of atoms in each grain
      //    std::cout << grains::num_grains <<std::endl;
      for(int i = 0; i < 4; i++) No_in_Sublattice[i].resize(grains::num_grains,0.0);

      std::vector <int> Local_Sub (grains::num_grains*4,0);
      std::vector <int> Chosen_array(grains::num_grains,0);
      std::vector <int> Largest_Sublattice(grains::num_grains,0);
      std::vector <int> Max_atoms(grains::num_grains,0);

      #ifdef MPICF
         stats::num_atoms = vmpi::num_core_atoms+vmpi::num_bdry_atoms;
      #else
         stats::num_atoms = atoms::num_atoms;
      #endif

      std::cerr << "A" << stats::num_atoms << "\t" << grains::num_grains << std::endl;

      //Calculates how many atoms are in the top layer of each sublattice in each grain.
      for (int atom = 0; atom < stats::num_atoms; atom++){

         for (int neighbour = atoms::neighbour_list_start_index[atom]; neighbour < atoms::neighbour_list_end_index[atom]; neighbour ++){
            // explain what if statement is testing - yes Sarah...
            //std::cout << atom << "\t" << neighbour <<atoms::type_array[atom] << '\t' << atoms::type_array[atoms::neighbour_list_array[neighbour]] << std::endl;
            if ((atoms::type_array[atom] >3) && (atoms::type_array[atoms::neighbour_list_array[neighbour]] < 4)){
               //         std::cerr << atoms::grain_array[atom] << "\t"<< atoms::type_array[atoms::neighbour_list_array[neighbour]] << "\t" << atoms::type_array[atom] <<endl;
               No_in_Sublattice[atoms::type_array[atoms::neighbour_list_array[neighbour]]][atoms::grain_array[atom]]++;
               // cerr << No_in_Sublattice[atoms::type_array[atoms::neighbour_list_array[neighbour]]][atoms::grain_array[atom]]<<endl;
            }
         }
      }

      int k = 0;
      for (int j = 0; j < grains::num_grains; j ++){
         for (int i = 0; i < 4; i ++){
            Local_Sub[k] = No_in_Sublattice[i][j];
            k++;

         }

      }
      std::cerr << "before" << Local_Sub[0] << '\t' << Local_Sub[1] << '\t' << Local_Sub[2] << '\t' << Local_Sub[3] << std::endl;

      #ifdef MPICF
         MPI_Allreduce(MPI_IN_PLACE, &Local_Sub[0],grains::num_grains*4,MPI_INT,MPI_SUM, MPI_COMM_WORLD);
      #endif

      std::cerr<< "after" << Local_Sub[0] << '\t' << Local_Sub[1] << '\t' << Local_Sub[2] << '\t' << Local_Sub[3] << std::endl;

      int l =0;

      //calculates which sublattice contains the most atoms for each grain.
      for (int j = 0; j < grains::num_grains; j++){

         for( int i = 0; i <4; i ++){
            //       std::cout << Local_Sub[l] << "\t" << Local_Sub[l] << std::endl;
            if ((Local_Sub[l] > Max_atoms[j]) & (Local_Sub[l] != 0)){
               Largest_Sublattice[j] = i;
               Max_atoms[j] =Local_Sub[l];
            }
            l++;
         }
         if (Local_Sub[l-1] !=0 ) cerr << Largest_Sublattice[j] <<endl;
      }
      int j = 0;


      for (int i = 0; i < grains::num_grains*4; i= i +4){
         if (Local_Sub[i] != 0){
            cerr <<"number in each sublattice for grain" << j << ";" <<  Local_Sub[i] << "\t" << Local_Sub[i +1] << "\t" << Local_Sub[i+2] << "\t" << Local_Sub[i+3] << "\t" << std::endl;
            zlog << zTs() <<"number in each sublattice for grain" << j << ";" <<  Local_Sub[i] << "\t" << Local_Sub[i +1] << "\t" << Local_Sub[i+2] << "\t" << Local_Sub[i+3] << "\t" << std::endl;
            j++;
         }
      }

      double result,angle;
      double min_angle = 1000;
      double Direction_Closest_to_Field;

      //Loop over all possible combinations to calculate which possible vector is closest to the applied field.
      for (int i = 0; i < 8; i ++){
         result = Configurations[i][0]*sim::H_vec[0] + Configurations[i][1]*sim::H_vec[1] + Configurations[i][2]*sim::H_vec[2];
         angle = acos(result);
         //Calculates the minimum angle between the applied field and the spin directions.
         if ( angle< min_angle){
            Direction_Closest_to_Field = i;
            min_angle = angle;
         }
      }
      // std::cout << Direction_Closest_to_Field <<std::endl;

      //Sets the sublattice with the largest number of atoms along the direction nearest the field
      //This minimises S.H
      for (int j = 0; j < grains::num_grains; j ++){
         for (int i = 0; i <8; i ++){
            if (Possible_Arrays[i][Largest_Sublattice[j]] == Direction_Closest_to_Field){
               //   std::cout << i <<std::endl;
               Chosen_array[j] = i;
               break;
            }
         }
      }
      
      std::cout << "a" <<std::endl;
      for (int i = 0; i <stats::num_atoms; i++){
         //	std::cout << atoms::type_array[i] << "\t" << atoms::z_spin_array[i] <<std::endl;
         if(atoms::type_array[i] > 3){
            atoms::x_spin_array[i] = sim::H_vec[0];
            atoms::y_spin_array[i] = sim::H_vec[1];
            atoms::z_spin_array[i] = sim::H_vec[2];

         }

         else {

            int Array = Possible_Arrays[Chosen_array[atoms::grain_array[i]]][atoms::type_array[i]];
            atoms::x_spin_array[i] = Configurations[Array][0];
            atoms::y_spin_array[i] = Configurations[Array][1];
            atoms::z_spin_array[i] = Configurations[Array][2];
         }
      }

      // Calculate magnetisation statistics
      stats::mag_m();

      // Output data
      vout::data();

   }
Beispiel #10
0
/// @brief Cell initialiser function
///
/// @details Determines number of cells, assigns atoms to cells, and sets up cell arrays
///
/// @section License
/// Use of this code, either in source or compiled form, is subject to license from the authors.
/// Copyright \htmlonly &copy \endhtmlonly Richard Evans, 2009-2011. All Rights Reserved.
///
/// @section Information
/// @author  Richard Evans, [email protected]
/// @version 1.0
/// @date    28/03/2011
///
/// @return EXIT_SUCCESS
/// 
/// @internal
///	Created:		05/02/2011
///	Revision:	  ---
///=====================================================================================
///
	int initialise(){
		
		// check calling of routine if error checking is activated
		if(err::check==true) std::cout << "cells::initialise has been called" << std::endl;
		
		cells::num_cells=0;
		cells::num_local_cells=0;
		
		zlog << zTs() << "Cell size = " << cells::size << std::endl; 
		
		// determine number of cells in each direction (with small shift to prevent the fence post problem)
		unsigned int ncellx = static_cast<unsigned int>(ceil((cs::system_dimensions[0]+0.01)/cells::size));
		unsigned int ncelly = static_cast<unsigned int>(ceil((cs::system_dimensions[1]+0.01)/cells::size));
		unsigned int ncellz = static_cast<unsigned int>(ceil((cs::system_dimensions[2]+0.01)/cells::size));
		
		//update total number of cells
		cells::num_cells=ncellx*ncelly*ncellz;
		
		zlog << zTs() << "Cells in x,y,z: " << ncellx << "\t" << ncelly << "\t" << ncellz << std::endl;
		zlog << zTs() << "Total number of cells: " << cells::num_cells << std::endl;
		zlog << zTs() << "Memory required for cell arrays: " << 80.0*double(cells::num_cells)/1.0e6 << " MB" << std::endl;

		// Determine number of cells in x,y,z
		const int d[3]={ncellx,ncelly,ncellz};
	
		// Set cell counter
		int cell=0;
		
		// Declare array for create space for 3D supercell array
		int*** supercell_array;
		//std::cout << "Memory required for cell list calculation:" << 8.0*double(d[0])*double(d[1])*double(d[2])/1.0e6 << " MB" << std::endl;
		try{supercell_array=new int**[d[0]];
			for(int i=0; i<d[0] ; i++){
				supercell_array[i]=new int*[d[1]];
				for(int j=0; j<d[1] ; j++){
					supercell_array[i][j]=new int[d[2]];
					for(int k=0; k<d[2] ; k++){
						supercell_array[i][j][k]=cell;
						cell++;
					}
				}
			}
		}
		catch(...){std::cerr << "Error allocating supercell_array for cell list calculation" << std::endl;err::vexit();}
		
		// slightly offset atomic coordinates to prevent fence post problem
      double atom_offset[3]={0.01,0.01,0.01};

		// For MPI version, only add local atoms
		#ifdef MPICF
			int num_local_atoms = vmpi::num_core_atoms+vmpi::num_bdry_atoms;
		#else
			int num_local_atoms = atoms::num_atoms;
		#endif

		// Assign atoms to cells                                                                                                                  
		for(int atom=0;atom<num_local_atoms;atom++){
			double c[3]={atoms::x_coord_array[atom]+atom_offset[0],atoms::y_coord_array[atom]+atom_offset[1],atoms::z_coord_array[atom]+atom_offset[2]};
			int scc[3]={0,0,0}; // super cell coordinates
			for(int i=0;i<3;i++){
				scc[i]=int(c[i]/cells::size); // Always round down for supercell coordinates
				// Always check cell in range
				if(scc[i]<0 || scc[i]>= d[i]){
					std::cerr << "Error - atom out of supercell range in neighbourlist calculation!" << std::endl;
					#ifdef MPICF
					std::cerr << "\tCPU Rank: " << vmpi::my_rank << std::endl;
					#endif 
					std::cerr << "\tAtom number:      " << atom << std::endl;
					std::cerr << "\tAtom coordinates: " << c[0] << "\t" << c[1] << "\t" << c[2] << "\t" << std::endl;
					std::cerr << "\tCell coordinates: " << scc[0] << "\t" << scc[1] << "\t" << scc[2] << "\t" << std::endl;
					std::cerr << "\tCell maxima:      " << d[0] << "\t" << d[1] << "\t" << d[2] << std::endl;
					err::vexit();
				}
			}
			// If no error for range then assign atom to cell.
			atoms::cell_array[atom]=supercell_array[scc[0]][scc[1]][scc[2]];
		}
		
		// Deallocate supercell array
		try{
			for(int i=0; i<d[0] ; i++){
				for(int j=0; j<d[1] ;j++){
					delete [] supercell_array[i][j];
				}
				delete [] supercell_array[i];
			}
		delete [] supercell_array;
		supercell_array=NULL;
		}
		catch(...){zlog << zTs() << "error deallocating supercell_array" << std::endl; err::vexit();}
		
		// Resize new cell arrays
		cells::x_coord_array.resize(cells::num_cells,0.0);
		cells::y_coord_array.resize(cells::num_cells,0.0);
		cells::z_coord_array.resize(cells::num_cells,0.0);
		
		cells::x_mag_array.resize(cells::num_cells,0.0);
		cells::y_mag_array.resize(cells::num_cells,0.0);
		cells::z_mag_array.resize(cells::num_cells,0.0);
		
		cells::x_field_array.resize(cells::num_cells,0.0);
		cells::y_field_array.resize(cells::num_cells,0.0);
		cells::z_field_array.resize(cells::num_cells,0.0);
		
		cells::num_atoms_in_cell.resize(cells::num_cells,0);
      cells::volume_array.resize(cells::num_cells,0.0);

      std::vector<double> total_moment_array(cells::num_cells,0.0);

		// Now add atoms to each cell as magnetic 'centre of mass'
		for(int atom=0;atom<num_local_atoms;atom++){
			int local_cell=atoms::cell_array[atom];
         int type = atoms::type_array[atom];
         const double mus = mp::material[type].mu_s_SI;
			cells::x_coord_array[local_cell]+=atoms::x_coord_array[atom]*mus;
			cells::y_coord_array[local_cell]+=atoms::y_coord_array[atom]*mus;
			cells::z_coord_array[local_cell]+=atoms::z_coord_array[atom]*mus;
         total_moment_array[local_cell]+=mus;
         cells::num_atoms_in_cell[local_cell]++;
		}

		// For MPI sum coordinates from all CPUs
		#ifdef MPICF
			MPI::COMM_WORLD.Allreduce(MPI_IN_PLACE,&cells::num_atoms_in_cell[0],cells::num_cells,MPI_INT,MPI_SUM);
			MPI::COMM_WORLD.Allreduce(MPI_IN_PLACE,&cells::x_coord_array[0],cells::num_cells,MPI_DOUBLE,MPI_SUM);
			MPI::COMM_WORLD.Allreduce(MPI_IN_PLACE,&cells::y_coord_array[0],cells::num_cells,MPI_DOUBLE,MPI_SUM);
			MPI::COMM_WORLD.Allreduce(MPI_IN_PLACE,&cells::z_coord_array[0],cells::num_cells,MPI_DOUBLE,MPI_SUM);
         MPI::COMM_WORLD.Allreduce(MPI_IN_PLACE,&total_moment_array[0],cells::num_cells,MPI_DOUBLE,MPI_SUM);
      #endif
		
		//if(vmpi::my_rank==0){
			//vinfo << "=========================================================================" << std::endl;
			//vinfo << "Number of atoms/cell: cell number, num atoms, coord" << std::endl;
			//vinfo << "=========================================================================" << std::endl;
		//}

      // Used to calculate magnetisation in each cell. Poor approximation when unit cell size ~ system size.
      const double atomic_volume = cs::unit_cell.dimensions[0]*cs::unit_cell.dimensions[1]*cs::unit_cell.dimensions[2]/cells::num_atoms_in_unit_cell;

		// Now find mean coordinates via magnetic 'centre of mass'
		for(int local_cell=0;local_cell<cells::num_cells;local_cell++){
			if(cells::num_atoms_in_cell[local_cell]>0){
            cells::x_coord_array[local_cell] = cells::x_coord_array[local_cell]/(total_moment_array[local_cell]);
            cells::y_coord_array[local_cell] = cells::y_coord_array[local_cell]/(total_moment_array[local_cell]);
            cells::z_coord_array[local_cell] = cells::z_coord_array[local_cell]/(total_moment_array[local_cell]);
            cells::volume_array[local_cell] = double(cells::num_atoms_in_cell[local_cell])*atomic_volume;
         }
			//if(vmpi::my_rank==0){
			//vinfo << local_cell << "\t" << cells::num_atoms_in_cell[local_cell] << "\t";
			//vinfo << cells::x_coord_array[local_cell] << "\t" << cells::y_coord_array[local_cell];
			//vinfo << "\t" << cells::z_coord_array[local_cell] << "\t" << std::endl;
			//}
		}

		//Set number of atoms in cell to zero
		for(int cell=0;cell<cells::num_cells;cell++){
		  cells::num_atoms_in_cell[cell]=0;
		}
		
		// Now re-update num_atoms in cell for local atoms only
		for(int atom=0;atom<num_local_atoms;atom++){
			int local_cell=atoms::cell_array[atom];
			cells::num_atoms_in_cell[local_cell]++;
		}
		
		// Calculate number of local cells
		for(int cell=0;cell<cells::num_cells;cell++){
			if(cells::num_atoms_in_cell[cell]!=0){
				cells::local_cell_array.push_back(cell);
				cells::num_local_cells++;
			}
		}
		
		zlog << zTs() << "Number of local cells on rank " << vmpi::my_rank << ": " << cells::num_local_cells << std::endl;
		
		cells::initialised=true;

      // Precalculate cell magnetisation
      cells::mag();

		return EXIT_SUCCESS;
	}
Beispiel #11
0
//------------------------------------------------------------------------------
//  Function to calculate neighbour interactions assuming fractional unit cell
//------------------------------------------------------------------------------
void calculate_interactions(unit_cell_t& unit_cell){

   // determine neighbour range
   const double rcut = unit_cell.cutoff_radius*exchange_interaction_range*1.001; // reduced to unit cell units
   const double rcutsq = rcut*rcut; // reduced to unit cell units

   // temporary for unit cell size
   const double ucsx = unit_cell.dimensions[0];
   const double ucsy = unit_cell.dimensions[1];
   const double ucsz = unit_cell.dimensions[2];

   // determine number of unit cells in x,y and z
   const int nx = 1 + 2*ceil(rcut); // number of replicated cells in x,y,z
   const int ny = 1 + 2*ceil(rcut);
   const int nz = 1 + 2*ceil(rcut);

   zlog << zTs() << "Generating neighbour interactions for a lattice of " << nx << " x " << ny << " x " << nz << " unit cells for neighbour calculation" << std::endl;

   // temporary array for replicated atoms
   std::vector<local::atom_t> ratoms;

   // replicate in x,y,z
   for(int x = 0; x < nx; ++x){
      for(int y = 0; y < ny; ++y){
         for(int z = 0; z < nz; ++z){
            for(int a=0; a < unit_cell.atom.size(); ++a){
               local::atom_t tmp;
               tmp.mat = unit_cell.atom[a].mat;
               tmp.x = (unit_cell.atom[a].x + double(x))*ucsx;
               tmp.y = (unit_cell.atom[a].y + double(y))*ucsy;
               tmp.z = (unit_cell.atom[a].z + double(z))*ucsz;
               tmp.id = a;
               tmp.idx = x; // unit cell id
               tmp.idy = y;
               tmp.idz = z;
               ratoms.push_back(tmp);
            }
         }
      }
   }

   // calculate neighbours and exchange for central cell
   int mid_cell_x = (nx-1)/2;
   int mid_cell_y = (ny-1)/2;
   int mid_cell_z = (nz-1)/2;

   const double nnrcut_sq = unit_cell.cutoff_radius*unit_cell.cutoff_radius*1.001*1.001; // nearest neighbour cutoff radius

   // loop over all i atoms
   for(int i=0; i < ratoms.size(); ++i){

      // check for i atoms only in central cell
      if( (ratoms[i].idx == mid_cell_x) && (ratoms[i].idy == mid_cell_y) && (ratoms[i].idz == mid_cell_z) ){

         // loop over all j atoms
         for(int j=0; j < ratoms.size(); ++j){

            // calculate interatomic radius_sq
            const double rx = ratoms[j].x - ratoms[i].x;
            const double ry = ratoms[j].y - ratoms[i].y;
            const double rz = ratoms[j].z - ratoms[i].z;
            double range_sq = rx*rx + ry*ry + rz*rz;
            // check for rij < rcut and i!= j
            if( range_sq < rcutsq && i != j ){
               // Neighbour found
               uc::interaction_t tmp;

               // Determine unit cell id for i and j atoms
               tmp.i = ratoms[i].id;
               tmp.j = ratoms[j].id;

               // Determine unit cell offsets
               tmp.dx = ratoms[j].idx - ratoms[i].idx;
               tmp.dy = ratoms[j].idy - ratoms[i].idy;
               tmp.dz = ratoms[j].idz - ratoms[i].idz;

               // save interaction range
               tmp.rij = sqrt(range_sq);

               // Determine normalised exchange constants
               tmp.Jij[0][0] = uc::internal::exchange(range_sq, nnrcut_sq); // xx
               tmp.Jij[0][1] = 0.0; // xy
               tmp.Jij[0][2] = 0.0; // xz

               tmp.Jij[1][0] = 0.0; // yx
               tmp.Jij[1][1] = uc::internal::exchange(range_sq, nnrcut_sq); // yy
               tmp.Jij[1][2] = 0.0; // yz

               tmp.Jij[2][0] = 0.0; // zx
               tmp.Jij[2][1] = 0.0; // zy
               tmp.Jij[2][2] = uc::internal::exchange(range_sq, nnrcut_sq); // zz

               unit_cell.interaction.push_back(tmp);

            }
         }
      }
   }

   // Set calculated interactions range
   int interaction_range=0;
   for(int i=0; i<unit_cell.interaction.size(); i++){
      if(abs(unit_cell.interaction[i].dx)>interaction_range) interaction_range=abs(unit_cell.interaction[i].dx);
      if(abs(unit_cell.interaction[i].dy)>interaction_range) interaction_range=abs(unit_cell.interaction[i].dy);
      if(abs(unit_cell.interaction[i].dz)>interaction_range) interaction_range=abs(unit_cell.interaction[i].dz);
   }
   unit_cell.interaction_range = interaction_range;

   // Normalise exchange interactions
   uc::internal::normalise_exchange(unit_cell);

   // Output interactions to screen
   /*for(int i=0; i<unit_cell.interaction.size(); i++){
      std::cerr << i << "\t" << unit_cell.interaction[i].i << "\t"
                << unit_cell.interaction[i].j << "\t"
                << unit_cell.interaction[i].dx << "\t"
                << unit_cell.interaction[i].dy << "\t"
                << unit_cell.interaction[i].dz << "\t"
                << unit_cell.interaction[i].rij << "\t"
                << unit_cell.interaction[i].Jij[0][0] << std::endl;
   }*/

   // Check for interactions
   if(unit_cell.interaction.size()==0){
      terminaltextcolor(RED);
      std::cerr << "Error! No interactions generated for " << uc::internal::crystal_structure << " crystal structure. Try increasing the interaction range. Aborting." << std::endl;
      terminaltextcolor(WHITE);
      zlog << zTs() << "Error! No interactions generated for " << uc::internal::crystal_structure << " crystal structure. Try increasing the interaction range. Aborting." << std::endl;
      err::vexit();
   }

   // Determine exchange type
   if(uc::internal::exchange_type == isotropic) unit_cell.exchange_type=-1;
   else if(uc::internal::exchange_type == vectorial) unit_cell.exchange_type=3;
   else{
      terminaltextcolor(RED);
      std::cerr << "Programmer error! Exchange type " << uc::internal::exchange_type << " is not a recognised value!" << std::endl;
      terminaltextcolor(WHITE);
      zlog << zTs() << "Programmer error! Exchange type " << uc::internal::exchange_type << " is not a recognised value!" << std::endl;
      err::vexit();
   }

   return;

}
//------------------------------------------------------------------------------
// Function to output non-magnetic atomic positions to disk
//------------------------------------------------------------------------------
void atoms_non_magnetic(){

      //------------------------------------------------------------
      // Determine non magnetic atoms to be outputted to coord list
      //------------------------------------------------------------

      // array of atom numbers to be outputted
      std::vector<uint64_t> atom_list(0);

      // get output bounds
      const double minB[3] = {atoms_output_min[0] * cs::system_dimensions[0],
                              atoms_output_min[1] * cs::system_dimensions[1],
                              atoms_output_min[2] * cs::system_dimensions[2]};

      const double maxB[3] = {atoms_output_max[0] * cs::system_dimensions[0],
                              atoms_output_max[1] * cs::system_dimensions[1],
                              atoms_output_max[2] * cs::system_dimensions[2]};

      // Determine non magnetic atoms to be outputted to coord list
      for (uint64_t atom = 0; atom < cs::non_magnetic_atoms_array.size(); atom++){

         const double cc[3] = {cs::non_magnetic_atoms_array[atom].x, cs::non_magnetic_atoms_array[atom].y, cs::non_magnetic_atoms_array[atom].z};

         // check atom within output bounds
         if ( (cc[0] >= minB[0]) && (cc[0] <= maxB[0]) ){
            if ( (cc[1] >= minB[1]) && (cc[1] <= maxB[1]) ){
               if ( (cc[2] >= minB[2]) && (cc[2] <= maxB[2]) ){
                  atom_list.push_back(atom); //non-magnetic atoms
               }
            }
         }

      }

      //------------------------------------------------
      // Create temporary buffers for atom information
      //------------------------------------------------
      uint64_t num_local_atoms = atom_list.size();
      uint64_t num_total_atoms = 0; // number of atoms across all processors

      #ifdef MPICF
         // calculate number of atoms to be output on all processors
         MPI_Allreduce(&num_local_atoms, &num_total_atoms, 1, MPI_UINT64_T, MPI_SUM, MPI_COMM_WORLD);
      #else
         num_total_atoms = num_local_atoms;
      #endif

      std::vector<int> atom_type_buffer(num_local_atoms);
      for(unsigned int atom = 0; atom < num_local_atoms; atom++) atom_type_buffer[atom] = cs::non_magnetic_atoms_array[ atom_list[atom] ].mat;

      std::vector<int> atom_category_buffer(num_local_atoms);
      for(unsigned int atom = 0; atom < num_local_atoms; atom++) atom_category_buffer[atom] = cs::non_magnetic_atoms_array[ atom_list[atom] ].cat;

      std::vector<double> atom_coord_buffer(3*num_local_atoms);
      for(unsigned int atom = 0; atom < num_local_atoms; atom++){
         const uint64_t atom_id = atom_list[atom]; // get atom array index
         atom_coord_buffer[3*atom + 0] = cs::non_magnetic_atoms_array[atom_id].x;
         atom_coord_buffer[3*atom + 1] = cs::non_magnetic_atoms_array[atom_id].y;
         atom_coord_buffer[3*atom + 2] = cs::non_magnetic_atoms_array[atom_id].z;
      }

      //------------------------------------------
      // Output Meta Data from root process
      //------------------------------------------
      // set number of files
      // const int files = config::internal::num_io_groups; // unused variable

      if(config::internal::mode != legacy && vmpi::my_rank == 0){
         config::internal::write_non_magnetic_meta(num_total_atoms);
      }

      //------------------------------------------
      // Output coordinate data
      //------------------------------------------

      // Determine output filename
      std::stringstream file_sstr;

      // set simple file name for single file output
      if(config::internal::num_io_groups == 1) file_sstr << "non-magnetic-atoms.data";
      // otherwise set indexed files
      else file_sstr << "non-magnetic-atoms-" << std::setfill('0') << std::setw(6) << config::internal::io_group_id << ".data";

      // convert string stream to string
      std::string filename = file_sstr.str();

      // Calculate number of bytes to be written to disk
      const double data_size = double(num_total_atoms) * 1.0e-9 * (3.0*double(sizeof(double) + 2.0*double(sizeof(int)) ) );

      // Output informative messages of actual data size to be outputed to disk (in binary mode)
      zlog << zTs() << "Total non-magnetic data filesize: " << 1000.0 * data_size << " MB" << std::endl;

      // Output informative message to log file on root process
      zlog << zTs() << "Outputting non-magnetic atomic coordinate file to disk ";

      // Variable for calculating output bandwidth
      double io_time = 1.0e-12;

      //-----------------------------------------------------
      // Parallel mode output
      //-----------------------------------------------------
      #ifdef MPICF

      // Determine io mode and call appropriate function for data
      switch(config::internal::mode){

         // legacy
         case config::internal::legacy:
            break;

         case config::internal::mpi_io:{
            vutil::vtimer_t timer; // instantiate timer
            MPI_File fh; // MPI file handle
            MPI_Status status; // MPI io status
            // convert filename to character string for output
            char *cfilename = (char*)filename.c_str();
            // Open file on all processors
            MPI_File_open(MPI_COMM_WORLD, cfilename, MPI_MODE_WRONLY | MPI_MODE_CREATE, MPI_INFO_NULL, &fh);
            // write number of atoms on root process
            if(vmpi::my_rank == 0) MPI_File_write(fh, &num_total_atoms, 1, MPI_UINT64_T, &status);

            // Calculate local byte offsets since MPI-IO is simple and doesn't update the file handle pointer after I/O
            MPI_Offset type_offset     = config::internal::linear_offset + sizeof(uint64_t);
            MPI_Offset category_offset = config::internal::linear_offset + num_total_atoms * sizeof(int) + sizeof(uint64_t);
            MPI_Offset data_offset     = config::internal::buffer_offset + 2 * num_total_atoms * sizeof(int) + sizeof(uint64_t);

            timer.start(); // start timer

            // Write data to disk
            MPI_File_write_at_all(fh, type_offset, &atom_type_buffer[0], atom_type_buffer.size(), MPI_INT, &status);
            MPI_File_write_at_all(fh, category_offset, &atom_category_buffer[0], atom_category_buffer.size(), MPI_INT, &status);
            MPI_File_write_at_all(fh, data_offset, &atom_coord_buffer[0], atom_coord_buffer.size(), MPI_DOUBLE, &status);

            timer.stop(); // Stop timer

            // Calculate elapsed time
            io_time = timer.elapsed_time();

            // Close file
            MPI_File_close(&fh);
            break;
         }

         case config::internal::fpprocess:
            io_time = write_coord_data(filename, atom_coord_buffer, atom_type_buffer, atom_category_buffer);
            break;

         case config::internal::fpnode:{
            // Gather data from all processors in io group
            std::vector<int> collated_atom_type_buffer(0);
            collate_int_data(atom_type_buffer, collated_atom_type_buffer);
            std::vector<int> collated_atom_category_buffer(0);
            collate_int_data(atom_category_buffer, collated_atom_category_buffer);
            std::vector<double> collated_atom_coord_buffer(0);
            collate_double_data(atom_coord_buffer, collated_atom_coord_buffer);
            // output data on master io processes
            if(config::internal::io_group_master) io_time = write_coord_data(filename, collated_atom_coord_buffer, collated_atom_type_buffer, collated_atom_category_buffer);
            // find longest time in all io nodes
            double max_io_time = 0.0;
            // calculate actual bandwidth on root process
            MPI_Reduce(&io_time, &max_io_time, 1, MPI_DOUBLE, MPI_MAX, 0, MPI_COMM_WORLD);
            io_time = max_io_time;
            break;
         }

      }

      #else
         //-----------------------------------------------------
         // Serial mode output (ignores most io directives)
         //-----------------------------------------------------
         // if new output (not legacy) then output non magnetic atoms
         if(config::internal::mode != config::internal::legacy) io_time = write_coord_data(filename, atom_coord_buffer, atom_type_buffer, atom_category_buffer);
      #endif

      // Output bandwidth to log file
      zlog << data_size/io_time << " GB/s in " << io_time << " s" << std::endl;

      return;

   }
int create_crystal_structure(std::vector<cs::catom_t> & catom_array){
	//----------------------------------------------------------
	// check calling of routine if error checking is activated
	//----------------------------------------------------------
	if(err::check==true){std::cout << "cs::create_crystal_structure has been called" << std::endl;}

	int min_bounds[3];
	int max_bounds[3];

	#ifdef MPICF
	if(vmpi::mpi_mode==0){
		min_bounds[0] = int(vmpi::min_dimensions[0]/unit_cell.dimensions[0]);
		min_bounds[1] = int(vmpi::min_dimensions[1]/unit_cell.dimensions[1]);
		min_bounds[2] = int(vmpi::min_dimensions[2]/unit_cell.dimensions[2]);
		max_bounds[0] = vmath::iceil(vmpi::max_dimensions[0]/unit_cell.dimensions[0]);
		max_bounds[1] = vmath::iceil(vmpi::max_dimensions[1]/unit_cell.dimensions[1]);
		max_bounds[2] = vmath::iceil(vmpi::max_dimensions[2]/unit_cell.dimensions[2]);
	}
	else{
		min_bounds[0]=0;
		min_bounds[1]=0;
		min_bounds[2]=0;
		max_bounds[0]=cs::total_num_unit_cells[0];
		max_bounds[1]=cs::total_num_unit_cells[1];
		max_bounds[2]=cs::total_num_unit_cells[2];
	}
	#else
		min_bounds[0]=0;
		min_bounds[1]=0;
		min_bounds[2]=0;
		max_bounds[0]=cs::total_num_unit_cells[0];
		max_bounds[1]=cs::total_num_unit_cells[1];
		max_bounds[2]=cs::total_num_unit_cells[2];
	#endif

	cs::local_num_unit_cells[0]=max_bounds[0]-min_bounds[0];
	cs::local_num_unit_cells[1]=max_bounds[1]-min_bounds[1];
	cs::local_num_unit_cells[2]=max_bounds[2]-min_bounds[2];

	int num_atoms=cs::local_num_unit_cells[0]*cs::local_num_unit_cells[1]*cs::local_num_unit_cells[2]*unit_cell.atom.size();

	// set catom_array size
	catom_array.reserve(num_atoms);

	// Initialise atoms number
	int atom=0;

   // find maximum height lh_category
   unsigned int maxlh=0;
   for(unsigned int uca=0;uca<unit_cell.atom.size();uca++) if(unit_cell.atom[uca].hc > maxlh) maxlh = unit_cell.atom[uca].hc;
   maxlh+=1;

   const double cff = 1.e-9; // Small numerical correction for atoms exactly on the borderline between processors

	// Duplicate unit cell
	for(int z=min_bounds[2];z<max_bounds[2];z++){
		for(int y=min_bounds[1];y<max_bounds[1];y++){
			for(int x=min_bounds[0];x<max_bounds[0];x++){

				// need to change this to accept non-orthogonal lattices
				// Loop over atoms in unit cell
				for(unsigned int uca=0;uca<unit_cell.atom.size();uca++){
					double cx = (double(x)+unit_cell.atom[uca].x)*unit_cell.dimensions[0]+cff;
					double cy = (double(y)+unit_cell.atom[uca].y)*unit_cell.dimensions[1]+cff;
					double cz = (double(z)+unit_cell.atom[uca].z)*unit_cell.dimensions[2]+cff;
					#ifdef MPICF
						if(vmpi::mpi_mode==0){
							// only generate atoms within allowed dimensions
                     if(   (cx>=vmpi::min_dimensions[0] && cx<vmpi::max_dimensions[0]) &&
                           (cy>=vmpi::min_dimensions[1] && cy<vmpi::max_dimensions[1]) &&
                           (cz>=vmpi::min_dimensions[2] && cz<vmpi::max_dimensions[2])){
						#endif
							if((cx<cs::system_dimensions[0]) && (cy<cs::system_dimensions[1]) && (cz<cs::system_dimensions[2])){
							catom_array.push_back(cs::catom_t());
							catom_array[atom].x=cx;
							catom_array[atom].y=cy;
							catom_array[atom].z=cz;
							//std::cout << atom << "\t" << cx << "\t" << cy <<"\t" << cz << std::endl;
							catom_array[atom].material=unit_cell.atom[uca].mat;
							catom_array[atom].uc_id=uca;
							catom_array[atom].lh_category=unit_cell.atom[uca].hc+z*maxlh;
							catom_array[atom].uc_category=unit_cell.atom[uca].lc;
							catom_array[atom].scx=x;
							catom_array[atom].scy=y;
							catom_array[atom].scz=z;
							atom++;
							}
						#ifdef MPICF
							}
						}
						else{
							if((cx<cs::system_dimensions[0]) && (cy<cs::system_dimensions[1]) && (cz<cs::system_dimensions[2])){
							catom_array.push_back(cs::catom_t());
							catom_array[atom].x=cx;
							catom_array[atom].y=cy;
							catom_array[atom].z=cz;
							catom_array[atom].material=unit_cell.atom[uca].mat;
							catom_array[atom].uc_id=uca;
							catom_array[atom].lh_category=unit_cell.atom[uca].hc+z*maxlh;
							catom_array[atom].uc_category=unit_cell.atom[uca].lc;
							catom_array[atom].scx=x;
							catom_array[atom].scy=y;
							catom_array[atom].scz=z;
							catom_array[atom].include=false; // assume no atoms until classification complete
							atom++;
							}
						}
						#endif
					}
				}
			}
		}

	// Check to see if actual and expected number of atoms agree, if not trim the excess
	if(atom!=num_atoms){
		std::vector<cs::catom_t> tmp_catom_array(num_atoms);
		tmp_catom_array=catom_array;
		catom_array.resize(atom);
		for(int a=0;a<atom;a++){
			catom_array[a]=tmp_catom_array[a];
		}
		tmp_catom_array.resize(0);
	}


	// If z-height material selection is enabled then do so
	if(cs::SelectMaterialByZHeight==true){

		// Check for interfacial roughness and call custom material assignment routine
		if(cs::interfacial_roughness==true) cs::roughness(catom_array);

      // Check for multilayer system and if required generate multilayers
      else if(cs::multilayers) cs::generate_multilayers(catom_array);

		// Otherwise perform normal assignement of materials
		else{

			// determine z-bounds for materials
			std::vector<double> mat_min(mp::num_materials);
			std::vector<double> mat_max(mp::num_materials);
         std::vector<bool> mat_fill(mp::num_materials);

         // Unroll min, max and fill for performance
			for(int mat=0;mat<mp::num_materials;mat++){
				mat_min[mat]=mp::material[mat].min*cs::system_dimensions[2];
				mat_max[mat]=mp::material[mat].max*cs::system_dimensions[2];
				// alloys generally are not defined by height, and so have max = 0.0
				if(mat_max[mat]<0.0000001) mat_max[mat]=-0.1;
            mat_fill[mat]=mp::material[mat].fill;
			}

			// Assign materials to generated atoms
			for(unsigned int atom=0;atom<catom_array.size();atom++){
				for(int mat=0;mat<mp::num_materials;mat++){
					const double cz=catom_array[atom].z;
					if((cz>=mat_min[mat]) && (cz<mat_max[mat]) && (mat_fill[mat]==false)){
						catom_array[atom].material=mat;
						catom_array[atom].include=true;
					}
				}
			}
		}

		// Delete unneeded atoms
		clear_atoms(catom_array);
	}

	// Check to see if any atoms have been generated
	if(atom==0){
		terminaltextcolor(RED);
		std::cout << "Error - no atoms have been generated, increase system dimensions!" << std::endl;
		terminaltextcolor(WHITE);
		zlog << zTs() << "Error: No atoms have been generated. Increase system dimensions." << std::endl;
		err::vexit();
	}

	// Now unselect all atoms by default for particle shape cutting
	for(unsigned int atom=0;atom<catom_array.size();atom++){
		catom_array[atom].include=false;
	}

	return EXIT_SUCCESS;
}
Beispiel #14
0
///
/// @brief        Initialise Constrained Monte Carlo module
///
/// Creates the rotation matices for the given angle and computes the
/// Boltzmann factor for the given temperature.
///
/// @return       void
///
void CMCMCinit(){

	// Check for calling of function
	if(err::check==true) std::cout << "sim::CMCMCinit has been called" << std::endl;
	
	// Create rotational matrices for cmc
	cmc::mat_polar_rot_matrix();

	cmc::atom_list.resize(mp::num_materials);
	// create list of matching materials
	for(int atom=0;atom<atoms::num_atoms;atom++){
		int mat=atoms::type_array[atom];
		cmc::atom_list[mat].push_back(atom);
	}
	
		// Check for rotational update
	if(sim::constraint_rotation==false || (sim::constraint_theta_changed==false && sim::constraint_phi_changed==false)){

		// Output message showing constraint direction re-initialisation
		zlog << zTs() << "Initialising spins in all materials to new constraint directions." << std::endl;

	// Initialise all spins along the constraint direction(s).
	for(int atom =0;atom<atoms::num_atoms;atom++){
		int imat=atoms::type_array[atom];
		if(mp::material[imat].constrained==true){
			double sx=sin(cmc::cmc_mat[imat].constraint_phi*M_PI/180.0)*cos(cmc::cmc_mat[imat].constraint_theta*M_PI/180.0);
			double sy=sin(cmc::cmc_mat[imat].constraint_phi*M_PI/180.0)*sin(cmc::cmc_mat[imat].constraint_theta*M_PI/180.0);
			double sz=cos(cmc::cmc_mat[imat].constraint_phi*M_PI/180.0);
			atoms::x_spin_array[atom]=sx;
			atoms::y_spin_array[atom]=sy;
			atoms::z_spin_array[atom]=sz;
		}
	}
	
	}
	else{

		// Output message showing constraint direction re-initialisation
		zlog << zTs() << "Initialising spins in material " << cmc::active_material << " by rotation to new constraint direction (phi, theta) " 
		<<  cmc::cmc_mat[cmc::active_material].constraint_phi << " , " << cmc::cmc_mat[cmc::active_material].constraint_theta << std::endl;

		// Rotate spins from old to new constraint direction

		// Determine angles to rotate spins by
		double theta_old = cmc::cmc_mat[cmc::active_material].constraint_theta; 
		double theta_new = cmc::cmc_mat[cmc::active_material].constraint_theta;

		double phi_old = cmc::cmc_mat[cmc::active_material].constraint_phi;
		double phi_new = cmc::cmc_mat[cmc::active_material].constraint_phi;

		// note - sim::constraint_phi, sim::constraint_theta are already at new angle
		if(sim::constraint_theta_changed) theta_old = cmc::cmc_mat[cmc::active_material].constraint_theta - cmc::cmc_mat[cmc::active_material].constraint_theta_delta;
		if(sim::constraint_phi_changed) phi_old     = cmc::cmc_mat[cmc::active_material].constraint_phi - cmc::cmc_mat[cmc::active_material].constraint_phi_delta;
		
		// Rotate all spins in active material from theta_old to theta = 0 (reference direction along x)
		cmc::rotate_material_spins_around_z_axis(-theta_old, cmc::active_material);

		// Rotate all spins in active material from phi_old to phi_new
		cmc::rotate_material_spins_around_x_axis(phi_new-phi_old, cmc::active_material);

		// Rotate all spins in active material from theta = 0 to theta = theta_new
		cmc::rotate_material_spins_around_z_axis(theta_new, cmc::active_material);

		// reset rotation flags
		sim::constraint_theta_changed = false;
		sim::constraint_phi_changed   = false;

	}

	// disable thermal field calculation
	sim::hamiltonian_simulation_flags[3]=0;
	
	// set initialised flag to true
	cmc::is_initialised=true;
	
	return;
}
Beispiel #15
0
	void zLogTsInit(std::string tmp){

		// Get program name and process ID
		std::string tmprev;
		int linelength = tmp.length();

		// set character triggers
		const char* key="/";	/// Word identifier

		// copy characters after last /
		for(int i=linelength-1;i>=0;i--){

			char c=tmp.at(i);

			if(c != *key){
				tmprev.push_back(c);
			}
			else break;
		}

		//reverse read into program name
		linelength=tmprev.size();
		for(int i=linelength-1;i>=0;i--){
			char c=tmprev.at(i);
			zLogProgramName.push_back(c);
		}

		// Get hostname
		char loghostname [80];
		#ifdef WIN_COMPILE
			DWORD sizelhn = sizeof ( loghostname );
			int GHS=!GetComputerName(loghostname, &sizelhn); //GetComputerName returns true when retrieves hostname
		#else
			int GHS=gethostname(loghostname, 80);
		#endif
	  terminaltextcolor(YELLOW);
		if(GHS!=0) std::cerr << "Warning: Unable to retrieve hostname for zlog file." << std::endl;
	  terminaltextcolor(WHITE);
		zLogHostName = loghostname;

		// Now get process ID
		#ifdef WIN_COMPILE
			zLogPid = _getpid();
		#else
			zLogPid = getpid();
		#endif

		// Open log filename
		if(vmpi::my_rank==0) zlog.open("log");

		// Mark as initialised;
		zLogInitialised=true;

		zlog << zTs() << "Logfile opened" << std::endl;

      //------------------------------------
   	// Determine current directory
   	//------------------------------------
   	char directory [256];

   	#ifdef WIN_COMPILE
   		_getcwd(directory, sizeof(directory));
   	#else
   		getcwd(directory, sizeof(directory));
   	#endif

      // write system and version information
      zlog << zTs() << "Executable : " << vout::zLogProgramName << std::endl;
      zlog << zTs() << "Host name  : " << vout::zLogHostName << ":" << std::endl;
      zlog << zTs() << "Directory  : " << directory << std::endl;
      zlog << zTs() << "Process ID : " << vout::zLogPid << std::endl;
      zlog << zTs() << "Version    : " << vinfo::version() << std::endl;
      zlog << zTs() << "Githash    : " << vinfo::githash() << std::endl;

		return;
	}
Beispiel #16
0
//----------------------------------------------------------------------
//
//   Function to generate rough surfaces (optionally unique) between
//   multiple materials at the interface of min/max.
//
//   A local height is defined which overrides the min/max values of
//   the extent of the material as defined in the material file. No
//   atoms are added or removed by the process, but this will override
//   structural effects such as core-shell systems.
//
//   The roughness is generated by creating seed points with a random
//   radius with a flat distribution around the mean. Within this
//   radius a stepwise change in the local height is defined, again
//   Gaussian distributed with a mean step height of zero. A maximum
//   Height is specified which truncates outlying values. Typical
//   values of the maximum step height would be 1-5 monolayers.
//
//   The seed points are used to generate a 2D array defining the local
//   height for any point in space. A loop over all atoms then reassigns
//   atoms to materials according to the local height, overriding the
//   materials set in the crsytal.
//
//------------------------------------------------------------------------
//
void roughness(std::vector<cs::catom_t> & catom_array){

	// Output instructuve message to log file
	zlog << zTs() << "Calculating interfacial roughness for generated system." << std::endl;

	// Construct a 2D array of height according to roughness resoution
	const double resolution=cs::interfacial_roughness_height_field_resolution; // Angstroms
	const unsigned int seed_density=cs::interfacial_roughness_seed_count;
	const double seed_radius=cs::interfacial_roughness_mean_seed_radius;
	const double seed_height_mean=cs::interfacial_roughness_mean_seed_height;
	const double seed_height_max=cs::interfacial_roughness_seed_height_max;
	const double seed_radius_variance=cs::interfacial_roughness_seed_radius_variance;

	// Declare array to store height field
	std::vector<std::vector<double> >height_field(0);

	// Calculate size of height_field array
	double nx = int(vmath::iceil(cs::system_dimensions[0]/resolution));
	double ny = int(vmath::iceil(cs::system_dimensions[1]/resolution));

	// Resize height_field array
	height_field.resize(nx);
	for(int i=0; i<nx; i++) height_field.at(i).resize(ny);

	// Generate seed points and radii
	std::vector<seed_point_t> seed_points(0);

	// Define local random generator for surface roughness
	MTRand rgrnd;

	// Initialise random number generator
	rgrnd.seed(cs::interfacial_roughness_random_seed);

	for(unsigned int p=0; p < seed_density ; p++){
		// Generate random point coordinates
		double x=rgrnd()*cs::system_dimensions[0];
		double y=rgrnd()*cs::system_dimensions[1];

		// Generate random radius with flat profile r = r0 +/- variance*rnd()
		double r=seed_radius*(1.0+seed_radius_variance*(2.0*rgrnd()-1.0));

		// Generate random height with gaussian profile
		double h=seed_height_mean*mtrandom::gaussianc(rgrnd);

		// Overwrite generated height if greater than maximum
		if(fabs(h)>seed_height_max) h=vmath::sign(h)*seed_height_max;

		// Check for type of roughness
		// Troughs
		if(cs::interfacial_roughness_type==-1) h = -1.0*fabs(h);
		// Peaks
		else if(cs::interfacial_roughness_type==1) h = fabs(h);

		// Save point characteristics to array
		seed_point_t tmp;
		tmp.x = x;
		tmp.y = y;
		tmp.radius = r;
		tmp.height = h;
		seed_points.push_back(tmp);
	}

	// Now apply seed points to generate local height field
	// Loop over all height field coordinates
	for(int ix = 0; ix < nx; ix++){
		for(int iy = 0; iy < ny; iy++){

			const double x = double(ix)*resolution; // real space coordinates
			const double y = double(iy)*resolution;

			// Loop over all seed points
			for(unsigned int p=0; p < seed_density ; p++){
				double rx=x-seed_points.at(p).x;
				double ry=y-seed_points.at(p).y;

				// Check for point in range
				if(rx*rx+ry*ry <= seed_points.at(p).radius*seed_points.at(p).radius) height_field.at(ix).at(iy)=seed_points.at(p).height;
			}
		}
	}

	// Assign materials to generated atoms
	for(unsigned int atom=0;atom<catom_array.size();atom++){

		// Determine height field coordinates
		const int hx = int(catom_array[atom].x/resolution);
		const int hy = int(catom_array[atom].y/resolution);

		// Loop over all materials and determine local height
		for(int mat=0;mat<mp::num_materials;mat++){

			double min=create::internal::mp[mat].min*cs::system_dimensions[2];
			double max=create::internal::mp[mat].max*cs::system_dimensions[2];
			double local_height = height_field.at(hx).at(hy);
         bool fill = mp::material[mat].fill;

			// optionally specify a material specific height here -- not yet implemented
			//if(cs::interfacial_roughness_local_height_field==true){
			//double local_height = height_field.at(mat).at(hx).at(hy);
			//}
         const int atom_uc_cat = catom_array[atom].uc_category;
         const int mat_uc_cat = create::internal::mp[mat].unit_cell_category;

			// Include atoms if within material height
			const double cz=catom_array[atom].z;
			if((cz>=min+local_height) && (cz<max+local_height) && (fill==false)  && (atom_uc_cat == mat_uc_cat) ){
				catom_array[atom].material=mat;
				catom_array[atom].include=true;
			}
		}
	}

	return;

}
Beispiel #17
0
///
/// @brief        Initialise Constrained Monte Carlo module
///
/// Creates the rotation matices for the given angle and computes the
/// Boltzmann factor for the given temperature.
///
/// @return       void
///
void CMCinit(){

	// Check for calling of function
	if(err::check==true) std::cout << "sim::CMCinit has been called" << std::endl;

	// Create rotational matrices for cmc
	cmc::polar_rot_matrix(sim::constraint_phi,sim::constraint_theta, cmc::polar_matrix, cmc::polar_matrix_tp, cmc::polar_vector);

	// Check for rotational update
	if((sim::constraint_rotation==false || (sim::constraint_theta_changed==false && sim::constraint_phi_changed==false)) && sim::checkpoint_loaded_flag==false){

		// Output message showing constraint direction re-initialisation
		zlog << zTs() << "Initialising spins to new constraint direction (phi, theta) " <<  sim::constraint_phi << " , " << sim::constraint_theta << std::endl;

		// Initialise all spins along the constraint direction.
		double sx=sin(sim::constraint_phi*M_PI/180.0)*cos(sim::constraint_theta*M_PI/180.0);
		double sy=sin(sim::constraint_phi*M_PI/180.0)*sin(sim::constraint_theta*M_PI/180.0);
		double sz=cos(sim::constraint_phi*M_PI/180.0);

		for(int atom =0;atom<atoms::num_atoms;atom++){
			atoms::x_spin_array[atom]=sx;
			atoms::y_spin_array[atom]=sy;
			atoms::z_spin_array[atom]=sz;
		}
	}
   // If the simulation is restarted from within the temperature loop, then the spin configurations must not be reinitialised
   else if(sim::checkpoint_loaded_flag==true){
      zlog << zTs() << "Not re-initialising spins to constraint directions since still in temperature loop" << std::endl;
   }
	else{

		// Output message showing constraint direction re-initialisation
		zlog << zTs() << "Initialising spins by rotation from last constraint direction to new constraint direction (phi, theta) "
		<<  sim::constraint_phi << " , " << sim::constraint_theta << std::endl;

		// Rotate spins from old to new constraint direction

		// Determine angles to rotate spins by
		double theta_old = sim::constraint_theta;
		double theta_new = sim::constraint_theta;

		double phi_old = sim::constraint_phi;
		double phi_new = sim::constraint_phi;

		// note - sim::constraint_phi, sim::constraint_theta are already at new angle
		if(sim::constraint_theta_changed) theta_old = sim::constraint_theta - sim::constraint_theta_delta;
		if(sim::constraint_phi_changed) phi_old     = sim::constraint_phi   - sim::constraint_phi_delta;

		// Rotate all spins from theta_old to theta = 0 (reference direction along x)
		cmc::rotate_spins_around_z_axis(-theta_old);

		// Rotate all spins from phi_old to phi_new
		cmc::rotate_spins_around_x_axis(phi_new-phi_old);

		// Rotate all spins from theta = 0 to theta = theta_new
		cmc::rotate_spins_around_z_axis(theta_new);

		// reset rotation flags
		sim::constraint_theta_changed = false;
		sim::constraint_phi_changed   = false;

	}

	// disable thermal field calculation
	sim::hamiltonian_simulation_flags[3]=0;

	// set initialised flag to true
	cmc::is_initialised=true;

	return;
}
Beispiel #18
0
/// @brief Cells output function
///
/// @details Outputs formatted data snapshot for visualisation
///
///   //------------------------------------------------------
///   // Atomistic coordinate configuration file for vampire
///   //------------------------------------------------------
///   // Date: xx/xx/xxxx xx.xx.xx
///   //------------------------------------------------------
///   Number of cells: $n_cells
///   //------------------------------------------------------
///   Number of atom files: $n_files
///   atoms-coords-00CPU0.cfg
///   atoms-coords-00CPU1.cfg
///   atoms-coords-00CPU2.cfg
///   //------------------------------------------------------
///   Number of local cells: $n_loc_cells
///   $material $category $x $y $z $species
///   $material $category $x $y $z $species
///   $material $category $x ...
///
/// @section License
/// Use of this code, either in source or compiled form, is subject to license from the authors.
/// Copyright \htmlonly &copy \endhtmlonly Richard Evans, 2009-2011. All Rights Reserved.
///
/// @section Information
/// @author  Richard Evans, [email protected]
/// @version 1.0
/// @date    31/05/2011
///
/// @internal
///   Created:    31/05/2011
///   Revision:     ---
///=====================================================================================
///
void legacy_cells_coords()
{

   // wait for all processes
   vmpi::barrier();

   // instantiate timer
   vutil::vtimer_t timer;

   // Set local output filename
   std::stringstream file_sstr;
   file_sstr << "cells-coords";
   file_sstr << ".cfg";
   std::string cfg_file = file_sstr.str();
   const char *cfg_filec = cfg_file.c_str();

   // start timer
   timer.start();

   // Output masterfile header on root process
   if (vmpi::my_rank == 0)
   {

      zlog << zTs() << "Outputting cell coordinates to disk" << std::flush;

      // Declare and open output file
      std::ofstream cfg_file_ofstr;
      cfg_file_ofstr.open(cfg_filec);

      // Get system date
      time_t rawtime = time(NULL);
      struct tm *timeinfo = localtime(&rawtime);

      cfg_file_ofstr << "#------------------------------------------------------" << std::endl;
      cfg_file_ofstr << "# Cell coordinates configuration file for vampire" << std::endl;
      cfg_file_ofstr << "#------------------------------------------------------" << std::endl;
      cfg_file_ofstr << "# Date: " << asctime(timeinfo);
      cfg_file_ofstr << "#------------------------------------------------------" << std::endl;
      cfg_file_ofstr << "# Number of cells: " << cells::num_cells << std::endl;
      cfg_file_ofstr << "#------------------------------------------------------" << std::endl;
      cfg_file_ofstr << "#" << std::endl;
      cfg_file_ofstr << "#" << std::endl;
      cfg_file_ofstr << "#" << std::endl;
      cfg_file_ofstr << "#" << std::endl;
      cfg_file_ofstr << "#" << std::endl;
      cfg_file_ofstr << "#------------------------------------------------------" << std::endl;

      config::internal::total_output_cells = 0;

      for (int cell = 0; cell < cells::num_cells; cell++){
         if (cells::num_atoms_in_cell_global[cell] > 0){
            cfg_file_ofstr << cell << "\t" << cells::num_atoms_in_cell_global[cell] << "\t" << cells::pos_and_mom_array[4 * cell + 0] << "\t" << cells::pos_and_mom_array[4 * cell + 1] << "\t" << cells::pos_and_mom_array[4 * cell + 2] << std::endl;
            config::internal::total_output_cells++;
         }
      }
      cfg_file_ofstr.close();
   }

   // stop the timer
   timer.stop();

   const double data_size = double(config::internal::total_output_cells) * 2.0 * sizeof(int) * 3.0 * sizeof(double);
   const double io_time = timer.elapsed_time();

   zlog << " of size " << data_size*1.0e-6 << " MB [ " << data_size*1.0e-9/timer.elapsed_time() << " GB/s in " << io_time << " s ]" << std::endl;

   // wait for all processes
   vmpi::barrier();

   return;
}
Beispiel #19
0
/// @brief Cell output function
///
/// @details Outputs formatted data snapshot for visualisation
///
///   #------------------------------------------------------
///   # Cell configuration file for vampire
///   #------------------------------------------------------
///   # Date: xx/xx/xxxx xx.xx.xx
///   #------------------------------------------------------
///   Number of cells: $n_cells
///   System dimensions: $max_x $max_y $max_z
///   Coordinates-file: $coord_file
///   Time: $t
///   Field: $H
///   Temperature: $T
///   Magnetisation: $mx $my $mz
///   Number of Materials: $n_mat
///   Material Properties 1:  $mu_s $mmx $mmy $mmz $mm
///   Material Properties 2:  $mu_s $mmx $mmy $mmz ...
///   #------------------------------------------------------
///
/// @section License
/// Use of this code, either in source or compiled form, is subject to license from the authors.
/// Copyright \htmlonly &copy \endhtmlonly Richard Evans, 2009-2011. All Rights Reserved.
///
/// @section Information
/// @author  Richard Evans, [email protected]
/// @version 1.0
/// @date    26/04/2013
///
/// @internal
///   Created:    26/04/2013
///   Revision:     ---
///=====================================================================================
///
void legacy_cells()
{

   // wait for all processes
   vmpi::barrier();

   // instantiate timer
   vutil::vtimer_t timer;

   // Set local output filename
   std::stringstream file_sstr;
   file_sstr << "cells-";
   file_sstr << std::setfill('0') << std::setw(8) << sim::output_cells_file_counter;
   file_sstr << ".cfg";
   std::string cfg_file = file_sstr.str();
   const char *cfg_filec = cfg_file.c_str();

   #ifdef MPICF
      // if flag to print cells field is active, all cpus send cells field to root proc
      if(dipole::activated) dipole::send_cells_field(cells::cell_id_array,
                                                     dipole::cells_field_array_x,
                                                     dipole::cells_field_array_y,
                                                     dipole::cells_field_array_z,
                                                     cells::num_local_cells);
   #endif

   // start timer
   timer.start();

   // Output masterfile header on root process
   if (vmpi::my_rank == 0)
   {

      zlog << zTs() << "Outputting cell configuration " << sim::output_cells_file_counter << " to disk" << std::flush;

      // Declare and open output file
      std::ofstream cfg_file_ofstr;
      cfg_file_ofstr.open(cfg_filec);

      // Get system date
      time_t rawtime = time(NULL);
      struct tm *timeinfo = localtime(&rawtime);

      cfg_file_ofstr << "#------------------------------------------------------" << std::endl;
      cfg_file_ofstr << "# Cell configuration file for vampire" << std::endl;
      cfg_file_ofstr << "#------------------------------------------------------" << std::endl;
      cfg_file_ofstr << "# Date: " << asctime(timeinfo);
      cfg_file_ofstr << "#------------------------------------------------------" << std::endl;
      cfg_file_ofstr << "# Number of spins: " << cells::num_cells << std::endl;
      cfg_file_ofstr << "# System dimensions:" << cs::system_dimensions[0] << "\t" << cs::system_dimensions[1] << "\t" << cs::system_dimensions[2] << std::endl;
      cfg_file_ofstr << "# Coordinates-file: cells-coord.cfg" << std::endl;
      cfg_file_ofstr << "# Time: " << double(sim::time) * mp::dt_SI << std::endl;
      cfg_file_ofstr << "# Field: " << sim::H_applied << std::endl;
      cfg_file_ofstr << "# Temperature: " << sim::temperature << std::endl;
      cfg_file_ofstr << "# Magnetisation: " << stats::system_magnetization.output_normalized_magnetization() << std::endl;
      cfg_file_ofstr << "#------------------------------------------------------" << std::endl;

      // Root process now outputs the cell magnetisations
      for (int cell = 0; cell < cells::num_cells; cell++){
         if (cells::num_atoms_in_cell_global[cell] > 0){
            cfg_file_ofstr << cells::mag_array_x[cell] << "\t" << cells::mag_array_y[cell] << "\t" << cells::mag_array_z[cell] << "\t";
            if(dipole::activated) cfg_file_ofstr << dipole::cells_field_array_x[cell] << "\t" << dipole::cells_field_array_y[cell] << "\t" << dipole::cells_field_array_z[cell] << "\n";
            else cfg_file_ofstr << "\n";
         }

      }

      cfg_file_ofstr.close();
   }

   // stop the timer
   timer.stop();

   double data_size = double(config::internal::total_output_cells) * 3.0 * sizeof(double);
   if(dipole::activated) data_size = data_size * 2.0;
   const double io_time = timer.elapsed_time();

   zlog << " of size " << data_size*1.0e-6 << " MB [ " << data_size*1.0e-9/timer.elapsed_time() << " GB/s in " << io_time << " s ]" << std::endl;

   sim::output_cells_file_counter++;

   // wait for all processes
   vmpi::barrier();

   return;
}
   //------------------------------------------------------------------------------
   //
   //    Function to set the type of exchange used in the code from a string
   //    processed from the unit cell file. Default is normalised isotropic
   //    exchange where the values of the exchange are set from the material
   //    file.
   //
   //    Function returns the number of exchange interactions specified in the
   //    unit cell file.
   //
   //------------------------------------------------------------------------------
   unsigned int set_exchange_type(std::string exchange_type_string){

      //----------------------------------------
      // check for standard isotropic exchange
      //----------------------------------------
      const std::string isotropic_str = "isotropic";
      if(isotropic_str == exchange_type_string){

         // set exchange type
         exchange::internal::exchange_type = internal::isotropic;

         // unset normalization flag
         exchange::internal::use_material_exchange_constants = false;

         return 1; // number of exchange interactions

      }

      //----------------------------------------
      // check for standard vectorial exchange
      //----------------------------------------
      const std::string vectorial_str = "vectorial";
      if(vectorial_str == exchange_type_string){

         // set exchange type
         exchange::internal::exchange_type = internal::vectorial;

         // unset normalization flag
         exchange::internal::use_material_exchange_constants = false;

         return 3; // number of exchange interactions

      }

      //----------------------------------------
      // check for standard tensorial exchange
      //----------------------------------------
      const std::string tensorial_str = "tensorial";
      if(tensorial_str == exchange_type_string){

         // set exchange type
         exchange::internal::exchange_type = internal::tensorial;

         // unset normalization flag
         exchange::internal::use_material_exchange_constants = false;

         return 9; // number of exchange interactions

      }

      //-----------------------------------------
      // check for normalised isotropic exchange
      //-----------------------------------------
      const std::string norm_isotropic_str = "normalised-isotropic";
      if(norm_isotropic_str == exchange_type_string){

         // set exchange type
         exchange::internal::exchange_type = internal::isotropic;

         // unset normalization flag
         exchange::internal::use_material_exchange_constants = true;

         return 1; // number of exchange interactions

      }

      //-----------------------------------------
      // check for normalised vectorial exchange
      //-----------------------------------------
      const std::string norm_vectorial_str = "normalised-vectorial";
      if(norm_vectorial_str == exchange_type_string){

         // set exchange type
         exchange::internal::exchange_type = internal::vectorial;

         // unset normalization flag
         exchange::internal::use_material_exchange_constants = true;

         return 3; // number of exchange interactions

      }

      //-----------------------------------------
      // check for normalised tensorial exchange
      //-----------------------------------------
      const std::string norm_tensorial_str = "normalised-tensorial";
      if(norm_tensorial_str == exchange_type_string){

         // set exchange type
         exchange::internal::exchange_type = internal::tensorial;

         // unset normalization flag
         exchange::internal::use_material_exchange_constants = true;

         return 9; // number of exchange interactions

      }

      terminaltextcolor(RED);
         zlog << zTs() << "\nError: Unkown exchange type \"" << exchange_type_string << "\" in unit cell file. Exiting!" << std::endl;
         std::cerr     << "\nError: Unkown exchange type \"" << exchange_type_string << "\" in unit cell file. Exiting!" << std::endl;
      terminaltextcolor(WHITE);

      // exit program
      err::vexit();

      return 0;

   }
Beispiel #21
0
/// @brief Function to run one a single program
///
/// @callgraph
/// @callergraph
///
/// @section License
/// Use of this code, either in source or compiled form, is subject to license from the authors.
/// Copyright \htmlonly &copy \endhtmlonly Richard Evans, 2009-2011. All Rights Reserved.
///
/// @section Information
/// @author  Richard Evans, [email protected]
/// @version 1.1
/// @date    09/03/2011
///
/// @return EXIT_SUCCESS
///
/// @internal
///	Created:		02/10/2008
///	Revision:	1.1 09/03/2011
///=====================================================================================
///
int run(){
	// Check for calling of function
	if(err::check==true) std::cout << "sim::run has been called" << std::endl;

	// Initialise simulation data structures
	sim::initialize(mp::num_materials);

   anisotropy::initialize(atoms::num_atoms, atoms::type_array, mp::mu_s_array);

   // now seed generator
	mtrandom::grnd.seed(vmpi::parallel_rng_seed(mtrandom::integration_seed));

   // Check for load spin configurations from checkpoint
   if(sim::load_checkpoint_flag) load_checkpoint();

   {
      // Set up statistical data sets
      #ifdef MPICF
         int num_atoms_for_statistics = vmpi::num_core_atoms+vmpi::num_bdry_atoms;
      #else
         int num_atoms_for_statistics = atoms::num_atoms;
      #endif
      // unroll list of non-magnetic materials
      std::vector<bool> non_magnetic_materials_array(mp::num_materials, false);
      for(int m = 0; m < mp::num_materials; m++){
         if( mp::material[m].non_magnetic == 2 ) non_magnetic_materials_array[m] = true;
      }
      stats::initialize(num_atoms_for_statistics, mp::num_materials, atoms::m_spin_array, atoms::type_array, atoms::category_array, non_magnetic_materials_array);
   }

   // Precalculate initial statistics
   stats::update(atoms::x_spin_array, atoms::y_spin_array, atoms::z_spin_array, atoms::m_spin_array);

   // initialise dipole field calculation
   dipole::initialize(cells::num_atoms_in_unit_cell,
                     cells::num_cells,
                     cells::num_local_cells,
                     cells::macro_cell_size,
                     cells::local_cell_array,
                     cells::num_atoms_in_cell,
                     cells::num_atoms_in_cell_global, // <----
                     cells::index_atoms_array,
                     cells::volume_array,
                     cells::pos_and_mom_array,
                     cells::atom_in_cell_coords_array_x,
                     cells::atom_in_cell_coords_array_y,
                     cells::atom_in_cell_coords_array_z,
                     atoms::type_array,
                     cells::atom_cell_id_array,
                     atoms::x_coord_array,
                     atoms::y_coord_array,
                     atoms::z_coord_array,
                     atoms::num_atoms
   );

   // Initialize GPU acceleration if enabled
   if(gpu::acceleration) gpu::initialize();

   // For MPI version, calculate initialisation time
	if(vmpi::my_rank==0){
		#ifdef MPICF
			std::cout << "Time for initialisation: " << MPI_Wtime()-vmpi::start_time << std::endl;
			zlog << zTs() << "Time for initialisation: " << MPI_Wtime()-vmpi::start_time << std::endl;
			vmpi::start_time=MPI_Wtime(); // reset timer
		#endif
   }

   // Set timer for runtime
   stopwatch_t stopwatch;
   stopwatch.start();

   // Precondition spins at equilibration temperature
   sim::internal::monte_carlo_preconditioning();

   // For MPI version, calculate initialisation time
   if(vmpi::my_rank==0){
		std::cout << "Starting Simulation with Program ";
		zlog << zTs() << "Starting Simulation with Program ";
	}

	// Now set initial compute time
	#ifdef MPICF
	vmpi::ComputeTime=MPI_Wtime();
	vmpi::WaitTime=MPI_Wtime();
	vmpi::TotalComputeTime=0.0;
	vmpi::TotalWaitTime=0.0;
	#endif

	// Select program to run
	switch(sim::program){
		case 0:
			if(vmpi::my_rank==0){
				std::cout << "Benchmark..." << std::endl;
				zlog << "Benchmark..." << std::endl;
			}
			program::bmark();
			break;

		case 1:
			if(vmpi::my_rank==0){
				std::cout << "Time-Series..." << std::endl;
				zlog << "Time-Series..." << std::endl;
			}
			program::time_series();
			break;

		case 2:
			if(vmpi::my_rank==0){
				std::cout << "Hysteresis-Loop..." << std::endl;
				zlog << "Hysteresis-Loop..." << std::endl;
			}
			program::hysteresis();
			break;

		case 3:
			if(vmpi::my_rank==0){
				std::cout << "Static-Hysteresis-Loop..." << std::endl;
				zlog << "Static-Hysteresis-Loop..." << std::endl;
			}
			program::static_hysteresis();
			break;

		case 4:
			if(vmpi::my_rank==0){
				std::cout << "Curie-Temperature..." << std::endl;
				zlog << "Curie-Temperature..." << std::endl;
			}
			program::curie_temperature();
			break;

		case 5:
			if(vmpi::my_rank==0){
				std::cout << "Field-Cool..." << std::endl;
				zlog << "Field-Cool..." << std::endl;
			}
			program::field_cool();
			break;

		case 6:
			if(vmpi::my_rank==0){
				std::cout << "Temperature-Pulse..." << std::endl;
				zlog << "Temperature-Pulse..." << std::endl;
			}
			program::temperature_pulse();
			break;

		case 7:
			if(vmpi::my_rank==0){
				std::cout << "HAMR-Simulation..." << std::endl;
				zlog << "HAMR-Simulation..." << std::endl;
			}
			program::hamr();
			break;

		case 8:
			if(vmpi::my_rank==0){
				std::cout << "CMC-Anisotropy..." << std::endl;
				zlog << "CMC-Anisotropy..." << std::endl;
			}
			program::cmc_anisotropy();
			break;

		case 9:
			if(vmpi::my_rank==0){
				std::cout << "Hybrid-CMC..." << std::endl;
				zlog << "Hybrid-CMC..." << std::endl;
			}
			program::hybrid_cmc();
			break;

      case 10:
         if(vmpi::my_rank==0){
            std::cout << "Reverse-Hybrid-CMC..." << std::endl;
            zlog << "Reverse-Hybrid-CMC..." << std::endl;
         }
         program::reverse_hybrid_cmc();
         break;

      case 11:
         if(vmpi::my_rank==0){
            std::cout << "LaGrange-Multiplier..." << std::endl;
            zlog << "LaGrange-Multiplier..." << std::endl;
         }
         program::lagrange_multiplier();
         break;

      case 12:
         if(vmpi::my_rank==0){
            std::cout << "partial-hysteresis-loop..." << std::endl;
            zlog << "partial-hysteresis-loop..." << std::endl;
         }
         program::partial_hysteresis_loop();
         break;

      case 13:
         if(vmpi::my_rank==0){
            std::cout << "localised-temperature-pulse..." << std::endl;
            zlog << "localised-temperature-pulse..." << std::endl;
         }
         program::localised_temperature_pulse();
         break;

      case 14:
         if(vmpi::my_rank==0){
            std::cout << "effective-damping..." << std::endl;
            zlog << "effective-damping..." << std::endl;
         }
         program::effective_damping();
         break;

		case 15:
	  		if(vmpi::my_rank==0){
	    		std::cout << "fmr..." << std::endl;
	    		zlog << "fmr..." << std::endl;
	  		}
	  		program::fmr();
	  		break;

		case 16:
	  		if(vmpi::my_rank==0){
	    		std::cout << "localised-field-cool..." << std::endl;
	    		zlog << "localised-field-cool..." << std::endl;
	  		}
	  		program::local_field_cool();
	  		break;

		case 50:
			if(vmpi::my_rank==0){
				std::cout << "Diagnostic-Boltzmann..." << std::endl;
				zlog << "Diagnostic-Boltzmann..." << std::endl;
			}
			program::boltzmann_dist();
			break;

	    case 51:
		  	if(vmpi::my_rank==0){
		       std::cout << "Setting..." << std::endl;
		       zlog << "Setting..." << std::endl;
	     	}
		  	program::setting_process();
		    break;

		default:{
			std::cerr << "Unknown Internal Program ID "<< sim::program << " requested, exiting" << std::endl;
			zlog << "Unknown Internal Program ID "<< sim::program << " requested, exiting" << std::endl;
			exit (EXIT_FAILURE);
			}
	}

   std::cout <<     "Simulation run time [s]: " << stopwatch.elapsed_seconds() << std::endl;
   zlog << zTs() << "Simulation run time [s]: " << stopwatch.elapsed_seconds() << std::endl;

   //------------------------------------------------
   // Output Monte Carlo statistics if applicable
   //------------------------------------------------
   if(sim::integrator==1){
      std::cout << "Monte Carlo statistics:" << std::endl;
      std::cout << "\tTotal moves: " << long(sim::mc_statistics_moves) << std::endl;
      std::cout << "\t" << ((sim::mc_statistics_moves - sim::mc_statistics_reject)/sim::mc_statistics_moves)*100.0 << "% Accepted" << std::endl;
      std::cout << "\t" << (sim::mc_statistics_reject/sim::mc_statistics_moves)*100.0                              << "% Rejected" << std::endl;
      zlog << zTs() << "Monte Carlo statistics:" << std::endl;
      zlog << zTs() << "\tTotal moves: " << sim::mc_statistics_moves << std::endl;
      zlog << zTs() << "\t" << ((sim::mc_statistics_moves - sim::mc_statistics_reject)/sim::mc_statistics_moves)*100.0 << "% Accepted" << std::endl;
      zlog << zTs() << "\t" << (sim::mc_statistics_reject/sim::mc_statistics_moves)*100.0                              << "% Rejected" << std::endl;
   }
   if(sim::integrator==3 || sim::integrator==4){
      std::cout << "Constrained Monte Carlo statistics:" << std::endl;
      std::cout << "\tTotal moves: " << cmc::mc_total << std::endl;
      std::cout << "\t" << (cmc::mc_success/cmc::mc_total)*100.0    << "% Accepted" << std::endl;
      std::cout << "\t" << (cmc::energy_reject/cmc::mc_total)*100.0 << "% Rejected (Energy)" << std::endl;
      std::cout << "\t" << (cmc::sphere_reject/cmc::mc_total)*100.0 << "% Rejected (Sphere)" << std::endl;
      zlog << zTs() << "Constrained Monte Carlo statistics:" << std::endl;
      zlog << zTs() << "\tTotal moves: " << cmc::mc_total << std::endl;
      zlog << zTs() << "\t" << (cmc::mc_success/cmc::mc_total)*100.0    << "% Accepted" << std::endl;
      zlog << zTs() << "\t" << (cmc::energy_reject/cmc::mc_total)*100.0 << "% Rejected (Energy)" << std::endl;
      zlog << zTs() << "\t" << (cmc::sphere_reject/cmc::mc_total)*100.0 << "% Rejected (Sphere)" << std::endl;
   }

	//program::LLB_Boltzmann();

   // De-initialize GPU
   if(gpu::acceleration) gpu::finalize();

   // optionally save checkpoint file
   if(sim::save_checkpoint_flag && !sim::save_checkpoint_continuous_flag) save_checkpoint();

	return EXIT_SUCCESS;
}