diff --git a/developers.txt b/developers.txt new file mode 100644 index 000000000..be9be8f8c --- /dev/null +++ b/developers.txt @@ -0,0 +1,18 @@ +Main Developers +----------------------------------------------- +Lead: Richard F L Evans, University of York +Sarah Jenkins, Canva UK +Andrea Meo, University of Bari, Italy +Matthew Ellis, University of Sheffield, UK +Mara Strungaru, University of Manchester, UK +Sergiu Ruta, Sheffield Hallam University, UK + +Contributors +----------------------------------------------- +Daniel Meilak, Andrew Naden, Jack Collings, +Oscar Arbelaez, Sam Morris, Rory Pond, +Weijia Fan, Milton Persson, Junlin Wang, +Wu Hong-Ye, Razvan Ababei, Sam Westmoreland, +Jackson Ross, Phanwadee Chureemart, +Pawel Sobieszczyk, Joseph Barker, Thomas Ostler, +Andreas Biternas, Roy W Chantrell diff --git a/hdr/anisotropy.hpp b/hdr/anisotropy.hpp index 1538b498f..a2ee5d0e7 100644 --- a/hdr/anisotropy.hpp +++ b/hdr/anisotropy.hpp @@ -3,9 +3,10 @@ // This file is part of the VAMPIRE open source package under the // Free BSD licence (see licence file for details). // -// (c) Sam Westmoreland 2016. All rights reserved. +// (c) Jack Collings and Sam Westmoreland 2016. All rights reserved. // // Email: sw766@york.ac.uk +// jbc525@york.ac.uk // //------------------------------------------------------------------------------ // @@ -26,6 +27,15 @@ //-------------------------------------------------------------------------------- namespace anisotropy { + //----------------------------------------------------------------------------- + // Function to check orthogonality, set orthonormality and generate last + // last anisotropy vector + //----------------------------------------------------------------------------- + void set_anisotropy_vectors( std::vector& v1, + std::vector& v2, + std::vector& v3, + int mat); + //----------------------------------------------------------------------------- // function to initialize anisotropy module //----------------------------------------------------------------------------- @@ -57,11 +67,37 @@ namespace anisotropy //----------------------------------------------------------------------------- double get_anisotropy_constant(const int material); double get_ku2(const int material); + double get_k2r1(const int material); + double get_k2r1_odd(const int material); + double get_k2r2(const int material); + double get_k2r2_odd(const int material); double get_ku4(const int material); + double get_k4r1(const int material); + double get_k4r1_odd(const int material); + double get_k4r2(const int material); + double get_k4r2_odd(const int material); + double get_k4r3(const int material); + double get_k4r3_odd(const int material); + double get_k4r4(const int material); + double get_k4r4_odd(const int material); double get_ku6(const int material); + double get_k6r1(const int material); + double get_k6r1_odd(const int material); + double get_k6r2(const int material); + double get_k6r2_odd(const int material); + double get_k6r3(const int material); + double get_k6r3_odd(const int material); + double get_k6r4(const int material); + double get_k6r4_odd(const int material); + double get_k6r5_odd(const int material); + double get_k6r5(const int material); + double get_k6r6(const int material); + double get_k6r6_odd(const int material); double get_kc4(const int material); double get_kc6(const int material); std::vector get_ku_vector(const int material); + std::vector get_kr_vector(const int material); + std::vector get_kl_vector(const int material); //----------------------------------------------------------------------------- // function to identify surface atoms @@ -78,6 +114,11 @@ namespace anisotropy //--------------------------------------------------------------------------- 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); + std::vector get_neel_tensor(); + + bool is_neel_enabled(); + + bool is_rotational_enabled(); } // end of anisotropy namespace #endif //ANISOTROPY_H_ diff --git a/hdr/atoms.hpp b/hdr/atoms.hpp index a9375b472..540da35ba 100644 --- a/hdr/atoms.hpp +++ b/hdr/atoms.hpp @@ -27,9 +27,14 @@ #include #include +#include #include "exchange.hpp" +// Load standard forms of fixed-width types (needed for some compilers) +using std::uint64_t; +using std::int64_t; + // unit vector type class uvec_t{ public: @@ -94,6 +99,7 @@ namespace atoms extern std::vector y_spin_array; extern std::vector z_spin_array; extern std::vector m_spin_array; /// Array of atomic spin moments + extern std::vector mass_spin_array; /// Array of mass of atoms extern std::vector x_total_spin_field_array; /// Total spin dependent fields extern std::vector y_total_spin_field_array; /// Total spin dependent fields @@ -101,6 +107,9 @@ namespace atoms extern std::vector x_total_external_field_array; /// Total external fields extern std::vector y_total_external_field_array; /// Total external fields extern std::vector z_total_external_field_array; /// Total external fields + extern std::vector x_thermal_field_array; /// Total external fields + extern std::vector y_thermal_field_array; /// Total external fields + extern std::vector z_thermal_field_array; /// Total external fields extern std::vector i_exchange_list; extern std::vector v_exchange_list; @@ -111,6 +120,14 @@ namespace atoms extern std::vector neighbour_eij_array; // unrolled list of eij unit vectors between neighbouring atoms + extern std::vector x_total_spin_forces_array; /// Total spin dependent fields + extern std::vector y_total_spin_forces_array; /// Total spin dependent fields + extern std::vector z_total_spin_forces_array; /// Total spin dependent fields + + extern std::vector x_velo_array; /// Total spin dependent fields + extern std::vector y_velo_array; /// Total spin dependent fields + extern std::vector z_velo_array; /// Total spin dependent fields + } diff --git a/hdr/category.hpp b/hdr/category.hpp index 0f590b505..1e2459b52 100644 --- a/hdr/category.hpp +++ b/hdr/category.hpp @@ -39,7 +39,7 @@ namespace cat{ double my; double mz; - // energy and toque variables + // energy and torque variables double torque; double mean_torque; double energy; @@ -48,6 +48,12 @@ namespace cat{ double theta; double phi; + // spin temperature variables + double spin_temp; + double mean_spin_temp; + double SxH2; + double SH; + // member functions category_t(); /// constructor diff --git a/hdr/cells.hpp b/hdr/cells.hpp index bf9a19d59..6773c65ea 100644 --- a/hdr/cells.hpp +++ b/hdr/cells.hpp @@ -31,6 +31,7 @@ namespace cells{ extern double macro_cell_size_x; /// lateral size of local macro-cells (A) extern double macro_cell_size_y; /// lateral size of local macro-cells (A) extern double macro_cell_size_z; /// lateral size of local macro-cells (A) + extern double atomic_volume; // volume of each magnetic moment for atomistic dipole self term extern std::vector local_cell_array; extern std::vector num_atoms_in_cell; /// number of atoms in each cell diff --git a/hdr/constants.hpp b/hdr/constants.hpp index a3e8a7108..f25d0f828 100644 --- a/hdr/constants.hpp +++ b/hdr/constants.hpp @@ -20,6 +20,7 @@ namespace constants{ extern const double muB; // Bohr Magneton (Joules / Tesla) extern const double kB; // Boltzmann constant (Joules / Kelvin) + extern const double kB_eV; //Boltzmann constant in eV } // end of exchange namespace diff --git a/hdr/create_atoms_class.hpp b/hdr/create_atoms_class.hpp index ec4c74883..66ab40b84 100644 --- a/hdr/create_atoms_class.hpp +++ b/hdr/create_atoms_class.hpp @@ -13,6 +13,12 @@ #ifndef CREATE_ATOMS_CLASS_H_ #define CREATE_ATOMS_CLASS_H_ +#include + +// Load standard forms of fixed-width types (needed for some compilers) +using std::uint64_t; +using std::int64_t; + //namespace create{ namespace cs{ diff --git a/hdr/demag.hpp b/hdr/demag.hpp index 08ebad574..d3c41e32f 100644 --- a/hdr/demag.hpp +++ b/hdr/demag.hpp @@ -34,6 +34,10 @@ #include #include +// Load standard forms of fixed-width types (needed for some compilers) +using std::uint64_t; +using std::int64_t; + namespace demag{ extern bool fast; diff --git a/hdr/dipole.hpp b/hdr/dipole.hpp index dea1dd7fb..1d5d2c9cc 100644 --- a/hdr/dipole.hpp +++ b/hdr/dipole.hpp @@ -14,6 +14,11 @@ // C++ standard library headers #include #include +#include + +// Load standard forms of fixed-width types (needed for some compilers) +using std::uint64_t; +using std::int64_t; // Vampire headers #include "dipole.hpp" @@ -55,6 +60,10 @@ namespace dipole{ extern double atomistic_cutoff; extern bool atomsitic_tensor_enabled; + extern int cells_num_cells; // number of macrocells + extern int cells_num_local_cells; // number of local macrocells + extern std::vector < double > cells_volume_array; // volume of each macrocell + //----------------------------------------------------------------------------- // Function to unroll cells dipolar field into atomic field //----------------------------------------------------------------------------- @@ -70,6 +79,11 @@ namespace dipole{ //------------------------------------------------------------------------------ double spin_magnetostatic_energy(const int atom, const double sx, const double sy, const double sz); + //-------------------------------------------------------- + // Function to output calculated dipole cell fields to file + //-------------------------------------------------------- + void output_dipole_fields(); + //-------------------------------------------------------- // Function to send cells field to be output in cfg file //-------------------------------------------------------- diff --git a/hdr/exchange.hpp b/hdr/exchange.hpp index 9773393f5..09c5312ae 100644 --- a/hdr/exchange.hpp +++ b/hdr/exchange.hpp @@ -72,6 +72,7 @@ class zten_t{ namespace exchange{ extern bool biquadratic; // flag to enable biquadratic exchange calculation + extern bool four_spin; // flag to enable four spin exchange calculation //----------------------------------------------------------------------------- // Function to initialise exchange module @@ -95,6 +96,7 @@ namespace exchange{ //--------------------------------------------------------------------------- double single_spin_energy(const int atom, const double sx, const double sy, const double sz); double single_spin_biquadratic_energy(const int atom, const double sx, const double sy, const double sz); + double single_spin_four_spin_energy(const int atom, const double sx, const double sy, const double sz); //----------------------------------------------------------------------------- // Function to calculate exchange fields for spins between start and end index diff --git a/hdr/lsf.hpp b/hdr/lsf.hpp new file mode 100644 index 000000000..906f12721 --- /dev/null +++ b/hdr/lsf.hpp @@ -0,0 +1,50 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) David R Papp 2024. All rights reserved. +// +//------------------------------------------------------------------------------ +// + +#ifndef LSF_H_ +#define LSF_H_ + +/// Header file for LLG namespace +namespace LSF_arrays{ + +//========================================================== +// Namespace to store persistant LSF integration arrays +//========================================================== + + extern std::vector x_lsf_array; + extern std::vector y_lsf_array; + extern std::vector z_lsf_array; + + extern std::vector x_euler_array; + extern std::vector y_euler_array; + extern std::vector z_euler_array; + + extern std::vector x_heun_array; + extern std::vector y_heun_array; + extern std::vector z_heun_array; + + extern std::vector x_spin_storage_array; + extern std::vector y_spin_storage_array; + extern std::vector z_spin_storage_array; + + extern std::vector x_initial_spin_array; + extern std::vector y_initial_spin_array; + extern std::vector z_initial_spin_array; + + extern bool LSF_set; + + extern std::vector mod_S; + + extern std::vector tx; + extern std::vector ty; + extern std::vector tz; + +} +#endif /*LSF_H_*/ diff --git a/hdr/lsf_mc.hpp b/hdr/lsf_mc.hpp new file mode 100644 index 000000000..2006b353c --- /dev/null +++ b/hdr/lsf_mc.hpp @@ -0,0 +1,23 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) David R Papp 2024. All rights reserved. +// +//------------------------------------------------------------------------------ +// + +// Program headers +#ifndef LSF_MC_H_ +#define LSF_MC_H_ + +namespace montecarlo{ + + // Declare spin vectors + extern std::vector mod_S; + extern bool mc_set; + extern void mcinit(); + +} +#endif /*LSF_MC_H_*/ diff --git a/hdr/lsf_rk4.hpp b/hdr/lsf_rk4.hpp new file mode 100644 index 000000000..2f6cb8203 --- /dev/null +++ b/hdr/lsf_rk4.hpp @@ -0,0 +1,54 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) David R Papp 2024. All rights reserved. +// +//------------------------------------------------------------------------------ +// + +#ifndef LSF_RK4_H_ +#define LSF_RK4_H_ + +// Header file for LSF-RK4 namespace +namespace LSF_RK4_arrays{ + +//========================================================== +// Namespace to store persistant LSF-RK4 integration arrays +//========================================================== + + extern std::vector x_lsf_array; + extern std::vector y_lsf_array; + extern std::vector z_lsf_array; + + extern std::vector x_initial_spin_array; + extern std::vector y_initial_spin_array; + extern std::vector z_initial_spin_array; + + extern std::vector x_k1_array; + extern std::vector y_k1_array; + extern std::vector z_k1_array; + + extern std::vector x_k2_array; + extern std::vector y_k2_array; + extern std::vector z_k2_array; + + extern std::vector x_k3_array; + extern std::vector y_k3_array; + extern std::vector z_k3_array; + + extern std::vector x_k4_array; + extern std::vector y_k4_array; + extern std::vector z_k4_array; + + extern bool LSF_RK4_set; + + extern std::vector mod_S; + + extern std::vector tx; + extern std::vector ty; + extern std::vector tz; + +} +#endif /*LSF_RK4_H_*/ diff --git a/hdr/material.hpp b/hdr/material.hpp index 19aaa62ad..bf73cb7aa 100644 --- a/hdr/material.hpp +++ b/hdr/material.hpp @@ -90,12 +90,12 @@ namespace mp std::vector override_atomsitic; double alpha; + double alpha_eq; double mu_s_SI; + double mass; double magnetisation; double gamma_rel; - double initial_spin[3]; - bool random_spins; double core_shell_size; double interface_roughness; @@ -113,6 +113,9 @@ namespace mp double one_oneplusalpha_sq; double alpha_oneplusalpha_sq; double H_th_sigma; +//modified SLD M Strungaru + double H_th_sigma_eq; + bool constrained; /// specifies primary or alternate integrator double temperature; /// Kelvin diff --git a/hdr/montecarlo.hpp b/hdr/montecarlo.hpp index c724b82ff..3a003e878 100644 --- a/hdr/montecarlo.hpp +++ b/hdr/montecarlo.hpp @@ -49,6 +49,8 @@ namespace montecarlo{ int cmc_mc_step(); void cmc_mc_step_mask(); void mc_step_parallel(std::vector &x_spin_array, std::vector &y_spin_array, std::vector &z_spin_array, std::vector &type_array); + void lsf_mc_step(); + void lsf_mc_step_parallel(std::vector &x_spin_array, std::vector &y_spin_array, std::vector &z_spin_array, std::vector &type_array); //--------------------------------------------------------------------------- // Provide access to CMCinit and CMCMCinit for cmc_anisotropy and @@ -58,6 +60,8 @@ namespace montecarlo{ void CMCMCinit(); void mc_parallel_init(std::vector &x, std::vector &y, std::vector &z, double min_dim[3], double max_dim[3]); + void lsf_mc_parallel_init(std::vector &x, std::vector &y, std::vector &z, + double min_dim[3], double max_dim[3]); void initialise_masked_cmc_mc(const int num_sets, // number of sets of constrained and unconstrained atoms const std::vector& mask, // unique ID for N sets of atoms with different constraints @@ -65,6 +69,7 @@ namespace montecarlo{ const std::vector& constraints); // list of 2N vectors listing constraint angles theta and phi extern bool mc_parallel_initialized; + extern bool lsf_mc_parallel_initialized; //--------------------------------------------------------------------------- // Function to perform monte carlo preconditioning diff --git a/hdr/program.hpp b/hdr/program.hpp index 6532ba29b..116d6f3e2 100644 --- a/hdr/program.hpp +++ b/hdr/program.hpp @@ -71,6 +71,9 @@ namespace program extern void mm_A_calculation(); extern void exchange_stiffness(); extern void electrical_pulse(); + extern void laser_electrical_pulse(); + extern void spin_waves(); // JRH + extern void field_pulse(); // Sundry programs and diagnostics not under general release extern int LLB_Boltzmann(); @@ -78,6 +81,7 @@ namespace program extern void boltzmann_dist(); extern void setting_process(); extern void boltzmann_dist_micromagnetic_llg(); + extern void hamr_cool(); } diff --git a/hdr/sim.hpp b/hdr/sim.hpp index 31f5b8b9a..7fca322e8 100644 --- a/hdr/sim.hpp +++ b/hdr/sim.hpp @@ -26,12 +26,17 @@ #define SIM_H_ //Headers +#include #include #include #include #include #include +// Load standard forms of fixed-width types (needed for some compilers) +using std::uint64_t; +using std::int64_t; + /// Enumerated lists for code readability enum pump_functions_t {square=0, two_temperature, double_pump_two_temperature, double_pump_square}; @@ -71,7 +76,8 @@ namespace sim{ // enumerated list for integrators enum integrator_t{ llg_heun = 0, monte_carlo = 1, llg_midpoint = 2, - cmc = 3, hybrid_cmc = 4, llg_quantum = 5}; + cmc = 3, hybrid_cmc = 4, llg_quantum = 5, lsf = 6, + lsf_mc = 7, lsf_rk4 = 8, suzuki_trotter = 9}; extern std::ofstream mag_file; extern uint64_t time; @@ -194,18 +200,48 @@ namespace sim{ extern int LLG_Midpoint(); extern int LLG_Midpoint_mpi(); extern int LLG_Midpoint_cuda(); - + extern int LSF_mpi(); + extern int LSF_RK4_mpi(); // Integrator initialisers extern int LLGinit(); + extern int LSFinit(); + extern int LSF_RK4_init(); // Field and energy functions - extern double calculate_spin_energy(const int atom); + extern double calculate_spin_energy(const int atom); extern double spin_applied_field_energy(const double, const double, const double); extern double spin_magnetostatic_energy(const int, const double, const double, const double); void calculate_spin_fields(const int start_index,const int end_index); void calculate_external_fields(const int start_index,const int end_index); + //spin temperature + extern double compute_spin_temperature(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& type_array, // type for atom + std::vector& x_spin_array, // coord vectors for atoms + std::vector& y_spin_array, + std::vector& z_spin_array, + std::vector& fields_array_x, // vectors for fields + std::vector& fields_array_y, + std::vector& fields_array_z, + std::vector& mu_s_array); + + extern double spin_temperature; + + //spin temperature + extern double compute_spin_temperature(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& type_array, // type for atom + std::vector& x_spin_array, // coord vectors for atoms + std::vector& y_spin_array, + std::vector& z_spin_array, + std::vector& fields_array_x, // vectors for fields + std::vector& fields_array_y, + std::vector& fields_array_z, + std::vector& mu_s_array); + + extern double spin_temperature; // LaGrange multiplier variables extern double lagrange_lambda_x; diff --git a/hdr/sld.hpp b/hdr/sld.hpp new file mode 100644 index 000000000..0bb9acba0 --- /dev/null +++ b/hdr/sld.hpp @@ -0,0 +1,184 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Mara Strungaru 2022. All rights reserved. +// +// Email: mara.strungaru@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +#ifndef SLD_H_ +#define SLD_H_ + +// C++ standard library headers +#include + +// Vampire headers +#include "sld.hpp" +#include + + +//-------------------------------------------------------------------------------- +// Namespace for variables and functions for sld module +//-------------------------------------------------------------------------------- +namespace sld{ + + //----------------------------------------------------------------------------- + // Externally visible SLD variables + //----------------------------------------------------------------------------- + extern bool enabled; // bolean to enable and disable sld components + + //----------------------------------------------------------------------------- + // Function to initialise sld module + //----------------------------------------------------------------------------- + void initialize(); + void tests(); + + + //--------------------------------------------------------------------------- + // Function to process input file parameters for sld module + //--------------------------------------------------------------------------- + bool match_input_parameter(std::string const key, std::string const word, std::string const value, std::string const unit, int const line); + + //--------------------------------------------------------------------------- + // 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); + + //--------------------------------------------------------------------------- + // Function to calculate forces in the spin-lattice module + //--------------------------------------------------------------------------- + void compute_forces(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& coord_array_x0, // coord vectors for atoms + const std::vector& coord_array_y0, + const std::vector& coord_array_z0, + const std::vector& coord_array_x, // coord vectors for atoms + const std::vector& coord_array_y, + const std::vector& coord_array_z, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z, + std::vector& potential_eng); + // + void compute_fields(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& coord_array_x, // coord vectors for atoms + const std::vector& coord_array_y, + const std::vector& coord_array_z, + const std::vector& spin_array_x, // coord vectors for atoms + const std::vector& spin_array_y, + const std::vector& spin_array_z, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z, + std::vector& fields_array_x, // vectors for forces + std::vector& fields_array_y, + std::vector& fields_array_z); + + void compute_forces_fields(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& coord_array_x0, // coord vectors for atoms + const std::vector& coord_array_y0, + const std::vector& coord_array_z0, + std::vector& coord_array_x, // coord vectors for atoms + std::vector& coord_array_y, + std::vector& coord_array_z, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z); + + extern int suzuki_trotter(); + + extern void stats_sld(); + + extern std::vector forces_array_x; + extern double var_test; + double PBC_wrap ( double dx, double L, bool bounds); + + double compute_spin_temperature(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& type_array, // type for atom + std::vector& x_spin_array, // coord vectors for atoms + std::vector& y_spin_array, + std::vector& z_spin_array, + std::vector& fields_array_x, // vectors for fields + std::vector& fields_array_y, + std::vector& fields_array_z, + std::vector& mu_s_array); + + double compute_lattice_temperature(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& type_array, // type for atom + std::vector& velo_array_x, // coord vectors for atoms + std::vector& velo_array_y, + std::vector& velo_array_z); + +// + double compute_potential_energy(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& type_array); + + double compute_kinetic_energy(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& type_array, // type for atom + std::vector& velo_array_x, // coord vectors for atoms + std::vector& velo_array_y, + std::vector& velo_array_z); + +// + double compute_effective_J(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + std::vector& sum_J); +// // + double compute_effective_C(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + std::vector& sum_C); +// + double compute_exchange_energy(const int start_index, // first atom for exchange interactions to be calculated + const int end_index); +// + double compute_coupling_energy(const int start_index, // first atom for exchange interactions to be calculated + const int end_index); + +// + extern double lattice_temperature; + extern double spin_temperature; + +/* + extern double potential_energy; + extern double kinetic_energy; + extern double sld_exchange_energy; + extern double sld_coupling_energy; + extern double sld_total_energy; + extern double sld_total_spin_energy;*/ + + + extern double J_eff; + extern double C_eff; + + void suzuki_trotter_parallel_init(std::vector &x, std::vector &y, std::vector &z, + double min_dim[3], double max_dim[3]); + extern bool suzuki_trotter_parallel_initialized; + void suzuki_trotter_step_parallel(std::vector &x_spin_array, std::vector &y_spin_array, std::vector &z_spin_array, std::vector &type_array); + + + +} // end of sld namespace + +#endif //SLD_H_ diff --git a/hdr/spininitialize.hpp b/hdr/spininitialize.hpp new file mode 100644 index 000000000..0157b002e --- /dev/null +++ b/hdr/spininitialize.hpp @@ -0,0 +1,93 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +#ifndef SPININITIALIZE_H_ +#define SPININITIALIZE_H_ + +// C++ standard library headers +#include + +// Vampire headers +#include "random.hpp" + +//-------------------------------------------------------------------------------- +// Namespace for variables and functions for spininitialize module +//-------------------------------------------------------------------------------- +namespace spininitialize{ + + //----------------------------------------------------------------------------- + // Grain-level magnetisation modes, set via set_grain_magnetisation_mode() + // below in response to the create:grain-magnetisation-direction keyword. + //----------------------------------------------------------------------------- + enum grain_magnetisation_mode_t{ + grain_mode_material = 0, // no grain-level post-processing (default) + grain_mode_alternating = 1 // flip spins for odd-numbered grains + }; + + //----------------------------------------------------------------------------- + // Function to initialise spininitialize module + //----------------------------------------------------------------------------- + void initialize(); + + //--------------------------------------------------------------------------- + // Function to process input file parameters for spininitialize module + //--------------------------------------------------------------------------- + bool match_input_parameter(std::string const key, std::string const word, std::string const value, std::string const unit, int const line); + + //--------------------------------------------------------------------------- + // 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); + + //--------------------------------------------------------------------------- + // Function to get the (normalised) initial spin direction for an atom of the + // given material at fractional coordinates (fx,fy,fz) of the system size. + // For materials with a "random" texture, prng is used to generate the + // random direction. + //--------------------------------------------------------------------------- + void initialize_spin(const int material, const double fx, const double fy, const double fz, + double& sx, double& sy, double& sz, MTRand& prng); + + //--------------------------------------------------------------------------- + // Function to set the grain-level magnetisation mode, called by the create + // module when it parses the create:grain-magnetisation-direction keyword. + // mode = 0 ("material"): no grain-level post-processing (default). + // mode = 1 ("alternating"): the spin direction computed from the material's + // texture is reversed (sx,sy,sz -> -sx,-sy,-sz) for every atom belonging to + // an odd-numbered grain (grains are numbered from 0), giving alternating + // (e.g. antiferromagnetic-like) grains. + //--------------------------------------------------------------------------- + void set_grain_magnetisation_mode(const int mode); + + //--------------------------------------------------------------------------- + // Function to apply the grain-level magnetisation mode (see + // set_grain_magnetisation_mode above) to the already-initialised spins of + // every atom. This is a one-off post-processing pass over atoms::*_spin_array, + // called once after all atoms have had their initial spin direction set by + // initialize_spin(). Keeping this logic out of initialize_spin() avoids an + // extra per-atom branch in the (much hotter) main initialisation loop for + // the common case where no grain-level alternation is requested. + //--------------------------------------------------------------------------- + void apply_grain_magnetisation_mode(); + + //--------------------------------------------------------------------------- + // Function to get the reference (uniform) spin direction for a material, for + // use by programs needing a single ground-state direction per sublattice + // (e.g. exchange stiffness calculation). Materials initialised with a + // non-uniform texture (domain wall, skyrmion, etc.) return the default + // direction (0,0,1). + //--------------------------------------------------------------------------- + void get_uniform_vector(const int material, double& sx, double& sy, double& sz); + +} // end of spininitialize namespace + +#endif //SPININITIALIZE_H_ diff --git a/hdr/spintextures.hpp b/hdr/spintextures.hpp new file mode 100644 index 000000000..e50fc13f6 --- /dev/null +++ b/hdr/spintextures.hpp @@ -0,0 +1,44 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Ricardo Rama-Eiroa 2025. All rights reserved. +// +// Email: ricardo.rama-eiroa@ed.ac.uk +// +//------------------------------------------------------------------------------ +// + +#ifndef SPINTEXTURES_H_ +#define SPINTEXTURES_H_ + +// C++ standard library headers +#include + +// Vampire headers +#include "spintextures.hpp" + +//-------------------------------------------------------------------------------- +// Namespace for variables and functions for spintextures module +//-------------------------------------------------------------------------------- +namespace spintextures{ + + //----------------------------------------------------------------------------- + // Function to initialise spintextures module + //----------------------------------------------------------------------------- + void initialize(); + + //--------------------------------------------------------------------------- + // Function to process input file parameters for spintextures module + //--------------------------------------------------------------------------- + bool match_input_parameter(std::string const key, std::string const word, std::string const value, std::string const unit, int const line); + + //--------------------------------------------------------------------------- + // 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); + +} // end of spintextures namespace + +#endif //SPINTEXTURES_H_ diff --git a/hdr/spintransport.hpp b/hdr/spintransport.hpp index 4ff89446b..f7931a24f 100644 --- a/hdr/spintransport.hpp +++ b/hdr/spintransport.hpp @@ -14,6 +14,7 @@ #define SPINTRANSPORT_H_ // C++ standard library headers +#include #include #include @@ -21,6 +22,10 @@ #include "create.hpp" #include "spintransport.hpp" +// Load standard forms of fixed-width types (needed for some compilers) +using std::uint64_t; +using std::int64_t; + //-------------------------------------------------------------------------------- // Namespace for variables and functions for spintransport module //-------------------------------------------------------------------------------- @@ -47,15 +52,15 @@ namespace spin_transport{ const std::vector& atoms_m_spin_array, // moments of atoms (muB) const std::vector& material_damping_array, // array of material level damping constants const std::vector& is_magnetic_material, // array of size num_mat to state whether material is magnetic (true) or not (false) - const std::vector non_magnetic_atoms_array // list of non-magnetic atoms + const std::vector& non_magnetic_atoms_array // list of non-magnetic atoms ); //----------------------------------------------------------------------------- // Function to update resistance, current and spin transfer torque fields //----------------------------------------------------------------------------- - void update(const unsigned int num_local_atoms, // number of local atoms - const std::vector& atoms_x_spin_array, // x-spin vector of atoms - const std::vector& atoms_y_spin_array, // y-spin vector of atoms + void update(const unsigned int num_local_atoms, // number of local atoms + const std::vector& atoms_x_spin_array, // x-spin vector of atoms + const std::vector& atoms_y_spin_array, // y-spin vector of atoms const std::vector& atoms_z_spin_array, // z-spin-vector of atoms const std::vector& atoms_m_spin_array); @@ -63,7 +68,8 @@ namespace spin_transport{ const unsigned int end_index, // last atom std::vector& atoms_x_field_array, // x-field of atoms std::vector& atoms_y_field_array, // y-field of atoms - std::vector& atoms_z_field_array); // z-field of atoms + std::vector& atoms_z_field_array, // z-field of atoms + const std::vector& atoms_type_array); // material ID of atom); //--------------------------------------------------------------------------- // Function to process input file parameters for spintransport module diff --git a/hdr/spinwaves.hpp b/hdr/spinwaves.hpp new file mode 100644 index 000000000..6b9f80a5b --- /dev/null +++ b/hdr/spinwaves.hpp @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) JoelHirst 2022. All rights reserved. +// +// Email: j.r.hirst@hallam.shu.ac.uk +// +//------------------------------------------------------------------------------ +// + +#ifndef SPINWAVES_H_ +#define SPINWAVES_H_ + +// C++ standard library headers +#include +#include // jrh + +// Vampire headers +#include "spinwaves.hpp" +#include "unitcell.hpp" // jrh +//-------------------------------------------------------------------------------- +// Namespace for variables and functions for spinwaves module +//-------------------------------------------------------------------------------- +namespace spinwaves{ + + + //----------------------------------------------------------------------------- + // Function to initialise spinwaves module + //----------------------------------------------------------------------------- + extern std::vector skx_r; + extern std::vector skx_i; + extern std::vector skx_r_node; + extern std::vector skx_i_node; + extern std::vector skx_r_node_transposed; + extern std::vector skx_i_node_transposed; + + + // THIS IS TEMPORARY + extern int nk_per_rank; + extern int scatterlength; + extern std::vector skx_r_scatter; + extern std::vector skx_i_scatter; + + + void fft_in_space( const std::vector& atom_coords_x, + const std::vector& atom_coords_y, + const std::vector& atom_coords_z, + const int time ); + + void fft_in_time(); + + //----------------------------------------------------------------------------- + // Function to initialise spinwaves module + //----------------------------------------------------------------------------- + void initialize(const double system_dimensions_x, + const double system_dimensions_y, + const double system_dimensions_z, + const double total_num_unit_cells_x, + const double total_num_unit_cells_y, + const double total_num_unit_cells_z, + const double unit_cell_size_x, + const double unit_cell_size_y, + const double unit_cell_size_z, + const std::vector& atom, + const std::vector& atom_coords_x, + const std::vector& atom_coords_y, + const std::vector& atom_coords_z); + + //--------------------------------------------------------------------------- + // Function to process input file parameters for spinwaves module + //--------------------------------------------------------------------------- + bool match_input_parameter(std::string const key, std::string const word, std::string const value, std::string const unit, int const line); + + //--------------------------------------------------------------------------- + // 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); + +} // end of spinwaves namespace + +#endif //SPINWAVES_H_ diff --git a/hdr/stats.hpp b/hdr/stats.hpp index 47ffc4d22..c79dd5b3f 100644 --- a/hdr/stats.hpp +++ b/hdr/stats.hpp @@ -29,12 +29,12 @@ namespace stats /// Statistics energy types enum energy_t { total = 0, exchange = 1, anisotropy = 2, applied_field = 3, magnetostatic = 4}; + /// SLD Statistics energy types + enum sld_energy_t { sld_total = 0, sld_exchange = 1, sld_coupling = 2, potential = 3, kinetic = 4}; + /// Statistics types enum stat_t { atotal=0, mean=1}; - /// Statistics output functions - //extern void output_energy(std::ostream&, enum energy_t, enum stat_t,bool header); - //------------------------------------------------- // New statistics module functions and variables //------------------------------------------------- @@ -61,6 +61,10 @@ namespace stats extern bool calculate_grain_energy; extern bool calculate_material_energy; + extern bool calculate_system_sld_energy; + extern bool calculate_grain_sld_energy; + extern bool calculate_material_sld_energy; + extern bool calculate_system_magnetization; extern bool calculate_grain_magnetization; extern bool calculate_material_magnetization; @@ -73,6 +77,18 @@ namespace stats extern bool calculate_grain_torque; extern bool calculate_material_torque; + extern bool calculate_system_spin_temp; + extern bool calculate_grain_spin_temp; + extern bool calculate_material_spin_temp; + + extern bool calculate_system_lattice_temp; + extern bool calculate_grain_lattice_temp; + extern bool calculate_material_lattice_temp; + + extern bool calculate_system_spin_temp; + extern bool calculate_grain_spin_temp; + extern bool calculate_material_spin_temp; + extern bool calculate_system_specific_heat; extern bool calculate_grain_specific_heat; extern bool calculate_material_specific_heat; @@ -83,6 +99,10 @@ namespace stats extern bool calculate_grain_susceptibility; extern bool calculate_material_susceptibility; + extern bool calculate_system_spin_length; + extern bool calculate_material_spin_length; + extern bool calculate_height_spin_length; + extern bool calculate_system_binder_cumulant; extern bool calculate_material_binder_cumulant; @@ -155,6 +175,70 @@ namespace stats }; + + //---------------------------------- + // Energy class definition for SLD statistics + //---------------------------------- + class sld_energy_statistic_t{ + + public: + sld_energy_statistic_t (std::string n):initialized(false){ + name = n; + }; + bool is_initialized(); + void set_mask(const int in_mask_size, const std::vector in_mask); + void get_mask(std::vector& out_mask, std::vector& out_normalisation); + void calculate(const std::vector& sx, const std::vector& sy, const std::vector& sz, + const std::vector& mm, const std::vector& mat, const double temperature); + + void reset_averages(); + + void set_sld_total_energy( std::vector& new_energy, std::vector& new_mean_energy); + void set_sld_exchange_energy( std::vector& new_energy, std::vector& new_mean_energy); + void set_sld_coupling_energy( std::vector& new_energy, std::vector& new_mean_energy); + void set_potential_energy( std::vector& new_energy, std::vector& new_mean_energy); + void set_kinetic_energy( std::vector& new_energy, std::vector& new_mean_energy); + + const std::vector& get_sld_total_energy(); + const std::vector& get_sld_exchange_energy(); + const std::vector& get_sld_coupling_energy(); + const std::vector& get_potential_energy(); + const std::vector& get_kinetic_energy(); + + void update_mean_counter(long counter); + + std::string output_sld_energy(enum sld_energy_t sld_energy_type, bool header); + std::string output_mean_sld_energy(enum sld_energy_t sld_energy_type, bool header); + + private: + bool initialized; + int num_atoms; + int mask_size; + double mean_counter; + + std::vector mask; + std::vector num_atoms_in_mask; + + + std::vector sld_total_energy; + std::vector sld_exchange_energy; + std::vector sld_coupling_energy; + std::vector potential_energy; + std::vector kinetic_energy; + + std::vector mean_sld_total_energy; + std::vector mean_sld_exchange_energy; + std::vector mean_sld_coupling_energy; + std::vector mean_potential_energy; + std::vector mean_kinetic_energy; + + std::vector zero_list; + std::vector normalisation; + + std::string name; + + }; + //---------------------------------- // Magnetization Class definition //---------------------------------- @@ -174,6 +258,9 @@ namespace stats void set_magnetization(std::vector& magnetization, std::vector& mean_magnetization, long counter); void reset_magnetization_averages(); const std::vector& get_magnetization(); + void save_checkpoint(std::ofstream& chkfile); + void load_checkpoint(std::ifstream& chkfile, bool chk_continue); + const std::vector& get_checkpoint_parameters(double& sum_mx, double& sum_my, double& sum_mz, double& sum_count); std::string output_magnetization(bool header); std::string output_normalized_magnetization(bool header); std::string output_normalized_magnetization_length(bool header); @@ -233,19 +320,96 @@ namespace stats }; + //---------------------------------- + // Spin temperature class definition + //---------------------------------- + class spin_temp_statistic_t{ + + public: + spin_temp_statistic_t (std::string n):initialized(false){ + name = n; + }; + bool is_initialized(); + void set_mask(const int mask_size, std::vector inmask, const std::vector& mm); + void get_mask(std::vector& out_mask, std::vector& out_normalisation); + void calculate_spin_temp(const std::vector& sx, const std::vector& sy, const std::vector& sz, + const std::vector& bxs, const std::vector& bys, const std::vector& bzs, + const std::vector& bxe, const std::vector& bye, const std::vector& bze, + const std::vector& mm); + void set_spin_temp(std::vector& spin_temp, std::vector& mean_spin_temp, long counter); + void reset_spin_temp_averages(); + const std::vector& get_spin_temp(); + std::string output_spin_temp(bool header); + std::string output_mean_spin_temp(bool header); + + private: + bool initialized; + int num_atoms; + int mask_size; + double mean_counter; + std::vector mask; + std::vector num_atoms_in_mask; + std::vector spin_temp; + std::vector mean_spin_temp; + std::vector SxH2; + std::vector SH; + std::vector zero_list; + std::vector normalisation; + std::string name; + + + }; + + //---------------------------------- + // lattice temperature class definition + //---------------------------------- + class lattice_temp_statistic_t{ + + public: + lattice_temp_statistic_t (std::string n):initialized(false){ + name = n; + }; + bool is_initialized(); + void set_mask(const int mask_size, std::vector in_mask, const std::vector& mm); + void get_mask(std::vector& out_mask, std::vector& out_normalisation); + + void calculate_lattice_temp(const std::vector& vx, const std::vector& vy, const std::vector& vz); + void set_lattice_temp(std::vector& lattice_temp, std::vector& mean_lattice_temp, long counter); + void reset_lattice_temp_averages(); + const std::vector& get_lattice_temp(); + std::string output_lattice_temp(bool header); + std::string output_mean_lattice_temp(bool header); + + private: + bool initialized; + int num_atoms; + int mask_size; + double mean_counter; + std::vector mask; + std::vector num_atoms_in_mask; + std::vector lattice_temp; + std::vector mean_lattice_temp; + std::vector zero_list; + std::vector normalisation; + std::string name; + + }; + //---------------------------------- // Specific Heat Class definition //---------------------------------- class specific_heat_statistic_t{ public: - specific_heat_statistic_t (std::string n):initialized(false){ - name = n; - }; - void initialize(energy_statistic_t& energy_statistic); - void calculate(const std::vector& energy); - void reset_averages(); - std::string output_mean_specific_heat(const double temperature,bool header); + specific_heat_statistic_t (std::string n):initialized(false){ + name = n; + }; + void initialize(energy_statistic_t& energy_statistic); + void calculate(const std::vector& energy); + void save_checkpoint(std::ofstream& chkfile); + void load_checkpoint(std::ifstream& chkfile, bool chk_continue); + void reset_averages(); + std::string output_mean_specific_heat(const double temperature,bool header); private: @@ -269,10 +433,12 @@ namespace stats susceptibility_statistic_t (std::string n):initialized(false){ name = n; }; - void initialize(magnetization_statistic_t& mag_stat); - void calculate(const std::vector& magnetization); - void reset_averages(); - std::string output_mean_susceptibility(const double temperature,bool header); + void initialize(magnetization_statistic_t& mag_stat); + void calculate(const std::vector& magnetization); + void save_checkpoint(std::ofstream& chkfile); + void load_checkpoint(std::ifstream& chkfile, bool chk_continue); + void reset_averages(); + std::string output_mean_susceptibility(const double temperature,bool header); //std::string output_mean_absolute_susceptibility(); private: @@ -287,6 +453,37 @@ namespace stats std::string name; }; + + //---------------------------------- + // Spin Length Class definition + //---------------------------------- + class spin_length_statistic_t{ + + public: + spin_length_statistic_t (std::string n):initialized(false){ + name = n; + }; + bool is_initialized(); + void set_mask(const int mask_size, std::vector inmask); + void get_mask(std::vector& out_mask); + void calculate_spin_length(const std::vector& sx, const std::vector& sy, const std::vector& sz); + void reset_averages(); + std::string output_mean_spin_length(bool header); + + private: + bool initialized; + int num_atoms; + int mask_size; + double mean_counter; + std::vector mask; + std::vector spin_length; + std::vector mean_spin_length; + std::vector zero_list; + std::vector normalisation; + std::string name; + + }; + //---------------------------------- // Standard Deviation of magnetisation in time Class definition //---------------------------------- @@ -348,23 +545,43 @@ namespace stats extern magnetization_statistic_t grain_magnetization; extern magnetization_statistic_t material_magnetization; extern magnetization_statistic_t material_grain_magnetization; - extern magnetization_statistic_t height_magnetization; - extern magnetization_statistic_t material_height_magnetization; - extern magnetization_statistic_t material_grain_height_magnetization; + extern magnetization_statistic_t height_magnetization; + extern magnetization_statistic_t material_height_magnetization; + extern magnetization_statistic_t material_grain_height_magnetization; extern torque_statistic_t system_torque; extern torque_statistic_t grain_torque; extern torque_statistic_t material_torque; - extern specific_heat_statistic_t system_specific_heat; + extern spin_temp_statistic_t system_spin_temp; + extern spin_temp_statistic_t grain_spin_temp; + extern spin_temp_statistic_t material_spin_temp; + + extern lattice_temp_statistic_t system_lattice_temp; + extern lattice_temp_statistic_t grain_lattice_temp; + extern lattice_temp_statistic_t material_lattice_temp; + + extern spin_temp_statistic_t system_spin_temp; + extern spin_temp_statistic_t grain_spin_temp; + extern spin_temp_statistic_t material_spin_temp; + + extern specific_heat_statistic_t system_specific_heat; extern specific_heat_statistic_t grain_specific_heat; - extern specific_heat_statistic_t material_specific_heat; + extern specific_heat_statistic_t material_specific_heat; - extern susceptibility_statistic_t system_susceptibility; + extern susceptibility_statistic_t system_susceptibility; extern susceptibility_statistic_t grain_susceptibility; - extern susceptibility_statistic_t material_susceptibility; + extern susceptibility_statistic_t material_susceptibility; + + extern standard_deviation_statistic_t material_standard_deviation; + + extern sld_energy_statistic_t system_sld_energy; + extern sld_energy_statistic_t grain_sld_energy; + extern sld_energy_statistic_t material_sld_energy; - extern standard_deviation_statistic_t material_standard_deviation; + extern spin_length_statistic_t system_spin_length; + extern spin_length_statistic_t material_spin_length; + extern spin_length_statistic_t height_spin_length; extern binder_cumulant_statistic_t system_binder_cumulant; extern binder_cumulant_statistic_t material_binder_cumulant; diff --git a/hdr/unitcell.hpp b/hdr/unitcell.hpp index c651fdd07..aef4c26e6 100644 --- a/hdr/unitcell.hpp +++ b/hdr/unitcell.hpp @@ -29,6 +29,8 @@ namespace unitcell{ //--------------------------------------------------------------------------- // Unit cell atom class definition //--------------------------------------------------------------------------- + extern std::string sw_crystal_structure; // JRH externally visible crystal structure for spinwaves path + class atom_t { public: double x; /// atom x-coordinate diff --git a/hdr/vio.hpp b/hdr/vio.hpp index ed5dc2039..3f7e6755d 100644 --- a/hdr/vio.hpp +++ b/hdr/vio.hpp @@ -25,6 +25,7 @@ #ifndef VIO_H_ #define VIO_H_ +#include #include #include #include @@ -44,6 +45,10 @@ #include #include +// Load standard forms of fixed-width types (needed for some compilers) +using std::uint64_t; +using std::int64_t; + // Global Output Streams extern std::ofstream zinfo; extern std::ofstream zmag; @@ -95,8 +100,8 @@ namespace vin{ extern void check_for_valid_vector(std::vector& u, std::string word, int line, std::string prefix, std::string unit, std::string unit_type, const std::vector& range_min, const std::vector& range_max, std::string input_file_type, std::string range_text); - extern void check_for_valid_bitsequence(std::vector& u, std::string word, int line, std::string prefix, const int range_min, - const int range_max, std::string input_file_type, std::string range_text); + extern void check_for_valid_bitsequence(std::vector& u, std::string word, int line, std::string prefix, const int range_min, + const int range_max, std::string input_file_type, std::string range_text); extern std::vector doubles_from_string(std::string value); extern std::vector integers_from_string(std::string value); @@ -106,6 +111,7 @@ namespace vin{ // simple functions to extract variables from strings extern uint64_t str_to_uint64(std::string input_str); + extern int str_to_int(std::string input_str); extern double str_to_double(std::string input_str); extern std::vector read_material; @@ -144,6 +150,8 @@ namespace vout{ extern int fw_size_int; extern int max_header; + extern std::string output_file_name; + //class that creates an object which acts like an output //stream but delivers fixed width output separated by //tabs diff --git a/hdr/vmath.hpp b/hdr/vmath.hpp index e282cd691..9ddf443d3 100644 --- a/hdr/vmath.hpp +++ b/hdr/vmath.hpp @@ -12,6 +12,11 @@ #include #include +#include + +// Load standard forms of fixed-width types (needed for some compilers) +using std::uint64_t; +using std::int64_t; /// @namespace ns /// @brief vmath namespace containing sundry math functions for vampire. @@ -46,6 +51,7 @@ namespace vmath{ /// extern bool point_in_polygon(double, double, double*, double*, int); extern bool point_in_polygon_factor(double, double, double, double*, double*, int); + extern bool point_in_polygon_scaled(double, double, double, double*, double*, int); extern bool point_in_polygon2(double, double, std::vector&, std::vector&, int); extern std::vector > matmul(std::vector > &, std::vector > &); extern std::vector matmul(std::vector &, std::vector > &); diff --git a/hdr/vmpi.hpp b/hdr/vmpi.hpp index 55d05b478..e4258caa8 100644 --- a/hdr/vmpi.hpp +++ b/hdr/vmpi.hpp @@ -32,6 +32,10 @@ #include #include +// Load standard forms of fixed-width types (needed for some compilers) +using std::uint64_t; +using std::int64_t; + #ifdef MPICF #include #endif @@ -64,7 +68,7 @@ namespace vmpi{ extern bool replicated_data_staged; ///< Flag for staged system generation - extern char hostname[20]; ///< Hostname of local CPU + extern std::string hostname; ///< Hostname of local CPU extern double min_dimensions[3]; ///< Minimum coordinates of system on local cpu extern double max_dimensions[3]; ///< Maximum coordinates of system on local cpu @@ -87,11 +91,13 @@ namespace vmpi{ extern std::vector send_start_index_array; extern std::vector send_num_array; extern std::vector send_spin_data_array; + extern std::vector send_coord_data_array; extern std::vector recv_atom_translation_array; extern std::vector recv_start_index_array; extern std::vector recv_num_array; extern std::vector recv_spin_data_array; + extern std::vector recv_coord_data_array; #ifdef MPICF extern std::vector requests; @@ -102,13 +108,17 @@ namespace vmpi{ extern void initialise(int argc, char *argv[]); extern int hosts(); extern int finalise(); - extern void geometric_decomposition(int, double []); + extern void geometric_decomposition(int, double [3]); extern double SwapTimer(double, double&); // functions for sending/receiving halo data extern void mpi_init_halo_swap(); extern void mpi_complete_halo_swap(); + // functions for sending/receiving halo data + extern void mpi_init_halo_swap_coords(); + extern void mpi_complete_halo_swap_coords(); + // wrapper functions avoiding MPI library extern void barrier(); extern uint64_t reduce_sum(uint64_t local); @@ -121,6 +131,7 @@ namespace vmpi{ extern void collate(std::vector& input, std::vector& output); extern void counts_and_displacements(std::vector& input, std::vector& output, std::vector& counts, std::vector& displacements); extern void fast_collate(std::vector& input, std::vector& output, std::vector& counts, std::vector& displacements); + extern void broadcast(std::vector& message, int source_rank); // function to seed random numbers in parallel uint32_t parallel_rng_seed(int seed); diff --git a/hdr/voronoi.hpp b/hdr/voronoi.hpp index 2fa95effc..3eaed2059 100644 --- a/hdr/voronoi.hpp +++ b/hdr/voronoi.hpp @@ -33,6 +33,11 @@ namespace create_voronoi{ extern double voronoi_sd; /// Standard Deviation of voronoi grains extern double area_cutoff; extern bool include_boundary_grains_real; + + extern bool bimodal_grains; // enable bimodal seed generation + extern double small_grain_diameter; // mean diameter of small population (Angstroms) + extern double small_grain_size_variance; // <0 sentinel = "use voronoi_sd" + extern double small_grain_fraction; // target number fraction, 0..1 } #endif //CREATE_VORONOI_H_ diff --git a/hdr/vtypes.hpp b/hdr/vtypes.hpp new file mode 100644 index 000000000..259d895a8 --- /dev/null +++ b/hdr/vtypes.hpp @@ -0,0 +1,163 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2025. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// +#ifndef VTYPES_H_ +#define VTYPES_H_ +// C++ standard library headers +#include +#include + +// Vampire headers +#include "errors.hpp" +#include "vio.hpp" + + +namespace vtype{ + + //--------------------------------- + // struct for storing unit vectors + //--------------------------------- + class vec_t{ + + public: + double x; + double y; + double z; + + // constructors + vec_t():x(0.0), y(0.0), z(0.0){}; // default + vec_t(double xi):x(xi), y(xi), z(xi){}; // single value initialisation + vec_t(double xi, double yi, double zi):x(xi), y(yi), z(zi){}; // 3-value initialisation + vec_t(std::vector initial_values){ + if(initial_values.size() == 3){ + x = initial_values[0]; + y = initial_values[1]; + z = initial_values[2]; + } + else{ + std::cerr << "Error: attempting to initialise vtypes::vec_t with a std::vector not containing three values" << std::endl; + zlog << zTs() << "Error: attempting to initialise vtypes::vec_t with a std::vector not containing three values" << std::endl; + err::vexit(); + } + }; // std::vector initialisation + /*vec_t(std::initializer_list initial_values):{ + if(initial_values.size() == 3){ + x = initial_values[0]; + y = initial_values[1]; + z = initial_values[2]; + } + else{ + std::cerr << "Error: attempting to initialise vtypes::vec_t with an initializer list {} not containing three values" << std::endl; + zlog << zTs() << "Error: attempting to initialise vtypes::vec_t with an initializer list {} not containing three values" << std::endl; + err::vexit(); + } + }; // std::vector initialisation*/ + + // Copy constructor + vec_t(const vec_t& other) : x(other.x), y(other.y), z(other.z) {} + + // Copy assignment operator + vec_t& operator=(const vec_t& other) { + if (this != &other) { + x = other.x; + y = other.y; + z = other.z; + } + return *this; + } + + // Move constructor + vec_t(vec_t&& other) noexcept : x(other.x), y(other.y), z(other.z) { + other.x = 0.0; + other.y = 0.0; + other.z = 0.0; + } + + // Move assignment operator + vec_t& operator=(vec_t&& other) noexcept { + if (this != &other) { + x = other.x; + y = other.y; + z = other.z; + other.x = 0.0; + other.y = 0.0; + other.z = 0.0; + } + return *this; + } + + }; + + //--------------------------------------------------------------------------- + // simple initialised classes for set variables + //--------------------------------------------------------------------------- + // Allows its input status to be set or unset, + // allowing for initialising a paremeter to the default or user-set value + //--------------------------------------------------------------------------- + + //-------------------------- + // doubles + //-------------------------- + class set_double_t{ + + private: + double value; // value + bool setf; // flag specifiying variable has been set + + public: + // class functions + // constructor + set_double_t() : value(0.0), setf(false) { } + + // setting function + void set(double in_value){ + value = in_value; + setf = true; + }; + + // get value function + double get(){ return value; }; + // check if variable is set + bool is_set(){ return setf; }; + + }; + + //-------------------------- + // ints + //-------------------------- + class set_int_t{ + + private: + int value; // value + bool setf; // flag specifiying variable has been set + + public: + // class functions + // constructor + set_int_t() : value(0), setf(false) { } + + // setting function + void set(int in_value){ + value = in_value; + setf = true; + }; + + // get value function + int get(){ return value; }; + + // check if variable is set + bool is_set(){ return setf; }; + + }; + +} + +#endif /*VTYPES_H_*/ diff --git a/makefile b/makefile index 718a0026f..2347668ab 100644 --- a/makefile +++ b/makefile @@ -4,20 +4,33 @@ # #=================================================================== +#---------------------------------------------------------------------- +# Optional libraries +#---------------------------------------------------------------------- +# Defaults are no extras (for easy compilation) +LIBS= +FFTW= + +# Uncomment these to add FFTW for spin waves and FFT dipole +#LIBS= -lm -lfftw3 -L/opt/local/lib/ +#FFTW= -DFFT -I/opt/local/include/ + +# Add the CUDA libraries +CUDALIBS=-L/usr/local/cuda/lib64/ -lcuda -lcudart + +#---------------------------------------------------------------------- +# Compilers +#---------------------------------------------------------------------- + # Specify compiler for MPI compilation with openmpi export OMPI_CXX=g++ -std=c++11 - #export OMPI_CXX=icc #export OMPI_CXX=pathCC + # Specify compiler for MPI compilation with mpich #export MPICH_CXX=g++ #export MPICH_CXX=bgxlc++ -# Include the FFTW library by uncommenting the -DFFT (off by default) -#export incFFT= -DFFT -DFFTW_OMP -fopenmp -#export FFTLIBS= -lfftw3_omp -lfftw3 - -# Compilers ICC=icc -std=c++11 -DCOMP='"Intel C++ Compiler"' GCC=g++ -std=c++11 -DCOMP='"GNU C++ Compiler"' LLVM=g++ -std=c++11 -DCOMP='"LLVM C++ Compiler"' @@ -26,33 +39,24 @@ IBM=bgxlc++ -DCOMP='"IBM XLC++ Compiler"' MPICC=mpicxx -DMPICF MPIICC=mpiicpc -DMPICF -LIBS= -lstdc++ -#-lm $(FFTLIBS) -L/opt/local/lib/ - -CCC_CFLAGS=-I./hdr -I./src/qvoronoi -O0 -CCC_LDFLAGS=-I./hdr -I./src/qvoronoi -O0 - export LANG=C export LC_ALL=C -# LIBS - -CUDALIBS=-L/usr/local/cuda/lib64/ -lcuda -lcudart - # Debug Flags ICC_DBCFLAGS= -O0 -C -I./hdr -I./src/qvoronoi ICC_DBLFLAGS= -C -I./hdr -I./src/qvoronoi -GCC_DBCFLAGS= -g -pg -fprofile-arcs -ftest-coverage -Wall -Wextra -O0 -fbounds-check -pedantic -std=c++0x -Wno-long-long -I./hdr -I./src/qvoronoi -Wsign-compare -GCC_DBLFLAGS= -g -pg -fprofile-arcs -ftest-coverage -lstdc++ -std=c++0x -fbounds-check -I./hdr -I./src/qvoronoi -Wsign-compare +GCC_DBCFLAGS= -g -pg -fprofile-arcs -ftest-coverage -Wall -Wextra -O0 -fbounds-check -pedantic -std=c++0x -Wno-long-long -I./hdr -I./src/qvoronoi $(FFTW) -Wsign-compare +GCC_DBLFLAGS= -g -pg -fprofile-arcs -ftest-coverage -lstdc++ -std=c++0x -fbounds-check -I./hdr -I./src/qvoronoi $(FFTW) -Wsign-compare PCC_DBCFLAGS= -O0 -I./hdr -I./src/qvoronoi PCC_DBLFLAGS= -O0 -I./hdr -I./src/qvoronoi + IBM_DBCFLAGS= -O0 -Wall -pedantic -Wextra -I./hdr -I./src/qvoronoi IBM_DBLFLAGS= -O0 -Wall -pedantic -Wextra -I./hdr -I./src/qvoronoi -LLVM_DBCFLAGS= -Wall -Wextra -O0 -pedantic -std=c++11 -Wno-long-long -I./hdr -I./src/qvoronoi -Wsign-compare -LLVM_DBLFLAGS= -Wall -Wextra -O0 -lstdc++ -I./hdr -I./src/qvoronoi -Wsign-compare +LLVM_DBCFLAGS= -Wall -Wextra -O0 -pedantic -std=c++11 -Wno-long-long -I./hdr -I./src/qvoronoi $(FFTW) -Wsign-compare +LLVM_DBLFLAGS= -Wall -Wextra -O0 -lstdc++ -I./hdr -I./src/qvoronoi $(FFTW) -Wsign-compare # Performance Flags ICC_CFLAGS= -O3 -axCORE-AVX2 -fno-alias -align -falign-functions -I./hdr -I./src/qvoronoi @@ -60,22 +64,24 @@ ICC_LDFLAGS= -I./hdr -I./src/qvoronoi -axCORE-AVX2 #ICC_CFLAGS= -O3 -xT -ipo -static -fno-alias -align -falign-functions -vec-report -I./hdr #ICC_LDFLAGS= -lstdc++ -ipo -I./hdr -xT -vec-report -LLVM_CFLAGS= -Wall -pedantic -O3 -mtune=native -funroll-loops -I./hdr -I./src/qvoronoi -LLVM_LDFLAGS= -lstdc++ -I./hdr -I./src/qvoronoi +LLVM_CFLAGS= -Wall -pedantic -O3 -mtune=native -funroll-loops -I./hdr -I./src/qvoronoi $(FFTW) +LLVM_LDFLAGS= -I./hdr -I./src/qvoronoi $(FFTW) -GCC_CFLAGS=-O3 -mtune=native -funroll-all-loops -fexpensive-optimizations -funroll-loops -I./hdr -I./src/qvoronoi -std=c++11 -Wsign-compare -GCC_LDFLAGS= -lstdc++ -I./hdr -I./src/qvoronoi -Wsign-compare +GCC_CFLAGS=-O3 -mtune=native -funroll-all-loops -fexpensive-optimizations -funroll-loops -I./hdr -I./src/qvoronoi $(FFTW) -std=c++11 -Wsign-compare +GCC_LDFLAGS= -lstdc++ -I./hdr -I./src/qvoronoi $(FFTW) -Wsign-compare PCC_CFLAGS=-O2 -march=barcelona -ipa -I./hdr -I./src/qvoronoi PCC_LDFLAGS= -I./hdr -I./src/qvoronoi -O2 -march=barcelona -ipa - IBM_CFLAGS=-O5 -qarch=450 -qtune=450 -I./hdr -I./src/qvoronoi IBM_LDFLAGS= -lstdc++ -I./hdr -I./src/qvoronoi -O5 -qarch=450 -qtune=450 CRAY_CFLAGS= -O3 -hfp3 -I./hdr -I./src/qvoronoi CRAY_LDFLAGS= -I./hdr -I./src/qvoronoi +CCC_CFLAGS=-I./hdr -I./src/qvoronoi -O0 +CCC_LDFLAGS=-I./hdr -I./src/qvoronoi -O0 + # Save git commit in simple function GHASH:=$(shell git rev-parse HEAD) @@ -110,24 +116,7 @@ obj/utility/errors.o \ obj/utility/statistics.o \ obj/utility/units.o \ obj/utility/vmath.o\ -obj/qvoronoi/geom.o\ -obj/qvoronoi/geom2.o\ -obj/qvoronoi/global.o\ -obj/qvoronoi/io.o\ -obj/qvoronoi/libqhull.o\ -obj/qvoronoi/mem.o\ -obj/qvoronoi/merge.o\ -obj/qvoronoi/poly.o\ -obj/qvoronoi/poly2.o\ -obj/qvoronoi/qhrandom.o\ -obj/qvoronoi/qset.o\ -obj/qvoronoi/qvoronoi.o\ -obj/qvoronoi/rboxlib.o\ -obj/qvoronoi/stat.o\ -obj/qvoronoi/user.o\ -obj/qvoronoi/usermem.o\ -obj/qvoronoi/userprintf.o\ -obj/qvoronoi/userprintf_rbox.o\ + # Include supplementary makefiles include src/anisotropy/makefile @@ -136,6 +125,7 @@ include src/create/makefile include src/config/makefile include src/constants/makefile include src/dipole/makefile +include src/environment/makefile include src/exchange/makefile include src/gpu/makefile include src/hamr/makefile @@ -147,12 +137,16 @@ include src/micromagnetic/makefile include src/mpi/makefile include src/neighbours/makefile include src/program/makefile +include src/qvoronoi/makefile include src/simulate/makefile +include src/spininitialize/makefile +include src/spinlattice/makefile +include src/spintextures/makefile include src/spintransport/makefile +include src/spinwaves/makefile include src/statistics/makefile include src/unitcell/makefile include src/vio/makefile -include src/environment/makefile # Cuda must be last for some odd reason include src/cuda/makefile @@ -167,18 +161,18 @@ PCCDB_OBJECTS=$(OBJECTS:.o=_pdb.o) IBMDB_OBJECTS=$(OBJECTS:.o=_ibmdb.o) LLVMDB_OBJECTS=$(OBJECTS:.o=_llvmdb.o) -MPI_OBJECTS=$(OBJECTS:.o=_mpi.o) -MPI_ICC_OBJECTS=$(OBJECTS:.o=_i_mpi.o) -MPI_LLVM_OBJECTS=$(OBJECTS:.o=_llvm_mpi.o) -MPI_PCC_OBJECTS=$(OBJECTS:.o=_p_mpi.o) -MPI_IBM_OBJECTS=$(OBJECTS:.o=_ibm_mpi.o) -MPI_CRAY_OBJECTS=$(OBJECTS:.o=_cray_mpi.o) -MPI_ICCDB_OBJECTS=$(OBJECTS:.o=_idb_mpi.o) -MPI_GCCDB_OBJECTS=$(OBJECTS:.o=_gdb_mpi.o) -MPI_PCCDB_OBJECTS=$(OBJECTS:.o=_pdb_mpi.o) -MPI_IBMDB_OBJECTS=$(OBJECTS:.o=_ibmdb_mpi.o) -MPI_CRAYDB_OBJECTS=$(OBJECTS:.o=_craydb_mpi.o) -MPI_ARCHER_OBJECTS=$(OBJECTS:.o=_archer_mpi.o) +MPI_OBJECTS=$(OBJECTS:.o=_par.o) +MPI_ICC_OBJECTS=$(OBJECTS:.o=_i_par.o) +MPI_LLVM_OBJECTS=$(OBJECTS:.o=_llvm_par.o) +MPI_PCC_OBJECTS=$(OBJECTS:.o=_p_par.o) +MPI_IBM_OBJECTS=$(OBJECTS:.o=_ibm_par.o) +MPI_CRAY_OBJECTS=$(OBJECTS:.o=_cray_par.o) +MPI_ICCDB_OBJECTS=$(OBJECTS:.o=_idb_par.o) +MPI_GCCDB_OBJECTS=$(OBJECTS:.o=_gdb_par.o) +MPI_PCCDB_OBJECTS=$(OBJECTS:.o=_pdb_par.o) +MPI_IBMDB_OBJECTS=$(OBJECTS:.o=_ibmdb_par.o) +MPI_CRAYDB_OBJECTS=$(OBJECTS:.o=_craydb_par.o) +MPI_ARCHER_OBJECTS=$(OBJECTS:.o=_archer_par.o) CLEXECUTABLE=vampire-opencl CUDAEXECUTABLE=vampire-cuda @@ -191,19 +185,25 @@ PEXECUTABLE=vampire-parallel # make serial and parallel versions and utilities all: serial parallel vdc +#------------------------------------------------------------------------------- # Serial Targets +#------------------------------------------------------------------------------- + +# serial - use for linux systems (default) serial: $(OBJECTS) $(GCC) $(GCC_LDFLAGS) $(OBJECTS) $(LIBS) -o $(EXECUTABLE) $(OBJECTS): obj/%.o: src/%.cpp $(GCC) -c -o $@ $(GCC_CFLAGS) $(OPTIONS) $< +# serial - use for linux systems with the intel compiler serial-intel: $(ICC_OBJECTS) $(ICC) $(ICC_LDFLAGS) $(LIBS) $(ICC_OBJECTS) -o $(EXECUTABLE)-intel $(ICC_OBJECTS): obj/%_i.o: src/%.cpp $(ICC) -c -o $@ $(ICC_CFLAGS) $(OPTIONS) $< +# serial - use for macOS systems serial-llvm: $(LLVM_OBJECTS) $(LLVM) $(LLVM_LDFLAGS) $(LIBS) $(LLVM_OBJECTS) -o $(EXECUTABLE) @@ -246,72 +246,76 @@ $(PCCDB_OBJECTS): obj/%_pdb.o: src/%.cpp #$(IBMDB_OBJECTS): obj/%_pdb.o: src/%.cpp # $(PCC) -c -o $@ $(PCC_DBCFLAGS) $< +#------------------------------------------------------------------------------- # MPI Targets +#------------------------------------------------------------------------------- +# parallel - use for linux systems (default) parallel: $(MPI_OBJECTS) $(MPICC) $(GCC_LDFLAGS) $(MPI_OBJECTS) $(LIBS) -o $(PEXECUTABLE) -$(MPI_OBJECTS): obj/%_mpi.o: src/%.cpp +$(MPI_OBJECTS): obj/%_par.o: src/%.cpp $(MPICC) -c -o $@ $(GCC_CFLAGS) $(OPTIONS) $< parallel-intel: $(MPI_ICC_OBJECTS) $(MPIICC) $(ICC_LDFLAGS) $(LIBS) $(MPI_ICC_OBJECTS) -o $(PEXECUTABLE)-intel -$(MPI_ICC_OBJECTS): obj/%_i_mpi.o: src/%.cpp - $(MPIICC) -c -o $@ $(ICC_CFLAGS) $< +$(MPI_ICC_OBJECTS): obj/%_i_par.o: src/%.cpp + $(MPIICC) -c -o $@ $(ICC_CFLAGS) $(OPTIONS) $< parallel-cray: $(MPI_CRAY_OBJECTS) $(MPICC) $(CRAY_LDFLAGS) $(LIBS) $(MPI_CRAY_OBJECTS) -o $(PEXECUTABLE) -$(MPI_CRAY_OBJECTS): obj/%_cray_mpi.o: src/%.cpp +$(MPI_CRAY_OBJECTS): obj/%_cray_par.o: src/%.cpp $(MPICC) -c -o $@ $(CRAY_CFLAGS) $< parallel-archer: $(MPI_ARCHER_OBJECTS) CC -DMPICF $(GCC_LDFLAGS) $(LIBS) $(MPI_ARCHER_OBJECTS) -o $(PEXECUTABLE) -$(MPI_ARCHER_OBJECTS): obj/%_archer_mpi.o: src/%.cpp - CC -DMPICF -c -o $@ $(GCC_CFLAGS) $< +$(MPI_ARCHER_OBJECTS): obj/%_archer_par.o: src/%.cpp + CC -DMPICF -c -o $@ $(GCC_CFLAGS) $(OPTIONS) $< +# parallel - use for macOS systems parallel-llvm: $(MPI_LLVM_OBJECTS) $(MPICC) $(LLVM_LDFLAGS) $(LIBS) $(MPI_LLVM_OBJECTS) -o $(PEXECUTABLE) -$(MPI_LLVM_OBJECTS): obj/%_llvm_mpi.o: src/%.cpp +$(MPI_LLVM_OBJECTS): obj/%_llvm_par.o: src/%.cpp $(MPICC) -c -o $@ $(LLVM_CFLAGS) $(OPTIONS) $< parallel-pathscale: $(MPI_PCC_OBJECTS) $(MPICC) $(PCC_LDFLAGS) $(LIBS) $(MPI_PCC_OBJECTS) -o $(PEXECUTABLE) -$(MPI_PCC_OBJECTS): obj/%_p_mpi.o: src/%.cpp +$(MPI_PCC_OBJECTS): obj/%_p_par.o: src/%.cpp $(MPICC) -c -o $@ $(PCC_CFLAGS) $(OPTIONS) $< parallel-ibm: $(MPI_IBM_OBJECTS) $(MPICC) $(IBM_LDFLAGS) $(MPI_IBM_OBJECTS) -o $(PEXECUTABLE) -$(MPI_IBM_OBJECTS): obj/%_ibm_mpi.o: src/%.cpp +$(MPI_IBM_OBJECTS): obj/%_ibm_par.o: src/%.cpp $(MPICC) -c -o $@ $(IBM_CFLAGS) $(OPTIONS) $< parallel-debug: $(MPI_GCCDB_OBJECTS) $(MPICC) $(GCC_DBLFLAGS) $(LIBS) $(MPI_GCCDB_OBJECTS) -o $(PEXECUTABLE)-debug -$(MPI_GCCDB_OBJECTS): obj/%_gdb_mpi.o: src/%.cpp +$(MPI_GCCDB_OBJECTS): obj/%_gdb_par.o: src/%.cpp $(MPICC) -c -o $@ $(GCC_DBCFLAGS) $(OPTIONS) $< parallel-intel-debug: $(MPI_ICCDB_OBJECTS) $(MPIICC) $(ICC_DBLFLAGS) $(LIBS) $(MPI_ICCDB_OBJECTS) -o $(PEXECUTABLE)-intel-debug -$(MPI_ICCDB_OBJECTS): obj/%_idb_mpi.o: src/%.cpp +$(MPI_ICCDB_OBJECTS): obj/%_idb_par.o: src/%.cpp $(MPIICC) -c -o $@ $(ICC_DBCFLAGS) $(OPTIONS) $< parallel-cray-debug: $(MPI_CRAY_OBJECTS) $(MPICC) $(CCC_LDFLAGS) $(LIBS) $(MPI_CRAYDB_OBJECTS) -o $(PEXECUTABLE) -$(MPI_CRAYDB_OBJECTS): obj/%_craydb_mpi.o: src/%.cpp +$(MPI_CRAYDB_OBJECTS): obj/%_craydb_par.o: src/%.cpp $(MPICC) -c -o $@ $(CCC_CFLAGS) $(OPTIONS) $< parallel-pathscale-debug: $(MPI_PCCDB_OBJECTS) $(MPICC) $(PCC_DBLFLAGS) $(LIBS) $(MPI_PCCDB_OBJECTS) -o $(PEXECUTABLE) -$(MPI_PCCDB_OBJECTS): obj/%_pdb_mpi.o: src/%.cpp +$(MPI_PCCDB_OBJECTS): obj/%_pdb_par.o: src/%.cpp $(MPICC) -c -o $@ $(PCC_DBCFLAGS) $(OPTIONS) $< clean: diff --git a/manual/3-GettingStarted/Chapter.tex b/manual/3-GettingStarted/Chapter.tex index ee77bd839..66efe42ec 100755 --- a/manual/3-GettingStarted/Chapter.tex +++ b/manual/3-GettingStarted/Chapter.tex @@ -17,9 +17,26 @@ \subsection*{Simulations} \section*{Input and Output Files} \phantomsection\addcontentsline{toc}{section}{Input and Output Files} -\vampire requires at least two files to run a simulation, the \textit{input} file and the \textit{material} file. The \textit{input} file defines all the properties of the simulated system, such as the dimensions or particle shape, as well as the simulation parameters and program output. The \textit{material} file defines the properties of all the materials used in the simulation, and is usually given the \textit{.mat} file extension. A sample material file \textit{Co.mat} is included with the code which defines a minimum set of parameters for Co. +\vampire requires at least two files to run a simulation, the \textit{input} file and the \textit{material} file. The \textit{input} file defines all the properties of the simulated system, such as the dimensions or particle shape, as well as the simulation parameters and program output. The \textit{material} file defines the properties of all the materials used in the simulation, and is usually given the \textit{.mat} file extension. A sample material file \textit{Co.mat} is included with the code which defines a minimum set of parameters for Co. The \textit{input} file can be specified with the \verb|--input-file| command line option, and the \textit{material} file is specified in the input file. + + +The output of the code includes a main \textit{output} file, which records data such as the magnetisation, timesteps, temperature etc. The format of the \textit{output} file is fully customisable, so that the amount of output data is limited to what is useful. The \textit{output} file can be specified with the \verb|--output-file| command line option. In addition to the output file, the other main available outputs are spin configuration files, which with post-processing allow output of snapshots of the magnetic configurations during the simulation. + + +To change the name of the \textit{input} and \textit{output} files, a command line parameter can be used. For example, to set the \textit{input} file for a serial simulation, or the \textit{output} file for a parallel simulation: + +\noindent +\begin{minipage}[c]{\textwidth} +\centering + \begin{minipage}[c]{0.8\textwidth} + \raggedright +\verb|vampire-serial --input-file [input_filename]| +\verb|vampire-parallel --output-file [output_filename]| + \end{minipage} +\end{minipage} + +If unspecified, the default \textit{input} file name is "input", and the default \textit{output} filename is "output". -The output of the code includes a main \textit{output} file, which records data such as the magnetisation, timesteps, temperature etc. The format of the \textit{output} file is fully customisable, so that the amount of output data is limited to what is useful. In addition to the output file, the other main available outputs are spin configuration files, which with post-processing allow output of snapshots of the magnetic configurations during the simulation. \section*{Sample input files} \phantomsection\addcontentsline{toc}{section}{Sample input files} diff --git a/manual/5-Visualization/Chapter.tex b/manual/5-Visualization/Chapter.tex index 094cad6de..457630a33 100644 --- a/manual/5-Visualization/Chapter.tex +++ b/manual/5-Visualization/Chapter.tex @@ -1,6 +1,8 @@ \chapter{Visualization}\label{chap:visualization} -\vampire provides tools for visualising systems using external programs such as Rasmol, Jmol and POV-Ray. To compile these utilities, use the following command in the main directory of your \vampire installation folder: +\vampire provides tools for visualising systems using external programs such as +Rasmol, Jmol and POV-Ray. To compile these utilities, use the following command +in the main directory of your \vampire installation folder: \noindent \begin{minipage}[c]{\textwidth} @@ -8,7 +10,9 @@ \chapter{Visualization}\label{chap:visualization} \textit{make vdc} \end{minipage} -The \vampire data converter, or \vdc, is run to produce the input files needed. To generate the input files, an output type must be specified in the command line. For example, to produce POV-Ray input files: +The \vampire data converter, or \vdc, is run to produce the input files needed. +To generate the input files, an output type must be specified in the command line. +For example, to produce POV-Ray input files: \noindent \begin{minipage}[c]{\textwidth} @@ -21,9 +25,16 @@ \chapter{Visualization}\label{chap:visualization} \section*{Getting started} \phantomsection\addcontentsline{toc}{section}{Getting started} -To generate the positions of your atoms, \textit{config:atoms} must be set in the input file. This will produce .data and .meta files containing the atomic and spin configuration of your system. Their frequency can be adjusted using the \textit{config:atoms-output-rate} paramter or they can be set to be output at the end of the simulation solely. +To generate the positions of your atoms, \textit{config:atoms} must be set in +the input file. This will produce .data and .meta files containing the atomic +and spin configuration of your system. Their frequency can be adjusted using the +\textit{config:atoms-output-rate} paramter or they can be set to be output at +the end of the simulation solely. -In addition, the format of the output can be text or binary format, the latter can help with particulary large systems. Files written in binary format are system specific and usually cannot be read by \vdc compiled on separate hardware. +In addition, the format of the output can be text or binary format, the latter +can help with particulary large systems. Files written in binary format are +system specific and usually cannot be read by \vdc compiled on separate +hardware. \subsection*{input} {\footnotesize @@ -41,9 +52,13 @@ \subsection*{input} \section*{Atomic visualization with rasmol} \phantomsection\addcontentsline{toc}{section}{Atomic visualization with rasmol} -To visualise your system using Rasmol, simply run \vdc in the same directory as your output with \textit{vdc -{}-xyz}. The \textit{config:atoms} files must be present. +To visualise your system using Rasmol, simply run \vdc in the same directory as +your output with \textit{vdc -{}-xyz}. The \textit{config:atoms} files must be +present. -This produced a file called \textit{crystal.xyz}, which is a chemical file format with information on the atomic positions. The format of the \textit{.xyz} format is as follows: +This produced a file called \textit{crystal.xyz}, which is a chemical file +format with information on the atomic positions. The format of the \textit{.xyz} +format is as follows: \subsection*{.xyz} {\footnotesize @@ -55,12 +70,17 @@ \subsection*{.xyz} \end{verbatim} } -The element in the .xyz file does not necessarily need to be the same as the atoms used in your system. They can instead be chosen for a different colour palette depending on the users requirements. +The element in the .xyz file does not necessarily need to be the same as the +atoms used in your system. They can instead be chosen for a different colour +palette depending on the users requirements. \section*{Atomic visualization with POV-Ray} \phantomsection\addcontentsline{toc}{section}{Atomic visualization with POV-Ray} -To produce pictures of your material of punishable quality and high configurability, it is also possible to use POV-Ray. After running \vdc, the file "spins.pov" contains all the necessary information and an image may be produced by using: +To produce pictures of your material of punishable quality and high +configurability, it is also possible to use POV-Ray. After running \vdc, the +file "spins.pov" contains all the necessary information and an image may be +produced by using: \noindent \begin{minipage}[c]{\textwidth} @@ -68,7 +88,8 @@ \section*{Atomic visualization with POV-Ray} \textit{povray spins.pov} \end{minipage} -When running povray it is also possible to select specific snapshots or ranges to render using the following flags: +When running povray it is also possible to select specific snapshots or ranges +to render using the following flags: \noindent \begin{minipage}[c]{\textwidth} @@ -84,9 +105,18 @@ \section*{Atomic visualization with POV-Ray} \textit{povray -W3600 -H2700 +A0.3 +KFI9 +KFF9 spins.pov} \end{minipage} -\noindent where the "-W" and "-H" flags define the width and heigh of the image (the resolution), and "+A" is used for antialiasing. +\noindent where the "-W" and "-H" flags define the width and heigh of the image +(the resolution), and "+A" is used for antialiasing. -Output from \vdc can be customised in several ways, either by passing parameters to the command line (using the '\textit{--}' notation), or by using a separate \vdc input file. The \vdc input file is a plain text file containing parameters and arguments which change the output behaviour. This should be helpful when many parameters, or multiple vdc runs with the same parameters, are needed. Comments can be included with the '\#' symbol and the special characters ' ,()\{\}[]:=!' can also be used but are ignored by \vdc. By default this file is called \textit{vdc-input}, and is read automatically. To change the name of the \vdc input file, a command line parameter can be used: +Output from \vdc can be customised in several ways, either by passing parameters +to the command line (using the '\textit{--}' notation), or by using a separate +\vdc input file. The \vdc input file is a plain text file containing parameters +and arguments which change the output behaviour. This should be helpful when +many parameters, or multiple vdc runs with the same parameters, are needed. +Comments can be included with the '\#' symbol and the special characters +' ,()\{\}[]:=!' can also be used but are ignored by \vdc. By default this file +is called \textit{vdc-input}, and is read automatically. To change the name of +the \vdc input file, a command line parameter can be used: \noindent \begin{minipage}[c]{\textwidth} @@ -94,7 +124,11 @@ \section*{Atomic visualization with POV-Ray} \textit{vdc -{}-input-file [filename]} \end{minipage} -There are many options that can be used to change all visualisation outputs including Rasmol, Jmol and POV-Ray. To get help with the usage of these parameters outside of the manual, it is also possible to print help messages from the command line by using the \textit{-h} or \textit{-{}-help} command line argument, followed by the name of a \textit{vdc-input} file parameter: +There are many options that can be used to change all visualisation outputs +including Rasmol, Jmol and POV-Ray. To get help with the usage of these +parameters outside of the manual, it is also possible to print help messages +from the command line by using the \textit{-h} or \textit{-{}-help} command line +argument, followed by the name of a \textit{vdc-input} file parameter: \noindent \begin{minipage}[c]{\textwidth} @@ -102,43 +136,116 @@ \section*{Atomic visualization with POV-Ray} \textit{vdc -h [parameter-name]} \end{minipage} -The help message should contain information on the parameter, as well as the type of the associated value given, the default value and example usage of the parameter. +The help message should contain information on the parameter, as well as the +type of the associated value given, the default value and example usage of the +parameter. \section*{General customisation options} \phantomsection\addcontentsline{toc}{section}{General Customisation options} -It may be beneficial to use only smaller portions of your full system when generating POV-Ray or Rasmol images. This can help with large systems where rendering can become a time constraint, or systems made up of several elements which might be less relevant for the visualisation. There are several similar command line options which can be used to cut up the system in different ways: - -{\zicf slice = float vector(6) [0-1 : default \{0,1,0,1,0,1\}]}\phantomsection\addcontentsline{toc}{subsection}{slice} The first slice type defines minimum and maximum values for each axis. Only atoms and spins inside these boundaries are included in the visualisation. The parameters passed to this argument are interpreted as fractional coordinates. - -{\zicf slice-void = float vector(6) [0-1 : default \textit{not set}]}\phantomsection\addcontentsline{toc}{subsection}{slice-void} This parameter will \textit{remove} all atoms and spins inside the given borders. This can be used to create cubic hollow systems where only surface atoms are shown, removing a very high percentage of atoms in the system, which can greatly reduce rendering time for both POV-Ray and Rasmol. - -{\zicf slice-sphere = float vector(3) [0-1 : \textit{xfrac},\textit{yfrac},\textit{zfrac}]}\phantomsection\addcontentsline{toc}{subsection}{slice-sphere} The sphere slice is also used to remove the atoms and spins at the centre of a system. This particular parameter lends itself well to spherical systems as it removes a spherical section of atoms. Three parameters are required, instead of six. Each one defines a region, centred on the centre of the original system, along the respective axis, equal to a fraction of the system size along that axis. As these parameters are not necessarily equal to each other, this can be used to create an ellipse of missing atoms at the centre of the system. - -{\zicf slice-cylinder = float vector(4) [0-1 : \textit{xfrac},\textit{yfrac},\textit{zmin},\textit{zmax}]}\phantomsection\addcontentsline{toc}{subsection}{slice-cylinder} This slice parameter can be used to remove all atoms outside a cylindracal section by defining the x,y-fractional sizes as well as a fractional minimum and maximum along the z-axis. - -{\zicf remove-materials = int [one or more values] }\phantomsection\addcontentsline{toc}{subsection}{remove-materials} In some cases whole materials are not relevant for visualisation purposes and can be altogether removed. To use this command line parameter a list of material indices need to be provided. Material indices start from 1. +It may be beneficial to use only smaller portions of your full system when +generating POV-Ray or Rasmol images. This can help with large systems where +rendering can become a time constraint, or systems made up of several elements +which might be less relevant for the visualisation. There are several similar +command line options which can be used to cut up the system in different ways: + +{\zicf slice = float vector(6) [0-1 : default \{0,1,0,1,0,1\}]} +\phantomsection\addcontentsline{toc}{subsection}{slice} The first slice type +defines minimum and maximum values for each axis. Only atoms and spins inside +these boundaries are included in the visualisation. The parameters passed to +this argument are interpreted as fractional coordinates. + +{\zicf slice-void = float vector(6) [0-1 : default \textit{not set}]} +\phantomsection\addcontentsline{toc}{subsection}{slice-void} This parameter will +\textit{remove} all atoms and spins inside the given borders. This can be used +to create cubic hollow systems where only surface atoms are shown, removing a +very high percentage of atoms in the system, which can greatly reduce rendering +time for both POV-Ray and Rasmol. + +{\zicf slice-sphere = float vector(3) [0-1 : \textit{xfrac},\textit{yfrac},\textit{zfrac}]} +\phantomsection\addcontentsline{toc}{subsection}{slice-sphere} The sphere slice +is also used to remove the atoms and spins at the centre of a system. This +particular parameter lends itself well to spherical systems as it removes a +spherical section of atoms. Three parameters are required, instead of six. Each +one defines a region, centred on the centre of the original system, along the +respective axis, equal to a fraction of the system size along that axis. As +these parameters are not necessarily equal to each other, this can be used to +create an ellipse of missing atoms at the centre of the system. + +{\zicf slice-cylinder = float vector(4) [0-1 : \textit{xfrac},\textit{yfrac},\textit{zmin},\textit{zmax}]} +\phantomsection\addcontentsline{toc}{subsection}{slice-cylinder} This slice +parameter can be used to remove all atoms outside a cylindracal section by +defining the x,y-fractional sizes as well as a fractional minimum and maximum +along the z-axis. + +{\zicf remove-materials = int [one or more values] } +\phantomsection\addcontentsline{toc}{subsection}{remove-materials} In some cases +whole materials are not relevant for visualisation purposes and can be +altogether removed. To use this command line parameter a list of material +indices need to be provided. Material indices start from 1. \section*{POV-Ray Customisation options} \phantomsection\addcontentsline{toc}{section}{POV-Ray Customisation options} -The following section contains a list of parameters that only affect POV-Ray output configurations. If another output type is requested, these parameters are ignored. - -{\zicf frame-start = int [default 0]}\phantomsection\addcontentsline{toc}{subsection}{frame-start} Depending on output options used in \vampire, multiple frames may be rendered by \vdc. \textit{frame-start} can be used to skip an initial number of frames. - -{\zicf frame-final = int [default 0]}\phantomsection\addcontentsline{toc}{subsection}{frame-final} Depending on output options used in \vampire, multiple frames may be rendered by \vdc. \textit{frame-final} can be used to skip later frames. - -{\zicf camera-position = float vector(3) [(-1,1) : default \textit{not set}]}\phantomsection\addcontentsline{toc}{subsection}{camera-position} POV-Ray camera position, set using fractional coordinates. Camera distance from look at point is calculated automatically however it can be changed by using \textit{camera-zoom}. - -{\zicf camera-look-at = float vector(3) [(-1,1) : default \textit{not set}]}\phantomsection\addcontentsline{toc}{subsection}{camera-look-at} POV-Ray camera look at position, set using fractional coordinates. The position is a location in the bounding box of the system, with centre (0,0,0). - -{\zicf camera-zoom = float vector(3) [0-$\infty$ : default \textit{not set}]}\phantomsection\addcontentsline{toc}{subsection}{camera-zoom} The default distance from the camera is automatically calculated according to the size of the system. This can be increased or reduced using \textit{camera-zoom} to multiply the default distance. Values less than 1.0 reduce the distance while values above 1.0 increase it. - -{\zicf background-colour = string [default Gray30]}\phantomsection\addcontentsline{toc}{subsection}{background-colour} POV-Ray includes various predefined colours such as \textit{White, Black, Gray}. Misspelled colour names will not be detected by vdc but will cause error in POV-Ray. - -{\zicf atom-sizes = float [one or more : default 1.2]}\phantomsection\addcontentsline{toc}{subsection}{atom-sizes} POV-Ray atom sizes. Atoms are represented by spheres with a defined radius. Individual materials can have different atoms sizes by including a list of floats, starting from material 1. - -{\zicf arrow-sizes = float [one or more : default 2.0]}\phantomsection\addcontentsline{toc}{subsection}{arrow-sizes} POV-Ray arrow sizes. Individual materials can have different arrow sizes by including a list of floats, starting from material 1. +The following section contains a list of parameters that only affect POV-Ray +output configurations. If another output type is requested, these parameters +are ignored. + +{\zicf frame-start = int [default 0]} +\phantomsection\addcontentsline{toc}{subsection}{frame-start} Depending on +output options used in \vampire, multiple frames may be rendered by \vdc. +\textit{frame-start} can be used to skip an initial number of frames. + +{\zicf frame-final = int [default 0]} +\phantomsection\addcontentsline{toc}{subsection}{frame-final} Depending on +output options used in \vampire, multiple frames may be rendered by \vdc. +\textit{frame-final} can be used to skip later frames. + +{\zicf camera-position = float vector(3) [(-1,1) : default \textit{not set}]} +\phantomsection\addcontentsline{toc}{subsection}{camera-position} POV-Ray camera +position, set using fractional coordinates. Camera distance from look at point +is calculated automatically however it can be changed by using +\textit{camera-zoom}. + +{\zicf camera-look-at = float vector(3) [(-1,1) : default \textit{not set}]} +\phantomsection\addcontentsline{toc}{subsection}{camera-look-at} POV-Ray camera +look at position, set using fractional coordinates. The position is a location +in the bounding box of the system, with centre (0,0,0). + +{\zicf camera-zoom = float vector(3) [0-$\infty$ : default \textit{not set}]} +\phantomsection\addcontentsline{toc}{subsection}{camera-zoom} The default +distance from the camera is automatically calculated according to the size of +the system. This can be increased or reduced using \textit{camera-zoom} to +multiply the default distance. Values less than 1.0 reduce the distance while +values above 1.0 increase it. + +{\zicf background-colour = string [default Gray30]} +\phantomsection\addcontentsline{toc}{subsection}{background-colour} POV-Ray +includes various predefined colours such as \textit{White, Black, Gray}. +Misspelled colour names will not be detected by vdc but will cause error +in POV-Ray. + +{\zicf atom-sizes = float [one or more : default 1.2]} +\phantomsection\addcontentsline{toc}{subsection}{atom-sizes} POV-Ray atom sizes. +Atoms are represented by spheres with a defined radius. Individual materials can +have different atoms sizes by including a list of floats, starting from +material 1. + +{\zicf arrow-sizes = float [one or more : default 2.0]} +\phantomsection\addcontentsline{toc}{subsection}{arrow-sizes} POV-Ray arrow +sizes. Individual materials can have different arrow sizes by including a list +of floats, starting from material 1. + +{\zicf povray-sticks = bool [true or false : default false]} +\phantomsection\addcontentsline{toc}{subsection}{arrow-sizes} Adds sticks +between nearby atoms for visualisation in POV-Ray to better visualise the +crystal structure. Note that the sticks algorithm is inefficient and only +suitable for a small number of atoms (a few thousand). + +{\zicf sticks-cutoff = float [one or more : default 2.0]} +\phantomsection\addcontentsline{toc}{subsection}{arrow-sizes} Defines the +cutoff range for sticks, and should be approximately the nearest +neighbour distance between atoms. \begin{figure*}[!h] \center @@ -147,7 +254,21 @@ \section*{POV-Ray Customisation options} \label{fig:CBWR_colourmap} \end{figure*} -{\zicf colourmap = string [default CBWR]}\phantomsection\addcontentsline{toc}{subsection}{colourmap} By default, a 1D colourmap is used. Aligned along the z-axis, spins in the \{0,0,1\} direction are red, while spins antiparallel to this \{0,0,-1\} are blue. Between these values, the colour transitions to white around the xy-plane. This corresponds to the \textit{CBWR} colourmap, a cyclic blue-white-red map, which lends itself well to 1D or 2D spin sytems where there are two principle spin directions, such as antiferromagnets and ferrimagnets. Some care must be taken to align the principle spin directions with the z-axis, as this is the axis along which colour is applied. This can also be changed using the \textit{vector-z} input parameter. There are several choices of possible colourmap configurations, the ones provided by default are made to be perceptually uniform and in some cases take account of colourblindness. Information on the colourmaps, the importance of perceptually uniform maps and how to adapt and use different maps can be found from \textit{"Peter Kovesi. Good Colour Maps: How to Design Them. 2015"}. +{\zicf colourmap = string [default CBWR]} +\phantomsection\addcontentsline{toc}{subsection}{colourmap} By default, a 1D +colourmap is used. Aligned along the z-axis, spins in the \{0,0,1\} direction +are red, while spins antiparallel to this \{0,0,-1\} are blue. Between these +values, the colour transitions to white around the xy-plane. This corresponds to +the \textit{CBWR} colourmap, a cyclic blue-white-red map, which lends itself +well to 1D or 2D spin sytems where there are two principle spin directions, such +as antiferromagnets and ferrimagnets. Some care must be taken to align the +principle spin directions with the z-axis, as this is the axis along which +colour is applied. This can also be changed using the \textit{vector-z} input +parameter. There are several choices of possible colourmap configurations, the +ones provided by default are made to be perceptually uniform and in some cases +take account of colourblindness. Information on the colourmaps, the importance +of perceptually uniform maps and how to adapt and use different maps can be +found from \textit{"Peter Kovesi. Good Colour Maps: How to Design Them. 2015"}. \begin{figure*}[!h] \center @@ -156,7 +277,12 @@ \section*{POV-Ray Customisation options} \label{fig:C2_colourmap} \end{figure*} -The \textit{C2} coloumap is also cyclic and useful for 3D magnetic systems such as vortex states. It has four principle directions of magenta, yellow, green and blue. As it is cyclic, there will be a smooth transition between colour at all angles, irrespective of what is chosen as the zero degree spin direction. Sytems which benefit from this colourmap may also use the \textit{3D} parameter which applies a brightness effect along the x-axis. +The \textit{C2} coloumap is also cyclic and useful for 3D magnetic systems such +as vortex states. It has four principle directions of magenta, yellow, green and +blue. As it is cyclic, there will be a smooth transition between colour at all +angles, irrespective of what is chosen as the zero degree spin direction. +Systems which benefit from this colourmap may also use the \textit{3D} parameter +which applies a brightness effect along the x-axis. \vspace{5pt} \begin{figure*}[!h] @@ -166,7 +292,12 @@ \section*{POV-Ray Customisation options} \label{fig:BWR_colourmap} \end{figure*} -The \textit{BWR} colourmap is very similar in properties to the CBWR map however it is not cyclic. This mean that spins along the positive z-axis will be red with a small positive y-component and blue with a small negative y-component. There will be an immediate flip from bright red to blue as this transition occurs. This can be used to emphasise the transition between spin directions. The transition point can be changed by using \textit{vector-z}. +The \textit{BWR} colourmap is very similar in properties to the CBWR map however +it is not cyclic. This mean that spins along the positive z-axis will be red +with a small positive y-component and blue with a small negative y-component. +There will be an immediate flip from bright red to blue as this transition +occurs. This can be used to emphasise the transition between spin directions. +The transition point can be changed by using \textit{vector-z}. \vspace{5pt} \begin{figure*}[!h] @@ -176,11 +307,23 @@ \section*{POV-Ray Customisation options} \label{fig:Rainbow_colourmap} \end{figure*} -The \textit{Rainbow} colourmap can be used in 2D systems where spins are aligned in many different directions such as high temperature simulations. While it is still designed to be somewhat perceptually uniform, this is very difficult to do with rainbow palettes hence its use typically loses detail when compared to other maps, however it is also one of the most vibrant. +The \textit{Rainbow} colourmap can be used in 2D systems where spins are aligned +in many different directions such as high temperature simulations. While it is +still designed to be somewhat perceptually uniform, this is very difficult to do +with rainbow palettes hence its use typically loses detail when compared to +other maps, however it is also one of the most vibrant. -{\zicf custom-colourmap = filename }\phantomsection\addcontentsline{toc}{subsection}{custom-colourmap} A user defined colourmap can also be used. To apply a different map, a file containing 256 colours in the RBG format must be provided in the same directory that \vdc is run. RGB values must be space separated, with no other information such as line numbers. The beginning of an example colourmap is shown below. +{\zicf custom-colourmap = filename } +\phantomsection\addcontentsline{toc}{subsection}{custom-colourmap} A user +defined colourmap can also be used. To apply a different map, a file containing +256 colours in the RBG format must be provided in the same directory that \vdc +is run. RGB values must be space separated, with no other information such as +line numbers. The beginning of an example colourmap is shown below. -Pregenerated perceptually uniform colourmaps of various forms, including those included in vampire by default, can be found in \textit{peterkovesi.com/projects/\newline colourmaps/index.html} under the Download secion. +Pregenerated perceptually uniform colourmaps of various forms, including those +included in vampire by default, can be found in +\textit{peterkovesi.com/projects/\newline colourmaps/index.html} under the +Download secion. \subsection*{custom\_colourmap\_file} {\footnotesize @@ -199,27 +342,52 @@ \subsection*{custom\_colourmap\_file} \end{verbatim} } -{\zicf 3D = bool [default false]}\phantomsection\addcontentsline{toc}{subsection}{3D} POV-Ray images produced by \vdc can have a 3D brightening effect applied. Spins which do not line only in the yz-plane have their brightness adjusted according to their x-axis spin component. - -{\zicf vector-z = float vector(3) [default \{0,0,1\}]}\phantomsection\addcontentsline{toc}{subsection}{vector-z} The principle axis, along which colour is applied, is the z-axis. This determines where colours will occur depending on the colourmap being used. By default the \textit{CBWR} map is used; spins along the positive z-direction are red, those along the negative z-direction are blue, and spins aligned along the xy-plane are white. - -In many cases, the overall magnetic moment does not necessarily lie along the z-axis. To remedy this, a new \textit{vector-z} may be defined. To redefine the z-axis, use the parameter \textit{vector-z} followed by a direction vector. This does not need to be normalised. - -For example, if the user defines \textit{vector-z} = \{1,1,1\}, spins along the \{1,1,1\} direction will be red, \{-1,-1,-1\} will be blue and those perpendicular to the given axis will be white. Brackets can be omitted. - -{\zicf vector-x = float vector(3) [default \{0,0,1\}]}\phantomsection\addcontentsline{toc}{subsection}{vector-x} In some cases, the colourmap may not be symmetric along the default xy-plane, such as the \textit{C2} colourmap. Here, spins along positive-y are magenta, while those antiparallel are green. This can be adjusted using a similar command line argument \textit{vector-x}, however this argument cannot be used without first defining \textit{vector-z}. - -{\zicf afm = int [one or more values] }\phantomsection\addcontentsline{toc}{subsection}{afm} POV-Ray visualization of antiferromagnets can be difficult due to the contrast of colours of antiparallel spins. To remedy this, it is possible to define materials as antiferromagnetic. These materials will have their colours flipped so that they match neighbouring spins while their spin direction remains antiferromagnetic. +{\zicf 3D = bool [default false]} +\phantomsection\addcontentsline{toc}{subsection}{3D} POV-Ray images produced by +\vdc can have a 3D brightening effect applied. Spins which do not line only in +the yz-plane have their brightness adjusted according to their x-axis spin +component. + +{\zicf vector-z = float vector(3) [default \{0,0,1\}]} +\phantomsection\addcontentsline{toc}{subsection}{vector-z} The principle axis, +along which colour is applied, is the z-axis. This determines where colours will +occur depending on the colourmap being used. By default the \textit{CBWR} map is +used; spins along the positive z-direction are red, those along the negative +z-direction are blue, and spins aligned along the xy-plane are white. + +In many cases, the overall magnetic moment does not necessarily lie along the +z-axis. To remedy this, a new \textit{vector-z} may be defined. To redefine the +z-axis, use the parameter \textit{vector-z} followed by a direction vector. This +does not need to be normalised. + +For example, if the user defines \textit{vector-z} = \{1,1,1\}, spins along the +\{1,1,1\} direction will be red, \{-1,-1,-1\} will be blue and those +perpendicular to the given axis will be white. Brackets can be omitted. + +{\zicf vector-x = float vector(3) [default \{0,0,1\}]} +\phantomsection\addcontentsline{toc}{subsection}{vector-x} In some cases, the +colourmap may not be symmetric along the default xy-plane, such as the +\textit{C2} colourmap. Here, spins along positive-y are magenta, while those +antiparallel are green. This can be adjusted using a similar command line +argument \textit{vector-x}, however this argument cannot be used without first +defining \textit{vector-z}. + +{\zicf afm = int [one or more values] } +\phantomsection\addcontentsline{toc}{subsection}{afm} POV-Ray visualization of +antiferromagnets can be difficult due to the contrast of colours of antiparallel +spins. To remedy this, it is possible to define materials as antiferromagnetic. +These materials will have their colours flipped so that they match neighbouring +spins while their spin direction remains antiferromagnetic. \section*{Micromagnetic visualization with POV-Ray} \phantomsection\addcontentsline{toc}{section}{Micromagnetic visualization with POV-Ray} cell2povray -macros +%macros -customization +%customization -colouring options +%colouring options \section*{Visualization Movies} \phantomsection\addcontentsline{toc}{section}{Visualization Movies} diff --git a/manual/InputFileCommandReference/Chapter.tex b/manual/InputFileCommandReference/Chapter.tex index d834285c1..b4bdbdcd0 100755 --- a/manual/InputFileCommandReference/Chapter.tex +++ b/manual/InputFileCommandReference/Chapter.tex @@ -5,17 +5,35 @@ \section*{System Generation} \phantomsection\addcontentsline{toc}{section}{System Generation} The following commands control generation of the simulated system, including dimensions, crystal structures etc. -{\zicf create:full}\phantomsection\addcontentsline{toc}{subsection}{create:full} Uses the entire generated system without any truncation or consideration of the \textit{create:particle-size} parameter. \textit{create:full} should be used when importing a complete system, such as a complete nanoparticle and where a further definition of the system shape is not required. This is the default if no system truncation is defined. - -{\zicf create:cube}\phantomsection\addcontentsline{toc}{subsection}{create:cube} Cuts a cuboid particle of size \textit{$l_x$ = $l_y$ = $l_z$ = create:particle-size} from the defined crystal lattice. - -{\zicf create:cylinder}\phantomsection\addcontentsline{toc}{subsection}{create:cylinder} Cuts a cylindrical particle of diameter \textit{create:particle-size} from the defined crystal lattice. The height of the cylinder extends to the whole extent of the system size \textit{create:system-size-z} in the $z$-direction. - -{\zicf create:ellipsoid}\phantomsection\addcontentsline{toc}{subsection}{create:ellipsoid} Cuts an ellipsoid particle of diameter \textit{create:particle-size} with fractional diameters of \textit{dimensions:particle-shape- factor-x},\textit{dimensions:particle-shape-factor-y}, \textit{dimensions:particle-shape-factor-z} from the defined crystal lattice. - -{\zicf create:sphere}\phantomsection\addcontentsline{toc}{subsection}{create:sphere} Cuts a spherical particle of diameter \textit{create:particle-size} from the defined crystal lattice. - -{\zicf create:truncated-octahedron}\phantomsection\addcontentsline{toc}{subsection}{create:truncated-octahedron} Cuts a truncated octahedron particle of diameter \textit{create:particle-size} from the defined crystal lattice. +{\zicf create:full}\phantomsection\addcontentsline{toc}{subsection}{create:full} +Uses the entire generated system without any truncation or consideration of the +\textit{create:particle-size} parameter. \textit{create:full} should be used when +importing a complete system, such as a complete nanoparticle and where a further +definition of the system shape is not required. This is the default if no system +truncation is defined. + +{\zicf create:cube}\phantomsection\addcontentsline{toc}{subsection}{create:cube} +Cuts a cuboid particle of size \textit{$l_x$ = $l_y$ = $l_z$ = create:particle-size} +from the defined crystal lattice. + +{\zicf create:cylinder}\phantomsection\addcontentsline{toc}{subsection}{create:cylinder} +Cuts a cylindrical particle of diameter \textit{create:particle-size} from the +defined crystal lattice. The height of the cylinder extends to the whole extent +of the system size \textit{create:system-size-z} in the $z$-direction. + +{\zicf create:ellipsoid}\phantomsection\addcontentsline{toc}{subsection}{create:ellipsoid} +Cuts an ellipsoid particle of diameter \textit{create:particle-size} with fractional +diameters of \textit{dimensions:particle-shape- factor-x}, +\textit{dimensions:particle-shape-factor-y}, +\textit{dimensions:particle-shape-factor-z} from the defined crystal lattice. + +{\zicf create:sphere}\phantomsection\addcontentsline{toc}{subsection}{create:sphere} +Cuts a spherical particle of diameter \textit{create:particle-size} from the +defined crystal lattice. + +{\zicf create:truncated-octahedron}\phantomsection\addcontentsline{toc}{subsection}{create:truncated-octahedron} +Cuts a truncated octahedron particle of diameter \textit{create:particle-size} +from the defined crystal lattice. % tear drop @@ -25,7 +43,7 @@ \section*{System Generation} %{\zicf create:hexagonal-particle-array}\phantomsection\addcontentsline{toc}{subsection}{create:hexagonal-particle-array} Defines the creation of a two-dimensional array of particles on a hexagonal lattice. The particles are separated by a distance \textit{create:particle-spacing}. If the system size is insufficient to contain at least a single entire particle of size \textit{create:particle-size} then no atoms will be generated and the program will terminate.\\ \par -{\zicf create:voronoi-film}\phantomsection\addcontentsline{toc}{subsection}{create:voronoi-film} Generates a two-dimensional voronoi structure of particles, with a mean grain size of \textit{create:particle-size} and variance \textit{create:voronoi-size-variance} as a fraction of the grain size. If \textit{create:voronoi-size-variance}=0 then hexagonal shaped grains are generated. The spacing between the grains (defined by the initial voronoi seed points) is controlled by \textit{create:particle-spacing}. The pseudo-random pattern uses a predefined random seed, and so the generated structure will be the same every time. A different structure can be generated by setting a new random seed using the \textit{create:voronoi-random-seed} parameter. Depending on the desired edge structure, the first row can be shifted using the \textit{create:voronoi-row-offset} flag which changes the start point of the voronoi pattern. The \textit{create:voronoi-rounded-grains} parameter generates a voronoi structure, but then applies a grain rounding algorithm to remove the sharp edges. +{\zicf create:voronoi-film}\phantomsection\addcontentsline{toc}{subsection}{create:voronoi-film} Generates a two-dimensional voronoi structure of particles, with a mean grain size of \textit{create:particle-size} and variance \textit{create:voronoi-size-variance} as a fraction of the grain size. If \textit{create:voronoi-size-variance}=0 then hexagonal shaped grains are generated. The spacing between the grains (defined by the initial voronoi seed points) is controlled by \textit{create:particle-spacing}. The pseudo-random pattern uses a predefined random seed, and so the generated structure will be the same every time. A different structure can be generated by setting a new random seed using the \textit{create:voronoi-random-seed} parameter. Depending on the desired edge structure, the first row can be shifted using the \textit{create:voronoi-row-offset} flag which changes the start point of the voronoi pattern. The \textit{create:voronoi-rounded-grains} parameter generates a voronoi structure, but then applies a grain rounding algorithm to remove the sharp edges. The \textit{create:voronoi-elliptical-rounding} parameter tapers the grains in the direction normal to the film, generating three-dimensional ellipsoidal grain shapes. The \textit{create:voronoi-bimodal-grains} parameter generates a film with two grain size populations (standard and small) instead of a single grain size. {\zicf create:voronoi-size-variance=[float]}\phantomsection\addcontentsline{toc}{subsection}{create:voronoi-size-variance} Controls the randomness of the voronoi grain structure. The voronoi structure is generated using a hexagonal array of seed points appropriately spaced according to the particle size and particle spacing. The seed points are then displaced in $x$ and $y$ according to a gaussian distribution of width \textit{create:voronoi-size-variance} times the particle size. The variance must be in the range 0.0-1.0. Typical values for a realistic looking grain structure are less than 0.2, and larger values will generally lead to oblique grain shapes and a large size distribution. % \\ \par @@ -38,6 +56,76 @@ \section*{System Generation} {\zicf create:voronoi-rounded-grains-area = float [0.0-1.0, default 0.9]}\phantomsection\addcontentsline{toc}{subsection}{create:voronoi-rounded-grains-area} Defines the fractional grain area where the expanding polygon is constrained, in the range 0.0-1.0. Values less than 1.0 will lead to truncation of the voronoi grain shapes, and very small values will generally lead to circular grains. A typical value is 0.9 for reasonable voronoi variance. +{\zicf create:voronoi-elliptical-rounding = float [0.0-1.0, default 0.0]}\phantomsection\addcontentsline{toc}{subsection}{create:voronoi-elliptical-rounding} +Controls the degree of elliptical rounding applied to voronoi grains in the direction normal +to the film. In the standard voronoi construction the grain boundaries are vertical, so that +each grain has the same cross-section at every height. This parameter progressively tapers the +grain cross-section away from a chosen height, generating grains which are ellipsoidal in three +dimensions while preserving the granular structure in the plane. A value of 0.0 gives the +standard voronoi structure with vertical grain boundaries, while a value of 1.0 gives fully +ellipsoidal grains which taper to a point at the top and bottom surfaces of the film. +Intermediate values interpolate smoothly between these limits, generating the wider grain +boundaries towards the film surfaces which are typical of sputtered granular media. The +in-plane grain shape at the widest cross-section is always the full voronoi cell, and so +increasing the rounding reduces the total magnetic volume of the system. The height of the +widest cross-section is set by \textit{create:voronoi-elliptical-rounding-height}. This +parameter may be combined with \textit{create:voronoi-rounded-grains}, which rounds the grain +shape within the plane. + +{\zicf create:voronoi-elliptical-rounding-height = float [0.0-1.0, default 0.5]}\phantomsection\addcontentsline{toc}{subsection}{create:voronoi-elliptical-rounding-height} +Defines the height of the widest grain cross-section when using +\textit{create:voronoi-elliptical-rounding}, expressed as a fraction of the total system height +\textit{dimensions:system-size-z}. The default value of 0.5 places the widest cross-section at +the centre of the system, generating grains which are symmetric about the mid-plane. Smaller +values move the widest cross-section towards the substrate and larger values towards the free +surface, in both cases generating asymmetric grains which still taper to the top and bottom +surfaces of the film. + +{\zicf create:voronoi-bimodal-grains flag [default false]}\phantomsection\addcontentsline{toc}{subsection}{create:voronoi-bimodal-grains} +Generates a voronoi film with two grain populations instead of the single grain size used by +default: a majority of \textit{standard} grains with mean diameter \textit{dimensions:particle-size} +(as usual), and a minority of \textit{small} grains with mean diameter +\textit{create:voronoi-small-grain-diameter}, mixed randomly throughout the film in the +proportion set by \textit{create:voronoi-small-grain-fraction}. This is useful for modelling +granular media with an engineered mixture of grain sizes, for example to study the effect of a +population of small, thermally less stable grains embedded in a matrix of standard grains. Seed +points for both populations are placed by a randomised packing algorithm rather than the +jittered hexagonal lattice used by default, so grain shapes and boundaries look qualitatively +different (more irregular, less hexagonal) even for the standard-sized population. Because the +two populations are mixed uniformly at random rather than segregated into separate regions, a +voronoi cell's size is governed mainly by the local density of \emph{all} neighbouring seed +points rather than by its own seed's placement radius; consequently the measured mean diameter +of each population is generally closer to the other population's diameter than the requested +values would suggest, and the two grain sizes should be read as a control on grain +\emph{count}/proportion and local packing constraint rather than a precise diameter target. Both +the realised fraction of small grains and the measured mean diameter of each population are +reported to the screen and log file when the system is generated, so the actual outcome for a +given set of parameters can be checked directly. All other voronoi options +(\textit{create:voronoi-rounded-grains}, \textit{create:voronoi-elliptical-rounding}, +\textit{create:voronoi-random-seed}, etc.) apply to bimodal grains exactly as they do to the +default single-population film. + +{\zicf create:voronoi-small-grain-diameter = float [default 2.0 nm]}\phantomsection\addcontentsline{toc}{subsection}{create:voronoi-small-grain-diameter} +Sets the target mean diameter of the small-grain population when using +\textit{create:voronoi-bimodal-grains}. Must be smaller than \textit{dimensions:particle-size}, +which continues to set the standard-grain population's diameter. + +{\zicf create:voronoi-small-grain-fraction = float [0.0-1.0, default 0.3]}\phantomsection\addcontentsline{toc}{subsection}{create:voronoi-small-grain-fraction} +Sets the target fraction of grains (by number) belonging to the small-grain population when +using \textit{create:voronoi-bimodal-grains}. The remaining grains belong to the standard +population. The realised fraction is reported when the system is generated and is normally +close to this target; large deviations indicate that the requested grain sizes and film +dimensions cannot support the requested mixture, in which case the film size or grain sizes +should be adjusted. + +{\zicf create:voronoi-small-grain-size-variance = float [0.0-1.0, default: same as create:voronoi-size-variance]}\phantomsection\addcontentsline{toc}{subsection}{create:voronoi-small-grain-size-variance} +Controls the size variance of the small-grain population when using +\textit{create:voronoi-bimodal-grains}, in exactly the same sense as +\textit{create:voronoi-size-variance}, which continues to control the standard population. If not +set, the small-grain population uses the same variance as the standard population. + +{\zicf create:grain-magnetisation-direction = material / alternating [default material]}\phantomsection\addcontentsline{toc}{subsection}{create:grain-magnetisation-direction} Controls how the initial spin direction set by \textit{material[\#]:initial-spin-direction} (see the Material File Command Reference) is applied across the grains of a granular system (e.g. one generated with \textit{create:voronoi-film}). Grains are numbered sequentially from 0 in the order they are generated. With the default value \textbf{material}, every atom is initialised exactly as specified by its material's texture, with no further modification. With \textbf{alternating}, the initial spin direction is reversed (multiplied by $-1$) for every atom belonging to an odd-numbered grain (grain 1, 3, 5, \ldots), while even-numbered grains (0, 2, 4, \ldots) are left unchanged. This gives neighbouring grains opposite initial magnetisation directions, which is useful for example to initialise antiferromagnetically-coupled grain assemblies or to study domain-wall pinning at grain boundaries. The reversal is applied on top of whichever texture is selected for the material (uniform vector, domain wall, skyrmion, etc.), so it simply flips the sign of the resulting spin direction at each atomic site. Note that with \textit{create:voronoi-bimodal-grains}, grains are numbered in the (randomised) order they are placed rather than in any spatial or size-related order, so \textbf{alternating} will not correlate with grain size or position. + {\zicf create:particle-centre-offset}\phantomsection\addcontentsline{toc}{subsection}{create:particle-parity} Shifts the origin of a particle to the centre of the nearest unit cell. {\zicf create:crystal-structure = string [sc, fcc, bcc, hcp, heusler, kagome, rocksalt, spinel; default sc]}\phantomsection\addcontentsline{toc}{subsection}{create:crystal-structure} Defines the default crystal lattice to be generated. The code supports the basic metallic crystal types simple cubic (sc), body-centred-cubic (bcc), face-centred-cubic (fcc) and hexagonal close-packed (hcp). The code also supports important magnetic structures such as Heusler alloys (heusler), rock-salt such as NiO (rocksalt) spinels such as magnetite (spinel) and kagome lattices. @@ -109,79 +197,172 @@ \section*{System dimensions}\phantomsection\addcontentsline{toc}{section}{System {\zicf dimensions:unit-cell-size-z}\phantomsection\addcontentsline{toc}{subsection}{dimensions:unit-cell-size-z} Defines the size of the unit cell if asymmetric. -{\zicf dimensions:system-size}\phantomsection\addcontentsline{toc}{subsection}{dimensions:system-size} Defines the size of the symmetric bulk crystal. +{\zicf dimensions:system-size = float [0.0 - 10 $\mu$ m, default 7.7 nm}\phantomsection\addcontentsline{toc}{subsection}{dimensions:system-size} +Defines the size of the symmetric bulk crystal in $x$, $y$ and $z$. Note that +large system sizes will generate large numbers of atoms and will require +supercomputing resources. -{\zicf dimensions:system-size-x}\phantomsection\addcontentsline{toc}{subsection}{dimensions:system-size-x} Defines the total size if the system along the $x$-axis. +{\zicf dimensions:system-size-x = float [0.0 - 10 $\mu$ m, default 7.7 nm}\phantomsection\addcontentsline{toc}{subsection}{dimensions:system-size-x} +Defines the total size if the system along the $x$-axis. Note that +large system sizes will generate large numbers of atoms and will require +supercomputing resources. -{\zicf dimensions:system-size-y}\phantomsection\addcontentsline{toc}{subsection}{dimensions:system-size-y} Defines the total size if the system along the $y$-axis. +{\zicf dimensions:system-size-y = float [0.0 - 10 $\mu$ m, default 7.7 nm}\phantomsection\addcontentsline{toc}{subsection}{dimensions:system-size-y} +Defines the total size if the system along the $y$-axis. Note that +large system sizes will generate large numbers of atoms and will require +supercomputing resources. -{\zicf dimensions:system-size-z}\phantomsection\addcontentsline{toc}{subsection}{dimensions:system-size-z} Defines the total size if the system along the $z$-axis. +{\zicf dimensions:system-size-z = float [0.0 - 10 $\mu$ m, default 7.7 nm}\phantomsection\addcontentsline{toc}{subsection}{dimensions:system-size-z} +Defines the total size if the system along the $z$-axis. Note that +large system sizes will generate large numbers of atoms and will require +supercomputing resources. {\zicf dimensions:particle-size = float}\phantomsection\addcontentsline{toc}{subsection}{dimensions:particle-size} Defines the size of particles cut from the bulk crystal. {\zicf dimensions:particle-spacing}\phantomsection\addcontentsline{toc}{subsection}{dimensions:particle-spacing} Defines the spacing between particles in particle arrays or voronoi media. -{\zicf dimensions:particle-shape-factor-x = float [0.001-1, default 1.0]} \phantomsection\addcontentsline{toc}{subsection}{dimensions:particle-shape-factor-x} Modifies the default particle shape to create elongated particles. The selected particle shape is modified by changing the effective particle size in the $x$ direction. This property scales the as a fraction of the particle-size along the $x$-direction. - -{\zicf dimensions:particle-shape-factor-y = float [0.001-1, default 1.0]} \phantomsection\addcontentsline{toc}{subsection}{dimensions:particle-shape-factor-y} Modifies the default particle shape to create elongated particles. The selected particle shape is modified by changing the effective particle size in the $y$ direction. This property scales the as a fraction of the particle-size along the $y$-direction. - -{\zicf dimensions:particle-shape-factor-z = float [0.001-1, default 1.0]}\phantomsection\addcontentsline{toc}{subsection}{dimensions:particle-shape-factor-z} Modifies the default particle shape to create elongated particles. The selected particle shape is modified by changing the effective particle size in the $z$ direction. This property scales the as a fraction of the particle-size along the $z$-direction. - -{\zicf dimensions:particle-array-offset-x [0-10$^4$ \AA]}\phantomsection\addcontentsline{toc}{subsection}{dimensions:particle-array-offset-x} Translates the 2-D particle array the chosen distance along the x-direction. - -{\zicf dimensions:particle-array-offset-y}\phantomsection\addcontentsline{toc}{subsection}{dimensions:particle-array-offset-y} Translates the 2-D particle array the chosen distance along the y-direction. - -{\zicf dimensions:double macro-cell-size}\phantomsection\addcontentsline{toc}{subsection}{dimensions:macro-cell-size} determines the macro cell size for calculation of the demagnetizing field and output of the magnetic configuration. Finer discretisation leads to more accurate results at the cost of significantly longer run times. The cell size should always be less than the system size, as highly asymmetric cells will lead to significant errors in the demagnetisation field calculation. - -\section*{Exchange calculation}\phantomsection\addcontentsline{toc}{section}{Exchange calculation} The following commands control the calculation of built-in exchange interactions for the system. - -{\zicf exchange:interaction-range}\phantomsection\addcontentsline{toc}{subsection}{exchange:interaction-range double default [1.0]} Determines the cutoff range exchange interactions for built-in crystal structures in terms of the nearest neighbour range. Larger ranges will enable more interactions via an exchange function which can include 2nd-10th nearest neighbour interaction shells or exponential functions. Note that longer ranged interactions are slower to calculate. In shell mode the computed interaction shells are printed in the log file. - -{\zicf exchange:function}\phantomsection\addcontentsline{toc}{subsection}{exchange:function string default [nearest-neighbour]} Determines the type of interaction to be used in the spin Hamiltonian. The default \textit{nearest-neighbour} option forces nearest neighbour interactions only. The \textit{shell} option groups neighbours at the same interaction distance into shells which can then be assigned different exchange constants. The \textit{exponential} option implements an exponential decay that is useful for simulating spin glasses and systems such as NdFeB where there are no well-defined neighbour shells. The \textit{material-exponential} function is similar to \textit{exponential}, however it allows different exponential exchange functions to be defined for different inter-material type (for materials as defined in the unit-cell module) interactions e.g. in NdFeB Nd-Fe interactions can have a different function defined vs Fe-Fe interactions. - -{\zicf exchange:decay-multiplier}\phantomsection\addcontentsline{toc}{subsection}{exchange:decay-multiplier double default [1.0]} Determines the value of $A$ to be used in $A\exp{-r/B}+C$ for \textit{exchange:function} = \textit{exponential}. - -{\zicf exchange:decay-length}\phantomsection\addcontentsline{toc}{subsection}{exchange:decay-length double default [0.4]} Determines the value of $B$ to be used in $A\exp{-r/B}+C$ for \textit{exchange:function} = \textit{exponential}. - -{\zicf exchange:decay-shift}\phantomsection\addcontentsline{toc}{subsection}{exchange:decay-shift double default [0.0]} Determines the value of $C$ to be used in $A\exp{-r/B}+C$ for \textit{exchange:function} = \textit{exponential}. - -{\zicf exchange:ucc-exchange-parameters[i][j]}\phantomsection\addcontentsline{toc}{subsection}{exchange:ucc-exchange-parameters[i][j] std::vector < std::vector > [i][j] default [1.0, 0.4, 0.0]} This is used in conjunction with \textit{exchange:function} = \textit{material-exchange}. i and j represent the unit cell category (material as per unit cell module) of the interacting atoms that the user wishes to set the exponential exchange function for. This variable is set to the three comma separated values: $A$, \textit{exchange:decay-multiplier}; $B$, \textit{exchange:decay-length}; $C$, \textit{exchange:decay-shift} in this order. - -{\zicf exchange:dmi-cutoff-range}\phantomsection\addcontentsline{toc}{subsection}{exchange:dmi-cutoff-range double default [1.0]} Determines the cutoff range for i-j-k interactions for the built-in DMI in \vampire. +{\zicf dimensions:particle-shape-factor-x = float [0.001-1, default 1.0]} \phantomsection\addcontentsline{toc}{subsection}{dimensions:particle-shape-factor-x} +Modifies the default particle shape to create elongated particles. The selected +particle shape is modified by changing the effective particle size in the $x$ +direction. This property scales the as a fraction of the particle-size along the +$x$-direction. + +{\zicf dimensions:particle-shape-factor-y = float [0.001-1, default 1.0]} \phantomsection\addcontentsline{toc}{subsection}{dimensions:particle-shape-factor-y} +Modifies the default particle shape to create elongated particles. The selected +particle shape is modified by changing the effective particle size in the $y$ +direction. This property scales the as a fraction of the particle-size along the +$y$-direction. + +{\zicf dimensions:particle-shape-factor-z = float [0.001-1, default 1.0]}\phantomsection\addcontentsline{toc}{subsection}{dimensions:particle-shape-factor-z} +Modifies the default particle shape to create elongated particles. The selected +particle shape is modified by changing the effective particle size in the $z$ +direction. This property scales the as a fraction of the particle-size along the +$z$-direction. + +{\zicf dimensions:particle-array-offset-x [0-10$^4$ \AA]}\phantomsection\addcontentsline{toc}{subsection}{dimensions:particle-array-offset-x} +Translates the 2-D particle array the chosen distance along the x-direction. + +{\zicf dimensions:particle-array-offset-y}\phantomsection\addcontentsline{toc}{subsection}{dimensions:particle-array-offset-y} +Translates the 2-D particle array the chosen distance along the y-direction. + +{\zicf dimensions:double macro-cell-size}\phantomsection\addcontentsline{toc}{subsection}{dimensions:macro-cell-size} +Determines the macro cell size for calculation of the demagnetizing field and +output of the magnetic configuration. Finer discretisation leads to more +accurate results at the cost of significantly longer run times. The cell size +should always be less than the system size, as highly asymmetric cells will lead +to significant errors in the demagnetisation field calculation. + +\section*{Exchange calculation}\phantomsection\addcontentsline{toc}{section}{Exchange calculation} +The following commands control the calculation of built-in exchange interactions +for the system. + +{\zicf exchange:interaction-range}\phantomsection\addcontentsline{toc}{subsection}{exchange:interaction-range double default [1.0]} +Determines the cutoff range exchange interactions for built-in crystal structures +in terms of the nearest neighbour range. Larger ranges will enable more interactions +via an exchange function which can include 2nd-10th nearest neighbour interaction +shells or exponential functions. Note that longer ranged interactions are slower +to calculate. In shell mode the computed interaction shells are printed in the +log file. + +{\zicf exchange:function}\phantomsection\addcontentsline{toc}{subsection}{exchange:function string default [nearest-neighbour]} +Determines the type of interaction to be used in the spin Hamiltonian. The +default \textit{nearest-neighbour} option forces nearest neighbour interactions +only. The \textit{shell} option groups neighbours at the same interaction +distance into shells which can then be assigned different exchange constants. +The \textit{exponential} option implements an exponential decay that is useful +for simulating spin glasses and systems such as NdFeB where there are no +well-defined neighbour shells. The \textit{material-exponential} function is +similar to \textit{exponential}, however it allows different exponential +exchange functions to be defined for different inter-material type (for +materials as defined in the unit-cell module) interactions e.g. in NdFeB +Nd-Fe interactions can have a different function defined vs Fe-Fe interactions. + +{\zicf exchange:decay-multiplier}\phantomsection\addcontentsline{toc}{subsection}{exchange:decay-multiplier double default [1.0]} +Determines the value of $A$ to be used in $A\exp{-r/B}+C$ for +\textit{exchange:function} = \textit{exponential}. + +{\zicf exchange:decay-length}\phantomsection\addcontentsline{toc}{subsection}{exchange:decay-length double default [0.4]} +Determines the value of $B$ to be used in $A\exp{-r/B}+C$ for +\textit{exchange:function} = \textit{exponential}. + +{\zicf exchange:decay-shift}\phantomsection\addcontentsline{toc}{subsection}{exchange:decay-shift double default [0.0]} +Determines the value of $C$ to be used in $A\exp{-r/B}+C$ for +\textit{exchange:function} = \textit{exponential}. + +{\zicf exchange:ucc-exchange-parameters[i][j]}\phantomsection\addcontentsline{toc}{subsection}{exchange:ucc-exchange-parameters[i][j] default [1.0, 0.4, 0.0]} +This is used in conjunction with \textit{exchange:function} = +\textit{material-exchange}. i and j represent the unit cell category (material +as per unit cell module) of the interacting atoms that the user wishes to set +the exponential exchange function for. This variable is set to the three comma +separated values: $A$, \textit{exchange:decay-multiplier}; $B$, +\textit{exchange:decay-length}; $C$, \textit{exchange:decay-shift} in this order. + +{\zicf exchange:dmi-cutoff-range}\phantomsection\addcontentsline{toc}{subsection}{exchange:dmi-cutoff-range double default [1.0]} +Determines the cutoff range for i-j-k interactions for the built-in DMI in \vampire. {\zicf exchange:ab-initio}\phantomsection\addcontentsline{toc}{subsection}{exchange:ab-initio flag default false} Interprets exchange constants in the ab-initio sense and applies a factor 2 increase in the strength of the exchange constants.\\ \section*{Anisotropy calculation} -\phantomsection\addcontentsline{toc}{section}{Anisotropy calculation} The following commands control the calculation of the magnetic anisotropy energy for the system. +\phantomsection\addcontentsline{toc}{section}{Anisotropy calculation} +The following commands control the calculation of the magnetic anisotropy energy +for the system. {\zicf anisotropy:surface-anisotropy-threshold = integer [default native]}\phantomsection\addcontentsline{toc} -{subsection}{anisotropy:surface-anisotropy-threshold} Determines minimal number of neighbours to classify as surface atom. The default value is the number of neighbours specified by the crystal or unit cell file. You can set this as a lower threshold. +{subsection}{anisotropy:surface-anisotropy-threshold} Determines minimal number +of neighbours to classify as surface atom. The default value is the number of +neighbours specified by the crystal or unit cell file. You can set this as a +lower threshold. {\zicf anisotropy:surface-anisotropy-nearest-neighbour-range = float [default $\infty$]}\phantomsection\addcontentsline{toc} -{subsection}{anisotropy:surface-anisotropy-nearest-neighbour-range} Sets the interaction range for the nearest neighbour list used for the surface anisotropy calculation. +{subsection}{anisotropy:surface-anisotropy-nearest-neighbour-range} Sets the +interaction range for the nearest neighbour list used for the surface anisotropy +calculation. {\zicf anisotropy:enable-bulk-neel-anisotropy = bool [default false]}\phantomsection\addcontentsline{toc} -{subsection}{anisotropy:enable-bulk-neel-anisotropy} Enables calculation of the N\'eel pair anisotropy in the bulk, irrespective of the number of neighbours, enabling the effect of localised spin-orbit interactions. Internally this sets a large threshold, and so specifying anisotropy:surface-anisotropy-threshold will override this flag. +{subsection}{anisotropy:enable-bulk-neel-anisotropy} Enables calculation of the +N\'eel pair anisotropy in the bulk, irrespective of the number of neighbours, +enabling the effect of localised spin-orbit interactions. Internally this sets a +large threshold, and so specifying anisotropy:surface-anisotropy-threshold will +override this flag. {\zicf anisotropy:neel-anisotropy-exponential-range = float [default $2.5$]}\phantomsection\addcontentsline{toc} -{subsection}{anisotropy:neel-anisotropy-exponential-range} Enables an exponentially range dependent N\'eel pair anisotropy so that lattice distortions and strains change the magnetoelastic compling strength. In the usual form the method only takes into account the symmetry ($L_{ij}(r) = const$). The value should be set to the typical lattice parameter otherwise the total anisotropy will be significantly higher or lower than expected. The functional form of the range dependence is +{subsection}{anisotropy:neel-anisotropy-exponential-range} Enables an +exponentially range dependent N\'eel pair anisotropy so that lattice distortions +and strains change the magnetoelastic compling strength. In the usual form the +method only takes into account the symmetry ($L_{ij}(r) = const$). The value +should be set to the typical lattice parameter otherwise the total anisotropy +will be significantly higher or lower than expected. The functional form of the +range dependence is \begin{equation} L_{ij}(r_{ij}) = L_0 \exp\left(-F\frac{r_{ij} - r_0}{r_{0}}\right) \end{equation} -\noindent where $r_{ij}$ is the pair separation, $r_0$ is the exponential range, $F$ is the exponential factor and $L_0$ is the usual N\'eel anisotropy constant. The functional form assures that at the first neighbour distance the value of the N\'eel anisotropy constant is the same as would be without the range-dependent form. +\noindent where $r_{ij}$ is the pair separation, $r_0$ is the exponential range, +$F$ is the exponential factor and $L_0$ is the usual N\'eel anisotropy constant. +The functional form assures that at the first neighbour distance the value of +the N\'eel anisotropy constant is the same as would be without the +range-dependent form. {\zicf anisotropy:neel-anisotropy-exponential-factor = float [default $5.52$]}\phantomsection\addcontentsline{toc} -{subsection}{anisotropy:neel-anisotropy-exponential-factor} Enables an exponentially range dependent N\'eel pair anisotropy so that lattice distortions and strains change the magnetoelastic compling strength. In the usual form the method only takes into account the symmetry ($L_{ij}(r) -= const$). The prefactor controls the falloff with increasing range. +{subsection}{anisotropy:neel-anisotropy-exponential-factor} Enables an +exponentially range dependent N\'eel pair anisotropy so that lattice distortions +and strains change the magnetoelastic compling strength. In the usual form the +method only takes into account the symmetry ($L_{ij}(r) = const$). The prefactor +controls the falloff with increasing range. \section*{Dipole field calculation} \phantomsection\addcontentsline{toc}{section}{Dipole field calculation} -The following commands control the calculation of the dipole-dipole field. By default the dipole fields are disabled for performance reasons, but for large systems (> 10 nm) the interactions can become important. The \vampire code implements several different solvers balancing accuracy and performance. The default in V5+ is the tensor method, which approximates the dipole dipole interactions at the macrocell level but calculating a dipole-dipole tensor which is exact if the magnetic moments in each cell are aligned. +The following commands control the calculation of the dipole-dipole field. By +default the dipole fields are disabled for performance reasons, but for large +systems (> 10 nm) the interactions can become important. The \vampire code +implements several different solvers balancing accuracy and performance. The +default in V5+ is the tensor method, which approximates the dipole dipole +interactions at the macrocell level but calculating a dipole-dipole tensor which +is exact if the magnetic moments in each cell are aligned. {\zicf dipole:solver = exclusive string [default tensor]}\phantomsection\addcontentsline{toc}{subsection}{dipole:solver} Declares the solver to be used for the dipole calculation. Available options are: @@ -253,6 +434,68 @@ \section*{HAMR calculation} Specifies the bit sequence to be simulated in the program \textit{hamr-simulation}. Acceptable values are -1 (opposite to field direction), 0 (zero field) and 1 (along field direction) and by default the vector is empty. +\section*{Spin-Lattice calculations} +This includes phonons (lattice dynamics) and uses a Suzuki-Trotter decomposition for the integration. Hence it needs the following: +sim:integrator=suzuki-trotter. + +{\zicf spin-lattice:potential-cutoff-range=float}\phantomsection\addcontentsline{toc}{subsubsection}{spin-lattice:potential-cutoff-range=float} +Distance cutoff for the interatomic potential calculations. Units is Angstrom. Default value is 7.8 !A; + +{\zicf spin-lattice:fields-cutoff-range=float}\phantomsection\addcontentsline{toc}{subsubsection}{spin-lattice:fields-cutoff-range=float} +Distance cutoff for the interatomic potential calculations. Units is Angstrom. Default value is 3.75 !A; + +{\zicf spin-lattice:coupling=string}\phantomsection\addcontentsline{toc}{subsubsection}{spin-lattice:coupling=string} + Flag that sets up the type of spin-lattice coupling. Possible values are : pseudodipolar, {neel-full}; + + {\zicf spin-lattice:potential=string}\phantomsection\addcontentsline{toc}{subsubsection}{spin-lattice:potential=string} + Flag that sets up the type of potential. Possible values are : harmonic, morse, embedded; + + +\section*{Phonon Pump Parameters} +The following commands control an external, time-dependent force pump designed to excite specific phonon modes in the lattice. This is used to simulate the effects of THz laser pulses on the spin-lattice system. All parameters are defined using the \textit{phonon:} prefix. + +{\zicf phonon:linear-pump = boolean}\phantomsection\addcontentsline{toc}{subsubsection}{phonon:linear-pump = boolean} +Flag to enable the linearly polarized phonon pump. If enabled, a force is applied to the atoms according to the parameters below. Default value is false. + +{\zicf phonon:frequency = float ! string}\phantomsection\addcontentsline{toc}{subsubsection}{phonon:frequency = float} +Specifies the frequency of the driving force. The value is converted to base SI units (Hz). It is recommended to specify units. Supported units: THz, GHz. Example: \textit{phonon:frequency = 8.3 ! THz}. + +{\zicf phonon:pulse-start-time = float ! string}\phantomsection\addcontentsline{toc}{subsubsection}{phonon:pulse-start-time = float} +Specifies the simulation time at which the force pump turns on. The value is converted to base SI units (s). It is recommended to specify units. Supported units: ps, fs. Example: \textit{phonon:pulse-start-time = 5.0 ! ps}. + +{\zicf phonon:pulse-end-time = float ! string}\phantomsection\addcontentsline{toc}{subsubsection}{phonon:pulse-end-time = float} +Specifies the simulation time at which the force pump turns off. The value is converted to base SI units (s). It is recommended to specify units. Supported units: ps, fs. Example: \textit{phonon:pulse-end-time = 80.0 ! ps}. + +{\zicf phonon:force-amplitude-x = float}\phantomsection\addcontentsline{toc}{subsubsection}{phonon:force-amplitude-x = float} +The amplitude of the force component applied in the x-direction. For a linear pump, this is the main driving force. For a chiral pump, this could be the amplitude of the cosine component. Units are Newtons (N). + +{\zicf phonon:force-amplitude-y = float}\phantomsection\addcontentsline{toc}{subsubsection}{phonon:force-amplitude-y = float} +The amplitude of the force component applied in the y-direction. For a chiral pump, this could be the amplitude of the sine component. Units are Newtons (N). + +{\zicf phonon:force-amplitude-z = float}\phantomsection\addcontentsline{toc}{subsubsection}{phonon:force-amplitude-z = float} +The amplitude of the force component applied in the z-direction. Units are Newtons (N). + +{\zicf phonon:wave-lambda-x = float}\phantomsection\addcontentsline{toc}{subsubsection}{phonon:wave-lambda-x = float} +The wavelength ($\lambda_x$) of the force wave in the x-direction. Used to calculate the final k-vector. Units are in Angstrom. + +{\zicf phonon:wave-lambda-y = float}\phantomsection\addcontentsline{toc}{subsubsection}{phonon:wave-lambda-y = float} +The wavelength ($\lambda_y$) of the force wave in the y-direction. Units are in Angstrom. + +{\zicf phonon:wave-lambda-z = float}\phantomsection\addcontentsline{toc}{subsubsection}{phonon:wave-lambda-z = float} +The wavelength ($\lambda_z$) of the force wave in the z-direction. Units are in Angstrom. + +\{\zicf phonon:wave-direction-x = float\}\phantomsection\textbackslash{}addcontentsline\{toc\}{subsubsection\}{phonon:wave-direction-x = float\} The x-component of the unit vector that defines the direction of propagation for the force wave. + +\{\zicf phonon:wave-direction-y = float\}\phantomsection\textbackslash{}addcontentsline\{toc\}{subsubsection\}{phonon:wave-direction-y = float\} The y-component of the unit vector that defines the direction of propagation. + +\{\zicf phonon:wave-direction-z = float\}\phantomsection\textbackslash{}addcontentsline\{toc\}{subsubsection\}{phonon:wave-direction-z = float\} The z-component of the unit vector that defines the direction of propagation. + + + + + + + \section*{Simulation Control} \phantomsection\addcontentsline{toc}{section}{Simulation Control} The following commands control the simulation, including the program, maximum temperatures, applied field strength etc. @@ -264,6 +507,7 @@ \section*{Simulation Control} \item[] llg-midpoint \item[] constrained-monte-carlo \item[] hybrid-constrained-monte-carlo + \item[] suzuki-trotter \end{itemize} {\zicf sim:program = exclusive string}\phantomsection\addcontentsline{toc}{subsection}{sim:program} Defines the simulation program to be used. @@ -299,10 +543,38 @@ \section*{Simulation Control} controlled by the parameter \textit{sim:electrical-pulse-time} with a rise time of \textit{sim:electrical-pulse-rise-time} and fall time of \textit{sim:electrical-pulse-fall-time}. The default pulse time is 1 ns, and -default fall and rise times are 0, reproducing a square pulse. The time +default fall and rise times are 0, reproducing a square pulse. An optional delay +before the pulse begins can be set with \textit{sim:electrical-pulse-delay}. The time dependence of the fractional voltage can be printed in the output file with the parameter \textit{output:fractional-electric-field-strength}. +{\zicf sim:program = laser-electrical-pulse}\phantomsection\addcontentsline{toc}{subsubsection}{laser-electrical-pulse} +Simulates the combined effect of an ultrafast laser pulse and an independent electrical pulse. +The laser pulse heats the electronic and phonon subsystems using the two-temperature model (TTM), +driven by the same parameters as \textit{sim:program = temperature-pulse} +(\textit{sim:laser-pulse-temporal-profile}, \textit{sim:laser-pulse-time}, \textit{sim:laser-pulse-power}, +\textit{sim:two-temperature-electron-heat-capacity}, \textit{sim:two-temperature-phonon-heat-capacity}, +\textit{sim:two-temperature-electron-phonon-coupling}, \textit{sim:equilibration-temperature}). +The electrical pulse modulates the spin-transfer or spin-orbit torque strength via a Gaussian envelope, +scaled by the STT/SOT field strengths set in the material file. +The electrical Gaussian is centred at $t_\mathrm{delay} + 3\tau_\mathrm{elec}$ from the start of the simulation, +where $t_\mathrm{delay}$ is set by \textit{sim:electrical-pulse-delay} and $\tau_\mathrm{elec}$ by +\textit{sim:electrical-pulse-time}. +The laser Gaussian peaks at $3\tau_\mathrm{laser}$, so setting delay $= 0$ with equal pulse times produces +perfectly overlapping pulses; a positive delay shifts the electrical pulse to arrive after the laser. +The fractional electrical field strength can be written to the output file with +\textit{output:fractional-electric-field-strength}. + +{\zicf sim:program = field-pulse}\phantomsection\addcontentsline{toc}{subsubsection}{field-pulse} +Simulates the effect of a field pulse from an external applied field. The +system is first equilibrated at constant temperature with zero field. A +Gaussian shaped field pulse is applied, with a maximum applied field strength of +\textit{sim:applied-field-strength} and characteristic time of +\textit{sim:field-pulse-time}. The default pulse time $\tau_\mathrm{P} = 1$ ns +with a maximum field at $t = 3\tau_\mathrm{P}$. The time dependence of the +applied field can be printed in the output file with the parameter +\textit{output:applied-field-strength}. + {\zicf sim:program = cmc-anisotropy}\phantomsection\addcontentsline{toc}{subsubsection}{cmc-anisotropy} Iterates through a series of angles at which the global magnetisation is contrained, allowing individual spins to vary, but preventing the system from reaching a true equilibrium. This allows for the examination of magnetocrystalline anisotropy energy and restoring torques. % Hybrid-CMC \\ % Reverse-Hybrid-CMC x @@ -325,12 +597,8 @@ \section*{Simulation Control} \textit{hamr:track-padding}, while \textit{hamr:NPS}/\textit{hamr:NFT-to-pole-spacing} set the shift between the centre of application of the external field and temperature pulse. -{\zicf sim:enable-dipole-fields flag}\phantomsection\addcontentsline{toc}{subsection}{sim:enable-dipole-fields} Enables calculation of the demagnetising field. - {\zicf sim:enable-fmr-field}\phantomsection\addcontentsline{toc}{subsection}{sim:enable-fmr-field} -{\zicf sim:enable-fast-dipole-fields = Bool [default false]}\phantomsection\addcontentsline{toc}{subsection}{sim:enable-fast-dipole-fields} Enables fast calculation of the demag field by pre calculation of the interaction matrix. - {\zicf sim:dipole-field-update-rate = integer [default 1000]}\phantomsection\addcontentsline{toc}{subsection}{sim:dipole-field-update-rate} Number of timesteps between recalculation of the demag field. Default value is suitable for slow calculations, fast dynamics will generally require much faster update rates. @@ -511,6 +779,18 @@ \section*{Simulation Control} default units of seconds and a default pulse time of 0, i.e. an instantaneous turning off of the current. +{\zicf sim:electrical-pulse-delay = float [default $0.0$ s]}\phantomsection\addcontentsline{toc}{subsection}{sim:electrical-pulse-delay} +Defines a time delay before the electrical pulse begins, with default units of seconds and a default of 0. +In \textit{sim:program = electrical-pulse} the trapezoidal pulse is shifted forward in time by this amount, +so the rise phase does not begin until $t = t_\mathrm{delay}$. +In \textit{sim:program = laser-electrical-pulse} it sets the offset from the laser pulse centre to the +electrical pulse centre; note that in this program \textit{sim:electrical-pulse-time} specifies the +Gaussian width $\tau_\mathrm{elec}$ rather than a trapezoidal constant-phase duration. + + {\zicf sim:field-pulse-time = float [default $1.0$ ns]}\phantomsection\addcontentsline{toc}{subsection}{sim:field-pulse-time} + Defines the pulse time in the program \textit{field-pulse} with default + units of seconds and a default pulse time of 1 ns. + \section*{Data output} \phantomsection\addcontentsline{toc}{section}{Data output} The following commands control what data is output to the \textit{output} file. The order in which they appear is the order in which they appear in the \textit{output} file. Most options output a single column of data, but some output multiple columns, particularly vector data or parameters related to materials, where one column per material is output. Note that this means that for vector data, one set of columns per material is output. @@ -646,6 +926,15 @@ \section*{Data output} {\zicf output:material-mean-specific-heat}\phantomsection\addcontentsline{toc}{subsection}{output:material-mean-specific-heat} Outputs the mean specific heat for each defined material in the system in units of $k_{\mathrm{B}}$ per spin. The data is formatted as one column per material. {\zicf output:fractional-electric-field-strength}\phantomsection\addcontentsline{toc}{subsection}{output:fractional-electric-field-strength} +Outputs the fractional electric field strngth (or voltage) during an \textit{electrical-pulse} simulation. + + +{\zicf output:spin-temperature}\phantomsection\addcontentsline{toc}{subsection}{output:spin-temperature} outputs the spin-temperature. Also possible to output the time-averaged values: mean-spin-temperature and material specific values: material-spin-temperature, mean-material-spin-temperature + +{\zicf output:lattice-temperature}\phantomsection\addcontentsline{toc}{subsection}{output:lattice-temperature} outputs the lattice-temperature. Also possible to output the time-averaged values: mean-lattice-temperature and material specific values: material-lattice-temperature, mean-material-lattice-temperature + + + Outputs the fractional electric field strngth (or voltage) during an \textit{electrical-pulse} simulation. {\zicf output:mpi-timings}\phantomsection\addcontentsline{toc}{subsection}{output:mpi-timings} @@ -660,6 +949,9 @@ \section*{Data output} {\zicf output:column-headers= flag [default false]}\phantomsection\addcontentsline{toc}{subsection}{output:column-headers} Controls the headers at the top of output columns in the output file. The default is false which writes no headers. +{\zicf output:mean-coupling-field-vec}\phantomsection\addcontentsline{toc}{subsection}{output:mean-coupling-field-vec}Outputs the three vector components of the mean pseudodipolar coupling field ($H_c$) averaged over all atoms. The output consists of three columns: Mean\_Hc\_x(T), Mean\_Hc\_y(T), and Mean\_Hc\_z(T). Units are Tesla (T). + + \section*{Configuration output} \phantomsection\addcontentsline{toc}{section}{Configuration output} These options enable the output of spin configuration snapshots during the simulation. The configurations can then be visualised using povray or other software generated with the vampire data converter (vdc) utility. diff --git a/manual/MaterialFileCommandReference/Chapter.tex b/manual/MaterialFileCommandReference/Chapter.tex index f0e95e168..4e57cdd67 100755 --- a/manual/MaterialFileCommandReference/Chapter.tex +++ b/manual/MaterialFileCommandReference/Chapter.tex @@ -53,18 +53,79 @@ \section*{Material File Parameters} %-------------------------------- % Magnetic anisotropy parameters %-------------------------------- +\noindent For details on how anisotropy has been implemented into VAMPIRE in the real spherical harmonic (tesseral harmonic) form, consult 10.1103/PhysRevB.107.064413. +\noindent In general the tesseral harmonic can be added via the following + +{\zicf material:()-()-anisotropy-constant = float [default 0.0 J/atom]}\phantomsection\addcontentsline{toc}{subsection}{material:l-m-anisotropy-constant} Defines the local single-ion magnetocrystalline anisotropy constant for an atomic site. The anisotropy energy is given by the expression +\begin{equation*} + E_{l,m} = -k_{l,m}T_{l,m}(\theta, \phi), +\end{equation*} +replace $l$ and $m$ with appropriate values for the desired tesseral harmonic. +E.g., for the $ l = 2 $, $ m = -2 $ harmonic, use "material[n]:2--2-anisotropy-constant = ". + +\noindent The standard orthonormal tesseral harmonics, as opposed to those which have the leading term set to negative one, may be selected via + +{\zicf material:()-()-orthonormal-tesseral-harmonic-anisotropy-constant = float [default 0.0 J/atom]}\phantomsection\addcontentsline{toc}{subsection}{material:l-m-anisotropy-constant} Defines the local single-ion magnetocrystalline anisotropy constant for an atomic site with normalised tesseral harmonics. + {\zicf material:uniaxial-anisotropy-constant = float [default 0.0 J/atom]}\phantomsection\addcontentsline{toc}{subsection}{material:uniaxial-anisotropy-constant} Defines the local second order single-ion magnetocrystalline anisotropy constant at each atomic site. The anisotropy energy is given by the expression \begin{equation*} -E_i = -\kuu (\sms_i \cdot \ei)^2 +E_{2,0} = -k_{2,0} (\sms_i \cdot \hat{\mathbf{c}}_z )^2 \end{equation*} -\noindent where $\sms_i$ is the local spin direction and \ei is the easy axis unit vector. Positive values of \kuu give a preferred easy axis orientation, and negative values give a preferred easy plane orientation of the spin perpendicular to the easy axis direction. +\noindent where $\sms_i$ is the local spin direction and $\hat{\mathbf{c}}_z$ is the easy/hard axis unit vector. Positive values of $k_{2,0}$ give a preferred easy axis orientation, and negative values give a preferred easy plane orientation of the spin perpendicular to the easy axis direction. {\zicf material:second-order-uniaxial-anisotropy-constant = float [default 0.0\newline J/atom]}\phantomsection\addcontentsline{toc}{subsection}{material:second-order-uniaxial-anisotropy-constant} Has the same meaning and is the preferred form for material:uniaxial-anisotropy-constant. -{\zicf material:fourth-order-uniaxial-anisotropy-constant = float [default 0.0\newline J/atom]}\phantomsection\addcontentsline{toc}{subsection}{material:fourth-order-uniaxial-anisotropy-constant} Implements fourth order uniaxial anisotropy as implemented with spherical -harmonics. +{\zicf material:2-0-order-anisotropy-constant = float [default 0.0\newline J/atom]}\phantomsection\addcontentsline{toc}{subsection}{material:2-0-anisotropy-constant} Has the same meaning, but in a form understood in relation to the tesseral harmonic formulation of anisotropy; 2 refers to order in $\theta$, and 0 to order in $\phi$. + +{\zicf material:second-order-theta-second-order-phi-anisotropy-constant = float [default 0.0\newline J/atom]}\phantomsection\addcontentsline{toc}{subsection}{material:second-order-theta-second-order-phi-anisotropy-constant} Defines the local second order theta second order phi single-ion magnetocrystalline anisotropy constant at each atomic site. + +\begin{equation*} +E_{2,2} = - k_{2,2} ( ( \sms_i \cdot \hat{\mathbf{c}}_x )^2 - ( \sms_i \cdot \hat{\mathbf{c}}_y )^2 ) +\end{equation*} + +\noindent where \hat{\mathbf{c}}_x and \hat{\mathbf{c}}_y are the anisotropic $x$ and $y$ axes respectively. + +{\zicf material:2-2-order-anisotropy-constant = float [default 0.0\newline J/atom]}\phantomsection\addcontentsline{toc}{subsection}{material:2-2-order-anisotropy-constant} Has the same meaning. + +{\zicf material:second-order-rotational-anisotropy-constant = float [default 0.0\newline J/atom]}\phantomsection\addcontentsline{toc}{subsection}{material:second-order-rotational-anisotropy-constant} Has the same meaning. + +{\zicf material:fourth-order-uniaxial-anisotropy-constant = float [default 0.0\newline J/atom]}\phantomsection\addcontentsline{toc}{subsection}{material:fourth-order-uniaxial-anisotropy-constant} Implements fourth order uniaxial anisotropy as implemented with spherical harmonics. + +\begin{equation*} + E_{4,0} = - k_{4,0} [ ( \sms_i \cdot \hat{\mathbf{c}}_z )^4 - \frac{6}{7} ( \sms_i \cdot \hat{\mathbf{c}}_z )^2 ] +\end{equation*} + +{\zicf material:4-0-order-anisotropy-constant = float [default 0.0\newline J/atom]}\phantomsection\addcontentsline{toc}{subsection}{material:4-0-order-anisotropy-constant} Has the same meaning, but in a form understood in relation to the tesseral harmonic formulation of anisotorpy; 4 refers to order in $\theta$, and 0 to order in $\phi$. + +{\zicf material:fourth-order-theta-second-order-phi-anisotropy-constant = float [default 0.0\newline J/atom]}\phantomsection\addcontentsline{toc}{subsection}{material:fourth-order-theta-second-order-phi-anisotropy-constant} Sets fourth order $\theta$ second order $\phi$ rotational anisotropy constant. + +{\zicf material:4-2-order-anisotropy-constant = float 0.0\newline J/atom}\phantomsection\addconstentsline{toc}{subsection}{material:4-2-order-anisotropy-constant} Has the same meaning. + +{\zicf material:fourth-order-theta-fourth-order-phi-anisotropy-constant = float 0.0\newline J/atom}\phantomsection\addcontentsline{toc}{subsection}{material:fourth-order-theta-fourth-order-phi-anisotropy-constant} Sets fourth order $\theta$ fourth order $\phi$ fully rotational anisotropy constant for the tesseral harmonic formalism. + +{\zicf material:4-4-order-anisotropy-constant = float 0.0\newline J/atom}\phantomsection\addconstentsline{toc}{subsection}{material:4-4-order-anisotropy-constant} Has the same meaning. + +{\zicf material:fourth-order-rotational-anisotropy-constant = float 0.0\newline J/atom}\phantomsection\addcontentsline{toc}{subsection}{material:fourth-order-rotational-anisotropy-constant} Has the same meaning. + +{\zicf material:sixth-order-uniaxial-anisotropy-constant = float 0.0\newline J/atom}\phantomsection\addcontentsline{toc}{subsection}{material:sixth-order-uniaxial-anisotropy-constant} Sets the sixth order uniaxial anisotropy constant for materials with single ion anisotropy. + +{\zicf material:6-0-order-anisotropy-constant = float 0.0\newline J/atom}\phantomsection\addcontentsline{toc}{subsection}{material:6-0-order-anisotropy-constant} Has the same meaning. + +{\zicf material:sixth-order-theta-second-order-phi-anisotropy-constant = float 0.0\newline J/atom}\phantomsection\addconstentsline{toc}{subsection}{material:sixth-order-theta-second-order-phi-anisotropy-constant} Sets the sixth order $\theta$ second order $\phi$ rotational anisotropy constant for single ion anisotropy in the tesseral harmonic formalism. + +{\zicf material:6-2-order-anisotropy-constant = float 0.0\newline J/atom}\phantomsection\addcontentsline{toc}{subsection}{material:6-2-order-anisotropy-constant} Has the same meaning. + +{\zicf material:sixth-order-theta-fourth-order-phi-anisotropy-constant = float 0.0\newline J/atom}\phantomsection\addcontentsline{toc}{subsection}{material:sixth-order-theta-fourth-order-phi-anisotropy-constant} Sets the sixth order $\theta$ fourth order $\phi$ rotational anisotorpy constant for single ion anisotropy in the teseral harmonic formalism. + +{\zicf material:6-4-order-anisotropy-constant = float 0.0\newline J/atom}\phantomsection\addcontentsline{toc}{subsection}{material:6-4-order-anisotropy-constant} Has the same meaning. + +{\zicf material:sixth-order-theta-sixth-order-phi-anisotropy-constant = float 0.0\newline J/atom}\phantomsection\addcontentsline{toc}{subsection}{material:sixth-order-theta-sixth-order-phi-anisotropy-constant} Sets the sixth order $theta$ sixth order $phi$ fully rotational anisotropy constant for single ion anisotropy in the tesseral harmonic formalism. + +{\zicf material:6-6-order-anisotropy-constant = float 0.0\newline J/atom}\phantomsection\addcontentsline{toc}{subsection}{material:6-6-order-anisotropy-constant} Has the same meaning. + +{\zicf material:sixth-order-rotational-anisotropy-constant = float 0.0\newline J/atom}\phantomsection\addcontentsline{toc}{subsection}{material:sixth-order-rotational-anisotropy-constant} Has the same meaning. {\zicf material:cubic-anisotropy-constant = float [default 0.0 J/atom]}\phantomsection\addcontentsline{toc}{subsection}{material:cubic-anisotropy-constant} Defines the local cubic magnetocrystalline anisotropy constant at each atomic site. The anisotropy energy is given by the expression @@ -137,7 +198,49 @@ \section*{Material File Parameters} {\zicf material:relative-gamma float [default 1]}\phantomsection\addcontentsline{toc}{subsection}{material:relative-gamma} Defines the gyromagnetic ratio of the material relative to that of the electron $\gamma_{\mathrm{e}} = 1.76$ T$^{-1}$s$^{-1}$. Valid values are in the range 0.01 - 100.0. For most materials $\gamma_{\mathrm{r}} = 1$. -{\zicf material:initial-spin-direction float vector /bool [default (001) / false]}\phantomsection\addcontentsline{toc}{subsection}{material:initial-spin-direction} Determines the initial direction of the spins in the material. Value can wither be a unit vector defining a direction in space, or a boolean which initialises each spin to a different random direction (equivalent to infinite temperature). As with other unit vectors, a normalised value or crystallographic notation (e.g. [110]) may be used. +{\zicf material:initial-spin-direction = texture-name, parameters... [default (001)]}\phantomsection\addcontentsline{toc}{subsection}{material:initial-spin-direction} Determines the initial spin configuration of the material at the start of the simulation. The simplest option is a unit vector defining a uniform direction in space, given either as a normalised value or in crystallographic notation (e.g. [110]). Alternatively, one of several predefined spin textures can be selected by giving its name followed by a comma-separated list of numerical parameters. The following textures are supported: + +Each texture is evaluated independently for every atom of the material, based on the fractional position $(f_x, f_y, f_z)$ of that atom within the overall system bounding box, where each coordinate runs from 0 to 1 across the full size of the system in that direction (regardless of the atom's material). All positional parameters below (centres, widths, radii, wavelengths) are therefore also given as fractions in the range 0-1, making the texture definition independent of the absolute size of the system. Textures are purely geometric: they set the initial direction of each spin before the simulation starts, and have no further effect once the simulation begins. Different materials in the same system may be given different textures (or none, in which case a material defaults to a uniform spin direction along $+z$). + +\begin{itemize} + +\item \verb|random| \\ +Initialises each spin in the material to an independent random direction, equivalent to an infinite temperature configuration. Example: +\begin{quote} +\verb|material[1]:initial-spin-direction = random| +\end{quote} + +\item \verb|domain-wall-x|, \verb|domain-wall-y|, \verb|domain-wall-z|, \emph{centre}, \emph{width} \\ +Initialises a 180$^\circ$ domain wall with a $\tanh$ profile, varying along the chosen axis. \emph{centre} and \emph{width} are given as a fraction of the system size along that axis. Example, for a domain wall centred at the middle of the system along $x$ with a width of half the system size: +\begin{quote} +\verb|material[1]:initial-spin-direction = domain-wall-x, 0.5, 0.5| +\end{quote} + +\item \verb|skyrmion|, \emph{cx}, \emph{cy}, \emph{radius} [, \emph{chirality}, \emph{polarity}] \\ +Initialises a skyrmion in the $x$-$y$ plane, with its core centred at fractional coordinates (\emph{cx}, \emph{cy}) and the given \emph{radius}, both as a fraction of the system size. The optional \emph{chirality} and \emph{polarity} parameters take values of $+1$ or $-1$ (the sign of the value given is used) and respectively control the sense of in-plane rotation and the out-of-plane direction of the core, and both default to $+1$. Example: +\begin{quote} +\verb|material[1]:initial-spin-direction = skyrmion, 0.5, 0.5, 0.25| +\end{quote} + +\item \verb|spin-spiral-x|, \verb|spin-spiral-y|, \verb|spin-spiral-z|, \emph{wavelength} \\ +Initialises a helical spin spiral (spin wave) propagating along the chosen axis, with the given \emph{wavelength} as a fraction of the system size along that axis. Example: +\begin{quote} +\verb|material[1]:initial-spin-direction = spin-spiral-x, 0.5| +\end{quote} + +\item \verb|vortex|, \emph{cx}, \emph{cy}, \emph{core-radius} [, \emph{chirality}, \emph{polarity}] \\ +Initialises an in-plane curling vortex in the $x$-$y$ plane, centred at fractional coordinates (\emph{cx}, \emph{cy}) with the given \emph{core-radius} (as a fraction of the system size) over which the spins rotate out of plane to form the vortex core. The optional \emph{chirality} and \emph{polarity} parameters behave as for \verb|skyrmion| above. Example: +\begin{quote} +\verb|material[1]:initial-spin-direction = vortex, 0.5, 0.5, 0.1| +\end{quote} + +\item \verb|vector-field|, \emph{filename} \\ +Initialises the spin direction of each atom by interpolating a user-supplied vector field, using inverse-distance weighting of the nearest defined points. The file named \emph{filename} contains one point per line in the format \verb|x,y,z,mx,my,mz|, where \verb|x,y,z| are the fractional coordinates (in the range 0-1) of the point within the system, and \verb|mx,my,mz| is the (not necessarily normalised) spin direction at that point. Blank lines and lines beginning with \verb|#| are ignored. Example: +\begin{quote} +\verb|material[1]:initial-spin-direction = vector-field, vector_field.dat| +\end{quote} + +\end{itemize} {\zicf material:material-element string [default "Fe"]}\phantomsection\addcontentsline{toc}{subsection}{material:material-element} Defines a purely descriptive chemical element for the material, which gives visual contrast in a range of interactive atomic structure viewers such as jmol, rasmol etc. In rasmol, Fe is a gold colour, H is white, Li is a deep red, O is red, B is green and Ag is a medium grey. This parameter has no relevance to the simulation at all, and only appears when outputting atomic coordinates, which can be post-processed to be viewable in rasmol. The contrast is particularly useful in inspecting the generated structures, particularly ones with a high degree of complexity. @@ -214,6 +317,19 @@ \section*{Material File Parameters} This keyword also works with the \textit{create:crystal-sublattice-materials} flag to allocate different materials to different sites in the simple crystals bcc, fcc, hcp and kagome. This feature is especially useful for simulating simple antiferromagnets and materials with different kinds of defects or site specific alloying. +The following commands are specific for the spin-lattice module and needed to include phonon dynamics. + +{\zicf material:mass = float [ 0-1 : default 5.7915e-3 ]}\phantomsection\addcontentsline{toc}{subsection}{material:mass} mass of the atom, for spin-lattice dynamics module; default values for bcc Fe; + +{\zicf material:damping-constant-lattice= float [ 0-1 : default 0.6]}\phantomsection\addcontentsline{toc}{subsection}{material:damping-constant-lattice} + +{\zicf material:exchange-J0=float [ 0-1 : default 0.904 !eV]}\phantomsection\addcontentsline{toc}{subsection}{material:exchange-J0} prefactor for the exchange function for SLD module; + +{\zicf material:harmonic-potential-V0=float [ 0-1 : default 0.15 !eV]}\phantomsection\addcontentsline{toc}{subsection}{material:exchange-J0} prefactor for the harmonic potential for SLD module; + +{\zicf material:coupling-C0=float [ 0-1 : default 0.5]}\phantomsection\addcontentsline{toc}{subsection}{material:coupling-C0} prefactor for the coupling function for SLD module; various coupling options available such as pseudodipolar or full-neel. + + %\section*{Example material files} %\phantomsection\addcontentsline{toc}{section}{Example material files} diff --git a/obj/spininitialize/.gitignore b/obj/spininitialize/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/obj/spinlattice/.gitignore b/obj/spinlattice/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/obj/spintextures/.gitignore b/obj/spintextures/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/obj/spinwaves/.gitignore b/obj/spinwaves/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/samples/grain-alternating-magnetisation/Co.mat b/samples/grain-alternating-magnetisation/Co.mat new file mode 100644 index 000000000..2007788c6 --- /dev/null +++ b/samples/grain-alternating-magnetisation/Co.mat @@ -0,0 +1,30 @@ +#=================================================== +# Sample vampire material file +# Cobalt thin film with a uniform initial spin +# direction along +z. See the input file for the +# create:grain-magnetisation-direction = alternating +# setting, which reverses this direction for every +# odd-numbered grain. +#=================================================== + +#--------------------------------------------------- +# Number of Materials +#--------------------------------------------------- +material:num-materials=1 + +#--------------------------------------------------- +# Material 1 - Cobalt +#--------------------------------------------------- +material[1]:material-name=Co +material[1]:damping-constant=1.0 +material[1]:exchange-matrix[1]=11.2e-21 +material[1]:atomic-spin-moment=1.72 !muB +material[1]:uniaxial-anisotropy-constant=1.0e-24 +material[1]:material-element=Co + +# Uniform spin direction along +z, before any grain-level +# alternation is applied by create:grain-magnetisation-direction +material[1]:initial-spin-direction = 0, 0, 1 + +material[1]:minimum-height=0.0 +material[1]:maximum-height=1.0 diff --git a/samples/grain-alternating-magnetisation/input b/samples/grain-alternating-magnetisation/input new file mode 100644 index 000000000..13390fe76 --- /dev/null +++ b/samples/grain-alternating-magnetisation/input @@ -0,0 +1,65 @@ +#---------------------------------------------------------- +# Sample vampire input file demonstrating +# create:grain-magnetisation-direction = alternating +# +# A thin film is divided into voronoi grains. Material 1 is +# initialised with a uniform spin direction along +z +# (material[1]:initial-spin-direction = 0,0,1). With +# create:grain-magnetisation-direction = alternating, every +# odd-numbered grain (1, 3, 5, ...) has this direction +# reversed to -z, while even-numbered grains (0, 2, 4, ...) +# keep +z, giving a "chessboard" pattern of alternating +# grain magnetisation. +#---------------------------------------------------------- + +#---------------------------------------------------------- +# Creation attributes +#---------------------------------------------------------- +create:crystal-structure = fcc +create:voronoi-film + +# Grain (particle) size and spacing for the voronoi structure +dimensions:particle-size = 5 !nm +dimensions:particle-spacing = 1 !nm + +# Flip the initial spin direction for odd-numbered grains +create:grain-magnetisation-direction = alternating + +#---------------------------------------------------------- +# System Dimensions +#---------------------------------------------------------- +dimensions:unit-cell-size = 3.524 !A +dimensions:system-size-x = 30 !nm +dimensions:system-size-y = 30 !nm +dimensions:system-size-z = 2 !nm + +#---------------------------------------------------------- +# Material File +#---------------------------------------------------------- +material:file = Co.mat + +#---------------------------------------------------------- +# Simulation attributes +#---------------------------------------------------------- +sim:temperature = 0.0 +sim:equilibration-time-steps = 0 +sim:total-time-steps = 1 +sim:time-steps-increment = 1 +sim:time-step = 1.0e-15 + +#---------------------------------------------------------- +# Program and integrator +#---------------------------------------------------------- +sim:program = benchmark +sim:integrator = llg-heun + +#---------------------------------------------------------- +# Data output +#---------------------------------------------------------- +output:real-time +output:magnetisation + +screen:time-steps +screen:magnetisation + +config:atoms diff --git a/samples/laser-electrical-pulse/Ni-stt.mat b/samples/laser-electrical-pulse/Ni-stt.mat new file mode 100644 index 000000000..71ff92fd2 --- /dev/null +++ b/samples/laser-electrical-pulse/Ni-stt.mat @@ -0,0 +1,26 @@ +#=================================================== +# Sample vampire material file +# Nickel with spin-transfer torque parameters +#=================================================== + +#--------------------------------------------------- +# Number of Materials +#--------------------------------------------------- +material:num-materials=1 + +#--------------------------------------------------- +# Material 1 - Nickel +# Exchange, moment and anisotropy from ab-initio +# Damping and STT parameters representative of Ni +# Initial spin along easy axis +z +#--------------------------------------------------- +material[1]:material-name=Ni +material[1]:damping-constant=0.05 +material[1]:exchange-matrix[1]=2.757e-21 +material[1]:atomic-spin-moment=0.606 !muB +material[1]:second-order-uniaxial-anisotropy-constant=5.47e-26 +material[1]:initial-spin-direction=0,0,1 +material[1]:material-element=Ni +material[1]:spin-transfer-relaxation-torque=0.1 +material[1]:minimum-height=0.0 +material[1]:maximum-height=1.0 diff --git a/samples/laser-electrical-pulse/input b/samples/laser-electrical-pulse/input new file mode 100644 index 000000000..f6fbd5028 --- /dev/null +++ b/samples/laser-electrical-pulse/input @@ -0,0 +1,77 @@ +#---------------------------------------------------------- +# Sample vampire input file for the laser-electrical-pulse +# program. +# +# Simulates an ultrafast laser pulse (two-temperature model) +# followed by a Gaussian spin-transfer torque (STT) pulse on +# a single-spin Nickel system at 300 K. +# +# Timing: +# Laser pulse peaks at t = 3 x laser-pulse-time = 300 fs +# STT pulse peaks at t = delay + 3 x electrical-pulse-time +# = 200 + 600 = 800 fs +# => STT pulse arrives 500 fs after the laser peak +#---------------------------------------------------------- + +#---------------------------------------------------------- +# Creation attributes +#---------------------------------------------------------- +create:single-spin + +#---------------------------------------------------------- +# Material file +#---------------------------------------------------------- +material:file = Ni-stt.mat + +#---------------------------------------------------------- +# Simulation attributes +#---------------------------------------------------------- +sim:equilibration-temperature = 300.0 +sim:time-step = 1 !fs +sim:time-steps-increment = 100 +sim:equilibration-time-steps = 10000 +sim:total-time-steps = 20000 + +#---------------------------------------------------------- +# Laser pulse - two-temperature model (Nickel parameters) +# electron heat capacity coefficient gamma (J/m^3/K^2) +# phonon heat capacity Cl (J/m^3/K) +# electron-phonon coupling G (J/m^3/K/s) +# heat-sink coupling (s^-1) - models thermal diffusion +#---------------------------------------------------------- +sim:laser-pulse-temporal-profile = two-temperature +sim:laser-pulse-time = 100 !fs +sim:laser-pulse-power = 20.0 +sim:two-temperature-electron-heat-capacity = 222.0 +sim:two-temperature-phonon-heat-capacity = 2.3e6 +sim:two-temperature-electron-phonon-coupling = 6.6e17 +sim:two-temperature-heat-sink-coupling = 1.0e11 + +#---------------------------------------------------------- +# Electrical (STT) pulse +# electrical-pulse-time sets the Gaussian width sigma +# electrical-pulse-delay sets the time offset from t = 0 +# Polarization anti-parallel to easy axis drives switching +#---------------------------------------------------------- +sim:electrical-pulse-time = 200 !fs +sim:electrical-pulse-delay = 200 !fs +sim:spin-transfer-torque-polarization-unit-vector = 0,0,-1 + +#---------------------------------------------------------- +# Program and integrator +#---------------------------------------------------------- +sim:program = laser-electrical-pulse +sim:integrator = llg-heun + +#---------------------------------------------------------- +# Data output +#---------------------------------------------------------- +output:real-time +output:electron-temperature +output:phonon-temperature +output:fractional-electric-field-strength +output:material-magnetisation + +screen:real-time +screen:electron-temperature +screen:material-magnetisation diff --git a/samples/spin-texture-domain-wall/Co.mat b/samples/spin-texture-domain-wall/Co.mat new file mode 100644 index 000000000..bd1d0b5bb --- /dev/null +++ b/samples/spin-texture-domain-wall/Co.mat @@ -0,0 +1,27 @@ +#=================================================== +# Sample vampire material file +# Cobalt thin film with a 180 degree domain wall as +# the initial spin configuration (see input file for +# the material:initial-spin-direction settings) +#=================================================== + +#--------------------------------------------------- +# Number of Materials +#--------------------------------------------------- +material:num-materials=1 + +#--------------------------------------------------- +# Material 1 - Cobalt +#--------------------------------------------------- +material[1]:material-name=Co +material[1]:damping-constant=1.0 +material[1]:exchange-matrix[1]=11.2e-21 +material[1]:atomic-spin-moment=1.72 !muB +material[1]:uniaxial-anisotropy-constant=1.0e-24 +material[1]:material-element=Co + +# Domain wall centred at x = 0.5 of the system size, width 0.2 +material[1]:initial-spin-direction = domain-wall-x, 0.5, 0.2 + +material[1]:minimum-height=0.0 +material[1]:maximum-height=1.0 diff --git a/samples/spin-texture-domain-wall/input b/samples/spin-texture-domain-wall/input new file mode 100644 index 000000000..996bc5e5e --- /dev/null +++ b/samples/spin-texture-domain-wall/input @@ -0,0 +1,53 @@ +#---------------------------------------------------------- +# Sample vampire input file demonstrating the +# material:initial-spin-direction = domain-wall-x texture +# +# A thin film is initialised with a 180 degree domain wall +# centred at x = 0.5 of the system size, with a width of +# 0.2 of the system size. Spins point along +z for x < centre +# and along -z for x > centre, with a tanh profile in between. +#---------------------------------------------------------- + +#---------------------------------------------------------- +# Creation attributes +#---------------------------------------------------------- +create:crystal-structure = fcc + +#---------------------------------------------------------- +# System Dimensions +#---------------------------------------------------------- +dimensions:unit-cell-size = 3.524 !A +dimensions:system-size-x = 20 !nm +dimensions:system-size-y = 20 !nm +dimensions:system-size-z = 1 !nm + +#---------------------------------------------------------- +# Material File +#---------------------------------------------------------- +material:file = Co.mat + +#---------------------------------------------------------- +# Simulation attributes +#---------------------------------------------------------- +sim:temperature = 0.0 +sim:equilibration-time-steps = 0 +sim:total-time-steps = 1 +sim:time-steps-increment = 1 +sim:time-step = 1.0e-15 + +#---------------------------------------------------------- +# Program and integrator +#---------------------------------------------------------- +sim:program = benchmark +sim:integrator = llg-heun + +#---------------------------------------------------------- +# Data output +#---------------------------------------------------------- +output:real-time +output:magnetisation + +screen:time-steps +screen:magnetisation + +config:atoms diff --git a/samples/spin-texture-skyrmion/Co.mat b/samples/spin-texture-skyrmion/Co.mat new file mode 100644 index 000000000..14bbae114 --- /dev/null +++ b/samples/spin-texture-skyrmion/Co.mat @@ -0,0 +1,27 @@ +#=================================================== +# Sample vampire material file +# Cobalt thin film with a skyrmion as the initial +# spin configuration (see input file for the +# material:initial-spin-direction settings) +#=================================================== + +#--------------------------------------------------- +# Number of Materials +#--------------------------------------------------- +material:num-materials=1 + +#--------------------------------------------------- +# Material 1 - Cobalt +#--------------------------------------------------- +material[1]:material-name=Co +material[1]:damping-constant=1.0 +material[1]:exchange-matrix[1]=11.2e-21 +material[1]:atomic-spin-moment=1.72 !muB +material[1]:uniaxial-anisotropy-constant=1.0e-24 +material[1]:material-element=Co + +# Skyrmion centred at (x,y) = (0.5, 0.5) of the system size, radius 0.3 +material[1]:initial-spin-direction = skyrmion, 0.5, 0.5, 0.3 + +material[1]:minimum-height=0.0 +material[1]:maximum-height=1.0 diff --git a/samples/spin-texture-skyrmion/input b/samples/spin-texture-skyrmion/input new file mode 100644 index 000000000..6aa8e6e48 --- /dev/null +++ b/samples/spin-texture-skyrmion/input @@ -0,0 +1,52 @@ +#---------------------------------------------------------- +# Sample vampire input file demonstrating the +# material:initial-spin-direction = skyrmion texture +# +# A thin film is initialised with a skyrmion centred in the +# middle of the system (x = 0.5, y = 0.5) with a radius of +# 0.3 of the system size, with default chirality and polarity. +#---------------------------------------------------------- + +#---------------------------------------------------------- +# Creation attributes +#---------------------------------------------------------- +create:crystal-structure = fcc + +#---------------------------------------------------------- +# System Dimensions +#---------------------------------------------------------- +dimensions:unit-cell-size = 3.524 !A +dimensions:system-size-x = 20 !nm +dimensions:system-size-y = 20 !nm +dimensions:system-size-z = 1 !nm + +#---------------------------------------------------------- +# Material File +#---------------------------------------------------------- +material:file = Co.mat + +#---------------------------------------------------------- +# Simulation attributes +#---------------------------------------------------------- +sim:temperature = 0.0 +sim:equilibration-time-steps = 0 +sim:total-time-steps = 1 +sim:time-steps-increment = 1 +sim:time-step = 1.0e-15 + +#---------------------------------------------------------- +# Program and integrator +#---------------------------------------------------------- +sim:program = benchmark +sim:integrator = llg-heun + +#---------------------------------------------------------- +# Data output +#---------------------------------------------------------- +output:real-time +output:magnetisation + +screen:time-steps +screen:magnetisation + +config:atoms diff --git a/samples/spin-texture-vector-field/Co.mat b/samples/spin-texture-vector-field/Co.mat new file mode 100644 index 000000000..37acdad7d --- /dev/null +++ b/samples/spin-texture-vector-field/Co.mat @@ -0,0 +1,27 @@ +#=================================================== +# Sample vampire material file +# Cobalt thin film with an initial spin configuration +# interpolated from a user-supplied vector field +# (see input file and field.dat) +#=================================================== + +#--------------------------------------------------- +# Number of Materials +#--------------------------------------------------- +material:num-materials=1 + +#--------------------------------------------------- +# Material 1 - Cobalt +#--------------------------------------------------- +material[1]:material-name=Co +material[1]:damping-constant=1.0 +material[1]:exchange-matrix[1]=11.2e-21 +material[1]:atomic-spin-moment=1.72 !muB +material[1]:uniaxial-anisotropy-constant=1.0e-24 +material[1]:material-element=Co + +# Initial spin direction interpolated from field.dat +material[1]:initial-spin-direction = vector-field, field.dat + +material[1]:minimum-height=0.0 +material[1]:maximum-height=1.0 diff --git a/samples/spin-texture-vector-field/field.dat b/samples/spin-texture-vector-field/field.dat new file mode 100644 index 000000000..6800cfafd --- /dev/null +++ b/samples/spin-texture-vector-field/field.dat @@ -0,0 +1,17 @@ +# Sample vector field file for material:initial-spin-direction = vector-field +# +# Each line gives a point "x,y,z,mx,my,mz" where x,y,z are fractional +# coordinates of the system size [0:1] and mx,my,mz is the (not +# necessarily normalised) spin direction at that point. Initial spins +# for each atom are obtained by inverse-distance-weighted interpolation +# of these points. +# +# This example rotates the in-plane spin direction around the four +# corners of the film, with the out-of-plane direction at the centre. +# +# x, y, z, mx, my, mz +0.0, 0.0, 0.5, 1, 0, 0 +1.0, 0.0, 0.5, 0, 1, 0 +0.0, 1.0, 0.5, 0, -1, 0 +1.0, 1.0, 0.5, -1, 0, 0 +0.5, 0.5, 0.5, 0, 0, 1 diff --git a/samples/spin-texture-vector-field/input b/samples/spin-texture-vector-field/input new file mode 100644 index 000000000..d9e684359 --- /dev/null +++ b/samples/spin-texture-vector-field/input @@ -0,0 +1,51 @@ +#---------------------------------------------------------- +# Sample vampire input file demonstrating the +# material:initial-spin-direction = vector-field texture +# +# A thin film is initialised by interpolating the user-supplied +# vector field defined in field.dat (inverse-distance weighting). +#---------------------------------------------------------- + +#---------------------------------------------------------- +# Creation attributes +#---------------------------------------------------------- +create:crystal-structure = fcc + +#---------------------------------------------------------- +# System Dimensions +#---------------------------------------------------------- +dimensions:unit-cell-size = 3.524 !A +dimensions:system-size-x = 20 !nm +dimensions:system-size-y = 20 !nm +dimensions:system-size-z = 1 !nm + +#---------------------------------------------------------- +# Material File +#---------------------------------------------------------- +material:file = Co.mat + +#---------------------------------------------------------- +# Simulation attributes +#---------------------------------------------------------- +sim:temperature = 0.0 +sim:equilibration-time-steps = 0 +sim:total-time-steps = 1 +sim:time-steps-increment = 1 +sim:time-step = 1.0e-15 + +#---------------------------------------------------------- +# Program and integrator +#---------------------------------------------------------- +sim:program = benchmark +sim:integrator = llg-heun + +#---------------------------------------------------------- +# Data output +#---------------------------------------------------------- +output:real-time +output:magnetisation + +screen:time-steps +screen:magnetisation + +config:atoms diff --git a/src/anisotropy/data.cpp b/src/anisotropy/data.cpp index e2dea1a5a..e8c0d782a 100644 --- a/src/anisotropy/data.cpp +++ b/src/anisotropy/data.cpp @@ -3,9 +3,11 @@ // This file is part of the VAMPIRE open source package under the // Free BSD licence (see licence file for details). // -// (c) Sam Westmoreland and Richard Evans 2017. All rights reserved. +// (c) Jack Collings, Sam Westmoreland and Richard Evans 2017. +// All rights reserved. // // Email: sw766@york.ac.uk +// jbc525@york.ac.uk // //------------------------------------------------------------------------------ // @@ -37,13 +39,38 @@ namespace anisotropy{ bool enable_neel_anisotropy = false; // Flag to turn on Neel anisotropy calculation (memory intensive at startup) bool enable_lattice_anisotropy = false; // Flag to turn on lattice anisotropy calculation bool enable_random_anisotropy = false; // Flag to enable random anisitropy initialisation + bool enable_rotational_anisotropy = false; // Flag for CUDA to store rotational data + + // Flags for spherical-harmonic decomposition of magnetocrystalline anisotropy + bool enable_uniaxial_second_order = false; // Flag to enable calculation of second order anisotropy + bool enable_rotational_2_1_order = false; // Flag to enable calculation of second order theta first ordrer phi anisotropy + bool enable_rotational_2_1_order_odd = false; // Flag to enable calculation of second order theta first order phi odd anisotropy + bool enable_rotational_2_2_order = false; // Flag to enable calculation of second order theta second order phi anisotropy + bool enable_rotational_2_2_order_odd = false; // Flag to enable calculation of second order theta second order phi odd anisotropy + bool enable_uniaxial_fourth_order = false; // Flag to enable calculation of fourth order anisotropy + bool enable_rotational_4_1_order = false; // Flag to enable calculation of fourth order theta first order phi anisotropy + bool enable_rotational_4_1_order_odd = false; // Flag to enable calculation of fourth order theta first order phi odd anisotropy + bool enable_rotational_4_2_order = false; // Flag to enable calculation of fourth order theta second order phi anisotorpy + bool enable_rotational_4_2_order_odd = false; // Flag to enable calculation of fourth order theta second order phi odd anisotropy + bool enable_rotational_4_3_order = false; // Flag to enable calculation of fourth order theta third order phi anisotropy + bool enable_rotational_4_3_order_odd = false; // Flag to enable calculation of fourth order theta third order phi odd anisotropy + bool enable_rotational_4_4_order = false; // Flag to enable calculation of fourth order theta fourth order phi anisotropy + bool enable_rotational_4_4_order_odd = false; // Flag to enable calculation of fourth order theta fourth order phi odd anisotropy + bool enable_uniaxial_sixth_order = false; // Flag to enable calculation of sixth order anisotropy + bool enable_rotational_6_1_order = false; // flag to enable calculation of sixth order theta first order phi anisotropy + bool enable_rotational_6_1_order_odd = false; // Flag to enable calculation of sixth order theta first order phi odd anisotropy + bool enable_rotational_6_2_order = false; // Flag to enable calculation of sixth order theta second order phi anisotropy + bool enable_rotational_6_2_order_odd = false; // Flag to enable calculation of sixth order theta second order phi odd anisotropy + bool enable_rotational_6_3_order = false; // Flag to enable calculation of sixth order theta third order phi anisotropy + bool enable_rotational_6_3_order_odd = false; // Flag to enable calculation of sixth order theta third order phi odd anisotropy + bool enable_rotational_6_4_order = false; // Flag to enable calculation of sixth order theta fourth order phi anisotropy + bool enable_rotational_6_4_order_odd = false; // Flag to enable calculation of sixth order theta fourth order phi odd anisotropy + bool enable_rotational_6_5_order = false; // Flag to enable calculation of sixth order theta fifth order phi anisotropy + bool enable_rotational_6_5_order_odd = false; // Flag to enable calculation of sixth order theta fifth order phi odd anisotropy + bool enable_rotational_6_6_order = false; // Flag to enable calculation of sixth order theta sixth order phi anisotropy + bool enable_rotational_6_6_order_odd = false; // Flag to enable calculation of sixth order theta sixth order phi odd anisotropy - bool enable_uniaxial_second_order = false; // Flag to enable calculation of second order anisotropy - bool enable_uniaxial_fourth_order = false; // Flag to enable calculation of fourth order anisotropy bool enable_biaxial_fourth_order_simple = false; // Flag to enable calculation of the simple version of the fourth order anisotropy - bool enable_uniaxial_sixth_order = false; // Flag to enable calculation of sixth order anisotropy - - bool enable_fourth_order_rotational = false; // Flag to enable 4th order rotational anisotropy bool enable_cubic_fourth_order = false; // Flag to enable calculation of fourth order cubic anisotropy bool enable_cubic_sixth_order = false; // Flag to enable calculation of sixth order cubic anisotropy @@ -59,14 +86,40 @@ namespace anisotropy{ // arrays for storing unrolled anisotropy constants in Tesla std::vector ku2(0); + std::vector k2r1(0); + std::vector k2r1_odd(0); + std::vector k2r2(0); + std::vector k2r2_odd(0); std::vector ku4(0); + std::vector k4r1(0); + std::vector k4r1_odd(0); + std::vector k4r2(0); + std::vector k4r2_odd(0); + std::vector k4r3(0); + std::vector k4r3_odd(0); + std::vector k4r4(0); + std::vector k4r4_odd(0); std::vector ku6(0); + std::vector k6r1(0); + std::vector k6r1_odd(0); + std::vector k6r2(0); + std::vector k6r2_odd(0); + std::vector k6r3(0); + std::vector k6r3_odd(0); + std::vector k6r4(0); + std::vector k6r4_odd(0); + std::vector k6r5(0); + std::vector k6r5_odd(0); + std::vector k6r6(0); + std::vector k6r6_odd(0); + std::vector kc4(0); std::vector kc6(0); - std::vector k4r(0); // unrolled arrays for storing easy axes for each material std::vector ku_vector(0); // 001 easy axis direction + std::vector kr_vector(0); // 100 rotational axis direction + std::vector kl_vector(0); // 010 last axis direction std::vector u1_vector(0); // Unit vector along [110] std::vector u2_vector(0); // Unit vector along [1-10] @@ -79,7 +132,7 @@ namespace anisotropy{ std::vector ku4_triaxial_vector_y(100,0); // std::vector ku4_triaxial_vector_z(100,0); // - //basis vectors for second order triaxial - must be orthogonality + //basis vectors for second order triaxial - must be orthogonal std::vector < double > ku_triaxial_basis1x(100,0.0); std::vector < double > ku_triaxial_basis1y(100,0.0); std::vector < double > ku_triaxial_basis1z(100,0.0); @@ -92,7 +145,7 @@ namespace anisotropy{ std::vector < double > ku_triaxial_basis3y(100,0.0); std::vector < double > ku_triaxial_basis3z(100,0.0); - //basis vectors for fourth order triaxial - must be orthogonality + //basis vectors for fourth order triaxial - must be orthogonal std::vector < double > ku4_triaxial_basis1x(100,0.0); std::vector < double > ku4_triaxial_basis1y(100,0.0); std::vector < double > ku4_triaxial_basis1z(100,0.0); diff --git a/src/anisotropy/energy.cpp b/src/anisotropy/energy.cpp index 45883e64a..6e1927569 100644 --- a/src/anisotropy/energy.cpp +++ b/src/anisotropy/energy.cpp @@ -3,9 +3,11 @@ // This file is part of the VAMPIRE open source package under the // Free BSD licence (see licence file for details). // -// (c) Sam Westmoreland and Richard Evans 2017. All rights reserved. +// (c) Jack Collings, Sam Westmoreland and Richard Evans 2017. +// All rights reserved. // // Email: richard.evans@york.ac.uk +// jbc525@york.ac.uk // //------------------------------------------------------------------------------ // @@ -35,16 +37,39 @@ namespace anisotropy{ // if not enabled then do nothing if(internal::enable_uniaxial_second_order) energy += internal::uniaxial_second_order_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_2_1_order) energy += internal::second_order_theta_first_order_phi_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_2_1_order_odd) energy += internal::second_order_theta_first_order_phi_odd_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_2_2_order) energy += internal::second_order_theta_second_order_phi_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_2_2_order_odd) energy += internal::second_order_theta_second_order_phi_odd_energy(atom, mat, sx, sy, sz); if(internal::enable_uniaxial_fourth_order) energy += internal::uniaxial_fourth_order_energy(atom, mat, sx, sy, sz); - if(internal::enable_biaxial_fourth_order_simple) energy += internal::biaxial_fourth_order_simple_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_4_1_order) energy += internal::fourth_order_theta_first_order_phi_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_4_1_order_odd) energy += internal::fourth_order_theta_first_order_phi_odd_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_4_2_order) energy += internal::fourth_order_theta_second_order_phi_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_4_2_order_odd) energy += internal::fourth_order_theta_second_order_phi_odd_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_4_3_order) energy += internal::fourth_order_theta_third_order_phi_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_4_3_order_odd) energy += internal::fourth_order_theta_third_order_phi_odd_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_4_4_order) energy += internal::fourth_order_theta_fourth_order_phi_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_4_4_order_odd) energy += internal::fourth_order_theta_fourth_order_phi_odd_energy(atom, mat, sx, sy, sz); if(internal::enable_uniaxial_sixth_order) energy += internal::uniaxial_sixth_order_energy (atom, mat, sx, sy, sz); + if(internal::enable_rotational_6_1_order) energy += internal::sixth_order_theta_first_order_phi_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_6_1_order_odd) energy += internal::sixth_order_theta_first_order_phi_odd_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_6_2_order) energy += internal::sixth_order_theta_second_order_phi_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_6_2_order_odd) energy += internal::sixth_order_theta_second_order_phi_odd_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_6_3_order) energy += internal::sixth_order_theta_third_order_phi_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_6_3_order_odd) energy += internal::sixth_order_theta_third_order_phi_odd_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_6_4_order) energy += internal::sixth_order_theta_fourth_order_phi_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_6_4_order_odd) energy += internal::sixth_order_theta_fourth_order_phi_odd_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_6_5_order) energy += internal::sixth_order_theta_fifth_order_phi_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_6_5_order_odd) energy += internal::sixth_order_theta_fifth_order_phi_odd_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_6_6_order) energy += internal::sixth_order_theta_sixth_order_phi_energy(atom, mat, sx, sy, sz); + if(internal::enable_rotational_6_6_order_odd) energy += internal::sixth_order_theta_sixth_order_phi_odd_energy(atom, mat, sx, sy, sz); + + if(internal::enable_biaxial_fourth_order_simple) energy += internal::biaxial_fourth_order_simple_energy(atom, mat, sx, sy, sz); if(internal::enable_cubic_fourth_order) energy += internal::cubic_fourth_order_energy(atom, mat, sx, sy, sz); if(internal::enable_cubic_fourth_order_rotation) energy += internal::cubic_fourth_order_rotation_energy(atom, mat, sx, sy, sz); if(internal::enable_cubic_sixth_order) energy += internal::cubic_sixth_order_energy (atom, mat, sx, sy, sz); - if(internal::enable_fourth_order_rotational) energy += internal::rotational_fourth_order_energy_fixed_basis(atom, mat, sx, sy, sz); - if(internal::enable_neel_anisotropy) energy += internal::neel_energy(atom, mat, sx, sy, sz); if(internal::enable_lattice_anisotropy) energy += internal::lattice_energy(atom, mat, sx, sy, sz, temperature); diff --git a/src/anisotropy/fields.cpp b/src/anisotropy/fields.cpp index 75f66d0af..4ed08532f 100644 --- a/src/anisotropy/fields.cpp +++ b/src/anisotropy/fields.cpp @@ -3,9 +3,11 @@ // This file is part of the VAMPIRE open source package under the // Free BSD licence (see licence file for details). // -// (c) Sam Westmoreland and Richard Evans 2017. All rights reserved. +// (c) Jack Collings, Sam Westmoreland and Richard Evans 2017. +// All rights reserved. // // Email: richard.evans@york.ac.uk +// jbc525@york.ac.uk // //------------------------------------------------------------------------------ // @@ -42,9 +44,84 @@ namespace anisotropy{ // second order uniaxial anisotropy internal::uniaxial_second_order_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + // second order theta first order phi anisotropy + internal::second_order_theta_first_order_phi_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // second order theta first order phi odd anisotropy + internal::second_order_theta_first_order_phi_odd_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // second order theta second order phi anisotropy + internal::second_order_theta_second_order_phi_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // second order theta second order phi odd anisotropy + internal::second_order_theta_second_order_phi_odd_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + // fourth order uniaxial anisotropy internal::uniaxial_fourth_order_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + // fourth order theta first order phi anisotropy + internal::fourth_order_theta_first_order_phi_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // fourth order theta first order phi odd anisotropy + internal::fourth_order_theta_first_order_phi_odd_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // fourth order theta second order phi anisotropy + internal::fourth_order_theta_second_order_phi_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // fourth order theta second order phi odd anisotropy + internal::fourth_order_theta_second_order_phi_odd_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // fourth order theta third order phi anisotropy + internal::fourth_order_theta_third_order_phi_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // fourth order theta third order phi odd anisotropy + internal::fourth_order_theta_third_order_phi_odd_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // fourth order theta fourth order phi anisotropy + internal::fourth_order_theta_fourth_order_phi_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // fourth order theta fourth order phi odd anisotropy + internal::fourth_order_theta_fourth_order_phi_odd_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // sixth order uniaxial anisotropy + internal::uniaxial_sixth_order_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // Sixth order theta first order phi anisotropy + internal::sixth_order_theta_first_order_phi_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // Sixth order theta first order phi odd anisotropy + internal::sixth_order_theta_first_order_phi_odd_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // Sixth order theta second order phi anisotropy + internal::sixth_order_theta_second_order_phi_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // Sixth order theta second order phi odd anisotropy + internal::sixth_order_theta_second_order_phi_odd_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // Sixth order theta third order phi anisotropy + internal::sixth_order_theta_third_order_phi_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // Sixth order theta third order phi odd anisotropy + internal::sixth_order_theta_third_order_phi_odd_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // sixth order theta fourth order phi anisotropy + internal::sixth_order_theta_fourth_order_phi_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // sixth order theta fourth order phi odd anisotropy + internal::sixth_order_theta_fourth_order_phi_odd_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // sixth order theta fifth order phi anisotropy + internal::sixth_order_theta_fifth_order_phi_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // sixth order theta fifth order phi odd anisotropy + internal::sixth_order_theta_fifth_order_phi_odd_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // sixth order theta sixth order phi anisotropy + internal::sixth_order_theta_sixth_order_phi_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + + // sixth order theta sixth order phi odd anisotropy + internal::sixth_order_theta_sixth_order_phi_odd_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + // fourth order biaxial anisotropy (simple version) internal::biaxial_fourth_order_simple_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); @@ -60,21 +137,15 @@ namespace anisotropy{ // triaxial fourth order anisotropy fixed basis internal::triaxial_fourth_order_fields_fixed_basis(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); - // sixth order uniaxial anisotropy - internal::uniaxial_sixth_order_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); - - // fourth order rotational anisotropy - internal::rotational_fourth_order_fields_fixed_basis(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); - // fourth order cubic anisotropy internal::cubic_fourth_order_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); - // fourth order cubic anisotropy (rotated basis) - internal::cubic_fourth_order_rotation_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); - // sixth order cubic anisotropy internal::cubic_sixth_order_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + // fourth order cubic anisotropy (rotated basis) + internal::cubic_fourth_order_rotation_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); + // Neel anisotropy internal::neel_fields(spin_array_x, spin_array_y, spin_array_z, type_array, field_array_x, field_array_y, field_array_z, start_index, end_index); diff --git a/src/anisotropy/get_anisotropy.cpp b/src/anisotropy/get_anisotropy.cpp index 8033fab59..d6daa276d 100644 --- a/src/anisotropy/get_anisotropy.cpp +++ b/src/anisotropy/get_anisotropy.cpp @@ -3,7 +3,8 @@ // This file is part of the VAMPIRE open source package under the // Free BSD licence (see licence file for details). // -// (c) Sam Westmoreland and Richard Evans 2017. All rights reserved. +// (c) Jack B. Collings (2022), Sam Westmoreland and Richard Evans 2017. +// All rights reserved. // // Email: richard.evans@york.ac.uk // @@ -34,6 +35,38 @@ namespace anisotropy{ return internal::mp[material].ku2; } + //-------------------------------------------------------------------------------- + // Function to get second order theta first order phi anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k2r1(const int material){ + return internal::mp[material].k2r1; + } + + //-------------------------------------------------------------------------------- + // Function to get second order theta first order phi odd anisotropy constant for + // a given material + //-------------------------------------------------------------------------------- + double get_k2r1_odd(const int material){ + return internal::mp[material].k2r1_odd; + } + + //-------------------------------------------------------------------------------- + // Function to get second order theta second order phi anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k2r2(const int material){ + return internal::mp[material].k2r2; + } + + //-------------------------------------------------------------------------------- + // Function to get second order theta second order phi odd anisotropy constant for + // a given material + //-------------------------------------------------------------------------------- + double get_k2r2_odd(const int material){ + return internal::mp[material].k2r2_odd; + } + //-------------------------------------------------------------------------------- // Function to get fourth order uniaxial anisotropy constant for a given material //-------------------------------------------------------------------------------- @@ -41,6 +74,70 @@ namespace anisotropy{ return internal::mp[material].ku4; } + //-------------------------------------------------------------------------------- + // Function to get fourth order theta first order phi anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k4r1(const int material){ + return internal::mp[material].k4r1; + } + + //-------------------------------------------------------------------------------- + // Function to get fourth order theta first order phi odd anisotropy constant for + // a given material + //-------------------------------------------------------------------------------- + double get_k4r1_odd(const int material){ + return internal::mp[material].k4r1_odd; + } + + //-------------------------------------------------------------------------------- + // Function to get fourth order theta second order phi anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k4r2(const int material){ + return internal::mp[material].k4r2; + } + + //-------------------------------------------------------------------------------- + // Function to get fourth order theta second order phi odd anisotropy constant for + // a given material + //-------------------------------------------------------------------------------- + double get_k4r2_odd(const int material){ + return internal::mp[material].k4r2_odd; + } + + //-------------------------------------------------------------------------------- + // Function to get fourth order theta fourth order phi anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k4r4(const int material){ + return internal::mp[material].k4r4; + } + + //-------------------------------------------------------------------------------- + // Function to get fourth order theta third order phi anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k4r3(const int material){ + return internal::mp[material].k4r3; + } + + //-------------------------------------------------------------------------------- + // Function to get fourth order theta third order phi odd anisotropy constant for + // a given material + //-------------------------------------------------------------------------------- + double get_k4r3_odd(const int material){ + return internal::mp[material].k4r3_odd; + } + + //-------------------------------------------------------------------------------- + // Function to get fourth order theta fourth order phi odd anisotropy constant for + // a given material + //-------------------------------------------------------------------------------- + double get_k4r4_odd(const int material){ + return internal::mp[material].k4r4_odd; + } + //-------------------------------------------------------------------------------- // Function to get sixth order uniaxial anisotropy constant for a given material //-------------------------------------------------------------------------------- @@ -48,6 +145,102 @@ namespace anisotropy{ return internal::mp[material].ku6; } + //-------------------------------------------------------------------------------- + // Function to get sixth order theta first order phi anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k6r1(const int material){ + return internal::mp[material].k6r1; + } + + //-------------------------------------------------------------------------------- + // Function to get sixth order theta first order phi odd anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k6r1_odd(const int material){ + return internal::mp[material].k6r1_odd; + } + + //-------------------------------------------------------------------------------- + // Function to get sixth order theta second order phi anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k6r2(const int material){ + return internal::mp[material].k6r2; + } + + //-------------------------------------------------------------------------------- + // Function to get sixth order theta second order phi odd anisotropy constant for + // a given material + //-------------------------------------------------------------------------------- + double get_k6r2_odd(const int material){ + return internal::mp[material].k6r2_odd; + } + + //-------------------------------------------------------------------------------- + // Function to get sixth order theta third order phi anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k6r3(const int material){ + return internal::mp[material].k6r3; + } + + //-------------------------------------------------------------------------------- + // Function to get sixth order theta third order phi odd anisotropy constant for + // a given material + //-------------------------------------------------------------------------------- + double get_k6r3_odd(const int material){ + return internal::mp[material].k6r3_odd; + } + + //-------------------------------------------------------------------------------- + // Function to get sixth order theta fourth order phi anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k6r4(const int material){ + return internal::mp[material].k6r4; + } + + //-------------------------------------------------------------------------------- + // Function to get sixth order theta fourth order phi odd anisotropy constant for + // a given material + //-------------------------------------------------------------------------------- + double get_k6r4_odd(const int material){ + return internal::mp[material].k6r4_odd; + } + + //-------------------------------------------------------------------------------- + // Function to get sixth order theta fifth order phi anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k6r5(const int material){ + return internal::mp[material].k6r5; + } + + //-------------------------------------------------------------------------------- + // Function to get sixth order theta fifth order phi odd anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k6r5_odd(const int material){ + return internal::mp[material].k6r5_odd; + } + + //-------------------------------------------------------------------------------- + // Function to get sixth order theta sixth order phi anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k6r6(const int material){ + return internal::mp[material].k6r6; + } + + //-------------------------------------------------------------------------------- + // Function to get sixth order theta sixth order phi odd anisotropy constant for a + // given material + //-------------------------------------------------------------------------------- + double get_k6r6_odd(const int material){ + return internal::mp[material].k6r6_odd; + } + //-------------------------------------------------------------------------------- // Function to get fourth order cubic anisotropy constant for a given material //-------------------------------------------------------------------------------- @@ -63,11 +256,38 @@ namespace anisotropy{ } //-------------------------------------------------------------------------------- - // Function to get unit vector defining axis for uniaxial anisotropy for a material + // Function to get unit vector defining axis for uniaxial anisotropy for a + // material //-------------------------------------------------------------------------------- std::vector get_ku_vector(const int material){ return internal::mp[material].ku_vector; } + std::vector get_kr_vector(const int material){ + return internal::mp[material].kr_vector; + } + + std::vector get_kl_vector(const int material){ + return internal::mp[material].kl_vector; + } + + //-------------------------------------------------------------------------------- + // Function to get pointer to neel tensor + //-------------------------------------------------------------------------------- + std::vector get_neel_tensor() { + return internal::neel_tensor; + } + //-------------------------------------------------------------------------------- + // Function to determine if Neel anisotropy is enabled + //-------------------------------------------------------------------------------- + bool is_neel_enabled() { + return internal::enable_neel_anisotropy; + } + //-------------------------------------------------------------------------------- + // Function to determine if Rotational anisotropy is enabled + //-------------------------------------------------------------------------------- + bool is_rotational_enabled() { + return internal::enable_rotational_anisotropy; + } } // end of anisotropy namespace diff --git a/src/anisotropy/initialize.cpp b/src/anisotropy/initialize.cpp index 88b0b2138..85a00f71d 100644 --- a/src/anisotropy/initialize.cpp +++ b/src/anisotropy/initialize.cpp @@ -3,9 +3,11 @@ // This file is part of the VAMPIRE open source package under the // Free BSD licence (see licence file for details). // -// (c) Sam Westmoreland and Richard Evans 2017. All rights reserved. +// (c) Jack Collings (2022), Sam Westmoreland and Richard Evans 2017. All +// rights reserved. // // Email: sw766@york.ac.uk +// jbc525@york.ac.uk // //------------------------------------------------------------------------------ // @@ -24,7 +26,62 @@ namespace anisotropy{ //---------------------------------------------------------------------------- - // function to initialize anisotropy module + // Function to take in two anisotropy basis vectors, check orthogonality, + // normalise them, and generate the last basis vector + //---------------------------------------------------------------------------- + + void set_anisotropy_vectors( std::vector& v1, + std::vector& v2, + std::vector& v3, + int mat){ + + // if we don't need a rotational basis then do nothing as the other vectors do not need to be checked + if(anisotropy::internal::mp[mat].need_rotated_basis == false) return; + + // Set primary axis + double e1[3] = {v1[0], v1[1], v1[2]}; + // Set secondary axis + double e2[3] = {v2[0], v2[1], v2[2]}; + + // Check that vectors are orthogonal + double e1dote2 = e1[0] * e2[0] + e1[1] * e2[1] + e1[2] * e2[2]; + if (e1dote2 > 1e-9 || e1dote2 < -1e-9){ + std::cerr << "Anisotropy basis vectors for material " << mat << " are not orthogonal. Exiting." << std::endl; + zlog << zTs() << "Anisotropy basis vectors for material " << mat << " are not orthogonal. Exiting" << std::endl; + err::vexit(); + } + + // Set final axis as cross product e1 ^ e2 + double e3[3] = {e1[1] * e2[2] - e1[2] * e2[1], e1[2] * e2[0] - e1[0] * e2[2], e1[0] * e2[1] - e1[1] * e2[0]}; + + // Check for zero vectors + double mod_e1 = sqrt(e1[0] * e1[0] + e1[1] * e1[1] + e1[2] * e1[2]); + double mod_e2 = sqrt(e2[0] * e2[0] + e2[1] * e2[1] + e2[2] * e2[2]); + double mod_e3 = sqrt(e3[0] * e3[0] + e3[1] * e3[1] + e3[2] * e3[2]); + + if (mod_e1 < 1e-9 || mod_e2 < 1e-9 || mod_e3 < 1e-9){ + std::cerr << "Anisotropy basis vectors for material " << mat << " are too small. Exiting." << std::endl; + zlog << zTs() << "Anisotropy basis vectors for material " << mat << " are too small. Exiting" << std::endl; + err::vexit(); + } + + // Normalise vectors to unit length + v1[0] = e1[0] / mod_e1; + v1[1] = e1[1] / mod_e1; + v1[2] = e1[2] / mod_e1; + + v2[0] = e2[0] / mod_e2; + v2[1] = e2[1] / mod_e2; + v2[2] = e2[2] / mod_e2; + + v3[0] = e3[0] / mod_e3; + v3[1] = e3[1] / mod_e3; + v3[2] = e3[2] / mod_e3; + + } + + //---------------------------------------------------------------------------- + // Function to initialize anisotropy module //---------------------------------------------------------------------------- void initialize (const unsigned int num_atoms, // number of atoms std::vector& atom_material_array, // atoms::atom_type_array @@ -35,13 +92,13 @@ namespace anisotropy{ //--------------------------------------------------------------------- // get number of materials for simulation //--------------------------------------------------------------------- - int init_num_materials = internal::mp.size(); + int init_num_materials = mp::num_materials; //internal::mp.size(); 30/04/24 JRH. Possible bug. num_materials was always being set to mp.size() which is 100. See line 107 of src/anisotropy/initialize.cpp // if no anisotropy constants initialised, then make sure anisotropy array is the correct size if(init_num_materials == 0) internal::mp.resize(mu_s_array.size()); // set actual number of materials - const int num_materials = internal::mp.size(); + const int num_materials = mp::num_materials; //internal::mp.size(); 30/04/24 JRH. Possible bug. num_materials was always being set to mp.size() which is 100. See line 107 of src/anisotropy/initialize.cpp // output informative message zlog << zTs() << "Initialising data structures for anisotropy calculation for " << num_materials << " materials" << std::endl; @@ -67,21 +124,141 @@ namespace anisotropy{ internal::ku2.resize(num_materials); for(int m = 0; m < num_materials; m++) internal::ku2[m] = internal::mp[m].ku2 * inverse_mu_s[m]; } + // Second order theta first order phi rotational + if(internal::enable_rotational_2_1_order){ + internal::k2r1.resize(num_materials); + for(int m = 0; m < num_materials; m++) internal::k2r1[m] = internal::mp[m].k2r1 * inverse_mu_s[m]; + } + // Second order theta first order phi odd rotational + if(internal::enable_rotational_2_1_order_odd){ + internal::k2r1_odd.resize(num_materials); + for(int m = 0; m < num_materials; m++) internal::k2r1_odd[m] = internal::mp[m].k2r1_odd * inverse_mu_s[m]; + } + // Second order theta second order phi rotational + if(internal::enable_rotational_2_2_order){ + internal::k2r2.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k2r2[m] = internal::mp[m].k2r2 * inverse_mu_s[m]; + } + // Second order theta second order phi odd rotational + if(internal::enable_rotational_2_2_order_odd){ + internal::k2r2_odd.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k2r2_odd[m] = internal::mp[m].k2r2_odd * inverse_mu_s[m]; + } // Fourth order uniaxial if(internal::enable_uniaxial_fourth_order){ internal::ku4.resize(num_materials); for(int m = 0; m < num_materials; m++) internal::ku4[m] = internal::mp[m].ku4 * inverse_mu_s[m]; } - // Fourth order biaxial (simple version) - if(internal::enable_biaxial_fourth_order_simple){ - internal::ku4.resize(num_materials); - for(int m = 0; m < num_materials; m++) internal::ku4[m] = internal::mp[m].ku4 * inverse_mu_s[m]; + // Fourth order theta first order phi rotational + if(internal::enable_rotational_4_1_order){ + internal::k4r1.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k4r1[m] = internal::mp[m].k4r1 * inverse_mu_s[m]; + } + // Fourth order theta first order phi odd rotational + if(internal::enable_rotational_4_1_order_odd){ + internal::k4r1_odd.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k4r1_odd[m] = internal::mp[m].k4r1_odd * inverse_mu_s[m]; + } + // Fourth order theta second order phi rotational + if(internal::enable_rotational_4_2_order){ + internal::k4r2.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k4r2[m] = internal::mp[m].k4r2 * inverse_mu_s[m]; + } + // Fourth order theta second order phi odd rotational + if(internal::enable_rotational_4_2_order_odd){ + internal::k4r2_odd.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k4r2_odd[m] = internal::mp[m].k4r2_odd * inverse_mu_s[m]; + } + // Fourth order theta third order phi rotational + if(internal::enable_rotational_4_3_order){ + internal::k4r3.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k4r3[m] = internal::mp[m].k4r3 * inverse_mu_s[m]; + } + // Fourth order theta third order phi odd rotational + if(internal::enable_rotational_4_3_order_odd){ + internal::k4r3_odd.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k4r3_odd[m] = internal::mp[m].k4r3_odd * inverse_mu_s[m]; + } + // Fourth order theta fourth order phi rotational + if(internal::enable_rotational_4_4_order){ + internal::k4r4.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k4r4[m] = internal::mp[m].k4r4 * inverse_mu_s[m]; + } + // Fourth order theta fourth order phi odd rotational + if(internal::enable_rotational_4_4_order_odd){ + internal::k4r4_odd.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k4r4_odd[m] = internal::mp[m].k4r4_odd * inverse_mu_s[m]; } // Sixth order uniaxial if(internal::enable_uniaxial_sixth_order){ internal::ku6.resize(num_materials); for(int m = 0; m < num_materials; m++) internal::ku6[m] = internal::mp[m].ku6 * inverse_mu_s[m]; } + // Sixth order theta first order phi rotational + if(internal::enable_rotational_6_1_order){ + internal::k6r1.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k6r1[m] = internal::mp[m].k6r1 * inverse_mu_s[m]; + } + // Sixth order theta first order phi odd rotational + if(internal::enable_rotational_6_1_order_odd){ + internal::k6r1_odd.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k6r1_odd[m] = internal::mp[m].k6r1_odd * inverse_mu_s[m]; + } + // Sixth order theta second order phi rotational + if(internal::enable_rotational_6_2_order){ + internal::k6r2.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k6r2[m] = internal::mp[m].k6r2 * inverse_mu_s[m]; + } + // Sixth order theta second order phi odd rotational + if(internal::enable_rotational_6_2_order_odd){ + internal::k6r2_odd.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k6r2_odd[m] = internal::mp[m].k6r2_odd * inverse_mu_s[m]; + } + // Sixth order theta third order phi rotational + if(internal::enable_rotational_6_3_order){ + internal::k6r3.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k6r3[m] = internal::mp[m].k6r3 * inverse_mu_s[m]; + } + // Sixth order theta third order phi odd rotational + if(internal::enable_rotational_6_3_order_odd){ + internal::k6r3_odd.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k6r3_odd[m] = internal::mp[m].k6r3_odd * inverse_mu_s[m]; + } + // Sixth order theta fourth order phi rotational + if(internal::enable_rotational_6_4_order){ + internal::k6r4.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k6r4[m] = internal::mp[m].k6r4 * inverse_mu_s[m]; + } + // Sixth order theta fourth order phi odd rotational + if(internal::enable_rotational_6_4_order_odd){ + internal::k6r4_odd.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k6r4_odd[m] = internal::mp[m].k6r4_odd * inverse_mu_s[m]; + } + // Sixth order theta fifth order phi rotational + if(internal::enable_rotational_6_5_order){ + internal::k6r5.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k6r5[m] = internal::mp[m].k6r5 * inverse_mu_s[m]; + } + // Sixth order theta fifth order phi odd rotational + if(internal::enable_rotational_6_5_order_odd){ + internal::k6r5_odd.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k6r5_odd[m] = internal::mp[m].k6r5_odd * inverse_mu_s[m]; + } + // Sixth order theta sixth order phi rotational + if(internal::enable_rotational_6_6_order){ + internal::k6r6.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k6r6[m] = internal::mp[m].k6r6 * inverse_mu_s[m]; + } + // Sixth order theta sixth order phi odd rotational + if(internal::enable_rotational_6_6_order_odd){ + internal::k6r6_odd.resize(num_materials); + for(int m = 0; m < num_materials; ++m) internal::k6r6_odd[m] = internal::mp[m].k6r6_odd * inverse_mu_s[m]; + } + // Fourth order biaxial (simple version) + if(internal::enable_biaxial_fourth_order_simple){ + internal::ku4.resize(num_materials); + for(int m = 0; m < num_materials; m++) internal::ku4[m] = internal::mp[m].ku4 * inverse_mu_s[m]; + } // Fourth order cubic if(internal::enable_cubic_fourth_order || internal::enable_cubic_fourth_order_rotation){ internal::kc4.resize(num_materials); @@ -92,11 +269,6 @@ namespace anisotropy{ internal::kc6.resize(num_materials); for(int m = 0; m < num_materials; m++) internal::kc6[m] = internal::mp[m].kc6 * inverse_mu_s[m]; } - // Fourth order rotational - if(internal::enable_fourth_order_rotational){ - internal::k4r.resize(num_materials); - for(int m = 0; m < num_materials; m++) internal::k4r[m] = internal::mp[m].k4r * inverse_mu_s[m]; - } //------------------------------------------------------------------------ // 4th order triaxial @@ -202,6 +374,7 @@ namespace anisotropy{ internal::ku4_triaxial_basis3z[mat] = 1; } + } //--------------------------------------------------------------------- @@ -345,63 +518,46 @@ namespace anisotropy{ } //--------------------------------------------------------------------- - // initialise axes for each material + // Initialise anisotropy basis axes for each material //--------------------------------------------------------------------- + internal::ku_vector.resize(num_materials); + internal::kr_vector.resize(num_materials); + internal::kl_vector.resize(num_materials); + + // Loop through all materials + for(int m = 0; m < num_materials; ++m){ + + // Vectors defining the anisotropy basis directions - for(int m = 0; m < num_materials; m++){ + // Check orthogonality, set orthonormality and generate the last anisotropy vector + set_anisotropy_vectors(internal::mp[m].ku_vector, internal::mp[m].kr_vector, internal::mp[m].kl_vector, m); - // unroll uniaxial easy axes + // Uniaxial easy/hard [0,0,1] (z) axis internal::ku_vector[m].x = internal::mp[m].ku_vector[0]; internal::ku_vector[m].y = internal::mp[m].ku_vector[1]; internal::ku_vector[m].z = internal::mp[m].ku_vector[2]; + // Rotational [1,0,0] (x) axis + internal::kr_vector[m].x = internal::mp[m].kr_vector[0]; + internal::kr_vector[m].y = internal::mp[m].kr_vector[1]; + internal::kr_vector[m].z = internal::mp[m].kr_vector[2]; + + // Last axis [0,1,0] (y) axis + internal::kl_vector[m].x = internal::mp[m].kl_vector[0]; + internal::kl_vector[m].y = internal::mp[m].kl_vector[1]; + internal::kl_vector[m].z = internal::mp[m].kl_vector[2]; + } //--------------------------------------------------------------------- - // initialise rotated axis directions for each material + // Initialise cubic anisotropy basis axes for each material //--------------------------------------------------------------------- - for(int mat = 0; mat < num_materials; mat++){ - - // Vectors defining the easy axis in cubic anisotropy (Roberto was here) - double e1[3] = { internal::mp[mat].kc_vector1[0], - internal::mp[mat].kc_vector1[1], - internal::mp[mat].kc_vector1[2] }; - - double e2[3] = { internal::mp[mat].kc_vector2[0], - internal::mp[mat].kc_vector2[1], - internal::mp[mat].kc_vector2[2] }; + for(int mat = 0; mat < num_materials; ++mat){ - // calculate e3 as vector product e1 ^ e2 - double e3[3] = { (internal::mp[mat].kc_vector1[1]*internal::mp[mat].kc_vector2[2] - internal::mp[mat].kc_vector1[2]*internal::mp[mat].kc_vector2[1]), - (internal::mp[mat].kc_vector1[2]*internal::mp[mat].kc_vector2[0] - internal::mp[mat].kc_vector1[0]*internal::mp[mat].kc_vector2[2]), - (internal::mp[mat].kc_vector1[0]*internal::mp[mat].kc_vector2[1] - internal::mp[mat].kc_vector1[1]*internal::mp[mat].kc_vector2[0])}; - - // Calculate vector lengths - double mod_e1 = sqrt(e1[0]*e1[0] + e1[1]*e1[1] + e1[2]*e1[2]); - double mod_e2 = sqrt(e2[0]*e2[0] + e2[1]*e2[1] + e2[2]*e2[2]); - double mod_e3 = sqrt(e3[0]*e3[0] + e3[1]*e3[1] + e3[2]*e3[2]); - - // check for zero vectors and exit with error - if(mod_e1 < 1e-9 || mod_e2 < 1e-9 || mod_e3 < 1e-9){ - std::cerr << "Error! Rotated cubic anisotropy vectors for material " << mat << " are not orthogonal. Exiting" << std::endl; - zlog << zTs() << "Error! Rotated cubic anisotropy vectors for material " << mat << " are not orthogonal. Exiting" << std::endl; - err::vexit(); - } - - // normalise vectors to unit length - internal::mp[mat].kc_vector1[0] = e1[0] / mod_e1; - internal::mp[mat].kc_vector1[1] = e1[1] / mod_e1; - internal::mp[mat].kc_vector1[2] = e1[2] / mod_e1; - - internal::mp[mat].kc_vector2[0] = e2[0] / mod_e2; - internal::mp[mat].kc_vector2[1] = e2[1] / mod_e2; - internal::mp[mat].kc_vector2[2] = e2[2] / mod_e2; - - internal::mp[mat].kc_vector3[0] = e3[0] / mod_e3; - internal::mp[mat].kc_vector3[1] = e3[1] / mod_e3; - internal::mp[mat].kc_vector3[2] = e3[2] / mod_e3; + // Check orthogonality, set orthonormality, and generate the last anisotropy vector + set_anisotropy_vectors(internal::mp[mat].kc_vector1, internal::mp[mat].kc_vector2, internal::mp[mat].kc_vector3, mat); } @@ -410,7 +566,6 @@ namespace anisotropy{ //--------------------------------------------------------------------- if(internal::enable_lattice_anisotropy){ - // arrays for storing unrolled parameters for lattice anisotropy internal::klattice.resize(num_materials); internal::klattice_array.resize(num_materials); // anisoptropy constant diff --git a/src/anisotropy/interface.cpp b/src/anisotropy/interface.cpp index 94ca19767..18c1c088a 100644 --- a/src/anisotropy/interface.cpp +++ b/src/anisotropy/interface.cpp @@ -110,18 +110,844 @@ namespace anisotropy{ //------------------------------------------------------------ // Check for material properties //------------------------------------------------------------ - //Minimal orthogonality TBD - std::string test = "second-order-uniaxial-anisotropy-constant"; // new form (preferred) - std::string test2 = "uniaxial-anisotropy-constant"; // legacy form (deprecated but probably never obsoleted) - if( (word == test) || (word == test2) ){ + + // Single-ion anisotropy energy in terms of orthogonal tesseral harmonics + // A simple orthogonal version is given, as well as an orthonormal version + + // Second order uniaxial anisotropy constant (negative of standard K_1) + std::string test = "2-0-order-anisotropy-constant"; + std::string test2 = "second-order-uniaxial-anisotropy-constant"; + std::string test3 = "uniaxial-anisotropy-constant"; // legacy form (deprecated but probably never obsoleted) + if( (word == test) || (word == test2) || (word == test3) ) + { + double ku2 = atof(value.c_str()); vin::check_for_valid_value(ku2, word, line, prefix, unit, "energy", -1e-17, 1e-17,"material"," < +/- 1.0e-17 J/atom"); internal::mp[super_index].ku2 = ku2; internal::enable_uniaxial_second_order = true; // Switch on second order tensor calculation for all spins return true; + } - // Triaxial anisotropy in second and fourth order + // Second order theta uniaxial anisotropy constant (orthonormal) + test = "2-0-orthonormal-tesseral-harmonic-anisotropy-constant"; + if ( word == test ) + { + + double ku2 = - atof( value.c_str() ) * ( 3.0 / 4.0 ) * sqrt( 5.0 / M_PI ); + vin::check_for_valid_value(ku2, word, line, prefix, unit, "energy", -1e-17, 1e-17,"material"," < +/- 1.0e-17 J/atom"); + internal::mp[super_index].ku2 = ku2; + internal::enable_uniaxial_second_order = true; // Switch on second order tensor calculation for all spins + return true; + + } + + // Second order theta first order phi anisotropy constant + test = "2-1-order-anisotropy-constant"; + test2 = "second-order-theta-first-order-phi-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k2r1 = atof(value.c_str()); + vin::check_for_valid_value(k2r1, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k2r1 = k2r1; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_2_1_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Second order theta first order phi anisotropy constant (orthonormal) + test = "2-1-orthonormal-tesseral-harmonic-anisotropy-constant"; + if ( word == test ) + { + + double k2r1 = - atof( value.c_str() ) * ( 0.25 ) * sqrt( 15.0 / M_PI ); + vin::check_for_valid_value(k2r1, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k2r1 = k2r1; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_2_1_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Second order theta first order phi odd anisotropy constant + test = "2--1-order-anisotropy-constant"; + test2 = "second-order-theta-first-order-phi-odd-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k2r1_odd = atof(value.c_str()); + vin::check_for_valid_value(k2r1_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k2r1_odd = k2r1_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_2_1_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Second order theta first order phi anisotropy constant (orthonormal) + test = "2--1-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k2r1_odd = - atof( value.c_str() ) * ( 0.25 ) * sqrt( 15.0 / M_PI ); + vin::check_for_valid_value(k2r1_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k2r1_odd = k2r1_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_2_1_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Second order theta second order phi anisotropy constant + test = "2-2-order-anisotropy-constant"; + test2 = "second-order-theta-second-order-phi-anisotropy-constant"; + test3 = "second-order-rotational-anisotropy-constant"; + if( (word == test) || (word == test2) || (word == test3) ) + { + + double k2r2 = atof(value.c_str()); + vin::check_for_valid_value(k2r2, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k2r2 = k2r2; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_2_2_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Second order theta second order phi anisotropy constant (orthonormal) + test = "2-2-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k2r2 = - atof( value.c_str() ) * 0.25 * sqrt( 15.0 / M_PI ); + vin::check_for_valid_value(k2r2, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k2r2 = k2r2; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_2_2_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Second order theta second order phi odd anisotropy constant + test = "2--2-order-anisotropy-constant"; + test2 = "second-order-theta-second-order-phi-odd-anisotropy-constant"; + test3 = "second-order-odd-rotational-anisotropy-constant"; + if( (word == test) || (word == test2) || (word == test3) ) + { + + double k2r2_odd = atof(value.c_str()); + vin::check_for_valid_value(k2r2_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k2r2_odd = k2r2_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_2_2_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Second order theta second order phi odd anisotropy constant (orthonormal) + test = "2--2-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k2r2_odd = - atof( value.c_str() ) * 0.25 * sqrt( 15.0 / M_PI ); + vin::check_for_valid_value(k2r2_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k2r2_odd = k2r2_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_2_2_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order uniaxial anisotropy constant + test = "4-0-order-anisotropy-constant"; + test2 = "fourth-order-uniaxial-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double ku4 = atof( value.c_str() ); + vin::check_for_valid_value(ku4, word, line, prefix, unit, "energy", -1e-17, 1e-17,"material"," < +/- 1.0e-17 J/atom"); + internal::mp[super_index].ku4 = ku4; + internal::enable_uniaxial_fourth_order = true; // Switch on second order tensor calculation for all spins (from spherical harmonics) + return true; + + } + + // Fourth order uniaxial anisotropy constant (orthonormal) + test = "4-0-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double ku4 = - atof( value.c_str() ) * ( 3.0 * 35.0 / 16.0 ) * sqrt( 1.0 / M_PI ); + vin::check_for_valid_value(ku4, word, line, prefix, unit, "energy", -1e-17, 1e-17,"material"," < +/- 1.0e-17 J/atom"); + internal::mp[super_index].ku4 = ku4; + internal::enable_uniaxial_fourth_order = true; // Switch on second order tensor calculation for all spins (from spherical harmonics) + return true; + + } + + // Fourth order theta first order phi anisotropy constant + test = "4-1-order-anisotropy-constant"; + test2 = "fourth-order-theta-first-order-phi-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k4r1 = atof(value.c_str()); + vin::check_for_valid_value(k4r1, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r1 = k4r1; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_1_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta first order phi anisotropy constant (orthonormal) + test = "4-1-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k4r1 = - atof( value.c_str() ) * ( 3.0 * 7.0 / 8.0 ) * sqrt( 10.0 / M_PI ); + vin::check_for_valid_value(k4r1, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r1 = k4r1; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_1_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta first order phi odd anisotropy constant + test = "4--1-order-anisotropy-constant"; + test2 = "fourth-order-theta-first-order-phi-odd-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k4r1_odd = atof(value.c_str()); + vin::check_for_valid_value(k4r1_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r1_odd = k4r1_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_1_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta first order phi odd anisotropy constant (orthonormal) + test = "4--1-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k4r1_odd = - atof( value.c_str() ) * ( 3.0 * 7.0 / 8.0 ) * sqrt( 10.0 / M_PI ); + vin::check_for_valid_value(k4r1_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r1_odd = k4r1_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_1_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta second order phi anisotropy constant + test = "4-2-order-anisotropy-constant"; + test2 = "fourth-order-theta-second-order-phi-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k4r2 = atof(value.c_str()); + vin::check_for_valid_value(k4r2, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r2 = k4r2; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_2_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta second order phi anisotropy constant (orthonormal) + test = "4-2-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k4r2 = - atof( value.c_str() ) * ( 3.0 * 7.0 ) * sqrt( 5.0 / M_PI ); + vin::check_for_valid_value(k4r2, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r2 = k4r2; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_2_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta second order phi odd anisotropy constant + test = "4--2-order-anisotropy-constant"; + test2 = "fourth-order-theta-second-order-phi-odd-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k4r2_odd = atof(value.c_str()); + vin::check_for_valid_value(k4r2_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r2_odd = k4r2_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_2_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta second order phi odd anisotropy constant (orthonormal) + test = "4--2-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k4r2_odd = - atof( value.c_str() ) * ( 3.0 * 7.0 ) * sqrt( 5.0 / M_PI ); + vin::check_for_valid_value(k4r2_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r2_odd = k4r2_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_2_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta third order phi anisotropy constant + test = "4-3-order-anisotropy-constant"; + test2 = "fourth-order-theta-third-order-phi-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k4r3 = atof(value.c_str()); + vin::check_for_valid_value(k4r3, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r3 = k4r3; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_3_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta third order phi anisotropy constant (orthonormal) + test = "4-3-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k4r3 = - atof( value.c_str() ) * ( 3.0 / 8.0 ) * sqrt( 70.0 / M_PI ); + vin::check_for_valid_value(k4r3, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r3 = k4r3; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_3_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta third order phi odd anisotropy constant + test = "4--3-order-anisotropy-constant"; + test2 = "fourth-order-theta-third-order-phi-odd-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k4r3_odd = atof(value.c_str()); + vin::check_for_valid_value(k4r3_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r3_odd = k4r3_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_3_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta third order phi odd anisotropy constant (orthonormal) + test = "4--3-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k4r3_odd = - atof( value.c_str() ) * ( 3.0 / 8.0 ) * sqrt( 70.0 / M_PI ); + vin::check_for_valid_value(k4r3_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r3_odd = k4r3_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_3_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta fourth order phi rotational anisotropy constant + test = "4-4-order-anisotropy-constant"; + test2 = "fourth-order-theta-fourth-order-phi-anisotropy-constant"; + test3 = "fourth-order-rotational-anisotropy-constant"; + if( (word == test) || (word == test2) || (word == test3) ) + { + + double k4r4 = atof(value.c_str()); + vin::check_for_valid_value(k4r4, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r4 = k4r4; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_4_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta fourth order phi rotational anisotropy constant (orthonormal) + test = "4-4-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k4r4 = - atof( value.c_str() ) * ( 3.0 / 16.0 ) * sqrt( 35.0 / M_PI ); + vin::check_for_valid_value(k4r4, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r4 = k4r4; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_4_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta fourth order phi odd anisotropy constant + test = "4--4-order-anisotropy-constant"; + test2 = "fourth-order-theta-fourth-order-phi-odd-anisotropy-constant"; + test3 = "fourth-order-odd-rotational-anisotropy-constant"; + if( (word == test) || (word == test2) || (word == test3) ) + { + + double k4r4_odd = atof(value.c_str()); + vin::check_for_valid_value(k4r4_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r4_odd = k4r4_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_4_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Fourth order theta fourth order phi odd anisotropy constant (orthonormal) + test = "4--4-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k4r4_odd = atof( value.c_str() ) * ( 3.0 / 16.0 ) * sqrt( 35.0 / M_PI ); + vin::check_for_valid_value(k4r4_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k4r4_odd = k4r4_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_4_4_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order uniaxial anisotropy constant + test = "6-0-order-anisotropy-constant"; + test2 = "sixth-order-uniaxial-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double ku6 = atof(value.c_str()); + vin::check_for_valid_value(ku6, word, line, prefix, unit, "energy", -1e-17, 1e-17,"material"," < +/- 1.0e-17 J/atom"); + internal::mp[super_index].ku6 = ku6; + internal::enable_uniaxial_sixth_order = true; // Switch on second order tensor calculation for all spins (from spherical harmonics) + return true; + + } + + // Sixth order uniaxial anisotropy constant (orthonormal) + test = "6-0-orthonormal-tesseral-harmonic-anisotropy-constant"; + if ( word == test ) + { + + double ku6 = - atof( value.c_str() ) * ( 231.0 / 32.0 ) * sqrt( 13.0 / M_PI ); + vin::check_for_valid_value(ku6, word, line, prefix, unit, "energy", -1e-17, 1e-17,"material"," < +/- 1.0e-17 J/atom"); + internal::mp[super_index].ku6 = ku6; + internal::enable_uniaxial_sixth_order = true; // Switch on second order tensor calculation for all spins (from spherical harmonics) + return true; + + } + + // Sixth order theta first order phi anisotropy constant + test = "6-1-order-anisotropy-constant"; + test2 = "sixth-order-theta-first-order-phi-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k6r1 = atof(value.c_str()); + vin::check_for_valid_value(k6r1, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r1 = k6r1; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_1_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta first order phi anisotropy constant (orthonormal) + test = "6-1-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k6r1 = - atof( value.c_str() ) * ( 33.0 / 16.0 ) * sqrt( 273.0 / M_PI ); + vin::check_for_valid_value(k6r1, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r1 = k6r1; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_1_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta first order phi odd anisotropy constant + test = "6--1-order-anisotropy-constant"; + test2 = "sixth-order-theta-first-order-phi-odd-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k6r1_odd = atof(value.c_str()); + vin::check_for_valid_value(k6r1_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r1_odd = k6r1_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_1_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta first order phi odd anisotropy constant (orthonormal) + test = "6--1-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( (word == test) ) + { + + double k6r1_odd = - atof( value.c_str() ) * ( 33.0 / 16.0 ) * sqrt( 273.0 / M_PI ); + vin::check_for_valid_value(k6r1_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r1_odd = k6r1_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_1_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta second order phi anisotropy constant + test = "6-2-order-anisotropy-constant"; + test2 = "sixth-order-theta-second-order-phi-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k6r2 = atof( value.c_str() ); + vin::check_for_valid_value(k6r2, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r2 = k6r2; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_2_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta second order phi anisotropy constant (orthonormal) + test = "6-2-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k6r2 = - atof( value.c_str() ) * ( 33.0 / 64.0 ) * sqrt( 2730.0 / M_PI ); + vin::check_for_valid_value(k6r2, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r2 = k6r2; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_2_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta second order phi odd anisotropy constant + test = "6--2-order-anisotropy-constant"; + test2 = "sixth-order-theta-second-order-phi-odd-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k6r2_odd = atof( value.c_str() ); + vin::check_for_valid_value(k6r2_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r2_odd = k6r2_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_2_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta second order phi odd anisotropy constant (orthonormal) + test = "6--2-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k6r2_odd = - atof( value.c_str() ) * ( 33.0 / 64.0 ) * sqrt( 2730.0 / M_PI ); + vin::check_for_valid_value(k6r2_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r2_odd = k6r2_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_2_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta third order phi anisotropy constant + test = "6-3-order-anisotropy-constant"; + test2 = "sixth-order-theta-third-order-phi-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k6r3 = atof( value.c_str() ); + vin::check_for_valid_value(k6r3, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r3 = k6r3; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_3_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta third order phi anisotropy constant + test = "6-3-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k6r3 = - atof( value.c_str() ) * ( 11.0 / 32.0 ) * sqrt( 2730.0 / M_PI ); + vin::check_for_valid_value(k6r3, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r3 = k6r3; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_3_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta third order phi odd anisotropy constant + test = "6--3-order-anisotropy-constant"; + test2 = "sixth-order-theta-third-order-phi-odd-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k6r3_odd = atof( value.c_str() ); + vin::check_for_valid_value(k6r3_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r3_odd = k6r3_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_3_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta third order phi odd anisotropy constant (orthonormal) + test = "6--3-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k6r3_odd = - atof( value.c_str() ) * ( 11.0 / 32.0 ) * sqrt( 2730.0 / M_PI ); + vin::check_for_valid_value(k6r3_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r3_odd = k6r3_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_3_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta fourth order phi anisotropy constant + test = "6-4-order-anisotropy-constant"; + test2 = "sixth-order-theta-fourth-order-phi-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k6r4 = atof(value.c_str()); + vin::check_for_valid_value(k6r4, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r4 = k6r4; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_4_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta fourth order phi anisotropy constant (orthonormal) + test = "6-4-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k6r4 = - atof( value.c_str() ) * ( 3.0 * 11.0 / 32.0 ) * sqrt( 91.0 / M_PI ); + vin::check_for_valid_value(k6r4, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r4 = k6r4; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_4_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta fourth order phi odd anisotropy constant + test = "6--4-order-anisotropy-constant"; + test2 = "sixth-order-theta-fourth-order-phi-odd-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k6r4_odd = atof( value.c_str() ); + vin::check_for_valid_value(k6r4_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r4_odd = k6r4_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_4_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta fourth order phi odd anisotropy constant (orthonormal) + test = "6--4-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k6r4_odd = - atof( value.c_str() ) * ( 3.0 * 11.0 / 32.0 ) * sqrt( 91.0 / M_PI ); + vin::check_for_valid_value(k6r4_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r4_odd = k6r4_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_4_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta fifth order phi anisotropy constant + test = "6-5-order-anisotropy-constant"; + test2 = "sixth-order-theta-fifth-order-phi-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k6r5 = atof( value.c_str() ); + vin::check_for_valid_value(k6r5, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r5 = k6r5; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_5_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta fifth order phi anisotropy constant (orthonormal) + test = "6-5-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k6r5 = - atof( value.c_str() ) * ( 3.0 / 32.0 ) * sqrt( 2002.0 / M_PI ); + vin::check_for_valid_value(k6r5, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r5 = k6r5; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_5_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sxith order theta fifth order phi odd anisotropy constant + test = "6--5-order-anisotropy-constant"; + test2 = "sixth-order-theta-fifth-order-phi-odd-anisotropy-constant"; + if( (word == test) || (word == test2) ) + { + + double k6r5_odd = atof( value.c_str() ); + vin::check_for_valid_value(k6r5_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r5_odd = k6r5_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_5_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sxith order theta fifth order phi odd anisotropy constant (orthonormal) + test = "6--5-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k6r5_odd = - atof( value.c_str() ) * ( 3.0 / 32.0 ) * sqrt( 2002.0 / M_PI ); + vin::check_for_valid_value(k6r5_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r5_odd = k6r5_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_5_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta sixth order phi anisotropy constant + test = "6-6-order-anisotropy-constant"; + test2 = "sixth-order-theta-sixth-order-phi-anisotropy-constant"; + test3 = "sixth-order-rotational-anisotropy-constant"; + if( (word == test) || (word == test2) || (word == test3) ) + { + + double k6r6 = atof( value.c_str() ); + vin::check_for_valid_value(k6r6, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r6 = k6r6; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_6_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta sixth order phi anisotropy constant (orthonormal) + test = "6-6-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k6r6 = - atof( value.c_str() ) * ( 1.0 / 64.0 ) * sqrt( 6006.0 / M_PI ); + vin::check_for_valid_value(k6r6, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r6 = k6r6; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_6_order = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta sixth order phi odd anisotropy constant + test = "6--6-order-anisotropy-constant"; + test2 = "sixth-order-theta-sixth-order-phi-odd-anisotropy-constant"; + test3 = "sixth-order-odd-rotational-anisotropy-constant"; + if( (word == test) || (word == test2) || (word == test3) ) + { + + double k6r6_odd = atof( value.c_str() ); + vin::check_for_valid_value(k6r6_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r6_odd = k6r6_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_6_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + // Sixth order theta sixth order phi odd anisotropy constant (orthonormal) + test = "6--6-orthonormal-tesseral-harmonic-anisotropy-constant"; + if( word == test ) + { + + double k6r6_odd = - atof( value.c_str() ) * ( 1.0 / 64.0 ) * sqrt( 6006.0 / M_PI ); + vin::check_for_valid_value(k6r6_odd, word, line, prefix, unit, "energy", -1e-17, 1e-17, "material", " < +/-1.0e-17 J/atom"); + internal::mp[super_index].k6r6_odd = k6r6_odd; + internal::mp[super_index].need_rotated_basis = true; + internal::enable_rotational_6_6_order_odd = true; + internal::enable_rotational_anisotropy = true; + return true; + + } + + //------------------------------------------------------------ + // Triaxial anisotropy in second and fourth order test="second-order-triaxial-anisotropy-vector"; if(word == test){ std::vector u(3); @@ -208,7 +1034,6 @@ namespace anisotropy{ } - test="fourth-order-triaxial-basis-vector-1"; if(word == test){ std::vector u(3); @@ -262,16 +1087,7 @@ namespace anisotropy{ } - //------------------------------------------------------------ - //Minimal orthogonality TBD - test = "fourth-order-uniaxial-anisotropy-constant"; - if( word == test ){ - double ku4 = atof(value.c_str()); - vin::check_for_valid_value(ku4, word, line, prefix, unit, "energy", -1e-17, 1e-17,"material"," < +/- 1.0e-17 J/atom"); - internal::mp[super_index].ku4 = ku4; - internal::enable_uniaxial_fourth_order = true; // Switch on second order tensor calculation for all spins (from spherical harmonics) - return true; - } + //------------------------------------------------------------ //Implementation of biaxial fourth-order anisotropy (simple version) test = "fourth-order-biaxial-anisotropy-constant"; @@ -282,17 +1098,7 @@ namespace anisotropy{ internal::enable_biaxial_fourth_order_simple = true; return true; } - //------------------------------------------------------------ - //Minimal orthogonality - test = "sixth-order-uniaxial-anisotropy-constant"; - if( word == test ){ - double ku6 = atof(value.c_str()); - vin::check_for_valid_value(ku6, word, line, prefix, unit, "energy", -1e-17, 1e-17,"material"," < +/- 1.0e-17 J/atom"); - internal::mp[super_index].ku6 = ku6; - internal::enable_uniaxial_sixth_order = true; // Switch on second order tensor calculation for all spins (from spherical harmonics) - return true; - } - //------------------------------------------------------------ + //Minimal orthogonality test = "fourth-order-cubic-anisotropy-constant"; // new form (preferred) test2 = "cubic-anisotropy-constant"; // legacy form (deprecated but probably never obsoleted) @@ -409,6 +1215,20 @@ namespace anisotropy{ } return true; } + //------------------------------------------------------------ + test = "rotational-anisotropy-direction"; + if(word == test){ + // temporary storage container + std::vector r(3); + // read values from string + r = vin::doubles_from_string(value); + // check for sane input and normalise if necessary + vin::check_for_valid_unit_vector(r, word, line, prefix, "material"); + // copy sanitised unit vector to material + internal::mp[super_index].kr_vector = r; + return true; + + } //-------------------------------------- // Direction 1 //-------------------------------------- @@ -446,18 +1266,6 @@ namespace anisotropy{ return true; } //------------------------------------------------------------ - // Fourth order rotational anisotropy - //------------------------------------------------------------ - test = "fourth-order-rotational-anisotropy-constant"; - if(word == test){ - double k4r = atof(value.c_str()); - // Test for valid range - vin::check_for_valid_value(k4r, word, line, prefix, unit, "energy", -1e-17, 1e-17,"material"," < +/- 1.0e-17 J/atom"); - internal::mp[super_index].k4r = k4r; - internal::enable_fourth_order_rotational = true; - return true; - } - //------------------------------------------------------------ /*test="uniaxial-anisotropy-tensor"; if(word==test){ std::vector t; diff --git a/src/anisotropy/internal.hpp b/src/anisotropy/internal.hpp index cba7ee409..62a1f1e13 100644 --- a/src/anisotropy/internal.hpp +++ b/src/anisotropy/internal.hpp @@ -3,7 +3,8 @@ // This file is part of the VAMPIRE open source package under the // Free BSD licence (see licence file for details). // -// (c) Roberto Moreno Ortega, Sam Westmoreland and Richard Evans 2017. All rights reserved. +// (c) Jack Collings, Roberto Moreno Ortega, Sam Westmoreland and +// Richard Evans 2017. All rights reserved. // // Email: richard.evans@york.ac.uk // @@ -44,31 +45,33 @@ namespace anisotropy{ // the class. During variable initialisation interpolating functions // are determined to calculate k(T) // - class lattice_anis_t{ + class lattice_anis_t + { private: unsigned int Tmax; // maximum array value in tabulated function double k_Tmax; // value of anisotropy at k_Tmax (used for all T > Tmax) - std::vector T; // input temperature points - std::vector k; // input lattice anisotropy points - std::vector m; // calculated m value - std::vector c; // calculated c value + std::vector< unsigned int > T; // input temperature points + std::vector< double > k; // input lattice anisotropy points + std::vector< double > m; // calculated m value + std::vector< double > c; // calculated c value public: - void add_point(double, double); + void add_point( double, double ); void set_interpolation_table(); - double get_lattice_anisotropy_constant(double); - void output_interpolated_function(int); + double get_lattice_anisotropy_constant( double ); + void output_interpolated_function( int ); }; //--------------------------------- // struct for storing unit vectors //--------------------------------- - struct evec_t{ + struct evec_t + { double x; double y; double z; @@ -77,37 +80,63 @@ namespace anisotropy{ //----------------------------------------------------------------------------- // materials class for storing anisotropy material parameters //----------------------------------------------------------------------------- - class mp_t{ + class mp_t + { private: public: // variables - double ku2; // second order uniaxial anisotropy constant (Ku1) - double ku4; // fourth order uniaxial anisotropy constant (Ku2) - double ku6; // sixth order uniaxial anisotropy constant (Ku3) + bool need_rotated_basis; // flag to determine if material needs rotational basis + double ku2; // second order uniaxial anisotropy constant ( - Ku1 ) + double k2r1; // second order theta first order phi anisotropy constant + double k2r1_odd; // second order theta first order phi odd anisotropy constant + double k2r2; // second order theta second order phi anisotropy constant + double k2r2_odd; // seconf order theta second order phi odd anisotropy constant + double ku4; // fourth order uniaxial anisotropy constant ( ~Ku2 ) + double k4r1; // fourth order theta first order phi anisotropy constant + double k4r1_odd; // fourth order theta first order phi odd anisotropy constant + double k4r2; // fourth order theta second order phi anisotropy constant + double k4r2_odd; // fourth order theta second order phi odd anisotropy constant + double k4r3; // fourth order theta third order phi odd anisotropy constant + double k4r3_odd; // fourth order theta third order phi odd anisotropy constant + double k4r4; // fourth order theta fourth order phi anisotropy constant + double k4r4_odd; // fourth order theta fourth order phi odd anisotropy constant + double ku6; // sixth order uniaxial anisotropy constant ( ~Ku3 ) + double k6r1; // sixth order theta first order phi anisotropy constant + double k6r1_odd; // sixth order theta first order phi odd anisotropy constant + double k6r2; // sixth order theta second order phi anisotropy constant + double k6r2_odd; // sixth order theta second order phi odd anisotropy constant + double k6r3; // sixth order theta third order phi anisotropy constant + double k6r3_odd; // sixth order theta third order phi odd anisotropy constant + double k6r4; // sixth order theta fourth order phi anisotropy constant + double k6r4_odd; // sixth order theta fourth order phi odd anisotropy constant + double k6r5; // sixth order theta fifth order phi anisotropy constant + double k6r5_odd; // sixth order theta fifth order phi odd anisotropy constant + double k6r6; // sixth order theta sixth order phi anisotropy constant + double k6r6_odd; // sixth order theta sixth order phi odd anisotropy constant double kc4; // fourth order cubic anisotropy constant (Kc1) double kc6; // sixth order cubic anisotropy constant (Kc2) - double k4r; // fourth order rotational anisotropy constant (k4r) - double k_lattice; // uniaxial lattice anisotropy constant - std::vector kij; // surface/Neel anisotropy pair constant + std::vector< double > kij; // surface/Neel anisotropy pair constant - std::vector ku_vector; // unit vector defining axis for uniaxial anisotropy + std::vector< double > ku_vector; // unit vector defining axis for uniaxial anisotropy + std::vector< double > kr_vector; // unit vector defining axis for rotational anisotropy + std::vector< double > kl_vector; // last unit vector perpendicular to kr_vector and ku_vector std::vector u1_vector; // unit vector defining axis for uniaxial anisotropy std::vector u2_vector; // unit vector defining axis for uniaxial anisotropy - std::vector kc_vector1; // first unit vector defining axis for cubic anisotropy - std::vector kc_vector2; // second unit vector defining axis for cubic anisotropy - std::vector kc_vector3; // third unit vector defining axis for cubic anisotropy + std::vector< double > kc_vector1; // first unit vector defining axis for cubic anisotropy + std::vector< double > kc_vector2; // second unit vector defining axis for cubic anisotropy + std::vector< double > kc_vector3; // third unit vector defining axis for cubic anisotropy - std::vector ku_tensor; // uniaxial second order anisotropy tensor - std::vector kc_tensor; // cubic fourth order anisotropy tensor + std::vector< double > ku_tensor; // uniaxial second order anisotropy tensor + std::vector< double > kc_tensor; // cubic fourth order anisotropy tensor lattice_anis_t lattice_anisotropy; // class containing lattice anisotropy data @@ -115,60 +144,100 @@ namespace anisotropy{ bool random_grain_anisotropy; // flag to control random anisotropy by grain // constructor - mp_t (const unsigned int max_materials = 100): - ku2(0.0), // set initial value of ku2 to zero - ku4(0.0), // set initial value of ku4 to zero - ku6(0.0), // set initial value of ku6 to zero - kc4(0.0), // set initial value of kc4 to zero - kc6(0.0), // set initial value of kc6 to zero - k4r(0.0), // set initial value of k4r to zero - k_lattice(0.0), // set initial value of k_lattice to zero - random_anisotropy(false), // disable random anisotropy - random_grain_anisotropy(false) // disable random grain anisotropy + mp_t ( const unsigned int max_materials = 100 ): + need_rotated_basis(false), // in genetral we do not need a rotated basis + ku2( 0.0 ), // set initial value of ku2 to zero + k2r1( 0.0 ), // set initial value of k2r1 to zero + k2r1_odd( 0.0 ), // set initial value of k2r1_odd to zero + k2r2( 0.0 ), // set initial value of k2r2 to zero + k2r2_odd( 0.0 ), // set initial value of k2r2_odd to zero + ku4( 0.0 ), // set initial value of ku4 to zero + k4r1( 0.0 ), // set initial value of k4r1 to zero + k4r1_odd( 0.0 ), // set initial value of k4r1_odd to zero + k4r2( 0.0 ), // set intital value of k4r2 to zero + k4r2_odd( 0.0 ), // set initial value of k4r2_odd to zero + k4r3( 0.0 ), // set initial value of k4r3_odd to zero + k4r3_odd( 0.0 ), // set initial value of k4r3_odd to zero + k4r4( 0.0 ), // set initial value of k4r4 to zero + k4r4_odd( 0.0 ), // set initial value of k4r4_odd to zero + ku6( 0.0 ), // set initial value of ku6 to zero + k6r1( 0.0 ), // set initial value of k6r1 to zero + k6r1_odd( 0.0 ), // set initial value of k6r1_odd to zero + k6r2( 0.0 ), // set initial value of k6r2 to zero + k6r2_odd( 0.0 ), // set initial value of k6r2_odd to zero + k6r3( 0.0 ), // set initial value of k6r3 to zero + k6r3_odd( 0.0 ), // set initial value of k6r3_odd to zero + k6r4( 0.0 ), // set initial value of k6r4 to zero + k6r4_odd( 0.0 ), // set initial value of k6r4_odd to zero + k6r5( 0.0 ), // set initial value of k6r5 to zero + k6r5_odd( 0.0 ), // set initial value of k6r5_odd to zero + k6r6( 0.0 ), // set initial value of k6r6 to zero + k6r6_odd( 0.0 ), // set initial value of k6r6_odd to zero + + kc4( 0.0 ), // set initial value of kc4 to zero + kc6( 0.0 ), // set initial value of kc6 to zero + + k_lattice( 0.0 ), // set initial value of k_lattice to zero + random_anisotropy( false ), // disable random anisotropy + random_grain_anisotropy( false ) // disable random grain anisotropy { // resize arrays to correct size - kij.resize(max_materials, 0.0); // initialise pair anisotropy constants to zero + kij.resize( max_materials, 0.0 ); // initialise pair anisotropy constants to zero // set default uniaxial and cubic directions - ku_vector.resize(3); // resize to three elements + ku_vector.resize( 3 ); // resize to three elements + + ku_vector[ 0 ] = 0.0; // set direction along [0,0,1] + ku_vector[ 1 ] = 0.0; + ku_vector[ 2 ] = 1.0; + + // set default axis from which rotational angle phi is measured + kr_vector.resize( 3 ); // resize to three elements + + kr_vector[ 0 ] = 1.0; // set direction along [1,0,0] + kr_vector[ 1 ] = 0.0; + kr_vector[ 2 ] = 0.0; - ku_vector[0] = 0.0; // set direction along [0,0,1] - ku_vector[1] = 0.0; - ku_vector[2] = 1.0; + // set default (y) axis perpendicular to both ku_vector and kr_vector + kl_vector.resize( 3 ); - const double oneosqrt2 = 1.0/sqrt(2.0); + kl_vector[ 0 ] = 0.0; + kl_vector[ 1 ] = 1.0; + kl_vector[ 2 ] = 0.0; - u1_vector.resize(3); // resize to three elements - u2_vector.resize(3); // resize to three elements + const double oneosqrt2 = 1.0 / sqrt( 2.0 ); - u1_vector[0] = oneosqrt2*1.0; // set direction along [1,1,0] - u1_vector[1] = oneosqrt2*1.0; - u1_vector[2] = 0.0; + u1_vector.resize( 3 ); // resize to three elements + u2_vector.resize( 3 ); // resize to three elements - u2_vector[0] = oneosqrt2*1.0; // set direction along [1,-1,0] - u2_vector[1] = -oneosqrt2*1.0; - u2_vector[2] = 0.0; + u1_vector[ 0 ] = oneosqrt2 * 1.0; // set direction along [1,1,0] + u1_vector[ 1 ] = oneosqrt2 * 1.0; + u1_vector[ 2 ] = 0.0; + + u2_vector[ 0 ] = oneosqrt2 * 1.0; // set direction along [1,-1,0] + u2_vector[ 1 ] = - oneosqrt2 * 1.0; + u2_vector[ 2 ] = 0.0; // set default uniaxial and cubic directions - kc_vector1.resize(3); // resize to three elements - kc_vector2.resize(3); // resize to three elements - kc_vector3.resize(3); // resize to three elements + kc_vector1.resize( 3 ); // resize to three elements + kc_vector2.resize( 3 ); // resize to three elements + kc_vector3.resize( 3 ); // resize to three elements - kc_vector1[0] = 1.0; // set direction alon [1,0,0] - kc_vector1[1] = 0.0; - kc_vector1[2] = 0.0; + kc_vector1[ 0 ] = 1.0; // set direction alon [1,0,0] + kc_vector1[ 1 ] = 0.0; + kc_vector1[ 2 ] = 0.0; - kc_vector2[0] = 0.0; // set direction alon [0,1,0] - kc_vector2[1] = 1.0; - kc_vector2[2] = 0.0; + kc_vector2[ 0 ] = 0.0; // set direction alon [0,1,0] + kc_vector2[ 1 ] = 1.0; + kc_vector2[ 2 ] = 0.0; - kc_vector3[0] = 0.0; // set direction alon [0,0,1] - kc_vector3[1] = 0.0; - kc_vector3[2] = 1.0; + kc_vector3[ 0 ] = 0.0; // set direction alon [0,0,1] + kc_vector3[ 1 ] = 0.0; + kc_vector3[ 2 ] = 1.0; // set tensors as empty by default - ku_tensor.resize(9, 0.0); - kc_tensor.resize(9, 0.0); + ku_tensor.resize( 9, 0.0 ); + kc_tensor.resize( 9, 0.0 ); }; // end of constructor @@ -182,39 +251,94 @@ namespace anisotropy{ extern bool initialised; // check module has been initialised - extern bool enable_uniaxial_second_order; // Flag to enable calculation of second order uniaxial anisotropy - extern bool enable_uniaxial_fourth_order; // Flag to enable calculation of fourth order uniaxial anisotropy + // Flags for tesseral harmonic (or real spherical harmonic) expansion anisotorpy terms + extern bool enable_uniaxial_second_order; // Flag to enable calculation of second order uniaxial anisotropy + extern bool enable_rotational_2_1_order; // Flag to enable calculation of second order theta first order phi anisotropy + extern bool enable_rotational_2_1_order_odd; // Flag to enable calculation of second order theta first order phi odd anisotropy + extern bool enable_rotational_2_2_order; // Flag to enable calculation of second order theta second order phi anisotropy + extern bool enable_rotational_2_2_order_odd; // Flag to enable calculation of second order theta second order phi odd anisotropy + extern bool enable_uniaxial_fourth_order; // Flag to enable calculation of fourth order uniaxial anisotropy + extern bool enable_rotational_4_1_order; // Flag to enable calculation of fourth order theta first order phi anisotropy + extern bool enable_rotational_4_1_order_odd; // Flag to enable calculation of fourth order theta first order phi odd anisotropy + extern bool enable_rotational_4_2_order; // Flag to enable calculation of fourth order theta second order phi anisotropy + extern bool enable_rotational_4_2_order_odd; // Flag to enable calculation of fourth order theta second order phi odd anisotropy + extern bool enable_rotational_4_3_order; // Flag to enable calculation of fourth order theta third order phi anisotropy + extern bool enable_rotational_4_3_order_odd; // Flag to enable calculation of fourth order theta third order phi odd anisotropy + extern bool enable_rotational_4_4_order; // Flag to enable calculation of fourth order theta fourth order phi anisotropy + extern bool enable_rotational_4_4_order_odd; // Flag to enable calculation of fourth order theta fourth order phi odd anisotropy + extern bool enable_uniaxial_sixth_order; // Flag to enable calculation of sixth order uniaxial anisotropy + extern bool enable_rotational_6_1_order; // Flag to enable calculation of sixth order theta first order phi anisotropy + extern bool enable_rotational_6_1_order_odd; // Flag to enable caluclation of sixth order theta first order phi odd anisotropy + extern bool enable_rotational_6_2_order; // Flag to enable calculation of sixth order theta second order phi anisotropy + extern bool enable_rotational_6_2_order_odd; // Flag to enable calculation of sixth order theta second order phi odd anisotropy + extern bool enable_rotational_6_3_order; // Flag to enable caluclation of sixth order theta third order phi anisotropy + extern bool enable_rotational_6_3_order_odd; // Flag to enable calculation of sixth order theta third order phi odd anisotropy + extern bool enable_rotational_6_4_order; // Flag to enable calculation of sixth order theta fourth order phi anisotropy + extern bool enable_rotational_6_4_order_odd; // Flag to enable calculation of sixth order theta fourth order phi odd anisotropy + extern bool enable_rotational_6_5_order; // Flag to enable calculation of sixth order theta fifth order phi anisotropy + extern bool enable_rotational_6_5_order_odd; // Flag to enable calculation of sixth order theta fifth order phi odd anisotropy + extern bool enable_rotational_6_6_order; // Flag to enable calculation of sixth order theta fourth order phi anisotropy + extern bool enable_rotational_6_6_order_odd; // Flag to enable calculation of sixth order theta fourth order phi odd anisotropy + + // Flags for biaxial anisotropy terms extern bool enable_biaxial_fourth_order_simple; // Flag to enable calculation of simplified fourth order biaxial anisotropy - extern bool enable_uniaxial_sixth_order; // Flag to enable calculation of sixth order uniaxial anisotropy + // Flags for cubic anisotropy terms extern bool enable_cubic_fourth_order; // Flag to enable calculation of fourth order cubic anisotropy extern bool enable_cubic_sixth_order; // Flag to enable calculation of sixth order cubic anisotropy extern bool enable_cubic_fourth_order_rotation; // Flag to enable calculation of rotated cubic anisotropy - extern bool enable_fourth_order_rotational; // Flag to enable 4th order rotational anisotropy - + // Flags for triaxial anisotropy extern bool enable_triaxial_anisotropy_rotated; extern bool enable_triaxial_fourth_order_rotated; extern bool enable_triaxial_anisotropy; extern bool enable_triaxial_fourth_order; + // Flags for other anisotropies extern bool enable_neel_anisotropy; // Flag to turn on Neel anisotropy calculation (memory intensive at startup) extern bool enable_lattice_anisotropy; // Flag to turn on lattice anisotropy calculation extern bool enable_random_anisotropy; // Flag to enable random anisitropy initialisation + extern bool enable_rotational_anisotropy; // Flag for CUDA to store rotational data // arrays for storing 1D collapsed Neel tensor - extern std::vector neel_tensor; - - // arrays for storing unrolled anisotropy constants in Tesla - extern std::vector ku2; - extern std::vector ku4; - extern std::vector ku6; - extern std::vector kc4; - extern std::vector kc6; - extern std::vector k4r; + extern std::vector< double > neel_tensor; + + // arrays for storing unrolled spherical-harmonic anisotropy constants in Tesla + extern std::vector< double > ku2; + extern std::vector< double > k2r1; + extern std::vector< double > k2r1_odd; + extern std::vector< double > k2r2; + extern std::vector< double > k2r2_odd; + extern std::vector< double > ku4; + extern std::vector< double > k4r1; + extern std::vector< double > k4r1_odd; + extern std::vector< double > k4r2; + extern std::vector< double > k4r2_odd; + extern std::vector< double > k4r3; + extern std::vector< double > k4r3_odd; + extern std::vector< double > k4r4; + extern std::vector< double > k4r4_odd; + extern std::vector< double > ku6; + extern std::vector< double > k6r1; + extern std::vector< double > k6r1_odd; + extern std::vector< double > k6r2; + extern std::vector< double > k6r2_odd; + extern std::vector< double > k6r3; + extern std::vector< double > k6r3_odd; + extern std::vector< double > k6r4; + extern std::vector< double > k6r4_odd; + extern std::vector< double > k6r5; + extern std::vector< double > k6r5_odd; + extern std::vector< double > k6r6; + extern std::vector< double > k6r6_odd; + + extern std::vector< double > kc4; + extern std::vector< double > kc6; // unrolled arrays for storing easy axes for each material - extern std::vector ku_vector; // 001 easy axis direction + extern std::vector< evec_t > ku_vector; // 001 easy axis direction + extern std::vector< evec_t > kr_vector; // 100 phi = 0 direction + extern std::vector< evec_t > kl_vector; // 010 phi = 90 direction extern bool native_neel_anisotropy_threshold; // enables site-dependent surface threshold extern unsigned int neel_anisotropy_threshold; // global threshold for surface atoms @@ -223,16 +347,16 @@ namespace anisotropy{ extern double neel_exponential_range; // r0 value for range dependence of Neel anisotropy extern double neel_exponential_factor; // F value for range dependence of Neel anisotropy - extern std::vector triaxial_second_order_fixed_basis; - extern std::vector triaxial_fourth_order_fixed_basis; + extern std::vector< bool > triaxial_second_order_fixed_basis; + extern std::vector< bool > triaxial_fourth_order_fixed_basis; - extern std::vector ku_triaxial_vector_x; // unit vector defining axis for uniaxial anisotropy - extern std::vector ku_triaxial_vector_y; // unit vector defining axis for uniaxial anisotropy - extern std::vector ku_triaxial_vector_z; // unit vector defining axis for uniaxial anisotropy + extern std::vector< double > ku_triaxial_vector_x; // unit vector defining axis for uniaxial anisotropy + extern std::vector< double > ku_triaxial_vector_y; // unit vector defining axis for uniaxial anisotropy + extern std::vector< double > ku_triaxial_vector_z; // unit vector defining axis for uniaxial anisotropy - extern std::vector ku4_triaxial_vector_x; // unit vector defining axis for uniaxial anisotropy - extern std::vector ku4_triaxial_vector_y; // unit vector defining axis for uniaxial anisotropy - extern std::vector ku4_triaxial_vector_z; // unit vector defining axis for uniaxial anisotropy + extern std::vector< double > ku4_triaxial_vector_x; // unit vector defining axis for uniaxial anisotropy + extern std::vector< double > ku4_triaxial_vector_y; // unit vector defining axis for uniaxial anisotropy + extern std::vector< double > ku4_triaxial_vector_z; // unit vector defining axis for uniaxial anisotropy //basis vectors for second order triaxial - must be orthogonality extern std::vector < double > ku_triaxial_basis1x; @@ -267,16 +391,278 @@ namespace anisotropy{ //------------------------------------------------------------------------- // internal function declarations //------------------------------------------------------------------------- - void uniaxial_second_order_fields(std::vector& spin_array_x, - std::vector& spin_array_y, - std::vector& spin_array_z, - std::vector& atom_material_array, - std::vector& field_array_x, - std::vector& field_array_y, - std::vector& field_array_z, - const int start_index, - const int end_index); + // Fields + void uniaxial_second_order_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void second_order_theta_first_order_phi_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + + void second_order_theta_first_order_phi_odd_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void second_order_theta_second_order_phi_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void second_order_theta_second_order_phi_odd_fields(std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void uniaxial_fourth_order_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void fourth_order_theta_first_order_phi_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void fourth_order_theta_first_order_phi_odd_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void fourth_order_theta_second_order_phi_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void fourth_order_theta_second_order_phi_odd_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void fourth_order_theta_third_order_phi_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void fourth_order_theta_third_order_phi_odd_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void fourth_order_theta_fourth_order_phi_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void fourth_order_theta_fourth_order_phi_odd_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void uniaxial_sixth_order_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void sixth_order_theta_first_order_phi_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void sixth_order_theta_first_order_phi_odd_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void sixth_order_theta_second_order_phi_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void sixth_order_theta_second_order_phi_odd_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void sixth_order_theta_third_order_phi_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void sixth_order_theta_third_order_phi_odd_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void sixth_order_theta_fourth_order_phi_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void sixth_order_theta_fourth_order_phi_odd_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void sixth_order_theta_fifth_order_phi_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void sixth_order_theta_fifth_order_phi_odd_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void sixth_order_theta_sixth_order_phi_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); + + void sixth_order_theta_sixth_order_phi_odd_fields( std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index ); void triaxial_second_order_fields_fixed_basis(std::vector& spin_array_x, std::vector& spin_array_y, @@ -318,16 +704,6 @@ namespace anisotropy{ const int start_index, const int end_index); - void uniaxial_fourth_order_fields(std::vector& spin_array_x, - std::vector& spin_array_y, - std::vector& spin_array_z, - std::vector& atom_material_array, - std::vector& field_array_x, - std::vector& field_array_y, - std::vector& field_array_z, - const int start_index, - const int end_index); - void biaxial_fourth_order_simple_fields(std::vector& spin_array_x, std::vector& spin_array_y, std::vector& spin_array_z, @@ -338,26 +714,6 @@ namespace anisotropy{ const int start_index, const int end_index); - void uniaxial_sixth_order_fields( std::vector& spin_array_x, - std::vector& spin_array_y, - std::vector& spin_array_z, - std::vector& atom_material_array, - std::vector& field_array_x, - std::vector& field_array_y, - std::vector& field_array_z, - const int start_index, - const int end_index); - - void rotational_fourth_order_fields_fixed_basis( std::vector& spin_array_x, - std::vector& spin_array_y, - std::vector& spin_array_z, - std::vector& atom_material_array, - std::vector& field_array_x, - std::vector& field_array_y, - std::vector& field_array_z, - const int start_index, - const int end_index); - void cubic_fourth_order_fields(std::vector& spin_array_x, std::vector& spin_array_y, std::vector& spin_array_z, @@ -409,11 +765,169 @@ namespace anisotropy{ const int end_index, const double temperature); - double uniaxial_second_order_energy( const int atom, - const int mat, - const double sx, - const double sy, - const double sz); + // Energies + + double uniaxial_second_order_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double second_order_theta_first_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double second_order_theta_first_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double second_order_theta_second_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double second_order_theta_second_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double uniaxial_fourth_order_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double fourth_order_theta_first_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double fourth_order_theta_first_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double fourth_order_theta_second_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double fourth_order_theta_second_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double fourth_order_theta_third_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double fourth_order_theta_third_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double fourth_order_theta_fourth_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double fourth_order_theta_fourth_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double uniaxial_sixth_order_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz); + + double sixth_order_theta_first_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double sixth_order_theta_first_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double sixth_order_theta_second_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double sixth_order_theta_second_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double sixth_order_theta_third_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double sixth_order_theta_third_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double sixth_order_theta_fourth_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double sixth_order_theta_fourth_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double sixth_order_theta_fifth_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double sixth_order_theta_fifth_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double sixth_order_theta_sixth_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); + + double sixth_order_theta_sixth_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ); double triaxial_second_order_energy_fixed_basis(const int atom, const int mat, @@ -439,31 +953,12 @@ namespace anisotropy{ const double sy, const double sz); - - double uniaxial_fourth_order_energy(const int atom, - const int mat, - const double sx, - const double sy, - const double sz); - double biaxial_fourth_order_simple_energy(const int atom, const int mat, const double sx, const double sy, const double sz); - double uniaxial_sixth_order_energy( const int atom, - const int mat, - const double sx, - const double sy, - const double sz); - - double rotational_fourth_order_energy_fixed_basis( const int atom, - const int mat, - const double sx, - const double sy, - const double sz); - double cubic_fourth_order_energy(const int atom, const int mat, const double sx, @@ -503,8 +998,11 @@ namespace anisotropy{ //------------------------------------------------------------------------- // simple inline function to convert atom,i,j into 1D tensor coordinates //------------------------------------------------------------------------- - inline unsigned int index(const unsigned int atom, const unsigned int i, const unsigned int j){ - return 9*atom + 3*i + j; + inline unsigned int index( const unsigned int atom, const unsigned int i, const unsigned int j ) + { + + return 9 * atom + 3 * i + j; + } } // end of anisotropy namespace diff --git a/src/anisotropy/makefile b/src/anisotropy/makefile index 938e2d46a..963876e21 100644 --- a/src/anisotropy/makefile +++ b/src/anisotropy/makefile @@ -1,4 +1,4 @@ -#---#-------------------------------------------------------------- +#-------------------------------------------------------------- # Makefile for anisotropy module #-------------------------------------------------------------- @@ -17,11 +17,34 @@ initialize_neel.o \ interface.o \ lattice.o \ neel.o \ -rotational_fixed_basis.o \ -uniaxial_second_order.o \ -uniaxial_fourth_order.o \ +uniaxial_order_2.o \ +rotational_order_2_1.o\ +rotational_order_2_1_odd.o\ +rotational_order_2_2.o \ +rotational_order_2_2_odd.o\ +uniaxial_order_4.o \ +rotational_order_4_1.o\ +rotational_order_4_1_odd.o\ +rotational_order_4_2.o \ +rotational_order_4_2_odd.o\ +rotational_order_4_3.o\ +rotational_order_4_3_odd.o\ +rotational_order_4_4.o \ +rotational_order_4_4_odd.o\ +uniaxial_order_6.o \ +rotational_order_6_1.o\ +rotational_order_6_1_odd.o\ +rotational_order_6_2.o \ +rotational_order_6_2_odd.o\ +rotational_order_6_3.o\ +rotational_order_6_3_odd.o\ +rotational_order_6_4.o \ +rotational_order_6_4_odd.o\ +rotational_order_6_5.o\ +rotational_order_6_5_odd.o\ +rotational_order_6_6.o \ +rotational_order_6_6_odd.o\ biaxial_fourth_order_simple.o \ -uniaxial_sixth_order.o \ triaxial_fixed_basis.o \ triaxial_rotational.o diff --git a/src/anisotropy/rotational_fixed_basis.cpp b/src/anisotropy/rotational_fixed_basis.cpp deleted file mode 100644 index 9affbe4d8..000000000 --- a/src/anisotropy/rotational_fixed_basis.cpp +++ /dev/null @@ -1,113 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This file is part of the VAMPIRE open source package under the -// Free BSD licence (see licence file for details). -// -// (c) Sarah Jenkins and Richard Evans 2020. All rights reserved. -// -// Email: sarah.jenkins@york.ac.uk richard.evans@york.ac.uk -// -//------------------------------------------------------------------------------ -// - -// C++ standard library headers - -// Vampire headers -#include "anisotropy.hpp" - -// anisotropy module headers -#include "internal.hpp" - -namespace anisotropy{ - - //------------------------------------------------------------------------------ - // Externally visible variables - //------------------------------------------------------------------------------ - - namespace internal{ - - //--------------------------------------------------------------------------------- - // Function to add fourth order rotational anisotropy of the form - // - // E_4r = sin^3 theta cos (4 phi) - // - // In cartesian coordinates this expands to - // - // E_4r = 1 + Sz^4 - 8Sx^2 + 8Sx^2Sz^2 + 8Sx^4 - 2Sz^2 - // = 1 + Sz^4 - 8Sy^2 + 8Sy^2Sz^2 + 8Sy^4 - 2Sz^2 - // E_4r = 1 - 8*Sx^2 + 8*Sx^4 - - // The associated internal field (-dE/dS) is then - // - // Hx = 16 Sx (1 - Sz^2 - 2Sx^2) - // Hy = 16 Sy (1 - Sz^2 - 2Sy^2) - // Hz = 4 Sz (1 - Sz^2 - 4Sx^2) - //-------------------------------------------------------------------------------------------------------------- - void rotational_fourth_order_fields_fixed_basis(std::vector& spin_array_x, - std::vector& spin_array_y, - std::vector& spin_array_z, - std::vector& atom_material_array, - std::vector& field_array_x, - std::vector& field_array_y, - std::vector& field_array_z, - const int start_index, - const int end_index){ - - //if not enabled then do nothing - if(!internal::enable_fourth_order_rotational) return; - - // Loop over all atoms between start and end index - for(int atom = start_index; atom < end_index; atom++){ - - // get atom material - const int mat = atom_material_array[atom]; - - const double sx = spin_array_x[atom]; // store spin direction in temporary variables - const double sy = spin_array_y[atom]; - const double sz = spin_array_z[atom]; - - // get reduced anisotropy constant ku/mu_s - const double k4r = internal::k4r[mat]; - - const double sx2 = sx*sx; - const double sy2 = sy*sy; - const double sz2 = sz*sz; - - field_array_x[atom] += k4r * 8.0 * sx * (1.0 - sz2 - 2.0 * sx2); - field_array_y[atom] += k4r * 8.0 * sy * (1.0 - sz2 - 2.0 * sy2); - field_array_z[atom] += k4r * 2.0 * sz * (1.0 - 2.0 * sz2 - 4.0 * sx2 - 4.0 * sy2); - - } - - return; - - } - - //--------------------------------------------------------------------------------- - // Function to add second order uniaxial anisotropy in x,y and z - // E = 2/3 * - ku2 (1/2) * (3sz^2 - 1) == -ku2 sz^2 + const - //--------------------------------------------------------------------------------- - double rotational_fourth_order_energy_fixed_basis( - const int atom, - const int mat, - const double sx, - const double sy, - const double sz){ - - // get reduced anisotropy constant ku/mu_s - const double k4r = internal::k4r[mat]; - - const double sx2 = sx*sx; - const double sz2 = sz*sz; - const double sx4 = sx2*sx2; - const double sz4 = sz2*sz2; - - const double energy = k4r*(1.0 + sz4 - 8.0*sx2 + 8.0*sx2*sz2 + 8.0*sx4-2.0*sz2); - - return energy; - - } - - } // end of internal namespace - -} // end of anisotropy namespace diff --git a/src/anisotropy/rotational_order_2_1.cpp b/src/anisotropy/rotational_order_2_1.cpp new file mode 100644 index 000000000..da2643a39 --- /dev/null +++ b/src/anisotropy/rotational_order_2_1.cpp @@ -0,0 +1,141 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{2r1} = -k_{2r1}cos{theta}sin{theta}cos{phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + void second_order_theta_first_order_phi_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_2_1_order ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // get atom material + const int mat = atom_material_array[ atom ]; + + // store spin direction in temporary variables + const double sx = spin_array_x[ atom ]; + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + // calculate S_y + const double Sx = sx * fx + sy * fy + sz * fz; + + // calculate S_z + const double Sz = sx * ex + sy * ey + sz * ez; + + // get reduced anisotropy constant ku/mu_s + const double k2r1 = internal::k2r1[ mat ]; + + // calculate field terms + const double x_component = k2r1 * Sz; + const double z_component = k2r1 * Sx; + + // sum in components of field + field_array_x[atom] += x_component * fx + z_component * ex; + field_array_y[atom] += x_component * fy + z_component * ey; + field_array_z[atom] += x_component * fz + z_component * ez; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 2-theta-1-phi odd anisotropy + //--------------------------------------------------------------------------------- + + double second_order_theta_first_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + // calculate cos{theta}sin{theta}sin{phi} + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sx = sx * fx + sy * fy + sz * fz; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k2r1 = internal::k2r1[ mat ]; + + return - k2r1 * Sx * Sz; + + } + + } + +} diff --git a/src/anisotropy/rotational_order_2_1_odd.cpp b/src/anisotropy/rotational_order_2_1_odd.cpp new file mode 100644 index 000000000..56d3e674f --- /dev/null +++ b/src/anisotropy/rotational_order_2_1_odd.cpp @@ -0,0 +1,141 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{2r-1} = -k_{2r-1}cos{theta}sin{theta}sin{phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + void second_order_theta_first_order_phi_odd_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_2_1_order_odd ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // get atom material + const int mat = atom_material_array[ atom ]; + + // store spin direction in temporary variables + const double sx = spin_array_x[ atom ]; + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate S_y + const double Sy = sx * gx + sy * gy + sz * gz; + + // calculate S_z + const double Sz = sx * ex + sy * ey + sz * ez; + + // get reduced anisotropy constant ku/mu_s + const double k2r1_odd = internal::k2r1_odd[ mat ]; + + // calculate field terms + const double y_component = k2r1_odd * Sz; + const double z_component = k2r1_odd * Sy; + + // sum in components of field + field_array_x[atom] += y_component * gx + z_component * ex; + field_array_y[atom] += y_component * gy + z_component * ey; + field_array_z[atom] += y_component * gz + z_component * ez; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 2-theta-1-phi odd anisotropy + //--------------------------------------------------------------------------------- + + double second_order_theta_first_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate cos{theta}sin{theta}sin{phi} + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sy = sx * gx + sy * gy + sz * gz; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k2r1_odd = internal::k2r1_odd[ mat ]; + + return - k2r1_odd * Sy * Sz; + + } + + } + +} diff --git a/src/anisotropy/rotational_order_2_2.cpp b/src/anisotropy/rotational_order_2_2.cpp new file mode 100644 index 000000000..e1ce20093 --- /dev/null +++ b/src/anisotropy/rotational_order_2_2.cpp @@ -0,0 +1,143 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings and Richard Evans 2022. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{22} = -k_{22}sin^2{theta}cos{2phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis and is detailed in an as yet unpublished paper. + // + //-------------------------------------------------------------------------------------------------------------- + + // Define useful constants + const double two = 2.0; + + void second_order_theta_second_order_phi_fields(std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index){ + + // if not enabled then do nothing + if(!internal::enable_rotational_2_2_order) return; + + // Loop over all atoms between start and end index + for(int atom = start_index; atom < end_index; ++atom){ + + // get atom material + const int mat = atom_material_array[atom]; + + const double sx = spin_array_x[atom]; // store spin direction in temporary variables + const double sy = spin_array_y[atom]; + const double sz = spin_array_z[atom]; + + const double fx = internal::kr_vector[mat].x; + const double fy = internal::kr_vector[mat].y; + const double fz = internal::kr_vector[mat].z; + + const double gx = internal::kl_vector[mat].x; + const double gy = internal::kl_vector[mat].y; + const double gz = internal::kl_vector[mat].z; + + // calculate S_x + const double Sx = sx * fx + sy * fy + sz * fz; + + // calculate S_y + const double Sy = sx * gx + sy * gy + sz * gz; + + // get reduced anisotropy constant ku/mu_s + const double twok2r2 = two * internal::k2r2[mat]; + + // calculate field terms + const double full_Sx = twok2r2 * Sx; + const double full_Sy = twok2r2 * Sy; + + // sum x-component of field, where x-direction is represented by fx, fy, fz + field_array_x[atom] += full_Sx * fx; + field_array_y[atom] += full_Sx * fy; + field_array_z[atom] += full_Sx * fz; + + // sum y-component of field, where y-direction is represented by gx, gy, gz + field_array_x[atom] -= full_Sy * gx; + field_array_y[atom] -= full_Sy * gy; + field_array_z[atom] -= full_Sy * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 2-theta-2-phi anisotropy + //--------------------------------------------------------------------------------- + + double second_order_theta_second_order_phi_energy(const int atom, + const int mat, + const double sx, + const double sy, + const double sz){ + + const double fx = internal::kr_vector[mat].x; + const double fy = internal::kr_vector[mat].y; + const double fz = internal::kr_vector[mat].z; + + const double gx = internal::kl_vector[mat].x; + const double gy = internal::kl_vector[mat].y; + const double gz = internal::kl_vector[mat].z; + + // calculate sin^2{theta}cos{2phi} = sin^2{theta}cos^2{phi} - sin^2{theta}sin^2{phi} = S_x^2 - S_y^2 + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sy = sx * gx + sy * gy + sz * gz; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k2r2 = internal::k2r2[mat]; + + return - k2r2 * (Sx * Sx - Sy * Sy); + + } + } +} diff --git a/src/anisotropy/rotational_order_2_2_odd.cpp b/src/anisotropy/rotational_order_2_2_odd.cpp new file mode 100644 index 000000000..df60187a8 --- /dev/null +++ b/src/anisotropy/rotational_order_2_2_odd.cpp @@ -0,0 +1,144 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{2r-2} = -k_{2r-2}sin^2{theta}sin{2phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + // Define useful constants + const double two = 2.0; + + void second_order_theta_second_order_phi_odd_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_2_2_order_odd ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // get atom material + const int mat = atom_material_array[ atom ]; + + // store spin direction in temporary variables + const double sx = spin_array_x[ atom ]; + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate S_x + const double Sx = sx * fx + sy * fy + sz * fz; + + // calculate S_y + const double Sy = sx * gx + sy * gy + sz * gz; + + // get reduced anisotropy constant ku/mu_s + const double two_k2r2_odd = two * internal::k2r2_odd[mat]; + + // calculate field terms + const double x_component = two_k2r2_odd * Sy; + const double y_component = two_k2r2_odd * Sx; + + // sum in components of field + field_array_x[atom] += x_component * fx + y_component * gx; + field_array_y[atom] += x_component * fy + y_component * gy; + field_array_z[atom] += x_component * fz + y_component * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 2-theta-2-phi odd anisotropy + //--------------------------------------------------------------------------------- + + double second_order_theta_second_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate sin^2{theta}sin{2phi} = 2sin^2{theta}sin{phi}cos{phi} = 2 * S_x * S_y + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sy = sx * gx + sy * gy + sz * gz; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double two_k2r2_odd = two * internal::k2r2_odd[ mat ]; + + return - two_k2r2_odd * Sx * Sy; + + } + + } + +} diff --git a/src/anisotropy/rotational_order_4_1.cpp b/src/anisotropy/rotational_order_4_1.cpp new file mode 100644 index 000000000..642ac9690 --- /dev/null +++ b/src/anisotropy/rotational_order_4_1.cpp @@ -0,0 +1,146 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{4r1} = -k_{4r1}sin{theta}cos{phi}(cos^3{theta} - (3/7)cos{theta}) + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + //Define useful constants + const double three = 3.0; + const double threeoseven = 3.0 / 7.0; + + void fourth_order_theta_first_order_phi_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_4_1_order ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // store spin direction in temporary variables + const double sx = spin_array_x[ atom ]; + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + // calculate S_z and S_z^2 + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sz2 = Sz * Sz; + + // calculate S_y and S_y^3 parts + const double Sx = sx * fx + sy * fy + sz * fz; + + // get reduced anisotropy constant ku/mu_s + const double k4r1 = internal::k4r1[ mat ]; + + // calculate full form to add to field + const double x_component = k4r1 * Sz * ( Sz2 - threeoseven ); + const double z_component = k4r1 * Sx * ( three * Sz2 - threeoseven ); + + // Sum components into field + field_array_x[ atom ] += x_component * fx + z_component * ex; + field_array_y[ atom ] += x_component * fy + z_component * ey; + field_array_z[ atom ] += x_component * fz + z_component * ez; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 4-theta-1-phi anisotropy + //--------------------------------------------------------------------------------- + double fourth_order_theta_first_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k4r1 = internal::k4r1[ mat ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + // Calculate - k{4r1} sin{theta}cos{phi}(cos^3{theta} - (3/7)cos{theta}) = - k{4r1} * Sx * Sz * ( Sz^2 - 3 / 7 ) + const double Sx = sx * fx + sy * fy + sz * fz; + + const double Sz = sx * ex + sy * ey + sz * ez; + + return - k4r1 * Sx * Sz * ( Sz * Sz - threeoseven ); + + } + + } + +} diff --git a/src/anisotropy/rotational_order_4_1_odd.cpp b/src/anisotropy/rotational_order_4_1_odd.cpp new file mode 100644 index 000000000..46f8ac98b --- /dev/null +++ b/src/anisotropy/rotational_order_4_1_odd.cpp @@ -0,0 +1,146 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{4r-1} = -k_{4r-1}sin{theta}sin{phi}(cos^3{theta} - (3/7)cos{theta}) + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + //Define useful constants + const double three = 3.0; + const double threeoseven = 3.0 / 7.0; + + void fourth_order_theta_first_order_phi_odd_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_4_1_order_odd ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // store spin direction in temporary variables + const double sx = spin_array_x[ atom ]; + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate S_z and S_z^2 + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sz2 = Sz * Sz; + + // calculate S_y and S_y^3 parts + const double Sy = sx * gx + sy * gy + sz * gz; + + // get reduced anisotropy constant ku/mu_s + const double k4r1_odd = internal::k4r1_odd[ mat ]; + + // calculate full form to add to field + const double y_component = k4r1_odd * Sz * ( Sz2 - threeoseven ); + const double z_component = k4r1_odd * Sy * ( three * Sz2 - threeoseven ); + + // Sum components into field + field_array_x[ atom ] += y_component * gx + z_component * ex; + field_array_y[ atom ] += y_component * gy + z_component * ey; + field_array_z[ atom ] += y_component * gz + z_component * ez; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 4-theta-1-phi-odd anisotropy + //--------------------------------------------------------------------------------- + double fourth_order_theta_first_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k4r1_odd = internal::k4r1_odd[ mat ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // Calculate - k{4r-1} sin{theta}sin{phi}(cos^3{theta} - (3/7)cos{theta}) = - k{4r-1} * Sy * ( Sz^3 - ( 3 / 7 ) * Sz ) + const double Sy = sx * gx + sy * gy + sz * gz; + + const double Sz = sx * ex + sy * ey + sz * ez; + + return - k4r1_odd * Sy * Sz * ( Sz * Sz - threeoseven ); + + } + + } + +} diff --git a/src/anisotropy/rotational_order_4_2.cpp b/src/anisotropy/rotational_order_4_2.cpp new file mode 100644 index 000000000..d464c11f0 --- /dev/null +++ b/src/anisotropy/rotational_order_4_2.cpp @@ -0,0 +1,156 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings and Richard Evans 2022. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{42} = -k_{42}sin^2{theta}(cos^2{theta} - 1/7)cos{2phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis and is detailed in an as yet unpublished paper. + // + //-------------------------------------------------------------------------------------------------------------- + + // Define useful constants + const double twelve_o_seven = 12.0/7.0; + const double four = 4.0; + + void fourth_order_theta_second_order_phi_fields(std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index){ + + // if not enabled then do nothing + if(!internal::enable_rotational_4_2_order) return; + + // Loop over all atoms between start and end index + for(int atom = start_index; atom < end_index; ++atom){ + + // get atom material + const int mat = atom_material_array[atom]; + + const double sx = spin_array_x[atom]; // store spin direction in temporary variables + const double sy = spin_array_y[atom]; + const double sz = spin_array_z[atom]; + + const double fx = internal::kr_vector[mat].x; + const double fy = internal::kr_vector[mat].y; + const double fz = internal::kr_vector[mat].z; + + const double gx = internal::kl_vector[mat].x; + const double gy = internal::kl_vector[mat].y; + const double gz = internal::kl_vector[mat].z; + + // calculate S_x and S_x^3 parts + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sxpart = twelve_o_seven * Sx; + + const double Sx3part = four * Sx * Sx * Sx; + + // calculate S_y and S_y^3 parts + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sypart = twelve_o_seven * Sy; + + const double Sy3part = four * Sy * Sy * Sy; + + // get reduced anisotropy constant ku/mu_s + const double k4r2 = internal::k4r2[mat]; + + // calculate full form to add to field + const double fullSx = k4r2 * (Sx3part - Sxpart); + const double fullSy = k4r2 * (Sy3part - Sypart); + + field_array_x[atom] += - fullSx * fx + fullSy * gx; + field_array_y[atom] += - fullSx * fy + fullSy * gy; + field_array_z[atom] += - fullSx * fz + fullSy * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 4-theta-2-phi anisotropy + //--------------------------------------------------------------------------------- + + const double one_o_seven = 1.0 / 7.0; + + double fourth_order_theta_second_order_phi_energy(const int atom, + const int mat, + const double sx, + const double sy, + const double sz){ + + const double ex = internal::ku_vector[mat].x; + const double ey = internal::ku_vector[mat].y; + const double ez = internal::ku_vector[mat].z; + + const double fx = internal::kr_vector[mat].x; + const double fy = internal::kr_vector[mat].y; + const double fz = internal::kr_vector[mat].z; + + const double gx = internal::kl_vector[mat].x; + const double gy = internal::kl_vector[mat].y; + const double gz = internal::kl_vector[mat].z; + + // calculate cos^2{theta} = S_z + const double costheta = sx * ex + sy * ey + sz * ez; + const double costheta2 = costheta * costheta; + + // calculate sin^2{theta}cos{2phi} = sin^2{theta}cos^2{phi} - sin^2{theta}sin^2{phi} + // = S_x^2 - S_y^2 + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sy = sx * gx + sy * gy + sz * gz; + const double sintheta2cos2phi = Sx * Sx - Sy * Sy; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k4r2 = internal::k4r2[mat]; + + return - k4r2 * (costheta2 - one_o_seven) * sintheta2cos2phi; + + } + } +} diff --git a/src/anisotropy/rotational_order_4_2_odd.cpp b/src/anisotropy/rotational_order_4_2_odd.cpp new file mode 100644 index 000000000..5fb35de15 --- /dev/null +++ b/src/anisotropy/rotational_order_4_2_odd.cpp @@ -0,0 +1,151 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{4r-2} = - k_{4r-2}sin^2{theta}(cos^2{theta} - 1/7)sin{2phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + // Define useful constants + const double six_o_seven = 6.0 / 7.0; + const double two = 2.0; + const double three = 3.0; + + void fourth_order_theta_second_order_phi_odd_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_4_2_order_odd ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // Get atom material + const int mat = atom_material_array[ atom ]; + + // Store spin direction in temporary variables + const double sx = spin_array_x[ atom ]; + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // Calculate S_x and S_x^2 parts + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + // Calculate S_y and S_y^2 parts + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + // Get reduced anisotropy constant ku / mu_s + const double two_k4r2_odd = two * internal::k4r2_odd[ mat ]; + + // Calculate full form to add to field + const double x_component = two_k4r2_odd * Sy * ( six_o_seven - three * Sx2 - Sy2 ); + const double y_component = two_k4r2_odd * Sx * ( six_o_seven - three * Sy2 - Sx2 ); + + field_array_x[ atom ] += x_component * fx + y_component * gx; + field_array_y[ atom ] += x_component * fy + y_component * gy; + field_array_z[ atom ] += x_component * fz + y_component * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 4-theta-2-phi odd anisotropy + //--------------------------------------------------------------------------------- + + double fourth_order_theta_second_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // Calculate Sx and its square + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + // Calculate Sy and its square + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double two_k4r2_odd = two * internal::k4r2_odd[ mat ]; + + return two_k4r2_odd * Sx * Sy * ( Sx2 + Sy2 - six_o_seven ); + + } + + } + +} diff --git a/src/anisotropy/rotational_order_4_3.cpp b/src/anisotropy/rotational_order_4_3.cpp new file mode 100644 index 000000000..2c80283f5 --- /dev/null +++ b/src/anisotropy/rotational_order_4_3.cpp @@ -0,0 +1,161 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{4r3} = -k_{4r3}sin^3{theta}cos{theta}cos{3phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + //Define useful constants + const double three = 3.0; + const double six = 6.0; + + void fourth_order_theta_third_order_phi_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_4_3_order ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // store spin direction in temporary variables + const double sx = spin_array_x[ atom ]; + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate S_z + const double Sz = sx * ex + sy * ey + sz * ez; + + // calculate S_x and S_x^3 parts + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + // calculate S_y and S_y^3 parts + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + // get reduced anisotropy constant ku/mu_s + const double k4r3 = internal::k4r3[ mat ]; + + // calculate full form to add to field + const double x_component = k4r3 * three * Sz * ( Sx2 - Sy2 ); + const double y_component = - k4r3 * six * Sx * Sy * Sz; + const double z_component = k4r3 * Sx * ( Sx2 - three * Sy2 ); + + // Sum components into field + field_array_x[ atom ] += x_component * fx + y_component * gx + z_component * ex; + field_array_y[ atom ] += x_component * fy + y_component * gy + z_component * ey; + field_array_z[ atom ] += x_component * fz + y_component * gz + z_component * ez; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 4-theta-3-phi anisotropy + //--------------------------------------------------------------------------------- + double fourth_order_theta_third_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k4r3 = internal::k4r3[ mat ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // Calculate - k{4r3} sin^3{theta}cos{theta}cos{3phi} = - k{4r3} * Sx * Sz ( Sx^2 - 3 * Sy^2 ) + const double Sx = sx * fx + sy * fy + sz * fz; + + const double Sy = sx * gx + sy * gy + sz * gz; + + const double Sz = sx * ex + sy * ey + sz * ez; + + return - k4r3 * Sx * Sz * ( Sx * Sx - three * Sy * Sy ); + + } + + } + +} diff --git a/src/anisotropy/rotational_order_4_3_odd.cpp b/src/anisotropy/rotational_order_4_3_odd.cpp new file mode 100644 index 000000000..2378f4949 --- /dev/null +++ b/src/anisotropy/rotational_order_4_3_odd.cpp @@ -0,0 +1,162 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{4r-3} = -k_{4r-3}sin^3{theta}cos{theta}sin{3phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + //Define useful constants + const double three = 3.0; + const double six = 6.0; + + void fourth_order_theta_third_order_phi_odd_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_4_3_order_odd ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // store spin direction in temporary variables + const double sx = spin_array_x[ atom ]; + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate S_z + const double Sz = sx * ex + sy * ey + sz * ez; + + // calculate S_x and S_x^3 parts + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + // calculate S_y and S_y^3 parts + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + // get reduced anisotropy constant ku/mu_s + const double k4r3_odd = internal::k4r3_odd[ mat ]; + + // calculate full form to add to field + const double x_component = k4r3_odd * six * Sx * Sy * Sz; + const double y_component = k4r3_odd * three * Sz * ( Sx2 - Sy2 ); + const double z_component = k4r3_odd * Sy * ( three * Sx2 - Sy2 ); + + // Sum components into field + field_array_x[ atom ] += x_component * fx + y_component * gx + z_component * ex; + field_array_y[ atom ] += x_component * fy + y_component * gy + z_component * ey; + field_array_z[ atom ] += x_component * fz + y_component * gz + z_component * ez; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 4-theta-3-phi-odd anisotropy + //--------------------------------------------------------------------------------- + double fourth_order_theta_third_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k4r3_odd = internal::k4r3_odd[ mat ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // Calculate - k{4r-3} sin^3{theta}cos{theta}sin{3phi} = - k{4r-3} * Sy * Sz ( 3 * ( Sx^2 + Sy^2 ) - 4 * Sy^2 ) + // = 4 * Sx^3 * S_y - 4 * S_x * S_y^3 + const double Sx = sx * fx + sy * fy + sz * fz; + + const double Sy = sx * gx + sy * gy + sz * gz; + + const double Sz = sx * ex + sy * ey + sz * ez; + + return - k4r3_odd * Sy * Sz * ( three * Sx * Sx - Sy * Sy ); + + } + + } + +} diff --git a/src/anisotropy/rotational_order_4_4.cpp b/src/anisotropy/rotational_order_4_4.cpp new file mode 100644 index 000000000..20aa1b44b --- /dev/null +++ b/src/anisotropy/rotational_order_4_4.cpp @@ -0,0 +1,149 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings and Richard Evans 2022. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{44} = -k_{4r4}sin^4{theta}cos{4phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis and is detailed in an as yet unpublished paper. + // + //-------------------------------------------------------------------------------------------------------------- + //Define useful constants + const double four = 4.0; + void fourth_order_theta_fourth_order_phi_fields(std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index){ + + // if not enabled then do nothing + if(!internal::enable_rotational_4_4_order) return; + + // Loop over all atoms between start and end index + for(int atom = start_index; atom < end_index; ++atom){ + + const double sx = spin_array_x[atom]; // store spin direction in temporary variables + const double sy = spin_array_y[atom]; + const double sz = spin_array_z[atom]; + + // get atom material + const int mat = atom_material_array[atom]; + + const double fx = internal::kr_vector[mat].x; + const double fy = internal::kr_vector[mat].y; + const double fz = internal::kr_vector[mat].z; + + const double gx = internal::kl_vector[mat].x; + const double gy = internal::kl_vector[mat].y; + const double gz = internal::kl_vector[mat].z; + + // calculate S_x and S_x^3 parts + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + // calculate S_y and S_y^3 parts + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + // get reduced anisotropy constant ku/mu_s + const double four_k4r4 = four * internal::k4r4[mat]; + + // calculate full form to add to field + const double fullx = four_k4r4 * Sx * (Sx2 - 3 * Sy2); + const double fully = four_k4r4 * Sy * (Sy2 - 3 * Sx2); + + field_array_x[atom] += fullx * fx; + field_array_y[atom] += fullx * fy; + field_array_z[atom] += fullx * fz; + + // sum y-component of field, where y-direction is represented by gx, gy, gz + field_array_x[atom] += fully * gx; + field_array_y[atom] += fully * gy; + field_array_z[atom] += fully * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 6-theta-2-phi anisotropy + //--------------------------------------------------------------------------------- + // Define useful constants + const double six = 6.0; + + double fourth_order_theta_fourth_order_phi_energy(const int atom, + const int mat, + const double sx, + const double sy, + const double sz){ + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k4r4 = internal::k4r4[mat]; + + const double fx = internal::kr_vector[mat].x; + const double fy = internal::kr_vector[mat].y; + const double fz = internal::kr_vector[mat].z; + + const double gx = internal::kl_vector[mat].x; + const double gy = internal::kl_vector[mat].y; + const double gz = internal::kl_vector[mat].z; + + // calculate sin^4{theta}cos{4phi} = sin^4{theta} * ( 8 * cos^4{phi} - 8 * cos^2{phi} + 1 ) + // = 8 * Sx^4 - 8 * sin^2{theta} * Sx^2 + sin^4{theta} + // = Sx^4 - 6 Sx^2 * Sy^2 + Sy^4 + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + return - k4r4 * (Sx2 * Sx2 - six * Sx2 * Sy2 + Sy2 * Sy2 ); + + } + } +} diff --git a/src/anisotropy/rotational_order_4_4_odd.cpp b/src/anisotropy/rotational_order_4_4_odd.cpp new file mode 100644 index 000000000..2b0ddaa65 --- /dev/null +++ b/src/anisotropy/rotational_order_4_4_odd.cpp @@ -0,0 +1,150 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{4,-4} = -k_{4r-4}sin^4{theta}sin{4phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + //Define useful constants + const double four = 4.0; + const double three = 3.0; + + void fourth_order_theta_fourth_order_phi_odd_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_4_4_order_odd ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // store spin direction in temporary variables + const double sx = spin_array_x[ atom ]; + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate S_x and S_x^3 parts + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + // calculate S_y and S_y^3 parts + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + // get reduced anisotropy constant ku/mu_s + const double four_k4r4_odd = four * internal::k4r4_odd[ mat ]; + + // calculate full form to add to field + const double x_component = four_k4r4_odd * Sy * ( three * Sx2 - Sy2 ); + const double y_component = four_k4r4_odd * Sx * ( Sx2 - three * Sy2 ); + + // Sum components into field + field_array_x[ atom ] += x_component * fx + y_component * gx; + field_array_y[ atom ] += x_component * fy + y_component * gy; + field_array_z[ atom ] += x_component * fz + y_component * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 4-theta-4-phi-odd anisotropy + //--------------------------------------------------------------------------------- + double fourth_order_theta_fourth_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double four_k4r4_odd = four * internal::k4r4_odd[ mat ]; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // Calculate sin^4{theta}sin{4phi} = sin^4{theta} * ( 4 * sin{phi} * cos^3{phi} - 4 * sin^3{phi} * cos{phi} ) + // = 4 * Sx^3 * S_y - 4 * S_x * S_y^3 + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + return - four_k4r4_odd * ( Sx * Sx2 * Sy - Sx * Sy * Sy2 ); + + } + + } + +} diff --git a/src/anisotropy/rotational_order_6_1.cpp b/src/anisotropy/rotational_order_6_1.cpp new file mode 100644 index 000000000..447dd579c --- /dev/null +++ b/src/anisotropy/rotational_order_6_1.cpp @@ -0,0 +1,150 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{6r1} = -k_{6r1}sin{theta} ( cos^5{theta} - ( 10 / 11 ) cos^3(theta) + ( 5 / 33 ) cos(theta) ) cos{phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + // Define useful constants + const double five = 5.0; + const double oneothirtythree = 1.0 / 33.0; + const double fiveothirtythree = 5.0 / 33.0; + const double sixoeleven = 6.0 / 11.0; + const double tenoeleven = 10.0 / 11.0; + + void sixth_order_theta_first_order_phi_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_6_1_order ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double sx = spin_array_x[ atom ]; // store spin direction in temporary variables + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + // calculate S_z and S_z^3 parts + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sz2 = Sz * Sz; + const double Sz4 = Sz2 * Sz2; + + // calculate S_y and S_y^3 parts + const double Sx = sx * fx + sy * fy + sz * fz; + + // get reduced anisotropy constant ku/mu_s + const double k6r1 = internal::k6r1[ mat ]; + + // calculate full form to add to field + const double x_component = k6r1 * Sz * ( Sz4 - tenoeleven * Sz2 + fiveothirtythree ); + const double z_component = k6r1 * Sx * five * ( Sz4 - sixoeleven * Sz2 + oneothirtythree ); + + field_array_x[ atom ] += z_component * ex + x_component * fx; + field_array_y[ atom ] += z_component * ey + x_component * fy; + field_array_z[ atom ] += z_component * ez + x_component * fz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 6-theta-1-phi anisotropy + //--------------------------------------------------------------------------------- + + double sixth_order_theta_first_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + // calculate - k_{6r1} Sx Sz ( Sz^4 - ( 10 / 11 ) Sz^2 + 5 / 33 ) + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sz2 = Sz * Sz; + + const double Sx = sx * fx + sy * fy + sz * fz; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k6r1 = internal::k6r1[ mat ]; + + return - k6r1 * Sx * Sz * ( Sz2 * Sz2 - tenoeleven * Sz2 + fiveothirtythree ); + + } + + } + +} diff --git a/src/anisotropy/rotational_order_6_1_odd.cpp b/src/anisotropy/rotational_order_6_1_odd.cpp new file mode 100644 index 000000000..99c7f0fde --- /dev/null +++ b/src/anisotropy/rotational_order_6_1_odd.cpp @@ -0,0 +1,150 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{6r-1} = -k_{6r-1}sin{theta} ( cos^5{theta} - ( 10 / 11 ) cos^3(theta) + ( 5 / 33 ) cos(theta) ) sin{phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + // Define useful constants + const double five = 5.0; + const double oneothirtythree = 1.0 / 33.0; + const double fiveothirtythree = 5.0 / 33.0; + const double sixoeleven = 6.0 / 11.0; + const double tenoeleven = 10.0 / 11.0; + + void sixth_order_theta_first_order_phi_odd_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_6_1_order_odd ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double sx = spin_array_x[ atom ]; // store spin direction in temporary variables + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate S_z and S_z^3 parts + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sz2 = Sz * Sz; + const double Sz4 = Sz2 * Sz2; + + // calculate S_y and S_y^3 parts + const double Sy = sx * gx + sy * gy + sz * gz; + + // get reduced anisotropy constant ku/mu_s + const double k6r1_odd = internal::k6r1_odd[ mat ]; + + // calculate full form to add to field + const double y_component = k6r1_odd * Sz * ( Sz4 - tenoeleven * Sz2 + fiveothirtythree ); + const double z_component = k6r1_odd * Sy * five * ( Sz4 - sixoeleven * Sz2 + oneothirtythree ); + + field_array_x[ atom ] += z_component * ex + y_component * gx; + field_array_y[ atom ] += z_component * ey + y_component * gy; + field_array_z[ atom ] += z_component * ez + y_component * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 6-theta-1-phi-odd anisotropy + //--------------------------------------------------------------------------------- + + double sixth_order_theta_first_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate - k_{6r-1} Sy Sz ( Sz^4 - ( 10 / 11 ) Sz^2 + 5 / 33 ) + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sz2 = Sz * Sz; + + const double Sy = sx * gx + sy * gy + sz * gz; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k6r1_odd = internal::k6r1_odd[ mat ]; + + return - k6r1_odd * Sy * Sz * ( Sz2 * Sz2 - tenoeleven * Sz2 + fiveothirtythree ); + + } + + } + +} diff --git a/src/anisotropy/rotational_order_6_2.cpp b/src/anisotropy/rotational_order_6_2.cpp new file mode 100644 index 000000000..56b20d81f --- /dev/null +++ b/src/anisotropy/rotational_order_6_2.cpp @@ -0,0 +1,158 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{ 6r2 } = - k_{ 6r2 } sin^2{ theta } ( cos^4{ theta } - ( 9 / 11 ) cos^2{ theta } + ( 1 / 33 ) ) cos{ 2 phi } + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + // define useful consts + const double sixteenothirtythree = 16.0 / 33.0; + const double one = 1.0; + const double two = 2.0; + const double three = 3.0; + const double six = 6.0; + + void sixth_order_theta_second_order_phi_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_6_2_order ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double sx = spin_array_x[ atom ]; // store spin direction in temporary variables + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate S_x and S_x^3 parts + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + // calculate S_y and S_y^3 parts + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + const double Sx2pSy2 = Sx2 + Sy2; + + // get reduced anisotropy constant ku/mu_s + const double two_k6r2 = two * internal::k6r2[ mat ]; + + // calculate full form to add to field + const double x_component = two_k6r2 * Sx * ( Sx2pSy2 * ( three * Sx2 - Sy2 ) + sixteenothirtythree * ( one - six * Sx2 ) ); + const double y_component = two_k6r2 * Sy * ( Sx2pSy2 * ( Sx2 - three * Sy2 ) + sixteenothirtythree * ( six * Sy2 - one ) ); + + field_array_x[ atom ] += x_component * fx + y_component * gx; + field_array_y[ atom ] += x_component * fy + y_component * gy; + field_array_z[ atom ] += x_component * fz + y_component * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 6-theta-2-phi anisotropy + //--------------------------------------------------------------------------------- + + // Define useful constants + const double sixteenoeleven = 16.0 / 11.0; + + double sixth_order_theta_second_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate sin^2{theta} * ( cos^4{theta} - ( 6 / 11 ) * cos^2{theta} + ( 1 / 33 ) ) * cos{2phi} + // = ( sin^4{theta} - ( 16 / 11 ) * sin^2{theta} + 16 / 33 ) * ( 2 * sin^2{theta} cos^2{phi} - sin^2{theta} ) + // = ( sin^4{theta} - ( 16 / 11 ) * sin^2{theta} + 16 / 33 ) * ( S_x^2 - S_y^2 ) + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + const double sintheta2 = Sx2 + Sy2; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k6r2 = internal::k6r2[ mat ]; + + return - k6r2 * ( sintheta2 * sintheta2 - sixteenoeleven * sintheta2 + sixteenothirtythree ) * ( Sx2 - Sy2 ); + + } + } +} diff --git a/src/anisotropy/rotational_order_6_2_odd.cpp b/src/anisotropy/rotational_order_6_2_odd.cpp new file mode 100644 index 000000000..ecd9f7e49 --- /dev/null +++ b/src/anisotropy/rotational_order_6_2_odd.cpp @@ -0,0 +1,158 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{6r-2} = - k_{6r-2} sin^2{ theta } ( cos^4{ theta } - ( 9 / 11 ) cos^2{ theta } + ( 1 / 33 ) ) sin{ 2 phi } + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + // define useful consts + const double two = 2.0; + const double three = 3.0; + const double five = 5.0; + const double six = 6.0; + const double sixteenoeleven = 16.0 / 11.0; + const double sixteenothirtythree = 16.0 / 33.0; + + void sixth_order_theta_second_order_phi_odd_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double > & spin_array_z, + std::vector< int > & atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_6_2_order_odd ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double sx = spin_array_x[ atom ]; // store spin direction in temporary variables + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate S_x and higher powers + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + const double Sx4 = Sx2 * Sx2; + + // calculate S_y and higher powers + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + const double Sy4 = Sy2 * Sy2; + + // Calculate Sx^2 * Sy^2 and higher powers + const double Sx2Sy2 = Sx2 * Sy2; + + // get reduced anisotropy constant ku/mu_s + const double two_k6r2_odd = two * internal::k6r2_odd[ mat ]; + + // calculate full form to add to field + const double x_component = two_k6r2_odd * Sy * ( five * Sx4 + six * Sx2Sy2 + Sy4 - sixteenoeleven * ( three * Sx2 + Sy2 ) + sixteenothirtythree ); + const double y_component = two_k6r2_odd * Sx * ( Sx4 + six * Sx2Sy2 + five * Sy4 - sixteenoeleven * ( Sx2 + three * Sy2 ) + sixteenothirtythree ); + + field_array_x[ atom ] += x_component * fx + y_component * gx; + field_array_y[ atom ] += x_component * fy + y_component * gy; + field_array_z[ atom ] += x_component * fz + y_component * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 6-theta-2-phi odd anisotropy + //--------------------------------------------------------------------------------- + + double sixth_order_theta_second_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate sin^2{theta} * ( cos^4{theta} - ( 6 / 11 ) * cos^2{theta} + ( 1 / 33 ) ) * cos{2phi} + // = 2 * Sx * Sy [ Sx^4 + 2 Sx^2 Sy^2 + Sy^4 - ( 16 / 11 ) ( Sx^2 + Sy^2 ) + 16 / 33 ] + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double two_k6r2_odd = two * internal::k6r2_odd[ mat ]; + + return - two_k6r2_odd * Sx * Sy * ( Sx2 * Sx2 + two * Sx2 * Sy2 + Sy2 * Sy2 - sixteenoeleven * ( Sx2 + Sy2 ) + sixteenothirtythree ); + + } + + } + +} diff --git a/src/anisotropy/rotational_order_6_3.cpp b/src/anisotropy/rotational_order_6_3.cpp new file mode 100644 index 000000000..0d448af76 --- /dev/null +++ b/src/anisotropy/rotational_order_6_3.cpp @@ -0,0 +1,157 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{6r3} = -k_{6r3}sin^3{theta}( cos^3(theta) (3/11)cos(theta) )cos{3phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + // Define useful constants + const double three = 3.0; + const double four = 4.0; + const double five = 5.0; + const double twelve = 12.0; + const double fourteen = 14.0; + const double fortytwo = 42.0; + const double oneoeleven = 1.0 / 11.0; + const double threeoeleven = 3.0 / 11.0; + + void sixth_order_theta_third_order_phi_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_6_3_order ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double sx = spin_array_x[ atom ]; // store spin direction in temporary variables + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + // calculate S_z and S_z^3 parts + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sz2 = Sz * Sz; + const double Sz4 = Sz2 * Sz2; + + // calculate S_y and S_y^3 parts + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + const double Sx2Sz2 = Sx2 * Sz2; + + // get reduced anisotropy constant ku/mu_s + const double k6r3 = internal::k6r3[ mat ]; + + // calculate full form to add to field + const double x_component = k6r3 * Sz * three * ( Sz4 + four * Sx2Sz2 - oneoeleven * ( fourteen * Sz2 + twelve * Sx2 - three ) ); + const double z_component = k6r3 * Sx * three * ( five * Sz4 + four * Sx2Sz2 - oneoeleven * ( fortytwo * Sz2 + four * Sx2 - three ) ); + + field_array_x[ atom ] += z_component * ex + x_component * fx; + field_array_y[ atom ] += z_component * ey + x_component * fy; + field_array_z[ atom ] += z_component * ez + x_component * fz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 6-theta-3-phi anisotropy + //--------------------------------------------------------------------------------- + + double sixth_order_theta_third_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + // calculate - k_{6r3} Sx Sz ( 3 Sz^4 + 4 Sy^2 Sz^2 - ( 3 / 11 ) ( 4 Sx^2 + 3 Sz^2 - 3 ) ) + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sz2 = Sz * Sz; + + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k6r3 = internal::k6r3[ mat ]; + + return - k6r3 * Sx * Sz * ( three * Sz2 * Sz2 + four * Sx2 * Sz2 - threeoeleven * ( fourteen * Sz2 + four * Sx2 - three ) ); + + } + + } + +} diff --git a/src/anisotropy/rotational_order_6_3_odd.cpp b/src/anisotropy/rotational_order_6_3_odd.cpp new file mode 100644 index 000000000..7fca08bcb --- /dev/null +++ b/src/anisotropy/rotational_order_6_3_odd.cpp @@ -0,0 +1,157 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{6r-3} = -k_{6r-3}sin^3{theta}( cos^3(theta) (3/11)cos(theta) )sin{3phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + // Define useful constants + const double three = 3.0; + const double four = 4.0; + const double five = 5.0; + const double twelve = 12.0; + const double fourteen = 14.0; + const double fortytwo = 42.0; + const double oneoeleven = 1.0 / 11.0; + const double threeoeleven = 3.0 / 11.0; + + void sixth_order_theta_third_order_phi_odd_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_6_3_order_odd ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double sx = spin_array_x[ atom ]; // store spin direction in temporary variables + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate S_z and S_z^3 parts + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sz2 = Sz * Sz; + const double Sz4 = Sz2 * Sz2; + + // calculate S_y and S_y^3 parts + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + const double Sy2Sz2 = Sy2 * Sz2; + + // get reduced anisotropy constant ku/mu_s + const double k6r3_odd = internal::k6r3_odd[ mat ]; + + // calculate full form to add to field + const double y_component = - k6r3_odd * Sz * three * ( Sz4 + four * Sy2Sz2 - oneoeleven * ( fourteen * Sz2 + twelve * Sy2 - three ) ); + const double z_component = - k6r3_odd * Sy * three * ( five * Sz4 + four * Sy2Sz2 - oneoeleven * ( fortytwo * Sz2 + four * Sy2 - three ) ); + + field_array_x[ atom ] += z_component * ex + y_component * gx; + field_array_y[ atom ] += z_component * ey + y_component * gy; + field_array_z[ atom ] += z_component * ez + y_component * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 6-theta-3-phi-odd anisotropy + //--------------------------------------------------------------------------------- + + double sixth_order_theta_third_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate k_{6r-3} Sy Sz ( 3 Sz^4 + 4 Sy^2 Sz^2 - (3/11) ( - 8 Sz^2 + 4 Sy^2 + 3 ) ) + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sz2 = Sz * Sz; + + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k6r3_odd = internal::k6r3_odd[ mat ]; + + return k6r3_odd * Sy * Sz * ( three * Sz2 * Sz2 + four * Sy2 * Sz2 - threeoeleven * ( - fourteen * Sz2 + four * Sy2 - three ) ); + + } + + } + +} diff --git a/src/anisotropy/rotational_order_6_4.cpp b/src/anisotropy/rotational_order_6_4.cpp new file mode 100644 index 000000000..5d4566d8a --- /dev/null +++ b/src/anisotropy/rotational_order_6_4.cpp @@ -0,0 +1,159 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings and Richard Evans 2022. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{64} = -k_{6r4}sin^4{theta}(cos^2{theta} - 1/11)cos{4phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis and is detailed in an as yet unpublished paper. + // + //-------------------------------------------------------------------------------------------------------------- + void sixth_order_theta_fourth_order_phi_fields(std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index){ + + // if not enabled then do nothing + if(!internal::enable_rotational_6_4_order) return; + + // define useful consts + const double twentyoeleven = 20.0 / 11.0; + const double ten = 10.0; + const double two = 2.0; + const double three = 3.0; + const double five = 5.0; + + // Loop over all atoms between start and end index + for(int atom = start_index; atom < end_index; ++atom){ + + // get atom material + const int mat = atom_material_array[atom]; + + const double sx = spin_array_x[atom]; // store spin direction in temporary variables + const double sy = spin_array_y[atom]; + const double sz = spin_array_z[atom]; + + const double fx = internal::kr_vector[mat].x; + const double fy = internal::kr_vector[mat].y; + const double fz = internal::kr_vector[mat].z; + + const double gx = internal::kl_vector[mat].x; + const double gy = internal::kl_vector[mat].y; + const double gz = internal::kl_vector[mat].z; + + // calculate S_x and S_x^3 parts + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + const double Sx4 = Sx2 * Sx2; + + // calculate S_y and S_y^3 parts + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + const double Sy4 = Sy2 * Sy2; + + const double Sx2Sy2 = Sx2 * Sy2; + + // get reduced anisotropy constant ku/mu_s + const double two_k6r4 = two * internal::k6r4[mat]; + + // calculate full form to add to field + const double fullx = two_k6r4 * Sx * (three * Sx4 - ten * Sx2Sy2 - five * Sy4 - twentyoeleven * (Sx2 - three * Sy2)); + const double fully = two_k6r4 * Sy * (three * Sy4 - ten * Sx2Sy2 - five * Sx4 - twentyoeleven * (Sy2 - three * Sx2)); + + field_array_x[atom] -= fullx*fx + fully * gx; + field_array_y[atom] -= fullx*fy + fully * gy; + field_array_z[atom] -= fullx*fz + fully * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 6-theta-4-phi anisotropy + //--------------------------------------------------------------------------------- + + // Define useful constants + const double tenoeleven = 10.0 / 11.0; + const double six = 6.0; + + double sixth_order_theta_fourth_order_phi_energy(const int atom, + const int mat, + const double sx, + const double sy, + const double sz){ + + const double fx = internal::kr_vector[mat].x; + const double fy = internal::kr_vector[mat].y; + const double fz = internal::kr_vector[mat].z; + + const double gx = internal::kl_vector[mat].x; + const double gy = internal::kl_vector[mat].y; + const double gz = internal::kl_vector[mat].z; + + // calculate - sin^4{theta}(cos^2{theta} - 1/11)cos{4phi} + // = ( sin^6{theta} - ( 10 / 11 ) * sin^4{theta} ) * ( 8 * cos^4{phi} - 8 * cos^2{phi} + 1 ) + // = ( sin^2{theta} - 10 / 11 ) * ( 8 * Sx^4 - 8 * (Sx^2 + Sy^2) * Sx^2 + ( Sx^2 + Sy^2 )^2 ) + // = ( Sx^2 + Sy^2 - 10 / 11 ) * ( Sx^4 - 6 * Sx^2 * Sy^2 + Sy^4 ) + + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + const double sintheta2 = Sx2 + Sy2; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k6r4 = internal::k6r4[mat]; + + return k6r4 * (sintheta2 - tenoeleven) * (Sx2 * Sx2 - six * Sx2 * Sy2 + Sy2 * Sy2); + + } + } +} diff --git a/src/anisotropy/rotational_order_6_4_odd.cpp b/src/anisotropy/rotational_order_6_4_odd.cpp new file mode 100644 index 000000000..67c11bab8 --- /dev/null +++ b/src/anisotropy/rotational_order_6_4_odd.cpp @@ -0,0 +1,154 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{6r-4} = -k_{6r-4}sin^4{theta}(cos^2{theta} - 1/11)sin{4phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + // Define useful constants + const double three = 3.0; + const double four = 4.0; + const double five = 5.0; + const double tenoeleven = 10.0 / 11.0; + + void sixth_order_theta_fourth_order_phi_odd_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_6_4_order_odd ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double sx = spin_array_x[ atom ]; // store spin direction in temporary variables + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate S_x and S_x^3 parts + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + const double Sx4 = Sx2 * Sx2; + + // calculate S_y and S_y^3 parts + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + const double Sy4 = Sy2 * Sy2; + + // get reduced anisotropy constant ku/mu_s + const double four_k6r4_odd = four * internal::k6r4_odd[ mat ]; + + // calculate full form to add to field + const double x_component = four_k6r4_odd * Sy * ( Sy4 - five * Sx4 + tenoeleven * ( three * Sx2 - Sy2 ) ); + const double y_component = four_k6r4_odd * Sx * ( five * Sy4 - Sx4 + tenoeleven * ( Sx2 - three * Sy2 ) ); + + field_array_x[ atom ] += x_component * fx + y_component * gx; + field_array_y[ atom ] += x_component * fy + y_component * gy; + field_array_z[ atom ] += x_component * fz + y_component * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 6-theta-4-phi odd anisotropy + //--------------------------------------------------------------------------------- + + double sixth_order_theta_fourth_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate sin^4{theta}(cos^2{theta} - 1/11)sin{4phi} + // = - 4 [ sin^2{theta} - 10/11 ] [ Sx^3 * Sy - Sx * Sy^3 ] + // = - 4 Sx * Sy * [ Sx^4 - Sy^4 - 10/11 * ( Sx^2 - Sy^2 ) ] + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double four_k6r4_odd = four * internal::k6r4_odd[ mat ]; + + return four_k6r4_odd * Sx * Sy * ( Sx2 * Sx2 - Sy2 * Sx2 + tenoeleven * ( Sy2 - Sx2 ) ); + + } + + } + +} diff --git a/src/anisotropy/rotational_order_6_5.cpp b/src/anisotropy/rotational_order_6_5.cpp new file mode 100644 index 000000000..ee7809f9a --- /dev/null +++ b/src/anisotropy/rotational_order_6_5.cpp @@ -0,0 +1,161 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{6r5} = -k_{6r5}sin^5{theta}cos(theta)cos{5phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + // Define useful constants + const double one = 1.0; + const double two = 2.0; + const double three = 3.0; + const double five = 5.0; + const double six = 6.0; + const double ten = 10.0; + const double twelve = 12.0; + const double sixteen = 16.0; + const double twenty = 20.0; + const double twentyfive = 25.0; + const double sixty = 60.0; + + void sixth_order_theta_fifth_order_phi_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_6_5_order ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double sx = spin_array_x[ atom ]; // store spin direction in temporary variables + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + // calculate S_z and S_z^3 parts + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sz2 = Sz * Sz; + const double Sz4 = Sz2 * Sz2; + + // calculate S_x and S_x^3 parts + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + const double Sx4 = Sx2 * Sx2; + + const double Sx2Sz2 = Sx2 * Sz2; + + // get reduced anisotropy constant ku/mu_s + const double k6r5 = internal::k6r5[ mat ]; + + // calculate full form to add to field + const double x_component = k6r5 * Sz * five * ( sixteen * Sx4 + twelve * Sx2Sz2 + Sz4 - two * ( six * Sx2 + Sz2 ) + one ); + const double z_component = k6r5 * Sx * ( sixteen * Sx4 + sixty * Sx2Sz2 + twentyfive * Sz4 - ten * ( two * Sx2 + three * Sz2 ) + five ); + + field_array_x[ atom ] += z_component * ex + x_component * fx; + field_array_y[ atom ] += z_component * ey + x_component * fy; + field_array_z[ atom ] += z_component * ez + x_component * fz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 6-theta-5-phi anisotropy + //--------------------------------------------------------------------------------- + + double sixth_order_theta_fifth_order_phi_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + // calculate - k_{6r5} Sx Sz ( 16Sx^4 + 20 Sx^2 Sz^2 + 5 Sz^4 - 20 Sx^2 - 10 Sz^2 + 5 ) + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sz2 = Sz * Sz; + + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k6r5 = internal::k6r5[ mat ]; + + return - k6r5 * Sx * Sz * ( sixteen * Sx2 * Sx2 + twenty * Sx2 * Sz2 + five * Sz2 * Sz2 - ten * ( two * Sx2 + Sz2 ) + five ); + + } + + } + +} diff --git a/src/anisotropy/rotational_order_6_5_odd.cpp b/src/anisotropy/rotational_order_6_5_odd.cpp new file mode 100644 index 000000000..7fd0e97d7 --- /dev/null +++ b/src/anisotropy/rotational_order_6_5_odd.cpp @@ -0,0 +1,161 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{6r-5} = -k_{6r-5}sin^5{theta}cos(theta)sin{5phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + // Define useful constants + const double one = 1.0; + const double two = 2.0; + const double three = 3.0; + const double five = 5.0; + const double six = 6.0; + const double ten = 10.0; + const double twelve = 12.0; + const double sixteen = 16.0; + const double twenty = 20.0; + const double twentyfive = 25.0; + const double sixty = 60.0; + + void sixth_order_theta_fifth_order_phi_odd_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_6_5_order_odd ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double sx = spin_array_x[ atom ]; // store spin direction in temporary variables + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate S_z and S_z^3 parts + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sz2 = Sz * Sz; + const double Sz4 = Sz2 * Sz2; + + // calculate S_y and S_y^3 parts + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + const double Sy4 = Sy2 * Sy2; + + const double Sy2Sz2 = Sy2 * Sz2; + + // get reduced anisotropy constant ku/mu_s + const double k6r5_odd = internal::k6r5_odd[ mat ]; + + // calculate full form to add to field + const double y_component = k6r5_odd * Sz * five * ( sixteen * Sy4 + twelve * Sy2Sz2 + Sz4 - two * ( six * Sy2 + Sz2 ) + one ); + const double z_component = k6r5_odd * Sy * ( sixteen * Sy4 + sixty * Sy2Sz2 + twentyfive * Sz4 - ten * ( two * Sy2 + three * Sz2 ) + five ); + + field_array_x[ atom ] += z_component * ex + y_component * gx; + field_array_y[ atom ] += z_component * ey + y_component * gy; + field_array_z[ atom ] += z_component * ez + y_component * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 6-theta-5-phi-odd anisotropy + //--------------------------------------------------------------------------------- + + double sixth_order_theta_fifth_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + const double ex = internal::ku_vector[ mat ].x; + const double ey = internal::ku_vector[ mat ].y; + const double ez = internal::ku_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate - k_{6r-5} ( ) + const double Sz = sx * ex + sy * ey + sz * ez; + const double Sz2 = Sz * Sz; + + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k6r5_odd = internal::k6r5_odd[ mat ]; + + return - k6r5_odd * Sy * Sz * ( sixteen * Sy2 * Sy2 + twenty * Sy2 * Sz2 + five * Sz2 * Sz2 - ten * ( two * Sy2 + Sz2 ) + five ); + + } + + } + +} diff --git a/src/anisotropy/rotational_order_6_6.cpp b/src/anisotropy/rotational_order_6_6.cpp new file mode 100644 index 000000000..29a64910f --- /dev/null +++ b/src/anisotropy/rotational_order_6_6.cpp @@ -0,0 +1,157 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings and Richard Evans 2022. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal{ + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{66} = -k_{6r6}sin^6{theta}cos{6phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis and is detailed in an as yet unpublished paper. + // + //-------------------------------------------------------------------------------------------------------------- + + // Define useful constants + const double ten = 10.0; + const double five = 5.0; + const double six = 6.0; + + void sixth_order_theta_sixth_order_phi_fields(std::vector& spin_array_x, + std::vector& spin_array_y, + std::vector& spin_array_z, + std::vector& atom_material_array, + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z, + const int start_index, + const int end_index){ + + // if not enabled then do nothing + if(!internal::enable_rotational_6_6_order) return; + + // Loop over all atoms between start and end index + for(int atom = start_index; atom < end_index; ++atom){ + + // get atom material + const int mat = atom_material_array[atom]; + + const double sx = spin_array_x[atom]; // store spin direction in temporary variables + const double sy = spin_array_y[atom]; + const double sz = spin_array_z[atom]; + + const double fx = internal::kr_vector[mat].x; + const double fy = internal::kr_vector[mat].y; + const double fz = internal::kr_vector[mat].z; + + const double gx = internal::kl_vector[mat].x; + const double gy = internal::kl_vector[mat].y; + const double gz = internal::kl_vector[mat].z; + + // calculate S_x and S_x^3 parts + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + const double Sx4 = Sx2 * Sx2; + + // calculate S_y and S_y^3 parts + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + const double Sy4 = Sy2 * Sy2; + + const double Sx2Sy2 = Sx2 * Sy2; + + // get reduced anisotropy constant ku/mu_s + const double six_k6r6 = six * internal::k6r6[mat]; + + // calculate full form to add to field + const double fullx = six_k6r6 * Sx * (Sx4 - ten * Sx2Sy2 + five * Sy4); + const double fully = - six_k6r6 * Sy * (Sy4 - ten * Sx2Sy2 + five * Sx4); + + field_array_x[atom] += fullx*fx + fully * gx; + field_array_y[atom] += fullx*fy + fully * gy; + field_array_z[atom] += fullx*fz + fully * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 6-theta-6-phi anisotropy + //--------------------------------------------------------------------------------- + + // Define useful constant + const double fifteen = 15.0; + + double sixth_order_theta_sixth_order_phi_energy(const int atom, + const int mat, + const double sx, + const double sy, + const double sz){ + + const double fx = internal::kr_vector[mat].x; + const double fy = internal::kr_vector[mat].y; + const double fz = internal::kr_vector[mat].z; + + const double gx = internal::kl_vector[mat].x; + const double gy = internal::kl_vector[mat].y; + const double gz = internal::kl_vector[mat].z; + + // calculate sin^6{theta}cos{6phi} + // = sin^6{theta}( 32 * cos^6{phi} - 48 * cos^4{phi} + 18 * cos^2{phi} - 1 ) + // = 32 * Sx^6 - 48 * ( Sx^2 + Sy^2 ) * Sx^4 + 18 * ( Sx^2 + Sy^2 )^2 * Sx^2 - ( Sx^2 + Sy^2 )^3 + // = Sx^6 - 15 * Sx^4 * Sy^2 + 15 * Sx^2 * Sy^4 - Sy^6 + + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + const double Sx4 = Sx2 * Sx2; + + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + const double Sy4 = Sy2 * Sy2; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double k6r6 = internal::k6r6[mat]; + + return - k6r6 * (Sx4 * Sx2 - fifteen * Sx4 * Sy2 + fifteen * Sx2 * Sy4 - Sy4 * Sy2); + + } + } +} diff --git a/src/anisotropy/rotational_order_6_6_odd.cpp b/src/anisotropy/rotational_order_6_6_odd.cpp new file mode 100644 index 000000000..62784c6fd --- /dev/null +++ b/src/anisotropy/rotational_order_6_6_odd.cpp @@ -0,0 +1,162 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Jack Collings 2024. All rights reserved. +// +// Email: jbc525@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ Standard Library Headers + +// Vampire Headers +#include "anisotropy.hpp" + +// Anisotropy Module Headers +#include "internal.hpp" + +namespace anisotropy +{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + + namespace internal + { + //--------------------------------------------------------------------------------- + // Function to add second order magnetocrystalline second order rotational + // anisotropy based on vector e for the easy/hard/z axis and another vector for the + // x axis. + // + // Higher order anisotropies generally need to be described using orthogonal + // functions. The usual form, a series in S, leads to cross pollution of terms, + // giving strange temperature dependencies. + // + // The anisotropies are described with a minimal orthogonal set expansion, + // preserving the orthogonality of different orders while being simple to + // implement and understand. Explicity the energies are described by normalising + // the inner summation of the 2,4,6 order spherical harmonics to the prefactor + // of the highest order term with an abritrary shift so that E(0) = 0. + // + // The rotational term here is given by + // E_{6r-6} = -k_{6r-6}sin^6{theta}sin{6phi} + // + // The field is found by taking the negative gradient w.r.t. the magnetic moment + // basis. + // + //-------------------------------------------------------------------------------------------------------------- + + // Define useful constants + const double ten = 10.0; + const double two = 2.0; + const double three = 3.0; + const double six = 6.0; + const double five = 5.0; + + void sixth_order_theta_sixth_order_phi_odd_fields( std::vector< double >& spin_array_x, + std::vector< double >& spin_array_y, + std::vector< double >& spin_array_z, + std::vector< int >& atom_material_array, + std::vector< double >& field_array_x, + std::vector< double >& field_array_y, + std::vector< double >& field_array_z, + const int start_index, + const int end_index ) + { + + // if not enabled then do nothing + if( !internal::enable_rotational_6_6_order_odd ) return; + + // Loop over all atoms between start and end index + for( int atom = start_index; atom < end_index; ++atom ) + { + + // get atom material + const int mat = atom_material_array[ atom ]; + + const double sx = spin_array_x[ atom ]; // store spin direction in temporary variables + const double sy = spin_array_y[ atom ]; + const double sz = spin_array_z[ atom ]; + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // calculate S_x and S_x^3 parts + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + const double Sx4 = Sx2 * Sx2; + + // calculate S_y and S_y^3 parts + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + const double Sy4 = Sy2 * Sy2; + + const double Sx2Sy2 = Sx2 * Sy2; + + // get reduced anisotropy constant ku/mu_s + const double six_k6r6_odd = six * internal::k6r6_odd[ mat ]; + + // calculate full form to add to field + const double x_component = six_k6r6_odd * Sy * ( five * Sx4 - ten * Sx2Sy2 + Sy4 ); + const double y_component = six_k6r6_odd * Sx * ( Sx4 - ten * Sx2Sy2 + five * Sy4 ); + + field_array_x[atom] += x_component * fx + y_component * gx; + field_array_y[atom] += x_component * fy + y_component * gy; + field_array_z[atom] += x_component * fz + y_component * gz; + + } + + return; + + } + + //--------------------------------------------------------------------------------- + // Function to add 6-theta-6-phi odd anisotropy + //--------------------------------------------------------------------------------- + + double sixth_order_theta_sixth_order_phi_odd_energy( const int atom, + const int mat, + const double sx, + const double sy, + const double sz ) + { + + const double fx = internal::kr_vector[ mat ].x; + const double fy = internal::kr_vector[ mat ].y; + const double fz = internal::kr_vector[ mat ].z; + + const double gx = internal::kl_vector[ mat ].x; + const double gy = internal::kl_vector[ mat ].y; + const double gz = internal::kl_vector[ mat ].z; + + // Calculate sin^6{theta}sin{6phi} + // = sin^6{theta}( 6cos^5{phi}sin{phi} - 20cos^3{phi}sin^3{phi} + 6cos{phi}sin^5{phi} ) + // = 6 * Sx^5 * Sy - 20 * Sx^3 * Sy^3 + 6 * Sx * Sy^5 + + const double Sx = sx * fx + sy * fy + sz * fz; + const double Sx2 = Sx * Sx; + const double Sx4 = Sx2 * Sx2; + + const double Sy = sx * gx + sy * gy + sz * gz; + const double Sy2 = Sy * Sy; + const double Sy4 = Sy2 * Sy2; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double two_k6r6_odd = two * internal::k6r6_odd[ mat ]; + + return - two_k6r6_odd * Sx * Sy * ( three * Sx4 - ten * Sx2 * Sy2 + three * Sy4 ); + + } + + } + +} diff --git a/src/anisotropy/triaxial_rotational.cpp b/src/anisotropy/triaxial_rotational.cpp index db46f98f6..09a620fdd 100644 --- a/src/anisotropy/triaxial_rotational.cpp +++ b/src/anisotropy/triaxial_rotational.cpp @@ -105,9 +105,9 @@ namespace anisotropy{ const double sz){ // Get basis vectors - const double eA[3] = {internal::ku4_triaxial_basis1x[mat],internal::ku4_triaxial_basis1y[mat],internal::ku4_triaxial_basis1z[mat]}; - const double eB[3] = {internal::ku4_triaxial_basis2x[mat],internal::ku4_triaxial_basis2y[mat],internal::ku4_triaxial_basis2z[mat]}; - const double eC[3] = {internal::ku4_triaxial_basis3x[mat],internal::ku4_triaxial_basis3y[mat],internal::ku4_triaxial_basis3z[mat]}; + const double eA[3] = {internal::ku_triaxial_basis1x[mat],internal::ku_triaxial_basis1y[mat],internal::ku_triaxial_basis1z[mat]}; + const double eB[3] = {internal::ku_triaxial_basis2x[mat],internal::ku_triaxial_basis2y[mat],internal::ku_triaxial_basis2z[mat]}; + const double eC[3] = {internal::ku_triaxial_basis3x[mat],internal::ku_triaxial_basis3y[mat],internal::ku_triaxial_basis3z[mat]}; // compute dot products with each basis vector const double sdoteA = eA[0]*sx + eA[1]*sy + eA[2]*sz; @@ -115,9 +115,9 @@ namespace anisotropy{ const double sdoteC = eC[0]*sx + eC[1]*sy + eC[2]*sz; // get reduced anisotropy constant ku/mu_s (Tesla) - const double kA = internal::ku4_triaxial_vector_x[mat]; - const double kB = internal::ku4_triaxial_vector_y[mat]; - const double kC = internal::ku4_triaxial_vector_z[mat]; + const double kA = internal::ku_triaxial_vector_x[mat]; + const double kB = internal::ku_triaxial_vector_y[mat]; + const double kC = internal::ku_triaxial_vector_z[mat]; // compute send and fourth order components const double sdoteA2 = sdoteA*sdoteA; diff --git a/src/anisotropy/uniaxial_second_order.cpp b/src/anisotropy/uniaxial_order_2.cpp similarity index 100% rename from src/anisotropy/uniaxial_second_order.cpp rename to src/anisotropy/uniaxial_order_2.cpp diff --git a/src/anisotropy/uniaxial_fourth_order.cpp b/src/anisotropy/uniaxial_order_4.cpp similarity index 85% rename from src/anisotropy/uniaxial_fourth_order.cpp rename to src/anisotropy/uniaxial_order_4.cpp index a519f3664..acd01cbef 100644 --- a/src/anisotropy/uniaxial_fourth_order.cpp +++ b/src/anisotropy/uniaxial_order_4.cpp @@ -52,6 +52,11 @@ namespace anisotropy{ // simultaneously. // //-------------------------------------------------------------------------------------------------------------- + + // Define useful constants + const double twelve_o_seven = 12.0 / 7.0; + const double four = 4.0; + void uniaxial_fourth_order_fields(std::vector& spin_array_x, std::vector& spin_array_y, std::vector& spin_array_z, @@ -65,11 +70,8 @@ namespace anisotropy{ // if not enabled then do nothing if(!internal::enable_uniaxial_fourth_order) return; - // constant factors - const double sixtyothirtyfive = 60.0/35.0; - // Loop over all atoms between start and end index - for(int atom = start_index; atom < end_index; atom++){ + for(int atom = start_index; atom < end_index; ++atom){ // get atom material const int mat = atom_material_array[atom]; @@ -85,15 +87,15 @@ namespace anisotropy{ // get reduced anisotropy constant ku/mu_s const double ku4 = internal::ku4[mat]; - const double sdote = (sx*ex + sy*ey + sz*ez); - const double sdote3 = sdote*sdote*sdote; + const double sdote = sx * ex + sy * ey + sz * ez; + const double sdote3 = sdote * sdote * sdote; // calculate field (double negative from scale factor and negative derivative) - const double k4 = -ku4*(4.0*sdote3 - sixtyothirtyfive*sdote); + const double k4 = ku4 * (four * sdote3 - twelve_o_seven * sdote); - field_array_x[atom] += ex*k4; - field_array_y[atom] += ey*k4; - field_array_z[atom] += ez*k4; + field_array_x[atom] += ex * k4; + field_array_y[atom] += ey * k4; + field_array_z[atom] += ez * k4; } @@ -104,9 +106,9 @@ namespace anisotropy{ //--------------------------------------------------------------------------------- // Function to add fourth order uniaxial anisotropy //--------------------------------------------------------------------------------- - // file scope constant - const double fiveothirtyfive = 5.0 / 35.0; - const double thirtyothirtyfive = 30.0 / 35.0; + + // Define useful constants + const double six_o_seven = 6.0 / 7.0; double uniaxial_fourth_order_energy(const int atom, const int mat, @@ -122,10 +124,10 @@ namespace anisotropy{ const double ey = internal::ku_vector[mat].y; const double ez = internal::ku_vector[mat].z; - const double sdote = (sx*ex + sy*ey + sz*ez); - const double sdote2 = sdote*sdote; + const double sdote = (sx * ex + sy * ey + sz * ez); + const double sdote2 = sdote * sdote; - return ku4*(sdote2*sdote2 - thirtyothirtyfive*sdote2 - fiveothirtyfive); + return - ku4 * ( sdote2 * sdote2 - six_o_seven * sdote2 ); } diff --git a/src/anisotropy/uniaxial_sixth_order.cpp b/src/anisotropy/uniaxial_order_6.cpp similarity index 81% rename from src/anisotropy/uniaxial_sixth_order.cpp rename to src/anisotropy/uniaxial_order_6.cpp index 8c93326d6..83819cad7 100644 --- a/src/anisotropy/uniaxial_sixth_order.cpp +++ b/src/anisotropy/uniaxial_order_6.cpp @@ -49,6 +49,13 @@ namespace anisotropy{ // direction is shared with the other uniaxial anisotropy coefficients. // //--------------------------------------------------------------------------------- + + // Define useful constants + const double two_o_eleven = 2.0 / 11.0; + const double thirtythree = 33.0; + const double thirty = 30.0; + const double five = 5.0; + void uniaxial_sixth_order_fields(std::vector& spin_array_x, std::vector& spin_array_y, std::vector& spin_array_z, @@ -62,12 +69,6 @@ namespace anisotropy{ // if not enabled then do nothing if(!internal::enable_uniaxial_sixth_order) return; - // constant factors - const double oneo16 = 1.0/16.0; - - // rescaling prefactor - const double scale = oneo16 * 2.0/3.0; // Factor to rescale anisotropies to usual scale - // Loop over all atoms between start and end index for(int atom = start_index; atom < end_index; atom++){ @@ -82,18 +83,18 @@ namespace anisotropy{ const double ey = internal::ku_vector[mat].y; const double ez = internal::ku_vector[mat].z; + const double sdote = sx * ex + sy * ey + sz * ez; + const double sdote2 = sdote * sdote; + const double sdote4 = sdote2 * sdote2; + // get reduced anisotropy constant ku/mu_s const double ku6 = internal::ku6[mat]; - const double sdote = (sx*ex + sy*ey + sz*ez); - const double sdote3 = sdote*sdote*sdote; - const double sdote5 = sdote3*sdote*sdote; - - const double k6 = scale*ku6*(1386.0*sdote5 - 1260.0*sdote3 + 210.0*sdote); + const double fullz = two_o_eleven * ku6 * sdote * ( thirtythree * sdote4 - thirty * sdote2 + five ); - field_array_x[atom] += ex*k6; - field_array_y[atom] += ey*k6; - field_array_z[atom] += ez*k6; + field_array_x[atom] += ex * fullz; + field_array_y[atom] += ey * fullz; + field_array_z[atom] += ez * fullz; } @@ -103,26 +104,31 @@ namespace anisotropy{ //--------------------------------------------------------------------------------- // Function to add sixth order uniaxial anisotropy - // E = 2/3 * - (1/16) * (231sz^6 - 315*sz^4 + 105sz^2 - 5) + // E = -ku6(cos^6{theta} - (15/11)cos^4{theta} + (5/11)cos^2{theta}) //--------------------------------------------------------------------------------- + + // Define useful constants + const double fiveoeleven = five / 11.0; + const double fifteenoeleven = 3.0 * fiveoeleven; + double uniaxial_sixth_order_energy(const int atom, const int mat, const double sx, const double sy, const double sz){ - // get reduced anisotropy constant ku/mu_s (Tesla) - const double ku6 = internal::ku6[mat]; - const double ex = internal::ku_vector[mat].x; const double ey = internal::ku_vector[mat].y; const double ez = internal::ku_vector[mat].z; - const double sdote = (sx*ex + sy*ey + sz*ez); - const double sdote2 = sdote*sdote; + const double sdote = sx * ex + sy * ey + sz * ez; + const double sdote2 = sdote * sdote; + const double sdote4 = sdote2 * sdote2; + + // get reduced anisotropy constant ku/mu_s (Tesla) + const double ku6 = internal::ku6[mat]; - // factor = 2/3 * -1/16 = -1/6 = -0.04166666666 - return -0.04166666666*ku6*(231.0*sdote2*sdote2*sdote2 - 315.0*sdote2*sdote2 + 105.0*sdote2); + return - ku6 * (sdote2 * sdote4 - fifteenoeleven * sdote4 + fiveoeleven * sdote2); } diff --git a/src/cells/data.cpp b/src/cells/data.cpp index ec90d9eb0..c56ab0f7c 100644 --- a/src/cells/data.cpp +++ b/src/cells/data.cpp @@ -35,7 +35,7 @@ namespace cells{ double macro_cell_size_x = 10.0; /// macro-cells size (A) double macro_cell_size_y = 10.0; /// macro-cells size (A) double macro_cell_size_z = 10.0; /// macro-cells size (A) - + double atomic_volume = 0.0; // volume of each magnetic moment for atomistic dipole self term std::vector local_cell_array; std::vector num_atoms_in_cell; /// number of atoms in each cell diff --git a/src/cells/initialize.cpp b/src/cells/initialize.cpp index bcfa69231..d339afb82 100644 --- a/src/cells/initialize.cpp +++ b/src/cells/initialize.cpp @@ -116,7 +116,7 @@ namespace cells{ supercell_array[i][j][k]=cell; // increment cell number - + cell++; } } @@ -269,6 +269,11 @@ namespace cells{ // Atomic volume is corrected by a factor which makes it a magnetic atomic volume const double factor_for_volume = double(total_atoms_non_filler)/double(num_atoms_magnetic); const double atomic_volume = factor_for_volume * unit_cell_size_x*unit_cell_size_y*unit_cell_size_z/double(cells::num_atoms_in_unit_cell); + + // Calculate local moment vloume for moment density in self term + cells::atomic_volume = atomic_volume; + zlog << zTs() << "Calculated (magnetic) atomic volume: " << cells::atomic_volume << " cubic Angstroms" << std::endl; + // std::cout << "\n\tnum_total_atoms_for_dipole\t" << total_atoms_non_filler << std::endl; // std::cout << "\n\tnum_atoms_magnetic\t" << num_atoms_magnetic << std::endl; // std::cout << "\n\tfactor_for_volume\t" << factor_for_volume << std::endl; @@ -285,7 +290,7 @@ namespace cells{ cells::pos_array[3*local_cell+0] = cells::pos_array[3*local_cell+0]/cells::num_atoms_in_cell[local_cell]; cells::pos_array[3*local_cell+1] = cells::pos_array[3*local_cell+1]/cells::num_atoms_in_cell[local_cell]; cells::pos_array[3*local_cell+2] = cells::pos_array[3*local_cell+2]/cells::num_atoms_in_cell[local_cell]; - + } } diff --git a/src/config/atoms_coords_sld.cpp b/src/config/atoms_coords_sld.cpp new file mode 100644 index 000000000..ce8f974ec --- /dev/null +++ b/src/config/atoms_coords_sld.cpp @@ -0,0 +1,170 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Mara Strungaru 2022. All rights reserved. +// +// Email: mara.strungaru@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include + + +// Vampire headers +#include "atoms.hpp" +#include "cells.hpp" +#include "config.hpp" +#include "material.hpp" +#include "sim.hpp" +#include "stats.hpp" +#include "vio.hpp" +#include "vmpi.hpp" +#include "vutil.hpp" + +// config module headers +#include "internal.hpp" + +namespace config{ + +namespace internal{ + +//------------------------------------------------------------------------------ +// Function to output atomic spin positions to disk +//------------------------------------------------------------------------------ +void atoms_coords_sld(){ + + // instantiate timer for total output time including overheads + vutil::vtimer_t total_timer; + + // start timer + total_timer.start(); + + //------------------------------------------ + // Output Meta Data + //------------------------------------------ + // get system magnetization data + const std::vector magnetisation = stats::system_magnetization.get_magnetization(); + // calculate real time + const double real_time = double(sim::time) * mp::dt_SI; + + if(config::internal::mode != legacy && vmpi::my_rank == 0){ + write_meta(real_time, sim::temperature, sim::H_vec[0], sim::H_vec[1], sim::H_vec[2], sim::H_applied, magnetisation[0], magnetisation[1], magnetisation[2]); + } + + //------------------------------------------ + // Output spin data + //------------------------------------------ + + // copy data to local buffer + copy_data_to_buffer(atoms::x_coord_array, atoms::y_coord_array, atoms::z_coord_array, local_output_atom_list, config::internal::local_buffer); + + // Determine output filename + std::stringstream file_sstr; + + // set simple file name for single file output + if(config::internal::num_io_groups == 1) file_sstr << "atoms-coords-sld-" << std::setfill('0') << std::setw(8) << sim::output_atoms_file_counter << ".data"; + // otherwise set indexed files + else{ + file_sstr << "atoms-coords-sld-" << std::setfill('0') << std::setw(8) << sim::output_atoms_file_counter << "-" << + std::setfill('0') << std::setw(6) << config::internal::io_group_id << ".data"; + } + + // convert stringstream to string + std::string filename = file_sstr.str(); + + // Output informative message to log file on root process + zlog << zTs() << "Outputting configuration file " << std::setfill('0') << std::setw(8) << sim::output_atoms_file_counter << " to disk " << std::flush; + + // 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: + io_time = legacy_atoms(); + 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, &total_output_atoms, 1, MPI_UINT64_T, &status); + + // Calculate local byte offset since MPI-IO is simple and doesn't update the file handle pointer after I/O + MPI_Offset data_offset = config::internal::buffer_offset + sizeof(uint64_t); + + timer.start(); // start timer + + // Write data to disk + MPI_File_write_at_all(fh, data_offset, &config::internal::local_buffer[0], config::internal::local_buffer.size(), MPI_DOUBLE, &status); + //MPI_File_write_ordered(fh, &config::internal::local_buffer[0], config::internal::local_buffer.size(), MPI_DOUBLE, &status); + + timer.stop(); // Stop timer + + // Close file + MPI_File_close(&fh); + + // Calculate elapsed time + io_time = timer.elapsed_time(); + break; + } + + case config::internal::fpprocess: + io_time = write_data(filename, config::internal::local_buffer); + break; + + case config::internal::fpnode: + // Gather data from all processors in io group + MPI_Gatherv(&local_buffer[0], local_buffer.size(), MPI_DOUBLE, &collated_buffer[0], &io_group_recv_counts[0], &io_group_displacements[0], MPI_DOUBLE, io_group_master_id, io_comm); + // output data on master io processes + if(config::internal::io_group_master) io_time = write_data(filename, config::internal::collated_buffer); + 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) + //----------------------------------------------------- + // check for legacy output + if(config::internal::mode == config::internal::legacy) io_time = config::internal::legacy_atoms(); + // otherwise use new one by default + else io_time = write_data(filename, config::internal::local_buffer); + #endif + + // stop total timer + total_timer.stop(); + + // Output bandwidth to log file + zlog << config::internal::io_data_size/io_time << " GB/s in " << io_time << " s [ " << total_timer.elapsed_time() << " s]" << std::endl; + + // increment file counter + //sim::output_atoms_file_counter++; + + return; + +} + +} // end of internal namespace +} // end of config namespace diff --git a/src/config/config.cpp b/src/config/config.cpp index 1f799ab5d..b14ae0d40 100644 --- a/src/config/config.cpp +++ b/src/config/config.cpp @@ -111,10 +111,19 @@ void output(){ // should include variables for data to be outputted, eg spins, c if (config::internal::output_rate_counter_coords == 0) { config::internal::atoms_coords(); + //for Spin-lattice simulations - Modified by Mara Strungaru 2022 + if (config::internal::sld_format) config::internal::atoms_coords_sld();// call function to output coords for dynamic lattice calc + if(atoms::num_non_magnetic_atoms > 0) config::internal::atoms_non_magnetic(); + } config::internal::atoms(); // call function to output spins coords + + //for Spin-lattice simulations - Modified by Mara Strungaru 2022 + if (config::internal::sld_format) config::internal::atoms_coords_sld();// call function to output coords for dynamic lattice calc + config::internal::output_rate_counter_coords++; //update the counter + } // for hysteresis programs else if ((program::program == 2) || (program::program ==3) || (program::program ==12)) diff --git a/src/config/data.cpp b/src/config/data.cpp index 7f1e5c22a..eb5a9442e 100644 --- a/src/config/data.cpp +++ b/src/config/data.cpp @@ -41,6 +41,8 @@ namespace config{ bool output_atoms_config_end = false; // flag to enable atoms output at the end of simulation int output_atoms_config_rate = 1000; // rate to output atoms + bool sld_format = false; // enable SLD output of latice configurations + bool output_cells_config = false; // flag to enable cells output bool output_cells_config_continuous = false; // flag to enable cells output bool output_cells_config_end = false; // flag to enable cells output at the end of simulation diff --git a/src/config/initialize.cpp b/src/config/initialize.cpp index ba75cbb53..58a9a33ad 100644 --- a/src/config/initialize.cpp +++ b/src/config/initialize.cpp @@ -15,6 +15,7 @@ // Vampire headers #include "atoms.hpp" #include "config.hpp" +#include "sld.hpp" #include "vio.hpp" // config module headers @@ -53,17 +54,19 @@ namespace config{ const double cc[3] = {atoms::x_coord_array[atom], atoms::y_coord_array[atom], atoms::z_coord_array[atom]}; - // check atom within output bounds - if ((cc[0] >= minB[0]) && (cc[0] <= maxB[0])) - { - if ((cc[1] >= minB[1]) && (cc[1] <= maxB[1])) + // check atom within local bounds (except for SLD) + if(!sld::enabled){ + if ((cc[0] >= minB[0]) && (cc[0] <= maxB[0])) { - if ((cc[2] >= minB[2]) && (cc[2] <= maxB[2])) + if ((cc[1] >= minB[1]) && (cc[1] <= maxB[1])) { - config::internal::local_output_atom_list.push_back(atom); + if ((cc[2] >= minB[2]) && (cc[2] <= maxB[2])) + { + config::internal::local_output_atom_list.push_back(atom); + } } } - } + } // end of sld check } @@ -111,7 +114,7 @@ namespace config{ // calculate linear integer and 3 vector buffer offsets for my_rank uint64_t rank_offset = 0; - for(unsigned int p=0; p < vmpi::my_rank; p++){ + for(int p=0; p < vmpi::my_rank; p++){ rank_offset += atoms_per_processor[p]; } diff --git a/src/config/interface.cpp b/src/config/interface.cpp index 98a808576..4f773889c 100644 --- a/src/config/interface.cpp +++ b/src/config/interface.cpp @@ -75,6 +75,13 @@ namespace config{ internal::output_atoms_config_rate=i; return EXIT_SUCCESS; } + // + //----------------------------------------- + test="sld"; + if(word==test){ + internal::sld_format = true; + return EXIT_SUCCESS; + } //-------------------------------------------------------------------- test="output-format"; if(word==test){ diff --git a/src/config/internal.hpp b/src/config/internal.hpp index 893f13f44..42a225544 100644 --- a/src/config/internal.hpp +++ b/src/config/internal.hpp @@ -33,7 +33,7 @@ namespace internal { // enumerated integers for option selection - enum format_t{ binary = 0, text = 1}; + enum format_t{ binary = 0, text = 1 }; enum mode_t{ legacy = 0, mpi_io = 1, fpprocess = 2, fpnode = 3}; //------------------------------------------------------------------------- @@ -50,6 +50,8 @@ namespace internal extern bool output_atoms_config_end; // flag to enable output of atomic configurations at end of simulation extern int output_atoms_config_rate; // rate to output atoms + extern bool sld_format; //flag to enable dynamic positions for SLD + extern bool output_cells_config; // flag to enable cells output extern bool output_cells_config_continuous; // flag to enable continuous output of cells configurations extern bool output_cells_config_end; // flag to enable output of cells configurations at end of simulation @@ -107,6 +109,7 @@ namespace internal void atoms(); void atoms_coords(); + void atoms_coords_sld(); void atoms_non_magnetic(); double legacy_atoms(); diff --git a/src/config/makefile b/src/config/makefile index e8d94edfd..7924d4da4 100644 --- a/src/config/makefile +++ b/src/config/makefile @@ -15,6 +15,7 @@ interface.o \ meta.o \ legacy.o \ write_coords.o \ +atoms_coords_sld.o \ write.o # Append module objects to global tree diff --git a/src/config/write.cpp b/src/config/write.cpp index fcccfb788..907ecd54b 100644 --- a/src/config/write.cpp +++ b/src/config/write.cpp @@ -30,6 +30,7 @@ namespace internal // Forward function declarations double write_data_text(std::string filename, const std::vector &buffer); +double write_data_text_sld(std::string filename, const std::vector &buffer_sld); double write_data_binary(std::string filename, const std::vector &buffer); //-------------------------------------------------------------------------------------------------------- @@ -86,6 +87,10 @@ double write_data(std::string filename, const std::vector &buffer){ case config::internal::text: io_time = write_data_text(filename, buffer); + + //case config::internal::text_sld: + // io_time = write_data_text_sld(filename, buffer); + break; } @@ -119,6 +124,7 @@ double write_data_text(std::string filename, const std::vector &buffer){ // output buffer to disk for(unsigned int index = 0; index < data_size; ++index){ + // add to anable high recision output: std::setprecision(17) ofile << buffer[3 * index + 0] << "\t" << buffer[3 * index + 1] << "\t" << buffer[3 * index + 2] << "\n"; diff --git a/src/constants/constants.cpp b/src/constants/constants.cpp index 4818c9e34..4a95d5a51 100644 --- a/src/constants/constants.cpp +++ b/src/constants/constants.cpp @@ -21,6 +21,7 @@ namespace constants{ // fundamental constants const double muB = 9.27400999e-24; // Bohr Magneton (Joules / Tesla) const double kB = 1.3806503e-23; // Boltzmann constant (Joules / Kelvin) + const double kB_eV = 8.61733324e-5; // Boltzmann constant (Joules / Kelvin) // derived constants diff --git a/src/create/agglomorate.cpp b/src/create/agglomorate.cpp new file mode 100644 index 000000000..557887e69 --- /dev/null +++ b/src/create/agglomorate.cpp @@ -0,0 +1,198 @@ +//----------------------------------------------------------------------------- +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) R F L Evans 2017. All rights reserved. +// +//----------------------------------------------------------------------------- + +// C++ standard library headers + +// Vampire headers +#include "create.hpp" +#include "errors.hpp" +#include "grains.hpp" +#include "vio.hpp" + +// Internal create header +#include "internal.hpp" + +namespace create{ + +namespace internal{ + + //------------------------------------------------------------ + // Function to create an agglomorate of nanoparticles + //------------------------------------------------------------ + void agglomorate(std::vector & catom_array){ + + zlog << zTs() << "Generating agglomorate system of grains..." << std::endl; + + //------------------------------------------------ + // load text file with grain positions and radii + //------------------------------------------------ + + // open input file + std::string agg_file_name = "agglomorate.txt"; + std::ifstream ifile; + ifile.open(agg_file_name); + + // check file is open + if(!ifile.is_open()){ + std::cerr << "Error opening file with agglomorate data in file " << agg_file_name << std::endl; + zlog << zTs() << "Error opening file with agglomorate data in file " << agg_file_name << std::endl; + err::vexit(); + } + + // Read number of grains from agglomorate file + int num_grains = 0; + ifile >> num_grains; + + // check number of grains is sensible + if( !( num_grains > 0 ) ){ + std::cerr << "Error with agglomorate file " << agg_file_name << " : number of grain must be at least 1" << std::endl; + zlog << zTs() << "Error with agglomorate file " << agg_file_name << " : number of grain must be at least 1" << std::endl; + err::vexit(); + } + + // Read data from agglomorate file + int ln = 0; + double gx,gy,gz,gr; + std::vector x,y,z,r; // vectors to store data + while(!ifile.eof()){ + + // read in whole line + std::string line; + getline(ifile,line); + + std::stringstream ss(line); + // check for empty lines + if(line != ""){ + + // read variables in each column + ss >> gx >> gy >> gz >> gr; + + // save to vectors + x.push_back(gx); + y.push_back(gy); + z.push_back(gz); + r.push_back(gr); + + std::cout << ln << "\t" << gx << "\t" << gy << "\t" << gz << std::endl; + ln++; + + } + } + + // check actual and expected number of grains is the same + if( num_grains != x.size() ){ + std::cerr << "Error - number of grains in agglomorate file " << x.size() << " is different from expected number of " << num_grains << ". Exiting" << std::endl; + zlog << zTs() << "Error - number of grains in agglomorate file " << x.size() << " is different from expected number of " << num_grains << ". Exiting" << std::endl; + err::vexit(); + } + + // set vector to store particle origin + std::vector particle_origin( 3, 0.0 ); + + // initialise counter to count number of generated particles + int particle_number = 0; + + // loop over all particles to generate local structures + for(int p = 0; p < num_grains; p++){ + + // Determine particle origin, multiplying by system size + particle_origin[0] = x[p] * cs::system_dimensions[0]; + particle_origin[1] = y[p] * cs::system_dimensions[1]; + particle_origin[2] = z[p] * cs::system_dimensions[2]; + + // Determine particle scale + double agg_particle_scale = r[p] * cs::particle_scale; + + // Check to see if a complete particle fits within the system bounds to avoid partial particles + const bool in_px = particle_origin[0] <= (cs::system_dimensions[0] - agg_particle_scale * 0.5); + const bool in_mx = particle_origin[0] >= agg_particle_scale * 0.5; + const bool in_py = particle_origin[1] <= (cs::system_dimensions[1] - agg_particle_scale * 0.5); + const bool in_my = particle_origin[1] >= agg_particle_scale * 0.5; + const bool in_pz = particle_origin[2] <= (cs::system_dimensions[2] - agg_particle_scale * 0.5); + const bool in_mz = particle_origin[2] >= agg_particle_scale * 0.5; + const bool inside = in_px && in_mx && in_py && in_my && in_pz && in_mz; + + // now generate particle of the desired shape if its fully insid the system + // note: could also potentially define agglomorate particles as + // fraction of normal particale size + if(inside){ + + // Use particle type flags to determine which particle shape to cut + // when particla radius is a function parameter + /*switch(cs::system_creation_flags[1]){ + case 0: // Bulk + create::internal::bulk(catom_array); + break; + case 1: // Cube + create::internal::cube(particle_origin,catom_array,particle_number); + break; + case 2: // Cylinder + create::internal::cylinder(particle_origin,catom_array,particle_number); + break; + case 3: // Ellipsoid + create::internal::ellipsoid(particle_origin,catom_array,particle_number); + break; + case 4: // Sphere + create::internal::sphere(particle_origin,catom_array,particle_number); + break; + case 5: // Truncated Octahedron + create::internal::truncated_octahedron(particle_origin,catom_array,particle_number); + break; + case 6: // Teardrop + create::internal::teardrop(particle_origin,catom_array,particle_number); + break; + case 7: // Faceted particle + create::internal::faceted(particle_origin,catom_array,particle_number); + break; + case 8: // Cone + create::internal::cone(particle_origin,catom_array,particle_number); + break; + case 9: // Bubble + create::internal::bubble(particle_origin,catom_array,particle_number); + break; + case 10: // Ellipse + create::internal::ellipse(particle_origin,catom_array,0); + break; + default: + std::cout << "Programmer error: unknown particle type requested for agglomorate particle system" << std::endl; + err::vexit(); + } // end of case statement*/ + + create::internal::sphere_size(particle_origin, catom_array, particle_number, agg_particle_scale); + + + // Increment Particle Number Counter + particle_number++; + + } // end of inside check + + } // end of particle generation loop + + // set number ofgrains to particle number + grains::num_grains = particle_number; + + // Check for no generated particles and print error message + if( particle_number == 0 ){ + zlog << zTs() << "Error: no particles generated in agglomorate." << std::endl; + zlog << zTs() << "Info: Agglomorates require that at least 1 complete particle fits within the system dimensions." << std::endl; + zlog << zTs() << "Info: Increase x and y system dimensions to at least one particle-scale." << std::endl; + err::vexit(); + } + + // Re-order atoms by particle number + create::internal::sort_atoms_by_grain(catom_array); + + zlog << zTs() << " done!" << std::endl; + + return; + } + +} // end of internal namespace + +} // end of create namespace diff --git a/src/create/centre_particle.cpp b/src/create/centre_particle.cpp index d9387037b..4011011a5 100644 --- a/src/create/centre_particle.cpp +++ b/src/create/centre_particle.cpp @@ -78,7 +78,8 @@ void centre_particle_on_atom(std::vector& particle_origin, std::vector & catom_array){ zlog<< zTs() << "Determining atomic composition:" << std::endl; // temporary counter for number of (local) atoms - unsigned int num_atoms = 0; + uint64_t num_atoms = 0; // array to store number of atoms in each material std::vector material_numbers(mp::num_materials,0); diff --git a/src/create/create.cpp b/src/create/create.cpp index 9c2783d78..877d38fc9 100644 --- a/src/create/create.cpp +++ b/src/create/create.cpp @@ -176,6 +176,7 @@ int create(){ // Determine number of local atoms #ifdef MPICF + vmpi::num_local_atoms = vmpi::num_core_atoms+vmpi::num_bdry_atoms; #else // set number of core atoms for serial code (to allow wraper functions to work seamlessly) vmpi::num_core_atoms = atoms::num_atoms; diff --git a/src/create/cs_set_atom_vars2.cpp b/src/create/cs_set_atom_vars2.cpp index deeac4272..e82ef764e 100644 --- a/src/create/cs_set_atom_vars2.cpp +++ b/src/create/cs_set_atom_vars2.cpp @@ -19,8 +19,10 @@ #include "exchange.hpp" #include "neighbours.hpp" #include "random.hpp" +#include "spininitialize.hpp" #include "vio.hpp" #include "vmpi.hpp" +#include "sld.hpp" // Add internal module header file #include "internal.hpp" @@ -58,6 +60,7 @@ void set_atom_vars(std::vector & catom_array, atoms::z_spin_array.resize(atoms::num_atoms,1.0); atoms::m_spin_array.resize(atoms::num_atoms,0.0); + atoms::type_array.resize( atoms::num_atoms,0); atoms::category_array.resize( atoms::num_atoms,0); atoms::grain_array.resize( atoms::num_atoms,0); @@ -68,9 +71,28 @@ void set_atom_vars(std::vector & catom_array, atoms::x_total_spin_field_array.resize(atoms::num_atoms,0.0); atoms::y_total_spin_field_array.resize(atoms::num_atoms,0.0); atoms::z_total_spin_field_array.resize(atoms::num_atoms,0.0); + + // For spin lattice only + if(sld::enabled){ + + atoms::mass_spin_array.resize(atoms::num_atoms,0.0); + + atoms::x_total_spin_forces_array.resize(atoms::num_atoms,0.0); + atoms::y_total_spin_forces_array.resize(atoms::num_atoms,0.0); + atoms::z_total_spin_forces_array.resize(atoms::num_atoms,0.0); + + atoms::x_velo_array.resize(atoms::num_atoms,0.0); + atoms::y_velo_array.resize(atoms::num_atoms,0.0); + atoms::z_velo_array.resize(atoms::num_atoms,0.0); + + } + atoms::x_total_external_field_array.resize(atoms::num_atoms,0.0); atoms::y_total_external_field_array.resize(atoms::num_atoms,0.0); atoms::z_total_external_field_array.resize(atoms::num_atoms,0.0); + atoms::x_thermal_field_array.resize(atoms::num_atoms,0.0); + atoms::y_thermal_field_array.resize(atoms::num_atoms,0.0); + atoms::z_thermal_field_array.resize(atoms::num_atoms,0.0); // atoms::x_dipolar_field_array.resize(atoms::num_atoms,0.0); // atoms::y_dipolar_field_array.resize(atoms::num_atoms,0.0); // atoms::z_dipolar_field_array.resize(atoms::num_atoms,0.0); @@ -98,25 +120,21 @@ void set_atom_vars(std::vector & catom_array, //std::cout << atom << " grain: " << catom_array[atom].grain << std::endl; atoms::grain_array[atom] = catom_array[atom].grain; - // initialise atomic spin positions - // Use a normalised gaussian for uniform distribution on a unit sphere + // initialise atomic spin positions according to the material's initial + // spin texture (uniform vector, random, domain wall, skyrmion, etc.) int mat=atoms::type_array[atom]; double sx,sy,sz; // spins - if(mp::material[mat].random_spins==true){ - sx=mtrandom::gaussianc(random_spin_rng); - sy=mtrandom::gaussianc(random_spin_rng); - sz=mtrandom::gaussianc(random_spin_rng); - } - else{ - sx=mp::material[mat].initial_spin[0]; - sy=mp::material[mat].initial_spin[1]; - sz=mp::material[mat].initial_spin[2]; - } - // now normalise spins - double modS=1.0/sqrt(sx*sx + sy*sy + sz*sz); - atoms::x_spin_array[atom]=sx*modS; - atoms::y_spin_array[atom]=sy*modS; - atoms::z_spin_array[atom]=sz*modS; + + // fractional coordinates of the atom within the system bounding box + const double fx = (cs::system_dimensions[0] > 0.0) ? catom_array[atom].x / cs::system_dimensions[0] : 0.0; + const double fy = (cs::system_dimensions[1] > 0.0) ? catom_array[atom].y / cs::system_dimensions[1] : 0.0; + const double fz = (cs::system_dimensions[2] > 0.0) ? catom_array[atom].z / cs::system_dimensions[2] : 0.0; + + spininitialize::initialize_spin(mat, fx, fy, fz, sx, sy, sz, random_spin_rng); + + atoms::x_spin_array[atom]=sx; + atoms::y_spin_array[atom]=sy; + atoms::z_spin_array[atom]=sz; atoms::m_spin_array[atom]=mp::material[mat].mu_s_SI/9.27400915e-24; // generate list of magnetic atoms @@ -125,6 +143,13 @@ void set_atom_vars(std::vector & catom_array, } + // apply any requested grain-level alternation of the initial spin + // directions (create:grain-magnetisation-direction). This is a single + // post-processing pass over all atoms, separate from the main loop above, + // since it is a relatively uncommon feature and would otherwise add an + // unnecessary per-atom branch to the much hotter initialisation loop. + spininitialize::apply_grain_magnetisation_mode(); + //--------------------------------------------------------------------------- // Identify surface atoms and initialise anisotropy data //--------------------------------------------------------------------------- diff --git a/src/create/data.cpp b/src/create/data.cpp index 11a3c4f20..6325a1aae 100644 --- a/src/create/data.cpp +++ b/src/create/data.cpp @@ -41,19 +41,21 @@ namespace create{ double faceted_particle_111_radius = 1.0; // 111 facet particle radius double cone_angle = 10.0; // factor to truncate cone - double voronoi_grain_size = 50.0; - double voronoi_grain_spacing = 10.0; - double bubble_radius = 0.3333; double bubble_nucleation_height = 0.0; bool generate_voronoi_substructure = false; + double voronoi_grain_substructure_size = 50.0; // mean grain size of the substructure within a particle + double voronoi_grain_substructure_spacing = 10.0; // spacing between substructure grains double voronoi_grain_substructure_crystallization_radius = 1.2; double voronoi_grain_substructure_overlap_factor = 1.0; - bool grain_poission = false; + + double voronoi_elliptical_rounding = 0.0; // 0.0 = vertical grain walls (default), 1.0 = ellipsoidal grains + double voronoi_elliptical_rounding_height = 0.5; // centre height of system bool select_material_by_geometry = false; // Toggle override of input material type by geometry bool select_material_by_z_height = false; // Toggle overwriting of material id by z-height + bool output_gv_file = true; // toggle output of grain positions to file } // end of internal namespace diff --git a/src/create/generate_crystal.cpp b/src/create/generate_crystal.cpp index 637f9f4eb..922a2f08b 100644 --- a/src/create/generate_crystal.cpp +++ b/src/create/generate_crystal.cpp @@ -43,12 +43,12 @@ int create_crystal_structure(std::vector & catom_array){ #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]); + min_bounds[0] = int(vmpi::min_dimensions[0]/unit_cell.dimensions[0]) - 1; + min_bounds[1] = int(vmpi::min_dimensions[1]/unit_cell.dimensions[1]) - 1; + min_bounds[2] = int(vmpi::min_dimensions[2]/unit_cell.dimensions[2]) - 1; + max_bounds[0] = vmath::iceil(vmpi::max_dimensions[0]/unit_cell.dimensions[0]) + 1; + max_bounds[1] = vmath::iceil(vmpi::max_dimensions[1]/unit_cell.dimensions[1]) + 1; + max_bounds[2] = vmath::iceil(vmpi::max_dimensions[2]/unit_cell.dimensions[2]) + 1; } else{ min_bounds[0]=0; @@ -88,6 +88,8 @@ int create_crystal_structure(std::vector & catom_array){ std::vector inc_uc_atom(mp::max_materials, false); for( auto m : create::internal::mp) inc_uc_atom[m.unit_cell_category] = true; + // This has been modified by Mara Strungaru to go from x to z, rather than z to x + // unmodified by RE as this is less optimal for large lateral samples in x,y which is a more common usage pattern in ASD // Duplicate unit cell for(int z=min_bounds[2];z & catom_array){ (cz>=vmpi::min_dimensions[2] && cz= 0.0 && cx < cs::system_dimensions[0] && + cy >= 0.0 && cy < cs::system_dimensions[1] && + cz >= 0.0 && cz < cs::system_dimensions[2] ){ catom_array.push_back(cs::catom_t()); catom_array[atom].x=cx; diff --git a/src/create/geometry.cpp b/src/create/geometry.cpp index 899fb7e68..b6c6930eb 100644 --- a/src/create/geometry.cpp +++ b/src/create/geometry.cpp @@ -149,7 +149,7 @@ void create::internal::geometry(std::vector& catom_array){ // make sure atoms are within their material heights, is in the polygon, and has the same unit cell category as the corresponding atom if(z >= mat_min[mat] && z < mat_max[mat] && vmath::point_in_polygon2(x,y,px,py,geo) && - catom_array[atom].uc_category == static_cast(create::internal::mp[mat].unit_cell_category) && // make sure unit cell category is preserved + catom_array[atom].uc_category == create::internal::mp[mat].unit_cell_category && // make sure unit cell category is preserved create::internal::mp[mat].geometry // define by geometry ){ catom_array[atom].material=mat; diff --git a/src/create/interface.cpp b/src/create/interface.cpp index 6ce07ad04..324856fbe 100644 --- a/src/create/interface.cpp +++ b/src/create/interface.cpp @@ -12,6 +12,7 @@ // Vampire headers #include "errors.hpp" #include "create.hpp" +#include "spininitialize.hpp" #include "vio.hpp" #include "voronoi.hpp" #include "random.hpp" @@ -123,13 +124,16 @@ namespace create{ cs::system_creation_flags[2]=0; return true; } - else + test="agglomorate"; + if(word==test){ + cs::system_creation_flags[2] = 5; + return true; + } test="particle-array"; if(word==test){ cs::system_creation_flags[2]=1; return true; } - else test="hexagonal-particle-array"; if(word==test){ cs::system_creation_flags[2]=2; @@ -191,6 +195,52 @@ namespace create{ return true; } //------------------------------------------------------------------- + test="voronoi-elliptical-rounding"; + if(word==test || word == "elliptical-rounding"){ + double er=atof(value.c_str()); + vin::check_for_valid_value(er, word, line, prefix, unit, "none", 0.0, 1.0,"input","0.0 - 1.0"); + create::internal::voronoi_elliptical_rounding=er; + return true; + } + //------------------------------------------------------------------- + test="voronoi-elliptical-rounding-height"; + if(word==test || word == "elliptical-rounding-height"){ + double erh=atof(value.c_str()); + vin::check_for_valid_value(erh, word, line, prefix, unit, "none", 0.0, 1.0,"input","0.0 - 1.0"); + create::internal::voronoi_elliptical_rounding_height=erh; + return true; + } + //------------------------------------------------------------------- + test="voronoi-bimodal-grains"; + if(word==test){ + create_voronoi::bimodal_grains=true; + return true; + } + //------------------------------------------------------------------- + test="voronoi-small-grain-diameter"; + if(word==test){ + double sgd=atof(value.c_str()); + vin::check_for_valid_value(sgd, word, line, prefix, unit, "length", 0.1, 1.0e7,"input","0.1 Angstroms - 1 millimetre"); + create_voronoi::small_grain_diameter=sgd; + return true; + } + //------------------------------------------------------------------- + test="voronoi-small-grain-fraction"; + if(word==test){ + double sgf=atof(value.c_str()); + vin::check_for_valid_value(sgf, word, line, prefix, unit, "none", 0.0, 1.0,"input","0.0 - 1.0"); + create_voronoi::small_grain_fraction=sgf; + return true; + } + //------------------------------------------------------------------- + test="voronoi-small-grain-size-variance"; + if(word==test){ + double sgv=atof(value.c_str()); + vin::check_for_valid_value(sgv, word, line, prefix, unit, "none", 0.0, 1.0,"input","0.0 - 1.0"); + create_voronoi::small_grain_size_variance=sgv; + return true; + } + //------------------------------------------------------------------- test="particle-centre-offset"; //parity if(word==test){ cs::particle_creation_parity=1; @@ -324,6 +374,36 @@ namespace create{ } } //-------------------------------------------------------------------- + // create:grain-magnetisation-direction = material | alternating + // + // Controls how the initial spin direction (as set by + // material[#]:initial-spin-direction, see the spininitialize module) + // is applied across grains: + // - "material" (default): every atom is initialised exactly as + // specified by its material's texture, with no further + // modification. + // - "alternating": the spin direction is reversed (negated) for + // every atom belonging to an odd-numbered grain (grains are + // numbered from 0), giving neighbouring grains opposite + // magnetisation directions. This is useful e.g. for setting up + // antiferromagnetically-coupled grain structures or testing + // domain-wall pinning at grain boundaries. + //-------------------------------------------------------------------- + test="grain-magnetisation-direction"; + if(word==test){ + std::string loctest="alternating"; + if(value==loctest){ + spininitialize::set_grain_magnetisation_mode(1); // grain_mode_alternating + return true; + } + else{ + // default: "material" (or any unrecognised value falls back to + // the default of no grain-level post-processing) + spininitialize::set_grain_magnetisation_mode(0); // grain_mode_material + return true; + } + } + //-------------------------------------------------------------------- test="interfacial-roughness-seed-radius"; if(word==test){ double irsr=atof(value.c_str()); @@ -379,12 +459,6 @@ namespace create{ return true; } //-------------------------------------------------------------------- - test="grains-poission"; //parity - if(word==test){ - create::internal::grain_poission=true; - return true; - } - //-------------------------------------------------------------------- test="height-categorization"; if(word==test){ // Test for different options @@ -427,20 +501,20 @@ namespace create{ return true; } //-------------------------------------------------------------------- - test="voronoi-grain-size"; + test="voronoi-grain-substructure-size"; if(word==test){ double psize=atof(value.c_str()); vin::check_for_valid_value(psize, word, line, prefix, unit, "length", 0.1, 1.0e7,"input","0.1 Angstroms - 1 millimetre"); - create::internal::voronoi_grain_size=psize; + create::internal::voronoi_grain_substructure_size=psize; return true; } else //-------------------------------------------------------------------- - test="voronoi-grain-spacing"; + test="voronoi-grain-substructure-spacing"; if(word==test){ double pspacing=atof(value.c_str()); vin::check_for_valid_value(pspacing, word, line, prefix, unit, "length", 0.0, 1.0e7,"input","0.0 Angstroms - 1 millimetre"); - create::internal::voronoi_grain_spacing=pspacing; + create::internal::voronoi_grain_substructure_spacing=pspacing; return true; } //-------------------------------------------------------------------- diff --git a/src/create/internal.hpp b/src/create/internal.hpp index 45d170a09..a1a41ee00 100644 --- a/src/create/internal.hpp +++ b/src/create/internal.hpp @@ -129,20 +129,21 @@ namespace create{ extern double faceted_particle_111_radius; // 111 facet radius extern double cone_angle; // angle of cone to truncate cylinder - extern double voronoi_grain_size; - extern double voronoi_grain_spacing; - extern double bubble_radius; extern double bubble_nucleation_height; extern bool generate_voronoi_substructure; + extern double voronoi_grain_substructure_size; // mean grain size of the substructure within a particle + extern double voronoi_grain_substructure_spacing; // spacing between substructure grains extern double voronoi_grain_substructure_crystallization_radius; extern double voronoi_grain_substructure_overlap_factor; - extern bool grain_poission; + extern double voronoi_elliptical_rounding; // degree of elliptical grain rounding (0-1) + extern double voronoi_elliptical_rounding_height; // height of widest grain cross-section as a fraction of system size z extern bool select_material_by_geometry; // Toggle override of input material type by geometry extern bool select_material_by_z_height; + extern bool output_gv_file; // toggle output of grain positions to file //----------------------------------------------------------------------------- // Internal functions for create module @@ -164,9 +165,11 @@ namespace create{ extern void faceted(std::vector& particle_origin, std::vector & catom_array, const int grain); extern void geometry(std::vector& catom_array); extern void sphere(std::vector& particle_origin, std::vector & catom_array, const int grain); + extern void sphere_size(std::vector& particle_origin, std::vector & catom_array, const int grain, const double agg_particle_scale); extern void teardrop(std::vector& particle_origin, std::vector & catom_array, const int grain); extern void truncated_octahedron(std::vector& particle_origin, std::vector & catom_array, const int grain); + extern void agglomorate(std::vector & catom_array); extern void particle(std::vector &); extern void particle_array(std::vector &); extern void hex_particle_array(std::vector &); @@ -179,9 +182,21 @@ namespace create{ void voronoi_grain_rounding(std::vector > & grain_coord_array, std::vector > > & grain_vertices_array); + double elliptical_rounding_factor(const double z); + void populate_vertex_points(std::vector > & grain_coord_array, std::vector > > & grain_vertices_array, - bool include_boundary_grains); + bool include_boundary_grains, + const double grain_size); + + int generate_bimodal_voronoi_seeds(std::vector >& grain_coord_array, + std::vector& is_small_grain, + const double domain_x, const double domain_y, + const double std_diameter, const double std_variance, + const double small_diameter, const double small_variance, + const double small_fraction); + + extern void write_grain_vertices(int id, double dx, double dy, std::ofstream& ofile, std::vector< std::vector >& vertices); extern bool compare_radius(core_radius_t first,core_radius_t second); extern void calculate_atomic_composition(std::vector & catom_array); diff --git a/src/create/makefile b/src/create/makefile index 2936d3a4c..e6900bb2f 100644 --- a/src/create/makefile +++ b/src/create/makefile @@ -4,8 +4,7 @@ # List module object filenames create_objects=\ -create.o \ -cs_set_atom_vars2.o \ +agglomorate.o \ alloy.o \ bubble.o \ bulk.o \ @@ -13,6 +12,8 @@ centre_particle.o \ cone.o \ compare_radius.o \ composition.o \ +create.o \ +cs_set_atom_vars2.o \ cube.o \ cylinder.o \ data.o \ @@ -32,14 +33,18 @@ particle.o \ roughness.o \ sort_atoms_by_grain.o \ sphere.o \ +sphere_size.o \ square_array.o \ system_type.o \ teardrop.o \ truncated_octahedron.o \ voronoi.o \ +voronoi_bimodal_seeds.o \ +voronoi_elliptical_rounding.o \ voronoi_grain_rounding.o \ voronoi_substructure.o \ -voronoi_vertex_points.o +voronoi_vertex_points.o \ +write_grain_vertices.o # Append module objects to global tree OBJECTS+=$(addprefix obj/create/,$(create_objects)) diff --git a/src/create/mpi.cpp b/src/create/mpi.cpp index 521d707d3..8beed702d 100644 --- a/src/create/mpi.cpp +++ b/src/create/mpi.cpp @@ -21,6 +21,7 @@ #include "create.hpp" #include "material.hpp" #include "errors.hpp" +#include "sld.hpp" #include "vio.hpp" #include "vmpi.hpp" #include "vutil.hpp" @@ -28,9 +29,6 @@ #ifdef MPICF - - - namespace create{ namespace internal{ @@ -147,7 +145,11 @@ namespace create{ std::vector cpu_range_array(6*vmpi::num_processors,0.0); // Linear Memory for MPI comms // Determine range+interaction range of all CPU's - double max_interaction_range=double(cs::unit_cell.interaction_range); + // Modified M Strungaru + // commented out by RE - this was a bugfix - why now removed? + //double max_interaction_range=double(cs::unit_cell.interaction_range); + // Determine range+interaction range of all CPU's (need +1 for interactions at different ends of the unit cell) + double max_interaction_range=double(cs::unit_cell.interaction_range+1); // Populate local ranges cpu_range_array[6*vmpi::my_rank+0]=vmpi::min_dimensions[0] - max_interaction_range*cs::unit_cell.dimensions[0]-0.01; @@ -345,7 +347,8 @@ namespace create{ }}}*/ for(int cpu=0;cpu num_CPUS - if(catom_array.size() recv_counter_array(vmpi::num_processors); @@ -877,6 +884,8 @@ namespace create{ // Resize translation and data arrays vmpi::send_atom_translation_array.resize(num_boundary_swaps); vmpi::send_spin_data_array.resize(3*num_boundary_swaps); + vmpi::send_coord_data_array.resize(3*num_boundary_swaps); + std::vector recv_data(num_send_data); // Send and receive atom numbers requested/to be sent requests.resize(0); diff --git a/src/create/sphere_size.cpp b/src/create/sphere_size.cpp new file mode 100644 index 000000000..a58e814ba --- /dev/null +++ b/src/create/sphere_size.cpp @@ -0,0 +1,107 @@ +//----------------------------------------------------------------------------- +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) R F L Evans 2026. All rights reserved. +// +//----------------------------------------------------------------------------- + +// C++ standard library headers +#include + +// Vampire headers +#include "create.hpp" + +// Internal create header +#include "internal.hpp" + +namespace create{ + +namespace internal{ + + //--------------------------------------------------------------------------- + // Special version of spherical particle maker for agglomorates with + // variable particle size + //--------------------------------------------------------------------------- + void sphere_size(std::vector& particle_origin, + std::vector & catom_array, + const int grain, + const double agg_particle_scale + ){ + + // Set particle radius + double particle_radius_squared = (agg_particle_scale*0.5)*(agg_particle_scale*0.5); + + // Loop over all atoms and mark atoms in sphere + const int num_atoms = catom_array.size(); + + // determine order for core-shell particles + std::list material_order(0); + for(int mat=0;mat mat_min(mp::num_materials); + std::vector mat_max(mp::num_materials); + std::vector mat_cssize(mp::num_materials); + std::vector uc_cat(mp::num_materials); // array of material -> unit cell material associations + + for(int mat=0;mat0.0){ + // Iterate over materials + for(std::list::iterator it = material_order.begin(); it != material_order.end(); it++){ + int mat = (it)->mat; + double my_radius = mat_cssize[mat]; + double max_range = my_radius*my_radius*particle_radius_squared; + double maxz=mat_max[mat]; + double minz=mat_min[mat]; + + // check for within core shell range + if(range_squared<=max_range){ + if((cz>=minz) && (cz & catom_array){ err::vexit(); break; + case 5: // Agglomorate + internal::agglomorate(catom_array); + break; + default:{ - std::cerr << "Unknown system type requested, exiting" << std::endl; + std::cerr << "Unknown system type requested in create module, exiting" << std::endl; err::vexit(); } } diff --git a/src/create/voronoi.cpp b/src/create/voronoi.cpp index 8ce48dc58..32ebac8d7 100644 --- a/src/create/voronoi.cpp +++ b/src/create/voronoi.cpp @@ -40,6 +40,11 @@ namespace create_voronoi{ double area_cutoff=0.8; double voronoi_sd=0.15; /// Standard Deviation of voronoi grains bool include_boundary_grains_real = false; + + bool bimodal_grains = false; + double small_grain_diameter = 20.0; // 2.0 nm in Angstroms + double small_grain_size_variance = -1.0; // sentinel: fall back to voronoi_sd + double small_grain_fraction = 0.3; } namespace cs{ @@ -60,7 +65,7 @@ int voronoi_film(std::vector & catom_array){ 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 size = cs::particle_scale + cs::particle_spacing; double grain_cell_size_x = size; double grain_cell_size_y = sqrt(3.0)*size; @@ -88,178 +93,42 @@ int voronoi_film(std::vector & catom_array){ int vp=int(create_voronoi::parity); int grain = 0; + + // parallel flag array recording which population each seed belongs to + // (only populated/used when create_voronoi::bimodal_grains is set) + std::vector is_small_grain; + // -------------------------------------------------------------------------- - // poisson distribution + // seed point generation // -------------------------------------------------------------------------- - if (create::internal::grain_poission){ - - std::ofstream file; - file.open("dist"); - - int sdx = cs::system_dimensions[0]; - int sdy = cs::system_dimensions[1]; - - //std::random_device rd; - std::mt19937 gen(12345); - //variance = exp ( 2.0 * mu + sigma * sigma ) * ( exp ( sigma * sigma ) - 1.0 ); - //mean = exp ( mu + 0.5 * sigma * sigma ); - std::lognormal_distribution<> d(log(grain_cell_size_x), grain_sd ); - std::cout << grain_cell_size_x << "\t" << grain_sd<< std::endl; - double initial_grain_pos_x = sdx/2.0; - double initial_grain_pos_y = sdy/2.0; - double initial_grain_r = d(gen)/2.0; - while (initial_grain_r > 2*grain_cell_size_x){ - initial_grain_r = d(gen)/2.0; + if (create_voronoi::bimodal_grains){ + + // validate ordering of the two populations + if(create_voronoi::small_grain_diameter >= cs::particle_scale){ + terminaltextcolor(RED); + std::cerr << "Error! create:voronoi-small-grain-diameter must be smaller than " + << "dimensions:particle-size for bimodal voronoi grains." << std::endl; + terminaltextcolor(WHITE); + zlog << zTs() << "Error! create:voronoi-small-grain-diameter must be smaller than " + << "dimensions:particle-size for bimodal voronoi grains." << std::endl; + err::vexit(); } - int maxattempts1 = 10; - int maxattempts2 = 1000; - - std::vector grains_x; - std::vector grains_y; - std::vector grains_r; - std::vector active; - grain=0; - grains_x.push_back(initial_grain_pos_x); - grains_y.push_back(initial_grain_pos_y); - grains_r.push_back(initial_grain_r); - active.push_back(true); - grain_coord_array.push_back(std::vector ()); - grain_vertices_array.push_back(std::vector >()); - grain_coord_array[grain].push_back(initial_grain_pos_x); - grain_coord_array[grain].push_back(initial_grain_pos_y); - int num_active_grains = 1; - grain++; - double PI = 3.14159265; - file << initial_grain_pos_x << '\t' << initial_grain_pos_y << '\t' << initial_grain_r << std::endl; - for (int attempt = 0; attempt < maxattempts1; attempt ++){ - for (size_t g =0; g < grains_x.size() ; g++){ - double r = d(gen)/2.0; - // std::cout << r << "\t" << grain_cell_size_x+ grain_sd*grain_cell_size_x << "\t" << grain_cell_size_x << std::endl; - while (r > grain_cell_size_x + grain_sd*grain_cell_size_x || r < grain_cell_size_x - grain_sd*grain_cell_size_x){ - r = d(gen)/2.0; - // std::cout << "A" <= 0 - 2*grain_cell_size_x && y >= 0 - 2*grain_cell_size_y){ - int within =0; - for (size_t grain = 0; grain < grains_x.size(); grain ++ ){ - double dx2 = grains_x[grain] - x; - double dy2 = grains_y[grain] - y; - double dist = sqrt(dx2*dx2 + dy2*dy2); - if (dist < grains_r[grain] + r){ - within = 1; - break; - } - } - if (within ==0){ - bool xmove = true; - bool ymove = true; - double tempx = x; - double tempy = y; - while (xmove || ymove){ - if (x > sdx/2.0) tempx = tempx - 1; - else tempx = tempx + 1; - for (size_t grain2 = 0; grain2 < grains_x.size(); grain2 ++ ){ - double dx2 = grains_x[grain2] - tempx; - double dy2 = grains_y[grain2] - tempy; - double dist = sqrt(dx2*dx2 + dy2*dy2); - if (dist < grains_r[grain2] + r){ - xmove = false; - break; - } - } - if (y > sdy/2.0) tempy = tempy - 1; - else tempy = tempy + 1; - for (size_t grain2 = 0; grain2 < grains_x.size(); grain2 ++ ){ - double dx2 = grains_x[grain2] - tempx; - double dy2 = grains_y[grain2] - tempy; - double dist = sqrt(dx2*dx2 + dy2*dy2); - if (dist < grains_r[grain2] + r){ - ymove = false; - break; - } - } - if (xmove) x = tempx; - if (ymove) y = tempy; - } + const double small_variance = (create_voronoi::small_grain_size_variance < 0.0) ? + create_voronoi::voronoi_sd : create_voronoi::small_grain_size_variance; - //file << grain << '\t' << x/10 << '\t' << y/10 << '\t' << r/10 << std::endl; - grains_x.push_back(x); - file << x << '\t' << y << '\t' << r << std::endl; - grain_coord_array.push_back(std::vector ()); - grain_vertices_array.push_back(std::vector >()); - grain_coord_array[grain].push_back(x); - grain_coord_array[grain].push_back(y); - grains_y.push_back(y); - grains_r.push_back(r); - active.push_back(true); - num_active_grains ++; - grain++; - added = true; - } - } - } - } - } - double sumV = 0; - double sumR = 0; - for (size_t i = 0; i < grains_x.size(); i ++){ - double r = grains_r[i]; - double V = 3.14*r*r; - sumV = sumV +V; - sumR = sumR +r; - } - //------------------------------------------------------------------------ - // output voroni statistics to screen - //------------------------------------------------------------------------ - double avR = sumR/grains_x.size(); - std::sort(grains_r.begin(),grains_r.end()); - int index = grains_r.size()/2; - double Mr = (grains_r[index-1] + grains_r[index])/2; - std::cout<< "Median grain radius: " << Mr << std::endl; - std::cout<< "Mean grain radius: " << avR << std::endl; - //------------------------------------------------------------------------ - double totalV = (2*sdx + 2*grain_cell_size_x)*(2*sdy + 2*grain_cell_size_y); - double frac = sumV/totalV; - std::cout<< " frac: " << frac << std::endl; - std::cout << grain_cell_size_x/2.0 - avR << "\t" << 2.0*(grain_cell_size_x/2.0 - avR)/grain_cell_size_x << std::endl; - frac = frac + (grain_cell_size_x/2.0 - avR)/grain_cell_size_x; - // std::cout << sumV << '\t' << totalV << '\t' << frac << "\t" << grains_x.size() << '\t'<< grain_coord_array.size() << std::endl; - - for (size_t i = 0; i < grain_coord_array.size(); i ++){ - // double r = grains_r[i]; - grain_coord_array[i][0] = grain_coord_array[i][0]* frac; - grain_coord_array[i][1] = grain_coord_array[i][1]* frac; - // sd = sd + sqrt(r*r - Mr*Mr); - // file << grain << '\t' << grain_coord_array[i][0]/10 << '\t' << grain_coord_array[i][1]/10 << '\t' << grains_r[i]/10 << std::endl; - } + grain = create::internal::generate_bimodal_voronoi_seeds( + grain_coord_array, is_small_grain, + cs::system_dimensions[0], cs::system_dimensions[1], + cs::particle_scale, create_voronoi::voronoi_sd, + create_voronoi::small_grain_diameter, small_variance, + create_voronoi::small_grain_fraction); + + // pre-size the (still-empty) per-grain vertex array to match + grain_vertices_array.resize(grain_coord_array.size()); - } // end of Poisson version + } else{ //Calculate pointers @@ -308,10 +177,6 @@ int voronoi_film(std::vector & catom_array){ } - - - // ----------------------- end of old version ------------------------------------- - //----------------------- // Check for grains >=1 //----------------------- @@ -324,11 +189,11 @@ int voronoi_film(std::vector & catom_array){ } // Calculate Voronoi construction using qhull removing boundary grains (false) - create::internal::populate_vertex_points(grain_coord_array, grain_vertices_array, true); + create::internal::populate_vertex_points(grain_coord_array, grain_vertices_array, true, cs::particle_scale); // 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); + double shrink_factor = cs::particle_scale/(cs::particle_scale+cs::particle_spacing); // Reduce vertices to relative coordinates for(unsigned int grain=0;grain & catom_array){ // round grains if necessary if(create_voronoi::rounded) create::internal::voronoi_grain_rounding(grain_coord_array, grain_vertices_array); + // report realised grain-size statistics for bimodal grains, since the RSA + // packer only approximately hits the requested diameters/fraction + if(create_voronoi::bimodal_grains){ + + int placed_small = 0; + int placed_std = 0; + double sum_diameter_small = 0.0; + double sum_diameter_std = 0.0; + + for(unsigned int grain=0; grain0 ? double(placed_small)/double(placed) : 0.0; + const double mean_diameter_small = placed_small>0 ? sum_diameter_small/placed_small : 0.0; + const double mean_diameter_std = placed_std>0 ? sum_diameter_std/placed_std : 0.0; + + std::cout << "Bimodal voronoi grains: requested (std, small) = (" << + cs::particle_scale/10.0 << ", " << create_voronoi::small_grain_diameter/10.0 << + ") nm, fraction small = " << create_voronoi::small_grain_fraction << std::endl; + std::cout << " Placed " << placed << " grains (" << placed_std << " standard, " << + placed_small << " small, realised fraction " << realised_fraction << ")" << std::endl; + std::cout << " Measured mean equivalent diameter: standard = " << mean_diameter_std/10.0 << + " nm, small = " << mean_diameter_small/10.0 << " nm" << std::endl; + + zlog << zTs() << "Bimodal voronoi grains: requested (std, small) = (" << + cs::particle_scale/10.0 << ", " << create_voronoi::small_grain_diameter/10.0 << + ") nm, fraction small = " << create_voronoi::small_grain_fraction << std::endl; + zlog << zTs() << " Placed " << placed << " grains (" << placed_std << " standard, " << + placed_small << " small, realised fraction " << realised_fraction << ")" << std::endl; + zlog << zTs() << " Measured mean equivalent diameter: standard = " << mean_diameter_std/10.0 << + " nm, small = " << mean_diameter_small/10.0 << " nm" << std::endl; + } + std::vector R_med; // Create a 2D supercell array of atom numbers to improve performance for systems with many grains @@ -394,13 +316,27 @@ int voronoi_film(std::vector & catom_array){ // sort by increasing radius material_order.sort(create::internal::compare_radius); + // determine whether elliptical grain rounding is active + const bool elliptical_rounding = create::internal::voronoi_elliptical_rounding > 0.0; + std::cout <<"Generating Voronoi Grains" << std::flush; zlog << zTs() << "Generating Voronoi Grains" << std::flush; + if(elliptical_rounding){ + zlog << zTs() << "Applying elliptical grain rounding with rounding factor " << + create::internal::voronoi_elliptical_rounding << " about height fraction " << + create::internal::voronoi_elliptical_rounding_height << std::endl; + } // arrays to store list of grain vertices double tmp_grain_pointx_array[max_vertices]; double tmp_grain_pointy_array[max_vertices]; + // optionally output grain vertices to file + std::ofstream gvfile; + if(create::internal::output_gv_file && vmpi::master){ + gvfile.open("grain_shapes.txt"); + } + // loop over all grains with vertices for(unsigned int grain=0;grain & catom_array){ } if(grain_vertices_array[grain].size()!=0){ + if(create::internal::output_gv_file){ + const double dx = grain_coord_array[grain][0]; + const double dy = grain_coord_array[grain][1]; + create::internal::write_grain_vertices(grain, dx, dy, gvfile, grain_vertices_array[grain]); + } + // initialise minimum and max supercell coordinates for grain int minx=10000000; int maxx=0; @@ -448,6 +390,12 @@ int voronoi_film(std::vector & catom_array){ double x = catom_array[atom].x; double y = catom_array[atom].y; + // compute in-plane scale factor for elliptical grain rounding + const double erf = create::internal::elliptical_rounding_factor(catom_array[atom].z); + + // skip atoms where the grain cross-section has closed completely + if(erf <= 0.0) continue; + if(mp::material[catom_array[atom].material].core_shell_size>0.0){ // Iterate over materials for(std::list::iterator it = material_order.begin(); it != material_order.end(); it++){ @@ -459,7 +407,13 @@ int voronoi_film(std::vector & catom_array){ 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){ + // note: point_in_polygon_factor() scales the polygon by factor^2; this + // legacy behaviour is retained when elliptical rounding is inactive so + // that existing core-shell structures are unchanged + const bool in_grain = elliptical_rounding ? + vmath::point_in_polygon_scaled(x-x0, y-y0, factor*erf, tmp_grain_pointx_array, tmp_grain_pointy_array, num_vertices) : + vmath::point_in_polygon_factor(x-x0, y-y0, factor, tmp_grain_pointx_array, tmp_grain_pointy_array, num_vertices); + if(in_grain){ if((cz>=minz) && (cz & catom_array){ } } } - // 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){ + // Check to see if site is within the (optionally elliptically rounded) grain + else if(vmath::point_in_polygon_scaled(x-x0,y-y0,erf,tmp_grain_pointx_array,tmp_grain_pointy_array,num_vertices)==true){ catom_array[atom].include=true; catom_array[atom].grain=grain; } diff --git a/src/create/voronoi_bimodal_seeds.cpp b/src/create/voronoi_bimodal_seeds.cpp new file mode 100644 index 000000000..a1503e1e4 --- /dev/null +++ b/src/create/voronoi_bimodal_seeds.cpp @@ -0,0 +1,194 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include + +// Vampire headers +#include "random.hpp" + +// create module headers +#include "internal.hpp" + +namespace create{ +namespace internal{ + +namespace{ + + // background grid used for O(1) amortised neighbour queries during + // random sequential addition (RSA) disc packing + class neighbour_grid_t{ + + public: + + neighbour_grid_t(const double xmin, const double ymin, + const double xmax, const double ymax, + const double cell_size): + xmin_(xmin), ymin_(ymin), cell_size_(cell_size) + { + nx_ = std::max(1, int(std::ceil((xmax - xmin) / cell_size))); + ny_ = std::max(1, int(std::ceil((ymax - ymin) / cell_size))); + cells_.resize(nx_ * ny_); + } + + // insert seed index i with coordinates (x,y) and radius r + void insert(const double x, const double y, const int index){ + cells_[cell_index(x, y)].push_back(index); + } + + // returns true if a disc at (x,y) with radius r overlaps any + // already-inserted seed, i.e. dist(seed) < r + r_seed + bool collides(const double x, const double y, const double r, + const std::vector& xs, const std::vector& ys, + const std::vector& rs) const { + + const int cx = cell_x(x); + const int cy = cell_y(y); + + for(int j = cy - 1; j <= cy + 1; j++){ + if(j < 0 || j >= ny_) continue; + for(int i = cx - 1; i <= cx + 1; i++){ + if(i < 0 || i >= nx_) continue; + const std::vector& cell = cells_[i + j * nx_]; + for(size_t k = 0; k < cell.size(); k++){ + const int idx = cell[k]; + const double dx = xs[idx] - x; + const double dy = ys[idx] - y; + const double min_dist = r + rs[idx]; + if(dx*dx + dy*dy < min_dist*min_dist) return true; + } + } + } + return false; + } + + private: + + int cell_x(const double x) const { + int i = int((x - xmin_) / cell_size_); + if(i < 0) i = 0; + if(i >= nx_) i = nx_ - 1; + return i; + } + + int cell_y(const double y) const { + int j = int((y - ymin_) / cell_size_); + if(j < 0) j = 0; + if(j >= ny_) j = ny_ - 1; + return j; + } + + int cell_index(const double x, const double y) const { + return cell_x(x) + cell_y(y) * nx_; + } + + double xmin_, ymin_, cell_size_; + int nx_, ny_; + std::vector< std::vector > cells_; + + }; + +} // end of anonymous namespace + +//----------------------------------------------------------------------------- +// Generates seed points for a bimodal (two grain-size population) Voronoi +// film using random sequential addition (RSA) disc packing on a background +// grid for fast neighbour queries. Seeds are appended to grain_coord_array +// (which may already contain entries) and a parallel is_small_grain flag is +// appended to record which population each accepted seed belongs to. Returns +// the number of seeds placed. +//----------------------------------------------------------------------------- +int generate_bimodal_voronoi_seeds(std::vector >& grain_coord_array, + std::vector& is_small_grain, + const double domain_x, const double domain_y, + const double std_diameter, const double std_variance, + const double small_diameter, const double small_variance, + const double small_fraction){ + + const double r_std = 0.5 * std_diameter; + const double r_small = 0.5 * small_diameter; + const double r_max = std::max(r_std, r_small); + + const double pad = 2.0 * r_max; + + const double xmin = -pad; + const double xmax = domain_x + pad; + const double ymin = -pad; + const double ymax = domain_y + pad; + + const double cell_size = r_std + r_small; + + neighbour_grid_t grid(xmin, ymin, xmax, ymax, cell_size); + + std::vector xs; + std::vector ys; + std::vector rs; + + const int FAILURE_LIMIT = 5000; + int consecutive_failures = 0; + int placed = 0; + int placed_small = 0; + + while(consecutive_failures < FAILURE_LIMIT){ + + // choose which population to attempt next based on which is currently + // furthest below its target share, rather than an independent coin + // flip per attempt. A per-attempt Bernoulli draw tracks the requested + // fraction poorly here: small grains need much less clearance than + // standard ones, so they keep succeeding long after standard grains + // have effectively jammed out, and the realised fraction drifts far + // above the request (observed ~0.3 requested -> ~0.85 realised on a + // typical 5nm/2nm film). Always attempting the currently-deficient + // population keeps the realised fraction close to the request, at the + // cost of placing fewer total grains once the larger population jams. + const bool is_small = (double(placed_small) < small_fraction * double(placed + 1)); + const double mean_d = is_small ? small_diameter : std_diameter; + const double sigma = is_small ? small_variance : std_variance; + + double r_i = 0.5 * std::exp(std::log(mean_d) + sigma * mtrandom::gaussian()); + // guard against pathological lognormal tail draws + const double r_min_clamp = 0.2 * mean_d * 0.5; + const double r_max_clamp = 2.0 * mean_d * 0.5; + if(r_i < r_min_clamp) r_i = r_min_clamp; + if(r_i > r_max_clamp) r_i = r_max_clamp; + + const double x = xmin + mtrandom::grnd() * (xmax - xmin); + const double y = ymin + mtrandom::grnd() * (ymax - ymin); + + if(!grid.collides(x, y, r_i, xs, ys, rs)){ + xs.push_back(x); + ys.push_back(y); + rs.push_back(r_i); + grid.insert(x, y, int(xs.size()) - 1); + + grain_coord_array.push_back(std::vector()); + grain_coord_array.back().push_back(x); + grain_coord_array.back().push_back(y); + is_small_grain.push_back(is_small); + + placed++; + if(is_small) placed_small++; + consecutive_failures = 0; + } + else{ + consecutive_failures++; + } + } + + return placed; + +} + +} // end of internal namespace +} // end of create namespace diff --git a/src/create/voronoi_elliptical_rounding.cpp b/src/create/voronoi_elliptical_rounding.cpp new file mode 100644 index 000000000..b8b6d0a26 --- /dev/null +++ b/src/create/voronoi_elliptical_rounding.cpp @@ -0,0 +1,86 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) R F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include + +// Vampire headers +#include "create.hpp" + +// create module headers +#include "internal.hpp" + +namespace create{ +namespace internal{ + +//------------------------------------------------------------------------------ +// +// Function to compute the in-plane scale factor applied to a voronoi grain +// cross-section at height z, giving grains an ellipsoidal profile in z while +// preserving the granular structure in the plane. +// +// The profile blends linearly between a vertical grain wall and a true +// ellipsoid according to the rounding parameter lambda: +// +// R(z) = 1 - lambda * ( 1 - E(z) ) +// +// where E(z) is the normalised ellipsoidal profile +// +// E(z) = sqrt( 1 - ( (z-z0) / c )^2 ) +// +// and c is the vertical semi-axis, taken separately above and below the +// origin z0 so that the grain always tapers to zero cross-section at both +// film surfaces for any choice of z0: +// +// c = z0 for z < z0 (lower half) +// c = h - z0 for z >= z0 (upper half) +// +// With z0 = h/2 the two semi-axes are equal and the profile is a true +// ellipsoid. Limits: +// +// lambda = 0 -> R(z) = 1 everywhere (vertical grain walls, as before) +// lambda = 1 -> R(z) = E(z) (fully ellipsoidal grains) +// +// At z = z0 the factor is always exactly 1, so the grains tile the plane at +// the origin height with no volume compensation applied. +// +//------------------------------------------------------------------------------ +double elliptical_rounding_factor(const double z){ + + const double lambda = create::internal::voronoi_elliptical_rounding; + + // no rounding requested - return unmodified grain cross-section + if(lambda <= 0.0) return 1.0; + + const double ssz = cs::system_dimensions[2]; + const double z0 = create::internal::voronoi_elliptical_rounding_height * ssz; + + // select the semi-axis for the relevant half of the grain + const double c = (z >= z0) ? (ssz - z0) : z0; + + // guard against a degenerate half when the origin sits on a surface + if(c < 1.0e-9) return 1.0 - lambda; + + const double dz = (z - z0)/c; + const double dz2 = dz*dz; + + // atoms beyond the poles of the ellipsoid (only reachable through rounding + // errors at the surfaces, or for atoms slightly outside the nominal system + // height) sit at zero ellipsoidal radius + if(dz2 >= 1.0) return 1.0 - lambda; + + return 1.0 - lambda*( 1.0 - sqrt(1.0 - dz2) ); + +} + +} // end of namespace internal +} // end of namespace create diff --git a/src/create/voronoi_substructure.cpp b/src/create/voronoi_substructure.cpp index 5a8249039..53e0ab178 100644 --- a/src/create/voronoi_substructure.cpp +++ b/src/create/voronoi_substructure.cpp @@ -66,7 +66,7 @@ void voronoi_substructure(std::vector & catom_array){ 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 size = create::internal::voronoi_grain_substructure_size + create::internal::voronoi_grain_substructure_spacing; double grain_cell_size_x = size; double grain_cell_size_y = sqrt(3.0)*size; @@ -137,10 +137,10 @@ void voronoi_substructure(std::vector & catom_array){ } // Calculate Voronoi construction using qhull not including boundary grains (false) - to be fixed! - create::internal::populate_vertex_points(grain_coord_array, grain_vertices_array, false); + create::internal::populate_vertex_points(grain_coord_array, grain_vertices_array, false, create::internal::voronoi_grain_substructure_size); // 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); + double shrink_factor = create::internal::voronoi_grain_substructure_size/(create::internal::voronoi_grain_substructure_size+create::internal::voronoi_grain_substructure_spacing); // Reduce vertices to relative coordinates for(unsigned int grain=0;grain & catom_array){ //------------------------------------------------------------------------------ // Set 3D structure for grains //------------------------------------------------------------------------------ - const double sphere_radius = create::internal::voronoi_grain_substructure_crystallization_radius; //*create::internal::voronoi_grain_size*radius_factor; + const double sphere_radius = create::internal::voronoi_grain_substructure_crystallization_radius; //*create::internal::voronoi_grain_substructure_size*radius_factor; // loop over all grains with vertices for(unsigned int grain=0;grain > & grain_coord_array, std::vector > > & grain_vertices_array, - bool include_boundary_grains){ // controls is boundary grains are removed + bool include_boundary_grains, // controls is boundary grains are removed + const double grain_size){ // mean grain size used to set the boundary grain tolerance //======================================================================================================== // Function to populate voronoi vertices for grains using qhull // @@ -203,7 +204,7 @@ void populate_vertex_points(std::vector > & grain_coord_ar } // but only include grains with a centre of mass inside the system dimensions - const double tol = create::internal::voronoi_grain_size * 0.25; // allow grains with half the mean grain size + const double tol = grain_size * 0.25; // allow grains with half the mean grain size bool inx = grain_coord_array[i][0] >= -tol && grain_coord_array[i][0] < cs::system_dimensions[0] + tol; bool iny = grain_coord_array[i][1] >= -tol && grain_coord_array[i][1] < cs::system_dimensions[1] + tol; if( !(inx && iny) ) inf = true; diff --git a/src/create/write_grain_vertices.cpp b/src/create/write_grain_vertices.cpp new file mode 100644 index 000000000..0c3dddefe --- /dev/null +++ b/src/create/write_grain_vertices.cpp @@ -0,0 +1,58 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2023. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers + +// Vampire headers +#include "create.hpp" +#include "vmpi.hpp" +#include "vio.hpp" + +// create module headers +#include "internal.hpp" + +//------------------------------------------------------------------------------ +// Function to write grain vertices to a file in the format +// num_vertices vx1 vy1 vx2 vy2 ... +//------------------------------------------------------------------------------ +// Expects the vertices array in the format vertices[grain][vertex][0] +//------------------------------------------------------------------------------ +void create::internal::write_grain_vertices(int id, double dx, double dy, std::ofstream& ofile, std::vector< std::vector >& vertices){ + + // only write on root process + if(!vmpi::master) return; + + // write number of vertices + ofile << vertices.size() << "\t"; + + const int num_vertices = vertices.size(); + + // write vertices + for(int i = 0; i < num_vertices; i++){ + ofile << vertices[i][0]+dx << "\t" << vertices[i][1]+dy << "\t"; + } + ofile << std::endl; + + //---------------------------------------------------------------- + // commented out code to generate individual files for each grain + //---------------------------------------------------------------- + //std::ofstream gfile; + //std::stringstream fn; + //fn << "grain" << id << ".txt"; + //gfile.open(fn.str()); + //for(int i = 0; i < vertices.size(); i++){ + // gfile << vertices[i][0]+dx << "\t" << vertices[i][1]+dy << "\n"; + //} + //gfile.close(); + + return; +} diff --git a/src/cuda/data.cu b/src/cuda/data.cu index 238ee00d9..979227f44 100644 --- a/src/cuda/data.cu +++ b/src/cuda/data.cu @@ -13,6 +13,7 @@ */ #include "data.hpp" +#include "typedefs.hpp" namespace vcuda{ @@ -120,6 +121,7 @@ namespace vcuda{ namespace mp { material_parameters_t *d_material_params; + material_parameters_rotational_t *d_material_params_r; } /* mp */ // Back to the top namespace @@ -142,6 +144,8 @@ namespace vcuda{ cu_real_t *d_y_mu0H_dip_field; cu_real_t *d_z_mu0H_dip_field; + cu_real_t *d_neel_tensor; + /*cu_real_array_t x_total_spin_field_array(0UL); cu_real_array_t y_total_spin_field_array(0UL); diff --git a/src/cuda/data.hpp b/src/cuda/data.hpp index 4534e16e7..40fa2b47a 100644 --- a/src/cuda/data.hpp +++ b/src/cuda/data.hpp @@ -119,6 +119,7 @@ namespace vcuda namespace mp { extern material_parameters_t *d_material_params; + extern material_parameters_rotational_t *d_material_params_r; } /* mp */ extern cu_real_t *d_x_spin_field; @@ -139,6 +140,8 @@ namespace vcuda extern cu_real_t *d_y_mu0H_dip_field; extern cu_real_t *d_z_mu0H_dip_field; + extern cu_real_t *d_neel_tensor; + /* * Required by the total external field calculator diff --git a/src/cuda/exchange_fields.cu b/src/cuda/exchange_fields.cu index f9eb70acb..0ac586424 100755 --- a/src/cuda/exchange_fields.cu +++ b/src/cuda/exchange_fields.cu @@ -272,7 +272,7 @@ namespace vcuda CUSPARSE_OPERATION_NON_TRANSPOSE, &alpha, J_matrix_d, vecX, &beta, vecY, CUSPARSE_REAL, - CUSPARSE_CSRMV_ALG1, + CUSPARSE_SPMV_CSR_ALG1, &buffer_size); cudaMalloc(&spmv_buffer_d, buffer_size); @@ -395,7 +395,7 @@ namespace vcuda &beta, vecY, CUSPARSE_REAL, - CUSPARSE_CSRMV_ALG1, + CUSPARSE_SPMV_CSR_ALG1, spmv_buffer_d); if (status != CUSPARSE_STATUS_SUCCESS) { diff --git a/src/cuda/external_fields.cu b/src/cuda/external_fields.cu index 14ef29eb0..91b2e2ab3 100644 --- a/src/cuda/external_fields.cu +++ b/src/cuda/external_fields.cu @@ -9,6 +9,7 @@ //------------------------------------------------------------------------------ // C++ standard library headers +#include #include // Vampire headers @@ -97,6 +98,74 @@ namespace internal{ } // end update_global_thermal_field + //------------------------------------------------------------------------------ + // Kernel function to add the global and material-specific FMR fields + //------------------------------------------------------------------------------ + __global__ void update_fmr_fields_kernel( + cu_real_t * x_field_array, cu_real_t * y_field_array, cu_real_t * z_field_array, + const cu_real_t Hx_fmr, const cu_real_t Hy_fmr, const cu_real_t Hz_fmr, + const double real_time, const bool local_fmr_field, + int * material, material_parameters_t * material_params, + const int n_atoms) + { + for (int atom = blockIdx.x * blockDim.x + threadIdx.x; + atom < n_atoms; + atom += blockDim.x * gridDim.x) + { + cu_real_t field_x = Hx_fmr; + cu_real_t field_y = Hy_fmr; + cu_real_t field_z = Hz_fmr; + + if (local_fmr_field) + { + const int mid = material[atom]; + const material_parameters_t mat = material_params[mid]; + const cu_real_t phase = cu_real_t(2.0 * M_PI) * mat.fmr_field_frequency * real_time; + + #ifdef CUDA_DP + const cu_real_t Hsinwt = mat.fmr_field_strength * sin(phase); + #else + const cu_real_t Hsinwt = mat.fmr_field_strength * sinf(phase); + #endif + + field_x += Hsinwt * mat.fmr_field_unit_x; + field_y += Hsinwt * mat.fmr_field_unit_y; + field_z += Hsinwt * mat.fmr_field_unit_z; + } + + x_field_array[atom] += field_x; + y_field_array[atom] += field_y; + z_field_array[atom] += field_z; + } + } + + + // Host function to calculate and add the FMR field + void update_fmr_fields() + { + const double real_time = sim::time * ::mp::dt_SI; + const double phase = (2.0 * M_PI) * + sim::fmr_field_frequency * real_time; + const double Hsinwt = sim::fmr_field_strength * sin(phase); + + // Update CPU up to date for output. + sim::fmr_field = Hsinwt; + + const cu_real_t Hx_fmr = sim::fmr_field_unit_vector[0] * Hsinwt; + const cu_real_t Hy_fmr = sim::fmr_field_unit_vector[1] * Hsinwt; + const cu_real_t Hz_fmr = sim::fmr_field_unit_vector[2] * Hsinwt; + + update_fmr_fields_kernel <<< cu::grid_size, cu::block_size >>> ( + cu::d_x_external_field, cu::d_y_external_field, cu::d_z_external_field, + Hx_fmr, Hy_fmr, Hz_fmr, + real_time, sim::local_fmr_field, + cu::atoms::d_materials, cu::mp::d_material_params, + ::atoms::num_atoms); + + check_cuda_errors (__FILE__, __LINE__); + } + + //------------------------------------------------------------------------------ // Kernel function to calculate external fields //------------------------------------------------------------------------------ @@ -143,6 +212,9 @@ namespace internal{ cu::update_applied_fields(); } + // Add the oscillating field only after it has been enabled by the FMR program. + if(sim::enable_fmr) cu::update_fmr_fields(); + // // update dipole field // update_dipolar_fields(); //-- disabled as causes NaN and deferred to CPU code for now diff --git a/src/cuda/finalize.cu b/src/cuda/finalize.cu index 2345e537c..60922b266 100644 --- a/src/cuda/finalize.cu +++ b/src/cuda/finalize.cu @@ -60,6 +60,76 @@ namespace vcuda{ cu::exchange::finalise_exchange(); check_cuda_errors (__FILE__, __LINE__); + // Free all device memory + + // Free rand states + cudaFree(d_rand_state); + // Free atom topologies + cudaFree(atoms::d_neighbours); + cudaFree(atoms::d_limits); + // Free material parameters + cudaFree(mp::d_material_params_r); + cudaFree(mp::d_material_params); + // Free dipole variables + cudaFree(cells::d_tensor_zz); + cudaFree(cells::d_tensor_yz); + cudaFree(cells::d_tensor_yy); + cudaFree(cells::d_tensor_xz); + cudaFree(cells::d_tensor_xy); + cudaFree(cells::d_tensor_xx); + + cudaFree(cells::d_num_atoms_in_cell); + + cudaFree(cells::d_z_cell_mu0H_field); + cudaFree(cells::d_y_cell_mu0H_field); + cudaFree(cells::d_x_cell_mu0H_field); + cudaFree(cells::d_z_cell_field); + cudaFree(cells::d_y_cell_field); + cudaFree(cells::d_x_cell_field); + // Free cell variables + cudaFree(cells::d_cell_id_array); + cudaFree(cells::d_num_atoms); + cudaFree(cells::d_volume); + + cudaFree(cells::d_z_mag); + cudaFree(cells::d_y_mag); + cudaFree(cells::d_x_mag); + + cudaFree(cells::d_z_coord); + cudaFree(cells::d_y_coord); + cudaFree(cells::d_x_coord); + // Free neel tensor + cudaFree(d_neel_tensor); + // Free field variables + cudaFree(d_z_mu0H_dip_field); + cudaFree(d_y_mu0H_dip_field); + cudaFree(d_x_mu0H_dip_field); + cudaFree(d_z_dip_field); + cudaFree(d_y_dip_field); + cudaFree(d_x_dip_field); + cudaFree(d_z_external_field); + cudaFree(d_y_external_field); + cudaFree(d_x_external_field); + cudaFree(d_z_spin_field); + cudaFree(d_y_spin_field); + cudaFree(d_x_spin_field); + cudaFree(d_spin_field); + // Free atom variables + cudaFree(atoms::d_cells); + cudaFree(atoms::d_materials); + // Free atom coords + cudaFree(atoms::d_z_coord); + cudaFree(atoms::d_y_coord); + cudaFree(atoms::d_x_coord); + // Free spin transfer buffers + cudaFree(h_z_spin_transfer_buffer); + cudaFree(h_y_spin_transfer_buffer); + cudaFree(h_x_spin_transfer_buffer); + // Free atom spins + cudaFree(atoms::d_z_spin); + cudaFree(atoms::d_y_spin); + cudaFree(atoms::d_x_spin); + cudaFree(atoms::d_spin); /* cu::atoms::x_spin_array.cu_real_array_t::~cu_real_array_t (); cu::atoms::y_spin_array.cu_real_array_t::~cu_real_array_t (); diff --git a/src/cuda/initialize.cu b/src/cuda/initialize.cu old mode 100755 new mode 100644 index f9b2782d1..93b0e2704 --- a/src/cuda/initialize.cu +++ b/src/cuda/initialize.cu @@ -133,8 +133,10 @@ namespace vcuda{ success = success && cu::__initialize_atoms (); success = success && cu::__initialize_fields (); + success = success && cu::__initialize_neel (); success = success && cu::__initialize_cells (); success = success && cu::__initialize_materials (); + success = success && cu::__initialize_materials_rotational (); success = success && cu::__initialize_topology (); success = success && cu::__initialize_curand (); success = success && cu::__initialize_stats (); @@ -186,6 +188,12 @@ namespace vcuda{ namespace internal { + std::vector double2cu_real_t(std::vector in){ + std::vector out(in.size()); + for(int i= 0; i neel_tensor = internal::double2cu_real_t(anisotropy::get_neel_tensor()); + //std::vector* neel_tensor_p = &neel_tensor; + size_t num_bytes = neel_tensor.size() * sizeof(cu_real_t); + + cudaMalloc((void**)&cu::d_neel_tensor,num_bytes); + cudaMemcpy(cu::d_neel_tensor, neel_tensor.data(), num_bytes, cudaMemcpyHostToDevice); + + return true; + } + bool __initialize_cells () { /* @@ -431,7 +453,7 @@ namespace vcuda{ bool __initialize_dipole(){ - // Initialise and copy dipolar fields for cells. + // Initialise and copy dipolar fields for cells. // It's done here because otherwise these objects are not yet initialised on the host when initialise_dipole() is called size_t num_bytes = ::cells::num_cells * sizeof(cu_real_t); std::vector tmp_buffer; @@ -468,7 +490,7 @@ namespace vcuda{ cudaMemcpy(cu::cells::d_num_atoms_in_cell, num_atoms_in_cell.data(), num_atoms_in_cell.size() * sizeof(int), cudaMemcpyHostToDevice); check_cuda_errors(__FILE__,__LINE__); - + // Initialise and copy dipolar tensor // Copy into vectors to avoid having to perform later std::copy() @@ -526,7 +548,6 @@ namespace vcuda{ bool __initialize_materials () { - /* * Serialize material data */ @@ -534,15 +555,15 @@ namespace vcuda{ std::vector _materials(num_mats); for (size_t i = 0; i < num_mats; i++) { - double mu_s_SI = ::mp::material[i].mu_s_SI; + cu_real_t mu_s_SI = ::mp::material[i].mu_s_SI; - double ku2 = ::anisotropy::get_ku2(i); // second order uniaxial anisotropy constant (Ku1) - double ku4 = ::anisotropy::get_ku4(i); // fourth order uniaxial anisotropy constant (Ku2) - double ku6 = ::anisotropy::get_ku6(i); // sixth order uniaxial anisotropy constant (Ku3) - double kc4 = ::anisotropy::get_kc4(i); // fourth order cubic anisotropy constant (Kc1) - //double kc6 = ::anisotropy::get_kc6(i); // sixth order cubic anisotropy constant (Kc2) + cu_real_t ku2 = ::anisotropy::get_ku2(i); // second order uniaxial anisotropy constant (Ku1) + cu_real_t ku4 = ::anisotropy::get_ku4(i); // fourth order uniaxial anisotropy constant (Ku2) + cu_real_t ku6 = ::anisotropy::get_ku6(i); // sixth order uniaxial anisotropy constant (Ku3) + cu_real_t kc4 = ::anisotropy::get_kc4(i); // fourth order cubic anisotropy constant (Kc1) + cu_real_t kc6 = ::anisotropy::get_kc6(i); // sixth order cubic anisotropy constant (Kc2) - std::vector ku_vector = ::anisotropy::get_ku_vector(i); // unit vector defining axis for uniaxial anisotropy + std::vector ku_vector = internal::double2cu_real_t(::anisotropy::get_ku_vector(i)); // unit vector defining axis for uniaxial anisotropy _materials[i].alpha = ::mp::material[i].alpha; _materials[i].gamma_rel = ::mp::material[i].gamma_rel; @@ -550,10 +571,11 @@ namespace vcuda{ _materials[i].i_mu_s_si = 1.0 / mu_s_SI; _materials[i].k_latt = 0.0; //::mp::material[i].Klatt_SI / mu_s_SI; // Divide anisotropy energy constants by mus_i to have it in units of field [T] - _materials[i].sh2 = ku2 * _materials[i].i_mu_s_si;// J/T + _materials[i].sh2 = ku2 * _materials[i].i_mu_s_si;// J/T _materials[i].sh4 = ku4 * _materials[i].i_mu_s_si; _materials[i].sh6 = ku6 * _materials[i].i_mu_s_si; _materials[i].kc4 = kc4 * _materials[i].i_mu_s_si; + _materials[i].kc6 = kc6 * _materials[i].i_mu_s_si; _materials[i].anisotropy_unit_x = ku_vector[0]; _materials[i].anisotropy_unit_y = ku_vector[1]; _materials[i].anisotropy_unit_z = ku_vector[2]; @@ -561,6 +583,11 @@ namespace vcuda{ _materials[i].applied_field_unit_x = ::mp::material[i].applied_field_unit_vector[0]; _materials[i].applied_field_unit_y = ::mp::material[i].applied_field_unit_vector[1]; _materials[i].applied_field_unit_z = ::mp::material[i].applied_field_unit_vector[2]; + _materials[i].fmr_field_strength = ::mp::material[i].fmr_field_strength; + _materials[i].fmr_field_frequency = ::mp::material[i].fmr_field_frequency; + _materials[i].fmr_field_unit_x = ::mp::material[i].fmr_field_unit_vector[0]; + _materials[i].fmr_field_unit_y = ::mp::material[i].fmr_field_unit_vector[1]; + _materials[i].fmr_field_unit_z = ::mp::material[i].fmr_field_unit_vector[2]; _materials[i].temperature = ::mp::material[i].temperature; _materials[i].temperature_rescaling_alpha = ::mp::material[i].temperature_rescaling_alpha; _materials[i].temperature_rescaling_Tc = ::mp::material[i].temperature_rescaling_Tc; @@ -579,6 +606,86 @@ namespace vcuda{ return true; } + bool __initialize_materials_rotational() { + //If no rotational parameters are defined, skip this function + if (!::anisotropy::is_rotational_enabled()) return true; + + size_t num_mats = ::mp::num_materials; + std::vector _materials_r(num_mats); + + for (size_t i = 0; i < num_mats; i++) + { + cu_real_t mu_s_SI = ::mp::material[i].mu_s_SI; + cu_real_t i_mu_s_SI = 1.0 / mu_s_SI; + + std::vector kr_vector = internal::double2cu_real_t(::anisotropy::get_kr_vector(i)); + std::vector kl_vector = internal::double2cu_real_t(::anisotropy::get_kl_vector(i)); + + cu_real_t k2r1 = ::anisotropy::get_k2r1(i); + cu_real_t k2r1_odd = ::anisotropy::get_k2r1_odd(i); + cu_real_t k2r2 = ::anisotropy::get_k2r2(i); + cu_real_t k2r2_odd = ::anisotropy::get_k2r2_odd(i); + cu_real_t k4r1 = ::anisotropy::get_k4r1(i); + cu_real_t k4r1_odd = ::anisotropy::get_k4r1_odd(i); + cu_real_t k4r2 = ::anisotropy::get_k4r2(i); + cu_real_t k4r2_odd = ::anisotropy::get_k4r2_odd(i); + cu_real_t k4r3 = ::anisotropy::get_k4r3(i); + cu_real_t k4r3_odd = ::anisotropy::get_k4r3_odd(i); + cu_real_t k4r4 = ::anisotropy::get_k4r4(i); + cu_real_t k4r4_odd = ::anisotropy::get_k4r4_odd(i); + cu_real_t k6r1 = ::anisotropy::get_k6r1(i); + cu_real_t k6r1_odd = ::anisotropy::get_k6r1_odd(i); + cu_real_t k6r2 = ::anisotropy::get_k6r2(i); + cu_real_t k6r2_odd = ::anisotropy::get_k6r2_odd(i); + cu_real_t k6r3 = ::anisotropy::get_k6r3(i); + cu_real_t k6r3_odd = ::anisotropy::get_k6r3_odd(i); + cu_real_t k6r4 = ::anisotropy::get_k6r4(i); + cu_real_t k6r4_odd = ::anisotropy::get_k6r4_odd(i); + cu_real_t k6r5 = ::anisotropy::get_k6r5(i); + cu_real_t k6r5_odd = ::anisotropy::get_k6r5_odd(i); + cu_real_t k6r6 = ::anisotropy::get_k6r6(i); + cu_real_t k6r6_odd = ::anisotropy::get_k6r6_odd(i); + + _materials_r[i].rotational_anisotropy_unit_x = kr_vector[0]; + _materials_r[i].rotational_anisotropy_unit_y = kr_vector[1]; + _materials_r[i].rotational_anisotropy_unit_z = kr_vector[2]; + + _materials_r[i].last_axis_anisotropy_unit_x = kl_vector[0]; + _materials_r[i].last_axis_anisotropy_unit_y = kl_vector[1]; + _materials_r[i].last_axis_anisotropy_unit_z = kl_vector[2]; + + _materials_r[i].k2r1 = k2r1 * i_mu_s_SI; + _materials_r[i].k2r1_odd = k2r1_odd * i_mu_s_SI; + _materials_r[i].k2r2 = k2r2 * i_mu_s_SI; + _materials_r[i].k2r2_odd = k2r2_odd * i_mu_s_SI; + _materials_r[i].k4r1 = k4r1 * i_mu_s_SI; + _materials_r[i].k4r1_odd = k4r1_odd * i_mu_s_SI; + _materials_r[i].k4r2 = k4r2 * i_mu_s_SI; + _materials_r[i].k4r2_odd = k4r2_odd * i_mu_s_SI; + _materials_r[i].k4r3 = k4r3 * i_mu_s_SI; + _materials_r[i].k4r3_odd = k4r3_odd * i_mu_s_SI; + _materials_r[i].k4r4 = k4r4 * i_mu_s_SI; + _materials_r[i].k4r4_odd = k4r4_odd * i_mu_s_SI; + _materials_r[i].k6r1 = k6r1 * i_mu_s_SI; + _materials_r[i].k6r1_odd = k6r1_odd * i_mu_s_SI; + _materials_r[i].k6r2 = k6r2 * i_mu_s_SI; + _materials_r[i].k6r2_odd = k6r2_odd * i_mu_s_SI; + _materials_r[i].k6r3 = k6r3 * i_mu_s_SI; + _materials_r[i].k6r3_odd = k6r3_odd * i_mu_s_SI; + _materials_r[i].k6r4 = k6r4 * i_mu_s_SI; + _materials_r[i].k6r4_odd = k6r4_odd * i_mu_s_SI; + _materials_r[i].k6r5 = k6r5 * i_mu_s_SI; + _materials_r[i].k6r5_odd = k6r5_odd * i_mu_s_SI; + _materials_r[i].k6r6 = k6r6 * i_mu_s_SI; + _materials_r[i].k6r6_odd = k6r6_odd * i_mu_s_SI; + + } + cudaMalloc((void**)&cu::mp::d_material_params_r, num_mats * sizeof(material_parameters_rotational_t)); + cudaMemcpy(cu::mp::d_material_params_r, _materials_r.data(), num_mats * sizeof(material_parameters_rotational_t), cudaMemcpyHostToDevice); + + return true; + } + bool __initialize_topology () { diff --git a/src/cuda/internal.hpp b/src/cuda/internal.hpp index 6227bf6bc..688dea8fd 100644 --- a/src/cuda/internal.hpp +++ b/src/cuda/internal.hpp @@ -55,10 +55,12 @@ namespace vcuda{ */ bool __initialize_atoms (); bool __initialize_fields (); + bool __initialize_neel (); bool __initialize_cells (); bool __initialize_dipole (); bool __initialize_hamr (); bool __initialize_materials (); + bool __initialize_materials_rotational(); bool __initialize_topology (); bool __initialize_curand (); bool __initialize_stats (); @@ -79,6 +81,9 @@ namespace vcuda{ void update_hamr_field (); void update_global_thermal_field (); void update_applied_fields (); + void update_fmr_fields (); + void update_neel_fields (); + void update_rotational_fields (); @@ -167,7 +172,33 @@ namespace vcuda{ const cu_real_t Hx, const cu_real_t Hy, const cu_real_t Hz, int * material, vcuda::internal::material_parameters_t * material_params, const int n_atoms); - + + __global__ void update_fmr_fields_kernel( + cu_real_t * x_field_array, cu_real_t * y_field_array, cu_real_t * z_field_array, + const cu_real_t Hx, const cu_real_t Hy, const cu_real_t Hz, + const double real_time, const bool local_fmr_field, + int * material, material_parameters_t * material_params, + const int n_atoms); + + __global__ void update_neel_fields_kernel ( + cu_real_t * d_neel_tensor, + cu_real_t * x_spin, + cu_real_t * y_spin, + cu_real_t * z_spin, + cu_real_t * x_sp_field, + cu_real_t * y_sp_field, + cu_real_t * z_sp_field, + int n_atoms + ); + + __global__ void update_rotational_fields_kernel ( + int * material, + material_parameters_t * material_params, + material_parameters_rotational_t * material_params_r, + cu_real_t * x_spin, cu_real_t * y_spin, cu_real_t * z_spin, + cu_real_t * x_sp_field, cu_real_t * y_sp_field, cu_real_t * z_sp_field, + int n_atoms + ); namespace stats{ extern bool use_cpu; diff --git a/src/cuda/makefile b/src/cuda/makefile index ea3c54024..6cea574cb 100644 --- a/src/cuda/makefile +++ b/src/cuda/makefile @@ -5,6 +5,9 @@ NVCC_VERSION=$(shell nvcc --version | tail -n 1 | cut -b 38-) NVCC=nvcc -DCOMP='"NVCC $(NVCC_VERSION)"' +# Correct incompatibility with -Wsign-compare flag and nvcc +GCC_LDFLAGS := $(filter-out -Wsign-compare,$(GCC_LDFLAGS)) + CUDALIBS=-L/usr/local/cuda/lib64/ -lcudart -lcusparse -lcurand # List module object filenames @@ -20,13 +23,14 @@ CUDA_OBJECTS= \ obj/cuda/internal.o \ obj/cuda/llg_heun.o \ obj/cuda/monte_carlo.o \ + obj/cuda/neel.o \ + obj/cuda/rotational_anisotropy.o \ obj/cuda/spin_fields.o \ obj/cuda/statistics.o \ obj/cuda/thermal_fields.o \ - obj/cuda/transfer.o + obj/cuda/transfer.o CUDA_ARCH=\ --gencode arch=compute_35,code=sm_35 \ -gencode arch=compute_50,code=sm_50 \ -gencode arch=compute_50,code=compute_50 \ -gencode arch=compute_61,code=sm_61 \ @@ -52,25 +56,38 @@ CUDA_MATRIX_TYPE = CSR #CUDA 8.0+ #CUDA_CFLAGS= -O3 -I./hdr -DCUDA $(CUDA_ARCH) --use_fast_math -ftz=true -std=c++11 -Wno-deprecated-gpu-targets #CUDA 10.0 + -CUDA_CFLAGS= -O3 -I./hdr -DCUDA $(CUDA_ARCH) --use_fast_math -ftz=true -std=c++14 -Wno-deprecated-gpu-targets +CUDA_CFLAGS= -O3 -I./hdr -DCUDA $(CUDA_ARCH) --use_fast_math -ftz=true -std=c++14 -Wno-deprecated-gpu-targets -diag-suppress 549 -Wno-deprecated-declarations $(CUDA_OBJECTS): obj/%.o: src/%.cu $(NVCC) $< -dc -o $@ $(CUDA_CFLAGS) $(CUDA_PR) -DCUDA_MATRIX=$(CUDA_MATRIX_TYPE) # Target to link the CUDA enabled vampire ifeq ($(shell uname -s), Darwin) -cuda: GCC_CFLAGS=$(LLVM_CFLAGS) -cuda: GCC_LDFLAGS=$(LLVM_LDFLAGS) -cuda: CUDALIBS=-lcudart -lcusparse -lcurand +cuda-build: GCC_CFLAGS=$(LLVM_CFLAGS) +cuda-build: GCC_LDFLAGS=$(LLVM_LDFLAGS) +cuda-build: CUDALIBS=-lcudart -lcusparse -lcurand endif -cuda: GCC_CFLAGS += -DCUDA -cuda: $(OBJECTS) $(CUDA_OBJECTS) +cuda-build: GCC_CFLAGS += -DCUDA +cuda-build: $(OBJECTS) $(CUDA_OBJECTS) $(NVCC) $(CUDA_CFLAGS) $(GCC_LDFLAGS) $(LIBS) ${CUDALIBS} $(OBJECTS) $(CUDA_OBJECTS) -o $(CUDAEXECUTABLE) +# CUDA default target - cleans before building +.PHONY: cuda +cuda: + $(MAKE) cuda-build + +# $(MAKE) clean + # CUDA debug target cuda-debug: CUDA_CFLAGS += -DCUDA_DEBUG -DCUDA_SPIN_DEBUG -cuda-debug: cuda +cuda-debug: clean +cuda-debug: cuda-build # CUDA debug target cuda-profile: CUDA_CFLAGS += -DCUDA_DEBUG -DCUDA_SPIN_DEBUG --profile -cuda-profile: cuda +cuda-profile: cuda-build + +# CUDA dev target - use to build without cleaning first +.PHONY: cuda-dev +cuda-dev: + $(MAKE) cuda-build diff --git a/src/cuda/monte_carlo.cu b/src/cuda/monte_carlo.cu index ddb4a6543..1ae57bc2c 100644 --- a/src/cuda/monte_carlo.cu +++ b/src/cuda/monte_carlo.cu @@ -75,6 +75,10 @@ namespace vcuda cu_real_t *d_rand_spin; cu_real_t *d_rand_accept; + // Variable to ensure length of d_rand_spin is always even no matter the number of atoms + // This is a restriction required by the cuRAND pseudorandom number generator + int d_rand_spin_length; + int colour_split() { @@ -189,12 +193,14 @@ namespace vcuda int initialise() { + //There should be 3 spins per atom with a redundant spin in the case of an odd number of atoms + d_rand_spin_length = (3*::atoms::num_atoms) + (::atoms::num_atoms % 2); curandCreateGenerator(&gen, CURAND_RNG_PSEUDO_DEFAULT); curandSetPseudoRandomGeneratorSeed(gen, mtrandom::integration_seed); - cudaMalloc((void**)&d_rand_spin, 3*::atoms::num_atoms * sizeof(cu_real_t)); + cudaMalloc((void**)&d_rand_spin, d_rand_spin_length * sizeof(cu_real_t)); cudaMalloc((void**)&d_rand_accept, ::atoms::num_atoms * sizeof(cu_real_t)); cudaMalloc((void**)&d_sl_atoms, ::atoms::num_atoms * sizeof(int)); @@ -217,7 +223,7 @@ namespace vcuda std::cout << "Trying a step..."<< std::endl; - cudaThreadSynchronize(); + cudaDeviceSynchronize(); cudaError_t error = cudaGetLastError(); if(error != cudaSuccess) { @@ -512,10 +518,10 @@ namespace vcuda // generate 3 random doubles per atom for the trial spin and 1 for the acceptance #ifdef CUDA_DP - curandGenerateNormalDouble( gen, d_rand_spin, 3*::atoms::num_atoms, 0.0, 1.0); + curandGenerateNormalDouble( gen, d_rand_spin, d_rand_spin_length, 0.0, 1.0); curandGenerateUniformDouble( gen, d_rand_accept, ::atoms::num_atoms); #else - curandGenerateNormal( gen, d_rand_spin, 3*::atoms::num_atoms, 0.0, 1.0); + curandGenerateNormal( gen, d_rand_spin, d_rand_spin_length, 0.0, 1.0); curandGenerateUniform( gen, d_rand_accept, ::atoms::num_atoms); #endif diff --git a/src/cuda/neel.cu b/src/cuda/neel.cu new file mode 100644 index 000000000..9967ab47b --- /dev/null +++ b/src/cuda/neel.cu @@ -0,0 +1,95 @@ +//------------------------------------------------------------------------------ +// +// This source file is part of the VAMPIRE open source package under the +// GNU GPL (version 2) licence (see licence file for details). +// +// (c) O Arbelaez Echeverri, M A Ellis & R F L Evans 2015. All rights reserved. +// Reviewed: Andrea Meo 2022 +// +//------------------------------------------------------------------------------ + +// C++ standard library headers + +// Vampire headers +#include "atoms.hpp" +#include "cuda.hpp" + +// Local cuda headers +#include "cuda_utils.hpp" +#include "data.hpp" +#include "internal.hpp" + +// Conditional compilation of all cuda code +#ifdef CUDA + +// namespace aliasing for brevity +namespace cu = vcuda::internal; + +// vampire cuda namespace +namespace vcuda{ + +// module internal namespace +namespace internal{ + +//------------------------------------------------------------------------------ +// Host function to calculate neel fields using gpu kernel +//------------------------------------------------------------------------------ +void update_neel_fields() +{ + //Call kernel to calculate neel fields + cu::update_neel_fields_kernel <<< cu::grid_size, cu::block_size >>> ( + cu::d_neel_tensor, cu::atoms::d_x_spin, cu::atoms::d_y_spin, cu::atoms::d_z_spin, cu::d_x_spin_field, cu::d_y_spin_field, cu::d_z_spin_field, ::atoms::num_atoms + ); + + check_cuda_errors(__FILE__, __LINE__); +} + +__global__ void update_neel_fields_kernel ( + cu_real_t * d_neel_tensor, + cu_real_t * x_spin, + cu_real_t * y_spin, + cu_real_t * z_spin, + cu_real_t * x_sp_field, + cu_real_t * y_sp_field, + cu_real_t * z_sp_field, + int n_atoms + ) +{ + for ( int i = blockIdx.x * blockDim.x + threadIdx.x; + i < n_atoms; + i += blockDim.x * gridDim.x) { + + const cu_real_t sx = x_spin[i]; + const cu_real_t sy = y_spin[i]; + const cu_real_t sz = z_spin[i]; + + //Get index for atom inside tensor + const int index = 9*i; + + // Second order neel + cu_real_t hx = 2.0 * ( d_neel_tensor[index + 0] * sx + + d_neel_tensor[index + 1] * sy + + d_neel_tensor[index + 2] * sz ); + + cu_real_t hy = 2.0 * ( d_neel_tensor[index + 3] * sx + + d_neel_tensor[index + 4] * sy + + d_neel_tensor[index + 5] * sz ); + + cu_real_t hz = 2.0 * ( d_neel_tensor[index + 6] * sx + + d_neel_tensor[index + 7] * sy + + d_neel_tensor[index + 8] * sz ); + + //Store net field + x_sp_field[i] += hx; + y_sp_field[i] += hy; + z_sp_field[i] += hz; + } + + return; +} + +} // end of internal namespace + +} // end of vcuda namespace + +#endif diff --git a/src/cuda/rotational_anisotropy.cu b/src/cuda/rotational_anisotropy.cu new file mode 100644 index 000000000..bde802c1c --- /dev/null +++ b/src/cuda/rotational_anisotropy.cu @@ -0,0 +1,359 @@ +//------------------------------------------------------------------------------ +// +// This source file is part of the VAMPIRE open source package under the +// GNU GPL (version 2) licence (see licence file for details). +// +// (c) O Arbelaez Echeverri, M A Ellis & R F L Evans 2015. All rights reserved. +// Reviewed: Andrea Meo 2022 +// +//------------------------------------------------------------------------------ + +// C++ standard library headers + +// Vampire headers +#include "atoms.hpp" +#include "cuda.hpp" + +// Local cuda headers +#include "cuda_utils.hpp" +#include "data.hpp" +#include "internal.hpp" +#include "typedefs.hpp" + +// Conditional compilation of all cuda code +#ifdef CUDA + +// namespace aliasing for brevity +namespace cu = vcuda::internal; + +// vampire cuda namespace +namespace vcuda{ + +// module internal namespace +namespace internal{ + +//------------------------------------------------------------------------------ +// Host function to calculate neel fields using gpu kernel +//------------------------------------------------------------------------------ +void update_rotational_fields() +{ + cu::update_rotational_fields_kernel <<>> (cu::atoms::d_materials, cu::mp::d_material_params, + cu::mp::d_material_params_r, + cu::atoms::d_x_spin, cu::atoms::d_y_spin, cu::atoms::d_z_spin, + cu::d_x_spin_field, cu::d_y_spin_field, cu::d_z_spin_field, + ::atoms::num_atoms); + + check_cuda_errors(__FILE__, __LINE__); +} + +__global__ void update_rotational_fields_kernel ( + int * material, + cu::material_parameters_t * material_params, + cu::material_parameters_rotational_t * material_params_r, + cu_real_t * x_spin, cu_real_t * y_spin, cu_real_t * z_spin, + cu_real_t * x_sp_field, cu_real_t * y_sp_field, cu_real_t * z_sp_field, + int n_atoms + ) +{ + // TODO: Figure out why the results are still different from the serial output + // + for ( int i = blockIdx.x * blockDim.x + threadIdx.x; + i < n_atoms; + i += blockDim.x * gridDim.x) { + int mid = material[i]; + //Load parameters from memory + cu::material_parameters_t material = material_params[mid]; + cu::material_parameters_rotational_t material_r = material_params_r[mid]; + + // Initialise register to hold total spin field + cu_real_t field_x = 0.0; + cu_real_t field_y = 0.0; + cu_real_t field_z = 0.0; + + // Registers to hold parts of spin field calculations + cu_real_t x_component = 0.0; + cu_real_t y_component = 0.0; + cu_real_t z_component = 0.0; + + const cu_real_t sx = x_spin[i]; + const cu_real_t sy = y_spin[i]; + const cu_real_t sz = z_spin[i]; + + const cu_real_t ex = material.anisotropy_unit_x; + const cu_real_t ey = material.anisotropy_unit_y; + const cu_real_t ez = material.anisotropy_unit_z; + + const cu_real_t fx = material_r.rotational_anisotropy_unit_x; + const cu_real_t fy = material_r.rotational_anisotropy_unit_y; + const cu_real_t fz = material_r.rotational_anisotropy_unit_z; + + const cu_real_t gx = material_r.last_axis_anisotropy_unit_x; + const cu_real_t gy = material_r.last_axis_anisotropy_unit_y; + const cu_real_t gz = material_r.last_axis_anisotropy_unit_z; + + const cu_real_t Sx = sx * fx + sy * fy + sz * fz; + const cu_real_t Sy = sx * gx + sy * gy + sz * gz; + const cu_real_t Sz = sx * ex + sy * ey + sz * ez; + + const cu_real_t Sx2 = Sx * Sx; + const cu_real_t Sy2 = Sy * Sy; + const cu_real_t Sz2 = Sz * Sz; + + const cu_real_t Sx3 = Sx2 * Sx; + const cu_real_t Sy3 = Sy2 * Sy; + + const cu_real_t Sx4 = Sx2 * Sx2; + const cu_real_t Sy4 = Sy2 * Sy2; + const cu_real_t Sz4 = Sz2 * Sz2; + + const cu_real_t Sx2pSy2 = Sx2 + Sy2; + + //rotational_order_2_1 + const cu_real_t k2r1 = material_r.k2r1; + + field_x += (k2r1 * Sz * fx) + (k2r1 * Sx * ex); + field_y += (k2r1 * Sz * fy) + (k2r1 * Sx * ey); + field_z += (k2r1 * Sz * fz) + (k2r1 * Sx * ez); + + //rotational_order_2_1_odd + const cu_real_t k2r1_odd = material_r.k2r1_odd; + + field_x += (k2r1_odd * Sz * gx) + (k2r1_odd * Sy * ex); + field_y += (k2r1_odd * Sz * gy) + (k2r1_odd * Sy * ey); + field_z += (k2r1_odd * Sz * gz) + (k2r1_odd * Sy * ez); + + //rotational_order_2_2 + const cu_real_t twok2r2 = 2.0 * material_r.k2r2; + + field_x += twok2r2 * Sx * fx; + field_y += twok2r2 * Sx * fy; + field_z += twok2r2 * Sx * fz; + + field_x -= twok2r2 * Sy * gx; + field_y -= twok2r2 * Sy * gy; + field_z -= twok2r2 * Sy * gz; + + //rotational_order_2_2_odd + const cu_real_t twok2r2_odd = 2.0 * material_r.k2r2_odd; + + field_x += (twok2r2_odd * Sy * fx) + (twok2r2_odd * Sx * gx); + field_y += (twok2r2_odd * Sy * fy) + (twok2r2_odd * Sx * gy); + field_z += (twok2r2_odd * Sy * fz) + (twok2r2_odd * Sx * gz); + + //rotational_order_4_1 + const cu_real_t k4r1 = material_r.k4r1; + + x_component = k4r1 * Sz * (Sz2 - (3.0/7.0)); + z_component = k4r1 * Sx * (3.0 * Sz2 - (3.0/7.0)); + + field_x += x_component * fx + z_component * ex; + field_y += x_component * fy + z_component * ey; + field_z += x_component * fz + z_component * ez; + + //rotational_order_4_1_odd + const cu_real_t k4r1_odd = material_r.k4r1_odd; + + y_component = k4r1_odd * Sz * (Sz2 - (3.0/7.0)); + z_component = k4r1_odd * Sy * (3.0 * Sz2 - (3.0/7.0)); + + field_x += y_component * gx + z_component * ex; + field_y += y_component * gy + z_component * ey; + field_z += y_component * gz + z_component * ez; + + //rotational_order_4_2 + const cu_real_t k4r2 = material_r.k4r2; + + x_component = k4r2 * ((Sx3 * 4.0) - (Sx * (12.0/7.0))); + y_component = k4r2 * ((Sy3 * 4.0) - (Sy * (12.0/7.0))); + + field_x += - x_component * fx + y_component * gx; + field_y += - x_component * fy + y_component * gy; + field_z += - x_component * fz + y_component * gz; + + //rotational_order_4_2_odd + const cu_real_t k4r2_odd = material_r.k4r2_odd; + + x_component = 2.0 * k4r2_odd * Sy * ((6.0/7.0) - 3.0 * Sx2 - Sy2); + y_component = 2.0 * k4r2_odd * Sx * ((6.0/7.0) - 3.0 * Sy2 - Sx2); + + field_x += x_component * fx + y_component * gx; + field_y += x_component * fy + y_component * gy; + field_z += x_component * fz + y_component * gz; + + //rotational_order_4_3 + const cu_real_t k4r3 = material_r.k4r3; + + x_component = k4r3 * 3.0 * Sz * (Sx2 - Sy2); + y_component = - k4r3 * 6.0 * Sx * Sy * Sz; + z_component = k4r3 * Sx * (Sx2 - 3.0 * Sy2); + + field_x += x_component * fx + y_component * gx + z_component * ex; + field_y += x_component * fy + y_component * gy + z_component * ey; + field_z += x_component * fz + y_component * gz + z_component * ez; + + //rotational_order_4_3_odd + const cu_real_t k4r3_odd = material_r.k4r3_odd; + + x_component = k4r3_odd * 6.0 * Sx * Sy * Sz; + y_component = k4r3_odd * 3.0 * Sz * (Sx2 - Sy2); + z_component = k4r3_odd * Sy * (3.0 * Sx2 - Sy2); + + field_x += x_component * fx + y_component * gx + z_component * ex; + field_y += x_component * fy + y_component * gy + z_component * ey; + field_z += x_component * fz + y_component * gz + z_component * ez; + + //rotational_order_4_4 + const cu_real_t k4r4 = material_r.k4r4; + + x_component = 4.0 * k4r4 * Sx * (Sx2 - 3.0 * Sy2); + y_component = 4.0 * k4r4 * Sy * (Sy2 - 3.0 * Sx2); + + field_x += x_component * fx; + field_y += x_component * fy; + field_z += x_component * fz; + + field_x += y_component * gx; + field_y += y_component * gy; + field_z += y_component * gz; + + //rotational_order_4_4_odd + const cu_real_t k4r4_odd = material_r.k4r4_odd; + + x_component = 4.0 * k4r4_odd * Sy * (3.0 * Sx2 - Sy2); + y_component = 4.0 * k4r4_odd * Sx * (Sx2 - 3.0 * Sy2); + + field_x += x_component * fx + y_component * gx; + field_y += x_component * fy + y_component * gy; + field_z += x_component * fz + y_component * gz; + + //rotational_order_6_1 + const cu_real_t k6r1 = material_r.k6r1; + + x_component = k6r1 * Sz * (Sz4 - (10.0/11.0) * Sz2 + (5.0/33.0)); + z_component = k6r1 * Sx * 5.0 * (Sz4 - (6.0/11.0) * Sz2 + (1.0/33.0)); + + field_x += z_component * ex + x_component * fx; + field_y += z_component * ey + x_component * fy; + field_z += z_component * ez + x_component * fz; + //rotational_order_6_1_odd + const cu_real_t k6r1_odd = material_r.k6r1_odd; + + y_component = k6r1_odd * Sz * (Sz4 - (10.0/11.0) * Sz2 + (5.0/33.0)); + z_component = k6r1_odd * Sy * 5.0 * (Sz4 - (6.0/11.0) * Sz2 + (1.0/33.0)); + + field_x += z_component * ex + y_component * gx; + field_y += z_component * ey + y_component * gy; + field_z += z_component * ez + y_component * gz; + + //rotational_order_6_2 + const cu_real_t two_k6r2 = 2.0 * material_r.k6r2; + + x_component = two_k6r2 * Sx * (Sx2pSy2 * (3.0 * Sx2 - Sy2) + (16.0/33.0) * (1.0 - 6.0 * Sx2)); + y_component = two_k6r2 * Sy * (Sx2pSy2 * (Sx2 - 3.0 * Sy2) + (16.0/33.0) * (6.0 * Sy2 - 1.0)); + + field_x += x_component * fx + y_component * gx; + field_y += x_component * fy + y_component * gy; + field_z += x_component * fz + y_component * gz; + + //rotational_order_6_2_odd + const cu_real_t two_k6r2_odd = 2.0 * material_r.k6r2_odd; + + x_component = two_k6r2_odd * Sy * (5.0 * Sx4 + 6.0 * (Sx2 * Sy2) + Sy4 - (16.0/11.0) * (3.0 * Sx2 + Sy2) + (16.0/33.0)); + y_component = two_k6r2_odd * Sx * (Sx4 + 6.0 * (Sx2 * Sy2) + 5.0 * Sy4 - (16.0/11.0) * (Sx2 + 3.0 * Sy2) + (16.0/33.0)); + + field_x += x_component * fx + y_component * gx; + field_y += x_component * fy + y_component * gy; + field_z += x_component * fz + y_component * gz; + + //rotational_order_6_3 + const cu_real_t k6r3 = material_r.k6r3; + + x_component = k6r3 * Sz * 3.0 * (Sz4 + 4.0 * (Sx2 * Sz2) - (1.0/11.0) * (14.0 * Sz2 + 12.0 * Sx2 - 3.0)); + z_component = k6r3 * Sx * 3.0 * (5.0 * Sz4 + 4.0 * (Sx2 * Sz2) - (1.0/11.0) * (42.0 * Sz2 + 4.0 * Sx2 - 3.0)); + + field_x += z_component * ex + x_component * fx; + field_y += z_component * ey + x_component * fy; + field_z += z_component * ez + x_component * fz; + + //rotational_order_6_3_odd + const cu_real_t k6r3_odd = material_r.k6r3_odd; + + y_component = - k6r3_odd * Sz * 3.0 * (Sz4 + 4.0 * (Sy2*Sz2) - (1.0/11.0) * (14.0 * Sz2 + 12.0 * Sy2 - 3.0)); + z_component = - k6r3_odd * Sy * 3.0 * (5.0 * Sz4 + 4.0 * (Sy2*Sz2) - (1.0/11.0) * (42.0 * Sz2 + 4.0 * Sy2 - 3.0)); + + field_x += z_component * ex + y_component * gx; + field_y += z_component * ey + y_component * gy; + field_z += z_component * ez + y_component * gz; + + //rotational_order_6_4 + const cu_real_t two_k6r4 = 2.0 * material_r.k6r4; + + x_component = two_k6r4 * Sx * (3.0 * Sx4 - 10.0 * (Sx2 * Sy2) - 5.0 * Sy4 - (20.0/11.0) * (Sx2 - 3.0 * Sy2)); + y_component = two_k6r4 * Sy * (3.0 * Sy4 - 10.0 * (Sx2 * Sy2) - 5.0 * Sx4 - (20.0/11.0) * (Sy2 - 3.0 * Sx2)); + + field_x -= x_component * fx + y_component * gx; + field_y -= x_component * fy + y_component * gy; + field_z -= x_component * fz + y_component * gz; + + //rotational_order_6_4_odd + const cu_real_t four_k6r4_odd = 4.0 * material_r.k6r4_odd; + + x_component = four_k6r4_odd * Sy * (Sy4 - 5.0 * Sx4 + (10.0/11.0) * (3.0 * Sx2 - Sy2)); + y_component = four_k6r4_odd * Sx * (5.0 * Sy4 - Sx4 + (10.0/11.0) * (Sx2 - 3.0 * Sy2)); + + field_x += x_component * fx + y_component * gx; + field_y += x_component * fy + y_component * gy; + field_z += x_component * fz + y_component * gz; + + //rotational_order_6_5 + const cu_real_t k6r5 = material_r.k6r5; + + x_component = k6r5 * Sz * 5.0 * (16.0 * Sx4 + 12.0 * (Sx2 * Sz2) + Sz4 - 2.0 * (6.0 * Sx2 + Sz2) + 1.0); + z_component = k6r5 * Sx * (16.0 * Sx4 + 60.0 * (Sx2 * Sz2) + 25.0 * Sz4 - 10.0 * (2.0 * Sx2 + 3.0 * Sz2) + 5.0); + + field_x += z_component * ex + x_component * fx; + field_y += z_component * ey + x_component * fy; + field_z += z_component * ez + x_component * fz; + + //rotational_order_6_5_odd + const cu_real_t k6r5_odd = material_r.k6r5_odd; + + y_component = k6r5_odd * Sz * 5.0 * (16.0 * Sy4 + 12.0 * (Sy2 * Sz2) + Sz4 - 2.0 * (6.0 * Sy2 + Sz2) + 1.0); + z_component = k6r5_odd * Sy * (16.0 * Sy4 + 60.0 * (Sy2 * Sz2) + 25.0 * Sz4 - 10.0 * (2.0 * Sy2 + 3.0 * Sz2) + 5.0); + + field_x += z_component * ex + y_component * gx; + field_y += z_component * ey + y_component * gy; + field_z += z_component * ez + y_component * gz; + + //rotational_order_6_6 + const cu_real_t six_k6r6 = 6.0 * material_r.k6r6; + + x_component = six_k6r6 * Sx * (Sx4 - 10.0 * (Sx2 * Sy2) + 5.0 * Sy4); + y_component = - six_k6r6 * Sy * (Sy4 - 10.0 * (Sx2 * Sy2) + 5.0 * Sx4); + + field_x += x_component * fx + y_component * gx; + field_y += x_component * fy + y_component * gy; + field_z += x_component * fz + y_component * gz; + + //rotational_order_6_6_odd + const cu_real_t six_k6r6_odd = 6.0 * material_r.k6r6_odd; + + x_component = six_k6r6_odd * Sy * (5.0 * Sx4 - 10.0 * (Sx2 * Sy2) + Sy4); + y_component = six_k6r6_odd * Sx * (Sx4 - 10.0 * (Sx2 * Sy2) + 5.0 * Sy4); + + field_x += x_component * fx + y_component * gx; + field_y += x_component * fy + y_component * gy; + field_z += x_component * fz + y_component * gz; + + //Apply all effects to the spin field + x_sp_field[i] += field_x; + y_sp_field[i] += field_y; + z_sp_field[i] += field_z; + } +} + +} // end of internal namespace + +} // end of vcuda namespace + +#endif diff --git a/src/cuda/spin_fields.cu b/src/cuda/spin_fields.cu index 38b9e309d..21dfe74eb 100644 --- a/src/cuda/spin_fields.cu +++ b/src/cuda/spin_fields.cu @@ -11,6 +11,7 @@ // C++ standard library headers // Vampire headers +#include "anisotropy.hpp" #include "cuda.hpp" // Local cuda headers @@ -44,6 +45,12 @@ void update_spin_fields () check_cuda_errors (__FILE__, __LINE__); + //If neel fields are enabled, execute the kernel + if (anisotropy::is_neel_enabled()) cu::update_neel_fields(); + + //If rotational fields are enabled, execute the kernel + if (anisotropy::is_rotational_enabled()) cu::update_rotational_fields(); + // Call kernel to calculate non-exchange spin fields cu::update_non_exchange_spin_fields_kernel <<< cu::grid_size, cu::block_size >>> ( cu::atoms::d_materials, cu::mp::d_material_params, @@ -127,7 +134,7 @@ __global__ void update_non_exchange_spin_fields_kernel ( * Spherical harmonics */ - const cu_real_t scale = 0.6666666666666667; + const cu_real_t scale = 2.0 / 3.0; // Reduced anisotropy constants ku/mu_s [J/T] const cu_real_t k2 = material.sh2; @@ -135,13 +142,31 @@ __global__ void update_non_exchange_spin_fields_kernel ( const cu_real_t k6 = material.sh6; const cu_real_t ek2 = k2 * 3.0 * sdote; - const cu_real_t ek4 = -k4 * 0.125 * (140.0 * sdote3 - 60.0 *sdote); - const cu_real_t ek6 = k6 * 0.0625 * (1386.0 * sdote5 - 1260.0 * sdote3 + 210.0 * sdote); - + const cu_real_t ek4 = k4 * (6.0 * sdote3 - ((18.0/7.0) * sdote)); + const cu_real_t ek6 = k6 * (3.0/11.0) * (((33.0)*sdote5) - (30.0 * sdote3) + (5.0*sdote)); + field_x += scale * ex * (ek2 + ek4 + ek6); field_y += scale * ey * (ek2 + ek4 + ek6); field_z += scale * ez * (ek2 + ek4 + ek6); + /* + * Cubic Anisotropy + */ + + // scale factor from derivative of E = -1/2 (sx^4 + sy^4 + sz^4) + const cu_real_t scalec = 0.5*4.0; + + const cu_real_t kc4 = material.kc4; + const cu_real_t kc6 = material.kc6; + + const cu_real_t sx2 = sx*sx; + const cu_real_t sy2 = sy*sy; + const cu_real_t sz2 = sz*sz; + + field_x += scalec * sx * ((sx2 * kc4) + (-sy2 * sz2 * kc6)); + field_y += scalec * sy * ((sy2 * kc4) + (-sz2 * sx2 * kc6)); + field_z += scalec * sz * ((sz2 * kc4) + (-sx2 * sy2 * kc6)); + /* * Lattice anisotropy */ diff --git a/src/cuda/statistics.cu b/src/cuda/statistics.cu old mode 100644 new mode 100755 index cc35c6f93..5d97997a8 --- a/src/cuda/statistics.cu +++ b/src/cuda/statistics.cu @@ -10,12 +10,15 @@ // C++ standard library headers // Vampire headers +#include "atoms.hpp" #include "cuda.hpp" // Local cuda headers #include "cuda_utils.hpp" #include "internal.hpp" +#include "sim.hpp" #include "statistics.hpp" +#include "stats.hpp" #include "typedefs.hpp" #ifdef CUDA @@ -26,65 +29,125 @@ namespace vcuda{ #ifdef CUDA - namespace stats - { - void update () - { - - // If enabled use CPU to calculate statistics by copying data from GPU - if(vcuda::internal::stats::use_cpu){ - - // copy spin data to CPU - /* - thrust::copy(internal::atoms::x_spin_array.begin(),internal::atoms::x_spin_array.end(),::atoms::x_spin_array.begin()); - thrust::copy(internal::atoms::y_spin_array.begin(),internal::atoms::y_spin_array.end(),::atoms::y_spin_array.begin()); - thrust::copy(internal::atoms::z_spin_array.begin(),internal::atoms::z_spin_array.end(),::atoms::z_spin_array.begin()); - */ - - cudaMemcpy(internal::h_x_spin_transfer_buffer, internal::atoms::d_x_spin, ::atoms::num_atoms * sizeof(cu::cu_real_t), cudaMemcpyDeviceToHost); - cudaMemcpy(internal::h_y_spin_transfer_buffer, internal::atoms::d_y_spin, ::atoms::num_atoms * sizeof(cu::cu_real_t), cudaMemcpyDeviceToHost); - cudaMemcpy(internal::h_z_spin_transfer_buffer, internal::atoms::d_z_spin, ::atoms::num_atoms * sizeof(cu::cu_real_t), cudaMemcpyDeviceToHost); - - std::copy(internal::h_x_spin_transfer_buffer, internal::h_x_spin_transfer_buffer + ::atoms::num_atoms, ::atoms::x_spin_array.begin()); - std::copy(internal::h_y_spin_transfer_buffer, internal::h_y_spin_transfer_buffer + ::atoms::num_atoms, ::atoms::y_spin_array.begin()); - std::copy(internal::h_z_spin_transfer_buffer, internal::h_z_spin_transfer_buffer + ::atoms::num_atoms, ::atoms::z_spin_array.begin()); - - // call cpu statistics functions - if(::stats::calculate_system_magnetization) ::stats::system_magnetization.calculate_magnetization(::atoms::x_spin_array, ::atoms::y_spin_array, ::atoms::z_spin_array, ::atoms::m_spin_array); - if(::stats::calculate_material_magnetization) ::stats::material_magnetization.calculate_magnetization(::atoms::x_spin_array, ::atoms::y_spin_array, ::atoms::z_spin_array, ::atoms::m_spin_array); - if(::stats::calculate_height_magnetization) ::stats::height_magnetization.calculate_magnetization(::atoms::x_spin_array, ::atoms::y_spin_array, ::atoms::z_spin_array, ::atoms::m_spin_array); - if(::stats::calculate_material_height_magnetization) ::stats::material_height_magnetization.calculate_magnetization(::atoms::x_spin_array, ::atoms::y_spin_array, ::atoms::z_spin_array, ::atoms::m_spin_array); - - // return before doing the GPU version - return; - } - - - // increase the counter - cu::stats::counter++; - - } - - void get () - { - - // If CPU stats calculation do nothing - if(vcuda::internal::stats::use_cpu) return; - } - - void reset () - { - // reset magnetization statistics - if(vcuda::internal::stats::use_cpu){ - if(::stats::calculate_system_magnetization) ::stats::system_magnetization.reset_magnetization_averages(); - if(::stats::calculate_material_magnetization) ::stats::material_magnetization.reset_magnetization_averages(); - if(::stats::calculate_height_magnetization) ::stats::height_magnetization.reset_magnetization_averages(); - if(::stats::calculate_material_height_magnetization) ::stats::material_height_magnetization.reset_magnetization_averages(); - return; - } - } - - } /* stats */ +namespace stats{ + void update (){ + + // If enabled use CPU to calculate statistics by copying data from GPU + if(vcuda::internal::stats::use_cpu){ + + // copy spin data to CPU + /* + thrust::copy(internal::atoms::x_spin_array.begin(),internal::atoms::x_spin_array.end(),::atoms::x_spin_array.begin()); + thrust::copy(internal::atoms::y_spin_array.begin(),internal::atoms::y_spin_array.end(),::atoms::y_spin_array.begin()); + thrust::copy(internal::atoms::z_spin_array.begin(),internal::atoms::z_spin_array.end(),::atoms::z_spin_array.begin()); + */ + + cudaMemcpy(internal::h_x_spin_transfer_buffer, internal::atoms::d_x_spin, ::atoms::num_atoms * sizeof(cu::cu_real_t), cudaMemcpyDeviceToHost); + cudaMemcpy(internal::h_y_spin_transfer_buffer, internal::atoms::d_y_spin, ::atoms::num_atoms * sizeof(cu::cu_real_t), cudaMemcpyDeviceToHost); + cudaMemcpy(internal::h_z_spin_transfer_buffer, internal::atoms::d_z_spin, ::atoms::num_atoms * sizeof(cu::cu_real_t), cudaMemcpyDeviceToHost); + + std::copy(internal::h_x_spin_transfer_buffer, internal::h_x_spin_transfer_buffer + ::atoms::num_atoms, ::atoms::x_spin_array.begin()); + std::copy(internal::h_y_spin_transfer_buffer, internal::h_y_spin_transfer_buffer + ::atoms::num_atoms, ::atoms::y_spin_array.begin()); + std::copy(internal::h_z_spin_transfer_buffer, internal::h_z_spin_transfer_buffer + ::atoms::num_atoms, ::atoms::z_spin_array.begin()); + + //--------------------------------------------------------------- + // call cpu statistics functions + //--------------------------------------------------------------- + + // update energy statistics + if(::stats::calculate_system_energy) ::stats::system_energy.calculate(::atoms::x_spin_array, ::atoms::y_spin_array, ::atoms::z_spin_array, ::atoms::m_spin_array, ::atoms::type_array , ::sim::temperature); + if (::stats::calculate_grain_energy) ::stats::grain_energy.calculate(::atoms::x_spin_array, ::atoms::y_spin_array, ::atoms::z_spin_array, ::atoms::m_spin_array, ::atoms::type_array, ::sim::temperature); + if(::stats::calculate_material_energy) ::stats::material_energy.calculate(::atoms::x_spin_array, ::atoms::y_spin_array, ::atoms::z_spin_array, ::atoms::m_spin_array, ::atoms::type_array, ::sim::temperature); + + // update magnetization statistics + if(::stats::calculate_system_magnetization) ::stats::system_magnetization.calculate_magnetization(::atoms::x_spin_array, ::atoms::y_spin_array, ::atoms::z_spin_array, ::atoms::m_spin_array); + if(::stats::calculate_material_magnetization) ::stats::material_magnetization.calculate_magnetization(::atoms::x_spin_array, ::atoms::y_spin_array, ::atoms::z_spin_array, ::atoms::m_spin_array); + if(::stats::calculate_height_magnetization) ::stats::height_magnetization.calculate_magnetization(::atoms::x_spin_array, ::atoms::y_spin_array, ::atoms::z_spin_array, ::atoms::m_spin_array); + if(::stats::calculate_material_height_magnetization) ::stats::material_height_magnetization.calculate_magnetization(::atoms::x_spin_array, ::atoms::y_spin_array, ::atoms::z_spin_array, ::atoms::m_spin_array); + if(::stats::calculate_grain_magnetization) ::stats::grain_magnetization.calculate_magnetization(::atoms::x_spin_array, ::atoms::y_spin_array, ::atoms::z_spin_array, ::atoms::m_spin_array); + if(::stats::calculate_material_grain_magnetization) ::stats::material_grain_magnetization.calculate_magnetization(::atoms::x_spin_array, ::atoms::y_spin_array, ::atoms::z_spin_array, ::atoms::m_spin_array); + if(::stats::calculate_material_grain_height_magnetization) ::stats::material_grain_height_magnetization.calculate_magnetization(::atoms::x_spin_array, ::atoms::y_spin_array, ::atoms::z_spin_array, ::atoms::m_spin_array); + + // update torque statistics + //if(stats::calculate_system_torque) stats::system_torque.calculate_torque(sx,sy,sz,bxs,bys,bzs,bxe,bye,bze,mm); + //if(stats::calculate_grain_torque) stats::grain_torque.calculate_torque(sx,sy,sz,bxs,bys,bzs,bxe,bye,bze,mm); + //if(stats::calculate_material_torque) stats::material_torque.calculate_torque(sx,sy,sz,bxs,bys,bzs,bxe,bye,bze,mm); + + // update specific heat statistics + if(::stats::calculate_system_specific_heat) ::stats::system_specific_heat.calculate(::stats::system_energy.get_total_energy()); + if(::stats::calculate_grain_specific_heat) ::stats::grain_specific_heat.calculate(::stats::grain_energy.get_total_energy()); + if(::stats::calculate_material_specific_heat) ::stats::material_specific_heat.calculate(::stats::material_energy.get_total_energy()); + + // standard deviation in time-step + if(::stats::calculate_material_standard_deviation) ::stats::material_standard_deviation.update(::stats::system_magnetization.get_magnetization()); + + // update susceptibility statistics + if(::stats::calculate_system_susceptibility) ::stats::system_susceptibility.calculate(::stats::system_magnetization.get_magnetization()); + if(::stats::calculate_grain_susceptibility) ::stats::grain_susceptibility.calculate(::stats::grain_magnetization.get_magnetization()); + if(::stats::calculate_material_susceptibility) ::stats::material_susceptibility.calculate(::stats::material_magnetization.get_magnetization()); + + // return before doing the GPU version + return; + } + + + // increase the counter + cu::stats::counter++; + + } + + void get (){ + + // If CPU stats calculation do nothing + if(vcuda::internal::stats::use_cpu) return; + } + + void reset (){ + + // reset magnetization statistics + if(vcuda::internal::stats::use_cpu){ + + // reset energy statistics + if(::stats::calculate_system_energy) ::stats::system_energy.reset_averages(); + if(::stats::calculate_grain_energy) ::stats::grain_energy.reset_averages(); + if(::stats::calculate_material_energy) ::stats::material_energy.reset_averages(); + + // reset magnetization statistics + if(::stats::calculate_system_magnetization) ::stats::system_magnetization.reset_magnetization_averages(); + if(::stats::calculate_grain_magnetization) ::stats::grain_magnetization.reset_magnetization_averages(); + if(::stats::calculate_material_magnetization) ::stats::material_magnetization.reset_magnetization_averages(); + if(::stats::calculate_material_grain_magnetization) ::stats::material_grain_magnetization.reset_magnetization_averages(); + if(::stats::calculate_height_magnetization) ::stats::height_magnetization.reset_magnetization_averages(); + if(::stats::calculate_material_height_magnetization) ::stats::material_height_magnetization.reset_magnetization_averages(); + if(::stats::calculate_material_grain_height_magnetization) ::stats::material_grain_height_magnetization.reset_magnetization_averages(); + + // update torque statistics + //if(stats::calculate_system_torque) stats::system_torque.reset_torque_averages(); + //if(stats::calculate_grain_torque) stats::grain_torque.reset_torque_averages(); + //if(stats::calculate_material_torque) stats::material_torque.reset_torque_averages(); + + // standard deviation in time-step + if(::stats::calculate_material_standard_deviation) ::stats::material_standard_deviation.reset_averages(); + + // reset specific_heat statistics + if(::stats::calculate_system_specific_heat) ::stats::system_specific_heat.reset_averages(); + if(::stats::calculate_grain_specific_heat) ::stats::grain_specific_heat.reset_averages(); + if(::stats::calculate_material_specific_heat) ::stats::material_specific_heat.reset_averages(); + + // reset susceptibility statistics + if(::stats::calculate_system_susceptibility) ::stats::system_susceptibility.reset_averages(); + if(::stats::calculate_grain_susceptibility) ::stats::grain_susceptibility.reset_averages(); + if(::stats::calculate_material_susceptibility) ::stats::material_susceptibility.reset_averages(); + + // reset binder cumulant statistics + //if(stats::calculate_system_binder_cumulant) stats::system_binder_cumulant.reset_averages(); + //if(stats::calculate_material_binder_cumulant) stats::material_binder_cumulant.reset_averages(); + + return; + } + } + +} /* stats */ namespace internal { diff --git a/src/cuda/typedefs.hpp b/src/cuda/typedefs.hpp index bd26ab407..ea0fc2a38 100644 --- a/src/cuda/typedefs.hpp +++ b/src/cuda/typedefs.hpp @@ -74,13 +74,55 @@ namespace internal{ cu_real_t applied_field_unit_x; cu_real_t applied_field_unit_y; cu_real_t applied_field_unit_z; + cu_real_t fmr_field_strength; + cu_real_t fmr_field_frequency; + cu_real_t fmr_field_unit_x; + cu_real_t fmr_field_unit_y; + cu_real_t fmr_field_unit_z; cu_real_t kc4; + cu_real_t kc6; cu_real_t temperature; cu_real_t temperature_rescaling_alpha; cu_real_t temperature_rescaling_Tc; cu_real_t H_th_sigma; }; + // TODO: Add struct for rotational params + + // struct for rotational material parameters + struct material_parameters_rotational_t { + cu_real_t rotational_anisotropy_unit_x; + cu_real_t rotational_anisotropy_unit_y; + cu_real_t rotational_anisotropy_unit_z; + cu_real_t last_axis_anisotropy_unit_x; + cu_real_t last_axis_anisotropy_unit_y; + cu_real_t last_axis_anisotropy_unit_z; + cu_real_t k2r1; + cu_real_t k2r1_odd; + cu_real_t k2r2; + cu_real_t k2r2_odd; + cu_real_t k4r1; + cu_real_t k4r1_odd; + cu_real_t k4r2; + cu_real_t k4r2_odd; + cu_real_t k4r3; + cu_real_t k4r3_odd; + cu_real_t k4r4; + cu_real_t k4r4_odd; + cu_real_t k6r1; + cu_real_t k6r1_odd; + cu_real_t k6r2; + cu_real_t k6r2_odd; + cu_real_t k6r3; + cu_real_t k6r3_odd; + cu_real_t k6r4; + cu_real_t k6r4_odd; + cu_real_t k6r5; + cu_real_t k6r5_odd; + cu_real_t k6r6; + cu_real_t k6r6_odd; + }; + // Type definition for array of material parameters //typedef thrust::device_vector cu_material_array_t; diff --git a/src/data/atoms.cpp b/src/data/atoms.cpp index cf203898d..5d9311db6 100644 --- a/src/data/atoms.cpp +++ b/src/data/atoms.cpp @@ -25,6 +25,7 @@ #include "atoms.hpp" #include +#include //========================================================== // Namespace atom variables @@ -56,15 +57,30 @@ namespace atoms{ std::vector x_spin_array(0); std::vector y_spin_array(0); std::vector z_spin_array(0); - std::vector m_spin_array(0); + std::vector m_spin_array(0); + std::vector mass_spin_array(0); std::vector x_total_spin_field_array(0); /// Total spin dependent fields std::vector y_total_spin_field_array(0); /// Total spin dependent fields std::vector z_total_spin_field_array(0); /// Total spin dependent fields + + std::vector x_total_spin_forces_array(0); /// Total spin dependent forces + std::vector y_total_spin_forces_array(0); /// Total spin dependent forces + std::vector z_total_spin_forces_array(0); /// Total spin dependent forces + + std::vector x_velo_array(0); /// velocities + std::vector y_velo_array(0); /// + std::vector z_velo_array(0); /// + std::vector x_total_external_field_array(0); /// Total external fields std::vector y_total_external_field_array(0); /// Total external fields std::vector z_total_external_field_array(0); /// Total external fields + std::vector x_thermal_field_array(0); /// Total external fields + std::vector y_thermal_field_array(0); /// Total external fields + std::vector z_thermal_field_array(0); /// Total external fields + + std::vector i_exchange_list(0); std::vector v_exchange_list(0); std::vector t_exchange_list(0); diff --git a/src/data/category.cpp b/src/data/category.cpp index 6aaa62a65..b570b66af 100644 --- a/src/data/category.cpp +++ b/src/data/category.cpp @@ -1,51 +1,17 @@ -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // -// Vampire - A code for atomistic simulation of magnetic materials +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). // -// Copyright (C) 2009-2012 R.F.L.Evans +// (c) Richard F L Evans and Mara strungaru 2023. All rights reserved. // -// Email:richard.evans@york.ac.uk +// Email: richard.evans@york.ac.uk // -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. +//------------------------------------------------------------------------------ // -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -// -// ---------------------------------------------------------------------------- -// -/// -/// @file -/// @brief Category Class data structure -/// -/// Categories identify a collection of atoms in a way similar to, but separate from, their material. -/// They support output of categorised magnetisation, eg in a plane of equal height, or radial dependent magneisation. -/// Also allows constraint of a subset of atoms using CMC/Lagrange. Can be used to track sublattices in a single material. -/// -/// @author Richard Evans, richard.evans@york.ac.uk -/// -/// @section License -/// Use of this code, either in source or compiled form, is subject to license from the authors. -/// Copyright \htmlonly © \endhtmlonly Richard Evans, 2011. All Rights Reserved. -/// -/// @internal -/// Created 30/04/2011 -/// Revision 1.0 -/// Copyright Copyright (c) 2011, Richard Evans -/// -///===================================================================================== -/// // Vampire header files -#include "atoms.hpp" +#include "atoms.hpp" #include "category.hpp" #include "grains.hpp" #include "material.hpp" @@ -73,11 +39,13 @@ namespace cat{ mean_torque(0.0), energy(0.0), theta(0.0), - phi(0.0) + phi(0.0), + spin_temp(0.0), + mean_spin_temp(0.0), + SxH2(0.0), + SH(0.0) { category_atoms.resize(0,0); - } - - + } // End of namespace cat diff --git a/src/data/grains.cpp b/src/data/grains.cpp index 0c0f38267..dbdf5ee0f 100644 --- a/src/data/grains.cpp +++ b/src/data/grains.cpp @@ -218,7 +218,7 @@ int set_properties(){ //-------------------------------------------------- // output grain coordinates to disk on root process //-------------------------------------------------- - if( vmpi::my_rank == 0 && grains::num_grains > 0){ + if( vmpi::my_rank == 0 && grains::num_grains > 1){ std::ofstream file4; file4.open("grain-coordinates.txt"); diff --git a/src/dipole/atomistic.cpp b/src/dipole/atomistic.cpp index ce9c3f2d8..97fa79fec 100644 --- a/src/dipole/atomistic.cpp +++ b/src/dipole/atomistic.cpp @@ -13,6 +13,7 @@ #include // Vampire headers +#include "cells.hpp" #include "dipole.hpp" #include "vio.hpp" #include "vmpi.hpp" @@ -151,6 +152,7 @@ namespace dipole{ std::vector& z_spin_array){ const double prefactor = 0.9274009994; // mu_o_4pi * muB / Angstrom^3 = 1.0e-7 * 9.274009994e-24 / 1.0e-30 = 0.9274009994 + const double eight_pi_o_three = 8.0 * M_PI / 3.0; // cast number of local atoms to a local constant const int num_atoms_on_my_processor = dp::num_local_atoms; @@ -185,6 +187,11 @@ namespace dipole{ int atom_i_in_total_list_j = atom_i; #endif + // self term for atom i (+2/3 mu_s * V_atom) * mu_0 / 4 pi + const double bx_self = eight_pi_o_three * dp::sx[atom_i] * dp::sm[atom_i] / cells::atomic_volume; + const double by_self = eight_pi_o_three * dp::sy[atom_i] * dp::sm[atom_i] / cells::atomic_volume; + const double bz_self = eight_pi_o_three * dp::sz[atom_i] * dp::sm[atom_i] / cells::atomic_volume; + // get coordinates of atom i const double xi = dp::cx[atom_i_in_total_list_j]; const double yi = dp::cy[atom_i_in_total_list_j]; @@ -282,9 +289,9 @@ namespace dipole{ } // save total dipole field to atomic field array - dipole::atom_dipolar_field_array_x[atom_i] = prefactor * bx; - dipole::atom_dipolar_field_array_y[atom_i] = prefactor * by; - dipole::atom_dipolar_field_array_z[atom_i] = prefactor * bz; + dipole::atom_dipolar_field_array_x[atom_i] = prefactor * (bx + bx_self); + dipole::atom_dipolar_field_array_y[atom_i] = prefactor * (by + by_self); + dipole::atom_dipolar_field_array_z[atom_i] = prefactor * (bz + bz_self); } diff --git a/src/dipole/data.cpp b/src/dipole/data.cpp index 435972ca0..a80b9f817 100644 --- a/src/dipole/data.cpp +++ b/src/dipole/data.cpp @@ -16,8 +16,6 @@ // dipole module headers #include "internal.hpp" - - namespace dipole{ //------------------------------------------------------------------------------ @@ -58,18 +56,23 @@ namespace dipole{ double atomistic_cutoff = 20.0; //distance in A; bool atomsitic_tensor_enabled = true; + int cells_num_cells; + int cells_num_local_cells; + std::vector cells_volume_array; + namespace internal{ std::vector < int > cell_dx; std::vector < int > cell_dy; std::vector < int > cell_dz; std::vector < std::vector < std::vector > > idarray; - //std::ofstream output_field; + //------------------------------------------------------------------------ // Shared variables inside dipole module //------------------------------------------------------------------------ bool initialised=false; bool output_atomistic_dipole_field = false; // flag to toggle output of atomic resolution dipole field + bool output_dipole_field = false; // flag to toggle output of dipole field int update_time=-1; /// last update time @@ -90,11 +93,8 @@ namespace dipole{ std::vector < int > atom_type_array; std::vector < int > atom_cell_id_array; - int cells_num_cells; - int cells_num_local_cells; std::vector cells_local_cell_array; std::vector cells_num_atoms_in_cell; - std::vector < double > cells_volume_array; std::vector cells_pos_and_mom_array; std::vector < int > proc_cell_index_array1D; diff --git a/src/dipole/fft_atomistic.cpp b/src/dipole/fft_atomistic.cpp index e7e26ca13..9e91fc9c2 100644 --- a/src/dipole/fft_atomistic.cpp +++ b/src/dipole/fft_atomistic.cpp @@ -116,16 +116,14 @@ namespace dipole{ #ifdef FFT - if( fftw_init_threads() == 0) - std::cout << "Error initialising threads for FFTW!" << std::endl; + #ifdef FFTW_OMP + int Nthreads = 1; + if( fftw_init_threads() == 0) std::cout << "Error initialising threads for FFTW!" << std::endl; + Nthreads = omp_get_max_threads(); + std::cout << "Planning FFT with Nthreads = " << Nthreads << std::endl; + fftw_plan_with_nthreads(Nthreads); + #endif - int Nthreads = 1; - -#ifdef FFTW_OMP - Nthreads = omp_get_max_threads(); - std::cout << "Planning FFT with Nthreads = " << Nthreads << std::endl; -#endif - fftw_plan_with_nthreads(Nthreads); const double prefactor = 0.9274009994; // mu_0 * muB / (4*pi*Angstrom^3) = 1.0e-7 * 9.274009994e-24 / 1.0e-30 = 0.9274009994 @@ -220,7 +218,7 @@ namespace dipole{ // loop over the system mesh to // construct the interaction matrix // w(r) = (\mu_0 / 4 pi r^5) (3 r \outer r - I r*r) - int ind = 0; + //int ind = 0; for( int i = 0 ; i < Nx; i++) { for( int j = 0; j < Ny; j++) { for( int k = 0; k < Nz; k++) { @@ -412,7 +410,9 @@ namespace dipole{ fftw_destroy_plan(plan_M); fftw_destroy_plan(plan_H); - fftw_cleanup_threads(); + #ifdef FFTW_OMP + fftw_cleanup_threads(); + #endif #endif return; diff --git a/src/dipole/fft_macrocell.cpp b/src/dipole/fft_macrocell.cpp index 6a0f512ff..ac2e45ad2 100644 --- a/src/dipole/fft_macrocell.cpp +++ b/src/dipole/fft_macrocell.cpp @@ -116,16 +116,13 @@ namespace dipole{ #ifdef FFT - if( fftw_init_threads() == 0) - std::cout << "Error initialising threads for FFTW!" << std::endl; - - int Nthreads = 1; - -#ifdef FFTW_OMP - Nthreads = omp_get_max_threads(); - std::cout << "Planning FFT with Nthreads = " << Nthreads << std::endl; -#endif - fftw_plan_with_nthreads(Nthreads); + #ifdef FFTW_OMP + int Nthreads = 1; + if( fftw_init_threads() == 0) std::cout << "Error initialising threads for FFTW!" << std::endl; + Nthreads = omp_get_max_threads(); + std::cout << "Planning FFT with Nthreads = " << Nthreads << std::endl; + fftw_plan_with_nthreads(Nthreads); + #endif const double prefactor = 0.9274009994; // mu_0 * muB / (4*pi*Angstrom^3) = 1.0e-7 * 9.274009994e-24 / 1.0e-30 = 0.9274009994 @@ -220,7 +217,7 @@ namespace dipole{ // loop over the system mesh to // construct the interaction matrix // w(r) = (\mu_0 / 4 pi r^5) (3 r \outer r - I r*r) - int ind = 0; + //int ind = 0; for( int i = 0 ; i < Nx; i++) { for( int j = 0; j < Ny; j++) { for( int k = 0; k < Nz; k++) { @@ -426,7 +423,9 @@ namespace dipole{ fftw_destroy_plan(plan_M); fftw_destroy_plan(plan_H); + #ifdef FFTW_OMP fftw_cleanup_threads(); + #endif #endif return; diff --git a/src/dipole/initialize.cpp b/src/dipole/initialize.cpp index 01ed9c60e..27a709867 100644 --- a/src/dipole/initialize.cpp +++ b/src/dipole/initialize.cpp @@ -66,8 +66,6 @@ namespace dipole{ return; } - if(vmpi::my_rank==0) dp_fields.open("dipole-field"); - //------------------------------------------------------------------------------------- // Set const for functions //------------------------------------------------------------------------------------- @@ -288,7 +286,7 @@ namespace dipole{ if (vmpi::my_rank == 0) { - int cells_non_zero = 0; /// Counter for cells with atoms inside + //int cells_non_zero = 0; /// Counter for cells with atoms inside int num_atoms_magnetic = 0.0; // Initialise tot num of magnetic atoms double Vtot = 0.0; /// Initialise total volume of the system // Define and initialise Demag factor N tensor components @@ -300,12 +298,10 @@ namespace dipole{ double Nzz = 0.0; // Calculate number of magnetic atoms and Vtot - for(int i = 0; i < cells::num_cells; i++) - { - if (dipole::internal::cells_num_atoms_in_cell[i] > 0) - { + for(int i = 0; i < cells::num_cells; i++){ + if (dipole::internal::cells_num_atoms_in_cell[i] > 0){ num_atoms_magnetic += dipole::internal::cells_num_atoms_in_cell[i]; - cells_non_zero++ ; + //cells_non_zero++; } } diff --git a/src/dipole/interface.cpp b/src/dipole/interface.cpp index 7a1e9f727..f66f24e4c 100644 --- a/src/dipole/interface.cpp +++ b/src/dipole/interface.cpp @@ -112,6 +112,13 @@ namespace dipole{ dipole::internal::output_atomistic_dipole_field = true; return true; } + //------------------------------------------------------------------- + test="output-dipole-field"; + if(word==test){ + // set flag to output atomistic dipole field + dipole::internal::output_dipole_field = true; + return true; + } //-------------------------------------------------------------------- // Keyword not found //-------------------------------------------------------------------- diff --git a/src/dipole/internal.hpp b/src/dipole/internal.hpp index 99402d0ec..5f2390954 100644 --- a/src/dipole/internal.hpp +++ b/src/dipole/internal.hpp @@ -41,6 +41,10 @@ namespace dipole{ //------------------------------------------------------------------------- extern bool initialised; extern bool output_atomistic_dipole_field; // flag to toggle output of atomic resolution dipole field + extern bool output_dipole_field; // flag to toggle output of dipole field + + // parameters for atomistic calculation + extern double atomistic_atomic_volume; // volume of each magnetic moment for atomistic self term // enumerated list of different dipole solvers enum solver_t{ @@ -52,6 +56,7 @@ namespace dipole{ fft = 5, // fft method wit tranlational invariance atomisticfft = 6 // atomistic dipole dipole with fft }; + extern std::vector < int > cell_dx; extern std::vector < int > cell_dy; extern std::vector < int > cell_dz; @@ -76,11 +81,13 @@ namespace dipole{ extern std::vector < int > atom_type_array; extern std::vector < int > atom_cell_id_array; - extern int cells_num_cells; - extern int cells_num_local_cells; + // aliased functions so existing dipole::internal:: uses keep working + using dipole::cells_num_cells; + using dipole::cells_num_local_cells; + using dipole::cells_volume_array; + extern std::vector cells_local_cell_array; extern std::vector cells_num_atoms_in_cell; - extern std::vector < double > cells_volume_array; extern std::vector cells_pos_and_mom_array; extern std::vector < int > proc_cell_index_array1D; @@ -330,7 +337,7 @@ namespace dipole{ std::vector& moments_array); //----------------------------------------------------------------- - // Function to output atomic resolution dipole field + // Functions to output dipole fields //----------------------------------------------------------------- void output_atomistic_dipole_fields(); diff --git a/src/dipole/makefile b/src/dipole/makefile index f468a624f..5618211dd 100644 --- a/src/dipole/makefile +++ b/src/dipole/makefile @@ -20,6 +20,7 @@ memory.o \ mpi.o \ mpi2.o \ output_atomistic_field.o \ +output_dipole_fields.o \ tensor.o \ update.o \ fft_macrocell.o \ diff --git a/src/dipole/mpi.cpp b/src/dipole/mpi.cpp index 14e11210b..e4f1fb19c 100644 --- a/src/dipole/mpi.cpp +++ b/src/dipole/mpi.cpp @@ -181,7 +181,7 @@ namespace dipole{ std::vector list_cpu_to_send_to; std::vector list_cells_to_send; std::vector list_cells_to_recv; - int num_send_atoms; + //int num_send_atoms; std::vector mpi_send_atoms_cell; std::vector mpi_send_num_atoms_in_cell; std::vector mpi_send_atoms_id; @@ -240,9 +240,9 @@ namespace dipole{ //loop to calcualte if two cells are within the dipole cut off range. for(int cpu=0; cpu bool_array(cells_num_cells,1); /// bool arrays to check whether a cell has been already considered for(int lc=cells_num_cells; lc 0 ){ @@ -674,7 +675,7 @@ namespace internal{ MPI_Isend(&cells_i_need_from_cpu[cpu][0], num_cells_to_recv[cpu], MPI_INT, cpu, 650, MPI_COMM_WORLD, &requests.back()); // increment message ID counter - send_message_ID++; + //send_message_ID++; } } @@ -820,7 +821,8 @@ namespace internal{ // buffer index counter to keep track of which atoms have been extracted (reset for each CPU) int buff_index = 0; - for( int i = 0; i < cells_i_need_from_cpu[cpu].size(); i++ ){ + const int nc_i_need = cells_i_need_from_cpu[cpu].size(); + for( int i = 0; i < nc_i_need; i++ ){ // get cell ID in order to find number of atoms to unpack const int cell = cells_i_need_from_cpu[cpu][i]; diff --git a/src/dipole/output_dipole_fields.cpp b/src/dipole/output_dipole_fields.cpp new file mode 100644 index 000000000..b9fd8164f --- /dev/null +++ b/src/dipole/output_dipole_fields.cpp @@ -0,0 +1,66 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2025. All rights reserved. +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +//#include +#include + +// Vampire headers +#include "cells.hpp" +#include "dipole.hpp" +#include "vio.hpp" +#include "vmpi.hpp" + +// dipole module headers +#include "internal.hpp" + +// alias interal dipole namespace for brevity +namespace dp = dipole::internal; + +namespace dipole{ + +//------------------------------------------------------------------------------ +// Function to output calculated dipole cell coords and fields on root process +// (externally visible: called from both the tensor/macrocell and hierarchical +// dipole solvers, so it lives in the public dipole:: namespace rather than +// dipole::internal::) +//------------------------------------------------------------------------------ +void output_dipole_fields(){ + + // If dipole output not enabled, then do nothing + if(!dipole::internal::output_dipole_field) return; + + // inform user that dipole fields are being outputted + zlog << zTs() << "Outputting dipole fields to file" << std::endl; + + // if rank = 0 open output file + if(vmpi::my_rank == 0){ + + dp_fields.open("dipole-field.txt"); + + for (int i = 0 ; i < dipole::cells_num_cells; i ++){ + dp_fields << i << "\t" << // cell ID + cells::num_atoms_in_cell[i] << "\t" << // num atoms in cell + cells::pos_and_mom_array[4*i+0] << "\t" << // x + cells::pos_and_mom_array[4*i+1] << "\t" << // y + cells::pos_and_mom_array[4*i+2] << "\t" << // z + cells::pos_and_mom_array[4*i+3] << "\t" << // m + dipole::cells_field_array_x[i] << "\t" << // Bx + dipole::cells_field_array_y[i] << "\t" << // By + dipole::cells_field_array_z[i] << "\t" << std::endl; // Bz + } + + } + + return; + +} + +} // end of namespace dipole diff --git a/src/dipole/update.cpp b/src/dipole/update.cpp index 6dd9aa22d..d0d963d32 100644 --- a/src/dipole/update.cpp +++ b/src/dipole/update.cpp @@ -118,12 +118,15 @@ namespace dipole{ MPI_Allreduce(MPI_IN_PLACE, &dipole::cells_field_array_y[0], dipole::internal::cells_num_cells, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); MPI_Allreduce(MPI_IN_PLACE, &dipole::cells_field_array_z[0], dipole::internal::cells_num_cells, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); #endif + for (int i = 0 ; i < dipole::internal::cells_num_cells; i ++){ if (dipole::cells_field_array_x[i] < -1000) dipole::cells_field_array_x[i] = 0.0; if (dipole::cells_field_array_y[i] < -1000) dipole::cells_field_array_y[i] = 0.0; if (dipole::cells_field_array_z[i] < -1000) dipole::cells_field_array_z[i] = 0.0; // std::cout << i << '\t' << dipole::cells_field_array_x[i] << '\t' << dipole::cells_field_array_y[i] << '\t' << dipole::cells_field_array_z[i] < four_spin_neighbour_list_array_i; // 1D list of j neighbours + std::vector four_spin_neighbour_list_array_j; // 1D list of j neighbours + std::vector four_spin_neighbour_list_array_k; // 1D list of k neighnours + std::vector four_spin_neighbour_list_array_l; // 1D list of l neighbours + std::vector four_spin_neighbour_list_start_index; // list of first four spin neighbour for atom i + std::vector four_spin_neighbour_list_end_index; // list of last four spin neighbours for atom i + std::vector four_spin_exchange_list; // value of four_spin + std::vector biquadratic_neighbour_list_array; // 1D list of biquadratic neighbours std::vector biquadratic_neighbour_interaction_type_array; // 1D list of biquadratic exchange interaction types std::vector biquadratic_neighbour_list_start_index; // list of first biquadratic neighbour for atom i diff --git a/src/exchange/fields.cpp b/src/exchange/fields.cpp index c9005f10c..ebce48905 100644 --- a/src/exchange/fields.cpp +++ b/src/exchange/fields.cpp @@ -60,6 +60,10 @@ namespace exchange{ field_array_x, field_array_y, field_array_z); } + if (exchange::four_spin){ + exchange::internal::four_spin_exchange_fields(start_index, end_index, field_array_x, field_array_y, field_array_z); + } + return; } diff --git a/src/exchange/four_spin_energy.cpp b/src/exchange/four_spin_energy.cpp new file mode 100644 index 000000000..5018b0019 --- /dev/null +++ b/src/exchange/four_spin_energy.cpp @@ -0,0 +1,87 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Mara Strungaru 2023. All rights reserved. +// +// Email: mara.strungaru@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers + +// Vampire headers +#include "atoms.hpp" // for exchange list type defs +#include "exchange.hpp" + +// exchange module headers +#include "internal.hpp" + +double dot_product2(double six, double siy, double siz, double sjx, double sjy, double sjz){ + double dp = six*sjx + siy*sjy + siz*sjz; + return dp; +} + +namespace exchange{ + +//----------------------------------------------------------------------------------------- +// Function to calculate Four Spin exchange fields for spins between start and end index +//----------------------------------------------------------------------------------------- +// Four Spin exchange given by +// +// E_4s = (Si.Sj)(Sk.Sl) + (Si.Sk)(Sj.Sl) + (Si.Sl)(Sj.Sk) +// +// Field given by: +// +// H_4s^x = -dE/dSix =1/3 Dq (Sxj (Sk . Sl) + Sxk (Sl . Sj) + Sxl (Sk . Sj)) +// H_4s^y = -dE/dSiy =1/3 Dq (Syj (Sk . Sl) + Syk (Sl . Sj) + Syl (Sk . Sj)) +// H_4s^z = -dE/dSiz =1/3 Dq (Szj (Sk . Sl) + Szk (Sl . Sj) + Szl (Sk . Sj)) +//----------------------------------------------------------------------------------------- + +double single_spin_four_spin_energy(const int atom, const double sx, const double sy, const double sz){ + + if(!exchange::four_spin) return 0.0; + + double energy=0.0; + + const double six = atoms::x_spin_array[atom]; + const double siy = atoms::y_spin_array[atom]; + const double siz = atoms::z_spin_array[atom]; + + // Loop over neighbouring spins to calculate exchange + for(int nn = internal::four_spin_neighbour_list_start_index[atom]; nn <= internal::four_spin_neighbour_list_end_index[atom]; ++nn){ + + const int natomj = internal::four_spin_neighbour_list_array_j[nn]; + const int natomk = internal::four_spin_neighbour_list_array_k[nn]; + const int natoml = internal::four_spin_neighbour_list_array_l[nn]; + + const double sjx = atoms::x_spin_array[natomj]; + const double sjy = atoms::y_spin_array[natomj]; + const double sjz = atoms::z_spin_array[natomj]; + + const double skx = atoms::x_spin_array[natomk]; + const double sky = atoms::y_spin_array[natomk]; + const double skz = atoms::z_spin_array[natomk]; + + const double slx = atoms::x_spin_array[natoml]; + const double sly = atoms::y_spin_array[natoml]; + const double slz = atoms::z_spin_array[natoml]; + + const double si_dot_sj = dot_product2(six,siy,siz,sjx,sjy,sjz); + const double si_dot_sk = dot_product2(six,siy,siz,skx,sky,skz); + const double si_dot_sl = dot_product2(six,siy,siz,slx,sly,slz); + const double sk_dot_sl = dot_product2(skx,sky,skz,slx,sly,slz); + const double sj_dot_sk = dot_product2(skx,sky,skz,sjx,sjy,sjz); + const double sj_dot_sl = dot_product2(sjx,sjy,sjz,slx,sly,slz); + const double Jij = internal::four_spin_exchange_list[nn]; + + energy = energy - 4.0*Jij/12.0*(si_dot_sj*sk_dot_sl + si_dot_sk*sj_dot_sl + si_dot_sl*sj_dot_sk); + + } + + return energy; +} + +} // end of namespace diff --git a/src/exchange/four_spin_fields.cpp b/src/exchange/four_spin_fields.cpp new file mode 100644 index 000000000..55baa3df1 --- /dev/null +++ b/src/exchange/four_spin_fields.cpp @@ -0,0 +1,82 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Mara Strungaru 2023. All rights reserved. +// +// Email: mara.strungaru@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers + +// Vampire headers +#include "atoms.hpp" // for exchange list type defs +#include "sim.hpp" +#include "exchange.hpp" + +// exchange module headers +#include "internal.hpp" + +double dot_product(double six, double siy, double siz, double sjx, double sjy, double sjz){ + double dp = six*sjx + siy*sjy + siz*sjz; + return dp; +} + +namespace exchange{ + +namespace internal{ + +void four_spin_exchange_fields(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, // field vectors for atoms + std::vector& field_array_x, + std::vector& field_array_y, + std::vector& field_array_z){ // last +1 atom to be calculated){ + + //std::vector < int > numbers(atoms::num_atoms,0); + + const int num_four_spin_neighbours = four_spin_neighbour_list_array_l.size(); + + // loop over all neighbours + for(int nn = 0; nn < num_four_spin_neighbours; ++nn){ + + // get neighbouring atom number + const int atom = four_spin_neighbour_list_array_i[nn]; + const int natomj = four_spin_neighbour_list_array_j[nn]; + const int natomk = four_spin_neighbour_list_array_k[nn]; + const int natoml = four_spin_neighbour_list_array_l[nn]; + //const int jmaterial = atoms::type_array[natomj]; + const double Jij = four_spin_exchange_list[nn]; + + const double sjx = atoms::x_spin_array[natomj]; + const double sjy = atoms::y_spin_array[natomj]; + const double sjz = atoms::z_spin_array[natomj]; + + const double skx = atoms::x_spin_array[natomk]; + const double sky = atoms::y_spin_array[natomk]; + const double skz = atoms::z_spin_array[natomk]; + + const double slx = atoms::x_spin_array[natoml]; + const double sly = atoms::y_spin_array[natoml]; + const double slz = atoms::z_spin_array[natoml]; + + const double sk_dot_sl = dot_product(skx,sky,skz,slx,sly,slz); + const double sj_dot_sk = dot_product(skx,sky,skz,sjx,sjy,sjz); + const double sj_dot_sl = dot_product(sjx,sjy,sjz,slx,sly,slz); + + double athird=1.0/3.0; + + field_array_x[atom] = field_array_x[atom] + (Jij*athird)*(sjx*sk_dot_sl + skx*sj_dot_sl + slx*sj_dot_sk); + field_array_y[atom] = field_array_y[atom] + (Jij*athird)*(sjy*sk_dot_sl + sky*sj_dot_sl + sly*sj_dot_sk); + field_array_z[atom] = field_array_z[atom] + (Jij*athird)*(sjz*sk_dot_sl + skz*sj_dot_sl + slz*sj_dot_sk); + + } + + return; + +} + +} // end of internal namespace +} // end of exchange namespace diff --git a/src/exchange/initialize.cpp b/src/exchange/initialize.cpp index 3c51c67e9..5482e82e1 100644 --- a/src/exchange/initialize.cpp +++ b/src/exchange/initialize.cpp @@ -276,6 +276,9 @@ namespace exchange{ // initialise biquadratic_exchange exchange::internal::initialize_biquadratic_exchange(); + // initialise four spin + exchange::internal::initialize_four_spin_exchange(bilinear); + // Calculate Dzyaloshinskii-Moriya interactions (must be done after exchange unrolling) exchange::internal::calculate_dmi(bilinear); diff --git a/src/exchange/initialize_four_spin.cpp b/src/exchange/initialize_four_spin.cpp new file mode 100644 index 000000000..ac19e97fd --- /dev/null +++ b/src/exchange/initialize_four_spin.cpp @@ -0,0 +1,205 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Mara Stungaru 2023. All rights reserved. +// +// Email: mss555@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers + +// Vampire headers +#include "errors.hpp" +#include "exchange.hpp" +#include "unitcell.hpp" +#include "vio.hpp" +#include "atoms.hpp" +#include "create.hpp" +#include "material.hpp" + + +// exchange module headers +#include "internal.hpp" + +// Vampire headers + +namespace exchange{ + +namespace internal{ + + void initialize_four_spin_exchange(std::vector >& cneighbourlist){ + + // if four spin exchange is not needed then do nothing + if(!internal::enable_fourspin) return; + + exchange::internal::four_spin_neighbour_list_start_index.resize(atoms::num_atoms,0); + exchange::internal::four_spin_neighbour_list_end_index.resize(atoms::num_atoms,0); + + //distances here are for a cubic system of normalised dimension + double nn_distance = internal::fs_cutoff_1; + double nnn_distance = internal::fs_cutoff_2; + + double d1,d2,d3; + double ucx,ucy,ucz; + double x_a,y_a,z_a; + double x_b,y_b,z_b; + double x_c,y_c,z_c; + int k1=1, k2=1,k3=1; + //int counter=0; // seemingly unused variables + //int counter_sort=0; + //int c1,c2; + + //vectors to store the nearest and next nearest neighbours + std::vector first_neigh(0); + std::vector start_first_neigh(0); + std::vector end_first_neigh(0); + + first_neigh.resize(20*atoms::num_atoms); + start_first_neigh.resize(atoms::num_atoms); + end_first_neigh.resize(atoms::num_atoms); + + //to print out the four-spin interaction + std::ofstream ofile; + ofile.open("fourspin_quartets.txt"); + + ucx=1.0; + ucy=1.0; + ucz=1.0; + + int counter1=0; + + //this part computes the first order exchange neighbourlist and second + start_first_neigh[0]=0; + + for(int i=0;i< atoms::num_atoms ;i++){ + const int start=atoms::neighbour_list_start_index[i]; + const int end=atoms::neighbour_list_end_index[i]+1; + int am=0; + double dist=0; + + for(int a=start;a= nn_distance-0.01)){ + first_neigh[counter1]=cneighbourlist[i][am].nn; + counter1=counter1+1; + } + + am=am+1; + + } + + end_first_neigh[i]=counter1-1; + start_first_neigh[i+1]=end_first_neigh[i]+1; + + } + + //now create the fourspin interactions + int n_interactions = 0; + + for(int i=0;i< atoms::num_atoms ;i++){ + const int start=start_first_neigh[i]; + const int end=end_first_neigh[i]+1; + const int imaterial = atoms::type_array[i]; + const double imus = 1.0 / mp::material[imaterial].mu_s_SI; // get inverse spin moment + + four_spin_neighbour_list_start_index[i] = n_interactions; + + for(int a=start;a= 1.01) x_a=atoms::x_coord_array[first_neigh[a]]+std::pow(-1,k1)*cs::system_dimensions[0]; + else x_a=atoms::x_coord_array[first_neigh[a]]; + + if (abs(atoms::y_coord_array[i]-atoms::y_coord_array[first_neigh[a]])/ucy >= 1.01) y_a=atoms::y_coord_array[first_neigh[a]]+std::pow(-1,k2)*cs::system_dimensions[1]; + else y_a=atoms::y_coord_array[first_neigh[a]]; + + if (abs(atoms::z_coord_array[i]-atoms::z_coord_array[first_neigh[a]])/ucz >= 1.01) z_a=atoms::z_coord_array[first_neigh[a]]+std::pow(-1,k3)*cs::system_dimensions[2]; + else z_a=atoms::z_coord_array[first_neigh[a]]; + + if (abs(atoms::x_coord_array[i]-atoms::x_coord_array[first_neigh[b]])/ucx >= 1.01) x_b=atoms::x_coord_array[first_neigh[b]]+std::pow(-1,k1)*cs::system_dimensions[0]; + else x_b=atoms::x_coord_array[first_neigh[b]]; + + if (abs(atoms::y_coord_array[i]-atoms::y_coord_array[first_neigh[b]])/ucy >= 1.01) y_b=atoms::y_coord_array[first_neigh[b]]+std::pow(-1,k2)*cs::system_dimensions[1]; + else y_b=atoms::y_coord_array[first_neigh[b]]; + + if (abs(atoms::z_coord_array[i]-atoms::z_coord_array[first_neigh[b]])/ucz >= 1.01) z_b=atoms::z_coord_array[first_neigh[b]]+std::pow(-1,k3)*cs::system_dimensions[2]; + else z_b=atoms::z_coord_array[first_neigh[b]]; + + if (abs(atoms::x_coord_array[i]-atoms::x_coord_array[first_neigh[c]])/ucx >= 1.01) x_c=atoms::x_coord_array[first_neigh[c]]+std::pow(-1,k1)*cs::system_dimensions[0]; + else x_c=atoms::x_coord_array[first_neigh[c]]; + + if (abs(atoms::y_coord_array[i]-atoms::y_coord_array[first_neigh[c]])/ucy >= 1.01) y_c=atoms::y_coord_array[first_neigh[c]]+std::pow(-1,k2)*cs::system_dimensions[1]; + else y_c=atoms::y_coord_array[first_neigh[c]]; + + if (abs(atoms::z_coord_array[i]-atoms::z_coord_array[first_neigh[c]])/ucz >= 1.01) z_c=atoms::z_coord_array[first_neigh[c]]+std::pow(-1,k3)*cs::system_dimensions[2]; + else z_c=atoms::z_coord_array[first_neigh[c]]; + + d1=std::sqrt(std::pow(((x_a-x_b)/ucx),2)+std::pow(((y_a-y_b)/ucy),2)+std::pow(((z_a-z_b)/ucz),2)); + d2=std::sqrt(std::pow(((x_c-x_b)/ucx),2)+std::pow(((y_c-y_b)/ucy),2)+std::pow(((z_c-z_b)/ucz),2)); + d3=std::sqrt(std::pow(((x_a-x_c)/ucx),2)+std::pow(((y_a-y_c)/ucy),2)+std::pow(((z_a-z_c)/ucz),2)); + + if (((d1 <= nnn_distance+0.01) && (d1 >= nnn_distance-0.01))&& ((d2 <= nnn_distance+0.01) && (d2 >= nnn_distance-0.01)) &&((d3 <= nnn_distance+0.01) && (d3 >= nnn_distance-0.01)) ){ + //get four spin exchange constant from material i to material j + //add j k l to arrays for atom i . + four_spin_neighbour_list_array_i.push_back(i); + four_spin_neighbour_list_array_j.push_back(first_neigh[a]); + four_spin_neighbour_list_array_k.push_back(first_neigh[b]); + four_spin_neighbour_list_array_l.push_back(first_neigh[c]); + + four_spin_neighbour_list_array_i.push_back(first_neigh[a]); + four_spin_neighbour_list_array_j.push_back(first_neigh[b]); + four_spin_neighbour_list_array_k.push_back(first_neigh[c]); + four_spin_neighbour_list_array_l.push_back(i); + + four_spin_neighbour_list_array_i.push_back(first_neigh[b]); + four_spin_neighbour_list_array_j.push_back(first_neigh[c]); + four_spin_neighbour_list_array_k.push_back(i); + four_spin_neighbour_list_array_l.push_back(first_neigh[a]); + + four_spin_neighbour_list_array_i.push_back(first_neigh[c]); + four_spin_neighbour_list_array_j.push_back(i); + four_spin_neighbour_list_array_k.push_back(first_neigh[a]); + four_spin_neighbour_list_array_l.push_back(first_neigh[b]); + + + // four times for all the permutations + double fs_value= exchange::internal::mp[atoms::type_array[i]].fs[atoms::type_array[first_neigh[a]]]; + four_spin_exchange_list.push_back(fs_value*imus); + four_spin_exchange_list.push_back(fs_value*imus); + four_spin_exchange_list.push_back(fs_value*imus); + four_spin_exchange_list.push_back(fs_value*imus); + + //4 interactions due to the permutations + n_interactions=n_interactions+1; + + ofile << n_interactions << "\t" << i << "\t" << a << "\t" << b << "\t" << c <<"\t"<<-0.23e-21*imus< dmi; // Dzyaloshinskii-Moriya interaction constant std::vector kitaev; // Dzyaloshinskii-Moriya interaction constant + std::vector fs; //Four-spin interaction constant // constructor mp_t (const unsigned int max_materials = 100) @@ -155,6 +156,8 @@ namespace exchange{ // resize arrays to correct size dmi.resize(max_materials, 0.0); // initialise pair anisotropy constants to zero kitaev.resize(max_materials, 0.0); // initialise pair anisotropy constants to zero + fs.resize(max_materials, 0.0); // initialise four-spin constants to zero + }; // end of constructor @@ -170,11 +173,16 @@ namespace exchange{ extern bool enable_dmi; // flag to enable dmi calculation extern bool enable_kitaev; // flag to enable kitaev calculation + extern bool enable_fourspin; extern double dmi_cutoff_range; // cutoff range for DMI calculation (Ã…ngstroms) extern double kitaev_cutoff_range; // cutoff range for Kitaev calculation (Ã…ngstroms) extern double exchange_factor; // scaling factor for exchange constants (usually to correct for ab-initio) + extern double fs; //fourspin interaction + extern double fs_cutoff_1; //First four-spin interaction range + extern double fs_cutoff_2; + extern exchange_t exchange_type; // exchange type to use in simulation extern exchange_t biquadratic_exchange_type; // biquadratic exchange type to use in simulation @@ -234,6 +242,14 @@ namespace exchange{ extern std::vector biquadratic_neighbour_list_start_index; // list of first biquadratic neighbour for atom i extern std::vector biquadratic_neighbour_list_end_index; // list of last biquadratic neighbour for atom i + extern std::vector four_spin_neighbour_list_array_j; // 1D list of j neighbours + extern std::vector four_spin_neighbour_list_array_k; // 1D list of k neighnours + extern std::vector four_spin_neighbour_list_array_l; // 1D list of l neighbours + extern std::vector four_spin_neighbour_list_array_i; // 1D list of l neighbours + extern std::vector four_spin_neighbour_list_start_index; // list of first four spin neighbour for atom i + extern std::vector four_spin_neighbour_list_end_index; // list of last four spin neighbours for atom i + extern std::vector four_spin_exchange_list; // value of fourspin + extern std::vector bq_i_exchange_list; // list of isotropic biquadratic exchange constants extern std::vector bq_v_exchange_list; // list of vectorial biquadratic exchange constants extern std::vector bq_t_exchange_list; // list of tensorial biquadratic exchange constants @@ -280,7 +296,14 @@ namespace exchange{ std::vector& field_array_y, std::vector& field_array_z); + void four_spin_exchange_fields(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, // last +1 atom to be calculated + std::vector& field_array_x, // field vectors for atoms + std::vector& field_array_y, + std::vector& field_array_z); + void initialize_biquadratic_exchange(); + void initialize_four_spin_exchange(std::vector >& cneighbourlist); } // end of internal namespace diff --git a/src/exchange/makefile b/src/exchange/makefile index 79c365d3e..f40151a2b 100644 --- a/src/exchange/makefile +++ b/src/exchange/makefile @@ -11,9 +11,12 @@ dmi.o \ energy.o \ exchange_fields.o \ fields.o \ +four_spin_fields.o \ +four_spin_energy.o \ get_exchange_type.o \ initialize.o \ initialize_biquadratic.o \ +initialize_four_spin.o \ interface.o \ kitaev.o \ unroll_normalised.o \ diff --git a/src/exchange/unroll_normalised.cpp b/src/exchange/unroll_normalised.cpp index 53ee1d9ce..a31756755 100644 --- a/src/exchange/unroll_normalised.cpp +++ b/src/exchange/unroll_normalised.cpp @@ -63,7 +63,7 @@ namespace exchange{ std::vector Jij = internal::bilinear_exchange_constants.get_exchange_values(imaterial, jmaterial, shell); // set exchange field, normalising to mu_s^i atoms::i_exchange_list[nn].Jij = cs::unit_cell.bilinear.interaction[i].Jij[0][0] * Jij[0] * imus; - // reset interation id to neighbour number - causes segfault if nn out of range + // reset interaction id to neighbour number - causes segfault if nn out of range atoms::neighbour_interaction_type_array[nn] = nn; } } @@ -165,7 +165,7 @@ namespace exchange{ err::vexit(); } - // reset interation id to neighbour number - causes segfault if nn out of range + // reset interaction id to neighbour number - causes segfault if nn out of range atoms::neighbour_interaction_type_array[nn] = nn; } // end of neighbour loop diff --git a/src/hamr/internal.hpp b/src/hamr/internal.hpp index 14c9d15b8..28e7f42e8 100644 --- a/src/hamr/internal.hpp +++ b/src/hamr/internal.hpp @@ -10,6 +10,8 @@ //----------------------------------------------------------------------------- // C++ standard library headers +#include + // Vampire headers #include "hamr.hpp" diff --git a/src/hierarchical/update.cpp b/src/hierarchical/update.cpp index 7e00b76cd..f7f23df64 100644 --- a/src/hierarchical/update.cpp +++ b/src/hierarchical/update.cpp @@ -26,7 +26,6 @@ #include "vutil.hpp" // dipole module headers #include "internal.hpp" -#include "../dipole/internal.hpp" // alias internal hierarchical namespace for brevity namespace ha = hierarchical::internal; @@ -47,7 +46,7 @@ void update(std::vector & x_spin_array, // atomic spin directions // update hierarchical magnetization in cells hierarchical::internal::calculate_hierarchical_magnetisation(x_spin_array, y_spin_array, z_spin_array, m_spin_array, magnetic); - + cells::mag(); // instantiate timer vutil::vtimer_t timer; @@ -62,7 +61,7 @@ void update(std::vector & x_spin_array, // atomic spin directions } // Compute dipole fields for all cells with atoms (local cells) - for(int lc = 0; lc < dipole::internal::cells_num_local_cells; lc++){ + for(int lc = 0; lc < dipole::cells_num_local_cells; lc++){ // get global cell ID from local cell list int cell_i = cells::cell_id_array[lc]; @@ -72,7 +71,7 @@ void update(std::vector & x_spin_array, // atomic spin directions const int end = ha::interaction_list_end_index[lc]; // Self demagnetisation factor multiplying m(i) - const double V = dipole::internal::cells_volume_array[cell_i]; + const double V = dipole::cells_volume_array[cell_i]; const double eightPI_three_cell_volume = 8.0*M_PI/(3.0*V); const double self_demag = eightPI_three_cell_volume; //std::cout << self_demag << std::endl; @@ -137,13 +136,13 @@ void update(std::vector & x_spin_array, // atomic spin directions #ifdef MPICF // Reduce fields on all processors so all have correct field values - MPI_Allreduce(MPI_IN_PLACE, &dipole::cells_field_array_x[0], dipole::internal::cells_num_cells, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &dipole::cells_field_array_y[0], dipole::internal::cells_num_cells, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &dipole::cells_field_array_z[0], dipole::internal::cells_num_cells, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &dipole::cells_field_array_x[0], dipole::cells_num_cells, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &dipole::cells_field_array_y[0], dipole::cells_num_cells, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &dipole::cells_field_array_z[0], dipole::cells_num_cells, MPI_DOUBLE, MPI_MAX, MPI_COMM_WORLD); #endif // Check for cells with unrealistic fields from initialisation and zero - for (int i = 0 ; i < dipole::internal::cells_num_cells; i ++){ + for (int i = 0 ; i < dipole::cells_num_cells; i ++){ if (dipole::cells_field_array_x[i] < -1000) dipole::cells_field_array_x[i] = 0.0; if (dipole::cells_field_array_y[i] < -1000) dipole::cells_field_array_y[i] = 0.0; if (dipole::cells_field_array_z[i] < -1000) dipole::cells_field_array_z[i] = 0.0; @@ -157,6 +156,8 @@ void update(std::vector & x_spin_array, // atomic spin directions //zlog << zTs() << "\tDIPOLE UPDATE. Time taken: " << timer.elapsed_time() << " s"<< std::endl; //std::cout << "dipole update time " << timer.elapsed_time() << " s" << std::endl; + dipole::output_dipole_fields(); + return; } // end of update function diff --git a/src/main/command.cpp b/src/main/command.cpp index 68d92df2b..b8073f05c 100644 --- a/src/main/command.cpp +++ b/src/main/command.cpp @@ -31,10 +31,8 @@ void command_line_args(int argc, char* argv[]){ // Loop over all arguments for(int arg = 1; arg < argc; arg++){ - // convert text to std::string std::string sw = argv[arg]; - //----------------------------- // version information //----------------------------- @@ -71,6 +69,24 @@ void command_line_args(int argc, char* argv[]){ } } + //----------------------------- + // output file name + //----------------------------- + else if(sw=="--output-file"){ + // check number of args not exceeded + if(arg+1 < argc){ + arg++; + vout::output_file_name = string(argv[arg]); + } + else{ + terminaltextcolor(RED); + std::cerr << "Error - no file specified for \'--output-file\' command line option" << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + return; + } + } + else{ terminaltextcolor(RED); std::cerr << "Error - unknown command line parameter \'" << sw << "\'" << std::endl; diff --git a/src/main/initialise_variables.cpp b/src/main/initialise_variables.cpp index f32ae6797..473e6df72 100644 --- a/src/main/initialise_variables.cpp +++ b/src/main/initialise_variables.cpp @@ -35,7 +35,6 @@ #include "unitcell.hpp" #include "vmath.hpp" #include "vmpi.hpp" - #include #include #include @@ -178,7 +177,7 @@ int default_system(){ material[0].mu_s_SI=1.5*9.27400915e-24; material[0].gamma_rel=1.0; material[0].element="Ag "; - + material[0].alpha_eq=0.1; // Disable Error Checking err::check=false; @@ -389,6 +388,7 @@ int set_derived_parameters(){ mp::material[mat].one_oneplusalpha_sq = -mp::material[mat].gamma_rel/(1.0+mp::material[mat].alpha*mp::material[mat].alpha); mp::material[mat].alpha_oneplusalpha_sq = mp::material[mat].alpha*mp::material[mat].one_oneplusalpha_sq; mp::material[mat].H_th_sigma = sqrt(2.0*mp::material[mat].alpha*1.3806503e-23 / (mp::material[mat].mu_s_SI*mp::material[mat].gamma_rel*dt)); + mp::material[mat].H_th_sigma_eq = sqrt(2.0*mp::material[mat].alpha_eq*1.3806503e-23 / (mp::material[mat].mu_s_SI*mp::material[mat].gamma_rel*dt)); // Rename un-named materials with material id std::string defname="material#n"; diff --git a/src/main/main.cpp b/src/main/main.cpp index 2669bed7f..080884d49 100644 --- a/src/main/main.cpp +++ b/src/main/main.cpp @@ -1,32 +1,20 @@ -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // -// Vampire - A code for atomistic simulation of magnetic materials +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). // -// Copyright (C) 2009-2012 R.F.L.Evans +// (c) Richard F L Evans 2023. All rights reserved. // -// Email:richard.evans@york.ac.uk +// Email: richard.evans@york.ac.uk // -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -// -// ---------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // #include #include #include #include "create.hpp" +#include "sld.hpp" #include "errors.hpp" #include "info.hpp" #include "material.hpp" @@ -42,13 +30,12 @@ namespace vmain{ std::string input_file_name = "input"; // default input file name } } - int simulate_system(); /// Main function for vampire /// Prints out program header and calls main program routines int main(int argc, char* argv[]){ - + vout::output_file_name="output"; // default output file name // For parallel execution intialise MPI vmpi::initialise(argc, argv); @@ -75,19 +62,15 @@ int main(int argc, char* argv[]){ 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: Sarah Jenkins, Andrea Meo, Andrew Naden, Matthew Ellis," << std::endl; - std::cout << " Oscar Arbelaez, Sam Morris, Rory Pond, Weijia Fan," << std::endl; - std::cout << " Phanwadee Chureemart, Pawel Sobieszczyk, Joe Barker, " << std::endl; - std::cout << " Thomas Ostler, Andreas Biternas, Roy W Chantrell," << std::endl; - std::cout << " Wu Hong-Ye, Razvan Ababei, Sam Westmoreland," << std::endl; - std::cout << " Daniel Meilak" << std::endl; + std::cout << " Main Developers: Richard F L Evans, Sarah Jenkins, Andrea Meo, " << std::endl; + std::cout << " Matthew Ellis, Mara Strungaru, Sergiu Ruta" << std::endl; + std::cout << " " << std::endl; + std::cout << " See developers.txt for details" << std::endl; std::cout << " " << std::endl; #ifdef COMP - std::cout << " Compiled with: " << COMP << std::endl; + std::cout << " Compiled with: " << COMP << std::endl; #endif - std::cout << " Compiler Flags: "; + std::cout << " Compiler Flags: "; #ifdef CUDA std::cout << "CUDA "; #endif @@ -123,6 +106,9 @@ int main(int argc, char* argv[]){ // Create system cs::create(); + //test initialise SLD + sld::initialize(); + // Simulate system sim::run(); @@ -140,62 +126,3 @@ int main(int argc, char* argv[]){ return EXIT_SUCCESS; } - -/// \mainpage Vampire -/// -/// \section intro_sec Introduction -/// -/// Vampire is an anacronym for Visual Atomistic and Micromagnetic Parallel IntegratoR Engine and -/// simulates the magnetic properties of materials using a classical spin model. Models can be -/// defined from the atomistic scale up to micrometer scale using a variety of methods.The code is -/// open source and has been developed by the Computational Magnetism Group at The University of -/// York, United Kingdom. -/// -/// \section features Program Features -/// \subsection features_ss1 System Generation -/// \arg Common crystal structures including Simple Cubic, FCC, BCC, Hexagonal close packed and user-defined -/// \arg Single Nanoparticles with spherical, truncated octahedron, cylindrical, and cubic geometries -/// \arg Regular arrays of nanoparticles, eg bit-patterned recording media -/// \arg Voronoi thin films generated by qvoronoi -/// \arg Multilayered materials including interface roughness and intermixing -/// \subsection features_ss2 Material Properties -/// \arg Generic Heisenberg Hamiltonians (Exchange, Applied Field) -/// \arg Uniaxial, Cubic, 2-ion and Neel Surface Anisotropies -/// \arg User-definable Hamiltonians (greater than nearest-neighbour) -/// \arg Support for Ab-initio input data (Exchange, Anisotropies) -/// \subsection features_ss3 Atomistic Spin Dynamics -/// \arg Landau-Lifshitz-Gilbert Equation of motion with Langevin Dynamics -/// \arg Landau-Lifshitz Equation of motion with Coloured Noise -/// \arg Heun and Semi-analytical numerical solvers -/// \subsection features_ss4 Monte Carlo Methods -/// \arg Basic Monte Carlo integration -/// \arg Constrained Monte Carlo -/// \subsection features_ss5 Energy Minimisation -/// \arg LaGrange Multiplier Energy Minimisation -/// \subsection features_ss6 Parallel Features -/// Vampire has been designed to run efficiently on parallel supercomputers -/// and supports the following parallel modes: -/// \arg Parallel Statistics -/// \arg 2D-decomposition (Thin films) -/// \arg CUDA Acceleration -/// \subsection features_ss5 Visualisation -/// \arg Output to PovRAY Format -/// \arg Realtime OpenGL visualisation (Coming Soon!) -/// \section install_sec Installation -/// Vampire is distributed as both executable (serial version) and source code (serial, parallel, and CUDA versions). -/// Compilation has been tested on a wide range of C++ compilers, including GNU, -/// Intel and Pathscale. -/// \subsection exec Installation for binary distribution -/// The executables, libraries and scripts come packaged in a vampire.x.x.xxx.tar.gz file. To install the software, -/// first unpack the archive with: \n -/// \verbatim tar -xzf vampire.x.x.xxx.tar.gz \endverbatim -/// Then change into the unpacked directory \n -/// \verbatim cd vampire \endverbatim -/// Finally run the install script as super user \n -/// \verbatim sudo ./install.sh \endverbatim -/// -/// \subsection bin Installation for source code distribution -/// Essentially three bits of information are required to compile -/// -/// etc... -/// diff --git a/src/main/makefile b/src/main/makefile index 3f9466a49..4e36bb95d 100644 --- a/src/main/makefile +++ b/src/main/makefile @@ -17,15 +17,15 @@ OBJECTS+=$(addprefix obj/main/,$(main_objects)) # Add specific options for every compiler (ugh) # GNU obj/main/githash.o : OPTIONS = -DGHASH='$(GHASH)' -obj/main/githash_mpi.o : OPTIONS = -DGHASH='$(GHASH)' +obj/main/githash_par.o : OPTIONS = -DGHASH='$(GHASH)' # GNU DEBUG obj/main/githash_gdb.o : OPTIONS = -DGHASH='$(GHASH)' -obj/main/githash_gdb_mpi.o : OPTIONS = -DGHASH='$(GHASH)' +obj/main/githash_gdb_par.o : OPTIONS = -DGHASH='$(GHASH)' # LLVM obj/main/githash_llvm.o : OPTIONS = -DGHASH='$(GHASH)' -obj/main/githash_llvm_mpi.o : OPTIONS = -DGHASH='$(GHASH)' +obj/main/githash_llvm_par.o : OPTIONS = -DGHASH='$(GHASH)' # INTEL obj/main/githash_i.o : OPTIONS = -DGHASH='$(GHASH)' -obj/main/githash_i_mpi.o : OPTIONS = -DGHASH='$(GHASH)' +obj/main/githash_i_par.o : OPTIONS = -DGHASH='$(GHASH)' # ARCHER -obj/main/githash_archer_mpi.o : OPTIONS = -DGHASH='$(GHASH)' +obj/main/githash_archer_par.o : OPTIONS = -DGHASH='$(GHASH)' diff --git a/src/main/material.cpp b/src/main/material.cpp index 964d90630..481740359 100644 --- a/src/main/material.cpp +++ b/src/main/material.cpp @@ -30,10 +30,10 @@ materials_t::materials_t (): name("material#n"), element("H"), alpha(1.0), + alpha_eq(1.0), mu_s_SI(1.72*9.27400915e-24), magnetisation(0.0), gamma_rel(1.0), - random_spins(false), core_shell_size(1.0), interface_roughness(0.0), density(1.0), @@ -45,6 +45,7 @@ materials_t::materials_t (): one_oneplusalpha_sq(0.5), alpha_oneplusalpha_sq(0.5), H_th_sigma(0.0), + H_th_sigma_eq(0.0), constrained(false), temperature(0.0), maximum_temperature(0.0), @@ -76,10 +77,6 @@ materials_t::materials_t (): intermixing[i]=0.0; alloy[i]=0.0; } - initial_spin[0]=0.0; - initial_spin[1]=0.0; - initial_spin[2]=1.0; - // Applied field direction default initialisation applied_field_unit_vector.at(0)=0.0; applied_field_unit_vector.at(1)=0.0; diff --git a/src/main/version.cpp b/src/main/version.cpp index c0e0a7d57..6d62a8564 100644 --- a/src/main/version.cpp +++ b/src/main/version.cpp @@ -19,7 +19,7 @@ namespace vinfo{ // variable string to store version number of code - std::string vampire_version = "6.0.0"; // vampire code version + std::string vampire_version = "7.0.0"; // vampire code version // wrapper function to return vampire version std::string version(){ diff --git a/src/micromagnetic/micromagnetic_llg.cpp b/src/micromagnetic/micromagnetic_llg.cpp index 8c7e96b89..84397eeae 100644 --- a/src/micromagnetic/micromagnetic_llg.cpp +++ b/src/micromagnetic/micromagnetic_llg.cpp @@ -196,7 +196,8 @@ namespace micromagnetic{ // For parallel version set arrays to large negative number every time // to allow parallel reduction to work (MPI_MAX always picks in positive order) #ifdef MPICF - for(int cell=0; cell< x_spin_storage_array.size(); cell++){ + const int ssa_size = x_spin_storage_array.size(); + for(int cell=0; cell < ssa_size; cell++){ x_spin_storage_array[cell] = 0.0; y_spin_storage_array[cell] = 0.0; z_spin_storage_array[cell] = 0.0; @@ -256,7 +257,8 @@ namespace micromagnetic{ // For parallel version set arrays to large negative number every time // to allow parallel reduction to work #ifdef MPICF - for(int cell=0; cell< x_array.size(); cell++){ + const int ca_size = x_array.size(); + for(int cell=0; cell< ca_size; cell++){ x_array[cell] = 0.0; y_array[cell] = 0.0; z_array[cell] = 0.0; diff --git a/src/montecarlo/cmc.cpp b/src/montecarlo/cmc.cpp index 492fdfd05..634dea982 100644 --- a/src/montecarlo/cmc.cpp +++ b/src/montecarlo/cmc.cpp @@ -461,14 +461,14 @@ int cmc_step(){ (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) ){ + // Check for lower energy state and accept unconditionally. Tested on the raw + // (unscaled) energy change rather than delta_energy21, since delta_energy21 is + // itself +/-infinity (or NaN, if the two terms have opposite sign) at T=0, where + // rescaled_material_kBTBohr diverges because sim::temperature=0 - mirrors the + // DE<0 shortcut used for the same reason in mc.cpp/mc_mpi.cpp/lsf_mc.cpp. + const bool lower_energy_state = (delta_energy1 + delta_energy2)<0.0; + probability = lower_energy_state ? 1.0 : exp(-delta_energy21)*((Mz_new/Mz_old)*(Mz_new/Mz_old))*std::fabs(spin2_init_mvd[2]/spin2_fin_mvd[2]); + if((lower_energy_state || (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]; @@ -488,7 +488,6 @@ int cmc_step(){ cmc::energy_reject += 1.0; statistics_reject += 1.0; } - //} } // if s2 not on unit sphere else{ diff --git a/src/montecarlo/cmc_mc.cpp b/src/montecarlo/cmc_mc.cpp index 28c6cd69b..ba1b6052e 100644 --- a/src/montecarlo/cmc_mc.cpp +++ b/src/montecarlo/cmc_mc.cpp @@ -507,34 +507,33 @@ int cmc_mc_step(){ (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]; + // Check for lower energy state and accept unconditionally. Tested on the raw + // (unscaled) energy change rather than delta_energy21, since delta_energy21 is + // itself +/-infinity (or NaN, if the two terms have opposite sign) at T=0, where + // rescaled_material_kBTBohr diverges because sim::temperature=0 - mirrors the + // DE<0 shortcut used for the same reason in mc.cpp/mc_mpi.cpp/lsf_mc.cpp. + const bool lower_energy_state = (delta_energy1 + delta_energy2)<0.0; + probability = lower_energy_state ? 1.0 : exp(-delta_energy21)*((Mz_new/Mz_old)*(Mz_new/Mz_old))*std::fabs(spin2_init_mvd[2]/spin2_fin_mvd[2]); + if((lower_energy_state || (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]; + 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; - statistics_reject += 1.0; - } - //} + cmc::energy_reject += 1.0; + statistics_reject += 1.0; + } } // if s2 not on unit sphere else{ diff --git a/src/montecarlo/data.cpp b/src/montecarlo/data.cpp index 91a640bcb..6f4292b9f 100644 --- a/src/montecarlo/data.cpp +++ b/src/montecarlo/data.cpp @@ -26,6 +26,7 @@ namespace montecarlo{ //------------------------------------------------------------------------------ bool mc_parallel_initialized = false; + bool lsf_mc_parallel_initialized = false; namespace cmc{ bool masked_cmc = false; // determine if generic masked constraint is required diff --git a/src/montecarlo/lsf_mc.cpp b/src/montecarlo/lsf_mc.cpp new file mode 100644 index 000000000..a3f65f7b3 --- /dev/null +++ b/src/montecarlo/lsf_mc.cpp @@ -0,0 +1,179 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) David R Papp 2024. All rights reserved. +// +//------------------------------------------------------------------------------ +// + +// Standard libraries +#include +#include +#include +#include +#include +#include + +// Vampire header files +#include "../simulate/internal.hpp" +#include "atoms.hpp" +#include "errors.hpp" +#include "exchange.hpp" +#include "internal.hpp" +#include "lsf_mc.hpp" +#include "material.hpp" +#include "montecarlo.hpp" +#include "random.hpp" +#include "sim.hpp" +#include "vio.hpp" +#include "vmath.hpp" + +namespace montecarlo{ + + std::vector mod_S; + bool mc_set = false; + + void mcinit(){ + + // Initialize spin length to 1.0 + mod_S.resize(atoms::num_atoms, 1.0); + + mc_set = true; + } + + /// Spin move + /// Move spin within cone near old position + void mc_angle_transverse(const std::vector &old_spin, std::vector &new_spin, const double angle, const int atom){ + + double factorx = mtrandom::grnd(); + double factory = mtrandom::grnd(); + double factorz = mtrandom::grnd(); + + // Function to ensure random numbers have a 50/50 chance of being -ve or +ve + double p; + p = mtrandom::grnd(); + if (p >= 0.5) + factorx *= -1; + p = mtrandom::grnd(); + if (p >= 0.5) + factory *= -1; + p = mtrandom::grnd(); + if (p >= 0.5) + factorz *= -1; + + new_spin[0] = old_spin[0] + factorx * angle; + new_spin[1] = old_spin[1] + factory * angle; + new_spin[2] = old_spin[2] + factorz * angle; + + return; + } + + void lsf_mc_step(){ + + // Enable calling of routine if error checking is activated + if (err::check == true){ + std::cout << "sim::lsf_mc has been called" << std::endl; + } + + // Temporaries + int atom = 0; + double Eold = 0.0; + double Enew = 0.0; + double DE = 0.0; + + // Material dependent temperature rescaling + std::vector rescaled_material_kBTBohr(internal::num_materials); + std::vector sigma_array(internal::num_materials); // range for tuned gaussian random move + for (int m = 0; m < internal::num_materials; ++m){ + double alpha = internal::temperature_rescaling_alpha[m]; + double Tc = internal::temperature_rescaling_Tc[m]; + 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; + + // Initialise LSF-MC + if (montecarlo::mc_set == false) + montecarlo::mcinit(); + + for (int i = 0; i < atoms::num_atoms; i++){ + + atom = int(atoms::num_atoms * mtrandom::grnd()); + + // add one to number of moves counter + statistics_moves += 1.0; + + // get material id + const int imaterial = atoms::type_array[atom]; + + // Save old spin position + internal::Sold[0] = atoms::x_spin_array[atom]; + internal::Sold[1] = atoms::y_spin_array[atom]; + internal::Sold[2] = atoms::z_spin_array[atom]; + + // Transverse step + montecarlo::mc_angle_transverse(internal::Sold, internal::Snew, montecarlo::internal::adaptive_sigma, atom); + + atoms::x_spin_array[atom] = internal::Sold[0]; + atoms::y_spin_array[atom] = internal::Sold[1]; + atoms::z_spin_array[atom] = internal::Sold[2]; + + // Calculate current energy + Eold = sim::calculate_spin_energy(atom); + + atoms::x_spin_array[atom] = internal::Snew[0]; + atoms::y_spin_array[atom] = internal::Snew[1]; + atoms::z_spin_array[atom] = internal::Snew[2]; + + // Calculate new energy + Enew = sim::calculate_spin_energy(atom); + + // Calculate difference in Joules/mu_B + DE = (Enew - Eold) * internal::mu_s_SI[imaterial] * 1.07828231e23; // 1/9.27400915e-24 + + double P = exp(-DE * rescaled_material_kBTBohr[imaterial]); + + if (DE < 0){ + + // Copy new spin position + atoms::x_spin_array[atom] = internal::Snew[0]; + atoms::y_spin_array[atom] = internal::Snew[1]; + atoms::z_spin_array[atom] = internal::Snew[2]; + } else if (P >= mtrandom::grnd()){ + + // Copy new spin position + atoms::x_spin_array[atom] = internal::Snew[0]; + atoms::y_spin_array[atom] = internal::Snew[1]; + atoms::z_spin_array[atom] = internal::Snew[2]; + } else{ + + atoms::x_spin_array[atom] = internal::Sold[0]; + atoms::y_spin_array[atom] = internal::Sold[1]; + atoms::z_spin_array[atom] = internal::Sold[2]; + + // add one to rejection counter + statistics_reject += 1.0; + } + } + + // calculate new adaptive step sigma angle + if (statistics_moves != 0){ + const double last_rejection_rate = statistics_reject / statistics_moves; + const double factor = 0.5 / last_rejection_rate; + montecarlo::internal::adaptive_sigma *= factor; + // check for excessive range (too small angle takes too long to grow, too large does not improve performance) and truncate + if (montecarlo::internal::adaptive_sigma > 60.0 || montecarlo::internal::adaptive_sigma < 1e-5) + montecarlo::internal::adaptive_sigma = 60.0; // FOR NORMAL RUNS + } + + // Save statistics to sim namespace variable + sim::mc_statistics_moves += statistics_moves; + sim::mc_statistics_reject += statistics_reject; + } + +} // namespace montecarlo diff --git a/src/montecarlo/lsf_mc_mpi.cpp b/src/montecarlo/lsf_mc_mpi.cpp new file mode 100644 index 000000000..53bd31f29 --- /dev/null +++ b/src/montecarlo/lsf_mc_mpi.cpp @@ -0,0 +1,338 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) David R Papp 2024. All rights reserved. +// +//------------------------------------------------------------------------------ +// + +#ifdef MPICF +// Standard Libraries +#include +#include +#include +#include +#include +#include + +// Vampire Header files +#include "atoms.hpp" +#include "errors.hpp" +#include "material.hpp" +#include "montecarlo.hpp" +#include "random.hpp" +#include "sim.hpp" +#include "vmath.hpp" +#include "vio.hpp" +#include "lsf_mc.hpp" +#include "exchange.hpp" +#include "../simulate/internal.hpp" + +// Internal header +#include "internal.hpp" + +namespace montecarlo{ + +//------------------------------------------------------------------------------ +// Initialise octant arrays to store which atom is in which octant for the +// checkerboard LSF-MC algorithm +//------------------------------------------------------------------------------ +void lsf_mc_parallel_init(std::vector &x, // atomic coordinates + std::vector &y, + std::vector &z, + double min_dim[3], // minimum dimensions on local processor + double max_dim[3]){ // maximum dimensions on local processor + + // Convenient shorthands + int catoms = vmpi::num_core_atoms; + int batoms = vmpi::num_bdry_atoms; + + double widthx = max_dim[0] - min_dim[0]; + double widthy = max_dim[1] - min_dim[1]; + double widthz = max_dim[2] - min_dim[2]; + + int octant_num = 0; //Count which octant loop is in + + // Determines which core atoms are in which octant and pushes the index of those + // atoms into the appropriate octant arrays. + for(int zoct=0; zoct<2; zoct++){ + for(int yoct=0; yoct<2; yoct++){ + for(int xoct=0; xoct<2; xoct++){ + // Loop through all core atoms + for (int i=0; i= min_dim[0] + widthx*xoct*0.5 && x[i] < min_dim[0] + widthx*0.5 + widthx*xoct*0.5 + && y[i] >= min_dim[1] + widthy*yoct*0.5 && y[i] < min_dim[1] + widthy*0.5 + widthy*yoct*0.5 + && z[i] >= min_dim[2] + widthz*zoct*0.5 && z[i] < min_dim[2] + widthz*0.5 + widthz*zoct*0.5) + { + internal::c_octants[octant_num].push_back(i); + } + } + octant_num++; + } + } + } + + octant_num = 0; + //Sort boundary atoms into appropriate octant arrays. + for(int zoct=0; zoct<2; zoct++){ + for(int yoct=0; yoct<2; yoct++){ + for(int xoct=0; xoct<2; xoct++){ + // Loop through all boundary atoms + for (int i=catoms; i= min_dim[0] + widthx*xoct*0.5 && x[i] < min_dim[0] + widthx*0.5 + widthx*xoct*0.5 + && y[i] >= min_dim[1] + widthy*yoct*0.5 && y[i] < min_dim[1] + widthy*0.5 + widthy*yoct*0.5 + && z[i] >= min_dim[2] + widthz*zoct*0.5 && z[i] < min_dim[2] + widthz*0.5 + widthz*zoct*0.5) + { + internal::b_octants[octant_num].push_back(i); + } + } + octant_num++; + } + } + } + + //-------------------------------------------------------------------- + // check that all atoms have been allocated an octant + //-------------------------------------------------------------------- + // core atoms + int num_atoms_in_octants = 0; + for(int i=0; i< 8; i++) num_atoms_in_octants += internal::c_octants[i].size(); + if(num_atoms_in_octants != catoms){ + std::cerr << "Programmer error: missing atoms in core octants in parallel monte carlo - LSF initialisation" << std::endl; + err::vexit(); + } + // boundary atoms + num_atoms_in_octants = 0; + for(int i=0; i< 8; i++) num_atoms_in_octants += internal::b_octants[i].size(); + if(num_atoms_in_octants != batoms){ + std::cerr << "Programmer error: missing atoms in boundary octants in parallel monte carlo - LSF initialisation" << std::endl; + err::vexit(); + } + + // set flag to indicate that parallel LSF-MC has been initialised + lsf_mc_parallel_initialized = true; + +} + +/// Spin move +/// Move spin within cone near old position +void mc_transverse(const std::vector& old_spin, std::vector& new_spin, const double angle){ + + double factorx = mtrandom::grnd(); + double factory = mtrandom::grnd(); + double factorz = mtrandom::grnd(); + + // Function to ensure random numbers have a 50/50 chance of being -ve or +ve + double p; + p = mtrandom::grnd(); + if (p >= 0.5) + factorx *= -1; + p = mtrandom::grnd(); + if (p >= 0.5) + factory *= -1; + p = mtrandom::grnd(); + if (p >= 0.5) + factorz *= -1; + + new_spin[0] = old_spin[0] + factorx * angle; + new_spin[1] = old_spin[1] + factory * angle; + new_spin[2] = old_spin[2] + factorz * angle; + + return; + +} + +//------------------------------------------------------------------------------ +// Integrates an LSF-Monte Carlo step in parallel +//------------------------------------------------------------------------------ +void lsf_mc_step_parallel(std::vector &x_spin_array, + std::vector &y_spin_array, + std::vector &z_spin_array, + std::vector &type_array){ + + // Temporaries + int atom=0; + double Eold=0.0; + double Enew=0.0; + double DE=0.0; + + // Material dependent temperature rescaling + std::vector rescaled_material_kBTBohr(internal::num_materials); + std::vector sigma_array(internal::num_materials); // range for tuned gaussian random move + for(int m=0; m= mtrandom::grnd()) continue; + // If rejected reset spin coordinates and continue + else{ + x_spin_array[atom] = internal::Sold[0]; + y_spin_array[atom] = internal::Sold[1]; + z_spin_array[atom] = internal::Sold[2]; + // add one to rejection counter + statistics_reject += 1.0; + continue; + } + } + } + + // Finish non-blocking data send/receive + vmpi::mpi_complete_halo_swap(); + + // + //Begin integrating boundary region + // + nmoves = internal::b_octants[octant].size(); + for(int i=0; i= mtrandom::grnd()) continue; + // If rejected reset spin coordinates and continue + else{ + x_spin_array[atom] = internal::Sold[0]; + y_spin_array[atom] = internal::Sold[1]; + z_spin_array[atom] = internal::Sold[2]; + // add one to rejection counter + statistics_reject += 1.0; + continue; + } + } + } + + // Swap timers compute -> wait + vmpi::TotalComputeTime+=vmpi::SwapTimer(vmpi::ComputeTime, vmpi::WaitTime); + + // Wait for other processors + vmpi::barrier(); + + // Swap timers wait -> compute + vmpi::TotalWaitTime += vmpi::SwapTimer(vmpi::WaitTime, vmpi::ComputeTime); + + } // end of octant loop + + //Collect statistics from all processors + double global_statistics_moves = 0.0; + double global_statistics_reject = 0.0; + MPI_Allreduce(&statistics_moves, &global_statistics_moves, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(&statistics_reject, &global_statistics_reject, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + + // calculate new adaptive step sigma angle (on per-processor basis using local, not global stats) + if(montecarlo::algorithm == montecarlo::adaptive){ + const double last_rejection_rate = statistics_reject / statistics_moves; + const double factor = 0.5 / last_rejection_rate; + montecarlo::internal::adaptive_sigma *= factor; + // check for excessive range (too small angle takes too long to grow, too large does not improve performance) and truncate + if (montecarlo::internal::adaptive_sigma > 60.0 || montecarlo::internal::adaptive_sigma < 1e-5) montecarlo::internal::adaptive_sigma = 60.0; + } + + // Save statistics to sim namespace variable + sim::mc_statistics_moves += global_statistics_moves; + sim::mc_statistics_reject += global_statistics_reject; + + return; + +} + +} // End of namespace montecarlo +#endif diff --git a/src/montecarlo/makefile b/src/montecarlo/makefile index 7d795e185..a5fa12e2a 100644 --- a/src/montecarlo/makefile +++ b/src/montecarlo/makefile @@ -13,7 +13,9 @@ cmc.o \ masked_cmc_mc.o \ cmc_mc.o \ monte_carlo_preconditioning.o \ -mc-mpi.o +mc_mpi.o \ +lsf_mc.o \ +lsf_mc_mpi.o \ # Append module objects to global tree OBJECTS+=$(addprefix obj/montecarlo/,$(montecarlo_objects)) diff --git a/src/montecarlo/masked_cmc_mc.cpp b/src/montecarlo/masked_cmc_mc.cpp index 3884b14ec..72816cf21 100644 --- a/src/montecarlo/masked_cmc_mc.cpp +++ b/src/montecarlo/masked_cmc_mc.cpp @@ -410,12 +410,14 @@ void cmc_mc_step_mask(){ (cmc::cmc_mask[mask1].M_other[1] + spin1_final[1] + spin2_final[1]- spin1_initial[1] - spin2_initial[1])*cmc::cmc_mask[mask1].ppolar_vector[1] + (cmc::cmc_mask[mask1].M_other[2] + spin1_final[2] + spin2_final[2]- spin1_initial[2] - spin2_initial[2])*cmc::cmc_mask[mask1].ppolar_vector[2]; - // Check for lower energy state and accept unconditionally (this allows it to flip sign, why?) - //if((delta_energy21<0.0) && (Mz_new >= 1e-15) ) continue; - - // If move is favorable then accept - const double 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 >= 1e-15 ) ){ + // Check for lower energy state and accept unconditionally. Tested on the raw + // (unscaled) energy change rather than delta_energy21, since delta_energy21 is + // itself +/-infinity (or NaN, if the two terms have opposite sign) at T=0, where + // rescaled_material_kBTBohr diverges because sim::temperature=0 - mirrors the + // DE<0 shortcut used for the same reason in mc.cpp/mc_mpi.cpp/lsf_mc.cpp. + const bool lower_energy_state = (delta_energy1 + delta_energy2)<0.0; + const double probability = lower_energy_state ? 1.0 : exp(-delta_energy21)*((Mz_new/Mz_old)*(Mz_new/Mz_old))*std::fabs(spin2_init_mvd[2]/spin2_fin_mvd[2]); + if( ( lower_energy_state || ( probability >= mtrandom::grnd() ) ) && ( Mz_new >= 1e-15 ) ){ cmc::cmc_mask[mask1].M_other[0] = cmc::cmc_mask[mask1].M_other[0] + spin1_final[0] + spin2_final[0] - spin1_initial[0] - spin2_initial[0]; cmc::cmc_mask[mask1].M_other[1] = cmc::cmc_mask[mask1].M_other[1] + spin1_final[1] + spin2_final[1] - spin1_initial[1] - spin2_initial[1]; cmc::cmc_mask[mask1].M_other[2] = cmc::cmc_mask[mask1].M_other[2] + spin1_final[2] + spin2_final[2] - spin1_initial[2] - spin2_initial[2]; diff --git a/src/montecarlo/mc-mpi.cpp b/src/montecarlo/mc_mpi.cpp similarity index 96% rename from src/montecarlo/mc-mpi.cpp rename to src/montecarlo/mc_mpi.cpp index 69997d258..a0091a920 100755 --- a/src/montecarlo/mc-mpi.cpp +++ b/src/montecarlo/mc_mpi.cpp @@ -92,13 +92,20 @@ void mc_parallel_init(std::vector &x, // atomic coordinates // core atoms int num_atoms_in_octants = 0; for(int i=0; i< 8; i++) num_atoms_in_octants += internal::c_octants[i].size(); + //std::cout<<"num_atoms_in_octants core "< send_start_index_array; std::vector send_num_array; std::vector send_spin_data_array; + std::vector send_coord_data_array; std::vector recv_atom_translation_array; std::vector recv_start_index_array; std::vector recv_num_array; std::vector recv_spin_data_array; + std::vector recv_coord_data_array; + #ifdef MPICF std::vector requests(0); std::vector stati(0); diff --git a/src/mpi/decomposition.cpp b/src/mpi/decomposition.cpp index 3fddb594e..2093b445f 100644 --- a/src/mpi/decomposition.cpp +++ b/src/mpi/decomposition.cpp @@ -135,11 +135,12 @@ namespace vmpi{ //------------------------------------------------------------------------ if(vmpi::my_rank==0){ - for(int i = 0; i< mdg.size(); i++){ + const int mdg_size = mdg.size(); + for(int i = 0; i< mdg_size; i++){ const double value = mdg[i]; - for(int j = 0; j< mdg.size(); j++){ + for(int j = 0; j< mdg_size; j++){ // calculate absolute numerical diff in value const double diff = fabs(mdg[j] - value); diff --git a/src/mpi/lsf_mpi.cpp b/src/mpi/lsf_mpi.cpp new file mode 100644 index 000000000..3a6624a73 --- /dev/null +++ b/src/mpi/lsf_mpi.cpp @@ -0,0 +1,341 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) David R Papp 2024. All rights reserved. +// +//------------------------------------------------------------------------------ +// + +#ifdef MPICF +#include "atoms.hpp" +#include "errors.hpp" +#include "exchange.hpp" +#include "material.hpp" +#include "sim.hpp" +#include "constants.hpp" +#include "random.hpp" +#include "lsf.hpp" +#include "vio.hpp" +#include "../simulate/internal.hpp" + +#include +#include +#include +#include +#include +#include + +int calculate_spin_fields(const int,const int); +int calculate_external_fields(const int,const int); + +namespace sim{ + +// LSF magnetic field function +void calculate_lsf_field(const int start_index, const int end_index){ + + // Check calling of routine if error checking is activated + if(err::check==true){std::cout << "calculate_lsf_magnetic_field has been called" << std::endl;} + + // LSF Hamiltonian calculation + for(int atom=start_index;atom wait + vmpi::TotalComputeTime+=vmpi::SwapTimer(vmpi::ComputeTime, vmpi::WaitTime); + + // Wait for other processors + vmpi::barrier(); + + // Swap timers wait -> compute + vmpi::TotalWaitTime+=vmpi::SwapTimer(vmpi::WaitTime, vmpi::ComputeTime); + + return EXIT_SUCCESS; +} + +} // end of namespace sim +#endif diff --git a/src/mpi/lsf_rk4_mpi.cpp b/src/mpi/lsf_rk4_mpi.cpp new file mode 100644 index 000000000..1b5effd4b --- /dev/null +++ b/src/mpi/lsf_rk4_mpi.cpp @@ -0,0 +1,480 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) David R Papp 2024. All rights reserved. +// +//------------------------------------------------------------------------------ +// + +#ifdef MPICF +#include "atoms.hpp" +#include "errors.hpp" +#include "exchange.hpp" +#include "material.hpp" +#include "sim.hpp" +#include "constants.hpp" +#include "random.hpp" +#include "lsf_rk4.hpp" +#include "vio.hpp" +#include "../simulate/internal.hpp" + +#include +#include +#include +#include +#include +#include + +int calculate_spin_fields(const int,const int); +int calculate_external_fields(const int,const int); + +namespace sim{ + +// LSF magnetic field function +void calculate_lsf_rk4_field(const int start_index, const int end_index){ + + // Check calling of routine if error checking is activated + if(err::check==true){std::cout << "calculate_lsf_magnetic_field has been called" << std::endl;} + + // LSF-RK4 Hamiltonian calculation + for(int atom=start_index;atom wait + vmpi::TotalComputeTime+=vmpi::SwapTimer(vmpi::ComputeTime, vmpi::WaitTime); + + // Wait for other processors + vmpi::barrier(); + + // Swap timers wait -> compute + vmpi::TotalWaitTime+=vmpi::SwapTimer(vmpi::WaitTime, vmpi::ComputeTime); + + return EXIT_SUCCESS; +} + +} // end of namespace sim +#endif diff --git a/src/mpi/makefile b/src/mpi/makefile index 0e8ecad26..58939c674 100644 --- a/src/mpi/makefile +++ b/src/mpi/makefile @@ -12,6 +12,8 @@ mpi_generic.o \ mpi_comms.o \ parallel_rng_seed.o \ wrapper.o \ +lsf_mpi.o \ +lsf_rk4_mpi.o # Append module objects to global tree OBJECTS+=$(addprefix obj/mpi/,$(mpi_objects)) diff --git a/src/mpi/mpi_comms.cpp b/src/mpi/mpi_comms.cpp index e76a3269f..dcc255fc0 100644 --- a/src/mpi/mpi_comms.cpp +++ b/src/mpi/mpi_comms.cpp @@ -35,10 +35,13 @@ // Locally allocated variables: // //===================================================================================== + #include "atoms.hpp" #include "errors.hpp" #include "vmpi.hpp" #include +#include +#include namespace vmpi{ @@ -56,7 +59,6 @@ void mpi_init_halo_swap(){ // Locally allocated variables: // //==================================================================================== - //using namespace mpi_comms; #ifdef MPICF //---------------------------------------------------------- @@ -90,7 +92,7 @@ void mpi_init_halo_swap(){ //---------------------------------------------------------- vmpi::requests.resize(0); - MPI_Request req; + MPI_Request req = 0; for (int p=0;p wait + vmpi::TotalComputeTime+=vmpi::SwapTimer(vmpi::ComputeTime, vmpi::WaitTime); + + // Wait for all comms to complete + vmpi::stati.resize(vmpi::requests.size()); + MPI_Waitall(vmpi::requests.size(),&vmpi::requests[0],&vmpi::stati[0]); + + // Swap timers wait -> compute + vmpi::TotalWaitTime+=vmpi::SwapTimer(vmpi::WaitTime, vmpi::ComputeTime); + + // Unpack received spins + for(unsigned int i=0;i& input, std::vector& output){ disp += counts[p]; // add number of counts to be recieved } // Check output array is correct size on master to hold collated data, if not then exit disgracefully - if( output.size() != disp ){ + const int op_size = output.size(); + if( op_size != disp ){ std::cerr << "Programmer error in vmpi::collate() due to output array being the wrong size." << std::endl; std::cerr << "\t Function call requires " << disp << " elements in output buffer which only has " << output.size() << " elements on master process" << std::endl; err::vexit(); @@ -215,7 +216,8 @@ void counts_and_displacements(std::vector& input, std::vector& o disp += counts[p]; // add number of counts to be recieved } // Check output array is correct size on master to hold collated data, if not then exit disgracefully - if( output.size() != disp ){ + const int op_size = output.size(); + if( op_size != disp ){ std::cerr << "Programmer error in vmpi::collate() due to output array being the wrong size." << std::endl; std::cerr << "\t Function call requires " << disp << " elements in output buffer which only has " << output.size() << " elements on master process" << std::endl; err::vexit(); @@ -269,7 +271,48 @@ void fast_collate(std::vector& input, std::vector& output, std:: } +//------------------------------------------------------------------------------ +// MPI broadcast with support for large messages +//------------------------------------------------------------------------------ +void broadcast(std::vector& message, int source_rank){ + +#ifdef MPICF + // get message size + const uint64_t message_size = message.size(); + + // for normal messages (& atom_array, // array of atom // Loop over all interactions in exchange template for(unsigned int i = 0; i < num_interactions; i++){ - const int atom=exchange.interaction[i].i; - const int natom=exchange.interaction[i].j; + const int atom = exchange.interaction[i].i; + const int natom = exchange.interaction[i].j; int nx = exchange.interaction[i].dx + scc[0]; int ny = exchange.interaction[i].dy + scc[1]; diff --git a/src/program/data.cpp b/src/program/data.cpp index 10417ba44..cbf2d0e96 100644 --- a/src/program/data.cpp +++ b/src/program/data.cpp @@ -31,7 +31,6 @@ namespace program{ //------------------------------------------------------------------------ // Shared variables inside program module //------------------------------------------------------------------------ - bool enabled = true; // bool to enable module //------------------------------------------------------------------------ @@ -41,6 +40,18 @@ namespace program{ double electrical_pulse_rise_time = 0.0; // linear rise time for electrical pulse (0.0 default) double electrical_pulse_fall_time = 0.0; // linear fall time for electrical pulse (0.0 default) int num_electrical_pulses = 1; + double electrical_pulse_delay = 0.0; // delay from laser pulse centre to electrical pulse centre (0.0 default) + + //------------------------------------------------------------------------ + // Field pulse program + //------------------------------------------------------------------------ + double field_pulse_time = 1.0e-9; // length of field pulses (1 ns default) + + //------------------------------------------------------------------------ + // Exchange stiffness program + //------------------------------------------------------------------------ + double exchange_stiffness_max_constraint_angle = 180.01; // degrees + double exchange_stiffness_delta_constraint_angle = 5; // 22.5 degrees //------------------------------------------------------------------------ // Material specific program parameters diff --git a/src/program/electrical_pulse.cpp b/src/program/electrical_pulse.cpp index f5d1ccb6d..2eb97850f 100644 --- a/src/program/electrical_pulse.cpp +++ b/src/program/electrical_pulse.cpp @@ -37,20 +37,27 @@ namespace pgi = program::internal; //------------------------------------------------------------------------------ void update_electric_field_strength(const double time_from_start){ + // shift time origin by user-specified delay (zero by default) + const double t = time_from_start - pgi::electrical_pulse_delay; + + // before pulse delay: current = 0 + if( t < 0.0 ){ + pg::fractional_electric_field_strength = 0.0; + } // implement rise time - if( time_from_start < pgi::electrical_pulse_rise_time ){ - pg::fractional_electric_field_strength = time_from_start / pgi::electrical_pulse_rise_time; + else if( t < pgi::electrical_pulse_rise_time ){ + pg::fractional_electric_field_strength = t / pgi::electrical_pulse_rise_time; } // implement continuous current - else if( time_from_start < pgi::electrical_pulse_rise_time + pgi::electrical_pulse_time ){ + else if( t < pgi::electrical_pulse_rise_time + pgi::electrical_pulse_time ){ pg::fractional_electric_field_strength = 1.0; } // implement fall time - else if( time_from_start < pgi::electrical_pulse_rise_time + pgi::electrical_pulse_time + pgi::electrical_pulse_fall_time) { - const double fractional_fall_time = time_from_start - (pgi::electrical_pulse_rise_time + pgi::electrical_pulse_time); + else if( t < pgi::electrical_pulse_rise_time + pgi::electrical_pulse_time + pgi::electrical_pulse_fall_time) { + const double fractional_fall_time = t - (pgi::electrical_pulse_rise_time + pgi::electrical_pulse_time); pg::fractional_electric_field_strength = 1.0 - fractional_fall_time / pgi::electrical_pulse_fall_time; } - // after pulse current = 0 + // after pulse: current = 0 else{ pg::fractional_electric_field_strength = 0.0; } diff --git a/src/program/exchange_stiffness.cpp b/src/program/exchange_stiffness.cpp index 34104d9c1..16fef0902 100644 --- a/src/program/exchange_stiffness.cpp +++ b/src/program/exchange_stiffness.cpp @@ -20,18 +20,19 @@ #include "montecarlo.hpp" #include "program.hpp" #include "sim.hpp" +#include "spininitialize.hpp" #include "stats.hpp" #include "vio.hpp" #include "vmath.hpp" +// program module headers +#include "internal.hpp" namespace program{ //-------------------------------------------------------------------------------- // constants but can be moved to input parameters if need be //-------------------------------------------------------------------------------- -const double exchange_stiffness_max_constraint_angle = 180.01; // degrees -const double exchange_stiffness_delta_constraint_angle = 5; // 22.5 degrees const double pi180 = M_PI/180.0; //-------------------------------------------------------------------------------- @@ -166,13 +167,13 @@ void exchange_stiffness(){ //--------------------------------------------------------------------------- // Main exchange calculation program //--------------------------------------------------------------------------- - const double mt = exchange_stiffness_max_constraint_angle; - const double dt = exchange_stiffness_delta_constraint_angle; + const double mt = program::internal::exchange_stiffness_max_constraint_angle; + const double dt = program::internal::exchange_stiffness_delta_constraint_angle; // set constraint phi component const double constraint_phi = 90.0; - const double cosphi = cos(constraint_phi*pi180); - const double sinphi = sin(constraint_phi*pi180); + //const double cosphi = cos(constraint_phi*pi180); + //const double sinphi = sin(constraint_phi*pi180); // loop over constraint angles ct (constraint_theta) for(double constraint_theta = 0.0; constraint_theta < mt; constraint_theta += dt){ @@ -183,24 +184,69 @@ void exchange_stiffness(){ m2_data.push_back( std::vector() ); torque_data.push_back( std::vector() ); + const double theta = double(constraint_theta); // angle from z per fractional coordinate + const double tr = theta*pi180; + + // rotation matrix for angle rotation + /*double rot[3][3]; + rot[0][0] = 1.0; + rot[0][1] = 0.0; + rot[0][2] = 0.0; + rot[1][0] = 0.0; + rot[1][1] = cos(tr); + rot[1][2] = sin(tr); + rot[2][0] = 0.0; + rot[2][1] = -sin(tr); + rot[2][2] = cos(tr); + + double xx = cosphi*sin(tr); + double yy = sinphi*sin(tr); + double zz = cos(tr); + + //std::cout << "-----------------------------------------------------" << std::endl; + //std::cout << xx << "\t" << yy << "\t" << zz << std::endl; + + double S[3] = {0,0,1}; + double Sp[3]; + + Sp[0] = rot[0][0]*S[0] + rot[0][1]*S[1] + rot[0][2]*S[2]; + Sp[1] = rot[1][0]*S[0] + rot[1][1]*S[1] + rot[1][2]*S[2]; + Sp[2] = rot[2][0]*S[0] + rot[2][1]*S[1] + rot[2][2]*S[2]; + + //std::cout << S[0] << " , " << S[1] << " , " << S[2] << " -> " << Sp[0] << " , " << Sp[1] << " , " << Sp[2] << std::endl; + + S[0] = 0.0; + S[1] = 0.0; + S[2] = -1.0; + + Sp[0] = rot[0][0]*S[0] + rot[0][1]*S[1] + rot[0][2]*S[2]; + Sp[1] = rot[1][0]*S[0] + rot[1][1]*S[1] + rot[1][2]*S[2]; + Sp[2] = rot[2][0]*S[0] + rot[2][1]*S[1] + rot[2][2]*S[2]; + + //std::cout << S[0] << " , " << S[1] << " , " << S[2] << " -> " << Sp[0] << " , " << Sp[1] << " , " << Sp[2] << std::endl;*/ + // initialise new spin positions (half rotation from 1st plane to mid plane, then second half rotation to max plane) for( int atom = 0 ; atom < atoms::num_atoms; atom++ ){ // get material ID const int mat = atoms::type_array[atom]; + const double sintr = sin(tr*fractional_coordinates[atom]); + const double costr = cos(tr*fractional_coordinates[atom]); // if constrained material initialise normal profile (constant angle) if( mat == constrained_material_id){ - const double theta = double(constraint_theta); // angle from z per fractional coordinate - atoms::x_spin_array[atom] = cosphi*sin( theta * pi180 * fractional_coordinates[atom] ); - atoms::y_spin_array[atom] = sinphi*sin( theta * pi180 * fractional_coordinates[atom] ); - atoms::z_spin_array[atom] = cos( theta * pi180 * fractional_coordinates[atom] ); + const double S[3] = {0,0,1}; + atoms::x_spin_array[atom] = S[0]; + atoms::y_spin_array[atom] = costr*S[1] + sintr*S[2]; + atoms::z_spin_array[atom] = -sintr*S[1] + costr*S[2]; } - // otherwise assume initial spin direction for sublattice (should work OK for most ferro, ferri and antiferromagnets) + // otherwise assume initial spin direction for sublattice before rotation (should work OK for most ferro, ferri and antiferromagnets) else{ - atoms::x_spin_array[atom] = mp::material[mat].initial_spin[0]; - atoms::y_spin_array[atom] = mp::material[mat].initial_spin[1]; - atoms::z_spin_array[atom] = mp::material[mat].initial_spin[2]; + double S[3] = {0.0,0.0,1.0}; + spininitialize::get_uniform_vector(mat, S[0], S[1], S[2]); + atoms::x_spin_array[atom] = S[0]; + atoms::y_spin_array[atom] = costr*S[1] + sintr*S[2]; + atoms::z_spin_array[atom] = -sintr*S[1] + costr*S[2]; } } @@ -216,6 +262,10 @@ void exchange_stiffness(){ constrained[1] = true; // constrain middle plane montecarlo::initialise_masked_cmc_mc(constraint_mask.size(), constraint_mask, constrained, phi_theta_constraints); + // set global constrained angle variables for data output (not used) + sim::constraint_phi = phi_theta_constraints[2]; + sim::constraint_theta = phi_theta_constraints[3]; + // initialise temperature sim::temperature=sim::Tmin; @@ -266,7 +316,7 @@ void exchange_stiffness(){ // store computed net torque in data array temperatures.push_back(sim::temperature); const int end = torque_data.size()-1; - const double net_torque = torques[3]*inv_n_atm_p2/counter - torques[0]*inv_n_atm_p1/counter; + const double net_torque = (torques[3]*inv_n_atm_p2/counter - torques[0]*inv_n_atm_p1/counter)*0.5; m1_data[end].push_back(magnetizations[0]/counter); m2_data[end].push_back(magnetizations[1]/counter); torque_data[end].push_back(net_torque); @@ -301,11 +351,15 @@ void exchange_stiffness(){ std::cout << " Final exchange fitting" << std::endl; std::cout << "---------------------------------------------------------------" << std::endl; + ofile.open("exchange-stiffness.txt"); + // loop over all temperatures for(size_t j = 0; j < temperatures.size(); j++){ // populate 1D data for fitting - for(size_t i = 0; i < torque_data.size(); i++) torque1D[i] = torque_data[i][j]; + for(size_t i = 0; i < torque_data.size(); i++){ + torque1D[i] = torque_data[i][j]; + } double m = 0.0; double c = 0.0; @@ -314,11 +368,31 @@ void exchange_stiffness(){ vmath::regression(angles, torque1D, m, c); // output resulting gradient to screen - std::cout << temperatures[j] << "\t" << m << "\t" << c << std::endl; + std::cout << temperatures[j] << "\t" << m << "\t" << c << "\t" << m1_data[0][j] << "\t" << m2_data[0][j] << std::endl; + ofile << temperatures[j] << "\t" << m << "\t" << c << "\t" << m1_data[0][j] << "\t" << m2_data[0][j] << std::endl; } + //--------------------------------------------------------------------------- + // Output final torques in plotable format + //--------------------------------------------------------------------------- + std::ofstream ofile2("exchange-stiffness-final-torques.txt"); + ofile2 << "#angle\t"; + for(size_t j = 0; j < temperatures.size(); j++) ofile2 << temperatures[j] << "\t"; + ofile2 << std::endl; + + // loop over all angles + for(size_t i = 0; i < torque_data.size(); i++){ + + ofile2 << angles[i] << "\t"; + // loop over all temperatures + for(size_t j = 0; j < temperatures.size(); j++){ + ofile2 << torque_data[i][j] << "\t"; + } + ofile2 << std::endl; + } + ofile2.close(); return; @@ -354,8 +428,8 @@ void set_constraint_mask(const std::vector& coordinates, // atomic coord } } - // determine coordinate of constrained atoms nearest middle - double middle = min_coord + (max_coord - min_coord) * 0.5; + // determine coordinate of constrained atoms nearest middle (shift about 1 atom to be slightly more than half) + double middle = min_coord + (max_coord - min_coord) * 0.5 + 3.0; double mid_coord = max_coord; double difference = 1.0e10; for( int atom = 0 ; atom < num_atoms; atom++){ @@ -466,9 +540,9 @@ void calculate_torque(const std::vector& mask, atoms::y_spin_array[atom], atoms::z_spin_array[atom]}; - const double H[3] = { atoms::x_total_spin_field_array[atom] + atoms::x_total_external_field_array[atom], - atoms::y_total_spin_field_array[atom] + atoms::y_total_external_field_array[atom], - atoms::z_total_spin_field_array[atom] + atoms::z_total_external_field_array[atom] }; + const double H[3] = { atoms::x_total_spin_field_array[atom] + atoms::x_total_external_field_array[atom] + atoms::x_thermal_field_array[atom], + atoms::y_total_spin_field_array[atom] + atoms::y_total_external_field_array[atom] + atoms::y_thermal_field_array[atom], + atoms::z_total_spin_field_array[atom] + atoms::z_total_external_field_array[atom] + atoms::z_thermal_field_array[atom] }; // compute torques const double ttx = mu*(S[1]*H[2] - S[2]*H[1]); diff --git a/src/program/field_pulse.cpp b/src/program/field_pulse.cpp new file mode 100644 index 000000000..71498c010 --- /dev/null +++ b/src/program/field_pulse.cpp @@ -0,0 +1,102 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard Evans 2023. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers + +// Vampire headers +#include "errors.hpp" +#include "program.hpp" +#include "sim.hpp" +#include "stats.hpp" +#include "vio.hpp" + +// program module headers +#include "internal.hpp" + +// namespace abbreviation for brevity +namespace pg = program; +namespace pgi = program::internal; + +//------------------------------------------------------------------------------ +// Function to calculate a time-dependent field pulse +//------------------------------------------------------------------------------ +namespace program{ + +void field_pulse(){ + + // check calling of routine if error checking is activated + if(err::check==true){std::cout << "program::field_pulse has been called" << std::endl;} + + // Set equilibration temperature and zero field + const double temp = sim::temperature; // current simulation temperature + sim::temperature = sim::Teq; + + // Save input applied field strength and set to zero for equilibration + const double max_field = sim::H_applied; + sim::H_applied = 0.0; + + // Equilibrate system + while( sim::time < sim::equilibration_time){ + + sim::integrate(sim::partial_time); + + // Calculate magnetisation statistics + stats::update(); + + // Output data + vout::data(); + + } + + // record starting time after equilibration + uint64_t start_time = sim::time; + + // Set constant temperature + sim::temperature = temp; + + // set centre time of field pulse + const double centre_time = 3.0 * pgi::field_pulse_time; + + // set pulse_time^2 + const double pulse_time_sq = pgi::field_pulse_time * pgi::field_pulse_time; + + // Simulate field pulse + while(sim::time < sim::total_time+start_time){ + + // loop over partial_time to update temperature every time + for(uint64_t tt=0; tt < sim::partial_time; tt++){ + + // Calculate time from pulse + double time_from_start = mp::dt_SI * double(sim::time-start_time); + + // Calculate applied field strength + double time_from_centre = time_from_start - centre_time; + sim::H_applied = max_field * exp(-(time_from_centre)*(time_from_centre)/pulse_time_sq); + + // Integrate system + sim::integrate(1); + + } + + // Calculate magnetisation statistics + stats::update(); + + // Output data + vout::data(); + + } + + return; + +} // end of electrical pulse + +} // end of namespace program diff --git a/src/program/hamr_cool.cpp b/src/program/hamr_cool.cpp new file mode 100644 index 000000000..6804fd2bd --- /dev/null +++ b/src/program/hamr_cool.cpp @@ -0,0 +1,122 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard Evans 2025. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// Standard Libraries +#include + +// Vampire Header files +#include "atoms.hpp" +#include "errors.hpp" +#include "material.hpp" +#include "program.hpp" +#include "random.hpp" +#include "sim.hpp" +#include "stats.hpp" +#include "vio.hpp" +#include "vmath.hpp" +#include "vmpi.hpp" + +namespace program{ + +//------------------------------------------------------------------------------ +// Function to cool the system under the action of a constant applied field at +// a defined cooling rate in K/ns +//------------------------------------------------------------------------------ +void hamr_cool(){ + + // check calling of routine if error checking is activated + if(err::check==true){std::cout << "program::hamr_cool has been called" << std::endl;} + + + + // Set equilibration temperature and field + sim::temperature = sim::Teq; + + //-------------------------------------------------------------- + // Equilibrate system + //-------------------------------------------------------------- + while( sim::time < sim::equilibration_time ){ + + // Integrate system + sim::integrate(sim::partial_time); + + // Calculate magnetisation statistics + stats::update(); + + // Output data + vout::data(); + + } + + // Cooling and field rate parameters + const double cooling_rate = 200.0; // K/ns + const double field_rate = 8.5333; // T/ns + + // derived delta T and delta B values per timestep + const double dT_per_dt = cooling_rate * 1.0e9 * mp::dt_SI; // K per timestep + const double dB_per_dt = field_rate * 1.0e9 * mp::dt_SI; // T per timestep + const double dT_max = sim::Tmax - sim::Tmin; + + const double B_max = sim::H_applied; + const double dB_max = -2.0 * B_max; + + // Calculate number of timesteps to reach final temperature and field + const int cooling_time = dT_max / dT_per_dt; + const int field_sw_time = dB_max / dB_per_dt; + + std::cout << dT_per_dt << "\t" << dB_per_dt << "\t" << dT_max << "\t" << dB_max << std::endl; + + // Print information about simulation time to user + std::cout << "Time steps to reach final temperature: " << cooling_time << std::endl; + if( cooling_time > sim::total_time){ + std::cerr << "WARNING: specified time steps of " << sim::total_time << " is insufficient to reach final temperature during hamr cooling program" << std::endl; + zlog << zTs() << "WARNING: specified time steps of " << sim::total_time << " is insufficient to reach final temperature during hamr cooling program" << std::endl; + } + + // save starting time after equilibration + uint64_t start_time = sim::time; + + //-------------------------------------------------------------- + // Perform field cooling + //-------------------------------------------------------------- + while( sim::time < sim::total_time + start_time ){ + + // loop over partial time + for(uint64_t tt=0; tt < sim::partial_time; tt++){ + + // Calculate dynamic temperature and field + double time_from_start = double( sim::time - start_time ); + + double Tlinear = sim::Tmax - (dT_max)*(time_from_start/cooling_time); + if( Tlinear >= sim::Tmin ) sim::temperature = Tlinear; + else sim::temperature = sim::Tmin; + + double B_linear = B_max - (dB_max)*(time_from_start/field_sw_time); + if( B_linear >= -B_max ) sim::H_applied = B_linear; + else sim::H_applied = -B_max; + + // Integrate system + sim::integrate(1); + + } + + // Calculate magnetisation statistics + stats::update(); + + // Output data + vout::data(); + + } + +} // end of hamr_cool() + +} //end of namespace program diff --git a/src/program/interface.cpp b/src/program/interface.cpp index e1cc6815f..9a6cdf67a 100644 --- a/src/program/interface.cpp +++ b/src/program/interface.cpp @@ -65,11 +65,6 @@ namespace program{ program::program=5; return true; } - test="localised-field-cool"; - if(value==test){ - program::program=16; - return true; - } test="laser-pulse"; if(value==test){ program::program=6; @@ -120,6 +115,26 @@ namespace program{ program::program=15; return true; } + test="localised-field-cool"; + if(value==test){ + program::program=16; + return true; + } + test="electrical-pulse"; + if(value==test){ + program::program = 17; + return true; + } + test="field-pulse"; + if(value==test){ + program::program = 18; + return true; + } + test="laser-electrical-pulse"; + if(value==test){ + program::program = 19; + return true; + } test="diagnostic-boltzmann"; if(value==test){ program::program=50; @@ -140,11 +155,6 @@ namespace program{ program::program=53; return true; } - test="electrical-pulse"; - if(value==test){ - program::program = 17; - return true; - } test="mm-A-calculation"; if(value==test){ program::program=54; @@ -165,6 +175,18 @@ namespace program{ program::program=73; return true; } + // JRH - Program option for spinwaves + test="spin-waves"; + if(value==test){ + program::program=74; + return true; + } + // HAMR cool + test="hamr-cool"; + if(value==test){ + program::program=75; + return true; + } else{ terminaltextcolor(RED); std::cout << word << '\t' << test << std::endl; @@ -177,6 +199,8 @@ namespace program{ std::cerr << "\t\"electrical-pulse\"" << std::endl; std::cerr << "\t\"exchange-stiffness\"" << std::endl; std::cerr << "\t\"field-cool\"" << std::endl; + std::cerr << "\t\"field-pulse\"" << std::endl; + std::cerr << "\t\"laser-electrical-pulse\"" << std::endl; std::cerr << "\t\"laser-pulse\"" << std::endl; std::cerr << "\t\"localised-field-cool\"" << std::endl; std::cerr << "\t\"localised-temperature-pulse\"" << std::endl; @@ -221,6 +245,40 @@ namespace program{ return true; } //-------------------------------------------------------------------- + test = "electrical-pulse-delay"; + if(word == test){ + double pd = atof(value.c_str()); + vin::check_for_valid_positive_value(pd, word, line, prefix, unit, "time", 0.0, 1.0,"input","0.0 - 1s"); + program::internal::electrical_pulse_delay = pd; + return true; + } + //-------------------------------------------------------------------- + test="exchange-stiffness-maximum-angle"; + if(word==test){ + double ma = atof(value.c_str()); // convert string to uint64_t + vin::check_for_valid_value(ma, word, line, prefix, unit, "", 0.0, 180.1,"input","0 - 180 degrees"); + program::internal::exchange_stiffness_max_constraint_angle = ma; + return true; + } + //-------------------------------------------------------------------- + test="exchange-stiffness-angle-increment"; + if(word==test){ + double ai = atof(value.c_str()); // convert string to uint64_t + vin::check_for_valid_value(ai, word, line, prefix, unit, "", 1.0, 90.0,"input","1 - 90 degrees"); + program::internal::exchange_stiffness_delta_constraint_angle = ai; + return true; + } + //------------------------------------------------------------------- + test = "field-pulse-time"; + if(word == test){ + double pt = atof(value.c_str()); // convert string to uint64_t + // Test for valid range + vin::check_for_valid_positive_value(pt, word, line, prefix, unit, "time", 0.0, 1.0,"input","0.0 - 1s"); + // save sanitized value + program::internal::field_pulse_time = pt; + return true; + } + //-------------------------------------------------------------------- // Keyword not found //-------------------------------------------------------------------- return false; diff --git a/src/program/internal.hpp b/src/program/internal.hpp index 2c13f5ef9..64ca19529 100644 --- a/src/program/internal.hpp +++ b/src/program/internal.hpp @@ -62,7 +62,6 @@ namespace program{ //------------------------------------------------------------------------- // Internal shared variables //------------------------------------------------------------------------- - extern bool enabled; // bool to enable module //------------------------------------------------------------------------ @@ -72,7 +71,22 @@ namespace program{ extern double electrical_pulse_rise_time; // linear rise time for electrical pulse (0.0 default) extern double electrical_pulse_fall_time; // linear fall time for electrical pulse (0.0 default) extern int num_electrical_pulses; + extern double electrical_pulse_delay; // delay from laser pulse centre to electrical pulse centre (laser-electrical-pulse program) + //------------------------------------------------------------------------ + // Field pulse program + //------------------------------------------------------------------------ + extern double field_pulse_time; // length of field pulses (1 ns default) + + //------------------------------------------------------------------------ + // Exchange stiffness program + //------------------------------------------------------------------------ + extern double exchange_stiffness_max_constraint_angle; // degrees + extern double exchange_stiffness_delta_constraint_angle; // degrees + + //------------------------------------------------------------------------ + // Material level parameters + //------------------------------------------------------------------------ extern std::vector mp; // array of material properties //------------------------------------------------------------------------- diff --git a/src/program/laser_electrical_pulse.cpp b/src/program/laser_electrical_pulse.cpp new file mode 100644 index 000000000..9ae896cff --- /dev/null +++ b/src/program/laser_electrical_pulse.cpp @@ -0,0 +1,109 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// +// Implements the laser-electrical-pulse program, which combines a laser pulse +// (via the two-temperature model) with an independent Gaussian electrical pulse +// delayed by a user-specified time. The two pulses share no coupling — they are +// parameterised independently and may be shifted relative to each other. +// + +// C++ standard library headers +#include +#include + +// Vampire headers +#include "errors.hpp" +#include "material.hpp" +#include "program.hpp" +#include "sim.hpp" +#include "stats.hpp" +#include "vio.hpp" +#include "vmpi.hpp" + +// program module headers +#include "internal.hpp" + +namespace pg = program; +namespace pgi = program::internal; + +// temperature_pulse_function is defined in temperature_pulse.cpp and dispatches +// to the appropriate TTM or square profile selected by sim:laser-pulse-temporal-profile +extern double temperature_pulse_function(double function_time); + +//------------------------------------------------------------------------------ +// Gaussian envelope for the electrical pulse. +// +// The pulse is centred at (delay + 3*tau) from simulation start, matching the +// convention used by the laser Gaussian in two_temperature_function where the +// peak sits at t = 3*pump_time. This means setting delay = 0 and +// electrical-pulse-time = laser-pulse-time produces perfectly overlapping pulses. +//------------------------------------------------------------------------------ +static void update_laser_electrical_gaussian(const double t_elec){ + + const double tau = pgi::electrical_pulse_time; + const double four_ln2 = 2.77258872224; // 4 ln 2 + const double reduced = (t_elec - 3.0 * tau) / tau; + pg::fractional_electric_field_strength = std::exp(-four_ln2 * reduced * reduced); + +} + +namespace program{ + +void laser_electrical_pulse(){ + + if(err::check) std::cout << "program::laser_electrical_pulse has been called" << std::endl; + + // Initialise TTM temperatures at equilibrium and zero the electrical field + sim::temperature = sim::Teq; + sim::TTTe = sim::Teq; + sim::TTTp = sim::Teq; + pg::fractional_electric_field_strength = 0.0; + + // Initialise per-material temperatures when local temperature mode is active + if(sim::local_temperature){ + for(unsigned int mat = 0; mat < mp::material.size(); mat++){ + if(mp::material[mat].couple_to_phonon_temperature) mp::material[mat].temperature = sim::TTTp; + else mp::material[mat].temperature = sim::TTTe; + } + } + + // Equilibrate system at Teq with no electrical field + while(sim::time < sim::equilibration_time){ + sim::integrate(sim::partial_time); + stats::update(); + vout::data(); + } + + const uint64_t start_time = sim::time; + + // Simulate combined laser + electrical pulse + while(sim::time < sim::total_time + start_time){ + + for(uint64_t tt = 0; tt < sim::partial_time; tt++){ + + const double t = mp::dt_SI * double(sim::time - start_time); + + // Update spin temperature via TTM laser pulse + sim::temperature = temperature_pulse_function(t); + + // Update Gaussian electrical pulse, offset by the user-specified delay + update_laser_electrical_gaussian(t - pgi::electrical_pulse_delay); + + sim::integrate(1); + } + + stats::update(); + vout::data(); + } + +} // end of laser_electrical_pulse + +} // end of namespace program diff --git a/src/program/makefile b/src/program/makefile index e42094552..a711c37b9 100644 --- a/src/program/makefile +++ b/src/program/makefile @@ -13,13 +13,16 @@ disk_tracks.o \ domain_wall.o \ effective_damping.o \ electrical_pulse.o \ +laser_electrical_pulse.o \ exchange_stiffness.o \ field_cool.o \ +field_pulse.o \ field_sweep.o \ fmr.o \ local_field_cool.o \ localised_temperature_pulse.o \ hamr.o \ +hamr_cool.o \ hybrid_cmc.o \ hysteresis.o \ initialize.o \ @@ -31,7 +34,8 @@ partial_hysteresis.o \ static_hysteresis.o \ setting.o \ time_series.o \ -temperature_pulse.o +temperature_pulse.o \ +spin_waves.o # Append module objects to global tree OBJECTS+=$(addprefix obj/program/,$(program_objects)) diff --git a/src/program/setting.cpp b/src/program/setting.cpp index fa756b049..3cff6abf4 100644 --- a/src/program/setting.cpp +++ b/src/program/setting.cpp @@ -116,7 +116,7 @@ namespace program{ 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)){ + if ((Local_Sub[l] > Max_atoms[j]) && (Local_Sub[l] != 0)){ Largest_Sublattice[j] = i; Max_atoms[j] =Local_Sub[l]; } diff --git a/src/program/spin_waves.cpp b/src/program/spin_waves.cpp new file mode 100644 index 000000000..a98bd7443 --- /dev/null +++ b/src/program/spin_waves.cpp @@ -0,0 +1,103 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Joel Hirst 2022. All rights reserved. +// +// Email: j.r.hirst@shu.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +// Standard Libraries +#include + +// Vampire Header files +#include "atoms.hpp" +#include "errors.hpp" +#include "material.hpp" +#include "program.hpp" +#include "random.hpp" +#include "sim.hpp" +#include "stats.hpp" +#include "vio.hpp" +#include "vmath.hpp" +#include "vmpi.hpp" +#include "spinwaves.hpp" // JRH + +namespace program{ + +//------------------------------------------------------------------------------ +// Program to calculate a simple time series +//------------------------------------------------------------------------------ +void spin_waves(){ + + // check calling of routine if error checking is activated + if(err::check==true) std::cout << "program::spin_waves has been called" << std::endl; + + double temp=sim::temperature; + + // Set equilibration temperature only if continue checkpoint not loaded + if(sim::load_checkpoint_flag && sim::load_checkpoint_continue_flag){} + else{ + // Set equilibration temperature + sim::temperature=sim::Teq; + } + + // Output data + vout::data(); + + // Equilibrate system + while(sim::time +#include +#include +#include +#include +#include + +// Vampire Header files +#include "lsf.hpp" +#include "atoms.hpp" +#include "constants.hpp" +#include "errors.hpp" +#include "exchange.hpp" +#include "internal.hpp" +#include "material.hpp" +#include "random.hpp" +#include "sim.hpp" +#include "vio.hpp" + +// Field calculation functions +int calculate_spin_fields(const int, const int); +int calculate_external_fields(const int, const int); + +namespace LSF_arrays{ + + // Local arrays for LSF integration + std::vector x_lsf_array; + std::vector y_lsf_array; + std::vector z_lsf_array; + + std::vector x_euler_array; + std::vector y_euler_array; + std::vector z_euler_array; + + std::vector x_heun_array; + std::vector y_heun_array; + std::vector z_heun_array; + + std::vector x_spin_storage_array; + std::vector y_spin_storage_array; + std::vector z_spin_storage_array; + + std::vector x_initial_spin_array; + std::vector y_initial_spin_array; + std::vector z_initial_spin_array; + + // Flag to define state of LSF arrays (initialised/uninitialised) + bool LSF_set = false; + + std::vector mod_S; + + std::vector tx; + std::vector ty; + std::vector tz; + +} +namespace sim{ + + int LSFinit(){ + // Check calling of routine if error checking is activated + if (err::check == true){ + std::cout << "sim:LSF_init has been called" << std::endl; + } + + using namespace LSF_arrays; + + x_lsf_array.resize(atoms::num_atoms, 0.0); + y_lsf_array.resize(atoms::num_atoms, 0.0); + z_lsf_array.resize(atoms::num_atoms, 0.0); + + x_spin_storage_array.resize(atoms::num_atoms, 0.0); + y_spin_storage_array.resize(atoms::num_atoms, 0.0); + z_spin_storage_array.resize(atoms::num_atoms, 0.0); + + x_initial_spin_array.resize(atoms::num_atoms, 0.0); + y_initial_spin_array.resize(atoms::num_atoms, 0.0); + z_initial_spin_array.resize(atoms::num_atoms, 0.0); + + x_euler_array.resize(atoms::num_atoms, 0.0); + y_euler_array.resize(atoms::num_atoms, 0.0); + z_euler_array.resize(atoms::num_atoms, 0.0); + + x_heun_array.resize(atoms::num_atoms, 0.0); + y_heun_array.resize(atoms::num_atoms, 0.0); + z_heun_array.resize(atoms::num_atoms, 0.0); + + mod_S.resize(atoms::num_atoms, 1.0); + + tx.resize(atoms::num_atoms, 0.0); + ty.resize(atoms::num_atoms, 0.0); + tz.resize(atoms::num_atoms, 0.0); + + // Disable external thermal field calculations + sim::hamiltonian_simulation_flags[3] = 0; + + LSF_set = true; + + return EXIT_SUCCESS; + } + + // LSF magnetic field function + void calculate_lsf_magnetic_field(const int start_index, const int end_index){ + + // Check calling of routine if error checking is activated + if (err::check == true){ + std::cout << "calculate_lsf_magnetic_field has been called" << std::endl; + } + + // LSF Hamiltonian calculation + for (int atom = start_index; atom < end_index; atom++){ + + const int imaterial = atoms::type_array[atom]; + + const double sx = atoms::x_spin_array[atom]; + const double sy = atoms::y_spin_array[atom]; + const double sz = atoms::z_spin_array[atom]; + + const double imu_S = -1.0 / mp::material[imaterial].mu_s_SI; + + const double L2 = 2.0 * sim::internal::lsf_second_order_coefficient[imaterial] * imu_S; + const double L4 = 4.0 * sim::internal::lsf_fourth_order_coefficient[imaterial] * imu_S; + const double L6 = 6.0 * sim::internal::lsf_sixth_order_coefficient[imaterial] * imu_S; + + const double ss2 = sx*sx + sy*sy + sz*sz; + + LSF_arrays::x_lsf_array[atom] = L2*sx + L4*sx*ss2 + L6*sx*ss2*ss2; + LSF_arrays::y_lsf_array[atom] = L2*sy + L4*sy*ss2 + L6*sy*ss2*ss2; + LSF_arrays::z_lsf_array[atom] = L2*sz + L4*sz*ss2 + L6*sz*ss2*ss2; + } + } + + namespace internal{ + + void lsf_step(){ + + // Check calling of routine if error checking is activated + if (err::check == true){ + std::cout << "sim::LSF has been called" << std::endl; + } + + using namespace LSF_arrays; + + // Check for initialisation of LSF integration arrays + if (LSF_set == false) + sim::LSFinit(); + + // Local variables for system integration + const int num_atoms = atoms::num_atoms; + double xyz[3]; // Local delta spin components + double S_new[3]; // New local spin moment + const double kB = 1.3806503e-23; + + // Calculate fields + calculate_spin_fields(0, num_atoms); + calculate_lsf_magnetic_field(0, num_atoms); + calculate_external_fields(0, num_atoms); + + // Store initial spin positions + for (int atom = 0; atom < num_atoms; atom++){ + x_initial_spin_array[atom] = atoms::x_spin_array[atom]; + y_initial_spin_array[atom] = atoms::y_spin_array[atom]; + z_initial_spin_array[atom] = atoms::z_spin_array[atom]; + } + + // Thermal noise based on Gaussian function + double sigma = (sqrt((2.0 * kB * sim::temperature * mp::gamma_SI) / (mp::dt_SI))); + generate(tx.begin(), tx.begin() + num_atoms, mtrandom::gaussian); + generate(ty.begin(), ty.begin() + num_atoms, mtrandom::gaussian); + generate(tz.begin(), tz.begin() + num_atoms, mtrandom::gaussian); + + // Calculate first GSE step + for (int atom = 0; atom < num_atoms; atom++){ + + const int imaterial = atoms::type_array[atom]; + const double alpha = mp::material[imaterial].alpha; + const double mu = mp::material[imaterial].mu_s_SI; + + // Store local spin in S and local field in H + const double S[3] = {atoms::x_spin_array[atom], atoms::y_spin_array[atom], atoms::z_spin_array[atom]}; + + const double H[3] = {atoms::x_total_spin_field_array[atom] + atoms::x_total_external_field_array[atom] + LSF_arrays::x_lsf_array[atom], + atoms::y_total_spin_field_array[atom] + atoms::y_total_external_field_array[atom] + LSF_arrays::y_lsf_array[atom], + atoms::z_total_spin_field_array[atom] + atoms::z_total_external_field_array[atom] + LSF_arrays::z_lsf_array[atom]}; + + // Calculate Delta S + xyz[0] = (-mp::gamma_SI * (S[1] * H[2] - S[2] * H[1])) + (mp::gamma_SI * alpha * H[0]) + (tx[atom] * (sigma * sqrt(alpha / mu))); + xyz[1] = (-mp::gamma_SI * (S[2] * H[0] - S[0] * H[2])) + (mp::gamma_SI * alpha * H[1]) + (ty[atom] * (sigma * sqrt(alpha / mu))); + xyz[2] = (-mp::gamma_SI * (S[0] * H[1] - S[1] * H[0])) + (mp::gamma_SI * alpha * H[2]) + (tz[atom] * (sigma * sqrt(alpha / mu))); + + // Store dS in euler array + x_euler_array[atom] = xyz[0]; + y_euler_array[atom] = xyz[1]; + z_euler_array[atom] = xyz[2]; + + // Calculate Euler Step + S_new[0] = S[0] + xyz[0] * mp::dt_SI; + S_new[1] = S[1] + xyz[1] * mp::dt_SI; + S_new[2] = S[2] + xyz[2] * mp::dt_SI; + + // Writing of Spin Values to Storage Array + x_spin_storage_array[atom] = S_new[0]; + y_spin_storage_array[atom] = S_new[1]; + z_spin_storage_array[atom] = S_new[2]; + } + // Copy new spins to spin array + for (int atom = 0; atom < num_atoms; atom++){ + atoms::x_spin_array[atom] = x_spin_storage_array[atom]; + atoms::y_spin_array[atom] = y_spin_storage_array[atom]; + atoms::z_spin_array[atom] = z_spin_storage_array[atom]; + } + + // Recalculate spin dependent fields + calculate_spin_fields(0, num_atoms); + calculate_lsf_magnetic_field(0, num_atoms); + + // Calculate second GSE step + for (int atom = 0; atom < num_atoms; atom++){ + + const int imaterial = atoms::type_array[atom]; + const double alpha = mp::material[imaterial].alpha; + const double mu = mp::material[imaterial].mu_s_SI; + + // Store local spin in S and local field in H + const double S[3] = {atoms::x_spin_array[atom], atoms::y_spin_array[atom], atoms::z_spin_array[atom]}; + + const double H[3] = {atoms::x_total_spin_field_array[atom] + atoms::x_total_external_field_array[atom] + LSF_arrays::x_lsf_array[atom], + atoms::y_total_spin_field_array[atom] + atoms::y_total_external_field_array[atom] + LSF_arrays::y_lsf_array[atom], + atoms::z_total_spin_field_array[atom] + atoms::z_total_external_field_array[atom] + LSF_arrays::z_lsf_array[atom]}; + + // Calculate Delta S + xyz[0] = (-mp::gamma_SI * (S[1] * H[2] - S[2] * H[1])) + (mp::gamma_SI * alpha * H[0]) + (tx[atom] * (sigma * sqrt(alpha / mu))); + xyz[1] = (-mp::gamma_SI * (S[2] * H[0] - S[0] * H[2])) + (mp::gamma_SI * alpha * H[1]) + (ty[atom] * (sigma * sqrt(alpha / mu))); + xyz[2] = (-mp::gamma_SI * (S[0] * H[1] - S[1] * H[0])) + (mp::gamma_SI * alpha * H[2]) + (tz[atom] * (sigma * sqrt(alpha / mu))); + + // Store dS in Heun array + x_heun_array[atom] = xyz[0]; + y_heun_array[atom] = xyz[1]; + z_heun_array[atom] = xyz[2]; + } + + // Calculate Heun Step + for (int atom = 0; atom < num_atoms; atom++){ + S_new[0] = x_initial_spin_array[atom] + (0.5 * mp::dt_SI * (x_euler_array[atom] + x_heun_array[atom])); + S_new[1] = y_initial_spin_array[atom] + (0.5 * mp::dt_SI * (y_euler_array[atom] + y_heun_array[atom])); + S_new[2] = z_initial_spin_array[atom] + (0.5 * mp::dt_SI * (z_euler_array[atom] + z_heun_array[atom])); + + // Copy new spins to spin array + atoms::x_spin_array[atom] = S_new[0]; + atoms::y_spin_array[atom] = S_new[1]; + atoms::z_spin_array[atom] = S_new[2]; + } + + // Store spin length data + for (int atom = 0; atom < num_atoms; atom++){ + const double sx = atoms::x_spin_array[atom]; + const double sy = atoms::y_spin_array[atom]; + const double sz = atoms::z_spin_array[atom]; + + mod_S[atom] = sqrt(sx * sx + sy * sy + sz * sz); + } + + return; + } + + } // End of indental namespace + +} // End of sim namespace diff --git a/src/simulate/LSF_RK4.cpp b/src/simulate/LSF_RK4.cpp new file mode 100644 index 000000000..1cd22108b --- /dev/null +++ b/src/simulate/LSF_RK4.cpp @@ -0,0 +1,336 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) David R Papp 2024. All rights reserved. +// +//------------------------------------------------------------------------------ +// + +// Standard Libraries +#include +#include +#include +#include +#include +#include + +// Vampire Header files +#include "lsf_rk4.hpp" +#include "atoms.hpp" +#include "constants.hpp" +#include "errors.hpp" +#include "exchange.hpp" +#include "internal.hpp" +#include "material.hpp" +#include "random.hpp" +#include "sim.hpp" +#include "vio.hpp" + +// Field calculation functions +int calculate_spin_fields(const int, const int); +int calculate_external_fields(const int, const int); + +namespace LSF_RK4_arrays{ + + // Local arrays for LSF integration + std::vector x_lsf_array; + std::vector y_lsf_array; + std::vector z_lsf_array; + + std::vector x_initial_spin_array; + std::vector y_initial_spin_array; + std::vector z_initial_spin_array; + + std::vector x_k1_array; + std::vector y_k1_array; + std::vector z_k1_array; + + std::vector x_k2_array; + std::vector y_k2_array; + std::vector z_k2_array; + + std::vector x_k3_array; + std::vector y_k3_array; + std::vector z_k3_array; + + std::vector x_k4_array; + std::vector y_k4_array; + std::vector z_k4_array; + + // Flag to define state of LSF arrays (initialised/uninitialised) + bool LSF_RK4_set = false; + + std::vector mod_S; + + std::vector tx; + std::vector ty; + std::vector tz; + +} +namespace sim{ + + int LSF_RK4_init(){ + // Check calling of routine if error checking is activated + if (err::check == true){ + std::cout << "sim:LSF_RK4_init has been called" << std::endl; + } + + using namespace LSF_RK4_arrays; + + x_lsf_array.resize(atoms::num_atoms, 0.0); + y_lsf_array.resize(atoms::num_atoms, 0.0); + z_lsf_array.resize(atoms::num_atoms, 0.0); + + x_initial_spin_array.resize(atoms::num_atoms, 0.0); + y_initial_spin_array.resize(atoms::num_atoms, 0.0); + z_initial_spin_array.resize(atoms::num_atoms, 0.0); + + x_k1_array.resize(atoms::num_atoms, 0.0); + y_k1_array.resize(atoms::num_atoms, 0.0); + z_k1_array.resize(atoms::num_atoms, 0.0); + + x_k2_array.resize(atoms::num_atoms, 0.0); + y_k2_array.resize(atoms::num_atoms, 0.0); + z_k2_array.resize(atoms::num_atoms, 0.0); + + x_k3_array.resize(atoms::num_atoms, 0.0); + y_k3_array.resize(atoms::num_atoms, 0.0); + z_k3_array.resize(atoms::num_atoms, 0.0); + + x_k4_array.resize(atoms::num_atoms, 0.0); + y_k4_array.resize(atoms::num_atoms, 0.0); + z_k4_array.resize(atoms::num_atoms, 0.0); + + mod_S.resize(atoms::num_atoms, 1.0); + + tx.resize(atoms::num_atoms, 0.0); + ty.resize(atoms::num_atoms, 0.0); + tz.resize(atoms::num_atoms, 0.0); + + // Disable external thermal field calculations + sim::hamiltonian_simulation_flags[3] = 0; + + LSF_RK4_set = true; + + return EXIT_SUCCESS; + } + + // LSF-RK4 magnetic field function + void calculate_lsf_rk4_magnetic_field(const int start_index, const int end_index){ + + // Check calling of routine if error checking is activated + if (err::check == true){ + std::cout << "calculate_lsf_rk4_magnetic_field has been called" << std::endl; + } + + // LSF-RK4 Hamiltonian calculation + for (int atom = start_index; atom < end_index; atom++){ + + const int imaterial = atoms::type_array[atom]; + + const double sx = atoms::x_spin_array[atom]; + const double sy = atoms::y_spin_array[atom]; + const double sz = atoms::z_spin_array[atom]; + + const double imu_S = -1.0 / mp::material[imaterial].mu_s_SI; + + const double L2 = 2.0 * sim::internal::lsf_second_order_coefficient[imaterial] * imu_S; + const double L4 = 4.0 * sim::internal::lsf_fourth_order_coefficient[imaterial] * imu_S; + const double L6 = 6.0 * sim::internal::lsf_sixth_order_coefficient[imaterial] * imu_S; + + const double ss2 = sx*sx + sy*sy + sz*sz; + + LSF_RK4_arrays::x_lsf_array[atom] = L2*sx + L4*sx*ss2 + L6*sx*ss2*ss2; + LSF_RK4_arrays::y_lsf_array[atom] = L2*sy + L4*sy*ss2 + L6*sy*ss2*ss2; + LSF_RK4_arrays::z_lsf_array[atom] = L2*sz + L4*sz*ss2 + L6*sz*ss2*ss2; + } + } + + namespace internal{ + + void lsf_rk4_step(){ + + // Check calling of routine if error checking is activated + if (err::check == true){ + std::cout << "sim::LSF_RK4 has been called" << std::endl; + } + + using namespace LSF_RK4_arrays; + + // Check for initialisation of LSF integration arrays + if (LSF_RK4_set == false) + sim::LSF_RK4_init(); + + // Local variables for system integration + const int num_atoms = atoms::num_atoms; + double xyz[3]; // Local delta spin components + const double kB = 1.3806503e-23; + + // Calculate fields + calculate_spin_fields(0, num_atoms); + calculate_lsf_rk4_magnetic_field(0, num_atoms); + calculate_external_fields(0, num_atoms); + + // Store initial spin positions + for (int atom = 0; atom < num_atoms; atom++){ + x_initial_spin_array[atom] = atoms::x_spin_array[atom]; + y_initial_spin_array[atom] = atoms::y_spin_array[atom]; + z_initial_spin_array[atom] = atoms::z_spin_array[atom]; + } + + // Thermal noise based on Gaussian function + double sigma = (sqrt((2.0 * kB * sim::temperature * mp::gamma_SI) / (mp::dt_SI))); + generate(tx.begin(), tx.begin() + num_atoms, mtrandom::gaussian); + generate(ty.begin(), ty.begin() + num_atoms, mtrandom::gaussian); + generate(tz.begin(), tz.begin() + num_atoms, mtrandom::gaussian); + + // Calculate K1 + for (int atom = 0; atom < num_atoms; atom++){ + + const int imaterial = atoms::type_array[atom]; + const double alpha = mp::material[imaterial].alpha; + const double mu = mp::material[imaterial].mu_s_SI; + + // Store local spin in S and local field in H + const double S[3] = {atoms::x_spin_array[atom], atoms::y_spin_array[atom], atoms::z_spin_array[atom]}; + + const double H[3] = {atoms::x_total_spin_field_array[atom] + atoms::x_total_external_field_array[atom] + LSF_RK4_arrays::x_lsf_array[atom], + atoms::y_total_spin_field_array[atom] + atoms::y_total_external_field_array[atom] + LSF_RK4_arrays::y_lsf_array[atom], + atoms::z_total_spin_field_array[atom] + atoms::z_total_external_field_array[atom] + LSF_RK4_arrays::z_lsf_array[atom]}; + + // Calculate Delta S + xyz[0] = (-mp::gamma_SI * (S[1] * H[2] - S[2] * H[1])) + (mp::gamma_SI * alpha * H[0]) + (tx[atom] * (sigma * sqrt(alpha / mu))); + xyz[1] = (-mp::gamma_SI * (S[2] * H[0] - S[0] * H[2])) + (mp::gamma_SI * alpha * H[1]) + (ty[atom] * (sigma * sqrt(alpha / mu))); + xyz[2] = (-mp::gamma_SI * (S[0] * H[1] - S[1] * H[0])) + (mp::gamma_SI * alpha * H[2]) + (tz[atom] * (sigma * sqrt(alpha / mu))); + + // k1 = \Delta S * \Delta t + x_k1_array[atom] = xyz[0] * mp::dt_SI; + y_k1_array[atom] = xyz[1] * mp::dt_SI; + z_k1_array[atom] = xyz[2] * mp::dt_SI; + + // Write new spin vectors + atoms::x_spin_array[atom] += 0.5*x_k1_array[atom]; + atoms::y_spin_array[atom] += 0.5*y_k1_array[atom]; + atoms::z_spin_array[atom] += 0.5*z_k1_array[atom]; + + } + + // Recalculate spin dependent fields + calculate_spin_fields(0, num_atoms); + calculate_lsf_rk4_magnetic_field(0, num_atoms); + + // Calculate K2 + for (int atom = 0; atom < num_atoms; atom++){ + + const int imaterial = atoms::type_array[atom]; + const double alpha = mp::material[imaterial].alpha; + const double mu = mp::material[imaterial].mu_s_SI; + + // Store local spin in S and local field in H + const double S[3] = {atoms::x_spin_array[atom], atoms::y_spin_array[atom], atoms::z_spin_array[atom]}; + + const double H[3] = {atoms::x_total_spin_field_array[atom] + atoms::x_total_external_field_array[atom] + LSF_RK4_arrays::x_lsf_array[atom], + atoms::y_total_spin_field_array[atom] + atoms::y_total_external_field_array[atom] + LSF_RK4_arrays::y_lsf_array[atom], + atoms::z_total_spin_field_array[atom] + atoms::z_total_external_field_array[atom] + LSF_RK4_arrays::z_lsf_array[atom]}; + + // Calculate Delta S + xyz[0] = (-mp::gamma_SI * (S[1] * H[2] - S[2] * H[1])) + (mp::gamma_SI * alpha * H[0]) + (tx[atom] * (sigma * sqrt(alpha / mu))); + xyz[1] = (-mp::gamma_SI * (S[2] * H[0] - S[0] * H[2])) + (mp::gamma_SI * alpha * H[1]) + (ty[atom] * (sigma * sqrt(alpha / mu))); + xyz[2] = (-mp::gamma_SI * (S[0] * H[1] - S[1] * H[0])) + (mp::gamma_SI * alpha * H[2]) + (tz[atom] * (sigma * sqrt(alpha / mu))); + + // k2 + x_k2_array[atom] = xyz[0] * mp::dt_SI; + y_k2_array[atom] = xyz[1] * mp::dt_SI; + z_k2_array[atom] = xyz[2] * mp::dt_SI; + + // Write new spin vectors + atoms::x_spin_array[atom] += 0.5*x_k2_array[atom]; + atoms::y_spin_array[atom] += 0.5*y_k2_array[atom]; + atoms::z_spin_array[atom] += 0.5*z_k2_array[atom]; + } + + // Calculate K3 + for (int atom = 0; atom < num_atoms; atom++){ + + const int imaterial = atoms::type_array[atom]; + const double alpha = mp::material[imaterial].alpha; + const double mu = mp::material[imaterial].mu_s_SI; + + // Store local spin in S and local field in H + const double S[3] = {atoms::x_spin_array[atom], atoms::y_spin_array[atom], atoms::z_spin_array[atom]}; + + const double H[3] = {atoms::x_total_spin_field_array[atom] + atoms::x_total_external_field_array[atom] + LSF_RK4_arrays::x_lsf_array[atom], + atoms::y_total_spin_field_array[atom] + atoms::y_total_external_field_array[atom] + LSF_RK4_arrays::y_lsf_array[atom], + atoms::z_total_spin_field_array[atom] + atoms::z_total_external_field_array[atom] + LSF_RK4_arrays::z_lsf_array[atom]}; + + // Calculate Delta S + xyz[0] = (-mp::gamma_SI * (S[1] * H[2] - S[2] * H[1])) + (mp::gamma_SI * alpha * H[0]) + (tx[atom] * (sigma * sqrt(alpha / mu))); + xyz[1] = (-mp::gamma_SI * (S[2] * H[0] - S[0] * H[2])) + (mp::gamma_SI * alpha * H[1]) + (ty[atom] * (sigma * sqrt(alpha / mu))); + xyz[2] = (-mp::gamma_SI * (S[0] * H[1] - S[1] * H[0])) + (mp::gamma_SI * alpha * H[2]) + (tz[atom] * (sigma * sqrt(alpha / mu))); + + // k3 + x_k3_array[atom] = xyz[0] * mp::dt_SI; + y_k3_array[atom] = xyz[1] * mp::dt_SI; + z_k3_array[atom] = xyz[2] * mp::dt_SI; + + // Write new spin vectors + atoms::x_spin_array[atom] += x_k3_array[atom]; + atoms::y_spin_array[atom] += y_k3_array[atom]; + atoms::z_spin_array[atom] += z_k3_array[atom]; + + } + + // Recalculate spin dependent fields + calculate_spin_fields(0, num_atoms); + calculate_lsf_rk4_magnetic_field(0, num_atoms); + + // Calculate K4 + for (int atom = 0; atom < num_atoms; atom++){ + + const int imaterial = atoms::type_array[atom]; + const double alpha = mp::material[imaterial].alpha; + const double mu = mp::material[imaterial].mu_s_SI; + + // Store local spin in S and local field in H + const double S[3] = {atoms::x_spin_array[atom], atoms::y_spin_array[atom], atoms::z_spin_array[atom]}; + + const double H[3] = {atoms::x_total_spin_field_array[atom] + atoms::x_total_external_field_array[atom] + LSF_RK4_arrays::x_lsf_array[atom], + atoms::y_total_spin_field_array[atom] + atoms::y_total_external_field_array[atom] + LSF_RK4_arrays::y_lsf_array[atom], + atoms::z_total_spin_field_array[atom] + atoms::z_total_external_field_array[atom] + LSF_RK4_arrays::z_lsf_array[atom]}; + + // Calculate Delta S + xyz[0] = (-mp::gamma_SI * (S[1] * H[2] - S[2] * H[1])) + (mp::gamma_SI * alpha * H[0]) + (tx[atom] * (sigma * sqrt(alpha / mu))); + xyz[1] = (-mp::gamma_SI * (S[2] * H[0] - S[0] * H[2])) + (mp::gamma_SI * alpha * H[1]) + (ty[atom] * (sigma * sqrt(alpha / mu))); + xyz[2] = (-mp::gamma_SI * (S[0] * H[1] - S[1] * H[0])) + (mp::gamma_SI * alpha * H[2]) + (tz[atom] * (sigma * sqrt(alpha / mu))); + + // k4 + x_k4_array[atom] = xyz[0] * mp::dt_SI; + y_k4_array[atom] = xyz[1] * mp::dt_SI; + z_k4_array[atom] = xyz[2] * mp::dt_SI; + + } + + // Calculate RK4 Step + for (int atom = 0; atom < num_atoms; atom++){ + atoms::x_spin_array[atom] = x_initial_spin_array[atom] + (1.0/6.0)*(x_k1_array[atom] + (2.0*x_k2_array[atom]) + (2.0*x_k3_array[atom]) + x_k4_array[atom]); + atoms::y_spin_array[atom] = y_initial_spin_array[atom] + (1.0/6.0)*(y_k1_array[atom] + (2.0*y_k2_array[atom]) + (2.0*y_k3_array[atom]) + y_k4_array[atom]); + atoms::z_spin_array[atom] = z_initial_spin_array[atom] + (1.0/6.0)*(z_k1_array[atom] + (2.0*z_k2_array[atom]) + (2.0*z_k3_array[atom]) + z_k4_array[atom]); + } + + // Store spin length data + for (int atom = 0; atom < num_atoms; atom++){ + const double sx = atoms::x_spin_array[atom]; + const double sy = atoms::y_spin_array[atom]; + const double sz = atoms::z_spin_array[atom]; + + mod_S[atom] = sqrt(sx * sx + sy * sy + sz * sz); + } + + return; + } + + } // End of indental namespace + +} // End of sim namespace diff --git a/src/simulate/data.cpp b/src/simulate/data.cpp index 267a28e7c..a5f30bc0d 100644 --- a/src/simulate/data.cpp +++ b/src/simulate/data.cpp @@ -8,6 +8,7 @@ //----------------------------------------------------------------------------- // C++ standard library headers +#include // Vampire headers #include "sim.hpp" @@ -78,29 +79,44 @@ namespace sim{ // Shared variables used within sim module //---------------------------------------------------------------------------- bool enable_spin_torque_fields = false; // flag to enable spin torque fields + bool enable_local_stt_polarizers = false; // flag to enable localised stt polarization vectors + bool enable_local_sot_polarizers = false; // flag to enable localised sot polarization vectors + bool enable_vcma_fields = false; // flag to enable voltage-controlled anisotropy fields std::vector mp; // array of material properties std::vector stt_asm; // array of spin transfer torque asymmetry - std::vector stt_rj; // array of adiabatic spin torques - std::vector stt_pj; // array of non-adiabatic spin torques - std::vector stt_polarization_unit_vector(3,0.0); // stt spin polarization direction + std::vector stt_rj; // array of adiabatic spin torques + std::vector stt_pj; // array of non-adiabatic spin torques + std::vector stt_pm; // array of spin orbit torque polarizer materials + vtype::vec_t stt_polarization_unit_vector; // stt spin polarization direction + std::vector stt_material_polarization_unit_vector; // stt spin polarization direction for each material std::vector sot_asm; // array of spin orbit torque asymmetry std::vector sot_rj; // array of adiabatic spin torques std::vector sot_pj; // array of non-adiabatic spin torques - std::vector sot_polarization_unit_vector(3,0.0); // sot spin polarization direction + std::vector sot_pm; // array of spin orbit torque polarizer materials + vtype::vec_t sot_polarization_unit_vector; // sot spin polarization direction + std::vector sot_material_polarization_unit_vector; // sot spin polarization direction for each material std::vector vcmak; // voltage controlled anisotropy coefficient + std::vector lsf_second_order_coefficient; + std::vector lsf_fourth_order_coefficient; // LSF coefficients + std::vector lsf_sixth_order_coefficient; + } // end of internal namespace //------------------------------------------------------------------------ // getter functions to give access to internal variables //------------------------------------------------------------------------ std::vector get_stt_polarization_unit_vector(){ - return sim::internal::stt_polarization_unit_vector; + std::vector sttpv(3); + sttpv[0] = sim::internal::stt_polarization_unit_vector.x; + sttpv[1] = sim::internal::stt_polarization_unit_vector.y; + sttpv[2] = sim::internal::stt_polarization_unit_vector.z; + return sttpv; } std::vector get_stt_rj(){ diff --git a/src/simulate/energy.cpp b/src/simulate/energy.cpp index 474bfc5a9..7a261df8d 100644 --- a/src/simulate/energy.cpp +++ b/src/simulate/energy.cpp @@ -64,6 +64,7 @@ #include "spintransport.hpp" #include "vio.hpp" #include "vmpi.hpp" +#include "constants.hpp" // sim module header #include "internal.hpp" @@ -140,6 +141,28 @@ double spin_magnetostatic_energy(const int atom, const double Sx, const double S return -1.0*(dipole::atom_mu0demag_field_array_x[atom]*Sx+dipole::atom_mu0demag_field_array_y[atom]*Sy+dipole::atom_mu0demag_field_array_z[atom]*Sz); } +// Calculates LSF energy +double spin_longitudinal_energy(const int atom){ + + // Standard Landau Hamiltonian + const int imaterial = atoms::type_array[atom]; + const double Sx = atoms::x_spin_array[atom]; + const double Sy = atoms::y_spin_array[atom]; + const double Sz = atoms::z_spin_array[atom]; + double spinlength = sqrt(Sx * Sx + Sy * Sy + Sz * Sz); + double mod_S2_i = spinlength * spinlength; + double mod_S4_i = mod_S2_i * mod_S2_i; + double mod_S6_i = mod_S4_i * mod_S2_i; + + double A = sim::internal::lsf_second_order_coefficient[imaterial]; + double B = sim::internal::lsf_fourth_order_coefficient[imaterial]; + double C = sim::internal::lsf_sixth_order_coefficient[imaterial]; + + double landau_energy = (1 / mp::material[imaterial].mu_s_SI) * ((A * mod_S2_i) + (B * mod_S4_i) + (C * mod_S6_i)); + + return landau_energy; // Tesla +} + /// @brief Calculates the total energy for a single spin. /// /// @section License @@ -178,10 +201,16 @@ double calculate_spin_energy(const int atom){ // Calculate total spin energy energy += exchange::single_spin_energy(atom, Sx, Sy, Sz); energy += exchange::single_spin_biquadratic_energy(atom, Sx, Sy, Sz); + energy += exchange::single_spin_four_spin_energy(atom, Sx, Sy, Sz); // calculate anisotropy energy for atom energy += anisotropy::single_spin_energy(atom, imaterial, Sx, Sy, Sz, sim::temperature); + // Landau energy + if(sim::integrator==sim::lsf_mc){ + energy += spin_longitudinal_energy(atom); + } + energy+=spin_applied_field_energy(Sx, Sy, Sz); energy+=spin_magnetostatic_energy(atom, Sx, Sy, Sz); diff --git a/src/simulate/fields.cpp b/src/simulate/fields.cpp index e4fd921bd..7dd00cfe3 100644 --- a/src/simulate/fields.cpp +++ b/src/simulate/fields.cpp @@ -1,36 +1,21 @@ -//----------------------------------------------------------------------------- -// -// Vampire - A code for atomistic simulation of magnetic materials -// -// Copyright (C) 2009-2012 R.F.L.Evans -// -// Email:richard.evans@york.ac.uk -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; if not, write to the Free Software Foundation, -// Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. -// -// ---------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // -//============================================================================= +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). // -// Fields +// (c) Richard F L Evans 2025. All rights reserved. // -// Subroutines to calculate fields for the hamiltonian +// Email: richard.evans@york.ac.uk // -// Version 1.0 R Evans 20/10/2008 +//------------------------------------------------------------------------------ // -//==================================================================================================== + +// C++ standard library headers +#include +#include +#include + +// Vampire headers #include "anisotropy.hpp" #include "atoms.hpp" #include "material.hpp" @@ -52,9 +37,6 @@ // sim module header #include "internal.hpp" -#include -#include -#include //======================== //function prototypes @@ -162,8 +144,8 @@ void calculate_external_fields(const int start_index,const int end_index){ else if(program::program==13){ // Local thermal Fields - ltmp::get_localised_thermal_fields(atoms::x_total_external_field_array,atoms::y_total_external_field_array, - atoms::z_total_external_field_array, start_index, end_index); + ltmp::get_localised_thermal_fields(atoms::x_thermal_field_array,atoms::y_thermal_field_array, + atoms::z_thermal_field_array, start_index, end_index); // Applied Fields if(sim::hamiltonian_simulation_flags[2]==1) calculate_applied_fields(start_index,end_index); @@ -183,7 +165,7 @@ void calculate_external_fields(const int start_index,const int end_index){ st::get_spin_torque_fields(atoms::x_total_external_field_array, atoms::y_total_external_field_array, atoms::z_total_external_field_array, start_index, end_index); // Get updated spin torque fields - spin_transport::calculate_field(start_index, end_index, atoms::x_total_external_field_array, atoms::y_total_external_field_array, atoms::z_total_external_field_array); + spin_transport::calculate_field(start_index, end_index, atoms::x_total_external_field_array, atoms::y_total_external_field_array, atoms::z_total_external_field_array, atoms::type_array); // FMR Fields only for fmr program if(sim::enable_fmr) calculate_fmr_fields(start_index,end_index); @@ -303,7 +285,7 @@ int calculate_thermal_fields(const int start_index,const int end_index){ sigma_prefactor.reserve(mp::material.size()); // Calculate material temperature (with optional rescaling) - for(unsigned int mat=0;mat matmag = stats::material_magnetization.get_magnetization(); + + //--------------------------------------------------------------------------- + // spin transfer torque + //--------------------------------------------------------------------------- + // check for global polarizer and overwrite material ones from initialisation + if(!enable_local_stt_polarizers){ + for(int m = 0 ; m < mp::num_materials ; m++){ + stt_material_polarization_unit_vector[m] = stt_polarization_unit_vector; // stt spin polarization direction + } + } + else{ + + // Default is the polarisers are set at initialisation for each material + // ... + + // for local polarisers check for dynamic ones + for(int m = 0 ; m < mp::num_materials ; m++){ + + // overwrite vector with polarising material + if(stt_pm[m] > -1){ + + // set polarizing material + const int pm = stt_pm[m]; + + // load material magnetisation components + double mx = matmag[4*pm+0]; + double my = matmag[4*pm+1]; + double mz = matmag[4*pm+2]; + double mm = matmag[4*pm+3]; + + //std::cout << "setting STT polarizer for material " << m+1 << " to material " << pm+1 << " ( m = " << mx << "\t" << my << "\t" << mz << " )" << std::endl; + + // non-unit vector to account for temperature effects + stt_material_polarization_unit_vector[m].x = mx * mm; + stt_material_polarization_unit_vector[m].y = my * mm; + stt_material_polarization_unit_vector[m].z = mz * mm; + + } + + } + } + + //--------------------------------------------------------------------------- + // spin orbit torque + //--------------------------------------------------------------------------- + // check for global polarizer and overwrite material ones from initialisation + if(!enable_local_sot_polarizers){ + for(int m = 0 ; m < mp::num_materials ; m++){ + sot_material_polarization_unit_vector[m] = sot_polarization_unit_vector; // stt spin polarization direction + } + } + else{ + + // Default is the polarisers are set at initialisation for each material + // ... + + // for local polarisers check for dynamic ones + for(int m = 0 ; m < mp::num_materials ; m++){ + + // set polarizing material + const int pm = stt_pm[m]; + + // overwrite vector with polarising material + if(sot_pm[m] > -1){ + // load material magnetisation components + double mx = matmag[4*pm+0]; + double my = matmag[4*pm+1]; + double mz = matmag[4*pm+2]; + double mm = matmag[4*pm+3]; + // non-unit vector to account for temperature effects + sot_material_polarization_unit_vector[m].x = mx*mm; + sot_material_polarization_unit_vector[m].y = my*mm; + sot_material_polarization_unit_vector[m].z = mz*mm; + } + + } + } + + return; + +} + //------------------------------------------------------------------------------ // Master function to calculate fields in large loop //------------------------------------------------------------------------------ @@ -471,6 +545,9 @@ void calculate_full_spin_fields(const int start_index,const int end_index){ using namespace sim::internal; + // set up dynamic torque polarization + set_torque_polarization_vectors(); + for(int atom=start_index;atom @@ -26,24 +29,37 @@ namespace sim{ sim::internal::stt_asm.resize(num_materials,0.0); sim::internal::stt_rj.resize(num_materials,0.0); sim::internal::stt_pj.resize(num_materials,0.0); + sim::internal::stt_pm.resize(num_materials,-1); // set to -1 indicating unset + sim::internal::stt_material_polarization_unit_vector.resize(num_materials); // unroll spin orbit torque arrays sim::internal::sot_asm.resize(num_materials,0.0); sim::internal::sot_rj.resize(num_materials,0.0); sim::internal::sot_pj.resize(num_materials,0.0); + sim::internal::sot_pm.resize(num_materials,-1); // set to -1 indicating unset + sim::internal::sot_material_polarization_unit_vector.resize(num_materials); sim::internal::vcmak.resize(num_materials, 0.0); + // Unroll LSF coefficient arrays + sim::internal::lsf_second_order_coefficient.resize(num_materials,0.0); + sim::internal::lsf_fourth_order_coefficient.resize(num_materials,0.0); + sim::internal::lsf_sixth_order_coefficient.resize(num_materials,0.0); + // loop over materials set by user for(unsigned int m=0; m < sim::internal::mp.size(); ++m){ // copy values set by user to arrays if(sim::internal::mp[m].stt_asm.is_set()) sim::internal::stt_asm[m] = sim::internal::mp[m].stt_asm.get(); if(sim::internal::mp[m].stt_rj.is_set()) sim::internal::stt_rj[m] = sim::internal::mp[m].stt_rj.get(); if(sim::internal::mp[m].stt_pj.is_set()) sim::internal::stt_pj[m] = sim::internal::mp[m].stt_pj.get(); + if(sim::internal::mp[m].stt_pm.is_set()) sim::internal::stt_pm[m] = sim::internal::mp[m].stt_pm.get(); + sim::internal::stt_material_polarization_unit_vector[m] = sim::internal::mp[m].stt_pv; if(sim::internal::mp[m].sot_asm.is_set()) sim::internal::sot_asm[m] = sim::internal::mp[m].sot_asm.get(); if(sim::internal::mp[m].sot_rj.is_set()) sim::internal::sot_rj[m] = sim::internal::mp[m].sot_rj.get(); if(sim::internal::mp[m].sot_pj.is_set()) sim::internal::sot_pj[m] = sim::internal::mp[m].sot_pj.get(); + if(sim::internal::mp[m].sot_pm.is_set()) sim::internal::sot_pm[m] = sim::internal::mp[m].sot_pm.get(); + sim::internal::sot_material_polarization_unit_vector[m] = sim::internal::mp[m].sot_pv; // set vcma coefficients (requires sim::internal::enable_vcma_fields == true) but this should be default if(sim::internal::mp[m].vcmak.is_set()){ @@ -51,6 +67,11 @@ namespace sim{ sim::internal::vcmak[m] = imu_s * sim::internal::mp[m].vcmak.get(); } + // Assign Landau coefficients based on material + if(sim::internal::mp[m].lsf_second_order_coefficient.is_set()) sim::internal::lsf_second_order_coefficient[m] = sim::internal::mp[m].lsf_second_order_coefficient.get(); + if(sim::internal::mp[m].lsf_fourth_order_coefficient.is_set()) sim::internal::lsf_fourth_order_coefficient[m] = sim::internal::mp[m].lsf_fourth_order_coefficient.get(); + if(sim::internal::mp[m].lsf_sixth_order_coefficient.is_set()) sim::internal::lsf_sixth_order_coefficient[m] = sim::internal::mp[m].lsf_sixth_order_coefficient.get(); + } return; diff --git a/src/simulate/initialize_modules.cpp b/src/simulate/initialize_modules.cpp index 5337595c3..d6f8ee1dc 100644 --- a/src/simulate/initialize_modules.cpp +++ b/src/simulate/initialize_modules.cpp @@ -18,6 +18,7 @@ #include "create.hpp" #include "dipole.hpp" #include "hamr.hpp" +#include "spinwaves.hpp" // jrh #include "ltmp.hpp" #include "sim.hpp" #include "spintorque.hpp" @@ -139,7 +140,7 @@ void initialize_modules(){ cs::non_magnetic_atoms_array); //---------------------------------------- - // Initialise hamr module + // Initialise hamr module //---------------------------------------- hamr::initialize(sim::Hmin, sim::Hmax, @@ -155,6 +156,25 @@ void initialize_modules(){ atoms::num_atoms ); + //---------------------------------------- + // Initialise spinwaves module + // jrh + //---------------------------------------- + spinwaves::initialize(cs::system_dimensions[0], + cs::system_dimensions[1], + cs::system_dimensions[2], + cs::total_num_unit_cells[0], + cs::total_num_unit_cells[1], + cs::total_num_unit_cells[2], + cs::unit_cell.dimensions[0], + cs::unit_cell.dimensions[1], + cs::unit_cell.dimensions[2], + cs::unit_cell.atom, + atoms::x_coord_array, + atoms::y_coord_array, + atoms::z_coord_array + ); + return; } diff --git a/src/simulate/interface.cpp b/src/simulate/interface.cpp index d13c5a0cd..8cc7d4725 100644 --- a/src/simulate/interface.cpp +++ b/src/simulate/interface.cpp @@ -1,11 +1,14 @@ -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // -// This source file is part of the VAMPIRE open source package under the -// GNU GPL (version 2) licence (see licence file for details). +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). // -// (c) R F L Evans 2014. All rights reserved. +// (c) Richard F L Evans 2025. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ // -//----------------------------------------------------------------------------- // C++ standard library headers #include @@ -17,6 +20,7 @@ // Internal sim header #include "internal.hpp" +#include "sld.hpp" namespace sim{ @@ -43,7 +47,9 @@ namespace sim{ // Test for valid range vin::check_for_valid_unit_vector(u, word, line, prefix, "input"); // save sanitized unit vector - sim::internal::stt_polarization_unit_vector = u; + sim::internal::stt_polarization_unit_vector.x = u[0]; + sim::internal::stt_polarization_unit_vector.y = u[1]; + sim::internal::stt_polarization_unit_vector.z = u[2]; return true; } //------------------------------------------------------------------- @@ -54,7 +60,9 @@ namespace sim{ // Test for valid range vin::check_for_valid_unit_vector(u, word, line, prefix, "input"); // save sanitized unit vector - sim::internal::sot_polarization_unit_vector = u; + sim::internal::sot_polarization_unit_vector.x = u[0]; + sim::internal::sot_polarization_unit_vector.y = u[1]; + sim::internal::sot_polarization_unit_vector.z = u[2]; return true; } //------------------------------------------------------------------- @@ -144,6 +152,30 @@ namespace sim{ sim::integrator = sim::llg_quantum; return true; } + test="spin-lattice"; + if( value == test ){ + sim::integrator = sim::suzuki_trotter; + return true; + } + // + //-------------------------------------------------------------------- + test="lsf"; + if( value == test ){ + sim::integrator = sim::lsf; + return true; + } + //-------------------------------------------------------------------- + test="lsf-mc"; + if( value == test ){ + sim::integrator = sim::lsf_mc; + return true; + } + //-------------------------------------------------------------------- + test="lsf-rk4"; + if( value == test ){ + sim::integrator = sim::lsf_rk4; + return true; + } //-------------------------------------------------------------------- else{ terminaltextcolor(RED); @@ -153,6 +185,9 @@ namespace sim{ std::cerr << "\t\"llg-quantum\"" << std::endl; std::cerr << "\t\"monte-carlo\"" << std::endl; std::cerr << "\t\"constrained-monte-carlo\"" << std::endl; + std::cerr << "\t\"lsf\"" << std::endl; + std::cerr << "\t\"lsf-mc\"" << std::endl; + std::cerr << "\t\"lsf-rk4\"" << std::endl; terminaltextcolor(WHITE); err::vexit(); } @@ -288,6 +323,31 @@ namespace sim{ return true; } //------------------------------------------------------------ + test="spin-transfer-torque-polarization-unit-vector"; + if(word==test){ + std::vector u(3); + u=vin::doubles_from_string(value); + // Test for valid range + vin::check_for_valid_unit_vector(u, word, line, prefix, "input"); + // save sanitized unit vector + sim::internal::mp[super_index].stt_pv.x = u[0]; + sim::internal::mp[super_index].stt_pv.y = u[1]; + sim::internal::mp[super_index].stt_pv.z = u[2]; + sim::internal::enable_local_stt_polarizers = true; + return true; + } + //------------------------------------------------------------ + test="spin-transfer-torque-polarization-material"; + if(word==test){ + int mid = vin::str_to_int(value); // convert string to int + // Test for valid range + vin::check_for_valid_int(mid, word, line, prefix, 1, mp::max_materials,"material","1 - 100"); + sim::internal::mp[super_index].stt_pm.set(mid-1); // save material ID as m-1 + zlog << zTs() << "Setting STT polarizer for material " << super_index+1 << " to material " << sim::internal::mp[super_index].stt_pm.get()+1 << std::endl; + sim::internal::enable_local_stt_polarizers = true; + return true; + } + //------------------------------------------------------------ // field-like parameter for material in spin orbit torque calculation test = "spin-orbit-relaxation-torque"; test2 = "spin-orbit-anti-damping-torque"; @@ -323,6 +383,31 @@ namespace sim{ return true; } //------------------------------------------------------------ + test="spin-orbit-torque-polarization-unit-vector"; + if(word==test){ + std::vector u(3); + u=vin::doubles_from_string(value); + // Test for valid range + vin::check_for_valid_unit_vector(u, word, line, prefix, "input"); + // save sanitized unit vector + sim::internal::mp[super_index].sot_pv.x = u[0]; + sim::internal::mp[super_index].sot_pv.y = u[1]; + sim::internal::mp[super_index].sot_pv.z = u[2]; + sim::internal::enable_local_sot_polarizers = true; + return true; + } + //------------------------------------------------------------ + test="spin-orbit-torque-polarization-material"; + if(word==test){ + int mid = vin::str_to_int(value); // convert string to int + // Test for valid range + vin::check_for_valid_int(mid, word, line, prefix, 1, mp::max_materials,"material","1 - 100"); + sim::internal::mp[super_index].sot_pm.set(mid-1); // save material ID as m-1 + zlog << zTs() << "Setting SOT polarizer for material " << super_index+1 << " to material " << sim::internal::mp[super_index].sot_pm.get()+1 << std::endl; + sim::internal::enable_local_sot_polarizers = true; + return true; + } + //------------------------------------------------------------ test = "voltage-controlled-magnetic-anisotropy-coefficient"; test2 = "vcma-coefficient"; // voltage dependent anisotropy (Joules / Volt == Coulombs) for vcma @@ -334,6 +419,33 @@ namespace sim{ sim::internal::enable_vcma_fields = true; // enable vcma fields return true; } + //------------------------------------------------------------ + test="lsf-second-order-coefficient"; // Landau coefficient A + if( word==test ){ + double a_l=atof(value.c_str()); + // Test for valid range + vin::check_for_valid_value(a_l, word, line, prefix, unit, "energy", -1.0e-16, 1.0e-16,"material","-1.0e-16 - 1.0e-16"); + sim::internal::mp[super_index].lsf_second_order_coefficient.set(a_l); + return true; + } + //------------------------------------------------------------ + test="lsf-fourth-order-coefficient"; // Landau coefficient B + if( word==test ){ + double b_l=atof(value.c_str()); + // Test for valid range + vin::check_for_valid_value(b_l, word, line, prefix, unit, "energy", -1.0e-16, 1.0e-16,"material","-1.0e-16 - 1.0e-16"); + sim::internal::mp[super_index].lsf_fourth_order_coefficient.set(b_l); + return true; + } + //------------------------------------------------------------ + test="lsf-sixth-order-coefficient"; // Landau coefficient C + if( word==test ){ + double c_l=atof(value.c_str()); + // Test for valid range + vin::check_for_valid_value(c_l, word, line, prefix, unit, "energy", -1.0e-16, 1.0e-16,"material","-1.0e-16 - 1.0e-16"); + sim::internal::mp[super_index].lsf_sixth_order_coefficient.set(c_l); + return true; + } //-------------------------------------------------------------------- // keyword not found //-------------------------------------------------------------------- diff --git a/src/simulate/internal.hpp b/src/simulate/internal.hpp index a309777a7..af0f125c7 100644 --- a/src/simulate/internal.hpp +++ b/src/simulate/internal.hpp @@ -1,13 +1,19 @@ #ifndef SIM_INTERNAL_H_ #define SIM_INTERNAL_H_ -//----------------------------------------------------------------------------- +//------------------------------------------------------------------------------ // -// This header file is part of the VAMPIRE open source package under the -// GNU GPL (version 2) licence (see licence file for details). +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). // -// (c) R F L Evans 2014. All rights reserved. +// (c) Richard F L Evans 2025. All rights reserved. // -//----------------------------------------------------------------------------- +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// Vampire headers +#include "vtypes.hpp" //--------------------------------------------------------------------- // Defines shared internal data structures and functions for the @@ -21,40 +27,21 @@ namespace sim{ //----------------------------------------------------------------------------- // Internal data types used for simulation module //----------------------------------------------------------------------------- - - // simple initialised class for set variables - class set_double_t{ - - private: - double value; // value - bool setf; // flag specifiying variable has been set - - public: - // class functions - // constructor - set_double_t() : value(0.0), setf(false) { } - - // setting function - void set(double in_value){ - value = in_value; - setf = true; - }; - - // get value function - double get(){ return value; }; - // check if variable is set - bool is_set(){ return setf; }; - - }; - struct mp_t{ - set_double_t stt_asm; // spin tranfer torque asymmetry - set_double_t stt_rj; // spin tranfer relaxation torque - set_double_t stt_pj; // spin transfer precession torque - set_double_t sot_asm; // spin orbit torque asymmetry - set_double_t sot_rj; // spin orbit relaxation torque - set_double_t sot_pj; // spin orbit precession torque - set_double_t vcmak; // voltage controlled anisotropy coefficient + vtype::set_double_t stt_asm; // spin tranfer torque asymmetry + vtype::set_double_t stt_rj; // spin tranfer relaxation torque + vtype::set_double_t stt_pj; // spin transfer precession torque + vtype::set_int_t stt_pm; // spin transfer polarisation material + vtype::vec_t stt_pv; // spin transfer polarisation vector x,y,z + vtype::set_double_t sot_asm; // spin orbit torque asymmetry + vtype::set_double_t sot_rj; // spin orbit relaxation torque + vtype::set_double_t sot_pj; // spin orbit precession torque + vtype::set_int_t sot_pm; // spin orbit polarisation material + vtype::vec_t sot_pv; // spin orbit polarisation vector x,y,z + vtype::set_double_t vcmak; // voltage controlled anisotropy coefficient + vtype::set_double_t lsf_second_order_coefficient; // Second order LSF coefficient + vtype::set_double_t lsf_fourth_order_coefficient; // Fourth order LSF coefficient + vtype::set_double_t lsf_sixth_order_coefficient; // Sixth order LSF coefficient }; //----------------------------------------------------------------------------- @@ -62,21 +49,31 @@ namespace sim{ //----------------------------------------------------------------------------- extern bool enable_spin_torque_fields; // flag to enable spin torque fields extern bool enable_vcma_fields; // flag to enable voltage-controlled anisotropy fields + extern bool enable_local_stt_polarizers; // flag to enable localised stt polarization vectors + extern bool enable_local_sot_polarizers; // flag to enable localised sot polarization vectors extern std::vector mp; // array of material properties extern std::vector stt_asm; // array of spin transfer torque asymmetry extern std::vector stt_rj; // array of adiabatic spin torques extern std::vector stt_pj; // array of non-adiabatic spin torques - extern std::vector stt_polarization_unit_vector; // stt spin polarization direction + extern std::vector stt_pm; // array of polarisation material for spin torques + extern vtype::vec_t stt_polarization_unit_vector; // stt spin polarization direction + extern std::vector stt_material_polarization_unit_vector; // array of stt spin polarization direction extern std::vector sot_asm; // array of spin orbit torque asymmetry extern std::vector sot_rj; // array of adiabatic spin torques extern std::vector sot_pj; // array of non-adiabatic spin torques - extern std::vector sot_polarization_unit_vector; // sot spin polarization direction + extern std::vector sot_pm; // array of polarisation material for spin torques + extern vtype::vec_t sot_polarization_unit_vector; // sot spin polarization direction + extern std::vector sot_material_polarization_unit_vector; // array of sot spin polarization direction extern std::vector vcmak; // voltage controlled anisotropy coefficient + extern std::vector lsf_second_order_coefficient; + extern std::vector lsf_fourth_order_coefficient; // LSF coefficients + extern std::vector lsf_sixth_order_coefficient; + // shared Functions void llg_quantum_step(); @@ -85,6 +82,8 @@ namespace sim{ //------------------------------------------------------------------------- extern void initialize_modules(); extern void increment_time(); + extern void lsf_step(); + extern void lsf_rk4_step(); } // end of internal namespace } // end of sim namespace diff --git a/src/simulate/makefile b/src/simulate/makefile index 3e90433b2..2a2475bad 100644 --- a/src/simulate/makefile +++ b/src/simulate/makefile @@ -9,7 +9,9 @@ increment_time.o \ initialize.o \ initialize_modules.o \ interface.o \ -llg_quantum.o +llg_quantum.o \ +LSF.o \ +LSF_RK4.o # Append module objects to global tree OBJECTS+=$(addprefix obj/simulate/,$(sim_objects)) diff --git a/src/simulate/sim.cpp b/src/simulate/sim.cpp index 6657a7254..47af2f830 100644 --- a/src/simulate/sim.cpp +++ b/src/simulate/sim.cpp @@ -68,6 +68,7 @@ #include "vmpi.hpp" #include "vutil.hpp" #include "micromagnetic.hpp" +#include "sld.hpp" // sim module headers #include "internal.hpp" @@ -211,15 +212,16 @@ int run(){ anisotropy::initialize(atoms::num_atoms, atoms::type_array, mp::mu_s_array); + #ifdef MPICF + if(sld::suzuki_trotter_parallel_initialized == false && sld::enabled) { + sld::suzuki_trotter_parallel_init(atoms::x_coord_array, atoms::y_coord_array, atoms::z_coord_array, + vmpi::min_dimensions, vmpi::max_dimensions); + } + #endif + // 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(); - - // For continuous checkpoints inform user about I/O - if(sim::save_checkpoint_continuous_flag) zlog << zTs() << "Continuously writing checkpoints to disk throughout simulation." << std::endl; - { // Set up statistical data sets #ifdef MPICF @@ -235,9 +237,16 @@ int run(){ stats::initialize(num_atoms_for_statistics, mp::num_materials, grains::num_grains, atoms::m_spin_array, atoms::type_array, atoms::grain_array, atoms::category_array, non_magnetic_materials_array); } - // Precalculate initial statistics and then reset averages + // Check for load spin configurations from checkpoint + if(sim::load_checkpoint_flag) load_checkpoint(); + + // Precalculate initial statistics and then reset averages if not continuing a previous simulation + // RE technically this double counts the last data point in the statistics, need to implement a reset_counter to fix. stats::update(); - stats::reset(); + if(!load_checkpoint_continue_flag) stats::reset(); + + // For continuous checkpoints inform user about I/O + if(sim::save_checkpoint_continuous_flag) zlog << zTs() << "Continuously writing checkpoints to disk throughout simulation." << std::endl; // Initialize GPU acceleration if enabled if(gpu::acceleration) gpu::initialize(); @@ -291,9 +300,6 @@ int run(){ atoms::num_atoms ); - - - if(environment::enabled) environment::initialize(cs::system_dimensions[0],cs::system_dimensions[1],cs::system_dimensions[2]); // For MPI version, calculate initialisation time @@ -471,6 +477,20 @@ int run(){ } program::electrical_pulse(); break; + case 18: + if(vmpi::my_rank==0){ + std::cout << "field-pulse..." << std::endl; + zlog << "field-pulse..." << std::endl; + } + program::field_pulse(); + break; + case 19: + if(vmpi::my_rank==0){ + std::cout << "laser-electrical-pulse..." << std::endl; + zlog << "laser-electrical-pulse..." << std::endl; + } + program::laser_electrical_pulse(); + break; case 50: if(vmpi::my_rank==0){ @@ -534,6 +554,24 @@ int run(){ } program::boltzmann_dist_micromagnetic_llg(); break; + // JRH call spin_waves program ------------------------------------------- + case 74: + if(vmpi::my_rank==0){ + std::cout << "Spin-waves..." << std::endl; + zlog << "Spin-waves..." << std::endl; + } + program::spin_waves(); + break; + //------------------------------------------------------------------------ + case 75: + if(vmpi::my_rank==0){ + std::cout << "HAMR cool..." << std::endl; + zlog << "HAMR cool..." << std::endl; + } + program::hamr_cool(); + break; + + //------------------------------------------------------------------------ default:{ std::cerr << "Unknown Internal Program ID "<< program::program << " requested, exiting" << std::endl; zlog << "Unknown Internal Program ID "<< program::program << " requested, exiting" << std::endl; @@ -715,6 +753,37 @@ void integrate_serial(uint64_t n_steps){ } break; + case sim::lsf: // LSF + for(uint64_t ti=0;ti mp; // array of material spin initialisation properties + + std::vector vector_field_filenames; // cache of loaded vector field file names + std::vector< std::vector > vector_field_data; // cache of loaded vector field data points + + } // end of internal namespace + +} // end of spininitialize namespace diff --git a/src/spininitialize/initialize.cpp b/src/spininitialize/initialize.cpp new file mode 100644 index 000000000..39ea92587 --- /dev/null +++ b/src/spininitialize/initialize.cpp @@ -0,0 +1,111 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers + +// Vampire headers +#include "atoms.hpp" +#include "spininitialize.hpp" + +// spininitialize module headers +#include "internal.hpp" + +namespace spininitialize{ + + //---------------------------------------------------------------------------- + // Function to initialize spininitialize module + //---------------------------------------------------------------------------- + void initialize(){ + + return; + + } + + //---------------------------------------------------------------------------- + // Function to get the (normalised) initial spin direction for an atom of the + // given material at fractional coordinates (fx,fy,fz) of the system size. + //---------------------------------------------------------------------------- + void initialize_spin(const int material, const double fx, const double fy, const double fz, + double& sx, double& sy, double& sz, MTRand& prng){ + + // compute the spin direction from the material's texture (uniform + // vector, random, domain wall, skyrmion, etc.) + internal::get_spin_direction(material, fx, fy, fz, sx, sy, sz, prng); + + return; + + } + + //---------------------------------------------------------------------------- + // Function to set the grain-level magnetisation mode (see + // spininitialize::grain_magnetisation_mode_t for the meaning of mode). + // Called by the create module when it parses the + // create:grain-magnetisation-direction keyword. + //---------------------------------------------------------------------------- + void set_grain_magnetisation_mode(const int mode){ + + internal::grain_magnetisation_mode = mode; + + return; + + } + + //---------------------------------------------------------------------------- + // Function to apply the grain-level magnetisation mode to the already- + // initialised spins of every atom (see hdr/spininitialize.hpp for the full + // rationale). Called once by create::internal::set_atom_vars after the main + // per-atom initialisation loop has populated atoms::x/y/z_spin_array and + // atoms::grain_array. + //---------------------------------------------------------------------------- + void apply_grain_magnetisation_mode(){ + + // default mode ("material"): spins are left exactly as computed by + // initialize_spin(), so there is nothing to do + if(internal::grain_magnetisation_mode == grain_mode_material) return; + + // "alternating" mode: reverse the spin direction of every atom belonging + // to an odd-numbered grain (grains are numbered from 0, so odd grains + // are 1, 3, 5, ...), giving neighbouring grains opposite magnetisation + // directions (e.g. an alternating "chessboard" pattern of grains). + for(int atom=0; atom= 0 && material < int(internal::mp.size()) && internal::mp[material].texture == internal::uniform_vector){ + sx = internal::mp[material].initial_spin[0]; + sy = internal::mp[material].initial_spin[1]; + sz = internal::mp[material].initial_spin[2]; + } + + return; + + } + +} // end of spininitialize namespace + diff --git a/src/spininitialize/interface.cpp b/src/spininitialize/interface.cpp new file mode 100644 index 000000000..b79fd1ab5 --- /dev/null +++ b/src/spininitialize/interface.cpp @@ -0,0 +1,273 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include +#include + +// Vampire headers +#include "spininitialize.hpp" +#include "errors.hpp" +#include "vio.hpp" + +// spininitialize module headers +#include "internal.hpp" + +namespace spininitialize{ + + //--------------------------------------------------------------------------- + // Function to process input file parameters for spininitialize module + //--------------------------------------------------------------------------- + bool match_input_parameter(std::string const key, std::string const word, std::string const value, std::string const unit, int const line){ + + // Check for valid key, if no match return false + std::string prefix="spin-initialize"; + if(key!=prefix) return false; + + //-------------------------------------------------------------------- + // Keyword not found + //-------------------------------------------------------------------- + return false; + + } + + //--------------------------------------------------------------------------- + // 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){ + + // add prefix string + std::string prefix="material:"; + + // Check for material id > current array size and if so dynamically expand mp array + if((unsigned int) super_index + 1 > internal::mp.size() && super_index + 1 < 101) internal::mp.resize(super_index + 1); + + //------------------------------------------------------------------------ + // material[#]:initial-spin-direction = , + // + // This single keyword sets the initial spin configuration for material + // (super_index+1). The value is a comma-separated list: the first token + // selects the texture (or, for backwards compatibility, is itself the + // first component of a plain unit vector), and any remaining tokens are + // the numerical parameters for that texture (e.g. centre/width for a + // domain wall). Each "else if" branch below handles one texture type. + //------------------------------------------------------------------------ + std::string test = "initial-spin-direction"; + if(word == test){ + + // split comma separated value into trimmed tokens, e.g. + // "domain-wall-x, 0.5, 0.2" -> {"domain-wall-x", "0.5", "0.2"} + std::vector tokens = internal::split_csv(value); + + if(tokens.empty()){ + terminaltextcolor(RED); + std::cerr << "Error on line " << line << " of material file - " << prefix << "[" << super_index+1 << "]:" << word << " has no value." << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + + // mat is the spin-initialisation record for this material; its + // fields are filled in by whichever branch below matches + internal::mp_t& mat = internal::mp[super_index]; + + // the first token is either a texture name (e.g. "skyrmion") or, + // for a plain unit vector, the x-component of the vector itself + const std::string& key = tokens[0]; + + //--------------------------------------------------------------- + // random spins (infinite temperature) + // syntax: initial-spin-direction = random + //--------------------------------------------------------------- + if(key == "random"){ + mat.texture = internal::random; + return true; + } + //--------------------------------------------------------------- + // domain-wall-x / domain-wall-y / domain-wall-z, centre, width + // syntax: initial-spin-direction = domain-wall-x, centre, width + // centre and width are fractions (0-1) of the system size along + // the chosen axis; see domain_wall_spin() in textures.cpp + //--------------------------------------------------------------- + else if(key == "domain-wall-x" || key == "domain-wall-y" || key == "domain-wall-z"){ + // exactly 3 tokens expected: the texture name + 2 numbers + if(tokens.size() != 3){ + terminaltextcolor(RED); + std::cerr << "Error on line " << line << " of material file - " << prefix << "[" << super_index+1 << "]:" << word << " = " << key << " requires exactly 2 numerical parameters (centre, width)." << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + mat.texture = internal::domain_wall; + mat.axis = (key == "domain-wall-x") ? 0 : (key == "domain-wall-y") ? 1 : 2; + mat.centre[0] = atof(tokens[1].c_str()); + mat.width = atof(tokens[2].c_str()); + if(mat.width <= 0.0){ + terminaltextcolor(RED); + std::cerr << "Error on line " << line << " of material file - " << prefix << "[" << super_index+1 << "]:" << word << " domain wall width must be greater than zero." << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + return true; + } + //--------------------------------------------------------------- + // skyrmion, cx, cy, radius [, chirality, polarity] + // syntax: initial-spin-direction = skyrmion, cx, cy, radius + // or: initial-spin-direction = skyrmion, cx, cy, radius, chirality, polarity + // (cx,cy) and radius are fractions (0-1) of the system size in the + // x-y plane; chirality and polarity are each +1 or -1 (default +1); + // see skyrmion_spin() in textures.cpp + //--------------------------------------------------------------- + else if(key == "skyrmion"){ + // either 4 tokens (name + cx,cy,radius) or 6 tokens + // (name + cx,cy,radius,chirality,polarity) are accepted + if(tokens.size() != 4 && tokens.size() != 6){ + terminaltextcolor(RED); + std::cerr << "Error on line " << line << " of material file - " << prefix << "[" << super_index+1 << "]:" << word << " = skyrmion requires 3 (cx, cy, radius) or 5 (cx, cy, radius, chirality, polarity) numerical parameters." << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + mat.texture = internal::skyrmion; + mat.centre[0] = atof(tokens[1].c_str()); + mat.centre[1] = atof(tokens[2].c_str()); + mat.width = atof(tokens[3].c_str()); + if(mat.width <= 0.0){ + terminaltextcolor(RED); + std::cerr << "Error on line " << line << " of material file - " << prefix << "[" << super_index+1 << "]:" << word << " skyrmion radius must be greater than zero." << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + if(tokens.size() == 6){ + mat.chirality = (atof(tokens[4].c_str()) < 0.0) ? -1 : 1; + mat.polarity = (atof(tokens[5].c_str()) < 0.0) ? -1 : 1; + } + return true; + } + //--------------------------------------------------------------- + // spin-spiral-x / spin-spiral-y / spin-spiral-z, wavelength + // syntax: initial-spin-direction = spin-spiral-x, wavelength + // wavelength is a fraction (0-1) of the system size along the + // chosen axis; see spin_spiral_spin() in textures.cpp + //--------------------------------------------------------------- + else if(key == "spin-spiral-x" || key == "spin-spiral-y" || key == "spin-spiral-z"){ + // exactly 2 tokens expected: the texture name + 1 number + if(tokens.size() != 2){ + terminaltextcolor(RED); + std::cerr << "Error on line " << line << " of material file - " << prefix << "[" << super_index+1 << "]:" << word << " = " << key << " requires exactly 1 numerical parameter (wavelength)." << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + mat.texture = internal::spin_spiral; + mat.axis = (key == "spin-spiral-x") ? 0 : (key == "spin-spiral-y") ? 1 : 2; + mat.width = atof(tokens[1].c_str()); + if(mat.width <= 0.0){ + terminaltextcolor(RED); + std::cerr << "Error on line " << line << " of material file - " << prefix << "[" << super_index+1 << "]:" << word << " spin spiral wavelength must be greater than zero." << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + return true; + } + //--------------------------------------------------------------- + // vortex, cx, cy, core-radius [, chirality, polarity] + // syntax: initial-spin-direction = vortex, cx, cy, core-radius + // or: initial-spin-direction = vortex, cx, cy, core-radius, chirality, polarity + // (cx,cy) and core-radius are fractions (0-1) of the system size in + // the x-y plane; a core-radius of 0 gives a purely in-plane vortex + // with no out-of-plane core; chirality and polarity are each +1 or + // -1 (default +1); see vortex_spin() in textures.cpp + //--------------------------------------------------------------- + else if(key == "vortex"){ + // either 4 tokens (name + cx,cy,core-radius) or 6 tokens + // (name + cx,cy,core-radius,chirality,polarity) are accepted + if(tokens.size() != 4 && tokens.size() != 6){ + terminaltextcolor(RED); + std::cerr << "Error on line " << line << " of material file - " << prefix << "[" << super_index+1 << "]:" << word << " = vortex requires 3 (cx, cy, core-radius) or 5 (cx, cy, core-radius, chirality, polarity) numerical parameters." << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + mat.texture = internal::vortex; + mat.centre[0] = atof(tokens[1].c_str()); + mat.centre[1] = atof(tokens[2].c_str()); + mat.width = atof(tokens[3].c_str()); + if(mat.width < 0.0){ + terminaltextcolor(RED); + std::cerr << "Error on line " << line << " of material file - " << prefix << "[" << super_index+1 << "]:" << word << " vortex core radius must not be negative." << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + if(tokens.size() == 6){ + mat.chirality = (atof(tokens[4].c_str()) < 0.0) ? -1 : 1; + mat.polarity = (atof(tokens[5].c_str()) < 0.0) ? -1 : 1; + } + return true; + } + //--------------------------------------------------------------- + // vector-field, + // syntax: initial-spin-direction = vector-field, filename + // filename contains rows "x,y,z,mx,my,mz" (fractional coordinates + // and a direction vector) which are interpolated for each atom by + // inverse-distance weighting; see get_vector_field_id() in + // parse.cpp and vector_field_spin() in vector_field.cpp + //--------------------------------------------------------------- + else if(key == "vector-field"){ + // exactly 2 tokens expected: the texture name + the filename + if(tokens.size() != 2){ + terminaltextcolor(RED); + std::cerr << "Error on line " << line << " of material file - " << prefix << "[" << super_index+1 << "]:" << word << " = vector-field requires exactly 1 filename parameter." << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + mat.texture = internal::vector_field; + // load the file (or retrieve it from the cache if another + // material already references the same filename) and store its + // index for later use by vector_field_spin() + mat.vector_field_id = internal::get_vector_field_id(tokens[1], line); + return true; + } + //--------------------------------------------------------------- + // otherwise expect a unit vector mx,my,mz (or crystallographic [hkl] notation) + // syntax: initial-spin-direction = mx,my,mz + // or: initial-spin-direction = [hkl] + // every atom of this material is then initialised pointing along + // this single fixed direction; see uniform_vector_spin() in + // textures.cpp + //--------------------------------------------------------------- + else{ + + // parse the full (un-split) value as a list of doubles, so that + // crystallographic notation such as "[1-10]" is handled + // correctly by doubles_from_string + std::vector u = vin::doubles_from_string(value); + + // check for sane input (3 components) and normalise to a unit + // vector if necessary + vin::check_for_valid_unit_vector(u, word, line, prefix, "material"); + + mat.texture = internal::uniform_vector; + mat.initial_spin[0] = u.at(0); + mat.initial_spin[1] = u.at(1); + mat.initial_spin[2] = u.at(2); + + return true; + } + + } + + //-------------------------------------------------------------------- + // Keyword not found + //-------------------------------------------------------------------- + return false; + + } + +} // end of spininitialize namespace diff --git a/src/spininitialize/internal.hpp b/src/spininitialize/internal.hpp new file mode 100644 index 000000000..d6c0b0af6 --- /dev/null +++ b/src/spininitialize/internal.hpp @@ -0,0 +1,178 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +#ifndef SPININITIALIZE_INTERNAL_H_ +#define SPININITIALIZE_INTERNAL_H_ +// +//--------------------------------------------------------------------- +// This header file defines shared internal data structures and +// functions for the spininitialize module. These functions and +// variables should not be accessed outside of this module. +//--------------------------------------------------------------------- + +// C++ standard library headers +#include +#include + +// Vampire headers +#include "spininitialize.hpp" +#include "random.hpp" + +// spininitialize module headers +#include "internal.hpp" + +namespace spininitialize{ + + namespace internal{ + + //------------------------------------------------------------------------- + // Internal data type definitions + //------------------------------------------------------------------------- + + //----------------------------------------------------------------------------- + // Enumeration of the different supported initial spin textures. + // + // Each material has exactly one texture, selected via + // material[#]:initial-spin-direction in the material file (see + // interface.cpp). The texture value determines which of the + // "individual texture functions" declared below (and implemented in + // textures.cpp / vector_field.cpp) is used to compute the initial + // spin direction of each atom of that material. + //----------------------------------------------------------------------------- + enum spin_texture_t{ + uniform_vector = 0, // single user-defined unit vector (default (0,0,1)) + random = 1, // spins initialised to random directions (infinite temperature) + domain_wall = 2, // 1D tanh-profile domain wall along x, y or z + skyrmion = 3, // radially symmetric skyrmion texture in the x-y plane + spin_spiral = 4, // helical spin spiral propagating along x, y or z + vortex = 5, // in-plane curling vortex texture in the x-y plane + vector_field = 6 // texture interpolated from a user-supplied vector field file + }; + + //----------------------------------------------------------------------------- + // A single point of a user-defined vector field + // (x,y,z) are fractional coordinates of the system size [0:1] + // (mx,my,mz) is the (not necessarily normalised) spin direction at that point + //----------------------------------------------------------------------------- + class field_point_t{ + public: + double x,y,z; + double mx,my,mz; + field_point_t(): x(0.0), y(0.0), z(0.0), mx(0.0), my(0.0), mz(1.0) {} + }; + + //----------------------------------------------------------------------------- + // internal materials class for storing material spin initialisation parameters + //----------------------------------------------------------------------------- + class mp_t{ + + private: + + public: + + //------------------------------ + // material parameter variables + //------------------------------ + + // type of spin texture for this material + spin_texture_t texture; + + // uniform_vector: direction of the spins + double initial_spin[3]; + + // domain_wall: axis along which the wall varies (0=x, 1=y, 2=z) + // spin_spiral: axis along which the spiral propagates (0=x, 1=y, 2=z) + int axis; + + // domain_wall: centre[0] = position of the wall centre (fraction of system size along axis) + // skyrmion, vortex: centre[0],centre[1] = centre of the texture in the x-y plane (fraction of system size) + double centre[2]; + + // domain_wall: width of the wall (fraction of system size along axis) + // skyrmion, vortex: radius of the texture (fraction of system size) + // spin_spiral: wavelength of the spiral (fraction of system size along axis) + double width; + + // skyrmion, vortex: sense of in-plane rotation (+1 or -1) + int chirality; + + // skyrmion, vortex: out-of-plane direction of the core (+1 or -1) + int polarity; + + // vector_field: index into internal::vector_field_data / internal::vector_field_filenames + int vector_field_id; + + // constructor + mp_t (const unsigned int max_materials = 100): + texture(uniform_vector), + axis(0), + width(0.1), + chirality(1), + polarity(1), + vector_field_id(-1) + { + initial_spin[0] = 0.0; + initial_spin[1] = 0.0; + initial_spin[2] = 1.0; + centre[0] = 0.5; + centre[1] = 0.5; + }; // end of constructor + + }; // end of internal::mp class + + //------------------------------------------------------------------------- + // Internal shared variables + //------------------------------------------------------------------------- + + extern bool enabled; // bool to enable module + + // grain-level magnetisation mode (one of the grain_magnetisation_mode_t + // values above), set via spininitialize::set_grain_magnetisation_mode() + extern int grain_magnetisation_mode; + + extern std::vector mp; // array of material spin initialisation properties + + // cache of loaded vector field files, indexed by internal::mp[mat].vector_field_id + extern std::vector vector_field_filenames; + extern std::vector< std::vector > vector_field_data; + + //------------------------------------------------------------------------- + // Internal function declarations + //------------------------------------------------------------------------- + + // split a comma-separated list of values into trimmed string tokens + std::vector split_csv(const std::string& value); + + // load (or retrieve from cache) a vector field file, returning its id + int get_vector_field_id(const std::string& filename, const int line); + + // normalise a 3-vector in place; returns false if the vector has zero length + bool normalise(double& sx, double& sy, double& sz); + + // dispatch function returning the initial spin direction (unnormalised) for a given material and fractional position + void get_spin_direction(const int mat, const double fx, const double fy, const double fz, + double& sx, double& sy, double& sz, MTRand& prng); + + // individual texture functions, all working in fractional coordinates [0:1] + void uniform_vector_spin(const mp_t& mat, double& sx, double& sy, double& sz); + void random_spin(MTRand& prng, double& sx, double& sy, double& sz); + void domain_wall_spin(const mp_t& mat, const double fx, const double fy, const double fz, double& sx, double& sy, double& sz); + void skyrmion_spin(const mp_t& mat, const double fx, const double fy, const double fz, double& sx, double& sy, double& sz); + void spin_spiral_spin(const mp_t& mat, const double fx, const double fy, const double fz, double& sx, double& sy, double& sz); + void vortex_spin(const mp_t& mat, const double fx, const double fy, const double fz, double& sx, double& sy, double& sz); + void vector_field_spin(const mp_t& mat, const double fx, const double fy, const double fz, double& sx, double& sy, double& sz); + + } // end of internal namespace + +} // end of spininitialize namespace + +#endif //SPININITIALIZE_INTERNAL_H_ diff --git a/src/spininitialize/makefile b/src/spininitialize/makefile new file mode 100644 index 000000000..14fe8d079 --- /dev/null +++ b/src/spininitialize/makefile @@ -0,0 +1,15 @@ +#-------------------------------------------------------------- +# Makefile for spininitialize module +#-------------------------------------------------------------- + +# List module object filenames +spininitialize_objects =\ +data.o \ +initialize.o \ +interface.o \ +parse.o \ +textures.o \ +vector_field.o + +# Append module objects to global tree +OBJECTS+=$(addprefix obj/spininitialize/,$(spininitialize_objects)) diff --git a/src/spininitialize/parse.cpp b/src/spininitialize/parse.cpp new file mode 100644 index 000000000..cff842061 --- /dev/null +++ b/src/spininitialize/parse.cpp @@ -0,0 +1,169 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include +#include +#include + +// Vampire headers +#include "spininitialize.hpp" +#include "errors.hpp" +#include "vio.hpp" + +// spininitialize module headers +#include "internal.hpp" + +namespace spininitialize{ + + namespace internal{ + + //----------------------------------------------------------------------------- + // Split a comma-separated string into trimmed tokens + //----------------------------------------------------------------------------- + std::vector split_csv(const std::string& value){ + + std::vector tokens; + std::stringstream ss(value); + std::string token; + + // std::getline with a ',' delimiter splits "value" into substrings + // between commas; each substring may still have leading/trailing + // spaces (e.g. "skyrmion, 0.5, 0.5" -> "skyrmion", " 0.5", " 0.5") + while(std::getline(ss, token, ',')){ + // trim leading and trailing whitespace + const std::string whitespace = " \t\r\n"; + size_t start = token.find_first_not_of(whitespace); + if(start == std::string::npos){ + // token was empty or all whitespace + tokens.push_back(""); + continue; + } + size_t end = token.find_last_not_of(whitespace); + tokens.push_back(token.substr(start, end - start + 1)); + } + + return tokens; + + } + + //----------------------------------------------------------------------------- + // Normalise a 3-vector in place. Returns false if the vector has zero length, + // in which case it is left unchanged. + //----------------------------------------------------------------------------- + bool normalise(double& sx, double& sy, double& sz){ + + // |s| = sqrt(sx^2 + sy^2 + sz^2) + const double mod_s = std::sqrt(sx*sx + sy*sy + sz*sz); + + // guard against dividing by zero for a degenerate (zero-length) + // vector; the caller decides what to do in this case + if(mod_s < 1.0e-12) return false; + + sx /= mod_s; + sy /= mod_s; + sz /= mod_s; + + return true; + + } + + //----------------------------------------------------------------------------- + // Load a vector field file containing rows of "x,y,z,mx,my,mz" where x,y,z are + // fractional coordinates of the system size [0:1] and mx,my,mz is the (not + // necessarily normalised) spin direction at that point. Files are cached so + // that multiple materials referencing the same file only load it once. + //----------------------------------------------------------------------------- + int get_vector_field_id(const std::string& filename, const int line){ + + // check cache for an already-loaded file: if another material + // already requested this filename, reuse its data rather than + // reading and storing the file twice + for(size_t i = 0; i < vector_field_filenames.size(); i++){ + if(vector_field_filenames[i] == filename) return int(i); + } + + // open the file + std::ifstream ifile(filename.c_str()); + + if(!ifile.is_open()){ + terminaltextcolor(RED); + std::cerr << "Error on line " << line << " of material file - vector field file \'" << filename << "\' could not be opened." << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + + std::vector points; + + // read the file one line at a time, expecting each non-comment, + // non-blank line to contain "x,y,z,mx,my,mz" + std::string line_str; + while(std::getline(ifile, line_str)){ + + // skip empty lines and comments (lines starting with '#') + std::vector tokens = split_csv(line_str); + if(tokens.size() == 1 && tokens[0].empty()) continue; + if(tokens[0].empty()) continue; + if(tokens[0][0] == '#') continue; + + if(tokens.size() != 6){ + terminaltextcolor(RED); + std::cerr << "Error - vector field file \'" << filename << "\' contains an invalid line (expected 6 comma-separated values \'x,y,z,mx,my,mz\'): \'" << line_str << "\'" << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + + // x,y,z are fractional coordinates (0-1) of the point within + // the system; mx,my,mz is the spin direction at that point, + // which need not be normalised by the user + field_point_t point; + point.x = atof(tokens[0].c_str()); + point.y = atof(tokens[1].c_str()); + point.z = atof(tokens[2].c_str()); + point.mx = atof(tokens[3].c_str()); + point.my = atof(tokens[4].c_str()); + point.mz = atof(tokens[5].c_str()); + + // normalise the supplied direction vector to a unit vector + if(!normalise(point.mx, point.my, point.mz)){ + terminaltextcolor(RED); + std::cerr << "Error - vector field file \'" << filename << "\' contains a point with a zero-length direction vector: \'" << line_str << "\'" << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + + points.push_back(point); + + } + + if(points.empty()){ + terminaltextcolor(RED); + std::cerr << "Error on line " << line << " of material file - vector field file \'" << filename << "\' contains no data points." << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + + // add to cache and return the new id, which is stored in + // mat.vector_field_id and used by vector_field_spin() to look up + // vector_field_data[id] + vector_field_filenames.push_back(filename); + vector_field_data.push_back(points); + + return int(vector_field_filenames.size() - 1); + + } + + } // end of internal namespace + +} // end of spininitialize namespace diff --git a/src/spininitialize/textures.cpp b/src/spininitialize/textures.cpp new file mode 100644 index 000000000..7c962c9df --- /dev/null +++ b/src/spininitialize/textures.cpp @@ -0,0 +1,302 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include + +// Vampire headers +#include "spininitialize.hpp" +#include "random.hpp" + +// spininitialize module headers +#include "internal.hpp" + +namespace spininitialize{ + + namespace internal{ + + namespace { + const double pi = 3.14159265358979323846; + } + + //----------------------------------------------------------------------------- + // uniform_vector: spins point along the user-defined direction mat.initial_spin + //----------------------------------------------------------------------------- + void uniform_vector_spin(const mp_t& mat, double& sx, double& sy, double& sz){ + + // every atom of this material gets the same fixed direction, as + // entered by the user (e.g. "0,0,1" or "[110]"); this has already + // been normalised to a unit vector by check_for_valid_unit_vector + // when the input file was read + sx = mat.initial_spin[0]; + sy = mat.initial_spin[1]; + sz = mat.initial_spin[2]; + + } + + //----------------------------------------------------------------------------- + // random: spins point in a random direction, uniformly distributed on the unit + // sphere (via normalised gaussian components) + //----------------------------------------------------------------------------- + void random_spin(MTRand& prng, double& sx, double& sy, double& sz){ + + // drawing each Cartesian component independently from a gaussian + // distribution and then normalising the resulting vector (done by + // the caller, get_spin_direction) gives a direction that is + // uniformly distributed over the surface of the unit sphere. This + // is NOT the same as drawing sx,sy,sz uniformly from [-1,1], which + // would bias spins towards the corners of the cube. + sx = mtrandom::gaussianc(prng); + sy = mtrandom::gaussianc(prng); + sz = mtrandom::gaussianc(prng); + + } + + //----------------------------------------------------------------------------- + // domain_wall: 1D tanh-profile Neel-type domain wall. + // + // Along the chosen axis the spin rotates from + (far below the wall + // centre) through the rotation axis itself (at the wall centre) to + // - (far above the wall centre). The rotation takes place in the + // plane spanned by and the next axis in cyclic order (x->y->z->x), + // i.e. for domain-wall-x the rotation plane is x-z, for domain-wall-y it + // is y-x and for domain-wall-z it is z-y. + // + // mat.centre[0] is the position of the wall centre (fraction of system + // size along axis) and mat.width controls the sharpness of the wall + // (fraction of system size); smaller values give a sharper wall. + //----------------------------------------------------------------------------- + void domain_wall_spin(const mp_t& mat, const double fx, const double fy, const double fz, double& sx, double& sy, double& sz){ + + // pick out the fractional coordinate along the wall's propagation + // axis (0=x, 1=y, 2=z), as set by domain-wall-x/y/z + const double pos[3] = {fx, fy, fz}; + + // t is a dimensionless "distance from the wall centre" measured in + // units of the wall width: t = 0 at the centre, |t| >> 1 far away. + // mat.centre[0] and mat.width are both fractions of the system size. + const double t = (pos[mat.axis] - mat.centre[0]) / mat.width; + + // standard tanh-profile domain wall: the polar angle theta of the + // spin (measured from the +axis direction) follows + // theta(t) = 2*atan(exp(t)) + // which smoothly varies from 0 (spin along +axis, t -> -inf) + // through pi/2 (spin perpendicular to axis, t = 0, the wall centre) + // to pi (spin along -axis, t -> +inf). + const double theta = 2.0 * atan(exp(t)); + + double m[3] = {0.0, 0.0, 0.0}; + m[mat.axis] = cos(theta); // component along the wall axis + m[(mat.axis+2)%3] = sin(theta); // in-plane rotation component + + sx = m[0]; + sy = m[1]; + sz = m[2]; + + } + + //----------------------------------------------------------------------------- + // skyrmion: radially symmetric "cone" skyrmion in the x-y plane. + // + // mat.centre = (cx, cy) is the centre of the skyrmion (fraction of system + // size), mat.width is its radius (fraction of system size). mat.polarity + // (+/-1) sets the out-of-plane direction of the core, and mat.chirality + // (+/-1) selects between a Neel-type (radial, +1) and Bloch-type + // (azimuthal, -1) in-plane rotation sense. + //----------------------------------------------------------------------------- + void skyrmion_spin(const mp_t& mat, const double fx, const double fy, const double fz, double& sx, double& sy, double& sz){ + + // (dx,dy) is the in-plane displacement of this atom from the + // skyrmion centre (cx,cy), in fractional coordinates + const double dx = fx - mat.centre[0]; + const double dy = fy - mat.centre[1]; + + // r is the radial distance from the centre, phi is the azimuthal + // (in-plane) angle around the centre + const double r = sqrt(dx*dx + dy*dy); + const double phi = atan2(dy, dx); + + const double R = mat.width; // skyrmion radius (fraction of system size) + + // theta(r) is the polar angle of the spin away from the z axis. + // It varies linearly from pi at the core (r=0, spin antiparallel to + // the core direction set below) to 0 at the skyrmion edge (r=R) + // and beyond, where spins simply point along +/-z (the background). + // std::min(r/R, 1.0) clamps theta to 0 outside the skyrmion radius. + const double theta = pi * (1.0 - std::min(r / R, 1.0)); + + // gamma is an extra in-plane rotation applied to the azimuthal + // angle phi, which sets the "type" of skyrmion: + // chirality = +1 -> gamma = 0 : Neel-type (radial in-plane spins) + // chirality = -1 -> gamma = pi/2 : Bloch-type (azimuthal in-plane spins) + const double gamma = (mat.chirality >= 0) ? 0.0 : (0.5 * pi); + + // convert (theta, phi+gamma) spherical angles to a Cartesian unit + // vector. At the core (theta=pi) sz = +polarity, i.e. the core + // points along the chosen polarity direction; far from the core + // (theta=0) sz = -polarity, the opposite (background) direction. + sx = sin(theta) * cos(phi + gamma); + sy = sin(theta) * sin(phi + gamma); + sz = -double(mat.polarity) * cos(theta); + + } + + //----------------------------------------------------------------------------- + // spin_spiral: helical spin spiral propagating along the chosen axis. + // + // The spin rotates in the plane spanned by the two axes perpendicular to + // the propagation axis (cyclically: x propagation -> y-z rotation plane, + // y -> z-x, z -> x-y), completing one full turn over mat.width (the + // wavelength, as a fraction of the system size along the axis). + //----------------------------------------------------------------------------- + void spin_spiral_spin(const mp_t& mat, const double fx, const double fy, const double fz, double& sx, double& sy, double& sz){ + + // fractional coordinate along the propagation axis (0=x, 1=y, 2=z) + const double pos[3] = {fx, fy, fz}; + + // k is the phase of the spiral at this position: it increases by + // 2*pi (one full rotation of the spin) every time pos[axis] + // advances by mat.width (the wavelength, as a fraction of the + // system size along the propagation axis) + const double k = 2.0 * pi * pos[mat.axis] / mat.width; + + // the spin has no component along the propagation axis; instead it + // rotates within the plane perpendicular to it, tracing out a helix + // as a function of position + double m[3] = {0.0, 0.0, 0.0}; + m[mat.axis] = 0.0; + m[(mat.axis+1)%3] = cos(k); + m[(mat.axis+2)%3] = sin(k); + + sx = m[0]; + sy = m[1]; + sz = m[2]; + + } + + //----------------------------------------------------------------------------- + // vortex: in-plane curling vortex in the x-y plane, with an optional + // out-of-plane core. + // + // mat.centre = (cx, cy) is the centre of the vortex (fraction of system + // size). mat.width is the core radius (fraction of system size); the + // out-of-plane component of the core decays as a gaussian with this + // radius and vanishes for mat.width = 0 (a pure in-plane vortex). + // mat.polarity (+/-1) sets the out-of-plane core direction and + // mat.chirality (+/-1) sets the sense (clockwise/anticlockwise) of the + // in-plane curling. + //----------------------------------------------------------------------------- + void vortex_spin(const mp_t& mat, const double fx, const double fy, const double fz, double& sx, double& sy, double& sz){ + + // (dx,dy) is the in-plane displacement of this atom from the + // vortex centre (cx,cy), in fractional coordinates + const double dx = fx - mat.centre[0]; + const double dy = fy - mat.centre[1]; + + // r is the radial distance from the centre, phi is the azimuthal + // (in-plane) angle around the centre + const double r = sqrt(dx*dx + dy*dy); + const double phi = atan2(dy, dx); + + // out-of-plane component mz: zero everywhere for a pure in-plane + // vortex (mat.width = 0). For mat.width > 0, mz follows a gaussian + // that peaks at the vortex centre (mz = +/-1 at r=0, set by + // mat.polarity) and decays to zero over a radius ~ mat.width, + // modelling the localised out-of-plane core seen in real vortices. + double mz = 0.0; + if(mat.width > 0.0){ + const double u = r / mat.width; + mz = double(mat.polarity) * exp(-u*u); + } + + // the remaining in-plane magnitude, so that (in_plane)^2 + mz^2 = 1 + const double in_plane = sqrt(std::max(0.0, 1.0 - mz*mz)); + + // the in-plane component curls azimuthally around the centre + // (tangential to circles of constant r), i.e. perpendicular to the + // radial direction (cos(phi), sin(phi)). mat.chirality = +1 gives + // anticlockwise curling, -1 gives clockwise. + sx = -sin(phi) * double(mat.chirality) * in_plane; + sy = cos(phi) * double(mat.chirality) * in_plane; + sz = mz; + + } + + //----------------------------------------------------------------------------- + // Master dispatch function returning the (unnormalised) initial spin + // direction for material mat at fractional coordinates (fx,fy,fz). + //----------------------------------------------------------------------------- + void get_spin_direction(const int mat, const double fx, const double fy, const double fz, + double& sx, double& sy, double& sz, MTRand& prng){ + + // default direction for materials with no spininitialize entry + // (e.g. if the mp array was never resized for this material because + // it has no initial-spin-direction keyword in the material file) + if(mat < 0 || mat >= int(mp.size())){ + sx = 0.0; + sy = 0.0; + sz = 1.0; + return; + } + + const mp_t& material = mp[mat]; + + // dispatch to the appropriate texture function based on the + // texture type selected for this material when the input file was + // parsed (see interface.cpp) + switch(material.texture){ + + case random: + random_spin(prng, sx, sy, sz); + break; + + case domain_wall: + domain_wall_spin(material, fx, fy, fz, sx, sy, sz); + break; + + case skyrmion: + skyrmion_spin(material, fx, fy, fz, sx, sy, sz); + break; + + case spin_spiral: + spin_spiral_spin(material, fx, fy, fz, sx, sy, sz); + break; + + case vortex: + vortex_spin(material, fx, fy, fz, sx, sy, sz); + break; + + case vector_field: + vector_field_spin(material, fx, fy, fz, sx, sy, sz); + break; + + case uniform_vector: + default: + uniform_vector_spin(material, sx, sy, sz); + break; + + } + + // normalise the resulting direction vector, falling back to +z if it is + // degenerate (e.g. exactly at a singular point of a texture) + if(!normalise(sx, sy, sz)){ + sx = 0.0; + sy = 0.0; + sz = 1.0; + } + + } + + } // end of internal namespace + +} // end of spininitialize namespace diff --git a/src/spininitialize/vector_field.cpp b/src/spininitialize/vector_field.cpp new file mode 100644 index 000000000..1b373d4f8 --- /dev/null +++ b/src/spininitialize/vector_field.cpp @@ -0,0 +1,96 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include + +// Vampire headers +#include "spininitialize.hpp" + +// spininitialize module headers +#include "internal.hpp" + +namespace spininitialize{ + + namespace internal{ + + //----------------------------------------------------------------------------- + // vector_field: spin direction interpolated from a user-supplied list of + // (x,y,z,mx,my,mz) points (fractional coordinates and unit vectors) using + // inverse-distance weighting over all points in the field. + // + // If the atom coincides (to within a small tolerance) with one of the + // supplied points, that point's direction is used directly. + //----------------------------------------------------------------------------- + void vector_field_spin(const mp_t& mat, const double fx, const double fy, const double fz, double& sx, double& sy, double& sz){ + + // look up the list of (x,y,z,mx,my,mz) points loaded from the field file + // for this material (cached in vector_field_data, see parse.cpp) + const std::vector& points = vector_field_data[mat.vector_field_id]; + + // inverse-distance weighting (IDW): each field point contributes a + // weight w_i = 1 / d_i^power, where d_i is the distance from the atom + // to that point. power = 2 gives an "inverse-square" weighting, so + // nearby points dominate strongly over distant ones. + const double power = 2.0; // inverse-distance weighting exponent + const double tol = 1.0e-12; // distance^2 below which a point is treated as coincident + + // running totals for the weighted sum: sum_w accumulates the + // normalisation (sum of all weights), sum_m{x,y,z} accumulate the + // weighted vector components + double sum_w = 0.0; + double sum_mx = 0.0; + double sum_my = 0.0; + double sum_mz = 0.0; + + // loop over every point in the field and accumulate its contribution + for(size_t i = 0; i < points.size(); i++){ + + // displacement (in fractional coordinates) from the field point to this atom + const double dx = fx - points[i].x; + const double dy = fy - points[i].y; + const double dz = fz - points[i].z; + + // squared distance (avoids an unnecessary sqrt in the common case) + const double d2 = dx*dx + dy*dy + dz*dz; + + // if the atom lies (almost) exactly on a field point, use its + // direction directly to avoid dividing by (near) zero + if(d2 < tol){ + sx = points[i].mx; + sy = points[i].my; + sz = points[i].mz; + return; + } + + // weight w_i = d_i^(-power) = (d2)^(-power/2) + const double w = 1.0 / pow(d2, 0.5*power); + + sum_w += w; + sum_mx += w * points[i].mx; + sum_my += w * points[i].my; + sum_mz += w * points[i].mz; + + } + + // weighted average direction: sum(w_i * m_i) / sum(w_i) + // this is not generally a unit vector, so it is renormalised by the + // caller (see get_spin_direction in textures.cpp) + sx = sum_mx / sum_w; + sy = sum_my / sum_w; + sz = sum_mz / sum_w; + + } + + } // end of internal namespace + +} // end of spininitialize namespace diff --git a/src/spinlattice/data.cpp b/src/spinlattice/data.cpp new file mode 100644 index 000000000..0d31c1769 --- /dev/null +++ b/src/spinlattice/data.cpp @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Mara Strungaru 2022. All rights reserved. +// +// Email: mara.strungaru@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include + +// Vampire headers +#include "sld.hpp" +#include "material.hpp" + +// sld module headers +#include "internal.hpp" + +namespace sld{ + + //------------------------------------------------------------------------------ + // Externally visible variables + //------------------------------------------------------------------------------ + bool enabled = false; + double var_test=0; + double spin_temperature; + double lattice_temperature; + double J_eff; + double C_eff; + bool suzuki_trotter_parallel_initialized = false; + + namespace internal{ + + //------------------------------------------------------------------------ + // Shared variables inside sld module + //------------------------------------------------------------------------ + + //Spin-lattice coupling variables + bool linear_pump_enabled; + double phonon_frequency; + double phonon_force_amplitude[3]; + double phonon_wavevector[3]; + double phonon_pulse_start_time; + double phonon_pulse_end_time; + double phonon_wave_lambda[3]; + double phonon_wave_direction[3]; + std::vector coupling_field_x; + std::vector coupling_field_y; + std::vector coupling_field_z; + + + bool enabled; // bool to enable module + + std::vector mp; // array of material properties + + double r_cut_pot; // mechanical potential cutoff + double r_cut_fields; + + double dr_init; // initial conditions + double th_velo; + + double morse_beta; + double morse_factor; + double alpha_m; + double r0_m; + double morse_D; + + + bool morse; + bool harmonic; //flag for harmonic potential + bool pseudodipolar; + bool full_neel; + + + //initial sld neighbor list + //std::vector sld_neighbour_list_start_index; + //std::vector sld_neighbour_list_end_index; + //std::vector sld_neighbour_list_array; + + std::vector x0_coord_array; + std::vector y0_coord_array; + std::vector z0_coord_array; + + + std::vector x_coord_storage_array; + std::vector y_coord_storage_array; + std::vector z_coord_storage_array; + + std::vector forces_array_x; + std::vector forces_array_y; + std::vector forces_array_z; + + std::vector fields_array_x; + std::vector fields_array_y; + std::vector fields_array_z; + + std::vector velo_array_x; + std::vector velo_array_y; + std::vector velo_array_z; + + std::vector potential_eng; + std::vector sumJ; + std::vector sumC; + std::vector exch_eng; + std::vector coupl_eng; + + std::vector test_atom_list; //Core atoms of each octant + + //MPI variables + std::vector > c_octants; //Core atoms of each octant + std::vector > b_octants; //Boundary atoms of each octant + std::vector all_atoms_octant_start_index; + std::vector all_atoms_octant_end_index; + std::vector all_atoms_octant; + + + } // end of internal namespace + +} // end of sld namespace diff --git a/src/spinlattice/energy.cpp b/src/spinlattice/energy.cpp new file mode 100644 index 000000000..c585e44dd --- /dev/null +++ b/src/spinlattice/energy.cpp @@ -0,0 +1,130 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Mara Strungaru 2022. All rights reserved. +// +// Email: mara.strungaru@york.ac.uk +// +// implementation based on the paper Phys. Rev. B 103, 024429, (2021) M.Strungaru, M.O.A. Ellis et al +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include + +// Vampire headers +#include "atoms.hpp" +#include "create.hpp" +#include "constants.hpp" +#include "material.hpp" +#include "sld.hpp" + +// sld module headers +#include "internal.hpp" + +namespace sld{ + + double compute_potential_energy(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& type_array){ + +// + double pot=0.0; + for (int at=start_index;at& type_array, // type for atom + std::vector& velo_array_x, // coord vectors for atoms + std::vector& velo_array_y, + std::vector& velo_array_z){ + +// + double kinetic=0; + for (int at=start_index;at& sumJ){ + + double sum1J=0.0; + for (int at=start_index;at& sumC){ + + double sum1C=0.0; + for (int at=start_index;at +#include +#include +#include +#include + +// Vampire headers +#include "anisotropy.hpp" +#include "atoms.hpp" +#include "create.hpp" +#include "sld.hpp" +#include "sim.hpp" + +// sld module headers +#include "internal.hpp" + +namespace sld{ + + void compute_fields(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& x_coord_array, // coord vectors for atoms + const std::vector& y_coord_array, + const std::vector& z_coord_array, + const std::vector& x_spin_array, // coord vectors for atoms + const std::vector& y_spin_array, + const std::vector& z_spin_array, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z, + std::vector& fields_array_x, // vectors for fields + std::vector& fields_array_y, + std::vector& fields_array_z){ + + + internal::compute_exchange(start_index, end_index, + neighbour_list_start_index, neighbour_list_end_index, + type_array, neighbour_list_array, + x_coord_array, y_coord_array, z_coord_array, + x_spin_array, y_spin_array, z_spin_array, + forces_array_x, forces_array_y, forces_array_z, + fields_array_x, fields_array_y, fields_array_z); + + if(sld::internal::pseudodipolar) internal::compute_sld_coupling(start_index, end_index, + neighbour_list_start_index, neighbour_list_end_index, + type_array, neighbour_list_array, + x_coord_array, y_coord_array, z_coord_array, + x_spin_array, y_spin_array, z_spin_array, + forces_array_x, forces_array_y, forces_array_z, + fields_array_x, fields_array_y, fields_array_z); + if(sld::internal::full_neel) internal::compute_sld_coupling_neel(start_index, end_index, + neighbour_list_start_index, neighbour_list_end_index, + type_array, neighbour_list_array, + x_coord_array, y_coord_array, z_coord_array, + x_spin_array, y_spin_array, z_spin_array, + forces_array_x, forces_array_y, forces_array_z, + fields_array_x, fields_array_y, fields_array_z); + + + + + //the following functions are taken from previous functions in VAMPIRE + + + // add external fields + // only after equilibration + + if (sim::time > sim::equilibration_time) { + + const double Hx=sim::H_vec[0]*sim::H_applied; + const double Hy=sim::H_vec[1]*sim::H_applied; + const double Hz=sim::H_vec[2]*sim::H_applied; + //add all the external fields to the fields array in sld_neighbour_list_array + for(int i=start_index;i& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& x_coord_array, // coord vectors for atoms + const std::vector& y_coord_array, + const std::vector& z_coord_array, + const std::vector& x_spin_array, // coord vectors for atoms + const std::vector& y_spin_array, + const std::vector& z_spin_array, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z, + std::vector& fields_array_x, // vectors for fields + std::vector& fields_array_y, + std::vector& fields_array_z){ + + double rx, ry, rz; + double dx, dy, dz; + double sx, sy, sz; + double sjx, sjy,sjz; + double si_dot_sj; + double fx = 0.0, fy = 0.0, fz = 0.0; + double hx = 0.0, hy = 0.0, hz = 0.0; + double rji_sqr, rji, inv_rji; //, inv_rji2; + double y, f_exch, energy = 0.0; + //double exch_J0 = sld::internal::mp[0].J0_ms.get(); //7034.8836847351113; // + //double exch_J0_prime = sld::internal::mp[0].J0_prime.get()/1.602176634e-19; //in J 0.72320000000000007 ; + double J; + int j; + double r_sqr_cut=sld::internal::r_cut_fields*sld::internal::r_cut_fields; + //double oneover3=1.0/3.0; + double exch_inv_rcut=1.0/sld::internal::r_cut_fields; + double sumJ=0.0; + + for(int i=start_index;i& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& x_coord_array, // coord vectors for atoms + const std::vector& y_coord_array, + const std::vector& z_coord_array, + const std::vector& x_spin_array, // coord vectors for atoms + const std::vector& y_spin_array, + const std::vector& z_spin_array, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z, + std::vector& fields_array_x, // vectors for fields + std::vector& fields_array_y, + std::vector& fields_array_z){ + + + double rx, ry, rz; + double dx, dy, dz; + double sx, sy, sz; + double sjx, sjy,sjz; + double si_dot_sj; + double fc_x = 0.0, fc_y = 0.0, fc_z = 0.0; + double hc_x = 0.0, hc_y = 0.0, hc_z = 0.0; + double rji_sqr, rji, inv_rji, inv_rji2, inv_rji4, inv_rji6; + double sj_dot_rji, si_dot_rji; + double energy_c; + int j; //, count_int; + + double r_sqr_cut=sld::internal::r_cut_fields*sld::internal::r_cut_fields; + double oneover3=1.0/3.0; + double sumC; + + for(int i=start_index;i& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& x_coord_array, // coord vectors for atoms + const std::vector& y_coord_array, + const std::vector& z_coord_array, + const std::vector& x_spin_array, // coord vectors for atoms + const std::vector& y_spin_array, + const std::vector& z_spin_array, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z, + std::vector& fields_array_x, // vectors for fields + std::vector& fields_array_y, + std::vector& fields_array_z){ + + + double rx, ry, rz; + double dx, dy, dz; + double sx, sy, sz; + double sjx, sjy,sjz; + double si_dot_sj; + double fc_x = 0.0, fc_y = 0.0, fc_z = 0.0; + double hc_x = 0.0, hc_y = 0.0, hc_z = 0.0; + double rji_sqr, rji, inv_rji, inv_rji2, inv_rji4, inv_rji6; + double sj_dot_rji, si_dot_rji; + double energy_c; + int j; //, count_int; + + double r_sqr_cut=sld::internal::r_cut_fields*sld::internal::r_cut_fields; + double oneover3=1.0/3.0; + //double sumC; + + for(int i=start_index;i +#include +#include +#include + +// Vampire headers +#include "sld.hpp" +#include "create.hpp" +#include "sim.hpp" +#include "errors.hpp" +#include "material.hpp" +#include + +// sld module headers +#include "internal.hpp" + + +namespace sld{ + + void compute_forces(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& x0_coord_array, // coord vectors for atoms + const std::vector& y0_coord_array, + const std::vector& z0_coord_array, + const std::vector& x_coord_array, // coord vectors for atoms + const std::vector& y_coord_array, + const std::vector& z_coord_array, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z, + std::vector& potential_eng){ + + + + // calculate harmonic potential forces + if(sld::internal::harmonic){ + + //std::cout<<"inside compute function harmonic" <& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& x0_coord_array, // coord vectors for atoms + const std::vector& y0_coord_array, + const std::vector& z0_coord_array, + const std::vector& x_coord_array, // coord vectors for atoms + const std::vector& y_coord_array, + const std::vector& z_coord_array, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z, + std::vector& potential_eng){ + + + + double rx, ry, rz; + double rx0, ry0, rz0; + double dx, dy, dz; + double dx0, dy0, dz0; + double fx = 0.0, fy = 0.0, fz = 0.0; + double rji_sqr, rji, rji0, inv_rji; + int j; + //int total_int; + double r_sqr_cut=sld::internal::r_cut_pot*sld::internal::r_cut_pot; + double energy; + + + + for(int i=start_index;i< end_index; ++i){ + + fx = 0.0; + fy = 0.0; + fz = 0.0; + energy=0.0; + //total_int=0; + + rx = x_coord_array[i]; + ry = y_coord_array[i]; + rz = z_coord_array[i]; + rx0 = x0_coord_array[i]; + ry0 = y0_coord_array[i]; + rz0 = z0_coord_array[i]; + + //note for sld_neighbour_list_array + // int nbr_end = neighbour_list_end_index[i]; + //for(int i=start_index;i<= end_index; ++i) + //for( int n = nbr_start; n <=nbr_end; ++n) + + + + int nbr_start = neighbour_list_start_index[i]; + int nbr_end = neighbour_list_end_index[i]+1; + + for( int n = nbr_start; n < nbr_end; ++n){ + j = neighbour_list_array[n]; + + if ( j != i){ + dx = -x_coord_array[j] + rx; + dy = -y_coord_array[j] + ry; + dz = -z_coord_array[j] + rz; + dx0 = -x0_coord_array[j] + rx0; + dy0 = -y0_coord_array[j] + ry0; + dz0 = -z0_coord_array[j] + rz0; + + dx = sld::PBC_wrap( dx, cs::system_dimensions[0], cs::pbc[0]); + dy = sld::PBC_wrap( dy, cs::system_dimensions[1], cs::pbc[1]); + dz = sld::PBC_wrap( dz, cs::system_dimensions[2], cs::pbc[2]); + dx0 = sld::PBC_wrap( dx0, cs::system_dimensions[0], cs::pbc[0]); + dy0 = sld::PBC_wrap( dy0, cs::system_dimensions[1], cs::pbc[1]); + dz0 = sld::PBC_wrap( dz0, cs::system_dimensions[2], cs::pbc[2]); + + + + rji_sqr = dx*dx + dy*dy + dz*dz; + + if( rji_sqr < r_sqr_cut){ + + //total_int++; + + + rji = sqrt(rji_sqr); + rji0 = sqrt(dx0*dx0 + dy0*dy0 + dz0*dz0); + inv_rji = 1.0/ rji; + + energy += (rji-rji0)*(rji-rji0); + + + fx -= (rji-rji0)*dx*inv_rji ; //2 (rji-rj0)*dx*inv_rji -> 2 went at the end + fy -= (rji-rji0)*dy*inv_rji ; + fz -= (rji-rji0)*dz*inv_rji ; + + + } + } + } + const unsigned int imat = type_array[i]; + + double V0=sld::internal::mp[imat].V0.get(); //0.15 + + forces_array_x[i] += V0 * 2.0 * fx; + forces_array_y[i] += V0 * 2.0 * fy; + forces_array_z[i] += V0 * 2.0 * fz; + potential_eng[i] = 0.5 * V0 * energy; + + + + } + + + return; + } + +void compute_forces_morse(const int start_index, + const int end_index, // last +1 atom to be calculated + const std::vector& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& x_coord_array, // coord vectors for atoms + const std::vector& y_coord_array, + const std::vector& z_coord_array, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z, + std::vector& potential_eng){ + + + + double rx, ry, rz; + double dx, dy, dz; + double fx = 0.0, fy = 0.0, fz = 0.0; + double rji_sqr, rji, inv_rji; // rji0, + int j; //, total_int; + double r_sqr_cut=sld::internal::r_cut_pot*sld::internal::r_cut_pot; + double energy; + + double alpha_m= sld::internal::alpha_m; //1.3885; + //double r0_m= sld::internal::r0_m;//2.845; + double morse_D=sld::internal::morse_D; //0.4174;; + double morse_beta=sld::internal::morse_beta;//exp( alpha_m * r0_m); + double morse_factor = sld::internal::morse_factor; //-2.0 * morse_D * alpha_m; + + + + + for(int i=start_index;i< end_index; ++i){ + + fx = 0.0; + fy = 0.0; + fz = 0.0; + energy=0.0; + //total_int=0; + + + rx = x_coord_array[i]; + ry = y_coord_array[i]; + rz = z_coord_array[i]; + + + int nbr_start = neighbour_list_start_index[i]; + int nbr_end = neighbour_list_end_index[i]+1; + + for( int n = nbr_start; n < nbr_end; ++n){ + j = neighbour_list_array[n]; + + if ( j != i){ + dx = x_coord_array[j] -rx; + dy = y_coord_array[j]- ry; + dz = z_coord_array[j]- rz; + + dx = sld::PBC_wrap( dx, cs::system_dimensions[0], cs::pbc[0]); + dy = sld::PBC_wrap( dy, cs::system_dimensions[1], cs::pbc[1]); + dz = sld::PBC_wrap( dz, cs::system_dimensions[2], cs::pbc[2]); + + + + rji_sqr = dx*dx + dy*dy + dz*dz; + + if( rji_sqr < r_sqr_cut){ + + rji = sqrt(rji_sqr); + inv_rji = 1.0/ rji; + + double y = morse_beta * exp( - alpha_m * rji); + double f_morse = y * ( y - 1.0); + + //std::cout<<"embedded "<& x_coord_array, + const std::vector& y_coord_array, + const std::vector& z_coord_array, + std::vector& forces_array_x, + std::vector& forces_array_y, + std::vector& forces_array_z) +{ + + + // Calculate the Current Physical Time + // Converts the current step number into the actual time in seconds. + uint64_t current_step = sim::time; + double dt = mp::dt_SI; // Get the duration of one step in seconds + double current_time = static_cast(current_step) * dt; // Total elapsed time (seconds) + + + if (current_time < sld::internal::phonon_pulse_start_time || current_time > sld::internal::phonon_pulse_end_time) { + return; // The pulse is off, so do nothing. + } + + // Calculate the Raw Force for Each Atom + // Loop through each atom to calculate the + // force from the THz pulse and add it to a running total for later averaging. + double sumx = 0.0, sumy = 0.0, sumz = 0.0; + std::vector f_thz_temp_x(end_index - start_index); // Temporary storage + std::vector f_thz_temp_y(end_index - start_index); + std::vector f_thz_temp_z(end_index - start_index); + + // Pre-calculate angular frequency (ω = 2πν) for efficiency + const double twopi_niu = sld::internal::phonon_frequency * 6.28318530718; + for (int i = start_index; i < end_index; ++i) { + + + // Calculate the position-dependent part of the wave (k·r) + double kr = sld::internal::phonon_wavevector[0] * x_coord_array[i] + + sld::internal::phonon_wavevector[1] * y_coord_array[i] + + sld::internal::phonon_wavevector[2] * z_coord_array[i]; + + // Calculate the argument for the main physics equation: cos(ωt - k·r) + double arg = current_time * twopi_niu - kr; + double cos_factor = std::cos(arg); // The oscillating part of the force + double sin_factor = std::sin(arg); + + // Calculate the raw force and store it temporarily + int local_index = i - start_index; + f_thz_temp_x[local_index] = sld::internal::phonon_force_amplitude[0] * cos_factor; + f_thz_temp_y[local_index] = sld::internal::phonon_force_amplitude[1] * sin_factor; + f_thz_temp_z[local_index] = sld::internal::phonon_force_amplitude[2]; + + // Add to the running total + sumx += f_thz_temp_x[local_index]; + sumy += f_thz_temp_y[local_index]; + sumz += f_thz_temp_z[local_index]; + + + } + + // Calculate the Center-of-Mass Correction + // Calculate the average force and will subtract it from each atom's individual force. + int counter = end_index - start_index; + double avg_fx = 0.0, avg_fy = 0.0, avg_fz = 0.0; + if (counter > 0) { + avg_fx = sumx / static_cast(counter); + avg_fy = sumy / static_cast(counter); + avg_fz = sumz / static_cast(counter); + } + + + // Apply the Final, Corrected Force + // Loop through the atoms again, retrieve the + // temporarily stored raw force, subtract the average + for (int i = start_index; i < end_index; ++i) { + int local_index = i - start_index; + double corrected_fx = f_thz_temp_x[local_index] - avg_fx; + double corrected_fy = f_thz_temp_y[local_index] - avg_fy; + double corrected_fz = f_thz_temp_z[local_index] - avg_fz; + + // Add the final calculated force to the atom's total force + forces_array_x[i] += corrected_fx; + forces_array_y[i] += corrected_fy; + forces_array_z[i] += corrected_fz; + + if (i == 0 && current_step % 200 == 0) { + std::cout << " Corrected Force X : " << corrected_fx << " N" << std::endl; + + } + } + + return; +} +} +} \ No newline at end of file diff --git a/src/spinlattice/initialize.cpp b/src/spinlattice/initialize.cpp new file mode 100644 index 000000000..63d3dc0e6 --- /dev/null +++ b/src/spinlattice/initialize.cpp @@ -0,0 +1,305 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Mara Strungaru 2022. All rights reserved. +// +// Email: mara.strungaru@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers + +// Vampire headers +#include "sld.hpp" +#include "atoms.hpp" +#include "iostream" +#include "neighbours.hpp" +#include "material.hpp" +#include "constants.hpp" + +// sld module headers +#include "internal.hpp" + +namespace sld{ + + //---------------------------------------------------------------------------- + // Function to initialize sld module + //---------------------------------------------------------------------------- + void initialize(){ + + // check for sld module being enabled + if(!sld::enabled) return; + + std::cout<<"Input parameters for Spin-lattice dynamics simulations:"< epsilon) { + sld::internal::phonon_wavevector[0] = two_pi / sld::internal::phonon_wave_lambda[0] * sld::internal::phonon_wave_direction[0]; + } else { + sld::internal::phonon_wavevector[0] = 0.0; + } + + if (std::abs(sld::internal::phonon_wave_lambda[1]) > epsilon) { + sld::internal::phonon_wavevector[1] = two_pi / sld::internal::phonon_wave_lambda[1] * sld::internal::phonon_wave_direction[1]; + } else { + sld::internal::phonon_wavevector[1] = 0.0; + } + + if (std::abs(sld::internal::phonon_wave_lambda[2]) > epsilon) { + sld::internal::phonon_wavevector[2] = two_pi / sld::internal::phonon_wave_lambda[2] * sld::internal::phonon_wave_direction[2]; + } else { + sld::internal::phonon_wavevector[2] = 0.0; + } + + + + } + + + //initialise exchange, coupling parameters + sld::internal::initialise_sld_parameters(); + + //for the morse potential: + + sld::internal::alpha_m= 1.3885; + sld::internal::r0_m=2.845; + sld::internal::morse_D=0.4174;; + sld::internal::morse_beta=exp( sld::internal::alpha_m * sld::internal::r0_m); + sld::internal::morse_factor=-2.0 *sld::internal::morse_D * sld::internal::alpha_m; + + + //sld::internal::thermal_velocity(atoms::x_velo_array, atoms::y_velo_array,atoms::z_velo_array); + + + //initialise for Parallel simulations + //Initialize parallel mc variables + suzuki_trotter_parallel_initialized = false; + internal::c_octants.resize(8); + internal::b_octants.resize(8); + + internal::all_atoms_octant_start_index.reserve(8); + internal::all_atoms_octant_end_index.reserve(8); + internal::all_atoms_octant.reserve(atoms::num_atoms); + + + sld::internal::initialise_positions(sld::internal::x0_coord_array, // coord vectors for atoms + sld::internal::y0_coord_array, + sld::internal::z0_coord_array, + atoms::x_coord_array, // coord vectors for atoms + atoms::y_coord_array, + atoms::z_coord_array, + sld::internal::dr_init); + + //sld::internal::thermal_velocity(atoms::x_velo_array, atoms::y_velo_array,atoms::z_velo_array); + + + + + // sld::tests(); + + + + return; + + + } + + + namespace internal{ + + void initialise_positions(std::vector& x0_coord_array, // coord vectors for atoms + std::vector& y0_coord_array, + std::vector& z0_coord_array, + std::vector& x_coord_array, // coord vectors for atoms + std::vector& y_coord_array, + std::vector& z_coord_array, + const double dr){ + + + x0_coord_array.resize(atoms::num_atoms,0); + y0_coord_array.resize(atoms::num_atoms,0); + z0_coord_array.resize(atoms::num_atoms,0); + + + + x_coord_storage_array.resize(atoms::num_atoms,0); + y_coord_storage_array.resize(atoms::num_atoms,0); + z_coord_storage_array.resize(atoms::num_atoms,0); + + for( int i = 0; i < atoms::num_atoms; i++){ + + x0_coord_array[i]=x_coord_array[i]; + y0_coord_array[i]=y_coord_array[i]; + z0_coord_array[i]=z_coord_array[i]; + + x_coord_storage_array[i]=x_coord_array[i]; + y_coord_storage_array[i]=y_coord_array[i]; + z_coord_storage_array[i]=z_coord_array[i]; + + + + } + //std::cout<<"random positions "<& x_velo_array, // velocities vectors + std::vector& y_velo_array, + std::vector& z_velo_array) + + { sld::internal::th_velo=10; + const double v_therm = sqrt( 3.0 * constants::kB_eV * sld::internal::th_velo / sld::internal::mp[0].mass.get()); + int N=atoms::num_atoms; + double rx,ry,s; + //double net_v[] = {0.0, 0.0, 0.0}; + + std::cout<<"velocities before "<= 1.0) + { + rx = 2.0;//2.0* (rand()/double(RAND_MAX)) - 1.0; + ry = 2.0;//2.0* (rand()/double(RAND_MAX)) - 1.0; + + s = rx*rx + ry*ry; + + }*/ + + s=0.5; + rx=2.0; + ry=2.0; + + x_velo_array[i] = v_therm * 2.0 * rx * sqrt( 1.0 - s); + y_velo_array[i] = v_therm * 2.0 * ry * sqrt( 1.0 - s); + z_velo_array[i] = v_therm * ( 1.0 - 2.0 * s); + + // net_v[0] += x_velo_array[i]; + // net_v[1] += y_velo_array[i]; + // net_v[2] += z_velo_array[i]; + } + + //net_v[0] /= double(N); + //net_v[1] /= double(N); + //net_v[2] /= double(N); + + /* for( int i = 0; i < N; i++) + { + x_velo_array[i] -= net_v[0]; + y_velo_array[i] -= net_v[1]; + z_velo_array[i] -= net_v[2]; + } + */ + std::cout<<"velocities after"< +#include + +// Vampire headers +#include "sld.hpp" +#include "errors.hpp" +#include "vio.hpp" + +// sld module headers +#include "internal.hpp" + +namespace sld{ + + //--------------------------------------------------------------------------- + // Function to process input file parameters for sld module + //--------------------------------------------------------------------------- + bool match_input_parameter(std::string const key, std::string const word, std::string const value, std::string const unit, int const line){ + + // Check for valid key, if no match return false + std::string prefix="spin-lattice"; + std::string prefix2="phonon"; //NEW + if(key!=prefix && key!=prefix2) return false; + //------------------------------------------------------------------------ + // If spin-transport parameter is requested, then enable module + //------------------------------------------------------------------------ + sld::enabled = true; + sld::internal::enabled=true; + + //---------------------------------- + // Now test for all valid options + //---------------------------------- + + std::string test = "linear-pump"; + if (word == test) { + sld::internal::linear_pump_enabled = true; // This is a boolean flag + return true; // We successfully matched the keyword + } + + test = "frequency"; + if (word == test) { + double val = atof(value.c_str()); + // Check for units in the 'unit' string provided by the parser + if (unit == "THz") { + val *= 1.0e12; + } else if (unit == "GHz") { + val *= 1.0e9; + } // else, assume base units (Hz) + sld::internal::phonon_frequency = val; + return true; + } + + test = "pulse-start-time"; + if (word == test) { + double val = atof(value.c_str()); + // Check for units in the 'unit' string provided by the parser + if (unit == "ps") { + val *= 1.0e-12; + } else if (unit == "fs") { + val *= 1.0e-15; + } // else, assume base units (s) + sld::internal::phonon_pulse_start_time = val; + return true; + } + + test = "pulse-end-time"; + if (word == test) { + double val = atof(value.c_str()); + // Check for units in the 'unit' string provided by the parser + if (unit == "ps") { + val *= 1.0e-12; + } else if (unit == "fs") { + val *= 1.0e-15; + } // else, assume base units (s) + sld::internal::phonon_pulse_end_time = val; + return true; + } + + // --- PARSING FOR FORCE AMPLITUDE --- + test = "force-amplitude-x"; + if (word == test) { + sld::internal::phonon_force_amplitude[0] = atof(value.c_str()); + return true; + } + test = "force-amplitude-y"; + if (word == test) { + sld::internal::phonon_force_amplitude[1] = atof(value.c_str()); + return true; + } + test = "force-amplitude-z"; + if (word == test) { + sld::internal::phonon_force_amplitude[2] = atof(value.c_str()); + return true; + } + + // --- PARSING FOR WAVE LAMBDA --- + test = "wave-lambda-x"; + if (word == test) { + sld::internal::phonon_wave_lambda[0] = atof(value.c_str()); + return true; + } + //---------------------------------- + test = "wave-lambda-y"; + if (word == test) { + sld::internal::phonon_wave_lambda[1] = atof(value.c_str()); + return true; + } + test = "wave-lambda-z"; + //---------------------------------- + if (word == test) { + sld::internal::phonon_wave_lambda[2] = atof(value.c_str()); + return true; + } + // --- PARSING FOR WAVE DIRECTION --- + test = "wave-direction-x"; + if (word == test) { + sld::internal::phonon_wave_direction[0] = atof(value.c_str()); + return true; + } + //---------------------------------- + test = "wave-direction-y"; + if (word == test) { + sld::internal::phonon_wave_direction[1] = atof(value.c_str()); + return true; + } + //---------------------------------- + test = "wave-direction-z"; + if (word == test) { + sld::internal::phonon_wave_direction[2] = atof(value.c_str()); + return true; + } + //---------------------------------- + test = "potential"; + if( word == test ){ + test="harmonic"; + if( value == test ){ + sld::internal::harmonic=true; + return true; + } + test="morse"; + if( value == test ){ + sld::internal::morse=true; + return true; + } + } + + test = "coupling"; + if( word == test ){ + test="pseudodipolar"; + if( value == test ){ + sld::internal::pseudodipolar=true; + return true; + } + test="full-neel"; + if( value == test ){ + sld::internal::full_neel=true; + return true; + } + } + + test = "potential-cutoff-range"; + if( word == test ){ + double r_c = vin::str_to_double(value); + vin::check_for_valid_value(r_c, word, line, prefix, unit, "length", 2.0, 20.0,"input","2 - 20 A"); + sld::internal::r_cut_pot= r_c; + return true; + } + + test = "fields-cutoff-range"; + if( word == test ){ + double r_cf = vin::str_to_double(value); + vin::check_for_valid_value(r_cf, word, line, prefix, unit, "length", 2, 20.0,"input","2 - 20 A"); + sld::internal::r_cut_fields= r_cf; + return true; + } + /* test = "fixed-lattice"; + if( word == test ){ + sld::internal::fixed-lattice=true; + return true; + } + test = "fixed-spin"; + if( word == test ){ + sld::internal::fixed-spin=true; + return true; +}*/ + +test = "initial-random-displacement"; +if( word == test ){ + double dr_in = vin::str_to_double(value); + vin::check_for_valid_value(dr_in, word, line, prefix, unit, "length", 0.001, 1.0,"input","0.001 - 1A"); + sld::internal::dr_init= dr_in; + return true; +} + +test = "initial-thermal-velocity"; +if( word == test ){ + double temp = vin::str_to_double(value); + vin::check_for_valid_value(temp, word, line, prefix, unit, "none", 0, 2000,"input","0 - 2000"); + sld::internal::th_velo= temp; + return true; +} + +//-------------------------------------------------------------------- +// Keyword not found +//-------------------------------------------------------------------- +return false; + +} + +//--------------------------------------------------------------------------- +// 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){ + + // add prefix string + std::string prefix="material:"; + + // Check for material id > current array size and if so dynamically expand mp array + if((unsigned int) super_index + 1 > internal::mp.size() && super_index + 1 < 101) internal::mp.resize(super_index + 1); + std::string test = "mass"; + if( word == test ){ + double m = vin::str_to_double(value); + vin::check_for_valid_value(m, word, line, prefix, unit, "mass", 1.0e-20, 1.0e20,"input","1E-20 - 1E20"); + sld::internal::mp[super_index].mass.set(m); + return true; + } + + test = "damping-constant-lattice"; + if( word == test ){ + double damp= vin::str_to_double(value); + vin::check_for_valid_value(damp, word, line, prefix, unit, "none", 0, 1.0,"input","0- 1"); + sld::internal::mp[super_index].damp_lat.set(damp); + return true; + } + + test = "equilibration-damping-constant-lattice"; + if( word == test ){ + double damp= vin::str_to_double(value); + vin::check_for_valid_value(damp, word, line, prefix, unit, "none", 0, 1.0,"input","0- 1"); + sld::internal::mp[super_index].eq_damp_lat.set(damp); + return true; + } + + test = "exchange-J0"; + if( word == test ){ + double j0 = vin::str_to_double(value); + vin::check_for_valid_value(j0, word, line, prefix, unit, "energy", 0, 5,"input","0 - 5 eV"); + sld::internal::mp[super_index].J0.set(j0); + return true; + } + + test = "harmonic-potential-V0"; + if( word == test ){ + double v0 = vin::str_to_double(value); + vin::check_for_valid_value(v0, word, line, prefix, unit, "energy", 1.0e-20, 1.0e20,"input","1E-20 - 1E20"); + sld::internal::mp[super_index].V0.set(v0); + return true; + } + + test = "coupling-C0"; + if( word == test ){ + double c0 = vin::str_to_double(value); + vin::check_for_valid_value(c0, word, line, prefix, unit, "mass", 0, 1,"input","0 - 1"); + sld::internal::mp[super_index].C0.set(c0); + return true; + } + + + //-------------------------------------------------------------------- + // Keyword not found + //-------------------------------------------------------------------- + return false; + +} + + + +} // end of sld namespace diff --git a/src/spinlattice/internal.hpp b/src/spinlattice/internal.hpp new file mode 100644 index 000000000..ff5ee4b29 --- /dev/null +++ b/src/spinlattice/internal.hpp @@ -0,0 +1,360 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Mara Strungaru 2022. All rights reserved. +// +// Email: mara.strungaru@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +#ifndef SLD_INTERNAL_H_ +#define SLD_INTERNAL_H_ +// +//--------------------------------------------------------------------- +// This header file defines shared internal data structures and +// functions for the sld module. These functions and +// variables should not be accessed outside of this module. +//--------------------------------------------------------------------- + +// C++ standard library headers +#include + +// Vampire headers +#include "sld.hpp" + +// sld module headers +#include "internal.hpp" + + +namespace sld{ + + namespace internal{ + + class set_double_t{ + + private: + double value; // value + bool setf; // flag specifiying variable has been set + + public: + // class functions + // constructor + set_double_t() : value(0.0), setf(false) { } + + // setting function + void set(double in_value){ + value = in_value; + setf = true; + }; + + // get value function + double get(){ return value; }; + // check if variable is set + bool is_set(){ return setf; }; + + }; + + //------------------------------------------------------------------------- + // Internal data type definitions + //------------------------------------------------------------------------- + + //----------------------------------------------------------------------------- + // internal materials class for storing material parameters + //----------------------------------------------------------------------------- + class mp_t{ + + private: + + public: + + //------------------------------ + // material parameter variables + //------------------------------ + set_double_t mass; + set_double_t V0; + set_double_t J0; + set_double_t C0; + set_double_t damp_lat; + set_double_t eq_damp_lat; + + set_double_t J0_ms; + set_double_t C0_ms; + set_double_t J0_prime; + set_double_t F_th_sigma; + set_double_t F_th_sigma_eq; + + + + + + + + // constructor + mp_t (const unsigned int max_materials = 100){ + mass.set(5.7915e-3); + V0.set(0.15); + J0.set(0.904); + J0_prime.set(3*0.904/7.8); + J0_ms.set(0.904/2.04028e-23); + C0.set(0.5); + C0_ms.set(0.5/2.04028e-23); + F_th_sigma.set(1.0); + F_th_sigma_eq.set(1.0); + damp_lat.set(0.06); + eq_damp_lat.set(0.6); + + + + }; // end of constructor + + + + }; // end of internal::mp class + + + // NEW: Parameters for the linear phonon pump + extern bool linear_pump_enabled; + extern double phonon_frequency; + extern double phonon_force_amplitude[3]; // For fx, fy, fz + extern double phonon_wavevector[3]; // For kx, ky, kz + extern double phonon_pulse_start_time; + extern double phonon_pulse_end_time; + extern std::vector coupling_field_x; + extern std::vector coupling_field_y; + extern std::vector coupling_field_z; + + // NEW: Intermediate storage for wave parameters from the input file + extern double phonon_wave_lambda[3]; // For λ_x, λ_y, λ_z + extern double phonon_wave_direction[3]; // For direction_x, direction_y, direction_z + + + + //------------------------------------------------------------------------- + // Internal shared variables + //------------------------------------------------------------------------- + + extern bool enabled; // bool to enable module + extern std::vector mp; // array of material properties + + extern double r_cut_pot; // mechanical potential cutoff + extern double r_cut_fields; // exchange/coupling cutoff + + extern double dr_init; + extern double th_velo; + + //for the morse potential + extern double morse_beta; + extern double morse_factor; + extern double alpha_m; + extern double r0_m; + extern double morse_D; + extern bool morse; + + extern bool harmonic; // bool to enable module + extern bool pseudodipolar; + extern bool full_neel; + + + //extern std::vector sld_neighbour_list_start_index; + //extern std::vector sld_neighbour_list_end_index; + //extern std::vector sld_neighbour_list_array; + + extern std::vector x0_coord_array; + extern std::vector y0_coord_array; + extern std::vector z0_coord_array; + + + extern std::vector x_coord_storage_array; + extern std::vector y_coord_storage_array; + extern std::vector z_coord_storage_array; + + + + extern std::vector forces_array_x; + extern std::vector forces_array_y; + extern std::vector forces_array_z; + + extern std::vector fields_array_x; + extern std::vector fields_array_y; + extern std::vector fields_array_z; + + extern std::vector velo_array_x; + extern std::vector velo_array_y; + extern std::vector velo_array_z; + extern std::vector potential_eng; + extern std::vector exch_eng; + extern std::vector coupl_eng; + extern std::vector sumJ; + extern std::vector sumC; + + extern std::vector test_atom_list; //Core atoms of each octant + + + + void initialise_positions(std::vector& x0_coord_array, // coord vectors for atoms + std::vector& y0_coord_array, + std::vector& z0_coord_array, + std::vector& x_coord_array, // coord vectors for atoms + std::vector& y_coord_array, + std::vector& z_coord_array, + const double dr); + + void thermal_velocity(std::vector& x_velo_array, // coord vectors for atoms + std::vector& y_velo_array, + std::vector& z_velo_array); + +//function to resize vectors and initialise rest of parameters + void initialise_sld_parameters(); + +//functions to compute potentials + void compute_forces_harmonic(const int start_index, + const int end_index, // last +1 atom to be calculated + const std::vector& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& x0_coord_array, // coord vectors for atoms + const std::vector& y0_coord_array, + const std::vector& z0_coord_array, + const std::vector& x_coord_array, // coord vectors for atoms + const std::vector& y_coord_array, + const std::vector& z_coord_array, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z, + std::vector& potential_eng); + +//functions to compute fields + void compute_exchange(const int start_index, + const int end_index, // last +1 atom to be calculated + const std::vector& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& x_coord_array, // coord vectors for atoms + const std::vector& y_coord_array, + const std::vector& z_coord_array, + const std::vector& x_spin_array, // coord vectors for atoms + const std::vector& y_spin_array, + const std::vector& z_spin_array, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z, + std::vector& fields_array_x, // vectors for forces + std::vector& fields_array_y, + std::vector& fields_array_z); + + + void compute_forces_morse(const int start_index, + const int end_index, // last +1 atom to be calculated + const std::vector& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& x_coord_array, // coord vectors for atoms + const std::vector& y_coord_array, + const std::vector& z_coord_array, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z, + std::vector& potential_eng); + + + void compute_thz(const int start_index, + const int end_index, + const std::vector& x_coord_array, // current coord vectors for atoms + const std::vector& y_coord_array, + const std::vector& z_coord_array, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z); + + +// + void compute_sld_coupling(const int start_index, + const int end_index, // last +1 atom to be calculated + const std::vector& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& x_coord_array, // coord vectors for atoms + const std::vector& y_coord_array, + const std::vector& z_coord_array, + const std::vector& x_spin_array, // spin vectors for atoms + const std::vector& y_spin_array, + const std::vector& z_spin_array, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z, + std::vector& fields_array_x, // vectors for forces + std::vector& fields_array_y, + std::vector& fields_array_z); + + void compute_sld_coupling_neel(const int start_index, + const int end_index, // last +1 atom to be calculated + const std::vector& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& x_coord_array, // coord vectors for atoms + const std::vector& y_coord_array, + const std::vector& z_coord_array, + const std::vector& x_spin_array, // spin vectors for atoms + const std::vector& y_spin_array, + const std::vector& z_spin_array, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z, + std::vector& fields_array_x, // vectors for forces + std::vector& fields_array_y, + std::vector& fields_array_z); + +// + + void cayley_update(const int start_index, + const int end_index, + double dt, + std::vector& x_spin_array, // coord vectors for atoms + std::vector& y_spin_array, + std::vector& z_spin_array, + std::vector& fields_array_x, // vectors for fields + std::vector& fields_array_y, + std::vector& fields_array_z); + // add spin noise + + void add_spin_noise(const int start_index, + const int end_index, + double dt, + const std::vector& type_array, // type for atom + const std::vector& x_spin_array, // coord vectors for atoms + const std::vector& y_spin_array, + const std::vector& z_spin_array, + std::vector& fields_array_x, // vectors for fields + std::vector& fields_array_y, + std::vector& fields_array_z, + std::vector& Hx_th, // vectors for fields + std::vector& Hy_th, + std::vector& Hz_th); + + //MPI variables + extern std::vector > c_octants; //Core atoms of each octant + extern std::vector > b_octants; //Boundary atoms of each octant + + extern std::vector all_atoms_octant_start_index; + extern std::vector all_atoms_octant_end_index; + extern std::vector all_atoms_octant; + + + //------------------------------------------------------------------------- + // Internal function declarations + //------------------------------------------------------------------------- + + } // end of internal namespace + +} // end of sld namespace + + +#endif //SLD_INTERNAL_H_ diff --git a/src/spinlattice/makefile b/src/spinlattice/makefile new file mode 100644 index 000000000..6ba6256c9 --- /dev/null +++ b/src/spinlattice/makefile @@ -0,0 +1,19 @@ +#-------------------------------------------------------------- +# Makefile for sld module +#-------------------------------------------------------------- + +# List module object filenames +spinlattice_objects =\ +data.o \ +initialize.o \ +interface.o\ +forces.o\ +fields.o\ +suzuki-trotter.o\ +temperatures.o\ +energy.o\ +suzuki-trotter-mpi.o\ +tests.o + +# Append module objects to global tree +OBJECTS+=$(addprefix obj/spinlattice/,$(spinlattice_objects)) diff --git a/src/spinlattice/suzuki-trotter-mpi.cpp b/src/spinlattice/suzuki-trotter-mpi.cpp new file mode 100755 index 000000000..cc3bad4b6 --- /dev/null +++ b/src/spinlattice/suzuki-trotter-mpi.cpp @@ -0,0 +1,1053 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Mara Strungaru 2022. All rights reserved. +// +// Email: mara.strungaru@york.ac.uk +// +// implementation based on the paper Phys. Rev. B 103, 024429, (2021) M.Strungaru, M.O.A. Ellis et al +//------------------------------------------------------------------------------ +// + +#ifdef MPICF +// Standard Libraries +#include +#include +#include + +// Vampire Header files +#include "atoms.hpp" +#include "create.hpp" +#include "errors.hpp" +#include "random.hpp" +#include "sim.hpp" +#include "sld.hpp" +#include "material.hpp" +#include "vmpi.hpp" + +// Internal header +#include "internal.hpp" + + + + + + + +namespace sld{ + + + +void suzuki_trotter_parallel_init(std::vector &x, // atomic coordinates + std::vector &y, + std::vector &z, + double min_dim[3], // minimum dimensions on local processor + double max_dim[3]){ // maximum dimensions on local processor + + + // Convenient shorthands + int catoms = vmpi::num_core_atoms; + int batoms = vmpi::num_bdry_atoms; + + + + double widthx = max_dim[0] - min_dim[0]; + double widthy = max_dim[1] - min_dim[1]; + double widthz = max_dim[2] - min_dim[2]; + + if (widthx<=sld::internal::r_cut_fields ||widthy<=sld::internal::r_cut_fields || widthy<=sld::internal::r_cut_fields ){ + std::cerr << "Error: System size needs to be increased so octants won't interact" << std::endl; + std::cerr<<"Width of octants is "<= min_dim[0] + widthx*xoct*0.5 && x[i] < min_dim[0] + widthx*0.5 + widthx*xoct*0.5 + && y[i] >= min_dim[1] + widthy*yoct*0.5 && y[i] < min_dim[1] + widthy*0.5 + widthy*yoct*0.5 + && z[i] >= min_dim[2] + widthz*zoct*0.5 && z[i] < min_dim[2] + widthz*0.5 + widthz*zoct*0.5) + { + internal::c_octants[octant_num].push_back(i); + } + } + octant_num++; + } + } + } + + + + octant_num = 0; + //Sort boundary atoms into appropriate octant arrays. + for(int zoct=0; zoct<2; zoct++){ + for(int yoct=0; yoct<2; yoct++){ + for(int xoct=0; xoct<2; xoct++){ + // Loop through all boundary atoms + for (int i=catoms; i= min_dim[0] + widthx*xoct*0.5 && x[i] < min_dim[0] + widthx*0.5 + widthx*xoct*0.5 + && y[i] >= min_dim[1] + widthy*yoct*0.5 && y[i] < min_dim[1] + widthy*0.5 + widthy*yoct*0.5 + && z[i] >= min_dim[2] + widthz*zoct*0.5 && z[i] < min_dim[2] + widthz*0.5 + widthz*zoct*0.5) + { + internal::b_octants[octant_num].push_back(i); + + } + } + octant_num++; + } + } + } + + //-------------------------------------------------------------------- + // check that all atoms have been allocated an octant + //-------------------------------------------------------------------- + // core atoms + int num_atoms_in_octants = 0; + for(int i=0; i< 8; i++) {num_atoms_in_octants += internal::c_octants[i].size();} + + if(num_atoms_in_octants != catoms){ + std::cerr << "Programmer error: missing atoms in core octants in suzuki-trotter initialisation" << std::endl; + err::vexit(); + } + // boundary atoms + num_atoms_in_octants = 0; + for(int i=0; i< 8; i++) { + num_atoms_in_octants += internal::b_octants[i].size();} + + if(num_atoms_in_octants != batoms){ + std::cout<< "num_atoms "< &x_spin_array, + std::vector &y_spin_array, + std::vector &z_spin_array, + std::vector &type_array){ + + + + + + double cay_dt=-mp::dt/4.0;//-dt4*consts::gyro - mp::dt contains gamma; + double dt2=0.5*mp::dt_SI*1e12; + + + + //vectors for thermal noise spin plus lattice + std::vector Hx_th(atoms::x_spin_array.size()); + std::vector Hy_th(atoms::x_spin_array.size()); + std::vector Hz_th(atoms::x_spin_array.size()); + + generate (Hx_th.begin(),Hx_th.end(), mtrandom::gaussian); + generate (Hy_th.begin(),Hy_th.end(), mtrandom::gaussian); + generate (Hz_th.begin(),Hz_th.end(), mtrandom::gaussian); + + //vectors for thermal forces + std::vector Fx_th(atoms::x_spin_array.size()); + std::vector Fy_th(atoms::x_spin_array.size()); + std::vector Fz_th(atoms::x_spin_array.size()); + + generate (Fx_th.begin(),Fx_th.end(), mtrandom::gaussian); + generate (Fy_th.begin(),Fy_th.end(), mtrandom::gaussian); + generate (Fz_th.begin(),Fz_th.end(), mtrandom::gaussian); + + //int indx_start, indx_end; + //int number_at=0; + int atom=0; + + +/*for(int atom=0;atom<=atoms::num_atoms-1;atom++){ + + if (atom==0) { + std::cout<= 0; octant--) { + vmpi::mpi_init_halo_swap(); + + + int core_at=internal::c_octants[octant].size(); + int bdry_at=internal::b_octants[octant].size(); + + for (int i=core_at-1;i>=0;i--){ + atom = internal::c_octants[octant][i]; + + + sld::compute_fields(atom, // first atom for exchange interactions to be calculated + atom+1, // last +1 atom to be calculated + atoms::neighbour_list_start_index, + atoms::neighbour_list_end_index, + atoms::type_array, // type for atom + atoms::neighbour_list_array, // list of interactions between atoms + atoms::x_coord_array, + atoms::y_coord_array, + atoms::z_coord_array, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::forces_array_x, + sld::internal::forces_array_y, + sld::internal::forces_array_z, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + sld::internal::add_spin_noise(atom, + atom+1, + mp::dt_SI*1e12, + atoms::type_array, // type for atom + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z, + Hx_th, // vectors for fields + Hy_th, + Hz_th); + + + sld::internal::cayley_update(atom, + atom+1, + cay_dt, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + + + }//end spin loop + + vmpi::mpi_complete_halo_swap(); + vmpi::barrier(); + + + for (int i=bdry_at-1;i>=0;i--){ + atom = internal::b_octants[octant][i]; + + sld::compute_fields(atom, // first atom for exchange interactions to be calculated + atom+1, // last +1 atom to be calculated + atoms::neighbour_list_start_index, + atoms::neighbour_list_end_index, + atoms::type_array, // type for atom + atoms::neighbour_list_array, // list of interactions between atoms + atoms::x_coord_array, + atoms::y_coord_array, + atoms::z_coord_array, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::forces_array_x, + sld::internal::forces_array_y, + sld::internal::forces_array_z, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + sld::internal::add_spin_noise(atom, + atom+1, + mp::dt_SI*1e12, + atoms::type_array, // type for atom + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z, + Hx_th, // vectors for fields + Hy_th, + Hz_th); + + + sld::internal::cayley_update(atom, + atom+1, + cay_dt, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + }//end spin loop + vmpi::barrier(); + + + } // end first octant loop return + + + //vrv update + //CORE atoms first + //all forces are set to 0 for computation + std::fill(sld::internal::forces_array_x.begin(), sld::internal::forces_array_x.end(), 0.0); + std::fill(sld::internal::forces_array_y.begin(), sld::internal::forces_array_y.end(), 0.0); + std::fill(sld::internal::forces_array_z.begin(), sld::internal::forces_array_z.end(), 0.0); + + vmpi::mpi_init_halo_swap_coords(); + + + + + sld::compute_fields(pre_comm_si, // first atom for exchange interactions to be calculated + pre_comm_ei, // last +1 atom to be calculated + atoms::neighbour_list_start_index, + atoms::neighbour_list_end_index, + atoms::type_array, // type for atom + atoms::neighbour_list_array, // list of interactions between atoms + atoms::x_coord_array, + atoms::y_coord_array, + atoms::z_coord_array, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::forces_array_x, + sld::internal::forces_array_y, + sld::internal::forces_array_z, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + + + + sld::compute_forces(pre_comm_si, // first atom for exchange interactions to be calculated + pre_comm_ei, // last +1 atom to be calculated + atoms::neighbour_list_start_index, + atoms::neighbour_list_end_index, + atoms::type_array, // type for atom + atoms::neighbour_list_array, // list of interactions between atoms + sld::internal::x0_coord_array, // list of isotropic exchange constants + sld::internal::y0_coord_array, // list of vectorial exchange constants + sld::internal::z0_coord_array, // list of tensorial exchange constants + atoms::x_coord_array, + atoms::y_coord_array, + atoms::z_coord_array, + sld::internal::forces_array_x, + sld::internal::forces_array_y, + sld::internal::forces_array_z, + sld::internal::potential_eng); + + //update position, Velocity + for(int atom=pre_comm_si;atom= 0; octant--) { + + vmpi::mpi_init_halo_swap(); + int core_at=internal::c_octants[octant].size(); + int bdry_at=internal::b_octants[octant].size(); + + + + for (int i=core_at-1;i>=0;i--){ + atom = internal::c_octants[octant][i]; + + sld::compute_fields(atom, // first atom for exchange interactions to be calculated + atom+1, // last +1 atom to be calculated + atoms::neighbour_list_start_index, + atoms::neighbour_list_end_index, + atoms::type_array, // type for atom + atoms::neighbour_list_array, // list of interactions between atoms + atoms::x_coord_array, + atoms::y_coord_array, + atoms::z_coord_array, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::forces_array_x, + sld::internal::forces_array_y, + sld::internal::forces_array_z, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + sld::internal::add_spin_noise(atom, + atom+1, + mp::dt_SI*1e12, + atoms::type_array, // type for atom + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z, + Hx_th, // vectors for fields + Hy_th, + Hz_th); + + + sld::internal::cayley_update(atom, + atom+1, + cay_dt, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + }//end spin loop + + vmpi::mpi_complete_halo_swap(); + vmpi::barrier(); + + + for (int i=bdry_at-1;i>=0;i--){ + atom = internal::b_octants[octant][i]; + + + sld::compute_fields(atom, // first atom for exchange interactions to be calculated + atom+1, // last +1 atom to be calculated + atoms::neighbour_list_start_index, + atoms::neighbour_list_end_index, + atoms::type_array, // type for atom + atoms::neighbour_list_array, // list of interactions between atoms + atoms::x_coord_array, + atoms::y_coord_array, + atoms::z_coord_array, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::forces_array_x, + sld::internal::forces_array_y, + sld::internal::forces_array_z, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + sld::internal::add_spin_noise(atom, + atom+1, + mp::dt_SI*1e12, + atoms::type_array, // type for atom + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z, + Hx_th, // vectors for fields + Hy_th, + Hz_th); + + + sld::internal::cayley_update(atom, + atom+1, + cay_dt, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + }//end spin loop + + vmpi::barrier(); + + } //end second octant loop return + + + +/*for(int atom=0;atom<=atoms::num_atoms-1;atom++){ + + if ( (atom==0)) { + std::cout< wait + vmpi::TotalComputeTime+=vmpi::SwapTimer(vmpi::ComputeTime, vmpi::WaitTime); + + // Wait for other processors + vmpi::barrier(); + + // Swap timers wait -> compute + vmpi::TotalWaitTime += vmpi::SwapTimer(vmpi::WaitTime, vmpi::ComputeTime); + + + return; + +} + +} // End of namespace sld +#endif diff --git a/src/spinlattice/suzuki-trotter.cpp b/src/spinlattice/suzuki-trotter.cpp new file mode 100644 index 000000000..59bbd51e9 --- /dev/null +++ b/src/spinlattice/suzuki-trotter.cpp @@ -0,0 +1,637 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Mara Strungaru 2022. All rights reserved. +// +// Email: mara.strungaru@york.ac.uk +// +// implementation based on the paper Phys. Rev. B 103, 024429, (2021) M.Strungaru, M.O.A. Ellis et al +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include + +// Vampire headers +#include "atoms.hpp" +#include "create.hpp" +#include "errors.hpp" +#include "material.hpp" +#include "random.hpp" +#include "sim.hpp" +#include "sld.hpp" + +//sld module headers M Strungaru +#include "internal.hpp" + +namespace sld{ + void compute_forces_fields(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& neighbour_list_start_index, + const std::vector& neighbour_list_end_index, + const std::vector& type_array, // type for atom + const std::vector& neighbour_list_array, // list of interactions between atom + const std::vector& x0_coord_array, // coord vectors for atoms + const std::vector& y0_coord_array, + const std::vector& z0_coord_array, + std::vector& x_coord_array, // coord vectors for atoms + std::vector& y_coord_array, + std::vector& z_coord_array, + std::vector& forces_array_x, // vectors for forces + std::vector& forces_array_y, + std::vector& forces_array_z){ + + return; + } + + void stats_sld(){ + const int num_atoms=atoms::num_atoms; + + std::fill(sld::internal::fields_array_x.begin(), sld::internal::fields_array_x.end(), 0.0); + std::fill(sld::internal::fields_array_y.begin(), sld::internal::fields_array_y.end(), 0.0); + std::fill(sld::internal::fields_array_z.begin(), sld::internal::fields_array_z.end(), 0.0); + + sld::compute_fields(0, // first atom for exchange interactions to be calculated + num_atoms, // last +1 atom to be calculated + atoms::neighbour_list_start_index, + atoms::neighbour_list_end_index, + atoms::type_array, // type for atom + atoms::neighbour_list_array, // list of interactions between atoms + atoms::x_coord_array, + atoms::y_coord_array, + atoms::z_coord_array, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::forces_array_x, + sld::internal::forces_array_y, + sld::internal::forces_array_z, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + + + sld::J_eff= sld::compute_effective_J(0,atoms::num_atoms, + sld::internal::sumJ); +// + sld::C_eff= sld::compute_effective_C(0,atoms::num_atoms, + sld::internal::sumC); + + + + } + + + + int suzuki_trotter(){ + const int num_atoms=atoms::num_atoms; + double cay_dt=-mp::dt/4.0;//-dt4*consts::gyro - mp::dt contains gamma; + double dt2=0.5*mp::dt_SI*1e12; + + + + //vectors for thermal noise spin plus lattice + std::vector Hx_th(atoms::x_spin_array.size()); + std::vector Hy_th(atoms::x_spin_array.size()); + std::vector Hz_th(atoms::x_spin_array.size()); + + generate (Hx_th.begin(),Hx_th.end(), mtrandom::gaussian); + generate (Hy_th.begin(),Hy_th.end(), mtrandom::gaussian); + generate (Hz_th.begin(),Hz_th.end(), mtrandom::gaussian); + + //vectors for thermal forces + std::vector Fx_th(atoms::x_spin_array.size()); + std::vector Fy_th(atoms::x_spin_array.size()); + std::vector Fz_th(atoms::x_spin_array.size()); + + generate (Fx_th.begin(),Fx_th.end(), mtrandom::gaussian); + generate (Fy_th.begin(),Fy_th.end(), mtrandom::gaussian); + generate (Fz_th.begin(),Fz_th.end(), mtrandom::gaussian); + + + std::fill(sld::internal::fields_array_x.begin(), sld::internal::fields_array_x.end(), 0.0); + std::fill(sld::internal::fields_array_y.begin(), sld::internal::fields_array_y.end(), 0.0); + std::fill(sld::internal::fields_array_z.begin(), sld::internal::fields_array_z.end(), 0.0); + + + /*for(int atom=0;atom<=num_atoms-1;atom++){ + + if ( (atom==555)) { + std::cout<=0;atom--){ + + + sld::compute_fields(atom, // first atom for exchange interactions to be calculated + atom+1, // last +1 atom to be calculated + atoms::neighbour_list_start_index, + atoms::neighbour_list_end_index, + atoms::type_array, // type for atom + atoms::neighbour_list_array, // list of interactions between atoms + atoms::x_coord_array, + atoms::y_coord_array, + atoms::z_coord_array, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::forces_array_x, + sld::internal::forces_array_y, + sld::internal::forces_array_z, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + + sld::internal::add_spin_noise(atom, + atom+1, + mp::dt_SI*1e12, + atoms::type_array, // type for atom + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z, + Hx_th, // vectors for fields + Hy_th, + Hz_th); + + sld::internal::cayley_update(atom, + atom+1, + cay_dt, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + } + + + + + //forces are set to 0 for computation + std::fill(sld::internal::forces_array_x.begin(), sld::internal::forces_array_x.end(), 0.0); + std::fill(sld::internal::forces_array_y.begin(), sld::internal::forces_array_y.end(), 0.0); + std::fill(sld::internal::forces_array_z.begin(), sld::internal::forces_array_z.end(), 0.0); + + + + sld::compute_fields(0, // first atom for exchange interactions to be calculated + num_atoms, // last +1 atom to be calculated + atoms::neighbour_list_start_index, + atoms::neighbour_list_end_index, + atoms::type_array, // type for atom + atoms::neighbour_list_array, // list of interactions between atoms + atoms::x_coord_array, + atoms::y_coord_array, + atoms::z_coord_array, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::forces_array_x, + sld::internal::forces_array_y, + sld::internal::forces_array_z, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + sld::compute_forces(0, // first atom for exchange interactions to be calculated + num_atoms, // last +1 atom to be calculated + atoms::neighbour_list_start_index, + atoms::neighbour_list_end_index, + atoms::type_array, // type for atom + atoms::neighbour_list_array, // list of interactions between atoms + sld::internal::x0_coord_array, // list of isotropic exchange constants + sld::internal::y0_coord_array, // list of vectorial exchange constants + sld::internal::z0_coord_array, // list of tensorial exchange constants + atoms::x_coord_array, + atoms::y_coord_array, + atoms::z_coord_array, + sld::internal::forces_array_x, + sld::internal::forces_array_y, + sld::internal::forces_array_z, + sld::internal::potential_eng); + + + //update position, Velocity + for(int atom=0;atom=0;atom--){ + + sld::compute_fields(atom, // first atom for exchange interactions to be calculated + atom+1, // last +1 atom to be calculated + atoms::neighbour_list_start_index, + atoms::neighbour_list_end_index, + atoms::type_array, // type for atom + atoms::neighbour_list_array, // list of interactions between atoms + atoms::x_coord_array, + atoms::y_coord_array, + atoms::z_coord_array, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::forces_array_x, + sld::internal::forces_array_y, + sld::internal::forces_array_z, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + sld::internal::add_spin_noise(atom, + atom+1, + mp::dt_SI*1e12, + atoms::type_array, // type for atom + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z, + Hx_th, // vectors for fields + Hy_th, + Hz_th); + + sld::internal::cayley_update(atom, + atom+1, + cay_dt, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + } + + /* + + std::fill(sld::internal::fields_array_x.begin(), sld::internal::fields_array_x.end(), 0.0); + std::fill(sld::internal::fields_array_y.begin(), sld::internal::fields_array_y.end(), 0.0); + std::fill(sld::internal::fields_array_z.begin(), sld::internal::fields_array_z.end(), 0.0); + + sld::compute_fields(0, // first atom for exchange interactions to be calculated + num_atoms, // last +1 atom to be calculated + atoms::neighbour_list_start_index, + atoms::neighbour_list_end_index, + atoms::type_array, // type for atom + atoms::neighbour_list_array, // list of interactions between atoms + atoms::x_coord_array, + atoms::y_coord_array, + atoms::z_coord_array, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::forces_array_x, + sld::internal::forces_array_y, + sld::internal::forces_array_z, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z); + + sld::spin_temperature= sld::compute_spin_temperature(0,atoms::num_atoms, + atoms::type_array, // type for atom + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + sld::internal::fields_array_x, + sld::internal::fields_array_y, + sld::internal::fields_array_z, + mp::mu_s_array); + // + sld::lattice_temperature= sld::compute_lattice_temperature(0,atoms::num_atoms, + atoms::type_array, + atoms::x_velo_array, + atoms::y_velo_array, + atoms::z_velo_array);*/ + + + /* for(int atom=0;atom<=num_atoms-1;atom++){ + + if ( (atom==555)) { + std::cout<& x_spin_array, // coord vectors for atoms + std::vector& y_spin_array, + std::vector& z_spin_array, + std::vector& fields_array_x, // vectors for fields + std::vector& fields_array_y, + std::vector& fields_array_z){ + + for( int i = start_index; i& type_array, // type for atom + const std::vector& x_spin_array, // coord vectors for atoms + const std::vector& y_spin_array, + const std::vector& z_spin_array, + std::vector& fields_array_x, // vectors for fields + std::vector& fields_array_y, + std::vector& fields_array_z, + std::vector& Hx_th, // vectors for fields + std::vector& Hy_th, + std::vector& Hz_th){ + + + for( int i = start_index; i +#include +#include + +// Vampire headers +#include "constants.hpp" +#include "material.hpp" +#include "sld.hpp" + +// sld module headers +#include "internal.hpp" + + +namespace sld{ + + + double compute_spin_temperature(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& type_array, // type for atom + std::vector& x_spin_array, // coord vectors for atoms + std::vector& y_spin_array, + std::vector& z_spin_array, + std::vector& fields_array_x, // vectors for fields + std::vector& fields_array_y, + std::vector& fields_array_z, + std::vector & mu_s_array){ + + double SxH2=0.0; + double SH=0.0; + for (int at=start_index;at& type_array, // type for atom + std::vector& velo_array_x, // coord vectors for atoms + std::vector& velo_array_y, + std::vector& velo_array_z){ + + double kinetic=0; + for (int at=start_index;at +#include +#include + +// Vampire headers +#include "atoms.hpp" +#include "constants.hpp" +#include "material.hpp" +#include "neighbours.hpp" +#include "sld.hpp" + +// sld module headers +#include "internal.hpp" + + +namespace sld{ + + //---------------------------------------------------------------------------- + // Function to initialize sld module + //---------------------------------------------------------------------------- + void tests(){ + std::cout<<"*******************************************************"< potential_engt; + potential_engt.resize(atoms::num_atoms,0); + + + std::vector xt_coord_array; + std::vector yt_coord_array; + std::vector zt_coord_array; + + xt_coord_array.resize(atoms::num_atoms,0.0); + yt_coord_array.resize(atoms::num_atoms,0.0); + zt_coord_array.resize(atoms::num_atoms,0.0); + + std::vector xt_spin_array; + std::vector yt_spin_array; + std::vector zt_spin_array; + + xt_spin_array.resize(atoms::num_atoms,0.0); + yt_spin_array.resize(atoms::num_atoms,0.0); + zt_spin_array.resize(atoms::num_atoms,1.0); + + std::vector forces_array_xt; + std::vector forces_array_yt; + std::vector forces_array_zt; + + forces_array_xt.resize(atoms::num_atoms,0.0); + forces_array_yt.resize(atoms::num_atoms,0.0); + forces_array_zt.resize(atoms::num_atoms,0.0); + + std::vector fields_array_xt; + std::vector fields_array_yt; + std::vector fields_array_zt; + + fields_array_xt.resize(atoms::num_atoms,0.0); + fields_array_yt.resize(atoms::num_atoms,0.0); + fields_array_zt.resize(atoms::num_atoms,0.0); + + std::ofstream ofile_c("test_coupling.txt"); + std::ofstream ofile_e("test_exchange.txt"); + std::ofstream ofile_p("test_potential.txt"); + std::ofstream ofile_s("test_spins.txt"); + + + //set full printing precision + std::cout< mp; // array of material properties + + } // end of internal namespace + +} // end of spintextures namespace + diff --git a/src/spintextures/initialize.cpp b/src/spintextures/initialize.cpp new file mode 100644 index 000000000..fa7069bf0 --- /dev/null +++ b/src/spintextures/initialize.cpp @@ -0,0 +1,33 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Ricardo Rama-Eiroa 2025. All rights reserved. +// +// Email: ricardo.rama-eiroa@ed.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers + +// Vampire headers +#include "spintextures.hpp" + +// spintextures module headers +#include "internal.hpp" + +namespace spintextures{ + + //---------------------------------------------------------------------------- + // Function to initialize spintextures module + //---------------------------------------------------------------------------- + void initialize(){ + + return; + + } + +} // end of spintextures namespace + diff --git a/src/spintextures/interface.cpp b/src/spintextures/interface.cpp new file mode 100644 index 000000000..8448ae57c --- /dev/null +++ b/src/spintextures/interface.cpp @@ -0,0 +1,61 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Ricardo Rama-Eiroa 2025. All rights reserved. +// +// Email: ricardo.rama-eiroa@ed.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include + +// Vampire headers +#include "spintextures.hpp" +#include "errors.hpp" +#include "vio.hpp" + +// spintextures module headers +#include "internal.hpp" + +namespace spintextures{ + + //--------------------------------------------------------------------------- + // Function to process input file parameters for spintextures module + //--------------------------------------------------------------------------- + bool match_input_parameter(std::string const key, std::string const word, std::string const value, std::string const unit, int const line){ + + // Check for valid key, if no match return false + std::string prefix="spintextures"; + if(key!=prefix) return false; + + //-------------------------------------------------------------------- + // Keyword not found + //-------------------------------------------------------------------- + return false; + + } + + //--------------------------------------------------------------------------- + // 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){ + + // add prefix string + std::string prefix="material:"; + + // Check for material id > current array size and if so dynamically expand mp array + if((unsigned int) super_index + 1 > internal::mp.size() && super_index + 1 < 101) internal::mp.resize(super_index + 1); + + //-------------------------------------------------------------------- + // Keyword not found + //-------------------------------------------------------------------- + return false; + + } + +} // end of spintextures namespace + diff --git a/src/spintextures/internal.hpp b/src/spintextures/internal.hpp new file mode 100644 index 000000000..cbd25b577 --- /dev/null +++ b/src/spintextures/internal.hpp @@ -0,0 +1,78 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Ricardo Rama-Eiroa 2025. All rights reserved. +// +// Email: ricardo.rama-eiroa@ed.ac.uk +// +//------------------------------------------------------------------------------ +// + +#ifndef SPINTEXTURES_INTERNAL_H_ +#define SPINTEXTURES_INTERNAL_H_ +// +//--------------------------------------------------------------------- +// This header file defines shared internal data structures and +// functions for the spintextures module. These functions and +// variables should not be accessed outside of this module. +//--------------------------------------------------------------------- + +// C++ standard library headers +#include + +// Vampire headers +#include "spintextures.hpp" + +// spintextures module headers +#include "internal.hpp" + +namespace spintextures{ + + namespace internal{ + + //------------------------------------------------------------------------- + // Internal data type definitions + //------------------------------------------------------------------------- + + //----------------------------------------------------------------------------- + // internal materials class for storing material parameters + //----------------------------------------------------------------------------- + class mp_t{ + + private: + + public: + + //------------------------------ + // material parameter variables + //------------------------------ + double test; + + // constructor + mp_t (const unsigned int max_materials = 100): + test(0.0) // constructor initialisation of test variable + { + // constructor body for initialising more complex data/arrays + }; // end of constructor + + }; // end of internal::mp class + + //------------------------------------------------------------------------- + // Internal shared variables + //------------------------------------------------------------------------- + + extern bool enabled; // bool to enable module + + extern std::vector mp; // array of material properties + + //------------------------------------------------------------------------- + // Internal function declarations + //------------------------------------------------------------------------- + + } // end of internal namespace + +} // end of spintextures namespace + +#endif //SPINTEXTURES_INTERNAL_H_ diff --git a/src/spintextures/makefile b/src/spintextures/makefile new file mode 100644 index 000000000..7637e1599 --- /dev/null +++ b/src/spintextures/makefile @@ -0,0 +1,12 @@ +#-------------------------------------------------------------- +# Makefile for spintextures module +#-------------------------------------------------------------- + +# List module object filenames +spintextures_objects =\ +data.o \ +initialize.o \ +interface.o + +# Append module objects to global tree +OBJECTS+=$(addprefix obj/spintextures/,$(spintextures_objects)) diff --git a/src/spintorque/spinaccumulation.cpp b/src/spintorque/spinaccumulation.cpp index 65f68ac8c..4d1ca4ab9 100644 --- a/src/spintorque/spinaccumulation.cpp +++ b/src/spintorque/spinaccumulation.cpp @@ -117,9 +117,13 @@ namespace st{ const double i_muB = 1.0/9.274e-24; // J/T const double i_e = 1.0/1.60217662e-19; // electronic charge (Coulombs) - const double microcell_volume = (st::internal::micro_cell_size * - st::internal::micro_cell_size * - st::internal::micro_cell_thickness)*1.e-30; // m^3 + + //volume unit normalisation needs to be material dependent; hard coding here to make independent of microcell size + // (26-04-2026 JLR) + const double microcell_volume = 6.783e-29;//(st::internal::micro_cell_size * + // st::internal::micro_cell_size * + // st::internal::micro_cell_thickness)*1.e-30; // m^3 20 A x 20 A x 2.69 A = 1.076e-27 m^3 + //Mn muffin tin: 4/3 pi (2.53 A)^3 = 67.83 A^3 = 6.783e-29 m^3 // loop over all 1D stacks (in parallel) for(int stack=0; stack mp; // array of material properties + std::vector atom_sublattice; // array to store which sublattice each atom is in for spin transport calculation // enumerated list of different current directions current_direction_t current_direction = st::internal::pz; // current direction (default along +z direction) @@ -45,6 +47,9 @@ namespace spin_transport{ double cell_size_y = 10.0; // cell size along y-direction (1 nm default size) double cell_size_z = 10.0; // cell size along z-direction (1 nm default size) + int cell_increment = 1; // cell increment depending on positive or negative current direction + + unsigned int num_sublattices = 1; // number of sublattices unsigned int num_stacks = 0; // number of stacks perpendicular to current direction unsigned int total_num_cells = 0; // number of cells @@ -63,6 +68,8 @@ namespace spin_transport{ // arrays to store average resistance and spin resistance in each cell std::vector cell_resistance; std::vector cell_spin_resistance; + std::vector cell_sl_resistance; + std::vector cell_sl_spin_resistance; // arrays to store cell properties std::vector magnetic; // boolean array to determine if cell is magnetic or not @@ -74,6 +81,19 @@ namespace spin_transport{ std::vector cell_relaxation_torque_rj; // cell specific prefactors for spin-torque relaxation bj std::vector cell_precession_torque_pj; // cell specific prefactors for spin-torque precession aj + // material specific arrays to store cell properties, flat 1D layout: index = cell * num_sublattices + sl + std::vector sl_magnetic; // boolean array to determine if cell is magnetic or not, by material type + std::vector cell_sl_alpha; // cell magnetization (average of constituent atoms) + std::vector cell_sl_isaturation; // inverse magnetic saturation at T=0 in each cell + std::vector cell_sl_relaxation_torque_rj; // cell specific prefactors for spin-torque relaxation bj + std::vector cell_sl_precession_torque_pj; // cell specific prefactors for spin-torque precession aj + std::vector cell_sl_magnetization_x; // normalised magnetization of each material in each cell + std::vector cell_sl_magnetization_y; // normalised magnetization of each material in each cell + std::vector cell_sl_magnetization_z; // normalised magnetization of each material in each cell + std::vector cell_sl_spin_torque_fields_x; // array of cell spin torque fields x + std::vector cell_sl_spin_torque_fields_y; // array of cell spin torque fields y + std::vector cell_sl_spin_torque_fields_z; // array of cell spin torque fields z + // array to store which cell each atom is in std::vector atom_in_cell; diff --git a/src/spintransport/field.cpp b/src/spintransport/field.cpp index fed94ae9a..12aec6ac3 100644 --- a/src/spintransport/field.cpp +++ b/src/spintransport/field.cpp @@ -23,11 +23,12 @@ namespace spin_transport{ //--------------------------------------------------------------------------- // Function to calculate spin transfer torque field for each atom //--------------------------------------------------------------------------- - void calculate_field(const unsigned int start_index, // first atom - const unsigned int end_index, // last atom - std::vector& atoms_x_field_array, // x-field of atoms - std::vector& atoms_y_field_array, // y-field of atoms - std::vector& atoms_z_field_array // z-field of atoms + void calculate_field(const unsigned int start_index, // first atom + const unsigned int end_index, // last atom + std::vector& atoms_x_field_array, // x-field of atoms + std::vector& atoms_y_field_array, // y-field of atoms + std::vector& atoms_z_field_array, // z-field of atoms + const std::vector& atoms_material_array // material ID of atom ){ //------------------------------------------------------------------------- @@ -38,19 +39,40 @@ namespace spin_transport{ //--------------------------------------------------------------------------- // loop over all atoms and apply cell spin torque field //--------------------------------------------------------------------------- - for(unsigned int atom = start_index; atom < end_index; atom++){ + if(!st::internal::sublattice){ + for(unsigned int atom = start_index; atom < end_index; atom++){ - // get cell id - const uint64_t cell = st::internal::atom_in_cell[atom]; + // get cell id + const uint64_t cell = st::internal::atom_in_cell[atom]; - atoms_x_field_array[atom] += st::internal::cell_spin_torque_fields[3*cell+0]; - atoms_y_field_array[atom] += st::internal::cell_spin_torque_fields[3*cell+1]; - atoms_z_field_array[atom] += st::internal::cell_spin_torque_fields[3*cell+2]; + atoms_x_field_array[atom] += st::internal::cell_spin_torque_fields[3*cell+0]; + atoms_y_field_array[atom] += st::internal::cell_spin_torque_fields[3*cell+1]; + atoms_z_field_array[atom] += st::internal::cell_spin_torque_fields[3*cell+2]; + } + } + else{ + const int num_sublattices = st::internal::num_sublattices; + for(unsigned int atom = start_index; atom < end_index; atom++){ + + // get cell id + const uint64_t cell = st::internal::atom_in_cell[atom]; + + // get sublattice of atom + const int sl = st::internal::atom_sublattice[atom]; + + // flat index: sl is the fastest-varying index + const int idx = (int)cell * num_sublattices + sl; + + atoms_x_field_array[atom] += st::internal::cell_sl_spin_torque_fields_x[idx]; + atoms_y_field_array[atom] += st::internal::cell_sl_spin_torque_fields_y[idx]; + atoms_z_field_array[atom] += st::internal::cell_sl_spin_torque_fields_z[idx]; + + } } return; } -} +} // end of spin_transport namespace diff --git a/src/spintransport/initialize.cpp b/src/spintransport/initialize.cpp index 678e367ec..61d36f594 100644 --- a/src/spintransport/initialize.cpp +++ b/src/spintransport/initialize.cpp @@ -3,7 +3,7 @@ // This file is part of the VAMPIRE open source package under the // Free BSD licence (see licence file for details). // -// (c) Richard F L Evans 2019. All rights reserved. +// (c) Richard F L Evans 2019,2026. All rights reserved. // // Email: richard.evans@york.ac.uk // @@ -23,12 +23,50 @@ namespace spin_transport{ - // simple struct to store 3D cell info - struct cell3D_t{ - uint64_t id; // id of cell - std::vector atom; // list of atoms in each cell - std::vector nm_atom; // list of non-magnetic atoms in each cell - }; + namespace internal{ + // function forward declarations + internal::v3cell3D_t + initialize_stack(const double system_size_x, // maximum dimensions of system along x-direction (angstroms) + const double system_size_y, // maximum dimensions of system along y-direction (angstroms) + const double system_size_z, // maximum dimensions of system along z-direction (angstroms) + const uint64_t num_atoms, // number of local atoms + //const std::vector& atoms_type_array, // material types of atoms + const std::vector& atoms_x_coord_array, // x-coordinates of atoms + const std::vector& atoms_y_coord_array, // y-coordinates of atoms + const std::vector& atoms_z_coord_array, // z-coordinates of atoms + //const std::vector& atoms_m_spin_array, // moments of atoms (muB) + //const std::vector& material_damping_array, // array of material level damping constants + //const std::vector& is_magnetic_material, // array of size num_mat to state whether material is magnetic (true) or not (false) + const std::vector& non_magnetic_atoms_array, // list of non-magnetic atoms + const int stack_x, const int stack_y, const int stack_z // direction of stacks relative to current direction + ); + + void initialise_cell_data( + const std::vector& atoms_type_array, // material types of atoms + const std::vector& atoms_m_spin_array, // moments of atoms (muB) + const std::vector& material_damping_array, // array of material level damping constants + const std::vector& is_magnetic_material, // array of size num_mat to state whether material is magnetic (true) or not (false) + const std::vector& non_magnetic_atoms_array, // list of non-magnetic atoms + internal::v3cell3D_t& cells3D, // cell data + const int stack_x, const int stack_y, const int stack_z // direction of stacks relative to current direction + + ); + + void initialise_sublattice_cell_data( + const uint64_t num_atoms, // number of local atoms + const int num_materials, // number of materials + const std::vector& atoms_type_array, // material types of atoms + const std::vector& atoms_m_spin_array, // moments of atoms (muB) + const std::vector& material_damping_array, // array of material level damping constants + const std::vector& is_magnetic_material, // array of size num_mat to state whether material is magnetic (true) or not (false) + const std::vector& non_magnetic_atoms_array, // list of non-magnetic atoms + internal::v3cell3D_t& cells3D, // Cell data + const int stack_x, const int stack_y, const int stack_z // direction of stacks relative to current direction + ); + + void determine_stack_order(); + + } //---------------------------------------------------------------------------- // Function to initialize spin transport module @@ -45,33 +83,10 @@ namespace spin_transport{ const std::vector& atoms_m_spin_array, // moments of atoms (muB) const std::vector& material_damping_array, // array of material level damping constants const std::vector& is_magnetic_material, // array of size num_mat to state whether material is magnetic (true) or not (false) - const std::vector non_magnetic_atoms_array // list of non-magnetic atoms + const std::vector& non_magnetic_atoms_array // list of non-magnetic atoms ){ - //------------------------------------------------------------------------- - // create 3D array of cells - // - // | | | | | - // | | | | | - // | | | | | - // | | | | | - // | | | | | - // stacks_z | | | | | - // | | | | | - // (current- | | ^ | | | - // direction) | | | | | | - // | | | | | | - // | | | | | | - // - // stacks_x - // - // - // - // - // - // - // - //------------------------------------------------------------------------- + //------------------------------------------------------------------------- // check that module is needed - if not do nothing @@ -86,17 +101,9 @@ namespace spin_transport{ spin_transport::internal::mp.resize(num_materials); } - // copy cell sizes to local variable to enable arbitrary current direction - const double cell_size[3] = { st::internal::cell_size_x, - st::internal::cell_size_y, - st::internal::cell_size_z }; - - // calculate number of cells in each direction x,y,z (rounding up) - const int num_cells[3] = { static_cast( ceil(system_size_x/cell_size[0]) ), - static_cast( ceil(system_size_y/cell_size[1]) ), - static_cast( ceil(system_size_z/cell_size[2]) ) }; - - + //------------------------------------------------------------------------- + // determine stack direction + //------------------------------------------------------------------------- int stack_x = 0; // spatial direction of stack arrays x,y,z int stack_y = 1; // (each mapping into a physical direction) int stack_z = 2; @@ -115,441 +122,605 @@ namespace spin_transport{ stack_z = 1; // y-direction } + //------------------------------------------------------------------------- + // Initialise stack properties + //------------------------------------------------------------------------- + internal::v3cell3D_t cells3D = internal::initialize_stack(system_size_x, system_size_y, system_size_z, + num_atoms, atoms_x_coord_array, atoms_y_coord_array, + atoms_z_coord_array, non_magnetic_atoms_array, stack_x, stack_y, stack_z); + //--------------------------------------------------------------------------------- - // Calculate cell IDs in 3D to calculate atom-cell associations + // Calculate cell-specific data //--------------------------------------------------------------------------------- - std::vector< std::vector < std::vector > > cells3D; // 3D list of cell IDs - - int cell_id =0; - // resize cell array to hold stack x_cells - cells3D.resize(num_cells[stack_x]); + if(st::internal::sublattice) internal::initialise_sublattice_cell_data(num_atoms, num_materials, atoms_type_array, atoms_m_spin_array, + material_damping_array, is_magnetic_material, non_magnetic_atoms_array, + cells3D, stack_x, stack_y, stack_z); + else internal::initialise_cell_data(atoms_type_array, atoms_m_spin_array, material_damping_array, is_magnetic_material, non_magnetic_atoms_array, + cells3D, stack_x, stack_y, stack_z); - // loop over all x-cells in stack - for(unsigned int i = 0; i < cells3D.size(); i++){ - // resize cell array[i] to hold stack y_cells - cells3D[i].resize(num_cells[stack_y]); - - // loop over all y-cells in stack - for(unsigned int j = 0; j < cells3D[i].size(); j++){ - - // resize cell array[i][j] to hold stack z_cells (each stack linear in memory along z (current) direction) - cells3D[i][j].resize(num_cells[stack_z]); // - - // loop over all z-cells to set linear cell ID for cell arrays - for(unsigned int k = 0; k < cells3D[i][j].size(); k++){ - cells3D[i][j][k].id = cell_id; - cell_id++; // increment cell number - } + //--------------------------------------------------------------------------------- + // Determine the direction of the stack integration and parallelisation + //--------------------------------------------------------------------------------- + internal::determine_stack_order(); + //--------------------------------------------------------------------------------- + // Output cell data to file on rank0 and only if not sublattice calculation + //--------------------------------------------------------------------------------- + if( vmpi::my_rank == 0 && !st::internal::sublattice){ + std::ofstream ofile("spin_transport_cell_data.txt"); + for(uint64_t i =0; i< st::internal::total_num_cells; i++){ + ofile << st::internal::cell_position[3*i+0] << "\t" << + st::internal::cell_position[3*i+1] << "\t" << + st::internal::cell_position[3*i+2] << "\t" << + st::internal::cell_magnetization[3*i+0] << "\t" << + st::internal::cell_magnetization[3*i+1] << "\t" << + st::internal::cell_magnetization[3*i+2] << "\t" << + st::internal::cell_alpha[i] << "\t" << + st::internal::cell_resistance[i] << "\t" << + st::internal::cell_spin_resistance[i] << std::endl; } - + ofile.close(); } //--------------------------------------------------------------------------------- - // Calculate atom-cell associations and cell positions + // Output cell data to file on rank0 and only if sublattice calculation //--------------------------------------------------------------------------------- + if( vmpi::my_rank == 0 && st::internal::sublattice){ + const int num_sublattices = st::internal::num_sublattices; + std::ofstream ofile("spin_transport_cell_data.txt"); + for(uint64_t i =0; i< st::internal::total_num_cells; i++){ + ofile << st::internal::cell_position[3*i+0] << "\t" << + st::internal::cell_position[3*i+1] << "\t" << + st::internal::cell_position[3*i+2] << "\t" << + st::internal::cell_resistance[i] << "\t"; + + for( int sl = 0; sl < num_sublattices; sl++ ){ + + // flat index: sl is the fastest-varying index + const int idx = int(i * num_sublattices + sl); + + const double isat = st::internal::cell_sl_isaturation[idx]; + double mix = st::internal::cell_sl_magnetization_x[idx] * isat; + double miy = st::internal::cell_sl_magnetization_y[idx] * isat; + double miz = st::internal::cell_sl_magnetization_z[idx] * isat; + ofile << mix << "\t" << miy << "\t" << miz << "\t" << st::internal::cell_sl_alpha[idx] << "\t" << + st::internal::cell_sl_resistance[idx] << "\t" << + st::internal::cell_sl_spin_resistance[idx] << "\t"; + } + ofile << std::endl; + } + ofile.close(); + } - // resize array to store which cell each atom is in - st::internal::atom_in_cell.resize(num_atoms); + return; - for(uint64_t atom = 0; atom < num_atoms; atom++){ + } - // get atomic coordinates - const double cx = atoms_x_coord_array[atom]; - const double cy = atoms_y_coord_array[atom]; - const double cz = atoms_z_coord_array[atom]; + namespace internal{ + + // function to initialise stack properties including list of atoms in each + // cell, current direction and returns cell structure for initialization + internal::v3cell3D_t + initialize_stack(const double system_size_x, // maximum dimensions of system along x-direction (angstroms) + const double system_size_y, // maximum dimensions of system along y-direction (angstroms) + const double system_size_z, // maximum dimensions of system along z-direction (angstroms) + const uint64_t num_atoms, // number of local atoms + //const std::vector& atoms_type_array, // material types of atoms + const std::vector& atoms_x_coord_array, // x-coordinates of atoms + const std::vector& atoms_y_coord_array, // y-coordinates of atoms + const std::vector& atoms_z_coord_array, // z-coordinates of atoms + //const std::vector& atoms_m_spin_array, // moments of atoms (muB) + //const std::vector& material_damping_array, // array of material level damping constants + //const std::vector& is_magnetic_material, // array of size num_mat to state whether material is magnetic (true) or not (false) + const std::vector& non_magnetic_atoms_array, // list of non-magnetic atoms + const int stack_x, const int stack_y, const int stack_z // direction of stacks relative to current direction + ){ + //------------------------------------------------------------------------- + // create 3D array of cells + // + // | | | | | + // | | | | | + // | | | | | + // | | | | | + // | | | | | + // stacks_z | | | | | + // | | | | | + // (current- | | ^ | | | + // direction) | | | | | | + // | | | | | | + // | | | | | | + // + // stacks_x + // + // + // + // + // + // + // + //------------------------------------------------------------------------- + + // copy cell sizes to local variable to enable arbitrary current direction + const double cell_size[3] = { st::internal::cell_size_x, + st::internal::cell_size_y, + st::internal::cell_size_z }; + + // calculate number of cells in each direction x,y,z (rounding up) + const int num_cells[3] = { static_cast( ceil(system_size_x/cell_size[0]) ), + static_cast( ceil(system_size_y/cell_size[1]) ), + static_cast( ceil(system_size_z/cell_size[2]) ) }; + + //--------------------------------------------------------------------------------- + // Calculate cell IDs in 3D to calculate atom-cell associations + //--------------------------------------------------------------------------------- + std::vector< std::vector < std::vector > > cells3D; // 3D list of cell IDs + + int cell_id =0; + + // resize cell array to hold stack x_cells + cells3D.resize(num_cells[stack_x]); + + // loop over all x-cells in stack + for(unsigned int i = 0; i < cells3D.size(); i++){ + + // resize cell array[i] to hold stack y_cells + cells3D[i].resize(num_cells[stack_y]); + + // loop over all y-cells in stack + for(unsigned int j = 0; j < cells3D[i].size(); j++){ + + // resize cell array[i][j] to hold stack z_cells (each stack linear in memory along z (current) direction) + cells3D[i][j].resize(num_cells[stack_z]); // + + // loop over all z-cells to set linear cell ID for cell arrays + for(unsigned int k = 0; k < cells3D[i][j].size(); k++){ + cells3D[i][j][k].id = cell_id; + cell_id++; // increment cell number + } - // calculate 3D cell ID in atoms coordinate system - unsigned int cellID[3] = { static_cast( cx / cell_size[0] ), - static_cast( cy / cell_size[1] ), - static_cast( cz / cell_size[2] ) }; + } - // now calculate in stack coordinate system - const uint64_t i = cellID[stack_x]; - const uint64_t j = cellID[stack_y]; - const uint64_t k = cellID[stack_z]; + } - // associate atom with stack cell ID - st::internal::atom_in_cell[atom] = cells3D[i][j][k].id; + //--------------------------------------------------------------------------------- + // Calculate atom-cell associations and cell positions + //--------------------------------------------------------------------------------- - // add atom to atoms in cell list - cells3D[i][j][k].atom.push_back(atom); + // resize array to store which cell each atom is in + st::internal::atom_in_cell.resize(num_atoms); - } + for(uint64_t atom = 0; atom < num_atoms; atom++){ - // include non-magnetic atoms - const uint64_t num_nm_atoms = non_magnetic_atoms_array.size(); - for(uint64_t atom = 0; atom < num_nm_atoms; atom++){ + // get atomic coordinates + const double cx = atoms_x_coord_array[atom]; + const double cy = atoms_y_coord_array[atom]; + const double cz = atoms_z_coord_array[atom]; - // get atomic coordinates - const double cx = non_magnetic_atoms_array[atom].x; - const double cy = non_magnetic_atoms_array[atom].y; - const double cz = non_magnetic_atoms_array[atom].z; + // calculate 3D cell ID in atoms coordinate system + unsigned int cellID[3] = { static_cast( cx / cell_size[0] ), + static_cast( cy / cell_size[1] ), + static_cast( cz / cell_size[2] ) }; - // calculate 3D cell ID in atoms coordinate system - unsigned int cellID[3] = { static_cast( cx / cell_size[0] ), - static_cast( cy / cell_size[1] ), - static_cast( cz / cell_size[2] ) }; + // now calculate in stack coordinate system + const uint64_t i = cellID[stack_x]; + const uint64_t j = cellID[stack_y]; + const uint64_t k = cellID[stack_z]; - // now calculate in stack coordinate system - const uint64_t i = cellID[stack_x]; - const uint64_t j = cellID[stack_y]; - const uint64_t k = cellID[stack_z]; + // associate atom with stack cell ID + st::internal::atom_in_cell[atom] = cells3D[i][j][k].id; - // add atom to atoms in non-magnetic cell list - cells3D[i][j][k].nm_atom.push_back(atom); + // add atom to atoms in cell list + cells3D[i][j][k].atom.push_back(atom); - } + } - //--------------------------------------------------------------------------------- - // Calculate stack data - //--------------------------------------------------------------------------------- + // include non-magnetic atoms + const uint64_t num_nm_atoms = non_magnetic_atoms_array.size(); + for(uint64_t atom = 0; atom < num_nm_atoms; atom++){ - // calculate number of stacks (x*y) - st::internal::num_stacks = num_cells[stack_x]*num_cells[stack_y]; - const uint64_t num_cells_in_stack = num_cells[stack_z]; // save number of cells in stack along current direction + // get atomic coordinates + const double cx = non_magnetic_atoms_array[atom].x; + const double cy = non_magnetic_atoms_array[atom].y; + const double cz = non_magnetic_atoms_array[atom].z; - // resize limits for each stack - st::internal::stack_start_index.resize(st::internal::num_stacks); - st::internal::stack_final_index.resize(st::internal::num_stacks); - st::internal::stack_resistance.resize(st::internal::num_stacks, 0.0); // total resistance in each stack - st::internal::stack_current.resize(st::internal::num_stacks, 0.0); // total current in each stack + // calculate 3D cell ID in atoms coordinate system + unsigned int cellID[3] = { static_cast( cx / cell_size[0] ), + static_cast( cy / cell_size[1] ), + static_cast( cz / cell_size[2] ) }; - // determine initial start and end cell of each stack - for(uint64_t s = 0; s < st::internal::num_stacks; s++){ - st::internal::stack_start_index[s] = s*num_cells_in_stack; - st::internal::stack_final_index[s] = s*num_cells_in_stack + num_cells_in_stack; // loop to less than this number - } + // now calculate in stack coordinate system + const uint64_t i = cellID[stack_x]; + const uint64_t j = cellID[stack_y]; + const uint64_t k = cellID[stack_z]; + + // add atom to atoms in non-magnetic cell list + cells3D[i][j][k].nm_atom.push_back(atom); - // calculate total number of cells - st::internal::total_num_cells = num_cells[0]*num_cells[1]*num_cells[2]; + } + //--------------------------------------------------------------------------------- + // Calculate stack data + //--------------------------------------------------------------------------------- - //--------------------------------------------------------------------------------- - // Accumulate derived cell data - //--------------------------------------------------------------------------------- + // calculate number of stacks (x*y) + st::internal::num_stacks = num_cells[stack_x]*num_cells[stack_y]; + const uint64_t num_cells_in_stack = num_cells[stack_z]; // save number of cells in stack along current direction - // resize boolean array to determine if cell is magnetic or not - st::internal::magnetic.resize(st::internal::total_num_cells, true); // initially assume all cells magnetic - - // resize arrays to store average resistance in each cell - st::internal::cell_resistance.resize(st::internal::total_num_cells,0.0); // also stores accumulated resistance before averaging - st::internal::cell_spin_resistance.resize(st::internal::total_num_cells,0.0); // also stores accumulated spin resistance before averaging - - // resize array to store average damping constant alpha in each cell - st::internal::cell_alpha.resize(st::internal::total_num_cells,0.0); // also stores accumulated resistance before averaging - - // resize array to store inverse total moment m_s (T = 0) - st::internal::cell_isaturation.resize(st::internal::total_num_cells,1.0); // also stores accumulated moment before inversion - - // resize arrays to store slonczewski prefactors - st::internal::cell_relaxation_torque_rj.resize(st::internal::total_num_cells,0.0); // also stores accumulated parameters before averaging - st::internal::cell_precession_torque_pj.resize(st::internal::total_num_cells,0.0); // also stores accumulated parameters before averaging - - // Temporary arrays to accumulate intermediate values - std::vector total_num_atoms_in_cell( st::internal::total_num_cells, 0 ); // array to store total number of magnetic and non-magnetic atoms in cell - std::vector total_num_magnetic_atoms( st::internal::total_num_cells, 0 ); // array to store total number of actually magnetic atoms (ignoring nm = keep atoms) [double format for easy normalisation] - std::vector total_resistivity_sq( st::internal::total_num_cells, 0.0); // array to store total resistivity^2 calculated from constituent atoms - std::vector total_spin_resistivity_sq( st::internal::total_num_cells, 0.0); // array to store total spin resistivity^2 calculated from constituent atoms - - // loop over all xy-cells (stacks) - for(unsigned int i = 0; i < cells3D.size(); i++){ - for(unsigned int j = 0; j < cells3D[i].size(); j++){ - - // loop over all cells in stack - for(unsigned int k = 0; k < cells3D[i][j].size(); k++){ - - // determine cell ID - const uint64_t cell = cells3D[i][j][k].id; - - // determine total number of local atoms - uint64_t num_atoms_in_cell = cells3D[i][j][k].atom.size() + cells3D[i][j][k].nm_atom.size(); - - // add contributions from atoms to calculate average resistivity - // Impurities in metals have a disproportionate effect on the resistance and so we approximate the - // average resistance of the cell as - // - // R1^2 + R2^2 + ... + Rn^2 - // R_eff = -------------------------- - // (R1 + R2 + ... Rn) - // - // so that the larger resistance values always dominate, even for small impurity amounts. - // Net effect is then similar for M/Ox interfaces in series, where the oxide dominates the resistance. - - // variables to accumulate resistances for cell - double resistivity = 0.0; - double resistivity_sq = 0.0; - double spin_resistivity = 0.0; - double spin_resistivity_sq = 0.0; - double total_moment = 0.0; - double total_alpha = 0.0; // damping constant - double total_rj = 0.0; // spin torque prefactor parameter - double total_pj = 0.0; // spin torque prefactor parameter - - // check for cells with only non-magnetic atoms = keep - uint64_t num_magnetic_atoms = 0.0; // counter for number of actually magnetic atoms - - // magnetic atoms - for(unsigned int atom = 0; atom < cells3D[i][j][k].atom.size(); atom++ ){ - // get atom number in total list - int atomID = cells3D[i][j][k].atom[atom]; - // get atom material - int mat = atoms_type_array[atomID]; // get material type - - // add resistivities - resistivity += st::internal::mp[mat].resistivity.get(); // add resistivity to total - resistivity_sq += st::internal::mp[mat].resistivity.get() * st::internal::mp[mat].resistivity.get(); // add resistivity^2 to total - spin_resistivity += st::internal::mp[mat].spin_resistivity.get(); // add spin resistivity to total - spin_resistivity_sq += st::internal::mp[mat].spin_resistivity.get() * st::internal::mp[mat].spin_resistivity.get(); // add spin resistivity^2 to total - - // check that atom is magnetic - if(is_magnetic_material[mat] == true){ - - // increment number of magnetic atoms counter - num_magnetic_atoms += 1.0; - - // calculate total moment - total_moment += atoms_m_spin_array[atomID]; - total_alpha += material_damping_array[mat]; - - // calculate total prefactor parameters - total_rj += st::internal::mp[mat].stt_rj.get(); - total_pj += st::internal::mp[mat].stt_pj.get(); + // resize limits for each stack + st::internal::stack_start_index.resize(st::internal::num_stacks); + st::internal::stack_final_index.resize(st::internal::num_stacks); + st::internal::stack_resistance.resize(st::internal::num_stacks, 0.0); // total resistance in each stack + st::internal::stack_current.resize(st::internal::num_stacks, 0.0); // total current in each stack - } + // determine initial start and end cell of each stack + for(uint64_t s = 0; s < st::internal::num_stacks; s++){ + st::internal::stack_start_index[s] = s*num_cells_in_stack; + st::internal::stack_final_index[s] = s*num_cells_in_stack + num_cells_in_stack - 1; // loop to less than or equal to this number + } - } + //--------------------------------------------------------------------------------- + // set up current direction for increment + //--------------------------------------------------------------------------------- + if(st::internal::current_direction == st::internal::px || + st::internal::current_direction == st::internal::py || + st::internal::current_direction == st::internal::pz){ + // positive loop for the current + st::internal::cell_increment = +1; + } + else{ + // negative loop for the current + st::internal::cell_increment = -1; + // loop over stacks and swap start and end cell IDs + for(uint64_t s = 0; s < st::internal::num_stacks; s++){ + int ssi = st::internal::stack_start_index[s]; + int sfi = st::internal::stack_final_index[s]; + st::internal::stack_start_index[s] = sfi; + st::internal::stack_final_index[s] = ssi; + } - // non-magnetic (remove) atoms - for(unsigned int atom = 0; atom < cells3D[i][j][k].nm_atom.size(); atom++ ){ - int mat = non_magnetic_atoms_array[atom].mat; // get material type - resistivity += st::internal::mp[mat].resistivity.get(); // add resistivity to total - resistivity_sq += st::internal::mp[mat].resistivity.get() * st::internal::mp[mat].resistivity.get(); // add resistivity^2 to total - spin_resistivity += st::internal::mp[mat].spin_resistivity.get(); // add spin resistivity to total - spin_resistivity_sq += st::internal::mp[mat].spin_resistivity.get() * st::internal::mp[mat].spin_resistivity.get(); // add spin resistivity^2 to total - } + } - // save accumulated values - st::internal::cell_resistance[cell] = resistivity; - st::internal::cell_spin_resistance[cell] = spin_resistivity; - st::internal::cell_relaxation_torque_rj[cell] = total_rj; - st::internal::cell_precession_torque_pj[cell] = total_pj; - st::internal::cell_isaturation[cell] = total_moment; // (mu_B) - st::internal::cell_alpha[cell] = total_alpha; // total damping - - total_num_atoms_in_cell[cell] = num_atoms_in_cell; // store total number of magnetic and non-magnetic atoms in cell - total_num_magnetic_atoms[cell] = num_magnetic_atoms; // store total number of magnetic atoms in cell in double format for normalisation - total_resistivity_sq[cell] = resistivity_sq; // store total resistivity^2 calculated from constituent atoms - total_spin_resistivity_sq[cell] = spin_resistivity_sq; // store total spin resistivity^2 calculated from constituent atoms - - } // end of cell loop - } // end of stack y loop - } // end of stack x loop - - //----------------------------------------------------------------------------------------------- - // reduce on all processors to enable unique determination of empty cells and average parameters - //----------------------------------------------------------------------------------------------- - #ifdef MPICF - // cast to int for MPI - int bufsize = st::internal::total_num_cells; - // reduce all cell totals onto all processors - MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_resistance[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_spin_resistance[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_relaxation_torque_rj[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_precession_torque_pj[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_isaturation[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_alpha[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &total_num_magnetic_atoms[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &total_num_atoms_in_cell[0], bufsize, MPI_UINT64_T, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &total_resistivity_sq[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - MPI_Allreduce(MPI_IN_PLACE, &total_spin_resistivity_sq[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - #endif - - //----------------------------------------------------------------------------------------------- - // Calculate average cell parameters - //----------------------------------------------------------------------------------------------- - - // cell size parameters for resitivity to resistance calculation - const double iA = 1.0 / (cell_size[stack_x] * cell_size[stack_y]); // Angstroms^-2 - const double l = cell_size[stack_z]; - - // constant prefactor for STT components - const double one_o_gamma_e = 1.0 / (1.76e11 * 1.602e-19); // muB / (gamma * e) but cell saturation already specified in muB's - - // loop over all xy-cells (stacks) - for(unsigned int i = 0; i < cells3D.size(); i++){ - for(unsigned int j = 0; j < cells3D[i].size(); j++){ - - // loop over all cells in stack - for(unsigned int k = 0; k < cells3D[i][j].size(); k++){ - - // determine cell ID - const uint64_t cell = cells3D[i][j][k].id; - - // determine total number of atoms on all processors - uint64_t num_atoms_in_cell = total_num_atoms_in_cell[cell]; - - // if cell is empty space assume uniform padding resistance - if(num_atoms_in_cell == 0){ - st::internal::cell_resistance[cell] = st::internal::environment_resistivity * l * iA; - st::internal::cell_spin_resistance[cell] = st::internal::environment_resistivity * l * iA; - st::internal::cell_isaturation[cell] = 0.0; // set inverse saturation to 0.0 - st::internal::magnetic[cell] = false; // set cell as non-magnetic - } - // otherwise add contributions from atoms to calculate average resistivity - else{ + // calculate total number of cells + st::internal::total_num_cells = num_cells[0]*num_cells[1]*num_cells[2]; - // check for empty cells or cells with only non-magnetic atoms (keep) and if so treat as non-magnetic - if(total_num_magnetic_atoms[cell] < 0.1){ - st::internal::magnetic[cell] = false; // no magnetic atoms -> non-magnetic cell - st::internal::cell_isaturation[cell] = 1.0; // assume 1 so inverse is still 1 (any value is fine but needs to be > 0) - } - else{ - // calculate mean spin torque prefactor parameters for magnetic cells - const double count = total_num_magnetic_atoms[cell]; // number of magnetic atoms in cell - const double total_moment = st::internal::cell_isaturation[cell]; // total magnetic moment - st::internal::cell_relaxation_torque_rj[cell] = st::internal::cell_relaxation_torque_rj[cell] * one_o_gamma_e / (count * total_moment); - st::internal::cell_precession_torque_pj[cell] = st::internal::cell_precession_torque_pj[cell] * one_o_gamma_e / (count * total_moment); - st::internal::cell_alpha[cell] = st::internal::cell_alpha[cell] / count; - } + return cells3D; - // calculate average resistivity - double mean_resistivity = 0.0; // variable to store mean resistivity in cell - double mean_spin_resistivity = 0.0; // variable to store mean spin resistivity in cell - // if resistance is non-zero, work out average - if( st::internal::cell_resistance[cell] > 0.0 ) mean_resistivity = total_resistivity_sq[cell] / st::internal::cell_resistance[cell]; - if( st::internal::cell_spin_resistance[cell] > 0.0 ) mean_spin_resistivity = total_spin_resistivity_sq[cell] / st::internal::cell_spin_resistance[cell]; + } - // set cell resistance - st::internal::cell_resistance[cell] = mean_resistivity * l * iA; - st::internal::cell_spin_resistance[cell] = mean_spin_resistivity * l * iA; + //-------------------------------------------------------------------------------- + // Function to generate cell data in the case of no sublattice resolved torques + //-------------------------------------------------------------------------------- + void initialise_cell_data( + const std::vector& atoms_type_array, // material types of atoms + const std::vector& atoms_m_spin_array, // moments of atoms (muB) + const std::vector& material_damping_array, // array of material level damping constants + const std::vector& is_magnetic_material, // array of size num_mat to state whether material is magnetic (true) or not (false) + const std::vector& non_magnetic_atoms_array, // list of non-magnetic atoms + internal::v3cell3D_t& cells3D, // Cell data + const int stack_x, const int stack_y, const int stack_z // direction of stacks relative to current direction + + ){ + + // copy cell sizes to local variable + const double cell_size[3] = { st::internal::cell_size_x, + st::internal::cell_size_y, + st::internal::cell_size_z }; + + // resize boolean array to determine if cell is magnetic or not + st::internal::magnetic.resize(st::internal::total_num_cells, true); // initially assume all cells magnetic + + // resize arrays to store average resistance in each cell + st::internal::cell_resistance.resize(st::internal::total_num_cells,0.0); // also stores accumulated resistance before averaging + st::internal::cell_spin_resistance.resize(st::internal::total_num_cells,0.0); // also stores accumulated spin resistance before averaging + + // resize array to store average damping constant alpha in each cell + st::internal::cell_alpha.resize(st::internal::total_num_cells,0.0); // also stores accumulated resistance before averaging + + // resize array to store inverse total moment m_s (T = 0) + st::internal::cell_isaturation.resize(st::internal::total_num_cells,1.0); // also stores accumulated moment before inversion + + // resize arrays to store slonczewski prefactors + st::internal::cell_relaxation_torque_rj.resize(st::internal::total_num_cells,0.0); // also stores accumulated parameters before averaging + st::internal::cell_precession_torque_pj.resize(st::internal::total_num_cells,0.0); // also stores accumulated parameters before averaging + + // Temporary arrays to accumulate intermediate values + std::vector total_num_atoms_in_cell( st::internal::total_num_cells, 0 ); // array to store total number of magnetic and non-magnetic atoms in cell + std::vector total_num_magnetic_atoms( st::internal::total_num_cells, 0 ); // array to store total number of actually magnetic atoms (ignoring nm = keep atoms) [double format for easy normalisation] + std::vector total_resistivity_sq( st::internal::total_num_cells, 0.0); // array to store total resistivity^2 calculated from constituent atoms + std::vector total_spin_resistivity_sq( st::internal::total_num_cells, 0.0); // array to store total spin resistivity^2 calculated from constituent atoms + + // loop over all xy-cells (stacks) + for(unsigned int i = 0; i < cells3D.size(); i++){ + for(unsigned int j = 0; j < cells3D[i].size(); j++){ + + // loop over all cells in stack + for(unsigned int k = 0; k < cells3D[i][j].size(); k++){ + + // determine cell ID + const uint64_t cell = cells3D[i][j][k].id; + + // determine total number of local atoms + uint64_t num_atoms_in_cell = cells3D[i][j][k].atom.size() + cells3D[i][j][k].nm_atom.size(); + + // add contributions from atoms to calculate average resistivity + // Impurities in metals have a disproportionate effect on the resistance and so we approximate the + // average resistance of the cell as + // + // R1^2 + R2^2 + ... + Rn^2 + // R_eff = -------------------------- + // (R1 + R2 + ... Rn) + // + // so that the larger resistance values always dominate, even for small impurity amounts. + // Net effect is then similar for M/Ox interfaces in series, where the oxide dominates the resistance. + + // variables to accumulate resistances for cell + double resistivity = 0.0; + double resistivity_sq = 0.0; + double spin_resistivity = 0.0; + double spin_resistivity_sq = 0.0; + double total_moment = 0.0; + double total_alpha = 0.0; // damping constant + double total_rj = 0.0; // spin torque prefactor parameter + double total_pj = 0.0; // spin torque prefactor parameter + + // check for cells with only non-magnetic atoms = keep + uint64_t num_magnetic_atoms = 0.0; // counter for number of actually magnetic atoms + + // magnetic atoms + for(unsigned int atom = 0; atom < cells3D[i][j][k].atom.size(); atom++ ){ + // get atom number in total list + int atomID = cells3D[i][j][k].atom[atom]; + // get atom material + int mat = atoms_type_array[atomID]; // get material type + + // add resistivities + resistivity += st::internal::mp[mat].resistivity.get(); // add resistivity to total + resistivity_sq += st::internal::mp[mat].resistivity.get() * st::internal::mp[mat].resistivity.get(); // add resistivity^2 to total + spin_resistivity += st::internal::mp[mat].spin_resistivity.get(); // add spin resistivity to total + spin_resistivity_sq += st::internal::mp[mat].spin_resistivity.get() * st::internal::mp[mat].spin_resistivity.get(); // add spin resistivity^2 to total + + // check that atom is magnetic + if(is_magnetic_material[mat] == true){ + + // increment number of magnetic atoms counter + num_magnetic_atoms += 1.0; + + // calculate total moment + total_moment += atoms_m_spin_array[atomID]; + total_alpha += material_damping_array[mat]; + + // calculate total prefactor parameters + total_rj += st::internal::mp[mat].stt_rj.get(); + total_pj += st::internal::mp[mat].stt_pj.get(); + + } - // set inverse total moment - st::internal::cell_isaturation[cell] = 1.0/st::internal::cell_isaturation[cell]; // (1/mu_B) + } - // set inverse saturation of non-magnetic cells to zero - if(total_num_magnetic_atoms[cell] < 0.1){ - st::internal::cell_isaturation[cell] = 0.0; + // non-magnetic (remove) atoms + for(unsigned int atom = 0; atom < cells3D[i][j][k].nm_atom.size(); atom++ ){ + int mat = non_magnetic_atoms_array[atom].mat; // get material type + resistivity += st::internal::mp[mat].resistivity.get(); // add resistivity to total + resistivity_sq += st::internal::mp[mat].resistivity.get() * st::internal::mp[mat].resistivity.get(); // add resistivity^2 to total + spin_resistivity += st::internal::mp[mat].spin_resistivity.get(); // add spin resistivity to total + spin_resistivity_sq += st::internal::mp[mat].spin_resistivity.get() * st::internal::mp[mat].spin_resistivity.get(); // add spin resistivity^2 to total } - } + // save accumulated values + st::internal::cell_resistance[cell] = resistivity; + st::internal::cell_spin_resistance[cell] = spin_resistivity; + st::internal::cell_relaxation_torque_rj[cell] = total_rj; + st::internal::cell_precession_torque_pj[cell] = total_pj; + st::internal::cell_isaturation[cell] = total_moment; // (mu_B) + st::internal::cell_alpha[cell] = total_alpha; // total damping + + total_num_atoms_in_cell[cell] = num_atoms_in_cell; // store total number of magnetic and non-magnetic atoms in cell + total_num_magnetic_atoms[cell] = num_magnetic_atoms; // store total number of magnetic atoms in cell in double format for normalisation + total_resistivity_sq[cell] = resistivity_sq; // store total resistivity^2 calculated from constituent atoms + total_spin_resistivity_sq[cell] = spin_resistivity_sq; // store total spin resistivity^2 calculated from constituent atoms + + } // end of cell loop + } // end of stack y loop + } // end of stack x loop + + //----------------------------------------------------------------------------------------------- + // reduce on all processors to enable unique determination of empty cells and average parameters + //----------------------------------------------------------------------------------------------- + #ifdef MPICF + // cast to int for MPI + int bufsize = st::internal::total_num_cells; + // reduce all cell totals onto all processors + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_resistance[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_spin_resistance[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_relaxation_torque_rj[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_precession_torque_pj[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_isaturation[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_alpha[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &total_num_magnetic_atoms[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &total_num_atoms_in_cell[0], bufsize, MPI_UINT64_T, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &total_resistivity_sq[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &total_spin_resistivity_sq[0], bufsize, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + #endif + + //----------------------------------------------------------------------------------------------- + // Calculate average cell parameters + //----------------------------------------------------------------------------------------------- + + // cell size parameters for resitivity to resistance calculation + const double iA = 1.0 / (cell_size[stack_x] * cell_size[stack_y]); // Angstroms^-2 + const double l = cell_size[stack_z]; + + // constant prefactor for STT components + //const double one_o_gamma_e = 1.0 / (1.76e11 * 1.602e-19); // muB / (gamma * e) but cell saturation already specified in muB's + + // loop over all xy-cells (stacks) + for(unsigned int i = 0; i < cells3D.size(); i++){ + for(unsigned int j = 0; j < cells3D[i].size(); j++){ + + // loop over all cells in stack + for(unsigned int k = 0; k < cells3D[i][j].size(); k++){ + + // determine cell ID + const uint64_t cell = cells3D[i][j][k].id; + + // determine total number of atoms on all processors + uint64_t num_atoms_in_cell = total_num_atoms_in_cell[cell]; + + // if cell is empty space assume uniform padding resistance + if(num_atoms_in_cell == 0){ + st::internal::cell_resistance[cell] = st::internal::environment_resistivity * l * iA; + st::internal::cell_spin_resistance[cell] = st::internal::environment_resistivity * l * iA; + st::internal::cell_isaturation[cell] = 0.0; // set inverse saturation to 0.0 + st::internal::magnetic[cell] = false; // set cell as non-magnetic + } + // otherwise add contributions from atoms to calculate average resistivity + else{ - } // end of cell loop - } // end of stack y loop - } // end of stack x loop + // check for empty cells or cells with only non-magnetic atoms (keep) and if so treat as non-magnetic + if(total_num_magnetic_atoms[cell] < 0.1){ + st::internal::magnetic[cell] = false; // no magnetic atoms -> non-magnetic cell + st::internal::cell_isaturation[cell] = 1.0; // assume 1 so inverse is still 1 (any value is fine but needs to be > 0) + } + else{ + // calculate mean spin torque prefactor parameters for magnetic cells + const double count = total_num_magnetic_atoms[cell]; // number of magnetic atoms in cell + //const double total_moment = st::internal::cell_isaturation[cell]; // total magnetic moment + //st::internal::cell_relaxation_torque_rj[cell] = st::internal::cell_relaxation_torque_rj[cell] * one_o_gamma_e / (count * total_moment); + st::internal::cell_relaxation_torque_rj[cell] = st::internal::cell_relaxation_torque_rj[cell] / count ; + //st::internal::cell_precession_torque_pj[cell] = st::internal::cell_precession_torque_pj[cell] * one_o_gamma_e / (count * total_moment); + st::internal::cell_precession_torque_pj[cell] = st::internal::cell_precession_torque_pj[cell] / count; + st::internal::cell_alpha[cell] = st::internal::cell_alpha[cell] / count; + } + + // calculate average resistivity + double mean_resistivity = 0.0; // variable to store mean resistivity in cell + double mean_spin_resistivity = 0.0; // variable to store mean spin resistivity in cell + // if resistance is non-zero, work out average + if( st::internal::cell_resistance[cell] > 0.0 ) mean_resistivity = total_resistivity_sq[cell] / st::internal::cell_resistance[cell]; + if( st::internal::cell_spin_resistance[cell] > 0.0 ) mean_spin_resistivity = total_spin_resistivity_sq[cell] / st::internal::cell_spin_resistance[cell]; + + // set cell resistance + st::internal::cell_resistance[cell] = mean_resistivity * l * iA; + st::internal::cell_spin_resistance[cell] = mean_spin_resistivity * l * iA; + + // set inverse total moment + st::internal::cell_isaturation[cell] = 1.0/st::internal::cell_isaturation[cell]; // (1/mu_B) + + // set inverse saturation of non-magnetic cells to zero + if(total_num_magnetic_atoms[cell] < 0.1){ + st::internal::cell_isaturation[cell] = 0.0; + } - //------------------------------------------------------------------------ - // Calculate cell data - //------------------------------------------------------------------------ - st::internal::cell_position.resize(3*st::internal::total_num_cells); - - // convert to system coordinate system - // translation from system coordinates to cell coordinates - const int stack[3] = { stack_x, stack_y, stack_z }; - // example 1: 2 0 1 - // example 2: 0 1 2 - // example 3: 1 2 0 - - // translation from cell coordinates to system coordinates - // example 1: istack[3] 1 2 0 - // example 2: istack[3] 0 1 2 - // example 3: istack[3] 2 0 1 - - // calculate inverse stack relations - int istack[3]; - for(int i=0; i<3; i++){ - if(stack[i] == 0) istack[0] = i; - if(stack[i] == 1) istack[1] = i; - if(stack[i] == 2) istack[2] = i; - } + } - //std::cout << " stack: " << stack[0] << "\t" << stack[1] << "\t" << stack[2] << std::endl; - //std::cout << "istack: " << istack[0] << "\t" << istack[1] << "\t" << istack[2] << std::endl; + } // end of cell loop + } // end of stack y loop + } // end of stack x loop + + //------------------------------------------------------------------------ + // Calculate cell data + //------------------------------------------------------------------------ + st::internal::cell_position.resize(3*st::internal::total_num_cells); + + // convert to system coordinate system + // translation from system coordinates to cell coordinates + const int stack[3] = { stack_x, stack_y, stack_z }; + // example 1: 2 0 1 + // example 2: 0 1 2 + // example 3: 1 2 0 + + // translation from cell coordinates to system coordinates + // example 1: istack[3] 1 2 0 + // example 2: istack[3] 0 1 2 + // example 3: istack[3] 2 0 1 + + // calculate inverse stack relations + int istack[3]; + for(int i=0; i<3; i++){ + if(stack[i] == 0) istack[0] = i; + if(stack[i] == 1) istack[1] = i; + if(stack[i] == 2) istack[2] = i; + } + + //std::cout << " stack: " << stack[0] << "\t" << stack[1] << "\t" << stack[2] << std::endl; + //std::cout << "istack: " << istack[0] << "\t" << istack[1] << "\t" << istack[2] << std::endl; - // loop over all xy-cells (stacks) - for(unsigned int i = 0; i < cells3D.size(); i++){ - for(unsigned int j = 0; j < cells3D[i].size(); j++){ + // loop over all xy-cells (stacks) + for(unsigned int i = 0; i < cells3D.size(); i++){ + for(unsigned int j = 0; j < cells3D[i].size(); j++){ - // loop over all cells in stack - for(unsigned int k = 0; k < cells3D[i][j].size(); k++){ + // loop over all cells in stack + for(unsigned int k = 0; k < cells3D[i][j].size(); k++){ - // determine cell ID - const uint64_t cell = cells3D[i][j][k].id; + // determine cell ID + const uint64_t cell = cells3D[i][j][k].id; - // determine cell counts in each direction - unsigned int xyz[3] = { i, j, k}; + // determine cell counts in each direction + unsigned int xyz[3] = { i, j, k}; - // calculate cell coordinates - const double x = double(xyz[istack[0]]) * cell_size[0]; - const double y = double(xyz[istack[1]]) * cell_size[1]; - const double z = double(xyz[istack[2]]) * cell_size[2]; + // calculate cell coordinates + const double x = double(xyz[istack[0]]) * cell_size[0]; + const double y = double(xyz[istack[1]]) * cell_size[1]; + const double z = double(xyz[istack[2]]) * cell_size[2]; - st::internal::cell_position[3*cell+0] = x; - st::internal::cell_position[3*cell+1] = y; - st::internal::cell_position[3*cell+2] = z; + st::internal::cell_position[3*cell+0] = x; + st::internal::cell_position[3*cell+1] = y; + st::internal::cell_position[3*cell+2] = z; + } } } + + //------------------------------------------------------------------------ + // resize cell vector data arrays (3N) and set to zero + //------------------------------------------------------------------------ + st::internal::cell_magnetization.resize(3*st::internal::total_num_cells, 0.0); + st::internal::cell_spin_torque_fields.resize(3*st::internal::total_num_cells, 0.0); + + return; + } //------------------------------------------------------------------------ // determine first and last stack to be computed on local processor //------------------------------------------------------------------------ - #ifdef MPICF - const unsigned int num_local_stacks = st::internal::num_stacks / vmpi::num_processors; - spin_transport::internal::first_stack = vmpi::my_rank * num_local_stacks; - spin_transport::internal::last_stack = (vmpi::my_rank+1) * num_local_stacks; - // add all surplus points to last processor - if( vmpi::my_rank == vmpi::num_processors - 1){ - spin_transport::internal::last_stack = st::internal::num_stacks; - } - // check for more processors than stacks, if so allocate one stack per processor - if( st::internal::num_stacks < vmpi::num_processors ){ - if( vmpi::my_rank < st::internal::num_stacks){ - spin_transport::internal::first_stack = vmpi::my_rank; - spin_transport::internal::last_stack = vmpi::my_rank + 1; + void determine_stack_order(){ + + #ifdef MPICF + const unsigned int num_local_stacks = st::internal::num_stacks / vmpi::num_processors; + spin_transport::internal::first_stack = vmpi::my_rank * num_local_stacks; + spin_transport::internal::last_stack = (vmpi::my_rank+1) * num_local_stacks; + // add all surplus points to last processor + if( vmpi::my_rank == vmpi::num_processors - 1){ + spin_transport::internal::last_stack = st::internal::num_stacks; } - else{ - spin_transport::internal::first_stack = 0; - spin_transport::internal::last_stack = 0; + // check for more processors than stacks, if so allocate one stack per processor + if( st::internal::num_stacks < static_cast(vmpi::num_processors) ){ + if( static_cast(vmpi::my_rank) < st::internal::num_stacks){ + spin_transport::internal::first_stack = vmpi::my_rank; + spin_transport::internal::last_stack = vmpi::my_rank + 1; + } + else{ + spin_transport::internal::first_stack = 0; + spin_transport::internal::last_stack = 0; + } } - } - // data output on distribution of stacks to processors - //std::cerr << "Num stacks, start, end : " << - // spin_transport::internal::last_stack - spin_transport::internal::first_stack << "\t" << - // spin_transport::internal::first_stack << "\t" << spin_transport::internal::last_stack << std::endl; - #else - // for serial exectuation always compute all stacks - spin_transport::internal::first_stack = 0; - spin_transport::internal::last_stack = st::internal::num_stacks; - #endif - - //------------------------------------------------------------------------ - // resize cell vector data arrays (3N) and set to zero - //------------------------------------------------------------------------ - st::internal::cell_magnetization.resize(3*st::internal::total_num_cells, 0.0); - st::internal::cell_spin_torque_fields.resize(3*st::internal::total_num_cells, 0.0); + // data output on distribution of stacks to processors + //std::cerr << "Num stacks, start, end : " << + // spin_transport::internal::last_stack - spin_transport::internal::first_stack << "\t" << + // spin_transport::internal::first_stack << "\t" << spin_transport::internal::last_stack << std::endl; + #else + // for serial exectuation always compute all stacks + spin_transport::internal::first_stack = 0; + spin_transport::internal::last_stack = st::internal::num_stacks; + #endif + + return; - if( vmpi::my_rank == 0 ){ - std::ofstream ofile("data.txt"); - for(uint64_t i =0; i< st::internal::total_num_cells; i++){ - ofile << st::internal::cell_position[3*i+0] << "\t" << - st::internal::cell_position[3*i+1] << "\t" << - st::internal::cell_position[3*i+2] << "\t" << - st::internal::cell_magnetization[3*i+0] << "\t" << - st::internal::cell_magnetization[3*i+1] << "\t" << - st::internal::cell_magnetization[3*i+2] << "\t" << - st::internal::cell_alpha[i] << "\t" << - st::internal::cell_resistance[i] << "\t" << - st::internal::cell_spin_resistance[i] << std::endl; - } - ofile.close(); } - return; - - } + } // end of internal namespace } // end of spin_transport namespace diff --git a/src/spintransport/interface.cpp b/src/spintransport/interface.cpp index 54f401e7c..2f1612afc 100644 --- a/src/spintransport/interface.cpp +++ b/src/spintransport/interface.cpp @@ -160,7 +160,7 @@ namespace spin_transport{ // Set resistivity for environment (cells with no atoms) double rho = vin::str_to_double(value); vin::check_for_valid_value(rho, word, line, prefix, unit, "resistivity", 1.0e-10, 1.0e12,"input","1E-10 - 1E12 Ohm metres"); - internal::environment_resistivity = rho; + internal::environment_resistivity = rho * 1.0e10; // save resistivity and convert to Ohm Angstroms return true; } //------------------------------------------------------------------------ @@ -174,6 +174,14 @@ namespace spin_transport{ st::internal::time_counter = ur; return true; } + //------------------------------------------------------------------------ + test = "sublattice-resolved"; + if( word == test ){ + bool tf = vin::check_for_valid_bool(value, word, line, prefix, "input"); + // set control flag for sublattice resolved (material specific) calculations + st::internal::sublattice = tf; + return true; + } // channel length //-------------------------------------------------------------------- // Keyword not found @@ -204,7 +212,7 @@ namespace spin_transport{ // Set resistivity for atom type double rho = vin::str_to_double(value); vin::check_for_valid_value(rho, word, line, prefix, unit, "resistivity", 1.0e-20, 1.0e20,"input","1E-20 - 1E20 Ohm metres"); - st::internal::mp[super_index].resistivity.set(rho); + st::internal::mp[super_index].resistivity.set(rho * 1.0e10); // save resistivity and convert to Ohm Angstroms return true; } //--------------------------------------------------------------------------- @@ -216,7 +224,7 @@ namespace spin_transport{ // Set resistivity for atom type double rho = vin::str_to_double(value); vin::check_for_valid_value(rho, word, line, prefix, unit, "resistivity", 0.0, 1.0e20,"input","0.0 - 1E20 Ohm metres"); - st::internal::mp[super_index].spin_resistivity.set(rho); + st::internal::mp[super_index].spin_resistivity.set(rho * 1.0e10); // save resistivity and convert to Ohm Angstroms return true; } //--------------------------------------------------------------------------- @@ -229,23 +237,37 @@ namespace spin_transport{ return true; }*/ //------------------------------------------------------------ - test = "spin-transport-relaxation-torque"; - // aj parameter for material in slonczewski torque calculation - if( word==test ){ - double aj=atof(value.c_str()); + test = "spin-transport-relaxation-torque-efficiency"; + std::string test2 = "spin-transport-spin-torque-efficiency"; + std::string test3 = "spin-transport-eta"; + // eta parameter for material in slonczewski torque calculation + if( word == test || word == test2 || word == test3 ){ + double eta = atof(value.c_str()); // Test for valid range - vin::check_for_valid_value(aj, word, line, prefix, unit, "field", -1.0e-2, 1.0e2,"input","-100 - 100T"); - st::internal::mp[super_index].stt_rj.set(aj); + vin::check_for_valid_value(eta, word, line, prefix, unit, "", -1.0, 1.0,"input","-1.0 - 1.0"); + st::internal::mp[super_index].stt_rj.set(eta); return true; } //------------------------------------------------------------ - test = "spin-transport-precession-torque"; - // bj parameter for material in slonczewski torque calculation - if( word==test ){ - double bj=atof(value.c_str()); + test = "spin-transport-precession-torque-efficiency"; + test2 = "spin-transport-spin-torque-beta"; + test3 = "spin-transport-beta"; + // beta parameter for material in slonczewski torque calculation + if( word == test || word == test2 || word == test3 ){ + double beta = atof(value.c_str()); + // Test for valid range + vin::check_for_valid_value(beta, word, line, prefix, unit, "", -1.0, 1.0,"input","-1.0 - 1.0"); + st::internal::mp[super_index].stt_pj.set(beta); + return true; + } + //------------------------------------------------------------ + test = "spin-transport-sublattice"; + // arbitrary number indicating group (sublattice) of atoms for which spin transport is calculated + if( word == test ){ + int stsl = atoi(value.c_str()); // Test for valid range - vin::check_for_valid_value(bj, word, line, prefix, unit, "field", -1.0e-2, 1.0e2,"input","-100 - 100T"); - st::internal::mp[super_index].stt_pj.set(bj); + vin::check_for_valid_int(stsl, word, line, prefix,1,100,"input","1 - 100"); + st::internal::mp[super_index].stsl = stsl; return true; } //-------------------------------------------------------------------- diff --git a/src/spintransport/internal.hpp b/src/spintransport/internal.hpp index cf217dde0..7326a84cc 100644 --- a/src/spintransport/internal.hpp +++ b/src/spintransport/internal.hpp @@ -70,6 +70,7 @@ namespace spin_transport{ //---------------- // variables //---------------- + int stsl; // spin transport sublattice (grouping of atoms for which spin transport is calculated) set_double_t resistivity; // spin-independent resistivity (Ohm m) set_double_t spin_resistivity; // spin-dependent resistivity (Ohm m) set_double_t stt_rj; // spin transport relaxation torque @@ -77,23 +78,36 @@ namespace spin_transport{ // constructor mp_t (const unsigned int max_materials = 100) { - resistivity.set(1.68e-8); // default value is for copper (Cu) - spin_resistivity.set(0.0); // default value is for copper (Cu) - stt_rj.set(0.0); // default value is for copper (Cu) - stt_pj.set(0.0); // default value is for copper (Cu) + stsl = 0; // default is to calculate spin transport for all atoms (sublattice 0) + resistivity.set(1.68e-8); // default value is for copper (Cu) + spin_resistivity.set(0.0); // default value is 0 + stt_rj.set(0.0); // default value is 0 + stt_pj.set(0.0); // default value is 0 }; // end of constructor }; // end of exchange::internal::mp class + // simple struct to store 3D cell info + struct cell3D_t{ + uint64_t id; // id of cell + std::vector atom; // list of atoms in each cell + std::vector nm_atom; // list of non-magnetic atoms in each cell + }; + + // compact data type for code readability + typedef std::vector< std::vector < std::vector > > v3cell3D_t; + //------------------------------------------------------------------------- // Internal shared variables //------------------------------------------------------------------------- extern bool enabled; // bool to enable spin transport calculation + extern bool sublattice; // bool to enable sublattice level spin transport calculation extern unsigned int update_rate; // number of timesteps between updates extern unsigned int time_counter; // number of timesteps since last update extern std::vector mp; // array of material properties + extern std::vector atom_sublattice; // array to store which sublattice each atom is in for spin transport calculation // enumerated list of different current directions enum current_direction_t {px,py,pz,mx,my,mz}; // +x,+y,+z,-x,-y,-z @@ -103,6 +117,9 @@ namespace spin_transport{ extern double cell_size_y; // cell size along y-direction extern double cell_size_z; // cell size along z-direction + extern int cell_increment; // cell increment depending on positive or negative current direction + + extern unsigned int num_sublattices; // number of sublattices extern unsigned int num_stacks; // number of stacks perpendicular to current direction extern unsigned int total_num_cells; // number of cells @@ -124,6 +141,8 @@ namespace spin_transport{ // arrays to store average resistance and spin resistance in each cell extern std::vector cell_resistance; extern std::vector cell_spin_resistance; + extern std::vector cell_sl_resistance; + extern std::vector cell_sl_spin_resistance; // arrays to store cell properties extern std::vector magnetic; // boolean array to determine if cell is magnetic or not @@ -135,6 +154,19 @@ namespace spin_transport{ extern std::vector cell_relaxation_torque_rj; // cell specific prefactors for spin-torque relaxation bj extern std::vector cell_precession_torque_pj; // cell specific prefactors for spin-torque precession aj + // material specific arrays to store cell properties, flat 1D layout: index = cell * num_sublattices + sl + extern std::vector sl_magnetic; // boolean array to determine if cell is magnetic or not + extern std::vector cell_sl_alpha; // cell magnetization (average of constituent atoms) + extern std::vector cell_sl_isaturation; // inverse magnetic saturation at T=0 in each cell + extern std::vector cell_sl_relaxation_torque_rj; // cell specific prefactors for spin-torque relaxation bj + extern std::vector cell_sl_precession_torque_pj; // cell specific prefactors for spin-torque precession aj + extern std::vector cell_sl_magnetization_x; // normalised magnetization of each material in each cell + extern std::vector cell_sl_magnetization_y; // normalised magnetization of each material in each cell + extern std::vector cell_sl_magnetization_z; // normalised magnetization of each material in each cell + extern std::vector cell_sl_spin_torque_fields_x; // array of cell spin torque fields x + extern std::vector cell_sl_spin_torque_fields_y; // array of cell spin torque fields y + extern std::vector cell_sl_spin_torque_fields_z; // array of cell spin torque fields z + // array to store which cell each atom is in extern std::vector atom_in_cell; @@ -148,14 +180,28 @@ namespace spin_transport{ const std::vector& atoms_m_spin_array // moment of atoms ); + void calculate_cell_sublattice_magnetization(const unsigned int num_local_atoms, // number of local atoms + const std::vector& atoms_x_spin_array, // x-spin vector of atoms + const std::vector& atoms_y_spin_array, // y-spin vector of atoms + const std::vector& atoms_z_spin_array, // z-spin-vector of atoms + const std::vector& atoms_m_spin_array // moment of atoms + ); + void calculate_magnetoresistance(); + void calculate_sublattice_resistance(); - void calculate_field(const unsigned int num_local_atoms, // number of local atoms + /*void calculate_field(const unsigned int num_local_atoms, // number of local atoms std::vector& atoms_x_field_array, // x-field of atoms std::vector& atoms_y_field_array, // y-field of atoms std::vector& atoms_z_field_array // z-field of atoms ); + void calculate_material_field(const unsigned int num_local_atoms, // number of local atoms + std::vector& atoms_x_field_array, // x-field of atoms + std::vector& atoms_y_field_array, // y-field of atoms + std::vector& atoms_z_field_array // z-field of atoms + );*/ + } // end of internal namespace } // end of spin_transport namespace diff --git a/src/spintransport/makefile b/src/spintransport/makefile index 4ec748370..79f24bd89 100644 --- a/src/spintransport/makefile +++ b/src/spintransport/makefile @@ -10,6 +10,9 @@ initialize.o \ interface.o \ magnetization.o \ resistance.o \ +sublattice-initialize.o \ +sublattice-magnetization.o \ +sublattice-resistance.o \ update.o \ voltage.o diff --git a/src/spintransport/resistance.cpp b/src/spintransport/resistance.cpp index 16cb0cd5a..636477705 100644 --- a/src/spintransport/resistance.cpp +++ b/src/spintransport/resistance.cpp @@ -41,9 +41,8 @@ void calculate_magnetoresistance(){ //std::fill(st::internal::stack_current.begin(), st::internal::stack_current.end(), 0.0); #endif - // TODO need to parallelise stack loop //--------------------------------------------------------------------------------------------------------- - // loop over all stacks to calculate stack resistance (can OpenMP this loop) + // loop over all stacks to calculate stack resistance (parallelised for MPI) //--------------------------------------------------------------------------------------------------------- for(uint64_t stack = st::internal::first_stack; stack < st::internal::last_stack; stack++){ @@ -51,6 +50,9 @@ void calculate_magnetoresistance(){ const unsigned int end = stack_final_index[stack]; const double isat = st::internal::cell_isaturation[start]; // saturation magnetization for cell i + // hbar / ( 2 e mu_B) = 1.054571817e-34 /(2.0 * 1.602176634e-19 * 9.2740100657e-24) = 35486911.9121 + const double hbar_o_2emuB = 35486911.9121; + double total_stack_resistance = 0.0; // load first cell reduced magnetization @@ -65,7 +67,10 @@ void calculate_magnetoresistance(){ //------------------------------------------------------------------------------------------------------ // loop over all other cells in stack starting at cell start+1 //------------------------------------------------------------------------------------------------------ - for(unsigned int cell = start+1 ; cell < end ; cell++){ + //for(unsigned int cell = start+1 ; cell < end ; cell++){ + const int cell_inc = st::internal::cell_increment; + // slightly unsafe loop structure, but designed for allowing forward and backward loops starting one after the first cell + for(unsigned int cell = start + cell_inc ; cell != end + cell_inc ; cell += cell_inc){ if(st::internal::magnetic[cell]){ // calculate next cell reduced magnetization @@ -89,9 +94,9 @@ void calculate_magnetoresistance(){ const double hz = (strj-alpha*stpj)*(mjx*miy - mjy*mix) + (stpj+alpha*strj)*miz; // save field (without current factor) - st::internal::cell_spin_torque_fields[3*cell+0] = hx; - st::internal::cell_spin_torque_fields[3*cell+1] = hy; - st::internal::cell_spin_torque_fields[3*cell+2] = hz; + st::internal::cell_spin_torque_fields[3*cell+0] = hx * jsat * hbar_o_2emuB; // multiply by inverse moment + st::internal::cell_spin_torque_fields[3*cell+1] = hy * jsat * hbar_o_2emuB; + st::internal::cell_spin_torque_fields[3*cell+2] = hz * jsat * hbar_o_2emuB; // update cell resistances and magnetization mix = mjx; @@ -123,32 +128,35 @@ void calculate_magnetoresistance(){ //----------------------------------------------------- // Compute stack current //----------------------------------------------------- - const double je = st::internal::voltage * program::fractional_electric_field_strength / total_stack_resistance; + const double I = st::internal::voltage * program::fractional_electric_field_strength / total_stack_resistance; //--------------------------------------------------------- // Compute cell spin torque fields based on stack currents //--------------------------------------------------------- - // loop over all other cells in stack starting at cell start+1 - for(unsigned int cell = start+1 ; cell < end ; cell++){ - st::internal::cell_spin_torque_fields[3*cell+0] *= je; - st::internal::cell_spin_torque_fields[3*cell+1] *= je; - st::internal::cell_spin_torque_fields[3*cell+2] *= je; + // // old loop for positive current only + // for(unsigned int cell = start+1 ; cell < end ; cell++){ + //--------------------------------------------------------- + // loop over all other cells in stack + // slightly unsafe loop structure, but designed for allowing forward and backward loops starting one after the first cell + for(unsigned int cell = start + cell_inc ; cell != end + cell_inc ; cell += cell_inc){ + st::internal::cell_spin_torque_fields[3*cell+0] *= I; + st::internal::cell_spin_torque_fields[3*cell+1] *= I; + st::internal::cell_spin_torque_fields[3*cell+2] *= I; } //----------------------------------------------------- // save stack resistance and current to arrays //----------------------------------------------------- st::internal::stack_resistance[stack] = total_stack_resistance; - st::internal::stack_current[stack] = je; + st::internal::stack_current[stack] = I; } // end of stack loop //------------------------------------------------------------------------------------------ - // Reduce cell spin trorque fields and stack currents and resistances on all processors + // Reduce cell spin torque fields and stack currents and resistances on all processors //------------------------------------------------------------------------------------------ #ifdef MPICF MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_spin_torque_fields[0], 3*st::internal::total_num_cells, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); - //MPI_Allreduce(MPI_IN_PLACE, &st::internal::stack_resistance[0], st::internal::num_stacks, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); MPI_Allreduce(MPI_IN_PLACE, &sum_inv_resistance, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); #endif diff --git a/src/spintransport/sublattice-initialize.cpp b/src/spintransport/sublattice-initialize.cpp new file mode 100644 index 000000000..8116fabc2 --- /dev/null +++ b/src/spintransport/sublattice-initialize.cpp @@ -0,0 +1,469 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include + +// Vampire headers +#include "create.hpp" +#include "spintransport.hpp" +#include "vio.hpp" +#include "vmpi.hpp" + +// spintransport module headers +#include "internal.hpp" + +namespace spin_transport{ + + namespace internal{ + + //-------------------------------------------------------------------------------- + // Function to generate cell data in the case of sublattice resolved torques + //-------------------------------------------------------------------------------- + void initialise_sublattice_cell_data( + const uint64_t num_atoms, // number of local atoms + const int num_materials, // number of materials + const std::vector& atoms_type_array, // material types of atoms + const std::vector& atoms_m_spin_array, // moments of atoms (muB) + const std::vector& material_damping_array, // array of material level damping constants + const std::vector& is_magnetic_material, // array of size num_mat to state whether material is magnetic (true) or not (false) + const std::vector& non_magnetic_atoms_array, // list of non-magnetic atoms + internal::v3cell3D_t& cells3D, // Cell data + const int stack_x, const int stack_y, const int stack_z // direction of stacks relative to current direction + + ){ + + // copy cell sizes to local variable + const double cell_size[3] = { st::internal::cell_size_x, + st::internal::cell_size_y, + st::internal::cell_size_z }; + + //--------------------------------------------------------------------------------- + // Determine number of unique sublattices and atom associations + //--------------------------------------------------------------------------------- + std::vector sublattice_list; // list of unique sublattices to calculate ST for + // get list of unique sublattices to calculate ST for + for(int mat = 0; mat < num_materials; mat++){ + int stsl = st::internal::mp[mat].stsl; + if( std::find(sublattice_list.begin(), sublattice_list.end(), stsl) == sublattice_list.end() ){ + sublattice_list.push_back(stsl); + } + } + + // relabel list in linear order for memory efficiency + for(int mat = 0; mat < num_materials; mat++){ + for(int stsl = 0; stsl < sublattice_list.size(); stsl++){ + if(st::internal::mp[mat].stsl == sublattice_list[stsl]){ + st::internal::mp[mat].stsl = stsl; + } + } + } + + // now associate each atom with the correct sublattice for spin transport calculation + st::internal::atom_sublattice.resize(num_atoms); + for(uint64_t atom = 0; atom < num_atoms; atom++){ + int mat = atoms_type_array[atom]; + st::internal::atom_sublattice[atom] = st::internal::mp[mat].stsl; + } + + // set num sublattices to number of unique sublattices in system + st::internal::num_sublattices = sublattice_list.size(); + + // define local constant for number of sublattices to avoid repeated access to global variable + const unsigned int num_sublattices = st::internal::num_sublattices; + + //--------------------------------------------------------------------------------- + // Initialise suplattice specific calculation of spin-transport + //--------------------------------------------------------------------------------- + + // get total number of cells + const int num_cells = st::internal::total_num_cells; + + // flat array size: one entry per (cell, sublattice) pair, sl is the fastest-varying index + const int num_cells_x_sl = num_cells * (int)num_sublattices; + + // resize boolean array to determine if cell is magnetic or not + st::internal::sl_magnetic.assign(num_cells_x_sl, true); // initially assume all cells magnetic + + // resize arrays to store average resistance in each cell + st::internal::cell_sl_resistance.assign( num_cells_x_sl, 0.0); // also stores accumulated resistance before averaging + st::internal::cell_sl_spin_resistance.assign(num_cells_x_sl, 0.0); // also stores accumulated spin resistance before averaging + + // resize array to store average damping constant alpha in each cell + st::internal::cell_sl_alpha.assign(num_cells_x_sl, 0.0); + + // resize array to store inverse total moment m_s (T = 0) + st::internal::cell_sl_isaturation.assign(num_cells_x_sl, 1.0); // also stores accumulated moment before inversion + + // resize arrays to store slonczewski prefactors + st::internal::cell_sl_relaxation_torque_rj.assign(num_cells_x_sl, 0.0); // also stores accumulated parameters before averaging + st::internal::cell_sl_precession_torque_pj.assign(num_cells_x_sl, 0.0); // also stores accumulated parameters before averaging + + // Temporary arrays to accumulate intermediate values + std::vector total_num_atoms_in_cell( num_cells, 0 ); // array to store total number of magnetic and non-magnetic atoms in cell + + // Temporary arrays to accumulate intermediate values for material-resolved calculation + std::vector cell_sl_num_atoms_in_cell( num_cells_x_sl, 0 ); // array to store total number of magnetic and non-magnetic atoms in cell + std::vector cell_sl_num_magnetic_atoms(num_cells_x_sl, 0.0); // array to store total number of actually magnetic atoms (ignoring nm = keep atoms) [double format for easy normalisation] + + std::vector sl_num_atoms_in_cell(num_sublattices); // array to store total number of magnetic and non-magnetic atoms in cell + std::vector< double > sl_num_magnetic_atoms(num_sublattices); // array to store total number of actually magnetic atoms (ignoring nm = keep atoms) + std::vector< double > sl_resistivity(num_sublattices); // array to store total resistivity calculated from constituent atoms + //std::vector< double > sl_resistivity_sq(num_sublattices); // array to store total resistivity^2 calculated from constituent atoms + std::vector< double > sl_spin_resistivity(num_sublattices); // array to store total spin resistivity calculated from constituent atoms + //std::vector< double > sl_spin_resistivity_sq(num_sublattices); // array to store total spin resistivity^2 calculated from constituent atoms + + // sublattice arrays to store total moments, alpha constants, and STT prefactors + std::vector< double > sl_total_moment(num_sublattices, 0.0); // array to store total moment in each cell for each sublattice + std::vector< double > sl_total_alpha(num_sublattices, 0.0); // array to store total moment in each cell for each sublattice + std::vector< double > sl_total_rj(num_sublattices, 0.0); // array to store total moment in each cell for each sublattice + std::vector< double > sl_total_pj(num_sublattices, 0.0); // array to store total moment in each cell for each sublattice + + // loop over all xy-cells (stacks) + for(unsigned int i = 0; i < cells3D.size(); i++){ + for(unsigned int j = 0; j < cells3D[i].size(); j++){ + + // loop over all cells in stack + for(unsigned int k = 0; k < cells3D[i][j].size(); k++){ + + // determine cell ID + const uint64_t cell = cells3D[i][j][k].id; + + // determine total number of local atoms + const uint64_t num_atoms_in_cell = cells3D[i][j][k].atom.size() + cells3D[i][j][k].nm_atom.size(); + total_num_atoms_in_cell[cell] = num_atoms_in_cell; + + // variables to accumulate resistances for cell + for(int sl = 0 ; sl < num_sublattices; sl++) sl_num_atoms_in_cell [sl] = 0 ; // array to store total number of magnetic and non-magnetic atoms in cell + for(int sl = 0 ; sl < num_sublattices; sl++) sl_num_magnetic_atoms [sl] = 0.0; // array to store total number of actually magnetic atoms (ignoring nm = keep atoms) + for(int sl = 0 ; sl < num_sublattices; sl++) sl_resistivity [sl] = 0.0; // array to store total resistivity calculated from constituent atoms + //for(int sl = 0 ; sl < num_sublattices; sl++) sl_resistivity_sq [sl] = 0.0; // array to store total resistivity^2 calculated from constituent atoms + for(int sl = 0 ; sl < num_sublattices; sl++) sl_spin_resistivity [sl] = 0.0; // array to store total spin resistivity calculated from constituent atoms + //for(int sl = 0 ; sl < num_sublattices; sl++) sl_spin_resistivity_sq[sl] = 0.0; // array to store total spin resistivity^2 calculated from constituent atoms + + for(int sl = 0 ; sl < num_sublattices; sl++) sl_total_moment[sl] = 0.0; + for(int sl = 0 ; sl < num_sublattices; sl++) sl_total_alpha[sl] = 0.0; // damping constant + for(int sl = 0 ; sl < num_sublattices; sl++) sl_total_rj[sl] = 0.0; // spin torque prefactor parameter + for(int sl = 0 ; sl < num_sublattices; sl++) sl_total_pj[sl] = 0.0; // spin torque prefactor parameter + + // check for cells with only non-magnetic atoms = keep + //uint64_t num_magnetic_atoms = 0; // counter for number of actually magnetic atoms + + // magnetic atoms + for(unsigned int atom = 0; atom < cells3D[i][j][k].atom.size(); atom++ ){ + // get atom number in total list + const int atomID = cells3D[i][j][k].atom[atom]; + // get atom material of atom + const int mat = atoms_type_array[atomID]; // get material type + // get sublattice of atom (indexed by global atom ID, not local loop index) + //const int sl = st::internal::atom_sublattice[atom]; + const int sl = st::internal::atom_sublattice[atomID]; //jeff + + // get resistivities + const double r = st::internal::mp[mat].resistivity.get(); + const double sr = st::internal::mp[mat].spin_resistivity.get(); + + // add resistivities + sl_resistivity[sl] += r; // add resistivity to total + //sl_resistivity_sq[sl] += r*r; // add resistivity^2 to total + sl_spin_resistivity[sl] += sr; // add spin resistivity to total + //sl_spin_resistivity_sq[sl] += sr*sr; // add spin resistivity^2 to total + + // add number of total atoms (magnetic and non-magnetic) for each sublattice for each cell + sl_num_atoms_in_cell[sl] += 1; + + // check that atom is magnetic + if(is_magnetic_material[mat] == true){ + + // increment number of atoms in sublattice counter + sl_num_magnetic_atoms[sl] += 1.0; + + // calculate total moment + sl_total_moment[sl] += atoms_m_spin_array[atomID]; + sl_total_alpha [sl] += material_damping_array[mat]; + + // calculate total prefactor parameters + sl_total_rj[sl] += st::internal::mp[mat].stt_rj.get(); + sl_total_pj[sl] += st::internal::mp[mat].stt_pj.get(); + + } + + } + + // non-magnetic (remove) atoms + for(unsigned int atom = 0; atom < cells3D[i][j][k].nm_atom.size(); atom++ ){ + + // get atom number in total list + const int atomID = cells3D[i][j][k].atom[atom]; //jeff + const int mat = non_magnetic_atoms_array[atomID].mat; // get material type jeff + //const int mat = non_magnetic_atoms_array[atomID].mat; // get material type + + // get sublattice of atom (indexed by global atom ID, not local loop index) + //const int sl = st::internal::atom_sublattice[atomID]; + const int sl = st::internal::mp[mat].stsl; // get sublattice from material (nm atoms not in atom_sublattice) jeff + + // add number of total atoms (magnetic and non-magnetic) for each sublattice for each cell + sl_num_atoms_in_cell[sl] += 1; + + // get resistivities + const double r = st::internal::mp[mat].resistivity.get(); + const double sr = st::internal::mp[mat].spin_resistivity.get(); + + sl_resistivity[sl] += r; // add resistivity to total + //sl_resistivity_sq[sl] += r*r; // add resistivity^2 to total + sl_spin_resistivity[sl] += sr; // add spin resistivity to total + //sl_spin_resistivity_sq[sl] += sr*sr; // add spin resistivity^2 to total + + } + + // save accumulated values + for( int sl = 0; sl < num_sublattices; sl++ ){ + + const int idx = int(cell) * int(num_sublattices) + sl; + + st::internal::cell_sl_resistance [idx] = sl_resistivity [sl]; // temporarily store rho_eff for each sublattice here + st::internal::cell_sl_spin_resistance [idx] = sl_spin_resistivity[sl]; // temporarily store spin-dependent rho_eff for each sublattice here + st::internal::cell_sl_relaxation_torque_rj[idx] = sl_total_rj [sl]; + st::internal::cell_sl_precession_torque_pj[idx] = sl_total_pj [sl]; + st::internal::cell_sl_isaturation [idx] = sl_total_moment [sl]; // (mu_B) + st::internal::cell_sl_alpha [idx] = sl_total_alpha [sl]; // total damping + + // temporary values + cell_sl_num_atoms_in_cell [idx] = sl_num_atoms_in_cell[sl]; // store total number of magnetic and non-magnetic atoms in cell + cell_sl_num_magnetic_atoms[idx] = sl_num_magnetic_atoms[sl]; // store total number of magnetic atoms in cell in double format for normalisation + + } + + } // end of cell loop + } // end of stack y loop + } // end of stack x loop + + //----------------------------------------------------------------------------------------------- + // reduce on all processors to enable unique determination of empty cells and average parameters + //----------------------------------------------------------------------------------------------- + #ifdef MPICF + + // reduce total num atoms in cell + MPI_Allreduce(MPI_IN_PLACE, &total_num_atoms_in_cell[0], int(num_cells), MPI_UINT64_T, MPI_SUM, MPI_COMM_WORLD); + + // reduce all cell totals onto all processors with a single call per array + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_sl_resistance [0], num_cells_x_sl, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_sl_spin_resistance [0], num_cells_x_sl, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_sl_relaxation_torque_rj[0], num_cells_x_sl, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_sl_precession_torque_pj[0], num_cells_x_sl, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_sl_isaturation [0], num_cells_x_sl, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_sl_alpha [0], num_cells_x_sl, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &cell_sl_num_atoms_in_cell [0], num_cells_x_sl, MPI_UINT64_T, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &cell_sl_num_magnetic_atoms [0], num_cells_x_sl, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + + #endif + + //----------------------------------------------------------------------------------------------- + // Calculate average cell parameters + //----------------------------------------------------------------------------------------------- + // The idea is that each sublattice takes up a fractional cross-sectional area of each cell. The + // total resistance follows the total resistance formula 1/R_T = 1/R_1 + 1/R_2 + ... + // + // The fractional cross sectional area of each sublattice is: + // + // frac_xsa = num_atoms_in_sl_in_cell / total_num_atoms_in_cell + // + // The resistance of each sublattice in the cell is then given by + // + // R_i = rho_eff L / (frac_xsa * A) + // + // where rho_eff is the mean resistivity of the constiuent materials, L is the length of the cell, + // A is the cross-sectional area of the cell, and i = 1,2,... is the sublattice index. + // + // The total resistance then follows the parallel resistor formula: + // + // 1/R_tot = 1/R_1 + 1/R_2 + ... 1/R_n + // + // calculating the total resistance for sublattices in each cell, then serial resistor formula + // for all cells in a stack + // + + + // cell size parameters for resitivity to resistance calculation + const double iA = 1.0 / (cell_size[stack_x] * cell_size[stack_y]); // Angstroms^-2 + const double L = cell_size[stack_z]; + + // loop over all xy-cells (stacks) + for(unsigned int i = 0; i < cells3D.size(); i++){ + for(unsigned int j = 0; j < cells3D[i].size(); j++){ + + // loop over all cells in stack + for(unsigned int k = 0; k < cells3D[i][j].size(); k++){ + + // determine cell ID + const uint64_t cell = cells3D[i][j][k].id; + + // determine total number of atoms on all processors + const uint64_t num_atoms_in_cell = total_num_atoms_in_cell[cell]; + + // loop over all sublattices + for( int sl = 0; sl < num_sublattices; sl++ ){ + + // flat index: sl is the fastest-varying index + const int idx = int(cell) * int(num_sublattices) + sl; + + // determine total number of sublattice atoms on all processors + const double num_sl_magnetic_atoms_in_cell = cell_sl_num_magnetic_atoms[idx]; + const uint64_t num_sl_atoms_in_cell = cell_sl_num_atoms_in_cell[idx]; + + //------------------------------------------------------------ + // if sublattice cell is empty space assume uniform padding resistance + //------------------------------------------------------------ + if(num_sl_atoms_in_cell == 0){ + // special case for sublattices with no atoms - assume zero additional resistance + // if the cell resistance is zero this will cause a "short" if in the entire stack + // so this is checked later when calculating the total resistance + st::internal::cell_sl_resistance[idx] = 0.0; + st::internal::cell_sl_spin_resistance[idx] = 0.0; + st::internal::cell_sl_isaturation[idx] = 0.0; // set inverse saturation to 0.0 + st::internal::sl_magnetic[idx] = false; // set sublattice in cell as non-magnetic + } + //------------------------------------------------------------------------- + // otherwise add contributions from atoms to calculate average resistivity + //------------------------------------------------------------------------- + else{ + //------------------------------------------------------------ + // check for empty cells or cells with only non-magnetic atoms (keep) and if so treat as non-magnetic + //------------------------------------------------------------ + if(num_sl_magnetic_atoms_in_cell < 0.1){ + // normalise resistance to number of atoms in sublattice + st::internal::cell_sl_resistance[idx] = st::internal::cell_sl_resistance[idx] / double(num_sl_atoms_in_cell); + st::internal::cell_sl_spin_resistance[idx] = st::internal::cell_sl_spin_resistance[idx] / double(num_sl_atoms_in_cell); + st::internal::sl_magnetic[idx] = false; // no magnetic atoms -> non-magnetic cell + st::internal::cell_sl_isaturation[idx] = 1.0; // assume 1 so inverse is still 1 (any value is fine but needs to be > 0) + } + //------------------------------------------------------------------------- + // finally consider magnetic atoms in each sublattice in each cell + //------------------------------------------------------------------------- + else{ + // calculate mean spin torque prefactor parameters for magnetic cells + const double count = num_sl_atoms_in_cell; // number of magnetic atoms in sublattice in cell + st::internal::cell_sl_resistance[idx] = st::internal::cell_sl_resistance[idx] / count; + st::internal::cell_sl_spin_resistance[idx] = st::internal::cell_sl_spin_resistance[idx] / count; + st::internal::cell_sl_relaxation_torque_rj[idx] = st::internal::cell_sl_relaxation_torque_rj[idx] / count; + st::internal::cell_sl_precession_torque_pj[idx] = st::internal::cell_sl_precession_torque_pj[idx] / count; + st::internal::cell_sl_alpha[idx] = st::internal::cell_sl_alpha[idx] / count; + } + + // set inverse total moment + st::internal::cell_sl_isaturation[idx] = 1.0/st::internal::cell_sl_isaturation[idx]; // (1/mu_B) + + // set inverse saturation of non-magnetic cells to zero + if(num_sl_magnetic_atoms_in_cell < 0.1){ + st::internal::cell_sl_isaturation[idx] = 0.0; + } + + //------------------------------------------------------------ + // calculate resistance values for each sublattice + //------------------------------------------------------------ + + // work out fractional area for each sublattice + const double fractional_area = double(num_sl_atoms_in_cell) / double(num_atoms_in_cell); + + // get average resistivity for each sublattice in this cell + const double sublattice_cell_resistivity = st::internal::cell_sl_resistance[idx]; + const double sublattice_cell_spin_resistivity = st::internal::cell_sl_spin_resistance[idx]; + + // set cell resistance R = rho * L / (f * A) + st::internal::cell_sl_resistance[idx] = sublattice_cell_resistivity * L * iA / fractional_area; + st::internal::cell_sl_spin_resistance[idx] = sublattice_cell_spin_resistivity * L * iA / fractional_area; + + } // end of if statement for cells containing atoms + + } // end of sublattice loop + } // end of cell loop + } // end of stack y loop + } // end of stack x loop + + //------------------------------------------------------------------------ + // Calculate cell data + //------------------------------------------------------------------------ + st::internal::cell_position.resize(3*st::internal::total_num_cells); + + // convert to system coordinate system + // translation from system coordinates to cell coordinates + const int stack[3] = { stack_x, stack_y, stack_z }; + // example 1: 2 0 1 + // example 2: 0 1 2 + // example 3: 1 2 0 + + // translation from cell coordinates to system coordinates + // example 1: istack[3] 1 2 0 + // example 2: istack[3] 0 1 2 + // example 3: istack[3] 2 0 1 + + // calculate inverse stack relations + int istack[3]; + for(int i=0; i<3; i++){ + if(stack[i] == 0) istack[0] = i; + if(stack[i] == 1) istack[1] = i; + if(stack[i] == 2) istack[2] = i; + } + + // Output stack directions + //std::cout << " stack: " << stack[0] << "\t" << stack[1] << "\t" << stack[2] << std::endl; + //std::cout << "istack: " << istack[0] << "\t" << istack[1] << "\t" << istack[2] << std::endl; + + // loop over all xy-cells (stacks) + for(unsigned int i = 0; i < cells3D.size(); i++){ + for(unsigned int j = 0; j < cells3D[i].size(); j++){ + + // loop over all cells in stack + for(unsigned int k = 0; k < cells3D[i][j].size(); k++){ + + // determine cell ID + const uint64_t cell = cells3D[i][j][k].id; + + // determine cell counts in each direction + unsigned int xyz[3] = { i, j, k}; + + // calculate cell coordinates + const double x = double(xyz[istack[0]]) * cell_size[0]; + const double y = double(xyz[istack[1]]) * cell_size[1]; + const double z = double(xyz[istack[2]]) * cell_size[2]; + + st::internal::cell_position[3*cell+0] = x; + st::internal::cell_position[3*cell+1] = y; + st::internal::cell_position[3*cell+2] = z; + + } + } + } + + //------------------------------------------------------------------------ + // TODO: Pre-processing step to accumulate resistances for GMR and TMR + //------------------------------------------------------------------------ + + //------------------------------------------------------------------------ + // resize cell vector data arrays and set to zero + //------------------------------------------------------------------------ + st::internal::cell_resistance.resize(num_cells,0.0); // also stores accumulated resistance before averaging + + st::internal::cell_sl_magnetization_x.assign( num_cells_x_sl, 0.0 ); + st::internal::cell_sl_magnetization_y.assign( num_cells_x_sl, 0.0 ); + st::internal::cell_sl_magnetization_z.assign( num_cells_x_sl, 0.0 ); + st::internal::cell_sl_spin_torque_fields_x.assign( num_cells_x_sl, 0.0 ); + st::internal::cell_sl_spin_torque_fields_y.assign( num_cells_x_sl, 0.0 ); + st::internal::cell_sl_spin_torque_fields_z.assign( num_cells_x_sl, 0.0 ); + + return; + + } + + } // end of internal namespace + +} // end of spin_transport namespace diff --git a/src/spintransport/sublattice-magnetization.cpp b/src/spintransport/sublattice-magnetization.cpp new file mode 100644 index 000000000..bcfd96ca3 --- /dev/null +++ b/src/spintransport/sublattice-magnetization.cpp @@ -0,0 +1,97 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include + +// Vampire headers +#include "spintransport.hpp" +#include "vmpi.hpp" + +// spintransport module headers +#include "internal.hpp" + +namespace spin_transport{ +namespace internal{ + +//--------------------------------------------------------------------------------------------------------- +// Function to update cell material magnetizations m / m_s^0 +//--------------------------------------------------------------------------------------------------------- +void calculate_cell_sublattice_magnetization(const unsigned int num_local_atoms, // number of local atoms + const std::vector& atoms_x_spin_array, // x-spin vector of atoms + const std::vector& atoms_y_spin_array, // y-spin vector of atoms + const std::vector& atoms_z_spin_array, // z-spin-vector of atoms + const std::vector& atoms_m_spin_array // moment of atoms +){ + + // define local constants for number of cells sublattices to avoid repeated access to global variables + const int num_sublattices = st::internal::num_sublattices; + const int num_cells = st::internal::total_num_cells; + const int num_cells_x_sl = num_cells * num_sublattices; + + //--------------------------------------------------------------------------- + // reset magnetization vector to zero + //--------------------------------------------------------------------------- + std::fill(st::internal::cell_sl_magnetization_x.begin(), st::internal::cell_sl_magnetization_x.end(), 0.0); + std::fill(st::internal::cell_sl_magnetization_y.begin(), st::internal::cell_sl_magnetization_y.end(), 0.0); + std::fill(st::internal::cell_sl_magnetization_z.begin(), st::internal::cell_sl_magnetization_z.end(), 0.0); + + //--------------------------------------------------------------------------- + // loop over all atoms and determine cell magnetizations (can OpenMP) + //--------------------------------------------------------------------------- + for(unsigned int atom = 0; atom < num_local_atoms; atom++){ + + // get cell id + const uint64_t cell = st::internal::atom_in_cell[atom]; + + // get magnetic moment (muB) + const double mm = atoms_m_spin_array[atom]; + + // get sublattice of atom + const int sl = st::internal::atom_sublattice[atom]; + + // flat index: sl is the fastest-varying index + const int idx = (int)cell * num_sublattices + sl; + + // add magnetization to cell + st::internal::cell_sl_magnetization_x[idx] += mm*atoms_x_spin_array[atom]; + st::internal::cell_sl_magnetization_y[idx] += mm*atoms_y_spin_array[atom]; + st::internal::cell_sl_magnetization_z[idx] += mm*atoms_z_spin_array[atom]; + + } + + // for(int cell = 0; cell < num_cells; cell++){ + // for( int sl = 0; sl < num_sublattices; sl++ ){ + // std::cout << "cell: " << cell << "\t" << + // st::internal::cell_sl_magnetization_x[cell][sl] << "\t" << + // st::internal::cell_sl_magnetization_y[cell][sl] << "\t" << + // st::internal::cell_sl_magnetization_z[cell][sl] << std::endl; + // } + // } + + //--------------------------------------------------------------------------- + // Reduce cell material magnetizations on all processors + //--------------------------------------------------------------------------- + #ifdef MPICF + // single reduction per component over the entire flat array + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_sl_magnetization_x[0], num_cells_x_sl, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_sl_magnetization_y[0], num_cells_x_sl, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_sl_magnetization_z[0], num_cells_x_sl, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + #endif + + return; + +} + +} // end of internal namespace +} // end of spin_transport namespace diff --git a/src/spintransport/sublattice-resistance.cpp b/src/spintransport/sublattice-resistance.cpp new file mode 100644 index 000000000..0ccd2986b --- /dev/null +++ b/src/spintransport/sublattice-resistance.cpp @@ -0,0 +1,296 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include + +// Vampire headers +#include "program.hpp" +#include "spintransport.hpp" +#include "vmpi.hpp" + +// spintransport module headers +#include "internal.hpp" + +namespace spin_transport{ +namespace internal{ + + double calculate_start_cell_resistance_and_magnetization(const int cell, + std::vector& sl_mix, // sublattice moments of previous cell + std::vector& sl_miy, + std::vector& sl_miz); + + // function to calculate cell resistance and torques + double calculate_cell_resistance_and_torques(const int cell, + std::vector& sl_mix, // sublattice moments of previous cell + std::vector& sl_miy, + std::vector& sl_miz); + +//--------------------------------------------------------------------------------------------------------- +// Function to calculate stack resistances +//--------------------------------------------------------------------------------------------------------- +void calculate_sublattice_resistance(){ + + // define local constants for number of cells sublattices to avoid repeated access to global variables + const int num_sublattices = st::internal::num_sublattices; + const int num_cells_x_sl = int(st::internal::total_num_cells) * num_sublattices; + + // variable to compute sum of inverse resistances over all stacks + double sum_inv_resistance = 0.0; + + // declare memory for storing initial sublattice magnetisations + std::vector sl_mix(num_sublattices); + std::vector sl_miy(num_sublattices); + std::vector sl_miz(num_sublattices); + + //--------------------------------------------------------------------------------------------------------- + // Zero spin torque arrray for parallel version to allow reduction + //--------------------------------------------------------------------------------------------------------- + #ifdef MPICF + std::fill(st::internal::cell_sl_spin_torque_fields_x.begin(), st::internal::cell_sl_spin_torque_fields_x.end(), 0.0); + std::fill(st::internal::cell_sl_spin_torque_fields_y.begin(), st::internal::cell_sl_spin_torque_fields_y.end(), 0.0); + std::fill(st::internal::cell_sl_spin_torque_fields_z.begin(), st::internal::cell_sl_spin_torque_fields_z.end(), 0.0); + //std::fill(st::internal::cell_resistance.begin(), st::internal::cell_resistance.end(), 0.0); + //std::fill(st::internal::stack_resistance.begin(), st::internal::stack_resistance.end(), 0.0); // needed for data output only + //std::fill(st::internal::stack_current.begin(), st::internal::stack_current.end(), 0.0); + #endif + + //--------------------------------------------------------------------------------------------------------- + // loop over all stacks to calculate stack resistance (Parallel loop in MPI) + //--------------------------------------------------------------------------------------------------------- + for(uint64_t stack = st::internal::first_stack; stack < st::internal::last_stack; stack++){ + + const unsigned int start = stack_start_index[stack]; + const unsigned int end = stack_final_index[stack]; + + // initialise resistance with the first cell + double total_stack_resistance = calculate_start_cell_resistance_and_magnetization(start, sl_mix, sl_miy, sl_miz); + + //------------------------------------------------------------------------------------------------------ + // loop over all other cells in stack starting at cell start+1 + //------------------------------------------------------------------------------------------------------ + const int cell_inc = st::internal::cell_increment; + + // slightly unsafe loop structure, but designed for allowing forward and backward loops starting one after the first cell + for(unsigned int cell = start + cell_inc ; cell != end + cell_inc ; cell += cell_inc){ + + // calculate resistance of cell and effective torques on each sublattice + double cell_resistance = calculate_cell_resistance_and_torques(cell, sl_mix, sl_miy, sl_miz); + + // add cell resistance to total resistance + total_stack_resistance += cell_resistance; + + // save cell resistance to calculate local voltage drop over the cell, and thereby calculate sublattice currents + st::internal::cell_resistance[cell] = cell_resistance; + + } + + //----------------------------------------------------- + // accumulate total inverse resistance + //----------------------------------------------------- + sum_inv_resistance += 1.0 / total_stack_resistance; + + //------------------------------------------------------------------------------------ + // Compute stack current (as this only depends on the resistance in each stack, R_i) + //------------------------------------------------------------------------------------ + const double I = st::internal::voltage * program::fractional_electric_field_strength / total_stack_resistance; + + //--------------------------------------------------------- + // Compute cell spin torque fields based on stack currents + //--------------------------------------------------------- + // // old loop for positive current only + // for(unsigned int cell = start+1 ; cell < end ; cell++){ + //--------------------------------------------------------- + // loop over all other cells in stack + // slightly unsafe loop structure, but designed for allowing forward and backward loops starting one after the first cell + for(unsigned int cell = start + cell_inc ; cell != end + cell_inc ; cell += cell_inc){ + + // Calculate vocal voltage drop (based on cell resistance) + const double V_cell = I * st::internal::cell_resistance[cell]; + + // loop over all sublattices, multiply by V_cell due to I_sl = V_cell / R_sl, + // as torque already pre-divided by R_sl + const int base = int(cell) * num_sublattices; + for( int sl = 0; sl < num_sublattices; sl++ ){ + st::internal::cell_sl_spin_torque_fields_x[base + sl] *= V_cell; + st::internal::cell_sl_spin_torque_fields_y[base + sl] *= V_cell; + st::internal::cell_sl_spin_torque_fields_z[base + sl] *= V_cell; + } + } + + //----------------------------------------------------- + // save stack resistance and current to arrays + //----------------------------------------------------- + st::internal::stack_resistance[stack] = total_stack_resistance; + st::internal::stack_current[stack] = I; + + } // end of stack loop + + //------------------------------------------------------------------------------------------ + // Reduce cell spin torque fields and stack currents and resistances on all processors + // Single reduction per component over the entire flat array + //------------------------------------------------------------------------------------------ + #ifdef MPICF + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_sl_spin_torque_fields_x[0], num_cells_x_sl, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_sl_spin_torque_fields_y[0], num_cells_x_sl, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &st::internal::cell_sl_spin_torque_fields_z[0], num_cells_x_sl, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &sum_inv_resistance, 1, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + #endif + + // save total resistance and current + st::total_resistance = 1.0 / sum_inv_resistance; + st::total_current = program::fractional_electric_field_strength * st::internal::voltage / st::total_resistance; + + return; + +} + + //----------------------------------------------------------------------------------- + // Function to calculate the resistance of a single cell, forming a set of parallel + // resistors + //----------------------------------------------------------------------------------- + double calculate_cell_resistance_and_torques(const int cell, + std::vector& sl_mix, // sublattice moments of previous cell + std::vector& sl_miy, + std::vector& sl_miz){ + + // define local constants for number of cells sublattices to avoid repeated access to global variables + const int num_sublattices = st::internal::num_sublattices; + + // hbar / ( 2 e mu_B) = 1.054571817e-34 /(2.0 * 1.602176634e-19 * 9.2740100657e-24) = 35486911.9121 + const double hbar_o_2emuB = 35486911.9121; + + // variable to hold the sum of inverse resistances for all sublattices + double sum_inv_resistances = 0.0; + + // flat base index for this cell: sl is the fastest-varying index + const int base = cell * num_sublattices; + + // loop over all sublattices + for( int sl = 0; sl < num_sublattices; sl++ ){ + + const int idx = base + sl; + + const double mix = sl_mix[sl]; + const double miy = sl_miy[sl]; + const double miz = sl_miz[sl]; + + // get standard and spin-dependent resistances + const double Rep = st::internal::cell_sl_resistance[idx]; + const double Rsp = st::internal::cell_sl_spin_resistance[idx]; + + // if the sublattice is magnetic then add to sum of inverse resistances + if(st::internal::sl_magnetic[idx]){ + + // calculate next cell reduced magnetization + const double jsat = st::internal::cell_sl_isaturation[idx]; + const double mjx = st::internal::cell_sl_magnetization_x[idx] * jsat; + const double mjy = st::internal::cell_sl_magnetization_y[idx] * jsat; + const double mjz = st::internal::cell_sl_magnetization_z[idx] * jsat; + const double alpha = st::internal::cell_sl_alpha[idx]; + const double mi_dot_mj = ( mix*mjx + miy*mjy + miz*mjz ); + + // calculate resistance and sum as 1/R since Rep and Rsp are serial resistances + const double R_sl = Rep + 0.5*Rsp*(1.0 - mi_dot_mj); + sum_inv_resistances += 1.0 / R_sl; + + // calculate relative contributions of adiabatic and non-adiabatic spin torque + const double strj = st::internal::cell_sl_relaxation_torque_rj[idx]; // eta parameter + const double stpj = st::internal::cell_sl_precession_torque_pj[idx]; // beta parameter (should be multiplied by eta?) + + // calculate field without current based on relative magnetization orientations + const double hx = (strj-alpha*stpj)*(mjy*miz - mjz*miy) + (stpj+alpha*strj)*mix; + const double hy = (strj-alpha*stpj)*(mjz*mix - mjx*miz) + (stpj+alpha*strj)*miy; + const double hz = (strj-alpha*stpj)*(mjx*miy - mjy*mix) + (stpj+alpha*strj)*miz; + + // save field (without current factor, but scaled with R_sl, with I = V/R, so multiply by V_cell to get local current) + st::internal::cell_sl_spin_torque_fields_x[idx] = hx * jsat * hbar_o_2emuB / R_sl; // multiply by inverse moment + st::internal::cell_sl_spin_torque_fields_y[idx] = hy * jsat * hbar_o_2emuB / R_sl; + st::internal::cell_sl_spin_torque_fields_z[idx] = hz * jsat * hbar_o_2emuB / R_sl; + + // update magnetization for next iteration + sl_mix[sl] = mjx; + sl_miy[sl] = mjy; + sl_miz[sl] = mjz; + + } + else{ + // otherwise just add the standard inverse resistances (skip empty sublattices with R=0) + if(Rep > 0.0) sum_inv_resistances += 1.0 / Rep; + + // here we would also add the spin resistance here for accumulation in the + // next step to include the effects of TMR, but for the sublattice + // calculation this is information is lost and its not clear how this impacts + // the cell level resistances that must be done with the inverse sum + // sum_inv_spin_resistances += 1.0 / Rsp; + + } + } // end of sublattice loop + + // calculate total resistance for the cell + const double total_inverse_resistance = sum_inv_resistances; + const double R = 1.0 / total_inverse_resistance; + + return R; + + } + + //----------------------------------------------------------------------------------- + // Function to calculate the resistance of the first cell in the stack + // Each sublattice forms a set of parallel resistors + //----------------------------------------------------------------------------------- + double calculate_start_cell_resistance_and_magnetization(const int cell, + std::vector& sl_mix, // sublattice moments of previous cell + std::vector& sl_miy, + std::vector& sl_miz){ + + // define local constants for number of cells sublattices to avoid repeated access to global variables + const int num_sublattices = st::internal::num_sublattices; + + // variable to hold the sum of inverse resistances for all sublattices + double sum_inv_resistances = 0.0; + + // flat base index for this cell: sl is the fastest-varying index + const int base = cell * num_sublattices; + + // loop over all sublattices + for( int sl = 0; sl < num_sublattices; sl++ ){ + + const int idx = base + sl; + + // if the sublattice is magnetic then add to sum of inverse resistances + if(st::internal::sl_magnetic[idx]){ + + // calculate next cell reduced magnetization + const double isat = st::internal::cell_sl_isaturation[idx]; + sl_mix[sl] = st::internal::cell_sl_magnetization_x[idx] * isat; + sl_miy[sl] = st::internal::cell_sl_magnetization_y[idx] * isat; + sl_miz[sl] = st::internal::cell_sl_magnetization_z[idx] * isat; + + } + + // skip empty sublattices (R=0) to avoid short-circuiting the parallel network + if(st::internal::cell_sl_resistance[idx] > 0.0) + sum_inv_resistances += 1.0 / st::internal::cell_sl_resistance[idx]; + + } // end of sublattice loop + + // calculate total resistance for the cell + const double R = 1.0 / sum_inv_resistances; + + return R; + + } + +} // end of internal namespace +} // end of spin_transport namespace diff --git a/src/spintransport/update.cpp b/src/spintransport/update.cpp index d85891ad9..a733dbbe1 100644 --- a/src/spintransport/update.cpp +++ b/src/spintransport/update.cpp @@ -16,6 +16,7 @@ // Vampire headers #include "spintransport.hpp" +#include "vmpi.hpp" // spintransport module headers #include "internal.hpp" @@ -51,34 +52,74 @@ void update(const unsigned int num_local_atoms, // number of local at //--------------------------------------------------------------------------------------------------------- // update cell magnetizations //--------------------------------------------------------------------------------------------------------- - st::internal::calculate_cell_magnetization(num_local_atoms, atoms_x_spin_array, atoms_y_spin_array, - atoms_z_spin_array, atoms_m_spin_array); + if(st::internal::sublattice){ + st::internal::calculate_cell_sublattice_magnetization(num_local_atoms, atoms_x_spin_array, atoms_y_spin_array, + atoms_z_spin_array, atoms_m_spin_array); + } + else{ + st::internal::calculate_cell_magnetization(num_local_atoms, atoms_x_spin_array, atoms_y_spin_array, + atoms_z_spin_array, atoms_m_spin_array); + } //--------------------------------------------------------------------------------------------------------- // calculate magnetoresistance //--------------------------------------------------------------------------------------------------------- - st::internal::calculate_magnetoresistance(); + if(st::internal::sublattice) st::internal::calculate_sublattice_resistance(); + else st::internal::calculate_magnetoresistance(); //--------------------------------------------------------------------------------------------------------- // test output of cell-level spin transport data //--------------------------------------------------------------------------------------------------------- - // std::ofstream ofile("stdata.txt"); - // for(int i =0; i< st::internal::total_num_cells; i++){ - // const double isat = st::internal::cell_isaturation[i]; - // ofile << st::internal::cell_position[3*i+0] << "\t" << - // st::internal::cell_position[3*i+1] << "\t" << - // st::internal::cell_position[3*i+2] << "\t" << - // st::internal::cell_magnetization[3*i+0] * isat << "\t" << - // st::internal::cell_magnetization[3*i+1] * isat << "\t" << - // st::internal::cell_magnetization[3*i+2] * isat << "\t" << - // st::internal::cell_spin_torque_fields[3*i+0] << "\t" << - // st::internal::cell_spin_torque_fields[3*i+1] << "\t" << - // st::internal::cell_spin_torque_fields[3*i+2] << "\t" << - // st::internal::cell_resistance[i] << "\t" << - // st::internal::cell_spin_resistance[i] << std::endl; - // } - // ofile.close(); + /*if( vmpi::my_rank == 0 && !st::internal::sublattice){ + std::ofstream ofile("stdata.txt"); + for(int i =0; i< st::internal::total_num_cells; i++){ + const double isat = st::internal::cell_isaturation[i]; + ofile << st::internal::cell_position[3*i+0] << "\t" << + st::internal::cell_position[3*i+1] << "\t" << + st::internal::cell_position[3*i+2] << "\t" << + st::internal::cell_magnetization[3*i+0] * isat << "\t" << + st::internal::cell_magnetization[3*i+1] * isat << "\t" << + st::internal::cell_magnetization[3*i+2] * isat << "\t" << + st::internal::cell_spin_torque_fields[3*i+0] << "\t" << + st::internal::cell_spin_torque_fields[3*i+1] << "\t" << + st::internal::cell_spin_torque_fields[3*i+2] << "\t" << + st::internal::cell_resistance[i] << "\t" << + st::internal::cell_spin_resistance[i] << std::endl; + } + ofile.close(); + std::cin.get(); + } + + if( vmpi::my_rank == 0 && st::internal::sublattice){ + const int num_sublattices = st::internal::num_sublattices; + std::ofstream ofile("stdata.txt"); + std::cout << "CRS: " << st::internal::cell_resistance.size() << "\t" << st::internal::cell_spin_resistance.size() << std::endl; + for(uint64_t i =0; i< st::internal::total_num_cells; i++){ + ofile << st::internal::cell_position[3*i+0] << "\t" << + st::internal::cell_position[3*i+1] << "\t" << + st::internal::cell_position[3*i+2] << "\t"; // << + //st::internal::cell_resistance[i] << "\t"; // << + //st::internal::cell_spin_resistance[i] << "\t"; + + for( int sl = 0; sl < num_sublattices; sl++ ){ + + const double isat = st::internal::cell_sl_isaturation[i][sl]; + double mix = st::internal::cell_sl_magnetization_x[i][sl] * isat; + double miy = st::internal::cell_sl_magnetization_y[i][sl] * isat; + double miz = st::internal::cell_sl_magnetization_z[i][sl] * isat; + ofile << mix << "\t" << miy << "\t" << miz << "\t" << st::internal::cell_sl_spin_torque_fields_x[i][sl] << "\t" << + st::internal::cell_sl_spin_torque_fields_y[i][sl] << "\t" << + st::internal::cell_sl_spin_torque_fields_z[i][sl] << "\t" << + st::internal::cell_sl_resistance[i][sl] << "\t" << + st::internal::cell_sl_spin_resistance[i][sl] << "\t"; + ; + } + ofile << std::endl; + } + ofile.close(); + std::cin.get(); + }*/ return; diff --git a/src/spinwaves/built_in_paths.cpp b/src/spinwaves/built_in_paths.cpp new file mode 100644 index 000000000..65897da58 --- /dev/null +++ b/src/spinwaves/built_in_paths.cpp @@ -0,0 +1,180 @@ + +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Sergiu Ruta 2022. All rights reserved. +// +// Email: sergiu.ruta@york.ac.uk j.r.hirst@shu.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers + +// Vampire headers +#include "spinwaves.hpp" +#include "unitcell.hpp" +#include "internal.hpp" +#include "errors.hpp" +#include "atoms.hpp" +#include "vmpi.hpp" +#include "vio.hpp" + + +// sw module headers +#include +#include +#include +#include +#include +#include +#include + +namespace spinwaves { + + namespace internal { + + void path_sc(){ + std::vector dummy_x = {0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.5, 0.5}; + std::vector dummy_y = {0.0, 0.5, 0.5, 0.5, 0.5, 0.0, 0.0, 0.5, 0.5, 0.5, 0.5, 0.5}; + std::vector dummy_z = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.5, 0.0}; + spinwaves::internal::pathx.swap(dummy_x); + spinwaves::internal::pathy.swap(dummy_y); + spinwaves::internal::pathz.swap(dummy_z); + } + + void path_bcc(){ + std::vector dummy_x = {0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.0, 0.5, 0.5, 0.0, 0.5, 0.5}; + std::vector dummy_y = {0.0, 1.0, 1.0, 0.5, 0.5, 0.0, 0.0, 0.5, 0.5, 1.0, 0.5, 0.5}; + std::vector dummy_z = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.0, 0.5, 0.0}; + spinwaves::internal::pathx.swap(dummy_x); + spinwaves::internal::pathy.swap(dummy_y); + spinwaves::internal::pathz.swap(dummy_z); + } + + void path_fcc(){ + std::vector dummy_x = {0.00, 0.00, 0.00, 0.25, 0.75, 0.00, 0.00, 0.50, 0.50, 0.50, 0.50, 0.00}; + std::vector dummy_y = {0.00, 1.00, 1.00, 1.00, 0.75, 0.00, 0.00, 0.50, 0.50, 1.00, 1.00, 1.00}; + std::vector dummy_z = {0.00, 0.00, 0.00, 0.25, 0.00, 0.00, 0.00, 0.50, 0.50, 0.00, 0.00, 0.00}; + spinwaves::internal::pathx.swap(dummy_x); + spinwaves::internal::pathy.swap(dummy_y); + spinwaves::internal::pathz.swap(dummy_z); + } + + void path_hcp(){ + std::vector dummy_x = {0.0, 0.5, 0.5, 1.0 , 1.0 , 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 1.0 , 1.0 , 0.0, 0.5, 0.5, 1.0 , 1.0}; + std::vector dummy_y = {0.0, 0.5, 0.5, 0.333333, 0.333333, 0.0, 0.0, 0.0, 0.0, 0.5, 0.5, 0.333333, 0.333333, 0.0, 0.5, 0.5, 0.333333, 0.333333}; + std::vector dummy_z = {0.0, 0.0, 0.0, 0.0 , 0.0 , 0.0, 0.0, 0.5, 0.5, 0.5, 0.5, 0.5 , 0.5 , 0.5, 0.5, 0.0, 0.5 , 0.0}; + spinwaves::internal::pathx.swap(dummy_x); + spinwaves::internal::pathy.swap(dummy_y); + spinwaves::internal::pathz.swap(dummy_z); + } + + void path_mn2au(){ + std::vector dummy_x = {0.0, 0.5, 0.5, 0.5, 0.5, 0.5, 0.5, 0.0, 0.0, 0.0, 0.0, 0.44444444, 0.55555555, 0.0, 0.5, 0.55555555, 0.44444444, 0.0}; + std::vector dummy_y = {0.0, 0.5, 0.5, 0.5, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 , 0.0 , 0.0, 0.5, 0.44444444, 0.44444444, 0.0}; + std::vector dummy_z = {0.0, 0.0, 0.0, 0.5, 0.5, 0.5, 0.5, 0.0, 0.0, 1.0, 1.0, 1.0 , 0.0 , 0.0, 0.0, 0.0 , 1.0 , 1.0}; + spinwaves::internal::pathx.swap(dummy_x); + spinwaves::internal::pathy.swap(dummy_y); + spinwaves::internal::pathz.swap(dummy_z); + } + + void path_rocksalt(){ + std::vector dummy_x = {0.0, 0.0, 0.0, 0.25, 0.75, 0.0, 0.0, 0.5, 0.5, 0.5, 0.5, 0.0}; + std::vector dummy_y = {0.0, 1.0, 1.0, 1.0 , 0.75 , 0.0 , 0.0 , 0.5 , 0.5 , 1.0 , 1.0 , 1.0}; + std::vector dummy_z = {0.0, 0.0, 0.0, 0.25, 0.0 , 0.0 , 0.0 , 0.5 , 0.5 , 0.0 , 0.0 , 0.0}; + spinwaves::internal::pathx.swap(dummy_x); + spinwaves::internal::pathy.swap(dummy_y); + spinwaves::internal::pathz.swap(dummy_z); + } + + void path_heusler(){ + std::vector dummy_x = {0.00, 0.00, 0.00, 0.25, 0.75, 0.00, 0.00, 0.50, 0.50, 0.50, 0.50, 0.00}; + std::vector dummy_y = {0.00, 1.00, 1.00, 1.00, 0.75, 0.00, 0.00, 0.50, 0.50, 1.00, 1.00, 1.00}; + std::vector dummy_z = {0.00, 0.00, 0.00, 0.25, 0.00, 0.00, 0.00, 0.50, 0.50, 0.00, 0.00, 0.00}; + spinwaves::internal::pathx.swap(dummy_x); + spinwaves::internal::pathy.swap(dummy_y); + spinwaves::internal::pathz.swap(dummy_z); + } + + + + int gcd(int a, int b){ + a = abs(a);b = abs(b); + if (a == 0) + return b; + if (b == 0) + return a; + if (a == b) + return a; + if (a > b) + return gcd(a - b, b); + return gcd(a, b - a); + } + + } + +} + + + + // void bcc() { + + // GAMMA H : [0.0, 0.0, 0.0] => [0.0, 1.0, 0.0] + // H N : [0.0, 1.0, 0.0] => [0.5, 0.5, 0.0] + // N GAMMA : [0.5, 0.5, 0.0] => [0.0, 0.0, 0.0] + // GAMMA P : [0.0, 0.0, 0.0] => [0.5, 0.5, 0.5] + // P H : [0.5, 0.5, 0.5] => [0.0, 1.0, 0.0] + // P N : [0.5, 0.5, 0.5] => [0.5, 0.5, 0.0] + + // } + + // void sc() { + + // GAMMA X : [0.0, 0.0, 0.0] => [0.0, 0.5, 0.0] + // X M : [0.0, 0.5, 0.0] => [0.5, 0.5, 0.0] + // M GAMMA : [0.5, 0.5, 0.0] => [0.0, 0.0, 0.0] + // GAMMA R : [0.0, 0.0, 0.0] => [0.5, 0.5, 0.5] + // R X : [0.5, 0.5, 0.5] => [0.0, 0.5, 0.0] + // R M : [0.5, 0.5, 0.5] => [0.5, 0.5, 0.0] + + // } + + // void fcc() { + + // GAMMA X : [0.0, 0.0, 0.0] => [0.0, 1.0, 0.0] + // X U : [0.0, 1.0, 0.0] => [0.25, 1.0, 0.25] + // K GAMMA : [0.75, 0.75, 0.0] => [0.0, 0.0, 0.0] + // GAMMA L : [0.0, 0.0, 0.0] => [0.5, 0.5, 0.5] + // L W : [0.5, 0.5, 0.5] => [0.5, 1.0, 0.0] + // W X : [0.5, 1.0, 0.0] => [0.0, 1.0, 0.0] + + // } + + // void hcp() { + + // GAMMA M [0 0 0] => [0.5 0.5 0] + // M K : [0.5 0.5 0] =>[1 0.333333 0] + // K GAMMA [1 0.333333 0] =>[0 0 0 ] + // GAMMA A [0 0 0 ] => [0 0 0.5 ] + // A L : [0 0 0.5 ] => [0.5 0.5 0.5 ] + // L H : [0.5 0.5 0.5 ] =>[1 0.333333 0.5 ] + // H A : [1 0.333333 0.5 ] =>[0 0 0.5 ] + // L M : [0.5 0.5 0.5 ] =>[0.5 0.5 0] + // H K : [1 0.333333 0.5 ] => [1 0.333333 0] + + // } + + // void mn2au(){ + + // GAMMA X : [0.0, 0.0, 0.0] => [0.5, 0.5, 0.0] + // X P : [0.5, 0.5, 0.0] => [0.5, 0.5, 0.5] + // P N : [0.5, 0.5, 0.5] => [0.5, 0.0, 0.5] + // N GAMMA : [0.5, 0.0, 0.5] => [0.0, 0.0, 0.0] + // GAMMA M : [0.0, 0.0, 0.0] => [0.0, 0.0, 1.0] + // M S : [0.0, 0.0, 1.0] => [0.444444444444444, 0.0, 1.0] + // S_0 GAMMA : [0.55555555555555, 0.0, 0.0] => [0.0, 0.0, 0.0] + // X R : [0.5, 0.5, 0.0] => [0.555555555555555, 0.4444444444444444, 0.0] + // G M : [0.4444444444444444, 0.4444444444444444, 1.0] => [0.0, 0.0, 1.0] diff --git a/src/spinwaves/data.cpp b/src/spinwaves/data.cpp new file mode 100644 index 000000000..175c200c5 --- /dev/null +++ b/src/spinwaves/data.cpp @@ -0,0 +1,91 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Sergiu Ruta 2022. All rights reserved. +// +// Email: sergiu.ruta@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers + +// Vampire headers +#include "spinwaves.hpp" + +// sw module headers +#include "internal.hpp" + +namespace spinwaves{ + + //------------------------------------------------------------------------------ + // Externally visible variables ------------------------------------------------ + //------------------------------------------------------------------------------ + std::vector skx_r; + std::vector skx_i; + std::vector skx_r_node; + std::vector skx_i_node; + std::vector skx_r_node_transposed; + std::vector skx_i_node_transposed; + + // THIS IS TEMPORARY + int nk_per_rank; + int scatterlength; + std::vector skx_r_scatter; + std::vector skx_i_scatter; + + namespace internal{ + + //------------------------------------------------------------------------ + // Shared variables inside sw module + //------------------------------------------------------------------------ + bool enabled; // bool to enable module + std::vector kx; + std::vector ky; + std::vector kz; + std::vector mp; // array of material properties + + // JRH Internally visible path vectors + std::vector pathx; + std::vector pathy; + std::vector pathz; + + // JRH internally visible filename + std::string filename; + std::string filetype = "path"; + + // fft-in-time options; + std::string reduc_ver = "rank0"; + std::vector component; + std::vector*> sw_array; + + + bool isf; + std::vector oss; + std::vector cm; + std::vector normk; + std::vector mat_in_spec; + std::vector mat; + int nspec = 1; + bool prefactor = true; + + + std::vector cos_k; + std::vector sin_k; + std::vector atom_mask; + std::vector spec_mask; + + + // JRH number of time and kpoints + int nk; + int nt; + + // to check number of spectrums + std::vector super_index_values; + + + } // end of internal namespace + +} // end of sw namespace diff --git a/src/spinwaves/fft_in_space.cpp b/src/spinwaves/fft_in_space.cpp new file mode 100644 index 000000000..f158a7e1b --- /dev/null +++ b/src/spinwaves/fft_in_space.cpp @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Sergiu Ruta 2022. All rights reserved. +// +// Email: sergiu.ruta@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include +#include +#include +#include + +// Vampire headers +#include "atoms.hpp" +#include "spinwaves.hpp" +#include "unitcell.hpp" +#include "vmpi.hpp" + +// sw module headers +#include "internal.hpp" + +namespace spinwaves { + + // Local variables + double sx; + int atom; + int spec; + int index; + int reduc_index; + + //---------------------------------------------------------------------------- + // Function to initialize sw module + //---------------------------------------------------------------------------- + void fft_in_space( const std::vector& rx, + const std::vector& ry, + const std::vector& rz, + const int time ){ + + for(int k=0;k +#include +#include +#include "vio.hpp" + +//sergiu for SW +#include "unitcell.hpp" +#include "vector" +#include "vmpi.hpp" +#include +#include "fstream" +#include "atoms.hpp" + +#ifdef FFT +#include +#endif + +namespace spinwaves { + + + + void fft_in_time(){ + + #ifdef FFT + + // Start time for time series fourier transform + // Set timer for runtime + std::cout << "Starting calculation of time series Discrete Fourier Transform." << std::endl; + zlog << zTs() << "Starting calculation of time series Discrete Fourier Transform." << std::endl; + stopwatch_t fft_stopwatch; + fft_stopwatch.start(); + + // real and imag indexing + const int real = 0; + const int imag = 1; + + //------------------------------------------------------------------------------------ + //fftw_complex combined_real_imag[internal::nt]; //- can lead to stack overflow + //fftw_complex combined_real_imag_fftd[internal::nt]; + // same using allocated memory + fftw_complex *combined_real_imag = fftw_alloc_complex(internal::nt); + fftw_complex *combined_real_imag_fftd = fftw_alloc_complex(internal::nt); + fftw_plan fft_in_time = fftw_plan_dft_1d(internal::nt, &combined_real_imag[0], &combined_real_imag_fftd[0], FFTW_FORWARD, FFTW_MEASURE); + + #ifdef MPICF + + // // make a mask that determines which kpoints will be dft'd. This prevents dft of lots of 0s on last rank for parallel implementation + std::vector kmask; + kmask.resize(nk_per_rank * vmpi::num_processors,0); + + for (int i = 0; i < internal::nk; i++){ + kmask[i] = 1; + } + std::cout << "Created mask for k-points." << std::endl; + zlog << zTs() << "Created mask for k-points." << std::endl; + + + + + // split comm world for time series fft if nranks > nk + MPI_Comm fft_com; + int color=1; + if (vmpi::my_rank < (internal::nk + nk_per_rank - 1)/nk_per_rank) color = 0; + + MPI_Comm_split(MPI_COMM_WORLD, color, vmpi::my_rank, &fft_com); + // We are in the new communicator + int new_rank, new_size; + MPI_Comm_rank(fft_com, &new_rank); + MPI_Comm_size(fft_com, &new_size); + + + if (color == 0){ + if (internal::reduc_ver == "rank0"){ + + // rearrange array for scatter - I think this bit needs to be done for serial and parallel + if (vmpi::my_rank == 0){ + for (int k = 0; k < internal::nk; k++){ + for (int time=0; time < internal::nt; time++){ + for (int spec = 0; spec < internal::nspec; spec++){ + // Fill FFTW arrays with values from spacial FFT. + skx_r_node_transposed[k*internal::nt*internal::nspec + time*internal::nspec+spec] = skx_r_node[time*internal::nk*internal::nspec + k*internal::nspec+spec]; + skx_i_node_transposed[k*internal::nt*internal::nspec + time*internal::nspec+spec] = skx_i_node[time*internal::nk*internal::nspec + k*internal::nspec+spec]; + } + } + } + } + std::cout << skx_r_node_transposed.size() << " " << scatterlength << std::endl; + std::cout << skx_r_scatter.size() << std::endl; + std::cout << new_size << std::endl; + MPI_Scatter(&skx_r_node_transposed[0], scatterlength, MPI_DOUBLE, &skx_r_scatter[0], scatterlength, MPI_DOUBLE, 0, fft_com); + MPI_Scatter(&skx_i_node_transposed[0], scatterlength, MPI_DOUBLE, &skx_i_scatter[0], scatterlength, MPI_DOUBLE, 0, fft_com); + // for (int j = 0; j < skx_r_scatter.size(); j++){ + // std::cout << new_rank << " " << new_size << " " << skx_r_scatter[j] << std::endl; + + // } + std::cout << "Time-series FFT distrubuted amongst each rank." << std::endl; + zlog << zTs() << "Time-series FFT distrubuted amongst each rank." << std::endl; + } + + // loop over the k-points on each rank + for (int k = 0; k < nk_per_rank; k++){ + for (int spec = 0; spec < internal::nspec; spec++){ + // if the mask is 1, the calculate the dft. + if (kmask[k+new_rank*nk_per_rank] == 1){ + + + // populate fftw_complex vector + for (int time=0; time < internal::nt; time++){ + int index = k*internal::nt*internal::nspec + time*internal::nspec + spec; + combined_real_imag[time][real] = skx_r_scatter[index]; + combined_real_imag[time][imag] = skx_i_scatter[index]; + } + + // exexcute the fft + fftw_execute(fft_in_time); + + // write intermediate structure factor to file + if (internal::isf == true) { + spinwaves::internal::complex_magnitude(combined_real_imag); + spinwaves::internal::write_intermediate_to_file(combined_real_imag, k, spec); + } + + // calculate one sided spectrum + if (internal::oss[spec] == true) spinwaves::internal::one_sided_spectrum(combined_real_imag_fftd); + + // normalise to largest amplitude for each k-value + if (internal::cm[spec] == true) spinwaves::internal::complex_magnitude(combined_real_imag_fftd); + + // normalise to largest amplitude for each k-value + if (internal::normk[spec] == true) spinwaves::internal::normalise_each_kpoint(combined_real_imag_fftd); + + // write each k-value to file + spinwaves::internal::write_to_file(combined_real_imag_fftd, k, spec); + + } + } + } + } + + MPI_Comm_free(&fft_com); + + #else + + // rearrange array for scatter - I think this bit needs to be done for serial and parallel + for (int k = 0; k < internal::nk; k++){ + for (int time=0; time < internal::nt; time++){ + for (int spec = 0; spec < internal::nspec; spec++){ + + // Fill FFTW arrays with values from spacial FFT. + skx_r_node_transposed[k*internal::nt*internal::nspec + time*internal::nspec+spec] = skx_r_node[time*internal::nk*internal::nspec + k*internal::nspec+spec]; + skx_i_node_transposed[k*internal::nt*internal::nspec + time*internal::nspec+spec] = skx_i_node[time*internal::nk*internal::nspec + k*internal::nspec+spec]; + + } + } + } + + for (int k = 0; k < internal::nk; k++){ + for (int spec = 0; spec < internal::nspec; spec++){ + for (int time=0; time < internal::nt; time++){ + + // Fill FFTW arrays with values from spacial FFT. + int index = k*internal::nt*internal::nspec + time*internal::nspec + spec; + combined_real_imag[time][real] = skx_r_node_transposed[index]; + combined_real_imag[time][imag] = skx_i_node_transposed[index]; + + } + + // exexcute the fft + fftw_execute(fft_in_time); + + // write intermediate structure factor to file + if (internal::isf == true) { + spinwaves::internal::complex_magnitude(combined_real_imag); + spinwaves::internal::write_intermediate_to_file(combined_real_imag, k, spec); + } + + // calculate one sided spectrum + if (internal::oss[spec] == true) spinwaves::internal::one_sided_spectrum(combined_real_imag_fftd); + + // normalise to largest amplitude for each k-value + if (internal::cm[spec] == true) spinwaves::internal::complex_magnitude(combined_real_imag_fftd); + + // normalise to largest amplitude for each k-value + if (internal::normk[spec] == true) spinwaves::internal::normalise_each_kpoint(combined_real_imag_fftd); + + // write each k-value to file + spinwaves::internal::write_to_file(combined_real_imag_fftd, k, spec); + } + } + + + #endif + + std::cout << "Total duration of time series discrete fourier transform [s]: " << fft_stopwatch.elapsed_seconds() << std::endl; + zlog << zTs() << "Total duration of time series discrete fourier transform [s]: " << fft_stopwatch.elapsed_seconds() << std::endl; + + // destroy fftw3 plan + fftw_destroy_plan(fft_in_time); + + // free complex alocated arrays + fftw_free(combined_real_imag); + fftw_free(combined_real_imag_fftd); + + #endif // end of FFT macro + + } + +} // end of spinwaves namespace diff --git a/src/spinwaves/fft_in_time_options.cpp b/src/spinwaves/fft_in_time_options.cpp new file mode 100644 index 000000000..6ccb036a9 --- /dev/null +++ b/src/spinwaves/fft_in_time_options.cpp @@ -0,0 +1,201 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Joel Hirst 2023. All rights reserved. +// +// Email: j.r.hirst@shu.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include +#include +#include +#include + +// Vampire headers +#include "atoms.hpp" +#include "spinwaves.hpp" +#include "unitcell.hpp" +#include "vmpi.hpp" + +// sw module headers +#include "internal.hpp" + +#ifdef FFT + #include +#endif + +namespace spinwaves { + + namespace internal { + + std::ofstream file_K_time_real; + std::ofstream file_K_time_imag; + int j2; + + #ifdef FFT + + const int real = 0; + const int imag = 1; + + // ============================================================================================================================================= + // Calculate one sided spectrum ================================================================================================================ + // ============================================================================================================================================= + void one_sided_spectrum(fftw_complex *in){ + + for (int j1 = 0; j1 < internal::nt/2; j1++){ + j2 = internal::nt-j1-1; + in[j1][real] = in[j1][real] + in[j2][real]; + in[j1][imag] = in[j1][imag] + in[j2][imag]; + + } + } + + void complex_magnitude(fftw_complex *os){ + + for (int j1 = 0; j1 < internal::nt; j1++){ + os[j1][real] = os[j1][real] * os[j1][real] + os[j1][imag] * os[j1][imag]; + os[j1][imag] = 0.0; + + } + + } + + + void normalise_each_kpoint(fftw_complex *os){ + + double largest = std::abs(os[0][real]); + //double index; + + // Find largest value in k_z array - REAL + for (int j1 = 1; j1 < internal::nt; j1++){ + + if (largest < std::abs(os[j1][real])){ + largest = std::abs(os[j1][real]); + //index = j1; + } + } + // normlise each value + for (int j1 = 0; j1 < internal::nt; j1++){ + os[j1][real] /= largest; + } + + largest = std::abs(os[0][imag]); + + // Find largest value in k_z array - COMPLEX + for (int j1 = 1; j1 < internal::nt; j1++){ + if (largest < std::abs(os[j1][imag])){ + largest = std::abs(os[j1][imag]); + //index = j1; + } + } + // normlise each value + for (int j1 = 0; j1 < internal::nt; j1++){ + os[j1][imag] /= largest; + } + + + } + + void write_to_file(fftw_complex *os, int k, int spec){ + + std::stringstream sstr_real; + std::stringstream sstr_imag; + + if (cm[spec] == true){ + + // output filename + #ifdef MPICF + sstr_real << "sw_spec_" << std::setw(2) << std::setfill('0') << spec+1 << "_real_" << std::setw(4) << std::setfill('0') << std::to_string(k+vmpi::my_rank*spinwaves::nk_per_rank) << ".dat"; + #else + sstr_real << "sw_spec_" << std::setw(2) << std::setfill('0') << spec+1 << "_real_" << std::setw(4) << std::setfill('0') << std::to_string(k) << ".dat"; + #endif + + // open file + file_K_time_real.open(sstr_real.str()); + + if (oss[spec] == true){ + for (int time=0; time < internal::nt/2; time++){ + file_K_time_real << os[time][real] << "\n"; + } + } + else if (oss[spec] == false){ + for (int time=0; time < internal::nt; time++){ + file_K_time_real << os[time][real] << "\n"; + } + } + + // close file + file_K_time_real.close(); + + } + else if (cm[spec] == false){ + + // output filenames + #ifdef MPICF + sstr_real << "sw_spec_" << std::setw(2) << std::setfill('0') << spec+1 << "_real_" << std::setw(4) << std::setfill('0') << std::to_string(k+vmpi::my_rank*spinwaves::nk_per_rank) << ".dat"; + sstr_imag << "sw_spec_" << std::setw(2) << std::setfill('0') << spec+1 << "_imag_" << std::setw(4) << std::setfill('0') << std::to_string(k+vmpi::my_rank*spinwaves::nk_per_rank) << ".dat"; + #else + sstr_real << "sw_spec_" << std::setw(2) << std::setfill('0') << spec+1 << "_real_" << std::setw(4) << std::setfill('0') << std::to_string(k) << ".dat"; + sstr_imag << "sw_spec_" << std::setw(2) << std::setfill('0') << spec+1 << "_imag_" << std::setw(4) << std::setfill('0') << std::to_string(k) << ".dat"; + #endif + + // open files + file_K_time_real.open(sstr_real.str()); + file_K_time_imag.open(sstr_imag.str()); + + if (oss[spec] == true){ + for (int time=0; time < internal::nt/2; time++){ + file_K_time_real << os[time][real] << "\n"; + file_K_time_imag << os[time][imag] << "\n"; + } + } + else if (oss[spec] == false){ + for (int time=0; time < internal::nt; time++){ + file_K_time_real << os[time][real] << "\n"; + file_K_time_imag << os[time][imag] << "\n"; + } + } + } + + file_K_time_real.close(); + file_K_time_imag.close(); + } + + void write_intermediate_to_file(fftw_complex *os, int k, int spec){ + + std::stringstream sstr_real; + // std::stringstream sstr_imag; + + // output filenames + #ifdef MPICF + sstr_real << "sw_intermediate_spec_" << std::setw(2) << std::setfill('0') << spec+1 << "_real_" << std::setw(4) << std::setfill('0') << std::to_string(k+vmpi::my_rank*spinwaves::nk_per_rank) << ".dat"; + // sstr_imag << "k_intermediate_imag_" << std::setw(4) << std::setfill('0') << std::to_string(k+vmpi::my_rank*spinwaves::nk_per_rank) << ".dat"; + #else + sstr_real << "sw_intermediate_spec_" << std::setw(2) << std::setfill('0') << spec+1 << "_real_" << std::setw(4) << std::setfill('0') << std::to_string(k) << ".dat"; + // sstr_imag << "k_intermediate_imag_" << std::setw(4) << std::setfill('0') << std::to_string(k) << ".dat"; + #endif + + // open files + file_K_time_real.open(sstr_real.str()); + // file_K_time_imag.open(sstr_imag.str()); + + for (int time=0; time < internal::nt; time++){ + file_K_time_real << os[time][real] << "\n"; + // file_K_time_imag << os[time][imag] << "\n"; + } + + file_K_time_real.close(); + // file_K_time_imag.close(); + } + + #endif // end of FFT macro + } + +} diff --git a/src/spinwaves/initialize.cpp b/src/spinwaves/initialize.cpp new file mode 100644 index 000000000..e21b955b0 --- /dev/null +++ b/src/spinwaves/initialize.cpp @@ -0,0 +1,109 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Sergiu Ruta 2022. All rights reserved. +// +// Email: sergiu.ruta@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include +#include +#include +#include +#include + +// Vampire headers +#include "atoms.hpp" +#include "spinwaves.hpp" +#include "errors.hpp" +#include "program.hpp" +#include "sim.hpp" +#include "vio.hpp" +#include "unitcell.hpp" +#include "vmpi.hpp" + +// sw module headers +#include "internal.hpp" + +namespace spinwaves{ + // std::vector Skx_FFT_array; + // std::vector Sky_FFT_array; + // std::vector Skz_FFT_array; + + //---------------------------------------------------------------------------- + // Function to initialize sw module + //---------------------------------------------------------------------------- + void initialize(const double system_dimensions_x, + const double system_dimensions_y, + const double system_dimensions_z, + const double total_num_unit_cells_x, + const double total_num_unit_cells_y, + const double total_num_unit_cells_z, + const double unit_cell_size_x, + const double unit_cell_size_y, + const double unit_cell_size_z, + const std::vector & atom, + const std::vector& atom_coords_x, + const std::vector& atom_coords_y, + const std::vector& atom_coords_z){ + + //------------------------------------------------------------------------------------- + // Check if spinwave calculation enabled, if not do nothing + //------------------------------------------------------------------------------------- + if(program::program!=74) return; + + // ensure that code was compiled with FFT library + #ifdef FFT + #else + std::cerr << "Error: code must be compiled with a linked FFTW library for the spin wave module to function, exiting" << std::endl; + err::vexit(); + #endif + + // check spectrum values in input file are in agreement with the spinwaves:number-of-spectrums + spinwaves::internal::check_numbering_of_spectrums(); + + //int Na=atom.size(); + //const double toll_Fikj=1e-5; // tollerance for the structure factor values smaller than tollerance are considered 0 + + // number of steps for sw calculations + //int numsteps = (sim::total_time / sim::partial_time); + + // if caclulating spinwaves for a specific material, we need to apply a mask + spinwaves::internal::calculate_material_mask(); + + // JRH determine whether to use path for a predefined crystal or whether path has been specified by user. + spinwaves::internal::determine_path(); + + // JRH calculate the values of $k$ at which to calculate the DSF for the user specified system dimensions. + if (internal::filetype == "specific-k") spinwaves::internal::determine_kpoints_from_user_specific_k(system_dimensions_x, system_dimensions_y, system_dimensions_z, unit_cell_size_x, unit_cell_size_y, unit_cell_size_z); + if (internal::filetype == "path") spinwaves::internal::determine_kpoints_from_user_high_sym_path(system_dimensions_x, system_dimensions_y, system_dimensions_z, unit_cell_size_x, unit_cell_size_y, unit_cell_size_z); + + // initialise arrays based on kpoints calculated above + spinwaves::internal::initialise_arrays(); + + // determine prefactor that will be used in fourier transform. sin(k_x*r_x) etc. + spinwaves::internal::calculate_fourier_prefactor(atom_coords_x, atom_coords_y, atom_coords_z); + + // determine which component of spin to calculate spinwave dispersion from + spinwaves::internal::determine_spin_component(); + + // output a file containing frequencies + spinwaves::internal::save_frequencies(); + + std::cout<< "SW initialisation completed."< +#include +#include + +// Vampire headers +#include "material.hpp" +#include "spinwaves.hpp" +#include "errors.hpp" +#include "vio.hpp" + +// sw module headers +#include "internal.hpp" + +namespace spinwaves{ + + int largest = 0; + + //--------------------------------------------------------------------------- + // Function to process input file parameters for sw module + //--------------------------------------------------------------------------- + bool match_input_parameter(std::string const key, std::string const word, std::string const value, std::string const unit, int const line){ + + // std::cout << key << std::endl; + // Check for the generic spinwaves key used for input file and reduction method + std::string prefix="spinwaves"; + if(key==prefix){ + + std::string test="kfile"; + if(word==test){ + std::string kpath_file=value; + // strip quotes + kpath_file.erase(std::remove(kpath_file.begin(), kpath_file.end(), '\"'), kpath_file.end()); + test=""; + // if filename not blank set ucf file name + if(kpath_file!=test){ + + // send name to internal variable + spinwaves::internal::filename=kpath_file; + + // check file exists + std::ifstream fin(kpath_file); + if (!fin){ + terminaltextcolor(RED); + std::cerr << "Error - cannot find file \'" << kpath_file << "\' in control statement \'spinwaves:" << word << "\' on line " << line << " of input file" << std::endl; + zlog << zTs() << "Error - cannot find file \'" << kpath_file << "\' in control statement \'spinwaves:" << word << "\' on line " << line << " of input file" << std::endl; + terminaltextcolor(WHITE); + err::vexit(); + } + + return true; + } + else{ + terminaltextcolor(RED); + std::cerr << "Error - empty filename in control statement \'spinwaves:" << word << "\' on line " << line << " of input file" << std::endl; + terminaltextcolor(WHITE); + return false; + } + } + test="filetype"; + if(word==test){ + std::string filetype_temp=value; + filetype_temp.erase(std::remove(filetype_temp.begin(), filetype_temp.end(), '\"'), filetype_temp.end()); + internal::filetype=filetype_temp; + + if (internal::filetype == "path" || internal::filetype== "specific-k"){ + return true; + } + else { + terminaltextcolor(RED); + std::cerr << "Error - Unexpected filetype for spinwave module. \'spinwaves:" << word << " = " << value << "\' on line " << line << " of input file" << std::endl; + terminaltextcolor(WHITE); + return false; + } + } + test="reduction-method"; + if(word==test){ + std::string reduc_string=value; + reduc_string.erase(std::remove(reduc_string.begin(), reduc_string.end(), '\"'), reduc_string.end()); + internal::reduc_ver=reduc_string; + + if (internal::reduc_ver == "rank0" || internal::reduc_ver == "direct-scatter"){ + return true; + } + else { + terminaltextcolor(RED); + std::cerr << "Error - Unexpected method for MPI reduction in spinwave model. \'spinwaves:" << word << " = " << value << "\' on line " << line << " of input file" << std::endl; + terminaltextcolor(WHITE); + return false; + } + } + + test="num-spectrums"; + if(word==test){ + uint64_t tt = vin::str_to_uint64(value); // convert string to uint64_t + vin::check_for_valid_int(tt, word, line, prefix, 1, 20,"input","1 - 20"); + internal::nspec = tt; + + // resize spectrum specific arrays + internal::oss.resize(internal::nspec); + internal::cm.resize(internal::nspec); + internal::normk.resize(internal::nspec); + internal::component.resize(internal::nspec); + + // populate with default values + for (int j = 0; j < internal::nspec; j++){ + internal::oss[j] = true; + internal::cm[j] = true; + internal::normk[j] = true; + internal::component[j] = "sx"; + } + return true; + } + test="fourier-prefactor"; + if(word==test){ + + if (value == "false"){ + internal::prefactor = false; + return true; + } + else if (value == "true"){ + internal::prefactor = true; + return true; + } + else { + terminaltextcolor(RED); + std::cerr << "Error - Unknown value in control statement \'spinwaves:" << word << " = " << value << "\' on line " << line << " of input file" << std::endl; + terminaltextcolor(WHITE); + return false; + } + } + test="intermediate-structure-factor"; + if(word==test){ + if (value == "false"){ + internal::isf = false; + return true; + } + else if (value == "true"){ + internal::isf = true; + return true; + } + else { + terminaltextcolor(RED); + std::cerr << "Error - Unknown value in control statement \'spinwaves:" << word << " = " << value << "\' on line " << line << " of input file" << std::endl; + terminaltextcolor(WHITE); + return false; + } + } + + } + + + // now check if the keys exist for each specific spectrum. + if (key.find("spinwaves[") != std::string::npos){ + + // I've set the maximum number of spectrums = 20. I can't imagine anyone would need that many. + for (int i = 0 ; i < 20; i++){ + + int len=i+1; + + // set the prefix as a substring + prefix = "spinwaves[" + std::to_string(len) + "]"; + + if (key==prefix){ + + // array to verify number of spectrums + internal::super_index_values.push_back(i); + + std::string test="one-sided"; + if(word==test){ + if (value == "false"){ + internal::oss[i] = "false"; + return true; + } + else if (value == "true"){ + internal::oss[i] = "false"; + return true; + } + else { + terminaltextcolor(RED); + std::cerr << "Error - Unknown value in control statement \'spinwaves:" << word << " = " << value << "\' on line " << line << " of input file" << std::endl; + terminaltextcolor(WHITE); + return false; + } + } + + test="material"; + if(word==test){ + + std::vector temp; + std::istringstream iss(value); + std::string token; + + // if (value == "all") + + while (std::getline(iss, token, ',')){ + //uint64_t tt = std::stoi(token); + + std::cout << i << " " << std::stoi(token)-1 << std::endl; + internal::mat.push_back(std::stoi(token)-1); + internal::mat_in_spec.push_back(i); + } + + return true; + } + // ------------------------------------------------------------------ + + + // ------------------------------------------------------------------ + // component of magnetisation to use for fourier transform ----------- + // ------------------------------------------------------------------ + test="complex-magnitude"; + if(word==test){ + if (value == "false"){ + internal::cm[i] = false; + return true; + } + else if (value == "true"){ + internal::cm[i] = true; + return true; + } + else { + terminaltextcolor(RED); + std::cerr << "Error - Unknown value in control statement \'spinwaves:" << word << " = " << value << "\' on line " << line << " of input file" << std::endl; + terminaltextcolor(WHITE); + return false; + } + } + // ---------------------------------------------------------------- + + // ------------------------------------------------------------------ + // component of magnetisation to use for fourier transform ----------- + // ------------------------------------------------------------------ + test="normalise-each-k"; + if(word==test){ + if (value == "false"){ + internal::normk[i] = false; + return true; + } + else if (value == "true"){ + internal::normk[i] = true; + return true; + } + else { + terminaltextcolor(RED); + std::cerr << "Error - Unknown value in control statement \'spinwaves:" << word << " = " << value << "\' on line " << line << " of input file" << std::endl; + terminaltextcolor(WHITE); + return false; + } + } + // ------------------------------------------------------------------ + + test="component"; + if(word==test){ + std::string component_temp=value; + component_temp.erase(std::remove(component_temp.begin(), component_temp.end(), '\"'), component_temp.end()); + internal::component[i]=component_temp; + + // check input file contains allowed entry. + if (component_temp == "sx" || component_temp == "sy" || component_temp == "sz"){ + return true; + } + else { + terminaltextcolor(RED); + std::cerr << "Error - Unexpected method for MPI reduction in spinwave model. \'spinwaves:" << word << " = " << value << "\' on line " << line << " of input file" << std::endl; + terminaltextcolor(WHITE); + return false; + } + } + } + } + } + + //-------------------------------------------------------------------- + // Keyword not found + //-------------------------------------------------------------------- + return false; + + } + + //--------------------------------------------------------------------------- + // 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){ + + // add prefix string + std::string prefix="material:"; + + // Check for material id > current array size and if so dynamically expand mp array + if((unsigned int) super_index + 1 > internal::mp.size() && super_index + 1 < 101) internal::mp.resize(super_index + 1); + + //-------------------------------------------------------------------- + // Keyword not found + //-------------------------------------------------------------------- + return false; + + } + +} // end of sw namespace diff --git a/src/spinwaves/internal.hpp b/src/spinwaves/internal.hpp new file mode 100644 index 000000000..5f28fa9b3 --- /dev/null +++ b/src/spinwaves/internal.hpp @@ -0,0 +1,148 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Sergiu Ruta 2022. All rights reserved. +// +// Email: sergiu.ruta@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +#ifndef SW_INTERNAL_H_ +#define SW_INTERNAL_H_ +// +//--------------------------------------------------------------------- +// This header file defines shared internal data structures and +// functions for the sw module. These functions and +// variables should not be accessed outside of this module. +//--------------------------------------------------------------------- + +// C++ standard library headers +#include + +// Vampire headers +#include "spinwaves.hpp" + +// sw module headers +#include "internal.hpp" +#ifdef FFT +#include +#endif +namespace spinwaves{ + + namespace internal{ + + //------------------------------------------------------------------------- + // Internal data type definitions + //------------------------------------------------------------------------- + + //----------------------------------------------------------------------------- + // internal materials class for storing material parameters + //----------------------------------------------------------------------------- + class mp_t{ + + private: + + public: + + //------------------------------ + // material parameter variables + //------------------------------ + double test; + + // constructor + mp_t (const unsigned int max_materials = 100): + test(0.0) // constructor initialisation of test variable + { + // constructor body for initialising more complex data/arrays + }; // end of constructor + + }; // end of internal::mp class + + //------------------------------------------------------------------------- + // Internal shared variables + //------------------------------------------------------------------------- + + extern bool enabled; // bool to enable module + + extern std::vector mp; // array of material properties + extern std::vector kx; + extern std::vector ky; + extern std::vector kz; + + // JRH extern file variables + extern std::string filename; + extern std::string filetype; + + // JRH path vectors + extern std::vector pathx; + extern std::vector pathy; + extern std::vector pathz; + + // JRH fourier prefactors + extern std::vector cos_k; + extern std::vector sin_k; + + // JRH mask for calculations of spinwave for specific material type + extern std::vector atom_mask; + extern std::vector spec_mask; + + //JRH time variables + extern int nt; + extern int nk; + + // JRH fft-in-time variables + extern std::string reduc_ver; + extern std::vector component; + extern std::vector*> sw_array; + extern std::vector oss, cm, normk; + extern std::vector mat, mat_in_spec; + extern bool prefactor; + extern bool isf; + extern int nspec; + extern std::vector super_index_values; + + + //------------------------------------------------------------------------- + // Internal function declarations + //------------------------------------------------------------------------- + + void path_sc(); + extern void path_bcc(); + extern void path_fcc(); + extern void path_hcp(); + extern void path_mn2au(); + extern void path_rocksalt(); + extern void determine_path(); + extern int gcd(int a, int b); + extern void save_frequencies(); + extern void initialise_arrays(); + extern void calculate_material_mask(); + extern void determine_spin_component(); + extern void check_numbering_of_spectrums(); + extern void calculate_fourier_prefactor(const std::vector& rx, const std::vector& ry, const std::vector& rz); + extern void determine_kpoints_from_user_specific_k(const double dimx, const double dimy, const double dimz, const double uc_x, const double uc_y, const double uc_z); + extern void determine_kpoints_from_user_high_sym_path(const double dimx, const double dimy, const double dimz, const double uc_x, const double uc_y, const double uc_z); + + + // post analysis functions + #ifdef FFT + extern void complex_magnitude(fftw_complex *os); + extern void one_sided_spectrum(fftw_complex *os); + extern void write_to_file(fftw_complex *os, int k, int spec); + extern void normalise_each_kpoint(fftw_complex *os); + extern void write_intermediate_to_file(fftw_complex *os, int k, int spec); + #endif + + // Peak finding at some point?????/ + // extern void write_peaks_to_file(); + // extern void calculate_spectrum_peaks(); + + + } // end of internal namespace + +} // end of sw namespace + +#endif //SW_INTERNAL_H_ diff --git a/src/spinwaves/makefile b/src/spinwaves/makefile new file mode 100644 index 000000000..09d68382d --- /dev/null +++ b/src/spinwaves/makefile @@ -0,0 +1,18 @@ +#-------------------------------------------------------------- +# Makefile for sw module +#-------------------------------------------------------------- + +# List module object filenames +spinwave_objects =\ +data.o \ +built_in_paths.o \ +paths.o \ +initialize.o \ +interface.o \ +fft_in_time.o \ +fft_in_time_options.o \ +fft_in_space.o \ +util.o + +# Append module objects to global tree +OBJECTS+=$(addprefix obj/spinwaves/,$(spinwave_objects)) diff --git a/src/spinwaves/paths.cpp b/src/spinwaves/paths.cpp new file mode 100644 index 000000000..66eb08806 --- /dev/null +++ b/src/spinwaves/paths.cpp @@ -0,0 +1,325 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Sergiu Ruta 2022. All rights reserved. +// +// Email: sergiu.ruta@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include +#include +#include +#include +#include + +// Vampire headers +#include "spinwaves.hpp" +#include "unitcell.hpp" +#include "internal.hpp" +#include "errors.hpp" +#include "atoms.hpp" +#include "vmpi.hpp" +#include "vio.hpp" +#include "sim.hpp" + +namespace spinwaves{ + + namespace internal { + + void initialise_arrays(){ + + // define number of time and kpoitns + internal::nk = spinwaves::internal::kx.size(); + internal::nt = (sim::total_time / sim::partial_time); + std::cout << "total number of spinwave kpoints " << internal::nk << std::endl; + std::cout << "total number of spinwave timepoints " << internal::nt << std::endl; + + + // Spinwaves have to be calculated for every k on every rank + spinwaves::skx_r.resize(internal::nk*internal::nspec,0.0); + spinwaves::skx_i.resize(internal::nk*internal::nspec,0.0); + + // calculate memory requirements + double mem = (4.0 * internal::nt * internal::nk * internal::nspec * sizeof(double)) / 1.0e6; + std::cout << "Spinwave module using " << mem << " MB of RAM per node for k vs time arrays for a simulation time of " << sim::total_time; + std::cout << " timesteps with the dynamic structure factor calculated every " << sim::partial_time << " timesteps." << std::endl; + + zlog << zTs() << "Spinwave module using " << mem << " MB of RAM per node for k vs time arrays for a simulation time of " << sim::total_time + << " timesteps with the dynamic structure factor calculated every " << sim::partial_time << " timesteps." << std::endl; + + + // the array containing the the values of k for every timepoint only needs to be stored on rank0 for each node. It also needs to be transposed + #ifdef MPICF + + nk_per_rank = std::ceil(static_cast(internal::nk) / static_cast(vmpi::num_processors)); + std::cout << "nk_per_rank " << nk_per_rank << std::endl; + scatterlength = nk_per_rank * internal::nt * internal::nspec; + std::cout << "scatterlength " << scatterlength << std::endl; + skx_r_scatter.resize(scatterlength,0.0); + skx_i_scatter.resize(scatterlength,0.0); + + // resize arrays + if (vmpi::my_rank == 0){ + + int lennode = internal::nt * internal::nspec * internal::nk; + spinwaves::skx_r_node.resize(lennode,0.0); + spinwaves::skx_i_node.resize(lennode,0.0); + + // need to pad for scatter of time-series data to each ranks. + int lentrans = internal::nt * internal::nspec * ((internal::nk + nk_per_rank - 1)/nk_per_rank) * nk_per_rank; + spinwaves::skx_r_node_transposed.resize(lentrans,0.0); + spinwaves::skx_i_node_transposed.resize(lentrans,0.0); + } + + #else + spinwaves::skx_r_node.resize(internal::nt*internal::nk*internal::nspec,0.0); + spinwaves::skx_i_node.resize(internal::nt*internal::nk*internal::nspec,0.0); + spinwaves::skx_r_node_transposed.resize(internal::nt*internal::nk*internal::nspec,0.0); + spinwaves::skx_i_node_transposed.resize(internal::nt*internal::nk*internal::nspec,0.0); + #endif + } + + void determine_path(){ + + std::ifstream file_read_K_path; + file_read_K_path.open(spinwaves::internal::filename); + + + // else read the path from the file specified in interface.cpp + if (file_read_K_path.is_open()){ + + std::cout << "Spinwaves path file " << spinwaves::internal::filename << " succesfully opened." << std::endl; + zlog << zTs() << "Spinwaves path file " << spinwaves::internal::filename << " succesfully opened." << std::endl; + + // read contents of path file + int linecount = 0; + std::string line; + while (std::getline(file_read_K_path, line)) { + std::istringstream iss(line); + linecount++; + double val1, val2, val3; + if (iss >> val1 >> val2 >> val3) { + spinwaves::internal::pathx.push_back(val1); + spinwaves::internal::pathy.push_back(val2); + spinwaves::internal::pathz.push_back(val3); + } + else { + std::cerr << "Error reading line: " << line << std::endl; + } + } + + std::cout << "Spinwaves path file " << spinwaves::internal::filename << " contains " << linecount << " lines." << std::endl; + zlog << zTs() << "Spinwaves path file" << spinwaves::internal::filename << " contains " << linecount << " lines." << std::endl; + + // close file and check + file_read_K_path.close(); + std::cout << "Spinwaves path file " << spinwaves::internal::filename << " has been closed." << std::endl; + zlog << zTs() << "Spinwaves path file " << spinwaves::internal::filename << " has been closed." << std::endl; + + } + // if filename has not been found in spiwnaves/interface.cpp use a built in path + else if (!file_read_K_path.is_open() && uc::sw_crystal_structure!=""){ + std::cout << "sw_crystal_structure = " << uc::sw_crystal_structure<< std::endl; + + // Determine which path to take depending on crystal structure + if(uc::sw_crystal_structure == "sc" ) spinwaves::internal::path_sc(); + else if(uc::sw_crystal_structure == "bcc" ) spinwaves::internal::path_bcc(); + else if(uc::sw_crystal_structure == "bcc-110" ) spinwaves::internal::path_bcc(); + else if(uc::sw_crystal_structure == "fcc" ) spinwaves::internal::path_fcc(); + else if(uc::sw_crystal_structure == "fcc-111" ) spinwaves::internal::path_fcc(); + else if(uc::sw_crystal_structure == "hcp" ) spinwaves::internal::path_hcp(); + else if(uc::sw_crystal_structure == "heusler" ) spinwaves::internal::path_fcc(); + // else if(uc::sw_crystal_structure == "honeycomb" ) spinwaves::internal::path_honeycomb(); + // else if(uc::sw_crystal_structure == "alpha-honeycomb") spinwaves::internal::path_honeycomb_alpha(); + // else if(uc::sw_crystal_structure == "beta-honeycomb" ) spinwaves::internal::path_honeycomb_beta(); + // else if(uc::sw_crystal_structure == "kagome" ) spinwaves::internal::path_kagome(); + else if(uc::sw_crystal_structure == "mn2au" ) spinwaves::internal::path_mn2au(); + // else if(uc::sw_crystal_structure == "NdFeB" ) spinwaves::internal::path_NdFeB(); + else if(uc::sw_crystal_structure == "rocksalt" ) spinwaves::internal::path_rocksalt(); + // else if(uc::sw_crystal_structure == "spinel" ) spinwaves::internal::path_spinel(); + // else if(uc::sw_crystal_structure == "spinel-layered" ) spinwaves::internal::path_spinel_layered(); + // else if(uc::sw_crystal_structure == "SmFeN" ) spinwaves::internal::path_SmFeN(); + else{ + terminaltextcolor(RED); + std::cerr << "Error: Unknown spinwaves crystal_type "<< uc::sw_crystal_structure << " found during spinwave path initialisation. Exiting." << std::endl; + terminaltextcolor(WHITE); + zlog << zTs() << "Error: Unknown spinwaves crystal_type "<< uc::sw_crystal_structure << " found during spinwave path initialisation. Exiting." << std::endl; + err::vexit(); + } + } + else { + terminaltextcolor(RED); + std::cerr << "Error: Cannot find spinwaves path file or crystal structure. Exiting." << std::endl; + std::cerr << "filename for kpath has been specified as: \"" << spinwaves::internal::filename << "\"." << std::endl; + std::cerr << "Crystal structure has been defined as: \"" << uc::sw_crystal_structure << "\"." << std::endl; + terminaltextcolor(WHITE); + zlog << zTs() << "Error: Cannot find spinwaves path file \""<< spinwaves::internal::filename << "\"." << std::endl; + zlog << zTs() << "Error: Cannot find spinwaves path file or crystal structure. Exiting." << std::endl; + zlog << zTs() << "filename for kpath has been specified as: \"" << spinwaves::internal::filename << std::endl; + zlog << zTs() << "Crystal structure has been defined as: \"" << uc::sw_crystal_structure << "\"." << std::endl; + err::vexit(); + } + } + + + + void determine_kpoints_from_user_specific_k(const double system_dimensions_x, + const double system_dimensions_y, + const double system_dimensions_z, + const double unit_cell_size_x, + const double unit_cell_size_y, + const double unit_cell_size_z){ + + + // kfile + std::ofstream kfile; + kfile.open("kvectors.out"); + + + // number of kpoints + int len=spinwaves::internal::pathx.size(); + + // reciprocal lattice vectors + double b[3]; + b[0] = 2.0 * M_PI * (unit_cell_size_y * unit_cell_size_z) / (unit_cell_size_x * (unit_cell_size_y * unit_cell_size_z)); + b[1] = 2.0 * M_PI * (unit_cell_size_z * unit_cell_size_x) / (unit_cell_size_x * (unit_cell_size_y * unit_cell_size_z)); + b[2] = 2.0 * M_PI * (unit_cell_size_x * unit_cell_size_y) / (unit_cell_size_x * (unit_cell_size_y * unit_cell_size_z)); + + std::cout << "Determining k-points from user specific k-values..." << std::endl; + + // loop over number of kpoints + for (int k = 0; k < len; k++){ + + // convert from user defined values in units of 2pi/a to m^{-1} and push back to array + double kxtemp = b[0] * pathx[k]; + double kytemp = b[1] * pathy[k]; + double kztemp = b[2] * pathz[k]; + + spinwaves::internal::kx.push_back(kxtemp); + spinwaves::internal::ky.push_back(kytemp); + spinwaves::internal::kz.push_back(kztemp); + + // save to kfile + kfile << kxtemp << " " << kytemp << " " << kztemp << "\n"; + + } + + kfile.close(); + + } + + void determine_kpoints_from_user_high_sym_path(const double system_dimensions_x, + const double system_dimensions_y, + const double system_dimensions_z, + const double unit_cell_size_x, + const double unit_cell_size_y, + const double unit_cell_size_z){ + + // kfile + std::ofstream kfile; + kfile.open("kvectors.out"); + + int len=spinwaves::internal::pathx.size(); + + // check path file contains an even number of high symm points. + if ( len % 2 != 0){ + terminaltextcolor(RED); + std::cerr << "Error: k-path file contains an odd number of high symmetry points." << std::endl; + terminaltextcolor(WHITE); + zlog << zTs() << "Error: k-path file contains an odd number of high symmetry points." << std::endl; + err::vexit(); + } + + std::cout << unit_cell_size_x << " " << unit_cell_size_y << " " << unit_cell_size_z << std::endl; + + + // Get reciprocal lattice vectors from cubic unit cell JRH 26/10/23 + // Based on equations through link: http://lampx.tugraz.at/~hadley/ss1/crystaldiffraction/fourier/reciprocal_lattice.php + // The assumption is made that the unit cell is cubic + double b[3]; + b[0] = 2.0 * M_PI * (unit_cell_size_y * unit_cell_size_z) / (unit_cell_size_x * (unit_cell_size_y * unit_cell_size_z)); + b[1] = 2.0 * M_PI * (unit_cell_size_z * unit_cell_size_x) / (unit_cell_size_x * (unit_cell_size_y * unit_cell_size_z)); + b[2] = 2.0 * M_PI * (unit_cell_size_x * unit_cell_size_y) / (unit_cell_size_x * (unit_cell_size_y * unit_cell_size_z)); + + zlog << zTs() << "Reciprocal Lattice Vectors:" << std::endl; + zlog << zTs() << b[0] << " 0.0 0.0" << std::endl; + zlog << zTs() << "0.0 " << b[1] << " 0.0" << std::endl; + zlog << zTs() << "0.0 0.0 " << b[2] << std::endl; + + + // Generate k-points + std::cout << "Determining k-points from user specified high-symmetry path..." << std::endl; + zlog << zTs() << "Determining k-points from user specified high-symmetry path..." << std::endl; + + double kx, ky, kz; + + for (int row=0; row(round(distancex)), static_cast(round(distancey))); + int common_denom = spinwaves::internal::gcd(res1, static_cast(round(distancez))); + + std::cout << "Spinwaves module has found " << common_denom << " k-points between locations "; + std::cout << "[" << kx0 << ", " << ky0 << ", " << kz0 << "] and "; + std::cout << "[" << kx1 << ", " << ky1 << ", " << kz1 << "]" << std::endl; + zlog << zTs() << "Spinwaves module has found " << common_denom << " k-points between locations "; + zlog << "[" << kx0 << ", " << ky0 << ", " << kz0 << "] and "; + zlog << "[" << kx1 << ", " << ky1 << ", " << kz1 << "]" << std::endl; + + kx=kx0; + ky=ky0; + kz=kz0; + + // make sure you always finish at the points specified by user. + + // loop over points between two locations + for (int k = 0; k < common_denom; k++){ + + kfile << b[0] * kx << " " << b[1] * ky << " " << b[2] * kz << std::endl; + + // make sure to convert to units of 2pi/latconst + spinwaves::internal::kx.push_back(b[0] * kx); + spinwaves::internal::ky.push_back(b[1] * ky); + spinwaves::internal::kz.push_back(b[2] * kz); + + kx = kx + distancex/static_cast(common_denom)/cellx; + ky = ky + distancey/static_cast(common_denom)/celly; + kz = kz + distancez/static_cast(common_denom)/cellz; + + } + + } + + // push back final row + kfile << b[0] * kx << " " << b[1] * ky << " " << b[2] * kz << std::endl; + spinwaves::internal::kx.push_back(b[0] * kx); + spinwaves::internal::ky.push_back(b[1] * ky); + spinwaves::internal::kz.push_back(b[2] * kz); + } + } +} diff --git a/src/spinwaves/util.cpp b/src/spinwaves/util.cpp new file mode 100644 index 000000000..9f8d09657 --- /dev/null +++ b/src/spinwaves/util.cpp @@ -0,0 +1,218 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Sergiu Ruta 2022. All rights reserved. +// +// Email: j.r.hirst@shu.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include +#include +#include +#include +#include + +// Vampire headers +#include "spinwaves.hpp" +#include "vmpi.hpp" +#include "unitcell.hpp" +#include "errors.hpp" +#include "sim.hpp" +#include "vio.hpp" +#include "atoms.hpp" +#include "program.hpp" + +// sw module headers +#include "internal.hpp" + +namespace spinwaves { + + namespace internal { + + void save_frequencies(){ + + #ifdef MPICF + if (vmpi::my_rank==0) { + + // open files + std::ofstream freq_file; + freq_file.open("frequencies.dat"); + for (int i=0; i < internal::nt; i++){ + freq_file << i/(sim::partial_time*mp::dt/1.76e11)/internal::nt << "\n"; + } + freq_file.close(); + } + #else + std::ofstream freq_file; + freq_file.open("frequencies.dat"); + for (int i=0; i < internal::nt; i++){ + freq_file << i/(sim::partial_time*mp::dt/1.76e11)/internal::nt << "\n"; + } + freq_file.close(); + #endif + + } + + void check_numbering_of_spectrums(){ + std::sort(internal::super_index_values.begin(), internal::super_index_values.end()); + internal::super_index_values.erase(std::unique(internal::super_index_values.begin(), internal::super_index_values.end()), internal::super_index_values.end()); + + // + if (static_cast(internal::nspec) != internal::super_index_values.size()){ + terminaltextcolor(RED); + std::cerr << "The value of spinwaves:number-of-spectrums does not agree with the values of \"spinwaves[x]:\" found in input file" << std::endl; + std::cerr << internal::nspec << " " << internal::super_index_values.size() << std::endl; + err::vexit(); + } + + for (int i = 0; i < internal::nspec; i++){ + if (internal::super_index_values[i] != i){ + terminaltextcolor(RED); + std::cerr << "Difference identified between spinwaves::num-spectrums and ordering of spectrum specific parameters." << std::endl; + std::cerr << "Number of spectrums specified with spinwaves::num-spectrums: " << internal::nspec << std::endl; + std::cerr << "Additional/Missing parameters for spectrum \"spinwaves[" << internal::super_index_values[i] << "]:\" found in input file" << std::endl; + err::vexit(); + } + } + } + + void determine_spin_component(){ + + internal::sw_array.resize(internal::nspec); + // Set spin_array based on the component we want to calculate spinwave from + for (int spec = 0; spec < internal::nspec; spec++){ + if (internal::component[spec] == "sx") { + internal::sw_array[spec] = &atoms::x_spin_array; + } else if (internal::component[spec] == "sy") { + internal::sw_array[spec] = &atoms::y_spin_array; + } else if (internal::component[spec] == "sz") { + internal::sw_array[spec] = &atoms::z_spin_array; + } + } + } + + // Determine the mask for calculations of SW spectrum for specific materials + void calculate_material_mask(){ + + #ifdef MPICF + + // loop over number of materials in EACH SPECTRUM + for (unsigned int i = 0; i < mat.size(); i++){ + + + + // check mat value does not exceed number of materials + if (mat[i] >= mp::num_materials){ + terminaltextcolor(RED); + std::cerr << "Error: Numeric value of materials in spectrum " << internal::mat_in_spec[i] << " exceeds the total number of materials." << std::endl; + err::vexit(); + } + + for (int atom=0;atom= mp::num_materials){ + terminaltextcolor(RED); + std::cerr << "Error: Numeric value of materials in spectrum " << internal::mat_in_spec[i] << " exceeds the total number of materials." << std::endl; + err::vexit(); + } + + for (int atom=0;atom& rx, const std::vector& ry, const std::vector& rz){ + + double arg; + int nk=spinwaves::internal::kx.size(); + + #ifdef MPICF + + // calculate memory requirements + if (vmpi::my_rank == 0){ + double mem = 0.0; + mem = (2.0 * vmpi::num_processors * nk*(internal::atom_mask.size()) * sizeof(double)) / 1.0e6; + std::cout << "Spinwave module using " << mem << " MB of RAM for real-space fourier prefactor." << std::endl; + + } + // initialise array size for prefactor + spinwaves::internal::cos_k.resize(nk*(internal::atom_mask.size()),0.0); + spinwaves::internal::sin_k.resize(nk*(internal::atom_mask.size()),0.0); + + for (int k=0; k < nk; k++){ + + double kx = spinwaves::internal::kx[k]; + double ky = spinwaves::internal::ky[k]; + double kz = spinwaves::internal::kz[k]; + + for(unsigned int j=0;j +#include +#include +#include + +// Vampire headers +#include "anisotropy.hpp" +#include "constants.hpp" +#include "dipole.hpp" +#include "errors.hpp" +#include "exchange.hpp" +#include "sim.hpp" +#include "stats.hpp" +#include "vmpi.hpp" +#include "vio.hpp" +#include "sld.hpp" +#include "atoms.hpp" + +namespace stats{ + +//------------------------------------------------------------------------------------------------------ +// Generalised class to calculate energy statistics for a subset of atoms defined by mask +// +// The class sets up data structures to hold statistic variables in order to calculate +// instantaneous and mean values of the energy. Specific values also exist for the exchange, +// anisotropy, applied and magnetostatic energies. +//------------------------------------------------------------------------------------------------------ + +//------------------------------------------------------------------------------------------------------ +// Constructor to initialize data structures +//------------------------------------------------------------------------------------------------------ +//energy_statistic_t::energy_statistic_t (): initialized(false){} + +//------------------------------------------------------------------------------------------------------ +// Function to determine if class is properly initialized +//------------------------------------------------------------------------------------------------------ +bool sld_energy_statistic_t::is_initialized(){ + return initialized; +} + +//------------------------------------------------------------------------------------------------------ +// Function to initialize mask +//------------------------------------------------------------------------------------------------------ +void sld_energy_statistic_t::set_mask(const int in_mask_size, const std::vector in_mask){ + + // Check that mask values never exceed mask_size + for(unsigned int atom=0; atom in_mask_size-1){ + terminaltextcolor(RED); + std::cerr << "Programmer Error - mask id " << in_mask[atom] << " is greater than number of elements for mask "<< in_mask_size << std::endl; + terminaltextcolor(WHITE); + zlog << zTs() << "Programmer Error - mask id " << in_mask[atom] << " is greater than number of elements for mask "<< in_mask_size << std::endl; + err::vexit(); + } + } + + // save mask to internal storage + num_atoms = in_mask.size(); + mask_size = in_mask_size - 1; // last element contains energy for non-magnetic atoms + mean_counter = 0.0; + mask = in_mask; // copy contents of vector + + // resize arrays to correct mask size (one value per mask) and set to zero + sld_total_energy.resize(in_mask_size, 0.0); + sld_exchange_energy.resize(in_mask_size, 0.0); + sld_coupling_energy.resize(in_mask_size, 0.0); + kinetic_energy.resize(in_mask_size, 0.0); + potential_energy.resize(in_mask_size, 0.0); + + mean_sld_total_energy.resize(in_mask_size, 0.0); + mean_sld_exchange_energy.resize(in_mask_size, 0.0); + mean_sld_coupling_energy.resize(in_mask_size, 0.0); + mean_kinetic_energy.resize(in_mask_size, 0.0); + mean_potential_energy.resize(in_mask_size, 0.0); + + normalisation.resize(in_mask_size, 0.0); + num_atoms_in_mask.resize(in_mask_size,0); + + + // calculate number of spins in each mask + for(int atom=0; atom& out_mask, std::vector& out_normalisation){ + + // copy data to objects + out_mask = mask; + out_normalisation = normalisation; + + return; + +} + +//------------------------------------------------------------------------------------------------------ +// Function to calculate spin energy given a mask and place result in energy array +//------------------------------------------------------------------------------------------------------ +void sld_energy_statistic_t::calculate(const std::vector& sx, // spin unit vector + const std::vector& sy, + const std::vector& sz, + const std::vector& mm, // magnetic moment (Tesla) + const std::vector& mat, // material id + const double temperature){ + + // initialise energies to zero + std::fill( sld_total_energy.begin(), sld_total_energy.end(), 0.0 ); + std::fill( sld_exchange_energy.begin(), sld_exchange_energy.end(), 0.0 ); + std::fill( sld_coupling_energy.begin(), sld_coupling_energy.end(), 0.0 ); + std::fill( kinetic_energy.begin(), kinetic_energy.end(), 0.0 ); + std::fill( potential_energy.begin(), potential_energy.end(), 0.0 ); + + //--------------------------------------------------------------------------- + // Calculate exchange energy (in Tesla) + //--------------------------------------------------------------------------- + + // loop over all atoms in mask + for( int atom = 0; atom < num_atoms; ++atom ){ + const int mask_id = mask[atom]; // get mask id + sld_exchange_energy[mask_id] += sld::compute_exchange_energy(atom,atom+1); + } + + + + //--------------------------------------------------------------------------- + // Calculate anisotropy energy (in Tesla) + //--------------------------------------------------------------------------- + for( int atom = 0; atom < num_atoms; ++atom ){ + const int mask_id = mask[atom]; // get mask id + sld_coupling_energy[mask_id] += sld::compute_coupling_energy(atom,atom+1); + } + + //--------------------------------------------------------------------------- + // Calculate kinetic energy (in Tesla) + //--------------------------------------------------------------------------- + for( int atom = 0; atom < num_atoms; ++atom ){ + const int mask_id = mask[atom]; // get mask id + kinetic_energy[mask_id] += sld::compute_kinetic_energy(atom,atom+1,atoms::type_array,atoms::x_velo_array,atoms::y_velo_array, atoms::z_velo_array); + } + + //--------------------------------------------------------------------------- + // Calculate potential field energy (in Tesla) + //--------------------------------------------------------------------------- + for( int atom = 0; atom < num_atoms; ++atom ){ + const int mask_id = mask[atom]; // get mask id + potential_energy[mask_id] += sld::compute_potential_energy(atom,atom+1,atoms::type_array); + } + + + //--------------------------------------------------------------------------- + // Calculate total energy (in Tesla) + //--------------------------------------------------------------------------- + for( int mask_id = 0; mask_id < mask_size; ++mask_id ){ + sld_total_energy[mask_id] = sld_exchange_energy[mask_id] + + sld_coupling_energy[mask_id] + + kinetic_energy[mask_id] + + potential_energy[mask_id]; + } + + // int total_atoms=atoms::num_atoms; + + //--------------------------------------------------------------------------- + // Reduce on all CPUS + //--------------------------------------------------------------------------- + #ifdef MPICF + MPI_Allreduce(MPI_IN_PLACE, &sld_exchange_energy[0], mask_size, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &sld_coupling_energy[0], mask_size, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &kinetic_energy[0], mask_size, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &potential_energy[0], mask_size, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + MPI_Allreduce(MPI_IN_PLACE, &sld_total_energy[0], mask_size, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + //MPI_Allreduce(MPI_IN_PLACE, &total_atoms, 1, MPI_INT, MPI_SUM, MPI_COMM_WORLD); + + #endif + + // divide by number of atoms + for(int mask_id=0; mask_id < mask_size; ++mask_id){ + + // determine inverse number of atoms in mask + //std::cout<& sld_energy_statistic_t::get_sld_total_energy(){ + + return sld_total_energy; + +} + +//------------------------------------------------------------------------------------------------------ +// Function to get const reference for total energy data (in Tesla) +//------------------------------------------------------------------------------------------------------ +const std::vector& sld_energy_statistic_t::get_sld_exchange_energy(){ + + return sld_exchange_energy; + +} + +//------------------------------------------------------------------------------------------------------ +// Function to get const reference for total energy data +//------------------------------------------------------------------------------------------------------ +const std::vector& sld_energy_statistic_t::get_sld_coupling_energy(){ + + return sld_coupling_energy; + +} + +//------------------------------------------------------------------------------------------------------ +// Function to get const reference for total energy data +//------------------------------------------------------------------------------------------------------ +const std::vector& sld_energy_statistic_t::get_potential_energy(){ + + return potential_energy; + +} + +//------------------------------------------------------------------------------------------------------ +// Function to get const reference for total energy data +//------------------------------------------------------------------------------------------------------ +const std::vector& sld_energy_statistic_t::get_kinetic_energy(){ + + return kinetic_energy; + +} + +//------------------------------------------------------------------------------------------------------ +// Function to set total energy data +//------------------------------------------------------------------------------------------------------ +void sld_energy_statistic_t::set_sld_total_energy(std::vector& new_energy, std::vector& new_mean_energy){ + + // copy energy vector + sld_total_energy = new_energy; + + const int array_size = mean_sld_total_energy.size(); + for( int i=0; i < array_size; ++i ){ + mean_sld_total_energy[i] += new_mean_energy[i]; + } + +} + +//------------------------------------------------------------------------------------------------------ +// Function to set exchange energy data +//------------------------------------------------------------------------------------------------------ +void sld_energy_statistic_t::set_sld_exchange_energy(std::vector& new_energy, std::vector& new_mean_energy){ + + // copy energy vector + sld_exchange_energy = new_energy; + + const int array_size = mean_sld_exchange_energy.size(); + for( int i=0; i < array_size; ++i ){ + mean_sld_exchange_energy[i] += new_mean_energy[i]; + } + +} + +//------------------------------------------------------------------------------------------------------ +// Function to set anisotropy energy data +//------------------------------------------------------------------------------------------------------ +void sld_energy_statistic_t::set_sld_coupling_energy(std::vector& new_energy, std::vector& new_mean_energy){ + + // copy energy vector + sld_coupling_energy = new_energy; + + const int array_size = mean_sld_coupling_energy.size(); + for( int i=0; i < array_size; ++i ){ + mean_sld_coupling_energy[i] += new_mean_energy[i]; + } + +} + +//------------------------------------------------------------------------------------------------------ +// Function to set applied field energy data +//------------------------------------------------------------------------------------------------------ +void sld_energy_statistic_t::set_potential_energy(std::vector& new_energy, std::vector& new_mean_energy){ + + // copy energy vector + potential_energy = new_energy; + + const int array_size = mean_potential_energy.size(); + for( int i=0; i < array_size; ++i ){ + mean_potential_energy[i] += new_mean_energy[i]; + } + +} + +//------------------------------------------------------------------------------------------------------ +// Function to set magnetostatic energy data +//------------------------------------------------------------------------------------------------------ +void sld_energy_statistic_t::set_kinetic_energy(std::vector& new_energy, std::vector& new_mean_energy){ + + // copy energy vector + kinetic_energy = new_energy; + + const int array_size = mean_kinetic_energy.size(); + for( int i=0; i < array_size; ++i ){ + mean_kinetic_energy[i] += new_mean_energy[i]; + } + +} + +//------------------------------------------------------------------------------------------------------ +// Function to update mean counter +//------------------------------------------------------------------------------------------------------ +void sld_energy_statistic_t::update_mean_counter(long counter){ + + // update counter + mean_counter += double(counter); + + return; + +} + +//------------------------------------------------------------------------------------------------------ +// Function to reset energy averages +//------------------------------------------------------------------------------------------------------ +void sld_energy_statistic_t::reset_averages(){ + + // reinitialise mean energies to zero + std::fill(mean_sld_total_energy.begin(), mean_sld_total_energy.end(), 0.0); + std::fill(mean_sld_exchange_energy.begin(), mean_sld_exchange_energy.end(), 0.0); + std::fill(mean_sld_coupling_energy.begin(), mean_sld_coupling_energy.end(), 0.0); + std::fill(mean_kinetic_energy.begin(), mean_kinetic_energy.end(), 0.0); + std::fill(mean_potential_energy.begin(), mean_potential_energy.end(), 0.0); + + // reset data counter + mean_counter = 0.0; + + return; + +} + +//------------------------------------------------------------------------------------------------------ +// Function to output normalised magnetisation values as string +//------------------------------------------------------------------------------------------------------ +std::string sld_energy_statistic_t::output_sld_energy(enum sld_energy_t sld_energy_type,bool header){ + + // result string stream + std::ostringstream res; + vout::fixed_width_output result(res,vout::fw_size); + // set custom precision if enabled + if(vout::custom_precision){ + res.precision(vout::precision); + //if(vout::fixed) res.setf( std::ios::fixed, std::ios::floatfield ); + if(vout::fixed) res.setf(std::ios::scientific); + } + + // could specify an output conversion unit here + // could run all of the for loops as one outside the switch. -AJN + if(header){ + for(int mask_id=0;mask_idmax_height) max_height=mask[atom]; + } + // Reduce maximum height on all CPUS + #ifdef MPICF + MPI_Allreduce(MPI_IN_PLACE, &max_height, 1, MPI_INT, MPI_MAX, MPI_COMM_WORLD); + #endif + + // reassign all non-magnetic atoms to last mask + for(int atom=0; atom < stats::num_atoms; ++atom){ + if(non_magnetic_materials_array[material_type_array[atom]]) mask[atom] = max_height+1; + } + + stats::height_spin_length.set_mask(max_height+2,mask); + } + //------------------------------------------------------------------------ // binder cumulant //------------------------------------------------------------------------ diff --git a/src/statistics/lattice_temperature.cpp b/src/statistics/lattice_temperature.cpp new file mode 100644 index 000000000..1b49aabbb --- /dev/null +++ b/src/statistics/lattice_temperature.cpp @@ -0,0 +1,275 @@ +//----------------------------------------------------------------------------- +// +// This source file is part of the VAMPIRE open source package under the +// GNU GPL (version 2) licence (see licence file for details). +// +// (c) Mara Strungaru 2022. All rights reserved. +// +//----------------------------------------------------------------------------- + +// C++ standard library headers +#include +#include +#include +#include +#include + +// Vampire headers +#include "constants.hpp" +#include "errors.hpp" +#include "sim.hpp" +#include "stats.hpp" +#include "vmpi.hpp" +#include "vio.hpp" +#include "sld.hpp" +#include "atoms.hpp" + + +namespace stats{ + + +//------------------------------------------------------------------------------------------------------ +// Function to determine if class is properly initialized +//------------------------------------------------------------------------------------------------------ +bool lattice_temp_statistic_t::is_initialized(){ + return initialized; +} + +//------------------------------------------------------------------------------------------------------ +// Function to initialize mask +//------------------------------------------------------------------------------------------------------ +void lattice_temp_statistic_t::set_mask(const int in_mask_size, std::vector in_mask, const std::vector& mm){ + + // Check that mask values never exceed mask_size + for(unsigned int atom=0; atom in_mask_size-1){ + terminaltextcolor(RED); + std::cerr << "Programmer Error - mask id " << in_mask[atom] << " is greater than number of elements for mask "<< in_mask_size << std::endl; + terminaltextcolor(WHITE); + zlog << zTs() << "Programmer Error - mask id " << in_mask[atom] << " is greater than number of elements for mask "<< in_mask_size << std::endl; + err::vexit(); + } + } + + // save mask to internal storage + num_atoms = in_mask.size(); + mask_size = in_mask_size - 1; // last element contains energy for non-magnetic atoms + mean_counter = 0.0; + mask=in_mask; // copy contents of vector + lattice_temp.resize(in_mask_size, 0.0); + mean_lattice_temp.resize(in_mask_size, 0.0); + normalisation.resize(in_mask_size, 0.0); + + + + + // calculate number of spins in each mask + for(int atom=0; atom& out_mask, std::vector& out_normalisation){ + + // copy data to objects + out_mask = mask; + out_normalisation = normalisation; + + + return; + +} + + +void lattice_temp_statistic_t::calculate_lattice_temp(const std::vector& velo_array_x, // coord vectors for atoms + const std::vector& velo_array_y, + const std::vector& velo_array_z){ + + + + std::fill(lattice_temp.begin(),lattice_temp.end(),0.0); + // calculate contributions of spins to each magetization category + for(int atom=0; atom < num_atoms; ++atom){ + + const unsigned int mask_id = mask[atom]; // get mask id + + if(mask_id>=lattice_temp.size()) std::cerr<<"ERROR"<& lattice_temp_statistic_t::get_lattice_temp(){ + + return lattice_temp; + +} + +//------------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------------ +void lattice_temp_statistic_t::set_lattice_temp(std::vector& new_lattice_temp, std::vector& new_mean_lattice_temp, long counter){ + + lattice_temp = new_lattice_temp; + + const size_t array_size = mean_lattice_temp.size(); + for(size_t i=0; i< array_size; ++i){ + mean_lattice_temp[i] += new_mean_lattice_temp[i]; + } + + // update counter + mean_counter += double(counter); + +} + +//------------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------------ +void lattice_temp_statistic_t::reset_lattice_temp_averages(){ + + std::fill(mean_lattice_temp.begin(),mean_lattice_temp.end(),0.0); + + // reset data counter + mean_counter = 0.0; + + return; + +} + +//------------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------------ +std::string lattice_temp_statistic_t::output_lattice_temp(bool header){ + + // result string stream + std::ostringstream res; + + // set custom precision if enabled + if(vout::custom_precision){ + res.precision(vout::precision); + if(vout::fixed) res.setf( std::ios::fixed, std::ios::floatfield ); + } + vout::fixed_width_output result(res,vout::fw_size); + + for(int mask_id=0; mask_id& magnetization_statistic_t::get_magnetization(){ } //------------------------------------------------------------------------------------------------------ -// Function to get magnetisation data +// Function to write mean magnetisation data to a checkpoint file +//------------------------------------------------------------------------------------------------------ +void magnetization_statistic_t::save_checkpoint(std::ofstream& chkfile){ + + const uint64_t num_elements = mean_magnetization.size(); + + chkfile.write(reinterpret_cast(&num_elements),sizeof(uint64_t)); + chkfile.write(reinterpret_cast(&mean_counter),sizeof(double)); + chkfile.write(reinterpret_cast(&mean_magnetization[0]),sizeof(double)*mean_magnetization.size()); + return; + +} + +//------------------------------------------------------------------------------------------------------ +// Function to write mean magnetisation data to a checkpoint file +//------------------------------------------------------------------------------------------------------ +void magnetization_statistic_t::load_checkpoint(std::ifstream& chkfile, bool chk_continue){ + + // load number of elements to see how much data to read + uint64_t num_elements = 0; + chkfile.read((char*)&num_elements,sizeof(uint64_t)); + + // set up data storage for reading + double read_mean_counter = 0.0; + std::vector read_mean_magnetization(num_elements, 0.0); + + // read data elements + chkfile.read((char*)&read_mean_counter,sizeof(double)); + chkfile.read((char*)&read_mean_magnetization[0],sizeof(double)*num_elements); + + // check that simulation is a continuation (in the case of not continuing do nothing) + if(chk_continue){ + + // check that the number of elements (materials, heights, etc) is the same + if(num_elements == mean_magnetization.size()){ + + // load mean counter and magnetization into class variables + mean_counter = read_mean_counter; + mean_magnetization = read_mean_magnetization; + + } + // if not, don't load them (allowing changing of stats after checkpoint) + // but print out warning message to user + else{ + zlog << zTs() << "Warning - checkpoint loaded for previously unused statistic " << expand_str(name) << std::endl; + } + } + + return; + +} + +//------------------------------------------------------------------------------------------------------ +// Function to set magnetisation data //------------------------------------------------------------------------------------------------------ void magnetization_statistic_t::set_magnetization(std::vector& new_magnetization, std::vector& new_mean_magnetization, long counter){ diff --git a/src/statistics/makefile b/src/statistics/makefile index d58cd7037..0e8a5e252 100644 --- a/src/statistics/makefile +++ b/src/statistics/makefile @@ -13,9 +13,13 @@ specific_heat.o \ standard_deviation.o\ reset.o \ susceptibility.o \ -binder_cumulant.o\ +binder_cumulant.o \ torque.o \ -update.o +update.o \ +energy_sld.o \ +spin_temperature.o \ +lattice_temperature.o \ +spin_length.o # Append module objects to global tree OBJECTS+=$(addprefix obj/statistics/,$(statistics_objects)) diff --git a/src/statistics/reset.cpp b/src/statistics/reset.cpp index ce9716802..a5d39142d 100644 --- a/src/statistics/reset.cpp +++ b/src/statistics/reset.cpp @@ -33,6 +33,11 @@ namespace stats{ if(stats::calculate_grain_energy) stats::grain_energy.reset_averages(); if(stats::calculate_material_energy) stats::material_energy.reset_averages(); + // reset sld energy statistics + if(stats::calculate_system_sld_energy) stats::system_sld_energy.reset_averages(); + if(stats::calculate_grain_sld_energy) stats::grain_sld_energy.reset_averages(); + if(stats::calculate_material_sld_energy) stats::material_sld_energy.reset_averages(); + // reset magnetization statistics if(stats::calculate_system_magnetization) stats::system_magnetization.reset_magnetization_averages(); if(stats::calculate_grain_magnetization) stats::grain_magnetization.reset_magnetization_averages(); @@ -47,8 +52,18 @@ namespace stats{ if(stats::calculate_grain_torque) stats::grain_torque.reset_torque_averages(); if(stats::calculate_material_torque) stats::material_torque.reset_torque_averages(); + // update spin temp statistics + if(stats::calculate_system_spin_temp) stats::system_spin_temp.reset_spin_temp_averages(); + if(stats::calculate_grain_spin_temp) stats::grain_spin_temp.reset_spin_temp_averages(); + if(stats::calculate_material_spin_temp) stats::material_spin_temp.reset_spin_temp_averages(); + + // update spin temp statistics + if(stats::calculate_system_lattice_temp) stats::system_lattice_temp.reset_lattice_temp_averages(); + if(stats::calculate_grain_lattice_temp) stats::grain_lattice_temp.reset_lattice_temp_averages(); + if(stats::calculate_material_lattice_temp) stats::material_lattice_temp.reset_lattice_temp_averages(); + // standard deviation in time-step - if(stats::calculate_material_standard_deviation) stats::material_standard_deviation.reset_averages(); + if(stats::calculate_material_standard_deviation) stats::material_standard_deviation.reset_averages(); // reset specific_heat statistics if(stats::calculate_system_specific_heat) stats::system_specific_heat.reset_averages(); @@ -60,6 +75,11 @@ namespace stats{ if(stats::calculate_grain_susceptibility) stats::grain_susceptibility.reset_averages(); if(stats::calculate_material_susceptibility) stats::material_susceptibility.reset_averages(); + // reset spin length statistics + if(stats::calculate_system_spin_length) stats::system_spin_length.reset_averages(); + if(stats::calculate_material_spin_length) stats::material_spin_length.reset_averages(); + if(stats::calculate_height_spin_length) stats::height_spin_length.reset_averages(); + // reset binder cumulant statistics if(stats::calculate_system_binder_cumulant) stats::system_binder_cumulant.reset_averages(); if(stats::calculate_material_binder_cumulant) stats::material_binder_cumulant.reset_averages(); diff --git a/src/statistics/specific_heat.cpp b/src/statistics/specific_heat.cpp index 2b6598948..f644129cb 100644 --- a/src/statistics/specific_heat.cpp +++ b/src/statistics/specific_heat.cpp @@ -23,6 +23,21 @@ #include "vmpi.hpp" #include "vio.hpp" +// file scope function in annonymous namespace to expand statistic type string +namespace { + +inline std::string expand_str(std::string str){ + + if(str == "s") return "system_specific_heat"; + if(str == "g") return "grain_specific_heat"; + if(str == "m") return "material_specific_heat"; + + return ""; + +} + +} + namespace stats{ //------------------------------------------------------------------------------------------------------ @@ -90,6 +105,64 @@ void specific_heat_statistic_t::calculate(const std::vector& energy){ } +//------------------------------------------------------------------------------------------------------ +// Function to write mean specific heat data to a checkpoint file +//------------------------------------------------------------------------------------------------------ +void specific_heat_statistic_t::save_checkpoint(std::ofstream& chkfile){ + + const uint64_t num_elements = mean_specific_heat.size(); + + chkfile.write(reinterpret_cast(&num_elements),sizeof(uint64_t)); + chkfile.write(reinterpret_cast(&mean_counter),sizeof(double)); + chkfile.write(reinterpret_cast(&mean_specific_heat[0]),sizeof(double)*mean_specific_heat.size()); + chkfile.write(reinterpret_cast(&mean_specific_heat_squared[0]),sizeof(double)*mean_specific_heat_squared.size()); + + return; + +} + +//------------------------------------------------------------------------------------------------------ +// Function to write mean specific heat data to a checkpoint file +//------------------------------------------------------------------------------------------------------ +void specific_heat_statistic_t::load_checkpoint(std::ifstream& chkfile, bool chk_continue){ + + // load number of elements to see how much data to read + uint64_t num_elements = 0; + chkfile.read((char*)&num_elements,sizeof(uint64_t)); + + // set up data storage for reading + double read_mean_counter = 0.0; + std::vector read_mean_specific_heat(num_elements, 0.0); + std::vector read_mean_specific_heat_squared(num_elements, 0.0); + + // read data elements + chkfile.read((char*)&read_mean_counter,sizeof(double)); + chkfile.read((char*)&read_mean_specific_heat[0],sizeof(double)*num_elements); + chkfile.read((char*)&read_mean_specific_heat_squared[0],sizeof(double)*num_elements); + + // check that simulation is a continuation (in the case of not continuing do nothing) + if(chk_continue){ + + // check that the number of elements (materials, heights, etc) is the same + if(num_elements == mean_specific_heat.size()){ + + // load mean counter and data into class variables + mean_counter = read_mean_counter; + mean_specific_heat = read_mean_specific_heat; + mean_specific_heat_squared = read_mean_specific_heat_squared; + + } + // if not, don't load them (allowing changing of stats after checkpoint) + // but print out warning message to user + else{ + zlog << zTs() << "Warning - checkpoint loaded for previously unused statistic " << expand_str(name) << std::endl; + } + } + + return; + +} + //------------------------------------------------------------------------------------------------------ // Function to reset statistical averages //------------------------------------------------------------------------------------------------------ diff --git a/src/statistics/spin_length.cpp b/src/statistics/spin_length.cpp new file mode 100644 index 000000000..da4265f43 --- /dev/null +++ b/src/statistics/spin_length.cpp @@ -0,0 +1,196 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) David R Papp 2024. All rights reserved. +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include +#include +#include +#include + +// Vampire headers +#include "errors.hpp" +#include "stats.hpp" +#include "vmpi.hpp" +#include "vio.hpp" +#include "constants.hpp" +#include "atoms.hpp" +#include "errors.hpp" +#include "sim.hpp" + +namespace stats{ + +//------------------------------------------------------------------------------------------------------ +// Function to determine if class is properly initialized +//------------------------------------------------------------------------------------------------------ +bool spin_length_statistic_t::is_initialized(){ + return initialized; +} + +//------------------------------------------------------------------------------------------------------ +// Function to initialize mask +//------------------------------------------------------------------------------------------------------ +void spin_length_statistic_t::set_mask(const int in_mask_size, std::vector in_mask){ + + // Check that mask values never exceed mask_size + for(unsigned int atom=0; atom in_mask_size-1){ + terminaltextcolor(RED); + std::cerr << "Programmer Error - mask id " << in_mask[atom] << " is greater than number of elements for mask "<< in_mask_size << std::endl; + terminaltextcolor(WHITE); + zlog << zTs() << "Programmer Error - mask id " << in_mask[atom] << " is greater than number of elements for mask "<< in_mask_size << std::endl; + err::vexit(); + } + } + + // save mask to internal storage + num_atoms = in_mask.size(); + mask_size = in_mask_size - 1; // last element contains magnetization for non-magnetic atoms + mean_counter = 0.0; + mask=in_mask; // copy contents of vector + spin_length.resize(in_mask_size, 0.0); + mean_spin_length.resize(in_mask_size, 0.0); + normalisation.resize(in_mask_size, 0.0); + + // calculate number of spins in each mask + for(int atom=0; atom num_atoms_in_mask(in_mask_size,0); + for(unsigned int atom=0; atom& out_mask){ + + // copy data to objects + out_mask = mask; + + return; + +} + +//------------------------------------------------------------------------------------------------------ +// Function to calculate spin length of the system and retain the mean value +//------------------------------------------------------------------------------------------------------- +void spin_length_statistic_t::calculate_spin_length(const std::vector& sx, // full spin vector + const std::vector& sy, + const std::vector& sz){ + + // initialise spin length to zero [.end() seems to be optimised away by the compiler...] + std::fill(spin_length.begin(),spin_length.end(),0.0); + + // calculate contributions of spins to spin length + for(int atom=0; atom +#include +#include +#include +#include + +// Vampire headers +#include "atoms.hpp" +#include "constants.hpp" +#include "errors.hpp" +#include "sld.hpp" +#include "sim.hpp" +#include "stats.hpp" +#include "vmpi.hpp" +#include "vio.hpp" + +namespace stats{ + +//------------------------------------------------------------------------------------------------------ +// Function to determine if class is properly initialized +//------------------------------------------------------------------------------------------------------ +bool spin_temp_statistic_t::is_initialized(){ + return initialized; +} + +//------------------------------------------------------------------------------------------------------ +// Function to initialize mask +//------------------------------------------------------------------------------------------------------ +void spin_temp_statistic_t::set_mask(const int in_mask_size, std::vector in_mask, const std::vector& mm){ + + // Check that mask values never exceed mask_size + for(unsigned int atom=0; atom in_mask_size-1){ + terminaltextcolor(RED); + std::cerr << "Programmer Error - mask id " << in_mask[atom] << " is greater than number of elements for mask "<< in_mask_size << std::endl; + terminaltextcolor(WHITE); + zlog << zTs() << "Programmer Error - mask id " << in_mask[atom] << " is greater than number of elements for mask "<< in_mask_size << std::endl; + err::vexit(); + } + } + + // save mask to internal storage + num_atoms = in_mask.size(); + mask_size = in_mask_size - 1; + mean_counter = 0.0; + mask=in_mask; // copy contents of vector + spin_temp.resize(in_mask_size, 0.0); + mean_spin_temp.resize(in_mask_size, 0.0); + SxH2.resize(in_mask_size,0.0); + SH.resize(in_mask_size,0.0); + + // determine mask id's with no atoms + num_atoms_in_mask.resize(in_mask_size,0); + for(unsigned int atom=0; atom& out_mask, std::vector& out_normalisation){ + + // copy data to objects + out_mask = mask; + + return; + +} + +//------------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------------ +void spin_temp_statistic_t::calculate_spin_temp(const std::vector& sx, // spin unit vector + const std::vector& sy, + const std::vector& sz, + const std::vector& bxs, // spin fields (tesla) + const std::vector& bys, + const std::vector& bzs, + const std::vector& bxe, // external fields (tesla) + const std::vector& bye, + const std::vector& bze, + const std::vector& mm){ + + //std::fill(spin_temp.begin(),spin_temp.end(),0.0); + //const int64_t num_atoms = sx.size(); + //double SxH2=0.0; + //double SH=0.0; + + // ASD version + //sim::calculate_spin_fields(0, num_atoms); + + // SLD version + std::fill(SxH2.begin(),SxH2.end(),0.0); + std::fill(SH.begin(),SH.end(),0.0); + + std::fill(atoms::x_total_spin_field_array.begin(), atoms::x_total_spin_field_array.end(), 0.0); + std::fill(atoms::y_total_spin_field_array.begin(), atoms::y_total_spin_field_array.end(), 0.0); + std::fill(atoms::z_total_spin_field_array.begin(), atoms::z_total_spin_field_array.end(), 0.0); + + sld::compute_fields(0, // first atom for exchange interactions to be calculated + num_atoms, // last +1 atom to be calculated + atoms::neighbour_list_start_index, + atoms::neighbour_list_end_index, + atoms::type_array, // type for atom + atoms::neighbour_list_array, // list of interactions between atoms + atoms::x_coord_array, + atoms::y_coord_array, + atoms::z_coord_array, + atoms::x_spin_array, + atoms::y_spin_array, + atoms::z_spin_array, + atoms::x_total_spin_forces_array, + atoms::y_total_spin_forces_array, + atoms::z_total_spin_forces_array, + atoms::x_total_spin_field_array, + atoms::y_total_spin_field_array, + atoms::z_total_spin_field_array); + + // calculate contributions of spins to each magetization category + for(int atom=0; atom < num_atoms; ++atom){ + + const int mask_id = mask[atom]; // get mask id + + // get atomic moment + const double mu = mm[atom]; + + // Store local spin in Sand local field in H + const double S[3] = {sx[atom], sy[atom], sz[atom] }; + const double B[3] = {bxs[atom], bys[atom], bzs[atom]}; + + double SxHx = S[1]*B[2]-S[2]*B[1]; + double SxHy = S[2]*B[0]-S[0]*B[2]; + double SxHz = S[0]*B[1]-S[1]*B[0]; + SxH2[mask_id] = SxH2[mask_id]+ mu*(SxHx*SxHx + SxHy*SxHy + SxHz*SxHz); + SH[mask_id] = SH[mask_id] + S[0]*B[0] + S[1]*B[1] + S[2]*B[2]; + spin_temp[mask_id]= SxH2[mask_id] / SH[mask_id]; + + } + + // Reduce on all CPUS + #ifdef MPICF + MPI_Allreduce(MPI_IN_PLACE, &spin_temp[0], mask_size, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); + #endif + + // Zero empty mask id's + for(unsigned int id=0; id& spin_temp_statistic_t::get_spin_temp(){ + + return spin_temp; + +} + +//------------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------------ +void spin_temp_statistic_t::set_spin_temp(std::vector& new_spin_temp, std::vector& new_mean_spin_temp, long counter){ + + spin_temp = new_spin_temp; + + const size_t array_size = mean_spin_temp.size(); + for(size_t i=0; i< array_size; ++i){ + mean_spin_temp[i] += new_mean_spin_temp[i]; + } + + // update counter + mean_counter += double(counter); + +} + +//------------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------------ +void spin_temp_statistic_t::reset_spin_temp_averages(){ + + std::fill(mean_spin_temp.begin(),mean_spin_temp.end(),0.0); + + // reset data counter + mean_counter = 0.0; + + return; + +} + +//------------------------------------------------------------------------------------------------------ +//------------------------------------------------------------------------------------------------------ +std::string spin_temp_statistic_t::output_spin_temp(bool header){ + + // result string stream + std::ostringstream res; + + // set custom precision if enabled + if(vout::custom_precision){ + res.precision(vout::precision); + if(vout::fixed) res.setf( std::ios::fixed, std::ios::floatfield ); + } + vout::fixed_width_output result(res,vout::fw_size); + + for(int mask_id=0; mask_id& magnetizat } +//------------------------------------------------------------------------------------------------------ +// Function to write mean susceptibility data to a checkpoint file +//------------------------------------------------------------------------------------------------------ +void susceptibility_statistic_t::save_checkpoint(std::ofstream& chkfile){ + + const uint64_t num_elements = mean_susceptibility.size(); + + chkfile.write(reinterpret_cast(&num_elements),sizeof(uint64_t)); + chkfile.write(reinterpret_cast(&mean_counter),sizeof(double)); + chkfile.write(reinterpret_cast(&mean_susceptibility[0]),sizeof(double)*mean_susceptibility.size()); + chkfile.write(reinterpret_cast(&mean_susceptibility_squared[0]),sizeof(double)*mean_susceptibility_squared.size()); + + return; + +} + +//------------------------------------------------------------------------------------------------------ +// Function to write mean susceptibility data to a checkpoint file +//------------------------------------------------------------------------------------------------------ +void susceptibility_statistic_t::load_checkpoint(std::ifstream& chkfile, bool chk_continue){ + + // load number of elements to see how much data to read + uint64_t num_elements = 0; + chkfile.read((char*)&num_elements,sizeof(uint64_t)); + + // set up data storage for reading + double read_mean_counter = 0.0; + std::vector read_mean_susceptibility(num_elements, 0.0); + std::vector read_mean_susceptibility_squared(num_elements, 0.0); + + // read data elements + chkfile.read((char*)&read_mean_counter,sizeof(double)); + chkfile.read((char*)&read_mean_susceptibility[0],sizeof(double)*num_elements); + chkfile.read((char*)&read_mean_susceptibility_squared[0],sizeof(double)*num_elements); + + // check that simulation is a continuation (in the case of not continuing do nothing) + if(chk_continue){ + + // check that the number of elements (materials, heights, etc) is the same + if(num_elements == mean_susceptibility.size()){ + + // load mean counter and data into class variables + mean_counter = read_mean_counter; + mean_susceptibility = read_mean_susceptibility; + mean_susceptibility_squared = read_mean_susceptibility_squared; + + } + // if not, don't load them (allowing changing of stats after checkpoint) + // but print out warning message to user + else{ + zlog << zTs() << "Warning - checkpoint loaded for previously unused statistic " << expand_str(name) << std::endl; + } + } + + return; + +} + //------------------------------------------------------------------------------------------------------ // Function to reset statistical averages //------------------------------------------------------------------------------------------------------ diff --git a/src/statistics/torque.cpp b/src/statistics/torque.cpp index 89452abdc..4bb2a8595 100644 --- a/src/statistics/torque.cpp +++ b/src/statistics/torque.cpp @@ -149,18 +149,6 @@ void torque_statistic_t::calculate_torque(const std::vector& sx, // spin MPI_Allreduce(MPI_IN_PLACE, &torque[0], 3*mask_size, MPI_DOUBLE, MPI_SUM, MPI_COMM_WORLD); #endif - // Calculate magnetisation length and normalize - /*for(int mask_id=0; mask_id < mask_size; ++mask_id){ - - // determine inverse number of atoms in mask - double inv_atoms_in_mask = 1.0 / double(num_atoms_in_mask[mask_id]); - - torque[3*mask_id + 0] *= inv_atoms_in_mask; - torque[3*mask_id + 1] *= inv_atoms_in_mask; - torque[3*mask_id + 2] *= inv_atoms_in_mask; - - }*/ - // Zero empty mask id's for(unsigned int id=0; id + +// Vampire headers +#include "unitcell.hpp" + +// unitcell module headers +#include "internal.hpp" + +namespace unitcell{ +namespace internal{ + +void build_perovskite(unitcell::unit_cell_t& unit_cell){ + + // Set basic unit cell properties + unit_cell.dimensions[0] = 1.0; + unit_cell.dimensions[1] = 1.0; + unit_cell.dimensions[2] = 1.0; + + unit_cell.shape[0][0]=1.0; + unit_cell.shape[0][1]=0.0; + unit_cell.shape[0][2]=0.0; + + unit_cell.shape[1][0]=0.0; + unit_cell.shape[1][1]=1.0; + unit_cell.shape[1][2]=0.0; + + unit_cell.shape[2][0]=0.0; + unit_cell.shape[2][1]=0.0; + unit_cell.shape[2][2]=1.0; + + unit_cell.lcsize=5; + unit_cell.hcsize=2; + unit_cell.interaction_range=1; + unit_cell.atom.resize(5); + unit_cell.surface_threshold=18; + //----------------------------- + // Ca site + //----------------------------- + unit_cell.atom[0].x=0.0; + unit_cell.atom[0].y=0.0; + unit_cell.atom[0].z=0.0; + unit_cell.atom[0].mat = uc::internal::sublattice_materials ? 0 : 0; // if sublattice material is defined, then identify at as same as ID + unit_cell.atom[0].lc=0; + unit_cell.atom[0].hc=0; + unit_cell.atom[0].ni=8; + //----------------------------- + // Ti site + //----------------------------- + unit_cell.atom[1].x=0.5; + unit_cell.atom[1].y=0.5; + unit_cell.atom[1].z=0.5; + unit_cell.atom[1].mat = uc::internal::sublattice_materials ? 1 : 1; // if sublattice material is defined, then identify at as same as ID + unit_cell.atom[1].lc=1; + unit_cell.atom[1].hc=1; + unit_cell.atom[1].ni=8; + //----------------------------- + // O site + //----------------------------- + unit_cell.atom[2].x=0.5; + unit_cell.atom[2].y=0.5; + unit_cell.atom[2].z=0.0; + unit_cell.atom[2].mat = uc::internal::sublattice_materials ? 2 : 2; // if sublattice material is defined, then identify at as same as ID + unit_cell.atom[2].lc=2; + unit_cell.atom[2].hc=0; + unit_cell.atom[2].ni=8; + //----------------------------- + // O site + //----------------------------- + unit_cell.atom[3].x=0.5; + unit_cell.atom[3].y=0.0; + unit_cell.atom[3].z=0.5; + unit_cell.atom[3].mat = uc::internal::sublattice_materials ? 3 : 2; // if sublattice material is defined, then identify at as same as ID + unit_cell.atom[3].lc=3; + unit_cell.atom[3].hc=1; + unit_cell.atom[3].ni=8; + //----------------------------- + // O site + //----------------------------- + unit_cell.atom[4].x=0.0; + unit_cell.atom[4].y=0.5; + unit_cell.atom[4].z=0.5; + unit_cell.atom[4].mat = uc::internal::sublattice_materials ? 4 : 2; // if sublattice material is defined, then identify at as same as ID + unit_cell.atom[4].lc=4; + unit_cell.atom[4].hc=1; + unit_cell.atom[4].ni=8; + + unit_cell.cutoff_radius = sqrt(3.0)/2.0; // normalised to unit cell size + + uc::internal::calculate_interactions(unit_cell); + + // Set actual unit cell size after calculating interactions + unit_cell.dimensions[0] *= unitcell::internal::unit_cell_size_x; + unit_cell.dimensions[1] *= unitcell::internal::unit_cell_size_y; + unit_cell.dimensions[2] *= unitcell::internal::unit_cell_size_z; + + return; + +} + +} // end of internal namespace +} // end of unitcell namespace diff --git a/src/unitcell/read.cpp b/src/unitcell/read.cpp index 73f843128..5d9b279d0 100644 --- a/src/unitcell/read.cpp +++ b/src/unitcell/read.cpp @@ -99,11 +99,11 @@ void read_unit_cell(unit_cell_t & unit_cell, std::string filename){ iss >> num_uc_atoms; //std::cout << "Reading in " << num_uc_atoms << " atoms" << std::endl; // resize unit_cell.atom array if within allowable bounds - if( (num_uc_atoms >0) && (num_uc_atoms <= 1000000)) unit_cell.atom.resize(num_uc_atoms); + if( (num_uc_atoms >0) && (num_uc_atoms <= 100000000)) unit_cell.atom.resize(num_uc_atoms); else { terminaltextcolor(RED); std::cerr << "Error! Requested number of atoms " << num_uc_atoms << " on line " << line_counter - << " of unit cell input file " << filename.c_str() << " is outside of valid range 1-1,000,000. Exiting" << std::endl; err::vexit(); + << " of unit cell input file " << filename.c_str() << " is outside of valid range 1-100,000,000. Exiting" << std::endl; err::vexit(); terminaltextcolor(WHITE); } diff --git a/src/unitcell/rocksalt-supercell.cpp b/src/unitcell/rocksalt-supercell.cpp new file mode 100644 index 000000000..45520ff54 --- /dev/null +++ b/src/unitcell/rocksalt-supercell.cpp @@ -0,0 +1,579 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2016. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include + +// Vampire headers +#include "unitcell.hpp" + +// unitcell module headers +#include "internal.hpp" + +namespace unitcell{ +namespace internal{ + +void build_rock_salt_supercell(unitcell::unit_cell_t& unit_cell){ + + // Set basic unit cell properties + unit_cell.dimensions[0] = 1.0; + unit_cell.dimensions[1] = 1.0; + unit_cell.dimensions[2] = 1.0; + + unit_cell.shape[0][0]=1.0; + unit_cell.shape[0][1]=0.0; + unit_cell.shape[0][2]=0.0; + + unit_cell.shape[1][0]=0.0; + unit_cell.shape[1][1]=1.0; + unit_cell.shape[1][2]=0.0; + + unit_cell.shape[2][0]=0.0; + unit_cell.shape[2][1]=0.0; + unit_cell.shape[2][2]=1.0; + + unit_cell.lcsize=3; + unit_cell.hcsize=4; + unit_cell.interaction_range=1; + unit_cell.atom.resize(64); + unit_cell.surface_threshold=6; + //----------------------------- + unit_cell.atom[0].x = 0; + unit_cell.atom[0].y = 0; + unit_cell.atom[0].z = 0; + unit_cell.atom[0].mat = uc::internal::sublattice_materials ? 0 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[0].lc = 1; + unit_cell.atom[0].hc = 0; + unit_cell.atom[0].ni = 6; + //--------------------------------------------- + unit_cell.atom[1].x = 0.25; + unit_cell.atom[1].y = 0; + unit_cell.atom[1].z = 0; + unit_cell.atom[1].mat = uc::internal::sublattice_materials ? 1 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[1].lc = 2; + unit_cell.atom[1].hc = 0; + unit_cell.atom[1].ni = 6; + //--------------------------------------------- + unit_cell.atom[2].x = 0; + unit_cell.atom[2].y = 0.25; + unit_cell.atom[2].z = 0; + unit_cell.atom[2].mat = uc::internal::sublattice_materials ? 2 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[2].lc = 2; + unit_cell.atom[2].hc = 0; + unit_cell.atom[2].ni = 6; + //--------------------------------------------- + unit_cell.atom[3].x = 0.25; + unit_cell.atom[3].y = 0.25; + unit_cell.atom[3].z = 0; + unit_cell.atom[3].mat = uc::internal::sublattice_materials ? 3 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[3].lc = 0; + unit_cell.atom[3].hc = 0; + unit_cell.atom[3].ni = 6; + //--------------------------------------------- + unit_cell.atom[4].x = 0; + unit_cell.atom[4].y = 0; + unit_cell.atom[4].z = 0.25; + unit_cell.atom[4].mat = uc::internal::sublattice_materials ? 4 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[4].lc = 2; + unit_cell.atom[4].hc = 1; + unit_cell.atom[4].ni = 6; + //--------------------------------------------- + unit_cell.atom[5].x = 0.25; + unit_cell.atom[5].y = 0; + unit_cell.atom[5].z = 0.25; + unit_cell.atom[5].mat = uc::internal::sublattice_materials ? 5 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[5].lc = 0; + unit_cell.atom[5].hc = 1; + unit_cell.atom[5].ni = 6; + //--------------------------------------------- + unit_cell.atom[6].x = 0; + unit_cell.atom[6].y = 0.25; + unit_cell.atom[6].z = 0.25; + unit_cell.atom[6].mat = uc::internal::sublattice_materials ? 6 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[6].lc = 0; + unit_cell.atom[6].hc = 1; + unit_cell.atom[6].ni = 6; + //--------------------------------------------- + unit_cell.atom[7].x = 0.25; + unit_cell.atom[7].y = 0.25; + unit_cell.atom[7].z = 0.25; + unit_cell.atom[7].mat = uc::internal::sublattice_materials ? 7 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[7].lc = 2; + unit_cell.atom[7].hc = 1; + unit_cell.atom[7].ni = 6; + //--------------------------------------------- + unit_cell.atom[8].x = 0.5; + unit_cell.atom[8].y = 0; + unit_cell.atom[8].z = 0; + unit_cell.atom[8].mat = uc::internal::sublattice_materials ? 8 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[8].lc = 0; + unit_cell.atom[8].hc = 0; + unit_cell.atom[8].ni = 6; + //--------------------------------------------- + unit_cell.atom[9].x = 0.75; + unit_cell.atom[9].y = 0; + unit_cell.atom[9].z = 0; + unit_cell.atom[9].mat = uc::internal::sublattice_materials ? 9 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[9].lc = 2; + unit_cell.atom[9].hc = 0; + unit_cell.atom[9].ni = 6; + //--------------------------------------------- + unit_cell.atom[10].x = 0.5; + unit_cell.atom[10].y = 0.25; + unit_cell.atom[10].z = 0; + unit_cell.atom[10].mat = uc::internal::sublattice_materials ? 10 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[10].lc = 2; + unit_cell.atom[10].hc = 0; + unit_cell.atom[10].ni = 6; + //--------------------------------------------- + unit_cell.atom[11].x = 0.75; + unit_cell.atom[11].y = 0.25; + unit_cell.atom[11].z = 0; + unit_cell.atom[11].mat = uc::internal::sublattice_materials ? 11 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[11].lc = 1; + unit_cell.atom[11].hc = 0; + unit_cell.atom[11].ni = 6; + //--------------------------------------------- + unit_cell.atom[12].x = 0.5; + unit_cell.atom[12].y = 0; + unit_cell.atom[12].z = 0.25; + unit_cell.atom[12].mat = uc::internal::sublattice_materials ? 12 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[12].lc = 2; + unit_cell.atom[12].hc = 1; + unit_cell.atom[12].ni = 6; + //--------------------------------------------- + unit_cell.atom[13].x = 0.75; + unit_cell.atom[13].y = 0; + unit_cell.atom[13].z = 0.25; + unit_cell.atom[13].mat = uc::internal::sublattice_materials ? 13 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[13].lc = 1; + unit_cell.atom[13].hc = 1; + unit_cell.atom[13].ni = 6; + //--------------------------------------------- + unit_cell.atom[14].x = 0.5; + unit_cell.atom[14].y = 0.25; + unit_cell.atom[14].z = 0.25; + unit_cell.atom[14].mat = uc::internal::sublattice_materials ? 14 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[14].lc = 1; + unit_cell.atom[14].hc = 1; + unit_cell.atom[14].ni = 6; + //--------------------------------------------- + unit_cell.atom[15].x = 0.75; + unit_cell.atom[15].y = 0.25; + unit_cell.atom[15].z = 0.25; + unit_cell.atom[15].mat = uc::internal::sublattice_materials ? 15 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[15].lc = 2; + unit_cell.atom[15].hc = 1; + unit_cell.atom[15].ni = 6; + //--------------------------------------------- + unit_cell.atom[16].x = 0; + unit_cell.atom[16].y = 0.5; + unit_cell.atom[16].z = 0; + unit_cell.atom[16].mat = uc::internal::sublattice_materials ? 16 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[16].lc = 0; + unit_cell.atom[16].hc = 0; + unit_cell.atom[16].ni = 6; + //--------------------------------------------- + unit_cell.atom[17].x = 0.25; + unit_cell.atom[17].y = 0.5; + unit_cell.atom[17].z = 0; + unit_cell.atom[17].mat = uc::internal::sublattice_materials ? 17 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[17].lc = 2; + unit_cell.atom[17].hc = 0; + unit_cell.atom[17].ni = 6; + //--------------------------------------------- + unit_cell.atom[18].x = 0; + unit_cell.atom[18].y = 0.75; + unit_cell.atom[18].z = 0; + unit_cell.atom[18].mat = uc::internal::sublattice_materials ? 18 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[18].lc = 2; + unit_cell.atom[18].hc = 0; + unit_cell.atom[18].ni = 6; + //--------------------------------------------- + unit_cell.atom[19].x = 0.25; + unit_cell.atom[19].y = 0.75; + unit_cell.atom[19].z = 0; + unit_cell.atom[19].mat = uc::internal::sublattice_materials ? 19 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[19].lc = 1; + unit_cell.atom[19].hc = 0; + unit_cell.atom[19].ni = 6; + //--------------------------------------------- + unit_cell.atom[20].x = 0; + unit_cell.atom[20].y = 0.5; + unit_cell.atom[20].z = 0.25; + unit_cell.atom[20].mat = uc::internal::sublattice_materials ? 20 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[20].lc = 2; + unit_cell.atom[20].hc = 1; + unit_cell.atom[20].ni = 6; + //--------------------------------------------- + unit_cell.atom[21].x = 0.25; + unit_cell.atom[21].y = 0.5; + unit_cell.atom[21].z = 0.25; + unit_cell.atom[21].mat = uc::internal::sublattice_materials ? 21 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[21].lc = 1; + unit_cell.atom[21].hc = 1; + unit_cell.atom[21].ni = 6; + //--------------------------------------------- + unit_cell.atom[22].x = 0; + unit_cell.atom[22].y = 0.75; + unit_cell.atom[22].z = 0.25; + unit_cell.atom[22].mat = uc::internal::sublattice_materials ? 22 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[22].lc = 1; + unit_cell.atom[22].hc = 1; + unit_cell.atom[22].ni = 6; + //--------------------------------------------- + unit_cell.atom[23].x = 0.25; + unit_cell.atom[23].y = 0.75; + unit_cell.atom[23].z = 0.25; + unit_cell.atom[23].mat = uc::internal::sublattice_materials ? 23 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[23].lc = 2; + unit_cell.atom[23].hc = 1; + unit_cell.atom[23].ni = 6; + //--------------------------------------------- + unit_cell.atom[24].x = 0.5; + unit_cell.atom[24].y = 0.5; + unit_cell.atom[24].z = 0; + unit_cell.atom[24].mat = uc::internal::sublattice_materials ? 24 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[24].lc = 1; + unit_cell.atom[24].hc = 0; + unit_cell.atom[24].ni = 6; + //--------------------------------------------- + unit_cell.atom[25].x = 0.75; + unit_cell.atom[25].y = 0.5; + unit_cell.atom[25].z = 0; + unit_cell.atom[25].mat = uc::internal::sublattice_materials ? 25 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[25].lc = 2; + unit_cell.atom[25].hc = 0; + unit_cell.atom[25].ni = 6; + //--------------------------------------------- + unit_cell.atom[26].x = 0.5; + unit_cell.atom[26].y = 0.75; + unit_cell.atom[26].z = 0; + unit_cell.atom[26].mat = uc::internal::sublattice_materials ? 26 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[26].lc = 2; + unit_cell.atom[26].hc = 0; + unit_cell.atom[26].ni = 6; + //--------------------------------------------- + unit_cell.atom[27].x = 0.75; + unit_cell.atom[27].y = 0.75; + unit_cell.atom[27].z = 0; + unit_cell.atom[27].mat = uc::internal::sublattice_materials ? 27 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[27].lc = 0; + unit_cell.atom[27].hc = 0; + unit_cell.atom[27].ni = 6; + //--------------------------------------------- + unit_cell.atom[28].x = 0.5; + unit_cell.atom[28].y = 0.5; + unit_cell.atom[28].z = 0.25; + unit_cell.atom[28].mat = uc::internal::sublattice_materials ? 28 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[28].lc = 2; + unit_cell.atom[28].hc = 1; + unit_cell.atom[28].ni = 6; + //--------------------------------------------- + unit_cell.atom[29].x = 0.75; + unit_cell.atom[29].y = 0.5; + unit_cell.atom[29].z = 0.25; + unit_cell.atom[29].mat = uc::internal::sublattice_materials ? 29 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[29].lc = 0; + unit_cell.atom[29].hc = 1; + unit_cell.atom[29].ni = 6; + //--------------------------------------------- + unit_cell.atom[30].x = 0.5; + unit_cell.atom[30].y = 0.75; + unit_cell.atom[30].z = 0.25; + unit_cell.atom[30].mat = uc::internal::sublattice_materials ? 30 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[30].lc = 0; + unit_cell.atom[30].hc = 1; + unit_cell.atom[30].ni = 6; + //--------------------------------------------- + unit_cell.atom[31].x = 0.75; + unit_cell.atom[31].y = 0.75; + unit_cell.atom[31].z = 0.25; + unit_cell.atom[31].mat = uc::internal::sublattice_materials ? 31 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[31].lc = 2; + unit_cell.atom[31].hc = 1; + unit_cell.atom[31].ni = 6; + //--------------------------------------------- + unit_cell.atom[32].x = 0; + unit_cell.atom[32].y = 0; + unit_cell.atom[32].z = 0.5; + unit_cell.atom[32].mat = uc::internal::sublattice_materials ? 32 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[32].lc = 0; + unit_cell.atom[32].hc = 2; + unit_cell.atom[32].ni = 6; + //--------------------------------------------- + unit_cell.atom[33].x = 0.25; + unit_cell.atom[33].y = 0; + unit_cell.atom[33].z = 0.5; + unit_cell.atom[33].mat = uc::internal::sublattice_materials ? 33 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[33].lc = 2; + unit_cell.atom[33].hc = 2; + unit_cell.atom[33].ni = 6; + //--------------------------------------------- + unit_cell.atom[34].x = 0; + unit_cell.atom[34].y = 0.25; + unit_cell.atom[34].z = 0.5; + unit_cell.atom[34].mat = uc::internal::sublattice_materials ? 34 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[34].lc = 2; + unit_cell.atom[34].hc = 2; + unit_cell.atom[34].ni = 6; + //--------------------------------------------- + unit_cell.atom[35].x = 0.25; + unit_cell.atom[35].y = 0.25; + unit_cell.atom[35].z = 0.5; + unit_cell.atom[35].mat = uc::internal::sublattice_materials ? 35 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[35].lc = 1; + unit_cell.atom[35].hc = 2; + unit_cell.atom[35].ni = 6; + //--------------------------------------------- + unit_cell.atom[36].x = 0; + unit_cell.atom[36].y = 0; + unit_cell.atom[36].z = 0.75; + unit_cell.atom[36].mat = uc::internal::sublattice_materials ? 36 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[36].lc = 2; + unit_cell.atom[36].hc = 3; + unit_cell.atom[36].ni = 6; + //--------------------------------------------- + unit_cell.atom[37].x = 0.25; + unit_cell.atom[37].y = 0; + unit_cell.atom[37].z = 0.75; + unit_cell.atom[37].mat = uc::internal::sublattice_materials ? 37 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[37].lc = 1; + unit_cell.atom[37].hc = 3; + unit_cell.atom[37].ni = 6; + //--------------------------------------------- + unit_cell.atom[38].x = 0; + unit_cell.atom[38].y = 0.25; + unit_cell.atom[38].z = 0.75; + unit_cell.atom[38].mat = uc::internal::sublattice_materials ? 38 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[38].lc = 1; + unit_cell.atom[38].hc = 3; + unit_cell.atom[38].ni = 6; + //--------------------------------------------- + unit_cell.atom[39].x = 0.25; + unit_cell.atom[39].y = 0.25; + unit_cell.atom[39].z = 0.75; + unit_cell.atom[39].mat = uc::internal::sublattice_materials ? 39 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[39].lc = 2; + unit_cell.atom[39].hc = 3; + unit_cell.atom[39].ni = 6; + //--------------------------------------------- + unit_cell.atom[40].x = 0.5; + unit_cell.atom[40].y = 0; + unit_cell.atom[40].z = 0.5; + unit_cell.atom[40].mat = uc::internal::sublattice_materials ? 40 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[40].lc = 1; + unit_cell.atom[40].hc = 2; + unit_cell.atom[40].ni = 6; + //--------------------------------------------- + unit_cell.atom[41].x = 0.75; + unit_cell.atom[41].y = 0; + unit_cell.atom[41].z = 0.5; + unit_cell.atom[41].mat = uc::internal::sublattice_materials ? 41 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[41].lc = 2; + unit_cell.atom[41].hc = 2; + unit_cell.atom[41].ni = 6; + //--------------------------------------------- + unit_cell.atom[42].x = 0.5; + unit_cell.atom[42].y = 0.25; + unit_cell.atom[42].z = 0.5; + unit_cell.atom[42].mat = uc::internal::sublattice_materials ? 42 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[42].lc = 2; + unit_cell.atom[42].hc = 2; + unit_cell.atom[42].ni = 6; + //--------------------------------------------- + unit_cell.atom[43].x = 0.75; + unit_cell.atom[43].y = 0.25; + unit_cell.atom[43].z = 0.5; + unit_cell.atom[43].mat = uc::internal::sublattice_materials ? 43 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[43].lc = 0; + unit_cell.atom[43].hc = 2; + unit_cell.atom[43].ni = 6; + //--------------------------------------------- + unit_cell.atom[44].x = 0.5; + unit_cell.atom[44].y = 0; + unit_cell.atom[44].z = 0.75; + unit_cell.atom[44].mat = uc::internal::sublattice_materials ? 44 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[44].lc = 2; + unit_cell.atom[44].hc = 3; + unit_cell.atom[44].ni = 6; + //--------------------------------------------- + unit_cell.atom[45].x = 0.75; + unit_cell.atom[45].y = 0; + unit_cell.atom[45].z = 0.75; + unit_cell.atom[45].mat = uc::internal::sublattice_materials ? 45 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[45].lc = 0; + unit_cell.atom[45].hc = 3; + unit_cell.atom[45].ni = 6; + //--------------------------------------------- + unit_cell.atom[46].x = 0.5; + unit_cell.atom[46].y = 0.25; + unit_cell.atom[46].z = 0.75; + unit_cell.atom[46].mat = uc::internal::sublattice_materials ? 46 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[46].lc = 0; + unit_cell.atom[46].hc = 3; + unit_cell.atom[46].ni = 6; + //--------------------------------------------- + unit_cell.atom[47].x = 0.75; + unit_cell.atom[47].y = 0.25; + unit_cell.atom[47].z = 0.75; + unit_cell.atom[47].mat = uc::internal::sublattice_materials ? 47 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[47].lc = 2; + unit_cell.atom[47].hc = 3; + unit_cell.atom[47].ni = 6; + //--------------------------------------------- + unit_cell.atom[48].x = 0; + unit_cell.atom[48].y = 0.5; + unit_cell.atom[48].z = 0.5; + unit_cell.atom[48].mat = uc::internal::sublattice_materials ? 48 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[48].lc = 1; + unit_cell.atom[48].hc = 2; + unit_cell.atom[48].ni = 6; + //--------------------------------------------- + unit_cell.atom[49].x = 0.25; + unit_cell.atom[49].y = 0.5; + unit_cell.atom[49].z = 0.5; + unit_cell.atom[49].mat = uc::internal::sublattice_materials ? 49 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[49].lc = 2; + unit_cell.atom[49].hc = 2; + unit_cell.atom[49].ni = 6; + //--------------------------------------------- + unit_cell.atom[50].x = 0; + unit_cell.atom[50].y = 0.75; + unit_cell.atom[50].z = 0.5; + unit_cell.atom[50].mat = uc::internal::sublattice_materials ? 50 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[50].lc = 2; + unit_cell.atom[50].hc = 2; + unit_cell.atom[50].ni = 6; + //--------------------------------------------- + unit_cell.atom[51].x = 0.25; + unit_cell.atom[51].y = 0.75; + unit_cell.atom[51].z = 0.5; + unit_cell.atom[51].mat = uc::internal::sublattice_materials ? 51 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[51].lc = 0; + unit_cell.atom[51].hc = 2; + unit_cell.atom[51].ni = 6; + //--------------------------------------------- + unit_cell.atom[52].x = 0; + unit_cell.atom[52].y = 0.5; + unit_cell.atom[52].z = 0.75; + unit_cell.atom[52].mat = uc::internal::sublattice_materials ? 52 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[52].lc = 2; + unit_cell.atom[52].hc = 3; + unit_cell.atom[52].ni = 6; + //--------------------------------------------- + unit_cell.atom[53].x = 0.25; + unit_cell.atom[53].y = 0.5; + unit_cell.atom[53].z = 0.75; + unit_cell.atom[53].mat = uc::internal::sublattice_materials ? 53 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[53].lc = 0; + unit_cell.atom[53].hc = 3; + unit_cell.atom[53].ni = 6; + //--------------------------------------------- + unit_cell.atom[54].x = 0; + unit_cell.atom[54].y = 0.75; + unit_cell.atom[54].z = 0.75; + unit_cell.atom[54].mat = uc::internal::sublattice_materials ? 54 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[54].lc = 0; + unit_cell.atom[54].hc = 3; + unit_cell.atom[54].ni = 6; + //--------------------------------------------- + unit_cell.atom[55].x = 0.25; + unit_cell.atom[55].y = 0.75; + unit_cell.atom[55].z = 0.75; + unit_cell.atom[55].mat = uc::internal::sublattice_materials ? 55 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[55].lc = 2; + unit_cell.atom[55].hc = 3; + unit_cell.atom[55].ni = 6; + //--------------------------------------------- + unit_cell.atom[56].x = 0.5; + unit_cell.atom[56].y = 0.5; + unit_cell.atom[56].z = 0.5; + unit_cell.atom[56].mat = uc::internal::sublattice_materials ? 56 : 0; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[56].lc = 0; + unit_cell.atom[56].hc = 2; + unit_cell.atom[56].ni = 6; + //--------------------------------------------- + unit_cell.atom[57].x = 0.75; + unit_cell.atom[57].y = 0.5; + unit_cell.atom[57].z = 0.5; + unit_cell.atom[57].mat = uc::internal::sublattice_materials ? 57 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[57].lc = 2; + unit_cell.atom[57].hc = 2; + unit_cell.atom[57].ni = 6; + //--------------------------------------------- + unit_cell.atom[58].x = 0.5; + unit_cell.atom[58].y = 0.75; + unit_cell.atom[58].z = 0.5; + unit_cell.atom[58].mat = uc::internal::sublattice_materials ? 58 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[58].lc = 2; + unit_cell.atom[58].hc = 2; + unit_cell.atom[58].ni = 6; + //--------------------------------------------- + unit_cell.atom[59].x = 0.75; + unit_cell.atom[59].y = 0.75; + unit_cell.atom[59].z = 0.5; + unit_cell.atom[59].mat = uc::internal::sublattice_materials ? 59 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[59].lc = 1; + unit_cell.atom[59].hc = 2; + unit_cell.atom[59].ni = 6; + //--------------------------------------------- + unit_cell.atom[60].x = 0.5; + unit_cell.atom[60].y = 0.5; + unit_cell.atom[60].z = 0.75; + unit_cell.atom[60].mat = uc::internal::sublattice_materials ? 60 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[60].lc = 2; + unit_cell.atom[60].hc = 3; + unit_cell.atom[60].ni = 6; + //--------------------------------------------- + unit_cell.atom[61].x = 0.75; + unit_cell.atom[61].y = 0.5; + unit_cell.atom[61].z = 0.75; + unit_cell.atom[61].mat = uc::internal::sublattice_materials ? 61 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[61].lc = 1; + unit_cell.atom[61].hc = 3; + unit_cell.atom[61].ni = 6; + //--------------------------------------------- + unit_cell.atom[62].x = 0.5; + unit_cell.atom[62].y = 0.75; + unit_cell.atom[62].z = 0.75; + unit_cell.atom[62].mat = uc::internal::sublattice_materials ? 62 : 1; // if sublattice material is defined, then identify as metal sublattice + unit_cell.atom[62].lc = 1; + unit_cell.atom[62].hc = 3; + unit_cell.atom[62].ni = 6; + //--------------------------------------------- + unit_cell.atom[63].x = 0.75; + unit_cell.atom[63].y = 0.75; + unit_cell.atom[63].z = 0.75; + unit_cell.atom[63].mat = uc::internal::sublattice_materials ? 63 : 2; // if sublattice material is defined, then identify as oxygen sublattice + unit_cell.atom[63].lc = 2; + unit_cell.atom[63].hc = 3; + unit_cell.atom[63].ni = 6; + //--------------------------------------------- + unit_cell.cutoff_radius = 0.51; // normalised to unit cell size (change to 0.26 for oxygen NNs) + + uc::internal::calculate_interactions(unit_cell); + + // Set actual unit cell size after calculating interactions + unit_cell.dimensions[0] *= unitcell::internal::unit_cell_size_x; + unit_cell.dimensions[1] *= unitcell::internal::unit_cell_size_y; + unit_cell.dimensions[2] *= unitcell::internal::unit_cell_size_z; + + std::cout << "Generated rocksalt-supercell with " << unit_cell.atom.size() << " atoms" << std::endl; + + return; + +} + +} // end of internal namespace +} // end of unitcell namespace diff --git a/src/unitcell/rocksalt.cpp b/src/unitcell/rocksalt.cpp index 768c803a1..aa62cc2b4 100644 --- a/src/unitcell/rocksalt.cpp +++ b/src/unitcell/rocksalt.cpp @@ -29,87 +29,87 @@ void build_rock_salt(unitcell::unit_cell_t& unit_cell){ unit_cell.dimensions[1] = 1.0; unit_cell.dimensions[2] = 1.0; - unit_cell.shape[0][0]=1.0; - unit_cell.shape[0][1]=0.0; - unit_cell.shape[0][2]=0.0; + unit_cell.shape[0][0] = 1.0; + unit_cell.shape[0][1] = 0.0; + unit_cell.shape[0][2] = 0.0; - unit_cell.shape[1][0]=0.0; - unit_cell.shape[1][1]=1.0; - unit_cell.shape[1][2]=0.0; + unit_cell.shape[1][0] = 0.0; + unit_cell.shape[1][1] = 1.0; + unit_cell.shape[1][2] = 0.0; - unit_cell.shape[2][0]=0.0; - unit_cell.shape[2][1]=0.0; - unit_cell.shape[2][2]=1.0; + unit_cell.shape[2][0] = 0.0; + unit_cell.shape[2][1] = 0.0; + unit_cell.shape[2][2] = 1.0; - unit_cell.lcsize=2; - unit_cell.hcsize=2; - unit_cell.interaction_range=1; + unit_cell.lcsize = 2; + unit_cell.hcsize = 2; + unit_cell.interaction_range = 1; unit_cell.atom.resize(8); - unit_cell.surface_threshold=6; + unit_cell.surface_threshold = 6; //----------------------------- - unit_cell.atom[0].x=0.0; - unit_cell.atom[0].y=0.0; - unit_cell.atom[0].z=0.0; - unit_cell.atom[0].mat=0; - unit_cell.atom[0].lc=0; - unit_cell.atom[0].hc=0; - unit_cell.atom[0].ni=6; + unit_cell.atom[0].x = 0.0; + unit_cell.atom[0].y = 0.0; + unit_cell.atom[0].z = 0.0; + unit_cell.atom[0].mat = uc::internal::sublattice_materials ? 0 : 0; // if sublattice material is defined, then identify as metal sublattice 0 + unit_cell.atom[0].lc = 0; + unit_cell.atom[0].hc = 0; + unit_cell.atom[0].ni = 6; //----------------------------- - unit_cell.atom[1].x=0.5; - unit_cell.atom[1].y=0.0; - unit_cell.atom[1].z=0.0; - unit_cell.atom[1].mat=1; - unit_cell.atom[1].lc=1; - unit_cell.atom[1].hc=0; - unit_cell.atom[1].ni=6; + unit_cell.atom[1].x = 0.5; + unit_cell.atom[1].y = 0.0; + unit_cell.atom[1].z = 0.0; + unit_cell.atom[1].mat = uc::internal::sublattice_materials ? 4 : 1; // if sublattice material is defined, then identify as Oxygen + unit_cell.atom[1].lc = 1; + unit_cell.atom[1].hc = 0; + unit_cell.atom[1].ni = 6; //----------------------------- - unit_cell.atom[2].x=0.0; - unit_cell.atom[2].y=0.5; - unit_cell.atom[2].z=0.0; - unit_cell.atom[2].mat=1; - unit_cell.atom[2].lc=1; - unit_cell.atom[2].hc=0; - unit_cell.atom[2].ni=6; + unit_cell.atom[2].x = 0.0; + unit_cell.atom[2].y = 0.5; + unit_cell.atom[2].z = 0.0; + unit_cell.atom[2].mat = uc::internal::sublattice_materials ? 4 : 1; // if sublattice material is defined, then identify as Oxygen + unit_cell.atom[2].lc = 1; + unit_cell.atom[2].hc = 0; + unit_cell.atom[2].ni = 6; //----------------------------- - unit_cell.atom[3].x=0.5; - unit_cell.atom[3].y=0.5; - unit_cell.atom[3].z=0.0; - unit_cell.atom[3].mat=0; - unit_cell.atom[3].lc=0; - unit_cell.atom[3].hc=0; - unit_cell.atom[3].ni=6; + unit_cell.atom[3].x = 0.5; + unit_cell.atom[3].y = 0.5; + unit_cell.atom[3].z = 0.0; + unit_cell.atom[3].mat=uc::internal::sublattice_materials ? 1 : 0; // if sublattice material is defined, then identify as metal sublattice 1 + unit_cell.atom[3].lc = 0; + unit_cell.atom[3].hc = 0; + unit_cell.atom[3].ni = 6; //----------------------------- - unit_cell.atom[4].x=0.0; - unit_cell.atom[4].y=0.0; - unit_cell.atom[4].z=0.5; - unit_cell.atom[4].mat=1; - unit_cell.atom[4].lc=1; - unit_cell.atom[4].hc=1; - unit_cell.atom[4].ni=6; + unit_cell.atom[4].x = 0.0; + unit_cell.atom[4].y = 0.0; + unit_cell.atom[4].z = 0.5; + unit_cell.atom[4].mat = uc::internal::sublattice_materials ? 4 : 1; // if sublattice material is defined, then identify as Oxygen + unit_cell.atom[4].lc = 1; + unit_cell.atom[4].hc = 1; + unit_cell.atom[4].ni = 6; //----------------------------- - unit_cell.atom[5].x=0.5; - unit_cell.atom[5].y=0.0; - unit_cell.atom[5].z=0.5; - unit_cell.atom[5].mat=0; - unit_cell.atom[5].lc=0; - unit_cell.atom[5].hc=1; - unit_cell.atom[5].ni=6; + unit_cell.atom[5].x = 0.5; + unit_cell.atom[5].y = 0.0; + unit_cell.atom[5].z = 0.5; + unit_cell.atom[5].mat = uc::internal::sublattice_materials ? 2 : 0; // if sublattice material is defined, then identify as metal sublattice 2 + unit_cell.atom[5].lc = 0; + unit_cell.atom[5].hc = 1; + unit_cell.atom[5].ni = 6; //----------------------------- - unit_cell.atom[6].x=0.0; - unit_cell.atom[6].y=0.5; - unit_cell.atom[6].z=0.5; - unit_cell.atom[6].mat=0; - unit_cell.atom[6].lc=0; - unit_cell.atom[6].hc=1; - unit_cell.atom[6].ni=6; + unit_cell.atom[6].x = 0.0; + unit_cell.atom[6].y = 0.5; + unit_cell.atom[6].z = 0.5; + unit_cell.atom[6].mat = uc::internal::sublattice_materials ? 3 : 0; // if sublattice material is defined, then identify as metal sublattice 3 + unit_cell.atom[6].lc = 0; + unit_cell.atom[6].hc = 1; + unit_cell.atom[6].ni = 6; //----------------------------- - unit_cell.atom[7].x=0.5; - unit_cell.atom[7].y=0.5; - unit_cell.atom[7].z=0.5; - unit_cell.atom[7].mat=1; - unit_cell.atom[7].lc=1; - unit_cell.atom[7].hc=1; - unit_cell.atom[7].ni=6; + unit_cell.atom[7].x = 0.5; + unit_cell.atom[7].y = 0.5; + unit_cell.atom[7].z = 0.5; + unit_cell.atom[7].mat = uc::internal::sublattice_materials ? 4 : 1; // if sublattice material is defined, then identify as Oxygen + unit_cell.atom[7].lc = 1; + unit_cell.atom[7].hc = 1; + unit_cell.atom[7].ni = 6; unit_cell.cutoff_radius = 0.5; // normalised to unit cell size diff --git a/src/utility/checkpoint.cpp b/src/utility/checkpoint.cpp index 29e7d65fe..31760560c 100644 --- a/src/utility/checkpoint.cpp +++ b/src/utility/checkpoint.cpp @@ -18,6 +18,8 @@ #include "errors.hpp" #include "random.hpp" #include "sim.hpp" +#include "sld.hpp" +#include "stats.hpp" #include "vio.hpp" #include "program.hpp" @@ -63,7 +65,6 @@ void save_checkpoint(){ std::vector mt_state(624); // 624 is hard coded in mt implementation. uint64 assumes same size as unsigned long int32_t mt_p=0; // position in rng state mt_p=mtrandom::grnd.get_state(mt_state); - //std::cout << "random generator state = " << mt_p << std::endl; // write checkpoint variables to file chkfile.write(reinterpret_cast(&natoms64),sizeof(uint64_t)); @@ -87,6 +88,38 @@ void save_checkpoint(){ chkfile.write(reinterpret_cast(&atoms::y_spin_array[0]),sizeof(double)*natoms64); chkfile.write(reinterpret_cast(&atoms::z_spin_array[0]),sizeof(double)*natoms64); + // Save positions and velocities only for SLD simulations + if(sld::enabled){ + + // write position array to file + chkfile.write(reinterpret_cast(&atoms::x_coord_array[0]),sizeof(double)*natoms64); + chkfile.write(reinterpret_cast(&atoms::y_coord_array[0]),sizeof(double)*natoms64); + chkfile.write(reinterpret_cast(&atoms::z_coord_array[0]),sizeof(double)*natoms64); + + // write velocity array to file + chkfile.write(reinterpret_cast(&atoms::x_velo_array[0]),sizeof(double)*natoms64); + chkfile.write(reinterpret_cast(&atoms::y_velo_array[0]),sizeof(double)*natoms64); + chkfile.write(reinterpret_cast(&atoms::z_velo_array[0]),sizeof(double)*natoms64); + + } + + // write statistical properties to file + stats::system_magnetization.save_checkpoint(chkfile); + stats::grain_magnetization.save_checkpoint(chkfile); + stats::material_magnetization.save_checkpoint(chkfile); + stats::material_grain_magnetization.save_checkpoint(chkfile); + stats::height_magnetization.save_checkpoint(chkfile); + stats::material_height_magnetization.save_checkpoint(chkfile); + stats::material_grain_height_magnetization.save_checkpoint(chkfile); + + stats::system_specific_heat.save_checkpoint(chkfile); + stats::grain_specific_heat.save_checkpoint(chkfile); + stats::material_specific_heat.save_checkpoint(chkfile); + + stats::system_susceptibility.save_checkpoint(chkfile); + stats::grain_susceptibility.save_checkpoint(chkfile); + stats::material_susceptibility.save_checkpoint(chkfile); + // close checkpoint file chkfile.close(); @@ -196,6 +229,36 @@ void load_checkpoint(){ chkfile.read((char*)&atoms::y_spin_array[0],sizeof(double)*natoms64); chkfile.read((char*)&atoms::z_spin_array[0],sizeof(double)*natoms64); + // Load positions and velocities only for SLD simulations + if(sld::enabled){ + + chkfile.read((char*)&atoms::x_coord_array[0],sizeof(double)*natoms64); + chkfile.read((char*)&atoms::y_coord_array[0],sizeof(double)*natoms64); + chkfile.read((char*)&atoms::z_coord_array[0],sizeof(double)*natoms64); + + chkfile.read((char*)&atoms::x_velo_array[0],sizeof(double)*natoms64); + chkfile.read((char*)&atoms::y_velo_array[0],sizeof(double)*natoms64); + chkfile.read((char*)&atoms::z_velo_array[0],sizeof(double)*natoms64); + + } + + // load statistical properties from file + stats::system_magnetization.load_checkpoint(chkfile,sim::load_checkpoint_continue_flag); + stats::grain_magnetization.load_checkpoint(chkfile,sim::load_checkpoint_continue_flag); + stats::material_magnetization.load_checkpoint(chkfile,sim::load_checkpoint_continue_flag); + stats::material_grain_magnetization.load_checkpoint(chkfile,sim::load_checkpoint_continue_flag); + stats::height_magnetization.load_checkpoint(chkfile,sim::load_checkpoint_continue_flag); + stats::material_height_magnetization.load_checkpoint(chkfile,sim::load_checkpoint_continue_flag); + stats::material_grain_height_magnetization.load_checkpoint(chkfile,sim::load_checkpoint_continue_flag); + + stats::system_specific_heat.load_checkpoint(chkfile,sim::load_checkpoint_continue_flag); + stats::grain_specific_heat.load_checkpoint(chkfile,sim::load_checkpoint_continue_flag); + stats::material_specific_heat.load_checkpoint(chkfile,sim::load_checkpoint_continue_flag); + + stats::system_susceptibility.load_checkpoint(chkfile,sim::load_checkpoint_continue_flag); + stats::grain_susceptibility.load_checkpoint(chkfile,sim::load_checkpoint_continue_flag); + stats::material_susceptibility.load_checkpoint(chkfile,sim::load_checkpoint_continue_flag); + // close checkpoint file chkfile.close(); diff --git a/src/utility/vmath.cpp b/src/utility/vmath.cpp index 573f586f0..e345a8875 100644 --- a/src/utility/vmath.cpp +++ b/src/utility/vmath.cpp @@ -105,6 +105,41 @@ bool point_in_polygon_factor(double x, double y, double factor, double *polyX, d } +//--------------------------------------------------------------------------------------------- +// +// Function to determine if a point lies within a polygon uniformly scaled about the origin +// +// Scaling the polygon by a factor f is equivalent to scaling the test point by 1/f, which +// is both simpler and numerically better behaved than scaling every vertex. +// +// Note: this supersedes point_in_polygon_factor(), which incorrectly scales the polygon by +// f^2. That function is retained unchanged for backwards compatibility of existing +// core-shell and grain-substructure geometries. +// +//--------------------------------------------------------------------------------------------- +bool point_in_polygon_scaled(double x, double y, double factor, double *polyX, double *polyY, int polySides){ + + // a degenerate or inverted polygon contains nothing + if(factor <= 0.0) return false; + + const double inv_factor = 1.0/factor; + const double sx = x*inv_factor; + const double sy = y*inv_factor; + + int j = polySides-1; + bool oddNodes = false; + + for(int i=0; i= sy) || (polyY[j] < sy && polyY[i] >= sy) ){ + if(polyX[i] + (sy-polyY[i])/(polyY[j]-polyY[i])*(polyX[j]-polyX[i]) < sx) oddNodes = !oddNodes; + } + j = i; + } + + return oddNodes; + +} + bool point_in_polygon2(double x, double y, std::vector& polyX, std::vector& polyY, const int polySides) { ///======================================================================================================== /// Function to decide if point is within polygon diff --git a/src/vio/data.cpp b/src/vio/data.cpp index f4d4ae340..c49a72091 100644 --- a/src/vio/data.cpp +++ b/src/vio/data.cpp @@ -28,6 +28,7 @@ std::ofstream dp_fields; namespace vout{ + std::string output_file_name; std::string zLogProgramName; /// Program Name std::string zLogHostName; /// Host Name bool zLogInitialised=false; /// Initialised flag diff --git a/src/vio/datalog.cpp b/src/vio/datalog.cpp index 53dade887..8c32b0670 100644 --- a/src/vio/datalog.cpp +++ b/src/vio/datalog.cpp @@ -20,6 +20,7 @@ #include "gpu.hpp" #include "grains.hpp" #include "sim.hpp" +#include "sld.hpp" #include "vio.hpp" #include "micromagnetic.hpp" @@ -272,8 +273,62 @@ namespace vout{ break; case 72: vout::fractional_electric_field_strength(stream, header); + break; + case 74: + vout::sysspintemp(stream, header); break; - case 997: //MP + case 75: + vout::mean_sysspintemp(stream, header); + break; + case 76: + vout::material_mean_sysspintemp(stream, header); + break; + case 77: + vout::material_spin_temp(stream, header); + break; + case 78: + vout::mean_system_spin_length(stream,header); + break; + case 79: + vout::mean_material_spin_length(stream,header); + break; + case 80: + vout::mean_height_spin_length(stream,header); + break; + case 81: + vout::potential_energy(stream, header); + break; + case 82: + vout::kinetic_energy(stream, header); + break; + case 83: + vout::sld_exchange_energy(stream, header); + break; + case 84: + vout::sld_coupling_energy(stream, header); + break; + case 85: + vout::sld_total_energy(stream, header); + break; + //case 86: + //vout::sld_total_spin_energy(stream, header); + //break; + case 87: + vout::syslatticetemp(stream, header); + break; + case 88: + vout::mean_syslatticetemp(stream, header); + break; + case 89: + vout::material_lattice_temp(stream, header); + break; + case 90: + vout::material_mean_syslatticetemp(stream, header); + break; + case 91: + vout::mean_coupling_field_vec(stream,header); + break; + case 997: //MP vout::material_binder_cumulant(stream,header); break; case 998: @@ -349,10 +404,10 @@ namespace vout{ if(vmpi::my_rank == 0){ if(!zmag.is_open()){ // check for checkpoint continue and append data - if(sim::load_checkpoint_flag && sim::load_checkpoint_continue_flag) zmag.open("output",std::ofstream::app); + if(sim::load_checkpoint_flag && sim::load_checkpoint_continue_flag) zmag.open(vout::output_file_name,std::ofstream::app); // otherwise overwrite file else{ - zmag.open("output",std::ofstream::trunc); + zmag.open(vout::output_file_name,std::ofstream::trunc); // write file header information write_output_file_header(zmag, file_output_list); } diff --git a/src/vio/get_string.cpp b/src/vio/get_string.cpp index 93908c007..74e97c2e1 100644 --- a/src/vio/get_string.cpp +++ b/src/vio/get_string.cpp @@ -16,8 +16,10 @@ #include // Vampire headers -#include "vio.hpp" #include "errors.hpp" +#include "vio.hpp" +#include "vmpi.hpp" +#include "vutil.hpp" // vio module headers #include "internal.hpp" @@ -35,7 +37,7 @@ namespace vin { if(vmpi::my_rank == 0) root = true; // number of characters in file (needed by all processors) - uint64_t len = 0; + uint64_t length = 0; // message buffer to store processed string as characters suitable for MPI Broadcast std::vector message(0); @@ -43,9 +45,18 @@ namespace vin { // Read in file on root if (root){ + // Save info to log file + zlog << zTs() << " Loading text file " << filename.c_str() << " on root process" << std::endl; + + // instantiate timer to calculate bandwidth + vutil::vtimer_t timer; + // ifstream declaration std::ifstream inputfile; + // start the timer + timer.start(); + // Open file inputfile.open(filename.c_str()); @@ -62,30 +73,44 @@ namespace vin { } // load file directly into std::string - std::string contents( (std::istreambuf_iterator(inputfile)), - std::istreambuf_iterator()); + std::string contents( (std::istreambuf_iterator(inputfile)), std::istreambuf_iterator() ); + + // stop the timer + timer.stop(); // get total number of characters in file - len = contents.length(); + length = contents.length(); // reserve correct amount of storage for message - message.reserve(len); + message.reserve(length); // copy contents to message buffer for broadcast std::copy(contents.begin(), contents.end(), std::back_inserter(message)); + // calculate size (MB) and bandwith and save to log file + const double file_size = length * sizeof(char)*1.0e-6; + const double bandwidth = file_size / timer.elapsed_time(); + zlog << zTs() << " File successfully loaded on root process with " << length << " characters and size " << file_size << " [ " << bandwidth << " ] MB/s" << std::endl; + } #ifdef MPICF // broadcast string size from root (0) to all processors - MPI_Bcast(&len, 1, MPI_UINT64_T, 0, MPI_COMM_WORLD); + MPI_Bcast(&length, 1, MPI_UINT64_T, 0, MPI_COMM_WORLD); + + // save progress to log file + zlog << zTs() << " Resizing array on all processes" << std::endl; // resize message buffer on all processors other than root - if(!root) message.resize(len); + if(!root) message.resize(length); + + // save progress to log file + zlog << zTs() << " Broadcasting file from root process" << std::endl; // broadcast message buffer from root (0) to all processors - MPI_Bcast(&message[0], message.size(), MPI_CHAR, 0, MPI_COMM_WORLD); + vmpi::broadcast(message, 0); + //MPI_Bcast(&message[0], message.size(), MPI_CHAR, 0, MPI_COMM_WORLD); #endif diff --git a/src/vio/grain.cpp b/src/vio/grain.cpp index c1c7973e1..339d2ebfc 100644 --- a/src/vio/grain.cpp +++ b/src/vio/grain.cpp @@ -99,7 +99,7 @@ void write_grain_file(){ //------------------------------------------ case grain::material_magnetisation: // inline function to output grain data - zgrain << stats::material_grain_magnetization.output_magnetization(header); + zgrain << stats::material_grain_magnetization.output_normalized_magnetization(header); break; //------------------------------------------ case grain::material_height_magnetisation: @@ -122,6 +122,11 @@ void write_grain_file(){ zgrain << stats::grain_specific_heat.output_mean_specific_heat(sim::temperature, header); break; //------------------------------------------ + //case grain::mean_spin_temp: + // // inline function to output grain data + // zgrain << stats::grain_specific_heat.output_mean_specific_heat(sim::temperature, header); + // break; + //------------------------------------------ } // end of case statement diff --git a/src/vio/internal.hpp b/src/vio/internal.hpp index 6b8c70f99..7eea403df 100644 --- a/src/vio/internal.hpp +++ b/src/vio/internal.hpp @@ -82,7 +82,12 @@ namespace vout{ mean_magnetisation_length, mean_specific_heat, mean_susceptibility, - mean_torque + mean_torque, + mean_spin_temp, + mean_lattice_temp, + SxH2, + SH, + mean_coupling_field_vec }; // internal variables @@ -123,6 +128,12 @@ namespace vout{ void mdoth(std::ostream& stream,bool header); void systorque(std::ostream& stream,bool header); void mean_systorque(std::ostream& stream,bool header); + + void sysspintemp(std::ostream& stream,bool header); + void mean_sysspintemp(std::ostream& stream,bool header); + void material_mean_sysspintemp(std::ostream& stream,bool header); + void material_spin_temp(std::ostream& stream, bool header); + void constraint_phi(std::ostream& stream,bool header); void constraint_theta(std::ostream& stream,bool header); void material_constraint_phi(std::ostream& stream,bool header); @@ -130,6 +141,7 @@ namespace vout{ void material_mean_systorque(std::ostream& stream,bool header); void material_torque(std::ostream& stream, bool header); void standard_deviation(std::ostream& stream,bool header); + void mean_system_susceptibility(std::ostream& stream,bool header); void system_binder_cumulant(std::ostream& stream,bool header); void phonon_temperature(std::ostream& stream,bool header); @@ -179,6 +191,36 @@ namespace vout{ void domain_wall_position(std::ostream& stream,bool header); void MRresistance(std::ostream& stream, bool header); + void mean_system_spin_length(std::ostream& stream,bool header); + void mean_material_spin_length(std::ostream& stream,bool header); + void mean_height_spin_length(std::ostream& stream,bool header); + + void spin_temperature(std::ostream& stream, bool header); + void lattice_temperature(std::ostream& stream, bool header); + + void material_spin_temp(std::ostream& stream, bool header); + void sysspintemp(std::ostream& stream,bool header); + void mean_sysspintemp(std::ostream& stream,bool header); + void material_mean_sysspintemp(std::ostream& stream,bool header); + + void syslatticetemp(std::ostream& stream,bool header); + void material_lattice_temp(std::ostream& stream, bool header); + void mean_syslatticetemp(std::ostream& stream,bool header); + void material_mean_syslatticetemp(std::ostream& stream,bool header); + void mean_coupling_field_vec(std::ostream& stream, bool header); + + void potential_energy(std::ostream& stream, bool header); + void kinetic_energy(std::ostream& stream, bool header); + void sld_exchange_energy(std::ostream& stream, bool header); + void sld_coupling_energy(std::ostream& stream, bool header); + void sld_total_energy(std::ostream& stream, bool header); + + void mean_potential_energy(std::ostream& stream, bool header); + void mean_kinetic_energy(std::ostream& stream, bool header); + void mean_sld_exchange_energy(std::ostream& stream, bool header); + void mean_sld_coupling_energy(std::ostream& stream, bool header); + void mean_sld_total_energy(std::ostream& stream, bool header); + //------------------------------------------------------------------------- // Funciton protypes for functions inside: datalog.cpp //------------------------------------------------------------------------- diff --git a/src/vio/makefile b/src/vio/makefile index 1920fcdb1..449ca163b 100644 --- a/src/vio/makefile +++ b/src/vio/makefile @@ -22,3 +22,6 @@ timestamp.o # Append module objects to global tree OBJECTS+=$(addprefix obj/vio/,$(vio_objects)) + +# Add specific options for qvoronoi +obj/vio/nullify%.o : OPTIONS = -w diff --git a/src/vio/match.cpp b/src/vio/match.cpp index 3e077f302..3a9e81939 100644 --- a/src/vio/match.cpp +++ b/src/vio/match.cpp @@ -41,6 +41,10 @@ #include "spintransport.hpp" #include "unitcell.hpp" #include "micromagnetic.hpp" +#include "sld.hpp" +#include "spininitialize.hpp" +#include "spinwaves.hpp" // JRH + // vio module headers #include "internal.hpp" #include "../create/internal.hpp" @@ -92,6 +96,9 @@ namespace vin{ else if(micromagnetic::match_input_parameter(key, word, value, unit, line)) return EXIT_SUCCESS; else if(environment::match_input_parameter(key, word, value, unit, line)) return EXIT_SUCCESS; else if(hamr::match_input_parameter(key, word, value, unit, line)) return EXIT_SUCCESS; + else if(sld::match_input_parameter(key, word, value, unit, line)) return EXIT_SUCCESS; + else if(spininitialize::match_input_parameter(key, word, value, unit, line)) return EXIT_SUCCESS; + else if(spinwaves::match_input_parameter(key, word, value, unit, line)) return EXIT_SUCCESS; // JRH spinwaves input parameters //=================================================================== // Test for create variables //=================================================================== @@ -974,6 +981,7 @@ namespace vin{ double H=atof(value.c_str()); check_for_valid_value(H, word, line, prefix, unit, "field", -1.e4, 1.0e4,"input","+/- 10,000 T"); sim::fmr_field_strength=H; + sim::hamiltonian_simulation_flags[5]=1; // enable fmr fields return EXIT_SUCCESS; } //-------------------------------------------------------------------- @@ -982,6 +990,7 @@ namespace vin{ double w = atof(value.c_str()); check_for_valid_value(w, word, line, prefix, unit, "frequency", 0.0, 1.0e14,"input","0 - 10,000 GHz"); sim::fmr_field_frequency = w; + sim::hamiltonian_simulation_flags[5]=1; // enable fmr fields return EXIT_SUCCESS; } //-------------------------------------------------------------------- @@ -1453,11 +1462,143 @@ namespace vin{ output_list.push_back(72); return EXIT_SUCCESS; } + //-------------------------------------------------------------------- + test="spin-temperature"; + if(word==test){ + stats::calculate_system_spin_temp = true; + output_list.push_back(74); + return EXIT_SUCCESS; + + } + //-------------------------------------------------------------------- + test="mean-spin-temperature"; + if(word==test){ + stats::calculate_system_spin_temp = true; + output_list.push_back(75); + return EXIT_SUCCESS; + } + //-------------------------------------------------------------------- + test="material-spin-temperature"; + if(word==test){ + stats::calculate_material_spin_temp = true; + output_list.push_back(77); + return EXIT_SUCCESS; + } + //-------------------------------------------------------------------- + test="material-mean-spin-temperature"; + if(word==test){ + stats::calculate_material_spin_temp = true; + output_list.push_back(76); + return EXIT_SUCCESS; + } + //-------------------------------------------------------------------- + test="mean-spin-length"; + if(word==test){ + // Set flags for calculation of spin length + stats::calculate_system_spin_length=true; + output_list.push_back(78); + return EXIT_SUCCESS; + } + //-------------------------------------------------------------------- + test="material-mean-spin-length"; + if(word==test){ + // Set flags for calculation of spin length + stats::calculate_material_spin_length=true; + output_list.push_back(79); + return EXIT_SUCCESS; + } + //-------------------------------------------------------------------- + test="mean-height-spin-length"; + if(word==test){ + // Set flags for calculation of spin length + stats::calculate_height_spin_length=true; + output_list.push_back(80); + return EXIT_SUCCESS; + } + //-------------------------------------------------------------------- + test="potential-energy"; + if(word==test){ + stats::calculate_system_sld_energy = true; + output_list.push_back(81); + return EXIT_SUCCESS; + } + //-------------------------------------------------------------------- + test="kinetic-energy"; + if(word==test){ + stats::calculate_system_sld_energy = true; + output_list.push_back(82); + return EXIT_SUCCESS; + } + //-------------------------------------------------------------------- + test="sld-exchange-energy"; + if(word==test){ + stats::calculate_system_sld_energy = true; + output_list.push_back(83); + return EXIT_SUCCESS; + } + //-------------------------------------------------------------------- + test="sld-coupling-energy"; + if(word==test){ + stats::calculate_system_sld_energy = true; + output_list.push_back(84); + return EXIT_SUCCESS; + } + //-------------------------------------------------------------------- + test="sld-total-energy"; + if(word==test){ + stats::calculate_system_sld_energy = true; + output_list.push_back(85); + return EXIT_SUCCESS; + } + + //-------------------------------------------------------------------- + test="mean-coupling-field-vec"; + if(word==test){ + output_list.push_back(91); + return EXIT_SUCCESS; + } + + //-------------------------------------------------------------------- + //test="sld-total-spin-energy"; + //if(word==test){ + // stats::calculate_system_sld_energy = true; + // output_list.push_back(86); + // return EXIT_SUCCESS; + //} + //-------------------------------------------------------------------- + //lattice temperature statistics + //-------------------------------------------------------------------- + test="lattice-temperature"; + if(word==test){ + stats::calculate_system_lattice_temp = true; + output_list.push_back(87); + return EXIT_SUCCESS; + } + test="mean-lattice-temperature"; + if(word==test){ + stats::calculate_system_lattice_temp = true; + output_list.push_back(88); + return EXIT_SUCCESS; + } + //-------------------------------------------------------------------- + test="material-lattice-temperature"; + if(word==test){ + stats::calculate_material_lattice_temp = true; + output_list.push_back(89); + return EXIT_SUCCESS; + } + //-------------------------------------------------------------------- + test="material-mean-lattice-temperature"; + if(word==test){ + stats::calculate_material_lattice_temp = true; + output_list.push_back(90); + return EXIT_SUCCESS; + } //-------------------------------------------------------------------- test="gnuplot-array-format"; if(word==test){ - vout::gnuplot_array_format=true; - return EXIT_SUCCESS; + vout::gnuplot_array_format=true; + return EXIT_SUCCESS; } //-------------------------------------------------------------------- test="output-rate"; @@ -1735,6 +1876,15 @@ namespace vin{ } //------------------------------------------------------------ else + test="equilibration-damping-constant"; + if(word==test){ + double damping=atof(value.c_str()); + check_for_valid_positive_value(damping, word, line, prefix, unit, "none", 0.0, 10.0,"material","0.0 - 10.0"); + read_material[super_index].alpha_eq=damping; + return EXIT_SUCCESS; + } + //------------------------------------------------------------ + else test="atomic-spin-moment"; if(word==test){ double mu_s=atof(value.c_str()); @@ -1755,36 +1905,6 @@ namespace vin{ } //------------------------------------------------------------ else - test="initial-spin-direction"; - if(word==test){ - // first test for random spins - test="random"; - if(value==test){ - read_material[super_index].random_spins=true; - } - else{ - // temporary storage container - std::vector u(3); - - // read values from string - u=doubles_from_string(value); - - // check for sane input and normalise if necessary - check_for_valid_unit_vector(u, word, line, prefix, "material"); - - // Copy sanitised unit vector to material - read_material[super_index].initial_spin[0]=u.at(0); - read_material[super_index].initial_spin[1]=u.at(1); - read_material[super_index].initial_spin[2]=u.at(2); - - // ensure random spins is unset - read_material[super_index].random_spins=false; - } - // return - return EXIT_SUCCESS; - } - //------------------------------------------------------------ - else test="material-element"; if(word==test){ // Test for 3 characters @@ -2289,6 +2409,8 @@ namespace vin{ else if(unitcell::match_material_parameter(word, value, unit, line, super_index, sub_index)) return EXIT_SUCCESS; else if(micromagnetic::match_material_parameter(word, value, unit, line, super_index, sub_index)) return EXIT_SUCCESS; else if(environment::match_material_parameter(word, value, unit, line, super_index, sub_index)) return EXIT_SUCCESS; + else if(sld::match_material_parameter(word, value, unit, line, super_index, sub_index)) return EXIT_SUCCESS; + else if(spininitialize::match_material_parameter(word, value, unit, line, super_index, sub_index)) return EXIT_SUCCESS; //-------------------------------------------------------------------- // keyword not found diff --git a/src/vio/match_grain_list.cpp b/src/vio/match_grain_list.cpp index 48fe25978..b0ae71e8a 100644 --- a/src/vio/match_grain_list.cpp +++ b/src/vio/match_grain_list.cpp @@ -119,6 +119,8 @@ namespace vout{ grain::output_list.push_back(grain::material_magnetisation); return EXIT_SUCCESS; } + + //-------------------------------------------------------------------- test="material-height-magnetisation"; if(word==test){ diff --git a/src/vio/outputfunctions.cpp b/src/vio/outputfunctions.cpp index 85b6b64e2..85671e068 100644 --- a/src/vio/outputfunctions.cpp +++ b/src/vio/outputfunctions.cpp @@ -22,7 +22,9 @@ #include "sim.hpp" #include "micromagnetic.hpp" #include "spintransport.hpp" - +#include "sld.hpp" +#include "atoms.hpp" +#include "../spinlattice/internal.hpp" // vio module headers #include "internal.hpp" @@ -39,6 +41,40 @@ namespace vout{ } return result.str(); } + + // New Output Function for Mean Perpendicular Coupling Field + + void mean_coupling_field_vec(std::ostream& stream, bool header){ + + if(header){ + stream << generic_output_double("Mean_Hc_x(T)", 0.0, header); + stream << generic_output_double("Mean_Hc_y(T)", 0.0, header); + stream << generic_output_double("Mean_Hc_z(T)", 0.0, header); + return; + } + + double sum_hx = 0.0, sum_hy = 0.0, sum_hz = 0.0; + for (int i = 0; i < atoms::num_atoms; ++i) { + sum_hx += sld::internal::coupling_field_x[i]; + sum_hy += sld::internal::coupling_field_y[i]; + sum_hz += sld::internal::coupling_field_z[i]; + } + + double mean_hx = 0.0, mean_hy = 0.0, mean_hz = 0.0; + if (atoms::num_atoms > 0) { + double n_atoms_double = static_cast(atoms::num_atoms); + mean_hx = sum_hx / n_atoms_double; + mean_hy = sum_hy / n_atoms_double; + mean_hz = sum_hz / n_atoms_double; + } + + stream << generic_output_double("", mean_hx, header); + stream << generic_output_double("", mean_hy, header); + stream << generic_output_double("", mean_hz, header); + } + + + //-------------------------------------------------------------------------- // Function to format a standard double variable including a file header //-------------------------------------------------------------------------- @@ -480,4 +516,89 @@ namespace vout{ stream << generic_output_double("frac_voltage", program::fractional_electric_field_strength, header); } + // Output Function 74 - with Header + void sysspintemp(std::ostream& stream, bool header){ + stream << stats::system_spin_temp.output_spin_temp(header); + } + + // Output Function 75 - with Header + void mean_sysspintemp(std::ostream& stream, bool header){ + stream << stats::system_spin_temp.output_mean_spin_temp(header); + } + + // Output Function 76 + void material_mean_sysspintemp(std::ostream& stream, bool header){ + stream << stats::material_spin_temp.output_mean_spin_temp(header); + } + + // Output Function 77 + void material_spin_temp(std::ostream& stream, bool header){ + stream << stats::material_spin_temp.output_spin_temp(header); + } + + // Output Function 78 - with Header + void mean_system_spin_length(std::ostream& stream, bool header){ + stream << stats::system_spin_length.output_mean_spin_length(header); + } + + // Output Function 79 - with Header + void mean_material_spin_length(std::ostream& stream, bool header){ + stream << stats::material_spin_length.output_mean_spin_length(header); + } + + // Output Function 80 - with Header + void mean_height_spin_length(std::ostream& stream, bool header){ + stream << stats::height_spin_length.output_mean_spin_length(header); + } + + // Output Function 81 + void potential_energy(std::ostream& stream, bool header){ + stream << stats::system_sld_energy.output_sld_energy(stats::potential, header); + } + + // Output Function 82 + void kinetic_energy(std::ostream& stream, bool header){ + stream << stats::system_sld_energy.output_sld_energy(stats::kinetic, header); + } + + // Output Function 83 + void sld_exchange_energy(std::ostream& stream, bool header){ + stream << stats::system_sld_energy.output_sld_energy(stats::sld_exchange, header); + } + + // Output Function 84 + void sld_coupling_energy(std::ostream& stream, bool header){ + stream << stats::system_sld_energy.output_sld_energy(stats::sld_coupling, header); + } + + // Output Function 85 + void sld_total_energy(std::ostream& stream, bool header){ + stream << stats::system_sld_energy.output_sld_energy(stats::sld_total, header); + } + + // Output Function 86 + //void sld_total_spin_energy(std::ostream& stream, bool header){ + // stream << stats::system_sld_energy.output_sld_energy(stats::sld_spin, header); + //} + + // Output Function 87 - with Header + void syslatticetemp(std::ostream& stream, bool header){ + stream << stats::system_lattice_temp.output_lattice_temp(header); + } + + // Output Function 88 - with Header + void mean_syslatticetemp(std::ostream& stream, bool header){ + stream << stats::system_lattice_temp.output_mean_lattice_temp(header); + } + + // Output Function 89 + void material_lattice_temp(std::ostream& stream, bool header){ + stream << stats::material_lattice_temp.output_lattice_temp(header); + } + + // Output Function 90 + void material_mean_syslatticetemp(std::ostream& stream, bool header){ + stream << stats::material_lattice_temp.output_mean_lattice_temp(header); + } + } diff --git a/src/vio/string_to_x.cpp b/src/vio/string_to_x.cpp index 5f54c6bb8..e290b04b7 100644 --- a/src/vio/string_to_x.cpp +++ b/src/vio/string_to_x.cpp @@ -49,6 +49,25 @@ uint64_t str_to_uint64(std::string input_str){ } +//------------------------------------------------------------ +// Simple function to convert string to int +//------------------------------------------------------------ +int str_to_int(std::string input_str){ + + // intermediate double variable + double value = 0.0; + + // load value into std::sstream for safe type conversion + std::stringstream value_ss(input_str); + + // read value into double + value_ss >> value; + + // return int as cast + return int(value); + +} + //------------------------------------------------------------------------------ // Simple function to convert string to double //------------------------------------------------------------------------------ diff --git a/template_folder/fe.mat b/template_folder/fe.mat new file mode 100644 index 000000000..aa58271ff --- /dev/null +++ b/template_folder/fe.mat @@ -0,0 +1,24 @@ +#=================================================== +# Sample vampire material file V5 +#=================================================== + +#--------------------------------------------------- +# Number of Materials +#--------------------------------------------------- +material:num-materials=1 +#--------------------------------------------------- +# Material 1 Cobalt Generic +#--------------------------------------------------- +material[1]:material-name=Co +material[1]:damping-constant=0.1 +material[1]:atomic-spin-moment=2.22 !muB +material[1]:initial-spin-direction=0,0,1 + +#material[1]:uniaxial-anisotropy-constant=1.0e-24 + +material[1]:mass=5.7915e-3 +material[1]:damping-constant-lattice=0.6 +material[1]:exchange-J0=0.904 !eV +material[1]:harmonic-potential-V0=0.15 !eV +material[1]:coupling-C0=0.5 + diff --git a/template_folder/fe2.mat b/template_folder/fe2.mat new file mode 100644 index 000000000..da8745222 --- /dev/null +++ b/template_folder/fe2.mat @@ -0,0 +1,40 @@ +#=================================================== +# Sample vampire material file V5 +#=================================================== + +#--------------------------------------------------- +# Number of Materials +#--------------------------------------------------- +material:num-materials=1 +#--------------------------------------------------- +# Material 1 Cobalt Generic +#--------------------------------------------------- +material[1]:material-name=Co +material[1]:damping-constant=0.1 +material[1]:equilibration-damping-constant=0.1 +material[1]:atomic-spin-moment=2.22 !muB +material[1]:initial-spin-direction=0,0,1 +#material[1]:uniaxial-anisotropy-constant=1.0e-24 +material[1]:mass=5.7915e-3 +material[1]:damping-constant-lattice=0.6 +material[1]:equilibration-damping-constant-lattice=0.6 + +material[1]:exchange-J0=0.904 !eV +material[1]:harmonic-potential-V0=0.15 !eV +material[1]:coupling-C0=0.5 +material[1]:minimum-height=0.0 +material[1]:maximum-height=1.0 + + +#material[2]:material-name=Co +#material[2]:damping-constant=0.01 +#material[2]:atomic-spin-moment=2.22 !muB +#material[2]:initial-spin-direction=0,0,1 +#material[2]:uniaxial-anisotropy-constant=1.0e-24 +#material[2]:mass=5.7915e-3 +#material[2]:damping-constant-lattice=0.6 +#material[2]:exchange-J0=0.000001 !eV +#material[2]:harmonic-potential-V0=0.1 !eV +#material[2]:coupling-C0=0.0 +#material[2]:minimum-height=0.5 +#material[2]:maximum-height=1.0 diff --git a/template_folder/input b/template_folder/input new file mode 100644 index 000000000..003a0d2d6 --- /dev/null +++ b/template_folder/input @@ -0,0 +1,88 @@ +#------------------------------------------ +# Sample vampire input file to perform +# benchmark calculation for v4.0 +# +#------------------------------------------ +create:crystal-structure=bcc +#create:periodic-boundaries-x +#create:periodic-boundaries-y +#create:periodic-boundaries-z + + +#------------------------------------------ +# Creation attributes: +#------------------------------------------ +dimensions:unit-cell-size = 2.87 !A +dimensions:system-size-x = 2.87 !nm +dimensions:system-size-y = 2.87 !nm +dimensions:system-size-z = 2.87 !nm + + + +#------------------------------------------ +# Material Files: +#------------------------------------------ +material:file = fe.mat + +#------------------------------------------ +# Simulation attributes: +#------------------------------------------ +sim:temperature = 300.0 +sim:time-steps-increment = 1 +sim:total-time-steps = 2000 +sim:time-step = 0.5 !fs +sim:equilibration-temperature=300.0 +sim:equilibration-time-steps=0 +#sim:save-checkpoint=end +sim:load-checkpoint=continue + +#------------------------------------------ +# Program and integrator details +#------------------------------------------ +sim:program = time-series +sim:integrator = spin-lattice +#sim:applied-field-strength=5 !T +#sim:applied-field-unit-vector=0,0,1 + +spin-lattice:potential-cutoff-range=7.8 !A +spin-lattice:fields-cutoff-range=3.75!A +spin-lattice:coupling=pseudodipolar +spin-lattice:potential=harmonic + + +#spin-lattice:initial-random-displacement=0.1 +#spin-lattice:initial-thermal-velocity=100 + +###exchange int range is in units of nearest neigh distances +exchange:interaction-range = 3 + +config:atoms +config:atoms-output-rate=1000 +config:sld + +output:real-time +output:magnetisation +#output:material-magnetisation + +output:spin-temperature +#output:material-spin-temperature + +output:lattice-temperature +#output:material-lattice-temperature + +#output:mean-spin-temperature + +output:output-rate=1 +output:precision=15 + +screen:real-time +screen:time-steps +screen:magnetisation +#screen:material-magnetisation + +screen:spin-temperature +#screen:material-spin-temperature + +screen:lattice-temperature +#screen:material-lattice-temperature + diff --git a/test/integration/src/integrator.cpp b/test/integration/src/integrator.cpp index 67fd4f3d8..b212eb0f9 100644 --- a/test/integration/src/integrator.cpp +++ b/test/integration/src/integrator.cpp @@ -47,7 +47,7 @@ bool integrator_test(const std::string dir, double rx, double ry, double rz, con ifile.open("output"); // read value after header - for(int i=0; i<982; i++) getline(ifile, line); + for(int i=0; i<983; i++) getline(ifile, line); std::stringstream liness(line); double v1 = 0.0; double vx = 0.0; diff --git a/test/unit/makefile b/test/unit/makefile index d9bcf05ec..09b9e3185 100644 --- a/test/unit/makefile +++ b/test/unit/makefile @@ -11,22 +11,36 @@ GCC=g++ LIBS=-lstdc++ # Flags -GCC_CFLAGS=-O3 -std=c++17 -I../../hdr/ +GCC_CFLAGS=-O3 -std=c++17 -I../../hdr/ -I../../src/ # Objects TEST_OBJECTS= \ obj/unit_tests.o \ obj/utility/units_test.o \ -obj/utility/utility_test.o +obj/utility/utility_test.o\ +obj/utility/spin_temperature_test.o \ +obj/utility/vmath_test.o \ +obj/create/create_tests.o \ +obj/create/voronoi_bimodal_seeds_test.o + VAMPIRE_OBJECTS= \ ../../obj/main/githash.o \ ../../obj/main/version.o \ +../../obj/main/material.o \ ../../obj/utility/errors.o \ ../../obj/utility/units.o \ +../../obj/utility/vmath.o \ ../../obj/vio/data.o \ ../../obj/vio/globalio.o \ -../../obj/vio/timestamp.o +../../obj/vio/timestamp.o\ +../../obj/constants/constants.o\ +../../obj/spinlattice/temperatures.o\ +../../obj/spinlattice/data.o\ +../../obj/random/mtrand.o\ +../../obj/random/random.o\ +../../obj/create/voronoi_bimodal_seeds.o + EXECUTABLE=unit_tests diff --git a/test/unit/src/create/create_tests.cpp b/test/unit/src/create/create_tests.cpp new file mode 100644 index 000000000..cf3f20847 --- /dev/null +++ b/test/unit/src/create/create_tests.cpp @@ -0,0 +1,41 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include + +// include header for test functions +#include "create_tests.hpp" +#include "voronoi_bimodal_seeds_test.hpp" + +namespace ut{ +//------------------------------------------------------------------------------ +// Function to test create module functions +//------------------------------------------------------------------------------ +int create_tests(const bool verbose){ + + if(verbose) std::cout << "Testing create module" << std::endl; + + int error_count = 0; + + error_count += ut::create::test_voronoi_bimodal_seeds(verbose); + + if(verbose) std::cout << "================================" << std::endl; + if(error_count == 0) std::cout << " create : PASS " << std::endl; + else std::cout << " create : FAIL " << error_count << std::endl; + if(verbose) std::cout << "================================" << std::endl; + + return error_count; + +} + +} diff --git a/test/unit/src/create/create_tests.hpp b/test/unit/src/create/create_tests.hpp new file mode 100644 index 000000000..d663a7c05 --- /dev/null +++ b/test/unit/src/create/create_tests.hpp @@ -0,0 +1,6 @@ +namespace ut{ + + // module level functions + int create_tests(const bool verbose); + +} diff --git a/test/unit/src/create/voronoi_bimodal_seeds_test.cpp b/test/unit/src/create/voronoi_bimodal_seeds_test.cpp new file mode 100644 index 000000000..19d618319 --- /dev/null +++ b/test/unit/src/create/voronoi_bimodal_seeds_test.cpp @@ -0,0 +1,241 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include + +// Vampire headers +#include "create/internal.hpp" +#include "random.hpp" + +// include header for test functions +#include "voronoi_bimodal_seeds_test.hpp" + +namespace ut{ + namespace create{ + +//------------------------------------------------------------------------------ +// No two placed seeds should ever be closer than the sum of the smallest +// radius each population's lognormal draw can produce after clamping (see +// generate_bimodal_voronoi_seeds(), r_min_clamp = 0.1*mean_diameter). This is +// a weaker, but always-valid, lower bound on the true per-seed exclusion +// distance, since individual drawn radii are not exposed by the function. +//------------------------------------------------------------------------------ +int test_no_collisions(const bool verbose){ + + int ec = 0; + + const double std_diameter = 50.0; // Angstroms + const double small_diameter = 20.0; // Angstroms + const double variance = 0.15; + + const double min_r_std = 0.1*std_diameter; + const double min_r_small = 0.1*small_diameter; + + const struct { double domain; double fraction; } cases[] = { + { 200.0, 0.1 }, { 200.0, 0.3 }, { 200.0, 0.5 }, { 300.0, 0.9 } + }; + + for(size_t c=0; c > grain_coord_array; + std::vector is_small_grain; + + ::create::internal::generate_bimodal_voronoi_seeds( + grain_coord_array, is_small_grain, + cases[c].domain, cases[c].domain, + std_diameter, variance, small_diameter, variance, cases[c].fraction); + + const size_t n = grain_coord_array.size(); + for(size_t i=0; i > grain_coord_array; + std::vector is_small_grain; + + ::create::internal::generate_bimodal_voronoi_seeds( + grain_coord_array, is_small_grain, 400.0, 400.0, 50.0, 0.15, 20.0, 0.15, fractions[i]); + + int placed_small = 0; + for(size_t g=0; g only standard seeds + { + mtrandom::grnd.seed(1); + + std::vector > grain_coord_array; + std::vector is_small_grain; + + ::create::internal::generate_bimodal_voronoi_seeds( + grain_coord_array, is_small_grain, 200.0, 200.0, 50.0, 0.15, 20.0, 0.15, 0.0); + + if(grain_coord_array.empty()){ + if(verbose) std::cout << "FAIL: small_fraction=0.0 placed no grains at all" << std::endl; + ec++; + } + for(size_t g=0; g > grain_coord_array; + std::vector is_small_grain; + + ::create::internal::generate_bimodal_voronoi_seeds( + grain_coord_array, is_small_grain, 0.0, 0.0, 50.0, 0.15, 20.0, 0.15, 0.3); + + // function returning at all (rather than hanging) is the test; a + // degenerate domain should still place at least the first seed + if(grain_coord_array.empty()){ + if(verbose) std::cout << "FAIL: degenerate zero-size domain placed no seeds at all" << std::endl; + ec++; + } + } + + return ec; +} + +//------------------------------------------------------------------------------ +// Function to test create module functions +//------------------------------------------------------------------------------ +int test_voronoi_bimodal_seeds(const bool verbose){ + + if(verbose) std::cout << "Testing create::internal::generate_bimodal_voronoi_seeds()" << std::endl; + + int ec = 0; + + ec += test_no_collisions(verbose); + ec += test_determinism(verbose); + ec += test_both_populations_present(verbose); + ec += test_degenerate_cases(verbose); + + return ec; + +} + +} +} diff --git a/test/unit/src/create/voronoi_bimodal_seeds_test.hpp b/test/unit/src/create/voronoi_bimodal_seeds_test.hpp new file mode 100644 index 000000000..11cce9741 --- /dev/null +++ b/test/unit/src/create/voronoi_bimodal_seeds_test.hpp @@ -0,0 +1,10 @@ +namespace ut{ + namespace create{ + +//------------------------------------------------------------------------------ +// Function to test create::internal::generate_bimodal_voronoi_seeds() +//------------------------------------------------------------------------------ +int test_voronoi_bimodal_seeds(const bool verbose); + +} +} diff --git a/test/unit/src/unit_tests.cpp b/test/unit/src/unit_tests.cpp index 1a91aa288..1a814594c 100644 --- a/test/unit/src/unit_tests.cpp +++ b/test/unit/src/unit_tests.cpp @@ -42,6 +42,7 @@ int main(int argc, char* argv[]){ std::cout << "--------------------------------------------------" << std::endl; if( module.utility || all ) error_count += ut::utility_tests(verbose); + if( module.create || all ) error_count += ut::create_tests(verbose); // Summary diff --git a/test/unit/src/unit_tests.hpp b/test/unit/src/unit_tests.hpp index 5b1cb3f2d..68daa314f 100644 --- a/test/unit/src/unit_tests.hpp +++ b/test/unit/src/unit_tests.hpp @@ -20,9 +20,11 @@ namespace ut{ // simple struct specifying modules to test struct module_t { bool utility = false; + bool create = false; }; // module level functions int utility_tests(const bool verbose); + int create_tests(const bool verbose); } diff --git a/test/unit/src/utility/spin_temperature_test.cpp b/test/unit/src/utility/spin_temperature_test.cpp new file mode 100644 index 000000000..0b783af61 --- /dev/null +++ b/test/unit/src/utility/spin_temperature_test.cpp @@ -0,0 +1,124 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2022. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include + +// include header for test functions +#include "sld.hpp" +#include "spinlattice/internal.hpp" + +#include "constants.hpp" +#include "material.hpp" + +namespace vmpi{ + extern int my_rank; +} + +namespace ut{ + +int floaterror(const double value, const double expected_value, const double precision, const std::string function); +/* double error = 0.0; + + // check for division by zero + if(fabs(expected_value) > 0.0) error = value / expected_value; + + // check for both zero + if(value == expected_value) return 0; // exact floating point comparison + + if(fabs(1.0-error) < precision) return 0; + else{ + std::cout << "FAIL: Floating point error in test of function " << function << ": value " << value << " should be the same as " << expected_value << " to precision " << precision << std::endl; + return 1; + } +}*/ + + + + namespace utility{ + /* + compute_spin_temperature(const int start_index, // first atom for exchange interactions to be calculated + const int end_index, + const std::vector& type_array, // type for atom + std::vector& x_spin_array, // coord vectors for atoms + std::vector& y_spin_array, + std::vector& z_spin_array, + std::vector& fields_array_x, // vectors for fields + std::vector& fields_array_y, + std::vector& fields_array_z)*/ + // int convert(std::string input_unit, double& value, std::string& type) + int spin_temperature_test(const int test_start_index, const int test_end_index, const std::vector& test_type_array, std::vector& test_x_spin_array, std::vector& test_y_spin_array, std::vector& test_z_spin_array, std::vector& test_fields_array_x, std::vector& test_fields_array_y, std::vector& test_fields_array_z, std::vector& test_mu_s_array, const double expected_value, const double precision){ + + int ec = 0; // error count increment + + // call function to be tested + //convert is from vampire + double test_value=sld::compute_spin_temperature(test_start_index, test_end_index, test_type_array, test_x_spin_array, test_y_spin_array, test_z_spin_array, test_fields_array_x, test_fields_array_y, test_fields_array_z, test_mu_s_array); + // check for numerical error + ec += ut::floaterror(test_value, expected_value, precision, "sld::compute_spin_temperature"); + + return ec; + } + +//------------------------------------------------------------------------------ +// Function to test utility module functions +//------------------------------------------------------------------------------ +int test_spin_temperature(const bool verbose){ + + //extern int convert(std::string input_unit, double& value, std::string& type); + //extern void convert(std::string input_unit, std::vector& value, std::string& type); + + // specify numerical precision required for all tests + const double precision = 0.00001; + const double expected_value=0.0; + + + int ec = 0; // error counter + + //----------------------------------------------------- + // Testing units::convert(std::string input_unit, double& value, std::string& type); + //----------------------------------------------------- + //int spin_temperature_test(const std::int test_start_index, const std::int test_start_index, const std::int test_end_index, const std::vector& test_type_array, std::vector& test_x_spin_array, std::vector& test_y_spin_array, std::vector& test_z_spin_array, std::vector& test_fields_array_x, std::vector& test_fields_array_y, std::vector& test_fields_array_z, const double expected_value, const double precision){ + std::vector x_spin_array; + x_spin_array.resize(2,0); + + std::vector y_spin_array; + y_spin_array.resize(2,0); + + std::vector z_spin_array; + z_spin_array.resize(2,1); + + std::vector fields_array_x; + fields_array_x.resize(2,0); + + std::vector fields_array_y; + fields_array_y.resize(2,0); + + std::vector fields_array_z; + fields_array_z.resize(2,100); + + std::vector type_array; + type_array.resize(2,0); + + std::vector mu_s_array; + mu_s_array.resize(2,2.22); + + ec += spin_temperature_test(0,2,type_array,x_spin_array,y_spin_array,z_spin_array,fields_array_x,fields_array_y,fields_array_z, mu_s_array, expected_value,precision); + + return ec; + +} + +} +} diff --git a/test/unit/src/utility/units_test.cpp b/test/unit/src/utility/units_test.cpp index 4cefe9b5e..b927b5825 100644 --- a/test/unit/src/utility/units_test.cpp +++ b/test/unit/src/utility/units_test.cpp @@ -56,6 +56,7 @@ int stringerror(const std::string value, const std::string expected_value, const int ec = 0; // error count increment // call function to be tested + //convert is from vampire units::convert(test_unit, test_value, test_unit_type); // check for numerical error @@ -85,7 +86,7 @@ int test_units(const bool verbose){ //----------------------------------------------------------------------------------------- ec += convert_test("meV", 1.0, 1.602176634e-22, "","energy",precision); ec += convert_test("T", 1.0, 1.0, "","field",precision); - ec += convert_test("A/m", 1.0, 1.0, "","magnetisation",precision); + ec += convert_test("A/m", 1.0, 1e-30, "","magnetisation",precision); ec += convert_test("", 1.0, 1.0, "","none",precision); // test no unit diff --git a/test/unit/src/utility/utility_test.cpp b/test/unit/src/utility/utility_test.cpp index 5fe1eb9c8..a8c228adc 100644 --- a/test/unit/src/utility/utility_test.cpp +++ b/test/unit/src/utility/utility_test.cpp @@ -27,6 +27,7 @@ int utility_tests(const bool verbose){ int error_count = 0; error_count += ut::utility::test_units(verbose); + error_count += ut::utility::test_vmath(verbose); if(verbose) std::cout << "================================" << std::endl; if(error_count == 0) std::cout << " utility : PASS " << std::endl; diff --git a/test/unit/src/utility/utility_test.hpp b/test/unit/src/utility/utility_test.hpp index f6f0e0ad4..ff60ed8ff 100644 --- a/test/unit/src/utility/utility_test.hpp +++ b/test/unit/src/utility/utility_test.hpp @@ -5,6 +5,7 @@ namespace ut{ // Function to test utility module functions //------------------------------------------------------------------------------ int test_units(const bool verbose); +int test_vmath(const bool verbose); } } diff --git a/test/unit/src/utility/vmath_test.cpp b/test/unit/src/utility/vmath_test.cpp new file mode 100644 index 000000000..0ed9b95e5 --- /dev/null +++ b/test/unit/src/utility/vmath_test.cpp @@ -0,0 +1,108 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) R F L Evans 2026. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include + +// include header for test functions +#include "vmath.hpp" +#include "vmath_test.hpp" + +namespace ut{ + + // shared floating point comparison helper defined in units_test.cpp + int floaterror(const double value, const double expected_value, const double precision, const std::string function); + + namespace utility{ + +int test_vmath(const bool verbose){ + + if(verbose) std::cout << "Testing vmath module" << std::endl; + + int ec = 0; + + //----------------------------------------------------------------------------------------- + // diamond of half-width 10, centred on the origin + //----------------------------------------------------------------------------------------- + double polyX[4] = { 10.0, 0.0, -10.0, 0.0 }; + double polyY[4] = { 0.0, 10.0, 0.0, -10.0 }; + + //----------------------------------------------------------------------------------------- + // unit factor must reproduce plain point_in_polygon() for a set of test points + //----------------------------------------------------------------------------------------- + const double test_x[] = { 0.0, 5.0, -5.0, 3.0, 9.9, 11.0, -9.9, 0.0 }; + const double test_y[] = { 0.0, 0.0, 0.0, 4.0, 0.0, 0.0, 0.0, 9.9 }; + for(int i=0; i<8; i++){ + const bool expected = vmath::point_in_polygon(test_x[i], test_y[i], polyX, polyY, 4); + const bool actual = vmath::point_in_polygon_scaled(test_x[i], test_y[i], 1.0, polyX, polyY, 4); + if(expected != actual){ + std::cout << "FAIL: point_in_polygon_scaled(factor=1.0) disagrees with point_in_polygon at (" + << test_x[i] << "," << test_y[i] << ")" << std::endl; + ec++; + } + } + + //----------------------------------------------------------------------------------------- + // linear (not quadratic) scaling: half-width must scale exactly as f, not f^2 + //----------------------------------------------------------------------------------------- + const double factors[] = { 1.0, 0.75, 0.5, 0.25 }; + const double eps = 1.0e-6; + for(int i=0; i<4; i++){ + const double f = factors[i]; + const double expected_half_width = 10.0*f; + + // just inside the scaled diamond along +x + const bool inside = vmath::point_in_polygon_scaled(expected_half_width - eps, 0.0, f, polyX, polyY, 4); + // just outside the scaled diamond along +x + const bool outside = vmath::point_in_polygon_scaled(expected_half_width + eps, 0.0, f, polyX, polyY, 4); + + if(!inside){ + std::cout << "FAIL: point_in_polygon_scaled(factor=" << f << ") excludes point just inside the expected boundary" << std::endl; + ec++; + } + if(outside){ + std::cout << "FAIL: point_in_polygon_scaled(factor=" << f << ") includes point just outside the expected boundary" << std::endl; + ec++; + } + } + + //----------------------------------------------------------------------------------------- + // degenerate and inverted scale factors contain nothing + //----------------------------------------------------------------------------------------- + if(vmath::point_in_polygon_scaled(0.0, 0.0, 0.0, polyX, polyY, 4) != false){ + std::cout << "FAIL: point_in_polygon_scaled(factor=0.0) should contain no points" << std::endl; + ec++; + } + if(vmath::point_in_polygon_scaled(0.0, 0.0, -1.0, polyX, polyY, 4) != false){ + std::cout << "FAIL: point_in_polygon_scaled(factor<0.0) should contain no points" << std::endl; + ec++; + } + + //----------------------------------------------------------------------------------------- + // repeated evaluation of a point on the scaled boundary must be deterministic + //----------------------------------------------------------------------------------------- + const bool boundary_first = vmath::point_in_polygon_scaled(5.0, 5.0, 0.5, polyX, polyY, 4); + const bool boundary_second = vmath::point_in_polygon_scaled(5.0, 5.0, 0.5, polyX, polyY, 4); + if(boundary_first != boundary_second){ + std::cout << "FAIL: point_in_polygon_scaled is non-deterministic for a point on the scaled boundary" << std::endl; + ec++; + } + + if(verbose && ec == 0) std::cout << " vmath tests passed" << std::endl; + + return ec; + +} + +} +} diff --git a/test/unit/src/utility/vmath_test.hpp b/test/unit/src/utility/vmath_test.hpp new file mode 100644 index 000000000..da88cdc9b --- /dev/null +++ b/test/unit/src/utility/vmath_test.hpp @@ -0,0 +1,10 @@ +namespace ut{ + namespace utility{ + +//------------------------------------------------------------------------------ +// Function to test vmath module functions +//------------------------------------------------------------------------------ +int test_vmath(const bool verbose); + +} +} diff --git a/util/initialise_new_vampire_module.cpp b/util/initialise_new_vampire_module.cpp index ee648b908..dd22484d4 100644 --- a/util/initialise_new_vampire_module.cpp +++ b/util/initialise_new_vampire_module.cpp @@ -17,6 +17,7 @@ // internal.hpp // Header file listing module shared variables // .hpp // Header file for extenrally visible functions // makefile // Module makefile +// obj/module/ // Folder for objective files // // This program is invoked using: // @@ -34,8 +35,8 @@ #include #include #include -#include "stdlib.h" #include +#include // Forward declaration of functions void process_command_line(int argc, char* argv[], std::string& namespace_name, std::string& author, std::string& email, std::string& year); @@ -47,6 +48,7 @@ void create_initialise(const std::string& file_header, const std::string& namesp void create_internal(const std::string& file_header, const std::string& nn); void create_module(const std::string& file_header, const std::string& nn); void create_makefile(const std::string& file_header, const std::string& nn); +void create_gitignore(const std::string& nn); // global constants const std::string blank = ""; @@ -60,7 +62,7 @@ int main(int argc, char* argv[]){ std::string namespace_name=""; std::string author=""; std::string email=""; - std::string year="2018"; + std::string year="2026"; // determine namespace name, author and email from command line process_command_line(argc, argv, namespace_name, author, email, year); @@ -86,6 +88,9 @@ int main(int argc, char* argv[]){ // Generate makefile create_makefile(file_header, namespace_name); + // Generate .gitgnore file to preserve folder structure + create_gitignore(namespace_name); + return EXIT_SUCCESS; } @@ -576,3 +581,40 @@ void create_makefile(const std::string& file_header, const std::string& nn){ return; } + +void create_gitignore(const std::string& nn){ + + // specify object file folder + std::stringstream objfolder_ss; + objfolder_ss << "../../obj/" << nn; + std::string objfolder = objfolder_ss.str(); + + // create obj/module folder + std::cout << "Generating obj/" << nn << "/ folder" << std::endl; + std::filesystem::create_directory(objfolder); + + // Check to see if file exists + std::stringstream gifile_ss; + gifile_ss << "../../obj/" << nn << "/.gitignore"; + std::string gifile = gifile_ss.str(); + if(file_exists(gifile)){ + std::cout << ".gitignore already exists - skipping initialisation." << std::endl; + return; + } + + std::cout << "Generating obj/" << nn << "/.gitgnore file" << std::endl; + + // Open file + std::ofstream ofile; + ofile.open(gifile); + + // Write header + ofile << "#--------------------------------------------------------------" << std::endl; + ofile << "# git ignore file for " << nn << " module" << std::endl; + ofile << "#--------------------------------------------------------------\n" << std::endl; + + ofile.close(); + + return; + +} diff --git a/util/povray_colours/color_wheel.inc b/util/povray_colours/color_wheel.inc index 04b3fff95..8562e1ffb 100644 --- a/util/povray_colours/color_wheel.inc +++ b/util/povray_colours/color_wheel.inc @@ -42,13 +42,13 @@ //--------------------- // Determine grading //--------------------- -//#declare factor=pow(cos(phi*pi/180),2); -//#declare factor=pow(cos(phi*pi/180),3); -#declare factor=cos(phi*pi/180); +#declare rfactor=pow(cos(phi*pi/180),2); +#declare rfactor=pow(cos(phi*pi/180),7); +//#declare rfactor=cos(phi*pi/180); #switch(phi) #range(0,90) - #declare factor=cos(phi*pi/180); + #declare factor=rfactor; #switch(rr) #range(0,factor) #declare rr=factor; @@ -66,7 +66,7 @@ #break #range(90,180) - #declare factor=1.+cos(phi*pi/180); + #declare factor=1.+rfactor; //#debug concat("Phi: ",str(phi,5,1),"\n") //#debug concat("factor: ",str(factor,5,1),"\n") #switch(rr) @@ -116,7 +116,17 @@ pigment {color rgb } // Macro for povray spins //------------------------------------------- #macro spinrgb(sx,sy,sz,cr,cg,cb) - #declare theta = acos(sx/(sqrt(sx*sx+sy*sy))); - #declare phi = acos(sz); +//#debug concat("sx : ",str(sx,5,2),"\n") +//#debug concat("sy : ",str(sy,5,2),"\n") +//#debug concat("sz : ",str(sz,5,2),"\n") +#declare delta = 0.01; +#declare sxxx=sx+delta; +#declare syyy=sy; +#declare ang = atan2(syyy,sxxx); +#declare theta=(180.0*(ang+pi)/pi); +#declare phi=180.0*acos(sz)/pi; +//#debug concat("phi : ",str(phi,5,2),"\n") +//#debug concat("theta : ",str(theta,5,2),"\n") + colour_wheel(theta, phi) #end diff --git a/util/scripts/extract_parameters.sh b/util/scripts/extract_parameters.sh new file mode 100644 index 000000000..02f29bae6 --- /dev/null +++ b/util/scripts/extract_parameters.sh @@ -0,0 +1,10 @@ +#!/bin/bash +#--------------------------------------------------------------- +# A simple script to get (most) of the vampire input parameters +#--------------------------------------------------------------- +grep -E 'test=|test =' src/*/interface.cpp src/vio/match.cpp | tr -d '\"; ' | tr '=' ' ' | awk '{print $2}' > input_paramaters.txt +grep -E 'test=|test =' src/*/interface.cpp src/vio/match.cpp | tr -d '\"; ' | tr '=/' ' ' | awk '{print $2}' > module.txt + +grep -A7 -E 'test=|test =' src/*/interface.cpp src/vio/match.cpp | grep -E "test=|test =|double|int|bool|check|string " > extra_info.txt + + diff --git a/util/vdc/atoms.cpp b/util/vdc/atoms.cpp new file mode 100644 index 000000000..8fbd5a08f --- /dev/null +++ b/util/vdc/atoms.cpp @@ -0,0 +1,106 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2017. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include +#include +#include + +// program header +#include "vdc.hpp" + +#ifdef _OPENMP + #include +#else + #define omp_get_thread_num() 0 +#endif + +namespace vdc{ + +// forward function declarations + +//------------------------------------------------------------------------------ +// Function to output crystal.xyz file compatible with rasmol +//------------------------------------------------------------------------------ +void output_atoms_txt_file(){ + + // output informative message to user + if(vdc::verbose) std::cout << "Writing atom data file... " << std::flush; + + // output xyz file + std::ofstream ofile; + ofile.open("atoms.txt"); + + // output number of atoms + ofile << vdc::sliced_atoms_list.size() + vdc::sliced_nm_atoms_list.size() << "\n"; + + #pragma omp parallel + { + + std::stringstream otext; + + // write magnetic atoms to output text stream in parallel + #pragma omp for + for(size_t i=0; i < vdc::sliced_atoms_list.size(); i++){ + + // get atom ID + unsigned int atom = vdc::sliced_atoms_list[i]; + + otext << atom << "\t" << + vdc::coordinates[3*atom + 0] << "\t" << + vdc::coordinates[3*atom + 1] << "\t" << + vdc::coordinates[3*atom + 2] << "\t" << + vdc::type[atom] << "\t" << + vdc::category[atom]; + + if(vdc::grains) otext << "\t" << vdc::grain[atom]; + otext << "\n"; + + } // end of parallel for + + // write non-magnetic atoms + #pragma omp for + for(size_t i=0; i < vdc::sliced_nm_atoms_list.size(); i++){ + + // get atom ID + unsigned int atom = vdc::sliced_nm_atoms_list[i]; + + otext << atom << "\t" << + vdc::nm_coordinates[3*atom + 0] << "\t" << + vdc::nm_coordinates[3*atom + 1] << "\t" << + vdc::nm_coordinates[3*atom + 2] << "\t" << + vdc::nm_type[atom] << "\t" << + vdc::nm_category[atom]; + if(vdc::grains) otext << "\t" << vdc::nm_grain[atom]; + otext << "\n"; + + } // end of parallel for + + // force each thread to write to file in order + #pragma omp critical + ofile << otext.str(); + + } // end of parallel region + + ofile << std::flush; + ofile.close(); + + // output informative message to user + if(vdc::verbose) std::cout << "done!" << std::endl; + + return; + +} + +} diff --git a/util/vdc/command.cpp b/util/vdc/command.cpp index 4a22bacb0..875b246f9 100644 --- a/util/vdc/command.cpp +++ b/util/vdc/command.cpp @@ -63,11 +63,13 @@ void command( int argc, char* argv[] ){ //------------------------------------------------------------------------ // Check for appropriate data outputs //------------------------------------------------------------------------ - if (sw == "--xyz" ){ vdc::xyz = true; } // xyz coordinate file output - else if (sw == "--povray"){ vdc::povray = true; } // pov coordinate file output - else if (sw == "--vtk" ){ vdc::vtk = true; } // vtk coordinate file output - else if (sw == "--text" ){ vdc::txt = true; } // plain text file output - else if (sw == "--cells" ){ // cell raw data + if (sw == "--xyz" ){ vdc::xyz = true; } // xyz coordinate file output + else if (sw == "--povray" ){ vdc::povray = true; } // pov coordinate file output + else if (sw == "--povray-sticks"){ vdc::povsticks = true; } // pov sticks file output + else if (sw == "--vtk" ){ vdc::vtk = true; } // vtk coordinate file output + else if (sw == "--text" ){ vdc::txt = true; } // plain text file output + else if (sw == "--grains" ){ vdc::grains = true; } // plain text file output + else if (sw == "--cells" ){ // cell raw data vdc::cells = true; // calculate cell data vdc::cellsf = true; // output cell data file } @@ -213,6 +215,8 @@ void command( int argc, char* argv[] ){ std::cerr << "Error! No output data formats requested. Available options are: " << std::endl; std::cerr << "\t\t --xyz Data output in .xyz format for viewing in rasmol/jmol" << std::endl; std::cerr << "\t\t --povray Data output in PoVRAY format for rendering" << std::endl; + std::cerr << "\t\t --povray-cells Data output in PoVRAY format for rendering" << std::endl; + std::cerr << "\t\t --povray-grains Data output in PoVRAY format for rendering" << std::endl; std::cerr << "\t\t --vtk Data output in VTK format for viewing in Paraview" << std::endl; std::cerr << "\t\t --text Data output in plain text format for plotting in gnuplot/excel etc" << std::endl; std::cerr << "\t\t --cells Data output in plain text format in cells" << std::endl; diff --git a/util/vdc/coords.cpp b/util/vdc/coords.cpp index 4e60d76ad..4ca319613 100644 --- a/util/vdc/coords.cpp +++ b/util/vdc/coords.cpp @@ -60,9 +60,21 @@ void process_coordinates(){ // Calculate systenm dimensions after slicing vdc::calculate_system_extent(vdc::sliced_atoms_list,vdc::sliced_nm_atoms_list); + // calculate grain properties + if(vdc::grains){ + vdc::load_grain_vertices(); + vdc::determine_atom_grain_id(); + } + + // output atoms text file + if(vdc::txt) output_atoms_txt_file(); + // output xyz file; if(vdc::xyz) output_xyz_file(); + // output sticks povray file + if(vdc::povsticks) output_sticks_file(); + return; } @@ -359,7 +371,7 @@ void calculate_system_extent(std::vector& magnetic_list, std::vector& // Find list of atoms in user defined slice //--------------------------------------------------------------- void slice_system(){ - + // work out borders for slice param for (slice_t &slice : vdc::slices){ @@ -379,7 +391,7 @@ void slice_system(){ slice.bound[3] = (slice.param[3]*vdc::system_size[1])-(vdc::system_size[1]*0.5)+vdc::system_centre[1]; slice.bound[5] = (slice.param[5]*vdc::system_size[2])-(vdc::system_size[2]*0.5)+vdc::system_centre[2]; break; - + case vdc::sphere : slice.bound.resize(3); // a,b,c @@ -388,7 +400,7 @@ void slice_system(){ slice.bound[1] = vdc::system_size[1]*slice.param[1]/2.0; slice.bound[2] = vdc::system_size[2]*slice.param[2]/2.0; break; - + case vdc::cylinder : slice.bound.resize(4); // a,b,zmin,zmax @@ -429,11 +441,11 @@ void slice_system(){ case vdc::box : in_bounds = box_slice(x,y,z,slice.bound); break; - + case vdc::box_void : in_bounds = !box_slice(x,y,z,slice.bound); break; - + case vdc::sphere : in_bounds = sphere_slice(x,y,z,slice.bound); break; @@ -448,7 +460,7 @@ void slice_system(){ } // if the atom is in any slice, add to final list and stop checking others - if (in_bounds){ + if (in_bounds){ vdc::sliced_atoms_list.push_back(atom); break; } @@ -485,11 +497,11 @@ void slice_system(){ case vdc::box : in_bounds = box_slice(x,y,z,slice.bound); break; - + case vdc::box_void : in_bounds = !box_slice(x,y,z,slice.bound); break; - + case vdc::sphere : in_bounds = sphere_slice(x,y,z,slice.bound); break; @@ -504,13 +516,13 @@ void slice_system(){ } // if the atom is in any slice, add to final list and stop checking others - if (in_bounds){ + if (in_bounds){ vdc::sliced_nm_atoms_list.push_back(atom); break; } } } - } + } // output informative message to user if(vdc::verbose) std::cout << "done!" << std::endl; @@ -519,8 +531,7 @@ void slice_system(){ } bool box_slice(const double &x, const double &y, const double &z, const std::vector &bound){ - - return (x >= bound[0] && x <= bound[1]) && (y >= bound[2] && y <= bound[3]) && (z >= bound[4] && z <= bound[5]); + return (x >= bound[0] && x <= bound[1]) && (y >= bound[2] && y <= bound[3]) && (z >= bound[4] && z <= bound[5]); } bool sphere_slice(const double &x, const double &y, const double &z, const std::vector &bound){ diff --git a/util/vdc/data.cpp b/util/vdc/data.cpp index 7141a806f..2ad6cbb84 100644 --- a/util/vdc/data.cpp +++ b/util/vdc/data.cpp @@ -23,8 +23,10 @@ namespace vdc{ // program option flags bool verbose = false; // flag to specify verbosity of output to user bool xyz = false; // flag to specify crystal.xyz file output + bool grains = false; // flag to enable grain calculations bool povray = false; // flag to specify povray file output bool povcells = false; // flag to specify povray cells output + bool povsticks= false; // flag to specify povray sticks output bool cells = false; // flag to specify cells output bool cellsf = false; // flag to output cell file bool vtk = false; // flag to specify vtk output @@ -61,6 +63,7 @@ namespace vdc{ std::vector category(0); std::vector type(0); + std::vector grain(0); std::vector coordinates(0); std::vector spins(0); @@ -88,6 +91,9 @@ namespace vdc{ double camera_zoom = 1.0; std::string background_colour = "Gray30"; + // povray stick settings + double sticks_cutoff = 2.6; // cutoff distance for sticks (Angstroms) + // povray shape sizes std::vector atom_sizes = {1.2}; std::vector arrow_sizes = {2.0}; @@ -96,6 +102,7 @@ namespace vdc{ uint64_t num_nm_atoms = 0; std::vector nm_category(0); std::vector nm_type(0); + std::vector nm_grain(0); std::vector nm_coordinates(0); // cell data @@ -110,6 +117,9 @@ namespace vdc{ std::vector cell_coords; std::vector>> cell_magnetization; + // grain data + std::vector < std::vector > grain_vertices_array; + // array to store subsidiary data file names std::vector coord_filenames(0); std::vector spin_filenames(0); @@ -163,6 +173,8 @@ namespace vdc{ {"camera-look-at" , set_camera_look_at}, {"camera-lookat" , set_camera_look_at}, {"camera-zoom" , set_camera_zoom}, + // povray sticks settings + {"sticks-cutoff" , set_sticks_cutoff}, // povray background colour {"background-colour", set_background_colour}, {"background-color" , set_background_colour}, diff --git a/util/vdc/functions.cpp b/util/vdc/functions.cpp index b96cd50c5..f6ba9c613 100644 --- a/util/vdc/functions.cpp +++ b/util/vdc/functions.cpp @@ -507,6 +507,38 @@ void set_camera_zoom(const input_t &input){ } } +//---------------------------------------------------------------------------------- +// Set povray stick cutoff +//---------------------------------------------------------------------------------- +void set_sticks_cutoff(const input_t &input){ + + // print help message if argument is "-h" + if (input.value[0] == "-h"){ + std::cout << "\"sticks-cutoff\"\tExpects 1 argument: positive real\n\n" + << "Povray sticks cutoff. Sticks connect nearby atoms to better\n" + << "visualise the atomic structure in povray. The cutoff gives\n" + << "the maximum distance in Angstroms between atoms connected\n" + << "sticks. Note that this option is very inefficient for large\n" + << "system sizes." + << "\nExample usage: sticks-cutoff = 3.54\t\n"; + std::exit(EXIT_SUCCESS); + } + + // check args + arg_count(input,1,"eq"); + + // convert to double and store + vdc::sticks_cutoff = std::stod(input.value[0]); + + // check range + if (vdc::sticks_cutoff <= 0.0){ + error_message(input,"sticks-cutoff must be greater than 0.0"); + } + + return; + +} + //---------------------------------------------------------------------------------- // Set povray background colour //---------------------------------------------------------------------------------- diff --git a/util/vdc/grains.cpp b/util/vdc/grains.cpp new file mode 100644 index 000000000..93c9f5fac --- /dev/null +++ b/util/vdc/grains.cpp @@ -0,0 +1,169 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2023. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include +#include +#include +#include +#include +#include + +// program header +#include "vdc.hpp" + +//------------------------------------------------------------------------------ +// Function to decide if test point is within polygon defined by points +//------------------------------------------------------------------------------ +inline bool point_in_polygon(vdc::xy_t test, std::vector& points){ // list of points defining a polygon + + // Add tiny amount to atomic coordinates to include atoms at 0,0,0 + test.x += 1e-10; + test.y += 1e-10; + + const int poly_sides = points.size(); + int j = poly_sides - 1 ; + bool odd_nodes=false; + + for ( int i = 0 ; i < poly_sides; i++) { + if ( ( points[i].y < test.y && points[j].y >= test.y ) || ( points[j].y < test.y && points[i].y >= test.y) ) { + if (points[i].x+(test.y-points[i].y) / ( points[j].y - points[i].y ) * ( points[j].x - points[i].x) < test.x) { + odd_nodes = !odd_nodes; + } + } + j=i; + } + + return odd_nodes; + +} + +namespace vdc{ + + //--------------------------------------------------------------------------- + // Function to load grain shape file from VAMPIRE + //--------------------------------------------------------------------------- + void load_grain_vertices(){ + + //-------------------------------- + // set up input file stream + //-------------------------------- + std::ifstream ifile; + + // open file and check for success + ifile.open("grain_shapes.txt"); + // if failed inform user and exit + if(!ifile.is_open()){ + std::cerr << "Error! grain_shapes.txt file not found required by vampire grain identification. Exiting" << std::endl; + exit(234); + } + + //-------------------------------- + // Load grain vertices + //-------------------------------- + int grain_id = 0; + std::string line; + + while(getline(ifile, line)){ + + // create stringstream from line + std::stringstream line_ss(line); + int num_vertices = 0; + line_ss >> num_vertices; + + // storage for vertices for this grain + std::vector vertices; + + // loop over all vertices and read in xy pairs + for(int v=0; v> vv.x >> vv.y; + vertices.push_back(vv); + } + + // save vertex list to grain vertices array + vdc::grain_vertices_array.push_back(vertices); + + } + + // close input file + ifile.close(); + + return; + + } + + //--------------------------------------------------------------------------- + // Function to determine grain ID of each atom + //--------------------------------------------------------------------------- + void determine_atom_grain_id(){ + + // set last grain number (for atoms outside of grains) + const int last_grain = vdc::grain_vertices_array.size()+1; + + // resize grain ID arrays + vdc::grain.resize( vdc::type.size(), last_grain ); + vdc::nm_grain.resize( vdc::nm_type.size(), last_grain ); + + for(size_t i=0; i < vdc::sliced_atoms_list.size(); i++){ + + // get atom ID and coordinates + const unsigned int atom = vdc::sliced_atoms_list[i]; + const double x = vdc::coordinates[3*atom + 0]; + const double y = vdc::coordinates[3*atom + 1]; + const double z = vdc::coordinates[3*atom + 2]; + const xy_t xy {x, y}; + + // loop over all grains to see if atom is inside + for(int g = 0; g < vdc::grain_vertices_array.size(); g++){ + if(point_in_polygon(xy, vdc::grain_vertices_array[g])){ + vdc::grain[atom] = g; + break; + } + } + + } + + for(size_t i=0; i < vdc::sliced_nm_atoms_list.size(); i++){ + + // get atom ID + unsigned int atom = vdc::sliced_nm_atoms_list[i]; + + const double x = vdc::nm_coordinates[3*atom + 0]; + const double y = vdc::nm_coordinates[3*atom + 1]; + const double z = vdc::nm_coordinates[3*atom + 2]; + const xy_t xy {x, y}; + + // loop over all grains to see if atom is inside + for(int g = 0; g < vdc::grain_vertices_array.size(); g++){ + if(point_in_polygon(xy, vdc::grain_vertices_array[g])){ + vdc::nm_grain[atom] = g; + break; + } + } + + } + + return; + } + + //--------------------------------------------------------------------------- + //--------------------------------------------------------------------------- + void generate_povray_grains(){ + + return; + } + + +} diff --git a/util/vdc/makefile b/util/vdc/makefile index 4806d37a8..ffd2c7204 100644 --- a/util/vdc/makefile +++ b/util/vdc/makefile @@ -16,6 +16,7 @@ GCC_CFLAGS=-O3 -std=c++0x # Objects OBJECTS= \ +obj/atoms.o \ obj/cells.o \ obj/colour.o \ obj/colourmaps.o \ @@ -23,12 +24,14 @@ obj/command.o \ obj/coords.o \ obj/data.o \ obj/functions.o \ +obj/grains.o \ obj/main.o \ obj/non_magnetic.o \ obj/povray.o \ obj/povray-cells.o \ obj/read.o \ obj/spins.o \ +obj/sticks.o \ obj/ssc.o \ obj/text.o \ obj/vtk.o \ diff --git a/util/vdc/povray.cpp b/util/vdc/povray.cpp index e879e51f4..56943788f 100644 --- a/util/vdc/povray.cpp +++ b/util/vdc/povray.cpp @@ -298,6 +298,52 @@ void output_povray_file(){ //std::cout << std::find(remove_materials.begin(), remove_materials.end(), ) << std::endl; } + //----------------------------------------------------- + // optionally output sticks macro if requested in vdc + //----------------------------------------------------- + /*if(vdc::povsticks){ // version with truncated cylinders + pfile << "\n" << std::endl; + pfile << "//----------------------------------------------------------------" << std::endl; + pfile << "// Sticks macro" << std::endl; + pfile << "//----------------------------------------------------------------" << std::endl; + pfile << "#macro stick(sx,sy,sz,ex,ey,ez,r1,r2)" << std::endl; + pfile << " #declare dx = ex-sx;" << std::endl; + pfile << " #declare dy = ey-sy;" << std::endl; + pfile << " #declare dz = ez-sz;" << std::endl; + pfile << " #declare r = sqrt(dx*dx + dy*dy + dz*dz);" << std::endl; + pfile << " #declare xh = 0.5*dx/r;" << std::endl; + pfile << " #declare yh = 0.5*dy/r;" << std::endl; + pfile << " #declare zh = 0.5*dz/r;" << std::endl; + pfile << " difference{" << std::endl; + pfile << " cylinder {" << std::endl; + pfile << " ," << std::endl; + pfile << " , 0.2" << std::endl; + pfile << " }" << std::endl; + pfile << " sphere{, r1*0.5}" << std::endl; + pfile << " sphere{, r2*0.5}" << std::endl; + pfile << " texture { pigment {color rgb < 0.1 0.1 0.1 >} finish {reflection ref diffuse dif ambient amb } }" << std::endl; + pfile << " }" << std::endl; + pfile << "#end\n" << std::endl; + }*/ + + if(vdc::povsticks){ + pfile << "\n" << std::endl; + pfile << "//----------------------------------------------------------------" << std::endl; + pfile << "// Sticks macro" << std::endl; + pfile << "//----------------------------------------------------------------" << std::endl; + pfile << "#macro stick(sx,sy,sz,ex,ey,ez,r1,r2)" << std::endl; + pfile << " difference{" << std::endl; + pfile << " cylinder {" << std::endl; + pfile << " ," << std::endl; + pfile << " , 0.2" << std::endl; + pfile << " }" << std::endl; + pfile << " sphere{, r1*0.5}" << std::endl; + pfile << " sphere{, r2*0.5}" << std::endl; + pfile << " texture { pigment {color rgb < 0.1 0.1 0.1 >} finish {reflection ref diffuse dif ambient amb } }" << std::endl; + pfile << " }" << std::endl; + pfile << "#end\n" << std::endl; + } + // Output material specific macros for(unsigned int indx=0; indx < vdc::materials.size(); indx++){ @@ -366,12 +412,16 @@ void output_povray_file(){ } } // end of check if material is to be removed } + // frame specific povray output pfile << "//----------------------------------------------------------------" << std::endl; - pfile << "// Include spin data" << std::endl; + pfile << "// Include spin and sticks data" << std::endl; pfile << "//----------------------------------------------------------------" << std::endl; pfile << "#include concat(\"spins-\", str(frame_number, -8, 0) \".inc\")" << std::endl; + // optionally include sticks + if(vdc::povsticks) pfile << "#include \"sticks.inc\"" << std::endl; + // close output file pfile.close(); diff --git a/util/vdc/spin-lattice.cpp b/util/vdc/spin-lattice.cpp new file mode 100644 index 000000000..e350d1d15 --- /dev/null +++ b/util/vdc/spin-lattice.cpp @@ -0,0 +1,252 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2017. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include +#include +#include +#include +#include + +// program header +#include "vdc.hpp" + +namespace vdc{ + +// forward function declarations +bool read_sld_spin_metadata(unsigned int file_id); +void read_sld_spin_data(); +void read_sld_coords_data(); + + +//------------------------------------------------------------------------------ +// Wrapper function to read coordinate metafile to initialise data structures +// and process coordinate data +//------------------------------------------------------------------------------ +void process_spins(){ + + unsigned int min_file_id = vdc::vdc_start_file_id; + unsigned int max_file_id = vdc::vdc_final_file_id; + + if(vdc::cells) vdc::initialise_cells(); + + if(vdc::ssc) vdc::initialise_ssc(); + + if(vdc::povray || vdc::povcells ) vdc::initialise_povray(); + + // output povray file + if(vdc::povray) output_povray_file(); + if(vdc::povcells) output_povray_cells_file(); + + unsigned int last_file_id = max_file_id; + + // loop over all spin files + for(unsigned int file_id = min_file_id; file_id < max_file_id; file_id++){ + + // read meta data + bool success = vdc::read_spin_metadata(file_id); + + // if no success then break out of for loop + if(!success) break; + + // read coordinate data + vdc::read_spin_data(); + + // output cells raw data + if(vdc::cells) vdc::output_cell_file(file_id); + + // output povray files + if(vdc::povray) output_inc_file(file_id); + if(vdc::povcells) output_cells_inc_file(file_id); + + // output vtk file + if(vdc::vtk) output_vtk_file(file_id); + + // output plain text file + if(vdc::txt) output_txt_file(file_id); + + // compute spin-spin correlation + if(vdc::ssc) output_ssc_file(file_id); + + last_file_id = file_id; + + } + + // set global start and end file id + vdc::start_file_id = min_file_id; + vdc::final_file_id = last_file_id; + + // output average ssc + if(vdc::ssc) output_average_ssc_file(); + + return; + +} + +//------------------------------------------------------------------------------ +// Function to read coordinate metafile +//------------------------------------------------------------------------------ +// +// Example metafile format: +// +// #------------------------------------------------------ +// # Atomistic spin configuration file for vampire v5+ +// #------------------------------------------------------ +// # Date: Fri Apr 7 21:42:33 2017 +// #------------------------------------------------------ +// Time: 1e-14 +// Field: 0 0 1 +// Temperature: 0 +// Magnetisation: 0.707068 5.51664e-05 0.707146 +// #------------------------------------------------------ +// Number of spin files: 1 +// spins-00000000.data +// #------------------------------------------------------ +// +//------------------------------------------------------------------------------ +bool read_spin_metadata(unsigned int file_id){ + + // determine file name + std::stringstream filename; + filename << "spins-"; + filename << std::setfill('0') << std::setw(8) << file_id; + filename << ".meta"; + + // open spins metadata file + std::ifstream smfile; + smfile.open(filename.str()); + + // check for open file, if not open then end program, end of snapshots + if(!smfile.is_open()){ + //std::cerr << "Error! Spins metadata file spins-" << std::setfill('0') << std::setw(8) + //<< file_id << ".meta cannot be opened. Exiting" << std::endl; + //exit(1); + return false; + } + + // Metafile found - inform the user and process data + if(vdc::verbose) std::cout << "--------------------------------------------------------------------" << std::endl; + std::cout << "Processing snapshot " << std::setfill('0') << std::setw(8) << file_id << std::endl; + if(vdc::verbose) std::cout << " Reading spin meta-data file " << filename.str() << std::endl; + + std::string line; // line string variable + + // read in file header (not useful - need to read in variables) + for(int i=0; i<10; i++) getline(smfile, line); + + // get number of subsidiary files + getline(smfile, line); + line.erase (line.begin(), line.begin()+22); + unsigned int num_spin_files=atoi(line.c_str()); + + if(vdc::verbose) std::cout << " Number of data files: " << num_spin_files << std::endl; + + vdc::spin_filenames.resize(0); + + for(unsigned int file = 0; file < num_spin_files; file++){ + getline(smfile, line); + line.erase(remove(line.begin(), line.end(), '\t'), line.end()); + line.erase(remove(line.begin(), line.end(), ' '), line.end()); + line.erase(remove(line.begin(), line.end(), '\r'), line.end()); + vdc::spin_filenames.push_back(line); + if(vdc::verbose) std::cout << " " << line << std::endl; + } + + return true; + +} + +//------------------------------------------------------------------------------ +// Function to read in coordinate data from subsidiary files +//------------------------------------------------------------------------------ +void read_spin_data(){ + + if(vdc::verbose) std::cout << " Reading spin data... " << std::flush; + + // resize arrays + if(vdc::spins.size() != 3*vdc::num_atoms) vdc::spins.resize(3*vdc::num_atoms); + + // index counter + uint64_t atom_id = 0; + + // loop over all files + for(unsigned int f = 0; f < vdc::spin_filenames.size(); f++){ + + switch (vdc::format){ + + case vdc::binary:{ + uint64_t num_atoms_in_file = 0; + // open file in binary mode + std::ifstream ifile; + ifile.open(spin_filenames[f].c_str(), std::ios::binary); // check for errors + // check for open file + if(!ifile.is_open()){ + std::cerr << std::endl << " Error! Spin data file \"" << spin_filenames[f] << "\" cannot be opened. Exiting" << std::endl; + exit(1); + } + // read number of atoms + ifile.read( (char*)&num_atoms_in_file,sizeof(uint64_t) ); + // read spin data + ifile.read((char*)&vdc::spins[atom_id*3], sizeof(double)*num_atoms_in_file*3); + // increment counter + atom_id += num_atoms_in_file; + ifile.close(); + break; + } + + case vdc::text:{ + // open file + std::ifstream ifile; + ifile.open(spin_filenames[f].c_str()); // check for errors + // check for open file + if(!ifile.is_open()){ + std::cerr << std::endl << " Error! Spin data file \"" << spin_filenames[f] << "\" cannot be opened. Exiting" << std::endl; + exit(1); + } + + uint64_t num_atoms_in_file = 0; + std::string line; + getline(ifile, line); + { + std::istringstream ss(line); + ss >> num_atoms_in_file; // interpret as uint64_t + } + double x,y,z; + // loop over all atoms in file and load as x,y,z sets + for(uint64_t idx = 0; idx < num_atoms_in_file; idx++){ + getline(ifile, line); + std::istringstream ss(line); + ss >> x >> y >> z; + vdc::spins[3*atom_id + 0] = x; + vdc::spins[3*atom_id + 1] = y; + vdc::spins[3*atom_id + 2] = z; + // increment atom counter + atom_id += 1; + } + ifile.close(); + break; + } + + } + + } + + // output informative message to user + if(vdc::verbose) std::cout << "done!" << std::endl; + + return; + +} + +} diff --git a/util/vdc/sticks.cpp b/util/vdc/sticks.cpp new file mode 100644 index 000000000..03209cd14 --- /dev/null +++ b/util/vdc/sticks.cpp @@ -0,0 +1,104 @@ +//------------------------------------------------------------------------------ +// +// This file is part of the VAMPIRE open source package under the +// Free BSD licence (see licence file for details). +// +// (c) Richard F L Evans 2017. All rights reserved. +// +// Email: richard.evans@york.ac.uk +// +//------------------------------------------------------------------------------ +// + +// C++ standard library headers +#include +#include +#include +#include +#include +#include +#include + +// program header +#include "vdc.hpp" + +namespace vdc{ + +// forward function declarations +bool read_spin_metadata(unsigned int file_id); +void read_spin_data(); + +//------------------------------------------------------------------------------ +// Function to output sticks.inc file compatible with povray +//------------------------------------------------------------------------------ +void output_sticks_file(){ + + // output informative message to user + if(vdc::verbose) std::cout << " Generating stick data and writing sticks file sticks.inc..." << std::flush; + + // open incfile + std::ofstream sfile; + sfile.open("sticks.inc"); + + const double scr2 = vdc::sticks_cutoff*vdc::sticks_cutoff; + + std::stringstream otext; // output file text asembled in memory for speed + + // loop over all atoms to find nearby atoms within cutoff + for(size_t i=0; i < vdc::sliced_atoms_list.size(); i++){ + + // get atom ID + unsigned int atomi = vdc::sliced_atoms_list[i]; + + // only for i > j + for(size_t j=i+1; j < vdc::sliced_atoms_list.size(); j++){ + + // get atom ID + unsigned int atomj = vdc::sliced_atoms_list[j]; + + // calculate range between atoms j-i + const double dx = (coordinates[3*atomj+0]-coordinates[3*atomi+0]); + const double dy = (coordinates[3*atomj+1]-coordinates[3*atomi+1]); + const double dz = (coordinates[3*atomj+2]-coordinates[3*atomi+2]); + const double dr2 = dx*dx + dy*dy + dz*dz; + + // check atoms are within cutoff + if(dr2 < scr2){ + + const int mati = vdc::type[atomi]+1; + const int matj = vdc::type[atomj]+1; + + // stick(sx,sy,sz,ex,ey,ez,ri,rj) + // calculate start and end of cylinder based on radius + + const double sx = coordinates[3*atomi+0]-vdc::system_centre[0]; + const double sy = coordinates[3*atomi+1]-vdc::system_centre[1]; + const double sz = coordinates[3*atomi+2]-vdc::system_centre[2]; + + const double ex = coordinates[3*atomj+0]-vdc::system_centre[0]; + const double ey = coordinates[3*atomj+1]-vdc::system_centre[1]; + const double ez = coordinates[3*atomj+2]-vdc::system_centre[2]; + + otext << "stick(" << sx << ", " << sy << ", " << sz << ", " << + ex << ", " << ey << ", " << ez << ", rscale" << mati << ", rscale" << matj << ")\n"; + } + + } + + } + + // output string to file + sfile << otext.str(); + + // flush data to include file and close + sfile << std::flush; + sfile.close(); + + // output informative message to user + if(vdc::verbose) std::cout << "done!" << std::endl; + + return; + +} + +} diff --git a/util/vdc/text.cpp b/util/vdc/text.cpp index 78f32b585..0256f796e 100644 --- a/util/vdc/text.cpp +++ b/util/vdc/text.cpp @@ -55,6 +55,9 @@ void output_txt_file(unsigned int spin_file_id){ std::ofstream txtfile; txtfile.open(txt_file.c_str()); + // output number of atoms + txtfile << vdc::sliced_atoms_list.size() + vdc::sliced_nm_atoms_list.size() << "\n"; + //--------------------------------------------------------------------------- // parallelise stream formatting for better performance // step 1: parallel formatted output to stringstream in memory @@ -67,7 +70,10 @@ void output_txt_file(unsigned int spin_file_id){ // write to output text stream in parallel #pragma omp for - for(unsigned int atom = 0; atom < vdc::num_atoms; atom++){ + for(size_t i=0; i < vdc::sliced_atoms_list.size(); i++){ + + // get atom ID + unsigned int atom = vdc::sliced_atoms_list[i]; // format text for plain text file otext << coordinates[3*atom+0]-vdc::system_centre[0] << "\t" << coordinates[3*atom+1]-vdc::system_centre[1] << "\t" << coordinates[3*atom+2]-vdc::system_centre[2] << "\t" << @@ -93,7 +99,10 @@ void output_txt_file(unsigned int spin_file_id){ // write to output text stream in parallel #pragma omp for - for(unsigned int atom = 0; atom < vdc::num_nm_atoms; atom++){ + for(size_t i=0; i < vdc::sliced_nm_atoms_list.size(); i++){ + + // get atom ID + unsigned int atom = vdc::sliced_nm_atoms_list[i]; // format text for text file otext << nm_coordinates[3*atom+0]-vdc::system_centre[0] << "\t" << nm_coordinates[3*atom+1]-vdc::system_centre[1] << "\t" << nm_coordinates[3*atom+2]-vdc::system_centre[2] << "\t" << diff --git a/util/vdc/vdc.hpp b/util/vdc/vdc.hpp index a2f9ce3a2..72674a216 100644 --- a/util/vdc/vdc.hpp +++ b/util/vdc/vdc.hpp @@ -19,17 +19,26 @@ #include #include #include +#include namespace vdc{ + // simple struct for xy coordinates + struct xy_t{ + double x; + double y; + }; + // input filename extern std::string input_file; // program option flags extern bool verbose; extern bool xyz; + extern bool grains; // flag to enable grain calculations extern bool povray; extern bool povcells; + extern bool povsticks; extern bool cells; extern bool cellsf; extern bool vtk; @@ -106,6 +115,7 @@ namespace vdc{ extern std::vector category; extern std::vector type; + extern std::vector grain; extern std::vector coordinates; extern std::vector spins; @@ -121,6 +131,9 @@ namespace vdc{ extern double camera_zoom; extern std::string background_colour; + // povray sticks settings + extern double sticks_cutoff; + // povray shape sizes extern std::vector atom_sizes; extern std::vector arrow_sizes; @@ -129,6 +142,7 @@ namespace vdc{ extern uint64_t num_nm_atoms; extern std::vector nm_category; extern std::vector nm_type; + extern std::vector nm_grain; extern std::vector nm_coordinates; // cell data @@ -143,6 +157,9 @@ namespace vdc{ extern std::vector cell_coords; extern std::vector< std::vector< std::vector > > cell_magnetization; + // grain data + extern std::vector < std::vector > grain_vertices_array; + // array to store subsidiary data file names extern std::vector coord_filenames; extern std::vector spin_filenames; @@ -175,6 +192,9 @@ namespace vdc{ // XYZ void output_xyz_file(); + // atoms + void output_atoms_txt_file(); + // VTK void output_vtk_file(unsigned int spin_file_id); @@ -187,18 +207,22 @@ namespace vdc{ void output_povray_file(); void output_cells_inc_file(unsigned int spin_file_id); void output_povray_cells_file(); + void output_sticks_file(); + + // grains + void load_grain_vertices(); + void determine_atom_grain_id(); + //void generate_povray_grains() // Colour void rgb( const double& sx, const double& sy, const double& sz, double &red, double &green, double &blue); void initialise_colourwheel(); - // SSC void initialise_ssc(); void output_average_ssc_file(); void output_ssc_file(unsigned int spin_file_id); - // CELL void initialise_cells(); void output_cell_file(unsigned int spin_file_id); @@ -221,6 +245,7 @@ namespace vdc{ void set_camera_position(const input_t &input); void set_camera_look_at(const input_t &input); void set_camera_zoom(const input_t &input); + void set_sticks_cutoff(const input_t &input); void set_background_colour(const input_t &input); void set_atom_sizes(const input_t &input); void set_arrow_sizes(const input_t &input); diff --git a/util/vdc/vtk.cpp b/util/vdc/vtk.cpp index 9bfdf70c6..5d37d1f5e 100644 --- a/util/vdc/vtk.cpp +++ b/util/vdc/vtk.cpp @@ -66,10 +66,10 @@ void output_vtk_file(unsigned int spin_file_id){ vtkfile << " " << std::endl; vtkfile << " " << std::endl; vtkfile << " " << std::endl; - for(size_t i=0; i < vdc::atoms_list.size(); i++){ + for(size_t i=0; i < vdc::sliced_atoms_list.size(); i++){ // get atom ID - unsigned int atom = vdc::atoms_list[i]; + unsigned int atom = vdc::sliced_atoms_list[i]; vtkfile << spins[3*atom+0] << " " << spins[3*atom+1] << " " << spins[3*atom+2] << " "; }