Exemple #1
0
    def test_init(self):
        matrix = np.array(
            [
                [-3.0, 3.0, 4.0, -0.0, 3.0, 3.0, 1.0, 14.0, 9.0, -4.0],
                [1.0, -3.0, -3.0, 12.0, -4.0, -1.0, 5.0, 11.0, 1.0, 12.0],
                [14.0, 7.0, 13.0, 15.0, 13.0, 5.0, -5.0, 10.0, 14.0, -2.0],
                [9.0, 13.0, 4.0, 1.0, 3.0, -4.0, 7.0, 0.0, 6.0, -4.0],
                [4.0, -4.0, 6.0, 1.0, 12.0, -4.0, -2.0, 13.0, 0.0, 6.0],
                [13.0, 7.0, -4.0, 12.0, -2.0, 9.0, 8.0, -5.0, 3.0, 1.0],
                [8.0, 1.0, 10.0, -4.0, -2.0, 4.0, 13.0, 12.0, -3.0, 13.0],
                [2.0, 11.0, 8.0, 1.0, -1.0, 5.0, -3.0, 4.0, 5.0, 0.0],
                [-0.0, 14.0, 4.0, 3.0, -1.0, -5.0, 7.0, -1.0, -1.0, 3.0],
                [2.0, -2.0, 10.0, 1.0, 6.0, -5.0, -3.0, 12.0, 0.0, 13.0],
            ]
        )

        m_list = [[0.9, 4, [1, 2, 3, 4, 8], "a"], [-1, 2, [5, 6, 7], "b"]]

        e_min = EwaldMinimizer(matrix, m_list, 50)

        self.assertEqual(
            len(e_min.output_lists), 15, "Wrong number of permutations returned"
        )
        self.assertAlmostEqual(
            e_min.minimized_sum, 111.63, 3, "Returned wrong minimum value"
        )
        self.assertEqual(
            len(e_min.best_m_list), 6, "Returned wrong number of permutations"
        )
    def _fast_ordering(self, structure, num_remove_dict, num_to_return=1):
        """
        This method uses the matrix form of ewaldsum to calculate the ewald
        sums of the potential structures. This is on the order of 4 orders of
        magnitude faster when there are large numbers of permutations to
        consider. There are further optimizations possible (doing a smarter
        search of permutations for example), but this wont make a difference
        until the number of permutations is on the order of 30,000.
        """
        self.logger.debug("Performing fast ordering")
        starttime = time.time()
        self.logger.debug("Performing initial ewald sum...")

        ewaldmatrix = EwaldSummation(structure).total_energy_matrix
        self.logger.debug("Ewald sum took {} seconds.".format(time.time() -
                                                              starttime))
        starttime = time.time()
        m_list = []
        for indices, num in num_remove_dict.items():
            m_list.append([0, num, list(indices), None])

        self.logger.debug("Calling EwaldMinimizer...")
        minimizer = EwaldMinimizer(
            ewaldmatrix,
            m_list,
            num_to_return,
            PartialRemoveSitesTransformation.ALGO_FAST,
        )
        self.logger.debug(
            "Minimizing Ewald took {} seconds.".format(time.time() -
                                                       starttime))

        all_structures = []

        lowest_energy = minimizer.output_lists[0][0]
        num_atoms = sum(structure.composition.values())

        for output in minimizer.output_lists:
            s = structure.copy()
            del_indices = []

            for manipulation in output[1]:
                if manipulation[1] is None:
                    del_indices.append(manipulation[0])
                else:
                    s.replace(manipulation[0], manipulation[1])
            s.remove_sites(del_indices)
            struct = s.get_sorted_structure()
            all_structures.append({
                "energy":
                output[0],
                "energy_above_minimum":
                (output[0] - lowest_energy) / num_atoms,
                "structure":
                struct,
            })

        return all_structures
Exemple #3
0
    def test_init(self):
        matrix = np.array([[-3., 3., 4., -0., 3., 3., 1., 14., 9., -4.],
                           [1., -3., -3., 12., -4., -1., 5., 11., 1., 12.],
                           [14., 7., 13., 15., 13., 5., -5., 10., 14., -2.],
                           [9., 13., 4., 1., 3., -4., 7., 0., 6., -4.],
                           [4., -4., 6., 1., 12., -4., -2., 13., 0., 6.],
                           [13., 7., -4., 12., -2., 9., 8., -5., 3., 1.],
                           [8., 1., 10., -4., -2., 4., 13., 12., -3., 13.],
                           [2., 11., 8., 1., -1., 5., -3., 4., 5., 0.],
                           [-0., 14., 4., 3., -1., -5., 7., -1., -1., 3.],
                           [2., -2., 10., 1., 6., -5., -3., 12., 0., 13.]])

        m_list = [[.9, 4, [1, 2, 3, 4, 8], 'a'], [-1, 2, [5, 6, 7], 'b']]

        e_min = EwaldMinimizer(matrix, m_list, 50)

        self.assertEqual(len(e_min.output_lists), 15,
                         "Wrong number of permutations returned")
        self.assertAlmostEqual(e_min.minimized_sum, 111.63, 3,
                               "Returned wrong minimum value")
        self.assertEqual(len(e_min.best_m_list), 6,
                         "Returned wrong number of permutations")
Exemple #4
0
    def apply_transformation(self, structure, return_ranked_list=False):
        """
        For this transformation, the apply_transformation method will return
        only the ordered structure with the lowest Ewald energy, to be
        consistent with the method signature of the other transformations.
        However, all structures are stored in the  all_structures attribute in
        the transformation object for easy access.

        Args:
            structure: Oxidation state decorated disordered structure to order
            return_ranked_list (bool): Whether or not multiple structures are
                returned. If return_ranked_list is a number, that number of
                structures is returned.

        Returns:
            Depending on returned_ranked list, either a transformed structure
            or a list of dictionaries, where each dictionary is of the form
            {"structure" = .... , "other_arguments"}
            the key "transformation" is reserved for the transformation that
            was actually applied to the structure.
            This transformation is parsed by the alchemy classes for generating
            a more specific transformation history. Any other information will
            be stored in the transformation_parameters dictionary in the
            transmuted structure class.
        """

        try:
            num_to_return = int(return_ranked_list)
        except ValueError:
            num_to_return = 1

        num_to_return = max(1, num_to_return)

        if self.no_oxi_states:
            structure = Structure.from_sites(structure)
            for i, site in enumerate(structure):
                structure[i] = {"%s0+" % k.symbol: v for k, v in site.species.items()}

        equivalent_sites = []
        exemplars = []
        # generate list of equivalent sites to order
        # equivalency is determined by sp_and_occu and symmetry
        # if symmetrized structure is true
        for i, site in enumerate(structure):
            if site.is_ordered:
                continue
            for j, ex in enumerate(exemplars):
                sp = ex.species
                if not site.species.almost_equals(sp):
                    continue
                if self.symmetrized_structures:
                    sym_equiv = structure.find_equivalent_sites(ex)
                    sym_test = site in sym_equiv
                else:
                    sym_test = True
                if sym_test:
                    equivalent_sites[j].append(i)
                    break
            else:
                equivalent_sites.append([i])
                exemplars.append(site)

        # generate the list of manipulations and input structure
        s = Structure.from_sites(structure)

        m_list = []
        for g in equivalent_sites:
            total_occupancy = sum((structure[i].species for i in g), Composition())
            total_occupancy = dict(total_occupancy.items())
            # round total occupancy to possible values
            for k, v in total_occupancy.items():
                if abs(v - round(v)) > 0.25:
                    raise ValueError("Occupancy fractions not consistent with size of unit cell")
                total_occupancy[k] = int(round(v))
            # start with an ordered structure
            initial_sp = max(total_occupancy.keys(), key=lambda x: abs(x.oxi_state))
            for i in g:
                s[i] = initial_sp
            # determine the manipulations
            for k, v in total_occupancy.items():
                if k == initial_sp:
                    continue
                m = [
                    k.oxi_state / initial_sp.oxi_state if initial_sp.oxi_state else 0,
                    v,
                    list(g),
                    k,
                ]
                m_list.append(m)
            # determine the number of empty sites
            empty = len(g) - sum(total_occupancy.values())
            if empty > 0.5:
                m_list.append([0, empty, list(g), None])

        matrix = EwaldSummation(s).total_energy_matrix
        ewald_m = EwaldMinimizer(matrix, m_list, num_to_return, self.algo)

        self._all_structures = []

        lowest_energy = ewald_m.output_lists[0][0]
        num_atoms = sum(structure.composition.values())

        for output in ewald_m.output_lists:
            s_copy = s.copy()
            # do deletions afterwards because they screw up the indices of the
            # structure
            del_indices = []
            for manipulation in output[1]:
                if manipulation[1] is None:
                    del_indices.append(manipulation[0])
                else:
                    s_copy[manipulation[0]] = manipulation[1]
            s_copy.remove_sites(del_indices)

            if self.no_oxi_states:
                s_copy.remove_oxidation_states()

            self._all_structures.append(
                {
                    "energy": output[0],
                    "energy_above_minimum": (output[0] - lowest_energy) / num_atoms,
                    "structure": s_copy.get_sorted_structure(),
                }
            )

        if return_ranked_list:
            return self._all_structures[:num_to_return]
        return self._all_structures[0]["structure"]