Ejemplo n.º 1
0
test_smooth_r4<float_type>::test_smooth_r4()
{
    typedef typename potential_type::matrix_type matrix_type;

    BOOST_TEST_MESSAGE("initialise simulation modules");

    // set module parameters
    npart_list = {500, 300};
    // the box length should not be greater than 2*r_c
    float box_length = 10;
    unsigned int const dimension = box_type::vector_type::static_size;
    boost::numeric::ublas::diagonal_matrix<typename box_type::matrix_type::value_type> edges(dimension);
    for (unsigned int i = 0; i < dimension; ++i) {
        edges(i, i) = box_length;
    }
    // smoothing parameter
    host_float_type const h = 1. / 256;

    float wca_cut = std::pow(2., 1. / 6);
    matrix_type cutoff_array(2, 2);
    cutoff_array <<=
        2.5, 2.
      , 2. , wca_cut;
    matrix_type epsilon_array(2, 2);
    epsilon_array <<=
        1., 2.
      , 2., 0.25;
    matrix_type sigma_array(2, 2);
    sigma_array <<=
        1., 2.
      , 2., 4.;

    // create modules
    particle = std::make_shared<particle_type>(accumulate(npart_list.begin(), npart_list.end(), 0), npart_list.size());
    box = std::make_shared<box_type>(edges);
    potential = std::make_shared<potential_type>(cutoff_array, h, epsilon_array, sigma_array);
    host_potential = std::make_shared<host_potential_type>(cutoff_array, h, epsilon_array, sigma_array);
    neighbour = std::make_shared<neighbour_type>(particle);
    force = std::make_shared<force_type>(potential, particle, particle, box, neighbour, 1);
    particle->on_prepend_force([=](){force->check_cache();});
    particle->on_force([=](){force->apply();});
}
Ejemplo n.º 2
0
///
/// @brief      Runs the Constrained Monte Carlo algorithm
///
/// Chooses nspins random spin pairs from the spin system and attempts a
/// Constrained Monte Carlo move on each pair, accepting for either lower
/// energy or with a Boltzmann thermal weighting.
///
/// @return     void
///
int ConstrainedMonteCarloMonteCarlo(){
	
	// Check for calling of function
	if(err::check==true) std::cout << "sim::ConstrainedMonteCarlo has been called" << std::endl;

	// check for cmc initialisation
	if(cmc::is_initialised==false) CMCMCinit();
	
	int atom_number1;
	int atom_number2;
	int imat1;
	int imat2;

	double delta_energy1;
	double delta_energy2;
	double delta_energy21;

	double Eold;
	double Enew;

   std::valarray<double> spin1_initial(3);
	std::valarray<double> spin1_final(3);
	double spin2_initial[3];
	double spin2_final[3];

	double spin1_init_mvd[3];
	double spin1_fin_mvd[3];
	double spin2_init_mvd[3];
	double spin2_fin_mvd[3];

	double M_other[3];
	double Mz_old;
	double Mz_new;

	double sqrt_ran;
	double probability;
	
   // Material dependent temperature rescaling
   std::vector<double> rescaled_material_kBTBohr(mp::num_materials);
   std::vector<double> sigma_array(mp::num_materials); // range for tuned gaussian random move
   for(int m=0; m<mp::num_materials; ++m){
      double alpha = mp::material[m].temperature_rescaling_alpha;
      double Tc = mp::material[m].temperature_rescaling_Tc;
      double rescaled_temperature = sim::temperature < Tc ? Tc*pow(sim::temperature/Tc,alpha) : sim::temperature;
      rescaled_material_kBTBohr[m] = 9.27400915e-24/(rescaled_temperature*1.3806503e-23);
      sigma_array[m] = pow(1.0/rescaled_material_kBTBohr[m],0.2)*0.08;
   }

	const int AtomExchangeType=atoms::exchange_type; // Cast as constant and pass to energy calculation for speed
	
	// save initial magnetisations
	for(int mat=0;mat<mp::num_materials;mat++){
	cmc::cmc_mat[mat].M_other[0] = 0.0;
	cmc::cmc_mat[mat].M_other[1] = 0.0;
	cmc::cmc_mat[mat].M_other[2] = 0.0;
	}
	for(int atom=0;atom<atoms::num_atoms;atom++){
		int mat=atoms::type_array[atom];
		cmc::cmc_mat[mat].M_other[0] += atoms::x_spin_array[atom]; //multiplied by polar_vector below
		cmc::cmc_mat[mat].M_other[1] += atoms::y_spin_array[atom];
		cmc::cmc_mat[mat].M_other[2] += atoms::z_spin_array[atom];
	}

	// make a sequence of Monte Carlo moves
	for (int mcs=0;mcs<atoms::num_atoms;mcs++){ 
		// Randomly select spin number 1
		atom_number1 = int(mtrandom::grnd()*atoms::num_atoms);
		imat1=atoms::type_array[atom_number1];
      sim::mc_delta_angle=sigma_array[imat1];
		
		// check for constrained or unconstrained
		if(mp::material[imat1].constrained==false){
			// normal MC

			// Save old spin position
			spin1_initial[0] = atoms::x_spin_array[atom_number1];
			spin1_initial[1] = atoms::y_spin_array[atom_number1];
			spin1_initial[2] = atoms::z_spin_array[atom_number1];

         // Make Monte Carlo move
         sim::mc_move(spin1_initial, spin1_final);

			// Calculate current energy
			Eold = sim::calculate_spin_energy(atom_number1, AtomExchangeType);
			
			// Copy new spin position
			atoms::x_spin_array[atom_number1] = spin1_final[0];
			atoms::y_spin_array[atom_number1] = spin1_final[1];
			atoms::z_spin_array[atom_number1] = spin1_final[2];

			// Calculate new energy
			Enew = sim::calculate_spin_energy(atom_number1, AtomExchangeType);
			
			// Calculate difference in Joules/mu_B
			delta_energy1 = (Enew-Eold)*mp::material[imat1].mu_s_SI*1.07828231e23; //1/9.27400915e-24
			
			// Check for lower energy state and accept unconditionally
			if(delta_energy1<0){
            cmc::mc_success += 1.0;
         }
			// Otherwise evaluate probability for move
			else{
				if(exp(-delta_energy1*rescaled_material_kBTBohr[imat1]) >= mtrandom::grnd()){
               cmc::mc_success += 1.0;
            }
				// If rejected reset spin coordinates and continue
				else{
					atoms::x_spin_array[atom_number1] = spin1_initial[0];
					atoms::y_spin_array[atom_number1] = spin1_initial[1];
					atoms::z_spin_array[atom_number1] = spin1_initial[2];
               cmc::energy_reject += 1.0;
				}
			}
		}
		else{
		// constrained MC move
		const int imat=imat1;
		
		// Save initial Spin 1
		spin1_initial[0] = atoms::x_spin_array[atom_number1];
		spin1_initial[1] = atoms::y_spin_array[atom_number1];
		spin1_initial[2] = atoms::z_spin_array[atom_number1];
		
		//spin1_init_mvd = matmul(polar_matrix, spin1_initial)
		spin1_init_mvd[0]=cmc::cmc_mat[imat].ppolar_matrix[0][0]*spin1_initial[0]+cmc::cmc_mat[imat].ppolar_matrix[0][1]*spin1_initial[1]+cmc::cmc_mat[imat].ppolar_matrix[0][2]*spin1_initial[2];
		spin1_init_mvd[1]=cmc::cmc_mat[imat].ppolar_matrix[1][0]*spin1_initial[0]+cmc::cmc_mat[imat].ppolar_matrix[1][1]*spin1_initial[1]+cmc::cmc_mat[imat].ppolar_matrix[1][2]*spin1_initial[2];
		spin1_init_mvd[2]=cmc::cmc_mat[imat].ppolar_matrix[2][0]*spin1_initial[0]+cmc::cmc_mat[imat].ppolar_matrix[2][1]*spin1_initial[1]+cmc::cmc_mat[imat].ppolar_matrix[2][2]*spin1_initial[2];

      // Make Monte Carlo move
      sim::mc_move(spin1_initial, spin1_final);

		//spin1_fin_mvd = matmul(polar_matrix, spin1_final)
		spin1_fin_mvd[0]=cmc::cmc_mat[imat].ppolar_matrix[0][0]*spin1_final[0]+cmc::cmc_mat[imat].ppolar_matrix[0][1]*spin1_final[1]+cmc::cmc_mat[imat].ppolar_matrix[0][2]*spin1_final[2];
		spin1_fin_mvd[1]=cmc::cmc_mat[imat].ppolar_matrix[1][0]*spin1_final[0]+cmc::cmc_mat[imat].ppolar_matrix[1][1]*spin1_final[1]+cmc::cmc_mat[imat].ppolar_matrix[1][2]*spin1_final[2];
		spin1_fin_mvd[2]=cmc::cmc_mat[imat].ppolar_matrix[2][0]*spin1_final[0]+cmc::cmc_mat[imat].ppolar_matrix[2][1]*spin1_final[1]+cmc::cmc_mat[imat].ppolar_matrix[2][2]*spin1_final[2];

		// Calculate current energy
		Eold = sim::calculate_spin_energy(atom_number1, AtomExchangeType);
			
		// Copy new spin position (provisionally accept move)
		atoms::x_spin_array[atom_number1] = spin1_final[0];
		atoms::y_spin_array[atom_number1] = spin1_final[1];
		atoms::z_spin_array[atom_number1] = spin1_final[2];

		// Calculate new energy
		Enew = sim::calculate_spin_energy(atom_number1, AtomExchangeType);
			
		// Calculate difference in Joules/mu_B
		delta_energy1 = (Enew-Eold)*mp::material[imat1].mu_s_SI*1.07828231e23; //1/9.27400915e-24

		// Compute second move

		// Randomly select spin number 2 (i/=j) of same material type
		atom_number2 = cmc::atom_list[imat1][int(mtrandom::grnd()*cmc::atom_list[imat1].size())];
		imat2=atoms::type_array[atom_number2];
		if(imat1!=imat2){
			terminaltextcolor(RED);
			std::cerr << "Error in MC/CMC integration! - atoms pairs are not from same material!" << std::endl;
			terminaltextcolor(WHITE);
			err::vexit();
		}
		
		// Save initial Spin 2
		spin2_initial[0] = atoms::x_spin_array[atom_number2];
		spin2_initial[1] = atoms::y_spin_array[atom_number2];
		spin2_initial[2] = atoms::z_spin_array[atom_number2];
		
		//spin2_init_mvd = matmul(polar_matrix, spin2_initial)
		spin2_init_mvd[0]=cmc::cmc_mat[imat].ppolar_matrix[0][0]*spin2_initial[0]+cmc::cmc_mat[imat].ppolar_matrix[0][1]*spin2_initial[1]+cmc::cmc_mat[imat].ppolar_matrix[0][2]*spin2_initial[2];
		spin2_init_mvd[1]=cmc::cmc_mat[imat].ppolar_matrix[1][0]*spin2_initial[0]+cmc::cmc_mat[imat].ppolar_matrix[1][1]*spin2_initial[1]+cmc::cmc_mat[imat].ppolar_matrix[1][2]*spin2_initial[2];
		spin2_init_mvd[2]=cmc::cmc_mat[imat].ppolar_matrix[2][0]*spin2_initial[0]+cmc::cmc_mat[imat].ppolar_matrix[2][1]*spin2_initial[1]+cmc::cmc_mat[imat].ppolar_matrix[2][2]*spin2_initial[2];

		// Calculate new spin based on constraint Mx=My=0
		spin2_fin_mvd[0] = spin1_init_mvd[0]+spin2_init_mvd[0]-spin1_fin_mvd[0];
		spin2_fin_mvd[1] = spin1_init_mvd[1]+spin2_init_mvd[1]-spin1_fin_mvd[1];

		if(((spin2_fin_mvd[0]*spin2_fin_mvd[0]+spin2_fin_mvd[1]*spin2_fin_mvd[1])<1.0) && (atom_number1 != atom_number2)){ 

			spin2_fin_mvd[2] = vmath::sign(spin2_init_mvd[2])*sqrt(1.0-spin2_fin_mvd[0]*spin2_fin_mvd[0] - spin2_fin_mvd[1]*spin2_fin_mvd[1]);

			//spin2_final = matmul(polar_matrix_tp, spin2_fin_mvd)
			spin2_final[0]=cmc::cmc_mat[imat].ppolar_matrix_tp[0][0]*spin2_fin_mvd[0]+cmc::cmc_mat[imat].ppolar_matrix_tp[0][1]*spin2_fin_mvd[1]+cmc::cmc_mat[imat].ppolar_matrix_tp[0][2]*spin2_fin_mvd[2];
			spin2_final[1]=cmc::cmc_mat[imat].ppolar_matrix_tp[1][0]*spin2_fin_mvd[0]+cmc::cmc_mat[imat].ppolar_matrix_tp[1][1]*spin2_fin_mvd[1]+cmc::cmc_mat[imat].ppolar_matrix_tp[1][2]*spin2_fin_mvd[2];
			spin2_final[2]=cmc::cmc_mat[imat].ppolar_matrix_tp[2][0]*spin2_fin_mvd[0]+cmc::cmc_mat[imat].ppolar_matrix_tp[2][1]*spin2_fin_mvd[1]+cmc::cmc_mat[imat].ppolar_matrix_tp[2][2]*spin2_fin_mvd[2];

			//Calculate Energy Difference 2
			// Calculate current energy
			Eold = sim::calculate_spin_energy(atom_number2, AtomExchangeType);

         // Copy new spin position (provisionally accept move)
			atoms::x_spin_array[atom_number2] = spin2_final[0];
			atoms::y_spin_array[atom_number2] = spin2_final[1];
			atoms::z_spin_array[atom_number2] = spin2_final[2];

			// Calculate new energy
			Enew = sim::calculate_spin_energy(atom_number2, AtomExchangeType);

         // Calculate difference in Joules/mu_B
			delta_energy2 = (Enew-Eold)*mp::material[imat2].mu_s_SI*1.07828231e23; //1/9.27400915e-24

			// Calculate Delta E for both spins
			delta_energy21 = delta_energy1*rescaled_material_kBTBohr[imat1] + delta_energy2*rescaled_material_kBTBohr[imat2];

			// Compute Mz_other, Mz, Mz'
			Mz_old = cmc::cmc_mat[imat].M_other[0]*cmc::cmc_mat[imat].ppolar_vector[0] + cmc::cmc_mat[imat].M_other[1]*cmc::cmc_mat[imat].ppolar_vector[1] + cmc::cmc_mat[imat].M_other[2]*cmc::cmc_mat[imat].ppolar_vector[2];

			Mz_new = (cmc::cmc_mat[imat].M_other[0] + spin1_final[0] + spin2_final[0]- spin1_initial[0] - spin2_initial[0])*cmc::cmc_mat[imat].ppolar_vector[0] +
						(cmc::cmc_mat[imat].M_other[1] + spin1_final[1] + spin2_final[1]- spin1_initial[1] - spin2_initial[1])*cmc::cmc_mat[imat].ppolar_vector[1] +
						(cmc::cmc_mat[imat].M_other[2] + spin1_final[2] + spin2_final[2]- spin1_initial[2] - spin2_initial[2])*cmc::cmc_mat[imat].ppolar_vector[2];

			// Check for lower energy state and accept unconditionally
			//if((delta_energy21<0.0) && (Mz_new>=0.0) ) continue;
			
			// Otherwise evaluate probability for move
			//else{
				// If move is favorable then accept
				probability = exp(-delta_energy21)*((Mz_new/Mz_old)*(Mz_new/Mz_old))*std::fabs(spin2_init_mvd[2]/spin2_fin_mvd[2]);
				if((probability>=mtrandom::grnd()) && (Mz_new>=0.0) ){
					cmc::cmc_mat[imat].M_other[0] = cmc::cmc_mat[imat].M_other[0] + spin1_final[0] + spin2_final[0] - spin1_initial[0] - spin2_initial[0];
					cmc::cmc_mat[imat].M_other[1] = cmc::cmc_mat[imat].M_other[1] + spin1_final[1] + spin2_final[1] - spin1_initial[1] - spin2_initial[1];
					cmc::cmc_mat[imat].M_other[2] = cmc::cmc_mat[imat].M_other[2] + spin1_final[2] + spin2_final[2] - spin1_initial[2] - spin2_initial[2];
					cmc::mc_success += 1.0;
				}
				//if both p1 and p2 not allowed then
				else{ 
					// reset spin positions
					atoms::x_spin_array[atom_number1] = spin1_initial[0];
					atoms::y_spin_array[atom_number1] = spin1_initial[1];
					atoms::z_spin_array[atom_number1] = spin1_initial[2];

					atoms::x_spin_array[atom_number2] = spin2_initial[0];
					atoms::y_spin_array[atom_number2] = spin2_initial[1];
					atoms::z_spin_array[atom_number2] = spin2_initial[2];

					cmc::energy_reject += 1.0;
				}
			//}
		}
		// if s2 not on unit sphere
		else{ 
			atoms::x_spin_array[atom_number1] = spin1_initial[0];
			atoms::y_spin_array[atom_number1] = spin1_initial[1];
			atoms::z_spin_array[atom_number1] = spin1_initial[2];
			cmc::sphere_reject+=1.0;
		}
		} // end of cmc move
		cmc::mc_total += 1.0;
	} // end of mc loop
	
	return EXIT_SUCCESS;
}
Ejemplo n.º 3
0
/// @brief Monte Carlo Integrator
///
/// @callgraph
/// @callergraph
///
/// @details Integrates the system using a Monte Carlo solver with tuned step width 
///
/// @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    05/02/2011
///
/// @return EXIT_SUCCESS
/// 
/// @internal
///	Created:		05/02/2011
///	Revision:	  ---
///=====================================================================================
///
int MonteCarlo(){
	
	// Check for calling of function
	if(err::check==true) std::cout << "sim::MonteCarlo has been called" << std::endl;

	// calculate number of steps to calculate
	int nmoves = atoms::num_atoms;

	// Declare arrays for spin states
	std::valarray<double> Sold(3);
	std::valarray<double> Snew(3);

	// Temporaries
	int atom=0;
	double r=1.0;
	double Eold=0.0;
	double Enew=0.0;
	double DE=0.0;
	const int AtomExchangeType=atoms::exchange_type;
	
   // Material dependent temperature rescaling
   std::vector<double> rescaled_material_kBTBohr(mp::num_materials);
   std::vector<double> sigma_array(mp::num_materials); // range for tuned gaussian random move
   for(int m=0; m<mp::num_materials; ++m){
      double alpha = mp::material[m].temperature_rescaling_alpha;
      double Tc = mp::material[m].temperature_rescaling_Tc;
      double rescaled_temperature = sim::temperature < Tc ? Tc*pow(sim::temperature/Tc,alpha) : sim::temperature;
      rescaled_material_kBTBohr[m] = 9.27400915e-24/(rescaled_temperature*1.3806503e-23);
      sigma_array[m] = rescaled_temperature < 1.0 ? 0.02 : pow(1.0/rescaled_material_kBTBohr[m],0.2)*0.08;
   }

   double statistics_moves = 0.0;
   double statistics_reject = 0.0;

	// loop over natoms to form a single Monte Carlo step
	for(int i=0;i<nmoves; i++){
		
      // add one to number of moves counter
      statistics_moves+=1.0;

		// pick atom
		atom = int(nmoves*mtrandom::grnd());
		
		// get material id
		const int imaterial=atoms::type_array[atom];

      // Calculate range for move
      sim::mc_delta_angle=sigma_array[imaterial];

		// Save old spin position
		Sold[0] = atoms::x_spin_array[atom];
		Sold[1] = atoms::y_spin_array[atom];
		Sold[2] = atoms::z_spin_array[atom];

      // Make Monte Carlo move
      sim::mc_move(Sold, Snew);

		// Calculate current energy
		Eold = sim::calculate_spin_energy(atom, AtomExchangeType);
		
		// Copy new spin position
		atoms::x_spin_array[atom] = Snew[0];
		atoms::y_spin_array[atom] = Snew[1];
		atoms::z_spin_array[atom] = Snew[2];

		// Calculate new energy
		Enew = sim::calculate_spin_energy(atom, AtomExchangeType);
		
		// Calculate difference in Joules/mu_B
		DE = (Enew-Eold)*mp::material[imaterial].mu_s_SI*1.07828231e23; //1/9.27400915e-24
		
		// Check for lower energy state and accept unconditionally
		if(DE<0) continue;
		// Otherwise evaluate probability for move
		else{
			if(exp(-DE*rescaled_material_kBTBohr[imaterial]) >= mtrandom::grnd()) continue;
			// If rejected reset spin coordinates and continue
			else{
				atoms::x_spin_array[atom] = Sold[0];
				atoms::y_spin_array[atom] = Sold[1];
				atoms::z_spin_array[atom] = Sold[2];
            // add one to rejection counter
            statistics_reject += 1.0;
				continue;
			}
		}
	}
	
   // Save statistics to sim namespace variable
   sim::mc_statistics_moves += statistics_moves;
   sim::mc_statistics_reject += statistics_reject;

	return EXIT_SUCCESS;
}
Ejemplo n.º 4
0
///
/// @brief      Runs the Constrained Monte Carlo algorithm
///
/// Chooses nspins random spin pairs from the spin system and attempts a
/// Constrained Monte Carlo move on each pair, accepting for either lower
/// energy or with a Boltzmann thermal weighting.
///
/// @return     void
///
int ConstrainedMonteCarlo(){

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

	// check for cmc initialisation
	if(cmc::is_initialised==false) CMCinit();

	int atom_number1;
	int atom_number2;
	int imat1;
	int imat2;

	double delta_energy1;
	double delta_energy2;
	double delta_energy21;

	double Eold;
	double Enew;

	std::valarray<double> spin1_initial(3);
	std::valarray<double> spin1_final(3);
	double spin2_initial[3];
	double spin2_final[3];

	double spin1_init_mvd[3];
	double spin1_fin_mvd[3];
	double spin2_init_mvd[3];
	double spin2_fin_mvd[3];

	double M_other[3];
	double Mz_old;
	double Mz_new;

	double probability;

   // Material dependent temperature rescaling
   std::vector<double> rescaled_material_kBTBohr(mp::num_materials);
   std::vector<double> sigma_array(mp::num_materials); // range for tuned gaussian random move
   for(int m=0; m<mp::num_materials; ++m){
      double alpha = mp::material[m].temperature_rescaling_alpha;
      double Tc = mp::material[m].temperature_rescaling_Tc;
      double rescaled_temperature = sim::temperature < Tc ? Tc*pow(sim::temperature/Tc,alpha) : sim::temperature;
      rescaled_material_kBTBohr[m] = 9.27400915e-24/(rescaled_temperature*1.3806503e-23);
      sigma_array[m] = rescaled_temperature < 1.0 ? 0.02 : pow(1.0/rescaled_material_kBTBohr[m],0.2)*0.08;
   }

	// copy matrices for speed
	double ppolar_vector[3];
	double ppolar_matrix[3][3];
	double ppolar_matrix_tp[3][3];

	for (int i=0;i<3;i++){
		ppolar_vector[i]=cmc::polar_vector[0][i];
		for (int j=0;j<3;j++){
			ppolar_matrix[i][j]=cmc::polar_matrix[i][j];
			ppolar_matrix_tp[i][j]=cmc::polar_matrix_tp[i][j];
		}
	}

	//sigma = ((kbT_bohr)**0.2)*0.08_dp
	//inv_kbT_bohr = 1.0_dp/kbT_bohr

	M_other[0] = 0.0;
	M_other[1] = 0.0;
	M_other[2] = 0.0;

	for(int atom=0;atom<atoms::num_atoms;atom++){
		const double mu = atoms::m_spin_array[atom];
		M_other[0] = M_other[0] + atoms::x_spin_array[atom] * mu; //multiplied by polar_vector below
		M_other[1] = M_other[1] + atoms::y_spin_array[atom] * mu;
		M_other[2] = M_other[2] + atoms::z_spin_array[atom] * mu;
	}

	for (int mcs=0;mcs<atoms::num_atoms;mcs++){
		// Randomly select spin number 1
		atom_number1 = int(mtrandom::grnd()*atoms::num_atoms);
		imat1=atoms::type_array[atom_number1];

		// get spin moment for atom 1
		const double mu1 = atoms::m_spin_array[atom_number1];

      sim::mc_delta_angle=sigma_array[imat1];

		// Save initial Spin 1
		spin1_initial[0] = atoms::x_spin_array[atom_number1];
		spin1_initial[1] = atoms::y_spin_array[atom_number1];
		spin1_initial[2] = atoms::z_spin_array[atom_number1];

		//spin1_init_mvd = matmul(polar_matrix, spin1_initial)
		spin1_init_mvd[0]=ppolar_matrix[0][0]*spin1_initial[0]+ppolar_matrix[0][1]*spin1_initial[1]+ppolar_matrix[0][2]*spin1_initial[2];
		spin1_init_mvd[1]=ppolar_matrix[1][0]*spin1_initial[0]+ppolar_matrix[1][1]*spin1_initial[1]+ppolar_matrix[1][2]*spin1_initial[2];
		spin1_init_mvd[2]=ppolar_matrix[2][0]*spin1_initial[0]+ppolar_matrix[2][1]*spin1_initial[1]+ppolar_matrix[2][2]*spin1_initial[2];

      // Make Monte Carlo move
      sim::mc_move(spin1_initial,spin1_final);

		//spin1_fin_mvd = matmul(polar_matrix, spin1_final)
		spin1_fin_mvd[0]=ppolar_matrix[0][0]*spin1_final[0]+ppolar_matrix[0][1]*spin1_final[1]+ppolar_matrix[0][2]*spin1_final[2];
		spin1_fin_mvd[1]=ppolar_matrix[1][0]*spin1_final[0]+ppolar_matrix[1][1]*spin1_final[1]+ppolar_matrix[1][2]*spin1_final[2];
		spin1_fin_mvd[2]=ppolar_matrix[2][0]*spin1_final[0]+ppolar_matrix[2][1]*spin1_final[1]+ppolar_matrix[2][2]*spin1_final[2];

		// Calculate Energy Difference 1
		//call calc_one_spin_energy(delta_energy1,spin1_final,atom_number1)

		// Calculate current energy
		Eold = sim::calculate_spin_energy(atom_number1);

		// Copy new spin position (provisionally accept move)
		atoms::x_spin_array[atom_number1] = spin1_final[0];
		atoms::y_spin_array[atom_number1] = spin1_final[1];
		atoms::z_spin_array[atom_number1] = spin1_final[2];

		// Calculate new energy
		Enew = sim::calculate_spin_energy(atom_number1);

		// Calculate difference in Joules/mu_B
		delta_energy1 = (Enew-Eold)*mp::material[imat1].mu_s_SI*1.07828231e23; //1/9.27400915e-24

		// Compute second move

		// Randomly select spin number 2 (i/=j)
		atom_number2 = int(mtrandom::grnd()*atoms::num_atoms);
		imat2=atoms::type_array[atom_number2];
      // get spin moment for atom 2
      const double mu2 = atoms::m_spin_array[atom_number2];

		// Save initial Spin 2
		spin2_initial[0] = atoms::x_spin_array[atom_number2];
		spin2_initial[1] = atoms::y_spin_array[atom_number2];
		spin2_initial[2] = atoms::z_spin_array[atom_number2];

		//spin2_init_mvd = matmul(polar_matrix, spin2_initial)
		spin2_init_mvd[0]=ppolar_matrix[0][0]*spin2_initial[0]+ppolar_matrix[0][1]*spin2_initial[1]+ppolar_matrix[0][2]*spin2_initial[2];
		spin2_init_mvd[1]=ppolar_matrix[1][0]*spin2_initial[0]+ppolar_matrix[1][1]*spin2_initial[1]+ppolar_matrix[1][2]*spin2_initial[2];
		spin2_init_mvd[2]=ppolar_matrix[2][0]*spin2_initial[0]+ppolar_matrix[2][1]*spin2_initial[1]+ppolar_matrix[2][2]*spin2_initial[2];

		// Calculate new spin based on constraint Mx=My=0
		const double mom_ratio = (mu1/mu2);
		spin2_fin_mvd[0] = mom_ratio*(spin1_init_mvd[0]-spin1_fin_mvd[0]) + spin2_init_mvd[0];
		spin2_fin_mvd[1] = mom_ratio*(spin1_init_mvd[1]-spin1_fin_mvd[1]) + spin2_init_mvd[1];

		if(((spin2_fin_mvd[0]*spin2_fin_mvd[0]+spin2_fin_mvd[1]*spin2_fin_mvd[1])<1.0) && (atom_number1 != atom_number2)){

			spin2_fin_mvd[2] = vmath::sign(spin2_init_mvd[2])*sqrt(1.0-spin2_fin_mvd[0]*spin2_fin_mvd[0] - spin2_fin_mvd[1]*spin2_fin_mvd[1]);

			//spin2_final = matmul(polar_matrix_tp, spin2_fin_mvd)
			spin2_final[0]=ppolar_matrix_tp[0][0]*spin2_fin_mvd[0]+ppolar_matrix_tp[0][1]*spin2_fin_mvd[1]+ppolar_matrix_tp[0][2]*spin2_fin_mvd[2];
			spin2_final[1]=ppolar_matrix_tp[1][0]*spin2_fin_mvd[0]+ppolar_matrix_tp[1][1]*spin2_fin_mvd[1]+ppolar_matrix_tp[1][2]*spin2_fin_mvd[2];
			spin2_final[2]=ppolar_matrix_tp[2][0]*spin2_fin_mvd[0]+ppolar_matrix_tp[2][1]*spin2_fin_mvd[1]+ppolar_matrix_tp[2][2]*spin2_fin_mvd[2];

			// Automatically accept move for Spin1 - now before if ^^
			//atomic_spin_array(:,atom_number1) = spin1_final(:)

			//Calculate Energy Difference 2
			// Calculate current energy
			Eold = sim::calculate_spin_energy(atom_number2);

			// Copy new spin position (provisionally accept move)
			atoms::x_spin_array[atom_number2] = spin2_final[0];
			atoms::y_spin_array[atom_number2] = spin2_final[1];
			atoms::z_spin_array[atom_number2] = spin2_final[2];

			// Calculate new energy
			Enew = sim::calculate_spin_energy(atom_number2);

			// Calculate difference in Joules/mu_B
			delta_energy2 = (Enew-Eold)*mp::material[imat2].mu_s_SI*1.07828231e23; //1/9.27400915e-24

			// Calculate Delta E for both spins
			delta_energy21 = delta_energy1*rescaled_material_kBTBohr[imat1] + delta_energy2*rescaled_material_kBTBohr[imat2];

			// Compute Mz_other, Mz, Mz'
			Mz_old = M_other[0]*ppolar_vector[0] + M_other[1]*ppolar_vector[1] + M_other[2]*ppolar_vector[2];

			Mz_new = (M_other[0] + mu1*spin1_final[0] + mu2*spin2_final[0]- mu1*spin1_initial[0] - mu2*spin2_initial[0])*ppolar_vector[0] +
						(M_other[1] + mu1*spin1_final[1] + mu2*spin2_final[1]- mu1*spin1_initial[1] - mu2*spin2_initial[1])*ppolar_vector[1] +
						(M_other[2] + mu1*spin1_final[2] + mu2*spin2_final[2]- mu1*spin1_initial[2] - mu2*spin2_initial[2])*ppolar_vector[2];

			// Check for lower energy state and accept unconditionally
			//if((delta_energy21<0.0) && (Mz_new>0.0)) continue;

			// Otherwise evaluate probability for move
			//else{
				// If move is favorable then accept
				probability = exp(-delta_energy21)*((Mz_new/Mz_old)*(Mz_new/Mz_old))*std::fabs(spin2_init_mvd[2]/spin2_fin_mvd[2]);
				if((probability>=mtrandom::grnd()) && (Mz_new>0.0) ){
					M_other[0] = M_other[0] + mu1*spin1_final[0] + mu2*spin2_final[0] - mu1*spin1_initial[0] - mu2*spin2_initial[0];
					M_other[1] = M_other[1] + mu1*spin1_final[1] + mu2*spin2_final[1] - mu1*spin1_initial[1] - mu2*spin2_initial[1];
					M_other[2] = M_other[2] + mu1*spin1_final[2] + mu2*spin2_final[2] - mu1*spin1_initial[2] - mu2*spin2_initial[2];
					cmc::mc_success += 1.0;
				}
				//if both p1 and p2 not allowed then
				else{
					// reset spin positions
					atoms::x_spin_array[atom_number1] = spin1_initial[0];
					atoms::y_spin_array[atom_number1] = spin1_initial[1];
					atoms::z_spin_array[atom_number1] = spin1_initial[2];

					atoms::x_spin_array[atom_number2] = spin2_initial[0];
					atoms::y_spin_array[atom_number2] = spin2_initial[1];
					atoms::z_spin_array[atom_number2] = spin2_initial[2];

					cmc::energy_reject += 1.0;
				}
			//}
		}
		// if s2 not on unit sphere
		else{
			atoms::x_spin_array[atom_number1] = spin1_initial[0];
			atoms::y_spin_array[atom_number1] = spin1_initial[1];
			atoms::z_spin_array[atom_number1] = spin1_initial[2];
			cmc::sphere_reject+=1.0;
		}

		cmc::mc_total += 1.0;
	}
	return EXIT_SUCCESS;
}