Instruction:
Write a Python 3 function for
processing INCAR parameter values.

Response:
def proc_val(key, val):
        """
        Static helper method to convert INCAR parameters to proper types, e.g.,
        integers, floats, lists, etc.

        Args:
            key: INCAR parameter key
            val: Actual value of INCAR parameter.
        """
        list_keys = ("LDAUU", "LDAUL", "LDAUJ", "MAGMOM", "DIPOL",
                     "LANGEVIN_GAMMA", "QUAD_EFG", "EINT")
        bool_keys = ("LDAU", "LWAVE", "LSCALU", "LCHARG", "LPLANE", "LUSE_VDW",
                     "LHFCALC", "ADDGRID", "LSORBIT", "LNONCOLLINEAR")
        float_keys = ("EDIFF", "SIGMA", "TIME", "ENCUTFOCK", "HFSCREEN",
                      "POTIM", "EDIFFG", "AGGAC", "PARAM1", "PARAM2")
        int_keys = ("NSW", "NBANDS", "NELMIN", "ISIF", "IBRION", "ISPIN",
                    "ICHARG", "NELM", "ISMEAR", "NPAR", "LDAUPRINT", "LMAXMIX",
                    "ENCUT", "NSIM", "NKRED", "NUPDOWN", "ISPIND", "LDAUTYPE",
                    "IVDW")

        def smart_int_or_float(numstr):
            if numstr.find(".") != -1 or numstr.lower().find("e") != -1:
                return float(numstr)
            else:
                return int(numstr)

        try:
            if key in list_keys:
                output = []
                toks = re.findall(
                    r"(-?\d+\.?\d*)\*?(-?\d+\.?\d*)?\*?(-?\d+\.?\d*)?", val)
                for tok in toks:
                    if tok[2] and "3" in tok[0]:
                        output.extend(
                            [smart_int_or_float(tok[2])] * int(tok[0])
                            * int(tok[1]))
                    elif tok[1]:
                        output.extend([smart_int_or_float(tok[1])] *
                                      int(tok[0]))
                    else:
                        output.append(smart_int_or_float(tok[0]))
                return output
            if key in bool_keys:
                m = re.match(r"^\.?([T|F|t|f])[A-Za-z]*\.?", val)
                if m:
                    if m.group(1) == "T" or m.group(1) == "t":
                        return True
                    else:
                        return False
                raise ValueError(key + " should be a boolean type!")

            if key in float_keys:
                return float(re.search(r"^-?\d*\.?\d*[e|E]?-?\d*", val).group(0))

            if key in int_keys:
                return int(re.match(r"^-?[0-9]+", val).group(0))

        except ValueError:
            pass

        # Not in standard keys. We will try a hierarchy of conversions.
        try:
            val = int(val)
            return val
        except ValueError:
            pass

        try:
            val = float(val)
            return val
        except ValueError:
            pass

        if "true" in val.lower():
            return True

        if "false" in val.lower():
            return False

        return val.strip().capitalize()


Instruction:
Can you implement a function in Python 3 that
returns a dictionary that contains the difference between two Incar objects.

Response:
def diff(self, other):
        """
        Diff function for Incar.  Compares two Incars and indicates which
        parameters are the same and which are not. Useful for checking whether
        two runs were done using the same parameters.

        Args:
            other (Incar): The other Incar object to compare to.

        Returns:
            Dict of the following format:
            {"Same" : parameters_that_are_the_same,
            "Different": parameters_that_are_different}
            Note that the parameters are return as full dictionaries of values.
            E.g. {"ISIF":3}
        """
        similar_param = {}
        different_param = {}
        for k1, v1 in self.items():
            if k1 not in other:
                different_param[k1] = {"INCAR1": v1, "INCAR2": None}
            elif v1 != other[k1]:
                different_param[k1] = {"INCAR1": v1, "INCAR2": other[k1]}
            else:
                similar_param[k1] = v1
        for k2, v2 in other.items():
            if k2 not in similar_param and k2 not in different_param:
                if k2 not in self:
                    different_param[k2] = {"INCAR1": None, "INCAR2": v2}
        return {"Same": similar_param, "Different": different_param}


Instruction:
Can you generate the documentation for the following Python 3 function
def gamma_automatic(kpts=(1, 1, 1), shift=(0, 0, 0)):
        return Kpoints("Automatic kpoint scheme", 0,
                       Kpoints.supported_modes.Gamma, kpts=[kpts],
                       kpts_shift=shift)

Response: A static constructor for an automatic Gamma centered Kpoint - set grid.


Instruction:
How would you implement a function in Python 3 that
returns an automatic Kpoint object based on a structure and a kpoint - based grid density.

Response:
def automatic_density(structure, kppa, force_gamma=False):
        """
        Returns an automatic Kpoint object based on a structure and a kpoint
        density. Uses Gamma centered meshes for hexagonal cells and
        Monkhorst-Pack grids otherwise.

        Algorithm:
            Uses a simple approach scaling the number of divisions along each
            reciprocal lattice vector proportional to its length.

        Args:
            structure (Structure): Input structure
            kppa (int): Grid density
            force_gamma (bool): Force a gamma centered mesh (default is to
                use gamma only for hexagonal cells or odd meshes)

        Returns:
            Kpoints
        """
        comment = "pymatgen v%s with grid density = %.0f / atom" % (
            __version__, kppa)
        if math.fabs((math.floor(kppa ** (1 / 3) + 0.5)) ** 3 - kppa) < 1:
            kppa += kppa * 0.01
        latt = structure.lattice
        lengths = latt.abc
        ngrid = kppa / structure.num_sites
        mult = (ngrid * lengths[0] * lengths[1] * lengths[2]) ** (1 / 3)

        num_div = [int(math.floor(max(mult / l, 1))) for l in lengths]

        is_hexagonal = latt.is_hexagonal()

        has_odd = any([i % 2 == 1 for i in num_div])
        if has_odd or is_hexagonal or force_gamma:
            style = Kpoints.supported_modes.Gamma
        else:
            style = Kpoints.supported_modes.Monkhorst

        return Kpoints(comment, 0, style, [num_div], [0, 0, 0])


Instruction:
Can you write a function in Python 3 where it
returns an automatic Kpoint object based on a structure and a kpoint - based grid density.

Response:
def automatic_gamma_density(structure, kppa):
        """
        Returns an automatic Kpoint object based on a structure and a kpoint
        density. Uses Gamma centered meshes always. For GW.

        Algorithm:
            Uses a simple approach scaling the number of divisions along each
            reciprocal lattice vector proportional to its length.

        Args:
            structure:
                Input structure
            kppa:
                Grid density
        """

        latt = structure.lattice
        lengths = latt.abc
        ngrid = kppa / structure.num_sites

        mult = (ngrid * lengths[0] * lengths[1] * lengths[2]) ** (1 / 3)
        num_div = [int(round(mult / l)) for l in lengths]

        # ensure that numDiv[i] > 0
        num_div = [i if i > 0 else 1 for i in num_div]

        # VASP documentation recommends to use even grids for n <= 8 and odd
        # grids for n > 8.
        num_div = [i + i % 2 if i <= 8 else i - i % 2 + 1 for i in num_div]

        style = Kpoints.supported_modes.Gamma

        comment = "pymatgen 4.7.6+ generated KPOINTS with grid density = " + \
                  "{} / atom".format(kppa)
        num_kpts = 0
        return Kpoints(comment, num_kpts, style, [num_div], [0, 0, 0])


Instruction:
Given the following Python 3 function, write the documentation
def automatic_linemode(divisions, ibz):
        kpoints = list()
        labels = list()
        for path in ibz.kpath["path"]:
            kpoints.append(ibz.kpath["kpoints"][path[0]])
            labels.append(path[0])
            for i in range(1, len(path) - 1):
                kpoints.append(ibz.kpath["kpoints"][path[i]])
                labels.append(path[i])
                kpoints.append(ibz.kpath["kpoints"][path[i]])
                labels.append(path[i])

            kpoints.append(ibz.kpath["kpoints"][path[-1]])
            labels.append(path[-1])

        return Kpoints("Line_mode KPOINTS file",
                       style=Kpoints.supported_modes.Line_mode,
                       coord_type="Reciprocal",
                       kpts=kpoints,
                       labels=labels,
                       num_kpts=int(divisions))

Response: A static constructor for automatic line mode KPOINTS file.


Instruction:
Here you have a function in Python 3, explain what it does
def from_string(string):
        lines = [line.strip() for line in string.splitlines()]

        comment = lines[0]
        num_kpts = int(lines[1].split()[0].strip())
        style = lines[2].lower()[0]

        # Fully automatic KPOINTS
        if style == "a":
            return Kpoints.automatic(int(lines[3]))

        coord_pattern = re.compile(r'^\s*([\d+.\-Ee]+)\s+([\d+.\-Ee]+)\s+'
                                   r'([\d+.\-Ee]+)')

        # Automatic gamma and Monk KPOINTS, with optional shift
        if style == "g" or style == "m":
            kpts = [int(i) for i in lines[3].split()]
            kpts_shift = (0, 0, 0)
            if len(lines) > 4 and coord_pattern.match(lines[4]):
                try:
                    kpts_shift = [float(i) for i in lines[4].split()]
                except ValueError:
                    pass
            return Kpoints.gamma_automatic(kpts, kpts_shift) if style == "g" \
                else Kpoints.monkhorst_automatic(kpts, kpts_shift)

        # Automatic kpoints with basis
        if num_kpts <= 0:
            style = Kpoints.supported_modes.Cartesian if style in "ck" \
                else Kpoints.supported_modes.Reciprocal
            kpts = [[float(j) for j in lines[i].split()] for i in range(3, 6)]
            kpts_shift = [float(i) for i in lines[6].split()]
            return Kpoints(comment=comment, num_kpts=num_kpts, style=style,
                           kpts=kpts, kpts_shift=kpts_shift)

        # Line-mode KPOINTS, usually used with band structures
        if style == "l":
            coord_type = "Cartesian" if lines[3].lower()[0] in "ck" \
                else "Reciprocal"
            style = Kpoints.supported_modes.Line_mode
            kpts = []
            labels = []
            patt = re.compile(r'([e0-9.\-]+)\s+([e0-9.\-]+)\s+([e0-9.\-]+)'
                              r'\s*!*\s*(.*)')
            for i in range(4, len(lines)):
                line = lines[i]
                m = patt.match(line)
                if m:
                    kpts.append([float(m.group(1)), float(m.group(2)),
                                 float(m.group(3))])
                    labels.append(m.group(4).strip())
            return Kpoints(comment=comment, num_kpts=num_kpts, style=style,
                           kpts=kpts, coord_type=coord_type, labels=labels)

        # Assume explicit KPOINTS if all else fails.
        style = Kpoints.supported_modes.Cartesian if style in "ck" \
            else Kpoints.supported_modes.Reciprocal
        kpts = []
        kpts_weights = []
        labels = []
        tet_number = 0
        tet_weight = 0
        tet_connections = None

        for i in range(3, 3 + num_kpts):
            toks = lines[i].split()
            kpts.append([float(j) for j in toks[0:3]])
            kpts_weights.append(float(toks[3]))
            if len(toks) > 4:
                labels.append(toks[4])
            else:
                labels.append(None)
        try:
            # Deal with tetrahedron method
            if lines[3 + num_kpts].strip().lower()[0] == "t":
                toks = lines[4 + num_kpts].split()
                tet_number = int(toks[0])
                tet_weight = float(toks[1])
                tet_connections = []
                for i in range(5 + num_kpts, 5 + num_kpts + tet_number):
                    toks = lines[i].split()
                    tet_connections.append((int(toks[0]),
                                            [int(toks[j])
                                             for j in range(1, 5)]))
        except IndexError:
            pass

        return Kpoints(comment=comment, num_kpts=num_kpts,
                       style=Kpoints.supported_modes[str(style)],
                       kpts=kpts, kpts_weights=kpts_weights,
                       tet_number=tet_number, tet_weight=tet_weight,
                       tet_connections=tet_connections, labels=labels)

Response: Reads a Kpoints object from a string.


Instruction:
Make a summary of the following Python 3 code
def as_dict(self):
        d = {"comment": self.comment, "nkpoints": self.num_kpts,
             "generation_style": self.style.name, "kpoints": self.kpts,
             "usershift": self.kpts_shift,
             "kpts_weights": self.kpts_weights, "coord_type": self.coord_type,
             "labels": self.labels, "tet_number": self.tet_number,
             "tet_weight": self.tet_weight,
             "tet_connections": self.tet_connections}

        optional_paras = ["genvec1", "genvec2", "genvec3", "shift"]
        for para in optional_paras:
            if para in self.__dict__:
                d[para] = self.__dict__[para]
        d["@module"] = self.__class__.__module__
        d["@class"] = self.__class__.__name__
        return d

Response: json friendly dict representation of Kpoints


Instruction:
Can you generate a brief explanation for the following Python 3 code
def element(self):
        element = self.keywords["VRHFIN"].split(":")[0].strip()
        try:
            return Element(element).symbol
        except ValueError:
            # VASP incorrectly gives the element symbol for Xe as "X"
            # Some potentials, e.g., Zr_sv, gives the symbol as r.
            if element == "X":
                return "Xe"
            return Element(self.symbol.split("_")[0]).symbol

Response: Attempt to return the atomic symbol based on the VRHFIN keyword.


Instruction:
Can you create a Python 3 function that
initializes the POTCARs from a list of symbols.

Response:
def set_symbols(self, symbols, functional=None,
                    sym_potcar_map=None):
        """
        Initialize the POTCAR from a set of symbols. Currently, the POTCARs can
        be fetched from a location specified in .pmgrc.yaml. Use pmg config
        to add this setting.

        Args:
            symbols ([str]): A list of element symbols
            functional (str): The functional to use. If None, the setting
                PMG_DEFAULT_FUNCTIONAL in .pmgrc.yaml is used, or if this is
                not set, it will default to PBE.
            sym_potcar_map (dict): A map of symbol:raw POTCAR string. If
                sym_potcar_map is specified, POTCARs will be generated from
                the given map data rather than the config file location.
        """
        del self[:]
        if sym_potcar_map:
            for el in symbols:
                self.append(PotcarSingle(sym_potcar_map[el]))
        else:
            for el in symbols:
                p = PotcarSingle.from_symbol_and_functional(el, functional)
                self.append(p)


Instruction:
Implement a Python 3 function for
writing the input to a directory.

Response:
def write_input(self, output_dir=".", make_dir_if_not_present=True):
        """
        Write VASP input to a directory.

        Args:
            output_dir (str): Directory to write to. Defaults to current
                directory (".").
            make_dir_if_not_present (bool): Create the directory if not
                present. Defaults to True.
        """
        if make_dir_if_not_present and not os.path.exists(output_dir):
            os.makedirs(output_dir)
        for k, v in self.items():
            with zopen(os.path.join(output_dir, k), "wt") as f:
                f.write(v.__str__())


Instruction:
Here you have a function in Python 3, explain what it does
def from_directory(input_dir, optional_files=None):
        sub_d = {}
        for fname, ftype in [("INCAR", Incar), ("KPOINTS", Kpoints),
                             ("POSCAR", Poscar), ("POTCAR", Potcar)]:
            fullzpath = zpath(os.path.join(input_dir, fname))
            sub_d[fname.lower()] = ftype.from_file(fullzpath)
        sub_d["optional_files"] = {}
        if optional_files is not None:
            for fname, ftype in optional_files.items():
                sub_d["optional_files"][fname] = \
                    ftype.from_file(os.path.join(input_dir, fname))
        return VaspInput(**sub_d)

Response: Reads in a set of VASP input from a directory.


Instruction:
Can you tell what is the following Python 3 function doing
def run_vasp(self, run_dir: PathLike = ".",
                 vasp_cmd: list = None,
                 output_file: PathLike = "vasp.out",
                 err_file: PathLike = "vasp.err"):
        """
        Write input files and run VASP.

        :param run_dir: Where to write input files and do the run.
        :param vasp_cmd: Args to be supplied to run VASP. Otherwise, the
            PMG_VASP_EXE in .pmgrc.yaml is used.
        :param output_file: File to write output.
        :param err_file: File to write err.
        """
        self.write_input(output_dir=run_dir)
        vasp_cmd = vasp_cmd or SETTINGS.get("PMG_VASP_EXE")
        vasp_cmd = [os.path.expanduser(os.path.expandvars(t)) for t in vasp_cmd]
        if not vasp_cmd:
            raise RuntimeError("You need to supply vasp_cmd or set the PMG_VASP_EXE in .pmgrc.yaml to run VASP.")
        with cd(run_dir):
            with open(output_file, 'w') as f_std, \
                    open(err_file, "w", buffering=1) as f_err:
                subprocess.check_call(vasp_cmd, stdout=f_std, stderr=f_err)

Response: Runs VASP on the specified directory.


Instruction:
How would you explain what the following Python 3 function does
def get_kpoints(structure, min_distance=0, min_total_kpoints=1,
                kppra=None, gap_distance=7, remove_symmetry=None,
                include_gamma="auto", header="simple", incar=None):
    """
    Get kpoints object from JHU servlet, per Wisesa-McGill-Mueller
    methodology.  Refer to http://muellergroup.jhu.edu/K-Points.html
    and P. Wisesa, K. A. McGill, T. Mueller, Phys. Rev. B 93,
    155109 (2016)

    Args:
        structure (Structure): structure object
        min_distance (float): The minimum allowed distance
            between lattice points on the real-space superlattice
        min_total_kpoints (int): The minimum allowed number of
            total k-points in the Brillouin zone.
        kppra (float): minimum k-points per reciprocal atom.
        gap_distance (float): auto-detection threshold for
            non-periodicity (in slabs, nanowires, etc.)
        remove_symmetry (string): optional flag to control
            symmetry options, can be none, structural,
            time_reversal, or all
        include_gamma (string or bool): whether to include
            gamma point
        header (string): "verbose" or "simple", denotes
            the verbosity of the header
        incar (Incar): incar object to upload
    """
    config = locals()
    config.pop("structure", "incar")

    # Generate PRECALC string
    precalc = ''.join(["{}={}\n".format(k, v) for k, v in config.items()])
    precalc = precalc.replace('_', '').upper()
    precalc = precalc.replace('REMOVESYMMETRY', 'REMOVE_SYMMETRY')
    precalc = precalc.replace('TIMEREVERSAL', 'TIME_REVERSAL')
    url = "http://muellergroup.jhu.edu:8080/PreCalcServer/PreCalcServlet"
    temp_dir_name = tempfile.mkdtemp()
    cwd = os.getcwd()
    os.chdir(temp_dir_name)

    precalc_file = open("PRECALC", 'w+')
    poscar_file = open("POSCAR", 'w+')
    incar_file = open("INCAR", 'w+')

    precalc_file.write(precalc)
    poscar_file.write(structure.to("POSCAR"))
    files = [("fileupload", precalc_file),
             ("fileupload", poscar_file)]
    if incar:
        incar_file.write(incar.get_string())
        files.append(("fileupload", incar_file))

    precalc_file.seek(0)
    poscar_file.seek(0)
    incar_file.seek(0)

    r = requests.post(url, files=files)

    precalc_file.close()
    poscar_file.close()
    incar_file.close()
    kpoints = Kpoints.from_string(r.text)
    os.chdir(cwd)

    shutil.rmtree(temp_dir_name)

    return kpoints

Response: Returns a k - points object from the JHU servlet.


Instruction:
Implement a function in Python 3 to
return the simplex facets of the Convex Hull.

Response:
def get_facets(qhull_data, joggle=False):
    """
    Get the simplex facets for the Convex hull.

    Args:
        qhull_data (np.ndarray): The data from which to construct the convex
            hull as a Nxd array (N being number of data points and d being the
            dimension)
        joggle (boolean): Whether to joggle the input to avoid precision
            errors.

    Returns:
        List of simplices of the Convex Hull.
    """
    if joggle:
        return ConvexHull(qhull_data, qhull_options="QJ i").simplices
    else:
        return ConvexHull(qhull_data, qhull_options="Qt i").simplices


Instruction:
Here you have a function in Python 3, explain what it does
def uniquelines(q):
    setoflines = set()
    for facets in q:
        for line in itertools.combinations(facets, 2):
            setoflines.add(tuple(sorted(line)))
    return setoflines

Response: Given all the facets convert it into a set of unique lines.


Instruction:
Create a Python 3 function to
convert a 2D coordinate into a triangular - based coordinate system.

Response:
def triangular_coord(coord):
    """
    Convert a 2D coordinate into a triangle-based coordinate system for a
    prettier phase diagram.

    Args:
        coordinate: coordinate used in the convex hull computation.

    Returns:
        coordinates in a triangular-based coordinate system.
    """
    unitvec = np.array([[1, 0], [0.5, math.sqrt(3) / 2]])
    result = np.dot(np.array(coord), unitvec)
    return result.transpose()


Instruction:
Write a Python 3 script for
ordering the entries (their coordinates) in a phase diagram plot according to the user specified ordering. Ordering should be given as ['Up', 'Left', 'Right'], where Up, Left and Right are the names of the entries in the upper, left and right corners of the triangle respectively. Args: lines: list of list of coordinates for lines in the PD. stable_entries: {coordinate : entry} for each stable node in the phase diagram. (Each coordinate can only have one stable phase) unstable_entries: {entry: coordinates} for all unstable nodes in the phase diagram. ordering: Ordering of the phase diagram, given as a list ['Up', 'Left','Right'] Returns: (newlines, newstable_entries, newunstable_entries): - newlines is a list of list of coordinates for lines in the PD. - newstable_entries is a {coordinate : entry} for each stable node in the phase diagram. (Each coordinate can only have one stable phase) - newunstable_entries is a {entry: coordinates} for all unstable nodes in the phase diagram.

Response:
def order_phase_diagram(lines, stable_entries, unstable_entries, ordering):
    """
    Orders the entries (their coordinates) in a phase diagram plot according
    to the user specified ordering.
    Ordering should be given as ['Up', 'Left', 'Right'], where Up,
    Left and Right are the names of the entries in the upper, left and right
    corners of the triangle respectively.

    Args:
        lines: list of list of coordinates for lines in the PD.
        stable_entries: {coordinate : entry} for each stable node in the
            phase diagram. (Each coordinate can only have one stable phase)
        unstable_entries: {entry: coordinates} for all unstable nodes in the
            phase diagram.
        ordering: Ordering of the phase diagram, given as a list ['Up',
            'Left','Right']

    Returns:
        (newlines, newstable_entries, newunstable_entries):
        - newlines is a list of list of coordinates for lines in the PD.
        - newstable_entries is a {coordinate : entry} for each stable node
        in the phase diagram. (Each coordinate can only have one
        stable phase)
        - newunstable_entries is a {entry: coordinates} for all unstable
        nodes in the phase diagram.
    """
    yup = -1000.0
    xleft = 1000.0
    xright = -1000.0

    for coord in stable_entries:
        if coord[0] > xright:
            xright = coord[0]
            nameright = stable_entries[coord].name
        if coord[0] < xleft:
            xleft = coord[0]
            nameleft = stable_entries[coord].name
        if coord[1] > yup:
            yup = coord[1]
            nameup = stable_entries[coord].name

    if (not nameup in ordering) or (not nameright in ordering) or \
            (not nameleft in ordering):
        raise ValueError(
            'Error in ordering_phase_diagram : \n"{up}", "{left}" and "{'
            'right}"'
            ' should be in ordering : {ord}'.format(up=nameup, left=nameleft,
                                                    right=nameright,
                                                    ord=ordering))

    cc = np.array([0.5, np.sqrt(3.0) / 6.0], np.float)

    if nameup == ordering[0]:
        if nameleft == ordering[1]:
            # The coordinates were already in the user ordering
            return lines, stable_entries, unstable_entries
        else:
            newlines = [[np.array(1.0 - x), y] for x, y in lines]
            newstable_entries = {(1.0 - c[0], c[1]): entry
                                 for c, entry in stable_entries.items()}
            newunstable_entries = {entry: (1.0 - c[0], c[1])
                                   for entry, c in
                                   unstable_entries.items()}
            return newlines, newstable_entries, newunstable_entries
    elif nameup == ordering[1]:
        if nameleft == ordering[2]:
            c120 = np.cos(2.0 * np.pi / 3.0)
            s120 = np.sin(2.0 * np.pi / 3.0)
            newlines = []
            for x, y in lines:
                newx = np.zeros_like(x)
                newy = np.zeros_like(y)
                for ii, xx in enumerate(x):
                    newx[ii] = c120 * (xx - cc[0]) - s120 * (y[ii] - cc[1]) + \
                               cc[0]
                    newy[ii] = s120 * (xx - cc[0]) + c120 * (y[ii] - cc[1]) + \
                               cc[1]
                newlines.append([newx, newy])
            newstable_entries = {
                (c120 * (c[0] - cc[0]) - s120 * (c[1] - cc[1]) + cc[0],
                 s120 * (c[0] - cc[0]) + c120 * (c[1] - cc[1]) + cc[1]): entry
                for c, entry in stable_entries.items()}
            newunstable_entries = {
                entry: (c120 * (c[0] - cc[0]) - s120 * (c[1] - cc[1]) + cc[0],
                        s120 * (c[0] - cc[0]) + c120 * (c[1] - cc[1]) + cc[1])
                for entry, c in unstable_entries.items()}
            return newlines, newstable_entries, newunstable_entries
        else:
            c120 = np.cos(2.0 * np.pi / 3.0)
            s120 = np.sin(2.0 * np.pi / 3.0)
            newlines = []
            for x, y in lines:
                newx = np.zeros_like(x)
                newy = np.zeros_like(y)
                for ii, xx in enumerate(x):
                    newx[ii] = -c120 * (xx - 1.0) - s120 * y[ii] + 1.0
                    newy[ii] = -s120 * (xx - 1.0) + c120 * y[ii]
                newlines.append([newx, newy])
            newstable_entries = {(-c120 * (c[0] - 1.0) - s120 * c[1] + 1.0,
                                  -s120 * (c[0] - 1.0) + c120 * c[1]): entry
                                 for c, entry in stable_entries.items()}
            newunstable_entries = {
                entry: (-c120 * (c[0] - 1.0) - s120 * c[1] + 1.0,
                        -s120 * (c[0] - 1.0) + c120 * c[1])
                for entry, c in unstable_entries.items()}
            return newlines, newstable_entries, newunstable_entries
    elif nameup == ordering[2]:
        if nameleft == ordering[0]:
            c240 = np.cos(4.0 * np.pi / 3.0)
            s240 = np.sin(4.0 * np.pi / 3.0)
            newlines = []
            for x, y in lines:
                newx = np.zeros_like(x)
                newy = np.zeros_like(y)
                for ii, xx in enumerate(x):
                    newx[ii] = c240 * (xx - cc[0]) - s240 * (y[ii] - cc[1]) + \
                               cc[0]
                    newy[ii] = s240 * (xx - cc[0]) + c240 * (y[ii] - cc[1]) + \
                               cc[1]
                newlines.append([newx, newy])
            newstable_entries = {
                (c240 * (c[0] - cc[0]) - s240 * (c[1] - cc[1]) + cc[0],
                 s240 * (c[0] - cc[0]) + c240 * (c[1] - cc[1]) + cc[1]): entry
                for c, entry in stable_entries.items()}
            newunstable_entries = {
                entry: (c240 * (c[0] - cc[0]) - s240 * (c[1] - cc[1]) + cc[0],
                        s240 * (c[0] - cc[0]) + c240 * (c[1] - cc[1]) + cc[1])
                for entry, c in unstable_entries.items()}
            return newlines, newstable_entries, newunstable_entries
        else:
            c240 = np.cos(4.0 * np.pi / 3.0)
            s240 = np.sin(4.0 * np.pi / 3.0)
            newlines = []
            for x, y in lines:
                newx = np.zeros_like(x)
                newy = np.zeros_like(y)
                for ii, xx in enumerate(x):
                    newx[ii] = -c240 * xx - s240 * y[ii]
                    newy[ii] = -s240 * xx + c240 * y[ii]
                newlines.append([newx, newy])
            newstable_entries = {(-c240 * c[0] - s240 * c[1],
                                  -s240 * c[0] + c240 * c[1]): entry
                                 for c, entry in stable_entries.items()}
            newunstable_entries = {entry: (-c240 * c[0] - s240 * c[1],
                                           -s240 * c[0] + c240 * c[1])
                                   for entry, c in unstable_entries.items()}
            return newlines, newstable_entries, newunstable_entries


Instruction:
Here you have a function in Python 3, explain what it does
def pd_coords(self, comp):
        if set(comp.elements).difference(self.elements):
            raise ValueError('{} has elements not in the phase diagram {}'
                             ''.format(comp, self.elements))
        return np.array(
            [comp.get_atomic_fraction(el) for el in self.elements[1:]])

Response: Returns the coordinates of the phase diagram in that space.


Instruction:
How would you implement a function in Python 3 that
returns the formation energy for an entry from the elemental references.

Response:
def get_form_energy(self, entry):
        """
        Returns the formation energy for an entry (NOT normalized) from the
        elemental references.

        Args:
            entry: A PDEntry-like object.

        Returns:
            Formation energy from the elemental references.
        """
        c = entry.composition
        return entry.energy - sum([c[el] * self.el_refs[el].energy_per_atom
                                   for el in c.elements])


Instruction:
Write a Python 3 function that can
get any facet that a composition falls into.

Response:
def _get_facet_and_simplex(self, comp):
        """
        Get any facet that a composition falls into. Cached so successive
        calls at same composition are fast.
        """
        c = self.pd_coords(comp)
        for f, s in zip(self.facets, self.simplexes):
            if s.in_simplex(c, PhaseDiagram.numerical_tol / 10):
                return f, s
        raise RuntimeError("No facet found for comp = {}".format(comp))


Instruction:
Can you generate a brief explanation for the following Python 3 code
def _get_facet_chempots(self, facet):
        complist = [self.qhull_entries[i].composition for i in facet]
        energylist = [self.qhull_entries[i].energy_per_atom for i in facet]
        m = [[c.get_atomic_fraction(e) for e in self.elements] for c in
             complist]
        chempots = np.linalg.solve(m, energylist)
        return dict(zip(self.elements, chempots))

Response: Calculates the chemical potentials for each element within a facet.


Instruction:
How would you explain what the following Python 3 function does
def get_decomposition(self, comp):
        facet, simplex = self._get_facet_and_simplex(comp)
        decomp_amts = simplex.bary_coords(self.pd_coords(comp))
        return {self.qhull_entries[f]: amt
                for f, amt in zip(facet, decomp_amts)
                if abs(amt) > PhaseDiagram.numerical_tol}

Response: Provides the decomposition at a particular composition.


Instruction:
Create a Python 3 function to
return the lowest energy of the given composition.

Response:
def get_hull_energy(self, comp):
        """
        Args:
            comp (Composition): Input composition

        Returns:
            Energy of lowest energy equilibrium at desired composition. Not
            normalized by atoms, i.e. E(Li4O2) = 2 * E(Li2O)
        """
        e = 0
        for k, v in self.get_decomposition(comp).items():
            e += k.energy_per_atom * v
        return e * comp.num_atoms


Instruction:
Here you have a function in Python 3, explain what it does
def get_decomp_and_e_above_hull(self, entry, allow_negative=False):
        if entry in self.stable_entries:
            return {entry: 1}, 0

        comp = entry.composition
        facet, simplex = self._get_facet_and_simplex(comp)
        decomp_amts = simplex.bary_coords(self.pd_coords(comp))
        decomp = {self.qhull_entries[f]: amt
                  for f, amt in zip(facet, decomp_amts)
                  if abs(amt) > PhaseDiagram.numerical_tol}
        energies = [self.qhull_entries[i].energy_per_atom for i in facet]
        ehull = entry.energy_per_atom - np.dot(decomp_amts, energies)
        if allow_negative or ehull >= -PhaseDiagram.numerical_tol:
            return decomp, ehull
        raise ValueError("No valid decomp found!")

Response: Provides the decomposition and energy above convex hull for an entry.


Instruction:
Can you tell what is the following Python 3 function doing
def get_equilibrium_reaction_energy(self, entry):
        if entry not in self.stable_entries:
            raise ValueError("Equilibrium reaction energy is available only "
                             "for stable entries.")
        if entry.is_element:
            return 0
        entries = [e for e in self.stable_entries if e != entry]
        modpd = PhaseDiagram(entries, self.elements)
        return modpd.get_decomp_and_e_above_hull(entry,
                                                 allow_negative=True)[1]

Response: Returns the reaction energy of a stable entry.


Instruction:
Can you tell what is the following Python 3 function doing
def get_transition_chempots(self, element):
        if element not in self.elements:
            raise ValueError("get_transition_chempots can only be called with "
                             "elements in the phase diagram.")

        critical_chempots = []
        for facet in self.facets:
            chempots = self._get_facet_chempots(facet)
            critical_chempots.append(chempots[element])

        clean_pots = []
        for c in sorted(critical_chempots):
            if len(clean_pots) == 0:
                clean_pots.append(c)
            else:
                if abs(c - clean_pots[-1]) > PhaseDiagram.numerical_tol:
                    clean_pots.append(c)
        clean_pots.reverse()
        return tuple(clean_pots)

Response: Returns a sorted sequence of critical chemical potentials for an element in the PhaseDiagram.


Instruction:
Create a Python 3 function to
get the critical compositions along the tieline between two compositions.

Response:
def get_critical_compositions(self, comp1, comp2):
        """
        Get the critical compositions along the tieline between two
        compositions. I.e. where the decomposition products change.
        The endpoints are also returned.
        Args:
            comp1, comp2 (Composition): compositions that define the tieline
        Returns:
            [(Composition)]: list of critical compositions. All are of
                the form x * comp1 + (1-x) * comp2
        """

        n1 = comp1.num_atoms
        n2 = comp2.num_atoms
        pd_els = self.elements

        # the reduced dimensionality Simplexes don't use the
        # first element in the PD
        c1 = self.pd_coords(comp1)
        c2 = self.pd_coords(comp2)

        # none of the projections work if c1 == c2, so just return *copies*
        # of the inputs
        if np.all(c1 == c2):
            return [comp1.copy(), comp2.copy()]

        intersections = [c1, c2]
        for sc in self.simplexes:
            intersections.extend(sc.line_intersection(c1, c2))
        intersections = np.array(intersections)

        # find position along line
        l = (c2 - c1)
        l /= np.sum(l ** 2) ** 0.5
        proj = np.dot(intersections - c1, l)

        # only take compositions between endpoints
        proj = proj[np.logical_and(proj > -self.numerical_tol,
                                   proj < proj[1] + self.numerical_tol)]
        proj.sort()

        # only unique compositions
        valid = np.ones(len(proj), dtype=np.bool)
        valid[1:] = proj[1:] > proj[:-1] + self.numerical_tol
        proj = proj[valid]

        ints = c1 + l * proj[:, None]
        # reconstruct full-dimensional composition array
        cs = np.concatenate([np.array([1 - np.sum(ints, axis=-1)]).T,
                             ints], axis=-1)
        # mixing fraction when compositions are normalized
        x = proj / np.dot(c2 - c1, l)
        # mixing fraction when compositions are not normalized
        x_unnormalized = x * n1 / (n2 + x * (n1 - n2))
        num_atoms = n1 + (n2 - n1) * x_unnormalized
        cs *= num_atoms[:, None]
        return [Composition((c, v) for c, v in zip(pd_els, m)) for m in cs]


Instruction:
Make a summary of the following Python 3 code
def get_element_profile(self, element, comp, comp_tol=1e-5):
        element = get_el_sp(element)
        element = Element(element.symbol)

        if element not in self.elements:
            raise ValueError("get_transition_chempots can only be called with"
                             " elements in the phase diagram.")
        gccomp = Composition({el: amt for el, amt in comp.items()
                              if el != element})
        elref = self.el_refs[element]
        elcomp = Composition(element.symbol)
        evolution = []

        for cc in self.get_critical_compositions(elcomp, gccomp)[1:]:
            decomp_entries = self.get_decomposition(cc).keys()
            decomp = [k.composition for k in decomp_entries]
            rxn = Reaction([comp], decomp + [elcomp])
            rxn.normalize_to(comp)
            c = self.get_composition_chempots(cc + elcomp * 1e-5)[element]
            amt = -rxn.coeffs[rxn.all_comp.index(elcomp)]
            evolution.append({'chempot': c,
                              'evolution': amt,
                              'element_reference': elref,
                              'reaction': rxn, 'entries': decomp_entries})
        return evolution

Response: Returns the evolution data for an element in the phase diagram.


Instruction:
Make a summary of the following Python 3 code
def get_chempot_range_map(self, elements, referenced=True, joggle=True):
        all_chempots = []
        pd = self
        facets = pd.facets
        for facet in facets:
            chempots = self._get_facet_chempots(facet)
            all_chempots.append([chempots[el] for el in pd.elements])
        inds = [pd.elements.index(el) for el in elements]
        el_energies = {el: 0.0 for el in elements}
        if referenced:
            el_energies = {el: pd.el_refs[el].energy_per_atom
                           for el in elements}
        chempot_ranges = collections.defaultdict(list)
        vertices = [list(range(len(self.elements)))]
        if len(all_chempots) > len(self.elements):
            vertices = get_facets(all_chempots, joggle=joggle)
        for ufacet in vertices:
            for combi in itertools.combinations(ufacet, 2):
                data1 = facets[combi[0]]
                data2 = facets[combi[1]]
                common_ent_ind = set(data1).intersection(set(data2))
                if len(common_ent_ind) == len(elements):
                    common_entries = [pd.qhull_entries[i]
                                      for i in common_ent_ind]
                    data = np.array([[all_chempots[i][j]
                                      - el_energies[pd.elements[j]]
                                      for j in inds] for i in combi])
                    sim = Simplex(data)
                    for entry in common_entries:
                        chempot_ranges[entry].append(sim)

        return chempot_ranges

Response: Returns a chemical potential range map for each stable entry.


Instruction:
Can you generate the documentation for the following Python 3 function
def getmu_vertices_stability_phase(self, target_comp, dep_elt, tol_en=1e-2):
        muref = np.array([self.el_refs[e].energy_per_atom
                          for e in self.elements if e != dep_elt])
        chempot_ranges = self.get_chempot_range_map(
            [e for e in self.elements if e != dep_elt])

        for e in self.elements:
            if not e in target_comp.elements:
                target_comp = target_comp + Composition({e: 0.0})
        coeff = [-target_comp[e] for e in self.elements if e != dep_elt]
        for e in chempot_ranges.keys():
            if e.composition.reduced_composition == \
                    target_comp.reduced_composition:
                multiplicator = e.composition[dep_elt] / target_comp[dep_elt]
                ef = e.energy / multiplicator
                all_coords = []
                for s in chempot_ranges[e]:
                    for v in s._coords:
                        elts = [e for e in self.elements if e != dep_elt]
                        res = {}
                        for i in range(len(elts)):
                            res[elts[i]] = v[i] + muref[i]
                        res[dep_elt] = (np.dot(v + muref, coeff) + ef) / \
                                       target_comp[dep_elt]
                        already_in = False
                        for di in all_coords:
                            dict_equals = True
                            for k in di:
                                if abs(di[k] - res[k]) > tol_en:
                                    dict_equals = False
                                    break
                            if dict_equals:
                                already_in = True
                                break
                        if not already_in:
                            all_coords.append(res)
        return all_coords

Response: This method returns the set of chemical potentials corresponding to the vertices of the simplex in the chemical potential phase diagram.


Instruction:
Write a Python 3 function for
returning a set of chemical potentials corresponding to the max and min chemical potential of the open element for a given composition. It is quite common to have for instance a ternary oxide (e.g., ABO3) for which you want to know what are the A and B chemical potential leading to the highest and lowest oxygen chemical potential (reducing and oxidizing conditions). This is useful for defect computations. Args: target_comp: A Composition object open_elt: Element that you want to constrain to be max or min Returns: {Element:(mu_min,mu_max)}: Chemical potentials are given in "absolute" values (i.e., not referenced to 0)

Response:
def get_chempot_range_stability_phase(self, target_comp, open_elt):
        """
        returns a set of chemical potentials corresponding to the max and min
        chemical potential of the open element for a given composition. It is
        quite common to have for instance a ternary oxide (e.g., ABO3) for
        which you want to know what are the A and B chemical potential leading
        to the highest and lowest oxygen chemical potential (reducing and
        oxidizing conditions). This is useful for defect computations.

        Args:
            target_comp: A Composition object
            open_elt: Element that you want to constrain to be max or min

        Returns:
             {Element:(mu_min,mu_max)}: Chemical potentials are given in
             "absolute" values (i.e., not referenced to 0)
        """
        muref = np.array([self.el_refs[e].energy_per_atom
                          for e in self.elements if e != open_elt])
        chempot_ranges = self.get_chempot_range_map(
            [e for e in self.elements if e != open_elt])
        for e in self.elements:
            if not e in target_comp.elements:
                target_comp = target_comp + Composition({e: 0.0})
        coeff = [-target_comp[e] for e in self.elements if e != open_elt]
        max_open = -float('inf')
        min_open = float('inf')
        max_mus = None
        min_mus = None
        for e in chempot_ranges.keys():
            if e.composition.reduced_composition == \
                    target_comp.reduced_composition:
                multiplicator = e.composition[open_elt] / target_comp[open_elt]
                ef = e.energy / multiplicator
                all_coords = []
                for s in chempot_ranges[e]:
                    for v in s._coords:
                        all_coords.append(v)
                        if (np.dot(v + muref, coeff) + ef) / target_comp[
                                open_elt] > max_open:
                            max_open = (np.dot(v + muref, coeff) + ef) / \
                                       target_comp[open_elt]
                            max_mus = v
                        if (np.dot(v + muref, coeff) + ef) / target_comp[
                                open_elt] < min_open:
                            min_open = (np.dot(v + muref, coeff) + ef) / \
                                       target_comp[open_elt]
                            min_mus = v
        elts = [e for e in self.elements if e != open_elt]
        res = {}
        for i in range(len(elts)):
            res[elts[i]] = (min_mus[i] + muref[i], max_mus[i] + muref[i])
        res[open_elt] = (min_open, max_open)
        return res


Instruction:
Write a Python 3 function for
returning the CompoundPhaseDiagram object which can then be used for plotting.

Response:
def get_compound_pd(self):
        """
        Get the CompoundPhaseDiagram object, which can then be used for
        plotting.

        Returns:
            (CompoundPhaseDiagram)
        """
        # For this plot, since the reactions are reported in formation
        # energies, we need to set the energies of the terminal compositions
        # to 0. So we make create copies with 0 energy.
        entry1 = PDEntry(self.entry1.composition, 0)
        entry2 = PDEntry(self.entry2.composition, 0)

        cpd = CompoundPhaseDiagram(
            self.rxn_entries + [entry1, entry2],
            [Composition(entry1.composition.reduced_formula),
             Composition(entry2.composition.reduced_formula)],
            normalize_terminal_compositions=False)
        return cpd


Instruction:
Can you implement a function in Python 3 that
plots data for the current phase diagram.

Response:
def pd_plot_data(self):
        """
        Plot data for phase diagram.
        2-comp - Full hull with energies
        3/4-comp - Projection into 2D or 3D Gibbs triangle.

        Returns:
            (lines, stable_entries, unstable_entries):
            - lines is a list of list of coordinates for lines in the PD.
            - stable_entries is a {coordinate : entry} for each stable node
            in the phase diagram. (Each coordinate can only have one
            stable phase)
            - unstable_entries is a {entry: coordinates} for all unstable
            nodes in the phase diagram.
        """
        pd = self._pd
        entries = pd.qhull_entries
        data = np.array(pd.qhull_data)
        lines = []
        stable_entries = {}
        for line in self.lines:
            entry1 = entries[line[0]]
            entry2 = entries[line[1]]
            if self._dim < 3:
                x = [data[line[0]][0], data[line[1]][0]]
                y = [pd.get_form_energy_per_atom(entry1),
                     pd.get_form_energy_per_atom(entry2)]
                coord = [x, y]
            elif self._dim == 3:
                coord = triangular_coord(data[line, 0:2])
            else:
                coord = tet_coord(data[line, 0:3])
            lines.append(coord)
            labelcoord = list(zip(*coord))
            stable_entries[labelcoord[0]] = entry1
            stable_entries[labelcoord[1]] = entry2

        all_entries = pd.all_entries
        all_data = np.array(pd.all_entries_hulldata)
        unstable_entries = dict()
        stable = pd.stable_entries
        for i in range(0, len(all_entries)):
            entry = all_entries[i]
            if entry not in stable:
                if self._dim < 3:
                    x = [all_data[i][0], all_data[i][0]]
                    y = [pd.get_form_energy_per_atom(entry),
                         pd.get_form_energy_per_atom(entry)]
                    coord = [x, y]
                elif self._dim == 3:
                    coord = triangular_coord([all_data[i, 0:2],
                                              all_data[i, 0:2]])
                else:
                    coord = tet_coord([all_data[i, 0:3], all_data[i, 0:3],
                                       all_data[i, 0:3]])
                labelcoord = list(zip(*coord))
                unstable_entries[entry] = labelcoord[0]

        return lines, stable_entries, unstable_entries


Instruction:
Create a Python 3 function to
draw the element profile evolution by varying the chemical potential of an element.

Response:
def plot_element_profile(self, element, comp, show_label_index=None,
                             xlim=5):
        """
        Draw the element profile plot for a composition varying different
        chemical potential of an element.
        X value is the negative value of the chemical potential reference to
        elemental chemical potential. For example, if choose Element("Li"),
        X= -(µLi-µLi0), which corresponds to the voltage versus metal anode.
        Y values represent for the number of element uptake in this composition
        (unit: per atom). All reactions are printed to help choosing the
        profile steps you want to show label in the plot.

        Args:
         element (Element): An element of which the chemical potential is
            considered. It also must be in the phase diagram.
         comp (Composition): A composition.
         show_label_index (list of integers): The labels for reaction products
            you want to show in the plot. Default to None (not showing any
            annotation for reaction products). For the profile steps you want
            to show the labels, just add it to the show_label_index. The
            profile step counts from zero. For example, you can set
            show_label_index=[0, 2, 5] to label profile step 0,2,5.
         xlim (float): The max x value. x value is from 0 to xlim. Default to
            5 eV.

        Returns:
            Plot of element profile evolution by varying the chemical potential
            of an element.
        """
        plt = pretty_plot(12, 8)
        pd = self._pd
        evolution = pd.get_element_profile(element, comp)
        num_atoms = evolution[0]["reaction"].reactants[0].num_atoms
        element_energy = evolution[0]['chempot']
        for i, d in enumerate(evolution):
            v = -(d["chempot"] - element_energy)
            print ("index= %s, -\u0394\u03BC=%.4f(eV)," % (i, v), d["reaction"])
            if i != 0:
                plt.plot([x2, x2], [y1, d["evolution"] / num_atoms],
                         'k', linewidth=2.5)
            x1 = v
            y1 = d["evolution"] / num_atoms

            if i != len(evolution) - 1:
                x2 = - (evolution[i + 1]["chempot"] - element_energy)
            else:
                x2 = 5.0
            if show_label_index is not None and i in show_label_index:
                products = [re.sub(r"(\d+)", r"$_{\1}$", p.reduced_formula)
                            for p in d["reaction"].products
                            if p.reduced_formula != element.symbol]
                plt.annotate(", ".join(products), xy=(v + 0.05, y1 + 0.05),
                             fontsize=24, color='r')
                plt.plot([x1, x2], [y1, y1], 'r', linewidth=3)
            else:
                plt.plot([x1, x2], [y1, y1], 'k', linewidth=2.5)

        plt.xlim((0, xlim))
        plt.xlabel("-$\\Delta{\\mu}$ (eV)")
        plt.ylabel("Uptake per atom")

        return plt


Instruction:
Write a Python 3 function that can
return a 2D plot of the data of the ICSD and MP.

Response:
def _get_2d_plot(self, label_stable=True, label_unstable=True,
                     ordering=None, energy_colormap=None, vmin_mev=-60.0,
                     vmax_mev=60.0, show_colorbar=True,
                     process_attributes=False, plt=None):
        """
        Shows the plot using pylab.  Usually I won't do imports in methods,
        but since plotting is a fairly expensive library to load and not all
        machines have matplotlib installed, I have done it this way.
        """
        if plt is None:
            plt = pretty_plot(8, 6)
        from matplotlib.font_manager import FontProperties
        if ordering is None:
            (lines, labels, unstable) = self.pd_plot_data
        else:
            (_lines, _labels, _unstable) = self.pd_plot_data
            (lines, labels, unstable) = order_phase_diagram(
                _lines, _labels, _unstable, ordering)
        if energy_colormap is None:
            if process_attributes:
                for x, y in lines:
                    plt.plot(x, y, "k-", linewidth=3, markeredgecolor="k")
                # One should think about a clever way to have "complex"
                # attributes with complex processing options but with a clear
                # logic. At this moment, I just use the attributes to know
                # whether an entry is a new compound or an existing (from the
                #  ICSD or from the MP) one.
                for x, y in labels.keys():
                    if labels[(x, y)].attribute is None or \
                                    labels[(x, y)].attribute == "existing":
                        plt.plot(x, y, "ko", **self.plotkwargs)
                    else:
                        plt.plot(x, y, "k*", **self.plotkwargs)
            else:
                for x, y in lines:
                    plt.plot(x, y, "ko-", **self.plotkwargs)
        else:
            from matplotlib.colors import Normalize, LinearSegmentedColormap
            from matplotlib.cm import ScalarMappable
            for x, y in lines:
                plt.plot(x, y, "k-", markeredgecolor="k")
            vmin = vmin_mev / 1000.0
            vmax = vmax_mev / 1000.0
            if energy_colormap == 'default':
                mid = - vmin / (vmax - vmin)
                cmap = LinearSegmentedColormap.from_list(
                    'my_colormap', [(0.0, '#005500'), (mid, '#55FF55'),
                                    (mid, '#FFAAAA'), (1.0, '#FF0000')])
            else:
                cmap = energy_colormap
            norm = Normalize(vmin=vmin, vmax=vmax)
            _map = ScalarMappable(norm=norm, cmap=cmap)
            _energies = [self._pd.get_equilibrium_reaction_energy(entry)
                         for coord, entry in labels.items()]
            energies = [en if en < 0.0 else -0.00000001 for en in _energies]
            vals_stable = _map.to_rgba(energies)
            ii = 0
            if process_attributes:
                for x, y in labels.keys():
                    if labels[(x, y)].attribute is None or \
                                    labels[(x, y)].attribute == "existing":
                        plt.plot(x, y, "o", markerfacecolor=vals_stable[ii],
                                 markersize=12)
                    else:
                        plt.plot(x, y, "*", markerfacecolor=vals_stable[ii],
                                 markersize=18)
                    ii += 1
            else:
                for x, y in labels.keys():
                    plt.plot(x, y, "o", markerfacecolor=vals_stable[ii],
                             markersize=15)
                    ii += 1

        font = FontProperties()
        font.set_weight("bold")
        font.set_size(24)

        # Sets a nice layout depending on the type of PD. Also defines a
        # "center" for the PD, which then allows the annotations to be spread
        # out in a nice manner.
        if len(self._pd.elements) == 3:
            plt.axis("equal")
            plt.xlim((-0.1, 1.2))
            plt.ylim((-0.1, 1.0))
            plt.axis("off")
            center = (0.5, math.sqrt(3) / 6)
        else:
            all_coords = labels.keys()
            miny = min([c[1] for c in all_coords])
            ybuffer = max(abs(miny) * 0.1, 0.1)
            plt.xlim((-0.1, 1.1))
            plt.ylim((miny - ybuffer, ybuffer))
            center = (0.5, miny / 2)
            plt.xlabel("Fraction", fontsize=28, fontweight='bold')
            plt.ylabel("Formation energy (eV/fu)", fontsize=28,
                       fontweight='bold')

        for coords in sorted(labels.keys(), key=lambda x: -x[1]):
            entry = labels[coords]
            label = entry.name

            # The follow defines an offset for the annotation text emanating
            # from the center of the PD. Results in fairly nice layouts for the
            # most part.
            vec = (np.array(coords) - center)
            vec = vec / np.linalg.norm(vec) * 10 if np.linalg.norm(vec) != 0 \
                else vec
            valign = "bottom" if vec[1] > 0 else "top"
            if vec[0] < -0.01:
                halign = "right"
            elif vec[0] > 0.01:
                halign = "left"
            else:
                halign = "center"
            if label_stable:
                if process_attributes and entry.attribute == 'new':
                    plt.annotate(latexify(label), coords, xytext=vec,
                                 textcoords="offset points",
                                 horizontalalignment=halign,
                                 verticalalignment=valign,
                                 fontproperties=font,
                                 color='g')
                else:
                    plt.annotate(latexify(label), coords, xytext=vec,
                                 textcoords="offset points",
                                 horizontalalignment=halign,
                                 verticalalignment=valign,
                                 fontproperties=font)

        if self.show_unstable:
            font = FontProperties()
            font.set_size(16)
            energies_unstable = [self._pd.get_e_above_hull(entry)
                                 for entry, coord in unstable.items()]
            if energy_colormap is not None:
                energies.extend(energies_unstable)
                vals_unstable = _map.to_rgba(energies_unstable)
            ii = 0
            for entry, coords in unstable.items():
                ehull = self._pd.get_e_above_hull(entry)
                if ehull < self.show_unstable:
                    vec = (np.array(coords) - center)
                    vec = vec / np.linalg.norm(vec) * 10 \
                        if np.linalg.norm(vec) != 0 else vec
                    label = entry.name
                    if energy_colormap is None:
                        plt.plot(coords[0], coords[1], "ks", linewidth=3,
                                 markeredgecolor="k", markerfacecolor="r",
                                 markersize=8)
                    else:
                        plt.plot(coords[0], coords[1], "s", linewidth=3,
                                 markeredgecolor="k",
                                 markerfacecolor=vals_unstable[ii],
                                 markersize=8)
                    if label_unstable:
                        plt.annotate(latexify(label), coords, xytext=vec,
                                     textcoords="offset points",
                                     horizontalalignment=halign, color="b",
                                     verticalalignment=valign,
                                     fontproperties=font)
                    ii += 1
        if energy_colormap is not None and show_colorbar:
            _map.set_array(energies)
            cbar = plt.colorbar(_map)
            cbar.set_label(
                'Energy [meV/at] above hull (in red)\nInverse energy ['
                'meV/at] above hull (in green)',
                rotation=-90, ha='left', va='center')
            ticks = cbar.ax.get_yticklabels()
            # cbar.ax.set_yticklabels(['${v}$'.format(
            #     v=float(t.get_text().strip('$'))*1000.0) for t in ticks])
        f = plt.gcf()
        f.set_size_inches((8, 6))
        plt.subplots_adjust(left=0.09, right=0.98, top=0.98, bottom=0.07)
        return plt


Instruction:
Write a Python 3 script to
write the phase diagram to an image in a stream.

Response:
def write_image(self, stream, image_format="svg", **kwargs):
        """
        Writes the phase diagram to an image in a stream.

        Args:
            stream:
                stream to write to. Can be a file stream or a StringIO stream.
            image_format
                format for image. Can be any of matplotlib supported formats.
                Defaults to svg for best results for vector graphics.
            \\*\\*kwargs: Pass through to get_plot functino.
        """
        plt = self.get_plot(**kwargs)

        f = plt.gcf()
        f.set_size_inches((12, 10))

        plt.savefig(stream, format=image_format)


Instruction:
Make a summary of the following Python 3 code
def plot_chempot_range_map(self, elements, referenced=True):
        self.get_chempot_range_map_plot(elements, referenced=referenced).show()

Response: This function plots the chemical potential range map for the given set of elements.


Instruction:
Given the following Python 3 function, write the documentation
def get_chempot_range_map_plot(self, elements, referenced=True):

        plt = pretty_plot(12, 8)
        chempot_ranges = self._pd.get_chempot_range_map(
            elements, referenced=referenced)
        missing_lines = {}
        excluded_region = []
        for entry, lines in chempot_ranges.items():
            comp = entry.composition
            center_x = 0
            center_y = 0
            coords = []
            contain_zero = any([comp.get_atomic_fraction(el) == 0
                                for el in elements])
            is_boundary = (not contain_zero) and \
                          sum([comp.get_atomic_fraction(el) for el in
                               elements]) == 1
            for line in lines:
                (x, y) = line.coords.transpose()
                plt.plot(x, y, "k-")

                for coord in line.coords:
                    if not in_coord_list(coords, coord):
                        coords.append(coord.tolist())
                        center_x += coord[0]
                        center_y += coord[1]
                if is_boundary:
                    excluded_region.extend(line.coords)

            if coords and contain_zero:
                missing_lines[entry] = coords
            else:
                xy = (center_x / len(coords), center_y / len(coords))
                plt.annotate(latexify(entry.name), xy, fontsize=22)

        ax = plt.gca()
        xlim = ax.get_xlim()
        ylim = ax.get_ylim()

        # Shade the forbidden chemical potential regions.
        excluded_region.append([xlim[1], ylim[1]])
        excluded_region = sorted(excluded_region, key=lambda c: c[0])
        (x, y) = np.transpose(excluded_region)
        plt.fill(x, y, "0.80")

        # The hull does not generate the missing horizontal and vertical lines.
        # The following code fixes this.
        el0 = elements[0]
        el1 = elements[1]
        for entry, coords in missing_lines.items():
            center_x = sum([c[0] for c in coords])
            center_y = sum([c[1] for c in coords])
            comp = entry.composition
            is_x = comp.get_atomic_fraction(el0) < 0.01
            is_y = comp.get_atomic_fraction(el1) < 0.01
            n = len(coords)
            if not (is_x and is_y):
                if is_x:
                    coords = sorted(coords, key=lambda c: c[1])
                    for i in [0, -1]:
                        x = [min(xlim), coords[i][0]]
                        y = [coords[i][1], coords[i][1]]
                        plt.plot(x, y, "k")
                        center_x += min(xlim)
                        center_y += coords[i][1]
                elif is_y:
                    coords = sorted(coords, key=lambda c: c[0])
                    for i in [0, -1]:
                        x = [coords[i][0], coords[i][0]]
                        y = [coords[i][1], min(ylim)]
                        plt.plot(x, y, "k")
                        center_x += coords[i][0]
                        center_y += min(ylim)
                xy = (center_x / (n + 2), center_y / (n + 2))
            else:
                center_x = sum(coord[0] for coord in coords) + xlim[0]
                center_y = sum(coord[1] for coord in coords) + ylim[0]
                xy = (center_x / (n + 1), center_y / (n + 1))

            plt.annotate(latexify(entry.name), xy,
                         horizontalalignment="center",
                         verticalalignment="center", fontsize=22)

        plt.xlabel("$\\mu_{{{0}}} - \\mu_{{{0}}}^0$ (eV)"
                   .format(el0.symbol))
        plt.ylabel("$\\mu_{{{0}}} - \\mu_{{{0}}}^0$ (eV)"
                   .format(el1.symbol))
        plt.tight_layout()
        return plt

Response: Returns a matplotlib plot of the chemical potential range map for the given set of elements.


Instruction:
Write a Python 3 script for
returning a contour phase diagram plot.

Response:
def get_contour_pd_plot(self):
        """
        Plot a contour phase diagram plot, where phase triangles are colored
        according to degree of instability by interpolation. Currently only
        works for 3-component phase diagrams.

        Returns:
            A matplotlib plot object.
        """
        from scipy import interpolate
        from matplotlib import cm

        pd = self._pd
        entries = pd.qhull_entries
        data = np.array(pd.qhull_data)

        plt = self._get_2d_plot()
        data[:, 0:2] = triangular_coord(data[:, 0:2]).transpose()
        for i, e in enumerate(entries):
            data[i, 2] = self._pd.get_e_above_hull(e)

        gridsize = 0.005
        xnew = np.arange(0, 1., gridsize)
        ynew = np.arange(0, 1, gridsize)

        f = interpolate.LinearNDInterpolator(data[:, 0:2], data[:, 2])
        znew = np.zeros((len(ynew), len(xnew)))
        for (i, xval) in enumerate(xnew):
            for (j, yval) in enumerate(ynew):
                znew[j, i] = f(xval, yval)

        plt.contourf(xnew, ynew, znew, 1000, cmap=cm.autumn_r)

        plt.colorbar()
        return plt


Instruction:
Can you tell what is the following Python 3 function doing
def all_input(self):
        d = {"HEADER": self.header(), "PARAMETERS": self.tags}

        if "RECIPROCAL" not in self.tags:
            d.update({"POTENTIALS": self.potential, "ATOMS": self.atoms})

        return d

Response: Returns all input files as a dict of filename - > feffio object


Instruction:
How would you explain what the following Python 3 function does
def write_input(self, output_dir=".", make_dir_if_not_present=True):
        if make_dir_if_not_present and not os.path.exists(output_dir):
            os.makedirs(output_dir)

        feff = self.all_input()

        feff_input = "\n\n".join(str(feff[k]) for k in
                                 ["HEADER", "PARAMETERS", "POTENTIALS", "ATOMS"]
                                 if k in feff)

        for k, v in feff.items():
            with open(os.path.join(output_dir, k), "w") as f:
                f.write(str(v))

        with open(os.path.join(output_dir, "feff.inp"), "w") as f:
            f.write(feff_input)

        # write the structure to cif file
        if "ATOMS" not in feff:
            self.atoms.struct.to(fmt="cif",
                                 filename=os.path.join(
                                     output_dir, feff["PARAMETERS"]["CIF"]))

Response: Writes a set of FEFF input files to a directory.


Instruction:
Here you have a function in Python 3, explain what it does
def tags(self):
        if "RECIPROCAL" in self.config_dict:
            if self.small_system:
                self.config_dict["CIF"] = "{}.cif".format(
                    self.structure.formula.replace(" ", ""))
                self.config_dict["TARGET"] = self.atoms.center_index + 1
                self.config_dict["COREHOLE"] = "RPA"
                logger.warning("Setting COREHOLE = RPA for K-space calculation")
                if not self.config_dict.get("KMESH", None):
                    abc = self.structure.lattice.abc
                    mult = (self.nkpts * abc[0] * abc[1] * abc[2]) ** (1 / 3)
                    self.config_dict["KMESH"] = [int(round(mult / l)) for l in abc]
            else:
                logger.warning("Large system(>=14 atoms) or EXAFS calculation, \
                                removing K-space settings")
                del self.config_dict["RECIPROCAL"]
                self.config_dict.pop("CIF", None)
                self.config_dict.pop("TARGET", None)
                self.config_dict.pop("KMESH", None)
                self.config_dict.pop("STRFAC", None)

        return Tags(self.config_dict)

Response: Returns a new instance of Tags class.


Instruction:
Given the following Python 3 function, write the documentation
def from_directory(input_dir):
        sub_d = {}
        for fname, ftype in [("HEADER", Header), ("PARAMETERS", Tags)]:
            fullzpath = zpath(os.path.join(input_dir, fname))
            sub_d[fname.lower()] = ftype.from_file(fullzpath)

        # Generation of FEFFDict set requires absorbing atom, need to search
        # the index of absorption atom in the structure according to the
        # distance matrix and shell species information contained in feff.inp

        absorber_index = []
        radius = None
        feffinp = zpath(os.path.join(input_dir, 'feff.inp'))

        if "RECIPROCAL" not in sub_d["parameters"]:
            input_atoms = Atoms.cluster_from_file(feffinp)
            shell_species = np.array([x.species_string for x in input_atoms])

            # First row of distance matrix represents the distance from the absorber to
            # the rest atoms
            distance_matrix = input_atoms.distance_matrix[0, :]

            # Get radius value
            from math import ceil
            radius = int(ceil(input_atoms.get_distance(input_atoms.index(input_atoms[0]),
                                                       input_atoms.index(input_atoms[-1]))))

            for site_index, site in enumerate(sub_d['header'].struct):

                if site.specie == input_atoms[0].specie:
                    site_atoms = Atoms(sub_d['header'].struct, absorbing_atom=site_index,
                                       radius=radius)
                    site_distance = np.array(site_atoms.get_lines())[:, 5].astype(np.float64)
                    site_shell_species = np.array(site_atoms.get_lines())[:, 4]
                    shell_overlap = min(shell_species.shape[0], site_shell_species.shape[0])

                    if np.allclose(distance_matrix[:shell_overlap], site_distance[:shell_overlap]) and \
                            np.all(site_shell_species[:shell_overlap] == shell_species[:shell_overlap]):
                        absorber_index.append(site_index)

        if "RECIPROCAL" in sub_d["parameters"]:
            absorber_index = sub_d["parameters"]["TARGET"]
            absorber_index[0] = int(absorber_index[0]) - 1

        # Generate the input set
        if 'XANES' in sub_d["parameters"]:
            CONFIG = loadfn(os.path.join(MODULE_DIR, "MPXANESSet.yaml"))
            if radius is None:
                radius = 10
            return FEFFDictSet(absorber_index[0], sub_d['header'].struct, radius=radius,
                               config_dict=CONFIG, edge=sub_d["parameters"]["EDGE"],
                               nkpts=1000, user_tag_settings=sub_d["parameters"])

Response:
Read in a set of FEFF input files from a directory which is
            is used when existing FEFF input needs some adjustment.


Instruction:
Make a summary of the following Python 3 code
def function_comparison(f1, f2, x1, x2, numpoints_check=500):
    xx = np.linspace(x1, x2, num=numpoints_check)
    y1 = f1(xx)
    y2 = f2(xx)
    if np.all(y1 < y2):
        return '<'
    elif np.all(y1 > y2):
        return '>'
    elif np.all(y1 == y2):
        return '='
    elif np.all(y1 <= y2):
        return '<='
    elif np.all(y1 >= y2):
        return '>='
    else:
        raise RuntimeError('Error in comparing functions f1 and f2 ...')

Response: Method that compares two functions returning a string that is the result of the function f1 and f2.


Instruction:
Make a summary of the following Python 3 code
def rectangle_surface_intersection(rectangle, f_lower, f_upper,
                                   bounds_lower=None, bounds_upper=None,
                                   check=True, numpoints_check=500):
    """
    Method to calculate the surface of the intersection of a rectangle (aligned with axes) and another surface
    defined by two functions f_lower and f_upper.

    Args:
        rectangle:
            Rectangle defined as : ((x1, x2), (y1, y2)).
        f_lower:
            Function defining the lower bound of the surface.
        f_upper:
            Function defining the upper bound of the surface.
        bounds_lower:
            Interval in which the f_lower function is defined.
        bounds_upper:
            Interval in which the f_upper function is defined.
        check:
            Whether to check if f_lower is always lower than f_upper.
        numpoints_check:
            Number of points used to check whether f_lower is always lower than f_upper

    Returns:
        The surface of the intersection of the rectangle and the surface defined by f_lower and f_upper.
    """
    x1 = np.min(rectangle[0])
    x2 = np.max(rectangle[0])
    y1 = np.min(rectangle[1])
    y2 = np.max(rectangle[1])
    # Check that f_lower is allways lower than f_upper between x1 and x2 if no bounds are given or between the bounds
    #  of the f_lower and f_upper functions if they are given.
    if check:
        if bounds_lower is not None:
            if bounds_upper is not None:
                if not all(np.array(bounds_lower) == np.array(bounds_upper)):
                    raise ValueError('Bounds should be identical for both f_lower and f_upper')
                if not '<' in function_comparison(f1=f_lower, f2=f_upper,
                                                  x1=bounds_lower[0], x2=bounds_lower[1],
                                                  numpoints_check=numpoints_check):
                    raise RuntimeError('Function f_lower is not allways lower or equal to function f_upper within '
                                       'the domain defined by the functions bounds.')
            else:
                raise ValueError('Bounds are given for f_lower but not for f_upper')
        elif bounds_upper is not None:
            if bounds_lower is None:
                raise ValueError('Bounds are given for f_upper but not for f_lower')
            else:
                if not '<' in function_comparison(f1=f_lower, f2=f_upper,
                                                  x1=bounds_lower[0], x2=bounds_lower[1],
                                                  numpoints_check=numpoints_check):
                    raise RuntimeError('Function f_lower is not allways lower or equal to function f_upper within '
                                       'the domain defined by the functions bounds.')
        else:
            if not '<' in function_comparison(f1=f_lower, f2=f_upper,
                                              x1=x1, x2=x2, numpoints_check=numpoints_check):
                raise RuntimeError('Function f_lower is not allways lower or equal to function f_upper within '
                                   'the domain defined by x1 and x2.')
    if bounds_lower is None:
        raise NotImplementedError('Bounds should be given right now ...')
    else:
        if x2 < bounds_lower[0] or x1 > bounds_lower[1]:
            return (0.0, 0.0)
        if x1 < bounds_lower[0]:
            xmin = bounds_lower[0]
        else:
            xmin = x1
        if x2 > bounds_lower[1]:
            xmax = bounds_lower[1]
        else:
            xmax = x2
        def diff(x):
            flwx = f_lower(x)
            fupx = f_upper(x)
            minup = np.min([fupx, y2 * np.ones_like(fupx)], axis=0)
            maxlw = np.max([flwx, y1 * np.ones_like(flwx)], axis=0)
            zeros = np.zeros_like(fupx)
            upper = np.where(y2 >= flwx, np.where(y1 <= fupx, minup, zeros), zeros)
            lower = np.where(y1 <= fupx, np.where(y2 >= flwx, maxlw, zeros), zeros)
            return upper-lower
        return quad(diff, xmin, xmax)

Response: This method calculates the surface of the intersection of a rectangle and another rectangle.


Instruction:
How would you explain what the following Python 3 function does
def my_solid_angle(center, coords):
    o = np.array(center)
    r = [np.array(c) - o for c in coords]
    r.append(r[0])
    n = [np.cross(r[i + 1], r[i]) for i in range(len(r) - 1)]
    n.append(np.cross(r[1], r[0]))
    phi = 0.0
    for i in range(len(n) - 1):
        try:
            value = math.acos(-np.dot(n[i], n[i + 1]) / (np.linalg.norm(n[i]) * np.linalg.norm(n[i + 1])))
        except ValueError:
            mycos = -np.dot(n[i], n[i + 1]) / (np.linalg.norm(n[i]) * np.linalg.norm(n[i + 1]))
            if 0.999999999999 < mycos < 1.000000000001:
                value = math.acos(1.0)
            elif -0.999999999999 > mycos > -1.000000000001:
                value = math.acos(-1.0)
            else:
                raise SolidAngleError(mycos)
        phi += value
    return phi + (3 - len(r)) * math.pi

Response: This method calculates the solid angle of a set of coords from the center.


Instruction:
Can you write a function in Python 3 where it
takes the elements of two vectors and returns a 3x3 matrix M.

Response:
def vectorsToMatrix(aa, bb):
    """
    Performs the vector multiplication of the elements of two vectors, constructing the 3x3 matrix.
    :param aa: One vector of size 3
    :param bb: Another vector of size 3
    :return: A 3x3 matrix M composed of the products of the elements of aa and bb :
     M_ij = aa_i * bb_j
    """
    MM = np.zeros([3, 3], np.float)
    for ii in range(3):
        for jj in range(3):
            MM[ii, jj] = aa[ii] * bb[jj]
    return MM


Instruction:
Given the following Python 3 function, write the documentation
def rotateCoords(coords, R):
    newlist = list()
    for pp in coords:
        rpp = matrixTimesVector(R, pp)
        newlist.append(rpp)
    return newlist

Response: Rotates the list of points using the rotation matrix R


Instruction:
How would you explain what the following Python 3 function does
def collinear(p1, p2, p3=None, tolerance=0.25):
    if p3 is None:
        triangle_area = 0.5 * np.linalg.norm(np.cross(p1, p2))
        dist = np.sort([np.linalg.norm(p2 - p1), np.linalg.norm(p1), np.linalg.norm(p2)])
    else:
        triangle_area = 0.5 * np.linalg.norm(np.cross(p1 - p3, p2 - p3))
        dist = np.sort([np.linalg.norm(p2 - p1), np.linalg.norm(p3 - p1), np.linalg.norm(p3 - p2)])
    largest_triangle_area = 0.5 * dist[0] * dist[1]
    return triangle_area < tolerance * largest_triangle_area

Response: Checks if three points p1 p2 and p3 are collinear.


Instruction:
How would you implement a function in Python 3 that
sorts a list of 2D points in anticlockwise order

Response:
def anticlockwise_sort(pps):
    """
    Sort a list of 2D points in anticlockwise order
    :param pps: List of points to be sorted
    :return: Sorted list of points
    """
    newpps = list()
    angles = np.zeros(len(pps), np.float)
    for ipp, pp in enumerate(pps):
        angles[ipp] = np.arctan2(pp[1], pp[0])
    iisorted = np.argsort(angles)
    for ii in range(len(pps)):
        newpps.append(pps[iisorted[ii]])
    return newpps


Instruction:
Can you generate a brief explanation for the following Python 3 code
def anticlockwise_sort_indices(pps):
    angles = np.zeros(len(pps), np.float)
    for ipp, pp in enumerate(pps):
        angles[ipp] = np.arctan2(pp[1], pp[0])
    return np.argsort(angles)

Response: Returns the indices that would sort a list of 2D points in anticlockwise order


Instruction:
How would you explain what the following Python 3 function does
def separation_in_list(separation_indices, separation_indices_list):
    sorted_separation = sort_separation(separation_indices)
    for sep in separation_indices_list:
        if len(sep[1]) == len(sorted_separation[1]) and np.allclose(sorted_separation[1], sep[1]):
            return True
    return False

Response: Checks if the separation indices of a plane are already in the list


Instruction:
Can you tell what is the following Python 3 function doing
def is_anion_cation_bond(valences, ii, jj):
    if valences == 'undefined':
        return True
    if valences[ii] == 0 or valences[jj] == 0:
        return True
    return (valences[ii] > 0 > valences[jj]) or (valences[jj] > 0 > valences[ii])

Response: Checks if two given sites are an anion and a cation.


Instruction:
Make a summary of the following Python 3 code
def is_in_plane(self, pp, dist_tolerance):
        return np.abs(np.dot(self.normal_vector, pp) + self._coefficients[3]) <= dist_tolerance

Response: Determines if point pp is in the plane within the tolerance dist_tolerance


Instruction:
Write a Python 3 function that can
check whether the resource is in the list of Planes.

Response:
def is_in_list(self, plane_list):
        """
        Checks whether the plane is identical to one of the Planes in the plane_list list of Planes
        :param plane_list: List of Planes to be compared to
        :return: True if the plane is in the list, False otherwise
        """
        for plane in plane_list:
            if self.is_same_plane_as(plane):
                return True
        return False


Instruction:
Explain what the following Python 3 code does
def indices_separate(self, points, dist_tolerance):
        side1 = list()
        inplane = list()
        side2 = list()
        for ip, pp in enumerate(points):
            if self.is_in_plane(pp, dist_tolerance):
                inplane.append(ip)
            else:
                if np.dot(pp + self.vector_to_origin, self.normal_vector) < 0.0:
                    side1.append(ip)
                else:
                    side2.append(ip)
        return [side1, inplane, side2]

Response: Returns three lists containing the indices of the points lying on one side of the plane and on the other side of the plane.


Instruction:
Given the following Python 3 function, write the documentation
def distance_to_point(self, point):
        return np.abs(np.dot(self.normal_vector, point) + self.d)

Response: Computes the absolute distance from the plane to the point


Instruction:
Here you have a function in Python 3, explain what it does
def distances(self, points):
        return [np.dot(self.normal_vector, pp) + self.d for pp in points]

Response: Computes the distances from the plane to each of the points


Instruction:
Implement a Python 3 function for
computing the distances from the plane to each of the points.

Response:
def distances_indices_sorted(self, points, sign=False):
        """
        Computes the distances from the plane to each of the points. Positive distances are on the side of the
        normal of the plane while negative distances are on the other side. Indices sorting the points from closest
        to furthest is also computed.
        :param points: Points for which distances are computed
        :param sign: Whether to add sign information in the indices sorting the points distances
        :return: Distances from the plane to the points (positive values on the side of the normal to the plane,
                 negative values on the other side), as well as indices of the points from closest to furthest. For
                 the latter, when the sign parameter is True, items of the sorting list are given as tuples of
                 (index, sign).
        """
        distances = [np.dot(self.normal_vector, pp) + self.d for pp in points]
        indices = sorted(range(len(distances)), key=lambda k: np.abs(distances[k]))
        if sign:
            indices = [(ii, int(np.sign(distances[ii]))) for ii in indices]
        return distances, indices


Instruction:
Here you have a function in Python 3, explain what it does
def distances_indices_groups(self, points, delta=None, delta_factor=0.05, sign=False):
        distances, indices = self.distances_indices_sorted(points=points)
        if delta is None:
            delta = delta_factor*np.abs(distances[indices[-1]])
        iends = [ii for ii, idist in enumerate(indices, start=1)
                 if ii == len(distances) or (np.abs(distances[indices[ii]])-np.abs(distances[idist])>delta)]
        if sign:
            indices = [(ii, int(np.sign(distances[ii]))) for ii in indices]
        grouped_indices = [indices[iends[ii - 1]:iend]
                           if ii > 0 else indices[:iend]
                           for ii, iend in enumerate(iends)]
        return distances, indices, grouped_indices

Response: Computes the distances between the plane and the points.


Instruction:
Make a summary of the following Python 3 code
def projectionpoints(self, pps):
        return [pp - np.dot(pp - self.p1, self.normal_vector) * self.normal_vector for pp in pps]

Response: Projects each point in the point list pps on plane and returns the list of projected points on plane


Instruction:
Can you write a function in Python 3 where it
returns a list of three orthogonal vectors the two first being parallel to the plane and the third one being the normal vector of the plane.

Response:
def orthonormal_vectors(self):
        """
        Returns a list of three orthogonal vectors, the two first being parallel to the plane and the
        third one is the normal vector of the plane
        :return: List of orthogonal vectors
        :raise: ValueError if all the coefficients are zero or if there is some other strange error
        """
        if self.e1 is None:
            diff = self.p2 - self.p1
            self.e1 = diff / norm(diff)
            self.e2 = np.cross(self.e3, self.e1)
        return [self.e1, self.e2, self.e3]


Instruction:
Implement a function in Python 3 to
return a list of three orthogonal vectors the two first being parallel to the plane and the third one being the normal vector of the plane.

Response:
def orthonormal_vectors_old(self):
        """
        Returns a list of three orthogonal vectors, the two first being parallel to the plane and the
        third one is the normal vector of the plane
        :return: List of orthogonal vectors
        :raise: ValueError if all the coefficients are zero or if there is some other strange error
        """
        if self.e1 is None:
            imax = np.argmax(np.abs(self.normal_vector))
            if imax == 0:
                self.e1 = np.array([self.e3[1], -self.e3[0], 0.0]) / np.sqrt(self.e3[0] ** 2 + self.e3[1] ** 2)
            elif imax == 1:
                self.e1 = np.array([0.0, self.e3[2], -self.e3[1]]) / np.sqrt(self.e3[1] ** 2 + self.e3[2] ** 2)
            elif imax == 2:
                self.e1 = np.array([-self.e3[2], 0.0, self.e3[0]]) / np.sqrt(self.e3[0] ** 2 + self.e3[2] ** 2)
            else:
                raise ValueError('Only three values in the normal vector, should not be here ...')
            self.e2 = np.cross(self.e3, self.e1)
        return [self.e1, self.e2, self.e3]


Instruction:
Explain what the following Python 3 code does
def project_and_to2dim_ordered_indices(self, pps, plane_center='mean'):
        pp2d = self.project_and_to2dim(pps, plane_center)
        return anticlockwise_sort_indices(pp2d)

Response: Projects each point list pps on plane and returns the indices that would sort the list of projected points in anticlockwise order


Instruction:
Can you generate the documentation for the following Python 3 function
def project_and_to2dim(self, pps, plane_center):
        proj = self.projectionpoints(pps)
        [u1, u2, u3] = self.orthonormal_vectors()
        PP = np.array([[u1[0], u2[0], u3[0]],
                       [u1[1], u2[1], u3[1]],
                       [u1[2], u2[2], u3[2]]])
        xypps = list()
        for pp in proj:
            xyzpp = np.dot(pp, PP)
            xypps.append(xyzpp[0:2])
        if str(plane_center) == str('mean'):
            mean = np.zeros(2, np.float)
            for pp in xypps:
                mean += pp
            mean /= len(xypps)
            xypps = [pp - mean for pp in xypps]
        elif plane_center is not None:
            projected_plane_center = self.projectionpoints([plane_center])[0]
            xy_projected_plane_center = np.dot(projected_plane_center, PP)[0:2]
            xypps = [pp - xy_projected_plane_center for pp in xypps]
        return xypps

Response: Projects the list of points pps to the plane and changes the basis from 3D to 2D basis of the plane.


Instruction:
Write a Python 3 script to
function to actually open a ring using OpenBabel s local opt.

Response:
def open_ring(mol_graph, bond, opt_steps):
    """
    Function to actually open a ring using OpenBabel's local opt. Given a molecule
    graph and a bond, convert the molecule graph into an OpenBabel molecule, remove
    the given bond, perform the local opt with the number of steps determined by
    self.steps, and then convert the resulting structure back into a molecule graph
    to be returned.
    """
    obmol = BabelMolAdaptor.from_molecule_graph(mol_graph)
    obmol.remove_bond(bond[0][0]+1, bond[0][1]+1)
    obmol.localopt(steps=opt_steps)
    return MoleculeGraph.with_local_env_strategy(obmol.pymatgen_mol, OpenBabelNN(), reorder=False, extend_structure=False)


Instruction:
Write a Python 3 script to
perform one step of iterative fragmentation on a list of molecule graphs.

Response:
def _fragment_one_level(self, mol_graphs):
        """
        Perform one step of iterative fragmentation on a list of molecule graphs. Loop through the graphs,
        then loop through each graph's edges and attempt to remove that edge in order to obtain two
        disconnected subgraphs, aka two new fragments. If successful, check to see if the new fragments
        are already present in self.unique_fragments, and append them if not. If unsucessful, we know
        that edge belongs to a ring. If we are opening rings, do so with that bond, and then again
        check if the resulting fragment is present in self.unique_fragments and add it if it is not.
        """
        unique_fragments_on_this_level = []
        for mol_graph in mol_graphs:
            for edge in mol_graph.graph.edges:
                bond = [(edge[0],edge[1])]
                try:
                    fragments = mol_graph.split_molecule_subgraphs(bond, allow_reverse=True)
                    for fragment in fragments:
                        found = False
                        for unique_fragment in self.unique_fragments:
                            if unique_fragment.isomorphic_to(fragment):
                                found = True
                                break
                        if not found:
                            self.unique_fragments.append(fragment)
                            unique_fragments_on_this_level.append(fragment)
                except MolGraphSplitError:
                    if self.open_rings:
                        fragment = open_ring(mol_graph, bond, self.opt_steps)
                        found = False
                        for unique_fragment in self.unique_fragments:
                            if unique_fragment.isomorphic_to(fragment):
                                found = True
                                break
                        if not found:
                            self.unique_fragments.append(fragment)
                            self.unique_fragments_from_ring_openings.append(fragment)
                            unique_fragments_on_this_level.append(fragment)
        return unique_fragments_on_this_level


Instruction:
How would you code a function in Python 3 to
open all rings in the unique_fragments list.

Response:
def _open_all_rings(self):
        """
        Having already generated all unique fragments that did not require ring opening,
        now we want to also obtain fragments that do require opening. We achieve this by
        looping through all unique fragments and opening each bond present in any ring
        we find. We also temporarily add the principle molecule graph to self.unique_fragments
        so that its rings are opened as well.
        """
        self.unique_fragments.insert(0, self.mol_graph)
        for fragment in self.unique_fragments:
            ring_edges = fragment.find_rings()
            if ring_edges != []:
                for bond in ring_edges[0]:
                    new_fragment = open_ring(fragment, [bond], self.opt_steps)
                    found = False
                    for unique_fragment in self.unique_fragments:
                        if unique_fragment.isomorphic_to(new_fragment):
                            found = True
                            break
                    if not found:
                        self.unique_fragments_from_ring_openings.append(new_fragment)
                        self.unique_fragments.append(new_fragment)
        # Finally, remove the principle molecule graph:
        self.unique_fragments.pop(0)


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

Response:
def as_dict(self):
        """
        Json-serializable dict representation of COHP.
        """
        d = {"@module": self.__class__.__module__,
             "@class": self.__class__.__name__,
             "are_coops": self.are_coops,
             "efermi": self.efermi,
             "energies": self.energies.tolist(),
             "COHP": {str(spin): pops.tolist()
                      for spin, pops in self.cohp.items()}}
        if self.icohp:
            d["ICOHP"] = {str(spin): pops.tolist()
                          for spin, pops in self.icohp.items()}
        return d


Instruction:
Can you generate the documentation for the following Python 3 function
def get_cohp(self, spin=None, integrated=False):
        if not integrated:
            populations = self.cohp
        else:
            populations = self.icohp

        if populations is None:
            return None
        elif spin is None:
            return populations
        else:
            if isinstance(spin, int):
                spin = Spin(spin)
            elif isinstance(spin, str):
                s = {"up": 1, "down": -1}[spin.lower()]
                spin = Spin(s)
            return {spin: populations[spin]}

Response: Returns the COHP or ICOHP for a particular spin.


Instruction:
How would you code a function in Python 3 to
return the COHP value for a particular energy.

Response:
def get_interpolated_value(self, energy, integrated=False):
        """
        Returns the COHP for a particular energy.

        Args:
            energy: Energy to return the COHP value for.
        """
        inter = {}
        for spin in self.cohp:
            if not integrated:
                inter[spin] = get_linear_interpolated_value(self.energies,
                                                            self.cohp[spin],
                                                            energy)
            elif self.icohp is not None:
                inter[spin] = get_linear_interpolated_value(self.energies,
                                                            self.icohp[spin],
                                                            energy)
            else:
                raise ValueError("ICOHP is empty.")
        return inter


Instruction:
Can you generate a brief explanation for the following Python 3 code
def has_antibnd_states_below_efermi(self, spin=None, limit=0.01):
        warnings.warn("This method has not been tested on many examples. Check the parameter limit, pls!")

        populations = self.cohp
        number_energies_below_efermi = len([x for x in self.energies if x <= self.efermi])

        if populations is None:
            return None
        elif spin is None:
            dict_to_return = {}
            for spin, cohpvalues in populations.items():
                if (max(cohpvalues[0:number_energies_below_efermi])) > limit:
                    dict_to_return[spin] = True
                else:
                    dict_to_return[spin] = False
        else:
            dict_to_return = {}
            if isinstance(spin, int):
                spin = Spin(spin)
            elif isinstance(spin, str):
                s = {"up": 1, "down": -1}[spin.lower()]
                spin = Spin(s)
            if (max(populations[spin][0:number_energies_below_efermi])) > limit:
                dict_to_return[spin] = True
            else:
                dict_to_return[spin] = False

        return dict_to_return

Response: Returns dict indicating if there are antibonding states below the Fermi level depending on the spin.


Instruction:
Can you tell what is the following Python 3 function doing
def from_dict(cls, d):
        if "ICOHP" in d:
            icohp = {Spin(int(key)): np.array(val)
                     for key, val in d["ICOHP"].items()}
        else:
            icohp = None
        return Cohp(d["efermi"], d["energies"],
                    {Spin(int(key)): np.array(val)
                     for key, val in d["COHP"].items()},
                    icohp=icohp, are_coops=d["are_coops"])

Response: Returns a COHP object from a dict representation of the COHP.