From a3ea266bfa1d60276e16b11a7065b7b1a515fea4 Mon Sep 17 00:00:00 2001 From: Gideon Date: Fri, 24 May 2019 18:10:59 +0200 Subject: [PATCH 01/45] Core: added Micromagnetic Hamiltonian. The class does not have any real functionality yet. A corresponding input parser has been added, but the kind and units of input parameters still need to be decided. --- core/include/engine/CMakeLists.txt | 1 + .../engine/Hamiltonian_Micromagnetic.hpp | 110 ++++++++ core/include/io/Configparser.hpp | 2 + core/src/engine/CMakeLists.txt | 2 + core/src/engine/Hamiltonian_Micromagnetic.cpp | 227 ++++++++++++++++ core/src/engine/Hamiltonian_Micromagnetic.cu | 243 ++++++++++++++++++ core/src/io/Configparser.cpp | 127 +++++++-- input/input.cfg | 2 +- 8 files changed, 694 insertions(+), 20 deletions(-) create mode 100644 core/include/engine/Hamiltonian_Micromagnetic.hpp create mode 100644 core/src/engine/Hamiltonian_Micromagnetic.cpp create mode 100644 core/src/engine/Hamiltonian_Micromagnetic.cu diff --git a/core/include/engine/CMakeLists.txt b/core/include/engine/CMakeLists.txt index d754d4d4d..2311d9234 100644 --- a/core/include/engine/CMakeLists.txt +++ b/core/include/engine/CMakeLists.txt @@ -3,6 +3,7 @@ set(HEADER_SPIRIT_ENGINE ${CMAKE_CURRENT_SOURCE_DIR}/Neighbours.hpp ${CMAKE_CURRENT_SOURCE_DIR}/Hamiltonian.hpp ${CMAKE_CURRENT_SOURCE_DIR}/Hamiltonian_Heisenberg.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/Hamiltonian_Micromagnetic.hpp ${CMAKE_CURRENT_SOURCE_DIR}/Hamiltonian_Gaussian.hpp ${CMAKE_CURRENT_SOURCE_DIR}/Eigenmodes.hpp ${CMAKE_CURRENT_SOURCE_DIR}/HTST.hpp diff --git a/core/include/engine/Hamiltonian_Micromagnetic.hpp b/core/include/engine/Hamiltonian_Micromagnetic.hpp new file mode 100644 index 000000000..ae1ad7491 --- /dev/null +++ b/core/include/engine/Hamiltonian_Micromagnetic.hpp @@ -0,0 +1,110 @@ +#pragma once +#ifndef HAMILTONIAN_MICROMAGNETIC_H +#define HAMILTONIAN_MICROMAGNETIC_H + +#include +#include + +#include "Spirit_Defines.h" +#include +#include +#include + +namespace Engine +{ + // enum class DDI_Method + // { + // FFT = SPIRIT_DDI_METHOD_FFT, + // FMM = SPIRIT_DDI_METHOD_FMM, + // Cutoff = SPIRIT_DDI_METHOD_CUTOFF, + // None = SPIRIT_DDI_METHOD_NONE + // }; + + /* + The Micromagnetic Hamiltonian + */ + class Hamiltonian_Micromagnetic : public Hamiltonian + { + public: + Hamiltonian_Micromagnetic( + scalar external_field_magnitude, Vector3 external_field_normal, + Matrix3 anisotropy_tensor, + scalar exchange_constant, + scalar dmi_constant, + std::shared_ptr geometry, + int spatial_gradient_order, + intfield boundary_conditions + ); + + Hamiltonian_Micromagnetic( + scalar external_field_magnitude, Vector3 external_field_normal, + Matrix3 anisotropy_tensor, + Matrix3 exchange_tensor, + Matrix3 dmi_tensor, + std::shared_ptr geometry, + int spatial_gradient_order, + intfield boundary_conditions + ); + + void Update_Interactions(); + + void Update_Energy_Contributions() override; + + void Hessian(const vectorfield & spins, MatrixX & hessian) override; + void Gradient(const vectorfield & spins, vectorfield & gradient) override; + void Energy_Contributions_per_Spin(const vectorfield & spins, std::vector> & contributions) override; + + // Calculate the total energy for a single spin to be used in Monte Carlo. + // Note: therefore the energy of pairs is weighted x2 and of quadruplets x4. + scalar Energy_Single_Spin(int ispin, const vectorfield & spins) override; + + // Hamiltonian name as string + const std::string& Name() override; + + // ------------ ... ------------ + int spatial_gradient_order; + intfield boundary_conditions; + + // ------------ Single Spin Interactions ------------ + // External magnetic field across the sample + scalar external_field_magnitude; + Vector3 external_field_normal; + Matrix3 anisotropy_tensor; + + // ------------ Pair Interactions ------------ + // Exchange interaction + Matrix3 exchange_tensor; + // DMI + Matrix3 dmi_tensor; + + private: + std::shared_ptr geometry; + + // ------------ Effective Field Functions ------------ + // Calculate the Zeeman effective field of a single Spin + void Gradient_Zeeman(vectorfield & gradient); + // Calculate the Anisotropy effective field of a single Spin + void Gradient_Anisotropy(const vectorfield & spins, vectorfield & gradient); + // Calculate the exchange interaction effective field of a Spin Pair + void Gradient_Exchange(const vectorfield & spins, vectorfield & gradient); + // Calculate the DMI effective field of a Spin Pair + void Gradient_DMI(const vectorfield & spins, vectorfield & gradient); + + // ------------ Energy Functions ------------ + // Indices for Energy vector + int idx_zeeman, idx_anisotropy, idx_exchange, idx_dmi, idx_ddi; + // Calculate the Zeeman energy of a Spin System + void E_Zeeman(const vectorfield & spins, scalarfield & Energy); + // Calculate the Anisotropy energy of a Spin System + void E_Anisotropy(const vectorfield & spins, scalarfield & Energy); + // Calculate the exchange interaction energy of a Spin System + void E_Exchange(const vectorfield & spins, scalarfield & Energy); + // Calculate the DMI energy of a Spin System + void E_DMI(const vectorfield & spins, scalarfield & Energy); + // Dipolar interactions + void E_DDI(const vectorfield & spins, scalarfield & Energy); + }; + + +} +#endif \ No newline at end of file diff --git a/core/include/io/Configparser.hpp b/core/include/io/Configparser.hpp index 3f3865c9e..16c946f13 100644 --- a/core/include/io/Configparser.hpp +++ b/core/include/io/Configparser.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include namespace IO @@ -26,6 +27,7 @@ namespace IO std::unique_ptr Parameters_Method_MMF_from_Config(const std::string configFile); std::unique_ptr Hamiltonian_from_Config(const std::string configFile, const std::shared_ptr geometry); std::unique_ptr Hamiltonian_Heisenberg_from_Config(const std::string configFile, const std::shared_ptr geometry, std::string hamiltonian_type); + std::unique_ptr Hamiltonian_Micromagnetic_from_Config(const std::string configFile, const std::shared_ptr geometry); std::unique_ptr Hamiltonian_Gaussian_from_Config(const std::string configFile, const std::shared_ptr geometry); };// end namespace IO #endif \ No newline at end of file diff --git a/core/src/engine/CMakeLists.txt b/core/src/engine/CMakeLists.txt index fb20c9b4d..d3c13956b 100644 --- a/core/src/engine/CMakeLists.txt +++ b/core/src/engine/CMakeLists.txt @@ -4,6 +4,8 @@ set(SOURCE_SPIRIT_ENGINE ${CMAKE_CURRENT_SOURCE_DIR}/Hamiltonian.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Hamiltonian_Heisenberg.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Hamiltonian_Heisenberg.cu + ${CMAKE_CURRENT_SOURCE_DIR}/Hamiltonian_Micromagnetic.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/Hamiltonian_Micromagnetic.cu ${CMAKE_CURRENT_SOURCE_DIR}/Hamiltonian_Gaussian.cpp ${CMAKE_CURRENT_SOURCE_DIR}/Eigenmodes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/HTST.cpp diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cpp b/core/src/engine/Hamiltonian_Micromagnetic.cpp new file mode 100644 index 000000000..2fca744a4 --- /dev/null +++ b/core/src/engine/Hamiltonian_Micromagnetic.cpp @@ -0,0 +1,227 @@ +#ifndef SPIRIT_USE_CUDA + +#include +#include +#include +#include +#include +#include + +#include +#include + + +using namespace Data; +using namespace Utility; +namespace C = Utility::Constants; +using Engine::Vectormath::check_atom_type; +using Engine::Vectormath::idx_from_pair; +using Engine::Vectormath::idx_from_tupel; + + +namespace Engine +{ + Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( + scalar external_field_magnitude, Vector3 external_field_normal, + Matrix3 anisotropy_tensor, + Matrix3 exchange_tensor, + Matrix3 dmi_tensor, + std::shared_ptr geometry, + int spatial_gradient_order, + intfield boundary_conditions + ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), geometry(geometry), + external_field_magnitude(external_field_magnitude), external_field_normal(external_field_normal), + anisotropy_tensor(anisotropy_tensor) + { + // Generate interaction pairs, constants etc. + this->Update_Interactions(); + } + + void Hamiltonian_Micromagnetic::Update_Interactions() + { + #if defined(SPIRIT_USE_OPENMP) + // When parallelising (cuda or openmp), we need all neighbours per spin + const bool use_redundant_neighbours = true; + #else + // When running on a single thread, we can ignore redundant neighbours + const bool use_redundant_neighbours = false; + #endif + + // TODO: make sure that the geometry can be treated with this model: + // - rectilinear, only one "atom" per cell + // if( geometry->n_cell_atoms != 1 ) + // Log(...) + + // TODO: generate neighbour information for pairwise interactions + + // TODO: prepare dipolar interactions + + // Update, which terms still contribute + this->Update_Energy_Contributions(); + } + + void Hamiltonian_Micromagnetic::Update_Energy_Contributions() + { + this->energy_contributions_per_spin = std::vector>(0); + + // External field + if( this->external_field_magnitude > 0 ) + { + this->energy_contributions_per_spin.push_back({"Zeeman", scalarfield(0)}); + this->idx_zeeman = this->energy_contributions_per_spin.size()-1; + } + else + this->idx_zeeman = -1; + // TODO: Anisotropy + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"Anisotropy", scalarfield(0) }); + // this->idx_anisotropy = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_anisotropy = -1; + // TODO: Exchange + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"Exchange", scalarfield(0) }); + // this->idx_exchange = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_exchange = -1; + // TODO: DMI + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"DMI", scalarfield(0) }); + // this->idx_dmi = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_dmi = -1; + // TODO: DDI + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"DDI", scalarfield(0) }); + // this->idx_ddi = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_ddi = -1; + } + + void Hamiltonian_Micromagnetic::Energy_Contributions_per_Spin(const vectorfield & spins, std::vector> & contributions) + { + if( contributions.size() != this->energy_contributions_per_spin.size() ) + { + contributions = this->energy_contributions_per_spin; + } + + int nos = spins.size(); + for( auto& contrib : contributions ) + { + // Allocate if not already allocated + if (contrib.second.size() != nos) contrib.second = scalarfield(nos, 0); + // Otherwise set to zero + else Vectormath::fill(contrib.second, 0); + } + + // External field + if( this->idx_zeeman >=0 ) E_Zeeman(spins, contributions[idx_zeeman].second); + + // Anisotropy + if( this->idx_anisotropy >=0 ) E_Anisotropy(spins, contributions[idx_anisotropy].second); + + // Exchange + if( this->idx_exchange >=0 ) E_Exchange(spins, contributions[idx_exchange].second); + // DMI + if( this->idx_dmi >=0 ) E_DMI(spins,contributions[idx_dmi].second); + } + + void Hamiltonian_Micromagnetic::E_Zeeman(const vectorfield & spins, scalarfield & Energy) + { + auto& mu_s = this->geometry->mu_s; + + #pragma omp parallel for + for( int icell = 0; icell < geometry->n_cells_total; ++icell ) + { + if( check_atom_type(this->geometry->atom_types[icell]) ) + Energy[icell] -= mu_s[icell] * this->external_field_magnitude * this->external_field_normal.dot(spins[icell]); + } + } + + void Hamiltonian_Micromagnetic::E_Anisotropy(const vectorfield & spins, scalarfield & Energy) + { + } + + void Hamiltonian_Micromagnetic::E_Exchange(const vectorfield & spins, scalarfield & Energy) + { + } + + void Hamiltonian_Micromagnetic::E_DMI(const vectorfield & spins, scalarfield & Energy) + { + } + + void Hamiltonian_Micromagnetic::E_DDI(const vectorfield & spins, scalarfield & Energy) + { + } + + + scalar Hamiltonian_Micromagnetic::Energy_Single_Spin(int ispin, const vectorfield & spins) + { + scalar Energy = 0; + return Energy; + } + + + void Hamiltonian_Micromagnetic::Gradient(const vectorfield & spins, vectorfield & gradient) + { + // Set to zero + Vectormath::fill(gradient, {0,0,0}); + + // External field + this->Gradient_Zeeman(gradient); + + // Anisotropy + this->Gradient_Anisotropy(spins, gradient); + + // Exchange + this->Gradient_Exchange(spins, gradient); + + // DMI + this->Gradient_DMI(spins, gradient); + } + + + void Hamiltonian_Micromagnetic::Gradient_Zeeman(vectorfield & gradient) + { + auto& mu_s = this->geometry->mu_s; + + #pragma omp parallel for + for( int icell = 0; icell < geometry->n_cells_total; ++icell ) + { + if( check_atom_type(this->geometry->atom_types[icell]) ) + gradient[icell] -= mu_s[icell] * this->external_field_magnitude * this->external_field_normal; + } + } + + void Hamiltonian_Micromagnetic::Gradient_Anisotropy(const vectorfield & spins, vectorfield & gradient) + { + } + + void Hamiltonian_Micromagnetic::Gradient_Exchange(const vectorfield & spins, vectorfield & gradient) + { + } + + void Hamiltonian_Micromagnetic::Gradient_DMI(const vectorfield & spins, vectorfield & gradient) + { + } + + + void Hamiltonian_Micromagnetic::Hessian(const vectorfield & spins, MatrixX & hessian) + { + } + + + // Hamiltonian name as string + static const std::string name = "Micromagnetic"; + const std::string& Hamiltonian_Micromagnetic::Name() { return name; } +} + +#endif \ No newline at end of file diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cu b/core/src/engine/Hamiltonian_Micromagnetic.cu new file mode 100644 index 000000000..d22dcb778 --- /dev/null +++ b/core/src/engine/Hamiltonian_Micromagnetic.cu @@ -0,0 +1,243 @@ +#ifdef SPIRIT_USE_CUDA + +#include +#include +#include +#include +#include +#include + +#include +#include + + +using namespace Data; +using namespace Utility; +namespace C = Utility::Constants; +using Engine::Vectormath::check_atom_type; +using Engine::Vectormath::idx_from_pair; +using Engine::Vectormath::idx_from_tupel; + + +namespace Engine +{ + Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( + scalar external_field_magnitude, Vector3 external_field_normal, + Matrix3 anisotropy_tensor, + scalar exchange_constant, + scalar dmi_constant, + std::shared_ptr geometry, + int spatial_gradient_order, + intfield boundary_conditions + ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), + external_field_magnitude(external_field_magnitude), + anisotropy_tensor(anisotropy_tensor) + { + // Generate interaction pairs, constants etc. + this->Update_Interactions(); + } + + Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( + scalar external_field_magnitude, Vector3 external_field_normal, + Matrix3 anisotropy_tensor, + Matrix3 exchange_tensor, + Matrix3 dmi_tensor, + std::shared_ptr geometry, + int spatial_gradient_order, + intfield boundary_conditions + ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), + external_field_magnitude(external_field_magnitude), + anisotropy_tensor(anisotropy_tensor) + { + // Generate interaction pairs, constants etc. + this->Update_Interactions(); + } + + void Hamiltonian_Micromagnetic::Update_Interactions() + { + #if defined(SPIRIT_USE_OPENMP) + // When parallelising (cuda or openmp), we need all neighbours per spin + const bool use_redundant_neighbours = true; + #else + // When running on a single thread, we can ignore redundant neighbours + const bool use_redundant_neighbours = false; + #endif + + // TODO: make sure that the geometry can be treated with this model: + // - rectilinear, only one "atom" per cell + // if( geometry->n_cell_atoms != 1 ) + // Log(...) + + // TODO: generate neighbour information for pairwise interactions + + // TODO: prepare dipolar interactions + + // Update, which terms still contribute + this->Update_Energy_Contributions(); + } + + void Hamiltonian_Micromagnetic::Update_Energy_Contributions() + { + this->energy_contributions_per_spin = std::vector>(0); + + // External field + if( this->external_field_magnitude > 0 ) + { + this->energy_contributions_per_spin.push_back({"Zeeman", scalarfield(0)}); + this->idx_zeeman = this->energy_contributions_per_spin.size()-1; + } + else + this->idx_zeeman = -1; + // TODO: Anisotropy + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"Anisotropy", scalarfield(0) }); + // this->idx_anisotropy = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_anisotropy = -1; + // TODO: Exchange + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"Exchange", scalarfield(0) }); + // this->idx_exchange = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_exchange = -1; + // TODO: DMI + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"DMI", scalarfield(0) }); + // this->idx_dmi = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_dmi = -1; + // TODO: DDI + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"DDI", scalarfield(0) }); + // this->idx_ddi = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_ddi = -1; + } + + void Hamiltonian_Micromagnetic::Energy_Contributions_per_Spin(const vectorfield & spins, std::vector> & contributions) + { + if( contributions.size() != this->energy_contributions_per_spin.size() ) + { + contributions = this->energy_contributions_per_spin; + } + + int nos = spins.size(); + for( auto& contrib : contributions ) + { + // Allocate if not already allocated + if (contrib.second.size() != nos) contrib.second = scalarfield(nos, 0); + // Otherwise set to zero + else Vectormath::fill(contrib.second, 0); + } + + // External field + if( this->idx_zeeman >=0 ) E_Zeeman(spins, contributions[idx_zeeman].second); + + // Anisotropy + if( this->idx_anisotropy >=0 ) E_Anisotropy(spins, contributions[idx_anisotropy].second); + + // Exchange + if( this->idx_exchange >=0 ) E_Exchange(spins, contributions[idx_exchange].second); + // DMI + if( this->idx_dmi >=0 ) E_DMI(spins,contributions[idx_dmi].second); + } + + void Hamiltonian_Micromagnetic::E_Zeeman(const vectorfield & spins, scalarfield & Energy) + { + auto& mu_s = this->geometry->mu_s; + + #pragma omp parallel for + for( int icell = 0; icell < geometry->n_cells_total; ++icell ) + { + if( check_atom_type(this->geometry->atom_types[icell]) ) + Energy[icell] -= mu_s[icell] * this->external_field_magnitude * this->external_field_normal.dot(spins[icell]); + } + } + + void Hamiltonian_Micromagnetic::E_Anisotropy(const vectorfield & spins, scalarfield & Energy) + { + } + + void Hamiltonian_Micromagnetic::E_Exchange(const vectorfield & spins, scalarfield & Energy) + { + } + + void Hamiltonian_Micromagnetic::E_DMI(const vectorfield & spins, scalarfield & Energy) + { + } + + void Hamiltonian_Micromagnetic::E_DDI(const vectorfield & spins, scalarfield & Energy) + { + } + + + scalar Hamiltonian_Micromagnetic::Energy_Single_Spin(int ispin, const vectorfield & spins) + { + scalar Energy = 0; + return Energy; + } + + + void Hamiltonian_Micromagnetic::Gradient(const vectorfield & spins, vectorfield & gradient) + { + // Set to zero + Vectormath::fill(gradient, {0,0,0}); + + // External field + this->Gradient_Zeeman(gradient); + + // Anisotropy + this->Gradient_Anisotropy(spins, gradient); + + // Exchange + this->Gradient_Exchange(spins, gradient); + + // DMI + this->Gradient_DMI(spins, gradient); + } + + + void Hamiltonian_Micromagnetic::Gradient_Zeeman(vectorfield & gradient) + { + auto& mu_s = this->geometry->mu_s; + + #pragma omp parallel for + for( int icell = 0; icell < geometry->n_cells_total; ++icell ) + { + if( check_atom_type(this->geometry->atom_types[icell]) ) + gradient[icell] -= mu_s[icell] * this->external_field_magnitude * this->external_field_normal; + } + } + + void Hamiltonian_Micromagnetic::Gradient_Anisotropy(const vectorfield & spins, vectorfield & gradient) + { + } + + void Hamiltonian_Micromagnetic::Gradient_Exchange(const vectorfield & spins, vectorfield & gradient) + { + } + + void Hamiltonian_Micromagnetic::Gradient_DMI(const vectorfield & spins, vectorfield & gradient) + { + } + + + void Hamiltonian_Micromagnetic::Hessian(const vectorfield & spins, MatrixX & hessian) + { + } + + + // Hamiltonian name as string + static const std::string name = "Micromagnetic"; + const std::string& Hamiltonian_Micromagnetic::Name() { return name; } +} + +#endif \ No newline at end of file diff --git a/core/src/io/Configparser.cpp b/core/src/io/Configparser.cpp index 11407d0a0..822930c8f 100644 --- a/core/src/io/Configparser.cpp +++ b/core/src/io/Configparser.cpp @@ -427,7 +427,7 @@ namespace IO }// end try catch( ... ) { - spirit_handle_exception_core(fmt::format("Unable to read mu_s from config file \"{}\"", configFile)); + spirit_handle_exception_core(fmt::format("Unable to read mu_s from config file \"{}\"", configFile)); } }// end if file="" else @@ -491,7 +491,7 @@ namespace IO } catch( ... ) { - spirit_rethrow( fmt::format("Unable to parse geometry from config file \"{}\"", configFile) ); + spirit_rethrow( fmt::format("Unable to parse geometry from config file \"{}\"", configFile) ); } return nullptr; @@ -1064,7 +1064,7 @@ namespace IO }// end try catch( ... ) { - spirit_handle_exception_core(fmt::format("Unable to read Hamiltonian type from config file \"{}\". Using default.", configFile)); + spirit_handle_exception_core(fmt::format("Unable to read Hamiltonian type from config file \"{}\". Using default.", configFile)); hamiltonian_type = "heisenberg_neighbours"; } } @@ -1075,22 +1075,18 @@ namespace IO std::unique_ptr hamiltonian; try { - if (hamiltonian_type == "heisenberg_neighbours" || hamiltonian_type == "heisenberg_pairs") - { + if( hamiltonian_type == "heisenberg_neighbours" || hamiltonian_type == "heisenberg_pairs" ) hamiltonian = Hamiltonian_Heisenberg_from_Config(configFile, geometry, hamiltonian_type); - } - else if (hamiltonian_type == "gaussian") - { + else if( hamiltonian_type == "micromagnetic" ) + hamiltonian = std::move(Hamiltonian_Micromagnetic_from_Config(configFile, geometry)); + else if( hamiltonian_type == "gaussian" ) hamiltonian = std::move(Hamiltonian_Gaussian_from_Config(configFile, geometry)); - } else - { spirit_throw(Exception_Classifier::System_not_Initialized, Log_Level::Severe, fmt::format("Hamiltonian: Invalid type \"{}\"", hamiltonian_type)); - } } catch( ... ) { - spirit_handle_exception_core(fmt::format("Unable to initialize Hamiltonian from config file \"{}\"", configFile)); + spirit_handle_exception_core(fmt::format("Unable to initialize Hamiltonian from config file \"{}\"", configFile)); } // Return @@ -1160,7 +1156,7 @@ namespace IO }// end try catch( ... ) { - spirit_handle_exception_core(fmt::format("Unable to read boundary conditions from config file \"{}\"", configFile)); + spirit_handle_exception_core(fmt::format("Unable to read boundary conditions from config file \"{}\"", configFile)); } @@ -1180,7 +1176,7 @@ namespace IO }// end try catch( ... ) { - spirit_handle_exception_core(fmt::format("Unable to read external field from config file \"{}\"", configFile)); + spirit_handle_exception_core(fmt::format("Unable to read external field from config file \"{}\"", configFile)); } try @@ -1237,7 +1233,7 @@ namespace IO }// end try catch( ... ) { - spirit_handle_exception_core(fmt::format("Unable to read anisotropy from config file \"{}\"", configFile)); + spirit_handle_exception_core(fmt::format("Unable to read anisotropy from config file \"{}\"", configFile)); } if (hamiltonian_type == "heisenberg_pairs") @@ -1268,7 +1264,7 @@ namespace IO }// end try catch( ... ) { - spirit_handle_exception_core(fmt::format("Unable to read interaction pairs from config file \"{}\"", configFile)); + spirit_handle_exception_core(fmt::format("Unable to read interaction pairs from config file \"{}\"", configFile)); } } else @@ -1354,7 +1350,7 @@ namespace IO }// end try catch( ... ) { - spirit_handle_exception_core(fmt::format("Unable to read DDI radius from config file \"{}\"", configFile)); + spirit_handle_exception_core(fmt::format("Unable to read DDI radius from config file \"{}\"", configFile)); } try @@ -1377,7 +1373,7 @@ namespace IO }// end try catch( ... ) { - spirit_handle_exception_core(fmt::format("Unable to read interaction quadruplets from config file \"{}\"", configFile)); + spirit_handle_exception_core(fmt::format("Unable to read interaction quadruplets from config file \"{}\"", configFile)); } } else @@ -1439,6 +1435,99 @@ namespace IO }// end Hamiltonian_Heisenberg_From_Config + std::unique_ptr Hamiltonian_Micromagnetic_from_Config(const std::string configFile, const std::shared_ptr geometry) + { + //-------------- Insert default values here ----------------------------- + // Boundary conditions (a, b, c) + std::vector boundary_conditions_i = { 0, 0, 0 }; + intfield boundary_conditions = { false, false, false }; + + // The order of the finite difference approximation of the spatial gradient + int spatial_gradient_order = 1; + + // External Magnetic Field + scalar field = 0; + Vector3 field_normal = { 0.0, 0.0, 1.0 }; + + Matrix3 anisotropy_tensor; + scalar exchange_magnitude = 0; + Matrix3 exchange_tensor; + scalar dmi_magnitude = 0; + Matrix3 dmi_tensor; + + //------------------------------- Parser -------------------------------- + Log(Log_Level::Info, Log_Sender::IO, "Hamiltonian_Micromagnetic: building"); + try + { + IO::Filter_File_Handle myfile(configFile); + + myfile.Read_Single(spatial_gradient_order, "spatial_gradient_order"); + + try + { + IO::Filter_File_Handle myfile(configFile); + + // Boundary conditions + myfile.Read_3Vector(boundary_conditions_i, "boundary_conditions"); + boundary_conditions[0] = (boundary_conditions_i[0] != 0); + boundary_conditions[1] = (boundary_conditions_i[1] != 0); + boundary_conditions[2] = (boundary_conditions_i[2] != 0); + }// end try + catch( ... ) + { + spirit_handle_exception_core(fmt::format("Unable to read boundary conditions from config file \"{}\"", configFile)); + } + + // TODO: + // if (myfile.Find("tensor_exchange")) + // ... + // else if (myfile.Find("exchange")) + // ... + // else + // ... + + // // Interaction Pairs + // if (myfile.Find("spatial_gradient_order")) + // interaction_pairs_file = configFile; + // else if (myfile.Find("interaction_pairs_file")) + // myfile.iss >> interaction_pairs_file; + + // if (interaction_pairs_file.length() > 0) + // { + // // The file name should be valid so we try to read it + // Pairs_from_File(interaction_pairs_file, geometry, n_pairs, + // exchange_pairs, exchange_magnitudes, + // dmi_pairs, dmi_magnitudes, dmi_normals); + // } + //else + //{ + // Log(Log_Level::Warning, Log_Sender::IO, "Hamiltonian_Heisenberg: Default Interaction pairs have not been implemented yet."); + // throw Exception::System_not_Initialized; + // // Not implemented! + //} + }// end try + catch( ... ) + { + spirit_handle_exception_core(fmt::format( + "Unable to parse all parameters of the Micromagnetic Hamiltonian from \"{}\"", configFile)); + } + + auto hamiltonian = std::unique_ptr(new Engine::Hamiltonian_Micromagnetic( + field, field_normal, + anisotropy_tensor, + exchange_tensor, + dmi_tensor, + geometry, + spatial_gradient_order, + boundary_conditions + )); + + Log(Log_Level::Info, Log_Sender::IO, "Hamiltonian_Micromagnetic: built"); + return hamiltonian; + + }// end Hamiltonian_Micromagnetic_from_Config + + std::unique_ptr Hamiltonian_Gaussian_from_Config(const std::string configFile, std::shared_ptr geometry) { //-------------- Insert default values here ----------------------------- @@ -1486,7 +1575,7 @@ namespace IO }// end try catch( ... ) { - spirit_handle_exception_core(fmt::format("Unable to read Hamiltonian_Gaussian parameters from config file \"{}\"", configFile)); + spirit_handle_exception_core(fmt::format("Unable to read Hamiltonian_Gaussian parameters from config file \"{}\"", configFile)); } } else diff --git a/input/input.cfg b/input/input.cfg index 3ca3727e7..e6a631955 100644 --- a/input/input.cfg +++ b/input/input.cfg @@ -28,7 +28,7 @@ save_neighbours_final 0 ################## Hamiltonian ################### ### Hamiltonian Type -### (heisenberg_neighbours, heisenberg_pairs, gaussian) +### (heisenberg_neighbours, heisenberg_pairs, micromagnetic, gaussian) hamiltonian heisenberg_pairs ### Boundary_conditions (a b c): 0(open), 1(periodical) From 013d8aeeeff91f7f2a9c86f2c647b3b525a80b9b Mon Sep 17 00:00:00 2001 From: Gideon Date: Sun, 26 May 2019 12:24:57 +0200 Subject: [PATCH 02/45] Core: fixed physical constants. Added Constants_Micromagnetic namespace. Also added Joule to other constants namespaces. --- core/include/utility/Constants.hpp | 98 ++++++++++++++++++++++++------ 1 file changed, 79 insertions(+), 19 deletions(-) diff --git a/core/include/utility/Constants.hpp b/core/include/utility/Constants.hpp index 5603c195e..541736830 100644 --- a/core/include/utility/Constants.hpp +++ b/core/include/utility/Constants.hpp @@ -4,10 +4,13 @@ namespace Utility { - // Constants by convention: - // Energy scale: Millielctronvolts - // Time scale: Picoseconds - // Magnetic fields scale: Tesla + /* + Constants by convention: + Spatial scale: Angstrom + Energy scale: Millielctronvolts + Time scale: Picoseconds + Magnetic fields scale: Tesla + */ namespace Constants { // The Bohr Magneton [meV/T] @@ -23,17 +26,19 @@ namespace Utility double const hbar = 0.6582119514; // Gyromagnetic ratio of electron [rad/(ps*T)] - // Also gives the Larmor precession frequency for electron double const gamma = 0.1760859644; // Electron (Landé) g-factor = gamma * hbar / mu_B [unitless] double const g_e = 2.00231930436182; // Millirydberg [mRy/meV] - double const mRy = 1.0/13.605693009; + double const mRy = 13.605693009; // erg [erg/meV] - double const erg = 6.2415091*1e14; + double const erg = 6.2415091*1e+14; + + // Joule [Joule/meV] + double const Joule = 6.2415091*1e+21; // Pi [rad] double const Pi = 3.141592653589793238462643383279502884197169399375105820974; @@ -42,35 +47,90 @@ namespace Utility double const Pi_2 = 1.570796326794896619231321691639751442098584699687552910487; } - // Constants_mRy by convention: - // Energy scale: Millirydberg - // Time scale: Picoseconds - // Magnetic fields scale: Tesla + /* + Constants_mRy by convention: + Spatial scale: Angstrom + Energy scale: Millirydberg + Time scale: Picoseconds + Magnetic fields scale: Tesla + */ namespace Constants_mRy { // The Bohr Magneton [mRy/T] - double const mu_B = Constants::mu_B / Constants::mRy; + double const mu_B = Constants::mu_B * Constants::mRy; + + // The vacuum permeability [T^2 m^3 / mRy] + double const mu_0 = Constants::mu_0 / Constants::mRy; // The Boltzmann constant [mRy/K] double const k_B = Constants::k_B / Constants::mRy; // Planck constant [mRy*ps/rad] - double const hbar = Constants::hbar / Constants::mRy; + double const hbar = Constants::hbar * Constants::mRy; + + // Gyromagnetic ratio of electron [rad/(ps*T)] + double const gamma = Constants::gamma; + + // Electron g-factor [unitless] + double const g_e = Constants::g_e; // Millielectronvolt [meV/mRy] double const meV = 1.0 / Constants::mRy; - // Gyromagnetic ratio of electron [rad/(ps*T)] - double const gamma = 0.1760859644; + // Joule [Joule/mRy] + double const mRy = Constants::Joule / Constants::mRy; - // Electron g-factor [unitless] - double const g_e = 2.00231930436182; + // erg [erg/mRy] + double const erg = Constants::erg / Constants::mRy; // Pi [rad] - double const Pi = 3.141592653589793238462643383279502884197169399375105820974; + double const Pi = Constants::Pi; // Pi/2 [rad] - double const Pi_2 = 1.570796326794896619231321691639751442098584699687552910487; + double const Pi_2 = Constants::Pi_2; + } + + /* + Constants by micromagnetic convention (SI units): + Spatial scale: meters + Energy scale: Joule + Time scale: seconds + Magnetic fields scale: Tesla + */ + namespace Constants_Micromagnetic + { + // The Bohr Magneton [Joule/T] + double const mu_B = Constants::mu_B * Constants::Joule; + + // The vacuum permeability [T^2 m^3 / Joule] + double const mu_0 = Constants::mu_0 / Constants::Joule; + + // The Boltzmann constant [J/K] + double const k_B = Constants::k_B * Constants::Joule; + + // Planck constant [J*s/rad] + double const hbar = Constants::hbar * Constants::Joule * 1e-12; + + // Gyromagnetic ratio of electron [rad/(s*T)] + double const gamma = Constants::gamma * 1e+12; + + // Electron (Landé) g-factor = gamma * hbar / mu_B [unitless] + double const g_e = Constants::g_e; + + // meV [meV/Joule] + double const meV = 1.0 / Constants::Joule; + + // Millirydberg [mRy/Joule] + double const mRy = Constants::mRy / Constants::Joule; + + // erg [erg/Joule] + double const erg = Constants::erg / Constants::Joule; + + // Pi [rad] + double const Pi = Constants::Pi; + + // Pi/2 [rad] + double const Pi_2 = Constants::Pi_2; } } From 44454001ecf3450e2b951d759f7e7829b0eb37ab Mon Sep 17 00:00:00 2001 From: Gideon Date: Sun, 26 May 2019 12:41:10 +0200 Subject: [PATCH 03/45] Core: added stub for chaning the Hamiltonian type. This will later be used to change it at runtime, especially enabling one to switch between atomistic and micromagnetic calculations. Also potentially fixed a crashing bug in the copy constructors of Spin_System: the geometry shared pointer of the Hamiltonian was not set correctly on copy. --- core/include/Spirit/Hamiltonian.h | 22 + core/include/data/Spin_System.hpp | 26 +- .../include/engine/Hamiltonian_Heisenberg.hpp | 5 +- .../engine/Hamiltonian_Micromagnetic.hpp | 4 +- core/src/Spirit/Hamiltonian.cpp | 900 ++++++++++-------- core/src/data/Spin_System.cpp | 28 +- core/src/engine/Hamiltonian_Micromagnetic.cpp | 16 + 7 files changed, 574 insertions(+), 427 deletions(-) diff --git a/core/include/Spirit/Hamiltonian.h b/core/include/Spirit/Hamiltonian.h index 450158609..e1a35f46d 100644 --- a/core/include/Spirit/Hamiltonian.h +++ b/core/include/Spirit/Hamiltonian.h @@ -56,11 +56,33 @@ Dipole-Dipole method #define SPIRIT_DDI_METHOD_CUTOFF 3 +/* +Definition of Hamiltonian types +-------------------------------------------------------------------- +*/ + +typedef enum +{ + Hamiltonian_Heisenberg = 0, + Hamiltonian_Micromagnetic = 1, + Hamiltonian_Gaussian = 2 +} Hamiltonian_Type; + /* Setters -------------------------------------------------------------------- */ +/* +Set the kind of Hamiltonian to be used by all systems. +Can be (case is ignored): + +- Heisenberg +- Micromagnetic +- Gaussian +*/ +PREFIX void Hamiltonian_Set_Kind(State *state, Hamiltonian_Type type, int idx_chain=-1) SUFFIX; + // Set the boundary conditions along the translation directions [a, b, c] PREFIX void Hamiltonian_Set_Boundary_Conditions(State *state, const bool* periodical, int idx_image=-1, int idx_chain=-1) SUFFIX; diff --git a/core/include/data/Spin_System.hpp b/core/include/data/Spin_System.hpp index 9808ca697..e15a112c3 100644 --- a/core/include/data/Spin_System.hpp +++ b/core/include/data/Spin_System.hpp @@ -26,10 +26,10 @@ namespace Data { public: // Constructor - Spin_System(std::unique_ptr hamiltonian, + Spin_System(std::unique_ptr hamiltonian, std::shared_ptr geometry, - std::unique_ptr llg_params, - std::unique_ptr mc_params, + std::unique_ptr llg_params, + std::unique_ptr mc_params, std::unique_ptr ema_params, std::unique_ptr mmf_params, bool iteration_allowed); @@ -52,16 +52,16 @@ namespace Data std::vector> modes; // Eigenvalues of the system std::vector eigenvalues; - // Orientations of the Spins: spins[dim][nos] - std::shared_ptr spins; - // Spin Hamiltonian - std::shared_ptr hamiltonian; - // Geometric Information - std::shared_ptr geometry; - // Parameters for LLG - std::shared_ptr llg_parameters; - // Parameters for MC - std::shared_ptr mc_parameters; + // Orientations of the Spins: spins[dim][nos] + std::shared_ptr spins; + // Spin Hamiltonian + std::shared_ptr hamiltonian; + // Geometric Information + std::shared_ptr geometry; + // Parameters for LLG + std::shared_ptr llg_parameters; + // Parameters for MC + std::shared_ptr mc_parameters; // Parameters for EMA std::shared_ptr ema_parameters; // Parameters for MMF diff --git a/core/include/engine/Hamiltonian_Heisenberg.hpp b/core/include/engine/Hamiltonian_Heisenberg.hpp index b508156c0..72b22f213 100644 --- a/core/include/engine/Hamiltonian_Heisenberg.hpp +++ b/core/include/engine/Hamiltonian_Heisenberg.hpp @@ -66,6 +66,8 @@ namespace Engine // Hamiltonian name as string const std::string& Name() override; + + std::shared_ptr geometry; // ------------ Single Spin Interactions ------------ // External magnetic field across the sample @@ -111,8 +113,6 @@ namespace Engine scalarfield quadruplet_magnitudes; private: - std::shared_ptr geometry; - // ------------ Effective Field Functions ------------ // Calculate the Zeeman effective field of a single Spin void Gradient_Zeeman(vectorfield & gradient); @@ -189,6 +189,5 @@ namespace Engine field it_bounds_write_dipole; }; - } #endif \ No newline at end of file diff --git a/core/include/engine/Hamiltonian_Micromagnetic.hpp b/core/include/engine/Hamiltonian_Micromagnetic.hpp index ae1ad7491..cbc53c271 100644 --- a/core/include/engine/Hamiltonian_Micromagnetic.hpp +++ b/core/include/engine/Hamiltonian_Micromagnetic.hpp @@ -61,6 +61,8 @@ namespace Engine // Hamiltonian name as string const std::string& Name() override; + std::shared_ptr geometry; + // ------------ ... ------------ int spatial_gradient_order; intfield boundary_conditions; @@ -78,8 +80,6 @@ namespace Engine Matrix3 dmi_tensor; private: - std::shared_ptr geometry; - // ------------ Effective Field Functions ------------ // Calculate the Zeeman effective field of a single Spin void Gradient_Zeeman(vectorfield & gradient); diff --git a/core/src/Spirit/Hamiltonian.cpp b/core/src/Spirit/Hamiltonian.cpp index 9955e6b14..8577d4fe6 100644 --- a/core/src/Spirit/Hamiltonian.cpp +++ b/core/src/Spirit/Hamiltonian.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -19,297 +20,406 @@ using namespace Utility; /*---------------------------------- Set Parameters ---------------------------------------------------- */ /*------------------------------------------------------------------------------------------------------ */ -void Hamiltonian_Set_Boundary_Conditions(State *state, const bool * periodical, int idx_image, int idx_chain) noexcept +void Hamiltonian_Set_Kind(State *state, Hamiltonian_Type type, int idx_chain) noexcept +try { - try + // TODO + if( type != Hamiltonian_Heisenberg && type != Hamiltonian_Micromagnetic && type != Hamiltonian_Gaussian ) + { + Log( Utility::Log_Level::Error, Utility::Log_Sender::API, fmt::format( + "Hamiltonian_Set_Kind: unknown type index {}", int(type)), -1, idx_chain ); + return; + } + + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + int idx_image = -1; + from_indices( state, idx_image, idx_chain, image, chain ); + + idx_image = 0; + std::string kind_str = ""; + if( type == Hamiltonian_Heisenberg ) + { + kind_str = "Heisenberg"; + + if( kind_str == image->hamiltonian->Name() ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, fmt::format( + "Hamiltonian is already of {} kind. Not doing anything.", kind_str), -1, idx_chain ); + return; + } + } + else if( type == Hamiltonian_Micromagnetic ) + { + kind_str = "Micromagnetic"; + + if( kind_str == image->hamiltonian->Name() ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, fmt::format( + "Hamiltonian is already of {} kind. Not doing anything.", kind_str), -1, idx_chain ); + return; + } + } + else if( type == Hamiltonian_Gaussian ) + { + kind_str = "Gaussian"; + + if( kind_str == image->hamiltonian->Name() ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, fmt::format( + "Hamiltonian is already of {} kind. Not doing anything.", kind_str), -1, idx_chain ); + return; + } + } + + for( auto& image : chain->images ) { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - image->Lock(); try { - image->hamiltonian->boundary_conditions[0] = periodical[0]; - image->hamiltonian->boundary_conditions[1] = periodical[1]; - image->hamiltonian->boundary_conditions[2] = periodical[2]; + if( type == Hamiltonian_Heisenberg ) + { + // TODO + // image->hamiltonian = std::shared_ptr<...>(new Engine::Hamiltonian_Heisenberg(...)); + + Log( Utility::Log_Level::Error, Utility::Log_Sender::API, fmt::format( + "Cannot yet set Hamiltonian kind to {} - this function is only a stub!", kind_str), -1, idx_chain ); + return; + } + else if( type == Hamiltonian_Micromagnetic ) + { + // TODO + image->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Micromagnetic( + 0, Vector3{0, 0, 1}, + Matrix3::Zero(), + 0, + 0, + image->geometry, + 2, + image->hamiltonian->boundary_conditions)); + } + else if( type == Hamiltonian_Gaussian ) + { + // TODO + // image->hamiltonian = std::shared_ptr<...>(new Engine::Hamiltonian_Gaussian(...)); + + Log( Utility::Log_Level::Error, Utility::Log_Sender::API, fmt::format( + "Cannot yet set Hamiltonian kind to {} - this function is only a stub!", kind_str), -1, idx_chain ); + return; + } } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } image->Unlock(); + ++idx_image; + } + + Log( Utility::Log_Level::All, Utility::Log_Sender::API, fmt::format( + "Set Hamiltonian kind to {}", kind_str), -1, idx_chain ); +} +catch( ... ) +{ + spirit_handle_exception_api(-1, idx_chain); +} - Log( Utility::Log_Level::Info, Utility::Log_Sender::API, - fmt::format("Set boundary conditions to {} {} {}", periodical[0], periodical[1], periodical[2]), - idx_image, idx_chain ); +void Hamiltonian_Set_Boundary_Conditions(State *state, const bool * periodical, int idx_image, int idx_chain) noexcept +try +{ + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + + image->Lock(); + try + { + image->hamiltonian->boundary_conditions[0] = periodical[0]; + image->hamiltonian->boundary_conditions[1] = periodical[1]; + image->hamiltonian->boundary_conditions[2] = periodical[2]; } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } + image->Unlock(); + + Log( Utility::Log_Level::Info, Utility::Log_Sender::API, + fmt::format("Set boundary conditions to {} {} {}", periodical[0], periodical[1], periodical[2]), + idx_image, idx_chain ); +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Set_Field(State *state, float magnitude, const float * normal, int idx_image, int idx_chain) noexcept +try { + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + + // Lock mutex because simulations may be running + image->Lock(); + try { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - // Lock mutex because simulations may be running - image->Lock(); - - try + // Set + if (image->hamiltonian->Name() == "Heisenberg") { - // Set - if (image->hamiltonian->Name() == "Heisenberg") - { - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - - // Normals - Vector3 new_normal{normal[0], normal[1], normal[2]}; - new_normal.normalize(); - - // Into the Hamiltonian - ham->external_field_magnitude = magnitude * Constants::mu_B; - ham->external_field_normal = new_normal; - - // Update Energies - ham->Update_Energy_Contributions(); - - Log( Utility::Log_Level::Info, Utility::Log_Sender::API, - fmt::format("Set external field to {}, direction ({}, {}, {})", magnitude, normal[0], normal[1], normal[2]), - idx_image, idx_chain ); - } - else - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - "External field cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + + // Normals + Vector3 new_normal{normal[0], normal[1], normal[2]}; + new_normal.normalize(); + + // Into the Hamiltonian + ham->external_field_magnitude = magnitude * Constants::mu_B; + ham->external_field_normal = new_normal; + + // Update Energies + ham->Update_Energy_Contributions(); } - catch( ... ) + else if (image->hamiltonian->Name() == "Micromagnetic") { - spirit_handle_exception_api(idx_image, idx_chain); + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + + // Normals + Vector3 new_normal{normal[0], normal[1], normal[2]}; + new_normal.normalize(); + + // Into the Hamiltonian + ham->external_field_magnitude = magnitude * Constants::mu_B; + ham->external_field_normal = new_normal; + + // Update Energies + ham->Update_Energy_Contributions(); } - - // Unlock mutex - image->Unlock(); + else + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "External field cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } + + // Unlock mutex + image->Unlock(); + + Log( Utility::Log_Level::Info, Utility::Log_Sender::API, + fmt::format("Set external field to {}, direction ({}, {}, {})", magnitude, normal[0], normal[1], normal[2]), + idx_image, idx_chain ); +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Set_Anisotropy( State *state, float magnitude, const float * normal, int idx_image, int idx_chain) noexcept +try { - try - { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - image->Lock(); + image->Lock(); - try + try + { + if (image->hamiltonian->Name() == "Heisenberg") { - if (image->hamiltonian->Name() == "Heisenberg") + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + int nos = image->nos; + int n_cell_atoms = image->geometry->n_cell_atoms; + + // Indices and Magnitudes + intfield new_indices(n_cell_atoms); + scalarfield new_magnitudes(n_cell_atoms); + for (int i = 0; ihamiltonian.get(); - int nos = image->nos; - int n_cell_atoms = image->geometry->n_cell_atoms; - - // Indices and Magnitudes - intfield new_indices(n_cell_atoms); - scalarfield new_magnitudes(n_cell_atoms); - for (int i = 0; ianisotropy_indices = new_indices; - ham->anisotropy_magnitudes = new_magnitudes; - ham->anisotropy_normals = new_normals; - - // Update Energies - ham->Update_Energy_Contributions(); - - Log( Utility::Log_Level::Info, Utility::Log_Sender::API, - fmt::format("Set anisotropy to {}, direction ({}, {}, {})", magnitude, normal[0], normal[1], normal[2]), - idx_image, idx_chain ); + new_indices[i] = i; + new_magnitudes[i] = magnitude; } - else - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - "Anisotropy cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain); - } - catch( ... ) - { - spirit_handle_exception_api(idx_image, idx_chain); + // Normals + Vector3 new_normal{ normal[0], normal[1], normal[2] }; + new_normal.normalize(); + vectorfield new_normals(nos, new_normal); + + // Into the Hamiltonian + ham->anisotropy_indices = new_indices; + ham->anisotropy_magnitudes = new_magnitudes; + ham->anisotropy_normals = new_normals; + + // Update Energies + ham->Update_Energy_Contributions(); + + Log( Utility::Log_Level::Info, Utility::Log_Sender::API, + fmt::format("Set anisotropy to {}, direction ({}, {}, {})", magnitude, normal[0], normal[1], normal[2]), + idx_image, idx_chain ); } - - image->Unlock(); + else + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Anisotropy cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain); } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } + + image->Unlock(); +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Set_Exchange(State *state, int n_shells, const float* jij, int idx_image, int idx_chain) noexcept +try { + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + + image->Lock(); + try { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - image->Lock(); - - try - { - if (image->hamiltonian->Name() == "Heisenberg") - { - // Update the Hamiltonian - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - ham->exchange_shell_magnitudes = scalarfield(jij, jij + n_shells); - ham->exchange_pairs_in = pairfield(0); - ham->exchange_magnitudes_in = scalarfield(0); - ham->Update_Interactions(); - - std::string message = fmt::format("Set exchange to {} shells", n_shells); - if (n_shells > 0) message += fmt::format(" Jij[0] = {}", jij[0]); - Log(Utility::Log_Level::Info, Utility::Log_Sender::API, message, idx_image, idx_chain); - } - else - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - "Exchange cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); - } - catch( ... ) + if (image->hamiltonian->Name() == "Heisenberg") { - spirit_handle_exception_api(idx_image, idx_chain); + // Update the Hamiltonian + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + ham->exchange_shell_magnitudes = scalarfield(jij, jij + n_shells); + ham->exchange_pairs_in = pairfield(0); + ham->exchange_magnitudes_in = scalarfield(0); + ham->Update_Interactions(); + + std::string message = fmt::format("Set exchange to {} shells", n_shells); + if (n_shells > 0) message += fmt::format(" Jij[0] = {}", jij[0]); + Log(Utility::Log_Level::Info, Utility::Log_Sender::API, message, idx_image, idx_chain); } - - image->Unlock(); + else + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Exchange cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } + + image->Unlock(); +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Set_DMI(State *state, int n_shells, const float * dij, int chirality, int idx_image, int idx_chain) noexcept +try { - try - { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - image->Lock(); + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + image->Lock(); - if( chirality != SPIRIT_CHIRALITY_BLOCH && - chirality != SPIRIT_CHIRALITY_NEEL && - chirality != SPIRIT_CHIRALITY_BLOCH_INVERSE && - chirality != SPIRIT_CHIRALITY_NEEL_INVERSE ) - { - Log( Utility::Log_Level::Error, Utility::Log_Sender::API, fmt::format( - "Hamiltonian_Set_DMI: Invalid DM chirality {}", chirality), idx_image, idx_chain ); - return; - } + if( chirality != SPIRIT_CHIRALITY_BLOCH && + chirality != SPIRIT_CHIRALITY_NEEL && + chirality != SPIRIT_CHIRALITY_BLOCH_INVERSE && + chirality != SPIRIT_CHIRALITY_NEEL_INVERSE ) + { + Log( Utility::Log_Level::Error, Utility::Log_Sender::API, fmt::format( + "Hamiltonian_Set_DMI: Invalid DM chirality {}", chirality), idx_image, idx_chain ); + return; + } - try - { - if (image->hamiltonian->Name() == "Heisenberg") - { - // Update the Hamiltonian - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - ham->dmi_shell_magnitudes = scalarfield(dij, dij + n_shells); - ham->dmi_shell_chirality = chirality; - ham->dmi_pairs_in = pairfield(0); - ham->dmi_magnitudes_in = scalarfield(0); - ham->dmi_normals_in = vectorfield(0); - ham->Update_Interactions(); - - std::string message = fmt::format("Set dmi to {} shells", n_shells); - if (n_shells > 0) message += fmt::format(" Dij[0] = {}", dij[0]); - Log(Utility::Log_Level::Info, Utility::Log_Sender::API, message, idx_image, idx_chain); - } - else - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, "DMI cannot be set on " + - image->hamiltonian->Name(), idx_image, idx_chain ); - } - catch( ... ) + try + { + if (image->hamiltonian->Name() == "Heisenberg") { - spirit_handle_exception_api(idx_image, idx_chain); + // Update the Hamiltonian + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + ham->dmi_shell_magnitudes = scalarfield(dij, dij + n_shells); + ham->dmi_shell_chirality = chirality; + ham->dmi_pairs_in = pairfield(0); + ham->dmi_magnitudes_in = scalarfield(0); + ham->dmi_normals_in = vectorfield(0); + ham->Update_Interactions(); + + std::string message = fmt::format("Set dmi to {} shells", n_shells); + if (n_shells > 0) message += fmt::format(" Dij[0] = {}", dij[0]); + Log(Utility::Log_Level::Info, Utility::Log_Sender::API, message, idx_image, idx_chain); } - - image->Unlock(); + else + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, "DMI cannot be set on " + + image->hamiltonian->Name(), idx_image, idx_chain ); } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } + + image->Unlock(); +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Set_DDI(State *state, int ddi_method, int n_periodic_images[3], float cutoff_radius, int idx_image, int idx_chain) noexcept +try { + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + image->Lock(); + try { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - image->Lock(); - - try - { - if (image->hamiltonian->Name() == "Heisenberg") - { - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - - ham->ddi_method = Engine::DDI_Method(ddi_method); - ham->ddi_n_periodic_images[0] = n_periodic_images[0]; - ham->ddi_n_periodic_images[1] = n_periodic_images[1]; - ham->ddi_n_periodic_images[2] = n_periodic_images[2]; - ham->ddi_cutoff_radius = cutoff_radius; - ham->Update_Interactions(); - - Log( Utility::Log_Level::Info, Utility::Log_Sender::API, fmt::format( - "Set ddi to method {}, periodic images {} {} {} and cutoff radius {}", - ddi_method, n_periodic_images[0], n_periodic_images[1], n_periodic_images[2], cutoff_radius), idx_image, idx_chain ); - } - else - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, "DDI cannot be set on " + - image->hamiltonian->Name(), idx_image, idx_chain ); - } - catch( ... ) + if (image->hamiltonian->Name() == "Heisenberg") { - spirit_handle_exception_api(idx_image, idx_chain); - } + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - image->Unlock(); + ham->ddi_method = Engine::DDI_Method(ddi_method); + ham->ddi_n_periodic_images[0] = n_periodic_images[0]; + ham->ddi_n_periodic_images[1] = n_periodic_images[1]; + ham->ddi_n_periodic_images[2] = n_periodic_images[2]; + ham->ddi_cutoff_radius = cutoff_radius; + ham->Update_Interactions(); + + Log( Utility::Log_Level::Info, Utility::Log_Sender::API, fmt::format( + "Set ddi to method {}, periodic images {} {} {} and cutoff radius {}", + ddi_method, n_periodic_images[0], n_periodic_images[1], n_periodic_images[2], cutoff_radius), idx_image, idx_chain ); + } + else + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, "DDI cannot be set on " + + image->hamiltonian->Name(), idx_image, idx_chain ); } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } + + image->Unlock(); +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); } /*------------------------------------------------------------------------------------------------------ */ @@ -317,264 +427,244 @@ void Hamiltonian_Set_DDI(State *state, int ddi_method, int n_periodic_images[3], /*------------------------------------------------------------------------------------------------------ */ const char * Hamiltonian_Get_Name(State * state, int idx_image, int idx_chain) noexcept +try { - try - { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - return image->hamiltonian->Name().c_str(); - } - catch( ... ) - { - spirit_handle_exception_api(idx_image, idx_chain); - return nullptr; - } + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + + return image->hamiltonian->Name().c_str(); +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); + return nullptr; } void Hamiltonian_Get_Boundary_Conditions(State *state, bool * periodical, int idx_image, int idx_chain) noexcept +try { - try - { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - periodical[0] = image->hamiltonian->boundary_conditions[0]; - periodical[1] = image->hamiltonian->boundary_conditions[1]; - periodical[2] = image->hamiltonian->boundary_conditions[2]; - } - catch( ... ) - { - spirit_handle_exception_api(idx_image, idx_chain); - } + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + + periodical[0] = image->hamiltonian->boundary_conditions[0]; + periodical[1] = image->hamiltonian->boundary_conditions[1]; + periodical[2] = image->hamiltonian->boundary_conditions[2]; +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Get_Field(State *state, float * magnitude, float * normal, int idx_image, int idx_chain) noexcept +try { - try + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + + if (image->hamiltonian->Name() == "Heisenberg") { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - if (image->hamiltonian->Name() == "Heisenberg") - { - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - if (ham->external_field_magnitude > 0) - { - // Magnitude - *magnitude = (float)(ham->external_field_magnitude / Constants::mu_B); + if (ham->external_field_magnitude > 0) + { + // Magnitude + *magnitude = (float)(ham->external_field_magnitude / Constants::mu_B); - // Normal - normal[0] = (float)ham->external_field_normal[0]; - normal[1] = (float)ham->external_field_normal[1]; - normal[2] = (float)ham->external_field_normal[2]; - } - else - { - *magnitude = 0; - normal[0] = 0; - normal[1] = 0; - normal[2] = 1; - } + // Normal + normal[0] = (float)ham->external_field_normal[0]; + normal[1] = (float)ham->external_field_normal[1]; + normal[2] = (float)ham->external_field_normal[2]; + } + else + { + *magnitude = 0; + normal[0] = 0; + normal[1] = 0; + normal[2] = 1; } } - catch( ... ) - { - spirit_handle_exception_api(idx_image, idx_chain); - } +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Get_Anisotropy(State *state, float * magnitude, float * normal, int idx_image, int idx_chain) noexcept +try { - try - { - std::shared_ptr image; - std::shared_ptr chain; + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + + if (image->hamiltonian->Name() == "Heisenberg") + { + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - if (image->hamiltonian->Name() == "Heisenberg") + if (ham->anisotropy_indices.size() > 0) { - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - - if (ham->anisotropy_indices.size() > 0) - { - // Magnitude - *magnitude = (float)ham->anisotropy_magnitudes[0]; + // Magnitude + *magnitude = (float)ham->anisotropy_magnitudes[0]; - // Normal - normal[0] = (float)ham->anisotropy_normals[0][0]; - normal[1] = (float)ham->anisotropy_normals[0][1]; - normal[2] = (float)ham->anisotropy_normals[0][2]; - } - else - { - *magnitude = 0; - normal[0] = 0; - normal[1] = 0; - normal[2] = 1; - } + // Normal + normal[0] = (float)ham->anisotropy_normals[0][0]; + normal[1] = (float)ham->anisotropy_normals[0][1]; + normal[2] = (float)ham->anisotropy_normals[0][2]; + } + else + { + *magnitude = 0; + normal[0] = 0; + normal[1] = 0; + normal[2] = 1; } } - catch( ... ) - { - spirit_handle_exception_api(idx_image, idx_chain); - } +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Get_Exchange_Shells(State *state, int * n_shells, float * jij, int idx_image, int idx_chain) noexcept +try { - try - { - std::shared_ptr image; - std::shared_ptr chain; + std::shared_ptr image; + std::shared_ptr chain; - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - if (image->hamiltonian->Name() == "Heisenberg") - { - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + if (image->hamiltonian->Name() == "Heisenberg") + { + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - *n_shells = ham->exchange_shell_magnitudes.size(); + *n_shells = ham->exchange_shell_magnitudes.size(); - // Note the array needs to be correctly allocated beforehand! - for (int i=0; iexchange_shell_magnitudes.size(); ++i) - { - jij[i] = (float)ham->exchange_shell_magnitudes[i]; - } + // Note the array needs to be correctly allocated beforehand! + for (int i=0; iexchange_shell_magnitudes.size(); ++i) + { + jij[i] = (float)ham->exchange_shell_magnitudes[i]; } } - catch( ... ) - { - spirit_handle_exception_api(idx_image, idx_chain); - } +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); } int Hamiltonian_Get_Exchange_N_Pairs(State *state, int idx_image, int idx_chain) noexcept +try { - try - { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - image->hamiltonian->Name() + " Hamiltonian: fetching exchange pairs is not yet implemented...", idx_image, idx_chain ); - return 0; - } - catch( ... ) - { - spirit_handle_exception_api(idx_image, idx_chain); - return 0; - } + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + image->hamiltonian->Name() + " Hamiltonian: fetching exchange pairs is not yet implemented...", idx_image, idx_chain ); + return 0; +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); + return 0; } void Hamiltonian_Get_Exchange_Pairs(State *state, float * idx[2], float * translations[3], float * Jij, int idx_image, int idx_chain) noexcept +try { - try - { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - image->hamiltonian->Name() + " Hamiltonian: fetching exchange pairs is not yet implemented...", idx_image, idx_chain ); - } - catch( ... ) - { - spirit_handle_exception_api(idx_image, idx_chain); - } + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + image->hamiltonian->Name() + " Hamiltonian: fetching exchange pairs is not yet implemented...", idx_image, idx_chain ); +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Get_DMI_Shells(State *state, int * n_shells, float * dij, int * chirality, int idx_image, int idx_chain) noexcept +try { - try - { - std::shared_ptr image; - std::shared_ptr chain; + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + + if (image->hamiltonian->Name() == "Heisenberg") + { + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); + *n_shells = ham->dmi_shell_magnitudes.size(); + *chirality = ham->dmi_shell_chirality; - if (image->hamiltonian->Name() == "Heisenberg") + for (int i=0; i<*n_shells; ++i) { - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - - *n_shells = ham->dmi_shell_magnitudes.size(); - *chirality = ham->dmi_shell_chirality; - - for (int i=0; i<*n_shells; ++i) - { - dij[i] = (float)ham->dmi_shell_magnitudes[i]; - } + dij[i] = (float)ham->dmi_shell_magnitudes[i]; } } - catch( ... ) - { - spirit_handle_exception_api(idx_image, idx_chain); - } +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); } int Hamiltonian_Get_DMI_N_Pairs(State *state, int idx_image, int idx_chain) noexcept +try { - try - { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - image->hamiltonian->Name() + " Hamiltonian: fetching DMI pairs is not yet implemented...", idx_image, idx_chain ); - return 0; - } - catch( ... ) - { - spirit_handle_exception_api(idx_image, idx_chain); - return 0; - } + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + image->hamiltonian->Name() + " Hamiltonian: fetching DMI pairs is not yet implemented...", idx_image, idx_chain ); + return 0; +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); + return 0; } void Hamiltonian_Get_DDI(State *state, int * ddi_method, int n_periodic_images[3], float * cutoff_radius, int idx_image, int idx_chain) noexcept +try { - try - { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - if (image->hamiltonian->Name() == "Heisenberg") - { - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - - *ddi_method = (int)ham->ddi_method; - n_periodic_images[0] = (int)ham->ddi_n_periodic_images[0]; - n_periodic_images[1] = (int)ham->ddi_n_periodic_images[1]; - n_periodic_images[2] = (int)ham->ddi_n_periodic_images[2]; - *cutoff_radius = (float)ham->ddi_cutoff_radius; - } - } - catch( ... ) - { - spirit_handle_exception_api(idx_image, idx_chain); + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + + if (image->hamiltonian->Name() == "Heisenberg") + { + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + + *ddi_method = (int)ham->ddi_method; + n_periodic_images[0] = (int)ham->ddi_n_periodic_images[0]; + n_periodic_images[1] = (int)ham->ddi_n_periodic_images[1]; + n_periodic_images[2] = (int)ham->ddi_n_periodic_images[2]; + *cutoff_radius = (float)ham->ddi_cutoff_radius; } +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); } \ No newline at end of file diff --git a/core/src/data/Spin_System.cpp b/core/src/data/Spin_System.cpp index d2adfd41b..44873e0cc 100644 --- a/core/src/data/Spin_System.cpp +++ b/core/src/data/Spin_System.cpp @@ -60,16 +60,24 @@ namespace Data this->E = other.E; this->E_array = other.E_array; this->effective_field = other.effective_field; + this->M = other.M; this->geometry = std::shared_ptr(new Data::Geometry(*other.geometry)); if (other.hamiltonian->Name() == "Heisenberg") { - this->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Heisenberg(*(Engine::Hamiltonian_Heisenberg*)(other.hamiltonian.get()))); + this->hamiltonian = std::shared_ptr( + new Engine::Hamiltonian_Heisenberg(*(Engine::Hamiltonian_Heisenberg*)(other.hamiltonian.get()))); + } + else if (other.hamiltonian->Name() == "Micromagnetic") + { + this->hamiltonian = std::shared_ptr( + new Engine::Hamiltonian_Micromagnetic(*(Engine::Hamiltonian_Micromagnetic*)(other.hamiltonian.get()))); } else if (other.hamiltonian->Name() == "Gaussian") { - this->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Gaussian(*(Engine::Hamiltonian_Gaussian*)(other.hamiltonian.get()))); + this->hamiltonian = std::shared_ptr( + new Engine::Hamiltonian_Gaussian(*(Engine::Hamiltonian_Gaussian*)(other.hamiltonian.get()))); } this->llg_parameters = std::shared_ptr(new Data::Parameters_Method_LLG(*other.llg_parameters)); @@ -103,16 +111,28 @@ namespace Data this->E = other.E; this->E_array = other.E_array; this->effective_field = other.effective_field; + this->M = other.M; this->geometry = std::shared_ptr(new Data::Geometry(*other.geometry)); if (other.hamiltonian->Name() == "Heisenberg") { - this->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Heisenberg(*(Engine::Hamiltonian_Heisenberg*)(other.hamiltonian.get()))); + auto ham = std::shared_ptr( + new Engine::Hamiltonian_Heisenberg(*(Engine::Hamiltonian_Heisenberg*)(other.hamiltonian.get()))); + ham->geometry = this->geometry; + this->hamiltonian = ham; + } + else if (other.hamiltonian->Name() == "Micromagnetic") + { + auto ham = std::shared_ptr( + new Engine::Hamiltonian_Micromagnetic(*(Engine::Hamiltonian_Micromagnetic*)(other.hamiltonian.get()))); + ham->geometry = this->geometry; + this->hamiltonian = ham; } else if (other.hamiltonian->Name() == "Gaussian") { - this->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Gaussian(*(Engine::Hamiltonian_Gaussian*)(other.hamiltonian.get()))); + this->hamiltonian = std::shared_ptr( + new Engine::Hamiltonian_Gaussian(*(Engine::Hamiltonian_Gaussian*)(other.hamiltonian.get()))); } this->llg_parameters = std::shared_ptr(new Data::Parameters_Method_LLG(*other.llg_parameters)); diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cpp b/core/src/engine/Hamiltonian_Micromagnetic.cpp index 2fca744a4..219d604d8 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cpp +++ b/core/src/engine/Hamiltonian_Micromagnetic.cpp @@ -37,6 +37,22 @@ namespace Engine this->Update_Interactions(); } + Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( + scalar external_field_magnitude, Vector3 external_field_normal, + Matrix3 anisotropy_tensor, + scalar exchange_constant, + scalar dmi_constant, + std::shared_ptr geometry, + int spatial_gradient_order, + intfield boundary_conditions + ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), geometry(geometry), + external_field_magnitude(external_field_magnitude), external_field_normal(external_field_normal), + anisotropy_tensor(anisotropy_tensor) + { + // Generate interaction pairs, constants etc. + this->Update_Interactions(); + } + void Hamiltonian_Micromagnetic::Update_Interactions() { #if defined(SPIRIT_USE_OPENMP) From b6a26d166a64f4493e3e873ca1be19e9ba5e01ce Mon Sep 17 00:00:00 2001 From: Gideon Date: Mon, 27 May 2019 10:09:16 +0200 Subject: [PATCH 04/45] Core: updated API and tests for constants. --- core/include/Spirit/Constants.h | 15 ++++++++++++--- core/python/spirit/constants.py | 26 +++++++++++++++++++------- core/python/test/constants.py | 26 +++++++++++++++++--------- core/src/Spirit/Constants.cpp | 23 +++++++++++++++++++---- 4 files changed, 67 insertions(+), 23 deletions(-) diff --git a/core/include/Spirit/Constants.h b/core/include/Spirit/Constants.h index f36720172..43b2b2dad 100644 --- a/core/include/Spirit/Constants.h +++ b/core/include/Spirit/Constants.h @@ -28,17 +28,26 @@ PREFIX scalar Constants_k_B() SUFFIX; // Planck constant [meV*ps / rad] PREFIX scalar Constants_hbar() SUFFIX; -// Millirydberg [mRy / meV] -PREFIX scalar Constants_mRy() SUFFIX; - // Gyromagnetic ratio of electron [rad / (s*T)] PREFIX scalar Constants_gamma() SUFFIX; // Electron g-factor [unitless] PREFIX scalar Constants_g_e() SUFFIX; +// Millirydberg [mRy / meV] +PREFIX scalar Constants_mRy() SUFFIX; + +// Erg [erg / meV] +PREFIX scalar Constants_erg() SUFFIX; + +// Joule [J / meV] +PREFIX scalar Constants_Joule() SUFFIX; + // Pi [rad] PREFIX scalar Constants_Pi() SUFFIX; +// Pi/2 [rad] +PREFIX scalar Constants_Pi_2() SUFFIX; + #include "DLL_Undefine_Export.h" #endif \ No newline at end of file diff --git a/core/python/spirit/constants.py b/core/python/spirit/constants.py index 203710c0f..23435c808 100644 --- a/core/python/spirit/constants.py +++ b/core/python/spirit/constants.py @@ -35,12 +35,6 @@ hbar = _hbar() """Planck's constant [meV*ps / rad]""" -_mRy = _spirit.Constants_mRy -_mRy.argtypes = None -_mRy.restype = scalar -mRy = _mRy() -"""Millirydberg [mRy / meV]""" - _gamma = _spirit.Constants_gamma _gamma.argtypes = None _gamma.restype = scalar @@ -57,4 +51,22 @@ _Pi.argtypes = None _Pi.restype = scalar pi = _Pi() -"""Pi [rad]""" \ No newline at end of file +"""Pi [rad]""" + +_mRy = _spirit.Constants_mRy +_mRy.argtypes = None +_mRy.restype = scalar +mRy = _mRy() +"""MilliRydberg [mRy / meV]""" + +_erg = _spirit.Constants_erg +_erg.argtypes = None +_erg.restype = scalar +erg = _erg() +"""Erg [erg / meV]""" + +_Joule = _spirit.Constants_Joule +_Joule.argtypes = None +_Joule.restype = scalar +Joule = _Joule() +"""Joule [J / meV]""" \ No newline at end of file diff --git a/core/python/test/constants.py b/core/python/test/constants.py index 7efee4716..90e6bfaf9 100644 --- a/core/python/test/constants.py +++ b/core/python/test/constants.py @@ -18,27 +18,35 @@ class TestConstants(unittest.TestCase): Energy: milli-eV Time: pico-sec Magnetic field: Tesla """ - + # TODO: Find a way to easily switch between units system (eg milli-eV to milli-Ry) and test it def test_Bohr_magneton(self): self.assertEqual( scalar(constants.mu_B).value, scalar(0.057883817555).value ) - + + def test_vacuum_permeability(self): + self.assertEqual( scalar(constants.mu_0).value, scalar(2.0133545*1e-28).value ) + def test_Boltzmann_Constant(self): self.assertEqual( scalar(constants.k_B).value, scalar(0.08617330350).value ) - + def test_Planck_constant(self): self.assertEqual( scalar(constants.hbar).value, scalar(0.6582119514).value ) - - def test_millirydberg(self): - self.assertEqual( scalar(constants.mRy).value, scalar(1.0/13.605693009).value ) - + def test_gyromagnetic_ratio_of_electron(self): self.assertEqual( scalar(constants.gamma).value, scalar(0.1760859644).value ) - + def test_electron_g_factor(self): self.assertEqual( scalar(constants.g_e).value, scalar(2.00231930436182).value ) - + + def test_milliRydberg(self): + self.assertEqual( scalar(constants.mRy).value, scalar(13.605693009).value ) + + def test_erg(self): + self.assertEqual( scalar(constants.erg).value, scalar(6.2415091*1e+14).value ) + + def test_Joule(self): + self.assertEqual( scalar(constants.Joule).value, scalar(6.2415091*1e+21).value ) ######### diff --git a/core/src/Spirit/Constants.cpp b/core/src/Spirit/Constants.cpp index 167ab57f4..e5f4f6d06 100644 --- a/core/src/Spirit/Constants.cpp +++ b/core/src/Spirit/Constants.cpp @@ -21,22 +21,37 @@ scalar Constants_hbar() noexcept return (scalar)Utility::Constants::hbar; } +scalar Constants_gamma() noexcept +{ + return (scalar)Utility::Constants::gamma; +} + +scalar Constants_g_e() noexcept +{ + return (scalar)Utility::Constants::g_e; +} + scalar Constants_mRy() noexcept { return (scalar)Utility::Constants::mRy; } -scalar Constants_gamma() noexcept +scalar Constants_erg() noexcept { - return (scalar)Utility::Constants::gamma; + return (scalar)Utility::Constants::erg; } -scalar Constants_g_e() noexcept +scalar Constants_Joule() noexcept { - return (scalar)Utility::Constants::g_e; + return (scalar)Utility::Constants::Joule; } scalar Constants_Pi() noexcept { return (scalar)Utility::Constants::Pi; +} + +scalar Constants_Pi_2() noexcept +{ + return (scalar)Utility::Constants::Pi_2; } \ No newline at end of file From 5e5b80163bf8ca36827c81bf9f105b517cb5a95a Mon Sep 17 00:00:00 2001 From: Gideon Date: Mon, 27 May 2019 10:23:23 +0200 Subject: [PATCH 05/45] UI-QT: added widget for micromagnetic Hamiltonian. Also added the ability to switch between Hamiltonians from the GUI. --- ui-cpp/include/CMakeLists.txt | 39 +- ui-cpp/include/HamiltonianGaussianWidget.hpp | 14 +- .../include/HamiltonianHeisenbergWidget.hpp | 69 +- .../HamiltonianMicromagneticWidget.hpp | 44 + ui-cpp/include/SettingsWidget.hpp | 38 +- ui-cpp/src/CMakeLists.txt | 39 +- ui-cpp/src/HamiltonianGaussianWidget.cpp | 27 +- ui-cpp/src/HamiltonianHeisenbergWidget.cpp | 42 +- ui-cpp/src/HamiltonianMicromagneticWidget.cpp | 198 +++++ ui-cpp/src/SettingsWidget.cpp | 153 ++-- ui-cpp/ui/CMakeLists.txt | 1 + ui-cpp/ui/HamiltonianGaussianWidget.ui | 114 ++- ui-cpp/ui/HamiltonianHeisenbergWidget.ui | 133 ++- ui-cpp/ui/HamiltonianMicromagneticWidget.ui | 816 ++++++++++++++++++ ui-cpp/ui/VisualisationSettingsWidget.ui | 40 +- 15 files changed, 1518 insertions(+), 249 deletions(-) create mode 100644 ui-cpp/include/HamiltonianMicromagneticWidget.hpp create mode 100644 ui-cpp/src/HamiltonianMicromagneticWidget.cpp create mode 100644 ui-cpp/ui/HamiltonianMicromagneticWidget.ui diff --git a/ui-cpp/include/CMakeLists.txt b/ui-cpp/include/CMakeLists.txt index defca40ce..481409cde 100644 --- a/ui-cpp/include/CMakeLists.txt +++ b/ui-cpp/include/CMakeLists.txt @@ -1,21 +1,22 @@ set(HEADER_UI_QT_ROOT - ${HEADER_UI_QT_ROOT} - ${CMAKE_CURRENT_SOURCE_DIR}/SpinWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/ControlWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/HamiltonianHeisenbergWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/HamiltonianGaussianWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/ConfigurationsWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/GeometryWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/MouseDecoratorWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/SettingsWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/ParametersWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/VisualisationSettingsWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/PlotsWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/DebugWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/PlotWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/IsosurfaceWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/InfoWidget.hpp - ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt - PARENT_SCOPE + ${HEADER_UI_QT_ROOT} + ${CMAKE_CURRENT_SOURCE_DIR}/SpinWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/ControlWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/HamiltonianHeisenbergWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/HamiltonianMicromagneticWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/HamiltonianGaussianWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/ConfigurationsWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/GeometryWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/MouseDecoratorWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/SettingsWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParametersWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/VisualisationSettingsWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/PlotsWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/DebugWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/PlotWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/IsosurfaceWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/InfoWidget.hpp + ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt + PARENT_SCOPE ) \ No newline at end of file diff --git a/ui-cpp/include/HamiltonianGaussianWidget.hpp b/ui-cpp/include/HamiltonianGaussianWidget.hpp index 916c70aca..d532814a7 100644 --- a/ui-cpp/include/HamiltonianGaussianWidget.hpp +++ b/ui-cpp/include/HamiltonianGaussianWidget.hpp @@ -7,6 +7,8 @@ #include #include +#include + #include "SpinWidget.hpp" #include "IsosurfaceWidget.hpp" //#include "SettingsWidget.hpp" @@ -20,10 +22,16 @@ class HamiltonianGaussianWidget : public QWidget, private Ui::HamiltonianGaussia Q_OBJECT public: - HamiltonianGaussianWidget(std::shared_ptr state); - void updateData(); + HamiltonianGaussianWidget(std::shared_ptr state); + void updateData(); + + std::shared_ptr state; + +signals: + void hamiltonianChanged(Hamiltonian_Type newType); - std::shared_ptr state; +private slots: + void clicked_change_hamiltonian(); }; #endif \ No newline at end of file diff --git a/ui-cpp/include/HamiltonianHeisenbergWidget.hpp b/ui-cpp/include/HamiltonianHeisenbergWidget.hpp index 8e7e5b6de..07de7b553 100644 --- a/ui-cpp/include/HamiltonianHeisenbergWidget.hpp +++ b/ui-cpp/include/HamiltonianHeisenbergWidget.hpp @@ -9,6 +9,8 @@ #include #include +#include + #include "SpinWidget.hpp" #include "IsosurfaceWidget.hpp" //#include "SettingsWidget.hpp" @@ -18,8 +20,8 @@ struct State; /* - Converts a QString to an std::string. - This function is needed sometimes due to weird behaviour of QString::toStdString(). + Converts a QString to an std::string. + This function is needed sometimes due to weird behaviour of QString::toStdString(). */ std::string string_q2std(QString qs); @@ -28,40 +30,43 @@ class HamiltonianHeisenbergWidget : public QWidget, private Ui::HamiltonianHeise Q_OBJECT public: - HamiltonianHeisenbergWidget(std::shared_ptr state, SpinWidget * spinWidget); - void updateData(); + HamiltonianHeisenbergWidget(std::shared_ptr state, SpinWidget * spinWidget); + void updateData(); -private slots: - void set_boundary_conditions(); - void set_mu_s(); - void set_external_field(); - void set_anisotropy(); - void set_nshells_exchange(); - void set_exchange(); - void set_nshells_dmi(); - void set_dmi(); - void set_ddi(); - void set_pairs_from_file(); - void set_pairs_from_text(); +signals: + void hamiltonianChanged(Hamiltonian_Type newType); +private slots: + void clicked_change_hamiltonian(); + void set_boundary_conditions(); + void set_mu_s(); + void set_external_field(); + void set_anisotropy(); + void set_nshells_exchange(); + void set_exchange(); + void set_nshells_dmi(); + void set_dmi(); + void set_ddi(); + void set_pairs_from_file(); + void set_pairs_from_text(); private: - void Load_Contents(); - void Setup_Input_Validators(); - void Setup_Slots(); - - std::shared_ptr state; - SpinWidget * spinWidget; - - // Spinboxes for interaction shells - std::vector exchange_shells; - std::vector dmi_shells; - - // Validator for Input into lineEdits - QRegularExpressionValidator * number_validator; - QRegularExpressionValidator * number_validator_unsigned; - QRegularExpressionValidator * number_validator_int; - QRegularExpressionValidator * number_validator_int_unsigned; + void Load_Contents(); + void Setup_Input_Validators(); + void Setup_Slots(); + + std::shared_ptr state; + SpinWidget * spinWidget; + + // Spinboxes for interaction shells + std::vector exchange_shells; + std::vector dmi_shells; + + // Validator for Input into lineEdits + QRegularExpressionValidator * number_validator; + QRegularExpressionValidator * number_validator_unsigned; + QRegularExpressionValidator * number_validator_int; + QRegularExpressionValidator * number_validator_int_unsigned; }; #endif \ No newline at end of file diff --git a/ui-cpp/include/HamiltonianMicromagneticWidget.hpp b/ui-cpp/include/HamiltonianMicromagneticWidget.hpp new file mode 100644 index 000000000..2404c445a --- /dev/null +++ b/ui-cpp/include/HamiltonianMicromagneticWidget.hpp @@ -0,0 +1,44 @@ +#pragma once +#ifndef HAMILTONIAN_MICROMAGNETIC_WIDGET_H +#define HAMILTONIAN_MICROMAGNETIC_WIDGET_H + +#include + +#include + +#include "SpinWidget.hpp" + +#include "ui_HamiltonianMicromagneticWidget.h" + +struct State; + +class HamiltonianMicromagneticWidget : public QWidget, private Ui::HamiltonianMicromagneticWidget +{ + Q_OBJECT + +public: + HamiltonianMicromagneticWidget(std::shared_ptr state, SpinWidget * spinWidget); + void updateData(); + +signals: + void hamiltonianChanged(Hamiltonian_Type newType); + +private slots: + void clicked_change_hamiltonian(); + void set_boundary_conditions(); + +private: + void Setup_Input_Validators(); + void Setup_Slots(); + + std::shared_ptr state; + SpinWidget * spinWidget; + + // Validator for Input into lineEdits + QRegularExpressionValidator * number_validator; + QRegularExpressionValidator * number_validator_unsigned; + QRegularExpressionValidator * number_validator_int; + QRegularExpressionValidator * number_validator_int_unsigned; +}; + +#endif \ No newline at end of file diff --git a/ui-cpp/include/SettingsWidget.hpp b/ui-cpp/include/SettingsWidget.hpp index db125efd4..5ecc93fa6 100644 --- a/ui-cpp/include/SettingsWidget.hpp +++ b/ui-cpp/include/SettingsWidget.hpp @@ -9,6 +9,7 @@ #include "ConfigurationsWidget.hpp" #include "ParametersWidget.hpp" #include "HamiltonianHeisenbergWidget.hpp" +#include "HamiltonianMicromagneticWidget.hpp" #include "HamiltonianGaussianWidget.hpp" #include "GeometryWidget.hpp" #include "VisualisationSettingsWidget.hpp" @@ -23,28 +24,31 @@ class SettingsWidget : public QWidget, private Ui::SettingsWidget Q_OBJECT public: - SettingsWidget(std::shared_ptr state, SpinWidget *spinWidget); - void SelectTab(int index); - void incrementNCellStep(int increment); - void toggleGeometry(); + SettingsWidget(std::shared_ptr state, SpinWidget *spinWidget); + void SelectTab(int index); + void incrementNCellStep(int increment); + void toggleGeometry(); - std::shared_ptr state; + std::shared_ptr state; public slots: - void updateData(); - // Configurations - void configurationAddNoise(); - void randomPressed(); - void lastConfiguration(); + void updateData(); + // Configurations + void configurationAddNoise(); + void randomPressed(); + void lastConfiguration(); + + void updateHamiltonian(Hamiltonian_Type); private: - SpinWidget *_spinWidget; - ConfigurationsWidget * configurationsWidget; - ParametersWidget * parametersWidget; - HamiltonianHeisenbergWidget * hamiltonianHeisenbergWidget; - HamiltonianGaussianWidget * hamiltonianGaussianWidget; - GeometryWidget * geometryWidget; - VisualisationSettingsWidget * visualisationSettingsWidget; + SpinWidget *_spinWidget; + ConfigurationsWidget * configurationsWidget; + ParametersWidget * parametersWidget; + HamiltonianHeisenbergWidget * hamiltonianHeisenbergWidget; + HamiltonianMicromagneticWidget * hamiltonianMicromagneticWidget; + HamiltonianGaussianWidget * hamiltonianGaussianWidget; + GeometryWidget * geometryWidget; + VisualisationSettingsWidget * visualisationSettingsWidget; }; #endif \ No newline at end of file diff --git a/ui-cpp/src/CMakeLists.txt b/ui-cpp/src/CMakeLists.txt index 8a9bdb0fa..80094f71c 100644 --- a/ui-cpp/src/CMakeLists.txt +++ b/ui-cpp/src/CMakeLists.txt @@ -1,21 +1,22 @@ set(SOURCE_UI_QT_ROOT - ${SOURCE_UI_QT_ROOT} - ${CMAKE_CURRENT_SOURCE_DIR}/SpinWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ControlWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/HamiltonianHeisenbergWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/HamiltonianGaussianWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ConfigurationsWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/GeometryWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/MouseDecoratorWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/SettingsWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/ParametersWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/VisualisationSettingsWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/PlotsWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/DebugWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/PlotWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/IsosurfaceWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/InfoWidget.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt - PARENT_SCOPE + ${SOURCE_UI_QT_ROOT} + ${CMAKE_CURRENT_SOURCE_DIR}/SpinWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/MainWindow.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ControlWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/HamiltonianHeisenbergWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/HamiltonianMicromagneticWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/HamiltonianGaussianWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ConfigurationsWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/GeometryWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/MouseDecoratorWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/SettingsWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/ParametersWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/VisualisationSettingsWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/PlotsWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/DebugWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/PlotWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/IsosurfaceWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/InfoWidget.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/CMakeLists.txt + PARENT_SCOPE ) diff --git a/ui-cpp/src/HamiltonianGaussianWidget.cpp b/ui-cpp/src/HamiltonianGaussianWidget.cpp index e6e1144b1..808c3d638 100644 --- a/ui-cpp/src/HamiltonianGaussianWidget.cpp +++ b/ui-cpp/src/HamiltonianGaussianWidget.cpp @@ -3,19 +3,36 @@ #include "HamiltonianGaussianWidget.hpp" #include -#include HamiltonianGaussianWidget::HamiltonianGaussianWidget(std::shared_ptr state) { - this->state = state; + this->state = state; - // Setup User Interface + // Setup User Interface this->setupUi(this); - // Load variables from State - this->updateData(); + // Load variables from State + this->updateData(); + + connect(this->pushButton_changeHamiltonian, SIGNAL(clicked()), this, SLOT(clicked_change_hamiltonian())); } void HamiltonianGaussianWidget::updateData() { +} + +void HamiltonianGaussianWidget::clicked_change_hamiltonian() +{ + bool ok; + std::string type_str = QInputDialog::getItem( this, "Select the Hamiltonian to use", "", + {"Heisenberg", "Micromagnetic", "Gaussian"}, 0, false, &ok ).toStdString(); + if( ok ) + { + if( type_str == "Heisenberg" ) + emit hamiltonianChanged(Hamiltonian_Heisenberg); + else if( type_str == "Micromagnetic" ) + emit hamiltonianChanged(Hamiltonian_Micromagnetic); + else if( type_str == "Gaussian" ) + emit hamiltonianChanged(Hamiltonian_Gaussian); + } } \ No newline at end of file diff --git a/ui-cpp/src/HamiltonianHeisenbergWidget.cpp b/ui-cpp/src/HamiltonianHeisenbergWidget.cpp index 4d03350c4..21a2e1b02 100644 --- a/ui-cpp/src/HamiltonianHeisenbergWidget.cpp +++ b/ui-cpp/src/HamiltonianHeisenbergWidget.cpp @@ -27,15 +27,6 @@ HamiltonianHeisenbergWidget::HamiltonianHeisenbergWidget(std::shared_ptr // Setup User Interface this->setupUi(this); - // We use a regular expression (regex) to filter the input into the lineEdits - QRegularExpression re("[+|-]?[\\d]*[\\.]?[\\d]*"); - this->number_validator = new QRegularExpressionValidator(re); - QRegularExpression re2("[\\d]*[\\.]?[\\d]*"); - this->number_validator_unsigned = new QRegularExpressionValidator(re2); - QRegularExpression re3("[+|-]?[\\d]*"); - this->number_validator_int = new QRegularExpressionValidator(re3); - QRegularExpression re4("[\\d]*"); - this->number_validator_int_unsigned = new QRegularExpressionValidator(re4); // Setup the validators for the various input fields this->Setup_Input_Validators(); @@ -127,6 +118,22 @@ void HamiltonianHeisenbergWidget::Load_Contents() +void HamiltonianHeisenbergWidget::clicked_change_hamiltonian() +{ + bool ok; + std::string type_str = QInputDialog::getItem( this, "Select the Hamiltonian to use", "", + {"Heisenberg", "Micromagnetic", "Gaussian"}, 0, false, &ok ).toStdString(); + if( ok ) + { + if( type_str == "Heisenberg" ) + emit hamiltonianChanged(Hamiltonian_Heisenberg); + else if( type_str == "Micromagnetic" ) + emit hamiltonianChanged(Hamiltonian_Micromagnetic); + else if( type_str == "Gaussian" ) + emit hamiltonianChanged(Hamiltonian_Gaussian); + } +} + // ----------------------------------------------------------------------------------- // -------------------------- Setters ------------------------------------------------ @@ -505,14 +512,24 @@ void HamiltonianHeisenbergWidget::set_pairs_from_text() void HamiltonianHeisenbergWidget::Setup_Input_Validators() { - // mu_s + // We use a regular expression (regex) to filter the input into the lineEdits + QRegularExpression re("[+|-]?[\\d]*[\\.]?[\\d]*"); + this->number_validator = new QRegularExpressionValidator(re); + QRegularExpression re2("[\\d]*[\\.]?[\\d]*"); + this->number_validator_unsigned = new QRegularExpressionValidator(re2); + QRegularExpression re3("[+|-]?[\\d]*"); + this->number_validator_int = new QRegularExpressionValidator(re3); + QRegularExpression re4("[\\d]*"); + this->number_validator_int_unsigned = new QRegularExpressionValidator(re4); + + // mu_s this->lineEdit_muSpin_aniso->setValidator(this->number_validator); - // external field + // external field this->lineEdit_extH_aniso->setValidator(this->number_validator); this->lineEdit_extHx_aniso->setValidator(this->number_validator); this->lineEdit_extHy_aniso->setValidator(this->number_validator); this->lineEdit_extHz_aniso->setValidator(this->number_validator); - // anisotropy + // anisotropy this->lineEdit_ani_aniso->setValidator(this->number_validator); this->lineEdit_anix_aniso->setValidator(this->number_validator); this->lineEdit_aniy_aniso->setValidator(this->number_validator); @@ -524,6 +541,7 @@ void HamiltonianHeisenbergWidget::Setup_Input_Validators() void HamiltonianHeisenbergWidget::Setup_Slots() { + connect(this->pushButton_changeHamiltonian, SIGNAL(clicked()), this, SLOT(clicked_change_hamiltonian())); // Boundary Conditions connect(this->checkBox_aniso_periodical_a, SIGNAL(stateChanged(int)), this, SLOT(set_boundary_conditions())); connect(this->checkBox_aniso_periodical_b, SIGNAL(stateChanged(int)), this, SLOT(set_boundary_conditions())); diff --git a/ui-cpp/src/HamiltonianMicromagneticWidget.cpp b/ui-cpp/src/HamiltonianMicromagneticWidget.cpp new file mode 100644 index 000000000..66859f89a --- /dev/null +++ b/ui-cpp/src/HamiltonianMicromagneticWidget.cpp @@ -0,0 +1,198 @@ +#include +#include + +#include "HamiltonianMicromagneticWidget.hpp" + +#include +#include +#include +#include +#include + + +HamiltonianMicromagneticWidget::HamiltonianMicromagneticWidget(std::shared_ptr state, SpinWidget * spinWidget) +{ + this->state = state; + this->spinWidget = spinWidget; + + // Setup User Interface + this->setupUi(this); + + // Setup the validators for the various input fields + this->Setup_Input_Validators(); + + // Load variables from SpinWidget and State + this->updateData(); + + // Connect signals and slots + this->Setup_Slots(); +} + +void HamiltonianMicromagneticWidget::updateData() +{ + float d, vd[3], jij[100], dij[100]; + int ddi_method, ddi_n_periodic_images[3]; + int n_neigh_shells_exchange, n_neigh_shells_dmi, dm_chirality; + int n_basis_atoms = Geometry_Get_N_Cell_Atoms(state.get()); + std::vector mu_s(n_basis_atoms); + + // Boundary conditions + bool boundary_conditions[3]; + Hamiltonian_Get_Boundary_Conditions(state.get(), boundary_conditions); + this->checkBox_aniso_periodical_a->setChecked(boundary_conditions[0]); + this->checkBox_aniso_periodical_b->setChecked(boundary_conditions[1]); + this->checkBox_aniso_periodical_c->setChecked(boundary_conditions[2]); + + // mu_s + Geometry_Get_mu_s(state.get(), mu_s.data()); + this->lineEdit_muSpin_aniso->setText(QString::number(mu_s[0])); + + // External magnetic field + Hamiltonian_Get_Field(state.get(), &d, vd); + this->lineEdit_extH_aniso->setText(QString::number(d)); + this->lineEdit_extHx_aniso->setText(QString::number(vd[0])); + this->lineEdit_extHy_aniso->setText(QString::number(vd[1])); + this->lineEdit_extHz_aniso->setText(QString::number(vd[2])); + if (d > 0.0) this->checkBox_extH_aniso->setChecked(true); + + // Anisotropy + Hamiltonian_Get_Anisotropy(state.get(), &d, vd); + this->lineEdit_ani_aniso->setText(QString::number(d)); + this->lineEdit_anix_aniso->setText(QString::number(vd[0])); + this->lineEdit_aniy_aniso->setText(QString::number(vd[1])); + this->lineEdit_aniz_aniso->setText(QString::number(vd[2])); + if (d > 0.0) this->checkBox_ani_aniso->setChecked(true); + + // // Exchange interaction (shells) + // Hamiltonian_Get_Exchange_Shells(state.get(), &n_neigh_shells_exchange, jij); + // if (n_neigh_shells_exchange > 0) this->checkBox_exchange->setChecked(true); + // else this->checkBox_exchange->setChecked(false); + // this->spinBox_nshells_exchange->setValue(n_neigh_shells_exchange); + // this->set_nshells_exchange(); + // for (int i = 0; i < n_neigh_shells_exchange; ++i) this->exchange_shells[i]->setValue(jij[i]); + + // // DMI + // Hamiltonian_Get_DMI_Shells(state.get(), &n_neigh_shells_dmi, dij, &dm_chirality); + // int index = 0; + // if( dm_chirality == -1 ) index = 1; + // else if( dm_chirality == 2 ) index = 2; + // else if( dm_chirality == -2 ) index = 3; + // this->comboBox_dmi_chirality->setCurrentIndex(index); + // if (n_neigh_shells_dmi > 0) this->checkBox_dmi->setChecked(true); + // this->spinBox_nshells_dmi->setValue(n_neigh_shells_dmi); + // this->set_nshells_dmi(); + // for (int i = 0; i < n_neigh_shells_dmi; ++i) this->dmi_shells[i]->setValue(dij[i]); + + // DDI + Hamiltonian_Get_DDI(state.get(), &ddi_method, ddi_n_periodic_images, &d); + this->checkBox_ddi->setChecked( ddi_method != SPIRIT_DDI_METHOD_NONE ); + if( ddi_method == SPIRIT_DDI_METHOD_NONE ) + this->comboBox_ddi_method->setCurrentIndex(0); + else if( ddi_method == SPIRIT_DDI_METHOD_FFT ) + this->comboBox_ddi_method->setCurrentIndex(0); + else if( ddi_method == SPIRIT_DDI_METHOD_FMM ) + this->comboBox_ddi_method->setCurrentIndex(1); + else if( ddi_method == SPIRIT_DDI_METHOD_CUTOFF ) + this->comboBox_ddi_method->setCurrentIndex(2); + this->spinBox_ddi_n_periodic_a->setValue(ddi_n_periodic_images[0]); + this->spinBox_ddi_n_periodic_b->setValue(ddi_n_periodic_images[1]); + this->spinBox_ddi_n_periodic_c->setValue(ddi_n_periodic_images[2]); + this->doubleSpinBox_ddi_radius->setValue(d); +} + +void HamiltonianMicromagneticWidget::clicked_change_hamiltonian() +{ + bool ok; + std::string type_str = QInputDialog::getItem( this, "Select the Hamiltonian to use", "", + {"Heisenberg", "Micromagnetic", "Gaussian"}, 0, false, &ok ).toStdString(); + if( ok ) + { + if( type_str == "Heisenberg" ) + emit hamiltonianChanged(Hamiltonian_Heisenberg); + else if( type_str == "Micromagnetic" ) + emit hamiltonianChanged(Hamiltonian_Micromagnetic); + else if( type_str == "Gaussian" ) + emit hamiltonianChanged(Hamiltonian_Gaussian); + } +} + + +// ----------------------------------------------------------------------------------- +// -------------------------- Setters ------------------------------------------------ +// ----------------------------------------------------------------------------------- + + +void HamiltonianMicromagneticWidget::set_boundary_conditions() +{ + // Closure to set the parameters of a specific spin system + auto apply = [this](int idx_image) -> void + { + // Boundary conditions + bool boundary_conditions[3]; + boundary_conditions[0] = this->checkBox_aniso_periodical_a->isChecked(); + boundary_conditions[1] = this->checkBox_aniso_periodical_b->isChecked(); + boundary_conditions[2] = this->checkBox_aniso_periodical_c->isChecked(); + Hamiltonian_Set_Boundary_Conditions(state.get(), boundary_conditions, idx_image); + }; + + if (this->comboBox_Hamiltonian_Ani_ApplyTo->currentText() == "Current Image") + { + apply(System_Get_Index(state.get())); + } + else if (this->comboBox_Hamiltonian_Ani_ApplyTo->currentText() == "Current Image Chain") + { + for (int i = 0; icomboBox_Hamiltonian_Ani_ApplyTo->currentText() == "All Images") + { + for (int img = 0; imgspinWidget->updateBoundingBoxIndicators(); +} + + +// ----------------------------------------------------------------------------------- +// --------------------------------- Setup ------------------------------------------- +// ----------------------------------------------------------------------------------- + + +void HamiltonianMicromagneticWidget::Setup_Input_Validators() +{ + // We use a regular expression (regex) to filter the input into the lineEdits + QRegularExpression re("[+|-]?[\\d]*[\\.]?[\\d]*"); + this->number_validator = new QRegularExpressionValidator(re); + QRegularExpression re2("[\\d]*[\\.]?[\\d]*"); + this->number_validator_unsigned = new QRegularExpressionValidator(re2); + QRegularExpression re3("[+|-]?[\\d]*"); + this->number_validator_int = new QRegularExpressionValidator(re3); + QRegularExpression re4("[\\d]*"); + this->number_validator_int_unsigned = new QRegularExpressionValidator(re4); + + // mu_s + this->lineEdit_muSpin_aniso->setValidator(this->number_validator); + // external field + this->lineEdit_extH_aniso->setValidator(this->number_validator); + this->lineEdit_extHx_aniso->setValidator(this->number_validator); + this->lineEdit_extHy_aniso->setValidator(this->number_validator); + this->lineEdit_extHz_aniso->setValidator(this->number_validator); + // anisotropy + this->lineEdit_ani_aniso->setValidator(this->number_validator); + this->lineEdit_anix_aniso->setValidator(this->number_validator); + this->lineEdit_aniy_aniso->setValidator(this->number_validator); + this->lineEdit_aniz_aniso->setValidator(this->number_validator); +} + +void HamiltonianMicromagneticWidget::Setup_Slots() +{ + connect(this->pushButton_changeHamiltonian, SIGNAL(clicked()), this, SLOT(clicked_change_hamiltonian())); + // Boundary Conditions + connect(this->checkBox_aniso_periodical_a, SIGNAL(stateChanged(int)), this, SLOT(set_boundary_conditions())); + connect(this->checkBox_aniso_periodical_b, SIGNAL(stateChanged(int)), this, SLOT(set_boundary_conditions())); + connect(this->checkBox_aniso_periodical_c, SIGNAL(stateChanged(int)), this, SLOT(set_boundary_conditions())); +} \ No newline at end of file diff --git a/ui-cpp/src/SettingsWidget.cpp b/ui-cpp/src/SettingsWidget.cpp index d8c3d43f0..7c2f36b11 100644 --- a/ui-cpp/src/SettingsWidget.cpp +++ b/ui-cpp/src/SettingsWidget.cpp @@ -12,92 +12,127 @@ SettingsWidget::SettingsWidget(std::shared_ptr state, SpinWidget *spinWidget) { - this->state = state; + this->state = state; _spinWidget = spinWidget; - // Setup User Interface - this->setupUi(this); - - // Configurations - this->configurationsWidget = new ConfigurationsWidget(state, spinWidget); - this->tab_Settings_Configurations->layout()->addWidget(this->configurationsWidget); - - // Parameters - this->parametersWidget = new ParametersWidget(state); - this->tab_Settings_Parameters->layout()->addWidget(this->parametersWidget); - - // Hamiltonian - std::string H_name = Hamiltonian_Get_Name(state.get()); - if (H_name == "Heisenberg") - { - this->hamiltonianHeisenbergWidget = new HamiltonianHeisenbergWidget(state, spinWidget); - this->tab_Settings_Hamiltonian->layout()->addWidget(this->hamiltonianHeisenbergWidget); - } - else if (H_name == "Gaussian") - { - this->hamiltonianGaussianWidget = new HamiltonianGaussianWidget(state); - this->tab_Settings_Hamiltonian->layout()->addWidget(this->hamiltonianGaussianWidget); - } - else - { - this->tabWidget_Settings->removeTab(2); - } - - // Geometry - this->geometryWidget = new GeometryWidget(state, spinWidget); - this->tab_Settings_Geometry->layout()->addWidget(this->geometryWidget); - connect(this->geometryWidget, SIGNAL(updateNeeded()), this, SLOT(updateData())); - this->tabWidget_Settings->removeTab(3); - - // Visualisation - this->visualisationSettingsWidget = new VisualisationSettingsWidget(state, spinWidget); - this->tab_Settings_Visualisation->layout()->addWidget(this->visualisationSettingsWidget); + // Setup User Interface + this->setupUi(this); + + // Configurations + this->configurationsWidget = new ConfigurationsWidget(state, spinWidget); + this->tab_Settings_Configurations->layout()->addWidget(this->configurationsWidget); + + // Parameters + this->parametersWidget = new ParametersWidget(state); + this->tab_Settings_Parameters->layout()->addWidget(this->parametersWidget); + + // Hamiltonian + std::string H_name = Hamiltonian_Get_Name(state.get()); + this->hamiltonianHeisenbergWidget = new HamiltonianHeisenbergWidget(state, spinWidget); + this->hamiltonianMicromagneticWidget = new HamiltonianMicromagneticWidget(state, spinWidget); + this->hamiltonianGaussianWidget = new HamiltonianGaussianWidget(state); + this->tab_Settings_Hamiltonian->layout()->addWidget(this->hamiltonianHeisenbergWidget); + this->tab_Settings_Hamiltonian->layout()->addWidget(this->hamiltonianMicromagneticWidget); + this->tab_Settings_Hamiltonian->layout()->addWidget(this->hamiltonianGaussianWidget); + this->hamiltonianHeisenbergWidget->hide(); + this->hamiltonianMicromagneticWidget->hide(); + this->hamiltonianGaussianWidget->hide(); + if (H_name == "Heisenberg") + this->hamiltonianHeisenbergWidget->show(); + else if (H_name == "Micromagnetic") + this->hamiltonianMicromagneticWidget->show(); + else if (H_name == "Gaussian") + this->hamiltonianGaussianWidget->show(); + if( H_name != "Heisenberg" && H_name != "Micromagnetic" && H_name != "Gaussian" ) + this->tabWidget_Settings->removeTab(2); + connect(this->hamiltonianHeisenbergWidget, SIGNAL(hamiltonianChanged(Hamiltonian_Type)), this, SLOT(updateHamiltonian(Hamiltonian_Type))); + connect(this->hamiltonianMicromagneticWidget, SIGNAL(hamiltonianChanged(Hamiltonian_Type)), this, SLOT(updateHamiltonian(Hamiltonian_Type))); + connect(this->hamiltonianGaussianWidget, SIGNAL(hamiltonianChanged(Hamiltonian_Type)), this, SLOT(updateHamiltonian(Hamiltonian_Type))); + + // Geometry + this->geometryWidget = new GeometryWidget(state, spinWidget); + this->tab_Settings_Geometry->layout()->addWidget(this->geometryWidget); + connect(this->geometryWidget, SIGNAL(updateNeeded()), this, SLOT(updateData())); + this->tabWidget_Settings->removeTab(3); + + // Visualisation + this->visualisationSettingsWidget = new VisualisationSettingsWidget(state, spinWidget); + this->tab_Settings_Visualisation->layout()->addWidget(this->visualisationSettingsWidget); +} + +void SettingsWidget::updateHamiltonian(Hamiltonian_Type type) +{ + // Update the state + Hamiltonian_Set_Kind(this->state.get(), type); + + // Update the GUI + this->hamiltonianHeisenbergWidget->hide(); + this->hamiltonianMicromagneticWidget->hide(); + this->hamiltonianGaussianWidget->hide(); + if( type == Hamiltonian_Heisenberg ) + { + this->hamiltonianHeisenbergWidget->show(); + this->hamiltonianHeisenbergWidget->updateData(); + } + else if( type == Hamiltonian_Micromagnetic ) + { + this->hamiltonianMicromagneticWidget->show(); + this->hamiltonianMicromagneticWidget->updateData(); + } + else if( type == Hamiltonian_Gaussian ) + { + this->hamiltonianGaussianWidget->show(); + this->hamiltonianGaussianWidget->updateData(); + } + else + this->tabWidget_Settings->removeTab(2); } void SettingsWidget::updateData() { - // Parameters - this->parametersWidget->updateData(); - // Hamiltonian - std::string H_name = Hamiltonian_Get_Name(state.get()); - if (H_name == "Heisenberg") this->hamiltonianHeisenbergWidget->updateData(); - else if (H_name == "Gaussian") this->hamiltonianGaussianWidget->updateData(); - // Geometry - this->geometryWidget->updateData(); - // Visualisation - this->visualisationSettingsWidget->updateData(); - - // ToDo: Also update Debug etc! + // Parameters + this->parametersWidget->updateData(); + // Hamiltonian + std::string H_name = Hamiltonian_Get_Name(state.get()); + if (H_name == "Heisenberg") this->hamiltonianHeisenbergWidget->updateData(); + else if (H_name == "Micromagnetic") this->hamiltonianMicromagneticWidget->updateData(); + else if (H_name == "Gaussian") this->hamiltonianGaussianWidget->updateData(); + // Geometry + this->geometryWidget->updateData(); + // Visualisation + this->visualisationSettingsWidget->updateData(); + + // ToDo: Also update Debug etc! } void SettingsWidget::SelectTab(int index) { - this->tabWidget_Settings->setCurrentIndex(index); + this->tabWidget_Settings->setCurrentIndex(index); } void SettingsWidget::incrementNCellStep(int increment) { - this->visualisationSettingsWidget->incrementNCellStep(increment); + this->visualisationSettingsWidget->incrementNCellStep(increment); } void SettingsWidget::lastConfiguration() { - this->configurationsWidget->lastConfiguration(); + this->configurationsWidget->lastConfiguration(); } void SettingsWidget::randomPressed() { - this->configurationsWidget->randomPressed(); + this->configurationsWidget->randomPressed(); } void SettingsWidget::configurationAddNoise() { - this->configurationsWidget->configurationAddNoise(); + this->configurationsWidget->configurationAddNoise(); } void SettingsWidget::toggleGeometry() { - if (this->tabWidget_Settings->count() > 4) - this->tabWidget_Settings->removeTab(3); - else - this->tabWidget_Settings->insertTab(3, this->tab_Settings_Geometry, "Geometry"); + if (this->tabWidget_Settings->count() > 4) + this->tabWidget_Settings->removeTab(3); + else + this->tabWidget_Settings->insertTab(3, this->tab_Settings_Geometry, "Geometry"); } \ No newline at end of file diff --git a/ui-cpp/ui/CMakeLists.txt b/ui-cpp/ui/CMakeLists.txt index a6f9b8028..3b5cc3fa9 100644 --- a/ui-cpp/ui/CMakeLists.txt +++ b/ui-cpp/ui/CMakeLists.txt @@ -11,6 +11,7 @@ set(UI_FILES ${CMAKE_CURRENT_SOURCE_DIR}/SettingsWidget.ui ${CMAKE_CURRENT_SOURCE_DIR}/ParametersWidget.ui ${CMAKE_CURRENT_SOURCE_DIR}/HamiltonianHeisenbergWidget.ui + ${CMAKE_CURRENT_SOURCE_DIR}/HamiltonianMicromagneticWidget.ui ${CMAKE_CURRENT_SOURCE_DIR}/HamiltonianGaussianWidget.ui ${CMAKE_CURRENT_SOURCE_DIR}/VisualisationSettingsWidget.ui PARENT_SCOPE diff --git a/ui-cpp/ui/HamiltonianGaussianWidget.ui b/ui-cpp/ui/HamiltonianGaussianWidget.ui index 456b64a60..0a4405a6b 100644 --- a/ui-cpp/ui/HamiltonianGaussianWidget.ui +++ b/ui-cpp/ui/HamiltonianGaussianWidget.ui @@ -14,48 +14,36 @@ Form - - + + - - + + - 120 - 0 + 16777215 + 25 - - - Current Image - - - - - Current Image Chain - - - - - All Images - - + + Change + - - - - 60 - 16777215 - + + + + 14 + true + - Apply to + Gaussian - + Qt::Horizontal @@ -69,14 +57,21 @@ - + - Unfortunately, this is not available yet + Unfortunately, this is not available in the GUI yet - + + + + Qt::Horizontal + + + + Qt::Vertical @@ -89,6 +84,61 @@ + + + + + + + 120 + 0 + + + + + Current Image + + + + + Current Image Chain + + + + + All Images + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 60 + 16777215 + + + + Apply to + + + + + diff --git a/ui-cpp/ui/HamiltonianHeisenbergWidget.ui b/ui-cpp/ui/HamiltonianHeisenbergWidget.ui index 45b74d60d..7c40c9d61 100644 --- a/ui-cpp/ui/HamiltonianHeisenbergWidget.ui +++ b/ui-cpp/ui/HamiltonianHeisenbergWidget.ui @@ -14,7 +14,7 @@ Form - + QFrame::NoFrame @@ -28,7 +28,7 @@ 0 0 376 - 1136 + 1079 @@ -136,7 +136,7 @@ - Anisotropy + Anisotropy [meV] @@ -226,7 +226,7 @@ - External Field + External Field [T] false @@ -252,14 +252,7 @@ - DMI - - - - - - - Periodical a + DMI [meV] @@ -313,7 +306,7 @@ - Exchange + Exchange [meV] @@ -333,13 +326,6 @@ - - - - Periodical c - - - @@ -386,7 +372,7 @@ - Dipole-dipole + Dipolar @@ -503,13 +489,6 @@ - - - - Periodical b - - - @@ -624,6 +603,51 @@ + + + + + + b + + + + + + + a + + + + + + + c + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Periodical boundaries + + + @@ -892,14 +916,61 @@ i j k l da_j db_j dc_j da_k db_k dc_k da_l db_l dc_l Q + + + + Qt::Horizontal + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 14 + true + + + + Heisenberg + + + + + + + + 16777215 + 25 + + + + Change + + + + + scrollArea_5 comboBox_Hamiltonian_Ani_ApplyTo - checkBox_aniso_periodical_a - checkBox_aniso_periodical_b - checkBox_aniso_periodical_c checkBox_extH_aniso lineEdit_extH_aniso lineEdit_muSpin_aniso diff --git a/ui-cpp/ui/HamiltonianMicromagneticWidget.ui b/ui-cpp/ui/HamiltonianMicromagneticWidget.ui new file mode 100644 index 000000000..a6ea75bbc --- /dev/null +++ b/ui-cpp/ui/HamiltonianMicromagneticWidget.ui @@ -0,0 +1,816 @@ + + + HamiltonianMicromagneticWidget + + + + 0 + 0 + 420 + 805 + + + + Form + + + + + + + + + 14 + true + false + + + + Micromagnetic + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 16777215 + 25 + + + + Change + + + + + + + + + Qt::Horizontal + + + + + + + QFrame::NoFrame + + + true + + + + + 0 + 0 + 396 + 724 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 60 + 16777215 + + + + Apply to + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 120 + 0 + + + + + Current Image + + + + + Current Image Chain + + + + + All Images + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + + + + 40 + 16777215 + + + + + + + + + 40 + 16777215 + + + + + + + + + 40 + 16777215 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + FFT + + + + + FMM + + + + + Cutoff + + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Number of Shells + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Number of Shells + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Minimum + + + + 30 + 20 + + + + + + + + + 100 + 16777215 + + + + Direction (x,y,z) + + + + + + + mu_spin + + + + + + + + + + + + 120 + 16777215 + + + + + Bloch + + + + + Reverse Bloch + + + + + Neel + + + + + Reverse Neel + + + + + + + + + 55 + 16777215 + + + + Chirality + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Exchange [J/m] + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + cutoff radius + + + + + + + + + 1000000.000000000000000 + + + + + + + Dipolar + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + periodic images + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Periodical boundaries + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + External Field [T] + + + false + + + false + + + + + + + DMI [J/m^2] + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 40 + 16777215 + + + + + + + + + + + + + 40 + 16777215 + + + + + + + + + 40 + 16777215 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 40 + 16777215 + + + + + + + + + 40 + 16777215 + + + + + + + + + + Anisotropy [J/m^3] + + + + + + + + + + + + + + + + + + + + + + Direction (x,y,z) + + + + + + + Qt::Horizontal + + + + 30 + 20 + + + + + + + + + + + + + 40 + 16777215 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + a + + + + + + + b + + + + + + + c + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + scrollArea_5 + comboBox_Hamiltonian_Ani_ApplyTo + checkBox_extH_aniso + lineEdit_extH_aniso + lineEdit_muSpin_aniso + lineEdit_extHx_aniso + lineEdit_extHy_aniso + lineEdit_extHz_aniso + checkBox_ani_aniso + lineEdit_ani_aniso + lineEdit_anix_aniso + lineEdit_aniy_aniso + lineEdit_aniz_aniso + checkBox_exchange + spinBox_nshells_exchange + checkBox_dmi + spinBox_nshells_dmi + checkBox_ddi + + + + diff --git a/ui-cpp/ui/VisualisationSettingsWidget.ui b/ui-cpp/ui/VisualisationSettingsWidget.ui index 1f32cfad2..b76ce06f2 100644 --- a/ui-cpp/ui/VisualisationSettingsWidget.ui +++ b/ui-cpp/ui/VisualisationSettingsWidget.ui @@ -458,19 +458,6 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -510,6 +497,19 @@ + + + + Qt::Horizontal + + + + 40 + 20 + + + + @@ -536,8 +536,8 @@ 0 0 - 468 - 1019 + 189 + 146 @@ -606,8 +606,8 @@ 0 0 - 468 - 1019 + 179 + 559 @@ -919,8 +919,8 @@ 0 0 - 468 - 1019 + 194 + 276 @@ -1102,7 +1102,7 @@ 0 0 468 - 1019 + 984 From ba3e89eb3ae9dc53629a334a3cd0a3430f0ee149 Mon Sep 17 00:00:00 2001 From: Gideon Date: Mon, 27 May 2019 13:35:50 +0200 Subject: [PATCH 06/45] Bugfixes for switching between Hamiltonians. - the API now allows switching to Hamiltonian_Heisenberg - the GUI was not updating the corresponding tab correctly, potentially leading to crashes - the CUDA backend of Hamiltonian_Micromagnetic can now be compiled --- core/src/Spirit/Hamiltonian.cpp | 24 ++++++++++--------- core/src/engine/Hamiltonian_Micromagnetic.cpp | 2 +- core/src/engine/Hamiltonian_Micromagnetic.cu | 6 +++-- ui-cpp/src/SettingsWidget.cpp | 8 ++++--- 4 files changed, 23 insertions(+), 17 deletions(-) diff --git a/core/src/Spirit/Hamiltonian.cpp b/core/src/Spirit/Hamiltonian.cpp index 8577d4fe6..6e1892bc2 100644 --- a/core/src/Spirit/Hamiltonian.cpp +++ b/core/src/Spirit/Hamiltonian.cpp @@ -72,6 +72,10 @@ try "Hamiltonian is already of {} kind. Not doing anything.", kind_str), -1, idx_chain ); return; } + + Log( Utility::Log_Level::Error, Utility::Log_Sender::API, fmt::format( + "Cannot yet set Hamiltonian kind to {} - this function is only a stub!", kind_str), -1, idx_chain ); + return; } for( auto& image : chain->images ) @@ -81,16 +85,18 @@ try { if( type == Hamiltonian_Heisenberg ) { - // TODO - // image->hamiltonian = std::shared_ptr<...>(new Engine::Hamiltonian_Heisenberg(...)); - - Log( Utility::Log_Level::Error, Utility::Log_Sender::API, fmt::format( - "Cannot yet set Hamiltonian kind to {} - this function is only a stub!", kind_str), -1, idx_chain ); - return; + // TODO: are these the desired defaults? + image->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Heisenberg( + 0, Vector3{0, 0, 1}, + {}, {}, {}, + {}, {}, SPIRIT_CHIRALITY_NEEL, Engine::DDI_Method::None, + {0, 0, 0}, 0, {}, {}, + image->geometry, + image->hamiltonian->boundary_conditions)); } else if( type == Hamiltonian_Micromagnetic ) { - // TODO + // TODO: are these the desired defaults? image->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Micromagnetic( 0, Vector3{0, 0, 1}, Matrix3::Zero(), @@ -104,10 +110,6 @@ try { // TODO // image->hamiltonian = std::shared_ptr<...>(new Engine::Hamiltonian_Gaussian(...)); - - Log( Utility::Log_Level::Error, Utility::Log_Sender::API, fmt::format( - "Cannot yet set Hamiltonian kind to {} - this function is only a stub!", kind_str), -1, idx_chain ); - return; } } catch( ... ) diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cpp b/core/src/engine/Hamiltonian_Micromagnetic.cpp index 219d604d8..5dab21371 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cpp +++ b/core/src/engine/Hamiltonian_Micromagnetic.cpp @@ -13,7 +13,7 @@ using namespace Data; using namespace Utility; -namespace C = Utility::Constants; +namespace C = Utility::Constants_Micromagnetic; using Engine::Vectormath::check_atom_type; using Engine::Vectormath::idx_from_pair; using Engine::Vectormath::idx_from_tupel; diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cu b/core/src/engine/Hamiltonian_Micromagnetic.cu index d22dcb778..a243ee2eb 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cu +++ b/core/src/engine/Hamiltonian_Micromagnetic.cu @@ -13,10 +13,12 @@ using namespace Data; using namespace Utility; -namespace C = Utility::Constants; +namespace C = Utility::Constants_Micromagnetic; using Engine::Vectormath::check_atom_type; using Engine::Vectormath::idx_from_pair; -using Engine::Vectormath::idx_from_tupel; +using Engine::Vectormath::cu_check_atom_type; +using Engine::Vectormath::cu_idx_from_pair; +using Engine::Vectormath::cu_tupel_from_idx; namespace Engine diff --git a/ui-cpp/src/SettingsWidget.cpp b/ui-cpp/src/SettingsWidget.cpp index 7c2f36b11..124966838 100644 --- a/ui-cpp/src/SettingsWidget.cpp +++ b/ui-cpp/src/SettingsWidget.cpp @@ -64,22 +64,24 @@ void SettingsWidget::updateHamiltonian(Hamiltonian_Type type) { // Update the state Hamiltonian_Set_Kind(this->state.get(), type); + // Determine type (result of Hamiltonian_Set_Kind is unclear) + std::string type_str = Hamiltonian_Get_Name(this->state.get()); // Update the GUI this->hamiltonianHeisenbergWidget->hide(); this->hamiltonianMicromagneticWidget->hide(); this->hamiltonianGaussianWidget->hide(); - if( type == Hamiltonian_Heisenberg ) + if( type_str == "Heisenberg" ) { this->hamiltonianHeisenbergWidget->show(); this->hamiltonianHeisenbergWidget->updateData(); } - else if( type == Hamiltonian_Micromagnetic ) + else if( type_str == "Micromagnetic" ) { this->hamiltonianMicromagneticWidget->show(); this->hamiltonianMicromagneticWidget->updateData(); } - else if( type == Hamiltonian_Gaussian ) + else if( type_str == "Gaussian" ) { this->hamiltonianGaussianWidget->show(); this->hamiltonianGaussianWidget->updateData(); From 88a97c72ba02d48871133b667aad09445c32517b Mon Sep 17 00:00:00 2001 From: Gideon Date: Tue, 4 Jun 2019 10:21:05 +0200 Subject: [PATCH 07/45] Core: micromagnetic parameters in configparser. --- core/src/Spirit/Hamiltonian.cpp | 90 +++++++++++---------- core/src/io/Configparser.cpp | 139 ++++++++++++++++++++++++-------- 2 files changed, 154 insertions(+), 75 deletions(-) diff --git a/core/src/Spirit/Hamiltonian.cpp b/core/src/Spirit/Hamiltonian.cpp index 6e1892bc2..61ef7b5ba 100644 --- a/core/src/Spirit/Hamiltonian.cpp +++ b/core/src/Spirit/Hamiltonian.cpp @@ -133,10 +133,10 @@ try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - + image->Lock(); try { @@ -151,7 +151,7 @@ try image->Unlock(); Log( Utility::Log_Level::Info, Utility::Log_Sender::API, - fmt::format("Set boundary conditions to {} {} {}", periodical[0], periodical[1], periodical[2]), + fmt::format("Set boundary conditions to {} {} {}", periodical[0], periodical[1], periodical[2]), idx_image, idx_chain ); } catch( ... ) @@ -164,13 +164,20 @@ try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - + + if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "External field cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); + return; + } + // Lock mutex because simulations may be running image->Lock(); - + try { // Set @@ -181,7 +188,7 @@ try // Normals Vector3 new_normal{normal[0], normal[1], normal[2]}; new_normal.normalize(); - + // Into the Hamiltonian ham->external_field_magnitude = magnitude * Constants::mu_B; ham->external_field_normal = new_normal; @@ -196,7 +203,7 @@ try // Normals Vector3 new_normal{normal[0], normal[1], normal[2]}; new_normal.normalize(); - + // Into the Hamiltonian ham->external_field_magnitude = magnitude * Constants::mu_B; ham->external_field_normal = new_normal; @@ -204,15 +211,12 @@ try // Update Energies ham->Update_Energy_Contributions(); } - else - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - "External field cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } - + // Unlock mutex image->Unlock(); @@ -225,13 +229,13 @@ catch( ... ) spirit_handle_exception_api(idx_image, idx_chain); } -void Hamiltonian_Set_Anisotropy( State *state, float magnitude, const float * normal, +void Hamiltonian_Set_Anisotropy( State *state, float magnitude, const float * normal, int idx_image, int idx_chain) noexcept try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); @@ -291,12 +295,12 @@ try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - + image->Lock(); - + try { if (image->hamiltonian->Name() == "Heisenberg") @@ -320,7 +324,7 @@ try { spirit_handle_exception_api(idx_image, idx_chain); } - + image->Unlock(); } catch( ... ) @@ -333,7 +337,7 @@ try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); image->Lock(); @@ -366,7 +370,7 @@ try Log(Utility::Log_Level::Info, Utility::Log_Sender::API, message, idx_image, idx_chain); } else - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, "DMI cannot be set on " + + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, "DMI cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); } catch( ... ) @@ -386,7 +390,7 @@ try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); image->Lock(); @@ -409,7 +413,7 @@ try ddi_method, n_periodic_images[0], n_periodic_images[1], n_periodic_images[2], cutoff_radius), idx_image, idx_chain ); } else - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, "DDI cannot be set on " + + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, "DDI cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); } catch( ... ) @@ -433,10 +437,10 @@ try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - + return image->hamiltonian->Name().c_str(); } catch( ... ) @@ -450,10 +454,10 @@ try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - + periodical[0] = image->hamiltonian->boundary_conditions[0]; periodical[1] = image->hamiltonian->boundary_conditions[1]; periodical[2] = image->hamiltonian->boundary_conditions[2]; @@ -468,10 +472,10 @@ try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - + if (image->hamiltonian->Name() == "Heisenberg") { auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); @@ -505,14 +509,14 @@ try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - + if (image->hamiltonian->Name() == "Heisenberg") { auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - + if (ham->anisotropy_indices.size() > 0) { // Magnitude @@ -569,10 +573,10 @@ try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, image->hamiltonian->Name() + " Hamiltonian: fetching exchange pairs is not yet implemented...", idx_image, idx_chain ); return 0; @@ -588,10 +592,10 @@ try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, image->hamiltonian->Name() + " Hamiltonian: fetching exchange pairs is not yet implemented...", idx_image, idx_chain ); } @@ -605,17 +609,17 @@ try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - + if (image->hamiltonian->Name() == "Heisenberg") { auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - + *n_shells = ham->dmi_shell_magnitudes.size(); *chirality = ham->dmi_shell_chirality; - + for (int i=0; i<*n_shells; ++i) { dij[i] = (float)ham->dmi_shell_magnitudes[i]; @@ -632,10 +636,10 @@ try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, image->hamiltonian->Name() + " Hamiltonian: fetching DMI pairs is not yet implemented...", idx_image, idx_chain ); return 0; @@ -651,10 +655,10 @@ try { std::shared_ptr image; std::shared_ptr chain; - + // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - + if (image->hamiltonian->Name() == "Heisenberg") { auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); diff --git a/core/src/io/Configparser.cpp b/core/src/io/Configparser.cpp index 822930c8f..aa4004979 100644 --- a/core/src/io/Configparser.cpp +++ b/core/src/io/Configparser.cpp @@ -1449,6 +1449,8 @@ namespace IO scalar field = 0; Vector3 field_normal = { 0.0, 0.0, 1.0 }; + scalar anisotropy_magnitude; + Vector3 anisotropy_normal; Matrix3 anisotropy_tensor; scalar exchange_magnitude = 0; Matrix3 exchange_tensor; @@ -1460,9 +1462,6 @@ namespace IO try { IO::Filter_File_Handle myfile(configFile); - - myfile.Read_Single(spatial_gradient_order, "spatial_gradient_order"); - try { IO::Filter_File_Handle myfile(configFile); @@ -1472,39 +1471,115 @@ namespace IO boundary_conditions[0] = (boundary_conditions_i[0] != 0); boundary_conditions[1] = (boundary_conditions_i[1] != 0); boundary_conditions[2] = (boundary_conditions_i[2] != 0); - }// end try + } catch( ... ) { spirit_handle_exception_core(fmt::format("Unable to read boundary conditions from config file \"{}\"", configFile)); } - // TODO: - // if (myfile.Find("tensor_exchange")) - // ... - // else if (myfile.Find("exchange")) - // ... - // else - // ... - - // // Interaction Pairs - // if (myfile.Find("spatial_gradient_order")) - // interaction_pairs_file = configFile; - // else if (myfile.Find("interaction_pairs_file")) - // myfile.iss >> interaction_pairs_file; - - // if (interaction_pairs_file.length() > 0) - // { - // // The file name should be valid so we try to read it - // Pairs_from_File(interaction_pairs_file, geometry, n_pairs, - // exchange_pairs, exchange_magnitudes, - // dmi_pairs, dmi_magnitudes, dmi_normals); - // } - //else - //{ - // Log(Log_Level::Warning, Log_Sender::IO, "Hamiltonian_Heisenberg: Default Interaction pairs have not been implemented yet."); - // throw Exception::System_not_Initialized; - // // Not implemented! - //} + // Precision of the spatial gradient calculation + myfile.Read_Single(spatial_gradient_order, "spatial_gradient_order"); + + // Field + myfile.Read_Single(field, "external_field_magnitude"); + myfile.Read_Vector3(field_normal, "external_field_normal"); + field_normal.normalize(); + if (field_normal.norm() < 1e-8) + { + field_normal = { 0,0,1 }; + Log(Log_Level::Warning, Log_Sender::IO, "Input for 'external_field_normal' had norm zero and has been set to (0,0,1)"); + } + + // TODO: anisotropy + if( myfile.Find("tensor_anisotropy") ) + { + for( int dim = 0; dim < 3; ++dim ) + { + myfile.GetLine(); + myfile.iss >> anisotropy_tensor(dim, 0) >> anisotropy_tensor(dim, 1) >> anisotropy_tensor(dim, 2); + } + } + else + { + // Read parameters from config + myfile.Read_Single(anisotropy_magnitude, "anisotropy_magnitude"); + myfile.Read_Vector3(anisotropy_normal, "anisotropy_normal"); + anisotropy_normal.normalize(); + auto& Kn = anisotropy_normal; + anisotropy_tensor << Kn[0]*Kn[0], Kn[0]*Kn[1], Kn[0]*Kn[2], + Kn[1]*Kn[0], Kn[1]*Kn[1], Kn[1]*Kn[2], + Kn[2]*Kn[0], Kn[2]*Kn[1], Kn[2]*Kn[2]; + anisotropy_tensor *= anisotropy_magnitude; + } + + // TODO: exchange + if( myfile.Find("tensor_exchange") ) + { + for( int dim = 0; dim < 3; ++dim ) + { + myfile.GetLine(); + myfile.iss >> exchange_tensor(dim, 0) >> exchange_tensor(dim, 1) >> exchange_tensor(dim, 2); + } + } + else + { + myfile.Read_Single(exchange_magnitude, "exchange"); + exchange_tensor << exchange_magnitude, 0, 0, + 0, exchange_magnitude, 0, + 0, 0, exchange_magnitude; + } + + // TODO: dmi + if( myfile.Find("tensor_dmi") ) + { + for( int dim = 0; dim < 3; ++dim ) + { + myfile.GetLine(); + myfile.iss >> dmi_tensor(dim, 0) >> dmi_tensor(dim, 1) >> dmi_tensor(dim, 2); + } + } + else + { + myfile.Read_Single(dmi_magnitude, "dmi"); + // dmi_tensor << dmi_magnitude, 0, 0, + // 0, dmi_magnitude, 0, + // 0, 0, dmi_magnitude; + Log(Log_Level::Warning, Log_Sender::IO, "'dmi' is not a supported input!"); + } + + // TODO: dipolar + try + { + IO::Filter_File_Handle myfile(configFile); + + // // DDI method + // myfile.Read_String(ddi_method_str, "ddi_method"); + // if( ddi_method_str == "none" ) + // ddi_method = Engine::DDI_Method::None; + // else if( ddi_method_str == "fft" ) + // ddi_method = Engine::DDI_Method::FFT; + // else if( ddi_method_str == "fmm" ) + // ddi_method = Engine::DDI_Method::FMM; + // else if( ddi_method_str == "cutoff" ) + // ddi_method = Engine::DDI_Method::Cutoff; + // else + // { + // Log(Log_Level::Warning, Log_Sender::IO, fmt::format( + // "Hamiltonian_Heisenberg: Keyword 'ddi_method' got passed invalid method \"{}\". Setting to \"none\".", ddi_method_str)); + // ddi_method_str = "none"; + // } + + // // Number of periodical images + // myfile.Read_3Vector(ddi_n_periodic_images, "ddi_n_periodic_images"); + // // myfile.Read_Single(ddi_n_periodic_images, "ddi_n_periodic_images"); + + // // Dipole-dipole cutoff radius + // myfile.Read_Single(ddi_radius, "ddi_radius"); + }// end try + catch( ... ) + { + spirit_handle_exception_core(fmt::format("Unable to read DDI radius from config file \"{}\"", configFile)); + } }// end try catch( ... ) { @@ -1521,7 +1596,7 @@ namespace IO spatial_gradient_order, boundary_conditions )); - + Log(Log_Level::Info, Log_Sender::IO, "Hamiltonian_Micromagnetic: built"); return hamiltonian; From 378e16b3f562be8542948d1073d3a06cc92fec2c Mon Sep 17 00:00:00 2001 From: Gideon Date: Tue, 4 Jun 2019 10:23:24 +0200 Subject: [PATCH 08/45] Updated docs with micromagnetic model. --- README.md | 3 +- core/README.md | 2 +- core/docs/Input.md | 95 +++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 88 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index ec002dceb..8a5c1330c 100644 --- a/README.md +++ b/README.md @@ -89,7 +89,8 @@ control of parameters. ### *Physics Features* -- Atomistic Spin Lattice Heisenberg Model including also DMI and dipole-dipole +- Atomistic spin lattice Heisenberg model including DMI and dipole-dipole interaction +- Micromagnetic model including DM and dipolar interactions - **Spin Dynamics simulations** obeying the [Landau-Lifschitz-Gilbert equation](https://en.wikipedia.org/wiki/Landau%E2%80%93Lifshitz%E2%80%93Gilbert_equation) - Direct **Energy minimisation** with different solvers diff --git a/core/README.md b/core/README.md index 389d6487b..8f2ab9fb3 100644 --- a/core/README.md +++ b/core/README.md @@ -4,7 +4,7 @@ Spirit This is the core library of the **Spirit** framework. It is meant to provide useful and easy API functions to enable productive work -with Atomistic Dynamics Simulations and Optimizations. +with Atomistic and Micromagnetic Dynamics Simulations and Optimizations. The current implementation is specific to atomistic spin models, but it may easily be generalised. diff --git a/core/docs/Input.md b/core/docs/Input.md index 6a5c8d829..055633710 100644 --- a/core/docs/Input.md +++ b/core/docs/Input.md @@ -6,11 +6,12 @@ The following sections will list and explain the input file keywords. 1. [General Settings and Log](#General) 2. [Geometry](#Geometry) 3. [Heisenberg Hamiltonian](#Heisenberg) -4. [Gaussian Hamiltonian](#Gaussian) -5. [Method Output](#MethodOutput) -6. [Method Parameters](#MethodParameters) -7. [Pinning](#Pinning) -8. [Disorder and Defects](#Defects) +4. [Micromagnetic Hamiltonian](#Micromagnetic) +5. [Gaussian Hamiltonian](#Gaussian) +6. [Method Output](#MethodOutput) +7. [Method Parameters](#MethodParameters) +8. [Pinning](#Pinning) +9. [Disorder and Defects](#Defects) General Settings and Log @@ -159,10 +160,10 @@ Heisenberg Hamiltonian To use a Heisenberg Hamiltonian, use either `heisenberg_neighbours` or `heisenberg_pairs` as input parameter after the `hamiltonian` keyword. -**General Parameters**: +**General Parameters:** ```Python -### Hamiltonian Type (heisenberg_neighbours, heisenberg_pairs, gaussian) +### Hamiltonian Type (heisenberg_neighbours, heisenberg_pairs, micromagnetic, gaussian) hamiltonian heisenberg_neighbours ### Boundary conditions (in a b c) = 0(open), 1(periodical) @@ -208,7 +209,7 @@ If the boundary conditions are periodic `ddi_n_periodic_images` specifies how ma *Note:* The images are appended on both sides (the edges get filled too) i.e. 1 0 0 -> one image in +a direction and one image in -a direction -**Neighbour shells**: +**Neighbour shells:** Using `hamiltonian heisenberg_neighbours`, pair-wise interactions are handled in terms of (isotropic) neighbour shells: @@ -290,6 +291,80 @@ Pairwise interactions are specified in meV per unique pair, while quadruplets are specified in meV per unique quadruplet. +Micromagnetic Hamiltonian +-------------------------------------------------- + +To use a micromagnetic Hamiltonian, use `micromagnetic` as input parameter after the `hamiltonian` keyword. + +Note that Spirit only supports rectilinear geometries when using this Hamiltonian. + +**Units:** + +In the micromagnetic model, Spirit uses SI units: +- `m` (meter) for distances +- `J` (Joule) for energies +- `s` (second) for time + +Therefore, +- `A [J/m]` for exchange stiffness +- `D [J/m^2]` for DMI +- `K [J/m^3]` for anisotropy +- `Ms [A/m]` for saturation magnetisation + +**General Parameters:** + +```Python +### boundary_conditions (in a b c) = 0(open), 1(periodical) +boundary_conditions 0 0 0 + +### The order of the finite difference approximation of the spatial gradient +spatial_gradient_order 2 +``` + +**Static:** + +```Python +# Saturation magnetisation [A/m] +Ms 1.3e6 +``` +while the magnetocrystalline anisotropy can be specified as an axis, +```Python +# Anisotropy [J/m^3] +anisotropy 0.3e6 +``` +or as a tensor +```Python +# Anisotropy [J/m^3] +tensor_anisotropy +0.3e6 0 0 +0 0.3e6 0 +0 0 0.3e6 +``` + +**Interactions:** + +The exchange interaction and DMI can each be set either as a constant, +```Python +# Stiffness [J/m] +exchange 10e-12 +# DMI [J/m^2] +dmi 6e-3 +``` +or as a tensor, +```Python +# Stiffness [J/m] +tensor_exchange +10e-12 0 0 + 0 10e-12 0 + 0 0 10e-12 +# DMI [J/m^2] +tensor_dmi + 0 -6e-3 6e-3 + 6e-3 0 -6e-3 +-6e-3 6e-3 0 +``` + + Gaussian Hamiltonian -------------------------------------------------- @@ -346,7 +421,7 @@ llg_output_configuration_step 1 # Save spin configuration at each step llg_output_configuration_archive 0 # Archive spin configuration at each step ``` -**MC**: +**MC:** ```Python mc_output_energy_step 0 mc_output_energy_archive 1 @@ -357,7 +432,7 @@ mc_output_configuration_step 1 mc_output_configuration_archive 0 ``` -**GNEB**: +**GNEB:** ```Python gneb_output_energies_step 0 # Save energies of images in chain gneb_output_energies_interpolated 1 # Also save interpolated energies From 67a100e1963d54895ff8e63f68d2d7034976633f Mon Sep 17 00:00:00 2001 From: Dmitrii Tolmachev Date: Tue, 25 Jun 2019 13:12:02 +0200 Subject: [PATCH 09/45] Core: added micromagnetic energy gradients. Anisotropy, exchange and DMI. The implementation of the energy calculation is tentative. Also added some logging of micromagnetic input parser. --- .../engine/Hamiltonian_Micromagnetic.hpp | 8 +- core/src/engine/Hamiltonian_Micromagnetic.cpp | 362 +++++++++++++++++- core/src/io/Configparser.cpp | 7 +- 3 files changed, 368 insertions(+), 9 deletions(-) diff --git a/core/include/engine/Hamiltonian_Micromagnetic.hpp b/core/include/engine/Hamiltonian_Micromagnetic.hpp index cbc53c271..39c59cef5 100644 --- a/core/include/engine/Hamiltonian_Micromagnetic.hpp +++ b/core/include/engine/Hamiltonian_Micromagnetic.hpp @@ -53,7 +53,7 @@ namespace Engine void Hessian(const vectorfield & spins, MatrixX & hessian) override; void Gradient(const vectorfield & spins, vectorfield & gradient) override; void Energy_Contributions_per_Spin(const vectorfield & spins, std::vector> & contributions) override; - + void Energy_Update(const vectorfield & spins, std::vector> & contributions, vectorfield & gradient); // Calculate the total energy for a single spin to be used in Monte Carlo. // Note: therefore the energy of pairs is weighted x2 and of quadruplets x4. scalar Energy_Single_Spin(int ispin, const vectorfield & spins) override; @@ -65,7 +65,6 @@ namespace Engine // ------------ ... ------------ int spatial_gradient_order; - intfield boundary_conditions; // ------------ Single Spin Interactions ------------ // External magnetic field across the sample @@ -78,6 +77,9 @@ namespace Engine Matrix3 exchange_tensor; // DMI Matrix3 dmi_tensor; + neighbourfield neigh; + field spatial_gradient; + bool A_is_nondiagonal=true; private: // ------------ Effective Field Functions ------------ @@ -89,10 +91,12 @@ namespace Engine void Gradient_Exchange(const vectorfield & spins, vectorfield & gradient); // Calculate the DMI effective field of a Spin Pair void Gradient_DMI(const vectorfield & spins, vectorfield & gradient); + void Spatial_Gradient(const vectorfield & spins); // ------------ Energy Functions ------------ // Indices for Energy vector int idx_zeeman, idx_anisotropy, idx_exchange, idx_dmi, idx_ddi; + void E_Update(const vectorfield & spins, scalarfield & Energy, vectorfield & gradient); // Calculate the Zeeman energy of a Spin System void E_Zeeman(const vectorfield & spins, scalarfield & Energy); // Calculate the Anisotropy energy of a Spin System diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cpp b/core/src/engine/Hamiltonian_Micromagnetic.cpp index 5dab21371..0f1c4d332 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cpp +++ b/core/src/engine/Hamiltonian_Micromagnetic.cpp @@ -31,7 +31,7 @@ namespace Engine intfield boundary_conditions ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), geometry(geometry), external_field_magnitude(external_field_magnitude), external_field_normal(external_field_normal), - anisotropy_tensor(anisotropy_tensor) + anisotropy_tensor(anisotropy_tensor), exchange_tensor(exchange_tensor), dmi_tensor(dmi_tensor) { // Generate interaction pairs, constants etc. this->Update_Interactions(); @@ -47,7 +47,7 @@ namespace Engine intfield boundary_conditions ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), geometry(geometry), external_field_magnitude(external_field_magnitude), external_field_normal(external_field_normal), - anisotropy_tensor(anisotropy_tensor) + anisotropy_tensor(anisotropy_tensor), exchange_tensor(exchange_tensor), dmi_tensor(dmi_tensor) { // Generate interaction pairs, constants etc. this->Update_Interactions(); @@ -73,6 +73,104 @@ namespace Engine // TODO: prepare dipolar interactions // Update, which terms still contribute + + neigh = neighbourfield(0); + Neighbour neigh_tmp; + neigh_tmp.i = 0; + neigh_tmp.j = 0; + neigh_tmp.idx_shell = 0; + //order x -x y -y z -z xy (-x)(-y) x(-y) (-x)y xz (-x)(-z) x(-z) (-x)z yz (-y)(-z) y(-z) (-y)z results in 9 parts of Hessian + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = +1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + + this->spatial_gradient = field(geometry->nos, Matrix3::Zero()); this->Update_Energy_Contributions(); } @@ -121,7 +219,7 @@ namespace Engine // else this->idx_ddi = -1; } - + void Hamiltonian_Micromagnetic::Energy_Contributions_per_Spin(const vectorfield & spins, std::vector> & contributions) { if( contributions.size() != this->energy_contributions_per_spin.size() ) @@ -137,7 +235,7 @@ namespace Engine // Otherwise set to zero else Vectormath::fill(contrib.second, 0); } - + // External field if( this->idx_zeeman >=0 ) E_Zeeman(spins, contributions[idx_zeeman].second); @@ -162,11 +260,20 @@ namespace Engine } } + void Hamiltonian_Micromagnetic::E_Update(const vectorfield & spins, scalarfield & Energy, vectorfield & gradient) { + scalar Ms = 1711e3; + #pragma omp parallel for + for (int icell = 0; icell < geometry->n_cells_total; ++icell) + { + Energy[icell] -= 0.5 *Ms* gradient[icell].dot(spins[icell]); + } + } + void Hamiltonian_Micromagnetic::E_Anisotropy(const vectorfield & spins, scalarfield & Energy) { } - void Hamiltonian_Micromagnetic::E_Exchange(const vectorfield & spins, scalarfield & Energy) + void Hamiltonian_Micromagnetic::E_Exchange(const vectorfield & spins, scalarfield & Energy) { } @@ -186,10 +293,14 @@ namespace Engine } - void Hamiltonian_Micromagnetic::Gradient(const vectorfield & spins, vectorfield & gradient) + void Hamiltonian_Micromagnetic::Gradient(const vectorfield & spins, vectorfield & gradient) { + // std::cout << this->boundary_conditions[0] << std::endl; + // std::cout << boundary_conditions[0] << std::endl; + // Set to zero Vectormath::fill(gradient, {0,0,0}); + this->Spatial_Gradient(spins); // External field this->Gradient_Zeeman(gradient); @@ -202,6 +313,15 @@ namespace Engine // DMI this->Gradient_DMI(spins, gradient); + scalar Ms = 1711e3; + double energy=0; + #pragma omp parallel for reduction(-:energy) + for (int icell = 0; icell < geometry->n_cells_total; ++icell) + { + energy -= 0.5 *Ms* gradient[icell].dot(spins[icell]); + } + // printf("Energy total: %f\n", energy/ geometry->n_cells_total); + } @@ -219,14 +339,244 @@ namespace Engine void Hamiltonian_Micromagnetic::Gradient_Anisotropy(const vectorfield & spins, vectorfield & gradient) { + scalar Ms = 1711e3; + Vector3 temp1{ 1,0,0 }; + Vector3 temp2{ 0,1,0 }; + Vector3 temp3{ 0,0,1 }; + #pragma omp parallel for + for (int icell = 0; icell < geometry->n_cells_total; ++icell) + { + for (int iani = 0; iani < 1; ++iani) + { + int ispin = icell; + gradient[ispin] += 2.0 * 5e3 / Ms * temp3 * temp3.dot(spins[ispin]); + //gradient[ispin] -= 2.0 * this->anisotropy_magnitudes[iani] / Ms * ((pow(temp2.dot(spins[ispin]),2)+ pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1)+ (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)+(pow(temp1.dot(spins[ispin]),2)+ pow(temp2.dot(spins[ispin]), 2))*(temp3.dot(spins[ispin])*temp3)); + gradient[ispin] += 2.0 * 50000 / Ms * ((pow(temp2.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1) + (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)); + + } + } } void Hamiltonian_Micromagnetic::Gradient_Exchange(const vectorfield & spins, vectorfield & gradient) { + scalar delta[3] = { 4e-8,4e-8,4e-8 }; + scalar Ms = 1711e3; + //nongradient implementation + /* + #pragma omp parallel for + for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell) + { + int ispin = icell;//basically id of a cell + for (unsigned int i = 0; i < 3; ++i) + { + + int ispin_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i]); + int ispin_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i + 1]); + if (ispin_plus == -1) { + ispin_plus = ispin; + } + if (ispin_minus == -1) { + ispin_minus = ispin; + } + gradient[ispin][i] -= exchange_tensor(i, i)*(spins[ispin_plus][i] - 2 * spins[ispin][i] + spins[ispin_minus][i]) / (delta[i]) / (delta[i]); + + } + if (A_is_nondiagonal == true) { + int ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[6]); + int ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[7]); + int ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[8]); + int ispin_minus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[9]); + + if (ispin_plus_plus == -1) { + ispin_plus_plus = ispin; + } + if (ispin_minus_minus == -1) { + ispin_minus_minus = ispin; + } + if (ispin_plus_minus == -1) { + ispin_plus_minus = ispin; + } + if (ispin_minus_plus == -1) { + ispin_minus_plus = ispin; + } + gradient[ispin][0] -= exchange_tensor(0, 1)*(spins[ispin_plus_plus][1] - spins[ispin_plus_minus][1] - spins[ispin_minus_plus][1] + spins[ispin_minus_minus][1]) / (delta[0]) / (delta[1]) / 4; + gradient[ispin][1] -= exchange_tensor(1, 0)*(spins[ispin_plus_plus][0] - spins[ispin_plus_minus][0] - spins[ispin_minus_plus][0] + spins[ispin_minus_minus][0]) / (delta[0]) / (delta[1]) / 4; + + ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[10]); + ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[11]); + ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[12]); + ispin_minus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[13]); + + if (ispin_plus_plus == -1) { + ispin_plus_plus = ispin; + } + if (ispin_minus_minus == -1) { + ispin_minus_minus = ispin; + } + if (ispin_plus_minus == -1) { + ispin_plus_minus = ispin; + } + if (ispin_minus_plus == -1) { + ispin_minus_plus = ispin; + } + gradient[ispin][0] -= exchange_tensor(0, 2)*(spins[ispin_plus_plus][2] - spins[ispin_plus_minus][2] - spins[ispin_minus_plus][2] + spins[ispin_minus_minus][2]) / (delta[0]) / (delta[2]) / 4; + gradient[ispin][2] -= exchange_tensor(2, 0)*(spins[ispin_plus_plus][0] - spins[ispin_plus_minus][0] - spins[ispin_minus_plus][0] + spins[ispin_minus_minus][0]) / (delta[0]) / (delta[2]) / 4; + + ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[14]); + ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[15]); + ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[16]); + ispin_minus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[17]); + + if (ispin_plus_plus == -1) { + ispin_plus_plus = ispin; + } + if (ispin_minus_minus == -1) { + ispin_minus_minus = ispin; + } + if (ispin_plus_minus == -1) { + ispin_plus_minus = ispin; + } + if (ispin_minus_plus == -1) { + ispin_minus_plus = ispin; + } + gradient[ispin][1] -= exchange_tensor(1, 2)*(spins[ispin_plus_plus][2] - spins[ispin_plus_minus][2] - spins[ispin_minus_plus][2] + spins[ispin_minus_minus][2]) / (delta[1]) / (delta[2]) / 4; + gradient[ispin][2] -= exchange_tensor(2, 1)*(spins[ispin_plus_plus][1] - spins[ispin_plus_minus][1] - spins[ispin_minus_plus][1] + spins[ispin_minus_minus][1]) / (delta[1]) / (delta[2]) / 4; + } + + }*/ + + //gradient implementation + #pragma omp parallel for + for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell) + { + int ispin = icell;//basically id of a cell + for (unsigned int i = 0; i < 3; ++i) + { + + int ispin_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i]); + int ispin_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i + 1]); + if (ispin_plus == -1) { + ispin_plus = ispin; + } + if (ispin_minus == -1) { + ispin_minus = ispin; + } + + gradient[ispin][i] += exchange_tensor(i, i)/Ms*(spins[ispin_plus][i] - 2 * spins[ispin][i] + spins[ispin_minus][i]) / (delta[i]) / (delta[i]); + + } + if (this->A_is_nondiagonal == true) { + //xy + int ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[0]); + int ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); + int ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2]); + int ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); + + if (ispin_right == -1) { + ispin_right = ispin; + } + if (ispin_left == -1) { + ispin_left = ispin; + } + if (ispin_top == -1) { + ispin_top = ispin; + } + if (ispin_bottom == -1) { + ispin_bottom = ispin; + } + gradient[ispin][0] += exchange_tensor(0, 1) / Ms *((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); + gradient[ispin][1] += exchange_tensor(1, 0) / Ms *((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); + + //xz + ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[0]); + ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); + ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[4]); + ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); + + if (ispin_right == -1) { + ispin_right = ispin; + } + if (ispin_left == -1) { + ispin_left = ispin; + } + if (ispin_top == -1) { + ispin_top = ispin; + } + if (ispin_bottom == -1) { + ispin_bottom = ispin; + } + gradient[ispin][0] += exchange_tensor(0, 2) / Ms *((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + gradient[ispin][2] += exchange_tensor(2, 0) / Ms *((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + + //yz + ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2]); + ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); + ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[4]); + ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); + + if (ispin_right == -1) { + ispin_right = ispin; + } + if (ispin_left == -1) { + ispin_left = ispin; + } + if (ispin_top == -1) { + ispin_top = ispin; + } + if (ispin_bottom == -1) { + ispin_bottom = ispin; + } + gradient[ispin][1] += exchange_tensor(1, 2) / Ms *((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[1]); + gradient[ispin][2] += exchange_tensor(2, 1) / Ms *((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[1]); + + } + + } + } + void Hamiltonian_Micromagnetic::Spatial_Gradient(const vectorfield & spins) { + scalar delta[3] = { 4e-8,4e-8,4e-8 }; + /* + dn1/dr1 dn1/dr2 dn1/dr3 + dn2/dr1 dn2/dr2 dn2/dr3 + dn3/dr1 dn3/dr2 dn3/dr3 + */ + #pragma omp parallel for + for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell) + { + int ispin = icell;//basically id of a cell + for (unsigned int i = 0; i < 3; ++i) + { + int ispin_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i]); + int ispin_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i + 1]); + if (ispin_plus == -1) { + ispin_plus = ispin; + } + if (ispin_minus == -1) { + ispin_minus = ispin; + } + spatial_gradient[ispin](0, i) = (spins[ispin_plus][0] - spins[ispin_minus][0]) / (delta[i]) / 2; + spatial_gradient[ispin](1, i) = (spins[ispin_plus][1] - spins[ispin_minus][1]) / (delta[i]) / 2; + spatial_gradient[ispin](2, i) = (spins[ispin_plus][2] - spins[ispin_minus][2]) / (delta[i]) / 2; + + } + } + } + void Hamiltonian_Micromagnetic::Gradient_DMI(const vectorfield & spins, vectorfield & gradient) { + #pragma omp parallel for + for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell) + { + int ispin = icell;//basically id of a cell + for (unsigned int i = 0; i < 3; ++i) + { + gradient[ispin][0] += 4*dmi_tensor(1, i)*spatial_gradient[ispin](2, i) - 4*dmi_tensor(2, i)*spatial_gradient[ispin](1, i); + gradient[ispin][1] += 4*dmi_tensor(2, i)*spatial_gradient[ispin](0, i) - 4*dmi_tensor(0, i)*spatial_gradient[ispin](2, i); + gradient[ispin][2] += 4*dmi_tensor(0, i)*spatial_gradient[ispin](1, i) - 4*dmi_tensor(1, i)*spatial_gradient[ispin](0, i); + } + } } diff --git a/core/src/io/Configparser.cpp b/core/src/io/Configparser.cpp index aa4004979..855ee0f1b 100644 --- a/core/src/io/Configparser.cpp +++ b/core/src/io/Configparser.cpp @@ -1586,7 +1586,12 @@ namespace IO spirit_handle_exception_core(fmt::format( "Unable to parse all parameters of the Micromagnetic Hamiltonian from \"{}\"", configFile)); } - + // Return + Log(Log_Level::Parameter, Log_Sender::IO, "Hamiltonian_Heisenberg:"); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<21} = {} {} {}", "boundary conditions", boundary_conditions[0], boundary_conditions[1], boundary_conditions[2])); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<21} = {}", "external field", field)); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<21} = {}", "field_normal", field_normal.transpose())); + auto hamiltonian = std::unique_ptr(new Engine::Hamiltonian_Micromagnetic( field, field_normal, anisotropy_tensor, From f51028c5aa451c1463dc4c9a6cb66f6cfc130243 Mon Sep 17 00:00:00 2001 From: Dmitrii Tolmachev Date: Fri, 28 Jun 2019 15:05:57 +0200 Subject: [PATCH 10/45] Hamiltonian micromagnetic -fixed anisotropy, exchange DMI - passing skyrmion convergence test now --- core/src/engine/Hamiltonian_Micromagnetic.cpp | 67 ++++++++++++------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cpp b/core/src/engine/Hamiltonian_Micromagnetic.cpp index 0f1c4d332..f5b9c85c2 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cpp +++ b/core/src/engine/Hamiltonian_Micromagnetic.cpp @@ -261,7 +261,7 @@ namespace Engine } void Hamiltonian_Micromagnetic::E_Update(const vectorfield & spins, scalarfield & Energy, vectorfield & gradient) { - scalar Ms = 1711e3; + scalar Ms = 1.4e6; #pragma omp parallel for for (int icell = 0; icell < geometry->n_cells_total; ++icell) { @@ -296,7 +296,7 @@ namespace Engine void Hamiltonian_Micromagnetic::Gradient(const vectorfield & spins, vectorfield & gradient) { // std::cout << this->boundary_conditions[0] << std::endl; - // std::cout << boundary_conditions[0] << std::endl; + // std::cout << boundary_conditions[0] << std::endl; // Set to zero Vectormath::fill(gradient, {0,0,0}); @@ -313,7 +313,7 @@ namespace Engine // DMI this->Gradient_DMI(spins, gradient); - scalar Ms = 1711e3; + scalar Ms = 1.4e6; double energy=0; #pragma omp parallel for reduction(-:energy) for (int icell = 0; icell < geometry->n_cells_total; ++icell) @@ -339,7 +339,7 @@ namespace Engine void Hamiltonian_Micromagnetic::Gradient_Anisotropy(const vectorfield & spins, vectorfield & gradient) { - scalar Ms = 1711e3; + scalar Ms = 1.4e6; Vector3 temp1{ 1,0,0 }; Vector3 temp2{ 0,1,0 }; Vector3 temp3{ 0,0,1 }; @@ -349,9 +349,9 @@ namespace Engine for (int iani = 0; iani < 1; ++iani) { int ispin = icell; - gradient[ispin] += 2.0 * 5e3 / Ms * temp3 * temp3.dot(spins[ispin]); + gradient[ispin] -= 2.0 * 8.44e6 / Ms * temp3 * temp3.dot(spins[ispin]); //gradient[ispin] -= 2.0 * this->anisotropy_magnitudes[iani] / Ms * ((pow(temp2.dot(spins[ispin]),2)+ pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1)+ (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)+(pow(temp1.dot(spins[ispin]),2)+ pow(temp2.dot(spins[ispin]), 2))*(temp3.dot(spins[ispin])*temp3)); - gradient[ispin] += 2.0 * 50000 / Ms * ((pow(temp2.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1) + (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)); + //gradient[ispin] += 2.0 * 50000 / Ms * ((pow(temp2.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1) + (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)); } } @@ -359,8 +359,8 @@ namespace Engine void Hamiltonian_Micromagnetic::Gradient_Exchange(const vectorfield & spins, vectorfield & gradient) { - scalar delta[3] = { 4e-8,4e-8,4e-8 }; - scalar Ms = 1711e3; + scalar delta[3] = { 3e-10,3e-10,3e-10 }; + scalar Ms = 1.4e6; //nongradient implementation /* #pragma omp parallel for @@ -453,16 +453,18 @@ namespace Engine for (unsigned int i = 0; i < 3; ++i) { - int ispin_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i]); - int ispin_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i + 1]); + int ispin_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i]); + int ispin_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i + 1]); if (ispin_plus == -1) { ispin_plus = ispin; } if (ispin_minus == -1) { ispin_minus = ispin; } - - gradient[ispin][i] += exchange_tensor(i, i)/Ms*(spins[ispin_plus][i] - 2 * spins[ispin][i] + spins[ispin_minus][i]) / (delta[i]) / (delta[i]); + + gradient[ispin][0] -= 2 * exchange_tensor(i, i) / Ms *(spins[ispin_plus][0] - 2 * spins[ispin][0] + spins[ispin_minus][0]) / (delta[i]) / (delta[i]); + gradient[ispin][1] -= 2 * exchange_tensor(i, i) / Ms * (spins[ispin_plus][1] - 2 * spins[ispin][1] + spins[ispin_minus][1]) / (delta[i]) / (delta[i]); + gradient[ispin][2] -= 2 * exchange_tensor(i, i) / Ms * (spins[ispin_plus][2] - 2 * spins[ispin][2] + spins[ispin_minus][2]) / (delta[i]) / (delta[i]); } if (this->A_is_nondiagonal == true) { @@ -484,9 +486,13 @@ namespace Engine if (ispin_bottom == -1) { ispin_bottom = ispin; } - gradient[ispin][0] += exchange_tensor(0, 1) / Ms *((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); - gradient[ispin][1] += exchange_tensor(1, 0) / Ms *((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); - + gradient[ispin][0] -= exchange_tensor(0, 1) / Ms *((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); + gradient[ispin][0] -= exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); + gradient[ispin][1] -= exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); + gradient[ispin][1] -= exchange_tensor(1, 0) / Ms *((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); + gradient[ispin][2] -= exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); + gradient[ispin][2] -= exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); + //xz ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[0]); ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); @@ -505,8 +511,12 @@ namespace Engine if (ispin_bottom == -1) { ispin_bottom = ispin; } - gradient[ispin][0] += exchange_tensor(0, 2) / Ms *((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - gradient[ispin][2] += exchange_tensor(2, 0) / Ms *((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + gradient[ispin][0] -= exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + gradient[ispin][0] -= exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + gradient[ispin][1] -= exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + gradient[ispin][1] -= exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + gradient[ispin][2] -= exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + gradient[ispin][2] -= exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); //yz ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2]); @@ -525,9 +535,13 @@ namespace Engine } if (ispin_bottom == -1) { ispin_bottom = ispin; - } - gradient[ispin][1] += exchange_tensor(1, 2) / Ms *((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[1]); - gradient[ispin][2] += exchange_tensor(2, 1) / Ms *((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[1]); + } + gradient[ispin][0] -= exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + gradient[ispin][0] -= exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + gradient[ispin][1] -= exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + gradient[ispin][1] -= exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + gradient[ispin][2] -= exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + gradient[ispin][2] -= exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); } @@ -536,7 +550,7 @@ namespace Engine } void Hamiltonian_Micromagnetic::Spatial_Gradient(const vectorfield & spins) { - scalar delta[3] = { 4e-8,4e-8,4e-8 }; + scalar delta[3] = { 3e-10,3e-10,3e-10 }; /* dn1/dr1 dn1/dr2 dn1/dr3 dn2/dr1 dn2/dr2 dn2/dr3 @@ -548,8 +562,8 @@ namespace Engine int ispin = icell;//basically id of a cell for (unsigned int i = 0; i < 3; ++i) { - int ispin_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i]); - int ispin_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i + 1]); + int ispin_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i]); + int ispin_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i + 1]); if (ispin_plus == -1) { ispin_plus = ispin; } @@ -566,15 +580,16 @@ namespace Engine void Hamiltonian_Micromagnetic::Gradient_DMI(const vectorfield & spins, vectorfield & gradient) { + scalar Ms = 1.4e6; #pragma omp parallel for for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell) { int ispin = icell;//basically id of a cell for (unsigned int i = 0; i < 3; ++i) { - gradient[ispin][0] += 4*dmi_tensor(1, i)*spatial_gradient[ispin](2, i) - 4*dmi_tensor(2, i)*spatial_gradient[ispin](1, i); - gradient[ispin][1] += 4*dmi_tensor(2, i)*spatial_gradient[ispin](0, i) - 4*dmi_tensor(0, i)*spatial_gradient[ispin](2, i); - gradient[ispin][2] += 4*dmi_tensor(0, i)*spatial_gradient[ispin](1, i) - 4*dmi_tensor(1, i)*spatial_gradient[ispin](0, i); + gradient[ispin][0] += 2*dmi_tensor(1, i) / Ms *spatial_gradient[ispin](2, i) - 2*dmi_tensor(2, i) / Ms *spatial_gradient[ispin](1, i); + gradient[ispin][1] += 2*dmi_tensor(2, i) / Ms *spatial_gradient[ispin](0, i) - 2*dmi_tensor(0, i) / Ms *spatial_gradient[ispin](2, i); + gradient[ispin][2] += 2*dmi_tensor(0, i) / Ms *spatial_gradient[ispin](1, i) - 2*dmi_tensor(1, i) / Ms *spatial_gradient[ispin](0, i); } } } From d9cd626600e81a204baae9ba7b5806404f881dc6 Mon Sep 17 00:00:00 2001 From: Dmitrii Tolmachev Date: Wed, 14 Aug 2019 14:56:00 +0200 Subject: [PATCH 11/45] Cuda implementation of micromagnetics (no ddi) --- .../engine/Hamiltonian_Micromagnetic.hpp | 2 +- core/src/engine/Hamiltonian_Micromagnetic.cpp | 6 +- core/src/engine/Hamiltonian_Micromagnetic.cu | 455 +++++++++++++++--- 3 files changed, 392 insertions(+), 71 deletions(-) diff --git a/core/include/engine/Hamiltonian_Micromagnetic.hpp b/core/include/engine/Hamiltonian_Micromagnetic.hpp index 39c59cef5..9041510c3 100644 --- a/core/include/engine/Hamiltonian_Micromagnetic.hpp +++ b/core/include/engine/Hamiltonian_Micromagnetic.hpp @@ -77,7 +77,7 @@ namespace Engine Matrix3 exchange_tensor; // DMI Matrix3 dmi_tensor; - neighbourfield neigh; + pairfield neigh; field spatial_gradient; bool A_is_nondiagonal=true; diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cpp b/core/src/engine/Hamiltonian_Micromagnetic.cpp index f5b9c85c2..d9003cda0 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cpp +++ b/core/src/engine/Hamiltonian_Micromagnetic.cpp @@ -74,7 +74,7 @@ namespace Engine // Update, which terms still contribute - neigh = neighbourfield(0); + neigh = pairfield(0); Neighbour neigh_tmp; neigh_tmp.i = 0; neigh_tmp.j = 0; @@ -359,7 +359,7 @@ namespace Engine void Hamiltonian_Micromagnetic::Gradient_Exchange(const vectorfield & spins, vectorfield & gradient) { - scalar delta[3] = { 3e-10,3e-10,3e-10 }; + scalar delta[3] = { 3e-11,3e-11,3e-10 }; scalar Ms = 1.4e6; //nongradient implementation /* @@ -550,7 +550,7 @@ namespace Engine } void Hamiltonian_Micromagnetic::Spatial_Gradient(const vectorfield & spins) { - scalar delta[3] = { 3e-10,3e-10,3e-10 }; + scalar delta[3] = { 3e-11,3e-11,3e-10 }; /* dn1/dr1 dn1/dr2 dn1/dr3 dn2/dr1 dn2/dr2 dn2/dr3 diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cu b/core/src/engine/Hamiltonian_Micromagnetic.cu index a243ee2eb..b4c04b424 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cu +++ b/core/src/engine/Hamiltonian_Micromagnetic.cu @@ -23,37 +23,37 @@ using Engine::Vectormath::cu_tupel_from_idx; namespace Engine { - Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( - scalar external_field_magnitude, Vector3 external_field_normal, - Matrix3 anisotropy_tensor, - scalar exchange_constant, - scalar dmi_constant, - std::shared_ptr geometry, - int spatial_gradient_order, - intfield boundary_conditions - ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), - external_field_magnitude(external_field_magnitude), - anisotropy_tensor(anisotropy_tensor) - { - // Generate interaction pairs, constants etc. - this->Update_Interactions(); - } - - Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( - scalar external_field_magnitude, Vector3 external_field_normal, - Matrix3 anisotropy_tensor, - Matrix3 exchange_tensor, - Matrix3 dmi_tensor, - std::shared_ptr geometry, - int spatial_gradient_order, - intfield boundary_conditions - ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), - external_field_magnitude(external_field_magnitude), - anisotropy_tensor(anisotropy_tensor) - { - // Generate interaction pairs, constants etc. - this->Update_Interactions(); - } + Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( + scalar external_field_magnitude, Vector3 external_field_normal, + Matrix3 anisotropy_tensor, + Matrix3 exchange_tensor, + Matrix3 dmi_tensor, + std::shared_ptr geometry, + int spatial_gradient_order, + intfield boundary_conditions + ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), geometry(geometry), + external_field_magnitude(external_field_magnitude), external_field_normal(external_field_normal), + anisotropy_tensor(anisotropy_tensor), exchange_tensor(exchange_tensor), dmi_tensor(dmi_tensor) + { + // Generate interaction pairs, constants etc. + this->Update_Interactions(); + } + + Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( + scalar external_field_magnitude, Vector3 external_field_normal, + Matrix3 anisotropy_tensor, + scalar exchange_constant, + scalar dmi_constant, + std::shared_ptr geometry, + int spatial_gradient_order, + intfield boundary_conditions + ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), geometry(geometry), + external_field_magnitude(external_field_magnitude), external_field_normal(external_field_normal), + anisotropy_tensor(anisotropy_tensor), exchange_tensor(exchange_tensor), dmi_tensor(dmi_tensor) + { + // Generate interaction pairs, constants etc. + this->Update_Interactions(); + } void Hamiltonian_Micromagnetic::Update_Interactions() { @@ -73,7 +73,102 @@ namespace Engine // TODO: generate neighbour information for pairwise interactions // TODO: prepare dipolar interactions - + neigh = pairfield(0); + Neighbour neigh_tmp; + neigh_tmp.i = 0; + neigh_tmp.j = 0; + neigh_tmp.idx_shell = 0; + //order x -x y -y z -z xy (-x)(-y) x(-y) (-x)y xz (-x)(-z) x(-z) (-x)z yz (-y)(-z) y(-z) (-y)z results in 9 parts of Hessian + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = +1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + this->spatial_gradient = field(geometry->nos, Matrix3::Zero()); // Update, which terms still contribute this->Update_Energy_Contributions(); } @@ -81,7 +176,7 @@ namespace Engine void Hamiltonian_Micromagnetic::Update_Energy_Contributions() { this->energy_contributions_per_spin = std::vector>(0); - + CU_CHECK_AND_SYNC(); // External field if( this->external_field_magnitude > 0 ) { @@ -150,20 +245,30 @@ namespace Engine if( this->idx_exchange >=0 ) E_Exchange(spins, contributions[idx_exchange].second); // DMI if( this->idx_dmi >=0 ) E_DMI(spins,contributions[idx_dmi].second); - } - - void Hamiltonian_Micromagnetic::E_Zeeman(const vectorfield & spins, scalarfield & Energy) - { - auto& mu_s = this->geometry->mu_s; - #pragma omp parallel for - for( int icell = 0; icell < geometry->n_cells_total; ++icell ) - { - if( check_atom_type(this->geometry->atom_types[icell]) ) - Energy[icell] -= mu_s[icell] * this->external_field_magnitude * this->external_field_normal.dot(spins[icell]); - } } + __global__ void CU_E_Zeeman1(const Vector3 * spins, const int * atom_types, const int n_cell_atoms, const scalar * mu_s, const scalar external_field_magnitude, const Vector3 external_field_normal, scalar * Energy, size_t n_cells_total) + { + for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; + icell < n_cells_total; + icell += blockDim.x * gridDim.x) + { + for (int ibasis = 0; ibasis < n_cell_atoms; ++ibasis) + { + int ispin = icell + ibasis; + if (cu_check_atom_type(atom_types[ispin])) + Energy[ispin] -= mu_s[ispin] * external_field_magnitude * external_field_normal.dot(spins[ispin]); + } + } + } + void Hamiltonian_Micromagnetic::E_Zeeman(const vectorfield & spins, scalarfield & Energy) + { + int size = geometry->n_cells_total; + CU_E_Zeeman1 << <(size + 1023) / 1024, 1024 >> > (spins.data(), this->geometry->atom_types.data(), geometry->n_cell_atoms, geometry->mu_s.data(), this->external_field_magnitude, this->external_field_normal, Energy.data(), size); + CU_CHECK_AND_SYNC(); + } + void Hamiltonian_Micromagnetic::E_Anisotropy(const vectorfield & spins, scalarfield & Energy) { } @@ -192,7 +297,7 @@ namespace Engine { // Set to zero Vectormath::fill(gradient, {0,0,0}); - + this->Spatial_Gradient(spins); // External field this->Gradient_Zeeman(gradient); @@ -204,32 +309,248 @@ namespace Engine // DMI this->Gradient_DMI(spins, gradient); - } - - - void Hamiltonian_Micromagnetic::Gradient_Zeeman(vectorfield & gradient) - { - auto& mu_s = this->geometry->mu_s; + scalar Ms = 1.4e6; + double energy = 0; + #pragma omp parallel for reduction(-:energy) + for (int icell = 0; icell < geometry->n_cells_total; ++icell) + { + //energy -= 0.5 *Ms* gradient[icell].dot(spins[icell]); + } + //printf("Energy total: %f\n", energy/ geometry->n_cells_total); - #pragma omp parallel for - for( int icell = 0; icell < geometry->n_cells_total; ++icell ) - { - if( check_atom_type(this->geometry->atom_types[icell]) ) - gradient[icell] -= mu_s[icell] * this->external_field_magnitude * this->external_field_normal; - } } - void Hamiltonian_Micromagnetic::Gradient_Anisotropy(const vectorfield & spins, vectorfield & gradient) - { - } - - void Hamiltonian_Micromagnetic::Gradient_Exchange(const vectorfield & spins, vectorfield & gradient) - { - } - void Hamiltonian_Micromagnetic::Gradient_DMI(const vectorfield & spins, vectorfield & gradient) - { - } + __global__ void CU_Gradient_Zeeman1(const int * atom_types, const int n_cell_atoms, const scalar * mu_s, const scalar external_field_magnitude, const Vector3 external_field_normal, Vector3 * gradient, size_t n_cells_total) + { + for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; + icell < n_cells_total; + icell += blockDim.x * gridDim.x) + { + for (int ibasis = 0; ibasis < n_cell_atoms; ++ibasis) + { + int ispin = icell + ibasis; + if (cu_check_atom_type(atom_types[ispin])) + gradient[ispin] -= mu_s[ispin] * external_field_magnitude*external_field_normal; + } + } + } + void Hamiltonian_Micromagnetic::Gradient_Zeeman(vectorfield & gradient) + { + int size = geometry->n_cells_total; + CU_Gradient_Zeeman1 << <(size + 1023) / 1024, 1024 >> > (this->geometry->atom_types.data(), geometry->n_cell_atoms, geometry->mu_s.data(), this->external_field_magnitude, this->external_field_normal, gradient.data(), size); + CU_CHECK_AND_SYNC(); + } + + __global__ void CU_Gradient_Anisotropy1(const Vector3 * spins, const int * atom_types, const int n_cell_atoms, Vector3 * gradient, size_t n_cells_total) + { + scalar Ms = 1.4e6; + Vector3 temp1{ 1,0,0 }; + Vector3 temp2{ 0,1,0 }; + Vector3 temp3{ 0,0,1 }; + for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; + icell < n_cells_total; + icell += blockDim.x * gridDim.x) + { + for (int iani = 0; iani < 1; ++iani) + { + int ispin = icell; + gradient[ispin] -= 2.0 * 8.44e6 / Ms * temp3 * temp3.dot(spins[ispin]); + //gradient[ispin] -= 2.0 * this->anisotropy_magnitudes[iani] / Ms * ((pow(temp2.dot(spins[ispin]),2)+ pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1)+ (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)+(pow(temp1.dot(spins[ispin]),2)+ pow(temp2.dot(spins[ispin]), 2))*(temp3.dot(spins[ispin])*temp3)); + //gradient[ispin] += 2.0 * 50000 / Ms * ((pow(temp2.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1) + (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)); + + } + } + } + + void Hamiltonian_Micromagnetic::Gradient_Anisotropy(const vectorfield & spins, vectorfield & gradient) + { + int size = geometry->n_cells_total; + CU_Gradient_Anisotropy1 << <(size + 1023) / 1024, 1024 >> > (spins.data(), this->geometry->atom_types.data(), this->geometry->n_cell_atoms, gradient.data(), size); + CU_CHECK_AND_SYNC(); + } + + __global__ void CU_Gradient_Exchange1(const Vector3 * spins, const int * atom_types, const int * boundary_conditions, const int * n_cells, int n_cell_atoms, + int n_pairs, const Pair * neigh, Vector3 * gradient, size_t size, bool A_is_nondiagonal, Matrix3 exchange_tensor) + { + int bc[3] = { boundary_conditions[0],boundary_conditions[1],boundary_conditions[2] }; + int nc[3] = { n_cells[0],n_cells[1],n_cells[2] }; + scalar delta[3] = { 3e-11,3e-11,3e-10 }; + scalar Ms = 1.4e6; + for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; + icell < size; + icell += blockDim.x * gridDim.x) + { + int ispin = icell;//basically id of a cell + for (unsigned int i = 0; i < 3; ++i) + { + + int ispin_plus = cu_idx_from_pair(ispin, bc, nc, n_cell_atoms, atom_types, neigh[2 * i]); + int ispin_minus = cu_idx_from_pair(ispin, bc, nc, n_cell_atoms, atom_types, neigh[2 * i + 1]); + if (ispin_plus == -1) { + ispin_plus = ispin; + } + if (ispin_minus == -1) { + ispin_minus = ispin; + } + + gradient[ispin][0] -= 2 * exchange_tensor(i, i) / Ms * (spins[ispin_plus][0] - 2 * spins[ispin][0] + spins[ispin_minus][0]) / (delta[i]) / (delta[i]); + gradient[ispin][1] -= 2 * exchange_tensor(i, i) / Ms * (spins[ispin_plus][1] - 2 * spins[ispin][1] + spins[ispin_minus][1]) / (delta[i]) / (delta[i]); + gradient[ispin][2] -= 2 * exchange_tensor(i, i) / Ms * (spins[ispin_plus][2] - 2 * spins[ispin][2] + spins[ispin_minus][2]) / (delta[i]) / (delta[i]); + + } + /*if (A_is_nondiagonal == true) { + //xy + int ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[0]); + int ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); + int ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2]); + int ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); + + if (ispin_right == -1) { + ispin_right = ispin; + } + if (ispin_left == -1) { + ispin_left = ispin; + } + if (ispin_top == -1) { + ispin_top = ispin; + } + if (ispin_bottom == -1) { + ispin_bottom = ispin; + } + gradient[ispin][0] -= 2 * exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); + gradient[ispin][0] -= 2 * exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); + gradient[ispin][1] -= 2 * exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); + gradient[ispin][1] -= 2 * exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); + gradient[ispin][2] -= 2 * exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); + gradient[ispin][2] -= 2 * exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); + + //xz + ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[0]); + ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); + ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[4]); + ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); + + if (ispin_right == -1) { + ispin_right = ispin; + } + if (ispin_left == -1) { + ispin_left = ispin; + } + if (ispin_top == -1) { + ispin_top = ispin; + } + if (ispin_bottom == -1) { + ispin_bottom = ispin; + } + gradient[ispin][0] -= 2 * exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + gradient[ispin][0] -= 2 * exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + gradient[ispin][1] -= 2 * exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + gradient[ispin][1] -= 2 * exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + gradient[ispin][2] -= 2 * exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + gradient[ispin][2] -= 2 * exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + + //yz + ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2]); + ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); + ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[4]); + ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); + + if (ispin_right == -1) { + ispin_right = ispin; + } + if (ispin_left == -1) { + ispin_left = ispin; + } + if (ispin_top == -1) { + ispin_top = ispin; + } + if (ispin_bottom == -1) { + ispin_bottom = ispin; + } + gradient[ispin][0] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + gradient[ispin][0] -= 2 * exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + gradient[ispin][1] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + gradient[ispin][1] -= 2 * exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + gradient[ispin][2] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + gradient[ispin][2] -= 2 * exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + + }*/ + + } + } + void Hamiltonian_Micromagnetic::Gradient_Exchange(const vectorfield & spins, vectorfield & gradient) + { + int size = geometry->n_cells_total; + CU_Gradient_Exchange1 << <(size + 1023) / 1024, 1024 >> > (spins.data(), this->geometry->atom_types.data(), boundary_conditions.data(), geometry->n_cells.data(), geometry->n_cell_atoms, + this->neigh.size(), this->neigh.data(), gradient.data(), size, A_is_nondiagonal, exchange_tensor); + CU_CHECK_AND_SYNC(); + } + + __global__ void CU_Spatial_Gradient(const Vector3 * spins, const int * atom_types, const int * boundary_conditions, const int * n_cells, int n_cell_atoms, + int n_pairs, const Pair * neigh, Matrix3 * spatial_gradient, size_t size) + { + scalar delta[3] = { 3e-11,3e-11,3e-10 }; + /* + dn1/dr1 dn1/dr2 dn1/dr3 + dn2/dr1 dn2/dr2 dn2/dr3 + dn3/dr1 dn3/dr2 dn3/dr3 + */ + int bc[3] = { boundary_conditions[0],boundary_conditions[1],boundary_conditions[2] }; + int nc[3] = { n_cells[0],n_cells[1],n_cells[2] }; + scalar Ms = 1.4e6; + for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; + icell < size; + icell += blockDim.x * gridDim.x) + { + int ispin = icell;//basically id of a cell + for (unsigned int i = 0; i < 3; ++i) + { + int ispin_plus = cu_idx_from_pair(ispin, bc,nc, n_cell_atoms, atom_types, neigh[2 * i]); + int ispin_minus = cu_idx_from_pair(ispin, bc, nc, n_cell_atoms, atom_types, neigh[2 * i + 1]); + if (ispin_plus == -1) { + ispin_plus = ispin; + } + if (ispin_minus == -1) { + ispin_minus = ispin; + } + spatial_gradient[ispin](0, i) = (spins[ispin_plus][0] - spins[ispin_minus][0]) / (delta[i]) / 2; + spatial_gradient[ispin](1, i) = (spins[ispin_plus][1] - spins[ispin_minus][1]) / (delta[i]) / 2; + spatial_gradient[ispin](2, i) = (spins[ispin_plus][2] - spins[ispin_minus][2]) / (delta[i]) / 2; + + } + } + } + void Hamiltonian_Micromagnetic::Spatial_Gradient(const vectorfield & spins) + { + int size = geometry->n_cells_total; + CU_Spatial_Gradient << <(size + 1023) / 1024, 1024 >> > (spins.data(), this->geometry->atom_types.data(), boundary_conditions.data(), geometry->n_cells.data(), geometry->n_cell_atoms, + this->neigh.size(), this->neigh.data(), spatial_gradient.data(), size); + CU_CHECK_AND_SYNC(); + } + + __global__ void CU_Gradient_DMI1(const Vector3 * spins, Vector3 * gradient, Matrix3 * spatial_gradient, size_t size, Matrix3 dmi_tensor) + { + scalar Ms = 1.4e6; + for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; + icell < size; + icell += blockDim.x * gridDim.x) + { + int ispin = icell;//basically id of a cell + for (unsigned int i = 0; i < 3; ++i) + { + gradient[ispin][0] += 2 * dmi_tensor(1, i) / Ms * spatial_gradient[ispin](2, i) - 2 * dmi_tensor(2, i) / Ms * spatial_gradient[ispin](1, i); + gradient[ispin][1] += 2 * dmi_tensor(2, i) / Ms * spatial_gradient[ispin](0, i) - 2 * dmi_tensor(0, i) / Ms * spatial_gradient[ispin](2, i); + gradient[ispin][2] += 2 * dmi_tensor(0, i) / Ms * spatial_gradient[ispin](1, i) - 2 * dmi_tensor(1, i) / Ms * spatial_gradient[ispin](0, i); + } + } + } + void Hamiltonian_Micromagnetic::Gradient_DMI(const vectorfield & spins, vectorfield & gradient) + { + int size = geometry->n_cells_total; + CU_Gradient_DMI1 << <(size + 1023) / 1024, 1024 >> > (spins.data(), gradient.data(), spatial_gradient.data(), size, dmi_tensor); + CU_CHECK_AND_SYNC(); + } void Hamiltonian_Micromagnetic::Hessian(const vectorfield & spins, MatrixX & hessian) From b8b0bcfc04367496a027832766caf08095d1cd7b Mon Sep 17 00:00:00 2001 From: Gideon Date: Tue, 24 Sep 2019 19:58:50 +0200 Subject: [PATCH 12/45] Core: Started to implement input parsing for micromagnetic hamiltonian. --- core/include/data/Geometry.hpp | 12 +- .../engine/Hamiltonian_Micromagnetic.hpp | 11 +- core/src/Spirit/Hamiltonian.cpp | 5 +- core/src/data/Geometry.cpp | 3 + core/src/engine/Hamiltonian_Micromagnetic.cpp | 743 +++++++++--------- core/src/io/Configparser.cpp | 72 +- 6 files changed, 433 insertions(+), 413 deletions(-) diff --git a/core/include/data/Geometry.hpp b/core/include/data/Geometry.hpp index 93e350be4..a892db8e9 100644 --- a/core/include/data/Geometry.hpp +++ b/core/include/data/Geometry.hpp @@ -138,10 +138,12 @@ namespace Data vectorfield mask_pinned_cells; // Dimensionality of the points int dimensionality; - // Center and Bounds + // Center and bounds [Å] Vector3 center, bounds_min, bounds_max; - // Unit Cell Bounds + // Unit cell bounds [Å] Vector3 cell_bounds_min, cell_bounds_max; + // Unit cell size [m] + Vector3 cell_size; private: // Generate the full set of spin positions @@ -160,14 +162,14 @@ namespace Data // std::vector _triangulation; std::vector _tetrahedra; - + // Temporaries to tell wether the triangulation or tetrahedra // need to be updated when the corresponding function is called int last_update_n_cell_step; intfield last_update_n_cells; }; - - //TODO: find better place (?) + + // TODO: find better place (?) std::vector compute_delaunay_triangulation_2D(const std::vector & points); } #endif diff --git a/core/include/engine/Hamiltonian_Micromagnetic.hpp b/core/include/engine/Hamiltonian_Micromagnetic.hpp index 9041510c3..304fd2f03 100644 --- a/core/include/engine/Hamiltonian_Micromagnetic.hpp +++ b/core/include/engine/Hamiltonian_Micromagnetic.hpp @@ -26,17 +26,9 @@ namespace Engine class Hamiltonian_Micromagnetic : public Hamiltonian { public: - Hamiltonian_Micromagnetic( - scalar external_field_magnitude, Vector3 external_field_normal, - Matrix3 anisotropy_tensor, - scalar exchange_constant, - scalar dmi_constant, - std::shared_ptr geometry, - int spatial_gradient_order, - intfield boundary_conditions - ); Hamiltonian_Micromagnetic( + scalar Ms, scalar external_field_magnitude, Vector3 external_field_normal, Matrix3 anisotropy_tensor, Matrix3 exchange_tensor, @@ -71,6 +63,7 @@ namespace Engine scalar external_field_magnitude; Vector3 external_field_normal; Matrix3 anisotropy_tensor; + scalar Ms; // ------------ Pair Interactions ------------ // Exchange interaction diff --git a/core/src/Spirit/Hamiltonian.cpp b/core/src/Spirit/Hamiltonian.cpp index 61ef7b5ba..05e4ae23b 100644 --- a/core/src/Spirit/Hamiltonian.cpp +++ b/core/src/Spirit/Hamiltonian.cpp @@ -98,10 +98,11 @@ try { // TODO: are these the desired defaults? image->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Micromagnetic( + 0, 0, Vector3{0, 0, 1}, Matrix3::Zero(), - 0, - 0, + Matrix3::Zero(), + Matrix3::Zero(), image->geometry, 2, image->hamiltonian->boundary_conditions)); diff --git a/core/src/data/Geometry.cpp b/core/src/data/Geometry.cpp index 4da814f28..eef715eb0 100644 --- a/core/src/data/Geometry.cpp +++ b/core/src/data/Geometry.cpp @@ -679,6 +679,9 @@ namespace Data } this->cell_bounds_min *= 0.5; this->cell_bounds_max *= 0.5; + + // Cell size in m + this->cell_size = 1e-10 * (this->cell_bounds_max - this->cell_bounds_min); } void Geometry::calculateGeometryType() diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cpp b/core/src/engine/Hamiltonian_Micromagnetic.cpp index d9003cda0..a666fef9f 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cpp +++ b/core/src/engine/Hamiltonian_Micromagnetic.cpp @@ -5,6 +5,8 @@ #include #include #include + +#include #include #include @@ -13,7 +15,7 @@ using namespace Data; using namespace Utility; -namespace C = Utility::Constants_Micromagnetic; +namespace C = Utility::Constants; using Engine::Vectormath::check_atom_type; using Engine::Vectormath::idx_from_pair; using Engine::Vectormath::idx_from_tupel; @@ -22,6 +24,7 @@ using Engine::Vectormath::idx_from_tupel; namespace Engine { Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( + scalar Ms, scalar external_field_magnitude, Vector3 external_field_normal, Matrix3 anisotropy_tensor, Matrix3 exchange_tensor, @@ -29,23 +32,7 @@ namespace Engine std::shared_ptr geometry, int spatial_gradient_order, intfield boundary_conditions - ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), geometry(geometry), - external_field_magnitude(external_field_magnitude), external_field_normal(external_field_normal), - anisotropy_tensor(anisotropy_tensor), exchange_tensor(exchange_tensor), dmi_tensor(dmi_tensor) - { - // Generate interaction pairs, constants etc. - this->Update_Interactions(); - } - - Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( - scalar external_field_magnitude, Vector3 external_field_normal, - Matrix3 anisotropy_tensor, - scalar exchange_constant, - scalar dmi_constant, - std::shared_ptr geometry, - int spatial_gradient_order, - intfield boundary_conditions - ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), geometry(geometry), + ) : Hamiltonian(boundary_conditions), Ms(Ms), spatial_gradient_order(spatial_gradient_order), geometry(geometry), external_field_magnitude(external_field_magnitude), external_field_normal(external_field_normal), anisotropy_tensor(anisotropy_tensor), exchange_tensor(exchange_tensor), dmi_tensor(dmi_tensor) { @@ -74,103 +61,103 @@ namespace Engine // Update, which terms still contribute - neigh = pairfield(0); - Neighbour neigh_tmp; - neigh_tmp.i = 0; - neigh_tmp.j = 0; - neigh_tmp.idx_shell = 0; - //order x -x y -y z -z xy (-x)(-y) x(-y) (-x)y xz (-x)(-z) x(-z) (-x)z yz (-y)(-z) y(-z) (-y)z results in 9 parts of Hessian - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = 1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = +1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 1; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 1; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - - this->spatial_gradient = field(geometry->nos, Matrix3::Zero()); + neigh = pairfield(0); + Neighbour neigh_tmp; + neigh_tmp.i = 0; + neigh_tmp.j = 0; + neigh_tmp.idx_shell = 0; + //order x -x y -y z -z xy (-x)(-y) x(-y) (-x)y xz (-x)(-z) x(-z) (-x)z yz (-y)(-z) y(-z) (-y)z results in 9 parts of Hessian + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = +1; + neigh_tmp.translations[2] = 0; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = -1; + neigh.push_back(neigh_tmp); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 1; + neigh.push_back(neigh_tmp); + + this->spatial_gradient = field(geometry->nos, Matrix3::Zero()); this->Update_Energy_Contributions(); } @@ -219,7 +206,7 @@ namespace Engine // else this->idx_ddi = -1; } - + void Hamiltonian_Micromagnetic::Energy_Contributions_per_Spin(const vectorfield & spins, std::vector> & contributions) { if( contributions.size() != this->energy_contributions_per_spin.size() ) @@ -235,7 +222,7 @@ namespace Engine // Otherwise set to zero else Vectormath::fill(contrib.second, 0); } - + // External field if( this->idx_zeeman >=0 ) E_Zeeman(spins, contributions[idx_zeeman].second); @@ -244,6 +231,7 @@ namespace Engine // Exchange if( this->idx_exchange >=0 ) E_Exchange(spins, contributions[idx_exchange].second); + // DMI if( this->idx_dmi >=0 ) E_DMI(spins,contributions[idx_dmi].second); } @@ -260,20 +248,19 @@ namespace Engine } } - void Hamiltonian_Micromagnetic::E_Update(const vectorfield & spins, scalarfield & Energy, vectorfield & gradient) { - scalar Ms = 1.4e6; - #pragma omp parallel for - for (int icell = 0; icell < geometry->n_cells_total; ++icell) - { - Energy[icell] -= 0.5 *Ms* gradient[icell].dot(spins[icell]); - } - } + void Hamiltonian_Micromagnetic::E_Update(const vectorfield & spins, scalarfield & Energy, vectorfield & gradient) { + #pragma omp parallel for + for( int icell = 0; icell < geometry->n_cells_total; ++icell ) + { + Energy[icell] -= 0.5 * Ms * gradient[icell].dot(spins[icell]); + } + } void Hamiltonian_Micromagnetic::E_Anisotropy(const vectorfield & spins, scalarfield & Energy) { } - void Hamiltonian_Micromagnetic::E_Exchange(const vectorfield & spins, scalarfield & Energy) + void Hamiltonian_Micromagnetic::E_Exchange(const vectorfield & spins, scalarfield & Energy) { } @@ -293,14 +280,11 @@ namespace Engine } - void Hamiltonian_Micromagnetic::Gradient(const vectorfield & spins, vectorfield & gradient) + void Hamiltonian_Micromagnetic::Gradient(const vectorfield & spins, vectorfield & gradient) { - // std::cout << this->boundary_conditions[0] << std::endl; - // std::cout << boundary_conditions[0] << std::endl; - // Set to zero Vectormath::fill(gradient, {0,0,0}); - this->Spatial_Gradient(spins); + this->Spatial_Gradient(spins); // External field this->Gradient_Zeeman(gradient); @@ -313,15 +297,14 @@ namespace Engine // DMI this->Gradient_DMI(spins, gradient); - scalar Ms = 1.4e6; - double energy=0; - #pragma omp parallel for reduction(-:energy) - for (int icell = 0; icell < geometry->n_cells_total; ++icell) - { - energy -= 0.5 *Ms* gradient[icell].dot(spins[icell]); - } - // printf("Energy total: %f\n", energy/ geometry->n_cells_total); + // double energy=0; + // #pragma omp parallel for reduction(-:energy) + // for( int icell = 0; icell < geometry->n_cells_total; ++icell ) + // { + // energy -= 0.5 * Ms * gradient[icell].dot(spins[icell]); + // } + // printf("Energy total: %f\n", energy/ geometry->n_cells_total); } @@ -339,259 +322,253 @@ namespace Engine void Hamiltonian_Micromagnetic::Gradient_Anisotropy(const vectorfield & spins, vectorfield & gradient) { - scalar Ms = 1.4e6; - Vector3 temp1{ 1,0,0 }; - Vector3 temp2{ 0,1,0 }; - Vector3 temp3{ 0,0,1 }; - #pragma omp parallel for - for (int icell = 0; icell < geometry->n_cells_total; ++icell) - { - for (int iani = 0; iani < 1; ++iani) - { - int ispin = icell; - gradient[ispin] -= 2.0 * 8.44e6 / Ms * temp3 * temp3.dot(spins[ispin]); - //gradient[ispin] -= 2.0 * this->anisotropy_magnitudes[iani] / Ms * ((pow(temp2.dot(spins[ispin]),2)+ pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1)+ (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)+(pow(temp1.dot(spins[ispin]),2)+ pow(temp2.dot(spins[ispin]), 2))*(temp3.dot(spins[ispin])*temp3)); - //gradient[ispin] += 2.0 * 50000 / Ms * ((pow(temp2.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1) + (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)); - - } - } + Vector3 temp1{ 1,0,0 }; + Vector3 temp2{ 0,1,0 }; + Vector3 temp3{ 0,0,1 }; + #pragma omp parallel for + for( int icell = 0; icell < geometry->n_cells_total; ++icell ) + { + // for( int iani = 0; iani < 1; ++iani ) + // { + // gradient[icell] -= 2.0 * 8.44e6 / Ms * temp3 * temp3.dot(spins[icell]); + gradient[icell] -= 2.0 * C::mu_B * anisotropy_tensor * spins[icell] / Ms; + //gradient[icell] -= 2.0 * this->anisotropy_magnitudes[iani] / Ms * ((pow(temp2.dot(spins[icell]),2)+ pow(temp3.dot(spins[icell]), 2))*(temp1.dot(spins[icell])*temp1)+ (pow(temp1.dot(spins[icell]), 2) + pow(temp3.dot(spins[icell]), 2))*(temp2.dot(spins[icell])*temp2)+(pow(temp1.dot(spins[icell]),2)+ pow(temp2.dot(spins[icell]), 2))*(temp3.dot(spins[icell])*temp3)); + //gradient[icell] += 2.0 * 50000 / Ms * ((pow(temp2.dot(spins[icell]), 2) + pow(temp3.dot(spins[icell]), 2))*(temp1.dot(spins[icell])*temp1) + (pow(temp1.dot(spins[icell]), 2) + pow(temp3.dot(spins[icell]), 2))*(temp2.dot(spins[icell])*temp2)); + // } + } } void Hamiltonian_Micromagnetic::Gradient_Exchange(const vectorfield & spins, vectorfield & gradient) { - scalar delta[3] = { 3e-11,3e-11,3e-10 }; - scalar Ms = 1.4e6; - //nongradient implementation - /* - #pragma omp parallel for - for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell) - { - int ispin = icell;//basically id of a cell - for (unsigned int i = 0; i < 3; ++i) - { - - int ispin_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i]); - int ispin_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i + 1]); - if (ispin_plus == -1) { - ispin_plus = ispin; - } - if (ispin_minus == -1) { - ispin_minus = ispin; - } - gradient[ispin][i] -= exchange_tensor(i, i)*(spins[ispin_plus][i] - 2 * spins[ispin][i] + spins[ispin_minus][i]) / (delta[i]) / (delta[i]); - - } - if (A_is_nondiagonal == true) { - int ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[6]); - int ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[7]); - int ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[8]); - int ispin_minus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[9]); - - if (ispin_plus_plus == -1) { - ispin_plus_plus = ispin; - } - if (ispin_minus_minus == -1) { - ispin_minus_minus = ispin; - } - if (ispin_plus_minus == -1) { - ispin_plus_minus = ispin; - } - if (ispin_minus_plus == -1) { - ispin_minus_plus = ispin; - } - gradient[ispin][0] -= exchange_tensor(0, 1)*(spins[ispin_plus_plus][1] - spins[ispin_plus_minus][1] - spins[ispin_minus_plus][1] + spins[ispin_minus_minus][1]) / (delta[0]) / (delta[1]) / 4; - gradient[ispin][1] -= exchange_tensor(1, 0)*(spins[ispin_plus_plus][0] - spins[ispin_plus_minus][0] - spins[ispin_minus_plus][0] + spins[ispin_minus_minus][0]) / (delta[0]) / (delta[1]) / 4; - - ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[10]); - ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[11]); - ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[12]); - ispin_minus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[13]); - - if (ispin_plus_plus == -1) { - ispin_plus_plus = ispin; - } - if (ispin_minus_minus == -1) { - ispin_minus_minus = ispin; - } - if (ispin_plus_minus == -1) { - ispin_plus_minus = ispin; - } - if (ispin_minus_plus == -1) { - ispin_minus_plus = ispin; - } - gradient[ispin][0] -= exchange_tensor(0, 2)*(spins[ispin_plus_plus][2] - spins[ispin_plus_minus][2] - spins[ispin_minus_plus][2] + spins[ispin_minus_minus][2]) / (delta[0]) / (delta[2]) / 4; - gradient[ispin][2] -= exchange_tensor(2, 0)*(spins[ispin_plus_plus][0] - spins[ispin_plus_minus][0] - spins[ispin_minus_plus][0] + spins[ispin_minus_minus][0]) / (delta[0]) / (delta[2]) / 4; - - ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[14]); - ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[15]); - ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[16]); - ispin_minus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[17]); - - if (ispin_plus_plus == -1) { - ispin_plus_plus = ispin; - } - if (ispin_minus_minus == -1) { - ispin_minus_minus = ispin; - } - if (ispin_plus_minus == -1) { - ispin_plus_minus = ispin; - } - if (ispin_minus_plus == -1) { - ispin_minus_plus = ispin; - } - gradient[ispin][1] -= exchange_tensor(1, 2)*(spins[ispin_plus_plus][2] - spins[ispin_plus_minus][2] - spins[ispin_minus_plus][2] + spins[ispin_minus_minus][2]) / (delta[1]) / (delta[2]) / 4; - gradient[ispin][2] -= exchange_tensor(2, 1)*(spins[ispin_plus_plus][1] - spins[ispin_plus_minus][1] - spins[ispin_minus_plus][1] + spins[ispin_minus_minus][1]) / (delta[1]) / (delta[2]) / 4; - } - - }*/ - - //gradient implementation - #pragma omp parallel for - for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell) - { - int ispin = icell;//basically id of a cell - for (unsigned int i = 0; i < 3; ++i) - { - - int ispin_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i]); - int ispin_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i + 1]); - if (ispin_plus == -1) { - ispin_plus = ispin; - } - if (ispin_minus == -1) { - ispin_minus = ispin; - } - - gradient[ispin][0] -= 2 * exchange_tensor(i, i) / Ms *(spins[ispin_plus][0] - 2 * spins[ispin][0] + spins[ispin_minus][0]) / (delta[i]) / (delta[i]); - gradient[ispin][1] -= 2 * exchange_tensor(i, i) / Ms * (spins[ispin_plus][1] - 2 * spins[ispin][1] + spins[ispin_minus][1]) / (delta[i]) / (delta[i]); - gradient[ispin][2] -= 2 * exchange_tensor(i, i) / Ms * (spins[ispin_plus][2] - 2 * spins[ispin][2] + spins[ispin_minus][2]) / (delta[i]) / (delta[i]); - - } - if (this->A_is_nondiagonal == true) { - //xy - int ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[0]); - int ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); - int ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2]); - int ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); - - if (ispin_right == -1) { - ispin_right = ispin; - } - if (ispin_left == -1) { - ispin_left = ispin; - } - if (ispin_top == -1) { - ispin_top = ispin; - } - if (ispin_bottom == -1) { - ispin_bottom = ispin; - } - gradient[ispin][0] -= exchange_tensor(0, 1) / Ms *((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); - gradient[ispin][0] -= exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); - gradient[ispin][1] -= exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); - gradient[ispin][1] -= exchange_tensor(1, 0) / Ms *((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); - gradient[ispin][2] -= exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); - gradient[ispin][2] -= exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); - - //xz - ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[0]); - ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); - ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[4]); - ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); - - if (ispin_right == -1) { - ispin_right = ispin; - } - if (ispin_left == -1) { - ispin_left = ispin; - } - if (ispin_top == -1) { - ispin_top = ispin; - } - if (ispin_bottom == -1) { - ispin_bottom = ispin; - } - gradient[ispin][0] -= exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); - gradient[ispin][0] -= exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); - gradient[ispin][1] -= exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); - gradient[ispin][1] -= exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); - gradient[ispin][2] -= exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - gradient[ispin][2] -= exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - - //yz - ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2]); - ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); - ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[4]); - ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); - - if (ispin_right == -1) { - ispin_right = ispin; - } - if (ispin_left == -1) { - ispin_left = ispin; - } - if (ispin_top == -1) { - ispin_top = ispin; - } - if (ispin_bottom == -1) { - ispin_bottom = ispin; - } - gradient[ispin][0] -= exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); - gradient[ispin][0] -= exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); - gradient[ispin][1] -= exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); - gradient[ispin][1] -= exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); - gradient[ispin][2] -= exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - gradient[ispin][2] -= exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - - } - - } - + auto& delta = geometry->cell_size; + // scalar delta[3] = { 3e-10, 3e-10, 3e-9 }; + // scalar delta[3] = { 277e-12, 277e-12, 277e-12 }; + + //nongradient implementation + /* + #pragma omp parallel for + for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell) + { + int ispin = icell;//basically id of a cell + for (unsigned int i = 0; i < 3; ++i) + { + + int ispin_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i]); + int ispin_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i + 1]); + if (ispin_plus == -1) { + ispin_plus = ispin; + } + if (ispin_minus == -1) { + ispin_minus = ispin; + } + gradient[ispin][i] -= exchange_tensor(i, i)*(spins[ispin_plus][i] - 2 * spins[ispin][i] + spins[ispin_minus][i]) / (delta[i]) / (delta[i]); + + } + if (A_is_nondiagonal == true) { + int ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[6]); + int ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[7]); + int ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[8]); + int ispin_minus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[9]); + + if (ispin_plus_plus == -1) { + ispin_plus_plus = ispin; + } + if (ispin_minus_minus == -1) { + ispin_minus_minus = ispin; + } + if (ispin_plus_minus == -1) { + ispin_plus_minus = ispin; + } + if (ispin_minus_plus == -1) { + ispin_minus_plus = ispin; + } + gradient[ispin][0] -= exchange_tensor(0, 1)*(spins[ispin_plus_plus][1] - spins[ispin_plus_minus][1] - spins[ispin_minus_plus][1] + spins[ispin_minus_minus][1]) / (delta[0]) / (delta[1]) / 4; + gradient[ispin][1] -= exchange_tensor(1, 0)*(spins[ispin_plus_plus][0] - spins[ispin_plus_minus][0] - spins[ispin_minus_plus][0] + spins[ispin_minus_minus][0]) / (delta[0]) / (delta[1]) / 4; + + ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[10]); + ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[11]); + ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[12]); + ispin_minus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[13]); + + if (ispin_plus_plus == -1) { + ispin_plus_plus = ispin; + } + if (ispin_minus_minus == -1) { + ispin_minus_minus = ispin; + } + if (ispin_plus_minus == -1) { + ispin_plus_minus = ispin; + } + if (ispin_minus_plus == -1) { + ispin_minus_plus = ispin; + } + gradient[ispin][0] -= exchange_tensor(0, 2)*(spins[ispin_plus_plus][2] - spins[ispin_plus_minus][2] - spins[ispin_minus_plus][2] + spins[ispin_minus_minus][2]) / (delta[0]) / (delta[2]) / 4; + gradient[ispin][2] -= exchange_tensor(2, 0)*(spins[ispin_plus_plus][0] - spins[ispin_plus_minus][0] - spins[ispin_minus_plus][0] + spins[ispin_minus_minus][0]) / (delta[0]) / (delta[2]) / 4; + + ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[14]); + ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[15]); + ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[16]); + ispin_minus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[17]); + + if (ispin_plus_plus == -1) { + ispin_plus_plus = ispin; + } + if (ispin_minus_minus == -1) { + ispin_minus_minus = ispin; + } + if (ispin_plus_minus == -1) { + ispin_plus_minus = ispin; + } + if (ispin_minus_plus == -1) { + ispin_minus_plus = ispin; + } + gradient[ispin][1] -= exchange_tensor(1, 2)*(spins[ispin_plus_plus][2] - spins[ispin_plus_minus][2] - spins[ispin_minus_plus][2] + spins[ispin_minus_minus][2]) / (delta[1]) / (delta[2]) / 4; + gradient[ispin][2] -= exchange_tensor(2, 1)*(spins[ispin_plus_plus][1] - spins[ispin_plus_minus][1] - spins[ispin_minus_plus][1] + spins[ispin_minus_minus][1]) / (delta[1]) / (delta[2]) / 4; + } + + }*/ + + // Gradient implementation + #pragma omp parallel for + for( unsigned int icell = 0; icell < geometry->n_cells_total; ++icell ) + { + for( unsigned int i = 0; i < 3; ++i ) + { + int icell_plus = idx_from_pair(icell, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i]); + int icell_minus = idx_from_pair(icell, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i+1]); + + if( icell_plus >= 0 || icell_minus >= 0 ) + { + if( icell_plus == -1 ) + icell_plus = icell; + if( icell_minus == -1 ) + icell_minus = icell; + + gradient[icell] -= 2 * C::mu_B * exchange_tensor * (spins[icell_plus] - 2*spins[icell] + spins[icell_minus]) / (Ms*delta[i]*delta[i]); + } + + // gradient[icell][0] -= 2*exchange_tensor(i, i) / Ms * (spins[icell_plus][0] - 2*spins[icell][0] + spins[icell_minus][0]) / (delta[i]) / (delta[i]); + // gradient[icell][1] -= 2*exchange_tensor(i, i) / Ms * (spins[icell_plus][1] - 2*spins[icell][1] + spins[icell_minus][1]) / (delta[i]) / (delta[i]); + // gradient[icell][2] -= 2*exchange_tensor(i, i) / Ms * (spins[icell_plus][2] - 2*spins[icell][2] + spins[icell_minus][2]) / (delta[i]) / (delta[i]); + } + // if( this->A_is_nondiagonal ) + // { + // int ispin = icell; + + // // xy + // int ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[0]); + // int ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); + // int ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2]); + // int ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); + + // if( ispin_right == -1 ) + // ispin_right = ispin; + // if( ispin_left == -1 ) + // ispin_left = ispin; + // if( ispin_top == -1 ) + // ispin_top = ispin; + // if( ispin_bottom == -1 ) + // ispin_bottom = ispin; + + // gradient[ispin][0] -= 2*exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); + // gradient[ispin][0] -= 2*exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); + // gradient[ispin][1] -= 2*exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); + // gradient[ispin][1] -= 2*exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); + // gradient[ispin][2] -= 2*exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); + // gradient[ispin][2] -= 2*exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); + + // // xz + // ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[0]); + // ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); + // ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[4]); + // ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); + + // if( ispin_right == -1 ) + // ispin_right = ispin; + // if( ispin_left == -1 ) + // ispin_left = ispin; + // if( ispin_top == -1 ) + // ispin_top = ispin; + // if( ispin_bottom == -1 ) + // ispin_bottom = ispin; + + // gradient[ispin][0] -= 2*exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + // gradient[ispin][0] -= 2*exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + // gradient[ispin][1] -= 2*exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + // gradient[ispin][1] -= 2*exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + // gradient[ispin][2] -= 2*exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + // gradient[ispin][2] -= 2*exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + + // // yz + // ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2]); + // ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); + // ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[4]); + // ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); + + // if( ispin_right == -1 ) + // ispin_right = ispin; + // if( ispin_left == -1 ) + // ispin_left = ispin; + // if( ispin_top == -1 ) + // ispin_top = ispin; + // if( ispin_bottom == -1 ) + // ispin_bottom = ispin; + + // gradient[ispin][0] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + // gradient[ispin][0] -= 2 * exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + // gradient[ispin][1] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + // gradient[ispin][1] -= 2 * exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + // gradient[ispin][2] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + // gradient[ispin][2] -= 2 * exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + + // } + } } - void Hamiltonian_Micromagnetic::Spatial_Gradient(const vectorfield & spins) { - scalar delta[3] = { 3e-11,3e-11,3e-10 }; - /* - dn1/dr1 dn1/dr2 dn1/dr3 - dn2/dr1 dn2/dr2 dn2/dr3 - dn3/dr1 dn3/dr2 dn3/dr3 - */ - #pragma omp parallel for - for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell) - { - int ispin = icell;//basically id of a cell - for (unsigned int i = 0; i < 3; ++i) - { - int ispin_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i]); - int ispin_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i + 1]); - if (ispin_plus == -1) { - ispin_plus = ispin; - } - if (ispin_minus == -1) { - ispin_minus = ispin; - } - spatial_gradient[ispin](0, i) = (spins[ispin_plus][0] - spins[ispin_minus][0]) / (delta[i]) / 2; - spatial_gradient[ispin](1, i) = (spins[ispin_plus][1] - spins[ispin_minus][1]) / (delta[i]) / 2; - spatial_gradient[ispin](2, i) = (spins[ispin_plus][2] - spins[ispin_minus][2]) / (delta[i]) / 2; - - } - } - } + void Hamiltonian_Micromagnetic::Spatial_Gradient(const vectorfield & spins) + { + auto& delta = geometry->cell_size; + // scalar delta[3] = { 3e-10,3e-10,3e-9 }; + // scalar delta[3] = { 277e-12, 277e-12, 277e-12 }; + /* + dn1/dr1 dn1/dr2 dn1/dr3 + dn2/dr1 dn2/dr2 dn2/dr3 + dn3/dr1 dn3/dr2 dn3/dr3 + */ + #pragma omp parallel for + for( unsigned int icell = 0; icell < geometry->n_cells_total; ++icell ) + { + spatial_gradient[icell] = Matrix3::Zero(); + for( unsigned int i = 0; i < 3; ++i ) + { + int icell_plus = idx_from_pair(icell, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i]); + int icell_minus = idx_from_pair(icell, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i+1]); + + if( icell_plus >= 0 || icell_minus >= 0 ) + { + if( icell_plus == -1 ) + icell_plus = icell; + if( icell_minus == -1 ) + icell_minus = icell; + + spatial_gradient[icell].col(i) += (spins[icell_plus] - spins[icell_minus]) / (2*delta[i]); + } + } + } + } void Hamiltonian_Micromagnetic::Gradient_DMI(const vectorfield & spins, vectorfield & gradient) { - scalar Ms = 1.4e6; - #pragma omp parallel for - for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell) - { - int ispin = icell;//basically id of a cell - for (unsigned int i = 0; i < 3; ++i) - { - gradient[ispin][0] += 2*dmi_tensor(1, i) / Ms *spatial_gradient[ispin](2, i) - 2*dmi_tensor(2, i) / Ms *spatial_gradient[ispin](1, i); - gradient[ispin][1] += 2*dmi_tensor(2, i) / Ms *spatial_gradient[ispin](0, i) - 2*dmi_tensor(0, i) / Ms *spatial_gradient[ispin](2, i); - gradient[ispin][2] += 2*dmi_tensor(0, i) / Ms *spatial_gradient[ispin](1, i) - 2*dmi_tensor(1, i) / Ms *spatial_gradient[ispin](0, i); - } - } + #pragma omp parallel for + for( unsigned int icell = 0; icell < geometry->n_cells_total; ++icell ) + { + for( unsigned int i = 0; i < 3; ++i ) + { + gradient[icell][0] -= 4*C::mu_B*( dmi_tensor(1,i) * spatial_gradient[icell](2,i) - dmi_tensor(2,i) * spatial_gradient[icell](1,i) ) / Ms; + gradient[icell][1] -= 4*C::mu_B*( dmi_tensor(2,i) * spatial_gradient[icell](0,i) - dmi_tensor(0,i) * spatial_gradient[icell](2,i) ) / Ms; + gradient[icell][2] -= 4*C::mu_B*( dmi_tensor(0,i) * spatial_gradient[icell](1,i) - dmi_tensor(1,i) * spatial_gradient[icell](0,i) ) / Ms; + } + } } diff --git a/core/src/io/Configparser.cpp b/core/src/io/Configparser.cpp index 855ee0f1b..68bacf7ff 100644 --- a/core/src/io/Configparser.cpp +++ b/core/src/io/Configparser.cpp @@ -6,6 +6,8 @@ #include #include +#include + #include #include #include @@ -406,7 +408,22 @@ namespace IO } } } - else Log(Log_Level::Error, Log_Sender::IO, fmt::format("Keyword 'mu_s' not found. Using Default: {}", cell_composition.mu_s[0])); + else if( myfile.Find("Ms") ) + { + // Total magnetisation + scalar Ms = 1e6; // [A/m] which is [1.0782822e23 mu_B / m^3] + myfile.Read_Single(Ms, "Ms"); + scalar V = std::pow(lattice_constant * 1e-10, 3) + * (bravais_vectors[0].cross(bravais_vectors[1])).dot(bravais_vectors[2]); + // * n_cells[0] * n_cells[1] * n_cells[2] * n_cell_atoms; + scalar mu_s = Ms * 1.0782822e23 * V; // per lattice site + + for (iatom = 0; iatom < n_cell_atoms; ++iatom) + cell_composition.mu_s[iatom] = mu_s; + } + else + Log(Log_Level::Error, Log_Sender::IO, fmt::format( + "Neither keyword 'mu_s' nor 'Ms' found. Using Default: {} mu_B", cell_composition.mu_s[0])); } // else // { @@ -427,7 +444,7 @@ namespace IO }// end try catch( ... ) { - spirit_handle_exception_core(fmt::format("Unable to read mu_s from config file \"{}\"", configFile)); + spirit_handle_exception_core(fmt::format("Unable to read mu_s or Ms from config file \"{}\"", configFile)); } }// end if file="" else @@ -446,7 +463,7 @@ namespace IO Log(Log_Level::Parameter, Log_Sender::IO, fmt::format("Basis cell: {} atom(s)", n_cell_atoms)); Log(Log_Level::Parameter, Log_Sender::IO, "Relative positions (first 10):"); for( int iatom = 0; iatom < n_cell_atoms && iatom < 10; ++iatom ) - Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" atom {} at ({}), mu_s={}", iatom, cell_atoms[iatom].transpose(), cell_composition.mu_s[iatom])); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" atom {} at ({}), mu_s = {} mu_B", iatom, cell_atoms[iatom].transpose(), cell_composition.mu_s[iatom])); Log(Log_Level::Parameter, Log_Sender::IO, "Absolute atom positions (first 10):", n_cell_atoms); for( int iatom = 0; iatom < n_cell_atoms && iatom < 10; ++iatom ) @@ -484,8 +501,9 @@ namespace IO Data::Geometry( bravais_vectors, n_cells, cell_atoms, cell_composition, lattice_constant, pinning, {defect_sites, defect_types} )); - Log(Log_Level::Parameter, Log_Sender::IO, fmt::format("Geometry: {} spins", geometry->nos)); Log(Log_Level::Parameter, Log_Sender::IO, fmt::format("Geometry is {}-dimensional", geometry->dimensionality)); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" containing {} spins", geometry->nos)); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" unit cell size: {}", geometry->cell_size.transpose())); Log(Log_Level::Info, Log_Sender::IO, "Geometry: built"); return geometry; } @@ -1437,6 +1455,12 @@ namespace IO std::unique_ptr Hamiltonian_Micromagnetic_from_Config(const std::string configFile, const std::shared_ptr geometry) { + if( geometry->classifier != Data::BravaisLatticeType::Rectilinear && + geometry->classifier != Data::BravaisLatticeType::SC) + { + spirit_throw(Exception_Classifier::System_not_Initialized, Log_Level::Severe, fmt::format( + "Hamiltonian: Cannot use micromagnetic Hamiltonian on non-rectilinear geometry (type {})", int(geometry->classifier))); + } //-------------- Insert default values here ----------------------------- // Boundary conditions (a, b, c) std::vector boundary_conditions_i = { 0, 0, 0 }; @@ -1445,6 +1469,9 @@ namespace IO // The order of the finite difference approximation of the spatial gradient int spatial_gradient_order = 1; + // Total magnetisation + scalar Ms = 1e6; + // External Magnetic Field scalar field = 0; Vector3 field_normal = { 0.0, 0.0, 1.0 }; @@ -1541,10 +1568,15 @@ namespace IO else { myfile.Read_Single(dmi_magnitude, "dmi"); - // dmi_tensor << dmi_magnitude, 0, 0, - // 0, dmi_magnitude, 0, - // 0, 0, dmi_magnitude; - Log(Log_Level::Warning, Log_Sender::IO, "'dmi' is not a supported input!"); + dmi_tensor << 0, dmi_magnitude/std::sqrt(3), 0, + -dmi_magnitude/std::sqrt(3), 0,0, + 0, 0, 0; + // dmi_tensor << dmi_magnitude/std::sqrt(3), 0, 0, + // 0, dmi_magnitude/std::sqrt(3), 0, + // 0, 0, dmi_magnitude/std::sqrt(3); + // dmi_tensor << 0, dmi_magnitude, -dmi_magnitude, + // -dmi_magnitude, 0, dmi_magnitude, + // dmi_magnitude, -dmi_magnitude, 0; } // TODO: dipolar @@ -1586,13 +1618,25 @@ namespace IO spirit_handle_exception_core(fmt::format( "Unable to parse all parameters of the Micromagnetic Hamiltonian from \"{}\"", configFile)); } - // Return - Log(Log_Level::Parameter, Log_Sender::IO, "Hamiltonian_Heisenberg:"); - Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<21} = {} {} {}", "boundary conditions", boundary_conditions[0], boundary_conditions[1], boundary_conditions[2])); - Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<21} = {}", "external field", field)); - Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<21} = {}", "field_normal", field_normal.transpose())); - + // Return + Log(Log_Level::Parameter, Log_Sender::IO, "Hamiltonian_Heisenberg:"); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} = {}", "discretisation order", spatial_gradient_order)); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} = {} {} {}", "boundary conditions", boundary_conditions[0], boundary_conditions[1], boundary_conditions[2])); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} = {}", "saturation magnetisation", Ms)); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} = {}", "external field", field)); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} = {}", "field normal", field_normal.transpose())); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} = {}", "anisotropy tensor", anisotropy_tensor.row(0))); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} {}", " ", anisotropy_tensor.row(1))); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} {}", " ", anisotropy_tensor.row(2))); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} = {}", "exchange tensor", exchange_tensor.row(0))); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} {}", " ", exchange_tensor.row(1))); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} {}", " ", exchange_tensor.row(2))); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} = {}", "dmi tensor", dmi_tensor.row(0))); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} {}", " ", dmi_tensor.row(1))); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} {}", " ", dmi_tensor.row(2))); + auto hamiltonian = std::unique_ptr(new Engine::Hamiltonian_Micromagnetic( + Ms, field, field_normal, anisotropy_tensor, exchange_tensor, From d566ada615bb7d479682c17b852311497742fff8 Mon Sep 17 00:00:00 2001 From: Gideon Date: Tue, 24 Sep 2019 22:27:30 +0200 Subject: [PATCH 13/45] Some improvements on usability of micromagnetics. - moved DDI_Method enum class to Hamiltonian.hpp - added settings for dipolar interactions to micromagnetic Hamiltonian - added some micromagnetic parameters to Hamiltonian API and made some minor improvements - made external field consistent for micromagnetic Hamiltonian --- core/include/engine/Hamiltonian.hpp | 10 + .../include/engine/Hamiltonian_Heisenberg.hpp | 14 +- .../engine/Hamiltonian_Micromagnetic.hpp | 35 ++- core/src/Spirit/Hamiltonian.cpp | 259 +++++++++++++----- core/src/engine/Hamiltonian_Micromagnetic.cpp | 8 +- core/src/io/Configparser.cpp | 73 ++--- 6 files changed, 279 insertions(+), 120 deletions(-) diff --git a/core/include/engine/Hamiltonian.hpp b/core/include/engine/Hamiltonian.hpp index 16cad5ec3..5dd7727ba 100644 --- a/core/include/engine/Hamiltonian.hpp +++ b/core/include/engine/Hamiltonian.hpp @@ -5,11 +5,21 @@ #include #include +#include + #include "Spirit_Defines.h" #include namespace Engine { + enum class DDI_Method + { + FFT = SPIRIT_DDI_METHOD_FFT, + FMM = SPIRIT_DDI_METHOD_FMM, + Cutoff = SPIRIT_DDI_METHOD_CUTOFF, + None = SPIRIT_DDI_METHOD_NONE + }; + /* The Hamiltonian contains the interaction parameters of a System. It also defines the functions to calculate the Effective Field and Energy. diff --git a/core/include/engine/Hamiltonian_Heisenberg.hpp b/core/include/engine/Hamiltonian_Heisenberg.hpp index 72b22f213..afec9ec02 100644 --- a/core/include/engine/Hamiltonian_Heisenberg.hpp +++ b/core/include/engine/Hamiltonian_Heisenberg.hpp @@ -9,19 +9,10 @@ #include #include #include -#include -#include "FFT.hpp" +#include namespace Engine { - enum class DDI_Method - { - FFT = SPIRIT_DDI_METHOD_FFT, - FMM = SPIRIT_DDI_METHOD_FMM, - Cutoff = SPIRIT_DDI_METHOD_CUTOFF, - None = SPIRIT_DDI_METHOD_NONE - }; - /* The Heisenberg Hamiltonian using Pairs contains all information on the interactions between spins. The information is presented in pair lists and parameter lists in order to easily e.g. calculate the energy of the system via summation. @@ -99,10 +90,9 @@ namespace Engine pairfield dmi_pairs; scalarfield dmi_magnitudes; vectorfield dmi_normals; - // Dipole Dipole interaction + // Dipole-dipole interaction DDI_Method ddi_method; intfield ddi_n_periodic_images; - // ddi cutoff variables scalar ddi_cutoff_radius; pairfield ddi_pairs; scalarfield ddi_magnitudes; diff --git a/core/include/engine/Hamiltonian_Micromagnetic.hpp b/core/include/engine/Hamiltonian_Micromagnetic.hpp index 304fd2f03..f21d2d097 100644 --- a/core/include/engine/Hamiltonian_Micromagnetic.hpp +++ b/core/include/engine/Hamiltonian_Micromagnetic.hpp @@ -8,18 +8,11 @@ #include "Spirit_Defines.h" #include #include +#include #include namespace Engine { - // enum class DDI_Method - // { - // FFT = SPIRIT_DDI_METHOD_FFT, - // FMM = SPIRIT_DDI_METHOD_FMM, - // Cutoff = SPIRIT_DDI_METHOD_CUTOFF, - // None = SPIRIT_DDI_METHOD_NONE - // }; - /* The Micromagnetic Hamiltonian */ @@ -33,6 +26,7 @@ namespace Engine Matrix3 anisotropy_tensor, Matrix3 exchange_tensor, Matrix3 dmi_tensor, + DDI_Method ddi_method, intfield ddi_n_periodic_images, scalar ddi_radius, std::shared_ptr geometry, int spatial_gradient_order, intfield boundary_conditions @@ -45,7 +39,7 @@ namespace Engine void Hessian(const vectorfield & spins, MatrixX & hessian) override; void Gradient(const vectorfield & spins, vectorfield & gradient) override; void Energy_Contributions_per_Spin(const vectorfield & spins, std::vector> & contributions) override; - void Energy_Update(const vectorfield & spins, std::vector> & contributions, vectorfield & gradient); + void Energy_Update(const vectorfield & spins, std::vector> & contributions, vectorfield & gradient); // Calculate the total energy for a single spin to be used in Monte Carlo. // Note: therefore the energy of pairs is weighted x2 and of quadruplets x4. scalar Energy_Single_Spin(int ispin, const vectorfield & spins) override; @@ -70,9 +64,16 @@ namespace Engine Matrix3 exchange_tensor; // DMI Matrix3 dmi_tensor; - pairfield neigh; - field spatial_gradient; - bool A_is_nondiagonal=true; + pairfield neigh; + field spatial_gradient; + bool A_is_nondiagonal=true; + // Dipole-dipole interaction + DDI_Method ddi_method; + intfield ddi_n_periodic_images; + scalar ddi_cutoff_radius; + pairfield ddi_pairs; + scalarfield ddi_magnitudes; + vectorfield ddi_normals; private: // ------------ Effective Field Functions ------------ @@ -84,12 +85,12 @@ namespace Engine void Gradient_Exchange(const vectorfield & spins, vectorfield & gradient); // Calculate the DMI effective field of a Spin Pair void Gradient_DMI(const vectorfield & spins, vectorfield & gradient); - void Spatial_Gradient(const vectorfield & spins); + void Spatial_Gradient(const vectorfield & spins); // ------------ Energy Functions ------------ // Indices for Energy vector int idx_zeeman, idx_anisotropy, idx_exchange, idx_dmi, idx_ddi; - void E_Update(const vectorfield & spins, scalarfield & Energy, vectorfield & gradient); + void E_Update(const vectorfield & spins, scalarfield & Energy, vectorfield & gradient); // Calculate the Zeeman energy of a Spin System void E_Zeeman(const vectorfield & spins, scalarfield & Energy); // Calculate the Anisotropy energy of a Spin System @@ -100,6 +101,12 @@ namespace Engine void E_DMI(const vectorfield & spins, scalarfield & Energy); // Dipolar interactions void E_DDI(const vectorfield & spins, scalarfield & Energy); + + // Plans for FT / rFT + FFT::FFT_Plan fft_plan_spins; + FFT::FFT_Plan fft_plan_reverse; + + field transformed_dipole_matrices; }; diff --git a/core/src/Spirit/Hamiltonian.cpp b/core/src/Spirit/Hamiltonian.cpp index 05e4ae23b..49da4d981 100644 --- a/core/src/Spirit/Hamiltonian.cpp +++ b/core/src/Spirit/Hamiltonian.cpp @@ -89,8 +89,9 @@ try image->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Heisenberg( 0, Vector3{0, 0, 1}, {}, {}, {}, - {}, {}, SPIRIT_CHIRALITY_NEEL, Engine::DDI_Method::None, - {0, 0, 0}, 0, {}, {}, + {}, {}, SPIRIT_CHIRALITY_NEEL, + Engine::DDI_Method::None, {0, 0, 0}, 0, + {}, {}, image->geometry, image->hamiltonian->boundary_conditions)); } @@ -103,14 +104,16 @@ try Matrix3::Zero(), Matrix3::Zero(), Matrix3::Zero(), + Engine::DDI_Method::None, {0, 0, 0}, 0, image->geometry, 2, image->hamiltonian->boundary_conditions)); } else if( type == Hamiltonian_Gaussian ) { - // TODO - // image->hamiltonian = std::shared_ptr<...>(new Engine::Hamiltonian_Gaussian(...)); + // TODO: are these the desired defaults? + image->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Gaussian( + {}, {}, {})); } } catch( ... ) @@ -182,7 +185,7 @@ try try { // Set - if (image->hamiltonian->Name() == "Heisenberg") + if( image->hamiltonian->Name() == "Heisenberg" ) { auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); @@ -197,7 +200,7 @@ try // Update Energies ham->Update_Energy_Contributions(); } - else if (image->hamiltonian->Name() == "Micromagnetic") + else if( image->hamiltonian->Name() == "Micromagnetic" ) { auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); @@ -206,7 +209,7 @@ try new_normal.normalize(); // Into the Hamiltonian - ham->external_field_magnitude = magnitude * Constants::mu_B; + ham->external_field_magnitude = magnitude; ham->external_field_normal = new_normal; // Update Energies @@ -222,7 +225,7 @@ try image->Unlock(); Log( Utility::Log_Level::Info, Utility::Log_Sender::API, - fmt::format("Set external field to {}, direction ({}, {}, {})", magnitude, normal[0], normal[1], normal[2]), + fmt::format("Set external field to {} [T], direction ({}, {}, {})", magnitude, normal[0], normal[1], normal[2]), idx_image, idx_chain ); } catch( ... ) @@ -240,12 +243,22 @@ try // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); + if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Anisotropy cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); + return; + } + image->Lock(); try { - if (image->hamiltonian->Name() == "Heisenberg") + std::string units = "N/A"; + + if( image->hamiltonian->Name() == "Heisenberg" ) { + units = "meV"; auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); int nos = image->nos; int n_cell_atoms = image->geometry->n_cell_atoms; @@ -270,14 +283,27 @@ try // Update Energies ham->Update_Energy_Contributions(); + } + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + units = "J/m^3"; + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + + Vector3 Kn{ normal[0], normal[1], normal[2] }; + Kn.normalize(); - Log( Utility::Log_Level::Info, Utility::Log_Sender::API, - fmt::format("Set anisotropy to {}, direction ({}, {}, {})", magnitude, normal[0], normal[1], normal[2]), - idx_image, idx_chain ); + ham->anisotropy_tensor << Kn[0]*Kn[0], Kn[0]*Kn[1], Kn[0]*Kn[2], + Kn[1]*Kn[0], Kn[1]*Kn[1], Kn[1]*Kn[2], + Kn[2]*Kn[0], Kn[2]*Kn[1], Kn[2]*Kn[2]; + ham->anisotropy_tensor *= magnitude; + + // Update Energies + ham->Update_Energy_Contributions(); } - else - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - "Anisotropy cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain); + + Log( Utility::Log_Level::Info, Utility::Log_Sender::API, + fmt::format("Set anisotropy to {} [{}], direction ({}, {}, {})", magnitude, units, normal[0], normal[1], normal[2]), + idx_image, idx_chain ); } catch( ... ) { @@ -300,11 +326,18 @@ try // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); + if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Exchange interaction cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); + return; + } + image->Lock(); try { - if (image->hamiltonian->Name() == "Heisenberg") + if( image->hamiltonian->Name() == "Heisenberg" ) { // Update the Hamiltonian auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); @@ -314,12 +347,26 @@ try ham->Update_Interactions(); std::string message = fmt::format("Set exchange to {} shells", n_shells); - if (n_shells > 0) message += fmt::format(" Jij[0] = {}", jij[0]); + if( n_shells > 0 ) message += fmt::format(" Jij[0] = {} [meV/bond]", jij[0]); Log(Utility::Log_Level::Info, Utility::Log_Sender::API, message, idx_image, idx_chain); } - else - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - "Exchange cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + if( n_shells > 1 ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Cannot set more than one shell of Exchange interaction on micromagnetic Hamiltonian.", idx_image, idx_chain ); + image->Unlock(); + return; + } + + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + auto Jij = jij[0]; + ham->exchange_tensor << Jij, 0, 0, + 0, Jij, 0, + 0, 0, Jij; + ham->Update_Interactions(); + } } catch( ... ) { @@ -341,7 +388,13 @@ try // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - image->Lock(); + + if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Dzyaloshinskii-Moriya interaction cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); + return; + } if( chirality != SPIRIT_CHIRALITY_BLOCH && chirality != SPIRIT_CHIRALITY_NEEL && @@ -353,9 +406,11 @@ try return; } + image->Lock(); + try { - if (image->hamiltonian->Name() == "Heisenberg") + if( image->hamiltonian->Name() == "Heisenberg" ) { // Update the Hamiltonian auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); @@ -367,12 +422,27 @@ try ham->Update_Interactions(); std::string message = fmt::format("Set dmi to {} shells", n_shells); - if (n_shells > 0) message += fmt::format(" Dij[0] = {}", dij[0]); + if( n_shells > 0 ) message += fmt::format(" Dij[0] = {} [meV/bond]", dij[0]); Log(Utility::Log_Level::Info, Utility::Log_Sender::API, message, idx_image, idx_chain); } - else - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, "DMI cannot be set on " + - image->hamiltonian->Name(), idx_image, idx_chain ); + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + if( n_shells > 1 ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Cannot set more than one shell of DM interaction on micromagnetic Hamiltonian.", idx_image, idx_chain ); + image->Unlock(); + return; + } + + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + auto Dij = dij[0]; + // TODO: handle chirality + ham->exchange_tensor << Dij, 0, 0, + 0, Dij, 0, + 0, 0, Dij; + ham->Update_Interactions(); + } } catch( ... ) { @@ -394,11 +464,19 @@ try // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); + + if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Dipolar interactions cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); + return; + } + image->Lock(); try { - if (image->hamiltonian->Name() == "Heisenberg") + if( image->hamiltonian->Name() == "Heisenberg" ) { auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); @@ -408,14 +486,19 @@ try ham->ddi_n_periodic_images[2] = n_periodic_images[2]; ham->ddi_cutoff_radius = cutoff_radius; ham->Update_Interactions(); - - Log( Utility::Log_Level::Info, Utility::Log_Sender::API, fmt::format( - "Set ddi to method {}, periodic images {} {} {} and cutoff radius {}", - ddi_method, n_periodic_images[0], n_periodic_images[1], n_periodic_images[2], cutoff_radius), idx_image, idx_chain ); } - else - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, "DDI cannot be set on " + - image->hamiltonian->Name(), idx_image, idx_chain ); + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Dipolar interactions have not yet been implemented on the micromagnetic Hamiltonian", idx_image, idx_chain ); + image->Unlock(); + return; + } + + Log( Utility::Log_Level::Info, Utility::Log_Sender::API, fmt::format( + "Set DDI to method {}, periodic images {} {} {} and cutoff radius {}", + ddi_method, n_periodic_images[0], n_periodic_images[1], n_periodic_images[2], cutoff_radius), + idx_image, idx_chain ); } catch( ... ) { @@ -477,28 +560,32 @@ try // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - if (image->hamiltonian->Name() == "Heisenberg") + if( image->hamiltonian->Name() == "Heisenberg" ) { auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - if (ham->external_field_magnitude > 0) - { - // Magnitude - *magnitude = (float)(ham->external_field_magnitude / Constants::mu_B); + *magnitude = (float)(ham->external_field_magnitude / Constants::mu_B); + normal[0] = (float)ham->external_field_normal[0]; + normal[1] = (float)ham->external_field_normal[1]; + normal[2] = (float)ham->external_field_normal[2]; + } + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); - // Normal - normal[0] = (float)ham->external_field_normal[0]; - normal[1] = (float)ham->external_field_normal[1]; - normal[2] = (float)ham->external_field_normal[2]; - } - else - { - *magnitude = 0; - normal[0] = 0; - normal[1] = 0; - normal[2] = 1; - } + *magnitude = (float)ham->external_field_magnitude; + normal[0] = (float)ham->external_field_normal[0]; + normal[1] = (float)ham->external_field_normal[1]; + normal[2] = (float)ham->external_field_normal[2]; + } + else + { + *magnitude = 0; + normal[0] = 0; + normal[1] = 0; + normal[2] = 1; } + } catch( ... ) { @@ -514,16 +601,13 @@ try // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - if (image->hamiltonian->Name() == "Heisenberg") + if( image->hamiltonian->Name() == "Heisenberg" ) { auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - if (ham->anisotropy_indices.size() > 0) + if( ham->anisotropy_indices.size() > 0 ) { - // Magnitude *magnitude = (float)ham->anisotropy_magnitudes[0]; - - // Normal normal[0] = (float)ham->anisotropy_normals[0][0]; normal[1] = (float)ham->anisotropy_normals[0][1]; normal[2] = (float)ham->anisotropy_normals[0][2]; @@ -536,6 +620,17 @@ try normal[2] = 1; } } + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + } + else + { + *magnitude = 0; + normal[0] = 0; + normal[1] = 0; + normal[2] = 1; + } } catch( ... ) { @@ -551,18 +646,30 @@ try // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - if (image->hamiltonian->Name() == "Heisenberg") + if( image->hamiltonian->Name() == "Heisenberg" ) { auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); *n_shells = ham->exchange_shell_magnitudes.size(); // Note the array needs to be correctly allocated beforehand! - for (int i=0; iexchange_shell_magnitudes.size(); ++i) + for( int i=0; iexchange_shell_magnitudes.size(); ++i ) { jij[i] = (float)ham->exchange_shell_magnitudes[i]; } } + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + + *n_shells = 1; + jij[0] = (float)ham->exchange_tensor(0,0); + } + else + { + *n_shells = 0; + } + } catch( ... ) { @@ -614,18 +721,30 @@ try // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - if (image->hamiltonian->Name() == "Heisenberg") + if( image->hamiltonian->Name() == "Heisenberg" ) { auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); *n_shells = ham->dmi_shell_magnitudes.size(); *chirality = ham->dmi_shell_chirality; - for (int i=0; i<*n_shells; ++i) + for( int i=0; i<*n_shells; ++i ) { dij[i] = (float)ham->dmi_shell_magnitudes[i]; } } + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + + *n_shells = 1; + dij[0] = (float)ham->dmi_tensor(0,0); + } + else + { + *n_shells = 0; + } + } catch( ... ) { @@ -660,7 +779,7 @@ try // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); - if (image->hamiltonian->Name() == "Heisenberg") + if( image->hamiltonian->Name() == "Heisenberg" ) { auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); @@ -670,6 +789,24 @@ try n_periodic_images[2] = (int)ham->ddi_n_periodic_images[2]; *cutoff_radius = (float)ham->ddi_cutoff_radius; } + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + + *ddi_method = (int)ham->ddi_method; + n_periodic_images[0] = (int)ham->ddi_n_periodic_images[0]; + n_periodic_images[1] = (int)ham->ddi_n_periodic_images[1]; + n_periodic_images[2] = (int)ham->ddi_n_periodic_images[2]; + *cutoff_radius = (float)ham->ddi_cutoff_radius; + } + else + { + *ddi_method = SPIRIT_DDI_METHOD_NONE; + n_periodic_images[0] = 0; + n_periodic_images[1] = 0; + n_periodic_images[2] = 0; + *cutoff_radius = 0; + } } catch( ... ) { diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cpp b/core/src/engine/Hamiltonian_Micromagnetic.cpp index a666fef9f..7cb5c88de 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cpp +++ b/core/src/engine/Hamiltonian_Micromagnetic.cpp @@ -29,12 +29,15 @@ namespace Engine Matrix3 anisotropy_tensor, Matrix3 exchange_tensor, Matrix3 dmi_tensor, + DDI_Method ddi_method, intfield ddi_n_periodic_images, scalar ddi_radius, std::shared_ptr geometry, int spatial_gradient_order, intfield boundary_conditions ) : Hamiltonian(boundary_conditions), Ms(Ms), spatial_gradient_order(spatial_gradient_order), geometry(geometry), external_field_magnitude(external_field_magnitude), external_field_normal(external_field_normal), - anisotropy_tensor(anisotropy_tensor), exchange_tensor(exchange_tensor), dmi_tensor(dmi_tensor) + anisotropy_tensor(anisotropy_tensor), exchange_tensor(exchange_tensor), dmi_tensor(dmi_tensor), + ddi_method(ddi_method), ddi_n_periodic_images(ddi_n_periodic_images), ddi_cutoff_radius(ddi_radius), + fft_plan_reverse(FFT::FFT_Plan()), fft_plan_spins(FFT::FFT_Plan()) { // Generate interaction pairs, constants etc. this->Update_Interactions(); @@ -310,13 +313,14 @@ namespace Engine void Hamiltonian_Micromagnetic::Gradient_Zeeman(vectorfield & gradient) { + // In this context this is magnetisation per cell auto& mu_s = this->geometry->mu_s; #pragma omp parallel for for( int icell = 0; icell < geometry->n_cells_total; ++icell ) { if( check_atom_type(this->geometry->atom_types[icell]) ) - gradient[icell] -= mu_s[icell] * this->external_field_magnitude * this->external_field_normal; + gradient[icell] -= mu_s[icell]*C::mu_B * this->external_field_magnitude * this->external_field_normal; } } diff --git a/core/src/io/Configparser.cpp b/core/src/io/Configparser.cpp index 68bacf7ff..aa9c8c6b7 100644 --- a/core/src/io/Configparser.cpp +++ b/core/src/io/Configparser.cpp @@ -416,7 +416,7 @@ namespace IO scalar V = std::pow(lattice_constant * 1e-10, 3) * (bravais_vectors[0].cross(bravais_vectors[1])).dot(bravais_vectors[2]); // * n_cells[0] * n_cells[1] * n_cells[2] * n_cell_atoms; - scalar mu_s = Ms * 1.0782822e23 * V; // per lattice site + scalar mu_s = Ms * 1.0782822e23 * V; // per cell for (iatom = 0; iatom < n_cell_atoms; ++iatom) cell_composition.mu_s[iatom] = mu_s; @@ -1484,6 +1484,12 @@ namespace IO scalar dmi_magnitude = 0; Matrix3 dmi_tensor; + // Dipolar + std::string ddi_method_str = "none"; + auto ddi_method = Engine::DDI_Method::None; + intfield ddi_n_periodic_images = { 4, 4, 4 }; + scalar ddi_radius = 0.0; + //------------------------------- Parser -------------------------------- Log(Log_Level::Info, Log_Sender::IO, "Hamiltonian_Micromagnetic: building"); try @@ -1568,50 +1574,51 @@ namespace IO else { myfile.Read_Single(dmi_magnitude, "dmi"); - dmi_tensor << 0, dmi_magnitude/std::sqrt(3), 0, - -dmi_magnitude/std::sqrt(3), 0,0, - 0, 0, 0; - // dmi_tensor << dmi_magnitude/std::sqrt(3), 0, 0, - // 0, dmi_magnitude/std::sqrt(3), 0, - // 0, 0, dmi_magnitude/std::sqrt(3); + // dmi_tensor << 0, dmi_magnitude/std::sqrt(3), 0, + // -dmi_magnitude/std::sqrt(3), 0,0, + // 0, 0, 0; + dmi_tensor << dmi_magnitude/std::sqrt(3), 0, 0, + 0, dmi_magnitude/std::sqrt(3), 0, + 0, 0, dmi_magnitude/std::sqrt(3); // dmi_tensor << 0, dmi_magnitude, -dmi_magnitude, // -dmi_magnitude, 0, dmi_magnitude, // dmi_magnitude, -dmi_magnitude, 0; } - // TODO: dipolar + try { IO::Filter_File_Handle myfile(configFile); - // // DDI method - // myfile.Read_String(ddi_method_str, "ddi_method"); - // if( ddi_method_str == "none" ) - // ddi_method = Engine::DDI_Method::None; - // else if( ddi_method_str == "fft" ) - // ddi_method = Engine::DDI_Method::FFT; - // else if( ddi_method_str == "fmm" ) - // ddi_method = Engine::DDI_Method::FMM; - // else if( ddi_method_str == "cutoff" ) - // ddi_method = Engine::DDI_Method::Cutoff; - // else - // { - // Log(Log_Level::Warning, Log_Sender::IO, fmt::format( - // "Hamiltonian_Heisenberg: Keyword 'ddi_method' got passed invalid method \"{}\". Setting to \"none\".", ddi_method_str)); - // ddi_method_str = "none"; - // } - - // // Number of periodical images - // myfile.Read_3Vector(ddi_n_periodic_images, "ddi_n_periodic_images"); - // // myfile.Read_Single(ddi_n_periodic_images, "ddi_n_periodic_images"); - - // // Dipole-dipole cutoff radius - // myfile.Read_Single(ddi_radius, "ddi_radius"); + // DDI method + myfile.Read_String(ddi_method_str, "ddi_method"); + if( ddi_method_str == "none" ) + ddi_method = Engine::DDI_Method::None; + else if( ddi_method_str == "fft" ) + ddi_method = Engine::DDI_Method::FFT; + else if( ddi_method_str == "fmm" ) + ddi_method = Engine::DDI_Method::FMM; + else if( ddi_method_str == "cutoff" ) + ddi_method = Engine::DDI_Method::Cutoff; + else + { + Log(Log_Level::Warning, Log_Sender::IO, fmt::format( + "Hamiltonian_Heisenberg: Keyword 'ddi_method' got passed invalid method \"{}\". Setting to \"none\".", ddi_method_str)); + ddi_method_str = "none"; + } + + // Number of periodical images + myfile.Read_3Vector(ddi_n_periodic_images, "ddi_n_periodic_images"); + // myfile.Read_Single(ddi_n_periodic_images, "ddi_n_periodic_images"); + + // Dipole-dipole cutoff radius + myfile.Read_Single(ddi_radius, "ddi_radius"); }// end try catch( ... ) { spirit_handle_exception_core(fmt::format("Unable to read DDI radius from config file \"{}\"", configFile)); } + }// end try catch( ... ) { @@ -1634,6 +1641,9 @@ namespace IO Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} = {}", "dmi tensor", dmi_tensor.row(0))); Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} {}", " ", dmi_tensor.row(1))); Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<24} {}", " ", dmi_tensor.row(2))); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<21} = {}", "ddi_method", ddi_method_str)); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<21} = ({} {} {})", "ddi_n_periodic_images", ddi_n_periodic_images[0], ddi_n_periodic_images[1], ddi_n_periodic_images[2])); + Log(Log_Level::Parameter, Log_Sender::IO, fmt::format(" {:<21} = {}", "ddi_radius", ddi_radius)); auto hamiltonian = std::unique_ptr(new Engine::Hamiltonian_Micromagnetic( Ms, @@ -1641,6 +1651,7 @@ namespace IO anisotropy_tensor, exchange_tensor, dmi_tensor, + ddi_method, ddi_n_periodic_images, ddi_radius, geometry, spatial_gradient_order, boundary_conditions From e2bc0ade2278289ce866c50bd74668e1ad6b08c0 Mon Sep 17 00:00:00 2001 From: Gideon Date: Tue, 24 Sep 2019 22:28:36 +0200 Subject: [PATCH 14/45] UI-Qt: connected a few micromagnetics settings. --- .../HamiltonianMicromagneticWidget.hpp | 2 + ui-cpp/src/HamiltonianMicromagneticWidget.cpp | 147 ++++- ui-cpp/ui/HamiltonianMicromagneticWidget.ui | 619 +++++++++--------- 3 files changed, 435 insertions(+), 333 deletions(-) diff --git a/ui-cpp/include/HamiltonianMicromagneticWidget.hpp b/ui-cpp/include/HamiltonianMicromagneticWidget.hpp index 2404c445a..3394b3a59 100644 --- a/ui-cpp/include/HamiltonianMicromagneticWidget.hpp +++ b/ui-cpp/include/HamiltonianMicromagneticWidget.hpp @@ -26,6 +26,8 @@ class HamiltonianMicromagneticWidget : public QWidget, private Ui::HamiltonianMi private slots: void clicked_change_hamiltonian(); void set_boundary_conditions(); + void set_Ms(); + void set_external_field(); private: void Setup_Input_Validators(); diff --git a/ui-cpp/src/HamiltonianMicromagneticWidget.cpp b/ui-cpp/src/HamiltonianMicromagneticWidget.cpp index 66859f89a..4485f8862 100644 --- a/ui-cpp/src/HamiltonianMicromagneticWidget.cpp +++ b/ui-cpp/src/HamiltonianMicromagneticWidget.cpp @@ -9,6 +9,16 @@ #include #include +// Small function for normalization of vectors +#define Exception_Division_by_zero 6666 +template +void normalize(T v[3]) +{ + T len = 0.0; + for (int i = 0; i < 3; ++i) len += std::pow(v[i], 2); + if (len == 0.0) throw Exception_Division_by_zero; + for (int i = 0; i < 3; ++i) v[i] /= std::sqrt(len); +} HamiltonianMicromagneticWidget::HamiltonianMicromagneticWidget(std::shared_ptr state, SpinWidget * spinWidget) { @@ -43,17 +53,17 @@ void HamiltonianMicromagneticWidget::updateData() this->checkBox_aniso_periodical_b->setChecked(boundary_conditions[1]); this->checkBox_aniso_periodical_c->setChecked(boundary_conditions[2]); - // mu_s + // Ms Geometry_Get_mu_s(state.get(), mu_s.data()); - this->lineEdit_muSpin_aniso->setText(QString::number(mu_s[0])); + this->lineEdit_mm_Ms->setText(QString::number(mu_s[0])); // External magnetic field Hamiltonian_Get_Field(state.get(), &d, vd); - this->lineEdit_extH_aniso->setText(QString::number(d)); - this->lineEdit_extHx_aniso->setText(QString::number(vd[0])); - this->lineEdit_extHy_aniso->setText(QString::number(vd[1])); - this->lineEdit_extHz_aniso->setText(QString::number(vd[2])); - if (d > 0.0) this->checkBox_extH_aniso->setChecked(true); + this->lineEdit_mm_field->setText(QString::number(d)); + this->lineEdit_mm_field_x->setText(QString::number(vd[0])); + this->lineEdit_mm_field_y->setText(QString::number(vd[1])); + this->lineEdit_mm_field_z->setText(QString::number(vd[2])); + if (d > 0.0) this->checkBox_mm_field->setChecked(true); // Anisotropy Hamiltonian_Get_Anisotropy(state.get(), &d, vd); @@ -85,19 +95,19 @@ void HamiltonianMicromagneticWidget::updateData() // DDI Hamiltonian_Get_DDI(state.get(), &ddi_method, ddi_n_periodic_images, &d); - this->checkBox_ddi->setChecked( ddi_method != SPIRIT_DDI_METHOD_NONE ); + this->checkBox_mm_ddi->setChecked( ddi_method != SPIRIT_DDI_METHOD_NONE ); if( ddi_method == SPIRIT_DDI_METHOD_NONE ) - this->comboBox_ddi_method->setCurrentIndex(0); + this->comboBox_mm_ddi_method->setCurrentIndex(0); else if( ddi_method == SPIRIT_DDI_METHOD_FFT ) - this->comboBox_ddi_method->setCurrentIndex(0); + this->comboBox_mm_ddi_method->setCurrentIndex(0); else if( ddi_method == SPIRIT_DDI_METHOD_FMM ) - this->comboBox_ddi_method->setCurrentIndex(1); + this->comboBox_mm_ddi_method->setCurrentIndex(1); else if( ddi_method == SPIRIT_DDI_METHOD_CUTOFF ) - this->comboBox_ddi_method->setCurrentIndex(2); - this->spinBox_ddi_n_periodic_a->setValue(ddi_n_periodic_images[0]); - this->spinBox_ddi_n_periodic_b->setValue(ddi_n_periodic_images[1]); - this->spinBox_ddi_n_periodic_c->setValue(ddi_n_periodic_images[2]); - this->doubleSpinBox_ddi_radius->setValue(d); + this->comboBox_mm_ddi_method->setCurrentIndex(2); + this->spinBox_mm_ddi_n_periodic_a->setValue(ddi_n_periodic_images[0]); + this->spinBox_mm_ddi_n_periodic_b->setValue(ddi_n_periodic_images[1]); + this->spinBox_mm_ddi_n_periodic_c->setValue(ddi_n_periodic_images[2]); + this->doubleSpinBox_mm_ddi_radius->setValue(d); } void HamiltonianMicromagneticWidget::clicked_change_hamiltonian() @@ -156,6 +166,89 @@ void HamiltonianMicromagneticWidget::set_boundary_conditions() this->spinWidget->updateBoundingBoxIndicators(); } +void HamiltonianMicromagneticWidget::set_Ms() +{ + // Closure to set the parameters of a specific spin system + auto apply = [this](int idx_image) -> void + { + // Ms + float Ms = this->lineEdit_mm_Ms->text().toFloat(); + Geometry_Set_mu_s(state.get(), Ms, idx_image); + }; + + if (this->comboBox_Hamiltonian_Ani_ApplyTo->currentText() == "Current Image") + { + apply(System_Get_Index(state.get())); + } + else if (this->comboBox_Hamiltonian_Ani_ApplyTo->currentText() == "Current Image Chain") + { + for (int i = 0; icomboBox_Hamiltonian_Ani_ApplyTo->currentText() == "All Images") + { + for (int img = 0; img void + { + float d, vd[3]; + + // External magnetic field + // magnitude + if( this->checkBox_mm_field->isChecked() ) d = this->lineEdit_mm_field->text().toFloat(); + else d = 0.0; + // normal + vd[0] = lineEdit_mm_field_x->text().toFloat(); + vd[1] = lineEdit_mm_field_y->text().toFloat(); + vd[2] = lineEdit_mm_field_z->text().toFloat(); + try { + normalize(vd); + } + catch (int ex) { + if (ex == Exception_Division_by_zero) { + vd[0] = 0.0; + vd[1] = 0.0; + vd[2] = 1.0; + Log_Send(state.get(), Log_Level_Warning, Log_Sender_UI, "B_vec = {0,0,0} replaced by {0,0,1}"); + lineEdit_mm_field_x->setText(QString::number(0.0)); + lineEdit_mm_field_y->setText(QString::number(0.0)); + lineEdit_mm_field_z->setText(QString::number(1.0)); + } + else { throw(ex); } + } + Hamiltonian_Set_Field(state.get(), d, vd, idx_image); + }; + + if (this->comboBox_Hamiltonian_Ani_ApplyTo->currentText() == "Current Image") + { + apply(System_Get_Index(state.get())); + } + else if (this->comboBox_Hamiltonian_Ani_ApplyTo->currentText() == "Current Image Chain") + { + for (int i = 0; icomboBox_Hamiltonian_Ani_ApplyTo->currentText() == "All Images") + { + for (int img = 0; imgnumber_validator_int_unsigned = new QRegularExpressionValidator(re4); - // mu_s - this->lineEdit_muSpin_aniso->setValidator(this->number_validator); + // Ms + this->lineEdit_mm_Ms->setValidator(this->number_validator); // external field - this->lineEdit_extH_aniso->setValidator(this->number_validator); - this->lineEdit_extHx_aniso->setValidator(this->number_validator); - this->lineEdit_extHy_aniso->setValidator(this->number_validator); - this->lineEdit_extHz_aniso->setValidator(this->number_validator); + this->lineEdit_mm_field->setValidator(this->number_validator); + this->lineEdit_mm_field_x->setValidator(this->number_validator); + this->lineEdit_mm_field_y->setValidator(this->number_validator); + this->lineEdit_mm_field_z->setValidator(this->number_validator); // anisotropy this->lineEdit_ani_aniso->setValidator(this->number_validator); this->lineEdit_anix_aniso->setValidator(this->number_validator); @@ -191,8 +284,16 @@ void HamiltonianMicromagneticWidget::Setup_Input_Validators() void HamiltonianMicromagneticWidget::Setup_Slots() { connect(this->pushButton_changeHamiltonian, SIGNAL(clicked()), this, SLOT(clicked_change_hamiltonian())); - // Boundary Conditions + // Boundary conditions connect(this->checkBox_aniso_periodical_a, SIGNAL(stateChanged(int)), this, SLOT(set_boundary_conditions())); connect(this->checkBox_aniso_periodical_b, SIGNAL(stateChanged(int)), this, SLOT(set_boundary_conditions())); connect(this->checkBox_aniso_periodical_c, SIGNAL(stateChanged(int)), this, SLOT(set_boundary_conditions())); + // Ms + connect(this->lineEdit_mm_Ms, SIGNAL(returnPressed()), this, SLOT(set_Ms())); + // External field + connect(this->checkBox_mm_field, SIGNAL(stateChanged(int)), this, SLOT(set_external_field())); + connect(this->lineEdit_mm_field, SIGNAL(returnPressed()), this, SLOT(set_external_field())); + connect(this->lineEdit_mm_field_x, SIGNAL(returnPressed()), this, SLOT(set_external_field())); + connect(this->lineEdit_mm_field_y, SIGNAL(returnPressed()), this, SLOT(set_external_field())); + connect(this->lineEdit_mm_field_z, SIGNAL(returnPressed()), this, SLOT(set_external_field())); } \ No newline at end of file diff --git a/ui-cpp/ui/HamiltonianMicromagneticWidget.ui b/ui-cpp/ui/HamiltonianMicromagneticWidget.ui index a6ea75bbc..eb4b0ab11 100644 --- a/ui-cpp/ui/HamiltonianMicromagneticWidget.ui +++ b/ui-cpp/ui/HamiltonianMicromagneticWidget.ui @@ -168,7 +168,7 @@ - + @@ -215,8 +215,92 @@ - - + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + cutoff radius + + + + + + + + + + + + 40 + 16777215 + + + + + + + + + 40 + 16777215 + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + 40 + 16777215 + + + + + + + + + + + + + + + + + + + + + FFT @@ -234,7 +318,44 @@ - + + + + Periodical boundaries + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Dipolar + + + + + + + DMI [J/m^2] + + + + @@ -261,10 +382,23 @@ - - - - + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Qt::Horizontal @@ -276,32 +410,23 @@ - - + + - Number of Shells + periodic images - - - - - - - Qt::Horizontal - - - - 40 - 20 - + + + + Anisotropy [J/m^3] - + - + @@ -332,62 +457,58 @@ - - - - mu_spin - - - - - - - - - - 120 - 16777215 - - - - - Bloch - - - - - Reverse Bloch - - - - - Neel - - - - - Reverse Neel - - - - + + + + 1000000.000000000000000 + + + + + + + External Field [T] + + + false + + + false + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + - + - 55 + 40 16777215 - - Chirality - - - + + Qt::Horizontal @@ -401,55 +522,73 @@ - - - - Exchange [J/m] + + + + Qt::Vertical - - - - + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + Exchange [J/m] + + + + + + + Qt::Vertical + + + QSizePolicy::Fixed + + + + 20 + 10 + + + + + + + + + + Direction (x,y,z) + + + - + Qt::Horizontal - 40 + 30 20 - - - - cutoff radius - - - - - - - 1000000.000000000000000 - - - - - - - Dipolar - - - - - - - + + + + Qt::Horizontal @@ -461,39 +600,19 @@ - - + + - periodic images + Number of Shells + + + - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - Periodical boundaries - - - - + Qt::Vertical @@ -509,27 +628,7 @@ - - - - External Field [T] - - - false - - - false - - - - - - - DMI [J/m^2] - - - - + @@ -545,7 +644,7 @@ - + 40 @@ -556,121 +655,53 @@ - - - - - - - 40 - 16777215 - - - - - - - - - 40 - 16777215 - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - 40 - 16777215 - - - - + + - + - 40 + 120 16777215 + + + Bloch + + + + + Reverse Bloch + + + + + Neel + + + + + Reverse Neel + + - - - - - - Anisotropy [J/m^3] - - - - - - - - - - - - - - - - - - - - - - Direction (x,y,z) - - - - - - - Qt::Horizontal - - - - 30 - 20 - - - - - - - - - + - 40 + 55 16777215 + + Chirality + - - + + Qt::Horizontal @@ -684,54 +715,6 @@ - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - - - - - Qt::Vertical - - - QSizePolicy::Fixed - - - - 20 - 10 - - - - @@ -770,6 +753,23 @@ + + + + Ms [A/m] + + + + + + + + 40 + 16777215 + + + + @@ -794,12 +794,11 @@ scrollArea_5 comboBox_Hamiltonian_Ani_ApplyTo - checkBox_extH_aniso - lineEdit_extH_aniso - lineEdit_muSpin_aniso - lineEdit_extHx_aniso - lineEdit_extHy_aniso - lineEdit_extHz_aniso + checkBox_mm_field + lineEdit_mm_field + lineEdit_mm_field_x + lineEdit_mm_field_y + lineEdit_mm_field_z checkBox_ani_aniso lineEdit_ani_aniso lineEdit_anix_aniso @@ -809,7 +808,7 @@ spinBox_nshells_exchange checkBox_dmi spinBox_nshells_dmi - checkBox_ddi + checkBox_mm_ddi From febe6408d82cce113aa2110999bfb92df181e820 Mon Sep 17 00:00:00 2001 From: Moritz Sallermann Date: Fri, 27 Sep 2019 13:24:25 +0200 Subject: [PATCH 15/45] Core: Cuda code compiles now. Hamiltonian_Micromagnetic.cu still needs to be fixed. --- core/src/engine/Hamiltonian_Micromagnetic.cu | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cu b/core/src/engine/Hamiltonian_Micromagnetic.cu index b4c04b424..f14c6593b 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cu +++ b/core/src/engine/Hamiltonian_Micromagnetic.cu @@ -24,26 +24,12 @@ using Engine::Vectormath::cu_tupel_from_idx; namespace Engine { Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( + scalar Ms, scalar external_field_magnitude, Vector3 external_field_normal, Matrix3 anisotropy_tensor, Matrix3 exchange_tensor, Matrix3 dmi_tensor, - std::shared_ptr geometry, - int spatial_gradient_order, - intfield boundary_conditions - ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), geometry(geometry), - external_field_magnitude(external_field_magnitude), external_field_normal(external_field_normal), - anisotropy_tensor(anisotropy_tensor), exchange_tensor(exchange_tensor), dmi_tensor(dmi_tensor) - { - // Generate interaction pairs, constants etc. - this->Update_Interactions(); - } - - Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( - scalar external_field_magnitude, Vector3 external_field_normal, - Matrix3 anisotropy_tensor, - scalar exchange_constant, - scalar dmi_constant, + DDI_Method ddi_method, intfield ddi_n_periodic_images, scalar ddi_radius, std::shared_ptr geometry, int spatial_gradient_order, intfield boundary_conditions From 270a3c8d20c9604a564f5c9679662c39c837cf78 Mon Sep 17 00:00:00 2001 From: Moritz Sallermann Date: Fri, 27 Sep 2019 13:58:16 +0200 Subject: [PATCH 16/45] Core: Made Hamiltonian_Micromagnetic.cu consistent with Hamiltonian_Micromagnetic.cpp --- core/src/engine/Hamiltonian_Micromagnetic.cu | 116 +++++++++---------- 1 file changed, 55 insertions(+), 61 deletions(-) diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cu b/core/src/engine/Hamiltonian_Micromagnetic.cu index f14c6593b..c540014bb 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cu +++ b/core/src/engine/Hamiltonian_Micromagnetic.cu @@ -25,14 +25,14 @@ namespace Engine { Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( scalar Ms, - scalar external_field_magnitude, Vector3 external_field_normal, - Matrix3 anisotropy_tensor, - Matrix3 exchange_tensor, - Matrix3 dmi_tensor, + scalar external_field_magnitude, Vector3 external_field_normal, + Matrix3 anisotropy_tensor, + Matrix3 exchange_tensor, + Matrix3 dmi_tensor, DDI_Method ddi_method, intfield ddi_n_periodic_images, scalar ddi_radius, - std::shared_ptr geometry, - int spatial_gradient_order, - intfield boundary_conditions + std::shared_ptr geometry, + int spatial_gradient_order, + intfield boundary_conditions ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), geometry(geometry), external_field_magnitude(external_field_magnitude), external_field_normal(external_field_normal), anisotropy_tensor(anisotropy_tensor), exchange_tensor(exchange_tensor), dmi_tensor(dmi_tensor) @@ -317,7 +317,7 @@ namespace Engine { int ispin = icell + ibasis; if (cu_check_atom_type(atom_types[ispin])) - gradient[ispin] -= mu_s[ispin] * external_field_magnitude*external_field_normal; + gradient[ispin] -= mu_s[ispin] * C::mu_B * external_field_magnitude*external_field_normal; } } } @@ -328,7 +328,7 @@ namespace Engine CU_CHECK_AND_SYNC(); } - __global__ void CU_Gradient_Anisotropy1(const Vector3 * spins, const int * atom_types, const int n_cell_atoms, Vector3 * gradient, size_t n_cells_total) + __global__ void CU_Gradient_Anisotropy1(const Vector3 * spins, const int * atom_types, const int n_cell_atoms, Vector3 * gradient, size_t n_cells_total, Matrix3 anisotropy_tensor) { scalar Ms = 1.4e6; Vector3 temp1{ 1,0,0 }; @@ -338,52 +338,47 @@ namespace Engine icell < n_cells_total; icell += blockDim.x * gridDim.x) { - for (int iani = 0; iani < 1; ++iani) - { - int ispin = icell; - gradient[ispin] -= 2.0 * 8.44e6 / Ms * temp3 * temp3.dot(spins[ispin]); - //gradient[ispin] -= 2.0 * this->anisotropy_magnitudes[iani] / Ms * ((pow(temp2.dot(spins[ispin]),2)+ pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1)+ (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)+(pow(temp1.dot(spins[ispin]),2)+ pow(temp2.dot(spins[ispin]), 2))*(temp3.dot(spins[ispin])*temp3)); - //gradient[ispin] += 2.0 * 50000 / Ms * ((pow(temp2.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1) + (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)); - - } + int ispin = icell; + gradient[ispin] -= 2.0 * C::mu_B * anisotropy_tensor * spins[ispin] / Ms; + //gradient[ispin] -= 2.0 * this->anisotropy_magnitudes[iani] / Ms * ((pow(temp2.dot(spins[ispin]),2)+ pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1)+ (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)+(pow(temp1.dot(spins[ispin]),2)+ pow(temp2.dot(spins[ispin]), 2))*(temp3.dot(spins[ispin])*temp3)); + //gradient[ispin] += 2.0 * 50000 / Ms * ((pow(temp2.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1) + (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)); } } void Hamiltonian_Micromagnetic::Gradient_Anisotropy(const vectorfield & spins, vectorfield & gradient) { int size = geometry->n_cells_total; - CU_Gradient_Anisotropy1 << <(size + 1023) / 1024, 1024 >> > (spins.data(), this->geometry->atom_types.data(), this->geometry->n_cell_atoms, gradient.data(), size); + CU_Gradient_Anisotropy1 << <(size + 1023) / 1024, 1024 >> > (spins.data(), this->geometry->atom_types.data(), this->geometry->n_cell_atoms, gradient.data(), size, this->anisotropy_tensor); CU_CHECK_AND_SYNC(); } __global__ void CU_Gradient_Exchange1(const Vector3 * spins, const int * atom_types, const int * boundary_conditions, const int * n_cells, int n_cell_atoms, - int n_pairs, const Pair * neigh, Vector3 * gradient, size_t size, bool A_is_nondiagonal, Matrix3 exchange_tensor) + int n_pairs, const Pair * neigh, Vector3 * gradient, size_t size, bool A_is_nondiagonal, Matrix3 exchange_tensor, const scalar * delta, const scalar Ms) { int bc[3] = { boundary_conditions[0],boundary_conditions[1],boundary_conditions[2] }; + int nc[3] = { n_cells[0],n_cells[1],n_cells[2] }; - scalar delta[3] = { 3e-11,3e-11,3e-10 }; - scalar Ms = 1.4e6; + for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; icell < size; icell += blockDim.x * gridDim.x) { - int ispin = icell;//basically id of a cell + // int ispin = icell;//basically id of a cell for (unsigned int i = 0; i < 3; ++i) { - int ispin_plus = cu_idx_from_pair(ispin, bc, nc, n_cell_atoms, atom_types, neigh[2 * i]); - int ispin_minus = cu_idx_from_pair(ispin, bc, nc, n_cell_atoms, atom_types, neigh[2 * i + 1]); - if (ispin_plus == -1) { - ispin_plus = ispin; - } - if (ispin_minus == -1) { - ispin_minus = ispin; - } + int icell_plus = cu_idx_from_pair(icell, bc, nc, n_cell_atoms, atom_types, neigh[2*i]); + int icell_minus = cu_idx_from_pair(icell, bc, nc, n_cell_atoms, atom_types, neigh[2*i + 1]); - gradient[ispin][0] -= 2 * exchange_tensor(i, i) / Ms * (spins[ispin_plus][0] - 2 * spins[ispin][0] + spins[ispin_minus][0]) / (delta[i]) / (delta[i]); - gradient[ispin][1] -= 2 * exchange_tensor(i, i) / Ms * (spins[ispin_plus][1] - 2 * spins[ispin][1] + spins[ispin_minus][1]) / (delta[i]) / (delta[i]); - gradient[ispin][2] -= 2 * exchange_tensor(i, i) / Ms * (spins[ispin_plus][2] - 2 * spins[ispin][2] + spins[ispin_minus][2]) / (delta[i]) / (delta[i]); + if( icell_plus >= 0 || icell_minus >= 0 ) + { + if( icell_plus == -1 ) + icell_plus = icell; + if( icell_minus == -1 ) + icell_minus = icell; + gradient[icell] -= 2 * C::mu_B * exchange_tensor * (spins[icell_plus] - 2*spins[icell] + spins[icell_minus]) / (Ms*delta[i]*delta[i]); + } } /*if (A_is_nondiagonal == true) { //xy @@ -467,74 +462,73 @@ namespace Engine } void Hamiltonian_Micromagnetic::Gradient_Exchange(const vectorfield & spins, vectorfield & gradient) { - int size = geometry->n_cells_total; + int size = geometry->n_cells_total; + scalar * delta = geometry->cell_size.data(); CU_Gradient_Exchange1 << <(size + 1023) / 1024, 1024 >> > (spins.data(), this->geometry->atom_types.data(), boundary_conditions.data(), geometry->n_cells.data(), geometry->n_cell_atoms, - this->neigh.size(), this->neigh.data(), gradient.data(), size, A_is_nondiagonal, exchange_tensor); + this->neigh.size(), this->neigh.data(), gradient.data(), size, A_is_nondiagonal, exchange_tensor, delta, this->Ms ); CU_CHECK_AND_SYNC(); } __global__ void CU_Spatial_Gradient(const Vector3 * spins, const int * atom_types, const int * boundary_conditions, const int * n_cells, int n_cell_atoms, - int n_pairs, const Pair * neigh, Matrix3 * spatial_gradient, size_t size) + int n_pairs, const Pair * neigh, Matrix3 * spatial_gradient, size_t size, scalar * delta, scalar Ms) { - scalar delta[3] = { 3e-11,3e-11,3e-10 }; /* dn1/dr1 dn1/dr2 dn1/dr3 dn2/dr1 dn2/dr2 dn2/dr3 dn3/dr1 dn3/dr2 dn3/dr3 */ - int bc[3] = { boundary_conditions[0],boundary_conditions[1],boundary_conditions[2] }; - int nc[3] = { n_cells[0],n_cells[1],n_cells[2] }; - scalar Ms = 1.4e6; + int bc[3] = { boundary_conditions[0], boundary_conditions[1], boundary_conditions[2] }; + int nc[3] = { n_cells[0], n_cells[1], n_cells[2] }; + for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; icell < size; icell += blockDim.x * gridDim.x) - { - int ispin = icell;//basically id of a cell + { for (unsigned int i = 0; i < 3; ++i) { - int ispin_plus = cu_idx_from_pair(ispin, bc,nc, n_cell_atoms, atom_types, neigh[2 * i]); - int ispin_minus = cu_idx_from_pair(ispin, bc, nc, n_cell_atoms, atom_types, neigh[2 * i + 1]); - if (ispin_plus == -1) { - ispin_plus = ispin; - } - if (ispin_minus == -1) { - ispin_minus = ispin; - } - spatial_gradient[ispin](0, i) = (spins[ispin_plus][0] - spins[ispin_minus][0]) / (delta[i]) / 2; - spatial_gradient[ispin](1, i) = (spins[ispin_plus][1] - spins[ispin_minus][1]) / (delta[i]) / 2; - spatial_gradient[ispin](2, i) = (spins[ispin_plus][2] - spins[ispin_minus][2]) / (delta[i]) / 2; + int icell_plus = cu_idx_from_pair(icell, bc, nc, n_cell_atoms, atom_types, neigh[2*i]); + int icell_minus = cu_idx_from_pair(icell, bc, nc, n_cell_atoms, atom_types, neigh[2*i + 1]); + + if( icell_plus >= 0 || icell_minus >= 0 ) + { + if( icell_plus == -1 ) + icell_plus = icell; + if( icell_minus == -1 ) + icell_minus = icell; + + spatial_gradient[icell].col(i) += (spins[icell_plus] - spins[icell_minus]) / (2*delta[i]); + } } } } + void Hamiltonian_Micromagnetic::Spatial_Gradient(const vectorfield & spins) { int size = geometry->n_cells_total; CU_Spatial_Gradient << <(size + 1023) / 1024, 1024 >> > (spins.data(), this->geometry->atom_types.data(), boundary_conditions.data(), geometry->n_cells.data(), geometry->n_cell_atoms, - this->neigh.size(), this->neigh.data(), spatial_gradient.data(), size); + this->neigh.size(), this->neigh.data(), spatial_gradient.data(), size, geometry->cell_size.data(), this->Ms); CU_CHECK_AND_SYNC(); } - __global__ void CU_Gradient_DMI1(const Vector3 * spins, Vector3 * gradient, Matrix3 * spatial_gradient, size_t size, Matrix3 dmi_tensor) + __global__ void CU_Gradient_DMI1(const Vector3 * spins, Vector3 * gradient, Matrix3 * spatial_gradient, size_t size, Matrix3 dmi_tensor, scalar Ms) { - scalar Ms = 1.4e6; for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; icell < size; icell += blockDim.x * gridDim.x) { - int ispin = icell;//basically id of a cell for (unsigned int i = 0; i < 3; ++i) { - gradient[ispin][0] += 2 * dmi_tensor(1, i) / Ms * spatial_gradient[ispin](2, i) - 2 * dmi_tensor(2, i) / Ms * spatial_gradient[ispin](1, i); - gradient[ispin][1] += 2 * dmi_tensor(2, i) / Ms * spatial_gradient[ispin](0, i) - 2 * dmi_tensor(0, i) / Ms * spatial_gradient[ispin](2, i); - gradient[ispin][2] += 2 * dmi_tensor(0, i) / Ms * spatial_gradient[ispin](1, i) - 2 * dmi_tensor(1, i) / Ms * spatial_gradient[ispin](0, i); + gradient[icell][0] -= 4 * C::mu_B * (dmi_tensor(1, i) * spatial_gradient[icell](2, i) - 2 * dmi_tensor(2, i) * spatial_gradient[icell](1, i)) / Ms; + gradient[icell][1] -= 4 * C::mu_B * (dmi_tensor(2, i) * spatial_gradient[icell](0, i) - 2 * dmi_tensor(0, i) * spatial_gradient[icell](2, i)) / Ms; + gradient[icell][2] -= 4 * C::mu_B * (dmi_tensor(0, i) * spatial_gradient[icell](1, i) - 2 * dmi_tensor(1, i) * spatial_gradient[icell](0, i)) / Ms; } } } void Hamiltonian_Micromagnetic::Gradient_DMI(const vectorfield & spins, vectorfield & gradient) { int size = geometry->n_cells_total; - CU_Gradient_DMI1 << <(size + 1023) / 1024, 1024 >> > (spins.data(), gradient.data(), spatial_gradient.data(), size, dmi_tensor); + CU_Gradient_DMI1 << <(size + 1023) / 1024, 1024 >> > (spins.data(), gradient.data(), spatial_gradient.data(), size, dmi_tensor, this->Ms); CU_CHECK_AND_SYNC(); } From da0838f8a87050f3e0698097b28027651f3101fa Mon Sep 17 00:00:00 2001 From: Dmitrii Tolmachev Date: Tue, 29 Oct 2019 15:56:13 +0100 Subject: [PATCH 17/45] Core: CUDA DDI implementation for micromagnetics --- .../engine/Hamiltonian_Micromagnetic.hpp | 40 +- core/src/engine/Hamiltonian_Micromagnetic.cu | 410 +++++++++++++++++- 2 files changed, 448 insertions(+), 2 deletions(-) diff --git a/core/include/engine/Hamiltonian_Micromagnetic.hpp b/core/include/engine/Hamiltonian_Micromagnetic.hpp index f21d2d097..61692a751 100644 --- a/core/include/engine/Hamiltonian_Micromagnetic.hpp +++ b/core/include/engine/Hamiltonian_Micromagnetic.hpp @@ -86,6 +86,11 @@ namespace Engine // Calculate the DMI effective field of a Spin Pair void Gradient_DMI(const vectorfield & spins, vectorfield & gradient); void Spatial_Gradient(const vectorfield & spins); + // Calculates the Dipole-Dipole contribution to the effective field of spin ispin within system s + void Gradient_DDI(const vectorfield& spins, vectorfield & gradient); + void Gradient_DDI_Cutoff(const vectorfield& spins, vectorfield & gradient); + void Gradient_DDI_Direct(const vectorfield& spins, vectorfield & gradient); + void Gradient_DDI_FFT(const vectorfield& spins, vectorfield & gradient); // ------------ Energy Functions ------------ // Indices for Energy vector @@ -101,12 +106,45 @@ namespace Engine void E_DMI(const vectorfield & spins, scalarfield & Energy); // Dipolar interactions void E_DDI(const vectorfield & spins, scalarfield & Energy); - + void E_DDI_Direct(const vectorfield& spins, scalarfield & Energy); + void E_DDI_Cutoff(const vectorfield& spins, scalarfield & Energy); + void E_DDI_FFT(const vectorfield& spins, scalarfield & Energy); + + // Preparations for DDI-Convolution Algorithm + void Prepare_DDI(); + void Clean_DDI(); + // Plans for FT / rFT FFT::FFT_Plan fft_plan_spins; FFT::FFT_Plan fft_plan_reverse; field transformed_dipole_matrices; + bool save_dipole_matrices = true; + field dipole_matrices; + + // Number of inter-sublattice contributions + int n_inter_sublattice; + // At which index to look up the inter-sublattice D-matrices + field inter_sublattice_lookup; + + // Lengths of padded system + field n_cells_padded; + // Total number of padded spins per sublattice + int sublattice_size; + + FFT::StrideContainer spin_stride; + FFT::StrideContainer dipole_stride; + + //Calculate the FT of the padded D matriess + void FFT_Dipole_Matrices(FFT::FFT_Plan & fft_plan_dipole, int img_a, int img_b, int img_c); + //Calculate the FT of the padded spins + void FFT_Spins(const vectorfield & spins); + + //Bounds for nested for loops. Only important for the CUDA version + field it_bounds_pointwise_mult; + field it_bounds_write_gradients; + field it_bounds_write_spins; + field it_bounds_write_dipole; }; diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cu b/core/src/engine/Hamiltonian_Micromagnetic.cu index c540014bb..5fcc67557 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cu +++ b/core/src/engine/Hamiltonian_Micromagnetic.cu @@ -9,7 +9,7 @@ #include #include - +#include "FFT.hpp" using namespace Data; using namespace Utility; @@ -155,6 +155,10 @@ namespace Engine neigh_tmp.translations[2] = 1; neigh.push_back(neigh_tmp); this->spatial_gradient = field(geometry->nos, Matrix3::Zero()); + + // Dipole-dipole + this->Prepare_DDI(); + // Update, which terms still contribute this->Update_Energy_Contributions(); } @@ -533,6 +537,410 @@ namespace Engine } + __global__ void CU_FFT_Pointwise_Mult1(FFT::FFT_cpx_type * ft_D_matrices, FFT::FFT_cpx_type * ft_spins, FFT::FFT_cpx_type * res_mult, int* iteration_bounds, int i_b1, int* inter_sublattice_lookup, FFT::StrideContainer dipole_stride, FFT::StrideContainer spin_stride, const scalar Ms) + { + int n = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2] * iteration_bounds[3]; + int tupel[4]; + int idx_b1, idx_b2, idx_d; + + for (int ispin = blockIdx.x * blockDim.x + threadIdx.x; ispin < n; ispin += blockDim.x * gridDim.x) + { + cu_tupel_from_idx(ispin, tupel, iteration_bounds, 4); // tupel now is {i_b2, a, b, c} + + int& b_inter = inter_sublattice_lookup[i_b1 + tupel[0] * iteration_bounds[0]]; + + idx_b1 = i_b1 * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + tupel[3] * spin_stride.c; + idx_b2 = tupel[0] * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + tupel[3] * spin_stride.c; + idx_d = b_inter * dipole_stride.basis + tupel[1] * dipole_stride.a + tupel[2] * dipole_stride.b + tupel[3] * dipole_stride.c; + + auto& fs_x = ft_spins[idx_b2]; + auto& fs_y = ft_spins[idx_b2 + 1 * spin_stride.comp]; + auto& fs_z = ft_spins[idx_b2 + 2 * spin_stride.comp]; + + auto& fD_xx = ft_D_matrices[idx_d]; + auto& fD_xy = ft_D_matrices[idx_d + 1 * dipole_stride.comp]; + auto& fD_xz = ft_D_matrices[idx_d + 2 * dipole_stride.comp]; + auto& fD_yy = ft_D_matrices[idx_d + 3 * dipole_stride.comp]; + auto& fD_yz = ft_D_matrices[idx_d + 4 * dipole_stride.comp]; + auto& fD_zz = ft_D_matrices[idx_d + 5 * dipole_stride.comp]; + + if (tupel[0] == 0) + { + res_mult[idx_b1].x = FFT::mult3D(fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z).x; + res_mult[idx_b1].y = FFT::mult3D(fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z).y; + res_mult[idx_b1 + 1 * spin_stride.comp].x = FFT::mult3D(fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z).x; + res_mult[idx_b1 + 1 * spin_stride.comp].y = FFT::mult3D(fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z).y; + res_mult[idx_b1 + 2 * spin_stride.comp].x = FFT::mult3D(fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z).x; + res_mult[idx_b1 + 2 * spin_stride.comp].y = FFT::mult3D(fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z).y; + } + else { + atomicAdd(&res_mult[idx_b1].x, FFT::mult3D(fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z).x); + atomicAdd(&res_mult[idx_b1].y, FFT::mult3D(fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z).y); + atomicAdd(&res_mult[idx_b1 + 1 * spin_stride.comp].x, FFT::mult3D(fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z).x); + atomicAdd(&res_mult[idx_b1 + 1 * spin_stride.comp].y, FFT::mult3D(fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z).y); + atomicAdd(&res_mult[idx_b1 + 2 * spin_stride.comp].x, FFT::mult3D(fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z).x); + atomicAdd(&res_mult[idx_b1 + 2 * spin_stride.comp].y, FFT::mult3D(fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z).y); + } + } + } + + __global__ void CU_Write_FFT_Gradients1(const FFT::FFT_real_type * resiFFT, Vector3 * gradient, FFT::StrideContainer spin_stride, int * iteration_bounds, int n_cell_atoms, scalar * mu_s, int sublattice_size, const scalar Ms) + { + int nos = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2] * iteration_bounds[3]; + int tupel[4]; + int idx_pad; + for (int idx_orig = blockIdx.x * blockDim.x + threadIdx.x; idx_orig < nos; idx_orig += blockDim.x * gridDim.x) + { + + cu_tupel_from_idx(idx_orig, tupel, iteration_bounds, 4); //tupel now is {ib, a, b, c} + idx_pad = tupel[0] * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + tupel[3] * spin_stride.c; + //printf("%d %f %f\n", idx_orig, resiFFT[idx_pad],gradient[idx_orig][0]); + gradient[idx_orig][0] -= C::mu_B * resiFFT[idx_pad]*Ms*1e-7/(sublattice_size); + gradient[idx_orig][1] -= C::mu_B * resiFFT[idx_pad + 1 * spin_stride.comp]*Ms*1e-7/(sublattice_size); + gradient[idx_orig][2] -= C::mu_B * resiFFT[idx_pad + 2 * spin_stride.comp]*Ms*1e-7/(sublattice_size); + } + } + + void Hamiltonian_Micromagnetic::Gradient_DDI(const vectorfield & spins, vectorfield & gradient) + { + //this->Gradient_DDI_Direct(spins, gradient); + this->Gradient_DDI_FFT(spins, gradient); + /* + if (this->ddi_method == DDI_Method::FFT) + { + printf("sasas"); + this->Gradient_DDI_FFT(spins, gradient); + } + else if (this->ddi_method == DDI_Method::Cutoff) + { + // TODO: Merge these implementations in the future + if (this->ddi_cutoff_radius >= 0) + this->Gradient_DDI_Cutoff(spins, gradient); + else + this->Gradient_DDI_Direct(spins, gradient); + } +*/ + } + void Hamiltonian_Micromagnetic::Gradient_DDI_Cutoff(const vectorfield & spins, vectorfield & gradient) + { + // TODO + } + void Hamiltonian_Micromagnetic::Gradient_DDI_FFT(const vectorfield & spins, vectorfield & gradient) + { + auto& ft_D_matrices = transformed_dipole_matrices; + + auto& ft_spins = fft_plan_spins.cpx_ptr; + + auto& res_iFFT = fft_plan_reverse.real_ptr; + auto& res_mult = fft_plan_reverse.cpx_ptr; + + int number_of_mults = it_bounds_pointwise_mult[0] * it_bounds_pointwise_mult[1] * it_bounds_pointwise_mult[2] * it_bounds_pointwise_mult[3]; + + FFT_Spins(spins); + + // TODO: also parallelize over i_b1 + // Loop over basis atoms (i.e sublattices) and add contribution of each sublattice + for (int i_b1 = 0; i_b1 < geometry->n_cell_atoms; ++i_b1) + CU_FFT_Pointwise_Mult1 << <(number_of_mults + 1023) / 1024, 1024 >> > (ft_D_matrices.data(), ft_spins.data(), res_mult.data(), it_bounds_pointwise_mult.data(), i_b1, inter_sublattice_lookup.data(), dipole_stride, spin_stride, Ms); + CU_CHECK_AND_SYNC(); + FFT::batch_iFour_3D(fft_plan_reverse); + scalar * delta = geometry->cell_size.data(); + CU_Write_FFT_Gradients1 << <(geometry->nos + 1023) / 1024, 1024 >> > (res_iFFT.data(), gradient.data(), spin_stride, it_bounds_write_gradients.data(), geometry->n_cell_atoms, geometry->mu_s.data(), delta, Ms); + CU_CHECK_AND_SYNC(); + }//end Field_DipoleDipole + + void Hamiltonian_Micromagnetic::Gradient_DDI_Direct(const vectorfield & spins, vectorfield & gradient) + { + int tupel1[3]; + int tupel2[3]; + int sublattice_size = it_bounds_write_dipole[0] * it_bounds_write_dipole[1] * it_bounds_write_dipole[2]; + //prefactor of ddi interaction + //scalar mult = 2.0133545*1e-28 * 0.057883817555 * 0.057883817555 / (4 * 3.141592653589793238462643383279502884197169399375105820974 * 1e-30); + scalar mult = 1 / (4 * 3.141592653589793238462643383279502884197169399375105820974); + scalar m0 = (4 * 3.141592653589793238462643383279502884197169399375105820974)*1e-7; + int img_a = boundary_conditions[0] == 0 ? 0 : ddi_n_periodic_images[0]; + int img_b = boundary_conditions[1] == 0 ? 0 : ddi_n_periodic_images[1]; + int img_c = boundary_conditions[2] == 0 ? 0 : ddi_n_periodic_images[2]; + scalar * delta = geometry->cell_size.data(); + for (int idx1 = 0; idx1 < geometry->nos; idx1++) + { + double kk=0; + for (int idx2 = 0; idx2 < geometry->nos; idx2++) + { + int a1 = idx1%(it_bounds_write_spins[1]); + int b1 = ((int)(idx1/it_bounds_write_spins[1]))%(it_bounds_write_spins[2]); + int c1 = (int)idx1/(it_bounds_write_spins[1]*it_bounds_write_spins[2]); + int a2 = idx2%(it_bounds_write_spins[1]); + int b2 = ((int)(idx2/it_bounds_write_spins[1]))%(it_bounds_write_spins[2]); + int c2 = (int)idx2/(it_bounds_write_spins[1]*it_bounds_write_spins[2]); + /*int a_idx = a < n_cells[0] ? a : a - iteration_bounds[0]; + int b_idx = b < n_cells[1] ? b : b - iteration_bounds[1]; + int c_idx = c < n_cells[2] ? c : c - iteration_bounds[2];*/ + int a_idx = a1-a2; + int b_idx = b1-b2; + int c_idx = c1-c2; + if ((a_idx==0) && (b_idx==0) && (c_idx==0)){ + //printf("test\n"); + //continue; + } + //printf("%d %d %d\n", a_idx,b_idx,c_idx); + /*if ((a_idx==20) || (b_idx==20) || (c_idx==1)){ + //printf("test1\n"); + //if (c_idx!=1) + //printf("%d %d %d %d\n", a_idx, b_idx, c_idx, dipole_stride.comp); + continue; + }*/ + //scalar delta[3] = { 3,3,0.3 }; + //int idx = b_inter * dipole_stride.basis + a * dipole_stride.a + b * dipole_stride.b + c * dipole_stride.c; + scalar Dxx = 0, Dxy = 0, Dxz = 0, Dyy = 0, Dyz = 0, Dzz = 0; + //asa + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + double r = sqrt((a_idx + i - 0.5f)*(a_idx + i - 0.5f)*cell_sizes[0]* cell_sizes[0] + (b_idx + j - 0.5f)*(b_idx + j-0.5f)*cell_sizes[1] * cell_sizes[1] + (c_idx + k - 0.5f)*(c_idx + k - 0.5f)*cell_sizes[2] * cell_sizes[2]); + Dxx += mult * pow(-1.0f, i + j + k) * atan(((c_idx + k-0.5f) * (b_idx + j - 0.5f) * cell_sizes[1]*cell_sizes[2]/cell_sizes[0] / r / (a_idx + i - 0.5f))); + //fft_dipole_inputs[idx + 1 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * log(abs(((c_idx + k - 0.5f)* cell_sizes[2] + r)/((c_idx + k - 0.5f)* cell_sizes[2] - r))); + //fft_dipole_inputs[idx + 2 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * log(abs(((b_idx + j - 0.5f)* cell_sizes[1] + r)/((b_idx + j - 0.5f)* cell_sizes[1] - r))); + Dxy -= mult * pow(-1.0f, i + j + k) * log((((c_idx + k - 0.5f)* cell_sizes[2] + r))); + Dxz -= mult * pow(-1.0f, i + j + k) * log((((b_idx + j - 0.5f)* cell_sizes[1] + r))); + + Dyy += mult * pow(-1.0f, i + j + k) * atan(((a_idx + i-0.5f) * (c_idx + k - 0.5f) * cell_sizes[2]*cell_sizes[0]/cell_sizes[1] / r / (b_idx + j - 0.5f))); + //fft_dipole_inputs[idx + 4 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * log(abs(((a_idx + i - 0.5f)* cell_sizes[0] + r)/((a_idx + i - 0.5f)* cell_sizes[0] - r))); + Dyz -= mult * pow(-1.0f, i + j + k) * log((((a_idx + i - 0.5f)* cell_sizes[0] + r))); + Dzz += mult * pow(-1.0f, i + j + k) * atan(((b_idx + j-0.5f) * (a_idx + i - 0.5f) * cell_sizes[0]*cell_sizes[1]/cell_sizes[2] / r / (c_idx + k - 0.5f))); + + } + } + }/* + Dxx=Nii(a_idx*delta[0],b_idx*delta[1],c_idx*delta[2],delta[0],delta[1],delta[2]); + Dxy=Nij(a_idx*delta[0],b_idx*delta[1],c_idx*delta[2],delta[0],delta[1],delta[2]); + Dxz=Nij(a_idx*delta[0],c_idx*delta[2], b_idx*delta[1],delta[0],delta[2],delta[1]); + Dyy=Nii(b_idx*delta[1],a_idx*delta[0],c_idx*delta[2],delta[1],delta[0],delta[2]); + Dyz=Nij(b_idx*delta[1],c_idx*delta[2], b_idx*delta[1],delta[1],delta[2],delta[0]); + Dzz=Nii(c_idx*delta[2],a_idx*delta[0],b_idx*delta[1],delta[2],delta[0],delta[1]);*/ + if (idx1==42){ + if ((a_idx==0) && (b_idx==0) && (c_idx==0)){ + printf("000 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); + } + if ((a_idx==1) && (b_idx==0) && (c_idx==0)){ + printf("100 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); + } + if ((a_idx==0) && (b_idx==1) && (c_idx==0)){ + printf("010 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); + } + if ((a_idx==-1) && (b_idx==1) && (c_idx==0)){ + printf("-110 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); + } + if ((a_idx==1) && (b_idx==1) && (c_idx==0)){ + printf("110 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); + } + if ((a_idx==2) && (b_idx==0) && (c_idx==0)){ + printf("200 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); + } + if ((a_idx==0) && (b_idx==2) && (c_idx==0)){ + printf("020 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); + } + if ((a_idx==2) && (b_idx==2) && (c_idx==0)){ + printf("220 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); + } + if ((a_idx==2) && (b_idx==-2) && (c_idx==0)){ + printf("2-20 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); + } + //printf("x=%f y=%f z=%f\n",spins[idx2][0],spins[idx2][1],spins[idx2][2]); + } + kk+=Dxx; + gradient[idx1][0] -= (Dxx * spins[idx2][0] + Dxy * spins[idx2][1] + Dxz * spins[idx2][2]) * Ms*m0; + gradient[idx1][1] -= (Dxy * spins[idx2][0] + Dyy * spins[idx2][1] + Dyz * spins[idx2][2]) * Ms*m0; + gradient[idx1][2] -= (Dxz * spins[idx2][0] + Dyz * spins[idx2][1] + Dzz * spins[idx2][2]) * Ms*m0; + } + if (idx1==30){ + //printf("x=%f y=%f z=%f\n",spins[idx1][0],spins[idx1][1],spins[idx1][2]); + //printf("kk=%f gx=%f gy=%f gz=%f\n",kk, gradient[idx1][0]/8e5/m0,gradient[idx1][1],gradient[idx1][2]); + + } + + } + } + __global__ void CU_Write_FFT_Spin_Input1(FFT::FFT_real_type* fft_spin_inputs, const Vector3 * spins, int * iteration_bounds, FFT::StrideContainer spin_stride, scalar * mu_s) + { + int nos = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2] * iteration_bounds[3]; + int tupel[4]; + int idx_pad; + for (int idx_orig = blockIdx.x * blockDim.x + threadIdx.x; idx_orig < nos; idx_orig += blockDim.x * gridDim.x) + { + cu_tupel_from_idx(idx_orig, tupel, iteration_bounds, 4); //tupel now is {ib, a, b, c} + idx_pad = tupel[0] * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + tupel[3] * spin_stride.c; + fft_spin_inputs[idx_pad] = spins[idx_orig][0]; + fft_spin_inputs[idx_pad + 1 * spin_stride.comp] = spins[idx_orig][1]; + fft_spin_inputs[idx_pad + 2 * spin_stride.comp] = spins[idx_orig][2]; + //printf("%f %f\n",fft_spin_inputs[idx_pad], fft_spin_inputs[idx_pad+30]); + } + } + + void Hamiltonian_Micromagnetic::FFT_Spins(const vectorfield & spins) + { + CU_Write_FFT_Spin_Input1 << <(geometry->nos + 1023) / 1024, 1024 >> > (fft_plan_spins.real_ptr.data(), spins.data(), it_bounds_write_spins.data(), spin_stride, geometry->mu_s.data()); + CU_CHECK_AND_SYNC(); + FFT::batch_Four_3D(fft_plan_spins); + } + __global__ void CU_Write_FFT_Dipole_Input1(FFT::FFT_real_type* fft_dipole_inputs, int* iteration_bounds, const Vector3* translation_vectors, int n_cell_atoms, Vector3* cell_atom_translations, int* n_cells, int* inter_sublattice_lookup, int* img, FFT::StrideContainer dipole_stride, const Vector3 cell_lengths) + { + int tupel[3]; + int sublattice_size = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2]; + //prefactor of ddi interaction + //scalar mult = 2.0133545*1e-28 * 0.057883817555 * 0.057883817555 / (4 * 3.141592653589793238462643383279502884197169399375105820974 * 1e-30); + //scalar mult = 1 / (4 * 3.141592653589793238462643383279502884197169399375105820974); + scalar mult = 1; + for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < sublattice_size; i += blockDim.x * gridDim.x) + { + cu_tupel_from_idx(i, tupel, iteration_bounds, 3); // tupel now is {a, b, c} + auto& a = tupel[0]; + auto& b = tupel[1]; + auto& c = tupel[2]; + /*if ((a>198)||(b>198)||(c>198)){ + printf("%d %d %d\n", a,b,c); + }*/ + /*int a_idx = a < n_cells[0] ? a : a - iteration_bounds[0]; + int b_idx = b < n_cells[1] ? b : b - iteration_bounds[1]; + int c_idx = c < n_cells[2] ? c : c - iteration_bounds[2];*/ + /*int a_idx = a +1 - (int)iteration_bounds[0]/2; + int b_idx = b +1- (int)iteration_bounds[1]/2; + int c_idx = c +1- (int)iteration_bounds[2]/2;*/ + int a_idx = a < n_cells[0] ? a : a - iteration_bounds[0]; + int b_idx = b < n_cells[1] ? b : b - iteration_bounds[1]; + int c_idx = c < n_cells[2] ? c : c - iteration_bounds[2]; + + int idx = a * dipole_stride.a + b * dipole_stride.b + c * dipole_stride.c; + + for (int i = 0; i < 2; i++) { + for (int j = 0; j < 2; j++) { + for (int k = 0; k < 2; k++) { + double r = sqrt((a_idx + i - 0.5f)*(a_idx + i - 0.5f)*cell_lengths[0]* cell_lengths[0] + (b_idx + j - 0.5f)*(b_idx + j-0.5f)*cell_lengths[1] * cell_lengths[1] + (c_idx + k - 0.5f)*(c_idx + k - 0.5f)*cell_lengths[2] * cell_lengths[2]); + fft_dipole_inputs[idx] += mult * pow(-1.0f, i + j + k) * atan(((c_idx + k-0.5f) * (b_idx + j - 0.5f) * cell_lengths[1]*cell_lengths[2]/cell_lengths[0] / r / (a_idx + i - 0.5f))); + //fft_dipole_inputs[idx + 1 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * log(abs(((c_idx + k - 0.5f)* cell_lengths[2] + r)/((c_idx + k - 0.5f)* cell_lengths[2] - r))); + //fft_dipole_inputs[idx + 2 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * log(abs(((b_idx + j - 0.5f)* cell_lengths[1] + r)/((b_idx + j - 0.5f)* cell_lengths[1] - r))); + fft_dipole_inputs[idx + 1 * dipole_stride.comp] -= mult * pow(-1.0f, i + j + k) * log((((c_idx + k - 0.5f)* cell_lengths[2] + r))); + fft_dipole_inputs[idx + 2 * dipole_stride.comp] -= mult * pow(-1.0f, i + j + k) * log((((b_idx + j - 0.5f)* cell_lengths[1] + r))); + + fft_dipole_inputs[idx + 3 * dipole_stride.comp] += mult * pow(-1.0f, i + j + k) * atan(((a_idx + i-0.5f) * (c_idx + k - 0.5f) * cell_lengths[2]*cell_lengths[0]/cell_lengths[1] / r / (b_idx + j - 0.5f))); + //fft_dipole_inputs[idx + 4 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * log(abs(((a_idx + i - 0.5f)* cell_lengths[0] + r)/((a_idx + i - 0.5f)* cell_lengths[0] - r))); + fft_dipole_inputs[idx + 4 * dipole_stride.comp] -= mult * pow(-1.0f, i + j + k) * log((((a_idx + i - 0.5f)* cell_lengths[0] + r))); + fft_dipole_inputs[idx + 5 * dipole_stride.comp] += mult * pow(-1.0f, i + j + k) * atan(((b_idx + j-0.5f) * (a_idx + i - 0.5f) * cell_lengths[0]*cell_lengths[1]/cell_lengths[2] / r / (c_idx + k - 0.5f))); + + } + } + } + + //if (fft_dipole_inputs[idx]<-0.03) + } + } + + void Hamiltonian_Micromagnetic::FFT_Dipole_Matrices(FFT::FFT_Plan & fft_plan_dipole, int img_a, int img_b, int img_c) + { + auto& fft_dipole_inputs = fft_plan_dipole.real_ptr; + + field img = { + img_a, + img_b, + img_c + }; + + // Work around to make bravais vectors and cell_atoms available to GPU as they are currently saves as std::vectors and not fields ... + auto translation_vectors = field(); + auto cell_atom_translations = field(); + + for (int i = 0; i < 3; i++) + translation_vectors.push_back(geometry->lattice_constant * geometry->bravais_vectors[i]); + + for (int i = 0; i < geometry->n_cell_atoms; i++) + cell_atom_translations.push_back(geometry->positions[i]); + + CU_Write_FFT_Dipole_Input1 << <(sublattice_size + 1023) / 1024, 1024 >> > + (fft_dipole_inputs.data(), it_bounds_write_dipole.data(), translation_vectors.data(), + geometry->n_cell_atoms, cell_atom_translations.data(), geometry->n_cells.data(), + inter_sublattice_lookup.data(), img.data(), dipole_stride, cell_sizes + ); + CU_CHECK_AND_SYNC(); + FFT::batch_Four_3D(fft_plan_dipole); + } + void Hamiltonian_Micromagnetic::Prepare_DDI() + { + Clean_DDI(); + + n_cells_padded.resize(3); + n_cells_padded[0] = (geometry->n_cells[0] > 1) ? 2 * geometry->n_cells[0] : 1; + n_cells_padded[1] = (geometry->n_cells[1] > 1) ? 2 * geometry->n_cells[1] : 1; + n_cells_padded[2] = (geometry->n_cells[2] > 1) ? 2 * geometry->n_cells[2] : 1; + sublattice_size = n_cells_padded[0] * n_cells_padded[1] * n_cells_padded[2]; + //printf("111 %d %d %d\n", n_cells_padded[0],n_cells_padded[1],n_cells_padded[2]); + + inter_sublattice_lookup.resize(geometry->n_cell_atoms * geometry->n_cell_atoms); + + //we dont need to transform over length 1 dims + std::vector fft_dims; + for (int i = 2; i >= 0; i--) //notice that reverse order is important! + { + if (n_cells_padded[i] > 1) + fft_dims.push_back(n_cells_padded[i]); + } + + //Count how many distinct inter-lattice contributions we need to store + n_inter_sublattice = 0; + for (int i = 0; i < geometry->n_cell_atoms; i++) + { + for (int j = 0; j < geometry->n_cell_atoms; j++) + { + if (i != 0 && i == j) continue; + n_inter_sublattice++; + } + } + printf("lex%d %d %d\n", n_inter_sublattice, fft_dims[0],fft_dims[1]); + //Set the iteration bounds for the nested for loops that are flattened in the kernels + it_bounds_write_spins = { geometry->n_cell_atoms, + geometry->n_cells[0], + geometry->n_cells[1], + geometry->n_cells[2] }; + + it_bounds_write_dipole = { n_cells_padded[0], + n_cells_padded[1], + n_cells_padded[2] }; + + it_bounds_pointwise_mult = { geometry->n_cell_atoms, + (n_cells_padded[0] / 2 + 1), // due to redundancy in real fft + n_cells_padded[1], + n_cells_padded[2] }; + + it_bounds_write_gradients = { geometry->n_cell_atoms, + geometry->n_cells[0], + geometry->n_cells[1], + geometry->n_cells[2] }; + + FFT::FFT_Plan fft_plan_dipole = FFT::FFT_Plan(fft_dims, false, 6 * n_inter_sublattice, sublattice_size); + fft_plan_spins = FFT::FFT_Plan(fft_dims, false, 3 * geometry->n_cell_atoms, sublattice_size); + fft_plan_reverse = FFT::FFT_Plan(fft_dims, true, 3 * geometry->n_cell_atoms, sublattice_size); + + field temp_s = { &spin_stride.comp, &spin_stride.basis, &spin_stride.a, &spin_stride.b, &spin_stride.c }; + field temp_d = { &dipole_stride.comp, &dipole_stride.basis, &dipole_stride.a, &dipole_stride.b, &dipole_stride.c };; + FFT::get_strides(temp_s, { 3, this->geometry->n_cell_atoms, n_cells_padded[0], n_cells_padded[1], n_cells_padded[2] }); + FFT::get_strides(temp_d, { 6, n_inter_sublattice, n_cells_padded[0], n_cells_padded[1], n_cells_padded[2] }); + /* + //perform FFT of dipole matrices + int img_a = boundary_conditions[0] == 0 ? 0 : ddi_n_periodic_images[0]; + int img_b = boundary_conditions[1] == 0 ? 0 : ddi_n_periodic_images[1]; + int img_c = boundary_conditions[2] == 0 ? 0 : ddi_n_periodic_images[2]; + + FFT_Dipole_Matrices(fft_plan_dipole, img_a, img_b, img_c); */ + FFT_Dipole_Matrices(fft_plan_dipole, 0, 0, 0); + + transformed_dipole_matrices = std::move(fft_plan_dipole.cpx_ptr); + }//end prepare + + void Hamiltonian_Micromagnetic::Clean_DDI() + { + fft_plan_spins = FFT::FFT_Plan(); + fft_plan_reverse = FFT::FFT_Plan(); + } + void Hamiltonian_Micromagnetic::Hessian(const vectorfield & spins, MatrixX & hessian) { } From d18004419dfd57a59d11f0c1f04773f75b91dc98 Mon Sep 17 00:00:00 2001 From: "G. P. Mueller" Date: Tue, 22 Dec 2020 16:41:31 +0100 Subject: [PATCH 18/45] Core: compilation fixes in `Hamiltonian_Micromagnetic`. --- core/src/engine/Hamiltonian_Micromagnetic.cu | 32 +++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cu b/core/src/engine/Hamiltonian_Micromagnetic.cu index 5fcc67557..e5f4c32f0 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cu +++ b/core/src/engine/Hamiltonian_Micromagnetic.cu @@ -3,13 +3,14 @@ #include #include #include +#include #include #include -#include #include #include -#include "FFT.hpp" + +#include using namespace Data; using namespace Utility; @@ -64,7 +65,7 @@ namespace Engine neigh_tmp.i = 0; neigh_tmp.j = 0; neigh_tmp.idx_shell = 0; - //order x -x y -y z -z xy (-x)(-y) x(-y) (-x)y xz (-x)(-z) x(-z) (-x)z yz (-y)(-z) y(-z) (-y)z results in 9 parts of Hessian + //order x -x y -y z -z xy (-x)(-y) x(-y) (-x)y xz (-x)(-z) x(-z) (-x)z yz (-y)(-z) y(-z) (-y)z results in 9 parts of Hessian neigh_tmp.translations[0] = 1; neigh_tmp.translations[1] = 0; neigh_tmp.translations[2] = 0; @@ -155,10 +156,10 @@ namespace Engine neigh_tmp.translations[2] = 1; neigh.push_back(neigh_tmp); this->spatial_gradient = field(geometry->nos, Matrix3::Zero()); - + // Dipole-dipole this->Prepare_DDI(); - + // Update, which terms still contribute this->Update_Energy_Contributions(); } @@ -641,11 +642,12 @@ namespace Engine // TODO: also parallelize over i_b1 // Loop over basis atoms (i.e sublattices) and add contribution of each sublattice for (int i_b1 = 0; i_b1 < geometry->n_cell_atoms; ++i_b1) - CU_FFT_Pointwise_Mult1 << <(number_of_mults + 1023) / 1024, 1024 >> > (ft_D_matrices.data(), ft_spins.data(), res_mult.data(), it_bounds_pointwise_mult.data(), i_b1, inter_sublattice_lookup.data(), dipole_stride, spin_stride, Ms); + CU_FFT_Pointwise_Mult1<<<(number_of_mults+1023)/1024, 1024>>>(ft_D_matrices.data(), ft_spins.data(), res_mult.data(), it_bounds_pointwise_mult.data(), i_b1, inter_sublattice_lookup.data(), dipole_stride, spin_stride, Ms); CU_CHECK_AND_SYNC(); FFT::batch_iFour_3D(fft_plan_reverse); - scalar * delta = geometry->cell_size.data(); - CU_Write_FFT_Gradients1 << <(geometry->nos + 1023) / 1024, 1024 >> > (res_iFFT.data(), gradient.data(), spin_stride, it_bounds_write_gradients.data(), geometry->n_cell_atoms, geometry->mu_s.data(), delta, Ms); + // scalar * delta = geometry->cell_size.data(); + int sublattice_size = it_bounds_write_dipole[0] * it_bounds_write_dipole[1] * it_bounds_write_dipole[2]; + CU_Write_FFT_Gradients1<<<(geometry->nos+1023)/1024, 1024>>>(res_iFFT.data(), gradient.data(), spin_stride, it_bounds_write_gradients.data(), geometry->n_cell_atoms, geometry->mu_s.data(), sublattice_size, Ms); CU_CHECK_AND_SYNC(); }//end Field_DipoleDipole @@ -693,6 +695,10 @@ namespace Engine //scalar delta[3] = { 3,3,0.3 }; //int idx = b_inter * dipole_stride.basis + a * dipole_stride.a + b * dipole_stride.b + c * dipole_stride.c; scalar Dxx = 0, Dxy = 0, Dxz = 0, Dyy = 0, Dyz = 0, Dzz = 0; + + Vector3 cell_sizes = {geometry->lattice_constant * geometry->bravais_vectors[0].norm(), + geometry->lattice_constant * geometry->bravais_vectors[1].norm(), + geometry->lattice_constant * geometry->bravais_vectors[2].norm()}; //asa for (int i = 0; i < 2; i++) { for (int j = 0; j < 2; j++) { @@ -855,6 +861,10 @@ namespace Engine for (int i = 0; i < geometry->n_cell_atoms; i++) cell_atom_translations.push_back(geometry->positions[i]); + Vector3 cell_sizes = {geometry->lattice_constant * geometry->bravais_vectors[0].norm(), + geometry->lattice_constant * geometry->bravais_vectors[1].norm(), + geometry->lattice_constant * geometry->bravais_vectors[2].norm()}; + CU_Write_FFT_Dipole_Input1 << <(sublattice_size + 1023) / 1024, 1024 >> > (fft_dipole_inputs.data(), it_bounds_write_dipole.data(), translation_vectors.data(), geometry->n_cell_atoms, cell_atom_translations.data(), geometry->n_cells.data(), @@ -928,19 +938,19 @@ namespace Engine int img_a = boundary_conditions[0] == 0 ? 0 : ddi_n_periodic_images[0]; int img_b = boundary_conditions[1] == 0 ? 0 : ddi_n_periodic_images[1]; int img_c = boundary_conditions[2] == 0 ? 0 : ddi_n_periodic_images[2]; - + FFT_Dipole_Matrices(fft_plan_dipole, img_a, img_b, img_c); */ FFT_Dipole_Matrices(fft_plan_dipole, 0, 0, 0); transformed_dipole_matrices = std::move(fft_plan_dipole.cpx_ptr); }//end prepare - + void Hamiltonian_Micromagnetic::Clean_DDI() { fft_plan_spins = FFT::FFT_Plan(); fft_plan_reverse = FFT::FFT_Plan(); } - + void Hamiltonian_Micromagnetic::Hessian(const vectorfield & spins, MatrixX & hessian) { } From e9ccd34fbc9e41c6b6bd13586769de575d2d14cb Mon Sep 17 00:00:00 2001 From: "G. P. Mueller" Date: Wed, 23 Dec 2020 14:46:19 +0100 Subject: [PATCH 19/45] Core: applied clang-format to Hamiltonian_Micromagnetic files. --- .../engine/Hamiltonian_Micromagnetic.hpp | 285 ++- core/src/engine/Hamiltonian_Micromagnetic.cpp | 1081 +++++----- core/src/engine/Hamiltonian_Micromagnetic.cu | 1898 +++++++++-------- 3 files changed, 1726 insertions(+), 1538 deletions(-) diff --git a/core/include/engine/Hamiltonian_Micromagnetic.hpp b/core/include/engine/Hamiltonian_Micromagnetic.hpp index 61692a751..43e4d39a5 100644 --- a/core/include/engine/Hamiltonian_Micromagnetic.hpp +++ b/core/include/engine/Hamiltonian_Micromagnetic.hpp @@ -1,152 +1,149 @@ #pragma once -#ifndef HAMILTONIAN_MICROMAGNETIC_H -#define HAMILTONIAN_MICROMAGNETIC_H - -#include -#include +#ifndef SPIRIT_CORE_HAMILTONIAN_MICROMAGNETIC_HPP +#define SPIRIT_CORE_HAMILTONIAN_MICROMAGNETIC_HPP #include "Spirit_Defines.h" -#include -#include -#include #include +#include +#include +#include + +#include +#include namespace Engine { - /* - The Micromagnetic Hamiltonian - */ - class Hamiltonian_Micromagnetic : public Hamiltonian - { - public: - - Hamiltonian_Micromagnetic( - scalar Ms, - scalar external_field_magnitude, Vector3 external_field_normal, - Matrix3 anisotropy_tensor, - Matrix3 exchange_tensor, - Matrix3 dmi_tensor, - DDI_Method ddi_method, intfield ddi_n_periodic_images, scalar ddi_radius, - std::shared_ptr geometry, - int spatial_gradient_order, - intfield boundary_conditions - ); - - void Update_Interactions(); - - void Update_Energy_Contributions() override; - - void Hessian(const vectorfield & spins, MatrixX & hessian) override; - void Gradient(const vectorfield & spins, vectorfield & gradient) override; - void Energy_Contributions_per_Spin(const vectorfield & spins, std::vector> & contributions) override; - void Energy_Update(const vectorfield & spins, std::vector> & contributions, vectorfield & gradient); - // Calculate the total energy for a single spin to be used in Monte Carlo. - // Note: therefore the energy of pairs is weighted x2 and of quadruplets x4. - scalar Energy_Single_Spin(int ispin, const vectorfield & spins) override; - - // Hamiltonian name as string - const std::string& Name() override; - - std::shared_ptr geometry; - - // ------------ ... ------------ - int spatial_gradient_order; - - // ------------ Single Spin Interactions ------------ - // External magnetic field across the sample - scalar external_field_magnitude; - Vector3 external_field_normal; - Matrix3 anisotropy_tensor; - scalar Ms; - - // ------------ Pair Interactions ------------ - // Exchange interaction - Matrix3 exchange_tensor; - // DMI - Matrix3 dmi_tensor; - pairfield neigh; - field spatial_gradient; - bool A_is_nondiagonal=true; - // Dipole-dipole interaction - DDI_Method ddi_method; - intfield ddi_n_periodic_images; - scalar ddi_cutoff_radius; - pairfield ddi_pairs; - scalarfield ddi_magnitudes; - vectorfield ddi_normals; - - private: - // ------------ Effective Field Functions ------------ - // Calculate the Zeeman effective field of a single Spin - void Gradient_Zeeman(vectorfield & gradient); - // Calculate the Anisotropy effective field of a single Spin - void Gradient_Anisotropy(const vectorfield & spins, vectorfield & gradient); - // Calculate the exchange interaction effective field of a Spin Pair - void Gradient_Exchange(const vectorfield & spins, vectorfield & gradient); - // Calculate the DMI effective field of a Spin Pair - void Gradient_DMI(const vectorfield & spins, vectorfield & gradient); - void Spatial_Gradient(const vectorfield & spins); - // Calculates the Dipole-Dipole contribution to the effective field of spin ispin within system s - void Gradient_DDI(const vectorfield& spins, vectorfield & gradient); - void Gradient_DDI_Cutoff(const vectorfield& spins, vectorfield & gradient); - void Gradient_DDI_Direct(const vectorfield& spins, vectorfield & gradient); - void Gradient_DDI_FFT(const vectorfield& spins, vectorfield & gradient); - - // ------------ Energy Functions ------------ - // Indices for Energy vector - int idx_zeeman, idx_anisotropy, idx_exchange, idx_dmi, idx_ddi; - void E_Update(const vectorfield & spins, scalarfield & Energy, vectorfield & gradient); - // Calculate the Zeeman energy of a Spin System - void E_Zeeman(const vectorfield & spins, scalarfield & Energy); - // Calculate the Anisotropy energy of a Spin System - void E_Anisotropy(const vectorfield & spins, scalarfield & Energy); - // Calculate the exchange interaction energy of a Spin System - void E_Exchange(const vectorfield & spins, scalarfield & Energy); - // Calculate the DMI energy of a Spin System - void E_DMI(const vectorfield & spins, scalarfield & Energy); - // Dipolar interactions - void E_DDI(const vectorfield & spins, scalarfield & Energy); - void E_DDI_Direct(const vectorfield& spins, scalarfield & Energy); - void E_DDI_Cutoff(const vectorfield& spins, scalarfield & Energy); - void E_DDI_FFT(const vectorfield& spins, scalarfield & Energy); - - // Preparations for DDI-Convolution Algorithm - void Prepare_DDI(); - void Clean_DDI(); - - // Plans for FT / rFT - FFT::FFT_Plan fft_plan_spins; - FFT::FFT_Plan fft_plan_reverse; - - field transformed_dipole_matrices; - bool save_dipole_matrices = true; - field dipole_matrices; - - // Number of inter-sublattice contributions - int n_inter_sublattice; - // At which index to look up the inter-sublattice D-matrices - field inter_sublattice_lookup; - - // Lengths of padded system - field n_cells_padded; - // Total number of padded spins per sublattice - int sublattice_size; - - FFT::StrideContainer spin_stride; - FFT::StrideContainer dipole_stride; - - //Calculate the FT of the padded D matriess - void FFT_Dipole_Matrices(FFT::FFT_Plan & fft_plan_dipole, int img_a, int img_b, int img_c); - //Calculate the FT of the padded spins - void FFT_Spins(const vectorfield & spins); - - //Bounds for nested for loops. Only important for the CUDA version - field it_bounds_pointwise_mult; - field it_bounds_write_gradients; - field it_bounds_write_spins; - field it_bounds_write_dipole; - }; - - -} + +/* + The Micromagnetic Hamiltonian +*/ +class Hamiltonian_Micromagnetic : public Hamiltonian +{ +public: + Hamiltonian_Micromagnetic( + scalar Ms, scalar external_field_magnitude, Vector3 external_field_normal, Matrix3 anisotropy_tensor, + Matrix3 exchange_tensor, Matrix3 dmi_tensor, DDI_Method ddi_method, intfield ddi_n_periodic_images, + scalar ddi_radius, std::shared_ptr geometry, int spatial_gradient_order, + intfield boundary_conditions ); + + void Update_Interactions(); + + void Update_Energy_Contributions() override; + + void Hessian( const vectorfield & spins, MatrixX & hessian ) override; + void Gradient( const vectorfield & spins, vectorfield & gradient ) override; + void Energy_Contributions_per_Spin( + const vectorfield & spins, std::vector> & contributions ) override; + void Energy_Update( + const vectorfield & spins, std::vector> & contributions, + vectorfield & gradient ); + // Calculate the total energy for a single spin to be used in Monte Carlo. + // Note: therefore the energy of pairs is weighted x2 and of quadruplets x4. + scalar Energy_Single_Spin( int ispin, const vectorfield & spins ) override; + + // Hamiltonian name as string + const std::string & Name() override; + + std::shared_ptr geometry; + + // ------------ ... ------------ + int spatial_gradient_order; + + // ------------ Single Spin Interactions ------------ + // External magnetic field across the sample + scalar external_field_magnitude; + Vector3 external_field_normal; + Matrix3 anisotropy_tensor; + scalar Ms; + + // ------------ Pair Interactions ------------ + // Exchange interaction + Matrix3 exchange_tensor; + // DMI + Matrix3 dmi_tensor; + pairfield neigh; + field spatial_gradient; + bool A_is_nondiagonal = true; + // Dipole-dipole interaction + DDI_Method ddi_method; + intfield ddi_n_periodic_images; + scalar ddi_cutoff_radius; + pairfield ddi_pairs; + scalarfield ddi_magnitudes; + vectorfield ddi_normals; + +private: + // ------------ Effective Field Functions ------------ + // Calculate the Zeeman effective field of a single Spin + void Gradient_Zeeman( vectorfield & gradient ); + // Calculate the Anisotropy effective field of a single Spin + void Gradient_Anisotropy( const vectorfield & spins, vectorfield & gradient ); + // Calculate the exchange interaction effective field of a Spin Pair + void Gradient_Exchange( const vectorfield & spins, vectorfield & gradient ); + // Calculate the DMI effective field of a Spin Pair + void Gradient_DMI( const vectorfield & spins, vectorfield & gradient ); + void Spatial_Gradient( const vectorfield & spins ); + // Calculates the Dipole-Dipole contribution to the effective field of spin ispin within system s + void Gradient_DDI( const vectorfield & spins, vectorfield & gradient ); + void Gradient_DDI_Cutoff( const vectorfield & spins, vectorfield & gradient ); + void Gradient_DDI_Direct( const vectorfield & spins, vectorfield & gradient ); + void Gradient_DDI_FFT( const vectorfield & spins, vectorfield & gradient ); + + // ------------ Energy Functions ------------ + // Indices for Energy vector + int idx_zeeman, idx_anisotropy, idx_exchange, idx_dmi, idx_ddi; + void E_Update( const vectorfield & spins, scalarfield & Energy, vectorfield & gradient ); + // Calculate the Zeeman energy of a Spin System + void E_Zeeman( const vectorfield & spins, scalarfield & Energy ); + // Calculate the Anisotropy energy of a Spin System + void E_Anisotropy( const vectorfield & spins, scalarfield & Energy ); + // Calculate the exchange interaction energy of a Spin System + void E_Exchange( const vectorfield & spins, scalarfield & Energy ); + // Calculate the DMI energy of a Spin System + void E_DMI( const vectorfield & spins, scalarfield & Energy ); + // Dipolar interactions + void E_DDI( const vectorfield & spins, scalarfield & Energy ); + void E_DDI_Direct( const vectorfield & spins, scalarfield & Energy ); + void E_DDI_Cutoff( const vectorfield & spins, scalarfield & Energy ); + void E_DDI_FFT( const vectorfield & spins, scalarfield & Energy ); + + // Preparations for DDI-Convolution Algorithm + void Prepare_DDI(); + void Clean_DDI(); + + // Plans for FT / rFT + FFT::FFT_Plan fft_plan_spins; + FFT::FFT_Plan fft_plan_reverse; + + field transformed_dipole_matrices; + bool save_dipole_matrices = true; + field dipole_matrices; + + // Number of inter-sublattice contributions + int n_inter_sublattice; + // At which index to look up the inter-sublattice D-matrices + field inter_sublattice_lookup; + + // Lengths of padded system + field n_cells_padded; + // Total number of padded spins per sublattice + int sublattice_size; + + FFT::StrideContainer spin_stride; + FFT::StrideContainer dipole_stride; + + // Calculate the FT of the padded D matriess + void FFT_Dipole_Matrices( FFT::FFT_Plan & fft_plan_dipole, int img_a, int img_b, int img_c ); + // Calculate the FT of the padded spins + void FFT_Spins( const vectorfield & spins ); + + // Bounds for nested for loops. Only important for the CUDA version + field it_bounds_pointwise_mult; + field it_bounds_write_gradients; + field it_bounds_write_spins; + field it_bounds_write_dipole; +}; + +} // namespace Engine + #endif \ No newline at end of file diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cpp b/core/src/engine/Hamiltonian_Micromagnetic.cpp index 7cb5c88de..bd86822fe 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cpp +++ b/core/src/engine/Hamiltonian_Micromagnetic.cpp @@ -1,17 +1,16 @@ #ifndef SPIRIT_USE_CUDA +#include #include -#include #include -#include +#include #include -#include -#include - -#include #include +#include +#include +#include using namespace Data; using namespace Utility; @@ -20,570 +19,646 @@ using Engine::Vectormath::check_atom_type; using Engine::Vectormath::idx_from_pair; using Engine::Vectormath::idx_from_tupel; - namespace Engine { - Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( - scalar Ms, - scalar external_field_magnitude, Vector3 external_field_normal, - Matrix3 anisotropy_tensor, - Matrix3 exchange_tensor, - Matrix3 dmi_tensor, - DDI_Method ddi_method, intfield ddi_n_periodic_images, scalar ddi_radius, - std::shared_ptr geometry, - int spatial_gradient_order, - intfield boundary_conditions - ) : Hamiltonian(boundary_conditions), Ms(Ms), spatial_gradient_order(spatial_gradient_order), geometry(geometry), - external_field_magnitude(external_field_magnitude), external_field_normal(external_field_normal), - anisotropy_tensor(anisotropy_tensor), exchange_tensor(exchange_tensor), dmi_tensor(dmi_tensor), - ddi_method(ddi_method), ddi_n_periodic_images(ddi_n_periodic_images), ddi_cutoff_radius(ddi_radius), - fft_plan_reverse(FFT::FFT_Plan()), fft_plan_spins(FFT::FFT_Plan()) + +Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( + scalar Ms, scalar external_field_magnitude, Vector3 external_field_normal, Matrix3 anisotropy_tensor, + Matrix3 exchange_tensor, Matrix3 dmi_tensor, DDI_Method ddi_method, intfield ddi_n_periodic_images, + scalar ddi_radius, std::shared_ptr geometry, int spatial_gradient_order, + intfield boundary_conditions ) + : Hamiltonian( boundary_conditions ), + Ms( Ms ), + spatial_gradient_order( spatial_gradient_order ), + geometry( geometry ), + external_field_magnitude( external_field_magnitude ), + external_field_normal( external_field_normal ), + anisotropy_tensor( anisotropy_tensor ), + exchange_tensor( exchange_tensor ), + dmi_tensor( dmi_tensor ), + ddi_method( ddi_method ), + ddi_n_periodic_images( ddi_n_periodic_images ), + ddi_cutoff_radius( ddi_radius ), + fft_plan_reverse( FFT::FFT_Plan() ), + fft_plan_spins( FFT::FFT_Plan() ) +{ + // Generate interaction pairs, constants etc. + this->Update_Interactions(); +} + +void Hamiltonian_Micromagnetic::Update_Interactions() +{ +#if defined( SPIRIT_USE_OPENMP ) + // When parallelising (cuda or openmp), we need all neighbours per spin + const bool use_redundant_neighbours = true; +#else + // When running on a single thread, we can ignore redundant neighbours + const bool use_redundant_neighbours = false; +#endif + + // TODO: make sure that the geometry can be treated with this model: + // - rectilinear, only one "atom" per cell + // if( geometry->n_cell_atoms != 1 ) + // Log(...) + + // TODO: generate neighbour information for pairwise interactions + + // TODO: prepare dipolar interactions + + // Update, which terms still contribute + + neigh = pairfield( 0 ); + Neighbour neigh_tmp; + neigh_tmp.i = 0; + neigh_tmp.j = 0; + neigh_tmp.idx_shell = 0; + // order x -x y -y z -z xy (-x)(-y) x(-y) (-x)y xz (-x)(-z) x(-z) (-x)z yz (-y)(-z) y(-z) (-y)z results in 9 parts of Hessian + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = +1; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = -1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = -1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 1; + neigh.push_back( neigh_tmp ); + + this->spatial_gradient = field( geometry->nos, Matrix3::Zero() ); + this->Update_Energy_Contributions(); +} + +void Hamiltonian_Micromagnetic::Update_Energy_Contributions() +{ + this->energy_contributions_per_spin = std::vector>( 0 ); + + // External field + if( this->external_field_magnitude > 0 ) { - // Generate interaction pairs, constants etc. - this->Update_Interactions(); + this->energy_contributions_per_spin.push_back( { "Zeeman", scalarfield( 0 ) } ); + this->idx_zeeman = this->energy_contributions_per_spin.size() - 1; } + else + this->idx_zeeman = -1; + // TODO: Anisotropy + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"Anisotropy", scalarfield(0) }); + // this->idx_anisotropy = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_anisotropy = -1; + // TODO: Exchange + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"Exchange", scalarfield(0) }); + // this->idx_exchange = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_exchange = -1; + // TODO: DMI + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"DMI", scalarfield(0) }); + // this->idx_dmi = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_dmi = -1; + // TODO: DDI + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"DDI", scalarfield(0) }); + // this->idx_ddi = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_ddi = -1; +} - void Hamiltonian_Micromagnetic::Update_Interactions() +void Hamiltonian_Micromagnetic::Energy_Contributions_per_Spin( + const vectorfield & spins, std::vector> & contributions ) +{ + if( contributions.size() != this->energy_contributions_per_spin.size() ) { - #if defined(SPIRIT_USE_OPENMP) - // When parallelising (cuda or openmp), we need all neighbours per spin - const bool use_redundant_neighbours = true; - #else - // When running on a single thread, we can ignore redundant neighbours - const bool use_redundant_neighbours = false; - #endif - - // TODO: make sure that the geometry can be treated with this model: - // - rectilinear, only one "atom" per cell - // if( geometry->n_cell_atoms != 1 ) - // Log(...) - - // TODO: generate neighbour information for pairwise interactions - - // TODO: prepare dipolar interactions - - // Update, which terms still contribute - - neigh = pairfield(0); - Neighbour neigh_tmp; - neigh_tmp.i = 0; - neigh_tmp.j = 0; - neigh_tmp.idx_shell = 0; - //order x -x y -y z -z xy (-x)(-y) x(-y) (-x)y xz (-x)(-z) x(-z) (-x)z yz (-y)(-z) y(-z) (-y)z results in 9 parts of Hessian - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = 1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = +1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 1; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 1; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - - this->spatial_gradient = field(geometry->nos, Matrix3::Zero()); - this->Update_Energy_Contributions(); + contributions = this->energy_contributions_per_spin; } - void Hamiltonian_Micromagnetic::Update_Energy_Contributions() + int nos = spins.size(); + for( auto & contrib : contributions ) { - this->energy_contributions_per_spin = std::vector>(0); - - // External field - if( this->external_field_magnitude > 0 ) - { - this->energy_contributions_per_spin.push_back({"Zeeman", scalarfield(0)}); - this->idx_zeeman = this->energy_contributions_per_spin.size()-1; - } + // Allocate if not already allocated + if( contrib.second.size() != nos ) + contrib.second = scalarfield( nos, 0 ); + // Otherwise set to zero else - this->idx_zeeman = -1; - // TODO: Anisotropy - // if( ... ) - // { - // this->energy_contributions_per_spin.push_back({"Anisotropy", scalarfield(0) }); - // this->idx_anisotropy = this->energy_contributions_per_spin.size()-1; - // } - // else - this->idx_anisotropy = -1; - // TODO: Exchange - // if( ... ) - // { - // this->energy_contributions_per_spin.push_back({"Exchange", scalarfield(0) }); - // this->idx_exchange = this->energy_contributions_per_spin.size()-1; - // } - // else - this->idx_exchange = -1; - // TODO: DMI - // if( ... ) - // { - // this->energy_contributions_per_spin.push_back({"DMI", scalarfield(0) }); - // this->idx_dmi = this->energy_contributions_per_spin.size()-1; - // } - // else - this->idx_dmi = -1; - // TODO: DDI - // if( ... ) - // { - // this->energy_contributions_per_spin.push_back({"DDI", scalarfield(0) }); - // this->idx_ddi = this->energy_contributions_per_spin.size()-1; - // } - // else - this->idx_ddi = -1; + Vectormath::fill( contrib.second, 0 ); } - void Hamiltonian_Micromagnetic::Energy_Contributions_per_Spin(const vectorfield & spins, std::vector> & contributions) - { - if( contributions.size() != this->energy_contributions_per_spin.size() ) - { - contributions = this->energy_contributions_per_spin; - } + // External field + if( this->idx_zeeman >= 0 ) + E_Zeeman( spins, contributions[idx_zeeman].second ); - int nos = spins.size(); - for( auto& contrib : contributions ) - { - // Allocate if not already allocated - if (contrib.second.size() != nos) contrib.second = scalarfield(nos, 0); - // Otherwise set to zero - else Vectormath::fill(contrib.second, 0); - } + // Anisotropy + if( this->idx_anisotropy >= 0 ) + E_Anisotropy( spins, contributions[idx_anisotropy].second ); - // External field - if( this->idx_zeeman >=0 ) E_Zeeman(spins, contributions[idx_zeeman].second); + // Exchange + if( this->idx_exchange >= 0 ) + E_Exchange( spins, contributions[idx_exchange].second ); - // Anisotropy - if( this->idx_anisotropy >=0 ) E_Anisotropy(spins, contributions[idx_anisotropy].second); + // DMI + if( this->idx_dmi >= 0 ) + E_DMI( spins, contributions[idx_dmi].second ); +} - // Exchange - if( this->idx_exchange >=0 ) E_Exchange(spins, contributions[idx_exchange].second); +void Hamiltonian_Micromagnetic::E_Zeeman( const vectorfield & spins, scalarfield & Energy ) +{ + auto & mu_s = this->geometry->mu_s; - // DMI - if( this->idx_dmi >=0 ) E_DMI(spins,contributions[idx_dmi].second); +#pragma omp parallel for + for( int icell = 0; icell < geometry->n_cells_total; ++icell ) + { + if( check_atom_type( this->geometry->atom_types[icell] ) ) + Energy[icell] + -= mu_s[icell] * this->external_field_magnitude * this->external_field_normal.dot( spins[icell] ); } +} - void Hamiltonian_Micromagnetic::E_Zeeman(const vectorfield & spins, scalarfield & Energy) +void Hamiltonian_Micromagnetic::E_Update( const vectorfield & spins, scalarfield & Energy, vectorfield & gradient ) +{ +#pragma omp parallel for + for( int icell = 0; icell < geometry->n_cells_total; ++icell ) { - auto& mu_s = this->geometry->mu_s; - - #pragma omp parallel for - for( int icell = 0; icell < geometry->n_cells_total; ++icell ) - { - if( check_atom_type(this->geometry->atom_types[icell]) ) - Energy[icell] -= mu_s[icell] * this->external_field_magnitude * this->external_field_normal.dot(spins[icell]); - } + Energy[icell] -= 0.5 * Ms * gradient[icell].dot( spins[icell] ); } +} - void Hamiltonian_Micromagnetic::E_Update(const vectorfield & spins, scalarfield & Energy, vectorfield & gradient) { - #pragma omp parallel for - for( int icell = 0; icell < geometry->n_cells_total; ++icell ) - { - Energy[icell] -= 0.5 * Ms * gradient[icell].dot(spins[icell]); - } - } +void Hamiltonian_Micromagnetic::E_Anisotropy( const vectorfield & spins, scalarfield & Energy ) {} - void Hamiltonian_Micromagnetic::E_Anisotropy(const vectorfield & spins, scalarfield & Energy) - { - } +void Hamiltonian_Micromagnetic::E_Exchange( const vectorfield & spins, scalarfield & Energy ) {} - void Hamiltonian_Micromagnetic::E_Exchange(const vectorfield & spins, scalarfield & Energy) - { - } +void Hamiltonian_Micromagnetic::E_DMI( const vectorfield & spins, scalarfield & Energy ) {} - void Hamiltonian_Micromagnetic::E_DMI(const vectorfield & spins, scalarfield & Energy) - { - } +void Hamiltonian_Micromagnetic::E_DDI( const vectorfield & spins, scalarfield & Energy ) {} - void Hamiltonian_Micromagnetic::E_DDI(const vectorfield & spins, scalarfield & Energy) - { - } +scalar Hamiltonian_Micromagnetic::Energy_Single_Spin( int ispin, const vectorfield & spins ) +{ + scalar Energy = 0; + return Energy; +} +void Hamiltonian_Micromagnetic::Gradient( const vectorfield & spins, vectorfield & gradient ) +{ + // Set to zero + Vectormath::fill( gradient, { 0, 0, 0 } ); + this->Spatial_Gradient( spins ); - scalar Hamiltonian_Micromagnetic::Energy_Single_Spin(int ispin, const vectorfield & spins) - { - scalar Energy = 0; - return Energy; - } + // External field + this->Gradient_Zeeman( gradient ); + // Anisotropy + this->Gradient_Anisotropy( spins, gradient ); - void Hamiltonian_Micromagnetic::Gradient(const vectorfield & spins, vectorfield & gradient) - { - // Set to zero - Vectormath::fill(gradient, {0,0,0}); - this->Spatial_Gradient(spins); + // Exchange + this->Gradient_Exchange( spins, gradient ); - // External field - this->Gradient_Zeeman(gradient); + // DMI + this->Gradient_DMI( spins, gradient ); - // Anisotropy - this->Gradient_Anisotropy(spins, gradient); + // double energy=0; + // #pragma omp parallel for reduction(-:energy) + // for( int icell = 0; icell < geometry->n_cells_total; ++icell ) + // { + // energy -= 0.5 * Ms * gradient[icell].dot(spins[icell]); + // } + // printf("Energy total: %f\n", energy/ geometry->n_cells_total); +} - // Exchange - this->Gradient_Exchange(spins, gradient); +void Hamiltonian_Micromagnetic::Gradient_Zeeman( vectorfield & gradient ) +{ + // In this context this is magnetisation per cell + auto & mu_s = this->geometry->mu_s; - // DMI - this->Gradient_DMI(spins, gradient); +#pragma omp parallel for + for( int icell = 0; icell < geometry->n_cells_total; ++icell ) + { + if( check_atom_type( this->geometry->atom_types[icell] ) ) + gradient[icell] -= mu_s[icell] * C::mu_B * this->external_field_magnitude * this->external_field_normal; + } +} - // double energy=0; - // #pragma omp parallel for reduction(-:energy) - // for( int icell = 0; icell < geometry->n_cells_total; ++icell ) +void Hamiltonian_Micromagnetic::Gradient_Anisotropy( const vectorfield & spins, vectorfield & gradient ) +{ + Vector3 temp1{ 1, 0, 0 }; + Vector3 temp2{ 0, 1, 0 }; + Vector3 temp3{ 0, 0, 1 }; +#pragma omp parallel for + for( int icell = 0; icell < geometry->n_cells_total; ++icell ) + { + // for( int iani = 0; iani < 1; ++iani ) // { - // energy -= 0.5 * Ms * gradient[icell].dot(spins[icell]); + // gradient[icell] -= 2.0 * 8.44e6 / Ms * temp3 * temp3.dot(spins[icell]); + gradient[icell] -= 2.0 * C::mu_B * anisotropy_tensor * spins[icell] / Ms; + // gradient[icell] -= 2.0 * this->anisotropy_magnitudes[iani] / Ms * ((pow(temp2.dot(spins[icell]),2)+ + // pow(temp3.dot(spins[icell]), 2))*(temp1.dot(spins[icell])*temp1)+ (pow(temp1.dot(spins[icell]), 2) + + // pow(temp3.dot(spins[icell]), 2))*(temp2.dot(spins[icell])*temp2)+(pow(temp1.dot(spins[icell]),2)+ + // pow(temp2.dot(spins[icell]), 2))*(temp3.dot(spins[icell])*temp3)); gradient[icell] += 2.0 * 50000 / Ms * + // ((pow(temp2.dot(spins[icell]), 2) + pow(temp3.dot(spins[icell]), 2))*(temp1.dot(spins[icell])*temp1) + + // (pow(temp1.dot(spins[icell]), 2) + pow(temp3.dot(spins[icell]), 2))*(temp2.dot(spins[icell])*temp2)); // } - // printf("Energy total: %f\n", energy/ geometry->n_cells_total); } +} - - void Hamiltonian_Micromagnetic::Gradient_Zeeman(vectorfield & gradient) +void Hamiltonian_Micromagnetic::Gradient_Exchange( const vectorfield & spins, vectorfield & gradient ) +{ + auto & delta = geometry->cell_size; +// scalar delta[3] = { 3e-10, 3e-10, 3e-9 }; +// scalar delta[3] = { 277e-12, 277e-12, 277e-12 }; + +// nongradient implementation +/* +#pragma omp parallel for +for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell) +{ + int ispin = icell;//basically id of a cell + for (unsigned int i = 0; i < 3; ++i) { - // In this context this is magnetisation per cell - auto& mu_s = this->geometry->mu_s; - #pragma omp parallel for - for( int icell = 0; icell < geometry->n_cells_total; ++icell ) - { - if( check_atom_type(this->geometry->atom_types[icell]) ) - gradient[icell] -= mu_s[icell]*C::mu_B * this->external_field_magnitude * this->external_field_normal; + int ispin_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, +geometry->atom_types, neigh[i]); int ispin_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, +geometry->n_cell_atoms, geometry->atom_types, neigh[i + 1]); if (ispin_plus == -1) { ispin_plus = ispin; } - } + if (ispin_minus == -1) { + ispin_minus = ispin; + } + gradient[ispin][i] -= exchange_tensor(i, i)*(spins[ispin_plus][i] - 2 * spins[ispin][i] + spins[ispin_minus][i]) +/ (delta[i]) / (delta[i]); - void Hamiltonian_Micromagnetic::Gradient_Anisotropy(const vectorfield & spins, vectorfield & gradient) - { - Vector3 temp1{ 1,0,0 }; - Vector3 temp2{ 0,1,0 }; - Vector3 temp3{ 0,0,1 }; - #pragma omp parallel for - for( int icell = 0; icell < geometry->n_cells_total; ++icell ) - { - // for( int iani = 0; iani < 1; ++iani ) - // { - // gradient[icell] -= 2.0 * 8.44e6 / Ms * temp3 * temp3.dot(spins[icell]); - gradient[icell] -= 2.0 * C::mu_B * anisotropy_tensor * spins[icell] / Ms; - //gradient[icell] -= 2.0 * this->anisotropy_magnitudes[iani] / Ms * ((pow(temp2.dot(spins[icell]),2)+ pow(temp3.dot(spins[icell]), 2))*(temp1.dot(spins[icell])*temp1)+ (pow(temp1.dot(spins[icell]), 2) + pow(temp3.dot(spins[icell]), 2))*(temp2.dot(spins[icell])*temp2)+(pow(temp1.dot(spins[icell]),2)+ pow(temp2.dot(spins[icell]), 2))*(temp3.dot(spins[icell])*temp3)); - //gradient[icell] += 2.0 * 50000 / Ms * ((pow(temp2.dot(spins[icell]), 2) + pow(temp3.dot(spins[icell]), 2))*(temp1.dot(spins[icell])*temp1) + (pow(temp1.dot(spins[icell]), 2) + pow(temp3.dot(spins[icell]), 2))*(temp2.dot(spins[icell])*temp2)); - // } + } + if (A_is_nondiagonal == true) { + int ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, +geometry->atom_types, neigh[6]); int ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, +geometry->n_cell_atoms, geometry->atom_types, neigh[7]); int ispin_plus_minus = idx_from_pair(ispin, +boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[8]); int ispin_minus_plus = +idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[9]); + + if (ispin_plus_plus == -1) { + ispin_plus_plus = ispin; + } + if (ispin_minus_minus == -1) { + ispin_minus_minus = ispin; + } + if (ispin_plus_minus == -1) { + ispin_plus_minus = ispin; + } + if (ispin_minus_plus == -1) { + ispin_minus_plus = ispin; + } + gradient[ispin][0] -= exchange_tensor(0, 1)*(spins[ispin_plus_plus][1] - spins[ispin_plus_minus][1] - +spins[ispin_minus_plus][1] + spins[ispin_minus_minus][1]) / (delta[0]) / (delta[1]) / 4; gradient[ispin][1] -= +exchange_tensor(1, 0)*(spins[ispin_plus_plus][0] - spins[ispin_plus_minus][0] - spins[ispin_minus_plus][0] + +spins[ispin_minus_minus][0]) / (delta[0]) / (delta[1]) / 4; + + ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, +geometry->atom_types, neigh[10]); ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, +geometry->n_cell_atoms, geometry->atom_types, neigh[11]); ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, +geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[12]); ispin_minus_plus = idx_from_pair(ispin, +boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[13]); + + if (ispin_plus_plus == -1) { + ispin_plus_plus = ispin; + } + if (ispin_minus_minus == -1) { + ispin_minus_minus = ispin; + } + if (ispin_plus_minus == -1) { + ispin_plus_minus = ispin; + } + if (ispin_minus_plus == -1) { + ispin_minus_plus = ispin; } + gradient[ispin][0] -= exchange_tensor(0, 2)*(spins[ispin_plus_plus][2] - spins[ispin_plus_minus][2] - +spins[ispin_minus_plus][2] + spins[ispin_minus_minus][2]) / (delta[0]) / (delta[2]) / 4; gradient[ispin][2] -= +exchange_tensor(2, 0)*(spins[ispin_plus_plus][0] - spins[ispin_plus_minus][0] - spins[ispin_minus_plus][0] + +spins[ispin_minus_minus][0]) / (delta[0]) / (delta[2]) / 4; + + ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, +geometry->atom_types, neigh[14]); ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, +geometry->n_cell_atoms, geometry->atom_types, neigh[15]); ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, +geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[16]); ispin_minus_plus = idx_from_pair(ispin, +boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[17]); + + if (ispin_plus_plus == -1) { + ispin_plus_plus = ispin; + } + if (ispin_minus_minus == -1) { + ispin_minus_minus = ispin; + } + if (ispin_plus_minus == -1) { + ispin_plus_minus = ispin; + } + if (ispin_minus_plus == -1) { + ispin_minus_plus = ispin; + } + gradient[ispin][1] -= exchange_tensor(1, 2)*(spins[ispin_plus_plus][2] - spins[ispin_plus_minus][2] - +spins[ispin_minus_plus][2] + spins[ispin_minus_minus][2]) / (delta[1]) / (delta[2]) / 4; gradient[ispin][2] -= +exchange_tensor(2, 1)*(spins[ispin_plus_plus][1] - spins[ispin_plus_minus][1] - spins[ispin_minus_plus][1] + +spins[ispin_minus_minus][1]) / (delta[1]) / (delta[2]) / 4; } - void Hamiltonian_Micromagnetic::Gradient_Exchange(const vectorfield & spins, vectorfield & gradient) +}*/ + +// Gradient implementation +#pragma omp parallel for + for( unsigned int icell = 0; icell < geometry->n_cells_total; ++icell ) { - auto& delta = geometry->cell_size; - // scalar delta[3] = { 3e-10, 3e-10, 3e-9 }; - // scalar delta[3] = { 277e-12, 277e-12, 277e-12 }; - - //nongradient implementation - /* - #pragma omp parallel for - for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell) + for( unsigned int i = 0; i < 3; ++i ) { - int ispin = icell;//basically id of a cell - for (unsigned int i = 0; i < 3; ++i) + int icell_plus = idx_from_pair( + icell, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, + neigh[2 * i] ); + int icell_minus = idx_from_pair( + icell, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, + neigh[2 * i + 1] ); + + if( icell_plus >= 0 || icell_minus >= 0 ) { - - int ispin_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i]); - int ispin_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[i + 1]); - if (ispin_plus == -1) { - ispin_plus = ispin; - } - if (ispin_minus == -1) { - ispin_minus = ispin; - } - gradient[ispin][i] -= exchange_tensor(i, i)*(spins[ispin_plus][i] - 2 * spins[ispin][i] + spins[ispin_minus][i]) / (delta[i]) / (delta[i]); - - } - if (A_is_nondiagonal == true) { - int ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[6]); - int ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[7]); - int ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[8]); - int ispin_minus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[9]); - - if (ispin_plus_plus == -1) { - ispin_plus_plus = ispin; - } - if (ispin_minus_minus == -1) { - ispin_minus_minus = ispin; - } - if (ispin_plus_minus == -1) { - ispin_plus_minus = ispin; - } - if (ispin_minus_plus == -1) { - ispin_minus_plus = ispin; - } - gradient[ispin][0] -= exchange_tensor(0, 1)*(spins[ispin_plus_plus][1] - spins[ispin_plus_minus][1] - spins[ispin_minus_plus][1] + spins[ispin_minus_minus][1]) / (delta[0]) / (delta[1]) / 4; - gradient[ispin][1] -= exchange_tensor(1, 0)*(spins[ispin_plus_plus][0] - spins[ispin_plus_minus][0] - spins[ispin_minus_plus][0] + spins[ispin_minus_minus][0]) / (delta[0]) / (delta[1]) / 4; - - ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[10]); - ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[11]); - ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[12]); - ispin_minus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[13]); - - if (ispin_plus_plus == -1) { - ispin_plus_plus = ispin; - } - if (ispin_minus_minus == -1) { - ispin_minus_minus = ispin; - } - if (ispin_plus_minus == -1) { - ispin_plus_minus = ispin; - } - if (ispin_minus_plus == -1) { - ispin_minus_plus = ispin; - } - gradient[ispin][0] -= exchange_tensor(0, 2)*(spins[ispin_plus_plus][2] - spins[ispin_plus_minus][2] - spins[ispin_minus_plus][2] + spins[ispin_minus_minus][2]) / (delta[0]) / (delta[2]) / 4; - gradient[ispin][2] -= exchange_tensor(2, 0)*(spins[ispin_plus_plus][0] - spins[ispin_plus_minus][0] - spins[ispin_minus_plus][0] + spins[ispin_minus_minus][0]) / (delta[0]) / (delta[2]) / 4; - - ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[14]); - ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[15]); - ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[16]); - ispin_minus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[17]); - - if (ispin_plus_plus == -1) { - ispin_plus_plus = ispin; - } - if (ispin_minus_minus == -1) { - ispin_minus_minus = ispin; - } - if (ispin_plus_minus == -1) { - ispin_plus_minus = ispin; - } - if (ispin_minus_plus == -1) { - ispin_minus_plus = ispin; - } - gradient[ispin][1] -= exchange_tensor(1, 2)*(spins[ispin_plus_plus][2] - spins[ispin_plus_minus][2] - spins[ispin_minus_plus][2] + spins[ispin_minus_minus][2]) / (delta[1]) / (delta[2]) / 4; - gradient[ispin][2] -= exchange_tensor(2, 1)*(spins[ispin_plus_plus][1] - spins[ispin_plus_minus][1] - spins[ispin_minus_plus][1] + spins[ispin_minus_minus][1]) / (delta[1]) / (delta[2]) / 4; + if( icell_plus == -1 ) + icell_plus = icell; + if( icell_minus == -1 ) + icell_minus = icell; + + gradient[icell] -= 2 * C::mu_B * exchange_tensor + * ( spins[icell_plus] - 2 * spins[icell] + spins[icell_minus] ) + / ( Ms * delta[i] * delta[i] ); } - }*/ - - // Gradient implementation - #pragma omp parallel for - for( unsigned int icell = 0; icell < geometry->n_cells_total; ++icell ) - { - for( unsigned int i = 0; i < 3; ++i ) - { - int icell_plus = idx_from_pair(icell, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i]); - int icell_minus = idx_from_pair(icell, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i+1]); - - if( icell_plus >= 0 || icell_minus >= 0 ) - { - if( icell_plus == -1 ) - icell_plus = icell; - if( icell_minus == -1 ) - icell_minus = icell; - - gradient[icell] -= 2 * C::mu_B * exchange_tensor * (spins[icell_plus] - 2*spins[icell] + spins[icell_minus]) / (Ms*delta[i]*delta[i]); - } - - // gradient[icell][0] -= 2*exchange_tensor(i, i) / Ms * (spins[icell_plus][0] - 2*spins[icell][0] + spins[icell_minus][0]) / (delta[i]) / (delta[i]); - // gradient[icell][1] -= 2*exchange_tensor(i, i) / Ms * (spins[icell_plus][1] - 2*spins[icell][1] + spins[icell_minus][1]) / (delta[i]) / (delta[i]); - // gradient[icell][2] -= 2*exchange_tensor(i, i) / Ms * (spins[icell_plus][2] - 2*spins[icell][2] + spins[icell_minus][2]) / (delta[i]) / (delta[i]); - } - // if( this->A_is_nondiagonal ) - // { - // int ispin = icell; - - // // xy - // int ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[0]); - // int ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); - // int ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2]); - // int ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); - - // if( ispin_right == -1 ) - // ispin_right = ispin; - // if( ispin_left == -1 ) - // ispin_left = ispin; - // if( ispin_top == -1 ) - // ispin_top = ispin; - // if( ispin_bottom == -1 ) - // ispin_bottom = ispin; - - // gradient[ispin][0] -= 2*exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); - // gradient[ispin][0] -= 2*exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); - // gradient[ispin][1] -= 2*exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); - // gradient[ispin][1] -= 2*exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); - // gradient[ispin][2] -= 2*exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); - // gradient[ispin][2] -= 2*exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); - - // // xz - // ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[0]); - // ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); - // ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[4]); - // ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); - - // if( ispin_right == -1 ) - // ispin_right = ispin; - // if( ispin_left == -1 ) - // ispin_left = ispin; - // if( ispin_top == -1 ) - // ispin_top = ispin; - // if( ispin_bottom == -1 ) - // ispin_bottom = ispin; - - // gradient[ispin][0] -= 2*exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); - // gradient[ispin][0] -= 2*exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); - // gradient[ispin][1] -= 2*exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); - // gradient[ispin][1] -= 2*exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); - // gradient[ispin][2] -= 2*exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - // gradient[ispin][2] -= 2*exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - - // // yz - // ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2]); - // ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); - // ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[4]); - // ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); - - // if( ispin_right == -1 ) - // ispin_right = ispin; - // if( ispin_left == -1 ) - // ispin_left = ispin; - // if( ispin_top == -1 ) - // ispin_top = ispin; - // if( ispin_bottom == -1 ) - // ispin_bottom = ispin; - - // gradient[ispin][0] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); - // gradient[ispin][0] -= 2 * exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); - // gradient[ispin][1] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); - // gradient[ispin][1] -= 2 * exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); - // gradient[ispin][2] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - // gradient[ispin][2] -= 2 * exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - - // } + // gradient[icell][0] -= 2*exchange_tensor(i, i) / Ms * (spins[icell_plus][0] - 2*spins[icell][0] + + // spins[icell_minus][0]) / (delta[i]) / (delta[i]); gradient[icell][1] -= 2*exchange_tensor(i, i) / Ms * + // (spins[icell_plus][1] - 2*spins[icell][1] + spins[icell_minus][1]) / (delta[i]) / (delta[i]); + // gradient[icell][2] + // -= 2*exchange_tensor(i, i) / Ms * (spins[icell_plus][2] - 2*spins[icell][2] + spins[icell_minus][2]) / + // (delta[i]) / (delta[i]); } + // if( this->A_is_nondiagonal ) + // { + // int ispin = icell; + + // // xy + // int ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, + // geometry->atom_types, neigh[0]); int ispin_left = idx_from_pair(ispin, boundary_conditions, + // geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); int ispin_top = + // idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, + // geometry->atom_types, neigh[2]); int ispin_bottom = idx_from_pair(ispin, boundary_conditions, + // geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); + + // if( ispin_right == -1 ) + // ispin_right = ispin; + // if( ispin_left == -1 ) + // ispin_left = ispin; + // if( ispin_top == -1 ) + // ispin_top = ispin; + // if( ispin_bottom == -1 ) + // ispin_bottom = ispin; + + // gradient[ispin][0] -= 2*exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](0, 0) - + // spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - + // spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); gradient[ispin][0] -= 2*exchange_tensor(1, 0) / Ms * + // ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + + // (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); + // gradient[ispin][1] + // -= 2*exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, + // 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / + // delta[0]); gradient[ispin][1] -= 2*exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - + // spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - + // spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); gradient[ispin][2] -= 2*exchange_tensor(0, 1) / Ms * + // ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + + // (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); + // gradient[ispin][2] + // -= 2*exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, + // 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / + // delta[0]); + + // // xz + // ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, + // geometry->atom_types, neigh[0]); ispin_left = idx_from_pair(ispin, boundary_conditions, + // geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); ispin_top = + // idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, + // geometry->atom_types, neigh[4]); ispin_bottom = idx_from_pair(ispin, boundary_conditions, + // geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); + + // if( ispin_right == -1 ) + // ispin_right = ispin; + // if( ispin_left == -1 ) + // ispin_left = ispin; + // if( ispin_top == -1 ) + // ispin_top = ispin; + // if( ispin_bottom == -1 ) + // ispin_bottom = ispin; + + // gradient[ispin][0] -= 2*exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](0, 0) - + // spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - + // spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); gradient[ispin][0] -= 2*exchange_tensor(2, 0) / Ms * + // ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + + // (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + // gradient[ispin][1] + // -= 2*exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, + // 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / + // delta[0]); gradient[ispin][1] -= 2*exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - + // spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - + // spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); gradient[ispin][2] -= 2*exchange_tensor(0, 2) / Ms * + // ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + + // (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + // gradient[ispin][2] + // -= 2*exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, + // 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / + // delta[0]); + + // // yz + // ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, + // geometry->atom_types, neigh[2]); ispin_left = idx_from_pair(ispin, boundary_conditions, + // geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); ispin_top = + // idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, + // geometry->atom_types, neigh[4]); ispin_bottom = idx_from_pair(ispin, boundary_conditions, + // geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); + + // if( ispin_right == -1 ) + // ispin_right = ispin; + // if( ispin_left == -1 ) + // ispin_left = ispin; + // if( ispin_top == -1 ) + // ispin_top = ispin; + // if( ispin_bottom == -1 ) + // ispin_bottom = ispin; + + // gradient[ispin][0] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](0, 1) - + // spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - + // spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); gradient[ispin][0] -= 2 * exchange_tensor(2, 1) / Ms + // * ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + + // (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); + // gradient[ispin][1] + // -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](1, 1) - + // spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - + // spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); gradient[ispin][1] -= 2 * exchange_tensor(2, 1) / Ms + // * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + + // (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + // gradient[ispin][2] + // -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](2, 1) - + // spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - + // spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); gradient[ispin][2] -= 2 * exchange_tensor(2, 1) / Ms + // * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + + // (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + + // } } +} - void Hamiltonian_Micromagnetic::Spatial_Gradient(const vectorfield & spins) +void Hamiltonian_Micromagnetic::Spatial_Gradient( const vectorfield & spins ) +{ + auto & delta = geometry->cell_size; +// scalar delta[3] = { 3e-10,3e-10,3e-9 }; +// scalar delta[3] = { 277e-12, 277e-12, 277e-12 }; +/* +dn1/dr1 dn1/dr2 dn1/dr3 +dn2/dr1 dn2/dr2 dn2/dr3 +dn3/dr1 dn3/dr2 dn3/dr3 +*/ +#pragma omp parallel for + for( unsigned int icell = 0; icell < geometry->n_cells_total; ++icell ) { - auto& delta = geometry->cell_size; - // scalar delta[3] = { 3e-10,3e-10,3e-9 }; - // scalar delta[3] = { 277e-12, 277e-12, 277e-12 }; - /* - dn1/dr1 dn1/dr2 dn1/dr3 - dn2/dr1 dn2/dr2 dn2/dr3 - dn3/dr1 dn3/dr2 dn3/dr3 - */ - #pragma omp parallel for - for( unsigned int icell = 0; icell < geometry->n_cells_total; ++icell ) + spatial_gradient[icell] = Matrix3::Zero(); + for( unsigned int i = 0; i < 3; ++i ) { - spatial_gradient[icell] = Matrix3::Zero(); - for( unsigned int i = 0; i < 3; ++i ) + int icell_plus = idx_from_pair( + icell, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, + neigh[2 * i] ); + int icell_minus = idx_from_pair( + icell, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, + neigh[2 * i + 1] ); + + if( icell_plus >= 0 || icell_minus >= 0 ) { - int icell_plus = idx_from_pair(icell, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i]); - int icell_minus = idx_from_pair(icell, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2*i+1]); - - if( icell_plus >= 0 || icell_minus >= 0 ) - { - if( icell_plus == -1 ) - icell_plus = icell; - if( icell_minus == -1 ) - icell_minus = icell; - - spatial_gradient[icell].col(i) += (spins[icell_plus] - spins[icell_minus]) / (2*delta[i]); - } + if( icell_plus == -1 ) + icell_plus = icell; + if( icell_minus == -1 ) + icell_minus = icell; + + spatial_gradient[icell].col( i ) += ( spins[icell_plus] - spins[icell_minus] ) / ( 2 * delta[i] ); } } } +} - void Hamiltonian_Micromagnetic::Gradient_DMI(const vectorfield & spins, vectorfield & gradient) +void Hamiltonian_Micromagnetic::Gradient_DMI( const vectorfield & spins, vectorfield & gradient ) +{ +#pragma omp parallel for + for( unsigned int icell = 0; icell < geometry->n_cells_total; ++icell ) { - #pragma omp parallel for - for( unsigned int icell = 0; icell < geometry->n_cells_total; ++icell ) + for( unsigned int i = 0; i < 3; ++i ) { - for( unsigned int i = 0; i < 3; ++i ) - { - gradient[icell][0] -= 4*C::mu_B*( dmi_tensor(1,i) * spatial_gradient[icell](2,i) - dmi_tensor(2,i) * spatial_gradient[icell](1,i) ) / Ms; - gradient[icell][1] -= 4*C::mu_B*( dmi_tensor(2,i) * spatial_gradient[icell](0,i) - dmi_tensor(0,i) * spatial_gradient[icell](2,i) ) / Ms; - gradient[icell][2] -= 4*C::mu_B*( dmi_tensor(0,i) * spatial_gradient[icell](1,i) - dmi_tensor(1,i) * spatial_gradient[icell](0,i) ) / Ms; - } + gradient[icell][0] -= 4 * C::mu_B + * ( dmi_tensor( 1, i ) * spatial_gradient[icell]( 2, i ) + - dmi_tensor( 2, i ) * spatial_gradient[icell]( 1, i ) ) + / Ms; + gradient[icell][1] -= 4 * C::mu_B + * ( dmi_tensor( 2, i ) * spatial_gradient[icell]( 0, i ) + - dmi_tensor( 0, i ) * spatial_gradient[icell]( 2, i ) ) + / Ms; + gradient[icell][2] -= 4 * C::mu_B + * ( dmi_tensor( 0, i ) * spatial_gradient[icell]( 1, i ) + - dmi_tensor( 1, i ) * spatial_gradient[icell]( 0, i ) ) + / Ms; } } +} +void Hamiltonian_Micromagnetic::Hessian( const vectorfield & spins, MatrixX & hessian ) {} - void Hamiltonian_Micromagnetic::Hessian(const vectorfield & spins, MatrixX & hessian) - { - } - - - // Hamiltonian name as string - static const std::string name = "Micromagnetic"; - const std::string& Hamiltonian_Micromagnetic::Name() { return name; } +// Hamiltonian name as string +static const std::string name = "Micromagnetic"; +const std::string & Hamiltonian_Micromagnetic::Name() +{ + return name; } +} // namespace Engine + #endif \ No newline at end of file diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cu b/core/src/engine/Hamiltonian_Micromagnetic.cu index e5f4c32f0..e62cb561a 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cu +++ b/core/src/engine/Hamiltonian_Micromagnetic.cu @@ -1,14 +1,14 @@ #ifdef SPIRIT_USE_CUDA +#include +#include #include -#include #include -#include -#include +#include #include -#include #include +#include #include @@ -16,949 +16,1065 @@ using namespace Data; using namespace Utility; namespace C = Utility::Constants_Micromagnetic; using Engine::Vectormath::check_atom_type; -using Engine::Vectormath::idx_from_pair; using Engine::Vectormath::cu_check_atom_type; using Engine::Vectormath::cu_idx_from_pair; using Engine::Vectormath::cu_tupel_from_idx; - +using Engine::Vectormath::idx_from_pair; namespace Engine { - Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( - scalar Ms, - scalar external_field_magnitude, Vector3 external_field_normal, - Matrix3 anisotropy_tensor, - Matrix3 exchange_tensor, - Matrix3 dmi_tensor, - DDI_Method ddi_method, intfield ddi_n_periodic_images, scalar ddi_radius, - std::shared_ptr geometry, - int spatial_gradient_order, - intfield boundary_conditions - ) : Hamiltonian(boundary_conditions), spatial_gradient_order(spatial_gradient_order), geometry(geometry), - external_field_magnitude(external_field_magnitude), external_field_normal(external_field_normal), - anisotropy_tensor(anisotropy_tensor), exchange_tensor(exchange_tensor), dmi_tensor(dmi_tensor) - { - // Generate interaction pairs, constants etc. - this->Update_Interactions(); - } - - void Hamiltonian_Micromagnetic::Update_Interactions() + +Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( + scalar Ms, scalar external_field_magnitude, Vector3 external_field_normal, Matrix3 anisotropy_tensor, + Matrix3 exchange_tensor, Matrix3 dmi_tensor, DDI_Method ddi_method, intfield ddi_n_periodic_images, + scalar ddi_radius, std::shared_ptr geometry, int spatial_gradient_order, + intfield boundary_conditions ) + : Hamiltonian( boundary_conditions ), + spatial_gradient_order( spatial_gradient_order ), + geometry( geometry ), + external_field_magnitude( external_field_magnitude ), + external_field_normal( external_field_normal ), + anisotropy_tensor( anisotropy_tensor ), + exchange_tensor( exchange_tensor ), + dmi_tensor( dmi_tensor ) +{ + // Generate interaction pairs, constants etc. + this->Update_Interactions(); +} + +void Hamiltonian_Micromagnetic::Update_Interactions() +{ +#if defined( SPIRIT_USE_OPENMP ) + // When parallelising (cuda or openmp), we need all neighbours per spin + const bool use_redundant_neighbours = true; +#else + // When running on a single thread, we can ignore redundant neighbours + const bool use_redundant_neighbours = false; +#endif + + // TODO: make sure that the geometry can be treated with this model: + // - rectilinear, only one "atom" per cell + // if( geometry->n_cell_atoms != 1 ) + // Log(...) + + // TODO: generate neighbour information for pairwise interactions + + // TODO: prepare dipolar interactions + neigh = pairfield( 0 ); + Neighbour neigh_tmp; + neigh_tmp.i = 0; + neigh_tmp.j = 0; + neigh_tmp.idx_shell = 0; + // order x -x y -y z -z xy (-x)(-y) x(-y) (-x)y xz (-x)(-z) x(-z) (-x)z yz (-y)(-z) y(-z) (-y)z results in 9 parts of Hessian + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = +1; + neigh_tmp.translations[2] = 0; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = -1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = -1; + neigh_tmp.translations[1] = 0; + neigh_tmp.translations[2] = 1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = 1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = -1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = 1; + neigh_tmp.translations[2] = -1; + neigh.push_back( neigh_tmp ); + + neigh_tmp.translations[0] = 0; + neigh_tmp.translations[1] = -1; + neigh_tmp.translations[2] = 1; + neigh.push_back( neigh_tmp ); + this->spatial_gradient = field( geometry->nos, Matrix3::Zero() ); + + // Dipole-dipole + this->Prepare_DDI(); + + // Update, which terms still contribute + this->Update_Energy_Contributions(); +} + +void Hamiltonian_Micromagnetic::Update_Energy_Contributions() +{ + this->energy_contributions_per_spin = std::vector>( 0 ); + CU_CHECK_AND_SYNC(); + // External field + if( this->external_field_magnitude > 0 ) + { + this->energy_contributions_per_spin.push_back( { "Zeeman", scalarfield( 0 ) } ); + this->idx_zeeman = this->energy_contributions_per_spin.size() - 1; + } + else + this->idx_zeeman = -1; + // TODO: Anisotropy + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"Anisotropy", scalarfield(0) }); + // this->idx_anisotropy = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_anisotropy = -1; + // TODO: Exchange + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"Exchange", scalarfield(0) }); + // this->idx_exchange = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_exchange = -1; + // TODO: DMI + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"DMI", scalarfield(0) }); + // this->idx_dmi = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_dmi = -1; + // TODO: DDI + // if( ... ) + // { + // this->energy_contributions_per_spin.push_back({"DDI", scalarfield(0) }); + // this->idx_ddi = this->energy_contributions_per_spin.size()-1; + // } + // else + this->idx_ddi = -1; +} + +void Hamiltonian_Micromagnetic::Energy_Contributions_per_Spin( + const vectorfield & spins, std::vector> & contributions ) +{ + if( contributions.size() != this->energy_contributions_per_spin.size() ) { - #if defined(SPIRIT_USE_OPENMP) - // When parallelising (cuda or openmp), we need all neighbours per spin - const bool use_redundant_neighbours = true; - #else - // When running on a single thread, we can ignore redundant neighbours - const bool use_redundant_neighbours = false; - #endif - - // TODO: make sure that the geometry can be treated with this model: - // - rectilinear, only one "atom" per cell - // if( geometry->n_cell_atoms != 1 ) - // Log(...) - - // TODO: generate neighbour information for pairwise interactions - - // TODO: prepare dipolar interactions - neigh = pairfield(0); - Neighbour neigh_tmp; - neigh_tmp.i = 0; - neigh_tmp.j = 0; - neigh_tmp.idx_shell = 0; - //order x -x y -y z -z xy (-x)(-y) x(-y) (-x)y xz (-x)(-z) x(-z) (-x)z yz (-y)(-z) y(-z) (-y)z results in 9 parts of Hessian - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = 1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = +1; - neigh_tmp.translations[2] = 0; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = -1; - neigh_tmp.translations[1] = 0; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 1; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = 1; - neigh_tmp.translations[2] = -1; - neigh.push_back(neigh_tmp); - - neigh_tmp.translations[0] = 0; - neigh_tmp.translations[1] = -1; - neigh_tmp.translations[2] = 1; - neigh.push_back(neigh_tmp); - this->spatial_gradient = field(geometry->nos, Matrix3::Zero()); - - // Dipole-dipole - this->Prepare_DDI(); - - // Update, which terms still contribute - this->Update_Energy_Contributions(); + contributions = this->energy_contributions_per_spin; } - void Hamiltonian_Micromagnetic::Update_Energy_Contributions() + int nos = spins.size(); + for( auto & contrib : contributions ) { - this->energy_contributions_per_spin = std::vector>(0); - CU_CHECK_AND_SYNC(); - // External field - if( this->external_field_magnitude > 0 ) - { - this->energy_contributions_per_spin.push_back({"Zeeman", scalarfield(0)}); - this->idx_zeeman = this->energy_contributions_per_spin.size()-1; - } + // Allocate if not already allocated + if( contrib.second.size() != nos ) + contrib.second = scalarfield( nos, 0 ); + // Otherwise set to zero else - this->idx_zeeman = -1; - // TODO: Anisotropy - // if( ... ) - // { - // this->energy_contributions_per_spin.push_back({"Anisotropy", scalarfield(0) }); - // this->idx_anisotropy = this->energy_contributions_per_spin.size()-1; - // } - // else - this->idx_anisotropy = -1; - // TODO: Exchange - // if( ... ) - // { - // this->energy_contributions_per_spin.push_back({"Exchange", scalarfield(0) }); - // this->idx_exchange = this->energy_contributions_per_spin.size()-1; - // } - // else - this->idx_exchange = -1; - // TODO: DMI - // if( ... ) - // { - // this->energy_contributions_per_spin.push_back({"DMI", scalarfield(0) }); - // this->idx_dmi = this->energy_contributions_per_spin.size()-1; - // } - // else - this->idx_dmi = -1; - // TODO: DDI - // if( ... ) - // { - // this->energy_contributions_per_spin.push_back({"DDI", scalarfield(0) }); - // this->idx_ddi = this->energy_contributions_per_spin.size()-1; - // } - // else - this->idx_ddi = -1; + Vectormath::fill( contrib.second, 0 ); } - void Hamiltonian_Micromagnetic::Energy_Contributions_per_Spin(const vectorfield & spins, std::vector> & contributions) + // External field + if( this->idx_zeeman >= 0 ) + E_Zeeman( spins, contributions[idx_zeeman].second ); + + // Anisotropy + if( this->idx_anisotropy >= 0 ) + E_Anisotropy( spins, contributions[idx_anisotropy].second ); + + // Exchange + if( this->idx_exchange >= 0 ) + E_Exchange( spins, contributions[idx_exchange].second ); + // DMI + if( this->idx_dmi >= 0 ) + E_DMI( spins, contributions[idx_dmi].second ); +} + +__global__ void CU_E_Zeeman1( + const Vector3 * spins, const int * atom_types, const int n_cell_atoms, const scalar * mu_s, + const scalar external_field_magnitude, const Vector3 external_field_normal, scalar * Energy, size_t n_cells_total ) +{ + for( auto icell = blockIdx.x * blockDim.x + threadIdx.x; icell < n_cells_total; icell += blockDim.x * gridDim.x ) { - if( contributions.size() != this->energy_contributions_per_spin.size() ) + for( int ibasis = 0; ibasis < n_cell_atoms; ++ibasis ) { - contributions = this->energy_contributions_per_spin; + int ispin = icell + ibasis; + if( cu_check_atom_type( atom_types[ispin] ) ) + Energy[ispin] -= mu_s[ispin] * external_field_magnitude * external_field_normal.dot( spins[ispin] ); } + } +} +void Hamiltonian_Micromagnetic::E_Zeeman( const vectorfield & spins, scalarfield & Energy ) +{ + int size = geometry->n_cells_total; + CU_E_Zeeman1<<<( size + 1023 ) / 1024, 1024>>>( + spins.data(), this->geometry->atom_types.data(), geometry->n_cell_atoms, geometry->mu_s.data(), + this->external_field_magnitude, this->external_field_normal, Energy.data(), size ); + CU_CHECK_AND_SYNC(); +} - int nos = spins.size(); - for( auto& contrib : contributions ) - { - // Allocate if not already allocated - if (contrib.second.size() != nos) contrib.second = scalarfield(nos, 0); - // Otherwise set to zero - else Vectormath::fill(contrib.second, 0); - } +void Hamiltonian_Micromagnetic::E_Anisotropy( const vectorfield & spins, scalarfield & Energy ) {} - // External field - if( this->idx_zeeman >=0 ) E_Zeeman(spins, contributions[idx_zeeman].second); +void Hamiltonian_Micromagnetic::E_Exchange( const vectorfield & spins, scalarfield & Energy ) {} - // Anisotropy - if( this->idx_anisotropy >=0 ) E_Anisotropy(spins, contributions[idx_anisotropy].second); +void Hamiltonian_Micromagnetic::E_DMI( const vectorfield & spins, scalarfield & Energy ) {} - // Exchange - if( this->idx_exchange >=0 ) E_Exchange(spins, contributions[idx_exchange].second); - // DMI - if( this->idx_dmi >=0 ) E_DMI(spins,contributions[idx_dmi].second); +void Hamiltonian_Micromagnetic::E_DDI( const vectorfield & spins, scalarfield & Energy ) {} +scalar Hamiltonian_Micromagnetic::Energy_Single_Spin( int ispin, const vectorfield & spins ) +{ + scalar Energy = 0; + return Energy; +} + +void Hamiltonian_Micromagnetic::Gradient( const vectorfield & spins, vectorfield & gradient ) +{ + // Set to zero + Vectormath::fill( gradient, { 0, 0, 0 } ); + this->Spatial_Gradient( spins ); + // External field + this->Gradient_Zeeman( gradient ); + + // Anisotropy + this->Gradient_Anisotropy( spins, gradient ); + + // Exchange + this->Gradient_Exchange( spins, gradient ); + + // DMI + this->Gradient_DMI( spins, gradient ); + scalar Ms = 1.4e6; + double energy = 0; +#pragma omp parallel for reduction( - : energy ) + for( int icell = 0; icell < geometry->n_cells_total; ++icell ) + { + // energy -= 0.5 *Ms* gradient[icell].dot(spins[icell]); } + // printf("Energy total: %f\n", energy/ geometry->n_cells_total); +} - __global__ void CU_E_Zeeman1(const Vector3 * spins, const int * atom_types, const int n_cell_atoms, const scalar * mu_s, const scalar external_field_magnitude, const Vector3 external_field_normal, scalar * Energy, size_t n_cells_total) - { - for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; - icell < n_cells_total; - icell += blockDim.x * gridDim.x) - { - for (int ibasis = 0; ibasis < n_cell_atoms; ++ibasis) - { - int ispin = icell + ibasis; - if (cu_check_atom_type(atom_types[ispin])) - Energy[ispin] -= mu_s[ispin] * external_field_magnitude * external_field_normal.dot(spins[ispin]); - } - } - } - void Hamiltonian_Micromagnetic::E_Zeeman(const vectorfield & spins, scalarfield & Energy) - { - int size = geometry->n_cells_total; - CU_E_Zeeman1 << <(size + 1023) / 1024, 1024 >> > (spins.data(), this->geometry->atom_types.data(), geometry->n_cell_atoms, geometry->mu_s.data(), this->external_field_magnitude, this->external_field_normal, Energy.data(), size); - CU_CHECK_AND_SYNC(); - } - - void Hamiltonian_Micromagnetic::E_Anisotropy(const vectorfield & spins, scalarfield & Energy) +__global__ void CU_Gradient_Zeeman1( + const int * atom_types, const int n_cell_atoms, const scalar * mu_s, const scalar external_field_magnitude, + const Vector3 external_field_normal, Vector3 * gradient, size_t n_cells_total ) +{ + for( auto icell = blockIdx.x * blockDim.x + threadIdx.x; icell < n_cells_total; icell += blockDim.x * gridDim.x ) { + for( int ibasis = 0; ibasis < n_cell_atoms; ++ibasis ) + { + int ispin = icell + ibasis; + if( cu_check_atom_type( atom_types[ispin] ) ) + gradient[ispin] -= mu_s[ispin] * C::mu_B * external_field_magnitude * external_field_normal; + } } +} +void Hamiltonian_Micromagnetic::Gradient_Zeeman( vectorfield & gradient ) +{ + int size = geometry->n_cells_total; + CU_Gradient_Zeeman1<<<( size + 1023 ) / 1024, 1024>>>( + this->geometry->atom_types.data(), geometry->n_cell_atoms, geometry->mu_s.data(), + this->external_field_magnitude, this->external_field_normal, gradient.data(), size ); + CU_CHECK_AND_SYNC(); +} - void Hamiltonian_Micromagnetic::E_Exchange(const vectorfield & spins, scalarfield & Energy) +__global__ void CU_Gradient_Anisotropy1( + const Vector3 * spins, const int * atom_types, const int n_cell_atoms, Vector3 * gradient, size_t n_cells_total, + Matrix3 anisotropy_tensor ) +{ + scalar Ms = 1.4e6; + Vector3 temp1{ 1, 0, 0 }; + Vector3 temp2{ 0, 1, 0 }; + Vector3 temp3{ 0, 0, 1 }; + for( auto icell = blockIdx.x * blockDim.x + threadIdx.x; icell < n_cells_total; icell += blockDim.x * gridDim.x ) { + int ispin = icell; + gradient[ispin] -= 2.0 * C::mu_B * anisotropy_tensor * spins[ispin] / Ms; + // gradient[ispin] -= 2.0 * this->anisotropy_magnitudes[iani] / Ms * ((pow(temp2.dot(spins[ispin]),2)+ + // pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1)+ (pow(temp1.dot(spins[ispin]), 2) + + // pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)+(pow(temp1.dot(spins[ispin]),2)+ + // pow(temp2.dot(spins[ispin]), 2))*(temp3.dot(spins[ispin])*temp3)); gradient[ispin] += 2.0 * 50000 / Ms * + // ((pow(temp2.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1) + + // (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)); } +} - void Hamiltonian_Micromagnetic::E_DMI(const vectorfield & spins, scalarfield & Energy) +void Hamiltonian_Micromagnetic::Gradient_Anisotropy( const vectorfield & spins, vectorfield & gradient ) +{ + int size = geometry->n_cells_total; + CU_Gradient_Anisotropy1<<<( size + 1023 ) / 1024, 1024>>>( + spins.data(), this->geometry->atom_types.data(), this->geometry->n_cell_atoms, gradient.data(), size, + this->anisotropy_tensor ); + CU_CHECK_AND_SYNC(); +} + +__global__ void CU_Gradient_Exchange1( + const Vector3 * spins, const int * atom_types, const int * boundary_conditions, const int * n_cells, + int n_cell_atoms, int n_pairs, const Pair * neigh, Vector3 * gradient, size_t size, bool A_is_nondiagonal, + Matrix3 exchange_tensor, const scalar * delta, const scalar Ms ) +{ + int bc[3] = { boundary_conditions[0], boundary_conditions[1], boundary_conditions[2] }; + + int nc[3] = { n_cells[0], n_cells[1], n_cells[2] }; + + for( auto icell = blockIdx.x * blockDim.x + threadIdx.x; icell < size; icell += blockDim.x * gridDim.x ) { + // int ispin = icell;//basically id of a cell + for( unsigned int i = 0; i < 3; ++i ) + { + + int icell_plus = cu_idx_from_pair( icell, bc, nc, n_cell_atoms, atom_types, neigh[2 * i] ); + int icell_minus = cu_idx_from_pair( icell, bc, nc, n_cell_atoms, atom_types, neigh[2 * i + 1] ); + + if( icell_plus >= 0 || icell_minus >= 0 ) + { + if( icell_plus == -1 ) + icell_plus = icell; + if( icell_minus == -1 ) + icell_minus = icell; + + gradient[icell] -= 2 * C::mu_B * exchange_tensor + * ( spins[icell_plus] - 2 * spins[icell] + spins[icell_minus] ) + / ( Ms * delta[i] * delta[i] ); + } + } + /*if (A_is_nondiagonal == true) { + //xy + int ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, + geometry->atom_types, neigh[0]); int ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, + geometry->n_cell_atoms, geometry->atom_types, neigh[1]); int ispin_top = idx_from_pair(ispin, + boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2]); int + ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, + geometry->atom_types, neigh[3]); + + if (ispin_right == -1) { + ispin_right = ispin; + } + if (ispin_left == -1) { + ispin_left = ispin; + } + if (ispin_top == -1) { + ispin_top = ispin; + } + if (ispin_bottom == -1) { + ispin_bottom = ispin; + } + gradient[ispin][0] -= 2 * exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](0, 0) - + spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - + spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); gradient[ispin][0] -= 2 * exchange_tensor(1, 0) / Ms * + ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); gradient[ispin][1] + -= 2 * exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) + / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); + gradient[ispin][1] -= 2 * exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - + spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - + spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); gradient[ispin][2] -= 2 * exchange_tensor(0, 1) / Ms * + ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); gradient[ispin][2] + -= 2 * exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) + / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); + + //xz + ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, + geometry->atom_types, neigh[0]); ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, + geometry->n_cell_atoms, geometry->atom_types, neigh[1]); ispin_top = idx_from_pair(ispin, boundary_conditions, + geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[4]); ispin_bottom = idx_from_pair(ispin, + boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); + + if (ispin_right == -1) { + ispin_right = ispin; + } + if (ispin_left == -1) { + ispin_left = ispin; + } + if (ispin_top == -1) { + ispin_top = ispin; + } + if (ispin_bottom == -1) { + ispin_bottom = ispin; + } + gradient[ispin][0] -= 2 * exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](0, 0) - + spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - + spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); gradient[ispin][0] -= 2 * exchange_tensor(2, 0) / Ms * + ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); gradient[ispin][1] + -= 2 * exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) + / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + gradient[ispin][1] -= 2 * exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - + spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - + spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); gradient[ispin][2] -= 2 * exchange_tensor(0, 2) / Ms * + ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); gradient[ispin][2] + -= 2 * exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) + / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + + //yz + ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, + geometry->atom_types, neigh[2]); ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, + geometry->n_cell_atoms, geometry->atom_types, neigh[3]); ispin_top = idx_from_pair(ispin, boundary_conditions, + geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[4]); ispin_bottom = idx_from_pair(ispin, + boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); + + if (ispin_right == -1) { + ispin_right = ispin; + } + if (ispin_left == -1) { + ispin_left = ispin; + } + if (ispin_top == -1) { + ispin_top = ispin; + } + if (ispin_bottom == -1) { + ispin_bottom = ispin; + } + gradient[ispin][0] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](0, 1) - + spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - + spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); gradient[ispin][0] -= 2 * exchange_tensor(2, 1) / Ms * + ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); gradient[ispin][1] + -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) + / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); + gradient[ispin][1] -= 2 * exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](1, 1) - + spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - + spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); gradient[ispin][2] -= 2 * exchange_tensor(1, 2) / Ms * + ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); gradient[ispin][2] + -= 2 * exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) + / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); + + }*/ } +} +void Hamiltonian_Micromagnetic::Gradient_Exchange( const vectorfield & spins, vectorfield & gradient ) +{ + int size = geometry->n_cells_total; + scalar * delta = geometry->cell_size.data(); + CU_Gradient_Exchange1<<<( size + 1023 ) / 1024, 1024>>>( + spins.data(), this->geometry->atom_types.data(), boundary_conditions.data(), geometry->n_cells.data(), + geometry->n_cell_atoms, this->neigh.size(), this->neigh.data(), gradient.data(), size, A_is_nondiagonal, + exchange_tensor, delta, this->Ms ); + CU_CHECK_AND_SYNC(); +} - void Hamiltonian_Micromagnetic::E_DDI(const vectorfield & spins, scalarfield & Energy) +__global__ void CU_Spatial_Gradient( + const Vector3 * spins, const int * atom_types, const int * boundary_conditions, const int * n_cells, + int n_cell_atoms, int n_pairs, const Pair * neigh, Matrix3 * spatial_gradient, size_t size, scalar * delta, + scalar Ms ) +{ + /* + dn1/dr1 dn1/dr2 dn1/dr3 + dn2/dr1 dn2/dr2 dn2/dr3 + dn3/dr1 dn3/dr2 dn3/dr3 + */ + int bc[3] = { boundary_conditions[0], boundary_conditions[1], boundary_conditions[2] }; + int nc[3] = { n_cells[0], n_cells[1], n_cells[2] }; + + for( auto icell = blockIdx.x * blockDim.x + threadIdx.x; icell < size; icell += blockDim.x * gridDim.x ) { + for( unsigned int i = 0; i < 3; ++i ) + { + int icell_plus = cu_idx_from_pair( icell, bc, nc, n_cell_atoms, atom_types, neigh[2 * i] ); + int icell_minus = cu_idx_from_pair( icell, bc, nc, n_cell_atoms, atom_types, neigh[2 * i + 1] ); + + if( icell_plus >= 0 || icell_minus >= 0 ) + { + if( icell_plus == -1 ) + icell_plus = icell; + if( icell_minus == -1 ) + icell_minus = icell; + + spatial_gradient[icell].col( i ) += ( spins[icell_plus] - spins[icell_minus] ) / ( 2 * delta[i] ); + } + } } +} +void Hamiltonian_Micromagnetic::Spatial_Gradient( const vectorfield & spins ) +{ + int size = geometry->n_cells_total; + CU_Spatial_Gradient<<<( size + 1023 ) / 1024, 1024>>>( + spins.data(), this->geometry->atom_types.data(), boundary_conditions.data(), geometry->n_cells.data(), + geometry->n_cell_atoms, this->neigh.size(), this->neigh.data(), spatial_gradient.data(), size, + geometry->cell_size.data(), this->Ms ); + CU_CHECK_AND_SYNC(); +} - scalar Hamiltonian_Micromagnetic::Energy_Single_Spin(int ispin, const vectorfield & spins) +__global__ void CU_Gradient_DMI1( + const Vector3 * spins, Vector3 * gradient, Matrix3 * spatial_gradient, size_t size, Matrix3 dmi_tensor, scalar Ms ) +{ + for( auto icell = blockIdx.x * blockDim.x + threadIdx.x; icell < size; icell += blockDim.x * gridDim.x ) { - scalar Energy = 0; - return Energy; + for( unsigned int i = 0; i < 3; ++i ) + { + gradient[icell][0] -= 4 * C::mu_B + * ( dmi_tensor( 1, i ) * spatial_gradient[icell]( 2, i ) + - 2 * dmi_tensor( 2, i ) * spatial_gradient[icell]( 1, i ) ) + / Ms; + gradient[icell][1] -= 4 * C::mu_B + * ( dmi_tensor( 2, i ) * spatial_gradient[icell]( 0, i ) + - 2 * dmi_tensor( 0, i ) * spatial_gradient[icell]( 2, i ) ) + / Ms; + gradient[icell][2] -= 4 * C::mu_B + * ( dmi_tensor( 0, i ) * spatial_gradient[icell]( 1, i ) + - 2 * dmi_tensor( 1, i ) * spatial_gradient[icell]( 0, i ) ) + / Ms; + } } +} +void Hamiltonian_Micromagnetic::Gradient_DMI( const vectorfield & spins, vectorfield & gradient ) +{ + int size = geometry->n_cells_total; + CU_Gradient_DMI1<<<( size + 1023 ) / 1024, 1024>>>( + spins.data(), gradient.data(), spatial_gradient.data(), size, dmi_tensor, this->Ms ); + CU_CHECK_AND_SYNC(); +} +__global__ void CU_FFT_Pointwise_Mult1( + FFT::FFT_cpx_type * ft_D_matrices, FFT::FFT_cpx_type * ft_spins, FFT::FFT_cpx_type * res_mult, + int * iteration_bounds, int i_b1, int * inter_sublattice_lookup, FFT::StrideContainer dipole_stride, + FFT::StrideContainer spin_stride, const scalar Ms ) +{ + int n = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2] * iteration_bounds[3]; + int tupel[4]; + int idx_b1, idx_b2, idx_d; - void Hamiltonian_Micromagnetic::Gradient(const vectorfield & spins, vectorfield & gradient) + for( int ispin = blockIdx.x * blockDim.x + threadIdx.x; ispin < n; ispin += blockDim.x * gridDim.x ) { - // Set to zero - Vectormath::fill(gradient, {0,0,0}); - this->Spatial_Gradient(spins); - // External field - this->Gradient_Zeeman(gradient); - - // Anisotropy - this->Gradient_Anisotropy(spins, gradient); - - // Exchange - this->Gradient_Exchange(spins, gradient); - - // DMI - this->Gradient_DMI(spins, gradient); - scalar Ms = 1.4e6; - double energy = 0; - #pragma omp parallel for reduction(-:energy) - for (int icell = 0; icell < geometry->n_cells_total; ++icell) - { - //energy -= 0.5 *Ms* gradient[icell].dot(spins[icell]); - } - //printf("Energy total: %f\n", energy/ geometry->n_cells_total); + cu_tupel_from_idx( ispin, tupel, iteration_bounds, 4 ); // tupel now is {i_b2, a, b, c} + int & b_inter = inter_sublattice_lookup[i_b1 + tupel[0] * iteration_bounds[0]]; + + idx_b1 + = i_b1 * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + tupel[3] * spin_stride.c; + idx_b2 = tupel[0] * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + + tupel[3] * spin_stride.c; + idx_d = b_inter * dipole_stride.basis + tupel[1] * dipole_stride.a + tupel[2] * dipole_stride.b + + tupel[3] * dipole_stride.c; + + auto & fs_x = ft_spins[idx_b2]; + auto & fs_y = ft_spins[idx_b2 + 1 * spin_stride.comp]; + auto & fs_z = ft_spins[idx_b2 + 2 * spin_stride.comp]; + + auto & fD_xx = ft_D_matrices[idx_d]; + auto & fD_xy = ft_D_matrices[idx_d + 1 * dipole_stride.comp]; + auto & fD_xz = ft_D_matrices[idx_d + 2 * dipole_stride.comp]; + auto & fD_yy = ft_D_matrices[idx_d + 3 * dipole_stride.comp]; + auto & fD_yz = ft_D_matrices[idx_d + 4 * dipole_stride.comp]; + auto & fD_zz = ft_D_matrices[idx_d + 5 * dipole_stride.comp]; + + if( tupel[0] == 0 ) + { + res_mult[idx_b1].x = FFT::mult3D( fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z ).x; + res_mult[idx_b1].y = FFT::mult3D( fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z ).y; + res_mult[idx_b1 + 1 * spin_stride.comp].x = FFT::mult3D( fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z ).x; + res_mult[idx_b1 + 1 * spin_stride.comp].y = FFT::mult3D( fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z ).y; + res_mult[idx_b1 + 2 * spin_stride.comp].x = FFT::mult3D( fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z ).x; + res_mult[idx_b1 + 2 * spin_stride.comp].y = FFT::mult3D( fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z ).y; + } + else + { + atomicAdd( &res_mult[idx_b1].x, FFT::mult3D( fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z ).x ); + atomicAdd( &res_mult[idx_b1].y, FFT::mult3D( fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z ).y ); + atomicAdd( + &res_mult[idx_b1 + 1 * spin_stride.comp].x, FFT::mult3D( fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z ).x ); + atomicAdd( + &res_mult[idx_b1 + 1 * spin_stride.comp].y, FFT::mult3D( fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z ).y ); + atomicAdd( + &res_mult[idx_b1 + 2 * spin_stride.comp].x, FFT::mult3D( fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z ).x ); + atomicAdd( + &res_mult[idx_b1 + 2 * spin_stride.comp].y, FFT::mult3D( fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z ).y ); + } } +} +__global__ void CU_Write_FFT_Gradients1( + const FFT::FFT_real_type * resiFFT, Vector3 * gradient, FFT::StrideContainer spin_stride, int * iteration_bounds, + int n_cell_atoms, scalar * mu_s, int sublattice_size, const scalar Ms ) +{ + int nos = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2] * iteration_bounds[3]; + int tupel[4]; + int idx_pad; + for( int idx_orig = blockIdx.x * blockDim.x + threadIdx.x; idx_orig < nos; idx_orig += blockDim.x * gridDim.x ) + { - __global__ void CU_Gradient_Zeeman1(const int * atom_types, const int n_cell_atoms, const scalar * mu_s, const scalar external_field_magnitude, const Vector3 external_field_normal, Vector3 * gradient, size_t n_cells_total) - { - for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; - icell < n_cells_total; - icell += blockDim.x * gridDim.x) - { - for (int ibasis = 0; ibasis < n_cell_atoms; ++ibasis) - { - int ispin = icell + ibasis; - if (cu_check_atom_type(atom_types[ispin])) - gradient[ispin] -= mu_s[ispin] * C::mu_B * external_field_magnitude*external_field_normal; - } - } - } - void Hamiltonian_Micromagnetic::Gradient_Zeeman(vectorfield & gradient) - { - int size = geometry->n_cells_total; - CU_Gradient_Zeeman1 << <(size + 1023) / 1024, 1024 >> > (this->geometry->atom_types.data(), geometry->n_cell_atoms, geometry->mu_s.data(), this->external_field_magnitude, this->external_field_normal, gradient.data(), size); - CU_CHECK_AND_SYNC(); - } - - __global__ void CU_Gradient_Anisotropy1(const Vector3 * spins, const int * atom_types, const int n_cell_atoms, Vector3 * gradient, size_t n_cells_total, Matrix3 anisotropy_tensor) - { - scalar Ms = 1.4e6; - Vector3 temp1{ 1,0,0 }; - Vector3 temp2{ 0,1,0 }; - Vector3 temp3{ 0,0,1 }; - for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; - icell < n_cells_total; - icell += blockDim.x * gridDim.x) - { - int ispin = icell; - gradient[ispin] -= 2.0 * C::mu_B * anisotropy_tensor * spins[ispin] / Ms; - //gradient[ispin] -= 2.0 * this->anisotropy_magnitudes[iani] / Ms * ((pow(temp2.dot(spins[ispin]),2)+ pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1)+ (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)+(pow(temp1.dot(spins[ispin]),2)+ pow(temp2.dot(spins[ispin]), 2))*(temp3.dot(spins[ispin])*temp3)); - //gradient[ispin] += 2.0 * 50000 / Ms * ((pow(temp2.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp1.dot(spins[ispin])*temp1) + (pow(temp1.dot(spins[ispin]), 2) + pow(temp3.dot(spins[ispin]), 2))*(temp2.dot(spins[ispin])*temp2)); - } - } - - void Hamiltonian_Micromagnetic::Gradient_Anisotropy(const vectorfield & spins, vectorfield & gradient) - { - int size = geometry->n_cells_total; - CU_Gradient_Anisotropy1 << <(size + 1023) / 1024, 1024 >> > (spins.data(), this->geometry->atom_types.data(), this->geometry->n_cell_atoms, gradient.data(), size, this->anisotropy_tensor); - CU_CHECK_AND_SYNC(); - } - - __global__ void CU_Gradient_Exchange1(const Vector3 * spins, const int * atom_types, const int * boundary_conditions, const int * n_cells, int n_cell_atoms, - int n_pairs, const Pair * neigh, Vector3 * gradient, size_t size, bool A_is_nondiagonal, Matrix3 exchange_tensor, const scalar * delta, const scalar Ms) - { - int bc[3] = { boundary_conditions[0],boundary_conditions[1],boundary_conditions[2] }; - - int nc[3] = { n_cells[0],n_cells[1],n_cells[2] }; - - for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; - icell < size; - icell += blockDim.x * gridDim.x) - { - // int ispin = icell;//basically id of a cell - for (unsigned int i = 0; i < 3; ++i) - { - - int icell_plus = cu_idx_from_pair(icell, bc, nc, n_cell_atoms, atom_types, neigh[2*i]); - int icell_minus = cu_idx_from_pair(icell, bc, nc, n_cell_atoms, atom_types, neigh[2*i + 1]); - - if( icell_plus >= 0 || icell_minus >= 0 ) - { - if( icell_plus == -1 ) - icell_plus = icell; - if( icell_minus == -1 ) - icell_minus = icell; + cu_tupel_from_idx( idx_orig, tupel, iteration_bounds, 4 ); // tupel now is {ib, a, b, c} + idx_pad = tupel[0] * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + + tupel[3] * spin_stride.c; + // printf("%d %f %f\n", idx_orig, resiFFT[idx_pad],gradient[idx_orig][0]); + gradient[idx_orig][0] -= C::mu_B * resiFFT[idx_pad] * Ms * 1e-7 / ( sublattice_size ); + gradient[idx_orig][1] -= C::mu_B * resiFFT[idx_pad + 1 * spin_stride.comp] * Ms * 1e-7 / ( sublattice_size ); + gradient[idx_orig][2] -= C::mu_B * resiFFT[idx_pad + 2 * spin_stride.comp] * Ms * 1e-7 / ( sublattice_size ); + } +} - gradient[icell] -= 2 * C::mu_B * exchange_tensor * (spins[icell_plus] - 2*spins[icell] + spins[icell_minus]) / (Ms*delta[i]*delta[i]); +void Hamiltonian_Micromagnetic::Gradient_DDI( const vectorfield & spins, vectorfield & gradient ) +{ + // this->Gradient_DDI_Direct(spins, gradient); + this->Gradient_DDI_FFT( spins, gradient ); + /* + if (this->ddi_method == DDI_Method::FFT) + { + printf("sasas"); + this->Gradient_DDI_FFT(spins, gradient); + } + else if (this->ddi_method == DDI_Method::Cutoff) + { + // TODO: Merge these implementations in the future + if (this->ddi_cutoff_radius >= 0) + this->Gradient_DDI_Cutoff(spins, gradient); + else + this->Gradient_DDI_Direct(spins, gradient); + } +*/ +} +void Hamiltonian_Micromagnetic::Gradient_DDI_Cutoff( const vectorfield & spins, vectorfield & gradient ) +{ + // TODO +} +void Hamiltonian_Micromagnetic::Gradient_DDI_FFT( const vectorfield & spins, vectorfield & gradient ) +{ + auto & ft_D_matrices = transformed_dipole_matrices; + + auto & ft_spins = fft_plan_spins.cpx_ptr; + + auto & res_iFFT = fft_plan_reverse.real_ptr; + auto & res_mult = fft_plan_reverse.cpx_ptr; + + int number_of_mults = it_bounds_pointwise_mult[0] * it_bounds_pointwise_mult[1] * it_bounds_pointwise_mult[2] + * it_bounds_pointwise_mult[3]; + + FFT_Spins( spins ); + + // TODO: also parallelize over i_b1 + // Loop over basis atoms (i.e sublattices) and add contribution of each sublattice + for( int i_b1 = 0; i_b1 < geometry->n_cell_atoms; ++i_b1 ) + CU_FFT_Pointwise_Mult1<<<( number_of_mults + 1023 ) / 1024, 1024>>>( + ft_D_matrices.data(), ft_spins.data(), res_mult.data(), it_bounds_pointwise_mult.data(), i_b1, + inter_sublattice_lookup.data(), dipole_stride, spin_stride, Ms ); + CU_CHECK_AND_SYNC(); + FFT::batch_iFour_3D( fft_plan_reverse ); + // scalar * delta = geometry->cell_size.data(); + int sublattice_size = it_bounds_write_dipole[0] * it_bounds_write_dipole[1] * it_bounds_write_dipole[2]; + CU_Write_FFT_Gradients1<<<( geometry->nos + 1023 ) / 1024, 1024>>>( + res_iFFT.data(), gradient.data(), spin_stride, it_bounds_write_gradients.data(), geometry->n_cell_atoms, + geometry->mu_s.data(), sublattice_size, Ms ); + CU_CHECK_AND_SYNC(); +} // end Field_DipoleDipole + +void Hamiltonian_Micromagnetic::Gradient_DDI_Direct( const vectorfield & spins, vectorfield & gradient ) +{ + int tupel1[3]; + int tupel2[3]; + int sublattice_size = it_bounds_write_dipole[0] * it_bounds_write_dipole[1] * it_bounds_write_dipole[2]; + // prefactor of ddi interaction + // scalar mult = 2.0133545*1e-28 * 0.057883817555 * 0.057883817555 / (4 + // * 3.141592653589793238462643383279502884197169399375105820974 * 1e-30); + scalar mult = 1 / ( 4 * 3.141592653589793238462643383279502884197169399375105820974 ); + scalar m0 = ( 4 * 3.141592653589793238462643383279502884197169399375105820974 ) * 1e-7; + int img_a = boundary_conditions[0] == 0 ? 0 : ddi_n_periodic_images[0]; + int img_b = boundary_conditions[1] == 0 ? 0 : ddi_n_periodic_images[1]; + int img_c = boundary_conditions[2] == 0 ? 0 : ddi_n_periodic_images[2]; + scalar * delta = geometry->cell_size.data(); + for( int idx1 = 0; idx1 < geometry->nos; idx1++ ) + { + double kk = 0; + for( int idx2 = 0; idx2 < geometry->nos; idx2++ ) + { + int a1 = idx1 % ( it_bounds_write_spins[1] ); + int b1 = ( (int)( idx1 / it_bounds_write_spins[1] ) ) % ( it_bounds_write_spins[2] ); + int c1 = (int)idx1 / ( it_bounds_write_spins[1] * it_bounds_write_spins[2] ); + int a2 = idx2 % ( it_bounds_write_spins[1] ); + int b2 = ( (int)( idx2 / it_bounds_write_spins[1] ) ) % ( it_bounds_write_spins[2] ); + int c2 = (int)idx2 / ( it_bounds_write_spins[1] * it_bounds_write_spins[2] ); + /*int a_idx = a < n_cells[0] ? a : a - iteration_bounds[0]; + int b_idx = b < n_cells[1] ? b : b - iteration_bounds[1]; + int c_idx = c < n_cells[2] ? c : c - iteration_bounds[2];*/ + int a_idx = a1 - a2; + int b_idx = b1 - b2; + int c_idx = c1 - c2; + if( ( a_idx == 0 ) && ( b_idx == 0 ) && ( c_idx == 0 ) ) + { + // printf("test\n"); + // continue; + } + // printf("%d %d %d\n", a_idx,b_idx,c_idx); + /*if ((a_idx==20) || (b_idx==20) || (c_idx==1)){ + //printf("test1\n"); + //if (c_idx!=1) + //printf("%d %d %d %d\n", a_idx, b_idx, c_idx, dipole_stride.comp); + continue; + }*/ + // scalar delta[3] = { 3,3,0.3 }; + // int idx = b_inter * dipole_stride.basis + a * dipole_stride.a + b * dipole_stride.b + c * dipole_stride.c; + scalar Dxx = 0, Dxy = 0, Dxz = 0, Dyy = 0, Dyz = 0, Dzz = 0; + + Vector3 cell_sizes = { geometry->lattice_constant * geometry->bravais_vectors[0].norm(), + geometry->lattice_constant * geometry->bravais_vectors[1].norm(), + geometry->lattice_constant * geometry->bravais_vectors[2].norm() }; + // asa + for( int i = 0; i < 2; i++ ) + { + for( int j = 0; j < 2; j++ ) + { + for( int k = 0; k < 2; k++ ) + { + double r = sqrt( + ( a_idx + i - 0.5f ) * ( a_idx + i - 0.5f ) * cell_sizes[0] * cell_sizes[0] + + ( b_idx + j - 0.5f ) * ( b_idx + j - 0.5f ) * cell_sizes[1] * cell_sizes[1] + + ( c_idx + k - 0.5f ) * ( c_idx + k - 0.5f ) * cell_sizes[2] * cell_sizes[2] ); + Dxx += mult * pow( -1.0f, i + j + k ) + * atan( + ( ( c_idx + k - 0.5f ) * ( b_idx + j - 0.5f ) * cell_sizes[1] * cell_sizes[2] + / cell_sizes[0] / r / ( a_idx + i - 0.5f ) ) ); + // fft_dipole_inputs[idx + 1 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * + // log(abs(((c_idx + k - 0.5f)* cell_sizes[2] + r)/((c_idx + k - 0.5f)* cell_sizes[2] - r))); + // fft_dipole_inputs[idx + 2 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * + // log(abs(((b_idx + j - 0.5f)* cell_sizes[1] + r)/((b_idx + j - 0.5f)* cell_sizes[1] - r))); + Dxy -= mult * pow( -1.0f, i + j + k ) * log( ( ( ( c_idx + k - 0.5f ) * cell_sizes[2] + r ) ) ); + Dxz -= mult * pow( -1.0f, i + j + k ) * log( ( ( ( b_idx + j - 0.5f ) * cell_sizes[1] + r ) ) ); + + Dyy += mult * pow( -1.0f, i + j + k ) + * atan( + ( ( a_idx + i - 0.5f ) * ( c_idx + k - 0.5f ) * cell_sizes[2] * cell_sizes[0] + / cell_sizes[1] / r / ( b_idx + j - 0.5f ) ) ); + // fft_dipole_inputs[idx + 4 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * + // log(abs(((a_idx + i - 0.5f)* cell_sizes[0] + r)/((a_idx + i - 0.5f)* cell_sizes[0] - r))); + Dyz -= mult * pow( -1.0f, i + j + k ) * log( ( ( ( a_idx + i - 0.5f ) * cell_sizes[0] + r ) ) ); + Dzz += mult * pow( -1.0f, i + j + k ) + * atan( + ( ( b_idx + j - 0.5f ) * ( a_idx + i - 0.5f ) * cell_sizes[0] * cell_sizes[1] + / cell_sizes[2] / r / ( c_idx + k - 0.5f ) ) ); + } + } + } /* + Dxx=Nii(a_idx*delta[0],b_idx*delta[1],c_idx*delta[2],delta[0],delta[1],delta[2]); + Dxy=Nij(a_idx*delta[0],b_idx*delta[1],c_idx*delta[2],delta[0],delta[1],delta[2]); + Dxz=Nij(a_idx*delta[0],c_idx*delta[2], b_idx*delta[1],delta[0],delta[2],delta[1]); + Dyy=Nii(b_idx*delta[1],a_idx*delta[0],c_idx*delta[2],delta[1],delta[0],delta[2]); + Dyz=Nij(b_idx*delta[1],c_idx*delta[2], b_idx*delta[1],delta[1],delta[2],delta[0]); + Dzz=Nii(c_idx*delta[2],a_idx*delta[0],b_idx*delta[1],delta[2],delta[0],delta[1]);*/ + if( idx1 == 42 ) + { + if( ( a_idx == 0 ) && ( b_idx == 0 ) && ( c_idx == 0 ) ) + { + printf( "000 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n", Dxx, Dxy, Dxz, Dyy, Dyz, Dzz ); + } + if( ( a_idx == 1 ) && ( b_idx == 0 ) && ( c_idx == 0 ) ) + { + printf( "100 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n", Dxx, Dxy, Dxz, Dyy, Dyz, Dzz ); + } + if( ( a_idx == 0 ) && ( b_idx == 1 ) && ( c_idx == 0 ) ) + { + printf( "010 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n", Dxx, Dxy, Dxz, Dyy, Dyz, Dzz ); + } + if( ( a_idx == -1 ) && ( b_idx == 1 ) && ( c_idx == 0 ) ) + { + printf( "-110 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n", Dxx, Dxy, Dxz, Dyy, Dyz, Dzz ); + } + if( ( a_idx == 1 ) && ( b_idx == 1 ) && ( c_idx == 0 ) ) + { + printf( "110 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n", Dxx, Dxy, Dxz, Dyy, Dyz, Dzz ); + } + if( ( a_idx == 2 ) && ( b_idx == 0 ) && ( c_idx == 0 ) ) + { + printf( "200 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n", Dxx, Dxy, Dxz, Dyy, Dyz, Dzz ); + } + if( ( a_idx == 0 ) && ( b_idx == 2 ) && ( c_idx == 0 ) ) + { + printf( "020 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n", Dxx, Dxy, Dxz, Dyy, Dyz, Dzz ); + } + if( ( a_idx == 2 ) && ( b_idx == 2 ) && ( c_idx == 0 ) ) + { + printf( "220 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n", Dxx, Dxy, Dxz, Dyy, Dyz, Dzz ); + } + if( ( a_idx == 2 ) && ( b_idx == -2 ) && ( c_idx == 0 ) ) + { + printf( "2-20 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n", Dxx, Dxy, Dxz, Dyy, Dyz, Dzz ); } - } - /*if (A_is_nondiagonal == true) { - //xy - int ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[0]); - int ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); - int ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2]); - int ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); - - if (ispin_right == -1) { - ispin_right = ispin; - } - if (ispin_left == -1) { - ispin_left = ispin; - } - if (ispin_top == -1) { - ispin_top = ispin; - } - if (ispin_bottom == -1) { - ispin_bottom = ispin; - } - gradient[ispin][0] -= 2 * exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); - gradient[ispin][0] -= 2 * exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); - gradient[ispin][1] -= 2 * exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); - gradient[ispin][1] -= 2 * exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); - gradient[ispin][2] -= 2 * exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); - gradient[ispin][2] -= 2 * exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); - - //xz - ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[0]); - ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); - ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[4]); - ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); - - if (ispin_right == -1) { - ispin_right = ispin; - } - if (ispin_left == -1) { - ispin_left = ispin; - } - if (ispin_top == -1) { - ispin_top = ispin; - } - if (ispin_bottom == -1) { - ispin_bottom = ispin; - } - gradient[ispin][0] -= 2 * exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); - gradient[ispin][0] -= 2 * exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); - gradient[ispin][1] -= 2 * exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); - gradient[ispin][1] -= 2 * exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); - gradient[ispin][2] -= 2 * exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - gradient[ispin][2] -= 2 * exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - - //yz - ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[2]); - ispin_left = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); - ispin_top = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[4]); - ispin_bottom = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); - - if (ispin_right == -1) { - ispin_right = ispin; - } - if (ispin_left == -1) { - ispin_left = ispin; - } - if (ispin_top == -1) { - ispin_top = ispin; - } - if (ispin_bottom == -1) { - ispin_bottom = ispin; - } - gradient[ispin][0] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); - gradient[ispin][0] -= 2 * exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); - gradient[ispin][1] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); - gradient[ispin][1] -= 2 * exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); - gradient[ispin][2] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - gradient[ispin][2] -= 2 * exchange_tensor(2, 1) / Ms * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - - }*/ - - } - } - void Hamiltonian_Micromagnetic::Gradient_Exchange(const vectorfield & spins, vectorfield & gradient) - { - int size = geometry->n_cells_total; - scalar * delta = geometry->cell_size.data(); - CU_Gradient_Exchange1 << <(size + 1023) / 1024, 1024 >> > (spins.data(), this->geometry->atom_types.data(), boundary_conditions.data(), geometry->n_cells.data(), geometry->n_cell_atoms, - this->neigh.size(), this->neigh.data(), gradient.data(), size, A_is_nondiagonal, exchange_tensor, delta, this->Ms ); - CU_CHECK_AND_SYNC(); - } - - __global__ void CU_Spatial_Gradient(const Vector3 * spins, const int * atom_types, const int * boundary_conditions, const int * n_cells, int n_cell_atoms, - int n_pairs, const Pair * neigh, Matrix3 * spatial_gradient, size_t size, scalar * delta, scalar Ms) - { - /* - dn1/dr1 dn1/dr2 dn1/dr3 - dn2/dr1 dn2/dr2 dn2/dr3 - dn3/dr1 dn3/dr2 dn3/dr3 - */ - int bc[3] = { boundary_conditions[0], boundary_conditions[1], boundary_conditions[2] }; - int nc[3] = { n_cells[0], n_cells[1], n_cells[2] }; - - for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; - icell < size; - icell += blockDim.x * gridDim.x) + // printf("x=%f y=%f z=%f\n",spins[idx2][0],spins[idx2][1],spins[idx2][2]); + } + kk += Dxx; + gradient[idx1][0] -= ( Dxx * spins[idx2][0] + Dxy * spins[idx2][1] + Dxz * spins[idx2][2] ) * Ms * m0; + gradient[idx1][1] -= ( Dxy * spins[idx2][0] + Dyy * spins[idx2][1] + Dyz * spins[idx2][2] ) * Ms * m0; + gradient[idx1][2] -= ( Dxz * spins[idx2][0] + Dyz * spins[idx2][1] + Dzz * spins[idx2][2] ) * Ms * m0; + } + if( idx1 == 30 ) { - for (unsigned int i = 0; i < 3; ++i) - { - int icell_plus = cu_idx_from_pair(icell, bc, nc, n_cell_atoms, atom_types, neigh[2*i]); - int icell_minus = cu_idx_from_pair(icell, bc, nc, n_cell_atoms, atom_types, neigh[2*i + 1]); + // printf("x=%f y=%f z=%f\n",spins[idx1][0],spins[idx1][1],spins[idx1][2]); + // printf("kk=%f gx=%f gy=%f gz=%f\n",kk, gradient[idx1][0]/8e5/m0,gradient[idx1][1],gradient[idx1][2]); + } + } +} +__global__ void CU_Write_FFT_Spin_Input1( + FFT::FFT_real_type * fft_spin_inputs, const Vector3 * spins, int * iteration_bounds, + FFT::StrideContainer spin_stride, scalar * mu_s ) +{ + int nos = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2] * iteration_bounds[3]; + int tupel[4]; + int idx_pad; + for( int idx_orig = blockIdx.x * blockDim.x + threadIdx.x; idx_orig < nos; idx_orig += blockDim.x * gridDim.x ) + { + cu_tupel_from_idx( idx_orig, tupel, iteration_bounds, 4 ); // tupel now is {ib, a, b, c} + idx_pad = tupel[0] * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + + tupel[3] * spin_stride.c; + fft_spin_inputs[idx_pad] = spins[idx_orig][0]; + fft_spin_inputs[idx_pad + 1 * spin_stride.comp] = spins[idx_orig][1]; + fft_spin_inputs[idx_pad + 2 * spin_stride.comp] = spins[idx_orig][2]; + // printf("%f %f\n",fft_spin_inputs[idx_pad], fft_spin_inputs[idx_pad+30]); + } +} - if( icell_plus >= 0 || icell_minus >= 0 ) +void Hamiltonian_Micromagnetic::FFT_Spins( const vectorfield & spins ) +{ + CU_Write_FFT_Spin_Input1<<<( geometry->nos + 1023 ) / 1024, 1024>>>( + fft_plan_spins.real_ptr.data(), spins.data(), it_bounds_write_spins.data(), spin_stride, + geometry->mu_s.data() ); + CU_CHECK_AND_SYNC(); + FFT::batch_Four_3D( fft_plan_spins ); +} +__global__ void CU_Write_FFT_Dipole_Input1( + FFT::FFT_real_type * fft_dipole_inputs, int * iteration_bounds, const Vector3 * translation_vectors, + int n_cell_atoms, Vector3 * cell_atom_translations, int * n_cells, int * inter_sublattice_lookup, int * img, + FFT::StrideContainer dipole_stride, const Vector3 cell_lengths ) +{ + int tupel[3]; + int sublattice_size = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2]; + // prefactor of ddi interaction + // scalar mult = 2.0133545*1e-28 * 0.057883817555 * 0.057883817555 / (4 + // * 3.141592653589793238462643383279502884197169399375105820974 * 1e-30); scalar mult = 1 / (4 + // * 3.141592653589793238462643383279502884197169399375105820974); + scalar mult = 1; + for( int i = blockIdx.x * blockDim.x + threadIdx.x; i < sublattice_size; i += blockDim.x * gridDim.x ) + { + cu_tupel_from_idx( i, tupel, iteration_bounds, 3 ); // tupel now is {a, b, c} + auto & a = tupel[0]; + auto & b = tupel[1]; + auto & c = tupel[2]; + /*if ((a>198)||(b>198)||(c>198)){ + printf("%d %d %d\n", a,b,c); + }*/ + /*int a_idx = a < n_cells[0] ? a : a - iteration_bounds[0]; + int b_idx = b < n_cells[1] ? b : b - iteration_bounds[1]; + int c_idx = c < n_cells[2] ? c : c - iteration_bounds[2];*/ + /*int a_idx = a +1 - (int)iteration_bounds[0]/2; + int b_idx = b +1- (int)iteration_bounds[1]/2; + int c_idx = c +1- (int)iteration_bounds[2]/2;*/ + int a_idx = a < n_cells[0] ? a : a - iteration_bounds[0]; + int b_idx = b < n_cells[1] ? b : b - iteration_bounds[1]; + int c_idx = c < n_cells[2] ? c : c - iteration_bounds[2]; + + int idx = a * dipole_stride.a + b * dipole_stride.b + c * dipole_stride.c; + + for( int i = 0; i < 2; i++ ) + { + for( int j = 0; j < 2; j++ ) + { + for( int k = 0; k < 2; k++ ) { - if( icell_plus == -1 ) - icell_plus = icell; - if( icell_minus == -1 ) - icell_minus = icell; - - spatial_gradient[icell].col(i) += (spins[icell_plus] - spins[icell_minus]) / (2*delta[i]); + double r = sqrt( + ( a_idx + i - 0.5f ) * ( a_idx + i - 0.5f ) * cell_lengths[0] * cell_lengths[0] + + ( b_idx + j - 0.5f ) * ( b_idx + j - 0.5f ) * cell_lengths[1] * cell_lengths[1] + + ( c_idx + k - 0.5f ) * ( c_idx + k - 0.5f ) * cell_lengths[2] * cell_lengths[2] ); + fft_dipole_inputs[idx] += mult * pow( -1.0f, i + j + k ) + * atan( + ( ( c_idx + k - 0.5f ) * ( b_idx + j - 0.5f ) * cell_lengths[1] + * cell_lengths[2] / cell_lengths[0] / r / ( a_idx + i - 0.5f ) ) ); + // fft_dipole_inputs[idx + 1 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * + // log(abs(((c_idx + k - 0.5f)* cell_lengths[2] + r)/((c_idx + k - 0.5f)* cell_lengths[2] - r))); + // fft_dipole_inputs[idx + 2 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * + // log(abs(((b_idx + j - 0.5f)* cell_lengths[1] + r)/((b_idx + j - 0.5f)* cell_lengths[1] - r))); + fft_dipole_inputs[idx + 1 * dipole_stride.comp] + -= mult * pow( -1.0f, i + j + k ) * log( ( ( ( c_idx + k - 0.5f ) * cell_lengths[2] + r ) ) ); + fft_dipole_inputs[idx + 2 * dipole_stride.comp] + -= mult * pow( -1.0f, i + j + k ) * log( ( ( ( b_idx + j - 0.5f ) * cell_lengths[1] + r ) ) ); + + fft_dipole_inputs[idx + 3 * dipole_stride.comp] + += mult * pow( -1.0f, i + j + k ) + * atan( + ( ( a_idx + i - 0.5f ) * ( c_idx + k - 0.5f ) * cell_lengths[2] * cell_lengths[0] + / cell_lengths[1] / r / ( b_idx + j - 0.5f ) ) ); + // fft_dipole_inputs[idx + 4 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * + // log(abs(((a_idx + i - 0.5f)* cell_lengths[0] + r)/((a_idx + i - 0.5f)* cell_lengths[0] - r))); + fft_dipole_inputs[idx + 4 * dipole_stride.comp] + -= mult * pow( -1.0f, i + j + k ) * log( ( ( ( a_idx + i - 0.5f ) * cell_lengths[0] + r ) ) ); + fft_dipole_inputs[idx + 5 * dipole_stride.comp] + += mult * pow( -1.0f, i + j + k ) + * atan( + ( ( b_idx + j - 0.5f ) * ( a_idx + i - 0.5f ) * cell_lengths[0] * cell_lengths[1] + / cell_lengths[2] / r / ( c_idx + k - 0.5f ) ) ); } + } + } - } - } - } - - void Hamiltonian_Micromagnetic::Spatial_Gradient(const vectorfield & spins) - { - int size = geometry->n_cells_total; - CU_Spatial_Gradient << <(size + 1023) / 1024, 1024 >> > (spins.data(), this->geometry->atom_types.data(), boundary_conditions.data(), geometry->n_cells.data(), geometry->n_cell_atoms, - this->neigh.size(), this->neigh.data(), spatial_gradient.data(), size, geometry->cell_size.data(), this->Ms); - CU_CHECK_AND_SYNC(); - } - - __global__ void CU_Gradient_DMI1(const Vector3 * spins, Vector3 * gradient, Matrix3 * spatial_gradient, size_t size, Matrix3 dmi_tensor, scalar Ms) - { - for (auto icell = blockIdx.x * blockDim.x + threadIdx.x; - icell < size; - icell += blockDim.x * gridDim.x) - { - for (unsigned int i = 0; i < 3; ++i) - { - gradient[icell][0] -= 4 * C::mu_B * (dmi_tensor(1, i) * spatial_gradient[icell](2, i) - 2 * dmi_tensor(2, i) * spatial_gradient[icell](1, i)) / Ms; - gradient[icell][1] -= 4 * C::mu_B * (dmi_tensor(2, i) * spatial_gradient[icell](0, i) - 2 * dmi_tensor(0, i) * spatial_gradient[icell](2, i)) / Ms; - gradient[icell][2] -= 4 * C::mu_B * (dmi_tensor(0, i) * spatial_gradient[icell](1, i) - 2 * dmi_tensor(1, i) * spatial_gradient[icell](0, i)) / Ms; - } - } - } - void Hamiltonian_Micromagnetic::Gradient_DMI(const vectorfield & spins, vectorfield & gradient) - { - int size = geometry->n_cells_total; - CU_Gradient_DMI1 << <(size + 1023) / 1024, 1024 >> > (spins.data(), gradient.data(), spatial_gradient.data(), size, dmi_tensor, this->Ms); - CU_CHECK_AND_SYNC(); - } - - - __global__ void CU_FFT_Pointwise_Mult1(FFT::FFT_cpx_type * ft_D_matrices, FFT::FFT_cpx_type * ft_spins, FFT::FFT_cpx_type * res_mult, int* iteration_bounds, int i_b1, int* inter_sublattice_lookup, FFT::StrideContainer dipole_stride, FFT::StrideContainer spin_stride, const scalar Ms) - { - int n = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2] * iteration_bounds[3]; - int tupel[4]; - int idx_b1, idx_b2, idx_d; - - for (int ispin = blockIdx.x * blockDim.x + threadIdx.x; ispin < n; ispin += blockDim.x * gridDim.x) - { - cu_tupel_from_idx(ispin, tupel, iteration_bounds, 4); // tupel now is {i_b2, a, b, c} - - int& b_inter = inter_sublattice_lookup[i_b1 + tupel[0] * iteration_bounds[0]]; - - idx_b1 = i_b1 * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + tupel[3] * spin_stride.c; - idx_b2 = tupel[0] * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + tupel[3] * spin_stride.c; - idx_d = b_inter * dipole_stride.basis + tupel[1] * dipole_stride.a + tupel[2] * dipole_stride.b + tupel[3] * dipole_stride.c; - - auto& fs_x = ft_spins[idx_b2]; - auto& fs_y = ft_spins[idx_b2 + 1 * spin_stride.comp]; - auto& fs_z = ft_spins[idx_b2 + 2 * spin_stride.comp]; - - auto& fD_xx = ft_D_matrices[idx_d]; - auto& fD_xy = ft_D_matrices[idx_d + 1 * dipole_stride.comp]; - auto& fD_xz = ft_D_matrices[idx_d + 2 * dipole_stride.comp]; - auto& fD_yy = ft_D_matrices[idx_d + 3 * dipole_stride.comp]; - auto& fD_yz = ft_D_matrices[idx_d + 4 * dipole_stride.comp]; - auto& fD_zz = ft_D_matrices[idx_d + 5 * dipole_stride.comp]; - - if (tupel[0] == 0) - { - res_mult[idx_b1].x = FFT::mult3D(fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z).x; - res_mult[idx_b1].y = FFT::mult3D(fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z).y; - res_mult[idx_b1 + 1 * spin_stride.comp].x = FFT::mult3D(fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z).x; - res_mult[idx_b1 + 1 * spin_stride.comp].y = FFT::mult3D(fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z).y; - res_mult[idx_b1 + 2 * spin_stride.comp].x = FFT::mult3D(fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z).x; - res_mult[idx_b1 + 2 * spin_stride.comp].y = FFT::mult3D(fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z).y; - } - else { - atomicAdd(&res_mult[idx_b1].x, FFT::mult3D(fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z).x); - atomicAdd(&res_mult[idx_b1].y, FFT::mult3D(fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z).y); - atomicAdd(&res_mult[idx_b1 + 1 * spin_stride.comp].x, FFT::mult3D(fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z).x); - atomicAdd(&res_mult[idx_b1 + 1 * spin_stride.comp].y, FFT::mult3D(fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z).y); - atomicAdd(&res_mult[idx_b1 + 2 * spin_stride.comp].x, FFT::mult3D(fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z).x); - atomicAdd(&res_mult[idx_b1 + 2 * spin_stride.comp].y, FFT::mult3D(fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z).y); - } - } - } - - __global__ void CU_Write_FFT_Gradients1(const FFT::FFT_real_type * resiFFT, Vector3 * gradient, FFT::StrideContainer spin_stride, int * iteration_bounds, int n_cell_atoms, scalar * mu_s, int sublattice_size, const scalar Ms) - { - int nos = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2] * iteration_bounds[3]; - int tupel[4]; - int idx_pad; - for (int idx_orig = blockIdx.x * blockDim.x + threadIdx.x; idx_orig < nos; idx_orig += blockDim.x * gridDim.x) - { - - cu_tupel_from_idx(idx_orig, tupel, iteration_bounds, 4); //tupel now is {ib, a, b, c} - idx_pad = tupel[0] * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + tupel[3] * spin_stride.c; - //printf("%d %f %f\n", idx_orig, resiFFT[idx_pad],gradient[idx_orig][0]); - gradient[idx_orig][0] -= C::mu_B * resiFFT[idx_pad]*Ms*1e-7/(sublattice_size); - gradient[idx_orig][1] -= C::mu_B * resiFFT[idx_pad + 1 * spin_stride.comp]*Ms*1e-7/(sublattice_size); - gradient[idx_orig][2] -= C::mu_B * resiFFT[idx_pad + 2 * spin_stride.comp]*Ms*1e-7/(sublattice_size); - } - } - - void Hamiltonian_Micromagnetic::Gradient_DDI(const vectorfield & spins, vectorfield & gradient) - { - //this->Gradient_DDI_Direct(spins, gradient); - this->Gradient_DDI_FFT(spins, gradient); - /* - if (this->ddi_method == DDI_Method::FFT) - { - printf("sasas"); - this->Gradient_DDI_FFT(spins, gradient); - } - else if (this->ddi_method == DDI_Method::Cutoff) - { - // TODO: Merge these implementations in the future - if (this->ddi_cutoff_radius >= 0) - this->Gradient_DDI_Cutoff(spins, gradient); - else - this->Gradient_DDI_Direct(spins, gradient); - } -*/ - } - void Hamiltonian_Micromagnetic::Gradient_DDI_Cutoff(const vectorfield & spins, vectorfield & gradient) - { - // TODO - } - void Hamiltonian_Micromagnetic::Gradient_DDI_FFT(const vectorfield & spins, vectorfield & gradient) - { - auto& ft_D_matrices = transformed_dipole_matrices; - - auto& ft_spins = fft_plan_spins.cpx_ptr; - - auto& res_iFFT = fft_plan_reverse.real_ptr; - auto& res_mult = fft_plan_reverse.cpx_ptr; - - int number_of_mults = it_bounds_pointwise_mult[0] * it_bounds_pointwise_mult[1] * it_bounds_pointwise_mult[2] * it_bounds_pointwise_mult[3]; - - FFT_Spins(spins); - - // TODO: also parallelize over i_b1 - // Loop over basis atoms (i.e sublattices) and add contribution of each sublattice - for (int i_b1 = 0; i_b1 < geometry->n_cell_atoms; ++i_b1) - CU_FFT_Pointwise_Mult1<<<(number_of_mults+1023)/1024, 1024>>>(ft_D_matrices.data(), ft_spins.data(), res_mult.data(), it_bounds_pointwise_mult.data(), i_b1, inter_sublattice_lookup.data(), dipole_stride, spin_stride, Ms); - CU_CHECK_AND_SYNC(); - FFT::batch_iFour_3D(fft_plan_reverse); - // scalar * delta = geometry->cell_size.data(); - int sublattice_size = it_bounds_write_dipole[0] * it_bounds_write_dipole[1] * it_bounds_write_dipole[2]; - CU_Write_FFT_Gradients1<<<(geometry->nos+1023)/1024, 1024>>>(res_iFFT.data(), gradient.data(), spin_stride, it_bounds_write_gradients.data(), geometry->n_cell_atoms, geometry->mu_s.data(), sublattice_size, Ms); - CU_CHECK_AND_SYNC(); - }//end Field_DipoleDipole - - void Hamiltonian_Micromagnetic::Gradient_DDI_Direct(const vectorfield & spins, vectorfield & gradient) - { - int tupel1[3]; - int tupel2[3]; - int sublattice_size = it_bounds_write_dipole[0] * it_bounds_write_dipole[1] * it_bounds_write_dipole[2]; - //prefactor of ddi interaction - //scalar mult = 2.0133545*1e-28 * 0.057883817555 * 0.057883817555 / (4 * 3.141592653589793238462643383279502884197169399375105820974 * 1e-30); - scalar mult = 1 / (4 * 3.141592653589793238462643383279502884197169399375105820974); - scalar m0 = (4 * 3.141592653589793238462643383279502884197169399375105820974)*1e-7; - int img_a = boundary_conditions[0] == 0 ? 0 : ddi_n_periodic_images[0]; - int img_b = boundary_conditions[1] == 0 ? 0 : ddi_n_periodic_images[1]; - int img_c = boundary_conditions[2] == 0 ? 0 : ddi_n_periodic_images[2]; - scalar * delta = geometry->cell_size.data(); - for (int idx1 = 0; idx1 < geometry->nos; idx1++) - { - double kk=0; - for (int idx2 = 0; idx2 < geometry->nos; idx2++) - { - int a1 = idx1%(it_bounds_write_spins[1]); - int b1 = ((int)(idx1/it_bounds_write_spins[1]))%(it_bounds_write_spins[2]); - int c1 = (int)idx1/(it_bounds_write_spins[1]*it_bounds_write_spins[2]); - int a2 = idx2%(it_bounds_write_spins[1]); - int b2 = ((int)(idx2/it_bounds_write_spins[1]))%(it_bounds_write_spins[2]); - int c2 = (int)idx2/(it_bounds_write_spins[1]*it_bounds_write_spins[2]); - /*int a_idx = a < n_cells[0] ? a : a - iteration_bounds[0]; - int b_idx = b < n_cells[1] ? b : b - iteration_bounds[1]; - int c_idx = c < n_cells[2] ? c : c - iteration_bounds[2];*/ - int a_idx = a1-a2; - int b_idx = b1-b2; - int c_idx = c1-c2; - if ((a_idx==0) && (b_idx==0) && (c_idx==0)){ - //printf("test\n"); - //continue; - } - //printf("%d %d %d\n", a_idx,b_idx,c_idx); - /*if ((a_idx==20) || (b_idx==20) || (c_idx==1)){ - //printf("test1\n"); - //if (c_idx!=1) - //printf("%d %d %d %d\n", a_idx, b_idx, c_idx, dipole_stride.comp); - continue; - }*/ - //scalar delta[3] = { 3,3,0.3 }; - //int idx = b_inter * dipole_stride.basis + a * dipole_stride.a + b * dipole_stride.b + c * dipole_stride.c; - scalar Dxx = 0, Dxy = 0, Dxz = 0, Dyy = 0, Dyz = 0, Dzz = 0; - - Vector3 cell_sizes = {geometry->lattice_constant * geometry->bravais_vectors[0].norm(), - geometry->lattice_constant * geometry->bravais_vectors[1].norm(), - geometry->lattice_constant * geometry->bravais_vectors[2].norm()}; - //asa - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 2; j++) { - for (int k = 0; k < 2; k++) { - double r = sqrt((a_idx + i - 0.5f)*(a_idx + i - 0.5f)*cell_sizes[0]* cell_sizes[0] + (b_idx + j - 0.5f)*(b_idx + j-0.5f)*cell_sizes[1] * cell_sizes[1] + (c_idx + k - 0.5f)*(c_idx + k - 0.5f)*cell_sizes[2] * cell_sizes[2]); - Dxx += mult * pow(-1.0f, i + j + k) * atan(((c_idx + k-0.5f) * (b_idx + j - 0.5f) * cell_sizes[1]*cell_sizes[2]/cell_sizes[0] / r / (a_idx + i - 0.5f))); - //fft_dipole_inputs[idx + 1 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * log(abs(((c_idx + k - 0.5f)* cell_sizes[2] + r)/((c_idx + k - 0.5f)* cell_sizes[2] - r))); - //fft_dipole_inputs[idx + 2 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * log(abs(((b_idx + j - 0.5f)* cell_sizes[1] + r)/((b_idx + j - 0.5f)* cell_sizes[1] - r))); - Dxy -= mult * pow(-1.0f, i + j + k) * log((((c_idx + k - 0.5f)* cell_sizes[2] + r))); - Dxz -= mult * pow(-1.0f, i + j + k) * log((((b_idx + j - 0.5f)* cell_sizes[1] + r))); - - Dyy += mult * pow(-1.0f, i + j + k) * atan(((a_idx + i-0.5f) * (c_idx + k - 0.5f) * cell_sizes[2]*cell_sizes[0]/cell_sizes[1] / r / (b_idx + j - 0.5f))); - //fft_dipole_inputs[idx + 4 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * log(abs(((a_idx + i - 0.5f)* cell_sizes[0] + r)/((a_idx + i - 0.5f)* cell_sizes[0] - r))); - Dyz -= mult * pow(-1.0f, i + j + k) * log((((a_idx + i - 0.5f)* cell_sizes[0] + r))); - Dzz += mult * pow(-1.0f, i + j + k) * atan(((b_idx + j-0.5f) * (a_idx + i - 0.5f) * cell_sizes[0]*cell_sizes[1]/cell_sizes[2] / r / (c_idx + k - 0.5f))); - - } - } - }/* - Dxx=Nii(a_idx*delta[0],b_idx*delta[1],c_idx*delta[2],delta[0],delta[1],delta[2]); - Dxy=Nij(a_idx*delta[0],b_idx*delta[1],c_idx*delta[2],delta[0],delta[1],delta[2]); - Dxz=Nij(a_idx*delta[0],c_idx*delta[2], b_idx*delta[1],delta[0],delta[2],delta[1]); - Dyy=Nii(b_idx*delta[1],a_idx*delta[0],c_idx*delta[2],delta[1],delta[0],delta[2]); - Dyz=Nij(b_idx*delta[1],c_idx*delta[2], b_idx*delta[1],delta[1],delta[2],delta[0]); - Dzz=Nii(c_idx*delta[2],a_idx*delta[0],b_idx*delta[1],delta[2],delta[0],delta[1]);*/ - if (idx1==42){ - if ((a_idx==0) && (b_idx==0) && (c_idx==0)){ - printf("000 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); - } - if ((a_idx==1) && (b_idx==0) && (c_idx==0)){ - printf("100 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); - } - if ((a_idx==0) && (b_idx==1) && (c_idx==0)){ - printf("010 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); - } - if ((a_idx==-1) && (b_idx==1) && (c_idx==0)){ - printf("-110 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); - } - if ((a_idx==1) && (b_idx==1) && (c_idx==0)){ - printf("110 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); - } - if ((a_idx==2) && (b_idx==0) && (c_idx==0)){ - printf("200 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); - } - if ((a_idx==0) && (b_idx==2) && (c_idx==0)){ - printf("020 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); - } - if ((a_idx==2) && (b_idx==2) && (c_idx==0)){ - printf("220 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); - } - if ((a_idx==2) && (b_idx==-2) && (c_idx==0)){ - printf("2-20 Dxx=%f Dxy=%f Dxz=%f Dyy=%f Dyz=%f Dzz=%f\n",Dxx,Dxy,Dxz,Dyy,Dyz, Dzz); - } - //printf("x=%f y=%f z=%f\n",spins[idx2][0],spins[idx2][1],spins[idx2][2]); - } - kk+=Dxx; - gradient[idx1][0] -= (Dxx * spins[idx2][0] + Dxy * spins[idx2][1] + Dxz * spins[idx2][2]) * Ms*m0; - gradient[idx1][1] -= (Dxy * spins[idx2][0] + Dyy * spins[idx2][1] + Dyz * spins[idx2][2]) * Ms*m0; - gradient[idx1][2] -= (Dxz * spins[idx2][0] + Dyz * spins[idx2][1] + Dzz * spins[idx2][2]) * Ms*m0; - } - if (idx1==30){ - //printf("x=%f y=%f z=%f\n",spins[idx1][0],spins[idx1][1],spins[idx1][2]); - //printf("kk=%f gx=%f gy=%f gz=%f\n",kk, gradient[idx1][0]/8e5/m0,gradient[idx1][1],gradient[idx1][2]); - - } - - } - } - __global__ void CU_Write_FFT_Spin_Input1(FFT::FFT_real_type* fft_spin_inputs, const Vector3 * spins, int * iteration_bounds, FFT::StrideContainer spin_stride, scalar * mu_s) - { - int nos = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2] * iteration_bounds[3]; - int tupel[4]; - int idx_pad; - for (int idx_orig = blockIdx.x * blockDim.x + threadIdx.x; idx_orig < nos; idx_orig += blockDim.x * gridDim.x) - { - cu_tupel_from_idx(idx_orig, tupel, iteration_bounds, 4); //tupel now is {ib, a, b, c} - idx_pad = tupel[0] * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + tupel[3] * spin_stride.c; - fft_spin_inputs[idx_pad] = spins[idx_orig][0]; - fft_spin_inputs[idx_pad + 1 * spin_stride.comp] = spins[idx_orig][1]; - fft_spin_inputs[idx_pad + 2 * spin_stride.comp] = spins[idx_orig][2]; - //printf("%f %f\n",fft_spin_inputs[idx_pad], fft_spin_inputs[idx_pad+30]); - } - } - - void Hamiltonian_Micromagnetic::FFT_Spins(const vectorfield & spins) - { - CU_Write_FFT_Spin_Input1 << <(geometry->nos + 1023) / 1024, 1024 >> > (fft_plan_spins.real_ptr.data(), spins.data(), it_bounds_write_spins.data(), spin_stride, geometry->mu_s.data()); - CU_CHECK_AND_SYNC(); - FFT::batch_Four_3D(fft_plan_spins); - } - __global__ void CU_Write_FFT_Dipole_Input1(FFT::FFT_real_type* fft_dipole_inputs, int* iteration_bounds, const Vector3* translation_vectors, int n_cell_atoms, Vector3* cell_atom_translations, int* n_cells, int* inter_sublattice_lookup, int* img, FFT::StrideContainer dipole_stride, const Vector3 cell_lengths) - { - int tupel[3]; - int sublattice_size = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2]; - //prefactor of ddi interaction - //scalar mult = 2.0133545*1e-28 * 0.057883817555 * 0.057883817555 / (4 * 3.141592653589793238462643383279502884197169399375105820974 * 1e-30); - //scalar mult = 1 / (4 * 3.141592653589793238462643383279502884197169399375105820974); - scalar mult = 1; - for (int i = blockIdx.x * blockDim.x + threadIdx.x; i < sublattice_size; i += blockDim.x * gridDim.x) - { - cu_tupel_from_idx(i, tupel, iteration_bounds, 3); // tupel now is {a, b, c} - auto& a = tupel[0]; - auto& b = tupel[1]; - auto& c = tupel[2]; - /*if ((a>198)||(b>198)||(c>198)){ - printf("%d %d %d\n", a,b,c); - }*/ - /*int a_idx = a < n_cells[0] ? a : a - iteration_bounds[0]; - int b_idx = b < n_cells[1] ? b : b - iteration_bounds[1]; - int c_idx = c < n_cells[2] ? c : c - iteration_bounds[2];*/ - /*int a_idx = a +1 - (int)iteration_bounds[0]/2; - int b_idx = b +1- (int)iteration_bounds[1]/2; - int c_idx = c +1- (int)iteration_bounds[2]/2;*/ - int a_idx = a < n_cells[0] ? a : a - iteration_bounds[0]; - int b_idx = b < n_cells[1] ? b : b - iteration_bounds[1]; - int c_idx = c < n_cells[2] ? c : c - iteration_bounds[2]; - - int idx = a * dipole_stride.a + b * dipole_stride.b + c * dipole_stride.c; - - for (int i = 0; i < 2; i++) { - for (int j = 0; j < 2; j++) { - for (int k = 0; k < 2; k++) { - double r = sqrt((a_idx + i - 0.5f)*(a_idx + i - 0.5f)*cell_lengths[0]* cell_lengths[0] + (b_idx + j - 0.5f)*(b_idx + j-0.5f)*cell_lengths[1] * cell_lengths[1] + (c_idx + k - 0.5f)*(c_idx + k - 0.5f)*cell_lengths[2] * cell_lengths[2]); - fft_dipole_inputs[idx] += mult * pow(-1.0f, i + j + k) * atan(((c_idx + k-0.5f) * (b_idx + j - 0.5f) * cell_lengths[1]*cell_lengths[2]/cell_lengths[0] / r / (a_idx + i - 0.5f))); - //fft_dipole_inputs[idx + 1 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * log(abs(((c_idx + k - 0.5f)* cell_lengths[2] + r)/((c_idx + k - 0.5f)* cell_lengths[2] - r))); - //fft_dipole_inputs[idx + 2 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * log(abs(((b_idx + j - 0.5f)* cell_lengths[1] + r)/((b_idx + j - 0.5f)* cell_lengths[1] - r))); - fft_dipole_inputs[idx + 1 * dipole_stride.comp] -= mult * pow(-1.0f, i + j + k) * log((((c_idx + k - 0.5f)* cell_lengths[2] + r))); - fft_dipole_inputs[idx + 2 * dipole_stride.comp] -= mult * pow(-1.0f, i + j + k) * log((((b_idx + j - 0.5f)* cell_lengths[1] + r))); - - fft_dipole_inputs[idx + 3 * dipole_stride.comp] += mult * pow(-1.0f, i + j + k) * atan(((a_idx + i-0.5f) * (c_idx + k - 0.5f) * cell_lengths[2]*cell_lengths[0]/cell_lengths[1] / r / (b_idx + j - 0.5f))); - //fft_dipole_inputs[idx + 4 * dipole_stride.comp] += -mult * pow(-1.0f, i + j + k) * log(abs(((a_idx + i - 0.5f)* cell_lengths[0] + r)/((a_idx + i - 0.5f)* cell_lengths[0] - r))); - fft_dipole_inputs[idx + 4 * dipole_stride.comp] -= mult * pow(-1.0f, i + j + k) * log((((a_idx + i - 0.5f)* cell_lengths[0] + r))); - fft_dipole_inputs[idx + 5 * dipole_stride.comp] += mult * pow(-1.0f, i + j + k) * atan(((b_idx + j-0.5f) * (a_idx + i - 0.5f) * cell_lengths[0]*cell_lengths[1]/cell_lengths[2] / r / (c_idx + k - 0.5f))); - - } - } - } - - //if (fft_dipole_inputs[idx]<-0.03) - } - } - - void Hamiltonian_Micromagnetic::FFT_Dipole_Matrices(FFT::FFT_Plan & fft_plan_dipole, int img_a, int img_b, int img_c) - { - auto& fft_dipole_inputs = fft_plan_dipole.real_ptr; - - field img = { - img_a, - img_b, - img_c - }; - - // Work around to make bravais vectors and cell_atoms available to GPU as they are currently saves as std::vectors and not fields ... - auto translation_vectors = field(); - auto cell_atom_translations = field(); - - for (int i = 0; i < 3; i++) - translation_vectors.push_back(geometry->lattice_constant * geometry->bravais_vectors[i]); - - for (int i = 0; i < geometry->n_cell_atoms; i++) - cell_atom_translations.push_back(geometry->positions[i]); - - Vector3 cell_sizes = {geometry->lattice_constant * geometry->bravais_vectors[0].norm(), - geometry->lattice_constant * geometry->bravais_vectors[1].norm(), - geometry->lattice_constant * geometry->bravais_vectors[2].norm()}; - - CU_Write_FFT_Dipole_Input1 << <(sublattice_size + 1023) / 1024, 1024 >> > - (fft_dipole_inputs.data(), it_bounds_write_dipole.data(), translation_vectors.data(), - geometry->n_cell_atoms, cell_atom_translations.data(), geometry->n_cells.data(), - inter_sublattice_lookup.data(), img.data(), dipole_stride, cell_sizes - ); - CU_CHECK_AND_SYNC(); - FFT::batch_Four_3D(fft_plan_dipole); - } - void Hamiltonian_Micromagnetic::Prepare_DDI() - { - Clean_DDI(); - - n_cells_padded.resize(3); - n_cells_padded[0] = (geometry->n_cells[0] > 1) ? 2 * geometry->n_cells[0] : 1; - n_cells_padded[1] = (geometry->n_cells[1] > 1) ? 2 * geometry->n_cells[1] : 1; - n_cells_padded[2] = (geometry->n_cells[2] > 1) ? 2 * geometry->n_cells[2] : 1; - sublattice_size = n_cells_padded[0] * n_cells_padded[1] * n_cells_padded[2]; - //printf("111 %d %d %d\n", n_cells_padded[0],n_cells_padded[1],n_cells_padded[2]); - - inter_sublattice_lookup.resize(geometry->n_cell_atoms * geometry->n_cell_atoms); - - //we dont need to transform over length 1 dims - std::vector fft_dims; - for (int i = 2; i >= 0; i--) //notice that reverse order is important! - { - if (n_cells_padded[i] > 1) - fft_dims.push_back(n_cells_padded[i]); - } - - //Count how many distinct inter-lattice contributions we need to store - n_inter_sublattice = 0; - for (int i = 0; i < geometry->n_cell_atoms; i++) - { - for (int j = 0; j < geometry->n_cell_atoms; j++) - { - if (i != 0 && i == j) continue; - n_inter_sublattice++; - } - } - printf("lex%d %d %d\n", n_inter_sublattice, fft_dims[0],fft_dims[1]); - //Set the iteration bounds for the nested for loops that are flattened in the kernels - it_bounds_write_spins = { geometry->n_cell_atoms, - geometry->n_cells[0], - geometry->n_cells[1], - geometry->n_cells[2] }; - - it_bounds_write_dipole = { n_cells_padded[0], - n_cells_padded[1], - n_cells_padded[2] }; - - it_bounds_pointwise_mult = { geometry->n_cell_atoms, - (n_cells_padded[0] / 2 + 1), // due to redundancy in real fft - n_cells_padded[1], - n_cells_padded[2] }; - - it_bounds_write_gradients = { geometry->n_cell_atoms, - geometry->n_cells[0], - geometry->n_cells[1], - geometry->n_cells[2] }; - - FFT::FFT_Plan fft_plan_dipole = FFT::FFT_Plan(fft_dims, false, 6 * n_inter_sublattice, sublattice_size); - fft_plan_spins = FFT::FFT_Plan(fft_dims, false, 3 * geometry->n_cell_atoms, sublattice_size); - fft_plan_reverse = FFT::FFT_Plan(fft_dims, true, 3 * geometry->n_cell_atoms, sublattice_size); - - field temp_s = { &spin_stride.comp, &spin_stride.basis, &spin_stride.a, &spin_stride.b, &spin_stride.c }; - field temp_d = { &dipole_stride.comp, &dipole_stride.basis, &dipole_stride.a, &dipole_stride.b, &dipole_stride.c };; - FFT::get_strides(temp_s, { 3, this->geometry->n_cell_atoms, n_cells_padded[0], n_cells_padded[1], n_cells_padded[2] }); - FFT::get_strides(temp_d, { 6, n_inter_sublattice, n_cells_padded[0], n_cells_padded[1], n_cells_padded[2] }); - /* - //perform FFT of dipole matrices - int img_a = boundary_conditions[0] == 0 ? 0 : ddi_n_periodic_images[0]; - int img_b = boundary_conditions[1] == 0 ? 0 : ddi_n_periodic_images[1]; - int img_c = boundary_conditions[2] == 0 ? 0 : ddi_n_periodic_images[2]; - - FFT_Dipole_Matrices(fft_plan_dipole, img_a, img_b, img_c); */ - FFT_Dipole_Matrices(fft_plan_dipole, 0, 0, 0); - - transformed_dipole_matrices = std::move(fft_plan_dipole.cpx_ptr); - }//end prepare - - void Hamiltonian_Micromagnetic::Clean_DDI() - { - fft_plan_spins = FFT::FFT_Plan(); - fft_plan_reverse = FFT::FFT_Plan(); - } - - void Hamiltonian_Micromagnetic::Hessian(const vectorfield & spins, MatrixX & hessian) + // if (fft_dipole_inputs[idx]<-0.03) + } +} + +void Hamiltonian_Micromagnetic::FFT_Dipole_Matrices( FFT::FFT_Plan & fft_plan_dipole, int img_a, int img_b, int img_c ) +{ + auto & fft_dipole_inputs = fft_plan_dipole.real_ptr; + + field img = { img_a, img_b, img_c }; + + // Work around to make bravais vectors and cell_atoms available to GPU as they are currently saves as std::vectors + // and not fields ... + auto translation_vectors = field(); + auto cell_atom_translations = field(); + + for( int i = 0; i < 3; i++ ) + translation_vectors.push_back( geometry->lattice_constant * geometry->bravais_vectors[i] ); + + for( int i = 0; i < geometry->n_cell_atoms; i++ ) + cell_atom_translations.push_back( geometry->positions[i] ); + + Vector3 cell_sizes = { geometry->lattice_constant * geometry->bravais_vectors[0].norm(), + geometry->lattice_constant * geometry->bravais_vectors[1].norm(), + geometry->lattice_constant * geometry->bravais_vectors[2].norm() }; + + CU_Write_FFT_Dipole_Input1<<<( sublattice_size + 1023 ) / 1024, 1024>>>( + fft_dipole_inputs.data(), it_bounds_write_dipole.data(), translation_vectors.data(), geometry->n_cell_atoms, + cell_atom_translations.data(), geometry->n_cells.data(), inter_sublattice_lookup.data(), img.data(), + dipole_stride, cell_sizes ); + CU_CHECK_AND_SYNC(); + FFT::batch_Four_3D( fft_plan_dipole ); +} +void Hamiltonian_Micromagnetic::Prepare_DDI() +{ + Clean_DDI(); + + n_cells_padded.resize( 3 ); + n_cells_padded[0] = ( geometry->n_cells[0] > 1 ) ? 2 * geometry->n_cells[0] : 1; + n_cells_padded[1] = ( geometry->n_cells[1] > 1 ) ? 2 * geometry->n_cells[1] : 1; + n_cells_padded[2] = ( geometry->n_cells[2] > 1 ) ? 2 * geometry->n_cells[2] : 1; + sublattice_size = n_cells_padded[0] * n_cells_padded[1] * n_cells_padded[2]; + // printf("111 %d %d %d\n", n_cells_padded[0],n_cells_padded[1],n_cells_padded[2]); + + inter_sublattice_lookup.resize( geometry->n_cell_atoms * geometry->n_cell_atoms ); + + // we dont need to transform over length 1 dims + std::vector fft_dims; + for( int i = 2; i >= 0; i-- ) // notice that reverse order is important! { + if( n_cells_padded[i] > 1 ) + fft_dims.push_back( n_cells_padded[i] ); + } + + // Count how many distinct inter-lattice contributions we need to store + n_inter_sublattice = 0; + for( int i = 0; i < geometry->n_cell_atoms; i++ ) + { + for( int j = 0; j < geometry->n_cell_atoms; j++ ) + { + if( i != 0 && i == j ) + continue; + n_inter_sublattice++; + } } + printf( "lex%d %d %d\n", n_inter_sublattice, fft_dims[0], fft_dims[1] ); + // Set the iteration bounds for the nested for loops that are flattened in the kernels + it_bounds_write_spins + = { geometry->n_cell_atoms, geometry->n_cells[0], geometry->n_cells[1], geometry->n_cells[2] }; + + it_bounds_write_dipole = { n_cells_padded[0], n_cells_padded[1], n_cells_padded[2] }; + + it_bounds_pointwise_mult = { geometry->n_cell_atoms, + ( n_cells_padded[0] / 2 + 1 ), // due to redundancy in real fft + n_cells_padded[1], n_cells_padded[2] }; + + it_bounds_write_gradients + = { geometry->n_cell_atoms, geometry->n_cells[0], geometry->n_cells[1], geometry->n_cells[2] }; + + FFT::FFT_Plan fft_plan_dipole = FFT::FFT_Plan( fft_dims, false, 6 * n_inter_sublattice, sublattice_size ); + fft_plan_spins = FFT::FFT_Plan( fft_dims, false, 3 * geometry->n_cell_atoms, sublattice_size ); + fft_plan_reverse = FFT::FFT_Plan( fft_dims, true, 3 * geometry->n_cell_atoms, sublattice_size ); + + field temp_s = { &spin_stride.comp, &spin_stride.basis, &spin_stride.a, &spin_stride.b, &spin_stride.c }; + field temp_d + = { &dipole_stride.comp, &dipole_stride.basis, &dipole_stride.a, &dipole_stride.b, &dipole_stride.c }; + ; + FFT::get_strides( + temp_s, { 3, this->geometry->n_cell_atoms, n_cells_padded[0], n_cells_padded[1], n_cells_padded[2] } ); + FFT::get_strides( temp_d, { 6, n_inter_sublattice, n_cells_padded[0], n_cells_padded[1], n_cells_padded[2] } ); + /* + //perform FFT of dipole matrices + int img_a = boundary_conditions[0] == 0 ? 0 : ddi_n_periodic_images[0]; + int img_b = boundary_conditions[1] == 0 ? 0 : ddi_n_periodic_images[1]; + int img_c = boundary_conditions[2] == 0 ? 0 : ddi_n_periodic_images[2]; + + FFT_Dipole_Matrices(fft_plan_dipole, img_a, img_b, img_c); */ + FFT_Dipole_Matrices( fft_plan_dipole, 0, 0, 0 ); + + transformed_dipole_matrices = std::move( fft_plan_dipole.cpx_ptr ); +} // end prepare + +void Hamiltonian_Micromagnetic::Clean_DDI() +{ + fft_plan_spins = FFT::FFT_Plan(); + fft_plan_reverse = FFT::FFT_Plan(); +} +void Hamiltonian_Micromagnetic::Hessian( const vectorfield & spins, MatrixX & hessian ) {} - // Hamiltonian name as string - static const std::string name = "Micromagnetic"; - const std::string& Hamiltonian_Micromagnetic::Name() { return name; } +// Hamiltonian name as string +static const std::string name = "Micromagnetic"; +const std::string & Hamiltonian_Micromagnetic::Name() +{ + return name; } +} // namespace Engine + #endif \ No newline at end of file From 7e4f952ab3fab1ca3c3160e42333540370c01e22 Mon Sep 17 00:00:00 2001 From: Moritz Date: Tue, 13 Apr 2021 21:16:57 +0200 Subject: [PATCH 20/45] Hamiltonian_Micromagnetic: Implemented magnetostatics --- .../include/engine/Demagnetization_Tensor.hpp | 263 ++++++++++++ .../engine/Hamiltonian_Micromagnetic.hpp | 12 +- core/src/engine/Hamiltonian_Micromagnetic.cpp | 397 ++++++++++++++++-- 3 files changed, 634 insertions(+), 38 deletions(-) create mode 100644 core/include/engine/Demagnetization_Tensor.hpp diff --git a/core/include/engine/Demagnetization_Tensor.hpp b/core/include/engine/Demagnetization_Tensor.hpp new file mode 100644 index 000000000..5a61047f7 --- /dev/null +++ b/core/include/engine/Demagnetization_Tensor.hpp @@ -0,0 +1,263 @@ +#pragma once +#ifndef DEMAGNETIZATION_TENSOR_HPP +#define DEMAGNETIZATION_TENSOR_HPP + +#include +#include +#include + +namespace Engine +{ +namespace Demagnetization_Tensor +{ + using namespace Utility; + namespace Exact + { + // Helper functions + template + scalar kappa(const scalar & x, const scalar & y, const scalar & z, const scalar & R) + { + // If the argument of the log gets large, it is zerod out by the prefactor in f/g. Therefore we just set it to zero here, to avoid division by zero. + if(R < std::numeric_limits::epsilon() || y*y + z*z < std::numeric_limits::epsilon() ) + return 0.0; + + return std::log((x + R) / (std::sqrt(y*y + z*z))); + } + + template + scalar delta(const scalar & x, const scalar & y, const scalar & z, const scalar & R) + { + if(std::abs(z*R) < std::numeric_limits::epsilon()) + return Constants::Pi / 2.0; + + return std::atan(x*y / (z*R)); + } + + // Helper function for Nxx. Symmetric in z and y. + template + scalar f(const scalar & x, const scalar & y, const scalar & z) + { + scalar R = std::sqrt(x*x + y*y + z*z); + return ( + (y/2.0) * (z-x) * (z+x) * kappa(y, x, z, R) + + (z/2.0) * (y-x) * (y+x) * kappa(z, x, y, R) + - x*y*z * delta(y, z, x, R) + + 1.0/6.0 * ((x-y)*(x+y) + (x-z)*(x+z)) * R + ); + } + + // Helper function for Nxy. Symmetric in x and y. + template + scalar g(const scalar & x, const scalar & y, const scalar & z) + { + scalar R = std::sqrt(x*x + y*y + z*z); + return ( + (x*y*z) * kappa(z, x, y, R) + + (y/6.0) * (3*z*z - y*y) * kappa(x, y, z, R) + + (x/6.0) * (3*z*z - x*x) * kappa(y, x, z, R) + - (z*z*z/6.0) * delta(x, y, z, R) + - (z*y*y/2.0) * delta(x, z, y, R) + - (z*x*x/2.0) * delta(y, z, x, R) + - x*y*R/3.0 + ); + } + + template + scalar gamma(const scalar & e1, const scalar & e2, const scalar & e3) + { + return 8.0/std::pow(-2, std::abs(e1) + std::abs(e2) + std::abs(e3)); + } + + // Exact term for the demagnetization tensor Nxx and Nxy components, see Newell 1993 + // These formulas suffer from loss of significant digits as the distance increases + // Therefore they also return an estimate of their error, which helps to guide the use of asymptotic formulas. + // See Donahue "Accurate computation of thedemagnetization tensor" + + template + scalar Nxx(const scalar & X, const scalar & Y, const scalar & Z, const scalar & dx, const scalar & dy, const scalar & dz, scalar & abs_error) + { + scalar cur_max = 0; + scalar res = 0; + for (int e1=-1; e1<=1; e1++) + { + for (int e2=-1; e2<=1; e2++) + { + for(int e3=-1; e3<=1; e3++) + { + auto tmp = gamma(e1,e2,e3) / (4*Constants::Pi * dx*dy*dz) * f(X+e1*dx, Y+e2*dy, Z+e3*dz); + res += tmp; + if(std::abs(tmp) > cur_max) + cur_max = std::abs(tmp); + if(std::abs(tmp) > cur_max) + cur_max = std::abs(res); + } + } + } + // The main sources of error are temporary values with large magnitudes, while the final result of the sum is small in magnitude. + // Therefore, we approximate the absolute error of the sum as the abosolute error of the largest summand. + abs_error = std::abs( cur_max ) / std::pow(10, std::numeric_limits::digits10); + return res; + } + + // See Nxx comment. + template + scalar Nxy(const scalar & X, const scalar & Y, const scalar & Z, const scalar & dx, const scalar & dy, const scalar & dz, scalar & abs_error) + { + scalar cur_max = 0; + scalar res = 0; + for (int e1=-1; e1<=1; e1++) + { + for (int e2=-1; e2<=1; e2++) + { + for(int e3=-1; e3<=1; e3++) + { + auto tmp = gamma(e1,e2,e3) / (4 * Constants::Pi * dx*dy*dz) * g(X+e1*dx, Y+e2*dy, Z+e3*dz); + res += tmp; + if(std::abs(tmp) > cur_max) + cur_max = std::abs(tmp); + if(std::abs(res) > cur_max) + cur_max = std::abs(res); + } + } + } + abs_error = std::abs( cur_max ) / std::pow(10, std::numeric_limits::digits10); + return res; + } + } + + namespace Asymptote + { + // The exact formula can be rewritten as + // Nxx = 1/(4*pi*dx*dy*dz) * 2(cosh(dx * del_x)-1) * 2(cosh(dy * del_y)-1) * 2(cosh(dz * del_z)-1) * f(x,y,z) + // where del_x is to be understood as the partial derivative wrt x etc. For Nxy replace f with g. + // The cosh terms compute the finite difference terms, in the exact formula, via taylor series + // e.g: h^2 f''(x) ~ f(x+h) - 2*f(x) + f(x-h) = h^2 * 2(cosh(h * d/dx) - 1) + // We compute the asymptotes by expanding 2(cosh(h * d/dx) - 1) up to finitely many terms + // The first term we get is: + // 1/(4 * pi) * dx dy dz (del_x^2 del_y^2 del_z^2 f(x,y,z)), + // which turns out to be just the dipole approximation. + // The following asymptotes also include the next higher terms. + // See Donahue "Accurate computation of thedemagnetization tensor". + + // Implements del_x^2 del_y^2 del_z^2 f(x,y,z) + template + scalar f2(const scalar & x, const scalar & y, const scalar & z, const scalar & R) + { + scalar R2 = R*R; + scalar R5 = R*R*R*R*R; + return (3.0*x*x - R*R)/R5; + } + + // Implements del_x^4 del_y^2 del_z^2 f(x,y,z) + template + scalar f2xx(const scalar & x, const scalar & y, const scalar & z, const scalar & R) + { + scalar R2 = R*R; + scalar R5 = R*R*R*R*R; + return (-40.0*x*x/R2 - 5.0 * (7.0*x*x/R2 - 1) * (-2.0*x*x + y*y + z*z) / R2 + 4.0) / R5; + } + + // Implements del_x^2 del_y^4 del_z^2 f(x,y,z) + template + scalar f2yy(const scalar & x, const scalar & y, const scalar & z, const scalar & R) + { + scalar R2 = R*R; + scalar R5 = R*R*R*R*R; + return (20.0*y*y/R2 - 5.0 * (7.0*y*y/R2 - 1.0) * (-2.0*x*x + y*y + z*z) / R2 - 2.0) / R5; + } + + // Implements del_x^2 del_y^2 del_z^4 f(x,y,z) + template + scalar f2zz(const scalar & x, const scalar & y, const scalar & z, const scalar & R) + { + return f2yy(x,z,y,R); // Swap y and z, since f(x,y,z) is symmetric in these + } + + // Implements del_x^2 del_y^2 del_z^2 g(x,y,z) + template + scalar g2(const scalar & x, const scalar & y, const scalar & z, const scalar & R) + { + scalar R2 = R*R; + scalar R5 = R*R*R*R*R; + return 3.0*x*y/R5; + } + + // Implements del_x^4 del_y^2 del_z^2 g(x,y,z) + template + scalar g2xx(const scalar & x, const scalar & y, const scalar & z, const scalar & R) + { + scalar R2 = R*R; + scalar R7 = R*R*R*R*R*R*R; + return 15.0*x*y*(7.0*x*x/R2 - 3.0)/R7; + } + + // Implements del_x^2 del_y^4 del_z^2 g(x,y,z) + template + scalar g2yy(const scalar & x, const scalar & y, const scalar & z, const scalar & R) + { + return g2xx(y,x,z,R); // Swap x and y, since g(x,y,z) is symmetric in these + } + + // Implements del_x^2 del_y^2 del_z^4 g(x,y,z) + template + scalar g2zz(const scalar & x, const scalar & y, const scalar & z, const scalar & R) + { + scalar R2 = R*R; + scalar R7 = R*R*R*R*R*R*R; + return 15.0 * x * y * (7.0*z*z/R2 - 1.0)/R7; + } + + template + scalar Nxx_asym(const scalar & X, const scalar & Y, const scalar & Z, const scalar & dx, const scalar & dy, const scalar & dz) + { + scalar R = std::sqrt(X*X + Y*Y + Z*Z); + return -1.0/(4.0 * Constants::Pi) * dx*dy*dz * ( f2(X,Y,Z,R) + 1.0/12.0 * ( dx*dx * f2xx(X,Y,Z,R) + dy*dy * f2yy(X,Y,Z,R) + dz*dz * f2zz(X,Y,Z,R) ) ); + } + + template + scalar Nxy_asym(const scalar & X, const scalar & Y, const scalar & Z, const scalar & dx, const scalar & dy, const scalar & dz) + { + scalar R = std::sqrt(X*X + Y*Y + Z*Z); + return -1.0/(4.0 * Constants::Pi) * dx*dy*dz * ( g2(X,Y,Z,R) + 1.0/12.0 * ( dx*dx * g2xx(X,Y,Z,R) + dy*dy * g2yy(X,Y,Z,R) + dz*dz * g2zz(X,Y,Z,R) ) ); + } + } + + namespace Automatic + { + // These functions implement an automatic switching between the asymptotic expression and the exact one. + // Based on the floating point precision and the reported estimated error + template + scalar Nxx(const scalar & X, const scalar & Y, const scalar & Z, const scalar & dx, const scalar & dy, const scalar & dz) + { + scalar abs_error = 0; + auto nxx_analytical = Exact::Nxx(X,Y,Z,dx,dy,dz,abs_error); + auto nxx_asym = Asymptote::Nxx_asym(X,Y,Z,dx,dy,dz); + + // If the asymptote is within the error due to loss of significance we use it instead of the exact formula + if(std::abs(nxx_analytical - nxx_asym) < 10*abs_error) + return nxx_asym; + + return nxx_analytical; + } + + template + scalar Nxy(const scalar & X, const scalar & Y, const scalar & Z, const scalar & dx, const scalar & dy, const scalar & dz) + { + scalar abs_error = 0; + auto nxy_analytical = Exact::Nxy(X,Y,Z,dx,dy,dz,abs_error); + auto nxy_asym = Asymptote::Nxy_asym(X,Y,Z,dx,dy,dz); + + // std::cout << "nxy_analytical " << nxy_analytical << "\n"; + // std::cout << "nxy_asym " << nxy_asym << "\n"; + // std::cout << "abs_error " << abs_error << "\n"; + + // If the asymptote is within the error due to loss of significance we use it instead of the exact formula + if(std::abs(nxy_analytical - nxy_asym) < 10*abs_error) + return nxy_asym; + + return nxy_analytical; + } + } +} +} +#endif \ No newline at end of file diff --git a/core/include/engine/Hamiltonian_Micromagnetic.hpp b/core/include/engine/Hamiltonian_Micromagnetic.hpp index 43e4d39a5..c802a719b 100644 --- a/core/include/engine/Hamiltonian_Micromagnetic.hpp +++ b/core/include/engine/Hamiltonian_Micromagnetic.hpp @@ -64,11 +64,12 @@ class Hamiltonian_Micromagnetic : public Hamiltonian pairfield neigh; field spatial_gradient; bool A_is_nondiagonal = true; + // Dipole-dipole interaction - DDI_Method ddi_method; - intfield ddi_n_periodic_images; - scalar ddi_cutoff_radius; - pairfield ddi_pairs; + DDI_Method ddi_method; + intfield ddi_n_periodic_images; + scalar ddi_cutoff_radius; + pairfield ddi_pairs; scalarfield ddi_magnitudes; vectorfield ddi_normals; @@ -129,11 +130,12 @@ class Hamiltonian_Micromagnetic : public Hamiltonian // Total number of padded spins per sublattice int sublattice_size; + bool ddi_pb_zero_padding = true; FFT::StrideContainer spin_stride; FFT::StrideContainer dipole_stride; // Calculate the FT of the padded D matriess - void FFT_Dipole_Matrices( FFT::FFT_Plan & fft_plan_dipole, int img_a, int img_b, int img_c ); + void FFT_Demag_Tensors( FFT::FFT_Plan & fft_plan_dipole, int img_a, int img_b, int img_c ); // Calculate the FT of the padded spins void FFT_Spins( const vectorfield & spins ); diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cpp b/core/src/engine/Hamiltonian_Micromagnetic.cpp index bd86822fe..e0c3b7b80 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cpp +++ b/core/src/engine/Hamiltonian_Micromagnetic.cpp @@ -1,9 +1,11 @@ #ifndef SPIRIT_USE_CUDA #include +#include #include #include #include + #include #include @@ -164,6 +166,7 @@ void Hamiltonian_Micromagnetic::Update_Interactions() neigh.push_back( neigh_tmp ); this->spatial_gradient = field( geometry->nos, Matrix3::Zero() ); + this->Prepare_DDI(); this->Update_Energy_Contributions(); } @@ -179,38 +182,38 @@ void Hamiltonian_Micromagnetic::Update_Energy_Contributions() } else this->idx_zeeman = -1; - // TODO: Anisotropy - // if( ... ) - // { - // this->energy_contributions_per_spin.push_back({"Anisotropy", scalarfield(0) }); - // this->idx_anisotropy = this->energy_contributions_per_spin.size()-1; - // } - // else - this->idx_anisotropy = -1; - // TODO: Exchange - // if( ... ) - // { - // this->energy_contributions_per_spin.push_back({"Exchange", scalarfield(0) }); - // this->idx_exchange = this->energy_contributions_per_spin.size()-1; - // } - // else - this->idx_exchange = -1; - // TODO: DMI - // if( ... ) - // { - // this->energy_contributions_per_spin.push_back({"DMI", scalarfield(0) }); - // this->idx_dmi = this->energy_contributions_per_spin.size()-1; - // } - // else - this->idx_dmi = -1; - // TODO: DDI - // if( ... ) - // { - // this->energy_contributions_per_spin.push_back({"DDI", scalarfield(0) }); - // this->idx_ddi = this->energy_contributions_per_spin.size()-1; - // } - // else - this->idx_ddi = -1; + + if( anisotropy_tensor.norm() == 0.0 ) + { + this->energy_contributions_per_spin.push_back( { "Anisotropy", scalarfield( 0 ) } ); + this->idx_anisotropy = this->energy_contributions_per_spin.size() - 1; + } + else + this->idx_anisotropy = -1; + + if( exchange_tensor.norm() == 0.0 ) + { + this->energy_contributions_per_spin.push_back( { "Exchange", scalarfield( 0 ) } ); + this->idx_exchange = this->energy_contributions_per_spin.size() - 1; + } + else + this->idx_exchange = -1; + + if( dmi_tensor.norm() == 0.0 ) + { + this->energy_contributions_per_spin.push_back( { "DMI", scalarfield( 0 ) } ); + this->idx_dmi = this->energy_contributions_per_spin.size() - 1; + } + else + this->idx_dmi = -1; + + if( this->ddi_method != DDI_Method::None ) + { + this->energy_contributions_per_spin.push_back( { "DDI", scalarfield( 0 ) } ); + this->idx_ddi = this->energy_contributions_per_spin.size() - 1; + } + else + this->idx_ddi = -1; } void Hamiltonian_Micromagnetic::Energy_Contributions_per_Spin( @@ -247,6 +250,10 @@ void Hamiltonian_Micromagnetic::Energy_Contributions_per_Spin( // DMI if( this->idx_dmi >= 0 ) E_DMI( spins, contributions[idx_dmi].second ); + + // DDI + if( this->idx_ddi >= 0 ) + E_DDI( spins, contributions[idx_ddi].second ); } void Hamiltonian_Micromagnetic::E_Zeeman( const vectorfield & spins, scalarfield & Energy ) @@ -277,7 +284,11 @@ void Hamiltonian_Micromagnetic::E_Exchange( const vectorfield & spins, scalarfie void Hamiltonian_Micromagnetic::E_DMI( const vectorfield & spins, scalarfield & Energy ) {} -void Hamiltonian_Micromagnetic::E_DDI( const vectorfield & spins, scalarfield & Energy ) {} +void Hamiltonian_Micromagnetic::E_DDI( const vectorfield & spins, scalarfield & Energy ) +{ + if( this->ddi_method == DDI_Method::FFT ) + this->E_DDI_FFT( spins, Energy ); +} scalar Hamiltonian_Micromagnetic::Energy_Single_Spin( int ispin, const vectorfield & spins ) { @@ -287,6 +298,7 @@ scalar Hamiltonian_Micromagnetic::Energy_Single_Spin( int ispin, const vectorfie void Hamiltonian_Micromagnetic::Gradient( const vectorfield & spins, vectorfield & gradient ) { + // Set to zero Vectormath::fill( gradient, { 0, 0, 0 } ); this->Spatial_Gradient( spins ); @@ -303,6 +315,9 @@ void Hamiltonian_Micromagnetic::Gradient( const vectorfield & spins, vectorfield // DMI this->Gradient_DMI( spins, gradient ); + // DDI + this->Gradient_DDI( spins, gradient ); + // double energy=0; // #pragma omp parallel for reduction(-:energy) // for( int icell = 0; icell < geometry->n_cells_total; ++icell ) @@ -650,6 +665,322 @@ void Hamiltonian_Micromagnetic::Gradient_DMI( const vectorfield & spins, vectorf } } +void Hamiltonian_Micromagnetic::Gradient_DDI( const vectorfield & spins, vectorfield & gradient ) +{ + if( this->ddi_method == DDI_Method::FFT ) + this->Gradient_DDI_FFT( spins, gradient ); +} + +void Hamiltonian_Micromagnetic::Gradient_DDI_FFT( const vectorfield & spins, vectorfield & gradient ) +{ + // Size of original geometry + int Na = geometry->n_cells[0]; + int Nb = geometry->n_cells[1]; + int Nc = geometry->n_cells[2]; + + auto cell_volume = geometry->cell_size[0] * geometry->cell_size[1] * geometry->cell_size[2]; + + FFT_Spins( spins ); + + auto & ft_D_matrices = transformed_dipole_matrices; + auto & ft_spins = fft_plan_spins.cpx_ptr; + + auto & res_iFFT = fft_plan_reverse.real_ptr; + auto & res_mult = fft_plan_reverse.cpx_ptr; + + int idx_s, idx_d; + + // Workaround for compability with intel compiler + const int c_n_cell_atoms = geometry->n_cell_atoms; + const int * c_it_bounds_pointwise_mult = it_bounds_pointwise_mult.data(); + +// Loop over basis atoms (i.e sublattices) +#pragma omp parallel for collapse( 3 ) + + for( int c = 0; c < c_it_bounds_pointwise_mult[2]; ++c ) + { + for( int b = 0; b < c_it_bounds_pointwise_mult[1]; ++b ) + { + for( int a = 0; a < c_it_bounds_pointwise_mult[0]; ++a ) + { + idx_s = a * spin_stride.a + b * spin_stride.b + c * spin_stride.c; + idx_d = a * dipole_stride.a + b * dipole_stride.b + c * dipole_stride.c; + + auto & fs_x = ft_spins[idx_s]; + auto & fs_y = ft_spins[idx_s + 1 * spin_stride.comp]; + auto & fs_z = ft_spins[idx_s + 2 * spin_stride.comp]; + + auto & fD_xx = ft_D_matrices[idx_d]; + auto & fD_xy = ft_D_matrices[idx_d + 1 * dipole_stride.comp]; + auto & fD_xz = ft_D_matrices[idx_d + 2 * dipole_stride.comp]; + auto & fD_yy = ft_D_matrices[idx_d + 3 * dipole_stride.comp]; + auto & fD_yz = ft_D_matrices[idx_d + 4 * dipole_stride.comp]; + auto & fD_zz = ft_D_matrices[idx_d + 5 * dipole_stride.comp]; + + FFT::addTo( + res_mult[idx_s + 0 * spin_stride.comp], FFT::mult3D( fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z ), + true ); + FFT::addTo( + res_mult[idx_s + 1 * spin_stride.comp], FFT::mult3D( fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z ), + true ); + FFT::addTo( + res_mult[idx_s + 2 * spin_stride.comp], FFT::mult3D( fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z ), + true ); + } + } + } // end iteration over padded lattice cells + + // Inverse Fourier Transform + FFT::batch_iFour_3D( fft_plan_reverse ); + + // Workaround for compability with intel compiler + const int * c_n_cells = geometry->n_cells.data(); + + // Place the gradients at the correct positions and mult with correct mu + for( int c = 0; c < c_n_cells[2]; ++c ) + { + for( int b = 0; b < c_n_cells[1]; ++b ) + { + for( int a = 0; a < c_n_cells[0]; ++a ) + { + int idx_orig = a + Na * ( b + Nb * c ); + int idx = a * spin_stride.a + b * spin_stride.b + c * spin_stride.c; + gradient[idx_orig][0] -= res_iFFT[idx] / sublattice_size; + gradient[idx_orig][1] -= res_iFFT[idx + 1 * spin_stride.comp] / sublattice_size; + gradient[idx_orig][2] -= res_iFFT[idx + 2 * spin_stride.comp] / sublattice_size; + } + } + } // end iteration sublattice 1 +} + +void Hamiltonian_Micromagnetic::E_DDI_FFT( const vectorfield & spins, scalarfield & Energy ) +{ + scalar Energy_DDI = 0; + vectorfield gradients_temp; + gradients_temp.resize( geometry->nos ); + Vectormath::fill( gradients_temp, { 0, 0, 0 } ); + this->Gradient_DDI_FFT( spins, gradients_temp ); + + // === DEBUG: begin gradient comparison === + // vectorfield gradients_temp_dir; + // gradients_temp_dir.resize(this->geometry->nos); + // Vectormath::fill(gradients_temp_dir, {0,0,0}); + // Gradient_DDI_Direct(spins, gradients_temp_dir); + + // //get deviation + // std::array deviation = {0,0,0}; + // std::array avg = {0,0,0}; + // for(int i = 0; i < this->geometry->nos; i++) + // { + // for(int d = 0; d < 3; d++) + // { + // deviation[d] += std::pow(gradients_temp[i][d] - gradients_temp_dir[i][d], 2); + // avg[d] += gradients_temp_dir[i][d]; + // } + // } + // std::cerr << "Avg. Gradient = " << avg[0]/this->geometry->nos << " " << avg[1]/this->geometry->nos << " " << + // avg[2]/this->geometry->nos << std::endl; std::cerr << "Avg. Deviation = " << deviation[0]/this->geometry->nos << + // " " << deviation[1]/this->geometry->nos << " " << deviation[2]/this->geometry->nos << std::endl; +//==== DEBUG: end gradient comparison ==== + +// TODO: add dot_scaled to Vectormath and use that +#pragma omp parallel for + for( int ispin = 0; ispin < geometry->nos; ispin++ ) + { + Energy[ispin] += 0.5 * spins[ispin].dot( gradients_temp[ispin] ); + // Energy_DDI += 0.5 * spins[ispin].dot(gradients_temp[ispin]); + } +} + +void Hamiltonian_Micromagnetic::FFT_Demag_Tensors( FFT::FFT_Plan & fft_plan_dipole, int img_a, int img_b, int img_c ) +{ + + auto delta = geometry->cell_size; + auto cell_volume = geometry->cell_size[0] * geometry->cell_size[1] * geometry->cell_size[2]; + + // Prefactor of DDI + // The energy is proportional to spin_direction * Demag_tensor * spin_direction + // The 'mult' factor is chosen such that the cell resolved energy has + // the dimension of total energy per cell in meV + + scalar mult = C::mu_0 / cell_volume * ( cell_volume * Ms * C::Joule ) * ( cell_volume * Ms * C::Joule ); + + // Size of original geometry + int Na = geometry->n_cells[0]; + int Nb = geometry->n_cells[1]; + int Nc = geometry->n_cells[2]; + + auto & fft_dipole_inputs = fft_plan_dipole.real_ptr; + + // Iterate over the padded system + const int * c_n_cells_padded = n_cells_padded.data(); + +#pragma omp parallel for collapse( 3 ) + for( int c = 0; c < c_n_cells_padded[2]; ++c ) + { + for( int b = 0; b < c_n_cells_padded[1]; ++b ) + { + for( int a = 0; a < c_n_cells_padded[0]; ++a ) + { + int a_idx = a < Na ? a : a - n_cells_padded[0]; + int b_idx = b < Nb ? b : b - n_cells_padded[1]; + int c_idx = c < Nc ? c : c - n_cells_padded[2]; + + scalar Dxx = 0, Dxy = 0, Dxz = 0, Dyy = 0, Dyz = 0, Dzz = 0; + + // Iterate over periodic images + for( int a_pb = -img_a; a_pb <= img_a; a_pb++ ) + { + for( int b_pb = -img_b; b_pb <= img_b; b_pb++ ) + { + for( int c_pb = -img_c; c_pb <= img_c; c_pb++ ) + { + scalar X = ( a_idx + a_pb * Na ) * delta[0]; + scalar Y = ( b_idx + b_pb * Nb ) * delta[1]; + scalar Z = ( c_idx + c_pb * Nc ) * delta[2]; + scalar dx = delta[0]; + scalar dy = delta[1]; + scalar dz = delta[2]; + + Dxx += mult * Demagnetization_Tensor::Automatic::Nxx( X, Y, Z, dx, dy, dz ); + Dxy += mult * Demagnetization_Tensor::Automatic::Nxy( X, Y, Z, dx, dy, dz ); + Dxz += mult * Demagnetization_Tensor::Automatic::Nxy( X, Z, Y, dx, dz, dy ); + Dyy += mult * Demagnetization_Tensor::Automatic::Nxx( Y, X, Z, dy, dx, dz ); + Dyz += mult * Demagnetization_Tensor::Automatic::Nxy( Z, Y, X, dz, dy, dx ); + Dzz += mult * Demagnetization_Tensor::Automatic::Nxx( Z, Y, X, dz, dy, dx ); + } + } + } + + int idx = a * dipole_stride.a + b * dipole_stride.b + c * dipole_stride.c; + + fft_dipole_inputs[idx] = Dxx; + fft_dipole_inputs[idx + 1 * dipole_stride.comp] = Dxy; + fft_dipole_inputs[idx + 2 * dipole_stride.comp] = Dxz; + fft_dipole_inputs[idx + 3 * dipole_stride.comp] = Dyy; + fft_dipole_inputs[idx + 4 * dipole_stride.comp] = Dyz; + fft_dipole_inputs[idx + 5 * dipole_stride.comp] = Dzz; + } + } + } + FFT::batch_Four_3D( fft_plan_dipole ); +} + +void Hamiltonian_Micromagnetic::FFT_Spins( const vectorfield & spins ) +{ + // size of original geometry + int Na = geometry->n_cells[0]; + int Nb = geometry->n_cells[1]; + int Nc = geometry->n_cells[2]; + + auto cell_volume = geometry->cell_size[0] * geometry->cell_size[1] * geometry->cell_size[2]; + + auto & fft_spin_inputs = fft_plan_spins.real_ptr; + +// iterate over the **original** system +#pragma omp parallel for collapse( 4 ) + for( int c = 0; c < Nc; ++c ) + { + for( int b = 0; b < Nb; ++b ) + { + for( int a = 0; a < Na; ++a ) + { + int idx_orig = a + Na * ( b + Nb * c ); + int idx = a * spin_stride.a + b * spin_stride.b + c * spin_stride.c; + + fft_spin_inputs[idx] = spins[idx_orig][0]; + fft_spin_inputs[idx + 1 * spin_stride.comp] = spins[idx_orig][1]; + fft_spin_inputs[idx + 2 * spin_stride.comp] = spins[idx_orig][2]; + } + } + } + + FFT::batch_Four_3D( fft_plan_spins ); +} + +void Hamiltonian_Micromagnetic::Prepare_DDI() +{ + Clean_DDI(); + + if( ddi_method != DDI_Method::FFT ) + return; + + // We perform zero-padding in a lattice direction if the dimension of the system is greater than 1 *and* + // - the boundary conditions are open, or + // - the boundary conditions are periodic and zero-padding is explicitly requested + n_cells_padded.resize( 3 ); + for( int i = 0; i < 3; i++ ) + { + n_cells_padded[i] = geometry->n_cells[i]; + bool perform_zero_padding = geometry->n_cells[i] > 1 && ( boundary_conditions[i] == 0 || ddi_pb_zero_padding ); + if( perform_zero_padding ) + n_cells_padded[i] *= 2; + } + sublattice_size = n_cells_padded[0] * n_cells_padded[1] * n_cells_padded[2]; + + FFT::FFT_Init(); + +// Workaround for bug in kissfft +// kissfft_ndr does not perform one-dimensional FFTs properly +#ifndef SPIRIT_USE_FFTW + int number_of_one_dims = 0; + for( int i = 0; i < 3; i++ ) + if( n_cells_padded[i] == 1 && ++number_of_one_dims > 1 ) + n_cells_padded[i] = 2; +#endif + + sublattice_size = n_cells_padded[0] * n_cells_padded[1] * n_cells_padded[2]; + + // We dont need to transform over length 1 dims + std::vector fft_dims; + for( int i = 2; i >= 0; i-- ) // notice that reverse order is important! + { + if( n_cells_padded[i] > 1 ) + fft_dims.push_back( n_cells_padded[i] ); + } + + // Create FFT plans + FFT::FFT_Plan fft_plan_dipole = FFT::FFT_Plan( fft_dims, false, 6, sublattice_size ); + fft_plan_spins = FFT::FFT_Plan( fft_dims, false, 3, sublattice_size ); + fft_plan_reverse = FFT::FFT_Plan( fft_dims, true, 3, sublattice_size ); + +#ifdef SPIRIT_USE_FFTW + field temp_s = { &spin_stride.comp, &spin_stride.basis, &spin_stride.a, &spin_stride.b, &spin_stride.c }; + field temp_d + = { &dipole_stride.comp, &dipole_stride.basis, &dipole_stride.a, &dipole_stride.b, &dipole_stride.c }; + + FFT::get_strides( + temp_s, { 3, this->geometry->n_cell_atoms, n_cells_padded[0], n_cells_padded[1], n_cells_padded[2] } ); + FFT::get_strides( temp_d, { 6, 1, n_cells_padded[0], n_cells_padded[1], n_cells_padded[2] } ); + it_bounds_pointwise_mult = { ( n_cells_padded[0] / 2 + 1 ), // due to redundancy in real fft + n_cells_padded[1], n_cells_padded[2] }; +#else + field temp_s = { &spin_stride.a, &spin_stride.b, &spin_stride.c, &spin_stride.comp, &spin_stride.basis }; + field temp_d + = { &dipole_stride.a, &dipole_stride.b, &dipole_stride.c, &dipole_stride.comp, &dipole_stride.basis }; + + FFT::get_strides( + temp_s, { n_cells_padded[0], n_cells_padded[1], n_cells_padded[2], 3, this->geometry->n_cell_atoms } ); + FFT::get_strides( temp_d, { n_cells_padded[0], n_cells_padded[1], n_cells_padded[2], 6, 1 } ); + it_bounds_pointwise_mult = { n_cells_padded[0], n_cells_padded[1], n_cells_padded[2] }; + ( it_bounds_pointwise_mult[fft_dims.size() - 1] /= 2 )++; +#endif + + // Perform FFT of dipole matrices + int img_a = boundary_conditions[0] == 0 ? 0 : ddi_n_periodic_images[0]; + int img_b = boundary_conditions[1] == 0 ? 0 : ddi_n_periodic_images[1]; + int img_c = boundary_conditions[2] == 0 ? 0 : ddi_n_periodic_images[2]; + + FFT_Demag_Tensors( fft_plan_dipole, img_a, img_b, img_c ); + transformed_dipole_matrices = std::move( fft_plan_dipole.cpx_ptr ); +} + +void Hamiltonian_Micromagnetic::Clean_DDI() +{ + fft_plan_spins = FFT::FFT_Plan(); + fft_plan_reverse = FFT::FFT_Plan(); +} + void Hamiltonian_Micromagnetic::Hessian( const vectorfield & spins, MatrixX & hessian ) {} // Hamiltonian name as string From 7d4432cd4eb09664e0b9f088e8ca244c68b0f881 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 14 Apr 2021 10:31:28 +0200 Subject: [PATCH 21/45] Improved how the demag tensor implementation handles edge cases. --- .../include/engine/Demagnetization_Tensor.hpp | 29 ++++++++++++++----- 1 file changed, 21 insertions(+), 8 deletions(-) diff --git a/core/include/engine/Demagnetization_Tensor.hpp b/core/include/engine/Demagnetization_Tensor.hpp index 5a61047f7..58c54a8fb 100644 --- a/core/include/engine/Demagnetization_Tensor.hpp +++ b/core/include/engine/Demagnetization_Tensor.hpp @@ -17,20 +17,33 @@ namespace Demagnetization_Tensor template scalar kappa(const scalar & x, const scalar & y, const scalar & z, const scalar & R) { - // If the argument of the log gets large, it is zerod out by the prefactor in f/g. Therefore we just set it to zero here, to avoid division by zero. - if(R < std::numeric_limits::epsilon() || y*y + z*z < std::numeric_limits::epsilon() ) - return 0.0; - - return std::log((x + R) / (std::sqrt(y*y + z*z))); + // If the argument of the log gets large, it is zeroed out by the prefactor in f or g. Therefore we just set it to zero here, to avoid division by zero. + auto res = std::log((x + R) / (std::sqrt(y*y + z*z))); + if(std::isnan(res) || std::isinf(res)) + res = 0; + return res; } template scalar delta(const scalar & x, const scalar & y, const scalar & z, const scalar & R) { - if(std::abs(z*R) < std::numeric_limits::epsilon()) - return Constants::Pi / 2.0; + auto arg = x*y / (z*R); + auto res = std::atan(arg); + + // If the arg is infinite atan(arg) will give +- Pi/2 depending on sign + // The std::atan function should know about this, but we do not rely on it here + if(std::isinf(arg)) + if(arg<0) + return -Constants::Pi/2; + else + return Constants::Pi/2; - return std::atan(x*y / (z*R)); + // If arg is nan it most likely means a division 0/0 ocurred, + // we just return 0 because the delta function is cancelled by prefactors in that case + if(std::isnan(arg)) + return 0; + + return res; } // Helper function for Nxx. Symmetric in z and y. From 143531e34cff9a93f8fad59a3b8f5ebb628aa1c9 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 14 Apr 2021 12:26:51 +0200 Subject: [PATCH 22/45] Demagnetization_Tensor: Decreased sensitivity for switching to asymptote --- core/include/engine/Demagnetization_Tensor.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/include/engine/Demagnetization_Tensor.hpp b/core/include/engine/Demagnetization_Tensor.hpp index 58c54a8fb..f4fbe359b 100644 --- a/core/include/engine/Demagnetization_Tensor.hpp +++ b/core/include/engine/Demagnetization_Tensor.hpp @@ -247,7 +247,7 @@ namespace Demagnetization_Tensor auto nxx_asym = Asymptote::Nxx_asym(X,Y,Z,dx,dy,dz); // If the asymptote is within the error due to loss of significance we use it instead of the exact formula - if(std::abs(nxx_analytical - nxx_asym) < 10*abs_error) + if(std::abs(nxx_analytical - nxx_asym) < 20.0*abs_error) return nxx_asym; return nxx_analytical; @@ -265,7 +265,7 @@ namespace Demagnetization_Tensor // std::cout << "abs_error " << abs_error << "\n"; // If the asymptote is within the error due to loss of significance we use it instead of the exact formula - if(std::abs(nxy_analytical - nxy_asym) < 10*abs_error) + if(std::abs(nxy_analytical - nxy_asym) < 20.0*abs_error) return nxy_asym; return nxy_analytical; From f12ce6e02f29d40b441df384bb03fa204e0425f7 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 14 Apr 2021 12:27:39 +0200 Subject: [PATCH 23/45] Hamiltonian_Micromagnetic: Implemented Gradient_DDI_Direct --- core/src/engine/Hamiltonian_Micromagnetic.cpp | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cpp b/core/src/engine/Hamiltonian_Micromagnetic.cpp index e0c3b7b80..6cd01ffed 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cpp +++ b/core/src/engine/Hamiltonian_Micromagnetic.cpp @@ -671,6 +671,54 @@ void Hamiltonian_Micromagnetic::Gradient_DDI( const vectorfield & spins, vectorf this->Gradient_DDI_FFT( spins, gradient ); } +void Hamiltonian_Micromagnetic::Gradient_DDI_Direct( const vectorfield & spins, vectorfield & gradient ) +{ + auto delta = geometry->cell_size; + auto cell_volume = geometry->cell_size[0] * geometry->cell_size[1] * geometry->cell_size[2]; + scalar mult = C::mu_0 / cell_volume * ( cell_volume * Ms * C::Joule ) * ( cell_volume * Ms * C::Joule ); + + int img_a = boundary_conditions[0] == 0 ? 0 : ddi_n_periodic_images[0]; + int img_b = boundary_conditions[1] == 0 ? 0 : ddi_n_periodic_images[1]; + int img_c = boundary_conditions[2] == 0 ? 0 : ddi_n_periodic_images[2]; + + for( int idx1 = 0; idx1 < geometry->nos; idx1++ ) + { + for( int idx2 = 0; idx2 < geometry->nos; idx2++ ) + { + scalar Dxx = 0, Dxy = 0, Dxz = 0, Dyy = 0, Dyz = 0, Dzz = 0; + auto diff = this->geometry->positions[idx2] - this->geometry->positions[idx1]; + + for( int a_pb = -img_a; a_pb <= img_a; a_pb++ ) + { + for( int b_pb = -img_b; b_pb <= img_b; b_pb++ ) + { + for( int c_pb = -img_c; c_pb <= img_c; c_pb++ ) + { + auto X = 1e-10 * diff[0] + a_pb * delta[0]; + auto Y = 1e-10 * diff[1] + b_pb * delta[1]; + auto Z = 1e-10 * diff[2] + c_pb * delta[2]; + + auto dx = delta[0]; + auto dy = delta[1]; + auto dz = delta[2]; + + Dxx += mult * Demagnetization_Tensor::Automatic::Nxx( X, Y, Z, dx, dy, dz ); + Dxy += mult * Demagnetization_Tensor::Automatic::Nxy( X, Y, Z, dx, dy, dz ); + Dxz += mult * Demagnetization_Tensor::Automatic::Nxy( X, Z, Y, dx, dz, dy ); + Dyy += mult * Demagnetization_Tensor::Automatic::Nxx( Y, X, Z, dy, dx, dz ); + Dyz += mult * Demagnetization_Tensor::Automatic::Nxy( Z, Y, X, dz, dy, dx ); + Dzz += mult * Demagnetization_Tensor::Automatic::Nxx( Z, Y, X, dz, dy, dx ); + } + } + } + + gradient[idx1][0] -= ( Dxx * spins[idx2][0] + Dxy * spins[idx2][1] + Dxz * spins[idx2][2] ); + gradient[idx1][1] -= ( Dxy * spins[idx2][0] + Dyy * spins[idx2][1] + Dyz * spins[idx2][2] ); + gradient[idx1][2] -= ( Dxz * spins[idx2][0] + Dyz * spins[idx2][1] + Dzz * spins[idx2][2] ); + } + } +} + void Hamiltonian_Micromagnetic::Gradient_DDI_FFT( const vectorfield & spins, vectorfield & gradient ) { // Size of original geometry From 2d84dbc47b799a0b836c7bc5096140a98d20aee6 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 14 Apr 2021 15:24:01 +0200 Subject: [PATCH 24/45] Hamiltonian_Micromagnetic: Moved definition of Ms out of the Hamiltonian (to match mu_s in atomistic case). Also added cell_volume to Geometry and changed definition of cell_size (from bounds_max - bounds_min to actual cell_size). Now these fields might also have some use in the atomistic case. --- core/include/data/Geometry.hpp | 4 ++++ .../engine/Hamiltonian_Micromagnetic.hpp | 3 +-- core/include/utility/Constants.hpp | 2 +- core/src/Spirit/Geometry.cpp | 6 ++++++ core/src/Spirit/Hamiltonian.cpp | 1 - core/src/data/Geometry.cpp | 19 ++++++++++++++++--- core/src/engine/Hamiltonian_Micromagnetic.cpp | 13 +++++++------ core/src/io/Configparser.cpp | 15 +++++++-------- 8 files changed, 42 insertions(+), 21 deletions(-) diff --git a/core/include/data/Geometry.hpp b/core/include/data/Geometry.hpp index 35e999bf7..468d0fb51 100644 --- a/core/include/data/Geometry.hpp +++ b/core/include/data/Geometry.hpp @@ -146,6 +146,10 @@ class Geometry Vector3 cell_bounds_min, cell_bounds_max; // Unit cell size [m] Vector3 cell_size; + // Unit cell volume [m^3] + scalar cell_volume; + // Saturation Magnetisation [A/m] + scalar Ms; private: // Generate the full set of spin positions diff --git a/core/include/engine/Hamiltonian_Micromagnetic.hpp b/core/include/engine/Hamiltonian_Micromagnetic.hpp index c802a719b..91f683b47 100644 --- a/core/include/engine/Hamiltonian_Micromagnetic.hpp +++ b/core/include/engine/Hamiltonian_Micromagnetic.hpp @@ -21,7 +21,7 @@ class Hamiltonian_Micromagnetic : public Hamiltonian { public: Hamiltonian_Micromagnetic( - scalar Ms, scalar external_field_magnitude, Vector3 external_field_normal, Matrix3 anisotropy_tensor, + scalar external_field_magnitude, Vector3 external_field_normal, Matrix3 anisotropy_tensor, Matrix3 exchange_tensor, Matrix3 dmi_tensor, DDI_Method ddi_method, intfield ddi_n_periodic_images, scalar ddi_radius, std::shared_ptr geometry, int spatial_gradient_order, intfield boundary_conditions ); @@ -54,7 +54,6 @@ class Hamiltonian_Micromagnetic : public Hamiltonian scalar external_field_magnitude; Vector3 external_field_normal; Matrix3 anisotropy_tensor; - scalar Ms; // ------------ Pair Interactions ------------ // Exchange interaction diff --git a/core/include/utility/Constants.hpp b/core/include/utility/Constants.hpp index 541736830..d15fa8d33 100644 --- a/core/include/utility/Constants.hpp +++ b/core/include/utility/Constants.hpp @@ -100,7 +100,7 @@ namespace Utility namespace Constants_Micromagnetic { // The Bohr Magneton [Joule/T] - double const mu_B = Constants::mu_B * Constants::Joule; + double const mu_B = Constants::mu_B / Constants::Joule; // The vacuum permeability [T^2 m^3 / Joule] double const mu_0 = Constants::mu_0 / Constants::Joule; diff --git a/core/src/Spirit/Geometry.cpp b/core/src/Spirit/Geometry.cpp index d4da47c14..2fb4ee903 100644 --- a/core/src/Spirit/Geometry.cpp +++ b/core/src/Spirit/Geometry.cpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,11 @@ void Helper_System_Set_Geometry(std::shared_ptr system, const // Heisenberg Hamiltonian if (system->hamiltonian->Name() == "Heisenberg") std::static_pointer_cast(system->hamiltonian)->Update_Interactions(); + + // Micromagnetic Hamiltonian + if (system->hamiltonian->Name() == "Micromagnetic") + std::static_pointer_cast(system->hamiltonian)->Update_Interactions(); + } void Helper_State_Set_Geometry(State * state, const Data::Geometry & old_geometry, const Data::Geometry & new_geometry) diff --git a/core/src/Spirit/Hamiltonian.cpp b/core/src/Spirit/Hamiltonian.cpp index 3e24d7921..28a65ca1a 100644 --- a/core/src/Spirit/Hamiltonian.cpp +++ b/core/src/Spirit/Hamiltonian.cpp @@ -99,7 +99,6 @@ try { // TODO: are these the desired defaults? image->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Micromagnetic( - 0, 0, Vector3{0, 0, 1}, Matrix3::Zero(), Matrix3::Zero(), diff --git a/core/src/data/Geometry.cpp b/core/src/data/Geometry.cpp index bfe44c801..0cb39915c 100644 --- a/core/src/data/Geometry.cpp +++ b/core/src/data/Geometry.cpp @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -70,6 +71,21 @@ namespace Data this->mu_s[ispin] = 0.0; } + // Cell size in m + this->cell_size = 1e-10 * this->lattice_constant * Vector3{ bravais_vectors[0].norm(), bravais_vectors[1].norm(), bravais_vectors[2].norm() }; + + // Cell volume in m^3 + this->cell_volume = std::pow(1e-10 * this->lattice_constant, 3.0) * bravais_vectors[0].dot( bravais_vectors[1].cross( bravais_vectors[2] ) ); + + for(auto bv : bravais_vectors) + std::cout << bv.transpose() << "\n--\n"; + std::cout << "cell_volume " << cell_volume << "\n"; + + // Saturation Magnetisation Density in A/m + this->Ms = 0; + for(auto & mu_s : cell_composition.mu_s) + this->Ms += mu_s * Utility::Constants_Micromagnetic::mu_B / cell_volume; + // Calculate the type of geometry this->calculateGeometryType(); @@ -722,9 +738,6 @@ namespace Data } this->cell_bounds_min *= 0.5; this->cell_bounds_max *= 0.5; - - // Cell size in m - this->cell_size = 1e-10 * (this->cell_bounds_max - this->cell_bounds_min); } void Geometry::calculateGeometryType() diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cpp b/core/src/engine/Hamiltonian_Micromagnetic.cpp index 6cd01ffed..72b6830a2 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cpp +++ b/core/src/engine/Hamiltonian_Micromagnetic.cpp @@ -25,12 +25,11 @@ namespace Engine { Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( - scalar Ms, scalar external_field_magnitude, Vector3 external_field_normal, Matrix3 anisotropy_tensor, + scalar external_field_magnitude, Vector3 external_field_normal, Matrix3 anisotropy_tensor, Matrix3 exchange_tensor, Matrix3 dmi_tensor, DDI_Method ddi_method, intfield ddi_n_periodic_images, scalar ddi_radius, std::shared_ptr geometry, int spatial_gradient_order, intfield boundary_conditions ) : Hamiltonian( boundary_conditions ), - Ms( Ms ), spatial_gradient_order( spatial_gradient_order ), geometry( geometry ), external_field_magnitude( external_field_magnitude ), @@ -274,7 +273,7 @@ void Hamiltonian_Micromagnetic::E_Update( const vectorfield & spins, scalarfield #pragma omp parallel for for( int icell = 0; icell < geometry->n_cells_total; ++icell ) { - Energy[icell] -= 0.5 * Ms * gradient[icell].dot( spins[icell] ); + Energy[icell] -= 0.5 * geometry->Ms * gradient[icell].dot( spins[icell] ); } } @@ -351,7 +350,7 @@ void Hamiltonian_Micromagnetic::Gradient_Anisotropy( const vectorfield & spins, // for( int iani = 0; iani < 1; ++iani ) // { // gradient[icell] -= 2.0 * 8.44e6 / Ms * temp3 * temp3.dot(spins[icell]); - gradient[icell] -= 2.0 * C::mu_B * anisotropy_tensor * spins[icell] / Ms; + gradient[icell] -= 2.0 * C::mu_B * anisotropy_tensor * spins[icell] / geometry->Ms; // gradient[icell] -= 2.0 * this->anisotropy_magnitudes[iani] / Ms * ((pow(temp2.dot(spins[icell]),2)+ // pow(temp3.dot(spins[icell]), 2))*(temp1.dot(spins[icell])*temp1)+ (pow(temp1.dot(spins[icell]), 2) + // pow(temp3.dot(spins[icell]), 2))*(temp2.dot(spins[icell])*temp2)+(pow(temp1.dot(spins[icell]),2)+ @@ -483,7 +482,7 @@ spins[ispin_minus_minus][1]) / (delta[1]) / (delta[2]) / 4; gradient[icell] -= 2 * C::mu_B * exchange_tensor * ( spins[icell_plus] - 2 * spins[icell] + spins[icell_minus] ) - / ( Ms * delta[i] * delta[i] ); + / ( geometry->Ms * delta[i] * delta[i] ); } // gradient[icell][0] -= 2*exchange_tensor(i, i) / Ms * (spins[icell_plus][0] - 2*spins[icell][0] + @@ -644,6 +643,7 @@ dn3/dr1 dn3/dr2 dn3/dr3 void Hamiltonian_Micromagnetic::Gradient_DMI( const vectorfield & spins, vectorfield & gradient ) { + auto Ms = geometry->Ms; #pragma omp parallel for for( unsigned int icell = 0; icell < geometry->n_cells_total; ++icell ) { @@ -673,6 +673,7 @@ void Hamiltonian_Micromagnetic::Gradient_DDI( const vectorfield & spins, vectorf void Hamiltonian_Micromagnetic::Gradient_DDI_Direct( const vectorfield & spins, vectorfield & gradient ) { + auto Ms = geometry->Ms; auto delta = geometry->cell_size; auto cell_volume = geometry->cell_size[0] * geometry->cell_size[1] * geometry->cell_size[2]; scalar mult = C::mu_0 / cell_volume * ( cell_volume * Ms * C::Joule ) * ( cell_volume * Ms * C::Joule ); @@ -845,7 +846,7 @@ void Hamiltonian_Micromagnetic::FFT_Demag_Tensors( FFT::FFT_Plan & fft_plan_dipo auto delta = geometry->cell_size; auto cell_volume = geometry->cell_size[0] * geometry->cell_size[1] * geometry->cell_size[2]; - + auto Ms = geometry->Ms; // Prefactor of DDI // The energy is proportional to spin_direction * Demag_tensor * spin_direction // The 'mult' factor is chosen such that the cell resolved energy has diff --git a/core/src/io/Configparser.cpp b/core/src/io/Configparser.cpp index 08f7aecab..0714f8f5c 100644 --- a/core/src/io/Configparser.cpp +++ b/core/src/io/Configparser.cpp @@ -426,7 +426,7 @@ std::shared_ptr Geometry_from_Config( const std::string configFi scalar V = std::pow( lattice_constant * 1e-10, 3 ) * ( bravais_vectors[0].cross( bravais_vectors[1] ) ).dot( bravais_vectors[2] ); // * n_cells[0] * n_cells[1] * n_cells[2] * n_cell_atoms; - scalar mu_s = Ms * 1.0782822e23 * V; // per cell + scalar mu_s = Ms * 1.0782822e23 * V / n_cell_atoms; // per cell for( iatom = 0; iatom < n_cell_atoms; ++iatom ) cell_composition.mu_s[iatom] = mu_s; @@ -526,7 +526,10 @@ std::shared_ptr Geometry_from_Config( const std::string configFi parameter_log.push_back( fmt::format( " {} spins", geometry->nos ) ); parameter_log.push_back( fmt::format( " the geometry is {}-dimensional", geometry->dimensionality ) ); - parameter_log.push_back( fmt::format( " unit cell size: {}", geometry->cell_size.transpose() ) ); + parameter_log.push_back( fmt::format( " unit cell size [m]: {}", geometry->cell_size.transpose() ) ); + parameter_log.push_back( fmt::format( " unit cell volume [m^3]: {}", geometry->cell_volume ) ); + parameter_log.push_back( fmt::format( " Ms[A/m]: {}", geometry->Ms ) ); + Log.SendBlock( Log_Level::Parameter, Log_Sender::IO, parameter_log ); @@ -1567,9 +1570,6 @@ Hamiltonian_Micromagnetic_from_Config( const std::string configFile, const std:: // The order of the finite difference approximation of the spatial gradient int spatial_gradient_order = 1; - // Total magnetisation - scalar Ms = 1e6; - // External Magnetic Field scalar field = 0; Vector3 field_normal = { 0.0, 0.0, 1.0 }; @@ -1725,14 +1725,13 @@ Hamiltonian_Micromagnetic_from_Config( const std::string configFile, const std:: fmt::format( "Unable to parse all parameters of the Micromagnetic Hamiltonian from \"{}\"", configFile ) ); } // Return - Log( Log_Level::Parameter, Log_Sender::IO, "Hamiltonian_Heisenberg:" ); + Log( Log_Level::Parameter, Log_Sender::IO, "Hamiltonian_Micromagnetic:" ); Log( Log_Level::Parameter, Log_Sender::IO, fmt::format( " {:<24} = {}", "discretisation order", spatial_gradient_order ) ); Log( Log_Level::Parameter, Log_Sender::IO, fmt::format( " {:<24} = {} {} {}", "boundary conditions", boundary_conditions[0], boundary_conditions[1], boundary_conditions[2] ) ); - Log( Log_Level::Parameter, Log_Sender::IO, fmt::format( " {:<24} = {}", "saturation magnetisation", Ms ) ); Log( Log_Level::Parameter, Log_Sender::IO, fmt::format( " {:<24} = {}", "external field", field ) ); Log( Log_Level::Parameter, Log_Sender::IO, fmt::format( " {:<24} = {}", "field normal", field_normal.transpose() ) ); @@ -1756,7 +1755,7 @@ Hamiltonian_Micromagnetic_from_Config( const std::string configFile, const std:: Log( Log_Level::Parameter, Log_Sender::IO, fmt::format( " {:<21} = {}", "ddi_radius", ddi_radius ) ); auto hamiltonian = std::unique_ptr( new Engine::Hamiltonian_Micromagnetic( - Ms, field, field_normal, anisotropy_tensor, exchange_tensor, dmi_tensor, ddi_method, ddi_n_periodic_images, + field, field_normal, anisotropy_tensor, exchange_tensor, dmi_tensor, ddi_method, ddi_n_periodic_images, ddi_radius, geometry, spatial_gradient_order, boundary_conditions ) ); Log( Log_Level::Info, Log_Sender::IO, "Hamiltonian_Micromagnetic: built" ); From a57b89881aa7c7ec1f7310b63ed77a8e16e58d11 Mon Sep 17 00:00:00 2001 From: Moritz Date: Fri, 28 May 2021 14:39:50 +0200 Subject: [PATCH 25/45] Micromagnetics: Current Snapshot --- core/include/data/Geometry.hpp | 5 +- .../include/engine/Demagnetization_Tensor.hpp | 2 +- .../engine/Hamiltonian_Micromagnetic.hpp | 4 +- core/src/Spirit/Hamiltonian.cpp | 2 +- core/src/data/Geometry.cpp | 14 +- core/src/engine/Hamiltonian_Micromagnetic.cpp | 325 ++++-------------- core/src/io/Configparser.cpp | 19 +- 7 files changed, 105 insertions(+), 266 deletions(-) diff --git a/core/include/data/Geometry.hpp b/core/include/data/Geometry.hpp index 468d0fb51..c9d1c9a49 100644 --- a/core/include/data/Geometry.hpp +++ b/core/include/data/Geometry.hpp @@ -148,8 +148,9 @@ class Geometry Vector3 cell_size; // Unit cell volume [m^3] scalar cell_volume; - // Saturation Magnetisation [A/m] - scalar Ms; + // Computes the saturation magnetisation density [A/m] + // from the atomistic basis cell composition + scalar getMs(); private: // Generate the full set of spin positions diff --git a/core/include/engine/Demagnetization_Tensor.hpp b/core/include/engine/Demagnetization_Tensor.hpp index f4fbe359b..d4d30ba68 100644 --- a/core/include/engine/Demagnetization_Tensor.hpp +++ b/core/include/engine/Demagnetization_Tensor.hpp @@ -150,7 +150,7 @@ namespace Demagnetization_Tensor // 1/(4 * pi) * dx dy dz (del_x^2 del_y^2 del_z^2 f(x,y,z)), // which turns out to be just the dipole approximation. // The following asymptotes also include the next higher terms. - // See Donahue "Accurate computation of thedemagnetization tensor". + // See Donahue "Accurate computation of the demagnetization tensor". // Implements del_x^2 del_y^2 del_z^2 f(x,y,z) template diff --git a/core/include/engine/Hamiltonian_Micromagnetic.hpp b/core/include/engine/Hamiltonian_Micromagnetic.hpp index 91f683b47..948ee2e5d 100644 --- a/core/include/engine/Hamiltonian_Micromagnetic.hpp +++ b/core/include/engine/Hamiltonian_Micromagnetic.hpp @@ -21,7 +21,7 @@ class Hamiltonian_Micromagnetic : public Hamiltonian { public: Hamiltonian_Micromagnetic( - scalar external_field_magnitude, Vector3 external_field_normal, Matrix3 anisotropy_tensor, + scalar Ms, scalar external_field_magnitude, Vector3 external_field_normal, Matrix3 anisotropy_tensor, Matrix3 exchange_tensor, Matrix3 dmi_tensor, DDI_Method ddi_method, intfield ddi_n_periodic_images, scalar ddi_radius, std::shared_ptr geometry, int spatial_gradient_order, intfield boundary_conditions ); @@ -49,6 +49,8 @@ class Hamiltonian_Micromagnetic : public Hamiltonian // ------------ ... ------------ int spatial_gradient_order; + scalar Ms; + // ------------ Single Spin Interactions ------------ // External magnetic field across the sample scalar external_field_magnitude; diff --git a/core/src/Spirit/Hamiltonian.cpp b/core/src/Spirit/Hamiltonian.cpp index 28a65ca1a..4d1d26b5a 100644 --- a/core/src/Spirit/Hamiltonian.cpp +++ b/core/src/Spirit/Hamiltonian.cpp @@ -99,7 +99,7 @@ try { // TODO: are these the desired defaults? image->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Micromagnetic( - 0, Vector3{0, 0, 1}, + 0, 0, Vector3{0, 0, 1}, Matrix3::Zero(), Matrix3::Zero(), Matrix3::Zero(), diff --git a/core/src/data/Geometry.cpp b/core/src/data/Geometry.cpp index 0cb39915c..a39f4bb65 100644 --- a/core/src/data/Geometry.cpp +++ b/core/src/data/Geometry.cpp @@ -81,11 +81,6 @@ namespace Data std::cout << bv.transpose() << "\n--\n"; std::cout << "cell_volume " << cell_volume << "\n"; - // Saturation Magnetisation Density in A/m - this->Ms = 0; - for(auto & mu_s : cell_composition.mu_s) - this->Ms += mu_s * Utility::Constants_Micromagnetic::mu_B / cell_volume; - // Calculate the type of geometry this->calculateGeometryType(); @@ -162,7 +157,14 @@ namespace Data } } - + scalar Geometry::getMs() + { + // Saturation Magnetisation Density in A/m + scalar Ms = 0; + for(auto & mu_s : cell_composition.mu_s) + Ms += mu_s * Utility::Constants_Micromagnetic::mu_B / cell_volume; + return Ms; + } std::vector compute_delaunay_triangulation_3D(const std::vector & points) try diff --git a/core/src/engine/Hamiltonian_Micromagnetic.cpp b/core/src/engine/Hamiltonian_Micromagnetic.cpp index 72b6830a2..f1f8a648a 100644 --- a/core/src/engine/Hamiltonian_Micromagnetic.cpp +++ b/core/src/engine/Hamiltonian_Micromagnetic.cpp @@ -25,11 +25,12 @@ namespace Engine { Hamiltonian_Micromagnetic::Hamiltonian_Micromagnetic( - scalar external_field_magnitude, Vector3 external_field_normal, Matrix3 anisotropy_tensor, + scalar Ms, scalar external_field_magnitude, Vector3 external_field_normal, Matrix3 anisotropy_tensor, Matrix3 exchange_tensor, Matrix3 dmi_tensor, DDI_Method ddi_method, intfield ddi_n_periodic_images, scalar ddi_radius, std::shared_ptr geometry, int spatial_gradient_order, intfield boundary_conditions ) - : Hamiltonian( boundary_conditions ), + : Ms( Ms ), + Hamiltonian( boundary_conditions ), spatial_gradient_order( spatial_gradient_order ), geometry( geometry ), external_field_magnitude( external_field_magnitude ), @@ -174,7 +175,7 @@ void Hamiltonian_Micromagnetic::Update_Energy_Contributions() this->energy_contributions_per_spin = std::vector>( 0 ); // External field - if( this->external_field_magnitude > 0 ) + if( std::abs( this->external_field_magnitude ) > 0 ) { this->energy_contributions_per_spin.push_back( { "Zeeman", scalarfield( 0 ) } ); this->idx_zeeman = this->energy_contributions_per_spin.size() - 1; @@ -182,7 +183,7 @@ void Hamiltonian_Micromagnetic::Update_Energy_Contributions() else this->idx_zeeman = -1; - if( anisotropy_tensor.norm() == 0.0 ) + if( anisotropy_tensor.norm() > 0.0 ) { this->energy_contributions_per_spin.push_back( { "Anisotropy", scalarfield( 0 ) } ); this->idx_anisotropy = this->energy_contributions_per_spin.size() - 1; @@ -190,7 +191,7 @@ void Hamiltonian_Micromagnetic::Update_Energy_Contributions() else this->idx_anisotropy = -1; - if( exchange_tensor.norm() == 0.0 ) + if( exchange_tensor.norm() > 0.0 ) { this->energy_contributions_per_spin.push_back( { "Exchange", scalarfield( 0 ) } ); this->idx_exchange = this->energy_contributions_per_spin.size() - 1; @@ -198,7 +199,7 @@ void Hamiltonian_Micromagnetic::Update_Energy_Contributions() else this->idx_exchange = -1; - if( dmi_tensor.norm() == 0.0 ) + if( dmi_tensor.norm() > 0.0 ) { this->energy_contributions_per_spin.push_back( { "DMI", scalarfield( 0 ) } ); this->idx_dmi = this->energy_contributions_per_spin.size() - 1; @@ -213,6 +214,14 @@ void Hamiltonian_Micromagnetic::Update_Energy_Contributions() } else this->idx_ddi = -1; + + // printf("idx_zeeman %i\n", idx_zeeman); + // printf("idx_exchange %i\n", idx_exchange); + // printf("idx_dmi %i\n", idx_dmi); + // printf("idx_anisotropy %i\n", idx_anisotropy); + // printf("idx_ddi %i\n", idx_ddi); + // std::cout << exchange_tensor << "\n"; + // std::cout << dmi_tensor << "\n ===== \n"; } void Hamiltonian_Micromagnetic::Energy_Contributions_per_Spin( @@ -257,14 +266,12 @@ void Hamiltonian_Micromagnetic::Energy_Contributions_per_Spin( void Hamiltonian_Micromagnetic::E_Zeeman( const vectorfield & spins, scalarfield & Energy ) { - auto & mu_s = this->geometry->mu_s; - #pragma omp parallel for for( int icell = 0; icell < geometry->n_cells_total; ++icell ) { if( check_atom_type( this->geometry->atom_types[icell] ) ) - Energy[icell] - -= mu_s[icell] * this->external_field_magnitude * this->external_field_normal.dot( spins[icell] ); + Energy[icell] -= C::Joule * geometry->cell_volume * Ms * this->external_field_magnitude + * this->external_field_normal.dot( spins[icell] ); } } @@ -273,13 +280,50 @@ void Hamiltonian_Micromagnetic::E_Update( const vectorfield & spins, scalarfield #pragma omp parallel for for( int icell = 0; icell < geometry->n_cells_total; ++icell ) { - Energy[icell] -= 0.5 * geometry->Ms * gradient[icell].dot( spins[icell] ); + Energy[icell] -= 0.5 * Ms * gradient[icell].dot( spins[icell] ); } } -void Hamiltonian_Micromagnetic::E_Anisotropy( const vectorfield & spins, scalarfield & Energy ) {} +void Hamiltonian_Micromagnetic::E_Anisotropy( const vectorfield & spins, scalarfield & Energy ) +{ + #pragma omp parallel for + for( int icell = 0; icell < geometry->n_cells_total; ++icell) + { + Energy[icell] -= geometry->cell_volume * C::Joule * spins[icell].dot(anisotropy_tensor * spins[icell]); + } +} -void Hamiltonian_Micromagnetic::E_Exchange( const vectorfield & spins, scalarfield & Energy ) {} +void Hamiltonian_Micromagnetic::E_Exchange( const vectorfield & spins, scalarfield & Energy ) +{ + auto delta = geometry->cell_size; +#pragma omp parallel for + for( unsigned int icell = 0; icell < geometry->n_cells_total; ++icell ) + { + Vector3 grad_n; + for( unsigned int alpha = 0; alpha < 3; ++alpha ) // alpha + { + int icell_plus = idx_from_pair( + icell, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, + neigh[2 * alpha] ); + + int icell_minus = idx_from_pair( + icell, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, + neigh[2 * alpha + 1] ); + + if( icell_plus >= 0 || icell_minus >= 0 ) + { + if( icell_plus == -1 ) + icell_plus = icell; + if( icell_minus == -1 ) + icell_minus = icell; + } + grad_n[alpha] = ( spins[icell_plus][alpha] - spins[icell][alpha] ) / delta[alpha]; + } + // Evaluate gradient s[alpha] + // J/m * 1/m * 1/m * m^3 + Energy[icell] -= C::Joule * geometry->cell_volume * ( grad_n.dot( exchange_tensor * grad_n) ); + } +} void Hamiltonian_Micromagnetic::E_DMI( const vectorfield & spins, scalarfield & Energy ) {} @@ -328,14 +372,13 @@ void Hamiltonian_Micromagnetic::Gradient( const vectorfield & spins, vectorfield void Hamiltonian_Micromagnetic::Gradient_Zeeman( vectorfield & gradient ) { - // In this context this is magnetisation per cell - auto & mu_s = this->geometry->mu_s; #pragma omp parallel for for( int icell = 0; icell < geometry->n_cells_total; ++icell ) { if( check_atom_type( this->geometry->atom_types[icell] ) ) - gradient[icell] -= mu_s[icell] * C::mu_B * this->external_field_magnitude * this->external_field_normal; + gradient[icell] + -= C::Joule * Ms * geometry->cell_volume * this->external_field_magnitude * this->external_field_normal; } } @@ -347,120 +390,14 @@ void Hamiltonian_Micromagnetic::Gradient_Anisotropy( const vectorfield & spins, #pragma omp parallel for for( int icell = 0; icell < geometry->n_cells_total; ++icell ) { - // for( int iani = 0; iani < 1; ++iani ) - // { - // gradient[icell] -= 2.0 * 8.44e6 / Ms * temp3 * temp3.dot(spins[icell]); - gradient[icell] -= 2.0 * C::mu_B * anisotropy_tensor * spins[icell] / geometry->Ms; - // gradient[icell] -= 2.0 * this->anisotropy_magnitudes[iani] / Ms * ((pow(temp2.dot(spins[icell]),2)+ - // pow(temp3.dot(spins[icell]), 2))*(temp1.dot(spins[icell])*temp1)+ (pow(temp1.dot(spins[icell]), 2) + - // pow(temp3.dot(spins[icell]), 2))*(temp2.dot(spins[icell])*temp2)+(pow(temp1.dot(spins[icell]),2)+ - // pow(temp2.dot(spins[icell]), 2))*(temp3.dot(spins[icell])*temp3)); gradient[icell] += 2.0 * 50000 / Ms * - // ((pow(temp2.dot(spins[icell]), 2) + pow(temp3.dot(spins[icell]), 2))*(temp1.dot(spins[icell])*temp1) + - // (pow(temp1.dot(spins[icell]), 2) + pow(temp3.dot(spins[icell]), 2))*(temp2.dot(spins[icell])*temp2)); - // } + gradient[icell] -= 2.0 * geometry->cell_volume * C::Joule * anisotropy_tensor * spins[icell]; } } void Hamiltonian_Micromagnetic::Gradient_Exchange( const vectorfield & spins, vectorfield & gradient ) { auto & delta = geometry->cell_size; -// scalar delta[3] = { 3e-10, 3e-10, 3e-9 }; -// scalar delta[3] = { 277e-12, 277e-12, 277e-12 }; - -// nongradient implementation -/* -#pragma omp parallel for -for (unsigned int icell = 0; icell < geometry->n_cells_total; ++icell) -{ - int ispin = icell;//basically id of a cell - for (unsigned int i = 0; i < 3; ++i) - { - - int ispin_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, -geometry->atom_types, neigh[i]); int ispin_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, -geometry->n_cell_atoms, geometry->atom_types, neigh[i + 1]); if (ispin_plus == -1) { ispin_plus = ispin; - } - if (ispin_minus == -1) { - ispin_minus = ispin; - } - gradient[ispin][i] -= exchange_tensor(i, i)*(spins[ispin_plus][i] - 2 * spins[ispin][i] + spins[ispin_minus][i]) -/ (delta[i]) / (delta[i]); - - } - if (A_is_nondiagonal == true) { - int ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, -geometry->atom_types, neigh[6]); int ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, -geometry->n_cell_atoms, geometry->atom_types, neigh[7]); int ispin_plus_minus = idx_from_pair(ispin, -boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[8]); int ispin_minus_plus = -idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[9]); - - if (ispin_plus_plus == -1) { - ispin_plus_plus = ispin; - } - if (ispin_minus_minus == -1) { - ispin_minus_minus = ispin; - } - if (ispin_plus_minus == -1) { - ispin_plus_minus = ispin; - } - if (ispin_minus_plus == -1) { - ispin_minus_plus = ispin; - } - gradient[ispin][0] -= exchange_tensor(0, 1)*(spins[ispin_plus_plus][1] - spins[ispin_plus_minus][1] - -spins[ispin_minus_plus][1] + spins[ispin_minus_minus][1]) / (delta[0]) / (delta[1]) / 4; gradient[ispin][1] -= -exchange_tensor(1, 0)*(spins[ispin_plus_plus][0] - spins[ispin_plus_minus][0] - spins[ispin_minus_plus][0] + -spins[ispin_minus_minus][0]) / (delta[0]) / (delta[1]) / 4; - - ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, -geometry->atom_types, neigh[10]); ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, -geometry->n_cell_atoms, geometry->atom_types, neigh[11]); ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, -geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[12]); ispin_minus_plus = idx_from_pair(ispin, -boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[13]); - - if (ispin_plus_plus == -1) { - ispin_plus_plus = ispin; - } - if (ispin_minus_minus == -1) { - ispin_minus_minus = ispin; - } - if (ispin_plus_minus == -1) { - ispin_plus_minus = ispin; - } - if (ispin_minus_plus == -1) { - ispin_minus_plus = ispin; - } - gradient[ispin][0] -= exchange_tensor(0, 2)*(spins[ispin_plus_plus][2] - spins[ispin_plus_minus][2] - -spins[ispin_minus_plus][2] + spins[ispin_minus_minus][2]) / (delta[0]) / (delta[2]) / 4; gradient[ispin][2] -= -exchange_tensor(2, 0)*(spins[ispin_plus_plus][0] - spins[ispin_plus_minus][0] - spins[ispin_minus_plus][0] + -spins[ispin_minus_minus][0]) / (delta[0]) / (delta[2]) / 4; - - ispin_plus_plus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, -geometry->atom_types, neigh[14]); ispin_minus_minus = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, -geometry->n_cell_atoms, geometry->atom_types, neigh[15]); ispin_plus_minus = idx_from_pair(ispin, boundary_conditions, -geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[16]); ispin_minus_plus = idx_from_pair(ispin, -boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[17]); - - if (ispin_plus_plus == -1) { - ispin_plus_plus = ispin; - } - if (ispin_minus_minus == -1) { - ispin_minus_minus = ispin; - } - if (ispin_plus_minus == -1) { - ispin_plus_minus = ispin; - } - if (ispin_minus_plus == -1) { - ispin_minus_plus = ispin; - } - gradient[ispin][1] -= exchange_tensor(1, 2)*(spins[ispin_plus_plus][2] - spins[ispin_plus_minus][2] - -spins[ispin_minus_plus][2] + spins[ispin_minus_minus][2]) / (delta[1]) / (delta[2]) / 4; gradient[ispin][2] -= -exchange_tensor(2, 1)*(spins[ispin_plus_plus][1] - spins[ispin_plus_minus][1] - spins[ispin_minus_plus][1] + -spins[ispin_minus_minus][1]) / (delta[1]) / (delta[2]) / 4; - } - -}*/ - -// Gradient implementation + // Gradient implementation #pragma omp parallel for for( unsigned int icell = 0; icell < geometry->n_cells_total; ++icell ) { @@ -480,128 +417,11 @@ spins[ispin_minus_minus][1]) / (delta[1]) / (delta[2]) / 4; if( icell_minus == -1 ) icell_minus = icell; - gradient[icell] -= 2 * C::mu_B * exchange_tensor + gradient[icell] -= 2 * C::Joule * exchange_tensor * geometry->cell_volume * ( spins[icell_plus] - 2 * spins[icell] + spins[icell_minus] ) - / ( geometry->Ms * delta[i] * delta[i] ); + / ( delta[i] * delta[i] ); } - - // gradient[icell][0] -= 2*exchange_tensor(i, i) / Ms * (spins[icell_plus][0] - 2*spins[icell][0] + - // spins[icell_minus][0]) / (delta[i]) / (delta[i]); gradient[icell][1] -= 2*exchange_tensor(i, i) / Ms * - // (spins[icell_plus][1] - 2*spins[icell][1] + spins[icell_minus][1]) / (delta[i]) / (delta[i]); - // gradient[icell][2] - // -= 2*exchange_tensor(i, i) / Ms * (spins[icell_plus][2] - 2*spins[icell][2] + spins[icell_minus][2]) / - // (delta[i]) / (delta[i]); } - // if( this->A_is_nondiagonal ) - // { - // int ispin = icell; - - // // xy - // int ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, - // geometry->atom_types, neigh[0]); int ispin_left = idx_from_pair(ispin, boundary_conditions, - // geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); int ispin_top = - // idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, - // geometry->atom_types, neigh[2]); int ispin_bottom = idx_from_pair(ispin, boundary_conditions, - // geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); - - // if( ispin_right == -1 ) - // ispin_right = ispin; - // if( ispin_left == -1 ) - // ispin_left = ispin; - // if( ispin_top == -1 ) - // ispin_top = ispin; - // if( ispin_bottom == -1 ) - // ispin_bottom = ispin; - - // gradient[ispin][0] -= 2*exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](0, 0) - - // spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](0, 1) - - // spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); gradient[ispin][0] -= 2*exchange_tensor(1, 0) / Ms * - // ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[1] + - // (spatial_gradient[ispin_right](0, 1) - spatial_gradient[ispin_left](0, 1)) / 4 / delta[0]); - // gradient[ispin][1] - // -= 2*exchange_tensor(0, 1) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, - // 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - spatial_gradient[ispin_left](1, 1)) / 4 / - // delta[0]); gradient[ispin][1] -= 2*exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - - // spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](1, 1) - - // spatial_gradient[ispin_left](1, 1)) / 4 / delta[0]); gradient[ispin][2] -= 2*exchange_tensor(0, 1) / Ms * - // ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[1] + - // (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / delta[0]); - // gradient[ispin][2] - // -= 2*exchange_tensor(1, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, - // 0)) / 4 / delta[1] + (spatial_gradient[ispin_right](2, 1) - spatial_gradient[ispin_left](2, 1)) / 4 / - // delta[0]); - - // // xz - // ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, - // geometry->atom_types, neigh[0]); ispin_left = idx_from_pair(ispin, boundary_conditions, - // geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[1]); ispin_top = - // idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, - // geometry->atom_types, neigh[4]); ispin_bottom = idx_from_pair(ispin, boundary_conditions, - // geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); - - // if( ispin_right == -1 ) - // ispin_right = ispin; - // if( ispin_left == -1 ) - // ispin_left = ispin; - // if( ispin_top == -1 ) - // ispin_top = ispin; - // if( ispin_bottom == -1 ) - // ispin_bottom = ispin; - - // gradient[ispin][0] -= 2*exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](0, 0) - - // spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - - // spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); gradient[ispin][0] -= 2*exchange_tensor(2, 0) / Ms * - // ((spatial_gradient[ispin_top](0, 0) - spatial_gradient[ispin_bottom](0, 0)) / 4 / delta[2] + - // (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); - // gradient[ispin][1] - // -= 2*exchange_tensor(0, 2) / Ms * ((spatial_gradient[ispin_top](1, 0) - spatial_gradient[ispin_bottom](1, - // 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / - // delta[0]); gradient[ispin][1] -= 2*exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](1, 0) - - // spatial_gradient[ispin_bottom](1, 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - - // spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); gradient[ispin][2] -= 2*exchange_tensor(0, 2) / Ms * - // ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, 0)) / 4 / delta[2] + - // (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - // gradient[ispin][2] - // -= 2*exchange_tensor(2, 0) / Ms * ((spatial_gradient[ispin_top](2, 0) - spatial_gradient[ispin_bottom](2, - // 0)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / - // delta[0]); - - // // yz - // ispin_right = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, - // geometry->atom_types, neigh[2]); ispin_left = idx_from_pair(ispin, boundary_conditions, - // geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[3]); ispin_top = - // idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, - // geometry->atom_types, neigh[4]); ispin_bottom = idx_from_pair(ispin, boundary_conditions, - // geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, neigh[5]); - - // if( ispin_right == -1 ) - // ispin_right = ispin; - // if( ispin_left == -1 ) - // ispin_left = ispin; - // if( ispin_top == -1 ) - // ispin_top = ispin; - // if( ispin_bottom == -1 ) - // ispin_bottom = ispin; - - // gradient[ispin][0] -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](0, 1) - - // spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](0, 2) - - // spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); gradient[ispin][0] -= 2 * exchange_tensor(2, 1) / Ms - // * ((spatial_gradient[ispin_top](0, 1) - spatial_gradient[ispin_bottom](0, 1)) / 4 / delta[2] + - // (spatial_gradient[ispin_right](0, 2) - spatial_gradient[ispin_left](0, 2)) / 4 / delta[0]); - // gradient[ispin][1] - // -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](1, 1) - - // spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](1, 2) - - // spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); gradient[ispin][1] -= 2 * exchange_tensor(2, 1) / Ms - // * ((spatial_gradient[ispin_top](1, 1) - spatial_gradient[ispin_bottom](1, 1)) / 4 / delta[2] + - // (spatial_gradient[ispin_right](1, 2) - spatial_gradient[ispin_left](1, 2)) / 4 / delta[0]); - // gradient[ispin][2] - // -= 2 * exchange_tensor(1, 2) / Ms * ((spatial_gradient[ispin_top](2, 1) - - // spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + (spatial_gradient[ispin_right](2, 2) - - // spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); gradient[ispin][2] -= 2 * exchange_tensor(2, 1) / Ms - // * ((spatial_gradient[ispin_top](2, 1) - spatial_gradient[ispin_bottom](2, 1)) / 4 / delta[2] + - // (spatial_gradient[ispin_right](2, 2) - spatial_gradient[ispin_left](2, 2)) / 4 / delta[0]); - - // } } } @@ -643,7 +463,6 @@ dn3/dr1 dn3/dr2 dn3/dr3 void Hamiltonian_Micromagnetic::Gradient_DMI( const vectorfield & spins, vectorfield & gradient ) { - auto Ms = geometry->Ms; #pragma omp parallel for for( unsigned int icell = 0; icell < geometry->n_cells_total; ++icell ) { @@ -673,7 +492,6 @@ void Hamiltonian_Micromagnetic::Gradient_DDI( const vectorfield & spins, vectorf void Hamiltonian_Micromagnetic::Gradient_DDI_Direct( const vectorfield & spins, vectorfield & gradient ) { - auto Ms = geometry->Ms; auto delta = geometry->cell_size; auto cell_volume = geometry->cell_size[0] * geometry->cell_size[1] * geometry->cell_size[2]; scalar mult = C::mu_0 / cell_volume * ( cell_volume * Ms * C::Joule ) * ( cell_volume * Ms * C::Joule ); @@ -828,13 +646,13 @@ void Hamiltonian_Micromagnetic::E_DDI_FFT( const vectorfield & spins, scalarfiel // } // } // std::cerr << "Avg. Gradient = " << avg[0]/this->geometry->nos << " " << avg[1]/this->geometry->nos << " " << - // avg[2]/this->geometry->nos << std::endl; std::cerr << "Avg. Deviation = " << deviation[0]/this->geometry->nos << - // " " << deviation[1]/this->geometry->nos << " " << deviation[2]/this->geometry->nos << std::endl; + // avg[2]/this->geometry->nos << std::endl; std::cerr << "Avg. Deviation = " << deviation[0]/this->geometry->nos + // << " " << deviation[1]/this->geometry->nos << " " << deviation[2]/this->geometry->nos << std::endl; //==== DEBUG: end gradient comparison ==== // TODO: add dot_scaled to Vectormath and use that #pragma omp parallel for - for( int ispin = 0; ispin < geometry->nos; ispin++ ) + for( int ispin = 0; ispin < geometry->nos; ispin++ ) { Energy[ispin] += 0.5 * spins[ispin].dot( gradients_temp[ispin] ); // Energy_DDI += 0.5 * spins[ispin].dot(gradients_temp[ispin]); @@ -843,16 +661,19 @@ void Hamiltonian_Micromagnetic::E_DDI_FFT( const vectorfield & spins, scalarfiel void Hamiltonian_Micromagnetic::FFT_Demag_Tensors( FFT::FFT_Plan & fft_plan_dipole, int img_a, int img_b, int img_c ) { + auto delta = geometry->cell_size; - auto delta = geometry->cell_size; - auto cell_volume = geometry->cell_size[0] * geometry->cell_size[1] * geometry->cell_size[2]; - auto Ms = geometry->Ms; // Prefactor of DDI // The energy is proportional to spin_direction * Demag_tensor * spin_direction // The 'mult' factor is chosen such that the cell resolved energy has // the dimension of total energy per cell in meV - scalar mult = C::mu_0 / cell_volume * ( cell_volume * Ms * C::Joule ) * ( cell_volume * Ms * C::Joule ); + scalar mult = C::mu_0 * geometry->cell_volume * (Ms) * (Ms) * C::Joule*C::Joule; + + std::cout << "cell_size " << geometry->cell_size.transpose() << "\n"; + std::cout << "cell_volume " << geometry->cell_volume << "\n"; + std::cout << "mult " << mult << "\n"; + std::cout << "Ms " << Ms << "\n"; // Size of original geometry int Na = geometry->n_cells[0]; diff --git a/core/src/io/Configparser.cpp b/core/src/io/Configparser.cpp index 0714f8f5c..8360b6a30 100644 --- a/core/src/io/Configparser.cpp +++ b/core/src/io/Configparser.cpp @@ -528,7 +528,7 @@ std::shared_ptr Geometry_from_Config( const std::string configFi parameter_log.push_back( fmt::format( " the geometry is {}-dimensional", geometry->dimensionality ) ); parameter_log.push_back( fmt::format( " unit cell size [m]: {}", geometry->cell_size.transpose() ) ); parameter_log.push_back( fmt::format( " unit cell volume [m^3]: {}", geometry->cell_volume ) ); - parameter_log.push_back( fmt::format( " Ms[A/m]: {}", geometry->Ms ) ); + // parameter_log.push_back( fmt::format( " Ms[A/m]: {}", geometry->Ms ) ); Log.SendBlock( Log_Level::Parameter, Log_Sender::IO, parameter_log ); @@ -1567,6 +1567,8 @@ Hamiltonian_Micromagnetic_from_Config( const std::string configFile, const std:: std::vector boundary_conditions_i = { 0, 0, 0 }; intfield boundary_conditions = { false, false, false }; + scalar Ms; + // The order of the finite difference approximation of the spatial gradient int spatial_gradient_order = 1; @@ -1612,6 +1614,15 @@ Hamiltonian_Micromagnetic_from_Config( const std::string configFile, const std:: // Precision of the spatial gradient calculation myfile.Read_Single( spatial_gradient_order, "spatial_gradient_order" ); + if( myfile.Find("Ms") ) + { + myfile.Read_Single(Ms, "Ms"); + } else { + Log( Log_Level::Warning, Log_Sender::IO, + "Input for 'Ms' has not been found. Inferring from atomistic cell instead." ); + Ms = geometry->getMs(); + } + // Field myfile.Read_Single( field, "external_field_magnitude" ); myfile.Read_Vector3( field_normal, "external_field_normal" ); @@ -1728,6 +1739,8 @@ Hamiltonian_Micromagnetic_from_Config( const std::string configFile, const std:: Log( Log_Level::Parameter, Log_Sender::IO, "Hamiltonian_Micromagnetic:" ); Log( Log_Level::Parameter, Log_Sender::IO, fmt::format( " {:<24} = {}", "discretisation order", spatial_gradient_order ) ); + Log( Log_Level::Parameter, Log_Sender::IO, + fmt::format( " {:<24} = {}", "Ms [A/m]", Ms ) ); Log( Log_Level::Parameter, Log_Sender::IO, fmt::format( " {:<24} = {} {} {}", "boundary conditions", boundary_conditions[0], boundary_conditions[1], @@ -1754,8 +1767,8 @@ Hamiltonian_Micromagnetic_from_Config( const std::string configFile, const std:: ddi_n_periodic_images[2] ) ); Log( Log_Level::Parameter, Log_Sender::IO, fmt::format( " {:<21} = {}", "ddi_radius", ddi_radius ) ); - auto hamiltonian = std::unique_ptr( new Engine::Hamiltonian_Micromagnetic( - field, field_normal, anisotropy_tensor, exchange_tensor, dmi_tensor, ddi_method, ddi_n_periodic_images, + auto hamiltonian = std::unique_ptr( new Engine::Hamiltonian_Micromagnetic( + Ms, field, field_normal, anisotropy_tensor, exchange_tensor, dmi_tensor, ddi_method, ddi_n_periodic_images, ddi_radius, geometry, spatial_gradient_order, boundary_conditions ) ); Log( Log_Level::Info, Log_Sender::IO, "Hamiltonian_Micromagnetic: built" ); From 8447409d4a72761a2a452859f26660e89ec1b024 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 9 Jun 2021 11:52:20 +0200 Subject: [PATCH 26/45] Incorporated upstream changes in core/data --- core/include/data/Geometry.hpp | 17 +- core/include/data/Parameters_Method.hpp | 3 +- core/include/data/Parameters_Method_EMA.hpp | 45 +- core/include/data/Parameters_Method_GNEB.hpp | 71 +- core/include/data/Parameters_Method_LLG.hpp | 93 +- core/include/data/Parameters_Method_MC.hpp | 79 +- core/include/data/Parameters_Method_MMF.hpp | 47 +- .../include/data/Parameters_Method_Solver.hpp | 5 +- core/include/data/Spin_System.hpp | 141 +- core/include/data/Spin_System_Chain.hpp | 171 +-- core/src/data/Geometry.cpp | 1205 +++++++++-------- core/src/data/Spin_System.cpp | 221 +-- core/src/data/Spin_System_Chain.cpp | 93 +- 13 files changed, 1115 insertions(+), 1076 deletions(-) diff --git a/core/include/data/Geometry.hpp b/core/include/data/Geometry.hpp index c9d1c9a49..3473823bb 100644 --- a/core/include/data/Geometry.hpp +++ b/core/include/data/Geometry.hpp @@ -11,18 +11,19 @@ namespace Data { + // TODO: replace that type with Eigen! -typedef struct +struct vector3_t { double x, y, z; -} vector3_t; -typedef struct +}; +struct vector2_t { double x, y; -} vector2_t; +}; -typedef std::array tetrahedron_t; -typedef std::array triangle_t; +using tetrahedron_t = std::array; +using triangle_t = std::array; enum class BravaisLatticeType { @@ -179,5 +180,7 @@ class Geometry // TODO: find better place (?) std::vector compute_delaunay_triangulation_2D( const std::vector & points ); + } // namespace Data -#endif + +#endif \ No newline at end of file diff --git a/core/include/data/Parameters_Method.hpp b/core/include/data/Parameters_Method.hpp index dc79549ce..8307c7929 100644 --- a/core/include/data/Parameters_Method.hpp +++ b/core/include/data/Parameters_Method.hpp @@ -40,5 +40,6 @@ struct Parameters_Method IO::VF_FileFormat output_vf_filetype = IO::VF_FileFormat::OVF_TEXT; }; -} +} // namespace Data + #endif \ No newline at end of file diff --git a/core/include/data/Parameters_Method_EMA.hpp b/core/include/data/Parameters_Method_EMA.hpp index bec426ac7..3d8322026 100644 --- a/core/include/data/Parameters_Method_EMA.hpp +++ b/core/include/data/Parameters_Method_EMA.hpp @@ -1,30 +1,33 @@ #pragma once -#ifndef DATA_PARAMETERS_METHOD_EMA_H -#define DATA_PARAMETERS_METHOD_EMA_H +#ifndef SPIRIT_DATA_PARAMETERS_METHOD_EMA_HPP +#define SPIRIT_DATA_PARAMETERS_METHOD_EMA_HPP #include namespace Data { - // EMA_Parameters contains all EMA information about the spin system - struct Parameters_Method_EMA : public Parameters_Method - { - int n_modes = 10; - int n_mode_follow = 0; - scalar frequency = 0.02; - scalar amplitude = 1; - bool snapshot = false; - // ----------------- Output -------------- - // Energy output settings - bool output_energy_step = false; - bool output_energy_archive = false; - bool output_energy_spin_resolved = false; - bool output_energy_divide_by_nspins = true; +// EMA_Parameters contains all EMA information about the spin system +struct Parameters_Method_EMA : Parameters_Method +{ + int n_modes = 10; + int n_mode_follow = 0; + scalar frequency = 0.02; + scalar amplitude = 1; + bool snapshot = false; + + // ----------------- Output -------------- + // Energy output settings + bool output_energy_step = false; + bool output_energy_archive = false; + bool output_energy_spin_resolved = false; + bool output_energy_divide_by_nspins = true; + + // Spin configurations output settings + bool output_configuration_step = false; + bool output_configuration_archive = false; +}; + +} // namespace Data - // Spin configurations output settings - bool output_configuration_step = false; - bool output_configuration_archive = false; - }; -} #endif diff --git a/core/include/data/Parameters_Method_GNEB.hpp b/core/include/data/Parameters_Method_GNEB.hpp index e6b912e61..42faec66d 100644 --- a/core/include/data/Parameters_Method_GNEB.hpp +++ b/core/include/data/Parameters_Method_GNEB.hpp @@ -1,43 +1,46 @@ #pragma once -#ifndef DATA_PARAMETERS_METHOD_GNEB_H -#define DATA_PARAMETERS_METHOD_GNEB_H +#ifndef SPIRIT_DATA_PARAMETERS_METHOD_GNEB_HPP +#define SPIRIT_DATA_PARAMETERS_METHOD_GNEB_HPP #include -#include #include +#include namespace Data { - // LLG_Parameters contains all LLG information about the spin system - struct Parameters_Method_GNEB : public Parameters_Method_Solver - { - // Strength of springs between images - scalar spring_constant = 1; - - // The ratio of energy to reaction coordinate in the spring force - // 0 is Rx only, 1 is E only - scalar spring_force_ratio = 0; - - // With which minimum norm per spin the path shortening force should be applied - scalar path_shortening_constant = 0; - - // Number of Energy interpolations between Images - int n_E_interpolations = 10; - - // Temperature [K] - scalar temperature = 0; - // Seed for RNG - int rng_seed = 2006; - // Mersenne twister PRNG - std::mt19937 prng = std::mt19937(rng_seed); - - // ----------------- Output -------------- - bool output_energies_step = false; - bool output_energies_divide_by_nspins = true; - bool output_energies_add_readability_lines = false; - bool output_energies_interpolated = false; - bool output_chain_step = false; - }; -} + +// LLG_Parameters contains all LLG information about the spin system +struct Parameters_Method_GNEB : Parameters_Method_Solver +{ + // Strength of springs between images + scalar spring_constant = 1; + + // The ratio of energy to reaction coordinate in the spring force + // 0 is Rx only, 1 is E only + scalar spring_force_ratio = 0; + + // With which minimum norm per spin the path shortening force should be applied + scalar path_shortening_constant = 0; + + // Number of Energy interpolations between Images + int n_E_interpolations = 10; + + // Temperature [K] + scalar temperature = 0; + // Seed for RNG + int rng_seed = 2006; + // Mersenne twister PRNG + std::mt19937 prng = std::mt19937( rng_seed ); + + // ----------------- Output -------------- + bool output_energies_step = false; + bool output_energies_divide_by_nspins = true; + bool output_energies_add_readability_lines = false; + bool output_energies_interpolated = false; + bool output_chain_step = false; +}; + +} // namespace Data + #endif \ No newline at end of file diff --git a/core/include/data/Parameters_Method_LLG.hpp b/core/include/data/Parameters_Method_LLG.hpp index da1a38a7d..72b88d118 100644 --- a/core/include/data/Parameters_Method_LLG.hpp +++ b/core/include/data/Parameters_Method_LLG.hpp @@ -1,54 +1,57 @@ #pragma once -#ifndef DATA_PARAMETERS_METHOD_LLG_H -#define DATA_PARAMETERS_METHOD_LLG_H +#ifndef SPIRIT_DATA_PARAMETERS_METHOD_LLG_HPP +#define SPIRIT_DATA_PARAMETERS_METHOD_LLG_HPP -#include #include +#include -#include #include +#include namespace Data { - // LLG_Parameters contains all LLG information about the spin system - struct Parameters_Method_LLG : public Parameters_Method_Solver - { - // Damping - scalar damping = 0.3; - scalar beta = 0; - - // Seed for RNG - int rng_seed = 2006; - // Mersenne twister PRNG - std::mt19937 prng = std::mt19937(rng_seed); - - // Temperature [K] - scalar temperature = 0; - // Temperature gradient [K] - Vector3 temperature_gradient_direction = Vector3{1, 0, 0}; - scalar temperature_gradient_inclination = 0; - - // - true: use gradient approximation for STT - // - false: use pinned monolayer approximation with current in z-direction - bool stt_use_gradient = true; - // Spin transfer torque parameter (prop to injected current density) - scalar stt_magnitude = 0; - // Spin current polarisation normal vector - Vector3 stt_polarisation_normal = Vector3{1, 0, 0}; - - // Do direct minimization instead of dynamics - bool direct_minimization = false; - - // ----------------- Output -------------- - // Energy output settings - bool output_energy_step = false; - bool output_energy_archive = false; - bool output_energy_spin_resolved = false; - bool output_energy_divide_by_nspins = true; - bool output_energy_add_readability_lines = false; - // Spin configurations output settings - bool output_configuration_step = false; - bool output_configuration_archive = false; - }; -} + +// LLG_Parameters contains all LLG information about the spin system +struct Parameters_Method_LLG : Parameters_Method_Solver +{ + // Damping + scalar damping = 0.3; + scalar beta = 0; + + // Seed for RNG + int rng_seed = 2006; + // Mersenne twister PRNG + std::mt19937 prng = std::mt19937( rng_seed ); + + // Temperature [K] + scalar temperature = 0; + // Temperature gradient [K] + Vector3 temperature_gradient_direction = Vector3{ 1, 0, 0 }; + scalar temperature_gradient_inclination = 0; + + // - true: use gradient approximation for STT + // - false: use pinned monolayer approximation with current in z-direction + bool stt_use_gradient = true; + // Spin transfer torque parameter (prop to injected current density) + scalar stt_magnitude = 0; + // Spin current polarisation normal vector + Vector3 stt_polarisation_normal = Vector3{ 1, 0, 0 }; + + // Do direct minimization instead of dynamics + bool direct_minimization = false; + + // ----------------- Output -------------- + // Energy output settings + bool output_energy_step = false; + bool output_energy_archive = false; + bool output_energy_spin_resolved = false; + bool output_energy_divide_by_nspins = true; + bool output_energy_add_readability_lines = false; + // Spin configurations output settings + bool output_configuration_step = false; + bool output_configuration_archive = false; +}; + +} // namespace Data + #endif \ No newline at end of file diff --git a/core/include/data/Parameters_Method_MC.hpp b/core/include/data/Parameters_Method_MC.hpp index 9e58d5b88..440ca3049 100644 --- a/core/include/data/Parameters_Method_MC.hpp +++ b/core/include/data/Parameters_Method_MC.hpp @@ -1,47 +1,50 @@ #pragma once -#ifndef DATA_PARAMETERS_METHOD_MC_H -#define DATA_PARAMETERS_METHOD_MC_H +#ifndef SPIRIT_DATA_PARAMETERS_METHOD_MC_HPP +#define SPIRIT_DATA_PARAMETERS_METHOD_MC_HPP #include -#include #include +#include namespace Data { - // LLG_Parameters contains all LLG information about the spin system - struct Parameters_Method_MC : public Parameters_Method - { - // Temperature [K] - scalar temperature = 0; - // Seed for RNG - int rng_seed = 2006; - - // Mersenne twister PRNG - std::mt19937 prng = std::mt19937(rng_seed); - - // Whether to sample spins randomly or in sequence in Metropolis algorithm - bool metropolis_random_sample = true; - // Whether to use the adaptive cone radius (otherwise just uses full sphere sampling) - bool metropolis_step_cone = true; - // Whether to adapt the metropolis cone angle throughout a MC run to try to hit a target acceptance ratio - bool metropolis_cone_adaptive = true; - // The metropolis cone angle - scalar metropolis_cone_angle = 30; - - // Target acceptance ratio of mc steps for adaptive cone angle - scalar acceptance_ratio_target = 0.5; - - // ----------------- Output -------------- - // Energy output settings - bool output_energy_step = false; - bool output_energy_archive = false; - bool output_energy_spin_resolved = false; - bool output_energy_divide_by_nspins = true; - bool output_energy_add_readability_lines = false; - // Spin configurations output settings - bool output_configuration_step = false; - bool output_configuration_archive = false; - }; -} + +// LLG_Parameters contains all LLG information about the spin system +struct Parameters_Method_MC : public Parameters_Method +{ + // Temperature [K] + scalar temperature = 0; + // Seed for RNG + int rng_seed = 2006; + + // Mersenne twister PRNG + std::mt19937 prng = std::mt19937( rng_seed ); + + // Whether to sample spins randomly or in sequence in Metropolis algorithm + bool metropolis_random_sample = true; + // Whether to use the adaptive cone radius (otherwise just uses full sphere sampling) + bool metropolis_step_cone = true; + // Whether to adapt the metropolis cone angle throughout a MC run to try to hit a target acceptance ratio + bool metropolis_cone_adaptive = true; + // The metropolis cone angle + scalar metropolis_cone_angle = 30; + + // Target acceptance ratio of mc steps for adaptive cone angle + scalar acceptance_ratio_target = 0.5; + + // ----------------- Output -------------- + // Energy output settings + bool output_energy_step = false; + bool output_energy_archive = false; + bool output_energy_spin_resolved = false; + bool output_energy_divide_by_nspins = true; + bool output_energy_add_readability_lines = false; + // Spin configurations output settings + bool output_configuration_step = false; + bool output_configuration_archive = false; +}; + +} // namespace Data + #endif \ No newline at end of file diff --git a/core/include/data/Parameters_Method_MMF.hpp b/core/include/data/Parameters_Method_MMF.hpp index 34a3e6d0d..c36c97fd1 100644 --- a/core/include/data/Parameters_Method_MMF.hpp +++ b/core/include/data/Parameters_Method_MMF.hpp @@ -1,32 +1,35 @@ #pragma once -#ifndef DATA_PARAMETERS_METHOD_MMF_H -#define DATA_PARAMETERS_METHOD_MMF_H +#ifndef SPIRIT_DATA_PARAMETERS_METHOD_MMF_HPP +#define SPIRIT_DATA_PARAMETERS_METHOD_MMF_HPP #include -#include #include +#include namespace Data { - // LLG_Parameters contains all LLG information about the spin system - struct Parameters_Method_MMF : public Parameters_Method_Solver - { - // Which mode to follow (based on some conditions) - int n_mode_follow = 0; - // Number of lowest modes to calculate - int n_modes = 10; - // ----------------- Output -------------- - // Energy output settings - bool output_energy_step = false; - bool output_energy_archive = false; - bool output_energy_spin_resolved = false; - bool output_energy_divide_by_nspins = true; - bool output_energy_add_readability_lines = false; - // Spin configurations output settings - bool output_configuration_step = false; - bool output_configuration_archive = false; - }; -} +// LLG_Parameters contains all LLG information about the spin system +struct Parameters_Method_MMF : public Parameters_Method_Solver +{ + // Which mode to follow (based on some conditions) + int n_mode_follow = 0; + // Number of lowest modes to calculate + int n_modes = 10; + + // ----------------- Output -------------- + // Energy output settings + bool output_energy_step = false; + bool output_energy_archive = false; + bool output_energy_spin_resolved = false; + bool output_energy_divide_by_nspins = true; + bool output_energy_add_readability_lines = false; + // Spin configurations output settings + bool output_configuration_step = false; + bool output_configuration_archive = false; +}; + +} // namespace Data + #endif \ No newline at end of file diff --git a/core/include/data/Parameters_Method_Solver.hpp b/core/include/data/Parameters_Method_Solver.hpp index b3686128a..1cda11b66 100644 --- a/core/include/data/Parameters_Method_Solver.hpp +++ b/core/include/data/Parameters_Method_Solver.hpp @@ -9,11 +9,12 @@ namespace Data { // Solver Parameters Base Class -struct Parameters_Method_Solver : public Parameters_Method +struct Parameters_Method_Solver : Parameters_Method { // Time step per iteration [ps] double dt = 1e-3; }; -} +} // namespace Data + #endif \ No newline at end of file diff --git a/core/include/data/Spin_System.hpp b/core/include/data/Spin_System.hpp index 12bbb47d1..45face291 100644 --- a/core/include/data/Spin_System.hpp +++ b/core/include/data/Spin_System.hpp @@ -1,86 +1,87 @@ #pragma once -#ifndef DATA_SPIN_SYSTEM_H -#define DATA_SPIN_SYSTEM_H - -#include -#include +#ifndef SPIRIT_DATA_SPIN_SYSTEM_HPP +#define SPIRIT_DATA_SPIN_SYSTEM_HPP #include "Spirit_Defines.h" -#include -#include #include +#include +#include #include #include -#include -#include #include +#include +#include #include +#include +#include + namespace Data { - /* - Spin_System contains all setup information on one system (one set of spins, one image). - This includes: Spin positions and orientations, Neighbours, Interaction constants, System parameters - */ - class Spin_System - { - public: - // Constructor - Spin_System(std::unique_ptr hamiltonian, - std::shared_ptr geometry, - std::unique_ptr llg_params, - std::unique_ptr mc_params, - std::unique_ptr ema_params, - std::unique_ptr mmf_params, - bool iteration_allowed); - // Copy Constructor - Spin_System(Spin_System const & other); - // Assignment operator - Spin_System& operator=(Spin_System const & other); - // Update - void UpdateEnergy(); - void UpdateEffectiveField(); +/* +Spin_System contains all setup information on one system (one set of spins, one image). +This includes: Spin positions and orientations, Neighbours, Interaction constants, System parameters +*/ +class Spin_System +{ +public: + // Constructor + Spin_System( + std::unique_ptr hamiltonian, std::shared_ptr geometry, + std::unique_ptr llg_params, std::unique_ptr mc_params, + std::unique_ptr ema_params, std::unique_ptr mmf_params, + bool iteration_allowed ); + // Copy constructor + Spin_System( Spin_System const & other ); + // Assignment operator + Spin_System & operator=( Spin_System const & other ); + + // Update + void UpdateEnergy(); + void UpdateEffectiveField(); + + // For multithreading + void Lock() noexcept; + void Unlock() noexcept; + + // Number of spins + int nos; + // Eigenmodes of the system: modes[nem][dim][nos] + std::vector> modes; + // Eigenvalues of the system + std::vector eigenvalues; + // Orientations of the spins: spins[dim][nos] + std::shared_ptr spins; + // Spin Hamiltonian + std::shared_ptr hamiltonian; + // Geometric information + std::shared_ptr geometry; + // Parameters for LLG + std::shared_ptr llg_parameters; + // Parameters for MC + std::shared_ptr mc_parameters; + // Parameters for EMA + std::shared_ptr ema_parameters; + // Parameters for MMF + std::shared_ptr mmf_parameters; + // Is it allowed to iterate on this system or do a singleshot? + bool iteration_allowed; + bool singleshot_allowed; - // For multithreading - void Lock() noexcept; - void Unlock() noexcept; + // Total energy of the spin system (to be updated from outside, i.e. SIB, GNEB, ...) + scalar E; + std::vector> E_array; + // Mean of magnetization + Vector3 M; + // Total effective field of the spins [3][nos] + vectorfield effective_field; - // Number of spins - int nos; - // Eigenmodes of the system: modes[nem][dim][nos] - std::vector> modes; - // Eigenvalues of the system - std::vector eigenvalues; - // Orientations of the Spins: spins[dim][nos] - std::shared_ptr spins; - // Spin Hamiltonian - std::shared_ptr hamiltonian; - // Geometric Information - std::shared_ptr geometry; - // Parameters for LLG - std::shared_ptr llg_parameters; - // Parameters for MC - std::shared_ptr mc_parameters; - // Parameters for EMA - std::shared_ptr ema_parameters; - // Parameters for MMF - std::shared_ptr mmf_parameters; - // Is it allowed to iterate on this system or do a singleshot? - bool iteration_allowed; - bool singleshot_allowed; +private: + // FIFO mutex for thread-safety + Utility::OrderedLock ordered_lock; +}; - // Total Energy of the spin system (to be updated from outside, i.e. SIB, GNEB, ...) - scalar E; - std::vector> E_array; - // Mean of magnetization - Vector3 M; - // Total effective field of the spins [3][nos] - vectorfield effective_field; +} // namespace Data - private: - // FIFO mutex for thread-safety - Ordered_Lock m_lock; - }; -} -#endif +#endif \ No newline at end of file diff --git a/core/include/data/Spin_System_Chain.hpp b/core/include/data/Spin_System_Chain.hpp index 4123680d0..641bc3d50 100644 --- a/core/include/data/Spin_System_Chain.hpp +++ b/core/include/data/Spin_System_Chain.hpp @@ -1,91 +1,96 @@ #pragma once -#ifndef DATA_SPIN_SYSTEM_CHAIN_H -#define DATA_SPIN_SYSTEM_CHAIN_H +#ifndef SPIRIT_DATA_SPIN_SYSTEM_CHAIN_HPP +#define SPIRIT_DATA_SPIN_SYSTEM_CHAIN_HPP #include "Spirit_Defines.h" -#include -#include #include +#include +#include namespace Data { - enum class GNEB_Image_Type - { - Normal = GNEB_IMAGE_NORMAL, - Climbing = GNEB_IMAGE_CLIMBING, - Falling = GNEB_IMAGE_FALLING, - Stationary = GNEB_IMAGE_STATIONARY - }; - - struct HTST_Info - { - bool sparse = false; - - // Relevant images - std::shared_ptr minimum; - std::shared_ptr saddle_point; - - // Eigenmodes - int n_eigenmodes_keep = 0; - VectorX eigenvalues_min = VectorX(0); - MatrixX eigenvectors_min = MatrixX(0, 0); - VectorX eigenvalues_sp = VectorX(0); - MatrixX eigenvectors_sp = MatrixX(0, 0); - VectorX perpendicular_velocity = VectorX(0); - scalar det_min = 0; - scalar det_sp = 0; - - // Prefactor constituents - scalar temperature_exponent = 0; - scalar me = 0; - scalar Omega_0 = 0; - scalar s = 0; - scalar volume_min = 0; - scalar volume_sp = 0; - scalar prefactor_dynamical = 0; - scalar prefactor = 0; - }; - - class Spin_System_Chain - { - public: - // Constructor - Spin_System_Chain(std::vector> images, std::shared_ptr gneb_parameters, bool iteration_allowed = false); - - // For multithreading - void Lock() const; - void Unlock() const; - - int noi; // Number of Images - std::vector> images; - int idx_active_image; - - // Parameters for GNEB Iterations - std::shared_ptr gneb_parameters; - - // Are we allowed to iterate on this chain or do a singleshot? - bool iteration_allowed; - bool singleshot_allowed; - - // Climbing and falling images - std::vector image_type; - - // Reaction coordinates of images in the chain - std::vector Rx; - - // Reaction coordinates of interpolated points - std::vector Rx_interpolated; - - // Total Energy of the spin systems and interpolated values - std::vector E_interpolated; - std::vector> E_array_interpolated; - - // If a prefactor calculation is performed on the chain, we keep the results - HTST_Info htst_info; - - private: - // Mutex for thread-safety - mutable std::mutex mutex; - }; -} + +enum class GNEB_Image_Type +{ + Normal = GNEB_IMAGE_NORMAL, + Climbing = GNEB_IMAGE_CLIMBING, + Falling = GNEB_IMAGE_FALLING, + Stationary = GNEB_IMAGE_STATIONARY +}; + +struct HTST_Info +{ + bool sparse = false; + + // Relevant images + std::shared_ptr minimum; + std::shared_ptr saddle_point; + + // Eigenmodes + int n_eigenmodes_keep = 0; + VectorX eigenvalues_min = VectorX( 0 ); + MatrixX eigenvectors_min = MatrixX( 0, 0 ); + VectorX eigenvalues_sp = VectorX( 0 ); + MatrixX eigenvectors_sp = MatrixX( 0, 0 ); + VectorX perpendicular_velocity = VectorX( 0 ); + scalar det_min = 0; + scalar det_sp = 0; + + // Prefactor constituents + scalar temperature_exponent = 0; + scalar me = 0; + scalar Omega_0 = 0; + scalar s = 0; + scalar volume_min = 0; + scalar volume_sp = 0; + scalar prefactor_dynamical = 0; + scalar prefactor = 0; +}; + +class Spin_System_Chain +{ +public: + // Constructor + Spin_System_Chain( + std::vector> images, std::shared_ptr gneb_parameters, + bool iteration_allowed = false ); + + // For multithreading + void Lock() noexcept; + void Unlock() noexcept; + + int noi; // Number of Images + std::vector> images; + int idx_active_image; + + // Parameters for GNEB Iterations + std::shared_ptr gneb_parameters; + + // Are we allowed to iterate on this chain or do a singleshot? + bool iteration_allowed; + bool singleshot_allowed; + + // Climbing and falling images + std::vector image_type; + + // Reaction coordinates of images in the chain + std::vector Rx; + + // Reaction coordinates of interpolated points + std::vector Rx_interpolated; + + // Total Energy of the spin systems and interpolated values + std::vector E_interpolated; + std::vector> E_array_interpolated; + + // If a prefactor calculation is performed on the chain, we keep the results + HTST_Info htst_info; + +private: + // FIFO mutex for thread-safety + Utility::OrderedLock ordered_lock; +}; + +} // namespace Data + #endif \ No newline at end of file diff --git a/core/src/data/Geometry.cpp b/core/src/data/Geometry.cpp index a39f4bb65..90797a26d 100644 --- a/core/src/data/Geometry.cpp +++ b/core/src/data/Geometry.cpp @@ -4,6 +4,7 @@ #include #include + #include #include @@ -13,794 +14,796 @@ #include -#include #include +#include namespace Data { - Geometry::Geometry(std::vector bravais_vectors, intfield n_cells, - std::vector cell_atoms, Basis_Cell_Composition cell_composition, - scalar lattice_constant, Pinning pinning, Defects defects) : - bravais_vectors(bravais_vectors), n_cells(n_cells), n_cell_atoms(cell_atoms.size()), - cell_atoms(cell_atoms), cell_composition(cell_composition), lattice_constant(lattice_constant), - nos(cell_atoms.size() * n_cells[0] * n_cells[1] * n_cells[2]), - nos_nonvacant(cell_atoms.size() * n_cells[0] * n_cells[1] * n_cells[2]), - n_cells_total(n_cells[0] * n_cells[1] * n_cells[2]), - pinning(pinning), defects(defects) - { - // Generate positions and atom types - this->positions = vectorfield(this->nos); - this->generatePositions(); - // Calculate some useful info - this->calculateBounds(); - this->calculateUnitCellBounds(); - this->calculateDimensionality(); +Geometry::Geometry( + std::vector bravais_vectors, intfield n_cells, std::vector cell_atoms, + Basis_Cell_Composition cell_composition, scalar lattice_constant, Pinning pinning, Defects defects ) + : bravais_vectors( bravais_vectors ), + n_cells( n_cells ), + n_cell_atoms( cell_atoms.size() ), + cell_atoms( cell_atoms ), + cell_composition( cell_composition ), + lattice_constant( lattice_constant ), + nos( cell_atoms.size() * n_cells[0] * n_cells[1] * n_cells[2] ), + nos_nonvacant( cell_atoms.size() * n_cells[0] * n_cells[1] * n_cells[2] ), + n_cells_total( n_cells[0] * n_cells[1] * n_cells[2] ), + pinning( pinning ), + defects( defects ) +{ + // Generate positions and atom types + this->positions = vectorfield( this->nos ); + this->generatePositions(); - // Calculate center of the System - this->center = 0.5 * (this->bounds_min + this->bounds_max); + // Calculate some useful info + this->calculateBounds(); + this->calculateUnitCellBounds(); + this->calculateDimensionality(); - // Generate default atom_types, mu_s and pinning masks - this->atom_types = intfield(this->nos, 0); - this->mu_s = scalarfield(this->nos, 1); - this->mask_unpinned = intfield(this->nos, 1); - this->mask_pinned_cells = vectorfield(this->nos, { 0,0,0 }); + // Calculate center of the System + this->center = 0.5 * ( this->bounds_min + this->bounds_max ); - // Set atom types, mu_s - this->applyCellComposition(); + // Generate default atom_types, mu_s and pinning masks + this->atom_types = intfield( this->nos, 0 ); + this->mu_s = scalarfield( this->nos, 1 ); + this->mask_unpinned = intfield( this->nos, 1 ); + this->mask_pinned_cells = vectorfield( this->nos, { 0, 0, 0 } ); - // Apply additional pinned sites - for( int isite=0; isite < pinning.sites.size(); ++isite ) - { - auto& site = pinning.sites[isite]; - int ispin = site.i + Engine::Vectormath::idx_from_translations( - this->n_cells, this->n_cell_atoms, - {site.translations[0], site.translations[1], site.translations[2]} ); - this->mask_unpinned[ispin] = 0; - this->mask_pinned_cells[ispin] = pinning.spins[isite]; - } + // Set atom types, mu_s + this->applyCellComposition(); - // Apply additional defect sites - for (int i = 0; i < defects.sites.size(); ++i) - { - auto& defect = defects.sites[i]; - int ispin = defects.sites[i].i + Engine::Vectormath::idx_from_translations( + // Apply additional pinned sites + for( int isite = 0; isite < pinning.sites.size(); ++isite ) + { + auto & site = pinning.sites[isite]; + int ispin = site.i + + Engine::Vectormath::idx_from_translations( this->n_cells, this->n_cell_atoms, - {defect.translations[0], defect.translations[1], defect.translations[2]} ); - this->atom_types[ispin] = defects.types[i]; - this->mu_s[ispin] = 0.0; - } - - // Cell size in m - this->cell_size = 1e-10 * this->lattice_constant * Vector3{ bravais_vectors[0].norm(), bravais_vectors[1].norm(), bravais_vectors[2].norm() }; - - // Cell volume in m^3 - this->cell_volume = std::pow(1e-10 * this->lattice_constant, 3.0) * bravais_vectors[0].dot( bravais_vectors[1].cross( bravais_vectors[2] ) ); + { site.translations[0], site.translations[1], site.translations[2] } ); + this->mask_unpinned[ispin] = 0; + this->mask_pinned_cells[ispin] = pinning.spins[isite]; + } - for(auto bv : bravais_vectors) - std::cout << bv.transpose() << "\n--\n"; - std::cout << "cell_volume " << cell_volume << "\n"; + // Apply additional defect sites + for( int i = 0; i < defects.sites.size(); ++i ) + { + auto & defect = defects.sites[i]; + int ispin = defects.sites[i].i + + Engine::Vectormath::idx_from_translations( + this->n_cells, this->n_cell_atoms, + { defect.translations[0], defect.translations[1], defect.translations[2] } ); + this->atom_types[ispin] = defects.types[i]; + this->mu_s[ispin] = 0.0; + } - // Calculate the type of geometry - this->calculateGeometryType(); + // Calculate the type of geometry + this->calculateGeometryType(); - // For updates of triangulation and tetrahedra - this->last_update_n_cell_step = -1; - this->last_update_n_cells = intfield(3, -1); - } + // For updates of triangulation and tetrahedra + this->last_update_n_cell_step = -1; + this->last_update_n_cells = intfield( 3, -1 ); +} - void Geometry::generatePositions() +void Geometry::generatePositions() +{ + const scalar epsilon = 1e-6; + + // Check for erronous input placing two spins on the same location + int max_a = std::min( 10, n_cells[0] ); + int max_b = std::min( 10, n_cells[1] ); + int max_c = std::min( 10, n_cells[2] ); + Vector3 diff; + for( int i = 0; i < n_cell_atoms; ++i ) { - const scalar epsilon = 1e-6; - - // Check for erronous input placing two spins on the same location - int max_a = std::min(10, n_cells[0]); - int max_b = std::min(10, n_cells[1]); - int max_c = std::min(10, n_cells[2]); - Vector3 diff; - for (int i = 0; i < n_cell_atoms; ++i) + for( int j = 0; j < n_cell_atoms; ++j ) { - for (int j = 0; j < n_cell_atoms; ++j) + for( int da = -max_a; da <= max_a; ++da ) { - for (int da = -max_a; da <= max_a; ++da) + for( int db = -max_b; db <= max_b; ++db ) { - for (int db = -max_b; db <= max_b; ++db) + for( int dc = -max_c; dc <= max_c; ++dc ) { - for (int dc = -max_c; dc <= max_c; ++dc) - { - // Norm is zero if translated basis atom is at position of another basis atom - diff = cell_atoms[i] - ( cell_atoms[j] + Vector3{scalar(da), scalar(db), scalar(dc)} ); + // Norm is zero if translated basis atom is at position of another basis atom + diff = cell_atoms[i] - ( cell_atoms[j] + Vector3{ scalar( da ), scalar( db ), scalar( dc ) } ); - if( (i != j || da != 0 || db != 0 || dc != 0) && - std::abs(diff[0]) < epsilon && - std::abs(diff[1]) < epsilon && - std::abs(diff[2]) < epsilon ) - { - Vector3 position = lattice_constant * ( - (da + cell_atoms[i][0]) * bravais_vectors[0] - + (db + cell_atoms[i][1]) * bravais_vectors[1] - + (dc + cell_atoms[i][2]) * bravais_vectors[2] ); - std::string message = fmt::format( - "Unable to initialize Spin-System, since 2 spins occupy the same space" - "within a margin of {} at absolute position ({}).\n" - "Index combination: i={} j={}, translations=({}, {}, {}).\n" - "Please check the config file!", epsilon, position.transpose(), i, j, da, db, dc); - spirit_throw(Utility::Exception_Classifier::System_not_Initialized, Utility::Log_Level::Severe, message); - } + if( ( i != j || da != 0 || db != 0 || dc != 0 ) && std::abs( diff[0] ) < epsilon + && std::abs( diff[1] ) < epsilon && std::abs( diff[2] ) < epsilon ) + { + Vector3 position = lattice_constant + * ( ( da + cell_atoms[i][0] ) * bravais_vectors[0] + + ( db + cell_atoms[i][1] ) * bravais_vectors[1] + + ( dc + cell_atoms[i][2] ) * bravais_vectors[2] ); + std::string message = fmt::format( + "Unable to initialize Spin-System, since 2 spins occupy the same space" + "within a margin of {} at absolute position ({}).\n" + "Index combination: i={} j={}, translations=({}, {}, {}).\n" + "Please check the config file!", + epsilon, position.transpose(), i, j, da, db, dc ); + spirit_throw( + Utility::Exception_Classifier::System_not_Initialized, Utility::Log_Level::Severe, + message ); } } } } } + } - // Generate positions - for (int dc = 0; dc < n_cells[2]; ++dc) + // Generate positions + for( int dc = 0; dc < n_cells[2]; ++dc ) + { + for( int db = 0; db < n_cells[1]; ++db ) { - for (int db = 0; db < n_cells[1]; ++db) + for( int da = 0; da < n_cells[0]; ++da ) { - for (int da = 0; da < n_cells[0]; ++da) + for( int iatom = 0; iatom < n_cell_atoms; ++iatom ) { - for (int iatom = 0; iatom < n_cell_atoms; ++iatom) - { - int ispin = iatom - + dc * n_cell_atoms * n_cells[1] * n_cells[0] - + db * n_cell_atoms * n_cells[0] - + da * n_cell_atoms; - - positions[ispin] = lattice_constant * ( - (da + cell_atoms[iatom][0]) * bravais_vectors[0] - + (db + cell_atoms[iatom][1]) * bravais_vectors[1] - + (dc + cell_atoms[iatom][2]) * bravais_vectors[2] ); - } + int ispin = iatom + dc * n_cell_atoms * n_cells[1] * n_cells[0] + db * n_cell_atoms * n_cells[0] + + da * n_cell_atoms; + + positions[ispin] = lattice_constant + * ( ( da + cell_atoms[iatom][0] ) * bravais_vectors[0] + + ( db + cell_atoms[iatom][1] ) * bravais_vectors[1] + + ( dc + cell_atoms[iatom][2] ) * bravais_vectors[2] ); } } } } +} - scalar Geometry::getMs() - { - // Saturation Magnetisation Density in A/m - scalar Ms = 0; - for(auto & mu_s : cell_composition.mu_s) - Ms += mu_s * Utility::Constants_Micromagnetic::mu_B / cell_volume; - return Ms; - } +scalar Geometry::getMs() +{ + // Saturation Magnetisation Density in A/m + scalar Ms = 0; + for(auto & mu_s : cell_composition.mu_s) + Ms += mu_s * Utility::Constants_Micromagnetic::mu_B / cell_volume; + return Ms; +} - std::vector compute_delaunay_triangulation_3D(const std::vector & points) - try +std::vector compute_delaunay_triangulation_3D( const std::vector & points ) +try +{ + const int ndim = 3; + std::vector tetrahedra; + tetrahedron_t tmp_tetrahedron; + int * current_index; + + orgQhull::Qhull qhull; + qhull.runQhull( "", ndim, points.size(), (coordT *)points.data(), "d Qt Qbb Qz" ); + orgQhull::QhullFacetList facet_list = qhull.facetList(); + for( const auto & facet : facet_list ) { - const int ndim = 3; - std::vector tetrahedra; - tetrahedron_t tmp_tetrahedron; - int *current_index; - - orgQhull::Qhull qhull; - qhull.runQhull("", ndim, points.size(), (coordT *) points.data(), "d Qt Qbb Qz"); - orgQhull::QhullFacetList facet_list = qhull.facetList(); - for(const auto & facet : facet_list) + if( !facet.isUpperDelaunay() ) { - if(!facet.isUpperDelaunay()) + current_index = &tmp_tetrahedron[0]; + for( const auto & vertex : facet.vertices() ) { - current_index = &tmp_tetrahedron[0]; - for(const auto & vertex : facet.vertices()) - { - *current_index++ = vertex.point().id(); - } - tetrahedra.push_back(tmp_tetrahedron); + *current_index++ = vertex.point().id(); } + tetrahedra.push_back( tmp_tetrahedron ); } - return tetrahedra; - } - catch( ... ) - { - spirit_handle_exception_core( "Could not compute 3D Delaunay triangulation of the Geometry. Probably Qhull threw an exception." ); - return std::vector(0); } + return tetrahedra; +} +catch( ... ) +{ + spirit_handle_exception_core( + "Could not compute 3D Delaunay triangulation of the Geometry. Probably Qhull threw an exception." ); + return std::vector( 0 ); +} - std::vector compute_delaunay_triangulation_2D(const std::vector & points) - try +std::vector compute_delaunay_triangulation_2D( const std::vector & points ) +try +{ + const int ndim = 2; + std::vector triangles; + triangle_t tmp_triangle; + int * current_index; + + orgQhull::Qhull qhull; + qhull.runQhull( "", ndim, points.size(), (coordT *)points.data(), "d Qt Qbb Qz" ); + for( const auto & facet : qhull.facetList() ) { - const int ndim = 2; - std::vector triangles; - triangle_t tmp_triangle; - int *current_index; - - orgQhull::Qhull qhull; - qhull.runQhull("", ndim, points.size(), (coordT *) points.data(), "d Qt Qbb Qz"); - for(const auto & facet : qhull.facetList()) + if( !facet.isUpperDelaunay() ) { - if(!facet.isUpperDelaunay()) + current_index = &tmp_triangle[0]; + for( const auto & vertex : facet.vertices() ) { - current_index = &tmp_triangle[0]; - for(const auto & vertex : facet.vertices()) - { - *current_index++ = vertex.point().id(); - } - triangles.push_back(tmp_triangle); + *current_index++ = vertex.point().id(); } + triangles.push_back( tmp_triangle ); } - return triangles; } - catch( ... ) + return triangles; +} +catch( ... ) +{ + spirit_handle_exception_core( + "Could not compute 2D Delaunay triangulation of the Geometry. Probably Qhull threw an exception." ); + return std::vector( 0 ); +} + +const std::vector & Geometry::triangulation( int n_cell_step, std::array ranges ) +{ + // Only every n_cell_step'th cell is used. So we check if there is still enough cells in all + // directions. Note: when visualising, 'n_cell_step' can be used to e.g. olny visualise + // every 2nd spin. + if( ( n_cells[0] / n_cell_step < 2 && n_cells[0] > 1 ) || ( n_cells[1] / n_cell_step < 2 && n_cells[1] > 1 ) + || ( n_cells[2] / n_cell_step < 2 && n_cells[2] > 1 ) ) { - spirit_handle_exception_core( "Could not compute 2D Delaunay triangulation of the Geometry. Probably Qhull threw an exception." ); - return std::vector(0); + _triangulation.clear(); + return _triangulation; } - const std::vector& Geometry::triangulation(int n_cell_step, std::array ranges) + // 2D: triangulation + if( this->dimensionality == 2 ) { - // Only every n_cell_step'th cell is used. So we check if there is still enough cells in all - // directions. Note: when visualising, 'n_cell_step' can be used to e.g. olny visualise - // every 2nd spin. - if ( (n_cells[0]/n_cell_step < 2 && n_cells[0] > 1) || - (n_cells[1]/n_cell_step < 2 && n_cells[1] > 1) || - (n_cells[2]/n_cell_step < 2 && n_cells[2] > 1) ) + // Check if the tetrahedra for this combination of n_cells and n_cell_step has already been calculated + if( this->last_update_n_cell_step != n_cell_step || this->last_update_n_cells[0] != n_cells[0] + || this->last_update_n_cells[1] != n_cells[1] || this->last_update_n_cells[2] != n_cells[2] + || this->last_update_cell_ranges != ranges ) { - _triangulation.clear(); - return _triangulation; - } - - // 2D: triangulation - if (this->dimensionality == 2) - { - // Check if the tetrahedra for this combination of n_cells and n_cell_step has already been calculated - if (this->last_update_n_cell_step != n_cell_step || - this->last_update_n_cells[0] != n_cells[0] || - this->last_update_n_cells[1] != n_cells[1] || - this->last_update_n_cells[2] != n_cells[2] || - this->last_update_cell_ranges != ranges) - { - this->last_update_n_cell_step = n_cell_step; - this->last_update_n_cells[0] = n_cells[0]; - this->last_update_n_cells[1] = n_cells[1]; - this->last_update_n_cells[2] = n_cells[2]; - this->last_update_cell_ranges = ranges; + this->last_update_n_cell_step = n_cell_step; + this->last_update_n_cells[0] = n_cells[0]; + this->last_update_n_cells[1] = n_cells[1]; + this->last_update_n_cells[2] = n_cells[2]; + this->last_update_cell_ranges = ranges; - _triangulation.clear(); + _triangulation.clear(); - std::vector points; + std::vector points; - int icell = 0, idx; + int icell = 0, idx; - int a_min = ranges[0] >= 0 & ranges[0] <= n_cells[0] ? ranges[0] : 0; - int a_max = ranges[1] >= 0 & ranges[1] <= n_cells[0] ? ranges[1] : n_cells[0]; - int b_min = ranges[2] >= 0 & ranges[2] <= n_cells[1] ? ranges[2] : 0; - int b_max = ranges[3] >= 0 & ranges[3] <= n_cells[1] ? ranges[3] : n_cells[1]; - int c_min = ranges[4] >= 0 & ranges[4] <= n_cells[2] ? ranges[4] : 0; - int c_max = ranges[5] >= 0 & ranges[5] <= n_cells[2] ? ranges[5] : n_cells[2]; + int a_min = ranges[0] >= 0 && ranges[0] <= n_cells[0] ? ranges[0] : 0; + int a_max = ranges[1] >= 0 && ranges[1] <= n_cells[0] ? ranges[1] : n_cells[0]; + int b_min = ranges[2] >= 0 && ranges[2] <= n_cells[1] ? ranges[2] : 0; + int b_max = ranges[3] >= 0 && ranges[3] <= n_cells[1] ? ranges[3] : n_cells[1]; + int c_min = ranges[4] >= 0 && ranges[4] <= n_cells[2] ? ranges[4] : 0; + int c_max = ranges[5] >= 0 && ranges[5] <= n_cells[2] ? ranges[5] : n_cells[2]; - int n_a = std::max(1, int(ceil( double((a_max - a_min)) / double(n_cell_step) ))); - int n_b = std::max(1, int(ceil( double((b_max - b_min)) / double(n_cell_step) ))); - int n_c = std::max(1, int(ceil( double((c_max - c_min)) / double(n_cell_step) ))); + int n_a = std::max( 1, int( ceil( double( ( a_max - a_min ) ) / double( n_cell_step ) ) ) ); + int n_b = std::max( 1, int( ceil( double( ( b_max - b_min ) ) / double( n_cell_step ) ) ) ); + int n_c = std::max( 1, int( ceil( double( ( c_max - c_min ) ) / double( n_cell_step ) ) ) ); - int n_points = n_cell_atoms * n_a * n_b * n_c; + int n_points = n_cell_atoms * n_a * n_b * n_c; - if( ( (n_a <= 1 || n_b <= 1) && dimensionality_basis != 2 ) || n_points < 3) - { - _triangulation.clear(); - return _triangulation; - } - points.resize(n_points); + if( ( ( n_a <= 1 || n_b <= 1 ) && dimensionality_basis != 2 ) || n_points < 3 ) + { + _triangulation.clear(); + return _triangulation; + } + points.resize( n_points ); - for (int cell_c=c_min; cell_c& Geometry::tetrahedra(int n_cell_step, std::array ranges) +const std::vector & Geometry::tetrahedra( int n_cell_step, std::array ranges ) +{ + // Only every n_cell_step'th cell is used. So we check if there is still enough cells in all + // directions. Note: when visualising, 'n_cell_step' can be used to e.g. olny visualise + // every 2nd spin. + if( n_cells[0] / n_cell_step < 2 || n_cells[1] / n_cell_step < 2 || n_cells[2] / n_cell_step < 2 ) { - // Only every n_cell_step'th cell is used. So we check if there is still enough cells in all - // directions. Note: when visualising, 'n_cell_step' can be used to e.g. olny visualise - // every 2nd spin. - if (n_cells[0]/n_cell_step < 2 || n_cells[1]/n_cell_step < 2 || n_cells[2]/n_cell_step < 2) - { - _tetrahedra.clear(); - return _tetrahedra; - } + _tetrahedra.clear(); + return _tetrahedra; + } - // 3D: Tetrahedra - if (this->dimensionality == 3) + // 3D: Tetrahedra + if( this->dimensionality == 3 ) + { + // Check if the tetrahedra for this combination of n_cells and n_cell_step has already been calculated + if( this->last_update_n_cell_step != n_cell_step || this->last_update_n_cells[0] != n_cells[0] + || this->last_update_n_cells[1] != n_cells[1] || this->last_update_n_cells[2] != n_cells[2] + || this->last_update_cell_ranges != ranges ) { - // Check if the tetrahedra for this combination of n_cells and n_cell_step has already been calculated - if (this->last_update_n_cell_step != n_cell_step || - this->last_update_n_cells[0] != n_cells[0] || - this->last_update_n_cells[1] != n_cells[1] || - this->last_update_n_cells[2] != n_cells[2] || - this->last_update_cell_ranges != ranges - ) + this->last_update_n_cell_step = n_cell_step; + this->last_update_n_cells[0] = n_cells[0]; + this->last_update_n_cells[1] = n_cells[1]; + this->last_update_n_cells[2] = n_cells[2]; + this->last_update_cell_ranges = ranges; + + int a_min = ranges[0] >= 0 && ranges[0] <= n_cells[0] ? ranges[0] : 0; + int a_max = ranges[1] >= 0 && ranges[1] <= n_cells[0] ? ranges[1] : n_cells[0]; + int b_min = ranges[2] >= 0 && ranges[2] <= n_cells[1] ? ranges[2] : 0; + int b_max = ranges[3] >= 0 && ranges[3] <= n_cells[1] ? ranges[3] : n_cells[1]; + int c_min = ranges[4] >= 0 && ranges[4] <= n_cells[2] ? ranges[4] : 0; + int c_max = ranges[5] >= 0 && ranges[5] <= n_cells[2] ? ranges[5] : n_cells[2]; + + int n_a = std::max( 1, int( ceil( double( ( a_max - a_min ) ) / double( n_cell_step ) ) ) ); + int n_b = std::max( 1, int( ceil( double( ( b_max - b_min ) ) / double( n_cell_step ) ) ) ); + int n_c = std::max( 1, int( ceil( double( ( c_max - c_min ) ) / double( n_cell_step ) ) ) ); + int n_points = n_cell_atoms * n_a * n_b * n_c; + + if( ( ( n_a <= 1 || n_b <= 1 || n_c <= 1 ) && dimensionality_basis < 3 ) || n_points < 4 ) { - this->last_update_n_cell_step = n_cell_step; - this->last_update_n_cells[0] = n_cells[0]; - this->last_update_n_cells[1] = n_cells[1]; - this->last_update_n_cells[2] = n_cells[2]; - this->last_update_cell_ranges = ranges; - - int a_min = ranges[0] >= 0 & ranges[0] <= n_cells[0] ? ranges[0] : 0; - int a_max = ranges[1] >= 0 & ranges[1] <= n_cells[0] ? ranges[1] : n_cells[0]; - int b_min = ranges[2] >= 0 & ranges[2] <= n_cells[1] ? ranges[2] : 0; - int b_max = ranges[3] >= 0 & ranges[3] <= n_cells[1] ? ranges[3] : n_cells[1]; - int c_min = ranges[4] >= 0 & ranges[4] <= n_cells[2] ? ranges[4] : 0; - int c_max = ranges[5] >= 0 & ranges[5] <= n_cells[2] ? ranges[5] : n_cells[2]; - - int n_a = std::max(1, int(ceil( double((a_max - a_min)) / double(n_cell_step) ))); - int n_b = std::max(1, int(ceil( double((b_max - b_min)) / double(n_cell_step) ))); - int n_c = std::max(1, int(ceil( double((c_max - c_min)) / double(n_cell_step) ))); - int n_points = n_cell_atoms * n_a * n_b * n_c; - - if( ( (n_a <= 1 || n_b <= 1 || n_c <= 1) && dimensionality_basis < 3 ) || n_points < 4) - { - _tetrahedra.clear(); - return _tetrahedra; - } + _tetrahedra.clear(); + return _tetrahedra; + } - // If we have only one spin in the basis our lattice is a simple regular geometry - bool is_simple_regular_geometry = n_cell_atoms == 1; + // If we have only one spin in the basis our lattice is a simple regular geometry + bool is_simple_regular_geometry = n_cell_atoms == 1; - // If we have a simple regular geometry everything can be calculated by hand - if (is_simple_regular_geometry) + // If we have a simple regular geometry everything can be calculated by hand + if( is_simple_regular_geometry ) + { + _tetrahedra.clear(); + int cell_indices[] = { 0, 1, 5, 3, 1, 3, 2, 5, 3, 2, 5, 6, 7, 6, 5, 3, 4, 7, 5, 3, 0, 4, 3, 5 }; + int x_offset = 1; + int y_offset = n_a; + int z_offset = n_a * n_b; + int offsets[] = { 0, + x_offset, + x_offset + y_offset, + y_offset, + z_offset, + x_offset + z_offset, + x_offset + y_offset + z_offset, + y_offset + z_offset }; + + for( int ix = 0; ix < n_a - 1; ix++ ) { - _tetrahedra.clear(); - int cell_indices[] = { - 0, 1, 5, 3, - 1, 3, 2, 5, - 3, 2, 5, 6, - 7, 6, 5, 3, - 4, 7, 5, 3, - 0, 4, 3, 5 - }; - int x_offset = 1; - int y_offset = n_a; - int z_offset = n_a * n_b; - int offsets[] = { - 0, x_offset, x_offset+y_offset, y_offset, - z_offset, x_offset+z_offset, x_offset+y_offset+z_offset, y_offset+z_offset - }; - - for (int ix = 0; ix < n_a-1; ix++) + for( int iy = 0; iy < n_b - 1; iy++ ) { - for (int iy = 0; iy < n_b-1; iy++) + for( int iz = 0; iz < n_c - 1; iz++ ) { - for (int iz = 0; iz < n_c-1; iz++) + int base_index = ix * x_offset + iy * y_offset + iz * z_offset; + for( int j = 0; j < 6; j++ ) { - int base_index = ix*x_offset+iy*y_offset+iz*z_offset; - for (int j = 0; j < 6; j++) + tetrahedron_t tetrahedron; + for( int k = 0; k < 4; k++ ) { - tetrahedron_t tetrahedron; - for (int k = 0; k < 4; k++) - { - int index = base_index + offsets[cell_indices[j*4+k]]; - tetrahedron[k] = index; - } - _tetrahedra.push_back(tetrahedron); + int index = base_index + offsets[cell_indices[j * 4 + k]]; + tetrahedron[k] = index; } + _tetrahedra.push_back( tetrahedron ); } } } } - // For general basis cells we calculate the Delaunay tetrahedra - else + } + // For general basis cells we calculate the Delaunay tetrahedra + else + { + std::vector points; + points.resize( n_points ); + int icell = 0, idx; + for( int cell_c = c_min; cell_c < c_max; cell_c += n_cell_step ) { - std::vector points; - points.resize(n_points); - int icell = 0, idx; - for (int cell_c=c_min; cell_c Geometry::BravaisVectorsSC() +{ + return { { scalar( 1 ), scalar( 0 ), scalar( 0 ) }, + { scalar( 0 ), scalar( 1 ), scalar( 0 ) }, + { scalar( 0 ), scalar( 0 ), scalar( 1 ) } }; +} - std::vector Geometry::BravaisVectorsSC() - { - return { { scalar(1), scalar(0), scalar(0) }, - { scalar(0), scalar(1), scalar(0) }, - { scalar(0), scalar(0), scalar(1) } }; - } +std::vector Geometry::BravaisVectorsFCC() +{ + return { { scalar( 0.5 ), scalar( 0.0 ), scalar( 0.5 ) }, + { scalar( 0.5 ), scalar( 0.5 ), scalar( 0.0 ) }, + { scalar( 0.0 ), scalar( 0.5 ), scalar( 0.5 ) } }; +} - std::vector Geometry::BravaisVectorsFCC() - { - return { { scalar(0.5), scalar(0.0), scalar(0.5) }, - { scalar(0.5), scalar(0.5), scalar(0.0) }, - { scalar(0.0), scalar(0.5), scalar(0.5) } }; - } +std::vector Geometry::BravaisVectorsBCC() +{ + return { { scalar( 0.5 ), scalar( 0.5 ), scalar( -0.5 ) }, + { scalar( -0.5 ), scalar( 0.5 ), scalar( -0.5 ) }, + { scalar( 0.5 ), scalar( -0.5 ), scalar( -0.5 ) } }; +} - std::vector Geometry::BravaisVectorsBCC() - { - return { { scalar( 0.5), scalar( 0.5), scalar(-0.5) }, - { scalar(-0.5), scalar( 0.5), scalar(-0.5) }, - { scalar( 0.5), scalar(-0.5), scalar(-0.5) } }; - } +std::vector Geometry::BravaisVectorsHex2D60() +{ + return { { scalar( 0.5 * std::sqrt( 3 ) ), scalar( -0.5 ), scalar( 0 ) }, + { scalar( 0.5 * std::sqrt( 3 ) ), scalar( 0.5 ), scalar( 0 ) }, + { scalar( 0 ), scalar( 0 ), scalar( 1 ) } }; +} - std::vector Geometry::BravaisVectorsHex2D60() - { - return { { scalar(0.5*std::sqrt(3)), scalar(-0.5), scalar(0) }, - { scalar(0.5*std::sqrt(3)), scalar( 0.5), scalar(0) }, - { scalar(0), scalar( 0), scalar(1) } }; - } +std::vector Geometry::BravaisVectorsHex2D120() +{ + return { { scalar( 0.5 ), scalar( -0.5 * std::sqrt( 3 ) ), scalar( 0 ) }, + { scalar( 0.5 ), scalar( 0.5 * std::sqrt( 3 ) ), scalar( 0 ) }, + { scalar( 0 ), scalar( 0 ), scalar( 1 ) } }; +} - std::vector Geometry::BravaisVectorsHex2D120() +void Geometry::applyCellComposition() +{ + int N = this->n_cell_atoms; + int Na = this->n_cells[0]; + int Nb = this->n_cells[1]; + int Nc = this->n_cells[2]; + int ispin, iatom, atom_type; + scalar concentration, rvalue; + std::vector visited( N ); + + std::mt19937 prng; + std::uniform_real_distribution distribution; + if( this->cell_composition.disordered ) { - return { { scalar(0.5), scalar(-0.5*std::sqrt(3)), scalar(0) }, - { scalar(0.5), scalar( 0.5*std::sqrt(3)), scalar(0) }, - { scalar(0), scalar( 0), scalar(1) } }; + // TODO: the seed should be a parameter and the instance a member of this class + prng = std::mt19937( 2006 ); + distribution = std::uniform_real_distribution( 0, 1 ); + // In the disordered case, unvisited atoms will be vacancies + this->atom_types = intfield( nos, -1 ); } - void Geometry::applyCellComposition() + for( int na = 0; na < Na; ++na ) { - int N = this->n_cell_atoms; - int Na = this->n_cells[0]; - int Nb = this->n_cells[1]; - int Nc = this->n_cells[2]; - int ispin, iatom, atom_type; - scalar concentration, rvalue; - std::vector visited(N); - - std::mt19937 prng; - std::uniform_real_distribution distribution; - if( this->cell_composition.disordered ) + for( int nb = 0; nb < Nb; ++nb ) { - // TODO: the seed should be a parameter and the instance a member of this class - prng = std::mt19937(2006); - distribution = std::uniform_real_distribution(0, 1); - // In the disordered case, unvisited atoms will be vacancies - this->atom_types = intfield(nos, -1); - } - - for (int na = 0; na < Na; ++na) - { - for (int nb = 0; nb < Nb; ++nb) + for( int nc = 0; nc < Nc; ++nc ) { - for (int nc = 0; nc < Nc; ++nc) + std::fill( visited.begin(), visited.end(), false ); + + for( int icomposition = 0; icomposition < this->cell_composition.iatom.size(); ++icomposition ) { - std::fill(visited.begin(), visited.end(), false); + iatom = this->cell_composition.iatom[icomposition]; - for (int icomposition = 0; icomposition < this->cell_composition.iatom.size(); ++icomposition) + if( !visited[iatom] ) { - iatom = this->cell_composition.iatom[icomposition]; + ispin = N * na + N * Na * nb + N * Na * Nb * nc + iatom; - if( !visited[iatom] ) + // In the disordered case, we only visit an atom if the dice will it + if( this->cell_composition.disordered ) { - ispin = N*na + N*Na*nb + N*Na*Nb*nc + iatom; - - // In the disordered case, we only visit an atom if the dice will it - if( this->cell_composition.disordered ) - { - concentration = this->cell_composition.concentration[icomposition]; - rvalue = distribution(prng); - if( rvalue <= concentration ) - { - this->atom_types[ispin] = this->cell_composition.atom_type[icomposition]; - this->mu_s[ispin] = this->cell_composition.mu_s[icomposition]; - visited[iatom] = true; - if( this->atom_types[ispin] < 0 ) - --this->nos_nonvacant; - } - } - // In the ordered case, we visit every atom - else + concentration = this->cell_composition.concentration[icomposition]; + rvalue = distribution( prng ); + if( rvalue <= concentration ) { this->atom_types[ispin] = this->cell_composition.atom_type[icomposition]; this->mu_s[ispin] = this->cell_composition.mu_s[icomposition]; - visited[iatom] = true; + visited[iatom] = true; if( this->atom_types[ispin] < 0 ) --this->nos_nonvacant; } + } + // In the ordered case, we visit every atom + else + { + this->atom_types[ispin] = this->cell_composition.atom_type[icomposition]; + this->mu_s[ispin] = this->cell_composition.mu_s[icomposition]; + visited[iatom] = true; + if( this->atom_types[ispin] < 0 ) + --this->nos_nonvacant; + } - // Pinning of boundary layers - if( (na < pinning.na_left || na >= Na - pinning.na_right) || - (nb < pinning.nb_left || nb >= Nb - pinning.nb_right) || - (nc < pinning.nc_left || nc >= Nc - pinning.nc_right) ) - { - // Pinned cells - this->mask_unpinned[ispin] = 0; - this->mask_pinned_cells[ispin] = pinning.pinned_cell[iatom]; - } + // Pinning of boundary layers + if( ( na < pinning.na_left || na >= Na - pinning.na_right ) + || ( nb < pinning.nb_left || nb >= Nb - pinning.nb_right ) + || ( nc < pinning.nc_left || nc >= Nc - pinning.nc_right ) ) + { + // Pinned cells + this->mask_unpinned[ispin] = 0; + this->mask_pinned_cells[ispin] = pinning.pinned_cell[iatom]; } } } } } } +} +void Geometry::calculateDimensionality() +{ + int dims_translations = 0; + Vector3 test_vec_basis, test_vec_translations; - void Geometry::calculateDimensionality() - { - int dims_translations = 0; - Vector3 test_vec_basis, test_vec_translations; - - const scalar epsilon = 1e-6; + const scalar epsilon = 1e-6; - // ----- Find dimensionality of the basis ----- - if ( n_cell_atoms == 1 ) - dimensionality_basis = 0; - else if( n_cell_atoms == 2 ) + // ----- Find dimensionality of the basis ----- + if( n_cell_atoms == 1 ) + dimensionality_basis = 0; + else if( n_cell_atoms == 2 ) + { + dimensionality_basis = 1; + test_vec_basis = positions[0] - positions[1]; + } + else + { + // Get basis atoms relative to the first atom + Vector3 v0 = positions[0]; + std::vector b_vectors( n_cell_atoms - 1 ); + for( int i = 1; i < n_cell_atoms; ++i ) + b_vectors[i - 1] = ( positions[i] - v0 ).normalized(); + + // Calculate basis dimensionality + // test vec is along line + test_vec_basis = b_vectors[0]; + // is it 1D? + int n_parallel = 0; + for( unsigned int i = 1; i < b_vectors.size(); ++i ) + { + if( 1 - std::abs( b_vectors[i].dot( test_vec_basis ) ) < epsilon ) + ++n_parallel; + // Else n_parallel will give us the last parallel vector + // Also the if-statement for dimensionality_basis=1 wont be met + else + break; + } + if( n_parallel == b_vectors.size() - 1 ) { dimensionality_basis = 1; - test_vec_basis = positions[0] - positions[1]; } else { - // Get basis atoms relative to the first atom - Vector3 v0 = positions[0]; - std::vector b_vectors(n_cell_atoms-1); - for( int i = 1; i < n_cell_atoms; ++i ) - b_vectors[i-1] = (positions[i] - v0).normalized(); - - // Calculate basis dimensionality - // test vec is along line - test_vec_basis = b_vectors[0]; - // is it 1D? - int n_parallel = 0; - for( unsigned int i = 1; i < b_vectors.size(); ++i ) + // test vec is normal to plane + test_vec_basis = b_vectors[0].cross( b_vectors[n_parallel + 1] ); + // is it 2D? + int n_in_plane = 0; + for( unsigned int i = 2; i < b_vectors.size(); ++i ) { - if( 1 - std::abs(b_vectors[i].dot(test_vec_basis)) < epsilon ) - ++n_parallel; - // Else n_parallel will give us the last parallel vector - // Also the if-statement for dimensionality_basis=1 wont be met - else - break; - } - if( n_parallel == b_vectors.size() - 1 ) - { - dimensionality_basis = 1; + if( std::abs( b_vectors[i].dot( test_vec_basis ) ) < epsilon ) + ++n_in_plane; } + if( n_in_plane == b_vectors.size() - 2 ) + dimensionality_basis = 2; else { - // test vec is normal to plane - test_vec_basis = b_vectors[0].cross(b_vectors[n_parallel+1]); - // is it 2D? - int n_in_plane = 0; - for( unsigned int i = 2; i < b_vectors.size(); ++i ) - { - if (std::abs(b_vectors[i].dot(test_vec_basis)) < epsilon) - ++n_in_plane; - } - if( n_in_plane == b_vectors.size() - 2 ) - dimensionality_basis = 2; - else - { - this->dimensionality_basis = 3; - this->dimensionality = 3; - return; - } + this->dimensionality_basis = 3; + this->dimensionality = 3; + return; } } + } - // ----- Find dimensionality of the translations ----- - // The following are zero if the corresponding pair is parallel or antiparallel - double t01, t02, t12; - t01 = std::abs(bravais_vectors[0].normalized().dot(bravais_vectors[1].normalized())) - 1.0; - t02 = std::abs(bravais_vectors[0].normalized().dot(bravais_vectors[2].normalized())) - 1.0; - t12 = std::abs(bravais_vectors[1].normalized().dot(bravais_vectors[2].normalized())) - 1.0; - // Check if pairs are linearly independent - int n_independent_pairs = 0; - if( t01 < epsilon && n_cells[0] > 1 && n_cells[1] > 1 ) ++n_independent_pairs; - if( t02 < epsilon && n_cells[0] > 1 && n_cells[2] > 1 ) ++n_independent_pairs; - if( t12 < epsilon && n_cells[1] > 1 && n_cells[2] > 1 ) ++n_independent_pairs; - // Calculate translations dimensionality - if( n_cells[0] == 1 && n_cells[1] == 1 && n_cells[2] == 1 ) - { - dims_translations = 0; - } - else if( n_independent_pairs == 0 ) - { - dims_translations = 1; - // Test if vec is along the line - for (int i=0; i<3; ++i) if (n_cells[i] > 1) test_vec_translations = bravais_vectors[i]; - } - else if( n_independent_pairs < 3 ) + // ----- Find dimensionality of the translations ----- + // The following are zero if the corresponding pair is parallel or antiparallel + double t01, t02, t12; + t01 = std::abs( bravais_vectors[0].normalized().dot( bravais_vectors[1].normalized() ) ) - 1.0; + t02 = std::abs( bravais_vectors[0].normalized().dot( bravais_vectors[2].normalized() ) ) - 1.0; + t12 = std::abs( bravais_vectors[1].normalized().dot( bravais_vectors[2].normalized() ) ) - 1.0; + // Check if pairs are linearly independent + int n_independent_pairs = 0; + if( t01 < epsilon && n_cells[0] > 1 && n_cells[1] > 1 ) + ++n_independent_pairs; + if( t02 < epsilon && n_cells[0] > 1 && n_cells[2] > 1 ) + ++n_independent_pairs; + if( t12 < epsilon && n_cells[1] > 1 && n_cells[2] > 1 ) + ++n_independent_pairs; + // Calculate translations dimensionality + if( n_cells[0] == 1 && n_cells[1] == 1 && n_cells[2] == 1 ) + { + dims_translations = 0; + } + else if( n_independent_pairs == 0 ) + { + dims_translations = 1; + // Test if vec is along the line + for( int i = 0; i < 3; ++i ) + if( n_cells[i] > 1 ) + test_vec_translations = bravais_vectors[i]; + } + else if( n_independent_pairs < 3 ) + { + dims_translations = 2; + // Test if vec is normal to plane + int n = 0; + std::vector plane( 2 ); + for( int i = 0; i < 3; ++i ) { - dims_translations = 2; - // Test if vec is normal to plane - int n = 0; - std::vector plane(2); - for( int i = 0; i < 3; ++i ) + if( n_cells[i] > 1 ) { - if( n_cells[i] > 1 ) - { - plane[n] = bravais_vectors[i]; - ++n; - } + plane[n] = bravais_vectors[i]; + ++n; } - test_vec_translations = plane[0].cross(plane[1]); } - else + test_vec_translations = plane[0].cross( plane[1] ); + } + else + { + this->dimensionality = 3; + return; + } + + // ----- Calculate dimensionality of system ----- + test_vec_basis.normalize(); + test_vec_translations.normalize(); + // If one dimensionality is zero, only the other counts + if( dimensionality_basis == 0 ) + { + this->dimensionality = dims_translations; + return; + } + else if( dims_translations == 0 ) + { + this->dimensionality = dimensionality_basis; + return; + } + // If both are linear or both are planar, the test vectors should be (anti)parallel if the geometry is 1D or 2D + else if( dimensionality_basis == dims_translations ) + { + if( std::abs( test_vec_basis.dot( test_vec_translations ) ) - 1 < epsilon ) { - this->dimensionality = 3; + this->dimensionality = dimensionality_basis; return; } - - - // ----- Calculate dimensionality of system ----- - test_vec_basis.normalize(); - test_vec_translations.normalize(); - // If one dimensionality is zero, only the other counts - if( dimensionality_basis == 0 ) + else if( dimensionality_basis == 1 ) { - this->dimensionality = dims_translations; + this->dimensionality = 2; return; } - else if( dims_translations == 0 ) + else if( dimensionality_basis == 2 ) { - this->dimensionality = dimensionality_basis; + this->dimensionality = 3; return; } - // If both are linear or both are planar, the test vectors should be (anti)parallel if the geometry is 1D or 2D - else if (dimensionality_basis == dims_translations) + } + // If one is linear (1D), and the other planar (2D) then the test vectors should be orthogonal if the geometry is 2D + else if( + ( dimensionality_basis == 1 && dims_translations == 2 ) + || ( dimensionality_basis == 2 && dims_translations == 1 ) ) + { + if( std::abs( test_vec_basis.dot( test_vec_translations ) ) < epsilon ) { - if( std::abs(test_vec_basis.dot(test_vec_translations)) - 1 < epsilon ) - { - this->dimensionality = dimensionality_basis; - return; - } - else if( dimensionality_basis == 1 ) - { - this->dimensionality = 2; - return; - } - else if( dimensionality_basis == 2 ) - { - this->dimensionality = 3; - return; - } + this->dimensionality = 2; + return; } - // If one is linear (1D), and the other planar (2D) then the test vectors should be orthogonal if the geometry is 2D - else if( (dimensionality_basis == 1 && dims_translations == 2) || (dimensionality_basis == 2 && dims_translations == 1) ) + else { - if( std::abs(test_vec_basis.dot(test_vec_translations)) < epsilon ) - { - this->dimensionality = 2; - return; - } - else - { - this->dimensionality = 3; - return; - } + this->dimensionality = 3; + return; } } +} - void Geometry::calculateBounds() +void Geometry::calculateBounds() +{ + this->bounds_max.setZero(); + this->bounds_min.setZero(); + for( int iatom = 0; iatom < nos; ++iatom ) { - this->bounds_max.setZero(); - this->bounds_min.setZero(); - for (int iatom = 0; iatom < nos; ++iatom) + for( int dim = 0; dim < 3; ++dim ) { - for (int dim = 0; dim < 3; ++dim) - { - if (this->positions[iatom][dim] < this->bounds_min[dim]) this->bounds_min[dim] = this->positions[iatom][dim]; - if (this->positions[iatom][dim] > this->bounds_max[dim]) this->bounds_max[dim] = this->positions[iatom][dim]; - } + if( this->positions[iatom][dim] < this->bounds_min[dim] ) + this->bounds_min[dim] = this->positions[iatom][dim]; + if( this->positions[iatom][dim] > this->bounds_max[dim] ) + this->bounds_max[dim] = this->positions[iatom][dim]; } } +} - void Geometry::calculateUnitCellBounds() +void Geometry::calculateUnitCellBounds() +{ + this->cell_bounds_max.setZero(); + this->cell_bounds_min.setZero(); + for( unsigned int ivec = 0; ivec < this->bravais_vectors.size(); ++ivec ) { - this->cell_bounds_max.setZero(); - this->cell_bounds_min.setZero(); - for (unsigned int ivec = 0; ivec < this->bravais_vectors.size(); ++ivec) + for( int iatom = 0; iatom < this->n_cell_atoms; ++iatom ) { - for (int iatom = 0; iatom < this->n_cell_atoms; ++iatom) + auto neighbour1 = this->positions[iatom] + this->lattice_constant * this->bravais_vectors[ivec]; + auto neighbour2 = this->positions[iatom] - this->lattice_constant * this->bravais_vectors[ivec]; + for( int dim = 0; dim < 3; ++dim ) { - auto neighbour1 = this->positions[iatom] + this->lattice_constant * this->bravais_vectors[ivec]; - auto neighbour2 = this->positions[iatom] - this->lattice_constant * this->bravais_vectors[ivec]; - for (int dim = 0; dim < 3; ++dim) - { - if (neighbour1[dim] < this->cell_bounds_min[dim]) this->cell_bounds_min[dim] = neighbour1[dim]; - if (neighbour1[dim] > this->cell_bounds_max[dim]) this->cell_bounds_max[dim] = neighbour1[dim]; - if (neighbour2[dim] < this->cell_bounds_min[dim]) this->cell_bounds_min[dim] = neighbour2[dim]; - if (neighbour2[dim] > this->cell_bounds_max[dim]) this->cell_bounds_max[dim] = neighbour2[dim]; - } + if( neighbour1[dim] < this->cell_bounds_min[dim] ) + this->cell_bounds_min[dim] = neighbour1[dim]; + if( neighbour1[dim] > this->cell_bounds_max[dim] ) + this->cell_bounds_max[dim] = neighbour1[dim]; + if( neighbour2[dim] < this->cell_bounds_min[dim] ) + this->cell_bounds_min[dim] = neighbour2[dim]; + if( neighbour2[dim] > this->cell_bounds_max[dim] ) + this->cell_bounds_max[dim] = neighbour2[dim]; } } - this->cell_bounds_min *= 0.5; - this->cell_bounds_max *= 0.5; } + this->cell_bounds_min *= 0.5; + this->cell_bounds_max *= 0.5; +} - void Geometry::calculateGeometryType() - { - const scalar epsilon = 1e-6; +void Geometry::calculateGeometryType() +{ + const scalar epsilon = 1e-6; - // Automatically try to determine GeometryType - // Single-atom unit cell - if (cell_atoms.size() == 1) - { - // If the basis vectors are orthogonal, it is a rectilinear lattice - if (std::abs(bravais_vectors[0].normalized().dot(bravais_vectors[1].normalized())) < epsilon && - std::abs(bravais_vectors[0].normalized().dot(bravais_vectors[2].normalized())) < epsilon) - { - // If equidistant it is simple cubic - if (bravais_vectors[0].norm() == bravais_vectors[1].norm() == bravais_vectors[2].norm()) - this->classifier = BravaisLatticeType::SC; - // Otherwise only rectilinear - else - this->classifier = BravaisLatticeType::Rectilinear; - } - } - // Regular unit cell with multiple atoms (e.g. bcc, fcc, hex) - //else if (n_cell_atoms == 2) - // Irregular unit cells arranged on a lattice (e.g. B20 or custom) - /*else if (n_cells[0] > 1 || n_cells[1] > 1 || n_cells[2] > 1) - { - this->classifier = BravaisLatticeType::Lattice; - }*/ - // A single irregular unit cell - else + // Automatically try to determine GeometryType + // Single-atom unit cell + if( cell_atoms.size() == 1 ) + { + // If the basis vectors are orthogonal, it is a rectilinear lattice + if( std::abs( bravais_vectors[0].normalized().dot( bravais_vectors[1].normalized() ) ) < epsilon + && std::abs( bravais_vectors[0].normalized().dot( bravais_vectors[2].normalized() ) ) < epsilon ) { - this->classifier = BravaisLatticeType::Irregular; + // If equidistant it is simple cubic + if( bravais_vectors[0].norm() == bravais_vectors[1].norm() == bravais_vectors[2].norm() ) + this->classifier = BravaisLatticeType::SC; + // Otherwise only rectilinear + else + this->classifier = BravaisLatticeType::Rectilinear; } } + // Regular unit cell with multiple atoms (e.g. bcc, fcc, hex) + // else if (n_cell_atoms == 2) + // Irregular unit cells arranged on a lattice (e.g. B20 or custom) + /*else if (n_cells[0] > 1 || n_cells[1] > 1 || n_cells[2] > 1) + { + this->classifier = BravaisLatticeType::Lattice; + }*/ + // A single irregular unit cell + else + { + this->classifier = BravaisLatticeType::Irregular; + } +} - void Geometry::Apply_Pinning(vectorfield & vf) +void Geometry::Apply_Pinning( vectorfield & vf ) +{ +#if defined( SPIRIT_ENABLE_PINNING ) + int N = this->n_cell_atoms; + int Na = this->n_cells[0]; + int Nb = this->n_cells[1]; + int Nc = this->n_cells[2]; + int ispin; + + for( int iatom = 0; iatom < N; ++iatom ) { - #if defined(SPIRIT_ENABLE_PINNING) - int N = this->n_cell_atoms; - int Na = this->n_cells[0]; - int Nb = this->n_cells[1]; - int Nc = this->n_cells[2]; - int ispin; - - for (int iatom = 0; iatom < N; ++iatom) + for( int na = 0; na < Na; ++na ) { - for (int na = 0; na < Na; ++na) + for( int nb = 0; nb < Nb; ++nb ) { - for (int nb = 0; nb < Nb; ++nb) + for( int nc = 0; nc < Nc; ++nc ) { - for (int nc = 0; nc < Nc; ++nc) - { - ispin = N*na + N*Na*nb + N*Na*Nb*nc + iatom; - if (!this->mask_unpinned[ispin]) - vf[ispin] = this->mask_pinned_cells[ispin]; - } + ispin = N * na + N * Na * nb + N * Na * Nb * nc + iatom; + if( !this->mask_unpinned[ispin] ) + vf[ispin] = this->mask_pinned_cells[ispin]; } } } - #endif } +#endif } +} // namespace Data \ No newline at end of file diff --git a/core/src/data/Spin_System.cpp b/core/src/data/Spin_System.cpp index 6967eecdd..1f33e833a 100644 --- a/core/src/data/Spin_System.cpp +++ b/core/src/data/Spin_System.cpp @@ -3,185 +3,196 @@ #include #include -#include -#include #include +#include +#include #include namespace Data { - Spin_System::Spin_System(std::unique_ptr hamiltonian, - std::shared_ptr geometry, - std::unique_ptr llg_params, - std::unique_ptr mc_params, - std::unique_ptr ema_params, - std::unique_ptr mmf_params, - bool iteration_allowed) : - iteration_allowed(iteration_allowed), singleshot_allowed(false), - hamiltonian(std::move(hamiltonian)), geometry(geometry), - llg_parameters(std::move(llg_params)), mc_parameters(std::move(mc_params)), - ema_parameters(std::move(ema_params)), mmf_parameters(std::move(mmf_params)) + +Spin_System::Spin_System( + std::unique_ptr hamiltonian, std::shared_ptr geometry, + std::unique_ptr llg_params, std::unique_ptr mc_params, + std::unique_ptr ema_params, std::unique_ptr mmf_params, + bool iteration_allowed ) +try : iteration_allowed( iteration_allowed ), + singleshot_allowed( false ), + hamiltonian( std::move( hamiltonian ) ), + geometry( geometry ), + llg_parameters( std::move( llg_params ) ), + mc_parameters( std::move( mc_params ) ), + ema_parameters( std::move( ema_params ) ), + mmf_parameters( std::move( mmf_params ) ) { // Get Number of Spins this->nos = this->geometry->nos; - // Initialize Spins Array - this->spins = std::shared_ptr(new vectorfield(nos)); - - // Initialize Modes container - this->modes = std::vector>(this->ema_parameters->n_modes, NULL); - - // Initialize Eigenvalues vector - this->eigenvalues = std::vector(this->modes.size(),0); + // Initialize Spins Array + this->spins = std::shared_ptr( new vectorfield( nos ) ); - // ... - this->E = 0; - this->E_array = std::vector>(0); - this->M = Vector3{0,0,0}; - this->effective_field = vectorfield(this->nos); + // Initialize Modes container + this->modes = std::vector>( this->ema_parameters->n_modes, NULL ); - }//end Spin_System constructor + // Initialize Eigenvalues vector + this->eigenvalues = std::vector( this->modes.size(), 0 ); + // ... + this->E = 0; + this->E_array = std::vector>( 0 ); + this->M = Vector3{ 0, 0, 0 }; + this->effective_field = vectorfield( this->nos ); +} +catch( ... ) +{ + spirit_rethrow( "Spin system initialisation failed" ); +} // Copy Constructor - Spin_System::Spin_System(Spin_System const & other) + Spin_System::Spin_System( Spin_System const & other ) + try { - this->nos = other.nos; - this->spins = std::shared_ptr(new vectorfield(*other.spins)); - this->modes = std::vector>(other.modes.size(), NULL); - this->eigenvalues = other.eigenvalues; - + this->nos = other.nos; + this->spins = std::shared_ptr( new vectorfield( *other.spins ) ); + this->modes = std::vector>( other.modes.size(), NULL ); + this->eigenvalues = other.eigenvalues; + // copy the modes - for (int i=0; imodes[i] = - std::shared_ptr(new vectorfield(*other.modes[i])); + for( int i = 0; i < other.modes.size(); i++ ) + if( other.modes[i] != NULL ) + this->modes[i] = std::shared_ptr( new vectorfield( *other.modes[i] ) ); - this->E = other.E; - this->E_array = other.E_array; + this->E = other.E; + this->E_array = other.E_array; this->effective_field = other.effective_field; - this->M = other.M; - this->geometry = std::shared_ptr(new Data::Geometry(*other.geometry)); + this->geometry = std::make_shared( *other.geometry ); - if (other.hamiltonian->Name() == "Heisenberg") + if( other.hamiltonian->Name() == "Heisenberg" ) { - this->hamiltonian = std::shared_ptr( - new Engine::Hamiltonian_Heisenberg(*(Engine::Hamiltonian_Heisenberg*)(other.hamiltonian.get()))); + this->hamiltonian = std::make_shared( + static_cast( *other.hamiltonian ) ); } else if (other.hamiltonian->Name() == "Micromagnetic") { - this->hamiltonian = std::shared_ptr( - new Engine::Hamiltonian_Micromagnetic(*(Engine::Hamiltonian_Micromagnetic*)(other.hamiltonian.get()))); + this->hamiltonian = std::make_shared( + static_cast( *other.hamiltonian ) ); } - else if (other.hamiltonian->Name() == "Gaussian") + else if( other.hamiltonian->Name() == "Gaussian" ) { - this->hamiltonian = std::shared_ptr( - new Engine::Hamiltonian_Gaussian(*(Engine::Hamiltonian_Gaussian*)(other.hamiltonian.get()))); + this->hamiltonian = std::make_shared( + static_cast( *other.hamiltonian ) ); } - this->llg_parameters = std::shared_ptr(new Data::Parameters_Method_LLG(*other.llg_parameters)); - - this->mc_parameters = std::shared_ptr(new Data::Parameters_Method_MC(*other.mc_parameters)); - - this->ema_parameters = std::shared_ptr(new Data::Parameters_Method_EMA(*other.ema_parameters)); - - this->mmf_parameters = std::shared_ptr(new Data::Parameters_Method_MMF(*other.mmf_parameters)); + this->llg_parameters = std::make_shared( *other.llg_parameters ); + this->mc_parameters = std::make_shared( *other.mc_parameters ); + this->ema_parameters = std::make_shared( *other.ema_parameters ); + this->mmf_parameters = std::make_shared( *other.mmf_parameters ); this->iteration_allowed = false; } + catch( ... ) + { + spirit_rethrow( "Copy-assigning spin system failed" ); + } - - // Copy Assignment operator - Spin_System& Spin_System::operator=(Spin_System const & other) + // Copy assignment operator + Spin_System & Spin_System::operator=( Spin_System const & other ) + try { - if (this != &other) + if( this != &other ) { - this->nos = other.nos; - this->spins = std::shared_ptr(new vectorfield(*other.spins)); - this->modes = std::vector>(other.modes.size(), NULL); - this->eigenvalues = other.eigenvalues; - + this->nos = other.nos; + this->spins = std::shared_ptr( new vectorfield( *other.spins ) ); + this->modes = std::vector>( other.modes.size(), NULL ); + this->eigenvalues = other.eigenvalues; + // copy the modes - for (int i=0; imodes[i] = - std::shared_ptr(new vectorfield(*other.modes[i])); + for( int i = 0; i < other.modes.size(); i++ ) + if( other.modes[i] != NULL ) + this->modes[i] = std::shared_ptr( new vectorfield( *other.modes[i] ) ); - this->E = other.E; - this->E_array = other.E_array; + this->E = other.E; + this->E_array = other.E_array; this->effective_field = other.effective_field; - this->M = other.M; - this->geometry = std::shared_ptr(new Data::Geometry(*other.geometry)); + this->geometry = std::make_shared( *other.geometry ); - if (other.hamiltonian->Name() == "Heisenberg") + if( other.hamiltonian->Name() == "Heisenberg" ) { - auto ham = std::shared_ptr( - new Engine::Hamiltonian_Heisenberg(*(Engine::Hamiltonian_Heisenberg*)(other.hamiltonian.get()))); - ham->geometry = this->geometry; - this->hamiltonian = ham; + this->hamiltonian = std::make_shared( + *(Engine::Hamiltonian_Heisenberg *)( other.hamiltonian.get() ) ); } - else if (other.hamiltonian->Name() == "Micromagnetic") + else if( other.hamiltonian->Name() == "Micromagnetic" ) { - auto ham = std::shared_ptr( - new Engine::Hamiltonian_Micromagnetic(*(Engine::Hamiltonian_Micromagnetic*)(other.hamiltonian.get()))); - ham->geometry = this->geometry; - this->hamiltonian = ham; + this->hamiltonian = std::make_shared( + *(Engine::Hamiltonian_Micromagnetic*)( other.hamiltonian.get() ) ); } - else if (other.hamiltonian->Name() == "Gaussian") + else if( other.hamiltonian->Name() == "Gaussian" ) { - this->hamiltonian = std::shared_ptr( - new Engine::Hamiltonian_Gaussian(*(Engine::Hamiltonian_Gaussian*)(other.hamiltonian.get()))); + this->hamiltonian = std::make_shared( + *(Engine::Hamiltonian_Gaussian *)( other.hamiltonian.get() ) ); } - this->llg_parameters = std::shared_ptr(new Data::Parameters_Method_LLG(*other.llg_parameters)); - - this->mc_parameters = std::shared_ptr(new Data::Parameters_Method_MC(*other.mc_parameters)); - - this->ema_parameters = std::shared_ptr(new Data::Parameters_Method_EMA(*other.ema_parameters)); + this->llg_parameters = std::make_shared( *other.llg_parameters ); + this->mc_parameters = std::make_shared( *other.mc_parameters ); + this->ema_parameters = std::make_shared( *other.ema_parameters ); + this->mmf_parameters = std::make_shared( *other.mmf_parameters ); this->iteration_allowed = false; } return *this; } - + catch( ... ) + { + spirit_rethrow( "Copy-assigning spin system failed" ); + return *this; + } void Spin_System::UpdateEnergy() + try { - this->E_array = this->hamiltonian->Energy_Contributions(*this->spins); - scalar sum = 0; - for (auto E : E_array) sum += E.second; + this->E_array = this->hamiltonian->Energy_Contributions( *this->spins ); + scalar sum = 0; + for( auto & E : E_array ) + sum += E.second; this->E = sum; } - + catch( ... ) + { + spirit_rethrow( "Spin_System::UpdateEnergy failed" ); + } void Spin_System::UpdateEffectiveField() + try { - this->hamiltonian->Gradient(*this->spins, this->effective_field); - Engine::Vectormath::scale(this->effective_field, -1); + this->hamiltonian->Gradient( *this->spins, this->effective_field ); + Engine::Vectormath::scale( this->effective_field, -1 ); + } + catch( ... ) + { + spirit_rethrow( "Spin_System::UpdateEffectiveField failed" ); } - void Spin_System::Lock() noexcept try { - this->m_lock.lock(); + this->ordered_lock.lock(); } catch( ... ) { - spirit_handle_exception_core("Locking the Spin_System failed!"); + spirit_handle_exception_core( "Locking the Spin_System failed!" ); } - void Spin_System::Unlock() noexcept try { - this->m_lock.unlock(); + this->ordered_lock.unlock(); } catch( ... ) { - spirit_handle_exception_core("Unlocking the Spin_System failed!"); + spirit_handle_exception_core( "Unlocking the Spin_System failed!" ); } -} \ No newline at end of file + +} // namespace Data \ No newline at end of file diff --git a/core/src/data/Spin_System_Chain.cpp b/core/src/data/Spin_System_Chain.cpp index 49b1f05f4..757743561 100644 --- a/core/src/data/Spin_System_Chain.cpp +++ b/core/src/data/Spin_System_Chain.cpp @@ -3,50 +3,49 @@ namespace Data { - Spin_System_Chain::Spin_System_Chain(std::vector> images, std::shared_ptr gneb_parameters, bool iteration_allowed) : - iteration_allowed(iteration_allowed), singleshot_allowed(false), - gneb_parameters(gneb_parameters) - { - this->noi = images.size(); - this->images = images; - //this->gneb_parameters = gneb_parameters; - - this->idx_active_image = 0; - - this->image_type = std::vector(this->noi, GNEB_Image_Type::Normal); - - this->Rx = std::vector(this->noi, 0); - int size_interpolated = this->noi + (this->noi - 1)*gneb_parameters->n_E_interpolations; - this->Rx_interpolated = std::vector(size_interpolated, 0); - this->E_interpolated = std::vector(size_interpolated, 0); - this->E_array_interpolated = std::vector>(7, std::vector(size_interpolated, 0)); - } - - void Spin_System_Chain::Lock() const - { - try - { - this->mutex.lock(); - for (auto& image : this->images) - image->Lock(); - } - catch( ... ) - { - spirit_handle_exception_core("Unlocking the Spin_System failed!"); - } - } - - void Spin_System_Chain::Unlock() const - { - try - { - for (auto& image : this->images) - image->Unlock(); - this->mutex.unlock(); - } - catch( ... ) - { - spirit_handle_exception_core("Unlocking the Spin_System failed!"); - } - } -} \ No newline at end of file + +Spin_System_Chain::Spin_System_Chain( + std::vector> images, std::shared_ptr gneb_parameters, + bool iteration_allowed ) + : iteration_allowed( iteration_allowed ), singleshot_allowed( false ), gneb_parameters( gneb_parameters ) +{ + this->noi = images.size(); + this->images = images; + // this->gneb_parameters = gneb_parameters; + + this->idx_active_image = 0; + + this->image_type = std::vector( this->noi, GNEB_Image_Type::Normal ); + + this->Rx = std::vector( this->noi, 0 ); + int size_interpolated = this->noi + ( this->noi - 1 ) * gneb_parameters->n_E_interpolations; + this->Rx_interpolated = std::vector( size_interpolated, 0 ); + this->E_interpolated = std::vector( size_interpolated, 0 ); + this->E_array_interpolated = std::vector>( 7, std::vector( size_interpolated, 0 ) ); +} + +void Spin_System_Chain::Lock() noexcept +try +{ + this->ordered_lock.lock(); + for( auto & image : this->images ) + image->Lock(); +} +catch( ... ) +{ + spirit_handle_exception_core( "Unlocking the Spin_System_Chain failed!" ); +} + +void Spin_System_Chain::Unlock() noexcept +try +{ + for( auto & image : this->images ) + image->Unlock(); + this->ordered_lock.unlock(); +} +catch( ... ) +{ + spirit_handle_exception_core( "Unlocking the Spin_System_Chain failed!" ); +} + +} // namespace Data \ No newline at end of file From ca00f12a99c1cbb6bd805cf0124c84b468b1a8ba Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 9 Jun 2021 12:05:53 +0200 Subject: [PATCH 27/45] Incorporated upstream changes in core/engine --- core/include/engine/FFT.hpp | 15 +- .../include/engine/Hamiltonian_Heisenberg.hpp | 12 +- core/include/engine/Method_Solver.hpp | 672 +++++++++--------- core/include/engine/Vectormath.hpp | 5 +- core/src/engine/Hamiltonian_Heisenberg.cpp | 86 +-- core/src/engine/Hamiltonian_Heisenberg.cu | 281 ++++---- core/src/engine/Sparse_HTST.cpp | 130 ++-- 7 files changed, 597 insertions(+), 604 deletions(-) diff --git a/core/include/engine/FFT.hpp b/core/include/engine/FFT.hpp index 0a26a34cf..4732888d6 100644 --- a/core/include/engine/FFT.hpp +++ b/core/include/engine/FFT.hpp @@ -3,6 +3,7 @@ #define FFT_H #include #include +#include #include #include #include @@ -25,7 +26,7 @@ #include #endif -namespace Engine +namespace Engine { namespace FFT { @@ -84,7 +85,7 @@ namespace Engine FFT_cpx_type res; res[0] = d1[0] * s1[0] + d2[0] * s2[0] + d3[0] * s3[0] - d1[1] * s1[1] - d2[1] * s2[1] - d3[1] * s3[1]; res[1] = d1[0] * s1[1] + d2[0] * s2[1] + d3[0] * s3[1] + d1[1] * s1[0] + d2[1] * s2[0] + d3[1] * s3[0]; - return res; + return res; } inline void addTo(FFT_cpx_type & a, const FFT_cpx_type & b, bool overwrite) @@ -112,7 +113,7 @@ namespace Engine FFT_cpx_type res; res.x = d1.x * s1.x + d2.x * s2.x + d3.x * s3.x - d1.y * s1.y - d2.y * s2.y - d3.y * s3.y; res.y = d1.x * s1.y + d2.x * s2.y + d3.x * s3.y + d1.y * s1.x + d2.y * s2.x + d3.y * s3.x; - return res; + return res; } inline __device__ void addTo(FFT_cpx_type & a, const FFT_cpx_type & b, bool overwrite = false) @@ -135,7 +136,7 @@ namespace Engine fftw_plan_with_nthreads(omp_get_max_threads()); #endif } - + inline void get_strides(field & strides, const field & maxVal) { strides.resize(maxVal.size()); @@ -173,7 +174,7 @@ namespace Engine //Constructor delegation FFT_Plan() : FFT_Plan({2,2,2}, true, 1, 8) - {} + {} FFT_Plan(std::vector dims, bool inverse, int n_transforms, int len) : dims(dims), @@ -187,7 +188,7 @@ namespace Engine } //copy constructor - FFT_Plan(FFT_Plan const & other) + FFT_Plan(FFT_Plan const & other) { this->dims = other.dims; this->inverse = other.inverse; @@ -219,7 +220,7 @@ namespace Engine } return *this; } - + //move assignment operator FFT_Plan& operator=(FFT_Plan const && other) { diff --git a/core/include/engine/Hamiltonian_Heisenberg.hpp b/core/include/engine/Hamiltonian_Heisenberg.hpp index 6de604639..82a5f576a 100644 --- a/core/include/engine/Hamiltonian_Heisenberg.hpp +++ b/core/include/engine/Hamiltonian_Heisenberg.hpp @@ -9,7 +9,8 @@ #include #include #include -#include +#include +#include "FFT.hpp" namespace Engine { @@ -61,9 +62,7 @@ namespace Engine // Hamiltonian name as string const std::string& Name() override; - - std::shared_ptr geometry; - + // ------------ Single Spin Interactions ------------ // External magnetic field across the sample scalar external_field_magnitude; @@ -94,7 +93,7 @@ namespace Engine pairfield dmi_pairs; scalarfield dmi_magnitudes; vectorfield dmi_normals; - // Dipole-dipole interaction + // Dipole Dipole interaction DDI_Method ddi_method; intfield ddi_n_periodic_images; bool ddi_pb_zero_padding; @@ -108,6 +107,8 @@ namespace Engine quadrupletfield quadruplets; scalarfield quadruplet_magnitudes; + std::shared_ptr geometry; + // ------------ Effective Field Functions ------------ // Calculate the Zeeman effective field of a single Spin void Gradient_Zeeman(vectorfield & gradient); @@ -192,5 +193,6 @@ namespace Engine field it_bounds_write_dipole; }; + } #endif \ No newline at end of file diff --git a/core/include/engine/Method_Solver.hpp b/core/include/engine/Method_Solver.hpp index e6123029a..8d2a2b448 100644 --- a/core/include/engine/Method_Solver.hpp +++ b/core/include/engine/Method_Solver.hpp @@ -6,20 +6,19 @@ #include #include #include -#include -#include -#include #include +#include #include -#include -#include +#include #include +#include +#include #include #include +#include #include #include -#include #include #include @@ -28,353 +27,366 @@ namespace Engine { - enum class Solver - { - None = -1, - SIB = Solver_SIB, - Heun = Solver_Heun, - Depondt = Solver_Depondt, - RungeKutta4 = Solver_RungeKutta4, - LBFGS_OSO = Solver_LBFGS_OSO, - LBFGS_Atlas = Solver_LBFGS_Atlas, - VP = Solver_VP, - VP_OSO = Solver_VP_OSO - }; - - /* - Base Class for Solver-based Simulation/Calculation Methods. - It is templated to allow a flexible choice of Solver to iterate the systems. - */ - template - class Method_Solver : public Method - { - public: - // Constructor to be used in derived classes - Method_Solver(std::shared_ptr parameters, int idx_img, int idx_chain) : - Method(parameters, idx_img, idx_chain) - { - } - - // // `Iterate` uses the `Solver_Iteration` function to evolve given systems according to the - // // `Calculate_Force` implementation of the Method-Subclass. - // // It iterates until: the maximum number of iterations is reached or the maximum - // // walltime is reaches or the force has converged or a file called `STOP` is found - // // or the calculation is stopped externally (via the API). - // virtual void Iterate() override; - - // Solver name as string - virtual std::string SolverName() override; - virtual std::string SolverFullName() override; - - // Iteration represents one iteration of a certain Solver - virtual void Iteration() override; - - protected: - - // Prepare random numbers for thermal fields, if needed - virtual void Prepare_Thermal_Field() {} - - // Calculate Forces onto Systems - // This is currently overridden by methods to specify how the forces on a set of configurations should be - // calculated. This function is used in `the Solver_...` functions. - // TODO: maybe rename to separate from deterministic and stochastic force functions - virtual void Calculate_Force(const std::vector> & configurations, std::vector & forces) - { - Log(Utility::Log_Level::Error, Utility::Log_Sender::All, "Tried to use Method_Solver::Calculate_Force() of the Method_Solver class!", this->idx_image, this->idx_chain); - } - - // Calculate virtual Forces onto Systems (can be precession and damping forces, correctly scaled) - // Calculate the effective force on a configuration. It is a combination of - // precession and damping terms for the Hamiltonian, spin currents and - // temperature. This function is used in `the Solver_...` functions. - // Default implementation: direct minimization - virtual void Calculate_Force_Virtual( - const std::vector> & configurations, - const std::vector & forces, - std::vector & forces_virtual) - { - // Not Implemented! - Log(Utility::Log_Level::Error, Utility::Log_Sender::All, "Tried to use Method_Solver::Calculate_Force_Virtual() of the Method_Solver class!", this->idx_image, this->idx_chain); - } - - - // Calculate maximum of absolute values of force components for a spin configuration - virtual scalar Force_on_Image_MaxAbsComponent(const vectorfield & image, vectorfield & force) final; - - // Calculate maximum torque for a spin configuration - virtual scalar MaxTorque_on_Image(const vectorfield & image, vectorfield & force) final; - - // ... - // virtual bool Iterations_Allowed() override; - // Check if the forces are converged - virtual bool Converged(); - - // Check if any stop criteria were encountered - bool ContinueIterating() override - { - return Method::ContinueIterating() && !this->Converged(); - } - - // Initialise contains the initialisations of arrays etc. for a certain solver - virtual void Initialize() override; - virtual void Finalize() override; - - // Log message blocks - virtual void Message_Start() override; - virtual void Message_Step() override; - virtual void Message_End() override; - - - //////////// DEPONDT //////////////////////////////////////////////////////////// - // Temporaries for virtual forces - std::vector rotationaxis; - std::vector forces_virtual_norm; - // Preccession angle - scalarfield angle; - - - //////////// LBFGS //////////////////////////////////////////////////////////// - - // General - int n_lbfgs_memory; - int local_iter; - scalar maxmove; - scalarfield rho; - scalarfield alpha; - - // Atlas coords - std::vector> atlas_updates; - std::vector> grad_atlas_updates; - std::vector atlas_coords3; - std::vector atlas_directions; - std::vector atlas_residuals; - std::vector atlas_residuals_last; - std::vector atlas_q_vec; - - // OSO - std::vector> delta_a; - std::vector> delta_grad; - std::vector searchdir; - std::vector grad; - std::vector grad_pr; - std::vector q_vec; - - // buffer variables for checking convergence for solver and Newton-Raphson - // std::vector r_dot_d, dda2; - - //////////// VP /////////////////////////////////////////////////////////////// - // "Mass of our particle" which we accelerate - scalar m = 1.0; - - // Force in previous step [noi][nos] - std::vector forces_previous; - // Velocity in previous step [noi][nos] - std::vector velocities_previous; - // Velocity used in the Steps [noi][nos] - std::vector velocities; - // Projection of velocities onto the forces [noi] - std::vector projection; - // |force|^2 - std::vector force_norm2; - - // Temporary Spins arrays - vectorfield temp1, temp2; - - // Actual Forces on the configurations - std::vector forces; - std::vector forces_predictor; - // Virtual Forces used in the Steps - std::vector forces_virtual; - std::vector forces_virtual_predictor; - - // RK 4 - std::vector> configurations_k1; - std::vector> configurations_k2; - std::vector> configurations_k3; - std::vector> configurations_k4; - - // Random vector array - vectorfield xi; - - // Pointers to Configurations (for Solver methods) - std::vector> configurations; - std::vector> configurations_predictor; - std::vector> configurations_temp; - }; - - - // Return the maximum of absolute values of force components for an image - template - scalar Method_Solver::Force_on_Image_MaxAbsComponent(const vectorfield & image, vectorfield & force) +enum class Solver +{ + None = -1, + SIB = Solver_SIB, + Heun = Solver_Heun, + Depondt = Solver_Depondt, + RungeKutta4 = Solver_RungeKutta4, + LBFGS_OSO = Solver_LBFGS_OSO, + LBFGS_Atlas = Solver_LBFGS_Atlas, + VP = Solver_VP, + VP_OSO = Solver_VP_OSO +}; + +/* + Base Class for Solver-based Simulation/Calculation Methods. + It is templated to allow a flexible choice of Solver to iterate the systems. +*/ +template +class Method_Solver : public Method +{ +public: + // Constructor to be used in derived classes + Method_Solver( std::shared_ptr parameters, int idx_img, int idx_chain ) + : Method( parameters, idx_img, idx_chain ) { - // Take out component in direction of v2 - Manifoldmath::project_tangential(force, image); - - // We want the Maximum of Absolute Values of all force components on all images - return Vectormath::max_abs_component(force); } - // Return the maximum norm of the torque for an image - template - scalar Method_Solver::MaxTorque_on_Image(const vectorfield & image, vectorfield & force) + // // `Iterate` uses the `Solver_Iteration` function to evolve given systems according to the + // // `Calculate_Force` implementation of the Method-Subclass. + // // It iterates until: the maximum number of iterations is reached or the maximum + // // walltime is reaches or the force has converged or a file called `STOP` is found + // // or the calculation is stopped externally (via the API). + // virtual void Iterate() override; + + // Solver name as string + virtual std::string SolverName() override; + virtual std::string SolverFullName() override; + + // Iteration represents one iteration of a certain Solver + virtual void Iteration() override; + +protected: + // Prepare random numbers for thermal fields, if needed + virtual void Prepare_Thermal_Field() {} + + // Calculate Forces onto Systems + // This is currently overridden by methods to specify how the forces on a set of configurations should be + // calculated. This function is used in `the Solver_...` functions. + // TODO: maybe rename to separate from deterministic and stochastic force functions + virtual void Calculate_Force( + const std::vector> & configurations, std::vector & forces ) { - // Take out component in direction of v2 - Manifoldmath::project_tangential(force, image); - return Vectormath::max_norm(force); + Log( Utility::Log_Level::Error, Utility::Log_Sender::All, + "Tried to use Method_Solver::Calculate_Force() of the Method_Solver class!", this->idx_image, + this->idx_chain ); } - template - bool Method_Solver::Converged() + // Calculate virtual Forces onto Systems (can be precession and damping forces, correctly scaled) + // Calculate the effective force on a configuration. It is a combination of + // precession and damping terms for the Hamiltonian, spin currents and + // temperature. This function is used in `the Solver_...` functions. + // Default implementation: direct minimization + virtual void Calculate_Force_Virtual( + const std::vector> & configurations, const std::vector & forces, + std::vector & forces_virtual ) { - bool converged = false; - if( this->max_torque < this->parameters->force_convergence ) converged = true; - return converged; + // Not Implemented! + Log( Utility::Log_Level::Error, Utility::Log_Sender::All, + "Tried to use Method_Solver::Calculate_Force_Virtual() of the Method_Solver class!", this->idx_image, + this->idx_chain ); } - // Default implementation: do nothing - template - void Method_Solver::Initialize() - { - }; + // Calculate maximum of absolute values of force components for a spin configuration + virtual scalar Force_on_Image_MaxAbsComponent( const vectorfield & image, vectorfield & force ) final; - // Default implementation: do nothing - template - void Method_Solver::Finalize() - { - }; - - // Default implementation: do nothing - template - void Method_Solver::Iteration() - { - }; + // Calculate maximum torque for a spin configuration + virtual scalar MaxTorque_on_Image( const vectorfield & image, vectorfield & force ) final; + // ... + // virtual bool Iterations_Allowed() override; + // Check if the forces are converged + virtual bool Converged(); - template - void Method_Solver::Message_Start() + // Check if any stop criteria were encountered + bool ContinueIterating() override { - using namespace Utility; - - //---- Log messages - std::vector block; - block.push_back( fmt::format("------------ Started {} Calculation ------------", this->Name()) ); - block.push_back( fmt::format(" Going to iterate {} step(s)", this->n_log) ); - block.push_back( fmt::format(" with {} iterations per step", this->n_iterations_log) ); - block.push_back( fmt::format(" Force convergence parameter: {:." + fmt::format("{}", this->print_precision) + "f}", this->parameters->force_convergence) ); - block.push_back( fmt::format(" Maximum torque: {:." + fmt::format("{}", this->print_precision) + "f}", this->max_torque) ); - block.push_back( fmt::format(" Solver: {}", this->SolverFullName()) ); - if( this->Name() == "GNEB" ) - { - scalar length = Manifoldmath::dist_geodesic(*this->configurations[0], *this->configurations[this->noi-1]); - block.push_back( fmt::format(" Total path length: {}", length) ); - } - block.push_back( "-----------------------------------------------------" ); - Log.SendBlock(Log_Level::All, this->SenderName, block, this->idx_image, this->idx_chain); + return Method::ContinueIterating() && !this->Converged(); } - template - void Method_Solver::Message_Step() - { - using namespace Utility; - - std::string percentage = fmt::format("{:.2f}%:", 100*double(this->iteration)/double(this->n_iterations)); - bool llg_dynamics = this->Name() == "LLG" - && !( this->systems[this->idx_image]->llg_parameters->direct_minimization - || solver == Solver::VP || solver == Solver::VP_OSO - || solver == Solver::LBFGS_OSO || solver == Solver::LBFGS_Atlas ); - - // Update time of current step - auto t_current = system_clock::now(); - - // Send log message - std::vector block; - block.push_back( fmt::format("----- {} Calculation ({} Solver): {}", this->Name(), this->SolverName(), Timing::DateTimePassed(t_current - this->t_start)) ); - block.push_back( fmt::format(" Time since last step: {}", Timing::DateTimePassed(t_current - this->t_last)) ); - block.push_back( fmt::format(" Completed {:>8} {} / {} iterations", percentage, this->iteration, this->n_iterations) ); - block.push_back( fmt::format(" Iterations / sec: {:.2f}", this->n_iterations_log / Timing::SecondsPassed(t_current - this->t_last)) ); - if( llg_dynamics ) - block.push_back( fmt::format(" Simulated time: {} ps", this->get_simulated_time()) ); - if( this->Name() == "GNEB" ) - { - scalar length = Manifoldmath::dist_geodesic(*this->configurations[0], *this->configurations[this->noi-1]); - block.push_back( fmt::format(" Total path length: {}", length) ); - } - block.push_back( fmt::format(" Force convergence parameter: {:." + fmt::format("{}", this->print_precision) + "f}", this->parameters->force_convergence) ); - block.push_back( fmt::format(" Maximum torque: {:." + fmt::format("{}", this->print_precision) + "f}", this->max_torque) ); - Log.SendBlock(Log_Level::All, this->SenderName, block, this->idx_image, this->idx_chain); - - // Update time of last step - this->t_last = t_current; - } + // Initialise contains the initialisations of arrays etc. for a certain solver + virtual void Initialize() override; + virtual void Finalize() override; + + // Log message blocks + virtual void Message_Start() override; + virtual void Message_Step() override; + virtual void Message_End() override; + + //////////// DEPONDT //////////////////////////////////////////////////////////// + // Temporaries for virtual forces + std::vector rotationaxis; + std::vector forces_virtual_norm; + // Preccession angle + scalarfield angle; + + //////////// LBFGS //////////////////////////////////////////////////////////// + + // General + int n_lbfgs_memory; + int local_iter; + scalar maxmove; + scalarfield rho; + scalarfield alpha; + + // Atlas coords + std::vector> atlas_updates; + std::vector> grad_atlas_updates; + std::vector atlas_coords3; + std::vector atlas_directions; + std::vector atlas_residuals; + std::vector atlas_residuals_last; + std::vector atlas_q_vec; + + // OSO + std::vector> delta_a; + std::vector> delta_grad; + std::vector searchdir; + std::vector grad; + std::vector grad_pr; + std::vector q_vec; + + // buffer variables for checking convergence for solver and Newton-Raphson + // std::vector r_dot_d, dda2; + + //////////// VP /////////////////////////////////////////////////////////////// + // "Mass of our particle" which we accelerate + scalar m = 1.0; + + // Force in previous step [noi][nos] + std::vector forces_previous; + // Velocity in previous step [noi][nos] + std::vector velocities_previous; + // Velocity used in the Steps [noi][nos] + std::vector velocities; + // Projection of velocities onto the forces [noi] + std::vector projection; + // |force|^2 + std::vector force_norm2; + + // Temporary Spins arrays + vectorfield temp1, temp2; + + // Actual Forces on the configurations + std::vector forces; + std::vector forces_predictor; + // Virtual Forces used in the Steps + std::vector forces_virtual; + std::vector forces_virtual_predictor; + + // RK 4 + std::vector> configurations_k1; + std::vector> configurations_k2; + std::vector> configurations_k3; + std::vector> configurations_k4; + + // Random vector array + vectorfield xi; + + // Pointers to Configurations (for Solver methods) + std::vector> configurations; + std::vector> configurations_predictor; + std::vector> configurations_temp; +}; + +// Return the maximum of absolute values of force components for an image +template +scalar Method_Solver::Force_on_Image_MaxAbsComponent( const vectorfield & image, vectorfield & force ) +{ + // Take out component in direction of v2 + Manifoldmath::project_tangential( force, image ); + // We want the Maximum of Absolute Values of all force components on all images + return Vectormath::max_abs_component( force ); +} + +// Return the maximum norm of the torque for an image +template +scalar Method_Solver::MaxTorque_on_Image( const vectorfield & image, vectorfield & force ) +{ + // Take out component in direction of v2 + Manifoldmath::project_tangential( force, image ); + return Vectormath::max_norm( force ); +} - template - void Method_Solver::Message_End() +template +bool Method_Solver::Converged() +{ + bool converged = false; + if( this->max_torque < this->parameters->force_convergence ) + converged = true; + return converged; +} + +// Default implementation: do nothing +template +void Method_Solver::Initialize(){}; + +// Default implementation: do nothing +template +void Method_Solver::Finalize(){}; + +// Default implementation: do nothing +template +void Method_Solver::Iteration(){}; + +template +void Method_Solver::Message_Start() +{ + using namespace Utility; + + //---- Log messages + std::vector block; + block.push_back( fmt::format( "------------ Started {} Calculation ------------", this->Name() ) ); + block.push_back( fmt::format( " Going to iterate {} step(s)", this->n_log ) ); + block.push_back( fmt::format( " with {} iterations per step", this->n_iterations_log ) ); + block.push_back( fmt::format( + " Force convergence parameter: {:." + fmt::format( "{}", this->print_precision ) + "f}", + this->parameters->force_convergence ) ); + block.push_back( fmt::format( + " Maximum torque: {:." + fmt::format( "{}", this->print_precision ) + "f}", + this->max_torque ) ); + block.push_back( fmt::format( " Solver: {}", this->SolverFullName() ) ); + if( this->Name() == "GNEB" ) { - using namespace Utility; - - std::string percentage = fmt::format("{:.2f}%:", 100*double(this->iteration)/double(this->n_iterations)); - bool llg_dynamics = this->Name() == "LLG" - && !( this->systems[this->idx_image]->llg_parameters->direct_minimization - || solver == Solver::VP || solver == Solver::VP_OSO - || solver == Solver::LBFGS_OSO || solver == Solver::LBFGS_Atlas ); - - //---- End timings - auto t_end = system_clock::now(); - - //---- Termination reason - std::string reason = ""; - if( this->StopFile_Present() ) - reason = "A STOP file has been found"; - else if( this->Converged() ) - reason = "The force converged"; - else if( this->Walltime_Expired(t_end - this->t_start) ) - reason = "The maximum walltime has been reached"; - - //---- Log messages - std::vector block; - block.push_back( fmt::format("------------ Terminated {} Calculation ------------", this->Name()) ); - if( reason.length() > 0 ) - block.push_back( fmt::format("------- Reason: {}", reason) ); - block.push_back( fmt::format(" Total duration: {}", Timing::DateTimePassed(t_end - this->t_start)) ); - block.push_back( fmt::format(" Completed {:>8} {} / {} iterations", percentage, this->iteration, this->n_iterations) ); - block.push_back( fmt::format(" Iterations / sec: {:.2f}", this->iteration / Timing::SecondsPassed(t_end - this->t_start)) ); - if( llg_dynamics ) - block.push_back( fmt::format(" Simulated time: {} ps", this->get_simulated_time()) ); - if( this->Name() == "GNEB" ) - { - scalar length = Manifoldmath::dist_geodesic(*this->configurations[0], *this->configurations[this->noi-1]); - block.push_back( fmt::format(" Total path length: {}", length) ); - } - block.push_back( fmt::format(" Force convergence parameter: {:."+fmt::format("{}",this->print_precision)+"f}", this->parameters->force_convergence) ); - block.push_back( fmt::format(" Maximum torque: {:."+fmt::format("{}",this->print_precision)+"f}", this->max_torque) ); - block.push_back( fmt::format(" Solver: {}", this->SolverFullName()) ); - block.push_back( "-----------------------------------------------------" ); - Log.SendBlock(Log_Level::All, this->SenderName, block, this->idx_image, this->idx_chain); + scalar length = Manifoldmath::dist_geodesic( *this->configurations[0], *this->configurations[this->noi - 1] ); + block.push_back( fmt::format( " Total path length: {}", length ) ); } + block.push_back( "-----------------------------------------------------" ); + Log.SendBlock( Log_Level::All, this->SenderName, block, this->idx_image, this->idx_chain ); +} - - template <> inline - std::string Method_Solver::SolverName() +template +void Method_Solver::Message_Step() +{ + using namespace Utility; + + std::string percentage = fmt::format( "{:.2f}%:", 100 * double( this->iteration ) / double( this->n_iterations ) ); + bool llg_dynamics + = this->Name() == "LLG" + && !( + this->systems[0]->llg_parameters->direct_minimization || solver == Solver::VP || solver == Solver::VP_OSO + || solver == Solver::LBFGS_OSO || solver == Solver::LBFGS_Atlas ); + + // Update time of current step + auto t_current = system_clock::now(); + + // Send log message + std::vector block; + block.push_back( fmt::format( + "----- {} Calculation ({} Solver): {}", this->Name(), this->SolverName(), + Timing::DateTimePassed( t_current - this->t_start ) ) ); + block.push_back( + fmt::format( " Time since last step: {}", Timing::DateTimePassed( t_current - this->t_last ) ) ); + block.push_back( + fmt::format( " Completed {:>8} {} / {} iterations", percentage, this->iteration, this->n_iterations ) ); + block.push_back( fmt::format( + " Iterations / sec: {:.2f}", + this->n_iterations_log / Timing::SecondsPassed( t_current - this->t_last ) ) ); + if( llg_dynamics ) + block.push_back( fmt::format( " Simulated time: {} ps", this->get_simulated_time() ) ); + if( this->Name() == "GNEB" ) { - return "None"; - }; + scalar length = Manifoldmath::dist_geodesic( *this->configurations[0], *this->configurations[this->noi - 1] ); + block.push_back( fmt::format( " Total path length: {}", length ) ); + } + block.push_back( fmt::format( + " Force convergence parameter: {:." + fmt::format( "{}", this->print_precision ) + "f}", + this->parameters->force_convergence ) ); + block.push_back( fmt::format( + " Maximum torque: {:." + fmt::format( "{}", this->print_precision ) + "f}", + this->max_torque ) ); + Log.SendBlock( Log_Level::All, this->SenderName, block, this->idx_image, this->idx_chain ); + + // Update time of last step + this->t_last = t_current; +} - template <> inline - std::string Method_Solver::SolverFullName() +template +void Method_Solver::Message_End() +{ + using namespace Utility; + + std::string percentage = fmt::format( "{:.2f}%:", 100 * double( this->iteration ) / double( this->n_iterations ) ); + bool llg_dynamics + = this->Name() == "LLG" + && !( + this->systems[0]->llg_parameters->direct_minimization || solver == Solver::VP || solver == Solver::VP_OSO + || solver == Solver::LBFGS_OSO || solver == Solver::LBFGS_Atlas ); + + //---- End timings + auto t_end = system_clock::now(); + + //---- Termination reason + std::string reason = ""; + if( this->StopFile_Present() ) + reason = "A STOP file has been found"; + else if( this->Converged() ) + reason = "The force converged"; + else if( this->Walltime_Expired( t_end - this->t_start ) ) + reason = "The maximum walltime has been reached"; + + //---- Log messages + std::vector block; + block.push_back( fmt::format( "------------ Terminated {} Calculation ------------", this->Name() ) ); + if( reason.length() > 0 ) + block.push_back( fmt::format( "------- Reason: {}", reason ) ); + block.push_back( fmt::format( " Total duration: {}", Timing::DateTimePassed( t_end - this->t_start ) ) ); + block.push_back( + fmt::format( " Completed {:>8} {} / {} iterations", percentage, this->iteration, this->n_iterations ) ); + block.push_back( fmt::format( + " Iterations / sec: {:.2f}", this->iteration / Timing::SecondsPassed( t_end - this->t_start ) ) ); + if( llg_dynamics ) + block.push_back( fmt::format( " Simulated time: {} ps", this->get_simulated_time() ) ); + if( this->Name() == "GNEB" ) { - return "None"; - }; - - // Include headers which specialize the Solver functions - #include - #include - #include - #include - #include - #include - #include - #include + scalar length = Manifoldmath::dist_geodesic( *this->configurations[0], *this->configurations[this->noi - 1] ); + block.push_back( fmt::format( " Total path length: {}", length ) ); + } + block.push_back( fmt::format( + " Force convergence parameter: {:." + fmt::format( "{}", this->print_precision ) + "f}", + this->parameters->force_convergence ) ); + block.push_back( fmt::format( + " Maximum torque: {:." + fmt::format( "{}", this->print_precision ) + "f}", + this->max_torque ) ); + block.push_back( fmt::format( " Solver: {}", this->SolverFullName() ) ); + block.push_back( "-----------------------------------------------------" ); + Log.SendBlock( Log_Level::All, this->SenderName, block, this->idx_image, this->idx_chain ); } +template<> +inline std::string Method_Solver::SolverName() +{ + return "None"; +}; + +template<> +inline std::string Method_Solver::SolverFullName() +{ + return "None"; +}; + +// Include headers which specialize the Solver functions +#include +#include +#include +#include +#include +#include +#include +#include +} // namespace Engine + #endif \ No newline at end of file diff --git a/core/include/engine/Vectormath.hpp b/core/include/engine/Vectormath.hpp index 077f02bf9..b0591095c 100644 --- a/core/include/engine/Vectormath.hpp +++ b/core/include/engine/Vectormath.hpp @@ -272,7 +272,7 @@ namespace Engine // Translations (cell) of spin i int nic = ispin / (N*Na*Nb); int nib = (ispin - nic*N*Na*Nb) / (N*Na); - int nia = ispin - nic*N*Na*Nb - nib*N*Na; + int nia = (ispin - nic*N*Na*Nb - nib*N*Na) / N; // Translations (cell) of spin j (possibly outside of non-periodical domain) int pm = 1; @@ -336,8 +336,7 @@ namespace Engine // Calculate the index of spin j according to it's translations int jspin = pair.j + (nja)*N + (njb)*N*Na + (njc)*N*Na*Nb; - // Invalid index if atom type of spin j is not correct - if ( pair.j != jspin%N || !cu_check_atom_type(atom_types[jspin]) ) + if ( !cu_check_atom_type(atom_types[jspin]) ) return -1; // Return a valid index diff --git a/core/src/engine/Hamiltonian_Heisenberg.cpp b/core/src/engine/Hamiltonian_Heisenberg.cpp index f46b93a29..cc85a23ee 100644 --- a/core/src/engine/Hamiltonian_Heisenberg.cpp +++ b/core/src/engine/Hamiltonian_Heisenberg.cpp @@ -66,7 +66,6 @@ namespace Engine quadruplets(quadruplets), quadruplet_magnitudes(quadruplet_magnitudes), ddi_method(ddi_method), ddi_n_periodic_images(ddi_n_periodic_images), ddi_pb_zero_padding(ddi_pb_zero_padding), ddi_cutoff_radius(ddi_radius), fft_plan_reverse(FFT::FFT_Plan()), fft_plan_spins(FFT::FFT_Plan()) - { // Generate interaction pairs, constants etc. this->Update_Interactions(); @@ -425,20 +424,24 @@ namespace Engine { for( unsigned int iquad = 0; iquad < quadruplets.size(); ++iquad ) { + const auto& quad = quadruplets[iquad]; + + int i = quad.i; + int j = quad.j; + int k = quad.k; + int l = quad.l; for( int da = 0; da < geometry->n_cells[0]; ++da ) { for( int db = 0; db < geometry->n_cells[1]; ++db ) { for( int dc = 0; dc < geometry->n_cells[2]; ++dc ) { - std::array translations = { da, db, dc }; - int ispin = quadruplets[iquad].i + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations); - int jspin = quadruplets[iquad].j + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_j); - int kspin = quadruplets[iquad].k + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_k); - int lspin = quadruplets[iquad].l + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_l); - - if( check_atom_type(this->geometry->atom_types[ispin]) && check_atom_type(this->geometry->atom_types[jspin]) && - check_atom_type(this->geometry->atom_types[kspin]) && check_atom_type(this->geometry->atom_types[lspin]) ) + int ispin = i + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, { da, db, dc }); + int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, {i, j, quad.d_j}); + int kspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, {i, k, quad.d_k}); + int lspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, {i, l, quad.d_l}); + + if( ispin >= 0 && jspin >= 0 && kspin >= 0 && lspin >= 0 ) { Energy[ispin] -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin])); Energy[jspin] -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin])); @@ -529,36 +532,9 @@ namespace Engine } } - // Quadruplets + // TODO: Quadruplets if( this->idx_quadruplet >= 0 ) { - for( unsigned int iquad = 0; iquad < quadruplets.size(); ++iquad ) - { - auto translations = Vectormath::translations_from_idx(geometry->n_cells, geometry->n_cell_atoms, icell); - int ispin = quadruplets[iquad].i + icell*geometry->n_cell_atoms; - int jspin = quadruplets[iquad].j + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_j); - int kspin = quadruplets[iquad].k + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_k); - int lspin = quadruplets[iquad].l + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_l); - - if( check_atom_type(this->geometry->atom_types[ispin]) && check_atom_type(this->geometry->atom_types[jspin]) && - check_atom_type(this->geometry->atom_types[kspin]) && check_atom_type(this->geometry->atom_types[lspin]) ) - { - Energy -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin])); - } - - #ifndef SPIRIT_USE_OPENMP - // TODO: mirrored quadruplet when unique quadruplets are used - // jspin = quadruplets[iquad].j + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_j, true); - // kspin = quadruplets[iquad].k + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_k, true); - // lspin = quadruplets[iquad].l + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_l, true); - - // if ( check_atom_type(this->geometry->atom_types[ispin]) && check_atom_type(this->geometry->atom_types[jspin]) && - // check_atom_type(this->geometry->atom_types[kspin]) && check_atom_type(this->geometry->atom_types[lspin]) ) - // { - // Energy -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin])); - // } - #endif - } } } return Energy; @@ -638,7 +614,7 @@ namespace Engine { // Kind of a bandaid fix this->Gradient_Quadruplet(spins, gradient); - if(energy_contributions_per_spin[idx_quadruplet].second.size() != spins.size()) + if(energy_contributions_per_spin[idx_quadruplet].second.size() != spins.size()) { energy_contributions_per_spin[idx_quadruplet].second.resize(spins.size()); }; @@ -913,24 +889,24 @@ namespace Engine { for( unsigned int iquad = 0; iquad < quadruplets.size(); ++iquad ) { - int i = quadruplets[iquad].i; - int j = quadruplets[iquad].j; - int k = quadruplets[iquad].k; - int l = quadruplets[iquad].l; + const auto& quad = quadruplets[iquad]; + + int i = quad.i; + int j = quad.j; + int k = quad.k; + int l = quad.l; for( int da = 0; da < geometry->n_cells[0]; ++da ) { for( int db = 0; db < geometry->n_cells[1]; ++db ) { for( int dc = 0; dc < geometry->n_cells[2]; ++dc ) { - std::array translations = { da, db, dc }; - int ispin = i + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations); - int jspin = j + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_j); - int kspin = k + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_k); - int lspin = l + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_l); - - if( check_atom_type(this->geometry->atom_types[ispin]) && check_atom_type(this->geometry->atom_types[jspin]) && - check_atom_type(this->geometry->atom_types[kspin]) && check_atom_type(this->geometry->atom_types[lspin]) ) + int ispin = i + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, { da, db, dc }); + int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, {i, j, quad.d_j}); + int kspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, {i, k, quad.d_k}); + int lspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, {i, l, quad.d_l}); + + if( ispin >= 0 && jspin >= 0 && kspin >= 0 && lspin >= 0 ) { gradient[ispin] -= quadruplet_magnitudes[iquad] * spins[jspin] * (spins[kspin].dot(spins[lspin])); gradient[jspin] -= quadruplet_magnitudes[iquad] * spins[ispin] * (spins[kspin].dot(spins[lspin])); @@ -1072,8 +1048,8 @@ namespace Engine } } - //// Dipole-Dipole - //for (unsigned int i_pair = 0; i_pair < this->DD_indices.size(); ++i_pair) + // // TODO: Dipole-Dipole + // for (unsigned int i_pair = 0; i_pair < this->DD_indices.size(); ++i_pair) // { // // indices // int idx_1 = DD_indices[i_pair][0]; @@ -1095,7 +1071,7 @@ namespace Engine // } // } - // Quadruplets + // TODO: Quadruplets } void Hamiltonian_Heisenberg::Sparse_Hessian(const vectorfield & spins, SpMatrixX & hessian) @@ -1251,8 +1227,8 @@ namespace Engine // Iterate over the padded system const int * c_n_cells_padded = n_cells_padded.data(); - std::array cell_sizes = {geometry->lattice_constant * geometry->bravais_vectors[0].norm(), - geometry->lattice_constant * geometry->bravais_vectors[1].norm(), + std::array cell_sizes = {geometry->lattice_constant * geometry->bravais_vectors[0].norm(), + geometry->lattice_constant * geometry->bravais_vectors[1].norm(), geometry->lattice_constant * geometry->bravais_vectors[2].norm()}; #pragma omp parallel for collapse(3) diff --git a/core/src/engine/Hamiltonian_Heisenberg.cu b/core/src/engine/Hamiltonian_Heisenberg.cu index f6188ca6d..ed3153fd0 100644 --- a/core/src/engine/Hamiltonian_Heisenberg.cu +++ b/core/src/engine/Hamiltonian_Heisenberg.cu @@ -256,7 +256,7 @@ namespace Engine { for (int ibasis=0; ibasis= 0) { Energy[ispin] -= 0.5 * magnitudes[ipair] * spins[ispin].dot(spins[jspin]); @@ -335,7 +335,7 @@ namespace Engine for(auto ipair = 0; ipair < n_pairs; ++ipair) { int ispin = pairs[ipair].i + icell*n_cell_atoms; - int jspin = cu_idx_from_pair(icell, bc, nc, n_cell_atoms, atom_types, pairs[ipair]); + int jspin = cu_idx_from_pair(ispin, bc, nc, n_cell_atoms, atom_types, pairs[ipair]); if (jspin >= 0) { Energy[ispin] -= 0.5 * magnitudes[ipair] * normals[ipair].dot(spins[ispin].cross(spins[jspin])); @@ -513,31 +513,41 @@ namespace Engine void Hamiltonian_Heisenberg::E_Quadruplet(const vectorfield & spins, scalarfield & Energy) { - // for (unsigned int iquad = 0; iquad < quadruplets.size(); ++iquad) - // { - // for (int da = 0; da < geometry->n_cells[0]; ++da) - // { - // for (int db = 0; db < geometry->n_cells[1]; ++db) - // { - // for (int dc = 0; dc < geometry->n_cells[2]; ++dc) - // { - // std::array translations = { da, db, dc }; - // // int i = quadruplets[iquad].i; - // // int j = quadruplets[iquad].j; - // // int k = quadruplets[iquad].k; - // // int l = quadruplets[iquad].l; - // int i = idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations); - // int j = idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_j); - // int k = idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_k); - // int l = idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_l); - // Energy[i] -= 0.25*quadruplet_magnitudes[iquad] * (spins[i].dot(spins[j])) * (spins[k].dot(spins[l])); - // Energy[j] -= 0.25*quadruplet_magnitudes[iquad] * (spins[i].dot(spins[j])) * (spins[k].dot(spins[l])); - // Energy[k] -= 0.25*quadruplet_magnitudes[iquad] * (spins[i].dot(spins[j])) * (spins[k].dot(spins[l])); - // Energy[l] -= 0.25*quadruplet_magnitudes[iquad] * (spins[i].dot(spins[j])) * (spins[k].dot(spins[l])); - // } - // } - // } - // } + for( unsigned int iquad = 0; iquad < quadruplets.size(); ++iquad ) + { + const auto& quad = quadruplets[iquad]; + + int i = quad.i; + int j = quad.j; + int k = quad.k; + int l = quad.l; + + const auto& d_j = quad.d_j; + const auto& d_k = quad.d_k; + const auto& d_l = quad.d_l; + + for( int da = 0; da < geometry->n_cells[0]; ++da ) + { + for( int db = 0; db < geometry->n_cells[1]; ++db ) + { + for( int dc = 0; dc < geometry->n_cells[2]; ++dc ) + { + int ispin = i + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, { da, db, dc }); + int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, {i, j, {d_j[0], d_j[1], d_j[2]}}); + int kspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, {i, k, {d_k[0], d_k[1], d_k[2]}}); + int lspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, {i, l, {d_l[0], d_l[1], d_l[2]}}); + + if( ispin >= 0 && jspin >= 0 && kspin >= 0 && lspin >= 0 ) + { + Energy[ispin] -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin])); + Energy[jspin] -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin])); + Energy[kspin] -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin])); + Energy[lspin] -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin])); + } + } + } + } + } } @@ -598,23 +608,9 @@ namespace Engine } } - // Quadruplets + // TODO: Quadruplets if (this->idx_quadruplet >= 0) { - for (unsigned int iquad = 0; iquad < quadruplets.size(); ++iquad) - { - auto translations = Vectormath::translations_from_idx(geometry->n_cells, geometry->n_cell_atoms, icell); - int ispin = quadruplets[iquad].i + icell*geometry->n_cell_atoms; - int jspin = quadruplets[iquad].j + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_j); - int kspin = quadruplets[iquad].k + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_k); - int lspin = quadruplets[iquad].l + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_l); - - if ( check_atom_type(this->geometry->atom_types[ispin]) && check_atom_type(this->geometry->atom_types[jspin]) && - check_atom_type(this->geometry->atom_types[kspin]) && check_atom_type(this->geometry->atom_types[lspin]) ) - { - Energy -= 0.25*quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * (spins[kspin].dot(spins[lspin])); - } - } } } return Energy; @@ -693,7 +689,7 @@ namespace Engine { // Kind of a bandaid fix this->Gradient_Quadruplet(spins, gradient); - if(energy_contributions_per_spin[idx_quadruplet].second.size() != spins.size()) + if(energy_contributions_per_spin[idx_quadruplet].second.size() != spins.size()) { energy_contributions_per_spin[idx_quadruplet].second.resize(spins.size()); }; @@ -712,7 +708,7 @@ namespace Engine { for (int ibasis=0; ibasis= 0) { gradient[ispin] -= magnitudes[ipair]*spins[jspin]; @@ -794,7 +790,7 @@ namespace Engine for(auto ipair = 0; ipair < n_pairs; ++ipair) { int ispin = pairs[ipair].i + icell*n_cell_atoms; - int jspin = cu_idx_from_pair(icell, bc, nc, n_cell_atoms, atom_types, pairs[ipair]); + int jspin = cu_idx_from_pair(ispin, bc, nc, n_cell_atoms, atom_types, pairs[ipair]); if (jspin >= 0) { gradient[ispin] -= magnitudes[ipair]*spins[jspin].cross(normals[ipair]); @@ -829,119 +825,146 @@ namespace Engine // TODO } - __global__ void CU_FFT_Pointwise_Mult(FFT::FFT_cpx_type * ft_D_matrices, FFT::FFT_cpx_type * ft_spins, FFT::FFT_cpx_type * res_mult, int* iteration_bounds, int i_b1, int* inter_sublattice_lookup, FFT::StrideContainer dipole_stride, FFT::StrideContainer spin_stride) + __global__ void CU_FFT_Pointwise_Mult( + FFT::FFT_cpx_type * ft_D_matrices, FFT::FFT_cpx_type * ft_spins, FFT::FFT_cpx_type * res_mult, + int * iteration_bounds, int * inter_sublattice_lookup, FFT::StrideContainer dipole_stride, + FFT::StrideContainer spin_stride ) { - int n = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2] * iteration_bounds[3]; + int nos = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2] * iteration_bounds[3]; int tupel[4]; - int idx_b1, idx_b2, idx_d; - for(int ispin = blockIdx.x * blockDim.x + threadIdx.x; ispin < n; ispin += blockDim.x * gridDim.x) + for( int ispin = blockIdx.x * blockDim.x + threadIdx.x; ispin < nos; ispin += blockDim.x * gridDim.x ) { - cu_tupel_from_idx(ispin, tupel, iteration_bounds, 4); // tupel now is {i_b2, a, b, c} - - int& b_inter = inter_sublattice_lookup[i_b1 + tupel[0] * iteration_bounds[0]]; - - idx_b1 = i_b1 * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + tupel[3] * spin_stride.c; - idx_b2 = tupel[0] * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + tupel[3] * spin_stride.c; - idx_d = b_inter * dipole_stride.basis + tupel[1] * dipole_stride.a + tupel[2] * dipole_stride.b + tupel[3] * dipole_stride.c; - - auto& fs_x = ft_spins[idx_b2 ]; - auto& fs_y = ft_spins[idx_b2 + 1 * spin_stride.comp]; - auto& fs_z = ft_spins[idx_b2 + 2 * spin_stride.comp]; + cu_tupel_from_idx( ispin, tupel, iteration_bounds, 4 ); // tupel now is {i_b1, a, b, c} + int i_b1 = tupel[0], a = tupel[1], b = tupel[2], c = tupel[3]; - auto& fD_xx = ft_D_matrices[idx_d ]; - auto& fD_xy = ft_D_matrices[idx_d + 1 * dipole_stride.comp]; - auto& fD_xz = ft_D_matrices[idx_d + 2 * dipole_stride.comp]; - auto& fD_yy = ft_D_matrices[idx_d + 3 * dipole_stride.comp]; - auto& fD_yz = ft_D_matrices[idx_d + 4 * dipole_stride.comp]; - auto& fD_zz = ft_D_matrices[idx_d + 5 * dipole_stride.comp]; + // Index to the first component of the spin (Remember: not the same as ispin, because we also have the spin + // component stride!) + int idx_b1 = i_b1 * spin_stride.basis + a * spin_stride.a + b * spin_stride.b + c * spin_stride.c; - if(tupel[0] == 0) + // Collect the intersublattice contributions + FFT::FFT_cpx_type res_temp_x{ 0, 0 }, res_temp_y{ 0, 0 }, res_temp_z{ 0, 0 }; + for( int i_b2 = 0; i_b2 < iteration_bounds[0]; i_b2++ ) { - res_mult[idx_b1 ].x = FFT::mult3D(fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z).x; - res_mult[idx_b1 ].y = FFT::mult3D(fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z).y; - res_mult[idx_b1 + 1 * spin_stride.comp].x = FFT::mult3D(fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z).x; - res_mult[idx_b1 + 1 * spin_stride.comp].y = FFT::mult3D(fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z).y; - res_mult[idx_b1 + 2 * spin_stride.comp].x = FFT::mult3D(fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z).x; - res_mult[idx_b1 + 2 * spin_stride.comp].y = FFT::mult3D(fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z).y; - } else { - atomicAdd(&res_mult[idx_b1 ].x, FFT::mult3D(fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z).x); - atomicAdd(&res_mult[idx_b1 ].y, FFT::mult3D(fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z).y); - atomicAdd(&res_mult[idx_b1 + 1 * spin_stride.comp].x, FFT::mult3D(fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z).x); - atomicAdd(&res_mult[idx_b1 + 1 * spin_stride.comp].y, FFT::mult3D(fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z).y); - atomicAdd(&res_mult[idx_b1 + 2 * spin_stride.comp].x, FFT::mult3D(fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z).x); - atomicAdd(&res_mult[idx_b1 + 2 * spin_stride.comp].y, FFT::mult3D(fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z).y); + // Index to the first component of the second spin + int idx_b2 = i_b2 * spin_stride.basis + a * spin_stride.a + b * spin_stride.b + c * spin_stride.c; + + int & b_inter = inter_sublattice_lookup[i_b1 + i_b2 * iteration_bounds[0]]; + // Index of the dipole matrix "connecting" the two spins + int idx_d = b_inter * dipole_stride.basis + a * dipole_stride.a + b * dipole_stride.b + c * dipole_stride.c; + + // Fourier transformed components of the second spin + auto & fs_x = ft_spins[idx_b2]; + auto & fs_y = ft_spins[idx_b2 + 1 * spin_stride.comp]; + auto & fs_z = ft_spins[idx_b2 + 2 * spin_stride.comp]; + + // Fourier transformed components of the dipole matrix + auto & fD_xx = ft_D_matrices[idx_d]; + auto & fD_xy = ft_D_matrices[idx_d + 1 * dipole_stride.comp]; + auto & fD_xz = ft_D_matrices[idx_d + 2 * dipole_stride.comp]; + auto & fD_yy = ft_D_matrices[idx_d + 3 * dipole_stride.comp]; + auto & fD_yz = ft_D_matrices[idx_d + 4 * dipole_stride.comp]; + auto & fD_zz = ft_D_matrices[idx_d + 5 * dipole_stride.comp]; + + FFT::addTo( res_temp_x, FFT::mult3D( fD_xx, fD_xy, fD_xz, fs_x, fs_y, fs_z ) ); + FFT::addTo( res_temp_y, FFT::mult3D( fD_xy, fD_yy, fD_yz, fs_x, fs_y, fs_z ) ); + FFT::addTo( res_temp_z, FFT::mult3D( fD_xz, fD_yz, fD_zz, fs_x, fs_y, fs_z ) ); } + // Add the temporary result + FFT::addTo( res_mult[idx_b1 + 0 * spin_stride.comp], res_temp_x, true ); + FFT::addTo( res_mult[idx_b1 + 1 * spin_stride.comp], res_temp_y, true ); + FFT::addTo( res_mult[idx_b1 + 2 * spin_stride.comp], res_temp_z, true ); } } - __global__ void CU_Write_FFT_Gradients(FFT::FFT_real_type * resiFFT, Vector3 * gradient, FFT::StrideContainer spin_stride , int * iteration_bounds, int n_cell_atoms, scalar * mu_s, int sublattice_size) + __global__ void CU_Write_FFT_Gradients( + FFT::FFT_real_type * resiFFT, Vector3 * gradient, FFT::StrideContainer spin_stride, int * iteration_bounds, + int n_cell_atoms, scalar * mu_s, int sublattice_size ) { int nos = iteration_bounds[0] * iteration_bounds[1] * iteration_bounds[2] * iteration_bounds[3]; int tupel[4]; int idx_pad; - for(int idx_orig = blockIdx.x * blockDim.x + threadIdx.x; idx_orig < nos; idx_orig += blockDim.x * gridDim.x) + for( int idx_orig = blockIdx.x * blockDim.x + threadIdx.x; idx_orig < nos; idx_orig += blockDim.x * gridDim.x ) { - cu_tupel_from_idx(idx_orig, tupel, iteration_bounds, 4); //tupel now is {ib, a, b, c} - idx_pad = tupel[0] * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + tupel[3] * spin_stride.c; - gradient[idx_orig][0] -= mu_s[idx_orig] * resiFFT[idx_pad ] / sublattice_size; + cu_tupel_from_idx( idx_orig, tupel, iteration_bounds, 4 ); // tupel now is {ib, a, b, c} + idx_pad = tupel[0] * spin_stride.basis + tupel[1] * spin_stride.a + tupel[2] * spin_stride.b + + tupel[3] * spin_stride.c; + gradient[idx_orig][0] -= mu_s[idx_orig] * resiFFT[idx_pad] / sublattice_size; gradient[idx_orig][1] -= mu_s[idx_orig] * resiFFT[idx_pad + 1 * spin_stride.comp] / sublattice_size; gradient[idx_orig][2] -= mu_s[idx_orig] * resiFFT[idx_pad + 2 * spin_stride.comp] / sublattice_size; } } - void Hamiltonian_Heisenberg::Gradient_DDI_FFT(const vectorfield & spins, vectorfield & gradient) + void Hamiltonian_Heisenberg::Gradient_DDI_FFT( const vectorfield & spins, vectorfield & gradient ) { - auto& ft_D_matrices = transformed_dipole_matrices; + auto & ft_D_matrices = transformed_dipole_matrices; - auto& ft_spins = fft_plan_spins.cpx_ptr; + auto & ft_spins = fft_plan_spins.cpx_ptr; - auto& res_iFFT = fft_plan_reverse.real_ptr; - auto& res_mult = fft_plan_reverse.cpx_ptr; + auto & res_iFFT = fft_plan_reverse.real_ptr; + auto & res_mult = fft_plan_reverse.cpx_ptr; - int number_of_mults = it_bounds_pointwise_mult[0] * it_bounds_pointwise_mult[1] * it_bounds_pointwise_mult[2] * it_bounds_pointwise_mult[3]; + int number_of_mults = it_bounds_pointwise_mult[0] * it_bounds_pointwise_mult[1] * it_bounds_pointwise_mult[2] + * it_bounds_pointwise_mult[3]; - FFT_Spins(spins); + FFT_Spins( spins ); // TODO: also parallelize over i_b1 // Loop over basis atoms (i.e sublattices) and add contribution of each sublattice - for(int i_b1 = 0; i_b1 < geometry->n_cell_atoms; ++i_b1) - CU_FFT_Pointwise_Mult<<<(number_of_mults + 1023) / 1024, 1024>>>(ft_D_matrices.data(), ft_spins.data(), res_mult.data(), it_bounds_pointwise_mult.data(), i_b1, inter_sublattice_lookup.data(), dipole_stride, spin_stride); + CU_FFT_Pointwise_Mult<<<( spins.size() + 1023 ) / 1024, 1024>>>( + ft_D_matrices.data(), ft_spins.data(), res_mult.data(), it_bounds_pointwise_mult.data(), + inter_sublattice_lookup.data(), dipole_stride, spin_stride ); + // cudaDeviceSynchronize(); + // std::cerr << "\n\n>>>>>>>>>>> Pointwise_Mult <<<<<<<<<\n"; + // for( int i = 0; i < 10; i++ ) + // std::cout << ( res_mult[i].x ) << " " << ( res_mult[i].y ) << " "; + // std::cerr << "\n>=====================================<\n\n"; + + FFT::batch_iFour_3D( fft_plan_reverse ); + + CU_Write_FFT_Gradients<<<( geometry->nos + 1023 ) / 1024, 1024>>>( + res_iFFT.data(), gradient.data(), spin_stride, it_bounds_write_gradients.data(), geometry->n_cell_atoms, + geometry->mu_s.data(), sublattice_size ); + } // end Field_DipoleDipole - FFT::batch_iFour_3D(fft_plan_reverse); + void Hamiltonian_Heisenberg::Gradient_Quadruplet(const vectorfield & spins, vectorfield & gradient) + { + for( unsigned int iquad = 0; iquad < quadruplets.size(); ++iquad ) + { + const auto& quad = quadruplets[iquad]; - CU_Write_FFT_Gradients<<<(geometry->nos + 1023) / 1024, 1024>>>(res_iFFT.data(), gradient.data(), spin_stride, it_bounds_write_gradients.data(), geometry->n_cell_atoms, geometry->mu_s.data(), sublattice_size); - }//end Field_DipoleDipole + int i = quad.i; + int j = quad.j; + int k = quad.k; + int l = quad.l; + const auto& d_j = quad.d_j; + const auto& d_k = quad.d_k; + const auto& d_l = quad.d_l; - void Hamiltonian_Heisenberg::Gradient_Quadruplet(const vectorfield & spins, vectorfield & gradient) - { - // for (unsigned int iquad = 0; iquad < quadruplets.size(); ++iquad) - // { - // int i = quadruplets[iquad].i; - // int j = quadruplets[iquad].j; - // int k = quadruplets[iquad].k; - // int l = quadruplets[iquad].l; - // for (int da = 0; da < geometry->n_cells[0]; ++da) - // { - // for (int db = 0; db < geometry->n_cells[1]; ++db) - // { - // for (int dc = 0; dc < geometry->n_cells[2]; ++dc) - // { - // std::array translations = { da, db, dc }; - // int ispin = idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations); - // int jspin = idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_j); - // int kspin = idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_k); - // int lspin = idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, translations, quadruplets[iquad].d_l); - // gradient[ispin] -= quadruplet_magnitudes[iquad] * spins[jspin] * (spins[kspin].dot(spins[lspin])); - // gradient[jspin] -= quadruplet_magnitudes[iquad] * spins[ispin] * (spins[kspin].dot(spins[lspin])); - // gradient[kspin] -= quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * spins[lspin]; - // gradient[lspin] -= quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * spins[kspin]; - // } - // } - // } - // } + for( int da = 0; da < geometry->n_cells[0]; ++da ) + { + for( int db = 0; db < geometry->n_cells[1]; ++db ) + { + for( int dc = 0; dc < geometry->n_cells[2]; ++dc ) + { + int ispin = i + Vectormath::idx_from_translations(geometry->n_cells, geometry->n_cell_atoms, { da, db, dc }); + int jspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, {i, j, {d_j[0], d_j[1], d_j[2]}}); + int kspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, {i, k, {d_k[0], d_k[1], d_k[2]}}); + int lspin = idx_from_pair(ispin, boundary_conditions, geometry->n_cells, geometry->n_cell_atoms, geometry->atom_types, {i, l, {d_l[0], d_l[1], d_l[2]}}); + + if( ispin >= 0 && jspin >= 0 && kspin >= 0 && lspin >= 0 ) + { + gradient[ispin] -= quadruplet_magnitudes[iquad] * spins[jspin] * (spins[kspin].dot(spins[lspin])); + gradient[jspin] -= quadruplet_magnitudes[iquad] * spins[ispin] * (spins[kspin].dot(spins[lspin])); + gradient[kspin] -= quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * spins[lspin]; + gradient[lspin] -= quadruplet_magnitudes[iquad] * (spins[ispin].dot(spins[jspin])) * spins[kspin]; + } + } + } + } + } } @@ -1058,8 +1081,8 @@ namespace Engine } } - //// Dipole-Dipole - //for (unsigned int i_pair = 0; i_pair < this->DD_indices.size(); ++i_pair) + // // TODO: Dipole-Dipole + // for (unsigned int i_pair = 0; i_pair < this->DD_indices.size(); ++i_pair) // { // // indices // int idx_1 = DD_indices[i_pair][0]; @@ -1081,7 +1104,7 @@ namespace Engine // } // } - // Quadruplets + // TODO: Quadruplets } void Hamiltonian_Heisenberg::Sparse_Hessian(const vectorfield & spins, SpMatrixX & hessian) diff --git a/core/src/engine/Sparse_HTST.cpp b/core/src/engine/Sparse_HTST.cpp index ae61b3568..caeb43d30 100644 --- a/core/src/engine/Sparse_HTST.cpp +++ b/core/src/engine/Sparse_HTST.cpp @@ -65,18 +65,39 @@ namespace Engine } } + void Inverse_Shift_PowerMethod(int n_iter, const SpMatrixX & matrix, scalar & evalue_estimate, VectorX & evec_estimate) + { + SpMatrixX tmp = SpMatrixX(matrix.rows(), matrix.cols()); + tmp.setIdentity(); + tmp *= evalue_estimate; + + Eigen::SparseLU > solver; + solver.analyzePattern(matrix - tmp); + solver.factorize(matrix - tmp); + + Log(Utility::Log_Level::All, Utility::Log_Sender::HTST, fmt::format(" ... Improve eigenpair estimate with power method for eigenvalue = {}", evalue_estimate)); + for(int i=0; i & evecs) { Log(Utility::Log_Level::All, Utility::Log_Sender::HTST, fmt::format(" Computing eigenvalues smaller than {}", max_evalue)); - - scalar tol = 1e-6; - scalar evalue_epsilon = 1e-4; - int n_log_step = 2500; + scalar tol = 1e-8; + int n_log_step = 20000; scalar cur = 2 * tol; scalar m = 0.01; scalar step_size = 1e-4; int n_iter = 0; int nos = matrix.rows()/2; + int max_iter = 10*n_log_step; + int n_iter_power = 500; scalar sigma_shift = std::max(scalar(5.0), 2*scalar(max_evalue)); @@ -85,6 +106,8 @@ namespace Engine VectorX velocity = VectorX::Zero(2*nos); scalar cur_evalue_estimate; scalar fnorm2, ratio, proj; + + scalar max_grad_comp = 0; bool run = true; // We try to find the lowest n_values eigenvalue/vector pairs @@ -123,11 +146,18 @@ namespace Engine if (proj<=0) velocity.setZero(); - else + else velocity = gradient*ratio; - // Update x and renormalize + // Update x x -= step_size * velocity + 0.5/m * step_size * gradient; + + // Re-orthogonalize + for (int i=0; i {})", n_iter, cur_evalue_estimate, std::sqrt(fnorm2), tol)); + Log(Utility::Log_Level::Info, Utility::Log_Sender::HTST, fmt::format(" ... Iteration {}: Evalue estimate = {}, Grad. norm = {} (> {})", n_iter, cur_evalue_estimate, max_grad_comp, tol)); - search = (std::sqrt(fnorm2) > tol); + search = max_grad_comp > tol && n_iter < max_iter; } + // We improve the eigenpair estimate with the inverse shift power method (This may be necessary for accurate cancellation of zero-modes) + Inverse_Shift_PowerMethod(n_iter_power, matrix, cur_evalue_estimate, x); + // Ideally we have found one eigenvalue/vector pair now // We save the eigenvalue evecs.push_back(x); @@ -159,63 +196,6 @@ namespace Engine } } - void Sparse_Get_Lowest_Eigenvector_VP(const SpMatrixX & matrix, int nos, const VectorX & init, scalar & lowest_evalue, VectorX & lowest_evec) - { - Log(Utility::Log_Level::All, Utility::Log_Sender::HTST, " Minimizing Rayleigh quotient to compute lowest eigenmode..."); - auto & x = lowest_evec; - x = init; - x.normalize(); - - scalar tol = 1e-6; - scalar cur = 2 * tol; - scalar m = 0.01; - scalar step_size = 1e-4; - int n_iter = 0; - - VectorX gradient = VectorX::Zero(2*nos); - VectorX gradient_prev = VectorX::Zero(2*nos); - VectorX velocity = VectorX::Zero(2*nos); - - VectorX mx; // temporary for matrix * x - scalar proj; // temporary for gradient * x - scalar fnorm2 = 2*tol*tol, ratio; - - while (std::sqrt(fnorm2) > tol) - { - // Compute gradient of Rayleigh quotient - mx = matrix * x; - gradient = 2 * mx; - proj = gradient.dot(x); - gradient -= proj * x; - - velocity = 0.5 * (gradient + gradient_prev) / m; - fnorm2 = gradient.squaredNorm(); - - proj = velocity.dot(gradient); - ratio = proj/fnorm2; - - if (proj<=0) - velocity.setZero(); - else - velocity = gradient*ratio; - - // Update x and renormalize - x -= step_size * velocity + 0.5/m * step_size * gradient; - x.normalize(); - - // Update prev gradient - gradient_prev = gradient; - - // Increment n_iter - lowest_evalue = x.dot(matrix * x); - n_iter++; - } - - // Compute the eigenvalue - lowest_evalue = x.dot(matrix * x); - Log(Utility::Log_Level::All, Utility::Log_Sender::HTST, fmt::format(" Finished after {} iterations", n_iter)); - } - // Note the two images should correspond to one minimum and one saddle point // Non-extremal images may yield incorrect Hessians and thus incorrect results void Calculate(Data::HTST_Info & htst_info) @@ -297,6 +277,11 @@ namespace Engine sparse_hessian_bordered_3N(image_sp, gradient_sp, sparse_hessian_sp, sparse_hessian_sp_geodesic_3N); SpMatrixX sparse_hessian_sp_geodesic_2N = tangent_basis.transpose() * sparse_hessian_sp_geodesic_3N * tangent_basis; + Log(Utility::Log_Level::Info, Utility::Log_Sender::HTST, " Sparse LU Decomposition of geodesic Hessian..."); + Eigen::SparseLU > solver; + solver.analyzePattern(sparse_hessian_sp_geodesic_2N); + solver.factorize(sparse_hessian_sp_geodesic_2N); + Log(Utility::Log_Level::Info, Utility::Log_Sender::HTST, " Evaluate lowest eigenmode of the Hessian..."); std::vector evecs_sp = std::vector(0); @@ -304,6 +289,8 @@ namespace Engine scalar lowest_evalue = evalues_sp[0]; VectorX & lowest_evector = evecs_sp[0]; + htst_info.det_sp = solver.logAbsDeterminant() - std::log(-lowest_evalue); + // Check if lowest eigenvalue < 0 (else it's not a SP) Log(Utility::Log_Level::Info, Utility::Log_Sender::HTST, " Check if actually a saddle point..."); if( lowest_evalue > -epsilon ) @@ -326,12 +313,6 @@ namespace Engine return; } - Log(Utility::Log_Level::Info, Utility::Log_Sender::HTST, " Sparse LU Decomposition of geodesic Hessian..."); - Eigen::SparseLU > solver; - solver.analyzePattern(sparse_hessian_sp_geodesic_2N); - solver.factorize(sparse_hessian_sp_geodesic_2N); - htst_info.det_sp = solver.logAbsDeterminant() - std::log(-lowest_evalue); - // Perpendicular velocity Log(Utility::Log_Level::Info, Utility::Log_Sender::HTST, " Calculate dynamical contribution"); @@ -344,7 +325,6 @@ namespace Engine VectorX x(2*nos); x = solver.solve(projected_velocity.transpose() * lowest_evector); htst_info.s = std::sqrt(lowest_evector.transpose() * projected_velocity * x ); - // Checking for zero modes at the saddle point... Log(Utility::Log_Level::Info, Utility::Log_Sender::HTST, " Checking for zero modes at the saddle point..."); for( int i=0; i < evalues_sp.size(); ++i ) @@ -434,10 +414,10 @@ namespace Engine scalar zero_mode_factor = 1; for (int i=0; i tripletList; tripletList.reserve(hessian.nonZeros()); - auto levi_civita = [](int i, int j, int k) + auto levi_civita = [](int i, int j, int k) { return -0.5 * (j-i) * (k-j) * (i-k); }; From 515d4c5b18d7ea4149489b2c478c97a2cc2c9558 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 9 Jun 2021 12:37:56 +0200 Subject: [PATCH 28/45] Incorporated upstream changes in core/io --- core/include/io/Configparser.hpp | 65 +- core/include/io/Configwriter.hpp | 63 +- core/include/io/Dataparser.hpp | 60 +- core/include/io/Datawriter.hpp | 64 +- core/include/io/Fileformat.hpp | 7 +- core/include/io/Filter_File_Handle.hpp | 250 ++--- core/include/io/IO.hpp | 42 +- core/include/io/OVF_File.hpp | 53 +- core/src/io/Configparser.cpp | 29 +- core/src/io/Configwriter.cpp | 584 ++++++----- core/src/io/Dataparser.cpp | 1297 ++++++++++++------------ core/src/io/Datawriter.cpp | 445 ++++---- core/src/io/Filter_File_Handle.cpp | 358 ++++--- core/src/io/IO.cpp | 142 ++- core/src/io/OVF_File.cpp | 243 ++--- 15 files changed, 1936 insertions(+), 1766 deletions(-) diff --git a/core/include/io/Configparser.hpp b/core/include/io/Configparser.hpp index 16c946f13..830c760f0 100644 --- a/core/include/io/Configparser.hpp +++ b/core/include/io/Configparser.hpp @@ -1,33 +1,54 @@ #pragma once -#ifndef IO_CONFIGPARSER_H -#define IO_CONFIGPARSER_H +#ifndef SPIRIT_IO_CONFIGPARSER_HPP +#define SPIRIT_IO_CONFIGPARSER_HPP #include -#include -#include -#include #include +#include #include +#include +#include +#include #include #include -#include namespace IO { - // Note that due to the modular structure of the input parsers, input may be given in one or in separate files. - // Input may be given incomplete. In this case a log entry is created and default values are used. - void Log_from_Config(const std::string configFile, bool force_quiet=false); - std::unique_ptr Spin_System_from_Config(const std::string configFile); - Data::Pinning Pinning_from_Config(const std::string configFile, int n_cell_atoms); - std::shared_ptr Geometry_from_Config(const std::string configFile); - std::unique_ptr Parameters_Method_LLG_from_Config(const std::string configFile); - std::unique_ptr Parameters_Method_MC_from_Config(const std::string configFile); - std::unique_ptr Parameters_Method_GNEB_from_Config(const std::string configFile); - std::unique_ptr Parameters_Method_EMA_from_Config(const std::string configFile); - std::unique_ptr Parameters_Method_MMF_from_Config(const std::string configFile); - std::unique_ptr Hamiltonian_from_Config(const std::string configFile, const std::shared_ptr geometry); - std::unique_ptr Hamiltonian_Heisenberg_from_Config(const std::string configFile, const std::shared_ptr geometry, std::string hamiltonian_type); - std::unique_ptr Hamiltonian_Micromagnetic_from_Config(const std::string configFile, const std::shared_ptr geometry); - std::unique_ptr Hamiltonian_Gaussian_from_Config(const std::string configFile, const std::shared_ptr geometry); -};// end namespace IO + +/* +Note that due to the modular structure of the input parsers, input may be given in one or in separate files. +Input may be given incomplete. In this case a log entry is created and default values are used. +*/ + +void Log_from_Config( const std::string configFile, bool force_quiet = false ); + +std::unique_ptr Spin_System_from_Config( const std::string configFile ); + +Data::Pinning Pinning_from_Config( const std::string configFile, int n_cell_atoms ); + +std::shared_ptr Geometry_from_Config( const std::string configFile ); + +std::unique_ptr Parameters_Method_LLG_from_Config( const std::string configFile ); + +std::unique_ptr Parameters_Method_MC_from_Config( const std::string configFile ); + +std::unique_ptr Parameters_Method_GNEB_from_Config( const std::string configFile ); + +std::unique_ptr Parameters_Method_EMA_from_Config( const std::string configFile ); + +std::unique_ptr Parameters_Method_MMF_from_Config( const std::string configFile ); + +std::unique_ptr +Hamiltonian_from_Config( const std::string configFile, const std::shared_ptr geometry ); + +std::unique_ptr Hamiltonian_Heisenberg_from_Config( + const std::string configFile, const std::shared_ptr geometry, std::string hamiltonian_type ); + +std::unique_ptr Hamiltonian_Micromagnetic_from_Config(const std::string configFile, const std::shared_ptr geometry); + +std::unique_ptr +Hamiltonian_Gaussian_from_Config( const std::string configFile, const std::shared_ptr geometry ); + +} // namespace IO + #endif \ No newline at end of file diff --git a/core/include/io/Configwriter.hpp b/core/include/io/Configwriter.hpp index 071974581..6e43817dc 100644 --- a/core/include/io/Configwriter.hpp +++ b/core/include/io/Configwriter.hpp @@ -1,31 +1,52 @@ #pragma once -#ifndef IO_CONFIGWRITER_H -#define IO_CONFIGWRITER_H +#ifndef SPIRIT_IO_CONFIGWRITER_HPP +#define SPIRIT_IO_CONFIGWRITER_HPP #include -#include -#include -#include #include +#include #include -#include +#include +#include #include +#include namespace IO { - void Folders_to_Config(const std::string configFile, - const std::shared_ptr parameters_llg, - const std::shared_ptr parameters_mc, - const std::shared_ptr parameters_gneb, - const std::shared_ptr parameters_mmf); - void Log_Levels_to_Config(const std::string configFile); - void Geometry_to_Config(const std::string configFile, const std::shared_ptr geometry); - void Parameters_Method_LLG_to_Config(const std::string configFile, const std::shared_ptr parameters); - void Parameters_Method_MC_to_Config(const std::string configFile, const std::shared_ptr parameters); - void Parameters_Method_GNEB_to_Config(const std::string configFile, const std::shared_ptr parameters); - void Parameters_Method_MMF_to_Config(const std::string configFile, const std::shared_ptr parameters); - void Hamiltonian_to_Config(const std::string configFile, const std::shared_ptr hamiltonian, const std::shared_ptr geometry); - void Hamiltonian_Heisenberg_to_Config(const std::string configFile, std::shared_ptr hamiltonian, const std::shared_ptr geometry); - void Hamiltonian_Gaussian_to_Config(const std::string configFile, const std::shared_ptr hamiltonian); -};// end namespace IO + +void Folders_to_Config( + const std::string configFile, const std::shared_ptr parameters_llg, + const std::shared_ptr parameters_mc, + const std::shared_ptr parameters_gneb, + const std::shared_ptr parameters_mmf ); + +void Log_Levels_to_Config( const std::string configFile ); + +void Geometry_to_Config( const std::string configFile, const std::shared_ptr geometry ); + +void Parameters_Method_LLG_to_Config( + const std::string configFile, const std::shared_ptr parameters ); + +void Parameters_Method_MC_to_Config( + const std::string configFile, const std::shared_ptr parameters ); + +void Parameters_Method_GNEB_to_Config( + const std::string configFile, const std::shared_ptr parameters ); + +void Parameters_Method_MMF_to_Config( + const std::string configFile, const std::shared_ptr parameters ); + +void Hamiltonian_to_Config( + const std::string configFile, const std::shared_ptr hamiltonian, + const std::shared_ptr geometry ); + +void Hamiltonian_Heisenberg_to_Config( + const std::string configFile, std::shared_ptr hamiltonian, + const std::shared_ptr geometry ); + +void Hamiltonian_Gaussian_to_Config( + const std::string configFile, const std::shared_ptr hamiltonian ); + +} // namespace IO + #endif \ No newline at end of file diff --git a/core/include/io/Dataparser.hpp b/core/include/io/Dataparser.hpp index fd8079094..42beaba7f 100644 --- a/core/include/io/Dataparser.hpp +++ b/core/include/io/Dataparser.hpp @@ -1,37 +1,43 @@ - #pragma once -#ifndef IO_DATAPARSER_H -#define IO_DATAPARSER_H +#ifndef SPIRIT_IO_DATAPARSER_HPP +#define SPIRIT_IO_DATAPARSER_HPP #include #include #include -#include #include #include +#include namespace IO { - void Read_NonOVF_Spin_Configuration( vectorfield& spins, Data::Geometry& geometry, - const int nos, const int idx_image_infile, - const std::string file ); - void Check_NonOVF_Chain_Configuration( std::shared_ptr chain, - const std::string file, int start_image_infile, - int end_image_infile, const int insert_idx, - int& noi_to_add, int& noi_to_read, const int idx_chain ); - void Anisotropy_from_File( const std::string anisotropyFile, - const std::shared_ptr geometry, int& n_indices, - intfield& anisotropy_index, scalarfield& anisotropy_magnitude, - vectorfield& anisotropy_normal ); - void Pairs_from_File( const std::string pairsFile, - const std::shared_ptr geometry, int& nop, - pairfield& exchange_pairs, scalarfield& exchange_magnitudes, - pairfield& dmi_pairs, scalarfield& dmi_magnitudes, - vectorfield& dmi_normals ); - void Quadruplets_from_File( const std::string quadrupletsFile, - const std::shared_ptr geometry, int& noq, - quadrupletfield& quadruplets, scalarfield& quadruplet_magnitudes ); - void Defects_from_File( const std::string defectsFile, int & n_defects, field & defect_sites, intfield & defect_types ); - void Pinned_from_File( const std::string pinnedFile, int & n_pinned, field & pinned_sites, vectorfield & pinned_spins ); -};// end namespace IO -#endif + +void Read_NonOVF_Spin_Configuration( + vectorfield & spins, Data::Geometry & geometry, const int nos, const int idx_image_infile, const std::string file ); + +void Check_NonOVF_Chain_Configuration( + std::shared_ptr chain, const std::string file, int start_image_infile, + int end_image_infile, const int insert_idx, int & noi_to_add, int & noi_to_read, const int idx_chain ); + +void Anisotropy_from_File( + const std::string anisotropyFile, const std::shared_ptr geometry, int & n_indices, + intfield & anisotropy_index, scalarfield & anisotropy_magnitude, vectorfield & anisotropy_normal ) noexcept; + +void Pairs_from_File( + const std::string pairsFile, const std::shared_ptr geometry, int & nop, pairfield & exchange_pairs, + scalarfield & exchange_magnitudes, pairfield & dmi_pairs, scalarfield & dmi_magnitudes, + vectorfield & dmi_normals ) noexcept; + +void Quadruplets_from_File( + const std::string quadrupletsFile, const std::shared_ptr geometry, int & noq, + quadrupletfield & quadruplets, scalarfield & quadruplet_magnitudes ) noexcept; + +void Defects_from_File( + const std::string defectsFile, int & n_defects, field & defect_sites, intfield & defect_types ) noexcept; + +void Pinned_from_File( + const std::string pinnedFile, int & n_pinned, field & pinned_sites, vectorfield & pinned_spins ) noexcept; + +} // namespace IO + +#endif \ No newline at end of file diff --git a/core/include/io/Datawriter.hpp b/core/include/io/Datawriter.hpp index c1fe6440b..7d528c435 100644 --- a/core/include/io/Datawriter.hpp +++ b/core/include/io/Datawriter.hpp @@ -1,6 +1,6 @@ #pragma once -#ifndef IO_DATAWRITER_H -#define IO_DATAWRITER_H +#ifndef SPIRIT_IO_DATAWRITER_HPP +#define SPIRIT_IO_DATAWRITER_HPP #include #include @@ -8,30 +8,36 @@ namespace IO { - // =========================== Saving Interactions ======================= - void Write_Neighbours_Exchange( const Data::Spin_System& system, const std::string filename ); - void Write_Neighbours_DMI( const Data::Spin_System& system, const std::string filename ); - - // =========================== Saving Energies =========================== - void Write_Energy_Header( const Data::Spin_System& s, const std::string filename, - std::vector firstcolumns={"iteration", "E_tot"}, - bool contributions=true, bool normalize_nos=true, - bool readability_toggle = true ); - // Appends the Energy of a spin system with energy contributions (without header) - void Append_Image_Energy( const Data::Spin_System& s, const int iteration, - const std::string filename, bool normalize_nos=true, - bool readability_toggle = true ); - // Save energy contributions of a spin system - void Write_Image_Energy( const Data::Spin_System& system, const std::string filename, - bool normalize_by_nos=true, bool readability_toggle = true ); - - // Saves Energies of all images with header and contributions - void Write_Chain_Energies( const Data::Spin_System_Chain& c, const int iteration, - const std::string filename, bool normalize_nos=true, - bool readability_toggle = true ); - // Saves the Energies interpolated by the GNEB method - void Write_Chain_Energies_Interpolated( const Data::Spin_System_Chain& c, - const std::string filename, bool normalize_nos=true, - bool readability_toggle = true); -}; -#endif + +void Write_Neighbours_Exchange( const Data::Spin_System & system, const std::string filename ); + +void Write_Neighbours_DMI( const Data::Spin_System & system, const std::string filename ); + +void Write_Energy_Header( + const Data::Spin_System & s, const std::string filename, + std::vector firstcolumns = { "iteration", "E_tot" }, bool contributions = true, + bool normalize_nos = true, bool readability_toggle = true ); + +// Appends the Energy of a spin system with energy contributions (without header) +void Append_Image_Energy( + const Data::Spin_System & s, const int iteration, const std::string filename, bool normalize_nos = true, + bool readability_toggle = true ); + +// Save energy contributions of a spin system +void Write_Image_Energy( + const Data::Spin_System & system, const std::string filename, bool normalize_by_nos = true, + bool readability_toggle = true ); + +// Saves Energies of all images with header and contributions +void Write_Chain_Energies( + const Data::Spin_System_Chain & c, const int iteration, const std::string filename, bool normalize_nos = true, + bool readability_toggle = true ); + +// Saves the Energies interpolated by the GNEB method +void Write_Chain_Energies_Interpolated( + const Data::Spin_System_Chain & c, const std::string filename, bool normalize_nos = true, + bool readability_toggle = true ); + +} // namespace IO + +#endif \ No newline at end of file diff --git a/core/include/io/Fileformat.hpp b/core/include/io/Fileformat.hpp index c8db6f35a..758633792 100644 --- a/core/include/io/Fileformat.hpp +++ b/core/include/io/Fileformat.hpp @@ -10,10 +10,9 @@ namespace IO { -// A variety of supported file formats for vector fields +// The supported OOMF Vector Field (OVF) file formats enum class VF_FileFormat { - // OOMF Vector Field file format OVF_BIN = IO_Fileformat_OVF_bin, OVF_BIN4 = IO_Fileformat_OVF_bin4, OVF_BIN8 = IO_Fileformat_OVF_bin8, @@ -37,6 +36,6 @@ inline std::string str( IO::VF_FileFormat format ) return "unknown"; } -}; +} // namespace IO -#endif +#endif \ No newline at end of file diff --git a/core/include/io/Filter_File_Handle.hpp b/core/include/io/Filter_File_Handle.hpp index 544d88265..600c13b64 100644 --- a/core/include/io/Filter_File_Handle.hpp +++ b/core/include/io/Filter_File_Handle.hpp @@ -1,147 +1,147 @@ #pragma once -#ifndef IO_FILTERFILEHANDLE_H -#define IO_FILTERFILEHANDLE_H +#ifndef SPIRIT_IO_FILTERFILEHANDLE_HPP +#define SPIRIT_IO_FILTERFILEHANDLE_HPP -#include -#include -#include -#include -#include -#include - -#include -#include #include #include #include +#include #include #include +#include +#include +#include +#include + namespace IO { - class Filter_File_Handle - { - private: - std::size_t found; - std::string line; - const std::string comment_tag; - std::string dump; - // Beggining and end of file stream indicator - std::ios::pos_type position_file_beg; - std::ios::pos_type position_file_end; - // Start and stop of file stream indicator - std::ios::pos_type position_start; - std::ios::pos_type position_stop; - int n_lines; - int n_comment_lines; - public: - std::string filename; - std::unique_ptr myfile; - std::istringstream iss; - // Constructs a Filter_File_Handle with string filename - Filter_File_Handle( const std::string& filename, const std::string comment_tag = "#" ); - // Destructor - ~Filter_File_Handle(); +class Filter_File_Handle +{ +private: + std::size_t found; + std::string line; + const std::string comment_tag; + std::string dump; + // Beggining and end of file stream indicator + std::ios::pos_type position_file_beg; + std::ios::pos_type position_file_end; + // Start and stop of file stream indicator + std::ios::pos_type position_start; + std::ios::pos_type position_stop; + int n_lines; + int n_comment_lines; - // Get the position of the file stream indicator - std::ios::pos_type GetPosition( std::ios::seekdir dir = std::ios::cur ); - // Set limits in the file stream indicator - void SetLimits( const std::ios::pos_type beg, const std::ios::pos_type end ); - // Reset the limits of the file stream indicator - void ResetLimits(); - // Reads next line of file into the handle (false -> end-of-file) - bool GetLine_Handle( const std::string str_to_remove = "" ); - // Reads the next line of file into the handle and into the iss - bool GetLine( const std::string str_to_remove = "" ); - // Reset the file stream to the start of the file - void ResetStream(); - // Tries to find s in the current file and if found outputs the line into internal iss - bool Find(const std::string & s, bool ignore_case=true); - // Tries to find s in the current line and if found outputs into internal iss - bool Find_in_Line(const std::string & s, bool ignore_case=true); - // Removes a set of chars from a string - void Remove_Chars_From_String(std::string &str, const char* charsToRemove); - // Removes comments from a string - bool Remove_Comments_From_String( std::string &str ); - // Read a string (separeated by whitespaces) into var. Capitalization is ignored. - void Read_String( std::string& var, std::string keyword, bool log_notfound = true ); - // Count the words of a string - int Count_Words( const std::string& str ); - // Returns the number of lines which are not starting with a comment - int Get_N_Non_Comment_Lines(); +public: + std::string filename; + std::unique_ptr myfile; + std::istringstream iss; - // Reads a single variable into var, with optional logging in case of failure. - // - //// NOTE: Capitalization is ignored (expected). - // - template - bool Read_Single( T & var, std::string keyword, bool log_notfound = true ) - try - { - if( Find(keyword) ) - { - iss >> var; - return true; - } - else if (log_notfound) - Log( Utility::Log_Level::Warning, Utility::Log_Sender::IO, fmt::format( - "Keyword '{}' not found. Using Default: {}", keyword, var) ); - return false; - } - catch (...) - { - spirit_handle_exception_core(fmt::format("Failed to read single variable \"{}\".", keyword)); - return false; - } + // Constructs a Filter_File_Handle with string filename + Filter_File_Handle( const std::string & filename, const std::string comment_tag = "#" ); + // Destructor + ~Filter_File_Handle(); - // Require a single field. In case that it is not found an execption is thrown. - // - //// NOTE: Capitalization is ignored (expected). - // - template - void Require_Single( T& var, std::string keyword ) - { - if( !Read_Single( var, keyword, false ) ) - { - spirit_throw(Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, - fmt::format("Required keyword \"{}\" not found.", keyword)); - } - } + // Get the position of the file stream indicator + std::ios::pos_type GetPosition( std::ios::seekdir dir = std::ios::cur ); + // Set limits in the file stream indicator + void SetLimits( const std::ios::pos_type beg, const std::ios::pos_type end ); + // Reset the limits of the file stream indicator + void ResetLimits(); + // Reads next line of file into the handle (false -> end-of-file) + bool GetLine_Handle( const std::string str_to_remove = "" ); + // Reads the next line of file into the handle and into the iss + bool GetLine( const std::string str_to_remove = "" ); + // Reset the file stream to the start of the file + void ResetStream(); + // Tries to find s in the current file and if found outputs the line into internal iss + bool Find( const std::string & s, bool ignore_case = true ); + // Tries to find s in the current line and if found outputs into internal iss + bool Find_in_Line( const std::string & s, bool ignore_case = true ); + // Removes a set of chars from a string + void Remove_Chars_From_String( std::string & str, const char * charsToRemove ); + // Removes comments from a string + bool Remove_Comments_From_String( std::string & str ); + // Read a string (separeated by whitespaces) into var. Capitalization is ignored. + void Read_String( std::string & var, std::string keyword, bool log_notfound = true ); + // Count the words of a string + int Count_Words( const std::string & str ); + // Returns the number of lines which are not starting with a comment + int Get_N_Non_Comment_Lines(); - // Reads a Vector3 into var, with optional logging in case of failure. - void Read_Vector3(Vector3 & var, std::string keyword, bool log_notfound = true) - try - { - if( Find(keyword) ) - iss >> var[0] >> var[1] >> var[2]; - else if (log_notfound) - Log( Utility::Log_Level::Warning, Utility::Log_Sender::IO, fmt::format( - "Keyword '{}' not found. Using Default: {}", keyword, var.transpose()) ); - } - catch (...) + // Reads a single variable into var, with optional logging in case of failure. + // + //// NOTE: Capitalization is ignored (expected). + // + template + bool Read_Single( T & var, std::string keyword, bool log_notfound = true ) + try + { + if( Find( keyword ) ) { - spirit_handle_exception_core(fmt::format("Failed to read Vector3 \"{}\".", keyword)); + iss >> var; + return true; } + else if( log_notfound ) + Log( Utility::Log_Level::Warning, Utility::Log_Sender::IO, + fmt::format( "Keyword '{}' not found. Using Default: {}", keyword, var ) ); + return false; + } + catch( ... ) + { + spirit_handle_exception_core( fmt::format( "Failed to read single variable \"{}\".", keyword ) ); + return false; + } - // Reads a 3-component object, with optional logging in case of failure - template - void Read_3Vector( T & var, std::string keyword, bool log_notfound = true ) - try - { - if( Find(keyword) ) - iss >> var[0] >> var[1] >> var[2]; - else if (log_notfound) - Log( Utility::Log_Level::Warning, Utility::Log_Sender::IO, fmt::format( - "Keyword '{}' not found. Using Default: ({} {} {})", - keyword, var[0], var[1], var[2] ) ); - } - catch (...) + // Require a single field. In case that it is not found an execption is thrown. + // + //// NOTE: Capitalization is ignored (expected). + // + template + void Require_Single( T & var, std::string keyword ) + { + if( !Read_Single( var, keyword, false ) ) { - spirit_handle_exception_core(fmt::format("Failed to read 3Vector \"{}\".", keyword)); + spirit_throw( + Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, + fmt::format( "Required keyword \"{}\" not found.", keyword ) ); } - };//end class FilterFileHandle -}// end namespace IO + } + + // Reads a Vector3 into var, with optional logging in case of failure. + void Read_Vector3( Vector3 & var, std::string keyword, bool log_notfound = true ) + try + { + if( Find( keyword ) ) + iss >> var[0] >> var[1] >> var[2]; + else if( log_notfound ) + Log( Utility::Log_Level::Warning, Utility::Log_Sender::IO, + fmt::format( "Keyword '{}' not found. Using Default: {}", keyword, var.transpose() ) ); + } + catch( ... ) + { + spirit_handle_exception_core( fmt::format( "Failed to read Vector3 \"{}\".", keyword ) ); + } + + // Reads a 3-component object, with optional logging in case of failure + template + void Read_3Vector( T & var, std::string keyword, bool log_notfound = true ) + try + { + if( Find( keyword ) ) + iss >> var[0] >> var[1] >> var[2]; + else if( log_notfound ) + Log( Utility::Log_Level::Warning, Utility::Log_Sender::IO, + fmt::format( "Keyword '{}' not found. Using Default: ({} {} {})", keyword, var[0], var[1], var[2] ) ); + } + catch( ... ) + { + spirit_handle_exception_core( fmt::format( "Failed to read 3Vector \"{}\".", keyword ) ); + } +}; + +} // namespace IO -#endif +#endif \ No newline at end of file diff --git a/core/include/io/IO.hpp b/core/include/io/IO.hpp index 4c21d0b57..469dd513f 100644 --- a/core/include/io/IO.hpp +++ b/core/include/io/IO.hpp @@ -1,30 +1,32 @@ #pragma once -#ifndef IO_H -#define IO_H +#ifndef SPIRIT_IO_IO_HPP +#define SPIRIT_IO_IO_HPP + +#include "Spirit_Defines.h" +#include #include #include -#include -#include "Spirit_Defines.h" - namespace IO { - // ------ Saving Helpers -------------------------------------------- - // Creates a new thread with String_to_File, which is immediately detached - void Dump_to_File(const std::string text, const std::string name); - // Takes a vector of strings of size "no" and dumps those into a file asynchronously - void Dump_to_File(const std::vector text, const std::string name, const int no); - - // Dumps the contents of the strings in text vector into file "name" - void Strings_to_File(const std::vector text, const std::string name, const int no); - // Dumps the contents of the string 'text' into a file - void String_to_File(const std::string text, const std::string name); - // Appends the contents of the string 'text' onto a file - void Append_String_to_File(const std::string text, const std::string name); - // ------------------------------------------------------------------ - -};// end namespace IO + +// Creates a new thread with String_to_File, which is immediately detached +void Dump_to_File( const std::string text, const std::string name ); + +// Takes a vector of strings of size "no" and dumps those into a file asynchronously +void Dump_to_File( const std::vector text, const std::string name, const int no ); + +// Dumps the contents of the strings in text vector into file "name" +void Strings_to_File( const std::vector text, const std::string name, const int no ); + +// Dumps the contents of the string 'text' into a file +void String_to_File( const std::string text, const std::string name ); + +// Appends the contents of the string 'text' onto a file +void Append_String_to_File( const std::string text, const std::string name ); + +} // namespace IO #endif diff --git a/core/include/io/OVF_File.hpp b/core/include/io/OVF_File.hpp index 4c9a6011c..6ef784ed5 100644 --- a/core/include/io/OVF_File.hpp +++ b/core/include/io/OVF_File.hpp @@ -1,6 +1,6 @@ #pragma once -#ifndef IO_OVFFILE_H -#define IO_OVFFILE_H +#ifndef SPIRIT_IO_OVFFILE_HPP +#define SPIRIT_IO_OVFFILE_HPP #include @@ -8,29 +8,30 @@ namespace IO { - struct OVF_Segment : ovf_segment - { - OVF_Segment(); - OVF_Segment(const Data::Spin_System & system); - ~OVF_Segment(); - }; - - struct OVF_File : ovf_file - { - OVF_File(const std::string & filename, bool should_exist=false); - ~OVF_File(); - - const char * latest_message(); - - void read_segment_header(int index, ovf_segment & segment); - void read_segment_data(int index, const ovf_segment & segment, float * data); - void read_segment_data(int index, const ovf_segment & segment, double * data); - - void write_segment(const ovf_segment & segment, float * data, int format=OVF_FORMAT_BIN); - void write_segment(const ovf_segment & segment, double * data, int format=OVF_FORMAT_BIN); - void append_segment(const ovf_segment & segment, float * data, int format=OVF_FORMAT_BIN); - void append_segment(const ovf_segment & segment, double * data, int format=OVF_FORMAT_BIN); - }; -} + +struct OVF_Segment : ::ovf_segment +{ + OVF_Segment(); + OVF_Segment( const Data::Spin_System & system ); + ~OVF_Segment(); +}; + +struct OVF_File : ::ovf_file +{ + OVF_File( const std::string & filename, bool should_exist = false ); + ~OVF_File(); + + const char * latest_message(); + + void read_segment_header( int index, ::ovf_segment & segment ); + void read_segment_data( int index, const ::ovf_segment & segment, float * data ); + void read_segment_data( int index, const ::ovf_segment & segment, double * data ); + void write_segment( const ::ovf_segment & segment, float * data, int format = OVF_FORMAT_BIN ); + void write_segment( const ::ovf_segment & segment, double * data, int format = OVF_FORMAT_BIN ); + void append_segment( const ::ovf_segment & segment, float * data, int format = OVF_FORMAT_BIN ); + void append_segment( const ::ovf_segment & segment, double * data, int format = OVF_FORMAT_BIN ); +}; + +} // namespace IO #endif \ No newline at end of file diff --git a/core/src/io/Configparser.cpp b/core/src/io/Configparser.cpp index 8360b6a30..4fef4cbd1 100644 --- a/core/src/io/Configparser.cpp +++ b/core/src/io/Configparser.cpp @@ -6,7 +6,8 @@ #include #include - +#include +#include #include #include @@ -16,14 +17,15 @@ #include #include - -#include -#include +// using namespace Utility; +using Utility::Log_Level; +using Utility::Log_Sender; using namespace Utility; namespace IO { + void Log_from_Config( const std::string configFile, bool force_quiet ) { // Verbosity and Reject Level are read as integers @@ -673,7 +675,7 @@ Data::Pinning Pinning_from_Config( const std::string configFile, int n_cell_atom } Log( Log_Level::Parameter, Log_Sender::IO, "Pinning: read" ); return pinning; -#else // SPIRIT_ENABLE_PINNING +#else // SPIRIT_ENABLE_PINNING Log( Log_Level::Parameter, Log_Sender::IO, "Pinning is disabled" ); if( configFile != "" ) { @@ -703,6 +705,7 @@ std::unique_ptr Parameters_Method_LLG_from_Config( // PRNG Seed std::srand( (unsigned int)std::time( 0 ) ); parameters->rng_seed = std::rand(); + parameters->prng = std::mt19937( parameters->rng_seed ); // Maximum wall time std::string str_max_walltime = "0"; @@ -738,6 +741,7 @@ std::unique_ptr Parameters_Method_LLG_from_Config( myfile.Read_Single( str_max_walltime, "llg_max_walltime" ); parameters->max_walltime_sec = (long int)Utility::Timing::DurationFromString( str_max_walltime ).count(); myfile.Read_Single( parameters->rng_seed, "llg_seed" ); + parameters->prng = std::mt19937( parameters->rng_seed ); myfile.Read_Single( parameters->n_iterations, "llg_n_iterations" ); myfile.Read_Single( parameters->n_iterations_log, "llg_n_iterations_log" ); myfile.Read_Single( parameters->dt, "llg_dt" ); @@ -894,6 +898,7 @@ std::unique_ptr Parameters_Method_MC_from_Config( co // PRNG Seed std::srand( (unsigned int)std::time( 0 ) ); parameters->rng_seed = std::rand(); + parameters->prng = std::mt19937( parameters->rng_seed ); // Maximum wall time std::string str_max_walltime = "0"; @@ -929,6 +934,7 @@ std::unique_ptr Parameters_Method_MC_from_Config( co myfile.Read_Single( str_max_walltime, "mc_max_walltime" ); parameters->max_walltime_sec = (long int)Utility::Timing::DurationFromString( str_max_walltime ).count(); myfile.Read_Single( parameters->rng_seed, "mc_seed" ); + parameters->prng = std::mt19937( parameters->rng_seed ); myfile.Read_Single( parameters->n_iterations, "mc_n_iterations" ); myfile.Read_Single( parameters->n_iterations_log, "mc_n_iterations_log" ); myfile.Read_Single( parameters->temperature, "mc_temperature" ); @@ -1174,15 +1180,23 @@ Hamiltonian_from_Config( const std::string configFile, std::shared_ptr Hamiltonian_Heisenberg_from_Conf // else //{ // Log(Log_Level::Warning, Log_Sender::IO, "Hamiltonian_Heisenberg: Default Interaction pairs have not - //been implemented yet."); throw Exception::System_not_Initialized; + // been implemented yet."); throw Exception::System_not_Initialized; // // Not implemented! //} } // end try @@ -1846,4 +1860,5 @@ Hamiltonian_Gaussian_from_Config( const std::string configFile, std::shared_ptr< Log( Log_Level::Debug, Log_Sender::IO, "Hamiltonian_Gaussian: built" ); return hamiltonian; } -} // end namespace IO + +} // namespace IO \ No newline at end of file diff --git a/core/src/io/Configwriter.cpp b/core/src/io/Configwriter.cpp index f428c37f1..e7a8f454b 100644 --- a/core/src/io/Configwriter.cpp +++ b/core/src/io/Configwriter.cpp @@ -1,320 +1,358 @@ -#include -#include +#include #include -#include +#include +#include #include -#include #include - -#include -#include -#include -#include +#include #include #include -using namespace Utility; +#include +#include +#include +#include namespace IO { - void Folders_to_Config(const std::string configFile, - const std::shared_ptr parameters_llg, - const std::shared_ptr parameters_mc, - const std::shared_ptr parameters_gneb, - const std::shared_ptr parameters_mmf) - { - std::string config = ""; - config += "################# Output Folders #################\n"; - config += "output_file_tag " + Log.file_tag + "\n"; - config += "log_output_folder " + Log.output_folder + "\n"; - config += "llg_output_folder " + parameters_llg->output_folder + "\n"; - config += "mc_output_folder " + parameters_mc->output_folder + "\n"; - config += "gneb_output_folder " + parameters_gneb->output_folder + "\n"; - config += "mmf_output_folder " + parameters_mmf->output_folder + "\n"; - config += "############### End Output Folders ###############"; - Append_String_to_File(config, configFile); - }// End Folders_to_Config +void Folders_to_Config( + const std::string configFile, const std::shared_ptr parameters_llg, + const std::shared_ptr parameters_mc, + const std::shared_ptr parameters_gneb, + const std::shared_ptr parameters_mmf ) +{ + std::string config = ""; + config += "################# Output Folders #################\n"; + config += "output_file_tag " + Log.file_tag + "\n"; + config += "log_output_folder " + Log.output_folder + "\n"; + config += "llg_output_folder " + parameters_llg->output_folder + "\n"; + config += "mc_output_folder " + parameters_mc->output_folder + "\n"; + config += "gneb_output_folder " + parameters_gneb->output_folder + "\n"; + config += "mmf_output_folder " + parameters_mmf->output_folder + "\n"; + config += "############### End Output Folders ###############"; + Append_String_to_File( config, configFile ); +} - void Log_Levels_to_Config(const std::string configFile) - { - std::string config = ""; - config += "############### Logging Parameters ###############\n"; - config += fmt::format("{:<22} {}\n", "log_to_file", (int)Log.messages_to_file); - config += fmt::format("{:<22} {}\n", "log_file_level", (int)Log.level_file); - config += fmt::format("{:<22} {}\n", "log_to_console", (int)Log.messages_to_console); - config += fmt::format("{:<22} {}\n", "log_console_level", (int)Log.level_console); - config += fmt::format("{:<22} {}\n", "log_input_save_initial", (int)Log.save_input_initial); - config += fmt::format("{:<22} {}\n", "log_input_save_final", (int)Log.save_input_final); - config += "############# End Logging Parameters #############"; - Append_String_to_File(config, configFile); - }// End Log_Levels_to_Config +void Log_Levels_to_Config( const std::string configFile ) +{ + std::string config = ""; + config += "############### Logging Parameters ###############\n"; + config += fmt::format( "{:<22} {}\n", "log_to_file", (int)Log.messages_to_file ); + config += fmt::format( "{:<22} {}\n", "log_file_level", (int)Log.level_file ); + config += fmt::format( "{:<22} {}\n", "log_to_console", (int)Log.messages_to_console ); + config += fmt::format( "{:<22} {}\n", "log_console_level", (int)Log.level_console ); + config += fmt::format( "{:<22} {}\n", "log_input_save_initial", (int)Log.save_input_initial ); + config += fmt::format( "{:<22} {}\n", "log_input_save_final", (int)Log.save_input_final ); + config += "############# End Logging Parameters #############"; + Append_String_to_File( config, configFile ); +} +void Geometry_to_Config( const std::string configFile, const std::shared_ptr geometry ) +{ + // TODO: this needs to be updated! + std::string config = ""; + config += "#################### Geometry ####################\n"; - void Geometry_to_Config(const std::string configFile, const std::shared_ptr geometry) + // Bravais lattice/vectors + if( geometry->classifier == Data::BravaisLatticeType::SC ) + config += "bravais_lattice sc\n"; + else if( geometry->classifier == Data::BravaisLatticeType::FCC ) + config += "bravais_lattice fcc\n"; + else if( geometry->classifier == Data::BravaisLatticeType::BCC ) + config += "bravais_lattice bcc\n"; + else if( geometry->classifier == Data::BravaisLatticeType::Hex2D ) + config += "bravais_lattice hex2d120\n"; + else { - // TODO: this needs to be updated! - std::string config = ""; - config += "#################### Geometry ####################\n"; + config += "bravais_vectors\n"; + config += fmt::format( + "{0}\n{1}\n{2}\n", geometry->bravais_vectors[0].transpose(), geometry->bravais_vectors[1].transpose(), + geometry->bravais_vectors[2].transpose() ); + } - // Bravais lattice/vectors - if (geometry->classifier == Data::BravaisLatticeType::SC) - config += "bravais_lattice sc\n"; - else if (geometry->classifier == Data::BravaisLatticeType::FCC) - config += "bravais_lattice fcc\n"; - else if (geometry->classifier == Data::BravaisLatticeType::BCC) - config += "bravais_lattice bcc\n"; - else if (geometry->classifier == Data::BravaisLatticeType::Hex2D) - config += "bravais_lattice hex2d120\n"; - else - { - config += "bravais_vectors\n"; - config += fmt::format("{0}\n{1}\n{2}\n", geometry->bravais_vectors[0].transpose(), geometry->bravais_vectors[1].transpose(), geometry->bravais_vectors[2].transpose()); - } - - // Number of cells - config += fmt::format("n_basis_cells {} {} {}\n", geometry->n_cells[0], geometry->n_cells[1], geometry->n_cells[2]); + // Number of cells + config + += fmt::format( "n_basis_cells {} {} {}\n", geometry->n_cells[0], geometry->n_cells[1], geometry->n_cells[2] ); - // Optionally basis - if (geometry->n_cell_atoms > 1) + // Optionally basis + if( geometry->n_cell_atoms > 1 ) + { + config += "basis\n"; + config += fmt::format( "{}\n", geometry->n_cell_atoms ); + for( int i = 0; i < geometry->n_cell_atoms; ++i ) { - config += "basis\n"; - config += fmt::format("{}\n", geometry->n_cell_atoms); - for (int i=0; in_cell_atoms; ++i) - { - config += fmt::format("{}\n", geometry->cell_atoms[i].transpose()); - } + config += fmt::format( "{}\n", geometry->cell_atoms[i].transpose() ); } + } - // Magnetic moment - if( !geometry->cell_composition.disordered ) - { - config += "mu_s "; - for (int i=0; in_cell_atoms; ++i) - config += fmt::format(" {}", geometry->mu_s[i]); - config += "\n"; - } - else - { - auto& iatom = geometry->cell_composition.iatom; - auto& atom_type = geometry->cell_composition.atom_type; - auto& mu_s = geometry->cell_composition.mu_s; - auto& concentration = geometry->cell_composition.concentration; - config += fmt::format("atom_types {}\n", iatom.size()); - for (int i=0; icell_composition.disordered ) + { + config += "mu_s "; + for( int i = 0; i < geometry->n_cell_atoms; ++i ) + config += fmt::format( " {}", geometry->mu_s[i] ); + config += "\n"; + } + else + { + auto & iatom = geometry->cell_composition.iatom; + auto & atom_type = geometry->cell_composition.atom_type; + auto & mu_s = geometry->cell_composition.mu_s; + auto & concentration = geometry->cell_composition.concentration; + config += fmt::format( "atom_types {}\n", iatom.size() ); + for( int i = 0; i < iatom.size(); ++i ) + config += fmt::format( "{} {} {} {}\n", iatom[i], atom_type[i], mu_s[i], concentration[i] ); + } - // Optionally lattice constant - if (std::abs(geometry->lattice_constant-1) > 1e-6) - config += fmt::format("lattice_constant {}\n", geometry->lattice_constant); + // Optionally lattice constant + if( std::abs( geometry->lattice_constant - 1 ) > 1e-6 ) + config += fmt::format( "lattice_constant {}\n", geometry->lattice_constant ); - config += "################## End Geometry ##################"; - Append_String_to_File(config, configFile); - }// end Geometry_to_Config + config += "################## End Geometry ##################"; + Append_String_to_File( config, configFile ); +} +void Parameters_Method_LLG_to_Config( + const std::string configFile, const std::shared_ptr parameters ) +{ + std::string config = ""; + config += "################# LLG Parameters #################\n"; + config += fmt::format( "{:<35} {:d}\n", "llg_output_any", parameters->output_any ); + config += fmt::format( "{:<35} {:d}\n", "llg_output_initial", parameters->output_initial ); + config += fmt::format( "{:<35} {:d}\n", "llg_output_final", parameters->output_final ); + config += fmt::format( "{:<35} {:d}\n", "llg_output_energy_step", parameters->output_energy_step ); + config += fmt::format( "{:<35} {:d}\n", "llg_output_energy_archive", parameters->output_energy_archive ); + config + += fmt::format( "{:<35} {:d}\n", "llg_output_energy_spin_resolved", parameters->output_energy_spin_resolved ); + config += fmt::format( + "{:<35} {:d}\n", "llg_output_energy_divide_by_nspins", parameters->output_energy_divide_by_nspins ); + config += fmt::format( "{:<35} {:d}\n", "llg_output_configuration_step", parameters->output_configuration_step ); + config + += fmt::format( "{:<35} {:d}\n", "llg_output_configuration_archive", parameters->output_configuration_archive ); + config += fmt::format( "{:<35} {:e}\n", "llg_force_convergence", parameters->force_convergence ); + config += fmt::format( "{:<35} {}\n", "llg_n_iterations", parameters->n_iterations ); + config += fmt::format( "{:<35} {}\n", "llg_n_iterations_log", parameters->n_iterations_log ); + config += fmt::format( "{:<35} {}\n", "llg_seed", parameters->rng_seed ); + config += fmt::format( "{:<35} {}\n", "llg_temperature", parameters->temperature ); + config += fmt::format( "{:<35} {}\n", "llg_damping", parameters->damping ); + config += fmt::format( + "{:<35} {}\n", "llg_dt", parameters->dt * Utility::Constants::mu_B / Utility::Constants::gamma ); + config += fmt::format( "{:<35} {}\n", "llg_stt_magnitude", parameters->stt_magnitude ); + config + += fmt::format( "{:<35} {}\n", "llg_stt_polarisation_normal", parameters->stt_polarisation_normal.transpose() ); + config += "############### End LLG Parameters ###############"; + Append_String_to_File( config, configFile ); +} - void Parameters_Method_LLG_to_Config(const std::string configFile, const std::shared_ptr parameters) - { - std::string config = ""; - config += "################# LLG Parameters #################\n"; - config += fmt::format("{:<35} {:d}\n", "llg_output_any", parameters->output_any); - config += fmt::format("{:<35} {:d}\n", "llg_output_initial", parameters->output_initial); - config += fmt::format("{:<35} {:d}\n", "llg_output_final", parameters->output_final); - config += fmt::format("{:<35} {:d}\n", "llg_output_energy_step", parameters->output_energy_step); - config += fmt::format("{:<35} {:d}\n", "llg_output_energy_archive", parameters->output_energy_archive); - config += fmt::format("{:<35} {:d}\n", "llg_output_energy_spin_resolved", parameters->output_energy_spin_resolved); - config += fmt::format("{:<35} {:d}\n", "llg_output_energy_divide_by_nspins", parameters->output_energy_divide_by_nspins); - config += fmt::format("{:<35} {:d}\n", "llg_output_configuration_step", parameters->output_configuration_step); - config += fmt::format("{:<35} {:d}\n", "llg_output_configuration_archive", parameters->output_configuration_archive); - config += fmt::format("{:<35} {:e}\n", "llg_force_convergence", parameters->force_convergence); - config += fmt::format("{:<35} {}\n", "llg_n_iterations", parameters->n_iterations); - config += fmt::format("{:<35} {}\n", "llg_n_iterations_log", parameters->n_iterations_log); - config += fmt::format("{:<35} {}\n", "llg_seed", parameters->rng_seed); - config += fmt::format("{:<35} {}\n", "llg_temperature", parameters->temperature); - config += fmt::format("{:<35} {}\n", "llg_damping", parameters->damping); - config += fmt::format("{:<35} {}\n", "llg_dt", parameters->dt/std::pow(10, -12) * Constants::mu_B/1.760859644/std::pow(10, 11)); - config += fmt::format("{:<35} {}\n", "llg_stt_magnitude", parameters->stt_magnitude); - config += fmt::format("{:<35} {}\n", "llg_stt_polarisation_normal", parameters->stt_polarisation_normal.transpose()); - config += "############### End LLG Parameters ###############"; - Append_String_to_File(config, configFile); - }// end Parameters_Method_LLG_to_Config +void Parameters_Method_MC_to_Config( + const std::string configFile, const std::shared_ptr parameters ) +{ + std::string config = ""; + config += "################# MC Parameters ##################\n"; + config += fmt::format( "{:<35} {:d}\n", "mc_output_any", parameters->output_any ); + config += fmt::format( "{:<35} {:d}\n", "mc_output_initial", parameters->output_initial ); + config += fmt::format( "{:<35} {:d}\n", "mc_output_final", parameters->output_final ); + config += fmt::format( "{:<35} {:d}\n", "mc_output_energy_step", parameters->output_energy_step ); + config += fmt::format( "{:<35} {:d}\n", "mc_output_energy_archive", parameters->output_energy_archive ); + config += fmt::format( "{:<35} {:d}\n", "mc_output_energy_spin_resolved", parameters->output_energy_spin_resolved ); + config += fmt::format( + "{:<35} {:d}\n", "mc_output_energy_divide_by_nspins", parameters->output_energy_divide_by_nspins ); + config += fmt::format( "{:<35} {:d}\n", "mc_output_configuration_step", parameters->output_configuration_step ); + config + += fmt::format( "{:<35} {:d}\n", "mc_output_configuration_archive", parameters->output_configuration_archive ); + config += fmt::format( "{:<35} {}\n", "mc_n_iterations", parameters->n_iterations ); + config += fmt::format( "{:<35} {}\n", "mc_n_iterations_log", parameters->n_iterations_log ); + config += fmt::format( "{:<35} {}\n", "mc_seed", parameters->rng_seed ); + config += fmt::format( "{:<35} {}\n", "mc_temperature", parameters->temperature ); + config += fmt::format( "{:<35} {}\n", "mc_acceptance_ratio", parameters->acceptance_ratio_target ); + config += "############### End MC Parameters ################"; + Append_String_to_File( config, configFile ); +} - void Parameters_Method_MC_to_Config(const std::string configFile, const std::shared_ptr parameters) - { - std::string config = ""; - config += "################# MC Parameters ##################\n"; - config += fmt::format("{:<35} {:d}\n", "mc_output_any", parameters->output_any); - config += fmt::format("{:<35} {:d}\n", "mc_output_initial", parameters->output_initial); - config += fmt::format("{:<35} {:d}\n", "mc_output_final", parameters->output_final); - config += fmt::format("{:<35} {:d}\n", "mc_output_energy_step", parameters->output_energy_step); - config += fmt::format("{:<35} {:d}\n", "mc_output_energy_archive", parameters->output_energy_archive); - config += fmt::format("{:<35} {:d}\n", "mc_output_energy_spin_resolved", parameters->output_energy_spin_resolved); - config += fmt::format("{:<35} {:d}\n", "mc_output_energy_divide_by_nspins", parameters->output_energy_divide_by_nspins); - config += fmt::format("{:<35} {:d}\n", "mc_output_configuration_step", parameters->output_configuration_step); - config += fmt::format("{:<35} {:d}\n", "mc_output_configuration_archive", parameters->output_configuration_archive); - config += fmt::format("{:<35} {}\n", "mc_n_iterations", parameters->n_iterations); - config += fmt::format("{:<35} {}\n", "mc_n_iterations_log", parameters->n_iterations_log); - config += fmt::format("{:<35} {}\n", "mc_seed", parameters->rng_seed); - config += fmt::format("{:<35} {}\n", "mc_temperature", parameters->temperature); - config += fmt::format("{:<35} {}\n", "mc_acceptance_ratio", parameters->acceptance_ratio_target); - config += "############### End MC Parameters ################"; - Append_String_to_File(config, configFile); - }// end Parameters_Method_MC_to_Config +void Parameters_Method_GNEB_to_Config( + const std::string configFile, const std::shared_ptr parameters ) +{ + std::string config = ""; + config += "################# GNEB Parameters ################\n"; + config += fmt::format( "{:<38} {:d}\n", "gneb_output_any", parameters->output_any ); + config += fmt::format( "{:<38} {:d}\n", "gneb_output_initial", parameters->output_initial ); + config += fmt::format( "{:<38} {:d}\n", "gneb_output_final", parameters->output_final ); + config += fmt::format( "{:<38} {:d}\n", "gneb_output_energies_step", parameters->output_energies_step ); + config += fmt::format( + "{:<38} {:d}\n", "gneb_output_energies_interpolated", parameters->output_energies_interpolated ); + config += fmt::format( + "{:<38} {:d}\n", "gneb_output_energies_divide_by_nspins", parameters->output_energies_divide_by_nspins ); + config += fmt::format( "{:<38} {:d}\n", "gneb_output_chain_step", parameters->output_chain_step ); + config += fmt::format( "{:<38} {:e}\n", "gneb_force_convergence", parameters->force_convergence ); + config += fmt::format( "{:<38} {}\n", "gneb_n_iterations", parameters->n_iterations ); + config += fmt::format( "{:<38} {}\n", "gneb_n_iterations_log", parameters->n_iterations_log ); + config += fmt::format( "{:<38} {}\n", "gneb_spring_constant", parameters->spring_constant ); + config += fmt::format( "{:<38} {}\n", "gneb_n_energy_interpolations", parameters->n_E_interpolations ); + config += "############### End GNEB Parameters ##############"; + Append_String_to_File( config, configFile ); +} - void Parameters_Method_GNEB_to_Config(const std::string configFile, const std::shared_ptr parameters) - { - std::string config = ""; - config += "################# GNEB Parameters ################\n"; - config += fmt::format("{:<38} {:d}\n", "gneb_output_any", parameters->output_any); - config += fmt::format("{:<38} {:d}\n", "gneb_output_initial", parameters->output_initial); - config += fmt::format("{:<38} {:d}\n", "gneb_output_final", parameters->output_final); - config += fmt::format("{:<38} {:d}\n", "gneb_output_energies_step", parameters->output_energies_step); - config += fmt::format("{:<38} {:d}\n", "gneb_output_energies_interpolated", parameters->output_energies_interpolated); - config += fmt::format("{:<38} {:d}\n", "gneb_output_energies_divide_by_nspins", parameters->output_energies_divide_by_nspins); - config += fmt::format("{:<38} {:d}\n", "gneb_output_chain_step", parameters->output_chain_step); - config += fmt::format("{:<38} {:e}\n", "gneb_force_convergence", parameters->force_convergence); - config += fmt::format("{:<38} {}\n", "gneb_n_iterations", parameters->n_iterations); - config += fmt::format("{:<38} {}\n", "gneb_n_iterations_log", parameters->n_iterations_log); - config += fmt::format("{:<38} {}\n", "gneb_spring_constant", parameters->spring_constant); - config += fmt::format("{:<38} {}\n", "gneb_n_energy_interpolations", parameters->n_E_interpolations); - config += "############### End GNEB Parameters ##############"; - Append_String_to_File(config, configFile); - }// end Parameters_Method_GNEB_to_Config +void Parameters_Method_MMF_to_Config( + const std::string configFile, const std::shared_ptr parameters ) +{ + std::string config = ""; + config += "################# MMF Parameters #################\n"; + config += fmt::format( "{:<38} {:d}\n", "mmf_output_any", parameters->output_any ); + config += fmt::format( "{:<38} {:d}\n", "mmf_output_initial", parameters->output_initial ); + config += fmt::format( "{:<38} {:d}\n", "mmf_output_final", parameters->output_final ); + config += fmt::format( "{:<38} {:d}\n", "mmf_output_energy_step", parameters->output_energy_step ); + config += fmt::format( "{:<38} {:d}\n", "mmf_output_energy_archive", parameters->output_energy_archive ); + config += fmt::format( + "{:<38} {:d}\n", "mmf_output_energy_divide_by_nspins", parameters->output_energy_divide_by_nspins ); + config += fmt::format( "{:<38} {:d}\n", "mmf_output_configuration_step", parameters->output_configuration_step ); + config + += fmt::format( "{:<38} {:d}\n", "mmf_output_configuration_archive", parameters->output_configuration_archive ); + config += fmt::format( "{:<38} {:e}\n", "mmf_force_convergence", parameters->force_convergence ); + config += fmt::format( "{:<38} {}\n", "mmf_n_iterations", parameters->n_iterations ); + config += fmt::format( "{:<38} {}\n", "mmf_n_iterations_log", parameters->n_iterations_log ); + config += "############### End MMF Parameters ###############"; + Append_String_to_File( config, configFile ); +} - void Parameters_Method_MMF_to_Config(const std::string configFile, const std::shared_ptr parameters) - { - std::string config = ""; - config += "################# MMF Parameters #################\n"; - config += fmt::format("{:<38} {:d}\n", "mmf_output_any", parameters->output_any); - config += fmt::format("{:<38} {:d}\n", "mmf_output_initial", parameters->output_initial); - config += fmt::format("{:<38} {:d}\n", "mmf_output_final", parameters->output_final); - config += fmt::format("{:<38} {:d}\n", "mmf_output_energy_step", parameters->output_energy_step); - config += fmt::format("{:<38} {:d}\n", "mmf_output_energy_archive", parameters->output_energy_archive); - config += fmt::format("{:<38} {:d}\n", "mmf_output_energy_divide_by_nspins", parameters->output_energy_divide_by_nspins); - config += fmt::format("{:<38} {:d}\n", "mmf_output_configuration_step", parameters->output_configuration_step); - config += fmt::format("{:<38} {:d}\n", "mmf_output_configuration_archive", parameters->output_configuration_archive); - config += fmt::format("{:<38} {:e}\n", "mmf_force_convergence", parameters->force_convergence); - config += fmt::format("{:<38} {}\n", "mmf_n_iterations", parameters->n_iterations); - config += fmt::format("{:<38} {}\n", "mmf_n_iterations_log", parameters->n_iterations_log); - config += "############### End MMF Parameters ###############"; - Append_String_to_File(config, configFile); - }// end Parameters_Method_MMF_to_Config +void Hamiltonian_to_Config( + const std::string configFile, const std::shared_ptr hamiltonian, + const std::shared_ptr geometry ) +{ + std::string config = ""; + config += "################### Hamiltonian ##################\n"; + std::string name; + if( hamiltonian->Name() == "Heisenberg" ) + name = "heisenberg_pairs"; + else if( hamiltonian->Name() == "Gaussian" ) + name = "gaussian"; + config += fmt::format( "{:<25} {}\n", "hamiltonian", name ); + config += fmt::format( + "{:<25} {} {} {}\n", "boundary_conditions", hamiltonian->boundary_conditions[0], + hamiltonian->boundary_conditions[1], hamiltonian->boundary_conditions[2] ); + Append_String_to_File( config, configFile ); - void Hamiltonian_to_Config(const std::string configFile, const std::shared_ptr hamiltonian, const std::shared_ptr geometry) - { - std::string config = ""; - config += "################### Hamiltonian ##################\n"; - std::string name; - if (hamiltonian->Name() == "Heisenberg") name = "heisenberg_pairs"; - else if (hamiltonian->Name() == "Gaussian") name = "gaussian"; - config += fmt::format("{:<25} {}\n", "hamiltonian", name); - config += fmt::format("{:<25} {} {} {}\n", "boundary_conditions", hamiltonian->boundary_conditions[0], hamiltonian->boundary_conditions[1], hamiltonian->boundary_conditions[2]); - Append_String_to_File(config, configFile); + if( hamiltonian->Name() == "Heisenberg" ) + Hamiltonian_Heisenberg_to_Config( configFile, hamiltonian, geometry ); + else if( hamiltonian->Name() == "Gaussian" ) + Hamiltonian_Gaussian_to_Config( configFile, hamiltonian ); - if (hamiltonian->Name() == "Heisenberg") Hamiltonian_Heisenberg_to_Config(configFile, hamiltonian, geometry); - else if (hamiltonian->Name() == "Gaussian") Hamiltonian_Gaussian_to_Config(configFile, hamiltonian); + config = "################# End Hamiltonian ################"; + Append_String_to_File( config, configFile ); +} - config = "################# End Hamiltonian ################"; - Append_String_to_File(config, configFile); - }// end Hamiltonian_to_Config +void Hamiltonian_Heisenberg_to_Config( + const std::string configFile, const std::shared_ptr hamiltonian, + const std::shared_ptr geometry ) +{ + int n_cells_tot = geometry->n_cells[0] * geometry->n_cells[1] * geometry->n_cells[2]; + std::string config = ""; + Engine::Hamiltonian_Heisenberg * ham = (Engine::Hamiltonian_Heisenberg *)hamiltonian.get(); + // External Field + config += "### External Field:\n"; + config += fmt::format( + "{:<25} {}\n", "external_field_magnitude", ham->external_field_magnitude / Utility::Constants::mu_B ); + config += fmt::format( "{:<25} {}\n", "external_field_normal", ham->external_field_normal.transpose() ); - void Hamiltonian_Heisenberg_to_Config(const std::string configFile, const std::shared_ptr hamiltonian, const std::shared_ptr geometry) + // Anisotropy + config += "### Anisotropy:\n"; + scalar K = 0; + Vector3 K_normal{ 0, 0, 0 }; + if( ham->anisotropy_indices.size() > 0 ) { - int n_cells_tot = geometry->n_cells[0]*geometry->n_cells[1]*geometry->n_cells[2]; - std::string config = ""; - Engine::Hamiltonian_Heisenberg* ham = (Engine::Hamiltonian_Heisenberg *)hamiltonian.get(); - - // External Field - config += "### External Field:\n"; - config += fmt::format("{:<25} {}\n", "external_field_magnitude", ham->external_field_magnitude / Constants::mu_B); - config += fmt::format("{:<25} {}\n", "external_field_normal", ham->external_field_normal.transpose()); + K = ham->anisotropy_magnitudes[0]; + K_normal = ham->anisotropy_normals[0]; + } + config += fmt::format( "{:<25} {}\n", "anisotropy_magnitude", K ); + config += fmt::format( "{:<25} {}\n", "anisotropy_normal", K_normal.transpose() ); - // Anisotropy - config += "### Anisotropy:\n"; - scalar K = 0; - Vector3 K_normal{ 0,0,0 }; - if (ham->anisotropy_indices.size() > 0) + config += "### Interaction pairs:\n"; + config += fmt::format( "n_interaction_pairs {}\n", ham->exchange_pairs.size() + ham->dmi_pairs.size() ); + if( ham->exchange_pairs.size() + ham->dmi_pairs.size() > 0 ) + { + config += fmt::format( + "{:^3} {:^3} {:^3} {:^3} {:^3} {:^15} {:^15} {:^15} {:^15} {:^15}\n", "i", "j", "da", "db", "dc", + "Jij", "Dij", "Dijx", "Dijy", "Dijz" ); + // Exchange + for( unsigned int i = 0; i < ham->exchange_pairs.size(); ++i ) { - K = ham->anisotropy_magnitudes[0]; - K_normal = ham->anisotropy_normals[0]; + config += fmt::format( + "{:^3} {:^3} {:^3} {:^3} {:^3} {:^15.8f} {:^15.8f} {:^15.8f} {:^15.8f} {:^15.8f}\n", + ham->exchange_pairs[i].i, ham->exchange_pairs[i].j, ham->exchange_pairs[i].translations[0], + ham->exchange_pairs[i].translations[1], ham->exchange_pairs[i].translations[2], + ham->exchange_magnitudes[i], 0.0, 0.0, 0.0, 0.0 ); } - config += fmt::format("{:<25} {}\n", "anisotropy_magnitude", K); - config += fmt::format("{:<25} {}\n", "anisotropy_normal", K_normal.transpose()); - - config += "### Interaction pairs:\n"; - config += fmt::format("n_interaction_pairs {}\n", ham->exchange_pairs.size() + ham->dmi_pairs.size()); - if (ham->exchange_pairs.size() + ham->dmi_pairs.size() > 0) + // DMI + for( unsigned int i = 0; i < ham->dmi_pairs.size(); ++i ) { - config += fmt::format("{:^3} {:^3} {:^3} {:^3} {:^3} {:^15} {:^15} {:^15} {:^15} {:^15}\n", - "i", "j", "da", "db", "dc", "Jij", "Dij", "Dijx", "Dijy", "Dijz"); - // Exchange - for (unsigned int i=0; iexchange_pairs.size(); ++i) - { - config += fmt::format("{:^3} {:^3} {:^3} {:^3} {:^3} {:^15.8f} {:^15.8f} {:^15.8f} {:^15.8f} {:^15.8f}\n", - ham->exchange_pairs[i].i, ham->exchange_pairs[i].j, - ham->exchange_pairs[i].translations[0], ham->exchange_pairs[i].translations[1], ham->exchange_pairs[i].translations[2], - ham->exchange_magnitudes[i], 0.0, 0.0, 0.0, 0.0); - } - // DMI - for (unsigned int i = 0; idmi_pairs.size(); ++i) - { - config += fmt::format("{:^3} {:^3} {:^3} {:^3} {:^3} {:^15.8f} {:^15.8f} {:^15.8f} {:^15.8f} {:^15.8f}\n", - ham->dmi_pairs[i].i, ham->dmi_pairs[i].j, - ham->dmi_pairs[i].translations[0], ham->dmi_pairs[i].translations[1], ham->dmi_pairs[i].translations[2], - 0.0, ham->dmi_magnitudes[i], ham->dmi_normals[i][0], ham->dmi_normals[i][1], ham->dmi_normals[i][2]); - } + config += fmt::format( + "{:^3} {:^3} {:^3} {:^3} {:^3} {:^15.8f} {:^15.8f} {:^15.8f} {:^15.8f} {:^15.8f}\n", + ham->dmi_pairs[i].i, ham->dmi_pairs[i].j, ham->dmi_pairs[i].translations[0], + ham->dmi_pairs[i].translations[1], ham->dmi_pairs[i].translations[2], 0.0, ham->dmi_magnitudes[i], + ham->dmi_normals[i][0], ham->dmi_normals[i][1], ham->dmi_normals[i][2] ); } + } - // Dipole-dipole - std::string ddi_method; - if (ham->ddi_method == Engine::DDI_Method::None) ddi_method = "none"; - else if (ham->ddi_method == Engine::DDI_Method::FFT) ddi_method = "fft"; - else if (ham->ddi_method == Engine::DDI_Method::FMM) ddi_method = "fmm"; - else if (ham->ddi_method == Engine::DDI_Method::Cutoff) ddi_method = "cutoff"; - config += "### Dipole-dipole interaction caclulation method\n### (fft, fmm, cutoff, none)"; - config += fmt::format("ddi_method {}\n", ddi_method); - config += "### DDI number of periodic images in (a b c)"; - config += fmt::format("ddi_n_periodic_images {} {} {}\n", ham->ddi_n_periodic_images[0], ham->ddi_n_periodic_images[1], ham->ddi_n_periodic_images[2]); - config += "### DDI cutoff radius (if cutoff is used)"; - config += fmt::format("ddi_radius {}\n", ham->ddi_cutoff_radius); + // Dipole-dipole + std::string ddi_method; + if( ham->ddi_method == Engine::DDI_Method::None ) + ddi_method = "none"; + else if( ham->ddi_method == Engine::DDI_Method::FFT ) + ddi_method = "fft"; + else if( ham->ddi_method == Engine::DDI_Method::FMM ) + ddi_method = "fmm"; + else if( ham->ddi_method == Engine::DDI_Method::Cutoff ) + ddi_method = "cutoff"; + config += "### Dipole-dipole interaction caclulation method\n### (fft, fmm, cutoff, none)"; + config += fmt::format( "ddi_method {}\n", ddi_method ); + config += "### DDI number of periodic images in (a b c)"; + config += fmt::format( + "ddi_n_periodic_images {} {} {}\n", ham->ddi_n_periodic_images[0], ham->ddi_n_periodic_images[1], + ham->ddi_n_periodic_images[2] ); + config += "### DDI cutoff radius (if cutoff is used)"; + config += fmt::format( "ddi_radius {}\n", ham->ddi_cutoff_radius ); - // Quadruplets - config += "### Quadruplets:\n"; - config += fmt::format("n_interaction_quadruplets {}\n", ham->quadruplets.size()); - if (ham->quadruplets.size() > 0) + // Quadruplets + config += "### Quadruplets:\n"; + config += fmt::format( "n_interaction_quadruplets {}\n", ham->quadruplets.size() ); + if( ham->quadruplets.size() > 0 ) + { + config += fmt::format( + "{:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^15}\n", "i", + "j", "k", "l", "da_j", "db_j", "dc_j", "da_k", "db_k", "dc_k", "da_l", "db_l", "dc_l", "Q" ); + for( unsigned int i = 0; i < ham->quadruplets.size(); ++i ) { - config += fmt::format("{:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^15}\n", - "i", "j", "k", "l", "da_j", "db_j", "dc_j", "da_k", "db_k", "dc_k", "da_l", "db_l", "dc_l", "Q"); - for (unsigned int i=0; iquadruplets.size(); ++i) - { - config += fmt::format("{:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^15.8f}\n", - ham->quadruplets[i].i, ham->quadruplets[i].j, ham->quadruplets[i].k, ham->quadruplets[i].l, - ham->quadruplets[i].d_j[0], ham->quadruplets[i].d_j[1], ham->quadruplets[i].d_j[2], - ham->quadruplets[i].d_k[0], ham->quadruplets[i].d_k[1], ham->quadruplets[i].d_k[2], - ham->quadruplets[i].d_l[0], ham->quadruplets[i].d_l[1], ham->quadruplets[i].d_l[2], - ham->quadruplet_magnitudes[i]); - } + config += fmt::format( + "{:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^3} {:^15.8f}\n", + ham->quadruplets[i].i, ham->quadruplets[i].j, ham->quadruplets[i].k, ham->quadruplets[i].l, + ham->quadruplets[i].d_j[0], ham->quadruplets[i].d_j[1], ham->quadruplets[i].d_j[2], + ham->quadruplets[i].d_k[0], ham->quadruplets[i].d_k[1], ham->quadruplets[i].d_k[2], + ham->quadruplets[i].d_l[0], ham->quadruplets[i].d_l[1], ham->quadruplets[i].d_l[2], + ham->quadruplet_magnitudes[i] ); } + } - Append_String_to_File(config, configFile); - }// end Hamiltonian_Heisenberg_Pairs_to_Config + Append_String_to_File( config, configFile ); +} - void Hamiltonian_Gaussian_to_Config(const std::string configFile, const std::shared_ptr hamiltonian) +void Hamiltonian_Gaussian_to_Config( + const std::string configFile, const std::shared_ptr hamiltonian ) +{ + std::string config = ""; + Engine::Hamiltonian_Gaussian * ham_gaussian = (Engine::Hamiltonian_Gaussian *)hamiltonian.get(); + config += fmt::format( "n_gaussians {}\n", ham_gaussian->n_gaussians ); + if( ham_gaussian->n_gaussians > 0 ) { - std::string config = ""; - Engine::Hamiltonian_Gaussian * ham_gaussian = (Engine::Hamiltonian_Gaussian *)hamiltonian.get(); - config += fmt::format("n_gaussians {}\n", ham_gaussian->n_gaussians); - if (ham_gaussian->n_gaussians > 0) + config += "gaussians\n"; + for( int i = 0; i < ham_gaussian->n_gaussians; ++i ) { - config += "gaussians\n"; - for (int i=0; i< ham_gaussian->n_gaussians; ++i) - { - config +=fmt::format("{} {} {}\n", ham_gaussian->amplitude[i], ham_gaussian->width[i], ham_gaussian->center[i].transpose()); - } + config += fmt::format( + "{} {} {}\n", ham_gaussian->amplitude[i], ham_gaussian->width[i], ham_gaussian->center[i].transpose() ); } - Append_String_to_File(config, configFile); - }// end Hamiltonian_Gaussian_to_Config -}// end namespace IO \ No newline at end of file + } + Append_String_to_File( config, configFile ); +} + +} // namespace IO \ No newline at end of file diff --git a/core/src/io/Dataparser.cpp b/core/src/io/Dataparser.cpp index 6a358a8b4..45743dc80 100644 --- a/core/src/io/Dataparser.cpp +++ b/core/src/io/Dataparser.cpp @@ -1,714 +1,759 @@ -#include -#include -#include +#include #include +#include +#include #include -#include -#include #include +#include -#include +#include #include -#include -#include +#include #include -#include +#include +#include -using namespace Utility; -using namespace Engine; +using Utility::Log_Level; +using Utility::Log_Sender; namespace IO { - /* - Reads a non-OVF spins file with plain text and discarding any headers starting with '#' - */ - void Read_NonOVF_Spin_Configuration( vectorfield& spins, Data::Geometry& geometry, - const int nos, const int idx_image_infile, - const std::string file ) - { - IO::Filter_File_Handle file_handle( file, "#" ); - // jump to the specified image in the file - for (int i=0; i<( nos * idx_image_infile ); i++) - file_handle.GetLine(); +// Reads a non-OVF spins file with plain text and discarding any headers starting with '#' +void Read_NonOVF_Spin_Configuration( + vectorfield & spins, Data::Geometry & geometry, const int nos, const int idx_image_infile, const std::string file ) +{ + IO::Filter_File_Handle file_handle( file, "#" ); - for (int i=0; i> spins[i][0]; - file_handle.iss >> spins[i][1]; - file_handle.iss >> spins[i][2]; + // jump to the specified image in the file + for( int i = 0; i < ( nos * idx_image_infile ); i++ ) + file_handle.GetLine(); - if (spins[i].norm() < 1e-5) - { - spins[i] = {0, 0, 1}; - // in case of spin vector close to zero we have a vacancy - #ifdef SPIRIT_ENABLE_DEFECTS - geometry.atom_types[i] = -1; - #endif - } + for( int i = 0; i < nos && file_handle.GetLine( "," ); i++ ) + { + file_handle.iss >> spins[i][0]; + file_handle.iss >> spins[i][1]; + file_handle.iss >> spins[i][2]; + + if( spins[i].norm() < 1e-5 ) + { + spins[i] = { 0, 0, 1 }; + // in case of spin vector close to zero we have a vacancy +#ifdef SPIRIT_ENABLE_DEFECTS + geometry.atom_types[i] = -1; +#endif } + } + + // normalize read in spins + Engine::Vectormath::normalize_vectors( spins ); +} + +void Check_NonOVF_Chain_Configuration( + std::shared_ptr chain, const std::string file, int start_image_infile, + int end_image_infile, const int insert_idx, int & noi_to_add, int & noi_to_read, const int idx_chain ) +{ + IO::Filter_File_Handle file_handle( file, "#" ); - // normalize read in spins - Vectormath::normalize_vectors( spins ); + int nol = file_handle.Get_N_Non_Comment_Lines(); + int noi = chain->noi; + int nos = chain->images[0]->nos; + + int noi_infile = nol / nos; + int remainder = nol % nos; + + if( remainder != 0 ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::IO, + fmt::format( "Calculated number of images in the nonOVF file is not integer" ), insert_idx, idx_chain ); } + // Check if the ending image is valid otherwise set it to the last image infile + if( end_image_infile < start_image_infile || end_image_infile >= noi_infile ) + { + end_image_infile = noi_infile - 1; + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + fmt::format( "Invalid end_image_infile. Value was set to the last image " + "of the file" ), + insert_idx, idx_chain ); + } - void Check_NonOVF_Chain_Configuration( std::shared_ptr chain, - const std::string file, int start_image_infile, - int end_image_infile, const int insert_idx, - int& noi_to_add, int& noi_to_read, const int idx_chain ) + // If the idx of the starting image is valid + if( start_image_infile < noi_infile ) { - IO::Filter_File_Handle file_handle( file, "#" ); + noi_to_read = end_image_infile - start_image_infile + 1; - int nol = file_handle.Get_N_Non_Comment_Lines(); - int noi = chain->noi; - int nos = chain->images[0]->nos; + noi_to_add = noi_to_read - ( noi - insert_idx ); + } + else + { + Log( Utility::Log_Level::Error, Utility::Log_Sender::IO, + fmt::format( "Invalid starting_idx. File {} has {} noi", file, noi_infile ), insert_idx, idx_chain ); + } +} + +// Read from Anisotropy file +void Anisotropy_from_File( + const std::string anisotropyFile, const std::shared_ptr geometry, int & n_indices, + intfield & anisotropy_index, scalarfield & anisotropy_magnitude, vectorfield & anisotropy_normal ) noexcept +try +{ + Log( Log_Level::Debug, Log_Sender::IO, "Reading anisotropy from file " + anisotropyFile ); + + std::vector columns( 5 ); // at least: 1 (index) + 3 (K) + // column indices of pair indices and interactions + int col_i = -1, col_K = -1, col_Kx = -1, col_Ky = -1, col_Kz = -1, col_Ka = -1, col_Kb = -1, col_Kc = -1; + bool K_magnitude = false, K_xyz = false, K_abc = false; + Vector3 K_temp = { 0, 0, 0 }; + int n_anisotropy; - int noi_infile = nol/nos; - int remainder = nol%nos; + Filter_File_Handle file( anisotropyFile ); - if( remainder != 0 ) + if( file.Find( "n_anisotropy" ) ) + { + // Read n interaction pairs + file.iss >> n_anisotropy; + Log( Log_Level::Debug, Log_Sender::IO, + fmt::format( "Anisotropy file {} should have {} vectors", anisotropyFile, n_anisotropy ) ); + } + else + { + // Read the whole file + n_anisotropy = (int)1e8; + // First line should contain the columns + file.ResetStream(); + Log( Log_Level::Debug, Log_Sender::IO, + "Trying to parse anisotropy columns from top of file " + anisotropyFile ); + } + + // Get column indices + file.GetLine(); // first line contains the columns + for( unsigned int i = 0; i < columns.size(); ++i ) + { + file.iss >> columns[i]; + std::transform( columns[i].begin(), columns[i].end(), columns[i].begin(), ::tolower ); + if( columns[i] == "i" ) + col_i = i; + else if( columns[i] == "k" ) { - Log( Utility::Log_Level::Warning, Utility::Log_Sender::IO, - fmt::format( "Calculated number of images in the nonOVF file is not integer"), - insert_idx, idx_chain ); + col_K = i; + K_magnitude = true; } + else if( columns[i] == "kx" ) + col_Kx = i; + else if( columns[i] == "ky" ) + col_Ky = i; + else if( columns[i] == "kz" ) + col_Kz = i; + else if( columns[i] == "ka" ) + col_Ka = i; + else if( columns[i] == "kb" ) + col_Kb = i; + else if( columns[i] == "kc" ) + col_Kc = i; + + if( col_Kx >= 0 && col_Ky >= 0 && col_Kz >= 0 ) + K_xyz = true; + if( col_Ka >= 0 && col_Kb >= 0 && col_Kc >= 0 ) + K_abc = true; + } - // Check if the ending image is valid otherwise set it to the last image infile - if( end_image_infile < start_image_infile || end_image_infile >= noi_infile ) + if( !K_xyz && !K_abc ) + Log( Log_Level::Warning, Log_Sender::IO, + fmt::format( "No anisotropy data could be found in header of file \"{}\"", anisotropyFile ) ); + + // Indices + int spin_i = 0; + scalar spin_K = 0, spin_K1 = 0, spin_K2 = 0, spin_K3 = 0; + // Arrays + anisotropy_index = intfield( 0 ); + anisotropy_magnitude = scalarfield( 0 ); + anisotropy_normal = vectorfield( 0 ); + + // Get actual Data + int i_anisotropy = 0; + std::string sdump; + while( file.GetLine() && i_anisotropy < n_anisotropy ) + { + // Read a line from the File + for( unsigned int i = 0; i < columns.size(); ++i ) + { + if( i == col_i ) + file.iss >> spin_i; + else if( i == col_K ) + file.iss >> spin_K; + else if( i == col_Kx && K_xyz ) + file.iss >> spin_K1; + else if( i == col_Ky && K_xyz ) + file.iss >> spin_K2; + else if( i == col_Kz && K_xyz ) + file.iss >> spin_K3; + else if( i == col_Ka && K_abc ) + file.iss >> spin_K1; + else if( i == col_Kb && K_abc ) + file.iss >> spin_K2; + else if( i == col_Kc && K_abc ) + file.iss >> spin_K3; + else + file.iss >> sdump; + } + K_temp = { spin_K1, spin_K2, spin_K3 }; + // Anisotropy vector orientation + if( K_abc ) { - end_image_infile = noi_infile - 1; - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - fmt::format( "Invalid end_image_infile. Value was set to the last image " - "of the file"), insert_idx, idx_chain ); + spin_K1 = K_temp.dot( geometry->lattice_constant * geometry->bravais_vectors[0] ); + spin_K2 = K_temp.dot( geometry->lattice_constant * geometry->bravais_vectors[1] ); + spin_K3 = K_temp.dot( geometry->lattice_constant * geometry->bravais_vectors[2] ); + K_temp = { spin_K1, spin_K2, spin_K3 }; } - // If the idx of the starting image is valid - if( start_image_infile < noi_infile ) + // Anisotropy vector normalisation + if( K_magnitude ) { - noi_to_read = end_image_infile - start_image_infile + 1; - - noi_to_add = noi_to_read - ( noi - insert_idx ); + K_temp.normalize(); + if( K_temp.norm() == 0 ) + K_temp = Vector3{ 0, 0, 1 }; } else { - Log( Utility::Log_Level::Error, Utility::Log_Sender::IO, - fmt::format( "Invalid starting_idx. File {} has {} noi", file, - noi_infile ), insert_idx, idx_chain ); + spin_K = K_temp.norm(); + if( spin_K != 0 ) + K_temp.normalize(); } + + // Add the index and parameters to the corresponding lists + if( spin_K != 0 ) + { + anisotropy_index.push_back( spin_i ); + anisotropy_magnitude.push_back( spin_K ); + anisotropy_normal.push_back( K_temp ); + } + ++i_anisotropy; + } // end while getline + n_indices = i_anisotropy; +} +catch( ... ) +{ + spirit_rethrow( fmt::format( "Could not read anisotropies from file \"{}\"", anisotropyFile ) ); +} + +// Read from Pairs file by Markus & Bernd +void Pairs_from_File( + const std::string pairsFile, const std::shared_ptr geometry, int & nop, pairfield & exchange_pairs, + scalarfield & exchange_magnitudes, pairfield & dmi_pairs, scalarfield & dmi_magnitudes, + vectorfield & dmi_normals ) noexcept +try +{ + Log( Log_Level::Debug, Log_Sender::IO, fmt::format( "Reading spin pairs from file \"{}\"", pairsFile ) ); + + std::vector columns( 20 ); // at least: 2 (indices) + 3 (J) + 3 (DMI) + // column indices of pair indices and interactions + int n_pairs = 0; + int col_i = -1, col_j = -1, col_da = -1, col_db = -1, col_dc = -1, col_J = -1, col_DMIx = -1, col_DMIy = -1, + col_DMIz = -1, col_Dij = -1, col_DMIa = -1, col_DMIb = -1, col_DMIc = -1; + bool J = false, DMI_xyz = false, DMI_abc = false, Dij = false; + int pair_periodicity = 0; + Vector3 pair_D_temp = { 0, 0, 0 }; + // Get column indices + Filter_File_Handle file( pairsFile ); + + if( file.Find( "n_interaction_pairs" ) ) + { + // Read n interaction pairs + file.iss >> n_pairs; + Log( Log_Level::Debug, Log_Sender::IO, fmt::format( "File {} should have {} pairs", pairsFile, n_pairs ) ); + } + else + { + // Read the whole file + n_pairs = (int)1e8; + // First line should contain the columns + file.ResetStream(); + Log( Log_Level::Debug, Log_Sender::IO, "Trying to parse spin pairs columns from top of file " + pairsFile ); } - /* - Read from Anisotropy file - */ - void Anisotropy_from_File(const std::string anisotropyFile, const std::shared_ptr geometry, int & n_indices, - intfield & anisotropy_index, scalarfield & anisotropy_magnitude, - vectorfield & anisotropy_normal) + file.GetLine(); + for( unsigned int i = 0; i < columns.size(); ++i ) { - Log(Log_Level::Debug, Log_Sender::IO, "Reading anisotropy from file " + anisotropyFile); - try + file.iss >> columns[i]; + std::transform( columns[i].begin(), columns[i].end(), columns[i].begin(), ::tolower ); + if( columns[i] == "i" ) + col_i = i; + else if( columns[i] == "j" ) + col_j = i; + else if( columns[i] == "da" ) + col_da = i; + else if( columns[i] == "db" ) + col_db = i; + else if( columns[i] == "dc" ) + col_dc = i; + else if( columns[i] == "jij" ) + { + col_J = i; + J = true; + } + else if( columns[i] == "dij" ) { - std::vector columns(5); // at least: 1 (index) + 3 (K) - // column indices of pair indices and interactions - int col_i = -1, col_K = -1, col_Kx = -1, col_Ky = -1, col_Kz = -1, col_Ka = -1, col_Kb = -1, col_Kc = -1; - bool K_magnitude = false, K_xyz = false, K_abc = false; - Vector3 K_temp = { 0, 0, 0 }; - int n_anisotropy; + col_Dij = i; + Dij = true; + } + else if( columns[i] == "dijx" ) + col_DMIx = i; + else if( columns[i] == "dijy" ) + col_DMIy = i; + else if( columns[i] == "dijz" ) + col_DMIz = i; + else if( columns[i] == "dija" ) + col_DMIx = i; + else if( columns[i] == "dijb" ) + col_DMIy = i; + else if( columns[i] == "dijc" ) + col_DMIz = i; + + if( col_DMIx >= 0 && col_DMIy >= 0 && col_DMIz >= 0 ) + DMI_xyz = true; + if( col_DMIa >= 0 && col_DMIb >= 0 && col_DMIc >= 0 ) + DMI_abc = true; + } - Filter_File_Handle file(anisotropyFile); + // Check if interactions have been found in header + if( !J && !DMI_xyz && !DMI_abc ) + Log( Log_Level::Warning, Log_Sender::IO, + fmt::format( "No interactions could be found in pairs file \"{}\"", pairsFile ) ); - if( file.Find("n_anisotropy") ) - { - // Read n interaction pairs - file.iss >> n_anisotropy; - Log(Log_Level::Debug, Log_Sender::IO, fmt::format("Anisotropy file {} should have {} vectors", anisotropyFile, n_anisotropy)); - } + // Get actual Pairs Data + int i_pair = 0; + std::string sdump; + while( file.GetLine() && i_pair < n_pairs ) + { + // Pair Indices + int pair_i = 0, pair_j = 0, pair_da = 0, pair_db = 0, pair_dc = 0; + scalar pair_Jij = 0, pair_Dij = 0, pair_D1 = 0, pair_D2 = 0, pair_D3 = 0; + // Read a Pair from the File + for( unsigned int i = 0; i < columns.size(); ++i ) + { + if( i == col_i ) + file.iss >> pair_i; + else if( i == col_j ) + file.iss >> pair_j; + else if( i == col_da ) + file.iss >> pair_da; + else if( i == col_db ) + file.iss >> pair_db; + else if( i == col_dc ) + file.iss >> pair_dc; + else if( i == col_J && J ) + file.iss >> pair_Jij; + else if( i == col_Dij && Dij ) + file.iss >> pair_Dij; + else if( i == col_DMIa && DMI_abc ) + file.iss >> pair_D1; + else if( i == col_DMIb && DMI_abc ) + file.iss >> pair_D2; + else if( i == col_DMIc && DMI_abc ) + file.iss >> pair_D3; + else if( i == col_DMIx && DMI_xyz ) + file.iss >> pair_D1; + else if( i == col_DMIy && DMI_xyz ) + file.iss >> pair_D2; + else if( i == col_DMIz && DMI_xyz ) + file.iss >> pair_D3; else - { - // Read the whole file - n_anisotropy = (int)1e8; - // First line should contain the columns - file.ResetStream(); - Log(Log_Level::Debug, Log_Sender::IO, "Trying to parse anisotropy columns from top of file " + anisotropyFile); - } - - // Get column indices - file.GetLine(); // first line contains the columns - for( unsigned int i = 0; i < columns.size(); ++i ) - { - file.iss >> columns[i]; - std::transform( columns[i].begin(), columns[i].end(), columns[i].begin(), ::tolower ); - if (columns[i] == "i") col_i = i; - else if (columns[i] == "k") { col_K = i; K_magnitude = true; } - else if (columns[i] == "kx") col_Kx = i; - else if (columns[i] == "ky") col_Ky = i; - else if (columns[i] == "kz") col_Kz = i; - else if (columns[i] == "ka") col_Ka = i; - else if (columns[i] == "kb") col_Kb = i; - else if (columns[i] == "kc") col_Kc = i; - - if (col_Kx >= 0 && col_Ky >= 0 && col_Kz >= 0) K_xyz = true; - if (col_Ka >= 0 && col_Kb >= 0 && col_Kc >= 0) K_abc = true; - } - - if( !K_xyz && !K_abc ) - Log(Log_Level::Warning, Log_Sender::IO, fmt::format( - "No anisotropy data could be found in header of file \"{}\"", anisotropyFile)); - - // Indices - int spin_i = 0; - scalar spin_K = 0, spin_K1 = 0, spin_K2 = 0, spin_K3 = 0; - // Arrays - anisotropy_index = intfield(0); - anisotropy_magnitude = scalarfield(0); - anisotropy_normal = vectorfield(0); - - // Get actual Data - int i_anisotropy = 0; - std::string sdump; - while( file.GetLine() && i_anisotropy < n_anisotropy ) - { - // Read a line from the File - for (unsigned int i = 0; i < columns.size(); ++i) - { - if (i == col_i) - file.iss >> spin_i; - else if (i == col_K) - file.iss >> spin_K; - else if (i == col_Kx && K_xyz) - file.iss >> spin_K1; - else if (i == col_Ky && K_xyz) - file.iss >> spin_K2; - else if (i == col_Kz && K_xyz) - file.iss >> spin_K3; - else if (i == col_Ka && K_abc) - file.iss >> spin_K1; - else if (i == col_Kb && K_abc) - file.iss >> spin_K2; - else if (i == col_Kc && K_abc) - file.iss >> spin_K3; - else - file.iss >> sdump; - } - K_temp = { spin_K1, spin_K2, spin_K3 }; - // Anisotropy vector orientation - if (K_abc) - { - spin_K1 = K_temp.dot(geometry->lattice_constant*geometry->bravais_vectors[0]); - spin_K2 = K_temp.dot(geometry->lattice_constant*geometry->bravais_vectors[1]); - spin_K3 = K_temp.dot(geometry->lattice_constant*geometry->bravais_vectors[2]); - K_temp = { spin_K1, spin_K2, spin_K3 }; - } + file.iss >> sdump; + } // end for columns - // Anisotropy vector normalisation - if (K_magnitude) - { - K_temp.normalize(); - if (K_temp.norm() == 0) - K_temp = Vector3{0, 0, 1}; - } - else - { - spin_K = K_temp.norm(); - if (spin_K != 0) - K_temp.normalize(); - } - - // Add the index and parameters to the corresponding lists - if (spin_K != 0) - { - anisotropy_index.push_back(spin_i); - anisotropy_magnitude.push_back(spin_K); - anisotropy_normal.push_back(K_temp); - } - ++i_anisotropy; - }// end while getline - n_indices = i_anisotropy; - }// end try - catch( ... ) + // DMI vector orientation + if( DMI_abc ) { - spirit_rethrow( fmt::format("Could not read anisotropies from file \"{}\"", anisotropyFile) ); + pair_D_temp = pair_D1 * geometry->lattice_constant * geometry->bravais_vectors[0] + + pair_D2 * geometry->lattice_constant * geometry->bravais_vectors[1] + + pair_D3 * geometry->lattice_constant * geometry->bravais_vectors[2]; + pair_D1 = pair_D_temp[0]; + pair_D2 = pair_D_temp[1]; + pair_D3 = pair_D_temp[2]; + } + // DMI vector normalisation + scalar dnorm = std::sqrt( std::pow( pair_D1, 2 ) + std::pow( pair_D2, 2 ) + std::pow( pair_D3, 2 ) ); + if( dnorm != 0 ) + { + pair_D1 = pair_D1 / dnorm; + pair_D2 = pair_D2 / dnorm; + pair_D3 = pair_D3 / dnorm; + } + if( !Dij ) + { + pair_Dij = dnorm; } - } - /* - Read from Pairs file by Markus & Bernd - */ - void Pairs_from_File(const std::string pairsFile, const std::shared_ptr geometry, int & nop, - pairfield & exchange_pairs, scalarfield & exchange_magnitudes, - pairfield & dmi_pairs, scalarfield & dmi_magnitudes, vectorfield & dmi_normals) - { - Log(Log_Level::Debug, Log_Sender::IO, fmt::format("Reading spin pairs from file \"{}\"", pairsFile)); - try + // Add the indices and parameters to the corresponding lists + if( pair_Jij != 0 ) { - std::vector columns(20); // at least: 2 (indices) + 3 (J) + 3 (DMI) - // column indices of pair indices and interactions - int n_pairs = 0; - int col_i = -1, col_j = -1, col_da = -1, col_db = -1, col_dc = -1, - col_J = -1, col_DMIx = -1, col_DMIy = -1, col_DMIz = -1, - col_Dij = -1, col_DMIa = -1, col_DMIb = -1, col_DMIc = -1; - bool J = false, DMI_xyz = false, DMI_abc = false, Dij = false; - int pair_periodicity = 0; - Vector3 pair_D_temp = { 0, 0, 0 }; - // Get column indices - Filter_File_Handle file(pairsFile); - - if( file.Find("n_interaction_pairs") ) + bool already_in; + already_in = false; + int atposition = -1; + for( unsigned int icheck = 0; icheck < exchange_pairs.size(); ++icheck ) { - // Read n interaction pairs - file.iss >> n_pairs; - Log(Log_Level::Debug, Log_Sender::IO, fmt::format("File {} should have {} pairs", pairsFile, n_pairs)); + auto & p = exchange_pairs[icheck]; + auto & t = p.translations; + std::array tnew = { pair_da, pair_db, pair_dc }; + if( ( pair_i == p.i && pair_j == p.j && tnew == std::array{ t[0], t[1], t[2] } ) + || ( pair_i == p.j && pair_j == p.i && tnew == std::array{ -t[0], -t[1], -t[2] } ) ) + { + already_in = true; + atposition = icheck; + break; + } } - else + if( already_in ) { - // Read the whole file - n_pairs = (int)1e8; - // First line should contain the columns - file.ResetStream(); - Log(Log_Level::Debug, Log_Sender::IO, "Trying to parse spin pairs columns from top of file " + pairsFile); + exchange_magnitudes[atposition] += pair_Jij; } - - file.GetLine(); - for (unsigned int i = 0; i < columns.size(); ++i) + else { - file.iss >> columns[i]; - std::transform( columns[i].begin(), columns[i].end(), columns[i].begin(), ::tolower ); - if (columns[i] == "i") col_i = i; - else if (columns[i] == "j") col_j = i; - else if (columns[i] == "da") col_da = i; - else if (columns[i] == "db") col_db = i; - else if (columns[i] == "dc") col_dc = i; - else if (columns[i] == "jij") { col_J = i; J = true; } - else if (columns[i] == "dij") { col_Dij = i; Dij = true; } - else if (columns[i] == "dijx") col_DMIx = i; - else if (columns[i] == "dijy") col_DMIy = i; - else if (columns[i] == "dijz") col_DMIz = i; - else if (columns[i] == "dija") col_DMIx = i; - else if (columns[i] == "dijb") col_DMIy = i; - else if (columns[i] == "dijc") col_DMIz = i; - - if (col_DMIx >= 0 && col_DMIy >= 0 && col_DMIz >= 0) DMI_xyz = true; - if (col_DMIa >= 0 && col_DMIb >= 0 && col_DMIc >= 0) DMI_abc = true; + exchange_pairs.push_back( { pair_i, pair_j, { pair_da, pair_db, pair_dc } } ); + exchange_magnitudes.push_back( pair_Jij ); } - - // Check if interactions have been found in header - if (!J && !DMI_xyz && !DMI_abc) - Log(Log_Level::Warning, Log_Sender::IO, fmt::format( - "No interactions could be found in pairs file \"{}\"", pairsFile)); - - // Get actual Pairs Data - int i_pair = 0; - std::string sdump; - while (file.GetLine() && i_pair < n_pairs) + } + if( pair_Dij != 0 ) + { + bool already_in; + int dfact = 1; + already_in = false; + int atposition = -1; + for( unsigned int icheck = 0; icheck < dmi_pairs.size(); ++icheck ) { - // Pair Indices - int pair_i = 0, pair_j = 0, pair_da = 0, pair_db = 0, pair_dc = 0; - scalar pair_Jij = 0, pair_Dij = 0, pair_D1 = 0, pair_D2 = 0, pair_D3 = 0; - // Read a Pair from the File - for (unsigned int i = 0; i < columns.size(); ++i) + auto & p = dmi_pairs[icheck]; + auto & t = p.translations; + std::array tnew = { pair_da, pair_db, pair_dc }; + if( pair_i == p.i && pair_j == p.j && tnew == std::array{ t[0], t[1], t[2] } ) { - if (i == col_i) - file.iss >> pair_i; - else if (i == col_j) - file.iss >> pair_j; - else if (i == col_da) - file.iss >> pair_da; - else if (i == col_db) - file.iss >> pair_db; - else if (i == col_dc) - file.iss >> pair_dc; - else if (i == col_J && J) - file.iss >> pair_Jij; - else if (i == col_Dij && Dij) - file.iss >> pair_Dij; - else if (i == col_DMIa && DMI_abc) - file.iss >> pair_D1; - else if (i == col_DMIb && DMI_abc) - file.iss >> pair_D2; - else if (i == col_DMIc && DMI_abc) - file.iss >> pair_D3; - else if (i == col_DMIx && DMI_xyz) - file.iss >> pair_D1; - else if (i == col_DMIy && DMI_xyz) - file.iss >> pair_D2; - else if (i == col_DMIz && DMI_xyz) - file.iss >> pair_D3; - else - file.iss >> sdump; - }// end for columns - - // DMI vector orientation - if (DMI_abc) - { - pair_D_temp = pair_D1 * geometry->lattice_constant * geometry->bravais_vectors[0] - + pair_D2 * geometry->lattice_constant * geometry->bravais_vectors[1] - + pair_D3 * geometry->lattice_constant * geometry->bravais_vectors[2]; - pair_D1 = pair_D_temp[0]; - pair_D2 = pair_D_temp[1]; - pair_D3 = pair_D_temp[2]; + already_in = true; + atposition = icheck; + break; } - // DMI vector normalisation - scalar dnorm = std::sqrt(std::pow(pair_D1, 2) + std::pow(pair_D2, 2) + std::pow(pair_D3, 2)); - if (dnorm != 0) - { - pair_D1 = pair_D1 / dnorm; - pair_D2 = pair_D2 / dnorm; - pair_D3 = pair_D3 / dnorm; - } - if (!Dij) - { - pair_Dij = dnorm; + else if( pair_i == p.j && pair_j == p.i && tnew == std::array{ -t[0], -t[1], -t[2] } ) + { // if the inverted pair is present, the DMI vector has to be mirrored due to its pseudo-vector behaviour + dfact = -1; + already_in = true; + atposition = icheck; + break; } + } + if( already_in ) + { // calculate new D vector by adding the two redundant ones and normalize again + Vector3 newD = dmi_magnitudes[atposition] * dmi_normals[atposition] + + dfact * pair_Dij * Vector3{ pair_D1, pair_D2, pair_D3 }; + scalar newdnorm = std::sqrt( std::pow( newD[0], 2 ) + std::pow( newD[1], 2 ) + std::pow( newD[2], 2 ) ); + dmi_magnitudes[atposition] = newdnorm; + dmi_normals[atposition] = newD / newdnorm; + } + else + { + dmi_pairs.push_back( { pair_i, pair_j, { pair_da, pair_db, pair_dc } } ); + dmi_magnitudes.push_back( pair_Dij ); + dmi_normals.push_back( Vector3{ pair_D1, pair_D2, pair_D3 } ); + } + } - // Add the indices and parameters to the corresponding lists - if (pair_Jij != 0) - { - bool already_in; - already_in = false; - int atposition = -1; - for (unsigned int icheck = 0; icheck < exchange_pairs.size(); ++icheck ) - { - auto& p = exchange_pairs[icheck]; - auto& t = p.translations; - std::array tnew = { pair_da, pair_db, pair_dc }; - if ( (pair_i == p.i && pair_j == p.j && tnew == std::array{ t[0], t[1], t[2]}) || - (pair_i == p.j && pair_j == p.i && tnew == std::array{-t[0], -t[1], -t[2]}) ) - { - already_in = true; - atposition = icheck; - break; - } - } - if (already_in) - { - exchange_magnitudes[atposition] += pair_Jij; - } - else - { - exchange_pairs.push_back({ pair_i, pair_j, { pair_da, pair_db, pair_dc } }); - exchange_magnitudes.push_back(pair_Jij); - } - } - if (pair_Dij != 0) - { - bool already_in; - int dfact = 1; - already_in = false; - int atposition = -1; - for (unsigned int icheck = 0; icheck < dmi_pairs.size(); ++icheck ) - { - auto& p = dmi_pairs[icheck]; - auto& t = p.translations; - std::array tnew = { pair_da, pair_db, pair_dc }; - if (pair_i == p.i && pair_j == p.j && tnew == std::array{ t[0], t[1], t[2] }) - { - already_in = true; - atposition = icheck; - break; - } - else if (pair_i == p.j && pair_j == p.i && tnew == std::array{-t[0], -t[1], -t[2]}) - { // if the inverted pair is present, the DMI vector has to be mirrored due to its pseudo-vector behaviour - dfact = -1; - already_in = true; - atposition = icheck; - break; - } - - } - if (already_in) - { // calculate new D vector by adding the two redundant ones and normalize again - Vector3 newD = dmi_magnitudes[atposition] * dmi_normals[atposition] - + dfact * pair_Dij * Vector3{pair_D1, pair_D2, pair_D3}; - scalar newdnorm = std::sqrt(std::pow(newD[0], 2) + std::pow(newD[1], 2) + std::pow(newD[2], 2)); - dmi_magnitudes[atposition] = newdnorm; - dmi_normals[atposition] = newD / newdnorm; - } - else - { - dmi_pairs.push_back({ pair_i, pair_j, { pair_da, pair_db, pair_dc } }); - dmi_magnitudes.push_back(pair_Dij); - dmi_normals.push_back(Vector3{pair_D1, pair_D2, pair_D3}); - } - } + ++i_pair; + } // end while GetLine + Log( Log_Level::Parameter, Log_Sender::IO, + fmt::format( + "Done reading {} spin pairs from file \"{}\", giving {} exchange and {} DM (symmetry-reduced) pairs.", + i_pair, pairsFile, exchange_pairs.size(), dmi_pairs.size() ) ); + nop = i_pair; +} +catch( ... ) +{ + spirit_rethrow( fmt::format( "Could not read pairs file \"{}\"", pairsFile ) ); +} + +// Read from Quadruplet file +void Quadruplets_from_File( + const std::string quadrupletsFile, const std::shared_ptr, int & noq, quadrupletfield & quadruplets, + scalarfield & quadruplet_magnitudes ) noexcept +try +{ + Log( Log_Level::Debug, Log_Sender::IO, + fmt::format( "Reading spin quadruplets from file \"{}\"", quadrupletsFile ) ); + + std::vector columns( 20 ); // at least: 4 (indices) + 3*3 (positions) + 1 (magnitude) + // column indices of pair indices and interactions + int col_i = -1; + int col_j = -1, col_da_j = -1, col_db_j = -1, col_dc_j = -1, periodicity_j = 0; + int col_k = -1, col_da_k = -1, col_db_k = -1, col_dc_k = -1, periodicity_k = 0; + int col_l = -1, col_da_l = -1, col_db_l = -1, col_dc_l = -1, periodicity_l = 0; + int col_Q = -1; + bool Q = false; + int max_periods_a = 0, max_periods_b = 0, max_periods_c = 0; + int quadruplet_periodicity = 0; + int n_quadruplets = 0; + + // Get column indices + Filter_File_Handle file( quadrupletsFile ); + + if( file.Find( "n_interaction_quadruplets" ) ) + { + // Read n interaction quadruplets + file.iss >> n_quadruplets; + Log( Log_Level::Debug, Log_Sender::IO, + fmt::format( "File {} should have {} quadruplets", quadrupletsFile, n_quadruplets ) ); + } + else + { + // Read the whole file + n_quadruplets = (int)1e8; + // First line should contain the columns + file.ResetStream(); + Log( Log_Level::Debug, Log_Sender::IO, + "Trying to parse quadruplet columns from top of file " + quadrupletsFile ); + } - ++i_pair; - }// end while GetLine - Log(Log_Level::Parameter, Log_Sender::IO, fmt::format( - "Done reading {} spin pairs from file \"{}\", giving {} exchange and {} DM (symmetry-reduced) pairs.", - i_pair, pairsFile, exchange_pairs.size(), dmi_pairs.size())); - nop = i_pair; - }// end try - catch( ... ) + file.GetLine(); + for( unsigned int i = 0; i < columns.size(); ++i ) + { + file.iss >> columns[i]; + std::transform( columns[i].begin(), columns[i].end(), columns[i].begin(), ::tolower ); + if( columns[i] == "i" ) + col_i = i; + else if( columns[i] == "j" ) + col_j = i; + else if( columns[i] == "da_j" ) + col_da_j = i; + else if( columns[i] == "db_j" ) + col_db_j = i; + else if( columns[i] == "dc_j" ) + col_dc_j = i; + else if( columns[i] == "k" ) + col_k = i; + else if( columns[i] == "da_k" ) + col_da_k = i; + else if( columns[i] == "db_k" ) + col_db_k = i; + else if( columns[i] == "dc_k" ) + col_dc_k = i; + else if( columns[i] == "l" ) + col_l = i; + else if( columns[i] == "da_l" ) + col_da_l = i; + else if( columns[i] == "db_l" ) + col_db_l = i; + else if( columns[i] == "dc_l" ) + col_dc_l = i; + else if( columns[i] == "q" ) { - spirit_rethrow(fmt::format("Could not read pairs file \"{}\"", pairsFile)); + col_Q = i; + Q = true; } } - - /* - Read from Quadruplet file - */ - void Quadruplets_from_File(const std::string quadrupletsFile, const std::shared_ptr, int & noq, - quadrupletfield & quadruplets, scalarfield & quadruplet_magnitudes) + // Check if interactions have been found in header + if( !Q ) + Log( Log_Level::Warning, Log_Sender::IO, + fmt::format( "No interactions could be found in header of quadruplets file ", quadrupletsFile ) ); + + // Quadruplet Indices + int q_i = 0; + int q_j = 0, q_da_j = 0, q_db_j = 0, q_dc_j = 0; + int q_k = 0, q_da_k = 0, q_db_k = 0, q_dc_k = 0; + int q_l = 0, q_da_l = 0, q_db_l = 0, q_dc_l = 0; + scalar q_Q; + + // Get actual Quadruplets Data + int i_quadruplet = 0; + std::string sdump; + while( file.GetLine() && i_quadruplet < n_quadruplets ) { - Log(Log_Level::Debug, Log_Sender::IO, fmt::format("Reading spin quadruplets from file \"{}\"", quadrupletsFile)); - try + // Read a Quadruplet from the File + for( unsigned int i = 0; i < columns.size(); ++i ) { - std::vector columns(20); // at least: 4 (indices) + 3*3 (positions) + 1 (magnitude) - // column indices of pair indices and interactions - int col_i = -1; - int col_j = -1, col_da_j = -1, col_db_j = -1, col_dc_j = -1, periodicity_j = 0; - int col_k = -1, col_da_k = -1, col_db_k = -1, col_dc_k = -1, periodicity_k = 0; - int col_l = -1, col_da_l = -1, col_db_l = -1, col_dc_l = -1, periodicity_l = 0; - int col_Q = -1; - bool Q = false; - int max_periods_a = 0, max_periods_b = 0, max_periods_c = 0; - int quadruplet_periodicity = 0; - int n_quadruplets = 0; - - // Get column indices - Filter_File_Handle file(quadrupletsFile); - - if( file.Find("n_interaction_quadruplets") ) - { - // Read n interaction quadruplets - file.iss >> n_quadruplets; - Log(Log_Level::Debug, Log_Sender::IO, fmt::format("File {} should have {} quadruplets", quadrupletsFile, n_quadruplets)); - } + // i + if( i == col_i ) + file.iss >> q_i; + // j + else if( i == col_j ) + file.iss >> q_j; + else if( i == col_da_j ) + file.iss >> q_da_j; + else if( i == col_db_j ) + file.iss >> q_db_j; + else if( i == col_dc_j ) + file.iss >> q_dc_j; + // k + else if( i == col_k ) + file.iss >> q_k; + else if( i == col_da_k ) + file.iss >> q_da_k; + else if( i == col_db_k ) + file.iss >> q_db_k; + else if( i == col_dc_k ) + file.iss >> q_dc_k; + // l + else if( i == col_l ) + file.iss >> q_l; + else if( i == col_da_l ) + file.iss >> q_da_l; + else if( i == col_db_l ) + file.iss >> q_db_l; + else if( i == col_dc_l ) + file.iss >> q_dc_l; + // Quadruplet magnitude + else if( i == col_Q && Q ) + file.iss >> q_Q; + // Otherwise dump the line else - { - // Read the whole file - n_quadruplets = (int)1e8; - // First line should contain the columns - file.ResetStream(); - Log(Log_Level::Debug, Log_Sender::IO, "Trying to parse quadruplet columns from top of file " + quadrupletsFile); - } + file.iss >> sdump; + } // end for columns - file.GetLine(); - for (unsigned int i = 0; i < columns.size(); ++i) - { - file.iss >> columns[i]; - std::transform( columns[i].begin(), columns[i].end(), columns[i].begin(), ::tolower ); - if (columns[i] == "i") col_i = i; - else if (columns[i] == "j") col_j = i; - else if (columns[i] == "da_j") col_da_j = i; - else if (columns[i] == "db_j") col_db_j = i; - else if (columns[i] == "dc_j") col_dc_j = i; - else if (columns[i] == "k") col_k = i; - else if (columns[i] == "da_k") col_da_k = i; - else if (columns[i] == "db_k") col_db_k = i; - else if (columns[i] == "dc_k") col_dc_k = i; - else if (columns[i] == "l") col_l = i; - else if (columns[i] == "da_l") col_da_l = i; - else if (columns[i] == "db_l") col_db_l = i; - else if (columns[i] == "dc_l") col_dc_l = i; - else if (columns[i] == "q") { col_Q = i; Q = true; } - } - - // Check if interactions have been found in header - if (!Q) - Log(Log_Level::Warning, Log_Sender::IO, fmt::format( - "No interactions could be found in header of quadruplets file ", quadrupletsFile)); - - // Quadruplet Indices - int q_i = 0; - int q_j = 0, q_da_j = 0, q_db_j = 0, q_dc_j = 0; - int q_k = 0, q_da_k = 0, q_db_k = 0, q_dc_k = 0; - int q_l = 0, q_da_l = 0, q_db_l = 0, q_dc_l = 0; - scalar q_Q; - - // Get actual Quadruplets Data - int i_quadruplet = 0; - std::string sdump; - while (file.GetLine() && i_quadruplet < n_quadruplets) - { - // Read a Quadruplet from the File - for (unsigned int i = 0; i < columns.size(); ++i) - { - // i - if (i == col_i) - file.iss >> q_i; - // j - else if (i == col_j) - file.iss >> q_j; - else if (i == col_da_j) - file.iss >> q_da_j; - else if (i == col_db_j) - file.iss >> q_db_j; - else if (i == col_dc_j) - file.iss >> q_dc_j; - // k - else if (i == col_k) - file.iss >> q_k; - else if (i == col_da_k) - file.iss >> q_da_k; - else if (i == col_db_k) - file.iss >> q_db_k; - else if (i == col_dc_k) - file.iss >> q_dc_k; - // l - else if (i == col_l) - file.iss >> q_l; - else if (i == col_da_l) - file.iss >> q_da_l; - else if (i == col_db_l) - file.iss >> q_db_l; - else if (i == col_dc_l) - file.iss >> q_dc_l; - // Quadruplet magnitude - else if (i == col_Q && Q) - file.iss >> q_Q; - // Otherwise dump the line - else - file.iss >> sdump; - }// end for columns - - - // Add the indices and parameter to the corresponding list - if (q_Q != 0) - { - quadruplets.push_back({ q_i, q_j, q_k, q_l, - { q_da_j, q_db_j, q_dc_j }, - { q_da_k, q_db_k, q_dc_k }, - { q_da_l, q_db_l, q_dc_l } }); - quadruplet_magnitudes.push_back(q_Q); - } - - ++i_quadruplet; - }// end while GetLine - Log(Log_Level::Parameter, Log_Sender::IO, fmt::format("Done reading {} spin quadruplets from file \"{}\"", i_quadruplet, quadrupletsFile)); - noq = i_quadruplet; - }// end try - catch( ... ) + // Add the indices and parameter to the corresponding list + if( q_Q != 0 ) { - spirit_rethrow( fmt::format("Could not read quadruplets from file \"{}\"", quadrupletsFile) ); + quadruplets.push_back( { q_i, + q_j, + q_k, + q_l, + { q_da_j, q_db_j, q_dc_j }, + { q_da_k, q_db_k, q_dc_k }, + { q_da_l, q_db_l, q_dc_l } } ); + quadruplet_magnitudes.push_back( q_Q ); } - } // End Quadruplets_from_File + ++i_quadruplet; + } // end while GetLine + Log( Log_Level::Parameter, Log_Sender::IO, + fmt::format( "Done reading {} spin quadruplets from file \"{}\"", i_quadruplet, quadrupletsFile ) ); + noq = i_quadruplet; +} +catch( ... ) +{ + spirit_rethrow( fmt::format( "Could not read quadruplets from file \"{}\"", quadrupletsFile ) ); +} + +void Defects_from_File( + const std::string defectsFile, int & n_defects, field & defect_sites, intfield & defect_types ) noexcept +try +{ + n_defects = 0; + defect_sites = field( 0 ); + defect_types = intfield( 0 ); - void Defects_from_File(const std::string defectsFile, int & n_defects, - field & defect_sites, intfield & defect_types) + Log( Log_Level::Debug, Log_Sender::IO, fmt::format( "Reading defects from file \"{}\"", defectsFile ) ); + Filter_File_Handle myfile( defectsFile ); + int nod = 0; + + if( myfile.Find( "n_defects" ) ) + { + // Read n interaction pairs + myfile.iss >> nod; + Log( Log_Level::Debug, Log_Sender::IO, fmt::format( "File \"{}\" should have {} defects", defectsFile, nod ) ); + } + else { - n_defects = 0; - defect_sites = field(0); - defect_types = intfield(0); + // Read the whole file + nod = (int)1e8; + // First line should contain the columns + myfile.ResetStream(); + Log( Log_Level::Debug, Log_Sender::IO, + fmt::format( "Trying to parse defects from top of file \"{}\"", defectsFile ) ); + } - try - { - Log(Log_Level::Debug, Log_Sender::IO, fmt::format("Reading defects from file \"{}\"", defectsFile)); - Filter_File_Handle myfile(defectsFile); - int nod = 0; + while( myfile.GetLine() && n_defects < nod ) + { + int _i, _da, _db, _dc, type; + myfile.iss >> _i >> _da >> _db >> _dc >> type; + defect_sites.push_back( { _i, { _da, _db, _dc } } ); + defect_types.push_back( type ); + ++n_defects; + } - if( myfile.Find("n_defects") ) - { - // Read n interaction pairs - myfile.iss >> nod; - Log(Log_Level::Debug, Log_Sender::IO, fmt::format("File \"{}\" should have {} defects", defectsFile, nod)); - } - else - { - // Read the whole file - nod = (int)1e8; - // First line should contain the columns - myfile.ResetStream(); - Log(Log_Level::Debug, Log_Sender::IO, fmt::format("Trying to parse defects from top of file \"{}\"", defectsFile)); - } + Log( Log_Level::Parameter, Log_Sender::IO, + fmt::format( "Done reading {} defects from file \"{}\"", n_defects, defectsFile ) ); +} +catch( ... ) +{ + spirit_rethrow( fmt::format( "Could not read defects file \"{}\"", defectsFile ) ); +} - while( myfile.GetLine() && n_defects < nod ) - { - int _i, _da, _db, _dc, type; - myfile.iss >> _i >> _da >> _db >> _dc >> type; - defect_sites.push_back( {_i, {_da, _db, _dc}} ); - defect_types.push_back(type); - ++n_defects; - } +void Pinned_from_File( + const std::string pinnedFile, int & n_pinned, field & pinned_sites, vectorfield & pinned_spins ) noexcept +try +{ + int nop = 0; + n_pinned = 0; + pinned_sites = field( 0 ); + pinned_spins = vectorfield( 0 ); - Log(Log_Level::Parameter, Log_Sender::IO, fmt::format("Done reading {} defects from file \"{}\"", n_defects, defectsFile)); - } - catch( ... ) - { - spirit_rethrow( fmt::format("Could not read defects file \"{}\"", defectsFile) ); - } - } // End Defects_from_File + Log( Log_Level::Debug, Log_Sender::IO, fmt::format( "Reading pinned sites from file \"{}\"", pinnedFile ) ); + Filter_File_Handle myfile( pinnedFile ); - void Pinned_from_File(const std::string pinnedFile, int & n_pinned, - field & pinned_sites, vectorfield & pinned_spins) + if( myfile.Find( "n_pinned" ) ) { - int nop = 0; - n_pinned = 0; - pinned_sites = field(0); - pinned_spins = vectorfield(0); - try - { - Log(Log_Level::Debug, Log_Sender::IO, fmt::format("Reading pinned sites from file \"{}\"", pinnedFile)); - Filter_File_Handle myfile(pinnedFile); - - if( myfile.Find("n_pinned") ) - { - // Read n interaction pairs - myfile.iss >> nop; - Log(Log_Level::Debug, Log_Sender::IO, fmt::format("File \"{}\" should have {} pinned sites", pinnedFile, nop)); - } - else - { - // Read the whole file - nop = (int)1e8; - // First line should contain the columns - myfile.ResetStream(); - Log(Log_Level::Debug, Log_Sender::IO, fmt::format("Trying to parse pinned sites from top of file \"{}\"", pinnedFile)); - } + // Read n interaction pairs + myfile.iss >> nop; + Log( Log_Level::Debug, Log_Sender::IO, + fmt::format( "File \"{}\" should have {} pinned sites", pinnedFile, nop ) ); + } + else + { + // Read the whole file + nop = (int)1e8; + // First line should contain the columns + myfile.ResetStream(); + Log( Log_Level::Debug, Log_Sender::IO, + fmt::format( "Trying to parse pinned sites from top of file \"{}\"", pinnedFile ) ); + } - while( myfile.GetLine() && n_pinned < nop ) - { - int _i, _da, _db, _dc; - scalar sx, sy, sz; - myfile.iss >> _i >> _da >> _db >> _dc >> sx >> sy >> sz; - pinned_sites.push_back( {_i, {_da, _db, _dc}} ); - pinned_spins.push_back({sx, sy, sz}); - ++n_pinned; - } + while( myfile.GetLine() && n_pinned < nop ) + { + int _i, _da, _db, _dc; + scalar sx, sy, sz; + myfile.iss >> _i >> _da >> _db >> _dc >> sx >> sy >> sz; + pinned_sites.push_back( { _i, { _da, _db, _dc } } ); + pinned_spins.push_back( { sx, sy, sz } ); + ++n_pinned; + } - Log(Log_Level::Parameter, Log_Sender::IO, fmt::format("Done reading {} pinned sites from file \"{}\"", n_pinned, pinnedFile)); - } - catch( ... ) - { - spirit_rethrow( fmt::format("Could not read pinned sites file \"{}\"", pinnedFile) ); - } - } // End Pinned_from_File + Log( Log_Level::Parameter, Log_Sender::IO, + fmt::format( "Done reading {} pinned sites from file \"{}\"", n_pinned, pinnedFile ) ); +} +catch( ... ) +{ + spirit_rethrow( fmt::format( "Could not read pinned sites file \"{}\"", pinnedFile ) ); +} +int ReadHeaderLine( FILE * fp, char * line ) +{ + char c; + int pos = 0; - int ReadHeaderLine(FILE * fp, char * line) + do { - char c; - int pos=0; + c = (char)fgetc( fp ); // Get current char and move pointer to the next position + if( c != EOF && c != '\n' ) + line[pos++] = c; // If it's not the end of the file + } while( c != EOF && c != '\n' ); // If it's not the end of the file or end of the line - do - { - c = (char)fgetc(fp); // Get current char and move pointer to the next position - if (c != EOF && c != '\n') line[pos++] = c; // If it's not the end of the file - } - while(c != EOF && c != '\n'); // If it's not the end of the file or end of the line + line[pos] = 0; // Complete the read line + if( ( pos == 0 || line[0] != '#' ) && c != EOF ) + return ReadHeaderLine( fp, line ); // Recursive call for ReadHeaderLine if the current line is empty - line[pos] = 0; // Complete the read line - if ((pos==0 || line[0]!='#') && c != EOF) - return ReadHeaderLine(fp, line);// Recursive call for ReadHeaderLine if the current line is empty + // The last symbol is the line end symbol + return pos - 1; +} - // The last symbol is the line end symbol - return pos-1; - } +void ReadDataLine( FILE * fp, char * line ) +{ + char c; + int pos = 0; - void ReadDataLine(FILE * fp, char * line) + do { - char c; - int pos=0; + c = (char)fgetc( fp ); + if( c != EOF && c != '\n' ) + line[pos++] = c; + } while( c != EOF && c != '\n' ); - do - { - c = (char)fgetc(fp); - if (c != EOF && c != '\n') line[pos++] = c; - } - while(c != EOF && c != '\n'); + line[pos] = 0; +} - line[pos] = 0; - } -}// end namespace IO \ No newline at end of file +} // namespace IO \ No newline at end of file diff --git a/core/src/io/Datawriter.cpp b/core/src/io/Datawriter.cpp index 2fae1f9a0..cd5d46181 100644 --- a/core/src/io/Datawriter.cpp +++ b/core/src/io/Datawriter.cpp @@ -1,20 +1,20 @@ -#include +#include #include +#include #include -#include #include #include -#include -#include -#include -#include -#include +#include + #include -#include #include - -#include +#include +#include +#include +#include +#include +#include #ifdef CORE_USE_THREADS #include @@ -22,256 +22,273 @@ namespace IO { - void Write_Neighbours_Exchange( const Data::Spin_System& system, const std::string filename ) + +void Write_Neighbours_Exchange( const Data::Spin_System & system, const std::string filename ) +{ + Engine::Hamiltonian_Heisenberg * ham = (Engine::Hamiltonian_Heisenberg *)system.hamiltonian.get(); + + int n_neighbours = ham->exchange_pairs.size(); + +#if defined( SPIRIT_USE_OPENMP ) + // When parallelising (cuda or openmp), all neighbours per spin are already there + const bool mirror_neighbours = false; +#else + // When running on a single thread, we need to re-create redundant neighbours + const bool mirror_neighbours = true; + n_neighbours *= 2; +#endif + + std::string output; + output.reserve( int( 0x02000000 ) ); // reserve 32[MByte] + + output += "### Interaction neighbours:\n"; + output += fmt::format( "n_neighbours_exchange {}\n", n_neighbours ); + + if( ham->exchange_pairs.size() > 0 ) { - Engine::Hamiltonian_Heisenberg* ham = - (Engine::Hamiltonian_Heisenberg *) system.hamiltonian.get(); - int n_neighbours = ham->exchange_pairs.size(); - - #if defined(SPIRIT_USE_OPENMP) - // When parallelising (cuda or openmp), all neighbours per spin are already there - const bool mirror_neighbours = false; - #else - // When running on a single thread, we need to re-create redundant neighbours - const bool mirror_neighbours = true; - n_neighbours *= 2; - #endif - - std::string output; - output.reserve( int( 0x02000000 ) ); // reserve 32[MByte] - - output += "### Interaction neighbours:\n"; - output += fmt::format( "n_neighbours_exchange {}\n", n_neighbours ); - - if (ham->exchange_pairs.size() > 0) + output += fmt::format( "{:^3} {:^3} {:^3} {:^3} {:^3} {:^15}\n", "i", "j", "da", "db", "dc", "Jij" ); + for( unsigned int i = 0; i < ham->exchange_pairs.size(); ++i ) { - output += fmt::format( "{:^3} {:^3} {:^3} {:^3} {:^3} {:^15}\n", - "i", "j", "da", "db", "dc", "Jij" ); - for (unsigned int i=0; iexchange_pairs.size(); ++i) + output += fmt::format( + "{:^3} {:^3} {:^3} {:^3} {:^3} {:^15.8f}\n", ham->exchange_pairs[i].i, ham->exchange_pairs[i].j, + ham->exchange_pairs[i].translations[0], ham->exchange_pairs[i].translations[1], + ham->exchange_pairs[i].translations[2], ham->exchange_magnitudes[i] ); + if( mirror_neighbours ) { - output += fmt::format( "{:^3} {:^3} {:^3} {:^3} {:^3} {:^15.8f}\n", - ham->exchange_pairs[i].i, ham->exchange_pairs[i].j, - ham->exchange_pairs[i].translations[0], ham->exchange_pairs[i].translations[1], - ham->exchange_pairs[i].translations[2], ham->exchange_magnitudes[i] ); - if( mirror_neighbours ) - { - // Mirrored interactions - output += fmt::format( "{:^3} {:^3} {:^3} {:^3} {:^3} {:^15.8f}\n", - ham->exchange_pairs[i].j, ham->exchange_pairs[i].i, - (-1) * ham->exchange_pairs[i].translations[0], - (-1) * ham->exchange_pairs[i].translations[1], - (-1) * ham->exchange_pairs[i].translations[2], - ham->exchange_magnitudes[i] ); - } + // Mirrored interactions + output += fmt::format( + "{:^3} {:^3} {:^3} {:^3} {:^3} {:^15.8f}\n", ham->exchange_pairs[i].j, + ham->exchange_pairs[i].i, ( -1 ) * ham->exchange_pairs[i].translations[0], + ( -1 ) * ham->exchange_pairs[i].translations[1], ( -1 ) * ham->exchange_pairs[i].translations[2], + ham->exchange_magnitudes[i] ); } } - - Dump_to_File( output, filename ); } - void Write_Neighbours_DMI( const Data::Spin_System& system, const std::string filename ) + Dump_to_File( output, filename ); +} + +void Write_Neighbours_DMI( const Data::Spin_System & system, const std::string filename ) +{ + Engine::Hamiltonian_Heisenberg * ham = (Engine::Hamiltonian_Heisenberg *)system.hamiltonian.get(); + + int n_neighbours = ham->dmi_pairs.size(); + +#if defined( SPIRIT_USE_OPENMP ) + // When parallelising (cuda or openmp), all neighbours per spin are already there + const bool mirror_neighbours = false; +#else + // When running on a single thread, we need to re-create redundant neighbours + const bool mirror_neighbours = true; + n_neighbours *= 2; +#endif + + std::string output; + output.reserve( int( 0x02000000 ) ); // reserve 32[MByte] + + output += "### Interaction neighbours:\n"; + output += fmt::format( "n_neighbours_dmi {}\n", n_neighbours ); + + if( ham->dmi_pairs.size() > 0 ) { - Engine::Hamiltonian_Heisenberg* ham = - (Engine::Hamiltonian_Heisenberg *) system.hamiltonian.get(); - int n_neighbours = ham->dmi_pairs.size(); - - #if defined(SPIRIT_USE_OPENMP) - // When parallelising (cuda or openmp), all neighbours per spin are already there - const bool mirror_neighbours = false; - #else - // When running on a single thread, we need to re-create redundant neighbours - const bool mirror_neighbours = true; - n_neighbours *= 2; - #endif - - std::string output; - output.reserve( int( 0x02000000 ) ); // reserve 32[MByte] - - output += "### Interaction neighbours:\n"; - output += fmt::format( "n_neighbours_dmi {}\n", n_neighbours ); - - if (ham->dmi_pairs.size() > 0) + output += fmt::format( + "{:^3} {:^3} {:^3} {:^3} {:^3} {:^15} {:^15} {:^15} {:^15}\n", "i", "j", "da", "db", "dc", "Dij", + "Dijx", "Dijy", "Dijz" ); + for( unsigned int i = 0; i < ham->dmi_pairs.size(); ++i ) { output += fmt::format( - "{:^3} {:^3} {:^3} {:^3} {:^3} {:^15} {:^15} {:^15} {:^15}\n", - "i", "j", "da", "db", "dc", "Dij", "Dijx", "Dijy", "Dijz"); - for (unsigned int i = 0; idmi_pairs.size(); ++i) + "{:^3} {:^3} {:^3} {:^3} {:^3} {:^15.8f} {:^15.8f} {:^15.8f} {:^15.8f}\n", ham->dmi_pairs[i].i, + ham->dmi_pairs[i].j, ham->dmi_pairs[i].translations[0], ham->dmi_pairs[i].translations[1], + ham->dmi_pairs[i].translations[2], ham->dmi_magnitudes[i], ham->dmi_normals[i][0], + ham->dmi_normals[i][1], ham->dmi_normals[i][2] ); + if( mirror_neighbours ) { + // Mirrored interactions output += fmt::format( "{:^3} {:^3} {:^3} {:^3} {:^3} {:^15.8f} {:^15.8f} {:^15.8f} {:^15.8f}\n", - ham->dmi_pairs[i].i, ham->dmi_pairs[i].j, - ham->dmi_pairs[i].translations[0], ham->dmi_pairs[i].translations[1], - ham->dmi_pairs[i].translations[2], ham->dmi_magnitudes[i], - ham->dmi_normals[i][0], ham->dmi_normals[i][1], ham->dmi_normals[i][2]); - if( mirror_neighbours ) - { - // Mirrored interactions - output += fmt::format( - "{:^3} {:^3} {:^3} {:^3} {:^3} {:^15.8f} {:^15.8f} {:^15.8f} {:^15.8f}\n", - ham->dmi_pairs[i].j, ham->dmi_pairs[i].i, (-1) * ham->dmi_pairs[i].translations[0], - (-1) * ham->dmi_pairs[i].translations[1], (-1) * ham->dmi_pairs[i].translations[2], - ham->dmi_magnitudes[i], (-1) * ham->dmi_normals[i][0], - (-1) * ham->dmi_normals[i][1], (-1) * ham->dmi_normals[i][2]); - } + ham->dmi_pairs[i].j, ham->dmi_pairs[i].i, ( -1 ) * ham->dmi_pairs[i].translations[0], + ( -1 ) * ham->dmi_pairs[i].translations[1], ( -1 ) * ham->dmi_pairs[i].translations[2], + ham->dmi_magnitudes[i], ( -1 ) * ham->dmi_normals[i][0], ( -1 ) * ham->dmi_normals[i][1], + ( -1 ) * ham->dmi_normals[i][2] ); } } - - Dump_to_File( output, filename ); } - void Write_Energy_Header( const Data::Spin_System & s, const std::string filename, - std::vector firstcolumns, bool contributions, - bool normalize_by_nos, bool readability_toggle ) + Dump_to_File( output, filename ); +} + +void Write_Energy_Header( + const Data::Spin_System & s, const std::string filename, std::vector firstcolumns, bool contributions, + bool normalize_by_nos, bool readability_toggle ) +{ + std::string separator = ""; + std::string line = ""; + for( unsigned int i = 0; i < firstcolumns.size(); ++i ) { - std::string separator = ""; - std::string line = ""; - for (unsigned int i=0; inos; + else + nd = 1; + + Write_Energy_Header( *c.images[0], filename, { "image", "Rx", "E_tot" } ); + + for( isystem = 0; isystem < (int)c.noi; ++isystem ) + { + auto & system = *c.images[isystem]; + std::string line + = fmt::format( " {:^20} || {:^20.10f} || {:^20.10f} |", isystem, c.Rx[isystem], system.E * nd ); + for( auto pair : system.E_array ) { - line += fmt::format("| {:^20.10f} ", pair.second * nd); + line += fmt::format( "| {:^20.10f} ", pair.second * nd ); } line += "\n"; - if (!readability_toggle) std::replace( line.begin(), line.end(), '|', ' '); - Append_String_to_File(line, filename); + if( !readability_toggle ) + std::replace( line.begin(), line.end(), '|', ' ' ); + Append_String_to_File( line, filename ); } +} - void Write_Chain_Energies( const Data::Spin_System_Chain & c, const int iteration, - const std::string filename, bool normalize_by_nos, - bool readability_toggle ) - { - int isystem; - scalar nd = 1.0; // nos divide - if (normalize_by_nos) nd = 1.0 / c.images[0]->nos; - else nd = 1; +void Write_Chain_Energies_Interpolated( + const Data::Spin_System_Chain & chain, const std::string filename, bool normalize_by_nos, bool readability_toggle ) +{ + int isystem, iinterp, idx; + scalar nd = 1.0; // nos divide + if( normalize_by_nos ) + nd = 1.0 / chain.images[0]->nos; + else + nd = 1; - Write_Energy_Header(*c.images[0], filename, {"image", "Rx", "E_tot"}); + Write_Energy_Header( *chain.images[0], filename, { "image", "iinterp", "Rx", "E_tot" } ); - for (isystem = 0; isystem < (int)c.noi; ++isystem) + for( isystem = 0; isystem < (int)chain.noi; ++isystem ) + { + auto & system = *chain.images[isystem]; + + for( iinterp = 0; iinterp < chain.gneb_parameters->n_E_interpolations + 1; ++iinterp ) { - auto& system = *c.images[isystem]; - std::string line = fmt::format(" {:^20} || {:^20.10f} || {:^20.10f} |", isystem, - c.Rx[isystem], system.E * nd ); - for (auto pair : system.E_array) + idx = isystem * ( chain.gneb_parameters->n_E_interpolations + 1 ) + iinterp; + std::string line = fmt::format( + " {:^20} || {:^20} || {:^20.10f} || {:^20.10f} ||", isystem, iinterp, chain.Rx_interpolated[idx], + chain.E_interpolated[idx] * nd ); + + // TODO: interpolated Energy contributions + bool first = true; + for( int p = 0; p < system.E_array.size(); p++ ) { - line += fmt::format("| {:^20.10f} ", pair.second * nd); + if( first ) + first = false; + else + line += "|"; + + line += fmt::format( " {:^20.10f} ", chain.E_array_interpolated[p][idx] * nd ); } line += "\n"; - if (!readability_toggle) std::replace( line.begin(), line.end(), '|', ' '); - Append_String_to_File(line, filename); - } - } - - void Write_Chain_Energies_Interpolated( const Data::Spin_System_Chain & chain, - const std::string filename, bool normalize_by_nos, - bool readability_toggle ) - { - int isystem, iinterp, idx; - scalar nd = 1.0; // nos divide - if (normalize_by_nos) nd = 1.0 / chain.images[0]->nos; - else nd = 1; - - Write_Energy_Header(*chain.images[0], filename, {"image", "iinterp", "Rx", "E_tot"}); + // Whether to use space or | as column separator + if( !readability_toggle ) + std::replace( line.begin(), line.end(), '|', ' ' ); - for (isystem = 0; isystem < (int)chain.noi; ++isystem) - { - auto& system = *chain.images[isystem]; + // Write + Append_String_to_File( line, filename ); - for (iinterp = 0; iinterp < chain.gneb_parameters->n_E_interpolations+1; ++iinterp) - { - idx = isystem * (chain.gneb_parameters->n_E_interpolations+1) + iinterp; - std::string line = fmt::format(" {:^20} || {:^20} || {:^20.10f} || {:^20.10f} ||", - isystem, iinterp, chain.Rx_interpolated[idx], - chain.E_interpolated[idx] * nd ); - - // TODO: interpolated Energy contributions - bool first = true; - for (int p=0; p -#include +#include +#include #include -#include #include #include -#include -#include -#include -#include #include -#include - -using namespace Utility; +#include +#include namespace IO { - Filter_File_Handle::Filter_File_Handle( const std::string& filename, - const std::string comment_tag ) : - filename(filename), comment_tag(comment_tag), iss("") - { - this->dump = ""; - this->line = ""; - this->found = std::string::npos; - this->myfile = std::unique_ptr( new std::ifstream( filename, - std::ios::in | std::ios::binary ) ); - //this->position = this->myfile->tellg(); - - // find begging and end positions of the file stream indicator - this->position_file_beg = this->myfile->tellg(); - this->myfile->seekg( 0, std::ios::end ); - this->position_file_end = this->myfile->tellg(); - this->myfile->seekg( 0, std::ios::beg ); - - // set limits of the file stream indicator to begging and end positions (eq. ResetLimits()) - this->position_start = this->position_file_beg; - this->position_stop = this->position_file_end; - - // find begging and end positions of the file stream indicator - this->position_file_beg = this->myfile->tellg(); - this->myfile->seekg( 0, std::ios::end ); - this->position_file_end = this->myfile->tellg(); - this->myfile->seekg( 0, std::ios::beg ); - - // set limits of the file stream indicator to begging and end positions (eq. ResetLimits()) - this->position_start = this->position_file_beg; - this->position_stop = this->position_file_end; - - // initialize number of lines - this->n_lines = 0; - this->n_comment_lines = 0; - - // if the file is not open - if( !this->myfile->is_open() ) - spirit_throw(Exception_Classifier::File_not_Found, Log_Level::Error, fmt::format("Could not open file \"{}\"", filename)); - } - Filter_File_Handle::~Filter_File_Handle() - { - myfile->close(); - } +Filter_File_Handle::Filter_File_Handle( const std::string & filename, const std::string comment_tag ) + : filename( filename ), comment_tag( comment_tag ), iss( "" ) +{ + this->dump = ""; + this->line = ""; + this->found = std::string::npos; + this->myfile = std::unique_ptr( new std::ifstream( filename, std::ios::in | std::ios::binary ) ); + // this->position = this->myfile->tellg(); + + // find begging and end positions of the file stream indicator + this->position_file_beg = this->myfile->tellg(); + this->myfile->seekg( 0, std::ios::end ); + this->position_file_end = this->myfile->tellg(); + this->myfile->seekg( 0, std::ios::beg ); + + // set limits of the file stream indicator to begging and end positions (eq. ResetLimits()) + this->position_start = this->position_file_beg; + this->position_stop = this->position_file_end; + + // find begging and end positions of the file stream indicator + this->position_file_beg = this->myfile->tellg(); + this->myfile->seekg( 0, std::ios::end ); + this->position_file_end = this->myfile->tellg(); + this->myfile->seekg( 0, std::ios::beg ); + + // set limits of the file stream indicator to begging and end positions (eq. ResetLimits()) + this->position_start = this->position_file_beg; + this->position_stop = this->position_file_end; + + // initialize number of lines + this->n_lines = 0; + this->n_comment_lines = 0; + + // if the file is not open + if( !this->myfile->is_open() ) + spirit_throw( + Utility::Exception_Classifier::File_not_Found, Utility::Log_Level::Error, + fmt::format( "Could not open file \"{}\"", filename ) ); +} + +Filter_File_Handle::~Filter_File_Handle() +{ + myfile->close(); +} - std::ios::pos_type Filter_File_Handle::GetPosition( std::ios::seekdir dir ) - { - this->myfile->seekg( 0, dir ); - return this->myfile->tellg(); - } +std::ios::pos_type Filter_File_Handle::GetPosition( std::ios::seekdir dir ) +{ + this->myfile->seekg( 0, dir ); + return this->myfile->tellg(); +} - void Filter_File_Handle::SetLimits( const std::ios::pos_type start, - const std::ios::pos_type stop ) - { - this->position_start = start; - this->position_stop = stop; - } +void Filter_File_Handle::SetLimits( const std::ios::pos_type start, const std::ios::pos_type stop ) +{ + this->position_start = start; + this->position_stop = stop; +} - void Filter_File_Handle::ResetLimits() - { - this->position_start = this->position_file_beg; - this->position_stop = this->position_file_end; - } +void Filter_File_Handle::ResetLimits() +{ + this->position_start = this->position_file_beg; + this->position_stop = this->position_file_end; +} - bool Filter_File_Handle::GetLine_Handle( const std::string str_to_remove ) +bool Filter_File_Handle::GetLine_Handle( const std::string str_to_remove ) +{ + this->line = ""; + + // if there is a next line + if( (bool)getline( *this->myfile, this->line ) ) { - this->line = ""; + this->n_lines++; + + // remove separator characters + Remove_Chars_From_String( this->line, (char *)"|+" ); - // if there is a next line - if( (bool) getline( *this->myfile, this->line ) ) + // remove any unwanted str from the line eg. delimiters + if( str_to_remove != "" ) + Remove_Chars_From_String( this->line, str_to_remove.c_str() ); + + // if the string does not start with a comment identifier + if( Remove_Comments_From_String( this->line ) ) { - this->n_lines++; - - // remove separator characters - Remove_Chars_From_String( this->line, (char *) "|+" ); - - // remove any unwanted str from the line eg. delimiters - if( str_to_remove != "" ) - Remove_Chars_From_String( this->line, str_to_remove.c_str() ); - - // if the string does not start with a comment identifier - if( Remove_Comments_From_String( this->line ) ) - { - return true; - } - else - { - this->n_comment_lines++; - return GetLine( str_to_remove ); - } + return true; } - return false; // if there is no next line, return false - } - - bool Filter_File_Handle::GetLine( const std::string str_to_remove ) - { - if( Filter_File_Handle::GetLine_Handle(str_to_remove) ) + else { - return Filter_File_Handle::Find_in_Line(""); + this->n_comment_lines++; + return GetLine( str_to_remove ); } - return false; } + return false; // if there is no next line, return false +} - void Filter_File_Handle::ResetStream() +bool Filter_File_Handle::GetLine( const std::string str_to_remove ) +{ + if( Filter_File_Handle::GetLine_Handle( str_to_remove ) ) { - myfile->clear(); - myfile->seekg(0, std::ios::beg); + return Filter_File_Handle::Find_in_Line( "" ); } + return false; +} + +void Filter_File_Handle::ResetStream() +{ + myfile->clear(); + myfile->seekg( 0, std::ios::beg ); +} - bool Filter_File_Handle::Find(const std::string & keyword, bool ignore_case) +bool Filter_File_Handle::Find( const std::string & keyword, bool ignore_case ) +{ + myfile->clear(); + // myfile->seekg( this->position_file_beg, std::ios::beg); + myfile->seekg( this->position_start ); + + while( GetLine() && ( GetPosition() <= this->position_stop ) ) { - myfile->clear(); - //myfile->seekg( this->position_file_beg, std::ios::beg); - myfile->seekg( this->position_start ); + if( Find_in_Line( keyword, ignore_case ) ) + return true; + } - while( GetLine() && (GetPosition() <= this->position_stop) ) - { - if( Find_in_Line(keyword, ignore_case) ) - return true; - } + return false; +} - return false; +bool Filter_File_Handle::Find_in_Line( const std::string & keyword, bool ignore_case ) +{ + std::string decap_keyword = keyword; + std::string decap_line = this->line; + if( ignore_case ) + { + std::transform( decap_keyword.begin(), decap_keyword.end(), decap_keyword.begin(), ::tolower ); + std::transform( decap_line.begin(), decap_line.end(), decap_line.begin(), ::tolower ); } - bool Filter_File_Handle::Find_in_Line( const std::string & keyword, bool ignore_case ) + // if s is found in line + if( !decap_line.compare( 0, decap_keyword.size(), decap_keyword ) ) { - std::string decap_keyword = keyword; - std::string decap_line = this->line; - if( ignore_case ) - { - std::transform( decap_keyword.begin(), decap_keyword.end(), decap_keyword.begin(), ::tolower ); - std::transform( decap_line.begin(), decap_line.end(), decap_line.begin(), ::tolower ); - } + this->iss.clear(); // empty the stream + this->iss.str( this->line ); // copy line into the iss stream - // if s is found in line - if( !decap_line.compare( 0, decap_keyword.size(), decap_keyword ) ) + // if s is not empty + if( keyword != "" ) { - this->iss.clear(); // empty the stream - this->iss.str(this->line); // copy line into the iss stream - - // if s is not empty - if( keyword != "" ) - { - int n_words = Count_Words( keyword ); - for( int i = 0; i < n_words; i++ ) - this->iss >> dump; - } - - return true; + int n_words = Count_Words( keyword ); + for( int i = 0; i < n_words; i++ ) + this->iss >> dump; } - return false; + + return true; } + return false; +} - void Filter_File_Handle::Remove_Chars_From_String(std::string &str, const char* charsToRemove) +void Filter_File_Handle::Remove_Chars_From_String( std::string & str, const char * charsToRemove ) +{ + for( unsigned int i = 0; i < strlen( charsToRemove ); ++i ) { - for( unsigned int i = 0; i < strlen(charsToRemove); ++i ) - { - str.erase(std::remove(str.begin(), str.end(), charsToRemove[i]), str.end()); - } + str.erase( std::remove( str.begin(), str.end(), charsToRemove[i] ), str.end() ); } +} - bool Filter_File_Handle::Remove_Comments_From_String( std::string &str ) - { - std::string::size_type start = this->line.find( this->comment_tag ); +bool Filter_File_Handle::Remove_Comments_From_String( std::string & str ) +{ + std::string::size_type start = this->line.find( this->comment_tag ); - // if the line starts with a comment return false - if( start == 0 ) return false; + // if the line starts with a comment return false + if( start == 0 ) + return false; - // if the line has a comment somewhere remove it by trimming - if( start != std::string::npos ) - line.erase( this->line.begin() + start , this->line.end() ); + // if the line has a comment somewhere remove it by trimming + if( start != std::string::npos ) + line.erase( this->line.begin() + start, this->line.end() ); - // return true - return true; - } + // return true + return true; +} - void Filter_File_Handle::Read_String( std::string& var, std::string keyword, bool log_notfound ) +void Filter_File_Handle::Read_String( std::string & var, std::string keyword, bool log_notfound ) +{ + if( Find( keyword ) ) { - if( Find(keyword) ) - { - getline( this->iss, var ); + getline( this->iss, var ); - // trim leading and trailing whitespaces - size_t start = var.find_first_not_of(" \t\n\r\f\v"); - size_t end = var.find_last_not_of(" \t\n\r\f\v"); - if( start != std::string::npos ) - var = var.substr( start, ( end - start + 1 ) ); - } - else if( log_notfound ) - Log( Utility::Log_Level::Warning, Utility::Log_Sender::IO, - fmt::format( "Keyword \"{}\" not found. Using Default: \"{}\"", keyword, var ) ); + // trim leading and trailing whitespaces + size_t start = var.find_first_not_of( " \t\n\r\f\v" ); + size_t end = var.find_last_not_of( " \t\n\r\f\v" ); + if( start != std::string::npos ) + var = var.substr( start, ( end - start + 1 ) ); } + else if( log_notfound ) + Log( Utility::Log_Level::Warning, Utility::Log_Sender::IO, + fmt::format( "Keyword \"{}\" not found. Using Default: \"{}\"", keyword, var ) ); +} - int Filter_File_Handle::Count_Words( const std::string& phrase ) +int Filter_File_Handle::Count_Words( const std::string & phrase ) +{ + std::istringstream phrase_stream( phrase ); + this->dump = ""; + int words = 0; + while( phrase_stream >> dump ) + ++words; + return words; +} + +int Filter_File_Handle::Get_N_Non_Comment_Lines() +{ + while( GetLine() ) { - std::istringstream phrase_stream( phrase ); - this->dump = ""; - int words = 0; - while( phrase_stream >> dump ) - ++words; - return words; - } + }; + ResetLimits(); + return ( this->n_lines - this->n_comment_lines ); +} - int Filter_File_Handle::Get_N_Non_Comment_Lines() - { - while( GetLine() ) { }; - ResetLimits(); - return ( this->n_lines - this->n_comment_lines ); - } -}// end namespace IO +} // namespace IO \ No newline at end of file diff --git a/core/src/io/IO.cpp b/core/src/io/IO.cpp index d04aa5bb2..a4b9799b3 100644 --- a/core/src/io/IO.cpp +++ b/core/src/io/IO.cpp @@ -1,98 +1,86 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - #include #include +#include +#include +#include + using Utility::Log_Level; using Utility::Log_Sender; namespace IO { - // ------ Saving Helpers -------------------------------------------- - /* - Dump_to_File detaches a thread which writes the given string to a file. - This is asynchronous (i.e. fire & forget) - */ - void Dump_to_File(const std::string text, const std::string name) - { - - #ifdef CORE_USE_THREADS - // thread: method args args args detatch thread - std::thread(String_to_File, text, name).detach(); - #else - String_to_File(text, name); - #endif - } +/* +Dump_to_File detaches a thread which writes the given string to a file. +This is asynchronous (i.e. fire & forget) +*/ +void Dump_to_File( const std::string text, const std::string name ) +{ +#ifdef CORE_USE_THREADS + // thread: method args args args detatch thread + std::thread( String_to_File, text, name ).detach(); +#else + String_to_File( text, name ); +#endif +} - void Dump_to_File(const std::vector text, const std::string name, const int no) - { - #ifdef CORE_USE_THREADS - std::thread(Strings_to_File, text, name, no).detach(); - #else - Strings_to_File(text, name, no); - #endif - } +void Dump_to_File( const std::vector text, const std::string name, const int no ) +{ +#ifdef CORE_USE_THREADS + std::thread( Strings_to_File, text, name, no ).detach(); +#else + Strings_to_File( text, name, no ); +#endif +} - /* - String_to_File is a simple string streamer - Writing a vector of strings to file - */ - void Strings_to_File(const std::vector text, const std::string name, const int no) - { +/* +String_to_File is a simple string streamer +Writing a vector of strings to file +*/ +void Strings_to_File( const std::vector text, const std::string name, const int no ) +{ - std::ofstream myfile; - myfile.open(name); - if (myfile.is_open()) - { - Log(Log_Level::Debug, Log_Sender::All, "Started writing " + name); - for (int i = 0; i < no; ++i) { - myfile << text[i]; - } - myfile.close(); - Log(Log_Level::Debug, Log_Sender::All, "Finished writing " + name); - } - else + std::ofstream myfile; + myfile.open( name ); + if( myfile.is_open() ) + { + Log( Log_Level::Debug, Log_Sender::All, "Started writing " + name ); + for( int i = 0; i < no; ++i ) { - Log(Log_Level::Error, Log_Sender::All, "Could not open " + name + " to write to file"); + myfile << text[i]; } + myfile.close(); + Log( Log_Level::Debug, Log_Sender::All, "Finished writing " + name ); } - - void Append_String_to_File(const std::string text, const std::string name) + else { - std::ofstream myfile; - myfile.open(name, std::ofstream::out | std::ofstream::app); - if (myfile.is_open()) - { - Log(Log_Level::Debug, Log_Sender::All, "Started writing " + name); - myfile << text; - myfile.close(); - Log(Log_Level::Debug, Log_Sender::All, "Finished writing " + name); - } - else - { - Log(Log_Level::Error, Log_Sender::All, "Could not open " + name + " to append to file"); - } + Log( Log_Level::Error, Log_Sender::All, "Could not open " + name + " to write to file" ); } +} - void String_to_File(const std::string text, const std::string name) +void Append_String_to_File( const std::string text, const std::string name ) +{ + std::ofstream myfile; + myfile.open( name, std::ofstream::out | std::ofstream::app ); + if( myfile.is_open() ) { - std::vector v(1); - v[0] = text; - Strings_to_File(v, name, 1); + Log( Log_Level::Debug, Log_Sender::All, "Started writing " + name ); + myfile << text; + myfile.close(); + Log( Log_Level::Debug, Log_Sender::All, "Finished writing " + name ); } + else + { + Log( Log_Level::Error, Log_Sender::All, "Could not open " + name + " to append to file" ); + } +} + +void String_to_File( const std::string text, const std::string name ) +{ + std::vector v( 1 ); + v[0] = text; + Strings_to_File( v, name, 1 ); +} - // ------------------------------------------------------------------ -} \ No newline at end of file +} // namespace IO \ No newline at end of file diff --git a/core/src/io/OVF_File.cpp b/core/src/io/OVF_File.cpp index 24d62c39b..374163b11 100644 --- a/core/src/io/OVF_File.cpp +++ b/core/src/io/OVF_File.cpp @@ -3,144 +3,157 @@ namespace IO { - OVF_Segment::OVF_Segment() - { - ovf_segment_initialize(this); - } - OVF_Segment::OVF_Segment(const Data::Spin_System & system) - { - ovf_segment_initialize(this); - - auto& geometry = *system.geometry; - - this->valuedim = 0; - this->valuelabels = const_cast(""); - this->valueunits = const_cast(""); - this->meshtype = const_cast("rectangular"); - this->meshunit = const_cast("nm"); - this->n_cells[0] = geometry.n_cells[0] * geometry.n_cell_atoms; - this->n_cells[1] = geometry.n_cells[1]; - this->n_cells[2] = geometry.n_cells[2]; - this->N = geometry.nos; - this->bounds_min[0] = geometry.bounds_min[0]*0.1; - this->bounds_min[1] = geometry.bounds_min[1]*0.1; - this->bounds_min[2] = geometry.bounds_min[2]*0.1; - this->bounds_max[0] = geometry.bounds_max[0]*0.1; - this->bounds_max[1] = geometry.bounds_max[1]*0.1; - this->bounds_max[2] = geometry.bounds_max[2]*0.1; - this->origin[0] = 0; - this->origin[1] = 0; - this->origin[2] = 0; - this->step_size[0] = geometry.lattice_constant * geometry.bravais_vectors[0][0]*0.1; - this->step_size[1] = geometry.lattice_constant * geometry.bravais_vectors[1][1]*0.1; - this->step_size[2] = geometry.lattice_constant * geometry.bravais_vectors[2][2]*0.1; - } +OVF_Segment::OVF_Segment() +{ + ovf_segment_initialize( this ); +} - OVF_Segment::~OVF_Segment() - { - // TODO: create ovf_segment_free - // free(this->title); - // free(this->comment); - // free(this->meshunit); - // free(this->meshtype); - // free(this->valuelabels); - // free(this->valueunits); - // free(this->meshunit); - // free(this->n_cells); - // free(this->step_size); - // free(this->bounds_min); - // free(this->bounds_max); - // free(this->origin); - } +OVF_Segment::OVF_Segment( const Data::Spin_System & system ) +{ + ovf_segment_initialize( this ); + auto & geometry = *system.geometry; - OVF_File::OVF_File(const std::string & filename, bool should_exist) - { - ovf_file_initialize(this, filename.c_str()); - - if( !this->found && should_exist ) - { - spirit_throw( Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, - fmt::format( "Unable open file \"{}\", are you sure it exists? Message: {}", - filename, this->latest_message() ) ); - } - } + this->valuedim = 0; + this->valuelabels = const_cast( "" ); + this->valueunits = const_cast( "" ); + this->meshtype = const_cast( "rectangular" ); + this->meshunit = const_cast( "nm" ); + this->n_cells[0] = geometry.n_cells[0] * geometry.n_cell_atoms; + this->n_cells[1] = geometry.n_cells[1]; + this->n_cells[2] = geometry.n_cells[2]; + this->N = geometry.nos; + this->bounds_min[0] = geometry.bounds_min[0] * 0.1; + this->bounds_min[1] = geometry.bounds_min[1] * 0.1; + this->bounds_min[2] = geometry.bounds_min[2] * 0.1; + this->bounds_max[0] = geometry.bounds_max[0] * 0.1; + this->bounds_max[1] = geometry.bounds_max[1] * 0.1; + this->bounds_max[2] = geometry.bounds_max[2] * 0.1; + this->origin[0] = 0; + this->origin[1] = 0; + this->origin[2] = 0; + this->step_size[0] = geometry.lattice_constant * geometry.bravais_vectors[0][0] * 0.1; + this->step_size[1] = geometry.lattice_constant * geometry.bravais_vectors[1][1] * 0.1; + this->step_size[2] = geometry.lattice_constant * geometry.bravais_vectors[2][2] * 0.1; +} - OVF_File::~OVF_File() - { - ovf_close(this); - } +OVF_Segment::~OVF_Segment() +{ + // TODO: create ovf_segment_free + // free(this->title); + // free(this->comment); + // free(this->meshunit); + // free(this->meshtype); + // free(this->valuelabels); + // free(this->valueunits); + // free(this->meshunit); + // free(this->n_cells); + // free(this->step_size); + // free(this->bounds_min); + // free(this->bounds_max); + // free(this->origin); +} - const char * OVF_File::latest_message() +OVF_File::OVF_File( const std::string & filename, bool should_exist ) +{ + ovf_file_initialize( this, filename.c_str() ); + + if( !this->found && should_exist ) { - return ovf_latest_message(this); + spirit_throw( + Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, + fmt::format( + "Unable open file \"{}\", are you sure it exists? Message: {}", filename, this->latest_message() ) ); } +} + +OVF_File::~OVF_File() +{ + ovf_close( this ); +} + +const char * OVF_File::latest_message() +{ + return ovf_latest_message( this ); +} - void OVF_File::read_segment_header(int index, ovf_segment & segment) +void OVF_File::read_segment_header( int index, ovf_segment & segment ) +{ + if( ovf_read_segment_header( this, index, &segment ) != OVF_OK ) { - if( ovf_read_segment_header(this, index, &segment) != OVF_OK ) - { - spirit_throw( Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, - fmt::format( "OVF header of segment {}/{} in file \"{}\" could not be parsed. Message: {}", - index+1, this->n_segments, this->file_name, this->latest_message() ) ); - } + spirit_throw( + Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, + fmt::format( + "OVF header of segment {}/{} in file \"{}\" could not be parsed. Message: {}", index + 1, + this->n_segments, this->file_name, this->latest_message() ) ); } +} - void OVF_File::read_segment_data(int index, const ovf_segment & segment, float * data) +void OVF_File::read_segment_data( int index, const ovf_segment & segment, float * data ) +{ + if( ovf_read_segment_data_4( this, index, &segment, data ) != OVF_OK ) { - if( ovf_read_segment_data_4(this, index, &segment, data) != OVF_OK ) - { - spirit_throw( Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, - fmt::format( "OVF data segment {}/{} in file \"{}\" could not be parsed. Message: {}", - index+1, this->n_segments, this->file_name, this->latest_message() ) ); - } + spirit_throw( + Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, + fmt::format( + "OVF data segment {}/{} in file \"{}\" could not be parsed. Message: {}", index + 1, this->n_segments, + this->file_name, this->latest_message() ) ); } - void OVF_File::read_segment_data(int index, const ovf_segment & segment, double * data) +} + +void OVF_File::read_segment_data( int index, const ovf_segment & segment, double * data ) +{ + if( ovf_read_segment_data_8( this, index, &segment, data ) != OVF_OK ) { - if( ovf_read_segment_data_8(this, index, &segment, data) != OVF_OK ) - { - spirit_throw( Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, - fmt::format( "OVF data segment {}/{} in file \"{}\" could not be parsed. Message: {}", - index+1, this->n_segments, this->file_name, this->latest_message() ) ); - } + spirit_throw( + Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, + fmt::format( + "OVF data segment {}/{} in file \"{}\" could not be parsed. Message: {}", index + 1, this->n_segments, + this->file_name, this->latest_message() ) ); } +} - void OVF_File::write_segment(const ovf_segment & segment, float * data, int format) +void OVF_File::write_segment( const ovf_segment & segment, float * data, int format ) +{ + if( ovf_write_segment_4( this, &segment, data, format ) != OVF_OK ) { - if( ovf_write_segment_4(this, &segment, data, format) != OVF_OK ) - { - spirit_throw( Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, - fmt::format( "Unable to write OVF file \"{}\". Message: {}", - this->file_name, this->latest_message() ) ); - } + spirit_throw( + Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, + fmt::format( "Unable to write OVF file \"{}\". Message: {}", this->file_name, this->latest_message() ) ); } - void OVF_File::write_segment(const ovf_segment & segment, double * data, int format) +} + +void OVF_File::write_segment( const ovf_segment & segment, double * data, int format ) +{ + if( ovf_write_segment_8( this, &segment, data, format ) != OVF_OK ) { - if( ovf_write_segment_8(this, &segment, data, format) != OVF_OK ) - { - spirit_throw( Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, - fmt::format( "Unable to write OVF file \"{}\". Message: {}", - this->file_name, this->latest_message() ) ); - } + spirit_throw( + Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, + fmt::format( "Unable to write OVF file \"{}\". Message: {}", this->file_name, this->latest_message() ) ); } +} - void OVF_File::append_segment(const ovf_segment & segment, float * data, int format) +void OVF_File::append_segment( const ovf_segment & segment, float * data, int format ) +{ + if( ovf_append_segment_4( this, &segment, data, format ) != OVF_OK ) { - if( ovf_append_segment_4(this, &segment, data, format) != OVF_OK ) - { - spirit_throw( Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, - fmt::format( "Unable to append segment to OVF file \"{}\". Message: {}", - this->file_name, this->latest_message() ) ); - } + spirit_throw( + Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, + fmt::format( + "Unable to append segment to OVF file \"{}\". Message: {}", this->file_name, this->latest_message() ) ); } - void OVF_File::append_segment(const ovf_segment & segment, double * data, int format) +} + +void OVF_File::append_segment( const ovf_segment & segment, double * data, int format ) +{ + if( ovf_append_segment_8( this, &segment, data, format ) != OVF_OK ) { - if( ovf_append_segment_8(this, &segment, data, format) != OVF_OK ) - { - spirit_throw( Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, - fmt::format( "Unable to append segment to OVF file \"{}\". Message: {}", - this->file_name, this->latest_message() ) ); - } + spirit_throw( + Utility::Exception_Classifier::Bad_File_Content, Utility::Log_Level::Error, + fmt::format( + "Unable to append segment to OVF file \"{}\". Message: {}", this->file_name, this->latest_message() ) ); } -} \ No newline at end of file +} + +} // namespace IO \ No newline at end of file From 0fc19d529a1ca272052a68bb21b5e6e655649262 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 9 Jun 2021 12:49:32 +0200 Subject: [PATCH 29/45] forgotten update of FFT.cu --- core/src/engine/FFT.cu | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/core/src/engine/FFT.cu b/core/src/engine/FFT.cu index 062eba258..e87c56e36 100644 --- a/core/src/engine/FFT.cu +++ b/core/src/engine/FFT.cu @@ -13,6 +13,7 @@ namespace Engine { std::cerr << "NOT IMPLEMENTED FOR cuFFT" << std::endl; } + void iFour_3D(FFT_cfg cfg, FFT_cpx_type * in, FFT_real_type * out) { std::cerr << "NOT IMPLEMENTED FOR cuFFT" << std::endl; @@ -20,13 +21,21 @@ namespace Engine void batch_Four_3D(FFT_Plan & plan) { - cufftExecR2C(plan.cfg, plan.real_ptr.data(), plan.cpx_ptr.data()); + auto res = cufftExecR2C(plan.cfg, plan.real_ptr.data(), plan.cpx_ptr.data()); + if(res != CUFFT_SUCCESS) + { + Log(Utility::Log_Level::Error, Utility::Log_Sender::All, fmt::format("cufftExecR2C failed with error: {}", res)); + } cudaDeviceSynchronize(); } void batch_iFour_3D(FFT_Plan & plan) { - cufftExecC2R(plan.cfg, plan.cpx_ptr.data(), plan.real_ptr.data()); + auto res = cufftExecC2R(plan.cfg, plan.cpx_ptr.data(), plan.real_ptr.data()); + if(res != CUFFT_SUCCESS) + { + Log(Utility::Log_Level::Error, Utility::Log_Sender::All, fmt::format("cufftExecC2R failed with error: {}", res)); + } cudaDeviceSynchronize(); } @@ -47,16 +56,28 @@ namespace Engine if(this->inverse == false) { - cufftPlanMany(&this->cfg, rank, n, inembed, istride, idist, onembed, ostride, odist, CUFFT_R2C, n_transforms); + auto res = cufftPlanMany(&this->cfg, rank, n, inembed, istride, idist, onembed, ostride, odist, CUFFT_R2C, n_transforms); + if(res != CUFFT_SUCCESS) + { + Log(Utility::Log_Level::Error, Utility::Log_Sender::All, fmt::format("cufftPlanMany failed with error: {}", res)); + } } else { - cufftPlanMany(&this->cfg, rank, n, inembed, istride, idist, onembed, ostride, odist, CUFFT_C2R, n_transforms); + auto res = cufftPlanMany(&this->cfg, rank, n, inembed, istride, idist, onembed, ostride, odist, CUFFT_C2R, n_transforms); + if(res != CUFFT_SUCCESS) + { + Log(Utility::Log_Level::Error, Utility::Log_Sender::All, fmt::format("cufftPlanMany failed with error: {}", res)); + } } } void FFT_Plan::Free_Configuration() { - cufftDestroy(this->cfg); + auto res = cufftDestroy(this->cfg); + if(res != CUFFT_SUCCESS) + { + Log(Utility::Log_Level::Error, Utility::Log_Sender::All, fmt::format("cufftDestroy failed with error: {}", res)); + } } } } From 9e1df711750a768c2e9a7cec65dd4f986337b96f Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 9 Jun 2021 12:50:55 +0200 Subject: [PATCH 30/45] Incorporated upstream changes in core/Spirit --- core/include/Spirit/System.h | 7 +- core/src/Spirit/Hamiltonian.cpp | 1142 ++++++++++++++++--------------- core/src/Spirit/System.cpp | 53 +- 3 files changed, 645 insertions(+), 557 deletions(-) diff --git a/core/include/Spirit/System.h b/core/include/Spirit/System.h index 574d5866d..ea264184f 100644 --- a/core/include/Spirit/System.h +++ b/core/include/Spirit/System.h @@ -53,8 +53,13 @@ PREFIX float System_Get_Rx(State * state, int idx_image=-1, int idx_chain=-1) SU // Returns the energy of a spin system. PREFIX float System_Get_Energy(State * state, int idx_image=-1, int idx_chain=-1) SUFFIX; +// Retrieves the names of the energy contributions, represented as a single string and separated by "|". E.g "Zeeman|Exchange|DMI" +// If 'names' is a nullptr, the required length of the char array is returned. +PREFIX int System_Get_Energy_Array_Names(State * state, char* names, int idx_image=-1, int idx_chain=-1) SUFFIX; + // Retrieves the energy contributions of a spin system. -PREFIX void System_Get_Energy_Array(State * state, float * energies, int idx_image=-1, int idx_chain=-1) SUFFIX; +// If 'energies' is a nullptr, the required length of the energies array is returned. +PREFIX int System_Get_Energy_Array(State * state, float * energies, bool divide_by_nspins=true, int idx_image=-1, int idx_chain=-1) SUFFIX; // Retrieves the eigenvalues of a spin system PREFIX void System_Get_Eigenvalues(State * state, float * eigenvalues, int idx_image=-1, int idx_chain=-1) SUFFIX; diff --git a/core/src/Spirit/Hamiltonian.cpp b/core/src/Spirit/Hamiltonian.cpp index 4d1d26b5a..63b059a92 100644 --- a/core/src/Spirit/Hamiltonian.cpp +++ b/core/src/Spirit/Hamiltonian.cpp @@ -21,494 +21,508 @@ using namespace Utility; /*------------------------------------------------------------------------------------------------------ */ void Hamiltonian_Set_Kind(State *state, Hamiltonian_Type type, int idx_chain) noexcept -try { - // TODO - if( type != Hamiltonian_Heisenberg && type != Hamiltonian_Micromagnetic && type != Hamiltonian_Gaussian ) + try { - Log( Utility::Log_Level::Error, Utility::Log_Sender::API, fmt::format( - "Hamiltonian_Set_Kind: unknown type index {}", int(type)), -1, idx_chain ); - return; - } - - std::shared_ptr image; - std::shared_ptr chain; + // TODO + if( type != Hamiltonian_Heisenberg && type != Hamiltonian_Micromagnetic && type != Hamiltonian_Gaussian ) + { + Log( Utility::Log_Level::Error, Utility::Log_Sender::API, fmt::format( + "Hamiltonian_Set_Kind: unknown type index {}", int(type)), -1, idx_chain ); + return; + } - // Fetch correct indices and pointers - int idx_image = -1; - from_indices( state, idx_image, idx_chain, image, chain ); + std::shared_ptr image; + std::shared_ptr chain; - idx_image = 0; - std::string kind_str = ""; - if( type == Hamiltonian_Heisenberg ) - { - kind_str = "Heisenberg"; + // Fetch correct indices and pointers + int idx_image = -1; + from_indices( state, idx_image, idx_chain, image, chain ); - if( kind_str == image->hamiltonian->Name() ) + idx_image = 0; + std::string kind_str = ""; + if( type == Hamiltonian_Heisenberg ) { - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, fmt::format( - "Hamiltonian is already of {} kind. Not doing anything.", kind_str), -1, idx_chain ); - return; + kind_str = "Heisenberg"; + + if( kind_str == image->hamiltonian->Name() ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, fmt::format( + "Hamiltonian is already of {} kind. Not doing anything.", kind_str), -1, idx_chain ); + return; + } } - } - else if( type == Hamiltonian_Micromagnetic ) - { - kind_str = "Micromagnetic"; + else if( type == Hamiltonian_Micromagnetic ) + { + kind_str = "Micromagnetic"; - if( kind_str == image->hamiltonian->Name() ) + if( kind_str == image->hamiltonian->Name() ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, fmt::format( + "Hamiltonian is already of {} kind. Not doing anything.", kind_str), -1, idx_chain ); + return; + } + } + else if( type == Hamiltonian_Gaussian ) { - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, fmt::format( - "Hamiltonian is already of {} kind. Not doing anything.", kind_str), -1, idx_chain ); + kind_str = "Gaussian"; + + if( kind_str == image->hamiltonian->Name() ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, fmt::format( + "Hamiltonian is already of {} kind. Not doing anything.", kind_str), -1, idx_chain ); + return; + } + + Log( Utility::Log_Level::Error, Utility::Log_Sender::API, fmt::format( + "Cannot yet set Hamiltonian kind to {} - this function is only a stub!", kind_str), -1, idx_chain ); return; } - } - else if( type == Hamiltonian_Gaussian ) - { - kind_str = "Gaussian"; - if( kind_str == image->hamiltonian->Name() ) + for( auto& image : chain->images ) { - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, fmt::format( - "Hamiltonian is already of {} kind. Not doing anything.", kind_str), -1, idx_chain ); - return; + image->Lock(); + try + { + if( type == Hamiltonian_Heisenberg ) + { + // TODO: are these the desired defaults? + image->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Heisenberg( + 0, Vector3{0, 0, 1}, + {}, {}, {}, + {}, {}, SPIRIT_CHIRALITY_NEEL, + Engine::DDI_Method::None, {0, 0, 0}, false, 0, + {}, {}, + image->geometry, + image->hamiltonian->boundary_conditions)); + } + else if( type == Hamiltonian_Micromagnetic ) + { + // TODO: are these the desired defaults? + image->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Micromagnetic( + 0, 0, Vector3{0, 0, 1}, + Matrix3::Zero(), + Matrix3::Zero(), + Matrix3::Zero(), + Engine::DDI_Method::None, {0, 0, 0}, 0, + image->geometry, + 2, + image->hamiltonian->boundary_conditions)); + } + else if( type == Hamiltonian_Gaussian ) + { + // TODO: are these the desired defaults? + image->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Gaussian( + {}, {}, {})); + } + } + catch( ... ) + { + spirit_handle_exception_api(idx_image, idx_chain); + } + image->Unlock(); + ++idx_image; } - Log( Utility::Log_Level::Error, Utility::Log_Sender::API, fmt::format( - "Cannot yet set Hamiltonian kind to {} - this function is only a stub!", kind_str), -1, idx_chain ); - return; + Log( Utility::Log_Level::All, Utility::Log_Sender::API, fmt::format( + "Set Hamiltonian kind to {}", kind_str), -1, idx_chain ); } + catch( ... ) + { + spirit_handle_exception_api(-1, idx_chain); + } +} - for( auto& image : chain->images ) +void Hamiltonian_Set_Boundary_Conditions(State *state, const bool * periodical, int idx_image, int idx_chain) noexcept +{ + try { + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + image->Lock(); try { - if( type == Hamiltonian_Heisenberg ) - { - // TODO: are these the desired defaults? - image->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Heisenberg( - 0, Vector3{0, 0, 1}, - {}, {}, {}, - {}, {}, SPIRIT_CHIRALITY_NEEL, - Engine::DDI_Method::None, {0, 0, 0}, false, 0, - {}, {}, - image->geometry, - image->hamiltonian->boundary_conditions)); - } - else if( type == Hamiltonian_Micromagnetic ) - { - // TODO: are these the desired defaults? - image->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Micromagnetic( - 0, 0, Vector3{0, 0, 1}, - Matrix3::Zero(), - Matrix3::Zero(), - Matrix3::Zero(), - Engine::DDI_Method::None, {0, 0, 0}, 0, - image->geometry, - 2, - image->hamiltonian->boundary_conditions)); - } - else if( type == Hamiltonian_Gaussian ) - { - // TODO: are these the desired defaults? - image->hamiltonian = std::shared_ptr(new Engine::Hamiltonian_Gaussian( - {}, {}, {})); - } + image->hamiltonian->boundary_conditions[0] = periodical[0]; + image->hamiltonian->boundary_conditions[1] = periodical[1]; + image->hamiltonian->boundary_conditions[2] = periodical[2]; } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } image->Unlock(); - ++idx_image; - } - Log( Utility::Log_Level::All, Utility::Log_Sender::API, fmt::format( - "Set Hamiltonian kind to {}", kind_str), -1, idx_chain ); -} -catch( ... ) -{ - spirit_handle_exception_api(-1, idx_chain); -} - -void Hamiltonian_Set_Boundary_Conditions(State *state, const bool * periodical, int idx_image, int idx_chain) noexcept -try -{ - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - image->Lock(); - try - { - image->hamiltonian->boundary_conditions[0] = periodical[0]; - image->hamiltonian->boundary_conditions[1] = periodical[1]; - image->hamiltonian->boundary_conditions[2] = periodical[2]; + Log( Utility::Log_Level::Info, Utility::Log_Sender::API, + fmt::format("Set boundary conditions to {} {} {}", periodical[0], periodical[1], periodical[2]), + idx_image, idx_chain ); } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } - image->Unlock(); - - Log( Utility::Log_Level::Info, Utility::Log_Sender::API, - fmt::format("Set boundary conditions to {} {} {}", periodical[0], periodical[1], periodical[2]), - idx_image, idx_chain ); -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Set_Field(State *state, float magnitude, const float * normal, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; + try + { + std::shared_ptr image; + std::shared_ptr chain; - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) - { - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - "External field cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); - return; - } + if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "External field cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); + return; + } - // Lock mutex because simulations may be running - image->Lock(); + // Lock mutex because simulations may be running + image->Lock(); - try - { - // Set - if( image->hamiltonian->Name() == "Heisenberg" ) + try { - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + // Set + if( image->hamiltonian->Name() == "Heisenberg" ) + { + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - // Normals - Vector3 new_normal{normal[0], normal[1], normal[2]}; - new_normal.normalize(); + // Normals + Vector3 new_normal{normal[0], normal[1], normal[2]}; + new_normal.normalize(); - // Into the Hamiltonian - ham->external_field_magnitude = magnitude * Constants::mu_B; - ham->external_field_normal = new_normal; + // Into the Hamiltonian + ham->external_field_magnitude = magnitude * Constants::mu_B; + ham->external_field_normal = new_normal; - // Update Energies - ham->Update_Energy_Contributions(); - } - else if( image->hamiltonian->Name() == "Micromagnetic" ) - { - auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + // Update Energies + ham->Update_Energy_Contributions(); + } + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); - // Normals - Vector3 new_normal{normal[0], normal[1], normal[2]}; - new_normal.normalize(); + // Normals + Vector3 new_normal{normal[0], normal[1], normal[2]}; + new_normal.normalize(); - // Into the Hamiltonian - ham->external_field_magnitude = magnitude; - ham->external_field_normal = new_normal; + // Into the Hamiltonian + ham->external_field_magnitude = magnitude; + ham->external_field_normal = new_normal; - // Update Energies - ham->Update_Energy_Contributions(); + // Update Energies + ham->Update_Energy_Contributions(); + } + } + catch( ... ) + { + spirit_handle_exception_api(idx_image, idx_chain); } + + // Unlock mutex + image->Unlock(); + + Log( Utility::Log_Level::Info, Utility::Log_Sender::API, + fmt::format("Set external field to {} [T], direction ({}, {}, {})", magnitude, normal[0], normal[1], normal[2]), + idx_image, idx_chain ); } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } - - // Unlock mutex - image->Unlock(); - - Log( Utility::Log_Level::Info, Utility::Log_Sender::API, - fmt::format("Set external field to {} [T], direction ({}, {}, {})", magnitude, normal[0], normal[1], normal[2]), - idx_image, idx_chain ); -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Set_Anisotropy( State *state, float magnitude, const float * normal, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) + try { - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - "Anisotropy cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); - return; - } + std::shared_ptr image; + std::shared_ptr chain; - image->Lock(); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - try - { - std::string units = "N/A"; + if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Anisotropy cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); + return; + } - if( image->hamiltonian->Name() == "Heisenberg" ) + image->Lock(); + + try { - units = "meV"; - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - int nos = image->nos; - int n_cell_atoms = image->geometry->n_cell_atoms; + std::string units = "N/A"; - // Indices and Magnitudes - intfield new_indices(n_cell_atoms); - scalarfield new_magnitudes(n_cell_atoms); - for (int i = 0; ihamiltonian->Name() == "Heisenberg" ) { - new_indices[i] = i; - new_magnitudes[i] = magnitude; + units = "meV"; + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + int nos = image->nos; + int n_cell_atoms = image->geometry->n_cell_atoms; + + // Indices and Magnitudes + intfield new_indices(n_cell_atoms); + scalarfield new_magnitudes(n_cell_atoms); + for (int i = 0; ianisotropy_indices = new_indices; + ham->anisotropy_magnitudes = new_magnitudes; + ham->anisotropy_normals = new_normals; + + // Update Energies + ham->Update_Energy_Contributions(); } - // Normals - Vector3 new_normal{ normal[0], normal[1], normal[2] }; - new_normal.normalize(); - vectorfield new_normals(nos, new_normal); - - // Into the Hamiltonian - ham->anisotropy_indices = new_indices; - ham->anisotropy_magnitudes = new_magnitudes; - ham->anisotropy_normals = new_normals; + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + units = "J/m^3"; + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); - // Update Energies - ham->Update_Energy_Contributions(); - } - else if( image->hamiltonian->Name() == "Micromagnetic" ) - { - units = "J/m^3"; - auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + Vector3 Kn{ normal[0], normal[1], normal[2] }; + Kn.normalize(); - Vector3 Kn{ normal[0], normal[1], normal[2] }; - Kn.normalize(); + ham->anisotropy_tensor << Kn[0]*Kn[0], Kn[0]*Kn[1], Kn[0]*Kn[2], + Kn[1]*Kn[0], Kn[1]*Kn[1], Kn[1]*Kn[2], + Kn[2]*Kn[0], Kn[2]*Kn[1], Kn[2]*Kn[2]; + ham->anisotropy_tensor *= magnitude; - ham->anisotropy_tensor << Kn[0]*Kn[0], Kn[0]*Kn[1], Kn[0]*Kn[2], - Kn[1]*Kn[0], Kn[1]*Kn[1], Kn[1]*Kn[2], - Kn[2]*Kn[0], Kn[2]*Kn[1], Kn[2]*Kn[2]; - ham->anisotropy_tensor *= magnitude; + // Update Energies + ham->Update_Energy_Contributions(); + } - // Update Energies - ham->Update_Energy_Contributions(); + Log( Utility::Log_Level::Info, Utility::Log_Sender::API, + fmt::format("Set anisotropy to {} [{}], direction ({}, {}, {})", magnitude, units, normal[0], normal[1], normal[2]), + idx_image, idx_chain ); + } + catch( ... ) + { + spirit_handle_exception_api(idx_image, idx_chain); } - Log( Utility::Log_Level::Info, Utility::Log_Sender::API, - fmt::format("Set anisotropy to {} [{}], direction ({}, {}, {})", magnitude, units, normal[0], normal[1], normal[2]), - idx_image, idx_chain ); + image->Unlock(); } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } - - image->Unlock(); -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Set_Exchange(State *state, int n_shells, const float* jij, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) + try { - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - "Exchange interaction cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); - return; - } + std::shared_ptr image; + std::shared_ptr chain; - image->Lock(); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - try - { - if( image->hamiltonian->Name() == "Heisenberg" ) + if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) { - // Update the Hamiltonian - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - ham->exchange_shell_magnitudes = scalarfield(jij, jij + n_shells); - ham->exchange_pairs_in = pairfield(0); - ham->exchange_magnitudes_in = scalarfield(0); - ham->Update_Interactions(); - - std::string message = fmt::format("Set exchange to {} shells", n_shells); - if( n_shells > 0 ) message += fmt::format(" Jij[0] = {} [meV/bond]", jij[0]); - Log(Utility::Log_Level::Info, Utility::Log_Sender::API, message, idx_image, idx_chain); + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Exchange interaction cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); + return; } - else if( image->hamiltonian->Name() == "Micromagnetic" ) + + image->Lock(); + + try { - if( n_shells > 1 ) + if( image->hamiltonian->Name() == "Heisenberg" ) { - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - "Cannot set more than one shell of Exchange interaction on micromagnetic Hamiltonian.", idx_image, idx_chain ); - image->Unlock(); - return; + // Update the Hamiltonian + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + ham->exchange_shell_magnitudes = scalarfield(jij, jij + n_shells); + ham->exchange_pairs_in = pairfield(0); + ham->exchange_magnitudes_in = scalarfield(0); + ham->Update_Interactions(); + + std::string message = fmt::format("Set exchange to {} shells", n_shells); + if( n_shells > 0 ) message += fmt::format(" Jij[0] = {} [meV/bond]", jij[0]); + Log(Utility::Log_Level::Info, Utility::Log_Sender::API, message, idx_image, idx_chain); } - - auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); - auto Jij = jij[0]; - ham->exchange_tensor << Jij, 0, 0, - 0, Jij, 0, - 0, 0, Jij; - ham->Update_Interactions(); + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + if( n_shells > 1 ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Cannot set more than one shell of Exchange interaction on micromagnetic Hamiltonian.", idx_image, idx_chain ); + image->Unlock(); + return; + } + + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + auto Jij = jij[0]; + ham->exchange_tensor << Jij, 0, 0, + 0, Jij, 0, + 0, 0, Jij; + ham->Update_Interactions(); + } + } + catch( ... ) + { + spirit_handle_exception_api(idx_image, idx_chain); } + + image->Unlock(); } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } - - image->Unlock(); -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Set_DMI(State *state, int n_shells, const float * dij, int chirality, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) - { - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - "Dzyaloshinskii-Moriya interaction cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); - return; - } - - if( chirality != SPIRIT_CHIRALITY_BLOCH && - chirality != SPIRIT_CHIRALITY_NEEL && - chirality != SPIRIT_CHIRALITY_BLOCH_INVERSE && - chirality != SPIRIT_CHIRALITY_NEEL_INVERSE ) + try { - Log( Utility::Log_Level::Error, Utility::Log_Sender::API, fmt::format( - "Hamiltonian_Set_DMI: Invalid DM chirality {}", chirality), idx_image, idx_chain ); - return; - } + std::shared_ptr image; + std::shared_ptr chain; - image->Lock(); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - try - { - if( image->hamiltonian->Name() == "Heisenberg" ) + if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) { - // Update the Hamiltonian - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - ham->dmi_shell_magnitudes = scalarfield(dij, dij + n_shells); - ham->dmi_shell_chirality = chirality; - ham->dmi_pairs_in = pairfield(0); - ham->dmi_magnitudes_in = scalarfield(0); - ham->dmi_normals_in = vectorfield(0); - ham->Update_Interactions(); + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Dzyaloshinskii-Moriya interaction cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); + return; + } - std::string message = fmt::format("Set dmi to {} shells", n_shells); - if( n_shells > 0 ) message += fmt::format(" Dij[0] = {} [meV/bond]", dij[0]); - Log(Utility::Log_Level::Info, Utility::Log_Sender::API, message, idx_image, idx_chain); + if( chirality != SPIRIT_CHIRALITY_BLOCH && + chirality != SPIRIT_CHIRALITY_NEEL && + chirality != SPIRIT_CHIRALITY_BLOCH_INVERSE && + chirality != SPIRIT_CHIRALITY_NEEL_INVERSE ) + { + Log( Utility::Log_Level::Error, Utility::Log_Sender::API, fmt::format( + "Hamiltonian_Set_DMI: Invalid DM chirality {}", chirality), idx_image, idx_chain ); + return; } - else if( image->hamiltonian->Name() == "Micromagnetic" ) + + image->Lock(); + + try { - if( n_shells > 1 ) + if( image->hamiltonian->Name() == "Heisenberg" ) { - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - "Cannot set more than one shell of DM interaction on micromagnetic Hamiltonian.", idx_image, idx_chain ); - image->Unlock(); - return; + // Update the Hamiltonian + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + ham->dmi_shell_magnitudes = scalarfield(dij, dij + n_shells); + ham->dmi_shell_chirality = chirality; + ham->dmi_pairs_in = pairfield(0); + ham->dmi_magnitudes_in = scalarfield(0); + ham->dmi_normals_in = vectorfield(0); + ham->Update_Interactions(); + + std::string message = fmt::format("Set dmi to {} shells", n_shells); + if( n_shells > 0 ) message += fmt::format(" Dij[0] = {} [meV/bond]", dij[0]); + Log(Utility::Log_Level::Info, Utility::Log_Sender::API, message, idx_image, idx_chain); + } + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + if( n_shells > 1 ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Cannot set more than one shell of DM interaction on micromagnetic Hamiltonian.", idx_image, idx_chain ); + image->Unlock(); + return; + } + + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + auto Dij = dij[0]; + // TODO: handle chirality + ham->exchange_tensor << Dij, 0, 0, + 0, Dij, 0, + 0, 0, Dij; + ham->Update_Interactions(); } - - auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); - auto Dij = dij[0]; - // TODO: handle chirality - ham->exchange_tensor << Dij, 0, 0, - 0, Dij, 0, - 0, 0, Dij; - ham->Update_Interactions(); } + catch( ... ) + { + spirit_handle_exception_api(idx_image, idx_chain); + } + + image->Unlock(); } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } - - image->Unlock(); -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Set_DDI(State *state, int ddi_method, int n_periodic_images[3], float cutoff_radius, bool pb_zero_padding, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; - - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); - - if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) + try { - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - "Dipolar interactions cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); - return; - } + std::shared_ptr image; + std::shared_ptr chain; - image->Lock(); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - try - { - if( image->hamiltonian->Name() == "Heisenberg" ) + if( image->hamiltonian->Name() != "Heisenberg" && image->hamiltonian->Name() != "Micromagnetic" ) { - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Dipolar interactions cannot be set on " + image->hamiltonian->Name(), idx_image, idx_chain ); + return; + } - ham->ddi_method = Engine::DDI_Method(ddi_method); - ham->ddi_n_periodic_images[0] = n_periodic_images[0]; - ham->ddi_n_periodic_images[1] = n_periodic_images[1]; - ham->ddi_n_periodic_images[2] = n_periodic_images[2]; - ham->ddi_cutoff_radius = cutoff_radius; - ham->ddi_pb_zero_padding = pb_zero_padding; - ham->Update_Interactions(); + image->Lock(); - Log( Utility::Log_Level::Info, Utility::Log_Sender::API, fmt::format( - "Set ddi to method {}, periodic images {} {} {}, cutoff radius {} and pb_zero_padding {}", - ddi_method, n_periodic_images[0], n_periodic_images[1], n_periodic_images[2], cutoff_radius, pb_zero_padding), idx_image, idx_chain ); + try + { + if( image->hamiltonian->Name() == "Heisenberg" ) + { + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + + ham->ddi_method = Engine::DDI_Method(ddi_method); + ham->ddi_n_periodic_images[0] = n_periodic_images[0]; + ham->ddi_n_periodic_images[1] = n_periodic_images[1]; + ham->ddi_n_periodic_images[2] = n_periodic_images[2]; + ham->ddi_cutoff_radius = cutoff_radius; + ham->ddi_pb_zero_padding = pb_zero_padding; + ham->Update_Interactions(); + + Log( Utility::Log_Level::Info, Utility::Log_Sender::API, fmt::format( + "Set ddi to method {}, periodic images {} {} {}, cutoff radius {} and pb_zero_padding {}", + ddi_method, n_periodic_images[0], n_periodic_images[1], n_periodic_images[2], cutoff_radius, pb_zero_padding), idx_image, idx_chain ); + } + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + "Dipolar interactions have not yet been implemented on the micromagnetic Hamiltonian", idx_image, idx_chain ); + image->Unlock(); + return; + } } - else if( image->hamiltonian->Name() == "Micromagnetic" ) + catch( ... ) { - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - "Dipolar interactions have not yet been implemented on the micromagnetic Hamiltonian", idx_image, idx_chain ); - image->Unlock(); - return; + spirit_handle_exception_api(idx_image, idx_chain); } + + image->Unlock(); } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); } - - image->Unlock(); -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); } /*------------------------------------------------------------------------------------------------------ */ @@ -516,100 +530,119 @@ catch( ... ) /*------------------------------------------------------------------------------------------------------ */ const char * Hamiltonian_Get_Name(State * state, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; + try + { + std::shared_ptr image; + std::shared_ptr chain; - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - return image->hamiltonian->Name().c_str(); -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); - return nullptr; + return image->hamiltonian->Name().c_str(); + } + catch( ... ) + { + spirit_handle_exception_api(idx_image, idx_chain); + return nullptr; + } } void Hamiltonian_Get_Boundary_Conditions(State *state, bool * periodical, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; + try + { + std::shared_ptr image; + std::shared_ptr chain; - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - periodical[0] = image->hamiltonian->boundary_conditions[0]; - periodical[1] = image->hamiltonian->boundary_conditions[1]; - periodical[2] = image->hamiltonian->boundary_conditions[2]; -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); + periodical[0] = image->hamiltonian->boundary_conditions[0]; + periodical[1] = image->hamiltonian->boundary_conditions[1]; + periodical[2] = image->hamiltonian->boundary_conditions[2]; + } + catch( ... ) + { + spirit_handle_exception_api(idx_image, idx_chain); + } } void Hamiltonian_Get_Field(State *state, float * magnitude, float * normal, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; + try + { + std::shared_ptr image; + std::shared_ptr chain; - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - if( image->hamiltonian->Name() == "Heisenberg" ) - { - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + if( image->hamiltonian->Name() == "Heisenberg" ) + { + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - *magnitude = (float)(ham->external_field_magnitude / Constants::mu_B); - normal[0] = (float)ham->external_field_normal[0]; - normal[1] = (float)ham->external_field_normal[1]; - normal[2] = (float)ham->external_field_normal[2]; - } - else if( image->hamiltonian->Name() == "Micromagnetic" ) - { - auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + *magnitude = (float)(ham->external_field_magnitude / Constants::mu_B); + normal[0] = (float)ham->external_field_normal[0]; + normal[1] = (float)ham->external_field_normal[1]; + normal[2] = (float)ham->external_field_normal[2]; + } + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + + *magnitude = (float)ham->external_field_magnitude; + normal[0] = (float)ham->external_field_normal[0]; + normal[1] = (float)ham->external_field_normal[1]; + normal[2] = (float)ham->external_field_normal[2]; + } + else + { + *magnitude = 0; + normal[0] = 0; + normal[1] = 0; + normal[2] = 1; + } - *magnitude = (float)ham->external_field_magnitude; - normal[0] = (float)ham->external_field_normal[0]; - normal[1] = (float)ham->external_field_normal[1]; - normal[2] = (float)ham->external_field_normal[2]; } - else + catch( ... ) { - *magnitude = 0; - normal[0] = 0; - normal[1] = 0; - normal[2] = 1; + spirit_handle_exception_api(idx_image, idx_chain); } - -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); } void Hamiltonian_Get_Anisotropy(State *state, float * magnitude, float * normal, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; + try + { + std::shared_ptr image; + std::shared_ptr chain; - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - if( image->hamiltonian->Name() == "Heisenberg" ) - { - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + if( image->hamiltonian->Name() == "Heisenberg" ) + { + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - if( ham->anisotropy_indices.size() > 0 ) + if( ham->anisotropy_indices.size() > 0 ) + { + *magnitude = (float)ham->anisotropy_magnitudes[0]; + normal[0] = (float)ham->anisotropy_normals[0][0]; + normal[1] = (float)ham->anisotropy_normals[0][1]; + normal[2] = (float)ham->anisotropy_normals[0][2]; + } + else + { + *magnitude = 0; + normal[0] = 0; + normal[1] = 0; + normal[2] = 1; + } + } + else if( image->hamiltonian->Name() == "Micromagnetic" ) { - *magnitude = (float)ham->anisotropy_magnitudes[0]; - normal[0] = (float)ham->anisotropy_normals[0][0]; - normal[1] = (float)ham->anisotropy_normals[0][1]; - normal[2] = (float)ham->anisotropy_normals[0][2]; + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); } else { @@ -619,196 +652,197 @@ try normal[2] = 1; } } - else if( image->hamiltonian->Name() == "Micromagnetic" ) - { - auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); - } - else + catch( ... ) { - *magnitude = 0; - normal[0] = 0; - normal[1] = 0; - normal[2] = 1; + spirit_handle_exception_api(idx_image, idx_chain); } } -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); -} void Hamiltonian_Get_Exchange_Shells(State *state, int * n_shells, float * jij, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; + try + { + std::shared_ptr image; + std::shared_ptr chain; - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - if( image->hamiltonian->Name() == "Heisenberg" ) - { - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + if( image->hamiltonian->Name() == "Heisenberg" ) + { + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - *n_shells = ham->exchange_shell_magnitudes.size(); + *n_shells = ham->exchange_shell_magnitudes.size(); - // Note the array needs to be correctly allocated beforehand! - for( int i=0; iexchange_shell_magnitudes.size(); ++i ) + // Note the array needs to be correctly allocated beforehand! + for( int i=0; iexchange_shell_magnitudes.size(); ++i ) + { + jij[i] = (float)ham->exchange_shell_magnitudes[i]; + } + } + else if( image->hamiltonian->Name() == "Micromagnetic" ) { - jij[i] = (float)ham->exchange_shell_magnitudes[i]; + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + + *n_shells = 1; + jij[0] = (float)ham->exchange_tensor(0,0); + } + else + { + *n_shells = 0; } - } - else if( image->hamiltonian->Name() == "Micromagnetic" ) - { - auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); - *n_shells = 1; - jij[0] = (float)ham->exchange_tensor(0,0); } - else + catch( ... ) { - *n_shells = 0; + spirit_handle_exception_api(idx_image, idx_chain); } - -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); } int Hamiltonian_Get_Exchange_N_Pairs(State *state, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; + try + { + std::shared_ptr image; + std::shared_ptr chain; - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - image->hamiltonian->Name() + " Hamiltonian: fetching exchange pairs is not yet implemented...", idx_image, idx_chain ); - return 0; -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); - return 0; + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + image->hamiltonian->Name() + " Hamiltonian: fetching exchange pairs is not yet implemented...", idx_image, idx_chain ); + return 0; + } + catch( ... ) + { + spirit_handle_exception_api(idx_image, idx_chain); + return 0; + } } void Hamiltonian_Get_Exchange_Pairs(State *state, float * idx[2], float * translations[3], float * Jij, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; + try + { + std::shared_ptr image; + std::shared_ptr chain; - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - image->hamiltonian->Name() + " Hamiltonian: fetching exchange pairs is not yet implemented...", idx_image, idx_chain ); -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + image->hamiltonian->Name() + " Hamiltonian: fetching exchange pairs is not yet implemented...", idx_image, idx_chain ); + } + catch( ... ) + { + spirit_handle_exception_api(idx_image, idx_chain); + } } void Hamiltonian_Get_DMI_Shells(State *state, int * n_shells, float * dij, int * chirality, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; + try + { + std::shared_ptr image; + std::shared_ptr chain; - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - if( image->hamiltonian->Name() == "Heisenberg" ) - { - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + if( image->hamiltonian->Name() == "Heisenberg" ) + { + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - *n_shells = ham->dmi_shell_magnitudes.size(); - *chirality = ham->dmi_shell_chirality; + *n_shells = ham->dmi_shell_magnitudes.size(); + *chirality = ham->dmi_shell_chirality; - for( int i=0; i<*n_shells; ++i ) + for( int i=0; i<*n_shells; ++i ) + { + dij[i] = (float)ham->dmi_shell_magnitudes[i]; + } + } + else if( image->hamiltonian->Name() == "Micromagnetic" ) { - dij[i] = (float)ham->dmi_shell_magnitudes[i]; + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + + *n_shells = 1; + dij[0] = (float)ham->dmi_tensor(0,0); + } + else + { + *n_shells = 0; } - } - else if( image->hamiltonian->Name() == "Micromagnetic" ) - { - auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); - *n_shells = 1; - dij[0] = (float)ham->dmi_tensor(0,0); } - else + catch( ... ) { - *n_shells = 0; + spirit_handle_exception_api(idx_image, idx_chain); } - -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); } int Hamiltonian_Get_DMI_N_Pairs(State *state, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; + try + { + std::shared_ptr image; + std::shared_ptr chain; - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, - image->hamiltonian->Name() + " Hamiltonian: fetching DMI pairs is not yet implemented...", idx_image, idx_chain ); - return 0; -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); - return 0; + Log( Utility::Log_Level::Warning, Utility::Log_Sender::API, + image->hamiltonian->Name() + " Hamiltonian: fetching DMI pairs is not yet implemented...", idx_image, idx_chain ); + return 0; + } + catch( ... ) + { + spirit_handle_exception_api(idx_image, idx_chain); + return 0; + } } void Hamiltonian_Get_DDI(State *state, int * ddi_method, int n_periodic_images[3], float * cutoff_radius, bool * pb_zero_padding, int idx_image, int idx_chain) noexcept -try { - std::shared_ptr image; - std::shared_ptr chain; + try + { + std::shared_ptr image; + std::shared_ptr chain; - // Fetch correct indices and pointers - from_indices( state, idx_image, idx_chain, image, chain ); + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); - if( image->hamiltonian->Name() == "Heisenberg" ) - { - auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); + if( image->hamiltonian->Name() == "Heisenberg" ) + { + auto ham = (Engine::Hamiltonian_Heisenberg*)image->hamiltonian.get(); - *ddi_method = (int)ham->ddi_method; - n_periodic_images[0] = (int)ham->ddi_n_periodic_images[0]; - n_periodic_images[1] = (int)ham->ddi_n_periodic_images[1]; - n_periodic_images[2] = (int)ham->ddi_n_periodic_images[2]; - *cutoff_radius = (float)ham->ddi_cutoff_radius; - *pb_zero_padding = ham->ddi_pb_zero_padding; - } - else if( image->hamiltonian->Name() == "Micromagnetic" ) - { - auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); + *ddi_method = (int)ham->ddi_method; + n_periodic_images[0] = (int)ham->ddi_n_periodic_images[0]; + n_periodic_images[1] = (int)ham->ddi_n_periodic_images[1]; + n_periodic_images[2] = (int)ham->ddi_n_periodic_images[2]; + *cutoff_radius = (float)ham->ddi_cutoff_radius; + *pb_zero_padding = ham->ddi_pb_zero_padding; + } + else if( image->hamiltonian->Name() == "Micromagnetic" ) + { + auto ham = (Engine::Hamiltonian_Micromagnetic*)image->hamiltonian.get(); - *ddi_method = (int)ham->ddi_method; - n_periodic_images[0] = (int)ham->ddi_n_periodic_images[0]; - n_periodic_images[1] = (int)ham->ddi_n_periodic_images[1]; - n_periodic_images[2] = (int)ham->ddi_n_periodic_images[2]; - *cutoff_radius = (float)ham->ddi_cutoff_radius; + *ddi_method = (int)ham->ddi_method; + n_periodic_images[0] = (int)ham->ddi_n_periodic_images[0]; + n_periodic_images[1] = (int)ham->ddi_n_periodic_images[1]; + n_periodic_images[2] = (int)ham->ddi_n_periodic_images[2]; + *cutoff_radius = (float)ham->ddi_cutoff_radius; + } + else + { + *ddi_method = SPIRIT_DDI_METHOD_NONE; + n_periodic_images[0] = 0; + n_periodic_images[1] = 0; + n_periodic_images[2] = 0; + *cutoff_radius = 0; + } } - else + catch( ... ) { - *ddi_method = SPIRIT_DDI_METHOD_NONE; - n_periodic_images[0] = 0; - n_periodic_images[1] = 0; - n_periodic_images[2] = 0; - *cutoff_radius = 0; + spirit_handle_exception_api(idx_image, idx_chain); } -} -catch( ... ) -{ - spirit_handle_exception_api(idx_image, idx_chain); } \ No newline at end of file diff --git a/core/src/Spirit/System.cpp b/core/src/Spirit/System.cpp index 7b15e7e3b..b0ba478b1 100644 --- a/core/src/Spirit/System.cpp +++ b/core/src/Spirit/System.cpp @@ -133,7 +133,7 @@ catch( ... ) return 0; } -void System_Get_Energy_Array(State * state, float * energies, int idx_image, int idx_chain) noexcept +int System_Get_Energy_Array_Names(State * state, char* names, int idx_image, int idx_chain) noexcept try { std::shared_ptr image; @@ -142,14 +142,63 @@ try // Fetch correct indices and pointers from_indices( state, idx_image, idx_chain, image, chain ); + int n_char_array = -1; // Start of with offset -1, because the last contributions gets no "|" delimiter for (unsigned int i=0; iE_array.size(); ++i) { - energies[i] = (float)image->E_array[i].second; + n_char_array += image->E_array[i].first.size() + 1; // Add +1 because we separate the contribution names with the character "|" + } + + // If 'names' is a nullptr, we return the required length of the names array + if(names==nullptr) + { + return n_char_array; + } else { // Else we try to fill the provided char array + int idx=0; + for (unsigned int i=0; iE_array.size(); ++i) + { + for(const char & cur_char : (image->E_array[i]).first) + { + names[idx++] = cur_char; + } + if(i != image->E_array.size()-1) + names[idx++] = '|'; + } + return -1; } } catch( ... ) { spirit_handle_exception_api(idx_image, idx_chain); + return -1; +} + + +int System_Get_Energy_Array(State * state, float * energies, bool divide_by_nspins, int idx_image, int idx_chain) noexcept +try +{ + std::shared_ptr image; + std::shared_ptr chain; + + // Fetch correct indices and pointers + from_indices( state, idx_image, idx_chain, image, chain ); + + scalar nd = divide_by_nspins ? 1/(scalar)image->nos : 1; + + if(energies == nullptr) + { + return image->E_array.size(); + } else { + for (unsigned int i=0; iE_array.size(); ++i) + { + energies[i] = nd * (float)image->E_array[i].second; + } + return -1; + } +} +catch( ... ) +{ + spirit_handle_exception_api(idx_image, idx_chain); + return -1; } void System_Get_Eigenvalues(State * state, float * eigenvalues, int idx_image, int idx_chain) noexcept From 30657fadbe291cb945395778431d697bc6146864 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 9 Jun 2021 13:00:31 +0200 Subject: [PATCH 31/45] Incorporated upstream changes in system.py --- core/python/spirit/system.py | 41 +++++++++++++++++++++++++++--------- core/python/test/system.py | 14 +++++++++--- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/core/python/spirit/system.py b/core/python/spirit/system.py index 7333d96f5..6f3341d1b 100644 --- a/core/python/spirit/system.py +++ b/core/python/spirit/system.py @@ -98,16 +98,37 @@ def get_eigenvalues(p_state, idx_image=-1, idx_chain=-1): _Get_Eigenvalues(ctypes.c_void_p(p_state), eigenvalues, ctypes.c_int(idx_image), ctypes.c_int(idx_chain)) return eigenvalues -# NOTE: excluded since there is no clean way to get the C++ pairs -### Get Energy array -# _Get_Energy_Array = _spirit.System_Get_Energy_Array -# _Get_Energy_Array.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_float), -# ctypes.c_int, ctypes.c_int] -# _Get_Energy_Array.restype = None -# def Get_Energy_Array(p_state, idx_image=-1, idx_chain=-1): -# Energies -# _Get_Energy_Array(ctypes.c_void_p(p_state), energies, -# ctypes.c_int(idx_image), ctypes.c_int(idx_chain)) +### Get Energy Contributions +### The result is a dictionary with strings as keys and floats as values +### The keys are the names of the energy contributions, the values the energy_contribution in meV +_Get_Energy_Array = _spirit.System_Get_Energy_Array +_Get_Energy_Array.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_float), ctypes.c_bool, + ctypes.c_int, ctypes.c_int] +_Get_Energy_Array.restype = None + +_Get_Energy_Array_Names = _spirit.System_Get_Energy_Array_Names +_Get_Energy_Array_Names.argtypes = [ctypes.c_void_p, ctypes.POINTER(ctypes.c_char), + ctypes.c_int, ctypes.c_int] +_Get_Energy_Array_Names.restype = ctypes.c_int +def get_energy_contributions(p_state, divide_by_nspins = True, idx_image=-1, idx_chain=-1): + NULL = ctypes.POINTER(ctypes.c_char)() + + n_char_array = _Get_Energy_Array_Names(ctypes.c_void_p(p_state), NULL, + ctypes.c_int(idx_image), ctypes.c_int(idx_chain)) + + energy_array_names = (n_char_array*ctypes.c_char)() + + _Get_Energy_Array_Names(ctypes.c_void_p(p_state), energy_array_names, + ctypes.c_int(idx_image), ctypes.c_int(idx_chain)) + + contrib_names = str(energy_array_names[:].decode("utf-8")).split("|") + n_contribs = len(contrib_names) + energies = (n_contribs*ctypes.c_float)() + + _Get_Energy_Array(ctypes.c_void_p(p_state), energies, divide_by_nspins, + ctypes.c_int(idx_image), ctypes.c_int(idx_chain)) + + return dict(zip(contrib_names, energies)) ### Get Chain number of images _Update_Data = _spirit.System_Update_Data diff --git a/core/python/test/system.py b/core/python/test/system.py index 99e623276..3c8ce0733 100644 --- a/core/python/test/system.py +++ b/core/python/test/system.py @@ -5,7 +5,7 @@ spirit_py_dir = os.path.abspath(os.path.join(os.path.dirname( __file__ ), "..")) sys.path.insert(0, spirit_py_dir) -from spirit import state, system, configuration +from spirit import state, system, configuration, hamiltonian import unittest @@ -42,8 +42,16 @@ def test_get_spin_directions(self): def test_get_energy(self): # NOTE: that test is trivial E = system.get_energy(self.p_state) - - + + def test_get_energy_contributions(self): + configuration.plus_z(self.p_state) + configuration.domain(self.p_state, [0,0,-1], border_cylindrical=2) + system.update_data(self.p_state) + E_contribs = system.get_energy_contributions(self.p_state, divide_by_nspins=False) + E = system.get_energy(self.p_state) + system.print_energy_array(p_state) + self.assertEqual( len(E_contribs.values()), 3 ) # There should be 3 contributions + self.assertAlmostEqual( sum(E_contribs.values()), E, places=5 ) #TODO: Apparently we can not go higher with the number of decimal places, because the order of summation differs. This Should be invesitgated. # NOTE: there is no way to test the system.Update_Data() and system.Print_Energy_Array() ######### From fb00b96a40ba3d501905bd3617fb445634ba78b7 Mon Sep 17 00:00:00 2001 From: Moritz Date: Wed, 9 Jun 2021 13:37:00 +0200 Subject: [PATCH 32/45] Incorporated upstream changes in core/utility --- core/include/utility/Configuration_Chain.hpp | 23 +- core/include/utility/Configurations.hpp | 89 +- core/include/utility/Constants.hpp | 190 ++-- core/include/utility/Cubic_Hermite_Spline.hpp | 21 +- core/include/utility/Exception.hpp | 179 ++-- core/include/utility/Logging.hpp | 289 +++-- core/include/utility/Ordered_Lock.hpp | 42 +- core/include/utility/Timing.hpp | 61 +- core/src/utility/Configuration_Chain.cpp | 96 +- core/src/utility/Configurations.cpp | 987 +++++++++--------- core/src/utility/Cubic_Hermite_Spline.cpp | 77 +- core/src/utility/Exception.cpp | 393 +++---- core/src/utility/Logging.cpp | 553 +++++----- core/src/utility/Timing.cpp | 195 ++-- 14 files changed, 1668 insertions(+), 1527 deletions(-) diff --git a/core/include/utility/Configuration_Chain.hpp b/core/include/utility/Configuration_Chain.hpp index 635d34b51..386161545 100644 --- a/core/include/utility/Configuration_Chain.hpp +++ b/core/include/utility/Configuration_Chain.hpp @@ -1,6 +1,6 @@ #pragma once -#ifndef UTILITY_CONFIGURATION_CHAIN_H -#define UTILITY_CONFIGURATION_CHAIN_H +#ifndef SPIRIT_UTILITY_CONFIGURATION_CHAIN_HPP +#define SPIRIT_UTILITY_CONFIGURATION_CHAIN_HPP #include "Spirit_Defines.h" #include @@ -9,15 +9,16 @@ namespace Utility { - namespace Configuration_Chain - { - // Add noise to the images of a transition (except the border images) - void Add_Noise_Temperature(std::shared_ptr c, int idx_1, int idx_2, scalar temperature); +namespace Configuration_Chain +{ + +// Add noise to the images of a transition (except the border images) +void Add_Noise_Temperature( std::shared_ptr c, int idx_1, int idx_2, scalar temperature ); - // Homogeneous rotation of all spins from first to last configuration of the given configurations - void Homogeneous_Rotation(std::shared_ptr c, int idx_1, int idx_2); +// Homogeneous rotation of all spins from first to last configuration of the given configurations +void Homogeneous_Rotation( std::shared_ptr c, int idx_1, int idx_2 ); - };//end namespace Configurations -}//end namespace Utility +} // namespace Configuration_Chain +} // namespace Utility -#endif +#endif \ No newline at end of file diff --git a/core/include/utility/Configurations.hpp b/core/include/utility/Configurations.hpp index a7a6b1245..95c8e9b14 100644 --- a/core/include/utility/Configurations.hpp +++ b/core/include/utility/Configurations.hpp @@ -1,57 +1,72 @@ #pragma once -#ifndef UTILITY_CONFIGURATIONS_H -#define UTILITY_CONFIGURATIONS_H +#ifndef SPIRIT_UTILITY_CONFIGURATIONS_HPP +#define SPIRIT_UTILITY_CONFIGURATIONS_HPP #include "Spirit_Defines.h" #include -#include -#include #include +#include +#include namespace Utility { - namespace Configurations - { - // Default filter function - typedef std::function< bool(const Vector3&, const Vector3&) > filterfunction; - filterfunction const defaultfilter = [](const Vector3& spin, const Vector3& pos)->bool { return true; }; - void filter_to_mask(const vectorfield & spins, const vectorfield & positions, filterfunction filter, intfield & mask); +namespace Configurations +{ + +// Default filter function +using filterfunction = std::function; +const filterfunction defaultfilter = []( const Vector3 & spin, const Vector3 & pos ) -> bool { return true; }; +void filter_to_mask( const vectorfield & spins, const vectorfield & positions, filterfunction filter, intfield & mask ); + +// TODO: replace the Spin_System references with smart pointers?? + +void Move( vectorfield & configuration, const Data::Geometry & geometry, int da, int db, int dc ); + +// Insert data in certain region +void Insert( + Data::Spin_System & s, const vectorfield & configuration, int shift = 0, filterfunction filter = defaultfilter ); + +// orients all spins with x>pos into the direction of the v +void Domain( Data::Spin_System & s, Vector3 direction, filterfunction filter = defaultfilter ); - // TODO: replace the Spin_System references with smart pointers?? +// points all Spins in random directions +void Random( Data::Spin_System & s, filterfunction filter = defaultfilter, bool external = false ); - void Move(vectorfield& configuration, const Data::Geometry & geometry, int da, int db, int dc); +// Add temperature-scaled random noise to a system +void Add_Noise_Temperature( + Data::Spin_System & s, scalar temperature, int delta_seed = 0, filterfunction filter = defaultfilter ); - // Insert data in certain region - void Insert(Data::Spin_System &s, const vectorfield& configuration, int shift = 0, filterfunction filter = defaultfilter); +// Creates a toroid +void Hopfion( Data::Spin_System & s, Vector3 pos, scalar r, int order = 1, filterfunction filter = defaultfilter ); - // orients all spins with x>pos into the direction of the v - void Domain(Data::Spin_System &s, Vector3 direction, filterfunction filter=defaultfilter); +// Creates a Skyrmion +void Skyrmion( + Data::Spin_System & s, Vector3 pos, scalar r, scalar order, scalar phase, bool upDown, bool achiral, bool rl, + bool experimental, filterfunction filter = defaultfilter ); - // points all Spins in random directions - void Random(Data::Spin_System &s, filterfunction filter=defaultfilter, bool external = false); - // Add temperature-scaled random noise to a system - void Add_Noise_Temperature(Data::Spin_System & s, scalar temperature, int delta_seed=0, filterfunction filter=defaultfilter); +// Creates a Skyrmion, following the circular domain wall ("swiss knife") profile +void DW_Skyrmion( + Data::Spin_System & s, Vector3 pos, scalar dw_radius, scalar dw_width, scalar order, scalar phase, bool upDown, + bool achiral, bool rl, filterfunction filter = defaultfilter ); - // Creates a toroid - void Hopfion(Data::Spin_System & s, Vector3 pos, scalar r, int order=1, filterfunction filter=defaultfilter); +// Spin Spiral +void SpinSpiral( + Data::Spin_System & s, std::string direction_type, Vector3 q, Vector3 axis, scalar theta, + filterfunction filter = defaultfilter ); - // Creates a Skyrmion - void Skyrmion(Data::Spin_System & s, Vector3 pos, scalar r, scalar order, scalar phase, bool upDown, bool achiral, bool rl, bool experimental, filterfunction filter=defaultfilter); +// 2q Spin Spiral +void SpinSpiral( + Data::Spin_System & s, std::string direction_type, Vector3 q1, Vector3 q2, Vector3 axis, scalar theta, + filterfunction filter = defaultfilter ); - // Creates a Skyrmion, following the circular domain wall ("swiss knife") profile - void DW_Skyrmion(Data::Spin_System & s, Vector3 pos, scalar dw_radius, scalar dw_width, scalar order, scalar phase, bool upDown, bool achiral, bool rl, filterfunction filter=defaultfilter); +// Set atom types within a region of space +void Set_Atom_Types( Data::Spin_System & s, int atom_type = 0, filterfunction filter = defaultfilter ); - // Spin Spiral - void SpinSpiral(Data::Spin_System & s, std::string direction_type, Vector3 q, Vector3 axis, scalar theta, filterfunction filter=defaultfilter); - // 2q Spin Spiral - void SpinSpiral(Data::Spin_System & s, std::string direction_type, Vector3 q1, Vector3 q2, Vector3 axis, scalar theta, filterfunction filter=defaultfilter); +// Set spins to be pinned +void Set_Pinned( Data::Spin_System & s, bool pinned, filterfunction filter = defaultfilter ); - // Set atom types within a region of space - void Set_Atom_Types(Data::Spin_System & s, int atom_type=0, filterfunction filter=defaultfilter); - // Set spins to be pinned - void Set_Pinned(Data::Spin_System & s, bool pinned, filterfunction filter=defaultfilter); - };//end namespace Configurations -}//end namespace Utility +} // namespace Configurations +} // namespace Utility -#endif +#endif \ No newline at end of file diff --git a/core/include/utility/Constants.hpp b/core/include/utility/Constants.hpp index d15fa8d33..05c567d27 100644 --- a/core/include/utility/Constants.hpp +++ b/core/include/utility/Constants.hpp @@ -1,137 +1,127 @@ #pragma once -#ifndef UTILITY_CONSTANTS_H -#define UTILITY_CONSTANTS_H +#ifndef SPIRIT_UTILITY_CONSTANTS_HPP +#define SPIRIT_UTILITY_CONSTANTS_HPP namespace Utility { - /* - Constants by convention: - Spatial scale: Angstrom - Energy scale: Millielctronvolts - Time scale: Picoseconds - Magnetic fields scale: Tesla - */ - namespace Constants - { - // The Bohr Magneton [meV/T] - double const mu_B = 0.057883817555; - - // The vacuum permeability [T^2 m^3 / meV] - double const mu_0 = 2.0133545*1e-28; - // The Boltzmann constant [meV/K] - double const k_B = 0.08617330350; +/* +Constants by convention: +- Energy scale: Millielctronvolts +- Time scale: Picoseconds +- Magnetic fields scale: Tesla +*/ +namespace Constants +{ + +// The Bohr Magneton [meV/T] +double const mu_B = 0.057883817555; - // Planck constant [meV*ps/rad] - double const hbar = 0.6582119514; +// The vacuum permeability [T^2 m^3 / meV] +double const mu_0 = 2.0133545 * 1e-28; - // Gyromagnetic ratio of electron [rad/(ps*T)] - double const gamma = 0.1760859644; +// The Boltzmann constant [meV/K] +double const k_B = 0.08617330350; - // Electron (Landé) g-factor = gamma * hbar / mu_B [unitless] - double const g_e = 2.00231930436182; +// Planck constant [meV*ps/rad] +double const hbar = 0.6582119514; - // Millirydberg [mRy/meV] - double const mRy = 13.605693009; +// Gyromagnetic ratio of electron [rad/(ps*T)] +// Also gives the Larmor precession frequency for electron +double const gamma = 0.1760859644; - // erg [erg/meV] - double const erg = 6.2415091*1e+14; +// Electron (Landé) g-factor = gamma * hbar / mu_B [unitless] +double const g_e = 2.00231930436182; - // Joule [Joule/meV] - double const Joule = 6.2415091*1e+21; +// Millirydberg [mRy/meV] +double const mRy = 1.0 / 13.605693009; - // Pi [rad] - double const Pi = 3.141592653589793238462643383279502884197169399375105820974; +// erg [erg/meV] +double const erg = 6.2415091 * 1e14; - // Pi/2 [rad] - double const Pi_2 = 1.570796326794896619231321691639751442098584699687552910487; - } +// Joule [Joule/meV] +double const Joule = 6.2415091 * 1e+21; - /* - Constants_mRy by convention: - Spatial scale: Angstrom - Energy scale: Millirydberg - Time scale: Picoseconds - Magnetic fields scale: Tesla - */ - namespace Constants_mRy - { - // The Bohr Magneton [mRy/T] - double const mu_B = Constants::mu_B * Constants::mRy; +// Pi [rad] +double const Pi = 3.141592653589793238462643383279502884197169399375105820974; - // The vacuum permeability [T^2 m^3 / mRy] - double const mu_0 = Constants::mu_0 / Constants::mRy; +// Pi/2 [rad] +double const Pi_2 = 1.570796326794896619231321691639751442098584699687552910487; - // The Boltzmann constant [mRy/K] - double const k_B = Constants::k_B / Constants::mRy; +} // namespace Constants - // Planck constant [mRy*ps/rad] - double const hbar = Constants::hbar * Constants::mRy; +/* +Constants_mRy by convention: +- Energy scale: Millirydberg +- Time scale: Picoseconds +- Magnetic fields scale: Tesla +*/ +namespace Constants_mRy +{ - // Gyromagnetic ratio of electron [rad/(ps*T)] - double const gamma = Constants::gamma; +// The Bohr Magneton [mRy/T] +double const mu_B = Constants::mu_B / Constants::mRy; - // Electron g-factor [unitless] - double const g_e = Constants::g_e; +// The Boltzmann constant [mRy/K] +double const k_B = Constants::k_B / Constants::mRy; - // Millielectronvolt [meV/mRy] - double const meV = 1.0 / Constants::mRy; +// Planck constant [mRy*ps/rad] +double const hbar = Constants::hbar / Constants::mRy; - // Joule [Joule/mRy] - double const mRy = Constants::Joule / Constants::mRy; +// Millielectronvolt [meV/mRy] +double const meV = 1.0 / Constants::mRy; - // erg [erg/mRy] - double const erg = Constants::erg / Constants::mRy; +// Gyromagnetic ratio of electron [rad/(ps*T)] +double const gamma = 0.1760859644; - // Pi [rad] - double const Pi = Constants::Pi; +// Electron g-factor [unitless] +double const g_e = 2.00231930436182; - // Pi/2 [rad] - double const Pi_2 = Constants::Pi_2; - } +} // namespace Constants_mRy + +/* +Constants by micromagnetic convention (SI units): + - Spatial scale: meters + - Energy scale: Joule + - Time scale: seconds + - Magnetic fields scale: Tesla +*/ +namespace Constants_Micromagnetic +{ +// The Bohr Magneton [Joule/T] +double const mu_B = Constants::mu_B / Constants::Joule; - /* - Constants by micromagnetic convention (SI units): - Spatial scale: meters - Energy scale: Joule - Time scale: seconds - Magnetic fields scale: Tesla - */ - namespace Constants_Micromagnetic - { - // The Bohr Magneton [Joule/T] - double const mu_B = Constants::mu_B / Constants::Joule; +// The vacuum permeability [T^2 m^3 / Joule] +double const mu_0 = Constants::mu_0 / Constants::Joule; - // The vacuum permeability [T^2 m^3 / Joule] - double const mu_0 = Constants::mu_0 / Constants::Joule; +// The Boltzmann constant [J/K] +double const k_B = Constants::k_B * Constants::Joule; - // The Boltzmann constant [J/K] - double const k_B = Constants::k_B * Constants::Joule; +// Planck constant [J*s/rad] +double const hbar = Constants::hbar * Constants::Joule * 1e-12; - // Planck constant [J*s/rad] - double const hbar = Constants::hbar * Constants::Joule * 1e-12; +// Gyromagnetic ratio of electron [rad/(s*T)] +double const gamma = Constants::gamma * 1e+12; - // Gyromagnetic ratio of electron [rad/(s*T)] - double const gamma = Constants::gamma * 1e+12; +// Electron (Landé) g-factor = gamma * hbar / mu_B [unitless] +double const g_e = Constants::g_e; - // Electron (Landé) g-factor = gamma * hbar / mu_B [unitless] - double const g_e = Constants::g_e; +// meV [meV/Joule] +double const meV = 1.0 / Constants::Joule; - // meV [meV/Joule] - double const meV = 1.0 / Constants::Joule; +// Millirydberg [mRy/Joule] +double const mRy = Constants::mRy / Constants::Joule; - // Millirydberg [mRy/Joule] - double const mRy = Constants::mRy / Constants::Joule; +// erg [erg/Joule] +double const erg = Constants::erg / Constants::Joule; - // erg [erg/Joule] - double const erg = Constants::erg / Constants::Joule; +// Pi [rad] +double const Pi = Constants::Pi; - // Pi [rad] - double const Pi = Constants::Pi; +// Pi/2 [rad] +double const Pi_2 = Constants::Pi_2; +} // namespace Constants_Micromagnetic - // Pi/2 [rad] - double const Pi_2 = Constants::Pi_2; - } -} +} // namespace Utility #endif \ No newline at end of file diff --git a/core/include/utility/Cubic_Hermite_Spline.hpp b/core/include/utility/Cubic_Hermite_Spline.hpp index 9406df0c9..accf15a5a 100644 --- a/core/include/utility/Cubic_Hermite_Spline.hpp +++ b/core/include/utility/Cubic_Hermite_Spline.hpp @@ -1,6 +1,6 @@ #pragma once -#ifndef UTILITY_CUBIC_HERMITE_SPLINE_H -#define UTILITY_CUBIC_HERMITE_SPLINE_H +#ifndef SPIRIT_UTILITY_CUBIC_HERMITE_SPLINE_HPP +#define SPIRIT_UTILITY_CUBIC_HERMITE_SPLINE_HPP #include "Spirit_Defines.h" @@ -8,11 +8,14 @@ namespace Utility { - namespace Cubic_Hermite_Spline - { - // Interplation by cubic Hermite spline, see http://de.wikipedia.org/wiki/Kubisch_Hermitescher_Spline - std::vector> Interpolate(const std::vector & x, const std::vector & p, const std::vector & m, int n_interpolations); - };//end namespace Cubic_Hermite_Spline -}//end namespace Utility +namespace Cubic_Hermite_Spline +{ + +// Interplation by cubic Hermite spline, see http://de.wikipedia.org/wiki/Kubisch_Hermitescher_Spline +std::vector> Interpolate( + const std::vector & x, const std::vector & p, const std::vector & m, int n_interpolations ); + +} // namespace Cubic_Hermite_Spline +} // namespace Utility -#endif +#endif \ No newline at end of file diff --git a/core/include/utility/Exception.hpp b/core/include/utility/Exception.hpp index a68a4e0c6..11a360224 100644 --- a/core/include/utility/Exception.hpp +++ b/core/include/utility/Exception.hpp @@ -1,98 +1,103 @@ #pragma once -#ifndef UTILITY_EXEPTION_H -#define UTILITY_EXEPTION_H +#ifndef SPIRIT_UTILITY_EXEPTION_HPP +#define SPIRIT_UTILITY_EXEPTION_HPP #include + #include namespace Utility { - enum class Exception_Classifier + +enum class Exception_Classifier +{ + File_not_Found, + System_not_Initialized, + Division_by_zero, + Simulated_domain_too_small, + Not_Implemented, + Non_existing_Image, + Non_existing_Chain, + Input_parse_failed, + Bad_File_Content, + Standard_Exception, + CUDA_Error, + Unknown_Exception + // TODO: from Chain.cpp + // Last image deletion ? + // Empty clipboard ? +}; + +// Spirit library exception class: +// Derived from std::runtime_error. +// Adds file, line and function information to the exception message. +// Contains an exception classifier and a level so that the handler +// can decide if execution should be stopped or can continue. +class S_Exception : public std::runtime_error +{ +public: + S_Exception( + Exception_Classifier classifier, Log_Level level, const std::string & message, const char * file, + unsigned int line, const std::string & function ) + : std::runtime_error( message ) { - File_not_Found, - System_not_Initialized, - Division_by_zero, - Simulated_domain_too_small, - Not_Implemented, - Non_existing_Image, - Non_existing_Chain, - Input_parse_failed, - Bad_File_Content, - Standard_Exception, - CUDA_Error, - Unknown_Exception - // TODO: from Chain.cpp - // Last image deletion ? - // Empty clipboard ? - }; - - - // Spirit library exception class: - // Derived from std::runtime_error. - // Adds file, line and function information to the exception message. - // Contains an exception classifier and a level so that the handler - // can decide if execution should be stopped or can continue. - class S_Exception : public std::runtime_error + this->classifier = classifier; + this->level = level; + this->message = message; + this->file = file; + this->line = line; + this->function = function; + + // The complete description + this->_what = fmt::format( "{}:{} in function \'{}\':\n{:>49}{}", file, line, function, " ", message ); + } + + ~S_Exception() throw() {} + + const char * what() const throw() { - public: - S_Exception(Exception_Classifier classifier, Log_Level level, const std::string & message, const char * file, unsigned int line, const std::string & function) : - std::runtime_error(message) - { - this->classifier = classifier; - this->level = level; - this->message = message; - this->file = file; - this->line = line; - this->function = function; - - // The complete description - this->_what = fmt::format("{}:{} in function \'{}\':\n{:>49}{}", file, line, function, " ", message); - } - - ~S_Exception() throw() {} - - const char *what() const throw() - { - return _what.c_str(); - } - - Exception_Classifier classifier; - Log_Level level; - std::string message; - std::string file; - unsigned int line; - std::string function; - - private: - std::string _what; - }; - - - // Rethrow (creating a std::nested_exception) an exception using the Exception class - // to add file and line info - void rethrow(const std::string & message, const char * file, unsigned int line, const std::string & function); - - // Handle_Exception_API finalizes what should be done when an exception is encountered at the API layer. - // This function should only be used inside API functions, since that is the top level at which an - // exception is caught. - void Handle_Exception_API( const char * file, unsigned int line, const std::string & function="", int idx_image=-1, int idx_chain=-1 ); - - // Handle_Exception_Core finalizes what should be done when an exception is encountered inside the core. - // This function should only be used inside the core, below the API layer. - void Handle_Exception_Core(std::string message, const char * file, unsigned int line, const std::string & function); - - - // Shorthand for throwing a Spirit library exception with file and line info - #define spirit_throw(classifier, level, message) throw Utility::S_Exception(classifier, level, message, __FILE__, __LINE__, __func__) - - // Rethrow any exception to create a backtraceable nested exception - #define spirit_rethrow(message) Utility::rethrow(message, __FILE__, __LINE__, __func__) - - // Handle exception with backtrace and logging information on the calling API function - #define spirit_handle_exception_api(idx_image, idx_chain) Utility::Handle_Exception_API(__FILE__, __LINE__, __func__, idx_image, idx_chain) - - // Handle exception with backtrace and logging information on the calling core function - #define spirit_handle_exception_core(message) Utility::Handle_Exception_Core(message, __FILE__, __LINE__, __func__) -} + return _what.c_str(); + } + + Exception_Classifier classifier; + Log_Level level; + std::string message; + std::string file; + unsigned int line; + std::string function; + +private: + std::string _what; +}; + +// Rethrow (creating a std::nested_exception) an exception using the Exception class +// to add file and line info +void rethrow( const std::string & message, const char * file, unsigned int line, const std::string & function ); + +// Handle_Exception_API finalizes what should be done when an exception is encountered at the API layer. +// This function should only be used inside API functions, since that is the top level at which an +// exception is caught. +void Handle_Exception_API( + const char * file, unsigned int line, const std::string & function = "", int idx_image = -1, int idx_chain = -1 ); + +// Handle_Exception_Core finalizes what should be done when an exception is encountered inside the core. +// This function should only be used inside the core, below the API layer. +void Handle_Exception_Core( std::string message, const char * file, unsigned int line, const std::string & function ); + +// Shorthand for throwing a Spirit library exception with file and line info +#define spirit_throw( classifier, level, message ) \ + throw Utility::S_Exception( classifier, level, message, __FILE__, __LINE__, __func__ ) + +// Rethrow any exception to create a backtraceable nested exception +#define spirit_rethrow( message ) Utility::rethrow( message, __FILE__, __LINE__, __func__ ) + +// Handle exception with backtrace and logging information on the calling API function +#define spirit_handle_exception_api( idx_image, idx_chain ) \ + Utility::Handle_Exception_API( __FILE__, __LINE__, __func__, idx_image, idx_chain ) + +// Handle exception with backtrace and logging information on the calling core function +#define spirit_handle_exception_core( message ) Utility::Handle_Exception_Core( message, __FILE__, __LINE__, __func__ ) + +} // namespace Utility #endif \ No newline at end of file diff --git a/core/include/utility/Logging.hpp b/core/include/utility/Logging.hpp index ced1447f2..ec7843d1e 100644 --- a/core/include/utility/Logging.hpp +++ b/core/include/utility/Logging.hpp @@ -1,166 +1,157 @@ #pragma once -#ifndef UTILITY_LOGGING_H -#define UTILITY_LOGGING_H +#ifndef SPIRIT_UTILITY_LOGGING_HPP +#define SPIRIT_UTILITY_LOGGING_HPP #include #include -#include -#include #include -#include +#include #include +#include +#include // Define Log as the singleton instance, so that messages can be sent with Log(..., message, ...) #ifndef Log - #define Log Utility::LoggingHandler::getInstance() +#define Log Utility::LoggingHandler::getInstance() #endif namespace Utility { - // Unfortunately, we must ensure the equivalence of the defines and - // the enums by setting them in this ugly way - /* - List of possible senders of a Log Entry - */ - enum class Log_Sender - { - All = Log_Sender_All, - IO = Log_Sender_IO, - GNEB = Log_Sender_GNEB, - LLG = Log_Sender_LLG, - MC = Log_Sender_MC, - MMF = Log_Sender_MMF, - API = Log_Sender_API, - UI = Log_Sender_UI, - HTST = Log_Sender_HTST, - EMA = Log_Sender_EMA - }; - - // Unfortunately, we must ensure the equivalence of the defines and - // the enums by setting them in this ugly way - /* - List of possible levels of a Log Entry - */ - enum class Log_Level - { - All = Log_Level_All, - Severe = Log_Level_Severe, - Error = Log_Level_Error, - Warning = Log_Level_Warning, - Parameter = Log_Level_Parameter, - Info = Log_Level_Info, - Debug = Log_Level_Debug - }; - - /* - The Log Entry - The Logging Handler contains a vector of Log Entries - */ - struct LogEntry - { - std::chrono::system_clock::time_point time; - Log_Sender sender; - Log_Level level; - std::vector message_lines; - int idx_image; - int idx_chain; - }; - - // Convert the contents of a Log Entry to a string - std::string LogEntryToString(LogEntry entry, bool braces_separators = true); - std::string LogBlockToString(std::vector entries, bool braces_separators = true); - - /* - The Logging Handler keeps all Log Entries and provides methods to dump or append - the entire Log to a file. - The Handler is a singleton. - */ - class LoggingHandler + +// List of possible senders of a Log Entry +enum class Log_Sender +{ + All = Log_Sender_All, + IO = Log_Sender_IO, + GNEB = Log_Sender_GNEB, + LLG = Log_Sender_LLG, + MC = Log_Sender_MC, + MMF = Log_Sender_MMF, + API = Log_Sender_API, + UI = Log_Sender_UI, + HTST = Log_Sender_HTST, + EMA = Log_Sender_EMA +}; + +// List of possible levels of a Log Entry +enum class Log_Level +{ + All = Log_Level_All, + Severe = Log_Level_Severe, + Error = Log_Level_Error, + Warning = Log_Level_Warning, + Parameter = Log_Level_Parameter, + Info = Log_Level_Info, + Debug = Log_Level_Debug +}; + +// The Logging Handler contains a vector of Log Entries +struct LogEntry +{ + std::chrono::system_clock::time_point time; + Log_Sender sender; + Log_Level level; + std::vector message_lines; + int idx_image; + int idx_chain; +}; + +// Convert the contents of a log entry to a string +std::string LogEntryToString( LogEntry entry, bool braces_separators = true ); +// Convert the contents of a log block to a formatted string +std::string LogBlockToString( std::vector entries, bool braces_separators = true ); + +/* +The Logging Handler keeps all Log Entries and provides methods to dump or append the entire Log to a file. +Note: the Handler is a singleton. +*/ +class LoggingHandler +{ +public: + // Send Log messages + void Send( Log_Level level, Log_Sender sender, std::string message, int idx_image = -1, int idx_chain = -1 ); + void operator()( Log_Level level, Log_Sender sender, std::string message, int idx_image = -1, int idx_chain = -1 ); + void SendBlock( + Log_Level level, Log_Sender sender, std::vector messages, int idx_image = -1, int idx_chain = -1 ); + void operator()( + Log_Level level, Log_Sender sender, std::vector messages, int idx_image = -1, int idx_chain = -1 ); + + // Get the Log's entries + std::vector GetEntries(); + + // Dumps the log to File fileName + void Append_to_File(); + void Dump_to_File(); + + // The file tag in from of the Log or Output files (if "