Ejemplo n.º 1
0
def port(picklefile):
    picklefile = Path(picklefile)

    name = picklefile.name

    vibname, key, pckl = name.rsplit('.', 3)
    assert pckl == 'pckl'

    cache = MultiFileJSONCache(picklefile.parent / vibname)

    obj = pickle.loads(picklefile.read_bytes())
    if isinstance(obj, np.ndarray):  # vibrations
        dct = {'forces': obj}
    else:  # Infrared
        forces, dipole = obj
        assert isinstance(forces, np.ndarray), f'not supported: {type(forces)}'
        assert isinstance(dipole, np.ndarray), f'not supported: {type(dipole)}'
        dct = {'forces': forces, 'dipole': dipole}

    outfilename = cache._filename(key)

    if key in cache:
        del cache[key]

    cache[key] = dct
    print(f'wrote {picklefile} ==> {outfilename}')
Ejemplo n.º 2
0
    def __init__(self,
                 atoms,
                 calc=None,
                 supercell=(1, 1, 1),
                 name=None,
                 delta=0.01,
                 center_refcell=False):
        """Init with an instance of class ``Atoms`` and a calculator.

        Parameters:

        atoms: Atoms object
            The atoms to work on.
        calc: Calculator
            Calculator for the supercell calculation.
        supercell: tuple
            Size of supercell given by the number of repetitions (l, m, n) of
            the small unit cell in each direction.
        name: str
            Base name to use for files.
        delta: float
            Magnitude of displacement in Ang.
        center_refcell: bool
            Reference cell in which the atoms will be displaced. If False, then
            corner cell in supercell is used. If True, then cell in the center
            of the supercell is used.

        """

        # Store atoms and calculator
        self.atoms = atoms
        self.calc = calc

        # Displace all atoms in the unit cell by default
        self.indices = np.arange(len(atoms))
        self.name = name
        self.delta = delta
        self.center_refcell = center_refcell
        self.supercell = supercell

        self.cache = MultiFileJSONCache('phonons-cache')
Ejemplo n.º 3
0
class ElphCache:
    def __init__(self, name):
        self.cache = MultiFileJSONCache(name)

    def read(self, displacement: tp.Union[interop.AseDisplacement, str]):
        d = self.cache[str(displacement)]
        return ElphDataset(**d)

    @contextmanager
    def lock(self, displacement: tp.Union[interop.AseDisplacement, str]):
        class MyHandle:
            def __init__(self, handle):
                self._handle = handle

            def write(self, data: ElphDataset):
                self._handle.save(data._asdict())

        with self.cache.lock(str(displacement)) as handle:
            if handle is None:
                yield None
            else:
                yield MyHandle(handle)
Ejemplo n.º 4
0
class Displacement:
    """Abstract base class for phonon and el-ph supercell calculations.

    Both phonons and the electron-phonon interaction in periodic systems can be
    calculated with the so-called finite-displacement method where the
    derivatives of the total energy and effective potential are obtained from
    finite-difference approximations, i.e. by displacing the atoms. This class
    provides the required functionality for carrying out the calculations for
    the different displacements in its ``run`` member function.

    Derived classes must overwrite the ``__call__`` member function which is
    called for each atomic displacement.

    """
    def __init__(self,
                 atoms,
                 calc=None,
                 supercell=(1, 1, 1),
                 name=None,
                 delta=0.01,
                 center_refcell=False):
        """Init with an instance of class ``Atoms`` and a calculator.

        Parameters:

        atoms: Atoms object
            The atoms to work on.
        calc: Calculator
            Calculator for the supercell calculation.
        supercell: tuple
            Size of supercell given by the number of repetitions (l, m, n) of
            the small unit cell in each direction.
        name: str
            Base name to use for files.
        delta: float
            Magnitude of displacement in Ang.
        center_refcell: bool
            Reference cell in which the atoms will be displaced. If False, then
            corner cell in supercell is used. If True, then cell in the center
            of the supercell is used.

        """

        # Store atoms and calculator
        self.atoms = atoms
        self.calc = calc

        # Displace all atoms in the unit cell by default
        self.indices = np.arange(len(atoms))
        self.name = name
        self.delta = delta
        self.center_refcell = center_refcell
        self.supercell = supercell

        self.cache = MultiFileJSONCache('phonons-cache')

    def define_offset(self):  # Reference cell offset

        if not self.center_refcell:
            # Corner cell
            self.offset = 0
        else:
            # Center cell
            N_c = self.supercell
            self.offset = (N_c[0] // 2 * (N_c[1] * N_c[2]) +
                           N_c[1] // 2 * N_c[2] + N_c[2] // 2)
        return self.offset

    @property  # type: ignore
    @ase.utils.deprecated('Please use phonons.supercell instead of .N_c')
    def N_c(self):
        return self._supercell

    @property
    def supercell(self):
        return self._supercell

    @supercell.setter
    def supercell(self, supercell):
        assert len(supercell) == 3
        self._supercell = tuple(supercell)
        self.define_offset()
        self._lattice_vectors_array = self.compute_lattice_vectors()

    @ase.utils.deprecated('Please use phonons.compute_lattice_vectors()'
                          ' instead of .lattice_vectors()')
    def lattice_vectors(self):
        return self.compute_lattice_vectors()

    def compute_lattice_vectors(self):
        """Return lattice vectors for cells in the supercell."""
        # Lattice vectors -- ordered as illustrated in class docstring

        # Lattice vectors relevative to the reference cell
        R_cN = np.indices(self.supercell).reshape(3, -1)
        N_c = np.array(self.supercell)[:, np.newaxis]
        if self.offset == 0:
            R_cN += N_c // 2
            R_cN %= N_c
        R_cN -= N_c // 2
        return R_cN

    def __call__(self, *args, **kwargs):
        """Member function called in the ``run`` function."""

        raise NotImplementedError("Implement in derived classes!.")

    def set_atoms(self, atoms):
        """Set the atoms to vibrate.

        Parameters:

        atoms: list
            Can be either a list of strings, ints or ...

        """

        assert isinstance(atoms, list)
        assert len(atoms) <= len(self.atoms)

        if isinstance(atoms[0], str):
            assert np.all([isinstance(atom, str) for atom in atoms])
            sym_a = self.atoms.get_chemical_symbols()
            # List for atomic indices
            indices = []
            for type in atoms:
                indices.extend(
                    [a for a, atom in enumerate(sym_a) if atom == type])
        else:
            assert np.all([isinstance(atom, int) for atom in atoms])
            indices = atoms

        self.indices = indices

    def _disp(self, a, i, step):
        from ase.vibrations.vibrations import Displacement as VDisplacement
        return VDisplacement(a, i, np.sign(step), abs(step), self)

    def run(self):
        """Run the calculations for the required displacements.

        This will do a calculation for 6 displacements per atom, +-x, +-y, and
        +-z. Only those calculations that are not already done will be
        started. Be aware that an interrupted calculation may produce an empty
        file (ending with .json), which must be deleted before restarting the
        job. Otherwise the calculation for that displacement will not be done.

        """

        # Atoms in the supercell -- repeated in the lattice vector directions
        # beginning with the last
        atoms_N = self.atoms * self.supercell

        # Set calculator if provided
        assert self.calc is not None, "Provide calculator in __init__ method"
        atoms_N.calc = self.calc

        # Do calculation on equilibrium structure
        eq_disp = self._disp(0, 0, 0)
        #with self.cache.lock(f'{self.name}.eq') as handle:
        with self.cache.lock(eq_disp.name) as handle:
            if handle is not None:
                output = self(atoms_N)
                # Write output to file
                if world.rank == 0:
                    handle.save(output)

        # Positions of atoms to be displaced in the reference cell
        natoms = len(self.atoms)
        offset = natoms * self.offset
        pos = atoms_N.positions[offset:offset + natoms].copy()

        # Loop over all displacements
        for a in self.indices:
            for i in range(3):
                for sign in [-1, 1]:
                    disp = self._disp(a, i, sign)
                    #key = '%s.%d%s%s' % (self.name, a, 'xyz'[i], ' +-'[sign])
                    with self.cache.lock(disp.name) as handle:
                        if handle is None:
                            continue
                        try:
                            atoms_N.positions[offset + a, i] = \
                                pos[a, i] + sign * self.delta

                            result = self.calculate(atoms_N, disp)
                            handle.save(result)
                        finally:
                            # Return to initial positions
                            atoms_N.positions[offset + a, i] = pos[a, i]

    def clean(self):
        """Delete generated json files."""

        if isfile(self.name + '.eq.json'):
            remove(self.name + '.eq.json')

        for a in self.indices:
            for i in 'xyz':
                for sign in '-+':
                    name = '%s.%d%s%s.json' % (self.name, a, i, sign)
                    if isfile(name):
                        remove(name)
Ejemplo n.º 5
0
def cache():
    return MultiFileJSONCache('cache')
Ejemplo n.º 6
0
 def __init__(self, name):
     self.cache = MultiFileJSONCache(name)