Skip to content

Commit

Permalink
Python API: Added function system.get_energy_contributions
Browse files Browse the repository at this point in the history
- Returns a dictionary with strings as keys and floats as values. Can be used like `energy_ex = contribs["Exchange"]`, etc.
  • Loading branch information
Moritz committed May 26, 2021
1 parent 1dd74ae commit b5351e5
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 16 deletions.
7 changes: 6 additions & 1 deletion core/include/Spirit/System.h
Expand Up @@ -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;
Expand Down
41 changes: 31 additions & 10 deletions core/python/spirit/system.py
Expand Up @@ -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
Expand Down
14 changes: 11 additions & 3 deletions core/python/test/system.py
Expand Up @@ -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

Expand Down Expand Up @@ -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()

#########
Expand Down
53 changes: 51 additions & 2 deletions core/src/Spirit/System.cpp
Expand Up @@ -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<Data::Spin_System> image;
Expand All @@ -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; i<image->E_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; i<image->E_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<Data::Spin_System> image;
std::shared_ptr<Data::Spin_System_Chain> 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; i<image->E_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
Expand Down

0 comments on commit b5351e5

Please sign in to comment.