Beispiel #1
0
    def create(self, system, positions, scaled_positions=False):
        """Return the local many-body tensor representation for the given
        system and positions.

        Args:
            system (:class:`ase.Atoms` | :class:`.System`): Input system.
            positions (iterable): Positions or atom index of points, from
                which local_mbtr is created. Can be a list of integer numbers
                or a list of xyz-coordinates.
            scaled_positions (boolean): Controls whether the given positions
                are given as scaled to the unit cell basis or not. Scaled
                positions require that a cell is available for the system.

        Returns:
            1D ndarray: The local many-body tensor representations of given
                positions, for k terms, as an array. These are ordered as given
                in positions.
        """
        # Transform the input system into the internal System-object
        system = self.get_system(system)

        # Ensure that the atomic number 0 is not present in the system
        if 0 in system.get_atomic_numbers():
            raise ValueError(
                "Please do not use the atomic number 0 in local MBTR "
                ", as it is reserved for the ghost atom used by the "
                "implementation.")

        # Ensuring self is updated
        self.update()

        # Checking scaled position
        if scaled_positions:
            if np.linalg.norm(system.get_cell()) == 0:
                raise ValueError(
                    "System doesn't have cell to justify scaled positions.")

        # Figure out the atom index or atom location from the given positions
        systems = []

        # If virtual positions requested, create new atoms with atomic number 0
        # at the requested position.
        for i_pos in positions:
            if self.virtual_positions:
                if not isinstance(i_pos, (list, tuple, np.ndarray)):
                    raise ValueError(
                        "The given position of type '{}' could not be "
                        "interpreted as a valid location. If you wish to use "
                        "existing atoms as centers, please set "
                        "'virtual_positions' to False.".format(type(i_pos)))
                if scaled_positions:
                    i_pos = np.dot(i_pos, system.get_cell())
                else:
                    i_pos = np.array(i_pos)

                i_pos = np.expand_dims(i_pos, axis=0)
                new_system = System('X', positions=i_pos)
                new_system += system
            else:
                if not np.issubdtype(type(i_pos), np.integer):
                    raise ValueError(
                        "The given position of type '{}' could not be "
                        "interpreted as a valid index. If you wish to use "
                        "custom locations as centers, please set "
                        "'virtual_positions' to True.".format(type(i_pos)))
                new_system = Atoms()
                center_atom = system[i_pos]
                new_system += center_atom
                new_system.set_atomic_numbers([0])
                system_copy = system.copy()
                del system_copy[i_pos]
                new_system += system_copy

            # Set the periodicity and cell to match the original system, as
            # they are lost in the system concatenation
            new_system.set_cell(system.get_cell())
            new_system.set_pbc(system.get_pbc())

            systems.append(new_system)

        # Request MBTR for each position. Return type depends on flattening and
        # whether a spares of dense result is requested.
        n_pos = len(positions)
        n_features = self.get_number_of_features()
        if self._flatten and self._sparse:
            data = []
            cols = []
            rows = []
            row_offset = 0
            for i, i_system in enumerate(systems):
                i_res = super().create(i_system)
                data.append(i_res.data)
                rows.append(i_res.row + row_offset)
                cols.append(i_res.col)

                # Increase the row offset
                row_offset += 1

            # Saves the descriptors as a sparse matrix
            data = np.concatenate(data)
            rows = np.concatenate(rows)
            cols = np.concatenate(cols)
            desc = coo_matrix((data, (rows, cols)),
                              shape=(n_pos, n_features),
                              dtype=np.float32)
        else:
            if self._flatten and not self._sparse:
                desc = np.empty((n_pos, n_features), dtype=np.float32)
            else:
                desc = np.empty((n_pos), dtype='object')
            for i, i_system in enumerate(systems):
                i_desc = super().create(i_system)
                desc[i] = i_desc

        return desc
Beispiel #2
0
    def create_single(
        self,
        system,
        positions,
    ):
        """Return the local many-body tensor representation for the given
        system and positions.

        Args:
            system (:class:`ase.Atoms` | :class:`.System`): Input system.
            positions (iterable): Positions or atom index of points, from
                which local_mbtr is created. Can be a list of integer numbers
                or a list of xyz-coordinates.

        Returns:
            1D ndarray: The local many-body tensor representations of given
            positions, for k terms, as an array. These are ordered as given in
            positions.
        """
        # Transform the input system into the internal System-object
        system = self.get_system(system)

        # Check that the system does not have elements that are not in the list
        # of atomic numbers
        atomic_number_set = set(system.get_atomic_numbers())
        self.check_atomic_numbers(atomic_number_set)

        # Ensure that the atomic number 0 is not present in the system
        if 0 in atomic_number_set:
            raise ValueError(
                "Please do not use the atomic number 0 in local MBTR"
                ", as it is reserved for the ghost atom used by the "
                "implementation.")

        # Figure out the atom index or atom location from the given positions
        systems = []

        # Positions specified, use them
        if positions is not None:

            # Check validity of position definitions and create final cartesian
            # position list
            list_positions = []
            if len(positions) == 0:
                raise ValueError(
                    "The argument 'positions' should contain a non-empty set of"
                    " atomic indices or cartesian coordinates with x, y and z "
                    "components.")
            for i in positions:
                if np.issubdtype(type(i), np.integer):
                    i_len = len(system)
                    if i >= i_len or i < 0:
                        raise ValueError(
                            "The provided index {} is not valid for the system "
                            "with {} atoms.".format(i, i_len))
                    list_positions.append(system.get_positions()[i])
                elif isinstance(i, (list, tuple, np.ndarray)):
                    if len(i) != 3:
                        raise ValueError(
                            "The argument 'positions' should contain a "
                            "non-empty set of atomic indices or cartesian "
                            "coordinates with x, y and z components.")
                    list_positions.append(i)
                else:
                    raise ValueError(
                        "Create method requires the argument 'positions', a "
                        "list of atom indices and/or positions.")

        for i_pos in positions:
            # Position designated as cartesian position, add a new atom at that
            # location with the chemical element X and place is as the first
            # atom in the system. The interaction limit makes sure that only
            # interactions of this first atom to every other atom are
            # considered.
            if isinstance(i_pos, (list, tuple, np.ndarray)):
                if len(i_pos) != 3:
                    raise ValueError(
                        "The argument 'positions' should contain a "
                        "non-empty set of atomic indices or cartesian "
                        "coordinates with x, y and z components.")
                i_pos = np.array(i_pos)
                i_pos = np.expand_dims(i_pos, axis=0)
                new_system = System('X', positions=i_pos)
                new_system += system
            # Position designated as integer, use the atom at that index as
            # center. For the calculation this central atoms is shifted to be
            # the first atom in the system, and the interaction limit makes
            # sure that only interactions of this first atom to every other
            # atom are considered.
            elif np.issubdtype(type(i_pos), np.integer):
                new_system = Atoms()
                center_atom = system[i_pos]
                new_system += center_atom
                new_system.set_atomic_numbers([0])
                system_copy = system.copy()
                del system_copy[i_pos]
                new_system += system_copy
            else:
                raise ValueError(
                    "Create method requires the argument 'positions', a "
                    "list of atom indices and/or positions.")

            # Set the periodicity and cell to match the original system, as
            # they are lost in the system concatenation
            new_system.set_cell(system.get_cell())
            new_system.set_pbc(system.get_pbc())

            systems.append(new_system)

        # Request MBTR for each position. Return type depends on flattening and
        # whether a spares of dense result is requested.
        n_pos = len(positions)
        n_features = self.get_number_of_features()
        if self._flatten and self._sparse:
            data = []
            cols = []
            rows = []
            row_offset = 0
            for i, i_system in enumerate(systems):
                i_res = super().create_single(i_system)
                data.append(i_res.data)
                rows.append(i_res.row + row_offset)
                cols.append(i_res.col)

                # Increase the row offset
                row_offset += 1

            # Saves the descriptors as a sparse matrix
            data = np.concatenate(data)
            rows = np.concatenate(rows)
            cols = np.concatenate(cols)
            desc = coo_matrix((data, (rows, cols)),
                              shape=(n_pos, n_features),
                              dtype=np.float32)
        else:
            if self._flatten and not self._sparse:
                desc = np.empty((n_pos, n_features), dtype=np.float32)
            else:
                desc = np.empty((n_pos), dtype='object')
            for i, i_system in enumerate(systems):
                i_desc = super().create_single(i_system)
                desc[i] = i_desc

        return desc