Source code for ase2sprkkr.asr.exchange

import numpy as np
from asr.core import command, option, ASRResult


@command(module='asr.exchange',
         creates=['gs_2mag.gpw', 'exchange.gpw'],
         requires=['gs.gpw'],
         resources='40:10h')
@option('--gs', help='Ground state on which exchange calculation is based',
        type=str)
def calculate(gs: str = 'gs.gpw') -> ASRResult:
    """Calculate two spin configurations."""
    from gpaw import GPAW
    from asr.utils import magnetic_atoms

    calc = GPAW(gs, fixdensity=False, txt=None)
    atoms = calc.atoms
    magnetic = magnetic_atoms(atoms)
    assert sum(magnetic) in [1, 2], \
        ('Cannot handle %d magnetic atoms' % sum(magnetic))
    if sum(magnetic) == 2:
        calc.reset()
        calc.set(txt='gs_2mag.txt')
        atoms.calc = calc
        atoms.get_potential_energy()
        calc.write('gs_2mag.gpw')

        a1, a2 = np.where(magnetic)[0]
        magmoms_i = calc.get_magnetic_moments()
        assert np.round(np.abs(magmoms_i[a1] / magmoms_i[a2]), 1) == 1, \
            'The two magnetic moments differ'
        magmoms_e = np.zeros(len(atoms), float)
        magmoms_e[a1] = np.max(np.abs(magmoms_i))
        if np.sign(magmoms_i[a1]) == np.sign(magmoms_i[a2]):
            magmoms_e[a2] = -magmoms_e[a1]
        else:
            magmoms_e[a2] = magmoms_e[a1]
        atoms.set_initial_magnetic_moments(magmoms_e)
        calc.reset()
        calc.set(txt='exchange.txt')
        atoms.calc = calc
        atoms.get_potential_energy()
        calc.write('exchange.gpw')

    else:
        a1 = np.where(magnetic)[0]
        mag = np.max(np.abs(calc.get_magnetic_moments()))
        magmoms = np.zeros(len(atoms), float)
        magmoms[a1] = mag
        atoms.set_initial_magnetic_moments(magmoms)
        atoms = atoms.repeat((2, 1, 1))
        calc.reset()
        calc.set(txt='gs_2mag.txt')
        atoms.calc = calc
        atoms.get_potential_energy()
        calc.write('gs_2mag.gpw')

        magnetic = magnetic_atoms(atoms)
        a1, a2 = np.where(magnetic)[0]
        magmoms_i = calc.get_magnetic_moments()
        assert np.round(magmoms_i[a1] / magmoms_i[a2], 1) == 1, \
            'The two magnetic moments differ'
        mag = np.max(np.abs(magmoms_i))
        magmoms_e = np.zeros(len(atoms), float)
        magmoms_e[a1] = mag
        magmoms_e[a2] = -mag
        atoms.set_initial_magnetic_moments(magmoms_e)
        calc.reset()
        calc.set(txt='exchange.txt')
        atoms.calc = calc
        atoms.get_potential_energy()
        calc.write('exchange.gpw')


[docs] def get_parameters(gs, exchange, txt=False, dis_cut=0.2, line=False, a0=None): """Extract Heisenberg parameters.""" from gpaw import GPAW from gpaw.spinorbit import soc_eigenstates from ase.dft.bandgap import bandgap calc_gs_2mag = GPAW(gs) calc_exchange = GPAW(exchange) m_gs = calc_gs_2mag.get_magnetic_moment() m_ex = calc_exchange.get_magnetic_moment() if np.abs(m_gs) > np.abs(m_ex): assert np.abs(m_gs) - np.abs(m_ex) > 0.1, \ 'AFM calculation did not converge to target state' calc_fm = calc_gs_2mag calc_afm = calc_exchange else: assert np.abs(m_ex) - np.abs(m_gs) > 0.1, \ 'AFM calculation did not converge to target state' calc_afm = calc_gs_2mag calc_fm = calc_exchange atoms = calc_fm.atoms if a0 is None: a0 = np.argmax(np.abs(calc_fm.get_magnetic_moments())) el = atoms[a0].symbol a_i = [] for i in range(len(atoms)): if atoms[i].symbol == el: a_i.append(i) atoms = atoms[a_i].repeat((3, 3, 1)) dis_i = atoms.get_distances(a0, range(len(atoms)), mic=True) dis0 = np.sort(dis_i)[1] N = len(np.where(np.sort(dis_i)[1:] / dis0 - 1 < dis_cut)[0]) E_fm = calc_fm.get_potential_energy() / 2 E_afm = calc_afm.get_potential_energy() / 2 gap_fm, p1, p2 = bandgap(calc_fm, output=None) gap_afm, p1, p2 = bandgap(calc_afm, output=None) E_fm_x, E_fm_y, E_fm_z = ( soc_eigenstates(calc_fm, theta=theta, phi=phi).calculate_band_energy() / 2 for theta, phi in [(90, 0), (90, 90), (0, 0)]) E_afm_x, E_afm_y, E_afm_z = ( soc_eigenstates(calc_afm, theta=theta, phi=phi).calculate_band_energy() / 2 for theta, phi in [(90, 0), (90, 90), (0, 0)]) E_fm_x = (E_fm_x + E_fm_y) / 2 E_afm_x = (E_afm_x + E_afm_y) / 2 dE_fm = (E_fm_x - E_fm_z) dE_afm = (E_afm_x - E_afm_z) S = np.abs(np.round(calc_fm.get_magnetic_moment() / 2)) S = S / 2 if S == 0: S = 1 / 2 if line: if N == 4: N_afm = 2 N_fm = 2 elif N == 6: N_afm = 4 N_fm = 2 else: print('Line not recognized') N_afm = N N_fm = 0 else: if N == 6: N_afm = 4 N_fm = 2 elif N == 9: N_afm = 3 N_fm = 6 else: N_afm = N N_fm = 0 J = (E_afm + E_afm_x - E_fm - E_fm_x) / (N_afm * S**2) if np.abs(S - 0.5) < 0.1: A = 0 if J > 0 or (N == 4 and line): B = dE_fm / (N * S**2) else: B = -dE_afm / (N_afm - N_fm) / S**2 else: A = dE_fm * (1 - N_fm / N_afm) + dE_afm * (1 + N_fm / N_afm) A /= (2 * S - 1) * S B = (dE_fm - dE_afm) / (N_afm * S**2) return J, A, B
@command(module='asr.exchange', requires=['gs_2mag.gpw', 'exchange.gpw'], dependencies=['asr.exchange@calculate', 'asr.gs']) def main() -> ASRResult: """Collect data.""" from ase.io import read N_gs = len(read('gs.gpw')) N_exchange = len(read('gs_2mag.gpw')) if N_gs == N_exchange: line = False else: line = True J, A, B = get_parameters('gs_2mag.gpw', 'exchange.gpw', line=line) data = {} data['J'] = J * 1000 data['__key_descriptions__'] = {'J': 'KVP: Exchange coupling [meV]'} data['A'] = A * 1000 data['__key_descriptions__'] = {'A': 'KVP: Single-ion anisotropy [meV]'} data['B'] = B * 1000 data['__key_descriptions__'] = {'B': 'KVP: Anisotropic exchange [meV]'} return data if __name__ == '__main__': main.cli()