Пример #1
0
    def sortStructure(self, sort = "type", reset_idx = False, verbose = 1):
        """Function for sorting the structure

        sort = str("type"/"index"), Sort by type-z-y-x or sort by index as loaded
        from simulation file.

        reset_idx = bool, If True then reset the index of the structure

        verbose = int, Print extra information
        """

        if sort.lower() == "type":
            """Sorts the structure based on type_i then z then y then x"""
            si = np.lexsort((self.pos[:, 0], self.pos[:, 1], self.pos[:, 2], self.type_i))

        elif sort.lower() == "index":
            """Sort by the idx property, (index as written by the simulation programs)"""
            si = np.argsort(self.idx)

        if verbose > 0:
            string = "Sorting structure by %s" % sort.lower()
            ut.infoPrint(string)

        """Sort as specified"""
        self.pos = self.pos[si]
        self.type_i = self.type_i[si]
        self.type_n = self.type_n[si]
        self.frozen = self.frozen[si]
        self.mass = self.mass[si]

        if reset_idx:
            self.resetIndex(verbose = verbose)
        else:
            self.idx = self.idx[si]
Пример #2
0
    def writeStructure(self, filename = None, format = "lammps",\
                       direct = False, sd = False, verbose = 1):
        """Function for writing the structure to specified file format

        filename = str(), Filename to write to

        format = str("lammps"/"vasp"/"eon"/"xyz"), Format to write to

        direct = bool, Write in direct coordinates (VASP)

        sd = bool, consider selective dynamics (VASP) and frozen atoms EON

        verbose = int, Print extra information
        """

        if filename is None: 
            if self.filename is None:
                filename = "Structure.%s" % format
            else:
                filename = self.filename

        """Write the structure object to specified file"""
        file_io.writeData(filename = filename, atoms = self, format = format,\
                          sd = sd, direct = direct, verbose = verbose - 1)
        
        if verbose > 0:
            string = "Structure written to file: %s (%s-format)" % (filename, format)
            ut.infoPrint(string)
Пример #3
0
    def resetIndex(self, verbose = 1):
        """Function for reseting the atomic indicies

        verbose = int, Print extra information
        """

        self.idx = np.arange(self.pos.shape[0])
        if verbose > 0:
            string = "Reseting atom index"
            ut.infoPrint(string)
Пример #4
0
    def getNearestNeighborCollection(self, idx = None, idx_to = None, NN = 8,\
                                     verbose = 1, limit = np.array([5, 5, 5]),\
                                     extend = np.array([1, 1, 1], dtype = bool)):
        """Function for getting nearest neighbors around specified atoms to 
        specified atoms collected as an average with a standard deviation

        idx = int, [int,], Index from which to calculate nearest neighbors

        NN = int, Number of nearest neighbors to keep

        idx_to = int, [int,], Calculate the NN considering only these atoms

        limit = np.ndarray([float, float, float]), Limit around the maximum extent
        of the included atoms, to speed up calculations

        extend = np.ndarray([1/0, 1/0, 1/0]), Extend the cell if needed in
        specified x, y, z directions

        verbose = int, Print extra information
        """

        """Check some defaults"""
        if idx is None: idx = np.arange(self.pos.shape[0])
        if isinstance(idx, (int, np.integer)): idx = np.array([idx])
        if idx_to is None: idx_to = np.arange(self.pos.shape[0])
        if isinstance(idx_to, (int, np.integer)): idx_to = np.array([idx_to])
        
        """Change to cartesian coordinates"""
        self.dir2car()

        distance = self.getNearestNeighbors(idx = idx, idx_to = idx_to, NN = NN,\
                                            verbose = verbose, limit = limit,\
                                            extend = extend)

        """Return if less than NN neighbors could be found"""
        if distance is None: 
            return

        if verbose > 0:
            string = "Calculated %i nearest neighbors for %i atoms" % (NN, distance.shape[0])
            ut.infoPrint(string)

        dist_mean = np.mean(distance, axis = 0)
        dist_std = np.std(distance, axis = 0)

        return dist_mean, dist_std
Пример #5
0
    def getAtoms(self, mode = "layer_z", opt = [0, 0.2], coordinates = "c"):
        """get index of atoms that match supplied criteria

        Mode is supplied as a dict with keyword specified as below
        and containing a list of 

        Mode and options
        ----------------
        layer_x - [x_center, dx], slice out atoms within x_center +- dx
        layer_y - [y_center, dy], slice out atoms within y_center +- dy
        layer_z - [z_center, dz], slice out atoms within z_center +- dz
        """
        
        if "c".startswith(coordinates.lower()):
            self.dir2car()
            string = "Changed to cartesian coordinates"
            ut.infoPrint(string)
        elif "d".startswith(coordinates.lower()):
            self.car2dir()
            string = "Changed to direct coordinates"
            ut.infoPrint(string)
        else:
            string = "Unrecognized option coordinates = %s, (can be c or d)" % coordinates
            ut.infoPrint(string)
            return

        if mode.lower() == "layer_x":
            mask = (self.pos[:, 0] >= (opt[0] - opt[1])) *\
                   (self.pos[:, 0] <= (opt[0] + opt[1]))
            index = np.arange(self.pos.shape[0])[mask]
        elif mode.lower() == "layer_y":
            mask = (self.pos[:, 1] >= (opt[0] - opt[1])) *\
                   (self.pos[:, 1] <= (opt[0] + opt[1]))
            index = np.arange(self.pos.shape[0])[mask]
        elif mode.lower() == "layer_z":
            mask = (self.pos[:, 2] >= (opt[0] - opt[1])) *\
                   (self.pos[:, 2] <= (opt[0] + opt[1]))
            index = np.arange(self.pos.shape[0])[mask]

        return index
Пример #6
0
    def plotRDF(self, idx = None, idx_to = None, r = 6, dr = 0.1, bins = None,\
                extend = np.array([1, 1, 1], dtype = bool), cumulative = False, legend = None,\
                row = 1, col = 1, N = 1, handle = False, save = False, format = "pdf",\
                dpi = 100, verbose = 1, **kwargs):
        """Function for ploting the RDF and cumulative distribution

        idx = int, [int,], Index from which to calculate nearest neighbors

        idx_to = int, [int,], Calculate the NN considering only these atoms

        r = float, Cut off used in the RDF

        dr = float, bins size used in the RDF

        bins = int, Alternative to dr, specify the total number of bins

        extend = np.ndarray([1/0, 1/0, 1/0]), Allow the cell to be extended in
        the specified x, y, z directions

        edges = bool, Include both edges

        cumulative = bool, Add the cumulative RDF value to a right y-axis

        legend = [str,] legend inputs

        handle = bool, If True only prepare the axis don't draw the plot

        row = int, Rows if used in subplots

        col = int, Columns if used in subplots

        N = int, Nr of plot if used in subplots

        save = bool or str, Name to save the file to or save to default name
        if True

        format = valid matplotlib format, Format to save the plot in

        dpi = int, DPI used when saving the plot

        kwargs = valid matplotlib plot kwargs
        """
        
        lbl_1 = None
        if idx is None:
            idx = [np.arange(self.pos.shape[0])]
        elif isinstance(idx, (int, np.integer)):
            idx = [np.array([idx])]
        elif isinstance(idx[0], (int, np.integer)):
            idx = [idx]
        elif isinstance(idx, str) and idx.lower() == "species":
            idx, lbl_1 = self.getElementIdx()[:2]

        lbl_2 = None
        if idx_to is None:
            idx_to = [np.arange(self.pos.shape[0])]
        elif isinstance(idx_to, (int, np.integer)):
            idx_to = [np.array([idx_to])]
        elif isinstance(idx_to[0], (int, np.integer)):
            idx_to = [idx_to]
        elif isinstance(idx_to, str) and idx_to.lower() == "species":
            idx_to, lbl_2 = self.getElementIdx()[:2]
            

        if len(idx) == 1:
            l_idx = np.zeros(len(idx_to), dtype = np.int)
        else:
            l_idx = np.arange(len(idx), dtype = np.int)
            
        if len(idx_to) == 1:
            l_idx_to = np.zeros(len(idx), dtype = np.int)
        else:
            l_idx_to = np.arange(len(idx_to), dtype = np.int)

        if l_idx.shape[0] != l_idx_to.shape[0]:
            string = "Length of idx and idx_to does not match (%i, %i). "\
                     "Can be (1,N), (N,1) or (N,N)"\
                     % (l_idx.shape[0], l_idx_to.shape[0])
            ut.infoPrint(string)
            return
            
        y = []; yt = []
        for i in range(l_idx.shape[0]):
            cnt, bin, tot = self.getRDF(idx = idx[l_idx[i]], idx_to = idx_to[l_idx_to[i]], r = r,\
                                        dr = 0.1, bins = bins, extend = extend,\
                                        edges = False, verbose = verbose)
            
            y.append(cnt)
            yt.append(tot)

        if not handle:
            hFig = plt.figure()

        hAx = plt.subplot(row, col, N)
        if cumulative:
            hAxR = hAx.twinx()
        label = "_ignore"
        for i, item in enumerate(y):
            if legend is not None:
                if legend.lower() == "idx":
                    label = "%i -> %i" % (l_idx[i], l_idx_to[i])
                else:
                    label = legend[i]
            elif lbl_1 is not None and lbl_2 is not None:
                label = "%2s -> %2s" % (lbl_1[i], lbl_2[i])
            elif lbl_1 is not None:
                label = "%2s -> %i" % (lbl_1[i], l_idx_to[i])
            elif lbl_2 is not None:
                label = "%i -> %2s" % (l_idx[i], lbl_2[i])

            hL = hAx.plot(bin, item, linestyle = "-", label = label, **kwargs)
            if cumulative:
                hAxR.plot(bin, yt[i], linestyle = "--", color = hL[-1].get_color(), **kwargs)

        hAx.set_xlabel("Radius, $\AA$")
        hAx.set_ylabel("Atoms / (Atom * Volume), ($1/\AA^3$)")
        if label != "_ignore":
            hAx.legend(framealpha = 1, loc = "upper left")

        hAx.set_title("RDF")
        plt.tight_layout()
        if save:
            if save is True:
                ut.save_fig(filename = "RDF.%s" % (format), format = format,\
                         dpi = dpi, verbose = verbose)
            else:
                ut.save_fig(filename = save, format = format, dpi = dpi,\
                         verbose = verbose)
            plt.close()
        else:
            plt.show()
Пример #7
0
    def plotNNC(self, idx, idx_to = None, NN = 8, verbose = True,\
               handle = False, row = 1, col = 1, N = 1, save = False,\
               format = "pdf", dpi = 100, legend = None, **kwargs):
        """Function for plotting a nearest neighbor collection with std

        idx = int, [int,], Index from which to calculate nearest neighbors

        NN = int, Number of nearest neighbors to keep

        idx_to = int, [int,], Calculate the NN considering only these atoms

        handle = bool, If True only prepare the axis don't draw the plot

        row = int, Rows if used in subplots

        col = int, Columns if used in subplots

        N = int, Nr of plot if used in subplots

        save = bool or str, Name to save the file to or save to default name
        if True

        format = valid matplotlib format, Format to save the plot in

        dpi = int, DPI used when saving the plot

        legend = [str,], Legend for the different entries

        **kvargs = valid matplotlib errorbar kwargs
        """

        lbl_1 = None
        if idx is None:
            idx = [np.arange(self.pos.shape[0])]
        elif isinstance(idx, (int, np.integer)):
            idx = [np.array([idx])]
        elif isinstance(idx[0], (int, np.integer)):
            idx = [idx]
        elif isinstance(idx, str) and idx.lower() == "species":
            idx, lbl_1 = self.getElementIdx()[:2]

        lbl_2 = None
        if idx_to is None:
            idx_to = [np.arange(self.pos.shape[0])]
        elif isinstance(idx_to, (int, np.integer)):
            idx_to = [np.array([idx_to])]
        elif isinstance(idx_to[0], (int, np.integer)):
            idx_to = [idx_to]
        elif isinstance(idx_to, str) and idx_to.lower() == "species":
            idx_to, lbl_2 = self.getElementIdx()[:2]


        if len(idx) == 1:
            l_idx = np.zeros(len(idx_to), dtype = np.int)
        else:
            l_idx = np.arange(len(idx), dtype = np.int)
            
        if len(idx_to) == 1:
            l_idx_to = np.zeros(len(idx), dtype = np.int)
        else:
            l_idx_to = np.arange(len(idx_to), dtype = np.int)
        
        if l_idx.shape[0] != l_idx_to.shape[0]:
            string = "Length of idx and idx_to does not match (%i, %i). "\
                     "Can be (1,N), (N,1) or (N,N)"\
                     % (l_idx.shape[0], l_idx_to.shape[0])
            ut.infoPrint(string)
            return

        x = []; y = []; s = []
        for i in range(l_idx.shape[0]):
            d_mean, d_std = self.getNearestNeighborCollection(idx = idx[l_idx[i]],\
                                                              idx_to = idx_to[l_idx_to[i]],\
                                                              NN = NN, verbose = verbose)
            
            x.append(np.arange(1, d_mean.shape[0] + 1))
            y.append(d_mean)
            s.append(d_std)

        if not handle:
            hFig = plt.figure()

        hAx = plt.subplot(row, col, N)
        label = "_ignore"
        for i, item in enumerate(y):
            if legend is not None:
                if legend.lower() == "idx":
                    label = "%i -> %i" % (l_idx[i], l_idx_to[i])
                else:
                    label = legend[i]
            elif lbl_1 is not None and lbl_2 is not None:
                label = "%2s -> %2s" % (lbl_1[i], lbl_2[i])
            elif lbl_1 is not None:
                label = "%2s -> %i" % (lbl_1[i], l_idx_to[i])
            elif lbl_2 is not None:
                label = "%i -> %2s" % (l_idx[i], lbl_2[i])

            hAx.errorbar(x[i], y[i], yerr = s[i], linestyle = ls, marker = m, capsize = cs,\
                         elinewidth = elw, markersize = ms, linewidth = lw, label = label, **kwargs)

        hAx.set_xlabel("Neighbor")
        hAx.set_ylabel("Distance, $(\AA)$")

        if label != "_ignore":
            hAx.legend(framealpha = 1, loc = "upper left")

        hAx.set_title("Nearest Neighbor Distances")
        plt.tight_layout()
        if save:
            if save is True:
                ut.save_fig(filename = "NNC.%s" % (format), format = format,\
                            dpi = dpi, verbose = verbose)
            else:
                ut.save_fig(filename = save, format = format, dpi = dpi,\
                            verbose = verbose)
            plt.close()
        else:
            plt.show()
Пример #8
0
    def getNearestNeighbors(self, idx = None, idx_to = None, NN = 8,\
                            verbose = 1, limit = np.array([5, 5, 5]),\
                            extend = np.array([1, 1, 1], dtype = bool)):
        """Function for getting index and distance to nearest neighbors 
        of specified atoms

        idx = int, [int,], Index from which to calculate nearest neighbors

        NN = int, Number of nearest neighbors to keep

        idx_to = int, [int,], Calculate the NN considering only these atoms

        limit = np.ndarray([float, float, float]), Limit around the maximum extent
        of the included atoms, to speed up calculations

        verbose = int, Print extra information
        """

        """Check some defaults"""
        if idx is None: idx = np.arange(self.pos.shape[0])
        if isinstance(idx, (int, np.integer)): idx = np.array([idx])
        if idx_to is None: idx_to = np.arange(self.pos.shape[0])
        if isinstance(idx_to, (int, np.integer)): idx_to = np.array([idx_to])
        
        """Change to cartesian coordinates"""
        self.dir2car()

        """Do a rough check which atoms must be included in the NN search"""
        max_pos = np.max(self.pos[idx, :], axis = 0) + limit
        min_pos = np.min(self.pos[idx, :], axis = 0) - limit

        lim = np.all(self.pos < max_pos, axis = 1) *\
              np.all(self.pos > min_pos, axis = 1)
        idx_to = np.intersect1d(self.idx[lim], idx_to)

        if verbose > 0:
            string = "Considering idx_to within [%.2f, %.2f] (x), [%.2f, "\
                     " %.2f] (y), [%.2f,  %.2f] (z)" % (min_pos[0], max_pos[0],\
                     min_pos[1], max_pos[1], min_pos[2], max_pos[2])
            ut.infoPrint(string)

        """Cell extension to comply with the sepecified limit"""
        box = self.getBoxLengths()
        rep = np.ceil(limit / box) - 1
        rep = rep.astype(np.int)
        rep[np.logical_not(extend)] = 0

        if np.any(rep > 0):
            if verbose > 0:
                string = "Replicating cell by %i, %i, %i (x, y, z)"\
                         % (rep[0] + 1, rep[1] + 1, rep[2] + 1)
                ut.infoPrint(string)

            """Extend teh cell"""
            pos_to, cell = self.getExtendedPositions(x = rep[0], y = rep[1], z = rep[2],\
                                                     idx = idx_to, return_cart = True,\
                                                     verbose = verbose - 1)

            """Change to cartesian coordinates"""
            self.dir2car()

            """Change back to direct coordinates using the new extended cell"""
            pos_to = np.matmul(np.linalg.inv(cell), pos_to.T)
            pos_from = np.matmul(np.linalg.inv(cell), self.pos.T) 

        else:
            """Change to direct coordinates"""
            self.car2dir()

            if verbose > 0:
                string = "Cell is only wrapped, not extended"
                ut.infoPrint(string)

            pos_to = self.pos.copy()[idx_to, :].T
            pos_from = self.pos.copy().T
            cell = self.cell

        if pos_to.shape[1] - 1 < NN:
            string = "Within current limits (%.2f, %.2f, %.2f) fewer NN (%i) "\
                     "are present than specified (%i)" % (limit[0], limit[1],\
                     limit[2], pos_to.shape[1] - 1, NN)
            ut.infoPrint(string)
            return

        distance = np.zeros((np.shape(idx)[0], NN))

        """Measure distances between all specified atoms, wrap cell"""
        for i, item in enumerate(idx):
            d = pos_to - pos_from[:, [item]]

            d[d >  0.5] -= 1
            d[d < -0.5] += 1

            """Convert to cartesian coordinates"""
            c = np.matmul(cell, d)

            """Calculate distances"""
            dist = np.sqrt(c[0, :]**2 + c[1, :]**2 + c[2, :]**2)

            """Remove distance to the same atom"""
            mask = dist > 0
            dist = dist[mask]

            """Sort distances"""
            si = np.argsort(dist)

            """Pick out the NN nearest in all variables"""
            distance[i, :] = dist[si][:NN]

        return distance
Пример #9
0
    def getNeighborDistance(self, idx = None, r = 6, idx_to = None,\
                            extend = np.array([1, 1, 1], dtype = bool),\
                            verbose = 1):
        """Function for getting the distance between specified atoms within
        radius r

        idx = int, [int,], Index from which to calculate nearest neighbors

        r = float, Radius or NN calculation

        idx_to = int, [int,], Calculate the NN considering only these atoms

        extend = np.ndarray([1/0, 1/0, 1/0]), Extend the cell if needed in
        specified x, y, z directions

        verbose = int, Print extra information
        """

        """Check some defaults"""
        if idx is None: idx = np.arange(self.pos.shape[0])
        if isinstance(idx, (int, np.integer)): idx = np.array([idx])
        if idx_to is None: idx_to = np.arange(self.pos.shape[0])
        if isinstance(idx_to, (int, np.integer)): idx_to = np.array([idx_to])
        extend = np.array(extend, dtype = bool)
        
        """Change to cartesian coordinates"""
        self.dir2car()

        """Check the rough maximum possible extent for relevant atoms"""
        max_pos = np.max(self.pos[idx, :], axis = 0) + r
        min_pos = np.min(self.pos[idx, :], axis = 0) - r
        lim = np.all(self.pos < max_pos, axis = 1) *\
              np.all(self.pos > min_pos, axis = 1)
        idx_to = np.intersect1d(self.idx[lim], idx_to)
        if verbose > 0:
            string = "Considering idx_to within [%.2f, %.2f] (x), [%.2f, "\
                     " %.2f] (y), [%.2f,  %.2f] (z)" % (min_pos[0], max_pos[0],\
                     min_pos[1], max_pos[1], min_pos[2], max_pos[2])
            ut.infoPrint(string)

        """Cell extension to comply with the sepecified r value"""
        box = self.getBoxLengths()
        rep = np.ceil(r / box) - 1
        rep = rep.astype(np.int)
        rep[np.logical_not(extend)] = 0

        """If the box < r then extend the box. Otherwise wrap the cell"""
        if np.any(box < r):

            if verbose > 0:
                string = "Replicating cell by %i, %i, %i (x, y, z)"\
                         % (rep[0] + 1, rep[1] + 1, rep[2] + 1)
                ut.infoPrint(string)

            pos_to, cell = self.getExtendedPositions(x = rep[0], y = rep[1], z = rep[2],\
                                                  idx = idx_to, return_cart = True,\
                                                  verbose = verbose - 1)

            """Change to cartesian coordinates"""
            self.dir2car()

            """Change back to direct coordinates using the new extended cell"""
            pos_to = np.matmul(np.linalg.inv(cell), pos_to.T).T
            pos_from = np.matmul(np.linalg.inv(cell), self.pos.T).T 
        else:
            """Change to direct coordinates"""
            self.car2dir()

            if verbose > 0:
                string = "Cell is only wrapped, not extended"
                ut.infoPrint(string)

            pos_to = self.pos.copy()[idx_to, :]
            pos_from = self.pos.copy()
            cell = self.cell

        ps = pos_to.shape[0]
        dist = np.zeros((np.shape(idx)[0] * ps, 3))

        """Measure distances between all specified atoms, wrap cell"""
        for i, item in enumerate(idx):
            d = pos_to - pos_from[[item], :]
        
            d[d >  0.5] -= 1
            d[d < -0.5] += 1

            dist[i * ps : (i + 1) * ps, :] = d

        """Convert to cartesian coordinates"""
        dist = np.matmul(cell, dist.T).T

        """Calculate the distances"""
        dist = np.linalg.norm(dist, axis = 1)

        """Remove distances outside of radius r"""
        dist = dist[dist < r]

        """Remove the 0 distances, (atom to it self)"""
        dist = dist[dist > 0]

        return dist