def test_forces_CuZr_glass(self):
        """Calculate interatomic forces in CuZr glass

        Reference: tabulated forces from a calculation 
        with Lammmps (git version patch_29Mar2019-2-g585403d65)

        The forces can be re-calculated using the following
        Lammps commands:
            units metal
            atom_style atomic
            boundary p p p
            read_data CuZr_glass_460_atoms.lammps.data.gz
            pair_style eam/alloy
            pair_coeff * * ZrCu.onecolumn.eam.alloy Zr Cu
            # The initial configuration is in equilibrium
            # and the remaining forces are small
            # Swap atom types to bring system out of
            # equilibrium and create nonzero forces
            group originally_Zr type 1
            group originally_Cu type 2
            set group originally_Zr type 2
            set group originally_Cu type 1
            run 0
            write_dump all custom &
                CuZr_glass_460_atoms_forces.lammps.dump.gz &
                id type x y z fx fy fz &
                modify sort id format float "%.14g"
        """
        if "lammps-dump" in io.formats.ioformats.keys():
            atoms = io.read("CuZr_glass_460_atoms_forces.lammps.dump.gz",
                            format="lammps-dump")
        elif "lammps-dump-text" in io.formats.ioformats.keys():
            atoms = io.read("CuZr_glass_460_atoms_forces.lammps.dump.gz",
                            format="lammps-dump-text")
        else:
            raise KeyError(
                "ase.io can read neither 'lammps-dump' nor 'lammps-dump-text'")
        old_atomic_numbers = atoms.get_atomic_numbers()
        sel, = np.where(old_atomic_numbers == 1)
        new_atomic_numbers = np.zeros_like(old_atomic_numbers)
        new_atomic_numbers[sel] = 40  # Zr
        sel, = np.where(old_atomic_numbers == 2)
        new_atomic_numbers[sel] = 29  # Cu
        atoms.set_atomic_numbers(new_atomic_numbers)
        calculator = EAM('ZrCu.onecolumn.eam.alloy')
        atoms.set_calculator(calculator)
        atoms.pbc = [True, True, True]
        forces = atoms.get_forces()
        # Read tabulated forces and compare
        with gzip.open("CuZr_glass_460_atoms_forces.lammps.dump.gz") as file:
            for line in file:
                if line.startswith(b"ITEM: ATOMS "):  # ignore header
                    break
            dump = np.loadtxt(file)
        forces_dump = dump[:, 5:8]
        self.assertArrayAlmostEqual(forces,
                                    forces_dump,
                                    tol=self.force_tolerance)
    def test_hessian_monoatomic(self):
        """Calculate Hessian matrix of pure Cu

        Reference: finite difference approximation of 
        Hessian from ASE
        """
        atoms = FaceCenteredCubic('Cu', size=[4, 4, 4])
        calculator = EAM('CuAg.eam.alloy')
        self._test_hessian(atoms, calculator)
    def test_hessian_amorphous_alloy(self):
        """Calculate Hessian matrix of amorphous alloy

        Reference: finite difference approximation of 
        Hessian from ASE
        """
        atoms = io.read('CuZr_glass_460_atoms.gz')
        atoms.pbc = [True, True, True]
        calculator = EAM('ZrCu.onecolumn.eam.alloy')
        self._test_hessian(atoms, calculator)
    def test_hessian_monoatomic_with_duplicate_pairs(self):
        """Calculate Hessian matrix of pure Cu

        In a small system, the same pair (i,j) will
        appear multiple times in the neighbor list,
        with different pair distance.

        Reference: finite difference approximation of 
        Hessian from ASE
        """
        atoms = FaceCenteredCubic('Cu', size=[2, 2, 2])
        calculator = EAM('CuAg.eam.alloy')
        self._test_hessian(atoms, calculator)
    def test_hessian_crystalline_alloy(self):
        """Calculate Hessian matrix of crystalline alloy

        Reference: finite difference approximation of 
        Hessian from ASE
        """
        calculator = EAM('ZrCu.onecolumn.eam.alloy')
        lattice_size = [4, 4, 4]
        # The lattice parameters are not correct, but that should be irrelevant
        # CuZr3
        atoms = L1_2(['Cu', 'Zr'], size=lattice_size, latticeconstant=4.0)
        self._test_hessian(atoms, calculator)
        # Cu3Zr
        atoms = L1_2(['Zr', 'Cu'], size=lattice_size, latticeconstant=4.0)
        self._test_hessian(atoms, calculator)
        # CuZr
        atoms = B2(['Zr', 'Cu'], size=lattice_size, latticeconstant=3.3)
        self._test_hessian(atoms, calculator)
    def test_dynamical_matrix(self):
        """Test dynamical matrix construction

        To obtain the dynamical matrix, one could either divide by
        masses immediately when constructing the matrix, or one could
        first form the complete Hessian and then divide by masses.
        The former method is implemented.
        """
        atoms = io.read('CuZr_glass_460_atoms.gz')
        atoms.pbc = [True, True, True]
        calculator = EAM('ZrCu.onecolumn.eam.alloy')
        dynamical_matrix = calculator.calculate_hessian_matrix(
            atoms, divide_by_masses=True)
        # The second method requires a copy of Hessian, since
        # sparse matrix does not properly support *= operator
        hessian = calculator.calculate_hessian_matrix(atoms)
        masses = atoms.get_masses()
        mass_row = np.repeat(masses, np.diff(hessian.indptr))
        mass_col = masses[hessian.indices]
        inverse_mass = np.sqrt(mass_row * mass_col)**-1.0
        blocks = (inverse_mass * np.ones(
            (inverse_mass.size, 3, 3), dtype=inverse_mass.dtype).T).T
        nat = len(atoms)
        dynamical_matrix_ref = hessian.multiply(
            bsr_matrix((blocks, hessian.indices, hessian.indptr),
                       shape=(3 * nat, 3 * nat)))
        dynamical_matrix = dynamical_matrix.todense()
        dynamical_matrix_ref = dynamical_matrix_ref.todense()
        self.assertArrayAlmostEqual(dynamical_matrix,
                                    dynamical_matrix.T,
                                    tol=self.hessian_tolerance)
        self.assertArrayAlmostEqual(dynamical_matrix_ref,
                                    dynamical_matrix_ref.T,
                                    tol=self.hessian_tolerance)
        self.assertArrayAlmostEqual(dynamical_matrix,
                                    dynamical_matrix_ref,
                                    tol=self.hessian_tolerance)
 def _test_for_size(size):
     atoms = FaceCenteredCubic('Cu', size=size)
     calculator = EAM('CuAg.eam.alloy')
     self._test_hessian(atoms, calculator)