Instruction:
Create a Python 3 function for
analyzing the parallel efficiency.

Response:
def pefficiency(self):
        """
        Analyze the parallel efficiency.

        Return:
            :class:`ParallelEfficiency` object.
        """
        timers = self.timers()

        # Number of CPUs employed in each calculation.
        ncpus = [timer.ncpus for timer in timers]

        # Find the minimum number of cpus used and its index in timers.
        min_idx = minloc(ncpus)
        min_ncpus = ncpus[min_idx]

        # Reference timer
        ref_t = timers[min_idx]

        # Compute the parallel efficiency (total and section efficiency)
        peff = {}
        ctime_peff = [(min_ncpus * ref_t.wall_time) / (t.wall_time * ncp) for (t, ncp) in zip(timers, ncpus)]
        wtime_peff = [(min_ncpus * ref_t.cpu_time) / (t.cpu_time * ncp) for (t, ncp) in zip(timers, ncpus)]
        n = len(timers)

        peff["total"] = {}
        peff["total"]["cpu_time"] = ctime_peff
        peff["total"]["wall_time"] = wtime_peff
        peff["total"]["cpu_fract"] = n * [100]
        peff["total"]["wall_fract"] = n * [100]

        for sect_name in self.section_names():
            #print(sect_name)
            ref_sect = ref_t.get_section(sect_name)
            sects = [t.get_section(sect_name) for t in timers]
            try:
                ctime_peff = [(min_ncpus * ref_sect.cpu_time) / (s.cpu_time * ncp) for (s, ncp) in zip(sects, ncpus)]
                wtime_peff = [(min_ncpus * ref_sect.wall_time) / (s.wall_time * ncp) for (s, ncp) in zip(sects, ncpus)]
            except ZeroDivisionError:
                ctime_peff = n * [-1]
                wtime_peff = n * [-1]

            assert sect_name not in peff
            peff[sect_name] = {}
            peff[sect_name]["cpu_time"] = ctime_peff
            peff[sect_name]["wall_time"] = wtime_peff

            peff[sect_name]["cpu_fract"] = [s.cpu_fract for s in sects]
            peff[sect_name]["wall_fract"] = [s.wall_fract for s in sects]

        return ParallelEfficiency(self._filenames, min_idx, peff)


Instruction:
Explain what the following Python 3 code does
def summarize(self, **kwargs):
        import pandas as pd
        colnames = ["fname", "wall_time", "cpu_time", "mpi_nprocs", "omp_nthreads", "mpi_rank"]

        frame = pd.DataFrame(columns=colnames)
        for i, timer in enumerate(self.timers()):
            frame = frame.append({k: getattr(timer, k) for k in colnames}, ignore_index=True)
        frame["tot_ncpus"] = frame["mpi_nprocs"] * frame["omp_nthreads"]

        # Compute parallel efficiency (use the run with min number of cpus to normalize).
        i = frame["tot_ncpus"].values.argmin()
        ref_wtime = frame.ix[i]["wall_time"]
        ref_ncpus = frame.ix[i]["tot_ncpus"]
        frame["peff"] = (ref_ncpus * ref_wtime) / (frame["wall_time"] * frame["tot_ncpus"])

        return frame

Response: Return pandas DataFrame with the most important results stored in the timers.


Instruction:
Can you implement a function in Python 3 that
plots the parallel efficiency of the current object.

Response:
def plot_efficiency(self, key="wall_time", what="good+bad", nmax=5, ax=None, **kwargs):
        """
        Plot the parallel efficiency

        Args:
            key: Parallel efficiency is computed using the wall_time.
            what: Specifies what to plot: `good` for sections with good parallel efficiency.
                `bad` for sections with bad efficiency. Options can be concatenated with `+`.
            nmax: Maximum number of entries in plot
            ax: matplotlib :class:`Axes` or None if a new figure should be created.

        ================  ====================================================
        kwargs            Meaning
        ================  ====================================================
        linewidth         matplotlib linewidth. Default: 2.0
        markersize        matplotlib markersize. Default: 10
        ================  ====================================================

        Returns:
            `matplotlib` figure
        """
        ax, fig, plt = get_ax_fig_plt(ax=ax)
        lw = kwargs.pop("linewidth", 2.0)
        msize = kwargs.pop("markersize", 10)
        what = what.split("+")

        timers = self.timers()
        peff = self.pefficiency()
        n = len(timers)
        xx = np.arange(n)

        #ax.set_color_cycle(['g', 'b', 'c', 'm', 'y', 'k'])
        ax.set_prop_cycle(color=['g', 'b', 'c', 'm', 'y', 'k'])

        lines, legend_entries = [], []
        # Plot sections with good efficiency.
        if "good" in what:
            good = peff.good_sections(key=key, nmax=nmax)
            for g in good:
                #print(g, peff[g])
                yy = peff[g][key]
                line, = ax.plot(xx, yy, "-->", linewidth=lw, markersize=msize)
                lines.append(line)
                legend_entries.append(g)

        # Plot sections with bad efficiency.
        if "bad" in what:
            bad = peff.bad_sections(key=key, nmax=nmax)
            for b in bad:
                #print(b, peff[b])
                yy = peff[b][key]
                line, = ax.plot(xx, yy, "-.<", linewidth=lw, markersize=msize)
                lines.append(line)
                legend_entries.append(b)

        # Add total if not already done
        if "total" not in legend_entries:
            yy = peff["total"][key]
            total_line, = ax.plot(xx, yy, "r", linewidth=lw, markersize=msize)
            lines.append(total_line)
            legend_entries.append("total")

        ax.legend(lines, legend_entries, loc="best", shadow=True)

        #ax.set_title(title)
        ax.set_xlabel('Total_NCPUs')
        ax.set_ylabel('Efficiency')
        ax.grid(True)

        # Set xticks and labels.
        labels = ["MPI=%d, OMP=%d" % (t.mpi_nprocs, t.omp_nthreads) for t in timers]
        ax.set_xticks(xx)
        ax.set_xticklabels(labels, fontdict=None, minor=False, rotation=15)

        return fig


Instruction:
Can you implement a function in Python 3 that
plots pie charts of the different timers.

Response:
def plot_pie(self, key="wall_time", minfract=0.05, **kwargs):
        """
        Plot pie charts of the different timers.

        Args:
            key: Keyword used to extract data from timers.
            minfract: Don't show sections whose relative weight is less that minfract.

        Returns:
            `matplotlib` figure
        """
        timers = self.timers()
        n = len(timers)

        # Make square figures and axes
        import matplotlib.pyplot as plt
        from matplotlib.gridspec import GridSpec
        fig = plt.gcf()
        gspec = GridSpec(n, 1)
        for idx, timer in enumerate(timers):
            ax = plt.subplot(gspec[idx, 0])
            ax.set_title(str(timer))
            timer.pie(ax=ax, key=key, minfract=minfract, show=False)

        return fig


Instruction:
Implement a function in Python 3 to
plot the stacked histogram of the different timers.

Response:
def plot_stacked_hist(self, key="wall_time", nmax=5, ax=None, **kwargs):
        """
        Plot stacked histogram of the different timers.

        Args:
            key: Keyword used to extract data from the timers. Only the first `nmax`
                sections with largest value are show.
            mmax: Maximum nuber of sections to show. Other entries are grouped together
                in the `others` section.
            ax: matplotlib :class:`Axes` or None if a new figure should be created.

        Returns:
            `matplotlib` figure
        """
        ax, fig, plt = get_ax_fig_plt(ax=ax)

        mpi_rank = "0"
        timers = self.timers(mpi_rank=mpi_rank)
        n = len(timers)

        names, values = [], []
        rest = np.zeros(n)

        for idx, sname in enumerate(self.section_names(ordkey=key)):
            sections = self.get_sections(sname)
            svals = np.asarray([s.__dict__[key] for s in sections])
            if idx < nmax:
                names.append(sname)
                values.append(svals)
            else:
                rest += svals

        names.append("others (nmax=%d)" % nmax)
        values.append(rest)

        # The dataset is stored in values. Now create the stacked histogram.
        ind = np.arange(n) # the locations for the groups
        width = 0.35       # the width of the bars
        colors = nmax * ['r', 'g', 'b', 'c', 'k', 'y', 'm']

        bars = []
        bottom = np.zeros(n)
        for idx, vals in enumerate(values):
            color = colors[idx]
            bar = ax.bar(ind, vals, width, color=color, bottom=bottom)
            bars.append(bar)
            bottom += vals

        ax.set_ylabel(key)
        ax.set_title("Stacked histogram with the %d most important sections" % nmax)

        ticks = ind + width / 2.0
        labels = ["MPI=%d, OMP=%d" % (t.mpi_nprocs, t.omp_nthreads) for t in timers]
        ax.set_xticks(ticks)
        ax.set_xticklabels(labels, rotation=15)

        # Add legend.
        ax.legend([bar[0] for bar in bars], names, loc="best")

        return fig


Instruction:
Write a Python 3 script for
calling all plot methods provided by the parser.

Response:
def plot_all(self, show=True, **kwargs):
        """
        Call all plot methods provided by the parser.
        """
        figs = []; app = figs.append
        app(self.plot_stacked_hist(show=show))
        app(self.plot_efficiency(show=show))
        app(self.plot_pie(show=show))
        return figs


Instruction:
Can you tell what is the following Python 3 function doing
def to_csvline(self, with_header=False):
        string = ""

        if with_header:
            string += "# " + " ".join(at for at in AbinitTimerSection.FIELDS) + "\n"

        string += ", ".join(str(v) for v in self.to_tuple()) + "\n"
        return string

Response: Return a string with data in CSV format


Instruction:
Write a Python 3 function that can
write data on file fileobj using CSV format.

Response:
def to_csv(self, fileobj=sys.stdout):
        """Write data on file fileobj using CSV format."""
        openclose = is_string(fileobj)

        if openclose:
            fileobj = open(fileobj, "w")

        for idx, section in enumerate(self.sections):
            fileobj.write(section.to_csvline(with_header=(idx == 0)))
        fileobj.flush()

        if openclose:
            fileobj.close()


Instruction:
Can you generate the documentation for the following Python 3 function
def to_table(self, sort_key="wall_time", stop=None):
        table = [list(AbinitTimerSection.FIELDS), ]
        ord_sections = self.order_sections(sort_key)

        if stop is not None:
            ord_sections = ord_sections[:stop]

        for osect in ord_sections:
            row = [str(item) for item in osect.to_tuple()]
            table.append(row)

        return table

Response: Return a list of lists with timer data


Instruction:
Make a summary of the following Python 3 code
def get_dataframe(self, sort_key="wall_time", **kwargs):
        import pandas as pd
        frame = pd.DataFrame(columns=AbinitTimerSection.FIELDS)

        for osect in self.order_sections(sort_key):
            frame = frame.append(osect.to_dict(), ignore_index=True)

        # Monkey patch
        frame.info =  self.info
        frame.cpu_time =  self.cpu_time
        frame.wall_time =  self.wall_time
        frame.mpi_nprocs =  self.mpi_nprocs
        frame.omp_nthreads =  self.omp_nthreads
        frame.mpi_rank =  self.mpi_rank
        frame.fname =  self.fname

        return frame

Response: Return a pandas DataFrame with entries sorted according to sort_key.


Instruction:
Given the following Python 3 function, write the documentation
def get_values(self, keys):
        if is_string(keys):
            return [s.__dict__[keys] for s in self.sections]
        else:
            values = []
            for k in keys:
                values.append([s.__dict__[k] for s in self.sections])
            return values

Response: Return a list of values associated to a particular list of keys.


Instruction:
Can you write a function in Python 3 where it
returns the names and values of the entries whose value [ key ] is < minval or whose fraction [ key ] is < minfract.

Response:
def names_and_values(self, key, minval=None, minfract=None, sorted=True):
        """
        Select the entries whose value[key] is >= minval or whose fraction[key] is >= minfract
        Return the names of the sections and the corresponding values.
        """
        values = self.get_values(key)
        names = self.get_values("name")

        new_names, new_values = [], []
        other_val = 0.0

        if minval is not None:
            assert minfract is None

            for n, v in zip(names, values):
                if v >= minval:
                    new_names.append(n)
                    new_values.append(v)
                else:
                    other_val += v

            new_names.append("below minval " + str(minval))
            new_values.append(other_val)

        elif minfract is not None:
            assert minval is None

            total = self.sum_sections(key)

            for n, v in zip(names, values):
                if v / total >= minfract:
                    new_names.append(n)
                    new_values.append(v)
                else:
                    other_val += v

            new_names.append("below minfract " + str(minfract))
            new_values.append(other_val)

        else:
            # all values
            new_names, new_values = names, values

        if sorted:
            # Sort new_values and rearrange new_names.
            fsort = lambda t: t[1]
            nandv = [nv for nv in zip(new_names, new_values)]
            nandv.sort(key=fsort)
            new_names, new_values = [n[0] for n in nandv], [n[1] for n in nandv]

        return new_names, new_values


Instruction:
Explain what the following Python 3 code does
def order_sections(self, key, reverse=True):
        fsort = lambda s: s.__dict__[key]
        return sorted(self.sections, key=fsort, reverse=reverse)

Response: Sort sections according to the value of key.


Instruction:
Write a Python 3 function that can
plot a pie chart for this timer.

Response:
def pie(self, key="wall_time", minfract=0.05, ax=None, **kwargs):
        """
        Plot pie chart for this timer.

        Args:
            key: Keyword used to extract data from the timer.
            minfract: Don't show sections whose relative weight is less that minfract.
            ax: matplotlib :class:`Axes` or None if a new figure should be created.

        Returns:
            `matplotlib` figure
        """
        ax, fig, plt = get_ax_fig_plt(ax=ax)
        # Set aspect ratio to be equal so that pie is drawn as a circle.
        ax.axis("equal")
        # Don't show section whose value is less that minfract
        labels, vals = self.names_and_values(key, minfract=minfract)
        ax.pie(vals, explode=None, labels=labels, autopct='%1.1f%%', shadow=True)
        return fig


Instruction:
Given the following Python 3 function, write the documentation
def get_atoms(structure, **kwargs):
        if not structure.is_ordered:
            raise ValueError("ASE Atoms only supports ordered structures")
        symbols = [str(site.specie.symbol) for site in structure]
        positions = [site.coords for site in structure]
        cell = structure.lattice.matrix
        return Atoms(symbols=symbols, positions=positions, pbc=True,
                     cell=cell, **kwargs)

Response: Returns ASE Atoms object from pymatgen. core. structure. Structure object


Instruction:
How would you explain what the following Python 3 function does
def get_structure(atoms, cls=None):
        symbols = atoms.get_chemical_symbols()
        positions = atoms.get_positions()
        lattice = atoms.get_cell()

        cls = Structure if cls is None else cls
        return cls(lattice, symbols, positions,
                   coords_are_cartesian=True)

Response: Returns pymatgen. core. structure. Structure object from ASE Atoms object


Instruction:
Make a summary of the following Python 3 code
def get_smeared_densities(self, sigma):

        from scipy.ndimage.filters import gaussian_filter1d
        diff = [self.frequencies[i + 1] - self.frequencies[i]
                for i in range(len(self.frequencies) - 1)]
        avgdiff = sum(diff) / len(diff)

        smeared_dens = gaussian_filter1d(self.densities, sigma / avgdiff)
        return smeared_dens

Response: Returns the densities but with a Gaussian smearing of the standard dev sigma applied.


Instruction:
Implement a Python 3 function for
returning a dict representation of the object.

Response:
def as_dict(self):
        """
        Json-serializable dict representation of PhononDos.
        """
        return {"@module": self.__class__.__module__,
                "@class": self.__class__.__name__,
                "frequencies": list(self.frequencies),
                "densities": list(self.densities)}


Instruction:
Can you generate a brief explanation for the following Python 3 code
def ind_zero_freq(self):
        ind = np.searchsorted(self.frequencies, 0)
        if ind >= len(self.frequencies):
            raise ValueError("No positive frequencies found")
        return ind

Response: Returns the index of the first point for which the frequencies are equal or greater than zero.


Instruction:
Write a Python 3 script for
calculating the Constant volume specific heat C_v at a given temperature.

Response:
def cv(self, t, structure=None):
        """
        Constant volume specific heat C_v at temperature T obtained from the integration of the DOS.
        Only positive frequencies will be used.
        Result in J/(K*mol-c). A mol-c is the abbreviation of a mole-cell, that is, the number
        of Avogadro times the atoms in a unit cell. To compare with experimental data the result
        should be divided by the number of unit formulas in the cell. If the structure is provided
        the division is performed internally and the result is in J/(K*mol)

        Args:
            t: a temperature in K
            structure: the structure of the system. If not None it will be used to determine the numer of
                formula units
        Returns:
            Constant volume specific heat C_v
        """

        if t == 0:
            return 0

        freqs = self._positive_frequencies
        dens = self._positive_densities

        csch2 = lambda x: 1.0 / (np.sinh(x) ** 2)

        wd2kt = freqs / (2 * BOLTZ_THZ_PER_K * t)
        cv = np.trapz(wd2kt ** 2 * csch2(wd2kt) * dens, x=freqs)
        cv *= const.Boltzmann * const.Avogadro

        if structure:
            formula_units = structure.composition.num_atoms / structure.composition.reduced_composition.num_atoms
            cv /= formula_units

        return cv


Instruction:
Can you generate the documentation for the following Python 3 function
def entropy(self, t, structure=None):

        if t == 0:
            return 0

        freqs = self._positive_frequencies
        dens = self._positive_densities

        coth = lambda x: 1.0 / np.tanh(x)

        wd2kt = freqs / (2 * BOLTZ_THZ_PER_K * t)
        s = np.trapz((wd2kt * coth(wd2kt) - np.log(2 * np.sinh(wd2kt))) * dens, x=freqs)

        s *= const.Boltzmann * const.Avogadro

        if structure:
            formula_units = structure.composition.num_atoms / structure.composition.reduced_composition.num_atoms
            s /= formula_units

        return s

Response: Returns the Vibrational entropy at temperature t obtained from the integration of the DOS.


Instruction:
Create a Python 3 function for
returning the internal energy at a given temperature

Response:
def internal_energy(self, t, structure=None):
        """
        Phonon contribution to the internal energy at temperature T obtained from the integration of the DOS.
        Only positive frequencies will be used.
        Result in J/mol-c. A mol-c is the abbreviation of a mole-cell, that is, the number
        of Avogadro times the atoms in a unit cell. To compare with experimental data the result
        should be divided by the number of unit formulas in the cell. If the structure is provided
        the division is performed internally and the result is in J/mol

        Args:
            t: a temperature in K
            structure: the structure of the system. If not None it will be used to determine the numer of
                formula units
        Returns:
            Phonon contribution to the internal energy
        """

        if t==0:
            return self.zero_point_energy(structure=structure)

        freqs = self._positive_frequencies
        dens = self._positive_densities

        coth = lambda x: 1.0 / np.tanh(x)

        wd2kt = freqs / (2 * BOLTZ_THZ_PER_K * t)
        e = np.trapz(freqs * coth(wd2kt) * dens, x=freqs) / 2

        e *= THZ_TO_J * const.Avogadro

        if structure:
            formula_units = structure.composition.num_atoms / structure.composition.reduced_composition.num_atoms
            e /= formula_units

        return e


Instruction:
Write a Python 3 script to
return the Helmholtz free energy at a given temperature.

Response:
def helmholtz_free_energy(self, t, structure=None):
        """
        Phonon contribution to the Helmholtz free energy at temperature T obtained from the integration of the DOS.
        Only positive frequencies will be used.
        Result in J/mol-c. A mol-c is the abbreviation of a mole-cell, that is, the number
        of Avogadro times the atoms in a unit cell. To compare with experimental data the result
        should be divided by the number of unit formulas in the cell. If the structure is provided
        the division is performed internally and the result is in J/mol

        Args:
            t: a temperature in K
            structure: the structure of the system. If not None it will be used to determine the numer of
                formula units
        Returns:
            Phonon contribution to the Helmholtz free energy
        """

        if t==0:
            return self.zero_point_energy(structure=structure)

        freqs = self._positive_frequencies
        dens = self._positive_densities

        wd2kt = freqs / (2 * BOLTZ_THZ_PER_K * t)
        f = np.trapz(np.log(2 * np.sinh(wd2kt)) * dens, x=freqs)

        f *= const.Boltzmann * const.Avogadro * t

        if structure:
            formula_units = structure.composition.num_atoms / structure.composition.reduced_composition.num_atoms
            f /= formula_units

        return f


Instruction:
Make a summary of the following Python 3 code
def zero_point_energy(self, structure=None):

        freqs = self._positive_frequencies
        dens = self._positive_densities

        zpe = 0.5 * np.trapz(freqs * dens, x=freqs)
        zpe *= THZ_TO_J * const.Avogadro

        if structure:
            formula_units = structure.composition.num_atoms / structure.composition.reduced_composition.num_atoms
            zpe /= formula_units

        return zpe

Response: Returns the zero point energy of the system. Only positive frequencies will be used.


Instruction:
Can you tell what is the following Python 3 function doing
def get_element_dos(self):

        el_dos = {}
        for site, atom_dos in self.pdos.items():
            el = site.specie
            if el not in el_dos:
                el_dos[el] = np.array(atom_dos)
            else:
                el_dos[el] += np.array(atom_dos)
        return {el: PhononDos(self.frequencies, densities)
                for el, densities in el_dos.items()}

Response: Get element projected Dos.


Instruction:
Make a summary of the following Python 3 code
def from_dict(cls, d):
        tdos = PhononDos.from_dict(d)
        struct = Structure.from_dict(d["structure"])
        pdoss = {}
        for at, pdos in zip(struct, d["pdos"]):
            pdoss[at] = pdos

        return cls(struct, tdos, pdoss)

Response: Returns CompleteDos object from dict representation.


Instruction:
Write a Python 3 function for
sorting a dictionary by value.

Response:
def sort_dict(d, key=None, reverse=False):
    """
    Sorts a dict by value.

    Args:
        d: Input dictionary
        key: Function which takes an tuple (key, object) and returns a value to
            compare and sort by. By default, the function compares the values
            of the dict i.e. key = lambda t : t[1]
        reverse: Allows to reverse sort order.

    Returns:
        OrderedDict object whose keys are ordered according to their value.
    """
    kv_items = [kv for kv in d.items()]

    # Sort kv_items according to key.
    if key is None:
        kv_items.sort(key=lambda t: t[1], reverse=reverse)
    else:
        kv_items.sort(key=key, reverse=reverse)

    # Build ordered dict.
    return collections.OrderedDict(kv_items)


Instruction:
Given the following Python 3 function, write the documentation
def min_max_indexes(seq):
    l = sorted(enumerate(seq), key=lambda s: s[1])
    return l[0][0], l[-1][0]

Response: Uses enumerate max and min to return the indices of the maximum and minimum values in a list with the minimum and maximum value


Instruction:
How would you implement a function in Python 3 that
trues if values are stricly increasing.

Response:
def strictly_increasing(values):
    """True if values are stricly increasing."""
    return all(x < y for x, y in zip(values, values[1:]))


Instruction:
Can you write a function in Python 3 where it
trues if values are stricly decreasing.

Response:
def strictly_decreasing(values):
    """True if values are stricly decreasing."""
    return all(x > y for x, y in zip(values, values[1:]))


Instruction:
Can you implement a function in Python 3 that
trues if values are not increasing.

Response:
def non_increasing(values):
    """True if values are not increasing."""
    return all(x >= y for x, y in zip(values, values[1:]))


Instruction:
Can you generate a brief explanation for the following Python 3 code
def non_decreasing(values):
    return all(x <= y for x, y in zip(values, values[1:]))

Response: True if values are not decreasing.


Instruction:
How would you explain what the following Python 3 function does
def monotonic(values, mode="<", atol=1.e-8):
    if len(values) == 1:
        return True

    if mode == ">":
        for i in range(len(values)-1):
            v, vp = values[i], values[i+1]
            if abs(vp - v) > atol and vp <= v:
                return False

    elif mode == "<":
        for i in range(len(values)-1):
            v, vp = values[i], values[i+1]
            if abs(vp - v) > atol and vp >= v:
                return False

    else:
        raise ValueError("Wrong mode %s" % str(mode))

    return True

Response: Returns True if values are monotonic.


Instruction:
Explain what the following Python 3 code does
def round_to_sigfigs(num, sigfigs):
    if type(sigfigs) != int:
        raise TypeError("Number of significant figures must be integer.")
    elif sigfigs < 1:
        raise ValueError("Number of significant figures "
                         "must be larger than zero.")
    elif num == 0:
        return num
    else:
        prec = int(sigfigs - np.ceil(np.log10(np.absolute(num))))
        return round(num, prec)

Response: Rounds a number to a specific number of significant figures instead of to a specific number of significant figures.


Instruction:
Implement a Python 3 function for
giving a symmetric matrix in upper triangular form as flat array indexes as : [ A_xx yy A_yy zz A_xz yz A_yy zz A_xy xz yz z z

Response:
def make_symmetric_matrix_from_upper_tri(val):
    """
    Given a symmetric matrix in upper triangular matrix form as flat array indexes as:
    [A_xx,A_yy,A_zz,A_xy,A_xz,A_yz]
    This will generate the full matrix:
    [[A_xx,A_xy,A_xz],[A_xy,A_yy,A_yz],[A_xz,A_yz,A_zz]
    """
    idx = [0,3,4,1,5,2]
    val = np.array(val)[idx]
    mask = ~np.tri(3,k=-1,dtype=bool)
    out = np.zeros((3,3),dtype=val.dtype)
    out[mask] = val
    out.T[mask] = val
    return out


Instruction:
Can you generate a brief explanation for the following Python 3 code
def g0w0_extended_work(structure, pseudos, kppa, nscf_nband, ecuteps, ecutsigx, scf_nband, accuracy="normal",
                       spin_mode="polarized", smearing="fermi_dirac:0.1 eV", response_models=["godby"], charge=0.0,
                       inclvkb=2, scr_nband=None, sigma_nband=None, workdir=None, manager=None, gamma=True, nksmall=20,
                       work_class=None, **extra_abivars):
    """
    Returns a :class:`Work` object that performs G0W0 calculations for the given the material.

    Args:
        structure: Pymatgen structure.
        pseudos: List of `Pseudo` objects.
        scf_ Defines the sampling used for the SCF run.
        nscf_nband: Number of bands included in the NSCF run.
        ecuteps: Cutoff energy [Ha] for the screening matrix.
        ecutsigx: Cutoff energy [Ha] for the exchange part of the self-energy.
        accuracy: Accuracy of the calculation.
        spin_mode: Spin polarization.
        smearing: Smearing technique.
        ppmodel: Plasmonpole technique.
        charge: Electronic charge added to the unit cell.
        scf_algorithm: Algorithm used for solving of the SCF cycle.
        inclvkb: Treatment of the dipole matrix elements (see abinit variable).
        scr_nband: Number of bands used to compute the screening (default is nscf_nband)
        sigma_nband: Number of bands used to compute the self-energy (default is nscf_nband)
        workdir: Working directory.
        manager: :class:`TaskManager` instance.
        nksamll: if not None, a DFT bandstucture calculation will be added after the sc run
        extra_abivars: Dictionary with extra variables passed to ABINIT.
    """
    # TODO: Cannot use istwfk != 1.

    # all these too many options are for development only the current idea for the final version is
    #if gamma:
    #    scf_ksampling = KSampling.automatic_density(structure=structure, kppa=10000, chksymbreak=0, shifts=(0, 0, 0))
    #    nscf_ksampling = KSampling.gamma_centered(kpts=(2, 2, 2))
    #    if kppa <= 13:
    #        nscf_ksampling = KSampling.gamma_centered(kpts=(scf_kppa, scf_kppa, scf_kppa))
    #    else:
    #        nscf_ksampling = KSampling.automatic_density(structure, scf_kppa, chksymbreak=0, shifts=(0, 0, 0))
    #else:
    #    scf_ksampling = KSampling.automatic_density(structure, scf_kppa, chksymbreak=0)
    #    nscf_ksampling = KSampling.automatic_density(structure, scf_kppa, chksymbreak=0)

    if gamma:
        if kppa == 1:
            scf_ksampling = KSampling.gamma_centered(kpts=(1, 1, 1))
            nscf_ksampling = KSampling.gamma_centered(kpts=(1, 1, 1))
        elif kppa == 2:
            scf_ksampling = KSampling.gamma_centered(kpts=(2, 2, 2))
            nscf_ksampling = KSampling.gamma_centered(kpts=(2, 2, 2))
        elif kppa < 0:
            scf_ksampling = KSampling.gamma_centered(kpts=(-kppa, -kppa, -kppa))
            nscf_ksampling = KSampling.gamma_centered(kpts=(2, 2, 2))
        elif kppa <= 13:
            scf_ksampling = KSampling.gamma_centered(kpts=(kppa, kppa, kppa))
            nscf_ksampling = KSampling.gamma_centered(kpts=(kppa, kppa, kppa))
        else:
            scf_ksampling = KSampling.automatic_density(structure, kppa, chksymbreak=0, shifts=(0, 0, 0))
            nscf_ksampling = KSampling.automatic_density(structure, kppa, chksymbreak=0, shifts=(0, 0, 0))
    else:
        #this is the original behaviour before the devellopment of the gwwrapper
        scf_ksampling = KSampling.automatic_density(structure, kppa, chksymbreak=0)
        nscf_ksampling = KSampling.automatic_density(structure, kppa, chksymbreak=0)

    print(scf_ksampling)
    print(nscf_ksampling)

    if "istwfk" not in extra_abivars:
        extra_abivars["istwfk"] = "*1"

    scf_inputs = []
    to_add = {}
    #scf_nband = min(nscf_nband)
    #print(scf_nband)
    extra_abivars.update(to_add)

    for k in extra_abivars.keys():
        if k[-2:] == '_s':
            var = k[:len(k)-2]
            values = extra_abivars.pop(k)
            to_add.update({k: values[-1]})
            for value in values:
                extra_abivars[var] = value
                extra_abivars['pawecutdg'] = extra_abivars['ecut']*2
                scf_inputs.append(ScfStrategy(structure, pseudos, scf_ksampling, accuracy=accuracy,
                                                spin_mode=spin_mode, smearing=smearing, charge=charge,
                                                scf_algorithm=None, nband=scf_nband, **extra_abivars))

    #temporary for testing a new approach ...
    spread_scr = False if os.path.isfile('no_spread_scr') else True

    if len(scf_strategy) == 0:
        scf_strategy.append(ScfStrategy(structure, pseudos, scf_ksampling, accuracy=accuracy, spin_mode=spin_mode,
                                        smearing=smearing, charge=charge, scf_algorithm=None, nband=scf_nband,
                                        **extra_abivars))

    nscf_strategy = NscfStrategy(scf_strategy[-1], nscf_ksampling, int(max(nscf_nband)*1.1)+1,
                                 nbdbuf=int(0.1*max(nscf_nband)), nstep=200, **extra_abivars)

    if scr_nband is None:
        scr_nband = nscf_nband
    if sigma_nband is None:
        sigma_nband = nscf_nband

    if ecutsigx < max(ecuteps):
        ecutsigx = max(ecuteps)

    sigma_strategy = []

    if 'cd' in response_models:
        hilbert = HilbertTransform(nomegasf=100, domegasf=None, spmeth=1, nfreqre=None, freqremax=None, nfreqim=None,
                                   freqremin=None)

    for response_model in response_models:
        for ecuteps_v in ecuteps:
            for nscf_nband_v in nscf_nband:
                scr_nband = nscf_nband_v
                sigma_nband = nscf_nband_v
                if response_model == 'cd':
                    screening = Screening(ecuteps_v, scr_nband, w_type="RPA", sc_mode="one_shot", hilbert=hilbert,
                                          ecutwfn=None, inclvkb=inclvkb)
                    self_energy = SelfEnergy("gw", "one_shot", sigma_nband, ecutsigx, screening, hilbert=hilbert)
                else:
                    ppmodel = response_model
                    screening = Screening(ecuteps_v, scr_nband, w_type="RPA", sc_mode="one_shot", ecutwfn=None,
                                          inclvkb=inclvkb)
                    self_energy = SelfEnergy("gw", "one_shot", sigma_nband, ecutsigx, screening, ppmodel=ppmodel,
                                             gw_qprange=1)
                scr_strategy = ScreeningStrategy(scf_strategy[-1], nscf_strategy, screening, **extra_abivars)
                sigma_strategy.append(SelfEnergyStrategy(scf_strategy[-1], nscf_strategy, scr_strategy, self_energy,
                                                         **extra_abivars))

    if work_class is None: work_class = G0W0Work
    print(work_class)

    return work_class(scf_strategy, nscf_strategy, scr_strategy, sigma_strategy, workdir=workdir, manager=manager,
                      spread_scr=spread_scr, nksmall=nksmall)

Response: Returns a new extended work object that performs G0W0 calculations for the given structure.


Instruction:
Write a Python 3 script for
returning a list of dictionaries describing the components of a structure in a bonded structure.

Response:
def get_structure_components(bonded_structure, inc_orientation=False,
                             inc_site_ids=False, inc_molecule_graph=False):
    """
    Gets information on the components in a bonded structure.

    Correctly determines the dimensionality of all structures, regardless of
    structure type or improper connections due to periodic boundary conditions.

    Requires a StructureGraph object as input. This can be generated using one
    of the NearNeighbor classes. For example, using the CrystalNN class::

        bonded_structure = CrystalNN().get_bonded_structure(structure)

    Based on the modified breadth-first-search algorithm described in:

    P. Larsem, M. Pandey, M. Strange, K. W. Jacobsen, 2018, arXiv:1808.02114

    Args:
        bonded_structure (StructureGraph): A structure with bonds, represented
            as a pymatgen structure graph. For example, generated using the
            CrystalNN.get_bonded_structure() method.
        inc_orientation (bool, optional): Whether to include the orientation
            of the structure component. For surfaces, the miller index is given,
            for one-dimensional structures, the direction of the chain is given.
        inc_site_ids (bool, optional): Whether to include the site indices
            of the sites in the structure component.
        inc_molecule_graph (bool, optional): Whether to include MoleculeGraph
            objects for zero-dimensional components.

    Returns:
        (list of dict): Information on the components in a structure as a list
        of dictionaries with the keys:

        - "structure_graph": A pymatgen StructureGraph object for the
            component.
        - "dimensionality": The dimensionality of the structure component as an
            int.
        - "orientation": If inc_orientation is `True`, the orientation of the
            component as a tuple. E.g. (1, 1, 1)
        - "site_ids": If inc_site_ids is `True`, the site indices of the
            sites in the component as a tuple.
        - "molecule_graph": If inc_molecule_graph is `True`, the site a
            MoleculeGraph object for zero-dimensional components.
    """
    import networkx as nx  # optional dependency therefore not top level import

    comp_graphs = (bonded_structure.graph.subgraph(c) for c in
                   nx.weakly_connected_components(bonded_structure.graph))

    components = []
    for graph in comp_graphs:
        dimensionality, vertices = calculate_dimensionality_of_site(
            bonded_structure, list(graph.nodes())[0], inc_vertices=True)

        component = {'dimensionality': dimensionality}

        if inc_orientation:
            if dimensionality in [1, 2]:
                vertices = np.array(vertices)

                g = vertices.sum(axis=0) / vertices.shape[0]

                # run singular value decomposition
                _, _, vh = np.linalg.svd(vertices - g)

                # get direction (first column is best fit line,
                # 3rd column is unitary norm)
                index = 2 if dimensionality == 2 else 0
                orientation = get_integer_index(vh[index, :])
            else:
                orientation = None

            component['orientation'] = orientation

        if inc_site_ids:
            component['site_ids'] = tuple(graph.nodes())

        if inc_molecule_graph and dimensionality == 0:
            component['molecule_graph'] = zero_d_graph_to_molecule_graph(
                bonded_structure, graph)

        component_structure = Structure.from_sites(
            [bonded_structure.structure[n] for n in sorted(graph.nodes())])

        sorted_graph = nx.convert_node_labels_to_integers(
            graph, ordering="sorted")
        component_graph = StructureGraph(
            component_structure,
            graph_data=json_graph.adjacency_data(sorted_graph))
        component['structure_graph'] = component_graph

        components.append(component)
    return components


Instruction:
Here you have a function in Python 3, explain what it does
def calculate_dimensionality_of_site(bonded_structure, site_index,
                                     inc_vertices=False):
    """
    Calculates the dimensionality of the component containing the given site.

    Implements directly the modified breadth-first-search algorithm described in
    Algorithm 1 of:

    P. Larsem, M. Pandey, M. Strange, K. W. Jacobsen, 2018, arXiv:1808.02114

    Args:
        bonded_structure (StructureGraph): A structure with bonds, represented
            as a pymatgen structure graph. For example, generated using the
            CrystalNN.get_bonded_structure() method.
        site_index (int): The index of a site in the component of interest.
        inc_vertices (bool, optional): Whether to return the vertices (site
            images) of the component.

    Returns:
        (int or tuple): If inc_vertices is False, the dimensionality of the
        component will be returned as an int. If inc_vertices is true, the
        function will return a tuple of (dimensionality, vertices), where
        vertices is a list of tuples. E.g. [(0, 0, 0), (1, 1, 1)].
    """
    def neighbours(comp_index):
        return [(s.index, s.jimage) for s
                in bonded_structure.get_connected_sites(comp_index)]

    def rank(vertices):
        if len(vertices) == 0:
            return -1
        elif len(vertices) == 1:
            return 0
        else:
            vertices = np.array(list(vertices))
            return np.linalg.matrix_rank(vertices[1:] - vertices[0])

    connected_sites = {i: neighbours(i) for i in
                       range(bonded_structure.structure.num_sites)}

    seen_vertices = set()
    seen_comp_vertices = defaultdict(set)

    queue = [(site_index, (0, 0, 0))]
    while len(queue) > 0:
        comp_i, image_i = queue.pop(0)

        if (comp_i, image_i) in seen_vertices:
            continue
        seen_vertices.add((comp_i, image_i))

        if (rank(seen_comp_vertices[comp_i].union({image_i})) >
                rank(seen_comp_vertices[comp_i])):
            seen_comp_vertices[comp_i].add(image_i)

        for comp_j, image_j in connected_sites[comp_i]:

            image_j = tuple(np.add(image_j, image_i))

            if (comp_j, image_j) in seen_vertices:
                continue

            if (rank(seen_comp_vertices[comp_j].union({image_j})) >
                    rank(seen_comp_vertices[comp_j])):
                queue.append((comp_j, image_j))

    if inc_vertices:
        return (rank(seen_comp_vertices[site_index]),
                list(seen_comp_vertices[site_index]))
    else:
        return rank(seen_comp_vertices[site_index])

Response: Calculates the dimensionality of the component containing the given site.


Instruction:
Can you generate the documentation for the following Python 3 function
def zero_d_graph_to_molecule_graph(bonded_structure, graph):
    import networkx as nx
    seen_indices = []
    sites = []

    start_index = list(graph.nodes())[0]
    queue = [(start_index, (0, 0, 0),
              bonded_structure.structure[start_index])]
    while len(queue) > 0:
        comp_i, image_i, site_i = queue.pop(0)

        if comp_i in [x[0] for x in seen_indices]:
            raise ValueError("Graph component is not 0D")

        seen_indices.append((comp_i, image_i))
        sites.append(site_i)

        for site_j in bonded_structure.get_connected_sites(
                comp_i, jimage=image_i):

            if ((site_j.index, site_j.jimage) not in seen_indices and
                    (site_j.index, site_j.jimage, site_j.site) not in queue):
                queue.append((site_j.index, site_j.jimage, site_j.site))

    # sort the list of indices and the graph by index to make consistent
    indices_ordering = np.argsort([x[0] for x in seen_indices])
    sorted_sites = np.array(sites, dtype=object)[indices_ordering]
    sorted_graph = nx.convert_node_labels_to_integers(graph, ordering="sorted")
    mol = Molecule([s.specie for s in sorted_sites],
                   [s.coords for s in sorted_sites])
    mol_graph = MoleculeGraph.with_edges(mol, nx.Graph(sorted_graph).edges())

    return mol_graph

Response: Converts a zero - dimensional networkx Graph object into a MoleculeGraph object.


Instruction:
Write a Python 3 function for
finding bonded atoms and returns a adjacency matrix of bonded atoms.

Response:
def find_connected_atoms(struct, tolerance=0.45, ldict=JmolNN().el_radius):
    """
    Finds bonded atoms and returns a adjacency matrix of bonded atoms.

    Author: "Gowoon Cheon"
    Email: "gcheon@stanford.edu"

    Args:
        struct (Structure): Input structure
        tolerance: length in angstroms used in finding bonded atoms. Two atoms
            are considered bonded if (radius of atom 1) + (radius of atom 2) +
            (tolerance) < (distance between atoms 1 and 2). Default
            value = 0.45, the value used by JMol and Cheon et al.
        ldict: dictionary of bond lengths used in finding bonded atoms. Values
            from JMol are used as default

    Returns:
        (np.ndarray): A numpy array of shape (number of atoms, number of atoms);
        If any image of atom j is bonded to atom i with periodic boundary
        conditions, the matrix element [atom i, atom j] is 1.
    """
    n_atoms = len(struct.species)
    fc = np.array(struct.frac_coords)
    fc_copy = np.repeat(fc[:, :, np.newaxis], 27, axis=2)
    neighbors = np.array(list(itertools.product([0, 1, -1], [0, 1, -1], [0, 1, -1]))).T
    neighbors = np.repeat(neighbors[np.newaxis, :, :], 1, axis=0)
    fc_diff = fc_copy - neighbors
    species = list(map(str, struct.species))
    # in case of charged species
    for i, item in enumerate(species):
        if not item in ldict.keys():
            species[i] = str(Specie.from_string(item).element)
    latmat = struct.lattice.matrix
    connected_matrix = np.zeros((n_atoms,n_atoms))

    for i in range(n_atoms):
        for j in range(i + 1, n_atoms):
            max_bond_length = ldict[species[i]] + ldict[species[j]] + tolerance
            frac_diff = fc_diff[j] - fc_copy[i]
            distance_ij = np.dot(latmat.T, frac_diff)
            # print(np.linalg.norm(distance_ij,axis=0))
            if sum(np.linalg.norm(distance_ij, axis=0) < max_bond_length) > 0:
                connected_matrix[i, j] = 1
                connected_matrix[j, i] = 1
    return connected_matrix


Instruction:
Explain what the following Python 3 code does
def find_clusters(struct, connected_matrix):
    n_atoms = len(struct.species)
    if n_atoms == 0:
        return [0, 0, 0]
    if 0 in np.sum(connected_matrix, axis=0):
        return [0, 1, 0]

    cluster_sizes = []
    clusters = []
    visited = [False for item in range(n_atoms)]
    connected_matrix += np.eye(len(connected_matrix))

    def visit(atom, atom_cluster):
        visited[atom] = True
        new_cluster = set(np.where(connected_matrix[atom] != 0)[0]).union(atom_cluster)
        atom_cluster = new_cluster
        for new_atom in atom_cluster:
            if not visited[new_atom]:
                visited[new_atom] = True
                atom_cluster = visit(new_atom, atom_cluster)
        return atom_cluster

    for i in range(n_atoms):
        if not visited[i]:
            atom_cluster = set()
            cluster=visit(i, atom_cluster)
            clusters.append(cluster)
            cluster_sizes.append(len(cluster))

    max_cluster = max(cluster_sizes)
    min_cluster = min(cluster_sizes)
    return [max_cluster, min_cluster, clusters]

Response: This function finds bonded clusters of atoms in the structure with periodic boundary conditions.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def deviator_stress(self):
        if not self.is_symmetric:
            raise warnings.warn("The stress tensor is not symmetric, "
                                "so deviator stress will not be either")
        return self - self.mean_stress*np.eye(3)

Response: returns the deviator component of the stress tensor


Instruction:
How would you code a function in Python 3 to
calculate the first Piola - Kirchoff stress for a single object.

Response:
def piola_kirchoff_1(self, def_grad):
        """
        calculates the first Piola-Kirchoff stress

        Args:
            def_grad (3x3 array-like): deformation gradient tensor
        """
        if not self.is_symmetric:
            raise ValueError("The stress tensor is not symmetric, \
                             PK stress is based on a symmetric stress tensor.")
        def_grad = SquareTensor(def_grad)
        return def_grad.det*np.dot(self, def_grad.inv.trans)


Instruction:
Can you generate the documentation for the following Python 3 function
def time2slurm(timeval, unit="s"):
    d, h, m, s = 24*3600, 3600, 60, 1

    timeval = Time(timeval, unit).to("s")
    days, hours = divmod(timeval, d)
    hours, minutes = divmod(hours, h)
    minutes, secs = divmod(minutes, m)

    return "%d-%d:%d:%d" % (days, hours, minutes, secs)

Response: Convert a time value in the given unit to a string following the slurm convention.


Instruction:
Write a Python 3 script to
convert a time value in the given unit to a string following the PbsPro convention.

Response:
def time2pbspro(timeval, unit="s"):
    """
    Convert a number representing a time value in the given unit (Default: seconds)
    to a string following the PbsPro convention: "hours:minutes:seconds".

    >>> assert time2pbspro(2, unit="d") == '48:0:0'
    """
    h, m, s = 3600, 60, 1

    timeval = Time(timeval, unit).to("s")
    hours, minutes = divmod(timeval, h)
    minutes, secs = divmod(minutes, m)

    return "%d:%d:%d" % (hours, minutes, secs)


Instruction:
How would you explain what the following Python 3 function does
def any2mb(s):
    if is_string(s):
        return int(Memory.from_string(s).to("Mb"))
    else:
        return int(s)

Response: Convert string or number to memory in megabytes.


Instruction:
Make a summary of the following Python 3 code
def max_cation_removal(self):

        # how much 'spare charge' is left in the redox metals for oxidation?
        oxid_pot = sum(
            [(Element(spec.symbol).max_oxidation_state - spec.oxi_state) * self.comp[spec] for spec
             in self.comp if is_redox_active_intercalation(Element(spec.symbol))])

        oxid_limit = oxid_pot / self.cation_charge

        #the number of A that exist in the structure for removal
        num_cation = self.comp[Specie(self.cation.symbol, self.cation_charge)]

        return min(oxid_limit, num_cation)

Response: Maximum number of cation A that can be removed while maintaining charge - balance.


Instruction:
Can you generate the documentation for the following Python 3 function
def _get_max_cap_ah(self, remove, insert):
        num_cations = 0
        if remove:
            num_cations += self.max_cation_removal
        if insert:
            num_cations += self.max_cation_insertion

        return num_cations * self.cation_charge * ELECTRON_TO_AMPERE_HOURS

Response: Get the maximum capacity in mAh for inserting and removing charged cationEntries.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def get_max_capgrav(self, remove=True, insert=True):
        weight = self.comp.weight
        if insert:
            weight += self.max_cation_insertion * self.cation.atomic_mass
        return self._get_max_cap_ah(remove, insert) / (weight / 1000)

Response: Returns the maximum grav in mAh for inserting and removing a charged cation Arc Arc.


Instruction:
Write a Python 3 function for
getting the max capacity in mAh for inserting and removing a charged cation into base structure.

Response:
def get_max_capvol(self, remove=True, insert=True, volume=None):
        """
        Give max capacity in mAh/cc for inserting and removing a charged cation into base structure.

        Args:
            remove: (bool) whether to allow cation removal
            insert: (bool) whether to allow cation insertion
            volume: (float) volume to use for normalization (default=volume of initial structure)

        Returns:
            max vol capacity in mAh/cc
        """

        vol = volume if volume else self.struc_oxid.volume
        return self._get_max_cap_ah(remove, insert) * 1000 * 1E24 / (vol * const.N_A)


Instruction:
Can you generate a brief explanation for the following Python 3 code
def get_removals_int_oxid(self):

        # the elements that can possibly be oxidized
        oxid_els = [Element(spec.symbol) for spec in self.comp if
                    is_redox_active_intercalation(spec)]

        numa = set()
        for oxid_el in oxid_els:
            numa = numa.union(self._get_int_removals_helper(self.comp.copy(), oxid_el, oxid_els, numa))

        # convert from num A in structure to num A removed
        num_cation = self.comp[Specie(self.cation.symbol, self.cation_charge)]
        return set([num_cation - a for a in numa])

Response: Returns a set of integer cation removals in the order of the redox metals.


Instruction:
Can you implement a function in Python 3 that
calculates the energy of a composition.

Response:
def get_energy(self, composition, strict=True):
        """
        Calculates the energy of a composition.

        Args:
            composition (Composition): input composition
            strict (bool): Whether all potentials must be specified
        """
        if strict and set(composition.keys()) > set(self.keys()):
            s = set(composition.keys()) - set(self.keys())
            raise ValueError("Potentials not specified for {}".format(s))
        return sum(self.get(k, 0) * v for k, v in composition.items())


Instruction:
Make a summary of the following Python 3 code
def with_edges(structure, edges):

        sg = StructureGraph.with_empty_graph(structure, name="bonds",
                                             edge_weight_name="weight",
                                             edge_weight_units="")

        for edge, props in edges.items():

            try:
                from_index = edge[0]
                to_index = edge[1]
                from_image = edge[2]
                to_image = edge[3]
            except TypeError:
                raise ValueError("Edges must be given as (from_index, to_index,"
                                 " from_image, to_image) tuples")

            if props is not None:
                if "weight" in props.keys():
                    weight = props["weight"]
                    del props["weight"]
                else:
                    weight = None

                if len(props.items()) == 0:
                    props = None
            else:
                weight = None

            nodes = sg.graph.nodes
            if not (from_index in nodes and to_index in nodes):
                raise ValueError("Edges cannot be added if nodes are not"
                                 " present in the graph. Please check your"
                                 " indices.")

            sg.add_edge(from_index, to_index, from_jimage=from_image,
                        to_jimage=to_image, weight=weight,
                        edge_properties=props)

        sg.set_node_attributes()
        return sg

Response: Returns a StructureGraph with the given edges added to the functional object.


Instruction:
Can you implement a function in Python 3 that
returns a StructureGraph with local_env edges added to the structure.

Response:
def with_local_env_strategy(structure, strategy):
        """
        Constructor for StructureGraph, using a strategy
        from :Class: `pymatgen.analysis.local_env`.

        :param structure: Structure object
        :param strategy: an instance of a
            :Class: `pymatgen.analysis.local_env.NearNeighbors` object
        :return:
        """

        sg = StructureGraph.with_empty_graph(structure, name="bonds",
                                             edge_weight_name="weight",
                                             edge_weight_units="")

        for n, neighbors in enumerate(strategy.get_all_nn_info(structure)):
            for neighbor in neighbors:

                # local_env will always try to add two edges
                # for any one bond, one from site u to site v
                # and another form site v to site u: this is
                # harmless, so warn_duplicates=False
                sg.add_edge(from_index=n,
                            from_jimage=(0, 0, 0),
                            to_index=neighbor['site_index'],
                            to_jimage=neighbor['image'],
                            weight=neighbor['weight'],
                            warn_duplicates=False)

        return sg


Instruction:
Write a Python 3 script to
add an edge from one site to another.

Response:
def add_edge(self, from_index, to_index,
                 from_jimage=(0, 0, 0), to_jimage=None,
                 weight=None, warn_duplicates=True,
                 edge_properties=None):
        """
        Add edge to graph.

        Since physically a 'bond' (or other connection
        between sites) doesn't have a direction, from_index,
        from_jimage can be swapped with to_index, to_jimage.

        However, images will always always be shifted so that
        from_index < to_index and from_jimage becomes (0, 0, 0).

        :param from_index: index of site connecting from
        :param to_index: index of site connecting to
        :param from_jimage (tuple of ints): lattice vector of periodic
        image, e.g. (1, 0, 0) for periodic image in +x direction
        :param to_jimage (tuple of ints): lattice vector of image
        :param weight (float): e.g. bond length
        :param warn_duplicates (bool): if True, will warn if
        trying to add duplicate edges (duplicate edges will not
        be added in either case)
        :param edge_properties (dict): any other information to
        store on graph edges, similar to Structure's site_properties
        :return:
        """

        # this is not necessary for the class to work, but
        # just makes it neater
        if to_index < from_index:
            to_index, from_index = from_index, to_index
            to_jimage, from_jimage = from_jimage, to_jimage

        # constrain all from_jimages to be (0, 0, 0),
        # initial version of this class worked even if
        # from_jimage != (0, 0, 0), but making this
        # assumption simplifies logic later
        if not np.array_equal(from_jimage, (0, 0, 0)):
            shift = from_jimage
            from_jimage = np.subtract(from_jimage, shift)
            to_jimage = np.subtract(to_jimage, shift)

        # automatic detection of to_jimage if user doesn't specify
        # will try and detect all equivalent images and add multiple
        # edges if appropriate
        if to_jimage is None:
            # assume we want the closest site
            warnings.warn("Please specify to_jimage to be unambiguous, "
                          "trying to automatically detect.")
            dist, to_jimage = self.structure[from_index]\
                .distance_and_image(self.structure[to_index])
            if dist == 0:
                # this will happen when from_index == to_index,
                # typically in primitive single-atom lattices
                images = [1, 0, 0], [0, 1, 0], [0, 0, 1]
                dists = []
                for image in images:
                    dists.append(self.structure[from_index]
                                 .distance_and_image(self.structure[from_index],
                                                                               jimage=image)[0])
                dist = min(dists)
            equiv_sites = self.structure.get_neighbors_in_shell(self.structure[from_index].coords,
                                                                dist,
                                                                dist*0.01,
                                                                include_index=True)
            for site, dist, to_index in equiv_sites:
                to_jimage = np.subtract(site.frac_coords, self.structure[from_index].frac_coords)
                to_jimage = to_jimage.astype(int)
                self.add_edge(from_index=from_index, from_jimage=(0, 0, 0),
                              to_jimage=to_jimage, to_index=to_index)
            return

        # sanitize types
        from_jimage, to_jimage = tuple(map(int, from_jimage)), tuple(map(int, to_jimage))
        from_index, to_index = int(from_index), int(to_index)

        # check we're not trying to add a duplicate edge
        # there should only ever be at most one edge
        # between a given (site, jimage) pair and another
        # (site, jimage) pair
        existing_edge_data = self.graph.get_edge_data(from_index, to_index)
        if existing_edge_data:
            for key, d in existing_edge_data.items():
                if d["to_jimage"] == to_jimage:
                    if warn_duplicates:
                        warnings.warn("Trying to add an edge that already exists from "
                                      "site {} to site {} in {}.".format(from_index,
                                                                         to_index,
                                                                         to_jimage))
                    return

        # generic container for additional edge properties,
        # similar to site properties
        edge_properties = edge_properties or {}

        if weight:
            self.graph.add_edge(from_index, to_index,
                                to_jimage=to_jimage,
                                weight=weight,
                                **edge_properties)
        else:
            self.graph.add_edge(from_index, to_index,
                                to_jimage=to_jimage,
                                **edge_properties)


Instruction:
Can you create a Python 3 function that
changes the weight of an edge in the StructureGraph.

Response:
def alter_edge(self, from_index, to_index, to_jimage=None,
                   new_weight=None, new_edge_properties=None):
        """
        Alters either the weight or the edge_properties of
        an edge in the StructureGraph.

        :param from_index: int
        :param to_index: int
        :param to_jimage: tuple
        :param new_weight: alter_edge does not require
        that weight be altered. As such, by default, this
        is None. If weight is to be changed, it should be a
        float.
        :param new_edge_properties: alter_edge does not require
        that edge_properties be altered. As such, by default,
        this is None. If any edge properties are to be changed,
        it should be a dictionary of edge properties to be changed.
        :return:
        """

        existing_edges = self.graph.get_edge_data(from_index, to_index)

        # ensure that edge exists before attempting to change it
        if not existing_edges:
            raise ValueError("Edge between {} and {} cannot be altered;\
                                no edge exists between those sites.".format(
                                from_index, to_index
                                ))

        if to_jimage is None:
            edge_index = 0
        else:
            for i, properties in existing_edges.items():
                if properties["to_jimage"] == to_jimage:
                    edge_index = i

        if new_weight is not None:
            self.graph[from_index][to_index][edge_index]['weight'] = new_weight

        if new_edge_properties is not None:
            for prop in list(new_edge_properties.keys()):
                self.graph[from_index][to_index][edge_index][prop] = new_edge_properties[prop]


Instruction:
Create a Python 3 function to
remove an edge from the StructureGraph.

Response:
def break_edge(self, from_index, to_index, to_jimage=None, allow_reverse=False):
        """
        Remove an edge from the StructureGraph. If no image is given, this method will fail.

        :param from_index: int
        :param to_index: int
        :param to_jimage: tuple
        :param allow_reverse: If allow_reverse is True, then break_edge will
        attempt to break both (from_index, to_index) and, failing that,
        will attempt to break (to_index, from_index).
        :return:
        """

        # ensure that edge exists before attempting to remove it
        existing_edges = self.graph.get_edge_data(from_index, to_index)
        existing_reverse = None

        if to_jimage is None:
            raise ValueError("Image must be supplied, to avoid ambiguity.")

        if existing_edges:
            for i, properties in existing_edges.items():
                if properties["to_jimage"] == to_jimage:
                    edge_index = i

            self.graph.remove_edge(from_index, to_index, edge_index)

        else:
            if allow_reverse:
                existing_reverse = self.graph.get_edge_data(to_index, from_index)

            if existing_reverse:
                for i, properties in existing_reverse.items():
                    if properties["to_jimage"] == to_jimage:
                        edge_index = i

                self.graph.remove_edge(to_index, from_index, edge_index)
            else:
                raise ValueError("Edge cannot be broken between {} and {};\
                                no edge exists between those sites.".format(
                                from_index, to_index
                                ))


Instruction:
Can you generate a brief explanation for the following Python 3 code
def substitute_group(self, index, func_grp, strategy, bond_order=1,
                         graph_dict=None, strategy_params=None):
        """
        Builds off of Structure.substitute to replace an atom in self.structure
        with a functional group. This method also amends self.graph to
        incorporate the new functional group.

        NOTE: Care must be taken to ensure that the functional group that is
        substituted will not place atoms to close to each other, or violate the
        dimensions of the Lattice.

        :param index: Index of atom to substitute.
        :param func_grp: Substituent molecule. There are two options:

                1. Providing an actual Molecule as the input. The first atom
                   must be a DummySpecie X, indicating the position of
                   nearest neighbor. The second atom must be the next
                   nearest atom. For example, for a methyl group
                   substitution, func_grp should be X-CH3, where X is the
                   first site and C is the second site. What the code will
                   do is to remove the index site, and connect the nearest
                   neighbor to the C atom in CH3. The X-C bond indicates the
                   directionality to connect the atoms.
                2. A string name. The molecule will be obtained from the
                   relevant template in func_groups.json.
        :param strategy: Class from pymatgen.analysis.local_env.
        :param bond_order: A specified bond order to calculate the bond
                length between the attached functional group and the nearest
                neighbor site. Defaults to 1.
        :param graph_dict: Dictionary representing the bonds of the functional
                group (format: {(u, v): props}, where props is a dictionary of
                properties, including weight. If None, then the algorithm
                will attempt to automatically determine bonds using one of
                a list of strategies defined in pymatgen.analysis.local_env.
        :param strategy_params: dictionary of keyword arguments for strategy.
                If None, default parameters will be used.
        :return:
        """

        def map_indices(grp):
            grp_map = {}

            # Get indices now occupied by functional group
            # Subtracting 1 because the dummy atom X should not count
            atoms = len(grp) - 1
            offset = len(self.structure) - atoms

            for i in range(atoms):
                grp_map[i] = i + offset

            return grp_map

        if isinstance(func_grp, Molecule):
            func_grp = copy.deepcopy(func_grp)
        else:
            try:
                func_grp = copy.deepcopy(FunctionalGroups[func_grp])
            except:
                raise RuntimeError("Can't find functional group in list. "
                                   "Provide explicit coordinate instead")

        self.structure.substitute(index, func_grp, bond_order=bond_order)

        mapping = map_indices(func_grp)

        # Remove dummy atom "X"
        func_grp.remove_species("X")

        if graph_dict is not None:
            for (u, v) in graph_dict.keys():
                edge_props = graph_dict[(u, v)]
                if "to_jimage" in edge_props.keys():
                    to_jimage = edge_props["to_jimage"]
                    del edge_props["to_jimage"]
                else:
                    # By default, assume that all edges should stay remain
                    # inside the initial image
                    to_jimage = (0, 0, 0)
                if "weight" in edge_props.keys():
                    weight = edge_props["weight"]
                    del edge_props["weight"]
                self.add_edge(mapping[u], mapping[v], to_jimage=to_jimage,
                              weight=weight, edge_properties=edge_props)

        else:
            if strategy_params is None:
                strategy_params = {}
            strat = strategy(**strategy_params)

            for site in mapping.values():
                neighbors = strat.get_nn_info(self.structure, site)

                for neighbor in neighbors:
                    self.add_edge(from_index=site,
                                  from_jimage=(0, 0, 0),
                                  to_index=neighbor['site_index'],
                                  to_jimage=neighbor['image'],
                                  weight=neighbor['weight'],
                                  warn_duplicates=False)

Response: Substitute an atom in self. structure with a functional group.


Instruction:
Write a Python 3 function that can
return a list of tuples of neighbors of site n and jimage indices and weight.

Response:
def get_connected_sites(self, n, jimage=(0, 0, 0)):
        """
        Returns a named tuple of neighbors of site n:
        periodic_site, jimage, index, weight.
        Index is the index of the corresponding site
        in the original structure, weight can be
        None if not defined.
        :param n: index of Site in Structure
        :param jimage: lattice vector of site
        :return: list of ConnectedSite tuples,
        sorted by closest first
        """

        connected_sites = set()
        connected_site_images = set()

        out_edges = [(u, v, d, 'out') for u, v, d in self.graph.out_edges(n, data=True)]
        in_edges = [(u, v, d, 'in') for u, v, d in self.graph.in_edges(n, data=True)]

        for u, v, d, dir in out_edges + in_edges:

            to_jimage = d['to_jimage']

            if dir == 'in':
                u, v = v, u
                to_jimage = np.multiply(-1, to_jimage)

            to_jimage = tuple(map(int, np.add(to_jimage, jimage)))
            site_d = self.structure[v].as_dict()
            site_d['abc'] = np.add(site_d['abc'], to_jimage).tolist()
            site = PeriodicSite.from_dict(site_d)

            # from_site if jimage arg != (0, 0, 0)
            relative_jimage = np.subtract(to_jimage, jimage)
            dist = self.structure[u].distance(self.structure[v], jimage=relative_jimage)

            weight = d.get('weight', None)

            if (v, to_jimage) not in connected_site_images:

                connected_site = ConnectedSite(site=site,
                                               jimage=to_jimage,
                                               index=v,
                                               weight=weight,
                                               dist=dist)

                connected_sites.add(connected_site)
                connected_site_images.add((v, to_jimage))

        # return list sorted by closest sites first
        connected_sites = list(connected_sites)
        connected_sites.sort(key=lambda x: x.dist)

        return connected_sites


Instruction:
Write a Python 3 script for
returning the number of neighbors of site n in graph terms.

Response:
def get_coordination_of_site(self, n):
        """
        Returns the number of neighbors of site n.
        In graph terms, simply returns degree
        of node corresponding to site n.
        :param n: index of site
        :return (int):
        """
        number_of_self_loops = sum([1 for n, v in self.graph.edges(n) if n == v])
        return self.graph.degree(n) - number_of_self_loops


Instruction:
Make a summary of the following Python 3 code
def draw_graph_to_file(self, filename="graph",
                           diff=None,
                           hide_unconnected_nodes=False,
                           hide_image_edges=True,
                           edge_colors=False,
                           node_labels=False,
                           weight_labels=False,
                           image_labels=False,
                           color_scheme="VESTA",
                           keep_dot=False,
                           algo="fdp"):
        """
        Draws graph using GraphViz.

        The networkx graph object itself can also be drawn
        with networkx's in-built graph drawing methods, but
        note that this might give misleading results for
        multigraphs (edges are super-imposed on each other).

        If visualization is difficult to interpret,
        `hide_image_edges` can help, especially in larger
        graphs.

        :param filename: filename to output, will detect filetype
        from extension (any graphviz filetype supported, such as
        pdf or png)
        :param diff (StructureGraph): an additional graph to
        compare with, will color edges red that do not exist in diff
        and edges green that are in diff graph but not in the
        reference graph
        :param hide_unconnected_nodes: if True, hide unconnected
        nodes
        :param hide_image_edges: if True, do not draw edges that
        go through periodic boundaries
        :param edge_colors (bool): if True, use node colors to
        color edges
        :param node_labels (bool): if True, label nodes with
        species and site index
        :param weight_labels (bool): if True, label edges with
        weights
        :param image_labels (bool): if True, label edges with
        their periodic images (usually only used for debugging,
        edges to periodic images always appear as dashed lines)
        :param color_scheme (str): "VESTA" or "JMOL"
        :param keep_dot (bool): keep GraphViz .dot file for later
        visualization
        :param algo: any graphviz algo, "neato" (for simple graphs)
        or "fdp" (for more crowded graphs) usually give good outputs
        :return:
        """

        if not which(algo):
            raise RuntimeError("StructureGraph graph drawing requires "
                               "GraphViz binaries to be in the path.")

        # Developer note: NetworkX also has methods for drawing
        # graphs using matplotlib, these also work here. However,
        # a dedicated tool like GraphViz allows for much easier
        # control over graph appearance and also correctly displays
        # mutli-graphs (matplotlib can superimpose multiple edges).

        g = self.graph.copy()

        g.graph = {'nodesep': 10.0, 'dpi': 300, 'overlap': "false"}

        # add display options for nodes
        for n in g.nodes():

            # get label by species name
            label = "{}({})".format(str(self.structure[n].specie), n) if node_labels else ""

            # use standard color scheme for nodes
            c = EL_COLORS[color_scheme].get(str(self.structure[n].specie.symbol), [0, 0, 0])

            # get contrasting font color
            # magic numbers account for perceived luminescence
            # https://stackoverflow.com/questions/1855884/determine-font-color-based-on-background-color
            fontcolor = '#000000' if 1 - (c[0] * 0.299 + c[1] * 0.587
                                          + c[2] * 0.114) / 255 < 0.5 else '#ffffff'

            # convert color to hex string
            color = "#{:02x}{:02x}{:02x}".format(c[0], c[1], c[2])

            g.add_node(n, fillcolor=color, fontcolor=fontcolor, label=label,
                       fontname="Helvetica-bold", style="filled", shape="circle")

        edges_to_delete = []

        # add display options for edges
        for u, v, k, d in g.edges(keys=True, data=True):

            # retrieve from/to images, set as origin if not defined
            to_image = d['to_jimage']

            # set edge style
            d['style'] = "solid"
            if to_image != (0, 0, 0):
                d['style'] = "dashed"
                if hide_image_edges:
                    edges_to_delete.append((u, v, k))

            # don't show edge directions
            d['arrowhead'] = "none"

            # only add labels for images that are not the origin
            if image_labels:
                d['headlabel'] = "" if to_image == (0, 0, 0) else "to {}".format((to_image))
                d['arrowhead'] = "normal" if d['headlabel'] else "none"

            # optionally color edges using node colors
            color_u = g.node[u]['fillcolor']
            color_v = g.node[v]['fillcolor']
            d['color_uv'] = "{};0.5:{};0.5".format(color_u, color_v) if edge_colors else "#000000"

            # optionally add weights to graph
            if weight_labels:
                units = g.graph.get('edge_weight_units', "")
                if d.get('weight'):
                    d['label'] = "{:.2f} {}".format(d['weight'], units)

            # update edge with our new style attributes
            g.edges[u, v, k].update(d)

        # optionally remove periodic image edges,
        # these can be confusing due to periodic boundaries
        if hide_image_edges:
            for edge_to_delete in edges_to_delete:
                g.remove_edge(*edge_to_delete)

        # optionally hide unconnected nodes,
        # these can appear when removing periodic edges
        if hide_unconnected_nodes:
            g = g.subgraph([n for n in g.degree() if g.degree()[n] != 0])

        # optionally highlight differences with another graph
        if diff:
            diff = self.diff(diff, strict=True)
            green_edges = []
            red_edges = []
            for u, v, k, d in g.edges(keys=True, data=True):
                if (u, v, d['to_jimage']) in diff['self']:
                    # edge has been deleted
                    red_edges.append((u, v, k))
                elif (u, v, d['to_jimage']) in diff['other']:
                    # edge has been added
                    green_edges.append((u, v, k))
            for u, v, k in green_edges:
                g.edges[u, v, k].update({'color_uv': '#00ff00'})
            for u, v, k in red_edges:
                g.edges[u, v, k].update({'color_uv': '#ff0000'})

        basename, extension = os.path.splitext(filename)
        extension = extension[1:]

        write_dot(g, basename+".dot")

        with open(filename, "w") as f:

            args = [algo, "-T", extension, basename+".dot"]
            rs = subprocess.Popen(args,
                                  stdout=f,
                                  stdin=subprocess.PIPE, close_fds=True)
            rs.communicate()
            if rs.returncode != 0:
                raise RuntimeError("{} exited with return code {}.".format(algo, rs.returncode))

        if not keep_dot:
            os.remove(basename+".dot")

Response: Draw a graph to a file.


Instruction:
Can you tell what is the following Python 3 function doing
def types_and_weights_of_connections(self):
        def get_label(u, v):
            u_label = self.structure[u].species_string
            v_label = self.structure[v].species_string
            return "-".join(sorted((u_label, v_label)))

        types = defaultdict(list)
        for u, v, d in self.graph.edges(data=True):
            label = get_label(u, v)
            types[label].append(d['weight'])

        return dict(types)

Response: Extract a dictionary summarizing the types and weights of the edges in the graph.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def weight_statistics(self):

        all_weights = [d.get('weight', None) for u, v, d
                       in self.graph.edges(data=True)]
        stats = describe(all_weights, nan_policy='omit')

        return {
            'all_weights': all_weights,
            'min': stats.minmax[0],
            'max': stats.minmax[1],
            'mean': stats.mean,
            'variance': stats.variance
        }

Response: Extract a statistical summary of edge weights present in


Instruction:
Write a Python 3 script to
extract information on the different co - ordination environments present in the graph.

Response:
def types_of_coordination_environments(self, anonymous=False):
        """
        Extract information on the different co-ordination environments
        present in the graph.

        :param anonymous: if anonymous, will replace specie names
        with A, B, C, etc.
        :return: a list of co-ordination environments,
        e.g. ['Mo-S(6)', 'S-Mo(3)']
        """

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

            centre_sp = site.species_string

            connected_sites = self.get_connected_sites(idx)
            connected_species = [connected_site.site.species_string
                                 for connected_site in connected_sites]

            labels = []
            for sp in set(connected_species):
                count = connected_species.count(sp)
                labels.append((count, sp))

            labels = sorted(labels, reverse=True)

            if anonymous:
                mapping = {centre_sp: 'A'}
                available_letters = [chr(66+i) for i in range(25)]
                for label in labels:
                    sp = label[1]
                    if sp not in mapping:
                        mapping[sp] = available_letters.pop(0)
                centre_sp = 'A'
                labels = [(label[0], mapping[label[1]]) for label in labels]

            labels = ["{}({})".format(label[1], label[0]) for label in labels]
            motif = '{}-{}'.format(centre_sp, ','.join(labels))
            motifs.add(motif)

        return sorted(list(motifs))


Instruction:
Can you write a function in Python 3 where it
creates a new object from a dictionary.

Response:
def from_dict(cls, d):
        """
        As in :Class: `pymatgen.core.Structure` except
        restoring graphs using `from_dict_of_dicts`
        from NetworkX to restore graph information.
        """
        s = Structure.from_dict(d['structure'])
        return cls(s, d['graphs'])


Instruction:
How would you code a function in Python 3 to
sort the structure in - place.

Response:
def sort(self, key=None, reverse=False):
        """
        Same as Structure.sort(), also remaps nodes in graph.
        :param key:
        :param reverse:
        :return:
        """

        old_structure = self.structure.copy()

        # sort Structure
        self.structure._sites = sorted(self.structure._sites, key=key, reverse=reverse)

        # apply Structure ordering to graph
        mapping = {idx:self.structure.index(site) for idx, site in enumerate(old_structure)}
        self.graph = nx.relabel_nodes(self.graph, mapping, copy=True)

        # normalize directions of edges
        edges_to_remove = []
        edges_to_add = []
        for u, v, k, d in self.graph.edges(keys=True, data=True):
            if v < u:
                new_v, new_u, new_d = u, v, d.copy()
                new_d['to_jimage'] = tuple(np.multiply(-1, d['to_jimage']).astype(int))
                edges_to_remove.append((u, v, k))
                edges_to_add.append((new_u, new_v, new_d))

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


Instruction:
Implement a function in Python 3 to
compare two StructureGraphs and returns a dictionary with keys self and other and edges that are present in only one StructureGraph and edges that are present in both StructureGraphs and both are both present in only one StructureGraph.

Response:
def diff(self, other, strict=True):
        """
        Compares two StructureGraphs. Returns dict with
        keys 'self', 'other', 'both' with edges that are
        present in only one StructureGraph ('self' and
        'other'), and edges that are present in both.

        The Jaccard distance is a simple measure of the
        dissimilarity between two StructureGraphs (ignoring
        edge weights), and is defined by 1 - (size of the
        intersection / size of the union) of the sets of
        edges. This is returned with key 'dist'.

        Important note: all node indices are in terms
        of the StructureGraph this method is called
        from, not the 'other' StructureGraph: there
        is no guarantee the node indices will be the
        same if the underlying Structures are ordered
        differently.

        :param other: StructureGraph
        :param strict: if False, will compare bonds
        from different Structures, with node indices
        replaced by Specie strings, will not count
        number of occurrences of bonds
        :return:
        """

        if self.structure != other.structure and strict:
            return ValueError("Meaningless to compare StructureGraphs if "
                              "corresponding Structures are different.")

        if strict:

            # sort for consistent node indices
            # PeriodicSite should have a proper __hash__() value,
            # using its frac_coords as a convenient key
            mapping = {tuple(site.frac_coords):self.structure.index(site) for site in other.structure}
            other_sorted = other.__copy__()
            other_sorted.sort(key=lambda site: mapping[tuple(site.frac_coords)])

            edges = {(u, v, d['to_jimage'])
                     for u, v, d in self.graph.edges(keys=False, data=True)}

            edges_other = {(u, v, d['to_jimage'])
                           for u, v, d in other_sorted.graph.edges(keys=False, data=True)}

        else:

            edges = {(str(self.structure[u].specie),
                      str(self.structure[v].specie))
                     for u, v, d in self.graph.edges(keys=False, data=True)}

            edges_other = {(str(other.structure[u].specie),
                            str(other.structure[v].specie))
                           for u, v, d in other.graph.edges(keys=False, data=True)}

        if len(edges) == 0 and len(edges_other) == 0:
            jaccard_dist = 0  # by definition
        else:
            jaccard_dist = 1 - len(edges.intersection(edges_other)) / len(edges.union(edges_other))

        return {
            'self': edges - edges_other,
            'other': edges_other - edges,
            'both': edges.intersection(edges_other),
            'dist': jaccard_dist
        }


Instruction:
Can you generate the documentation for the following Python 3 function
def get_subgraphs_as_molecules(self, use_weights=False):

        # creating a supercell is an easy way to extract
        # molecules (and not, e.g., layers of a 2D crystal)
        # without adding extra logic
        if getattr(self, '_supercell_sg', None) is None:
            self._supercell_sg = supercell_sg = self*(3,3,3)

        # make undirected to find connected subgraphs
        supercell_sg.graph = nx.Graph(supercell_sg.graph)

        # find subgraphs
        all_subgraphs = list(nx.connected_component_subgraphs(supercell_sg.graph))

        # discount subgraphs that lie across *supercell* boundaries
        # these will subgraphs representing crystals
        molecule_subgraphs = []
        for subgraph in all_subgraphs:
            intersects_boundary = any([d['to_jimage'] != (0, 0, 0)
                                      for u, v, d in subgraph.edges(data=True)])
            if not intersects_boundary:
                molecule_subgraphs.append(subgraph)

        # add specie names to graph to be able to test for isomorphism
        for subgraph in molecule_subgraphs:
            for n in subgraph:
                subgraph.add_node(n, specie=str(supercell_sg.structure[n].specie))

        # now define how we test for isomorphism
        def node_match(n1, n2):
            return n1['specie'] == n2['specie']
        def edge_match(e1, e2):
            if use_weights:
                return e1['weight'] == e2['weight']
            else:
                return True

        # prune duplicate subgraphs
        unique_subgraphs = []
        for subgraph in molecule_subgraphs:

            already_present = [nx.is_isomorphic(subgraph, g,
                                                node_match=node_match,
                                                edge_match=edge_match)
                               for g in unique_subgraphs]

            if not any(already_present):
                unique_subgraphs.append(subgraph)

        # get Molecule objects for each subgraph
        molecules = []
        for subgraph in unique_subgraphs:

            coords = [supercell_sg.structure[n].coords for n
                      in subgraph.nodes()]
            species = [supercell_sg.structure[n].specie for n
                      in subgraph.nodes()]

            molecule = Molecule(species, coords)

            # shift so origin is at center of mass
            molecule = molecule.get_centered_molecule()

            molecules.append(molecule)

        return molecules

Response: Retrieve subgraphs as molecules useful for extracting the crystal as a list of unique Molecules.


Instruction:
Can you implement a function in Python 3 that
returns a MoleculeGraph object with an empty graph.

Response:
def with_empty_graph(cls, molecule, name="bonds",
                         edge_weight_name=None,
                         edge_weight_units=None):
        """
        Constructor for MoleculeGraph, returns a MoleculeGraph
        object with an empty graph (no edges, only nodes defined
        that correspond to Sites in Molecule).

        :param molecule (Molecule):
        :param name (str): name of graph, e.g. "bonds"
        :param edge_weight_name (str): name of edge weights,
        e.g. "bond_length" or "exchange_constant"
        :param edge_weight_units (str): name of edge weight units
        e.g. "Å" or "eV"
        :return (MoleculeGraph):
        """

        if edge_weight_name and (edge_weight_units is None):
            raise ValueError("Please specify units associated "
                             "with your edge weights. Can be "
                             "empty string if arbitrary or "
                             "dimensionless.")

        # construct graph with one node per site
        # graph attributes don't change behavior of graph,
        # they're just for book-keeping
        graph = nx.MultiDiGraph(edge_weight_name=edge_weight_name,
                                edge_weight_units=edge_weight_units,
                                name=name)
        graph.add_nodes_from(range(len(molecule)))

        graph_data = json_graph.adjacency_data(graph)

        return cls(molecule, graph_data=graph_data)


Instruction:
Explain what the following Python 3 code does
def with_edges(molecule, edges):

        mg = MoleculeGraph.with_empty_graph(molecule, name="bonds",
                                            edge_weight_name="weight",
                                            edge_weight_units="")

        for edge, props in edges.items():

            try:
                from_index = edge[0]
                to_index = edge[1]
            except TypeError:
                raise ValueError("Edges must be given as (from_index, to_index)"
                                 "tuples")

            if props is not None:
                if "weight" in props.keys():
                    weight = props["weight"]
                    del props["weight"]
                else:
                    weight = None

                if len(props.items()) == 0:
                    props = None
            else:
                weight = None

            nodes = mg.graph.nodes
            if not (from_index in nodes and to_index in nodes):
                raise ValueError("Edges cannot be added if nodes are not"
                                 " present in the graph. Please check your"
                                 " indices.")

            mg.add_edge(from_index, to_index, weight=weight,
                        edge_properties=props)

        mg.set_node_attributes()
        return mg

Response: Returns a MoleculeGraph with the given edges added.


Instruction:
How would you explain what the following Python 3 function does
def with_local_env_strategy(molecule, strategy, reorder=True,
                                extend_structure=True):
        """
        Constructor for MoleculeGraph, using a strategy
        from :Class: `pymatgen.analysis.local_env`.

        :param molecule: Molecule object
        :param strategy: an instance of a
            :Class: `pymatgen.analysis.local_env.NearNeighbors` object
        :param reorder: bool, representing if graph nodes need to be reordered
            following the application of the local_env strategy
        :param extend_structure: If True (default), then a large artificial box
            will be placed around the Molecule, because some strategies assume
            periodic boundary conditions.
        :return: mg, a MoleculeGraph
        """

        mg = MoleculeGraph.with_empty_graph(molecule, name="bonds",
                                            edge_weight_name="weight",
                                            edge_weight_units="")

        # NearNeighbor classes only (generally) work with structures
        # molecules have to be boxed first
        coords = molecule.cart_coords

        if extend_structure:
            a = max(coords[:, 0]) - min(coords[:, 0]) + 100
            b = max(coords[:, 1]) - min(coords[:, 1]) + 100
            c = max(coords[:, 2]) - min(coords[:, 2]) + 100

            molecule = molecule.get_boxed_structure(a, b, c, no_cross=True)

        for n in range(len(molecule)):
            neighbors = strategy.get_nn_info(molecule, n)
            for neighbor in neighbors:

                # all bonds in molecules should not cross
                # (artificial) periodic boundaries
                if not np.array_equal(neighbor['image'], [0, 0, 0]):
                    continue

                # local_env will always try to add two edges
                # for any one bond, one from site u to site v
                # and another form site v to site u: this is
                # harmless, so warn_duplicates=False
                mg.add_edge(from_index=n,
                            to_index=neighbor['site_index'],
                            weight=neighbor['weight'],
                            warn_duplicates=False)

        if reorder:
            # Reverse order of nodes to match with molecule
            n = len(mg.molecule)
            mapping = {i: (n-i) for i in range(n)}
            mapping = {i: (j-1) for i, j in mapping.items()}

            mg.graph = nx.relabel_nodes(mg.graph, mapping)

        duplicates = []
        for edge in mg.graph.edges:
            if edge[2] != 0:
                duplicates.append(edge)

        for duplicate in duplicates:
            mg.graph.remove_edge(duplicate[0], duplicate[1], key=duplicate[2])

        mg.set_node_attributes()
        return mg

Response: Returns a MoleculeGraph with a local_env strategy.


Instruction:
Can you generate the documentation for the following Python 3 function
def add_edge(self, from_index, to_index,
                 weight=None, warn_duplicates=True,
                 edge_properties=None):
        """
        Add edge to graph.

        Since physically a 'bond' (or other connection
        between sites) doesn't have a direction, from_index,
        from_jimage can be swapped with to_index, to_jimage.

        However, images will always always be shifted so that
        from_index < to_index and from_jimage becomes (0, 0, 0).

        :param from_index: index of site connecting from
        :param to_index: index of site connecting to
        :param weight (float): e.g. bond length
        :param warn_duplicates (bool): if True, will warn if
        trying to add duplicate edges (duplicate edges will not
        be added in either case)
        :param edge_properties (dict): any other information to
        store on graph edges, similar to Structure's site_properties
        :return:
        """

        # this is not necessary for the class to work, but
        # just makes it neater
        if to_index < from_index:
            to_index, from_index = from_index, to_index

        # sanitize types
        from_index, to_index = int(from_index), int(to_index)

        # check we're not trying to add a duplicate edge
        # there should only ever be at most one edge
        # between two sites
        existing_edge_data = self.graph.get_edge_data(from_index, to_index)
        if existing_edge_data and warn_duplicates:
            warnings.warn("Trying to add an edge that already exists from "
                            "site {} to site {}.".format(from_index,
                                                         to_index))
            return

        # generic container for additional edge properties,
        # similar to site properties
        edge_properties = edge_properties or {}

        if weight:
            self.graph.add_edge(from_index, to_index,
                                weight=weight,
                                **edge_properties)
        else:
            self.graph.add_edge(from_index, to_index,
                                **edge_properties)

Response: Add an edge from one site to another.