예제 #1
0
파일: test_coord.py 프로젝트: utf/pymatgen
    def test_lattice_points_in_supercell(self):
        supercell = np.array([[1, 3, 5], [-3, 2, 3], [-5, 3, 1]])
        points = coord.lattice_points_in_supercell(supercell)
        self.assertAlmostEqual(len(points), abs(np.linalg.det(supercell)))
        self.assertGreaterEqual(np.min(points), -1e-10)
        self.assertLessEqual(np.max(points), 1 - 1e-10)

        supercell = np.array([[-5, -5, -3], [0, -4, -2], [0, -5, -2]])
        points = coord.lattice_points_in_supercell(supercell)
        self.assertAlmostEqual(len(points), abs(np.linalg.det(supercell)))
        self.assertGreaterEqual(np.min(points), -1e-10)
        self.assertLessEqual(np.max(points), 1 - 1e-10)
예제 #2
0
파일: ce.py 프로젝트: qchempku2017/CMO
    def _generate_mappings(self):
        """
        Find all the supercell indices associated with each cluster
        """
        ts = lattice_points_in_supercell(self.supercell_matrix)
        self.cluster_indices = []
        self.clusters_by_sites = defaultdict(list)
        for sc in self.cluster_expansion.symmetrized_clusters:
            prim_fcoords = np.array([c.sites for c in sc.equivalent_clusters])
            fcoords = np.dot(prim_fcoords, self.prim_to_supercell)
            #tcoords contains all the coordinates of the symmetrically equivalent clusters
            #the indices are: [equivalent cluster (primitive cell), translational image, index of site in cluster, coordinate index]
            tcoords = fcoords[:, None, :, :] + ts[None, :, None, :]
            tcs = tcoords.shape
            inds = coord_list_mapping_pbc(tcoords.reshape((-1, 3)),
                                self.fcoords, atol=SITE_TOL).reshape((tcs[0] * tcs[1], tcs[2]))
            self.cluster_indices.append((sc, inds))
            #symmetrized cluster, 2d array of index groups that correspond to the cluster
            #the 2d array may have some duplicates. This is due to symetrically equivalent
            #groups being matched to the same sites (eg in simply cubic all 6 nn interactions
            #will all be [0, 0] indices. This multiplicity disappears as supercell size
            #increases, so I haven't implemented a more efficient method

            # now we store the symmetrized clusters grouped by site index in the supercell,
            # to be used by delta_corr. We also store a reduced index array, where only the
            # rows with the site index are stored. The ratio is needed because the correlations
            # are averages over the full inds array.
            for site_index in np.unique(inds):
                in_inds = np.any(inds == site_index, axis=-1)
                ratio = len(inds) / np.sum(in_inds)
                self.clusters_by_sites[site_index].append((sc.bit_combos, sc.sc_b_id, inds[in_inds], ratio))
예제 #3
0
 def sc_generator(s1, s2):
     s2_fc = np.array(s2.frac_coords)
     if fu == 1:
         cc = np.array(s1.cart_coords)
         for l, sc_m in self._get_lattices(s2.lattice, s1, fu):
             fc = l.get_fractional_coords(cc)
             fc -= np.floor(fc)
             yield fc, s2_fc, av_lat(l, s2.lattice), sc_m
     else:
         fc_init = np.array(s1.frac_coords)
         for l, sc_m in self._get_lattices(s2.lattice, s1, fu):
             fc = np.dot(fc_init, np.linalg.inv(sc_m))
             lp = lattice_points_in_supercell(sc_m)
             fc = (fc[:, None, :] + lp[None, :, :]).reshape((-1, 3))
             fc -= np.floor(fc)
             yield fc, s2_fc, av_lat(l, s2.lattice), sc_m
예제 #4
0
 def sc_generator(s1, s2):
     s2_fc = np.array(s2.frac_coords)
     if fu == 1:
         cc = np.array(s1.cart_coords)
         for l, sc_m in self._get_lattices(s2.lattice, s1, fu):
             fc = l.get_fractional_coords(cc)
             fc -= np.floor(fc)
             yield fc, s2_fc, av_lat(l, s2.lattice), sc_m
     else:
         fc_init = np.array(s1.frac_coords)
         for l, sc_m in self._get_lattices(s2.lattice, s1, fu):
             fc = np.dot(fc_init, np.linalg.inv(sc_m))
             lp = lattice_points_in_supercell(sc_m)
             fc = (fc[:, None, :] + lp[None, :, :]).reshape((-1, 3))
             fc -= np.floor(fc)
             yield fc, s2_fc, av_lat(l, s2.lattice), sc_m
예제 #5
0
def get_supercell_points(supercell_dim, points, replicate_backwards=True):
    scale_matrix = np.array(supercell_dim, np.int16)
    if scale_matrix.shape != (3, 3):
        scale_matrix = np.array(scale_matrix * np.eye(3), np.int16)

    f_lat = lattice_points_in_supercell(scale_matrix)

    # get a list of supercell images, e.g. [[1, 0, 0], [2, 0, 0]]
    images = np.dot(f_lat, scale_matrix)

    if replicate_backwards:
        images = np.unique(np.concatenate((images, -images)), axis=0)

    repeated_points = np.tile(points, (len(images), 1))
    repeated_images = np.repeat(images, len(points), axis=0)

    return repeated_images + repeated_points
예제 #6
0
    def __mul__(self, scaling_matrix):
        """
        Replicates the graph, creating a supercell,
        intelligently joining together
        edges that lie on periodic boundaries.
        In principle, any operations on the expanded
        graph could also be done on the original
        graph, but a larger graph can be easier to
        visualize and reason about.
        :param scaling_matrix: same as Structure.__mul__
        :return:
        """

        # Developer note: a different approach was also trialed, using
        # a simple Graph (instead of MultiDiGraph), with node indices
        # representing both site index and periodic image. Here, the
        # number of nodes != number of sites in the Structure. This
        # approach has many benefits, but made it more difficult to
        # keep the graph in sync with its corresponding Structure.

        # Broadly, it would be easier to multiply the Structure
        # *before* generating the StructureGraph, but this isn't
        # possible when generating the graph using critic2 from
        # charge density.

        # Multiplication works by looking for the expected position
        # of an image node, and seeing if that node exists in the
        # supercell. If it does, the edge is updated. This is more
        # computationally expensive than just keeping track of the
        # which new lattice images present, but should hopefully be
        # easier to extend to a general 3x3 scaling matrix.

        # code adapted from Structure.__mul__
        scale_matrix = np.array(scaling_matrix, np.int16)
        if scale_matrix.shape != (3, 3):
            scale_matrix = np.array(scale_matrix * np.eye(3), np.int16)
        else:
            # TODO: test __mul__ with full 3x3 scaling matrices
            raise NotImplementedError('Not tested with 3x3 scaling matrices yet.')
        new_lattice = Lattice(np.dot(scale_matrix, self.structure.lattice.matrix))

        f_lat = lattice_points_in_supercell(scale_matrix)
        c_lat = new_lattice.get_cartesian_coords(f_lat)

        new_sites = []
        new_graphs = []

        for v in c_lat:

            # create a map of nodes from original graph to its image
            mapping = {n: n + len(new_sites) for n in range(len(self.structure))}

            for idx, site in enumerate(self.structure):

                s = PeriodicSite(site.species_and_occu, site.coords + v,
                                 new_lattice, properties=site.properties,
                                 coords_are_cartesian=True, to_unit_cell=False)

                new_sites.append(s)

            new_graphs.append(nx.relabel_nodes(self.graph, mapping, copy=True))

        new_structure = Structure.from_sites(new_sites)

        # merge all graphs into one big graph
        new_g = nx.MultiDiGraph()
        for new_graph in new_graphs:
            new_g = nx.union(new_g, new_graph)

        edges_to_remove = []  # tuple of (u, v, k)
        edges_to_add = []  # tuple of (u, v, attr_dict)

        # list of new edges inside supercell
        # for duplicate checking
        edges_inside_supercell = [{u, v} for u, v, d in new_g.edges(data=True)
                                  if d['to_jimage'] == (0, 0, 0)]
        new_periodic_images = []

        orig_lattice = self.structure.lattice

        # use k-d tree to match given position to an
        # existing Site in Structure
        kd_tree = KDTree(new_structure.cart_coords)

        # tolerance in Å for sites to be considered equal
        # this could probably be a lot smaller
        tol = 0.05

        for u, v, k, d in new_g.edges(keys=True, data=True):

            to_jimage = d['to_jimage']  # for node v

            # reduce unnecessary checking
            if to_jimage != (0, 0, 0):

                # get index in original site
                n_u = u % len(self.structure)
                n_v = v % len(self.structure)

                # get fractional co-ordinates of where atoms defined
                # by edge are expected to be, relative to original
                # lattice (keeping original lattice has
                # significant benefits)
                v_image_frac = np.add(self.structure[n_v].frac_coords, to_jimage)
                u_frac = self.structure[n_u].frac_coords

                # using the position of node u as a reference,
                # get relative Cartesian co-ordinates of where
                # atoms defined by edge are expected to be
                v_image_cart = orig_lattice.get_cartesian_coords(v_image_frac)
                u_cart = orig_lattice.get_cartesian_coords(u_frac)
                v_rel = np.subtract(v_image_cart, u_cart)

                # now retrieve position of node v in
                # new supercell, and get absolute Cartesian
                # co-ordinates of where atoms defined by edge
                # are expected to be
                v_expec = new_structure[u].coords + v_rel

                # now search in new structure for these atoms
                # query returns (distance, index)
                v_present = kd_tree.query(v_expec)
                v_present = v_present[1] if v_present[0] <= tol else None

                # check if image sites now present in supercell
                # and if so, delete old edge that went through
                # periodic boundary
                if v_present is not None:

                    new_u = u
                    new_v = v_present
                    new_d = d.copy()

                    # node now inside supercell
                    new_d['to_jimage'] = (0, 0, 0)

                    edges_to_remove.append((u, v, k))

                    # make sure we don't try to add duplicate edges
                    # will remove two edges for everyone one we add
                    if {new_u, new_v} not in edges_inside_supercell:

                        # normalize direction
                        if new_v < new_u:
                            new_u, new_v = new_v, new_u

                        edges_inside_supercell.append({new_u, new_v})
                        edges_to_add.append((new_u, new_v, new_d))

                else:

                    # want to find new_v such that we have
                    # full periodic boundary conditions
                    # so that nodes on one side of supercell
                    # are connected to nodes on opposite side

                    v_expec_frac = new_structure.lattice.get_fractional_coords(v_expec)

                    # find new to_jimage
                    # use np.around to fix issues with finite precision leading to incorrect image
                    v_expec_image = np.around(v_expec_frac, decimals=3)
                    v_expec_image = v_expec_image - v_expec_image%1

                    v_expec_frac = np.subtract(v_expec_frac, v_expec_image)
                    v_expec = new_structure.lattice.get_cartesian_coords(v_expec_frac)
                    v_present = kd_tree.query(v_expec)
                    v_present = v_present[1] if v_present[0] <= tol else None

                    if v_present is not None:

                        new_u = u
                        new_v = v_present
                        new_d = d.copy()
                        new_to_jimage = tuple(map(int, v_expec_image))

                        # normalize direction
                        if new_v < new_u:
                            new_u, new_v = new_v, new_u
                            new_to_jimage = tuple(np.multiply(-1, d['to_jimage']).astype(int))

                        new_d['to_jimage'] = new_to_jimage

                        edges_to_remove.append((u, v, k))

                        if (new_u, new_v, new_to_jimage) not in new_periodic_images:
                            edges_to_add.append((new_u, new_v, new_d))
                            new_periodic_images.append((new_u, new_v, new_to_jimage))

        logger.debug("Removing {} edges, adding {} new edges.".format(len(edges_to_remove),
                                                                      len(edges_to_add)))

        # add/delete marked edges
        for edges_to_remove in edges_to_remove:
            new_g.remove_edge(*edges_to_remove)
        for (u, v, d) in edges_to_add:
            new_g.add_edge(u, v, **d)

        # return new instance of StructureGraph with supercell
        d = {"@module": self.__class__.__module__,
             "@class": self.__class__.__name__,
             "structure": new_structure.as_dict(),
             "graphs": json_graph.adjacency_data(new_g)}

        sg = StructureGraph.from_dict(d)

        return sg
예제 #7
0
    def __mul__(self, scaling_matrix):
        """
        Replicates the graph, creating a supercell,
        intelligently joining together
        edges that lie on periodic boundaries.
        In principle, any operations on the expanded
        graph could also be done on the original
        graph, but a larger graph can be easier to
        visualize and reason about.
        :param scaling_matrix: same as Structure.__mul__
        :return:
        """

        # Developer note: a different approach was also trialed, using
        # a simple Graph (instead of MultiDiGraph), with node indices
        # representing both site index and periodic image. Here, the
        # number of nodes != number of sites in the Structure. This
        # approach has many benefits, but made it more difficult to
        # keep the graph in sync with its corresponding Structure.

        # Broadly, it would be easier to multiply the Structure
        # *before* generating the StructureGraph, but this isn't
        # possible when generating the graph using critic2 from
        # charge density.

        # Multiplication works by looking for the expected position
        # of an image node, and seeing if that node exists in the
        # supercell. If it does, the edge is updated. This is more
        # computationally expensive than just keeping track of the
        # which new lattice images present, but should hopefully be
        # easier to extend to a general 3x3 scaling matrix.

        # code adapted from Structure.__mul__
        scale_matrix = np.array(scaling_matrix, np.int16)
        if scale_matrix.shape != (3, 3):
            scale_matrix = np.array(scale_matrix * np.eye(3), np.int16)
        else:
            # TODO: test __mul__ with full 3x3 scaling matrices
            raise NotImplementedError('Not tested with 3x3 scaling matrices yet.')
        new_lattice = Lattice(np.dot(scale_matrix, self.structure.lattice.matrix))

        f_lat = lattice_points_in_supercell(scale_matrix)
        c_lat = new_lattice.get_cartesian_coords(f_lat)

        new_sites = []
        new_graphs = []

        for v in c_lat:

            # create a map of nodes from original graph to its image
            mapping = {n: n + len(new_sites) for n in range(len(self.structure))}

            for idx, site in enumerate(self.structure):

                s = PeriodicSite(site.species_and_occu, site.coords + v,
                                 new_lattice, properties=site.properties,
                                 coords_are_cartesian=True, to_unit_cell=False)

                new_sites.append(s)

            new_graphs.append(nx.relabel_nodes(self.graph, mapping, copy=True))

        new_structure = Structure.from_sites(new_sites)

        # merge all graphs into one big graph
        new_g = nx.MultiDiGraph()
        for new_graph in new_graphs:
            new_g = nx.union(new_g, new_graph)

        edges_to_remove = []  # tuple of (u, v, k)
        edges_to_add = []  # tuple of (u, v, attr_dict)

        # list of new edges inside supercell
        # for duplicate checking
        edges_inside_supercell = [{u, v} for u, v, d in new_g.edges(data=True)
                                  if d['to_jimage'] == (0, 0, 0)]
        new_periodic_images = []

        orig_lattice = self.structure.lattice

        # use k-d tree to match given position to an
        # existing Site in Structure
        kd_tree = KDTree(new_structure.cart_coords)

        # tolerance in Å for sites to be considered equal
        # this could probably be a lot smaller
        tol = 0.05

        for u, v, k, d in new_g.edges(keys=True, data=True):

            to_jimage = d['to_jimage']  # for node v

            # reduce unnecessary checking
            if to_jimage != (0, 0, 0):

                # get index in original site
                n_u = u % len(self.structure)
                n_v = v % len(self.structure)

                # get fractional co-ordinates of where atoms defined
                # by edge are expected to be, relative to original
                # lattice (keeping original lattice has
                # significant benefits)
                v_image_frac = np.add(self.structure[n_v].frac_coords, to_jimage)
                u_frac = self.structure[n_u].frac_coords

                # using the position of node u as a reference,
                # get relative Cartesian co-ordinates of where
                # atoms defined by edge are expected to be
                v_image_cart = orig_lattice.get_cartesian_coords(v_image_frac)
                u_cart = orig_lattice.get_cartesian_coords(u_frac)
                v_rel = np.subtract(v_image_cart, u_cart)

                # now retrieve position of node v in
                # new supercell, and get absolute Cartesian
                # co-ordinates of where atoms defined by edge
                # are expected to be
                v_expec = new_structure[u].coords + v_rel

                # now search in new structure for these atoms
                # query returns (distance, index)
                v_present = kd_tree.query(v_expec)
                v_present = v_present[1] if v_present[0] <= tol else None

                # check if image sites now present in supercell
                # and if so, delete old edge that went through
                # periodic boundary
                if v_present is not None:

                    new_u = u
                    new_v = v_present
                    new_d = d.copy()

                    # node now inside supercell
                    new_d['to_jimage'] = (0, 0, 0)

                    edges_to_remove.append((u, v, k))

                    # make sure we don't try to add duplicate edges
                    # will remove two edges for everyone one we add
                    if {new_u, new_v} not in edges_inside_supercell:

                        # normalize direction
                        if new_v < new_u:
                            new_u, new_v = new_v, new_u

                        edges_inside_supercell.append({new_u, new_v})
                        edges_to_add.append((new_u, new_v, new_d))

                else:

                    # want to find new_v such that we have
                    # full periodic boundary conditions
                    # so that nodes on one side of supercell
                    # are connected to nodes on opposite side

                    v_expec_frac = new_structure.lattice.get_fractional_coords(v_expec)

                    # find new to_jimage
                    # use np.around to fix issues with finite precision leading to incorrect image
                    v_expec_image = np.around(v_expec_frac, decimals=3)
                    v_expec_image = v_expec_image - v_expec_image%1

                    v_expec_frac = np.subtract(v_expec_frac, v_expec_image)
                    v_expec = new_structure.lattice.get_cartesian_coords(v_expec_frac)
                    v_present = kd_tree.query(v_expec)
                    v_present = v_present[1] if v_present[0] <= tol else None

                    if v_present is not None:

                        new_u = u
                        new_v = v_present
                        new_d = d.copy()
                        new_to_jimage = tuple(map(int, v_expec_image))

                        # normalize direction
                        if new_v < new_u:
                            new_u, new_v = new_v, new_u
                            new_to_jimage = tuple(np.multiply(-1, d['to_jimage']).astype(int))

                        new_d['to_jimage'] = new_to_jimage

                        edges_to_remove.append((u, v, k))

                        if (new_u, new_v, new_to_jimage) not in new_periodic_images:
                            edges_to_add.append((new_u, new_v, new_d))
                            new_periodic_images.append((new_u, new_v, new_to_jimage))

        logger.debug("Removing {} edges, adding {} new edges.".format(len(edges_to_remove),
                                                                      len(edges_to_add)))

        # add/delete marked edges
        for edges_to_remove in edges_to_remove:
            new_g.remove_edge(*edges_to_remove)
        for (u, v, d) in edges_to_add:
            new_g.add_edge(u, v, **d)

        # return new instance of StructureGraph with supercell
        d = {"@module": self.__class__.__module__,
             "@class": self.__class__.__name__,
             "structure": new_structure.as_dict(),
             "graphs": json_graph.adjacency_data(new_g)}

        sg = StructureGraph.from_dict(d)

        return sg
예제 #8
0
def make_supercell(structure,
                   distance,
                   method='bec',
                   wrap=True,
                   standardize=True,
                   do_niggli_first=True,
                   diagonal=False,
                   implementation='fort',
                   verbosity=1):
    """
    Creates from a given structure a supercell based on the required minimal dimension
    :param structure: The pymatgen structure to create the supercell for
    :param float distance: The minimum image distance as a float,
        The cell created will not have any periodic image below this distance
    :param str method: The method to get the optimal supercell. For now, the only
        implemented option is *best enclosing cell*
    :param bool wrap: Wrap the atoms into the created cell after replication
    :param bool standardize: Standardize the created cell.
        This is done based on the rules in Hinuma etal, http://arxiv.org/abs/1506.01455
        However, only rules for the triclinic case are applied, so further
        standardization using spglib is recommended, if a truly standardized
        cell is required.
    :param bool do_niggli_first: Start with a niggli reduction of the cell,
        to enable a faster search. Disable if there are problems with the reduction
        of if the cell is already Niggli or LLL reduced.
    :param bool diagonal: Whether to return the diagonal solution, instead
        of the optimal cell.
    :param str implementation: Either fortran ('fort') or python-implementation ('pyth'),
        defaults to 'fort'
    :param int verbosity: Sets the verbosity level.

    :returns: A new pymatgen core structure instance and the used scaling matrix
    :returns: The scaling matrix used.
    """
    if not isinstance(structure, Structure):
        raise TypeError("Structure passed has to be a pymatgen structure")
    try:
        distance = float(distance)
        assert distance > 1e-12, "Non-positive number"
    except Exception as e:
        print("You have to pass positive float or integer as distance")
        raise e

    if not isinstance(wrap, bool):
        raise TypeError("wrap has to be a boolean")
    if not isinstance(standardize, bool):
        raise TypeError("standardize has to be a boolean")

    # I'm getting the niggli reduced structure as first:
    if verbosity > 1:
        print("given cell:\n", structure._lattice)
    if do_niggli_first:
        starting_structure = structure.get_reduced_structure(
            reduction_algo=u'niggli')
    else:
        starting_structure = structure

    if verbosity > 1:
        print("starting cell:\n", starting_structure._lattice)
        for i, v in enumerate(starting_structure._lattice.matrix):
            print(i, np.linalg.norm(v))

    # the lattice of the niggle reduced structure:
    lattice_cellvecs = np.array(starting_structure._lattice.matrix,
                                dtype=np.float64)
    # trial_vecs are all possible vectors sorted by the norm
    if method == 'bec':
        if diagonal:
            lattice_cellvecs = np.array(lattice_cellvecs)
            # I get the diagonal solutions
            scale_matrix, supercell_cellvecs = get_diagonal_solution_bec(
                lattice_cellvecs, distance)
        else:
            # I get all possible midpoint vectors, based on the distance,
            # which for BEC method is the diameter of the sphere
            (norms_of_sorted_Gr_r2, sorted_Gc_r2, sorted_Gr_r2, r_outer,
             v_diag) = get_possible_solutions(lattice_cellvecs,
                                              distance,
                                              verbosity=verbosity)
            if verbosity:
                print("I received {} possible solutions".format(
                    len(norms_of_sorted_Gr_r2)))
            # I pass these trial vectors into the function to find the minimum volume:
            if implementation == 'pyth':
                scale_matrix, supercell_cellvecs = get_optimal_solution_bec(
                    norms_of_sorted_Gr_r2,
                    sorted_Gc_r2,
                    sorted_Gr_r2,
                    r_outer,
                    v_diag,
                    r_inner=distance,
                    verbosity=verbosity)
            elif implementation == 'fort':
                scale_matrix, supercell_cellvecs = fort_optimal_supercell_bec(
                    norms_of_sorted_Gr_r2, sorted_Gc_r2, sorted_Gr_r2, r_outer,
                    v_diag, distance, verbosity, len(norms_of_sorted_Gr_r2))
            else:
                raise RuntimeError("Implementation {}".formt(implementation))
    elif method == 'hnf':
        if diagonal:
            lattice_cellvecs = np.array(lattice_cellvecs)
            scale_matrix, supercell_cellvecs = get_diagonal_solution_hnf(
                lattice_cellvecs, distance)
        else:
            if implementation == 'pyth':
                scale_matrix, supercell_cellvecs = get_optimal_solution_hnf(
                    lattice_cellvecs, distance, verbosity)
            elif implementation == 'fort':
                scale_matrix, supercell_cellvecs = fort_optimal_supercell_hnf(
                    lattice_cellvecs, distance, verbosity)
            else:
                raise RuntimeError("Implementation {}".formt(implementation))

        #raise NotImplementedError("HNF has not been fully implemented")
    else:
        raise ValueError("Unknown method {}".format(method))
    # Constructing the new lattice:
    new_lattice = Lattice(supercell_cellvecs)
    # I create f_lat, which are the fractional lattice points of the niggle_reduced:
    f_lat = lattice_points_in_supercell(scale_matrix)
    # and transforrm to cartesian coords here:
    c_lat = new_lattice.get_cartesian_coords(f_lat)
    #~ cellT = supercell_cellvecs.T

    if verbosity > 1:
        print("Given Scaling:\n")
        print(scale_matrix)
        print("Given lattice:\n")
        print(new_lattice)
        for i, v in enumerate(new_lattice.matrix):
            print(i, np.linalg.norm(v))

    new_sites = []
    if verbosity:
        print("Done, constructing structure")
    for site in starting_structure:
        for v in c_lat:
            new_sites.append(
                PeriodicSite(site.species_and_occu,
                             site.coords + v,
                             new_lattice,
                             properties=site.properties,
                             coords_are_cartesian=True,
                             to_unit_cell=wrap))

    supercell = Structure.from_sites(new_sites)

    if standardize:
        supercell = standardize_cell(supercell, wrap)
        if verbosity > 1:
            print("Cell after standardization:\n", new_lattice)
            for i, v in enumerate(new_lattice.matrix):
                print(i, np.linalg.norm(v))
    return supercell, scale_matrix