Пример #1
0
    def read(self, method='standard', direction='central'):
        """Read data from a pre-performed calculation."""

        self.timer.start('read')
        self.timer.start('vibrations')
        Vibrations.read(self, method, direction)
        # we now have:
        # self.H     : Hessian matrix
        # self.im    : 1./sqrt(masses)
        # self.modes : Eigenmodes of the mass weighted Hessian
        self.om_Q = self.hnu.real    # energies in eV
        self.om_v = self.om_Q
        # pre-factors for one vibrational excitation
        with np.errstate(divide='ignore'):
            self.vib01_Q = np.where(self.om_Q > 0,
                                    1. / np.sqrt(2 * self.om_Q), 0)
        # -> sqrt(amu) * Angstrom
        self.vib01_Q *= np.sqrt(u.Ha * u._me / u._amu) * u.Bohr
        self.timer.stop('vibrations')


        self.timer.start('excitations')
        self.init_parallel_read()
        if not hasattr(self, 'ex0E_p'):
            if self.overlap:
                self.read_excitations_overlap()
            else:
                self.read_excitations()
        self.timer.stop('excitations')
        self.timer.stop('read')
Пример #2
0
    def test_harmonic_vibrations(self, testdir):
        """Check the numerics with a trivial case: one atom in harmonic well"""
        rng = np.random.RandomState(42)

        k = rng.rand()

        ref_atoms = Atoms('H', positions=np.zeros([1, 3]))
        atoms = ref_atoms.copy()
        mass = atoms.get_masses()[0]

        atoms.calc = ForceConstantCalculator(D=np.eye(3) * k,
                                             ref=ref_atoms,
                                             f0=np.zeros((1, 3)))
        vib = Vibrations(atoms, name='harmonic')
        vib.run()
        vib.read()

        expected_energy = (
            units._hbar  # In J/s
            * np.sqrt(k  # In eV/A^2
                      * units._e  # eV -> J
                      * units.m**2  # A^-2 -> m^-2
                      / mass  # in amu
                      / units._amu  # amu^-1 -> kg^-1
                      )) / units._e  # J/s -> eV/s

        assert np.allclose(vib.get_energies(), expected_energy)
Пример #3
0
 def read(self, method='standard', direction='central'):
     """Read data from a pre-performed calculation."""
     if not hasattr(self, 'modes'):
         self.timer.start('read vibrations')
         Vibrations.read(self, method, direction)
         # we now have:
         # self.H     : Hessian matrix
         # self.im    : 1./sqrt(masses)
         # self.modes : Eigenmodes of the mass weighted H
         self.om_r = self.hnu.real  # energies in eV
         self.timer.stop('read vibrations')
     if not hasattr(self, 'ex0E_p'):
         self.read_excitations()
Пример #4
0
 def read(self, method='standard', direction='central'):
     """Read data from a pre-performed calculation."""
     if not hasattr(self, 'modes'):
         self.timer.start('read vibrations')
         Vibrations.read(self, method, direction)
         # we now have:
         # self.H     : Hessian matrix
         # self.im    : 1./sqrt(masses)
         # self.modes : Eigenmodes of the mass weighted H
         self.om_r = self.hnu.real    # energies in eV
         self.timer.stop('read vibrations')
     if not hasattr(self, 'ex0E_p'):
         self.read_excitations()
Пример #5
0
    def test_pickle_manipulation(self, n2_emt):
        atoms = n2_emt
        vib = Vibrations(atoms, name='interrupt')
        vib.run()

        disp_file = 'interrupt.1x-.pckl'
        comb_file = 'interrupt.all.pckl'
        assert os.path.isfile(disp_file)
        assert not os.path.isfile(comb_file)

        with pytest.raises(RuntimeError):
            vib.split()

        # Build a combined file
        assert vib.combine() == 13

        # Individual displacements should be gone, combination should exist
        assert not os.path.isfile(disp_file)
        assert os.path.isfile(comb_file)

        # Not allowed to run after data has been combined
        with pytest.raises(RuntimeError):
            vib.run()
            # But reading is allowed
            vib.read()

        # Splitting should fail if any split file already exists
        with open(disp_file, 'w') as f:
            f.write("hello")
        with pytest.raises(RuntimeError):
            vib.split()
        os.remove(disp_file)

        # Now split() for real: replace .all.pckl file with displacements
        vib.split()
        assert os.path.isfile(disp_file)
        assert not os.path.isfile(comb_file)

        # Not allowed to clobber existing combined file
        with open(comb_file, 'w') as f:
            f.write("Hello")
        with pytest.raises(RuntimeError):
            vib.combine()
        os.remove(comb_file)

        # Combining data also fails if some data is missing
        os.remove('interrupt.1x-.pckl')
        with pytest.raises(RuntimeError):
            vib.combine()

        vib.clean()
Пример #6
0
    def test_json_manipulation(self, testdir, random_dimer):
        vib = Vibrations(random_dimer, name='interrupt')
        vib.run()

        disp_file = Path('interrupt/cache.1x-.json')
        comb_file = Path('interrupt/combined.json')
        assert disp_file.is_file()
        assert not comb_file.is_file()

        # Should do nothing harmful as files are already split
        # (It used to raise an error but this is no longer implemented.)
        vib.split()

        # Build a combined file
        assert vib.combine() == 13

        # Individual displacements should be gone, combination should exist
        assert not disp_file.is_file()
        assert comb_file.is_file()

        # Not allowed to run after data has been combined
        with pytest.raises(RuntimeError):
            vib.run()
        # But reading is allowed
        vib.read()

        # Splitting should fail if any split file already exists
        with open(disp_file, 'w') as fd:
            fd.write("hello")

        with pytest.raises(AssertionError):
            vib.split()

        os.remove(disp_file)

        # Now split() for real: replace .all.json file with displacements
        vib.split()
        assert disp_file.is_file()
        assert not comb_file.is_file()
Пример #7
0
    def test_vibration_on_surface(self, testdir):
        from ase.build import fcc111, add_adsorbate
        ag_slab = fcc111('Ag', (4, 4, 2), a=2)
        n2 = Atoms('N2',
                   positions=[[0., 0., 0.], [0., np.sqrt(2),
                                             np.sqrt(2)]])
        add_adsorbate(ag_slab, n2, height=1, position='fcc')

        # Add an interaction between the N atoms
        hessian_bottom_corner = np.zeros((2, 3, 2, 3))
        hessian_bottom_corner[-1, :, -2] = [1, 1, 1]
        hessian_bottom_corner[-2, :, -1] = [1, 1, 1]

        hessian = np.zeros((34, 3, 34, 3))
        hessian[32:, :, 32:, :] = hessian_bottom_corner

        ag_slab.calc = ForceConstantCalculator(hessian.reshape(
            (34 * 3, 34 * 3)),
                                               ref=ag_slab.copy(),
                                               f0=np.zeros((34, 3)))

        # Check that Vibrations with restricted indices returns correct Hessian
        vibs = Vibrations(ag_slab, indices=[-2, -1])
        vibs.run()
        vibs.read()

        assert_array_almost_equal(vibs.get_vibrations().get_hessian(),
                                  hessian_bottom_corner)

        # These should blow up if the vectors don't match number of atoms
        vibs.summary()
        vibs.write_jmol()

        for i in range(6):
            # Frozen atoms should have zero displacement
            assert_array_almost_equal(vibs.get_mode(i)[0], [0., 0., 0.])

            # The N atoms should have finite displacement
            assert np.all(vibs.get_mode(i)[-2:, :])
Пример #8
0
    def vibrate(self, atoms: Atoms, indices: list, read_only=False):
        '''

        This method uses ase.vibrations module, see more for info.
        User provides the FHI-aims parameters, the Atoms object and list
        of indices of atoms to be vibrated. Variables related to FHI-aims are governed by the React object.
        Calculation folders are generated automatically and a sockets calculator is used for efficiency.

        Work in progress

        Args:
            atoms: Atoms object
            indices: list
                List of indices of atoms that require vibrations
            read_only: bool
                Flag for postprocessing - if True, the method only extracts information from existing files,
                no calculations are performed

        Returns:
            Zero-Point Energy: float
        '''
        '''Retrieve common properties'''
        basis_set = self.basis_set
        hpc = self.hpc
        params = self.params
        parent_dir = os.getcwd()
        dimensions = sum(atoms.pbc)

        if not self.filename:
            '''develop a naming scheme based on chemical formula'''
            self.filename = atoms.get_chemical_formula()

        vib_dir = parent_dir + "/VibData_" + self.filename + "/Vibs"
        print(vib_dir)

        vib = Vibrations(atoms, indices=indices, name=vib_dir)
        '''If a calculation was terminated prematurely (e.g. time limit) empty .json files remain and the calculation
        of the corresponding stretch modes would be skipped on restart. The line below prevents this'''
        vib.clean(empty_files=True)
        '''Extract vibration data from existing files'''
        if read_only:
            vib.read()

        else:
            '''Calculate required vibration modes'''
            required_cache = [
                os.path.join(vib_dir, "cache." + str(x) + y + ".json")
                for x in indices
                for y in ["x+", "x-", "y+", "y-", "y-", "z+", "z-"]
            ]
            check_required_modes_files = np.array(
                [os.path.exists(file) for file in required_cache])

            if np.all(check_required_modes_files == True):
                vib.read()
            else:
                '''Set the environment variables for geometry optimisation'''
                set_aims_command(hpc=hpc,
                                 basis_set=basis_set,
                                 defaults=2020,
                                 nodes_per_instance=self.nodes_per_instance)
                '''Generate a unique folder for aims calculation'''
                counter, subdirectory_name = self._restart_setup(
                    "Vib",
                    filename=self.filename,
                    restart=False,
                    verbose=False)

                os.makedirs(subdirectory_name, exist_ok=True)
                os.chdir(subdirectory_name)
                '''Name the aims output file'''
                out = str(counter) + "_" + str(self.filename) + ".out"
                '''Calculate vibrations and write the in a separate directory'''
                with _calc_generator(params, out_fn=out,
                                     dimensions=dimensions)[0] as calculator:
                    if not self.dry_run:
                        atoms.calc = calculator
                    else:
                        atoms.calc = EMT()

                    vib = Vibrations(atoms, indices=indices, name=vib_dir)
                    vib.run()

            vib.summary()
        '''Generate a unique folder for aims calculation'''
        if not read_only:
            os.chdir(vib_dir)
            vib.write_mode()
        os.chdir(parent_dir)

        return vib.get_zero_point_energy()