Instruction:
Write a Python 3 script to
apply our input structure to our list of transformations and output a list of ordered structures that have been pruned for duplicates and for those with low symmetry (optional). Args: sanitized_input_structure: A sanitized input structure (_sanitize_input_structure) transformations: A dict of transformations (values) and name of enumeration strategy (key), the enumeration strategy name is just for record keeping Returns: None (sets self.ordered_structures and self.ordered_structures_origins instance variables)

Response:
def _generate_ordered_structures(self, sanitized_input_structure, transformations):
        """
        Apply our input structure to our list of transformations and output a list
        of ordered structures that have been pruned for duplicates and for those
        with low symmetry (optional).

        Args:
            sanitized_input_structure: A sanitized input structure
            (_sanitize_input_structure)
            transformations: A dict of transformations (values) and name of
            enumeration strategy (key), the enumeration strategy name is just
            for record keeping

        Returns: None (sets self.ordered_structures
        and self.ordered_structures_origins instance variables)

        """

        ordered_structures = self.ordered_structures
        ordered_structures_origins = self.ordered_structure_origins

        # utility function to combine outputs from several transformations
        def _add_structures(
            ordered_structures, ordered_structures_origins, structures_to_add, origin=""
        ):
            """
            Transformations with return_ranked_list can return either
            just Structures or dicts (or sometimes lists!) -- until this
            is fixed, we use this function to concat structures given
            by the transformation.
            """
            if structures_to_add:
                # type conversion
                if isinstance(structures_to_add, Structure):
                    structures_to_add = [structures_to_add]
                structures_to_add = [
                    s["structure"] if isinstance(s, dict) else s
                    for s in structures_to_add
                ]
                # concatenation
                ordered_structures += structures_to_add
                ordered_structures_origins += [origin] * len(structures_to_add)
                self.logger.info(
                    "Adding {} ordered structures: {}".format(
                        len(structures_to_add), origin
                    )
                )

            return ordered_structures, ordered_structures_origins

        for origin, trans in self.transformations.items():
            structures_to_add = trans.apply_transformation(
                self.sanitized_structure, return_ranked_list=self.num_orderings
            )
            ordered_structures, ordered_structures_origins = _add_structures(
                ordered_structures,
                ordered_structures_origins,
                structures_to_add,
                origin=origin,
            )

        # in case we've introduced duplicates, let's remove them
        self.logger.info("Pruning duplicate structures.")
        structures_to_remove = []
        for idx, ordered_structure in enumerate(ordered_structures):
            if idx not in structures_to_remove:
                duplicate_checker = CollinearMagneticStructureAnalyzer(
                    ordered_structure, overwrite_magmom_mode="none"
                )
                for check_idx, check_structure in enumerate(ordered_structures):
                    if check_idx not in structures_to_remove and check_idx != idx:
                        if duplicate_checker.matches_ordering(check_structure):
                            structures_to_remove.append(check_idx)

        if len(structures_to_remove):
            self.logger.info(
                "Removing {} duplicate ordered structures".format(
                    len(structures_to_remove)
                )
            )
            ordered_structures = [
                s
                for idx, s in enumerate(ordered_structures)
                if idx not in structures_to_remove
            ]
            ordered_structures_origins = [
                o
                for idx, o in enumerate(ordered_structures_origins)
                if idx not in structures_to_remove
            ]

        # also remove low symmetry structures
        if self.truncate_by_symmetry:

            # by default, keep structures with 5 most symmetric space groups
            if not isinstance(self.truncate_by_symmetry, int):
                self.truncate_by_symmetry = 5

            self.logger.info("Pruning low symmetry structures.")

            # first get a list of symmetries present
            symmetry_int_numbers = [
                s.get_space_group_info()[1] for s in ordered_structures
            ]

            # then count the number of symmetry operations for that space group
            num_sym_ops = [
                len(SpaceGroup.from_int_number(n).symmetry_ops)
                for n in symmetry_int_numbers
            ]

            # find the largest values...
            max_symmetries = sorted(list(set(num_sym_ops)), reverse=True)

            # ...and decide which ones to keep
            if len(max_symmetries) > self.truncate_by_symmetry:
                max_symmetries = max_symmetries[0:5]
            structs_to_keep = [
                (idx, num)
                for idx, num in enumerate(num_sym_ops)
                if num in max_symmetries
            ]

            # sort so that highest symmetry structs are first
            structs_to_keep = sorted(
                structs_to_keep, key=lambda x: (x[1], -x[0]), reverse=True
            )

            self.logger.info(
                "Removing {} low symmetry "
                "ordered structures".format(
                    len(ordered_structures) - len(structs_to_keep)
                )
            )

            ordered_structures = [ordered_structures[i] for i, _ in structs_to_keep]
            ordered_structures_origins = [
                ordered_structures_origins[i] for i, _ in structs_to_keep
            ]

            # and ensure fm is always at index 0
            fm_index = ordered_structures_origins.index("fm")
            ordered_structures.insert(0, ordered_structures.pop(fm_index))
            ordered_structures_origins.insert(
                0, ordered_structures_origins.pop(fm_index)
            )

        # if our input structure isn't in our generated structures,
        # let's add it manually and also keep a note of which structure
        # is our input: this is mostly for book-keeping/benchmarking
        self.input_index = None
        self.input_origin = None
        if self.input_analyzer.ordering != Ordering.NM:
            matches = [
                self.input_analyzer.matches_ordering(s) for s in ordered_structures
            ]
            if not any(matches):
                ordered_structures.append(self.input_analyzer.structure)
                ordered_structures_origins.append("input")
                self.logger.info(
                    "Input structure not present in enumerated structures, adding..."
                )
            else:
                self.logger.info(
                    "Input structure was found in enumerated "
                    "structures at index {}".format(matches.index(True))
                )
                self.input_index = matches.index(True)
                self.input_origin = ordered_structures_origins[self.input_index]

        self.ordered_structures = ordered_structures
        self.ordered_structure_origins = ordered_structures_origins


Instruction:
Implement a Python 3 function for
converting a string to a float.

Response:
def str2float(text):
    """
    Remove uncertainty brackets from strings and return the float.
    """

    try:
        # Note that the ending ) is sometimes missing. That is why the code has
        # been modified to treat it as optional. Same logic applies to lists.
        return float(re.sub(r"\(.+\)*", "", text))
    except TypeError:
        if isinstance(text, list) and len(text) == 1:
            return float(re.sub(r"\(.+\)*", "", text[0]))
    except ValueError as ex:
        if text.strip() == ".":
            return 0
        raise ex


Instruction:
Can you generate the documentation for the following Python 3 function
def _sanitize_data(self, data):

        """
        This part of the code deals with handling formats of data as found in
        CIF files extracted from the Springer Materials/Pauling File
        databases, and that are different from standard ICSD formats.
        """

        # check for implicit hydrogens, warn if any present
        if "_atom_site_attached_hydrogens" in data.data.keys():
            attached_hydrogens = [str2float(x) for x in data.data['_atom_site_attached_hydrogens']
                                  if str2float(x) != 0]
            if len(attached_hydrogens) > 0:
                self.errors.append("Structure has implicit hydrogens defined, "
                                   "parsed structure unlikely to be suitable for use "
                                   "in calculations unless hydrogens added.")

        # Check to see if "_atom_site_type_symbol" exists, as some test CIFs do
        # not contain this key.
        if "_atom_site_type_symbol" in data.data.keys():

            # Keep a track of which data row needs to be removed.
            # Example of a row: Nb,Zr '0.8Nb + 0.2Zr' .2a .m-3m 0 0 0 1 14
            # 'rhombic dodecahedron, Nb14'
            # Without this code, the above row in a structure would be parsed
            # as an ordered site with only Nb (since
            # CifParser would try to parse the first two characters of the
            # label "Nb,Zr") and occupancy=1.
            # However, this site is meant to be a disordered site with 0.8 of
            # Nb and 0.2 of Zr.
            idxs_to_remove = []

            new_atom_site_label = []
            new_atom_site_type_symbol = []
            new_atom_site_occupancy = []
            new_fract_x = []
            new_fract_y = []
            new_fract_z = []

            for idx, el_row in enumerate(data["_atom_site_label"]):

                # CIF files from the Springer Materials/Pauling File have
                # switched the label and symbol. Thus, in the
                # above shown example row, '0.8Nb + 0.2Zr' is the symbol.
                # Below, we split the strings on ' + ' to
                # check if the length (or number of elements) in the label and
                # symbol are equal.
                if len(data["_atom_site_type_symbol"][idx].split(' + ')) > \
                        len(data["_atom_site_label"][idx].split(' + ')):

                    # Dictionary to hold extracted elements and occupancies
                    els_occu = {}

                    # parse symbol to get element names and occupancy and store
                    # in "els_occu"
                    symbol_str = data["_atom_site_type_symbol"][idx]
                    symbol_str_lst = symbol_str.split(' + ')
                    for elocc_idx in range(len(symbol_str_lst)):
                        # Remove any bracketed items in the string
                        symbol_str_lst[elocc_idx] = re.sub(
                            r'\([0-9]*\)', '',
                            symbol_str_lst[elocc_idx].strip())

                        # Extract element name and its occupancy from the
                        # string, and store it as a
                        # key-value pair in "els_occ".
                        els_occu[str(re.findall(r'\D+', symbol_str_lst[
                            elocc_idx].strip())[1]).replace('', '')] = \
                            float('0' + re.findall(r'\.?\d+', symbol_str_lst[
                                elocc_idx].strip())[1])

                    x = str2float(data["_atom_site_fract_x"][idx])
                    y = str2float(data["_atom_site_fract_y"][idx])
                    z = str2float(data["_atom_site_fract_z"][idx])

                    for et, occu in els_occu.items():
                        # new atom site labels have 'fix' appended
                        new_atom_site_label.append(
                            et + '_fix' + str(len(new_atom_site_label)))
                        new_atom_site_type_symbol.append(et)
                        new_atom_site_occupancy.append(str(occu))
                        new_fract_x.append(str(x))
                        new_fract_y.append(str(y))
                        new_fract_z.append(str(z))

                    idxs_to_remove.append(idx)

            # Remove the original row by iterating over all keys in the CIF
            # data looking for lists, which indicates
            # multiple data items, one for each row, and remove items from the
            # list that corresponds to the removed row,
            # so that it's not processed by the rest of this function (which
            # would result in an error).
            for original_key in data.data:
                if isinstance(data.data[original_key], list):
                    for id in sorted(idxs_to_remove, reverse=True):
                        del data.data[original_key][id]

            if len(idxs_to_remove) > 0:

                self.errors.append("Pauling file corrections applied.")

                data.data["_atom_site_label"] += new_atom_site_label
                data.data["_atom_site_type_symbol"] += new_atom_site_type_symbol
                data.data["_atom_site_occupancy"] += new_atom_site_occupancy
                data.data["_atom_site_fract_x"] += new_fract_x
                data.data["_atom_site_fract_y"] += new_fract_y
                data.data["_atom_site_fract_z"] += new_fract_z

        """
        This fixes inconsistencies in naming of several magCIF tags
        as a result of magCIF being in widespread use prior to
        specification being finalized (on advice of Branton Campbell).
        """

        if self.feature_flags["magcif"]:

            # CIF-1 style has all underscores, interim standard
            # had period before magn instead of before the final
            # component (e.g. xyz)
            # we want to standardize on a specific key, to simplify
            # parsing code
            correct_keys = ["_space_group_symop_magn_operation.xyz",
                            "_space_group_symop_magn_centering.xyz",
                            "_space_group_magn.name_BNS",
                            "_space_group_magn.number_BNS",
                            "_atom_site_moment_crystalaxis_x",
                            "_atom_site_moment_crystalaxis_y",
                            "_atom_site_moment_crystalaxis_z",
                            "_atom_site_moment_label"]

            # cannot mutate OrderedDict during enumeration,
            # so store changes we want to make
            changes_to_make = {}

            for original_key in data.data:
                for correct_key in correct_keys:
                    # convert to all underscore
                    trial_key = "_".join(correct_key.split("."))
                    test_key = "_".join(original_key.split("."))
                    if trial_key == test_key:
                        changes_to_make[correct_key] = original_key

            # make changes
            for correct_key, original_key in changes_to_make.items():
                data.data[correct_key] = data.data[original_key]

            # renamed_keys maps interim_keys to final_keys
            renamed_keys = {
                "_magnetic_space_group.transform_to_standard_Pp_abc":
                    "_space_group_magn.transform_BNS_Pp_abc"}
            changes_to_make = {}

            for interim_key, final_key in renamed_keys.items():
                if data.data.get(interim_key):
                    changes_to_make[final_key] = interim_key

            if len(changes_to_make) > 0:
                self.errors.append("Keys changed to match new magCIF specification.")

            for final_key, interim_key in changes_to_make.items():
                data.data[final_key] = data.data[interim_key]

        # check for finite precision frac co-ordinates (e.g. 0.6667 instead of 0.6666666...7)
        # this can sometimes cause serious issues when applying symmetry operations
        important_fracs = (1/3., 2/3.)
        fracs_to_change = {}
        for label in ('_atom_site_fract_x', '_atom_site_fract_y', '_atom_site_fract_z'):
            if label in data.data.keys():
                for idx, frac in enumerate(data.data[label]):
                    try:
                        frac = str2float(frac)
                    except:
                        # co-ordinate might not be defined e.g. '?'
                        continue
                    for comparison_frac in important_fracs:
                        if abs(1 - frac/comparison_frac) < 1e-4:
                            fracs_to_change[(label, idx)] = str(comparison_frac)
        if fracs_to_change:
            self.errors.append("Some fractional co-ordinates rounded to ideal values to "
                               "avoid finite precision errors.")
            for (label, idx), val in fracs_to_change.items():
                data.data[label][idx] = val

        return data

Response: This function checks that the data for the CifBlock contains the correct CifEntry and returns the data.


Instruction:
Create a Python 3 function to
generate unique coordinates using coord and symmetry positions and also their corresponding magnetic moments.

Response:
def _unique_coords(self, coords_in, magmoms_in=None, lattice=None):
        """
        Generate unique coordinates using coord and symmetry positions
        and also their corresponding magnetic moments, if supplied.
        """
        coords = []
        if magmoms_in:
            magmoms = []
            if len(magmoms_in) != len(coords_in):
                raise ValueError
            for tmp_coord, tmp_magmom in zip(coords_in, magmoms_in):
                for op in self.symmetry_operations:
                    coord = op.operate(tmp_coord)
                    coord = np.array([i - math.floor(i) for i in coord])
                    if isinstance(op, MagSymmOp):
                        # Up to this point, magmoms have been defined relative
                        # to crystal axis. Now convert to Cartesian and into
                        # a Magmom object.
                        magmom = Magmom.from_moment_relative_to_crystal_axes(
                            op.operate_magmom(tmp_magmom),
                            lattice=lattice
                        )
                    else:
                        magmom = Magmom(tmp_magmom)
                    if not in_coord_list_pbc(coords, coord,
                                             atol=self._site_tolerance):
                        coords.append(coord)
                        magmoms.append(magmom)
            return coords, magmoms
        else:
            for tmp_coord in coords_in:
                for op in self.symmetry_operations:
                    coord = op.operate(tmp_coord)
                    coord = np.array([i - math.floor(i) for i in coord])
                    if not in_coord_list_pbc(coords, coord,
                                             atol=self._site_tolerance):
                        coords.append(coord)
            return coords, [Magmom(0)] * len(coords)


Instruction:
Can you generate the documentation for the following Python 3 function
def get_lattice(self, data, length_strings=("a", "b", "c"),
                    angle_strings=("alpha", "beta", "gamma"),
                    lattice_type=None):
        """
        Generate the lattice from the provided lattice parameters. In
        the absence of all six lattice parameters, the crystal system
        and necessary parameters are parsed
        """
        try:

            lengths = [str2float(data["_cell_length_" + i])
                       for i in length_strings]
            angles = [str2float(data["_cell_angle_" + i])
                      for i in angle_strings]
            if not lattice_type:
                return Lattice.from_lengths_and_angles(lengths, angles)

            else:
                return getattr(Lattice, lattice_type)(*(lengths + angles))

        except KeyError:
            # Missing Key search for cell setting
            for lattice_lable in ["_symmetry_cell_setting",
                                  "_space_group_crystal_system"]:
                if data.data.get(lattice_lable):
                    lattice_type = data.data.get(lattice_lable).lower()
                    try:

                        required_args = getargspec(
                            getattr(Lattice, lattice_type)).args

                        lengths = (l for l in length_strings
                                   if l in required_args)
                        angles = (a for a in angle_strings
                                  if a in required_args)
                        return self.get_lattice(data, lengths, angles,
                                                lattice_type=lattice_type)
                    except AttributeError as exc:
                        self.errors.append(str(exc))
                        warnings.warn(exc)

                else:
                    return None

Response: Generate the crystal system lattice from the provided lattice parameters.


Instruction:
Write a Python 3 function for
returning a list of symops for the equivalent positions of the space group and symmetry operations.

Response:
def get_symops(self, data):
        """
        In order to generate symmetry equivalent positions, the symmetry
        operations are parsed. If the symops are not present, the space
        group symbol is parsed, and symops are generated.
        """
        symops = []
        for symmetry_label in ["_symmetry_equiv_pos_as_xyz",
                               "_symmetry_equiv_pos_as_xyz_",
                               "_space_group_symop_operation_xyz",
                               "_space_group_symop_operation_xyz_"]:
            if data.data.get(symmetry_label):
                xyz = data.data.get(symmetry_label)
                if isinstance(xyz, str):
                    msg = "A 1-line symmetry op P1 CIF is detected!"
                    warnings.warn(msg)
                    self.errors.append(msg)
                    xyz = [xyz]
                try:
                    symops = [SymmOp.from_xyz_string(s)
                              for s in xyz]
                    break
                except ValueError:
                    continue
        if not symops:
            # Try to parse symbol
            for symmetry_label in ["_symmetry_space_group_name_H-M",
                                   "_symmetry_space_group_name_H_M",
                                   "_symmetry_space_group_name_H-M_",
                                   "_symmetry_space_group_name_H_M_",
                                   "_space_group_name_Hall",
                                   "_space_group_name_Hall_",
                                   "_space_group_name_H-M_alt",
                                   "_space_group_name_H-M_alt_",
                                   "_symmetry_space_group_name_hall",
                                   "_symmetry_space_group_name_hall_",
                                   "_symmetry_space_group_name_h-m",
                                   "_symmetry_space_group_name_h-m_"]:
                sg = data.data.get(symmetry_label)

                if sg:
                    sg = sub_spgrp(sg)
                    try:
                        spg = space_groups.get(sg)
                        if spg:
                            symops = SpaceGroup(spg).symmetry_ops
                            msg = "No _symmetry_equiv_pos_as_xyz type key found. " \
                                    "Spacegroup from %s used." % symmetry_label
                            warnings.warn(msg)
                            self.errors.append(msg)
                            break
                    except ValueError:
                        # Ignore any errors
                        pass

                    try:
                        for d in _get_cod_data():
                            if sg == re.sub(r"\s+", "",
                                            d["hermann_mauguin"]):
                                xyz = d["symops"]
                                symops = [SymmOp.from_xyz_string(s)
                                          for s in xyz]
                                msg = "No _symmetry_equiv_pos_as_xyz type key found. " \
                                        "Spacegroup from %s used." % symmetry_label
                                warnings.warn(msg)
                                self.errors.append(msg)
                                break
                    except Exception as ex:
                        continue

                    if symops:
                        break
        if not symops:
            # Try to parse International number
            for symmetry_label in ["_space_group_IT_number",
                                   "_space_group_IT_number_",
                                   "_symmetry_Int_Tables_number",
                                   "_symmetry_Int_Tables_number_"]:
                if data.data.get(symmetry_label):
                    try:
                        i = int(str2float(data.data.get(symmetry_label)))
                        symops = SpaceGroup.from_int_number(i).symmetry_ops
                        break
                    except ValueError:
                        continue

        if not symops:
            msg = "No _symmetry_equiv_pos_as_xyz type key found. " \
                  "Defaulting to P1."
            warnings.warn(msg)
            self.errors.append(msg)
            symops = [SymmOp.from_xyz_string(s) for s in ['x', 'y', 'z']]

        return symops


Instruction:
Implement a Python 3 function for
returning a list of all magnetic symmetry operations for the specified magnetic space group.

Response:
def get_magsymops(self, data):
        """
        Equivalent to get_symops except for magnetic symmetry groups.
        Separate function since additional operation for time reversal symmetry
        (which changes magnetic moments on sites) needs to be returned.
        """
        magsymmops = []

        # check to see if magCIF file explicitly contains magnetic symmetry operations
        if data.data.get("_space_group_symop_magn_operation.xyz"):

            xyzt = data.data.get("_space_group_symop_magn_operation.xyz")
            if isinstance(xyzt, str):
                xyzt = [xyzt]
            magsymmops = [MagSymmOp.from_xyzt_string(s) for s in xyzt]

            if data.data.get("_space_group_symop_magn_centering.xyz"):

                xyzt = data.data.get("_space_group_symop_magn_centering.xyz")
                if isinstance(xyzt, str):
                    xyzt = [xyzt]
                centering_symops = [MagSymmOp.from_xyzt_string(s) for s in xyzt]

                all_ops = []
                for op in magsymmops:
                    for centering_op in centering_symops:
                        new_translation = [i - np.floor(i) for i
                                           in
                                           op.translation_vector + centering_op.translation_vector]
                        new_time_reversal = op.time_reversal * centering_op.time_reversal
                        all_ops.append(
                            MagSymmOp.from_rotation_and_translation_and_time_reversal(
                                rotation_matrix=op.rotation_matrix,
                                translation_vec=new_translation,
                                time_reversal=new_time_reversal))
                magsymmops = all_ops

        # else check to see if it specifies a magnetic space group
        elif data.data.get("_space_group_magn.name_BNS") or data.data.get(
                "_space_group_magn.number_BNS"):

            if data.data.get("_space_group_magn.name_BNS"):
                # get BNS label for MagneticSpaceGroup()
                id = data.data.get("_space_group_magn.name_BNS")
            else:
                # get BNS number for MagneticSpaceGroup()
                # by converting string to list of ints
                id = list(map(int, (
                data.data.get("_space_group_magn.number_BNS").split("."))))

            msg = MagneticSpaceGroup(id)

            if data.data.get("_space_group_magn.transform_BNS_Pp_abc"):
                if data.data.get(
                        "_space_group_magn.transform_BNS_Pp_abc") != "a,b,c;0,0,0":
                    return NotImplementedError(
                        "Non-standard settings not currently supported.")
            elif data.data.get("_space_group_magn.transform_BNS_Pp"):
                return NotImplementedError(
                    "Incomplete specification to implement.")

            magsymmops = msg.symmetry_ops

        if not magsymmops:
            msg = "No magnetic symmetry detected, using primitive symmetry."
            warnings.warn(msg)
            self.errors.append(msg)
            magsymmops = [MagSymmOp.from_xyzt_string("x, y, z, 1")]

        return magsymmops


Instruction:
Make a summary of the following Python 3 code
def parse_oxi_states(self, data):
        try:
            oxi_states = {
                data["_atom_type_symbol"][i]:
                    str2float(data["_atom_type_oxidation_number"][i])
                for i in range(len(data["_atom_type_symbol"]))}
            # attempt to strip oxidation state from _atom_type_symbol
            # in case the label does not contain an oxidation state
            for i, symbol in enumerate(data["_atom_type_symbol"]):
                oxi_states[re.sub(r"\d?[\+,\-]?$", "", symbol)] = \
                    str2float(data["_atom_type_oxidation_number"][i])

        except (ValueError, KeyError):
            oxi_states = None
        return oxi_states

Response: Parse oxidation states from data dictionary


Instruction:
Write a Python 3 function that can
parse atomic magnetic moments from data dictionary.

Response:
def parse_magmoms(self, data, lattice=None):
        """
        Parse atomic magnetic moments from data dictionary
        """
        if lattice is None:
            raise Exception(
                'Magmoms given in terms of crystal axes in magCIF spec.')
        try:
            magmoms = {
                data["_atom_site_moment_label"][i]:
                    np.array(
                        [str2float(data["_atom_site_moment_crystalaxis_x"][i]),
                         str2float(data["_atom_site_moment_crystalaxis_y"][i]),
                         str2float(data["_atom_site_moment_crystalaxis_z"][i])]
                    )
                for i in range(len(data["_atom_site_moment_label"]))
            }
        except (ValueError, KeyError):
            return None
        return magmoms


Instruction:
Can you tell what is the following Python 3 function doing
def _parse_symbol(self, sym):
        # Common representations for elements/water in cif files
        # TODO: fix inconsistent handling of water
        special = {"Hw": "H", "Ow": "O", "Wat": "O",
                   "wat": "O", "OH": "", "OH2": "", "NO3": "N"}

        parsed_sym = None
        # try with special symbols, otherwise check the first two letters,
        # then the first letter alone. If everything fails try extracting the
        # first letters.
        m_sp = re.match("|".join(special.keys()), sym)
        if m_sp:
            parsed_sym = special[m_sp.group()]
        elif Element.is_valid_symbol(sym[:2].title()):
            parsed_sym = sym[:2].title()
        elif Element.is_valid_symbol(sym[0].upper()):
            parsed_sym = sym[0].upper()
        else:
            m = re.match(r"w?[A-Z][a-z]*", sym)
            if m:
                parsed_sym = m.group()

        if parsed_sym is not None and (m_sp or not re.match(r"{}\d*".format(parsed_sym), sym)):
            msg = "{} parsed as {}".format(sym, parsed_sym)
            warnings.warn(msg)
            self.errors.append(msg)

        return parsed_sym

Response: Parse a string with a symbol to extract a string representing an element.


Instruction:
How would you explain what the following Python 3 function does
def _get_structure(self, data, primitive):

        def get_num_implicit_hydrogens(sym):
            num_h = {"Wat": 2, "wat": 2, "O-H": 1}
            return num_h.get(sym[:3], 0)

        lattice = self.get_lattice(data)

        # if magCIF, get magnetic symmetry moments and magmoms
        # else standard CIF, and use empty magmom dict
        if self.feature_flags["magcif_incommensurate"]:
            raise NotImplementedError(
                "Incommensurate structures not currently supported.")
        elif self.feature_flags["magcif"]:
            self.symmetry_operations = self.get_magsymops(data)
            magmoms = self.parse_magmoms(data, lattice=lattice)
        else:
            self.symmetry_operations = self.get_symops(data)
            magmoms = {}

        oxi_states = self.parse_oxi_states(data)

        coord_to_species = OrderedDict()
        coord_to_magmoms = OrderedDict()

        def get_matching_coord(coord):
            keys = list(coord_to_species.keys())
            coords = np.array(keys)
            for op in self.symmetry_operations:
                c = op.operate(coord)
                inds = find_in_coord_list_pbc(coords, c,
                                              atol=self._site_tolerance)
                # cant use if inds, because python is dumb and np.array([0]) evaluates
                # to False
                if len(inds):
                    return keys[inds[0]]
            return False

        for i in range(len(data["_atom_site_label"])):
            try:
                # If site type symbol exists, use it. Otherwise, we use the
                # label.
                symbol = self._parse_symbol(data["_atom_site_type_symbol"][i])
                num_h = get_num_implicit_hydrogens(
                    data["_atom_site_type_symbol"][i])
            except KeyError:
                symbol = self._parse_symbol(data["_atom_site_label"][i])
                num_h = get_num_implicit_hydrogens(data["_atom_site_label"][i])
            if not symbol:
                continue

            if oxi_states is not None:
                o_s = oxi_states.get(symbol, 0)
                # use _atom_site_type_symbol if possible for oxidation state
                if "_atom_site_type_symbol" in data.data.keys():
                    oxi_symbol = data["_atom_site_type_symbol"][i]
                    o_s = oxi_states.get(oxi_symbol, o_s)
                try:
                    el = Specie(symbol, o_s)
                except:
                    el = DummySpecie(symbol, o_s)
            else:
                el = get_el_sp(symbol)

            x = str2float(data["_atom_site_fract_x"][i])
            y = str2float(data["_atom_site_fract_y"][i])
            z = str2float(data["_atom_site_fract_z"][i])
            magmom = magmoms.get(data["_atom_site_label"][i],
                                 np.array([0, 0, 0]))

            try:
                occu = str2float(data["_atom_site_occupancy"][i])
            except (KeyError, ValueError):
                occu = 1

            if occu > 0:
                coord = (x, y, z)
                match = get_matching_coord(coord)
                comp_d = {el: occu}
                if num_h > 0:
                    comp_d["H"] = num_h
                    self.errors.append("Structure has implicit hydrogens defined, "
                                       "parsed structure unlikely to be suitable for use "
                                       "in calculations unless hydrogens added.")
                comp = Composition(comp_d)
                if not match:
                    coord_to_species[coord] = comp
                    coord_to_magmoms[coord] = magmom
                else:
                    coord_to_species[match] += comp
                    # disordered magnetic not currently supported
                    coord_to_magmoms[match] = None

        sum_occu = [sum(c.values()) for c in coord_to_species.values()
                    if not set(c.elements) == {Element("O"), Element("H")}]
        if any([o > 1 for o in sum_occu]):
            msg = "Some occupancies (%s) sum to > 1! If they are within " \
                    "the tolerance, they will be rescaled." % str(sum_occu)
            warnings.warn(msg)
            self.errors.append(msg)

        allspecies = []
        allcoords = []
        allmagmoms = []
        allhydrogens = []

        # check to see if magCIF file is disordered
        if self.feature_flags["magcif"]:
            for k, v in coord_to_magmoms.items():
                if v is None:
                    # Proposed solution to this is to instead store magnetic
                    # moments as Specie 'spin' property, instead of site
                    # property, but this introduces ambiguities for end user
                    # (such as unintended use of `spin` and Specie will have
                    # fictious oxidation state).
                    raise NotImplementedError(
                        'Disordered magnetic structures not currently supported.')

        if coord_to_species.items():
            for comp, group in groupby(
                    sorted(list(coord_to_species.items()), key=lambda x: x[1]),
                    key=lambda x: x[1]):
                tmp_coords = [site[0] for site in group]
                tmp_magmom = [coord_to_magmoms[tmp_coord] for tmp_coord in
                              tmp_coords]

                if self.feature_flags["magcif"]:
                    coords, magmoms = self._unique_coords(tmp_coords,
                                                          magmoms_in=tmp_magmom,
                                                          lattice=lattice)
                else:
                    coords, magmoms = self._unique_coords(tmp_coords)

                if set(comp.elements) == {Element("O"), Element("H")}:
                    # O with implicit hydrogens
                    im_h = comp["H"]
                    species = Composition({"O": comp["O"]})
                else:
                    im_h = 0
                    species = comp

                allhydrogens.extend(len(coords) * [im_h])
                allcoords.extend(coords)
                allspecies.extend(len(coords) * [species])
                allmagmoms.extend(magmoms)

            # rescale occupancies if necessary
            for i, species in enumerate(allspecies):
                totaloccu = sum(species.values())
                if 1 < totaloccu <= self._occupancy_tolerance:
                    allspecies[i] = species / totaloccu

        if allspecies and len(allspecies) == len(allcoords) \
                and len(allspecies) == len(allmagmoms):
            site_properties = dict()
            if any(allhydrogens):
                assert len(allhydrogens) == len(allcoords)
                site_properties["implicit_hydrogens"] = allhydrogens

            if self.feature_flags["magcif"]:
                site_properties["magmom"] = allmagmoms

            if len(site_properties) == 0:
                site_properties = None

            struct = Structure(lattice, allspecies, allcoords,
                               site_properties=site_properties)

            struct = struct.get_sorted_structure()

            if primitive and self.feature_flags['magcif']:
                struct = struct.get_primitive_structure(use_site_props=True)
            elif primitive:
                struct = struct.get_primitive_structure()
                struct = struct.get_reduced_structure()

            return struct

Response: Generate structure from part of the cif.


Instruction:
Can you tell what is the following Python 3 function doing
def get_structures(self, primitive=True):
        structures = []
        for d in self._cif.data.values():
            try:
                s = self._get_structure(d, primitive)
                if s:
                    structures.append(s)
            except (KeyError, ValueError) as exc:
                # Warn the user (Errors should never pass silently)
                # A user reported a problem with cif files produced by Avogadro
                # in which the atomic coordinates are in Cartesian coords.
                self.errors.append(str(exc))
                warnings.warn(str(exc))
        if self.errors:
            warnings.warn("Issues encountered while parsing CIF:")
            for error in self.errors:
                warnings.warn(error)
        if len(structures) == 0:
            raise ValueError("Invalid cif file with no structures!")
        return structures

Response: Returns a list of structures in CIF file.


Instruction:
How would you explain what the following Python 3 function does
def get_bibtex_string(self):

        bibtex_keys = {'author': ('_publ_author_name', '_citation_author_name'),
                       'title': ('_publ_section_title', '_citation_title'),
                       'journal': ('_journal_name_full', '_journal_name_abbrev',
                                   '_citation_journal_full', '_citation_journal_abbrev'),
                       'volume': ('_journal_volume', '_citation_journal_volume'),
                       'year': ('_journal_year', '_citation_year'),
                       'number': ('_journal_number', '_citation_number'),
                       'page_first': ('_journal_page_first', '_citation_page_first'),
                       'page_last': ('_journal_page_last', '_citation_page_last'),
                       'doi': ('_journal_DOI', '_citation_DOI')}

        entries = {}

        # TODO: parse '_publ_section_references' when it exists?
        # TODO: CIF specification supports multiple citations.

        for idx, data in enumerate(self._cif.data.values()):

            # convert to lower-case keys, some cif files inconsistent
            data = {k.lower(): v for k, v in data.data.items()}

            bibtex_entry = {}

            for field, tags in bibtex_keys.items():
                for tag in tags:
                    if tag in data:
                        if isinstance(data[tag], list):
                            bibtex_entry[field] = data[tag][0]
                        else:
                            bibtex_entry[field] = data[tag]

            # convert to bibtex author format ('and' delimited)
            if 'author' in bibtex_entry:
                # separate out semicolon authors
                if isinstance(bibtex_entry["author"], str):
                    if ";" in bibtex_entry["author"]:
                        bibtex_entry["author"] = bibtex_entry["author"].split(";")

                if isinstance(bibtex_entry['author'], list):
                    bibtex_entry['author'] = ' and '.join(bibtex_entry['author'])

            # convert to bibtex page range format, use empty string if not specified
            if ('page_first' in bibtex_entry) or ('page_last' in bibtex_entry):
                bibtex_entry['pages'] = '{0}--{1}'.format(bibtex_entry.get('page_first', ''),
                                                          bibtex_entry.get('page_last', ''))
                bibtex_entry.pop('page_first', None)  # and remove page_first, page_list if present
                bibtex_entry.pop('page_last', None)

            # cite keys are given as cif-reference-idx in order they are found
            entries['cif-reference-{}'.format(idx)] = Entry('article', list(bibtex_entry.items()))

        return BibliographyData(entries).to_string(bib_format='bibtex')

Response: Get BibTeX string from CIF file.


Instruction:
Can you write a function in Python 3 where it
converts a list of SlabEntry objects to a dictionary.

Response:
def entry_dict_from_list(all_slab_entries):
    """
    Converts a list of SlabEntry to an appropriate dictionary. It is
    assumed that if there is no adsorbate, then it is a clean SlabEntry
    and that adsorbed SlabEntry has the clean_entry parameter set.

    Args:
        all_slab_entries (list): List of SlabEntry objects

    Returns:
        (dict): Dictionary of SlabEntry with the Miller index as the main
            key to a dictionary with a clean SlabEntry as the key to a
            list of adsorbed SlabEntry.
    """

    entry_dict = {}

    for entry in all_slab_entries:
        hkl = tuple(entry.miller_index)
        if hkl not in entry_dict.keys():
            entry_dict[hkl] = {}
        if entry.clean_entry:
            clean = entry.clean_entry
        else:
            clean = entry
        if clean not in entry_dict[hkl].keys():
            entry_dict[hkl][clean] = []
        if entry.adsorbates:
            entry_dict[hkl][clean].append(entry)

    return entry_dict


Instruction:
Write a Python 3 function for
using dot product of numpy array to subchemical potentials into the surface grand potential. This is much faster than using the subs function in sympy.

Response:
def sub_chempots(gamma_dict, chempots):
    """
    Uses dot product of numpy array to sub chemical potentials
        into the surface grand potential. This is much faster
        than using the subs function in sympy.
    Args:
        gamma_dict (dict): Surface grand potential equation
            as a coefficient dictionary
        chempots (dict): Dictionary assigning each chemical
            potential (key) in gamma a value
    Returns:
        Surface energy as a float
    """

    coeffs = [gamma_dict[k] for k in gamma_dict.keys()]
    chempot_vals = []
    for k in gamma_dict.keys():
        if k not in chempots.keys():
            chempot_vals.append(k)
        elif k == 1:
            chempot_vals.append(1)
        else:
            chempot_vals.append(chempots[k])

    return np.dot(coeffs, chempot_vals)


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

        d = {"@module": self.__class__.__module__,
             "@class": self.__class__.__name__}
        d["structure"] = self.structure
        d["energy"] = self.energy
        d["miller_index"] = self.miller_index
        d["label"] = self.label
        d["coverage"] = self.coverage
        d["adsorbates"] = self.adsorbates
        d["clean_entry"] = self.clean_entry

        return d

Response: Returns a dict which contains Slab Entry data.


Instruction:
Can you tell what is the following Python 3 function doing
def gibbs_binding_energy(self, eads=False):

        n = self.get_unit_primitive_area
        Nads = self.Nads_in_slab

        BE = (self.energy - n * self.clean_entry.energy) / Nads - \
             sum([ads.energy_per_atom for ads in self.adsorbates])
        return BE * Nads if eads else BE

Response: Calculates the adsorption energy or Gibbs s binding energy of an adsorbate on a surface.


Instruction:
Create a Python 3 function to
calculate the surface energy of a bulk entry.

Response:
def surface_energy(self, ucell_entry, ref_entries=None):
        """
        Calculates the surface energy of this SlabEntry.
        Args:
            ucell_entry (entry): An entry object for the bulk
            ref_entries (list: [entry]): A list of entries for each type
                of element to be used as a reservoir for nonstoichiometric
                systems. The length of this list MUST be n-1 where n is the
                number of different elements in the bulk entry. The chempot
                of the element ref_entry that is not in the list will be
                treated as a variable.

        Returns (Add (Sympy class)): Surface energy
        """

        # Set up
        ref_entries = [] if not ref_entries else ref_entries

        # Check if appropriate ref_entries are present if the slab is non-stoichiometric
        # TODO: There should be a way to identify which specific species are
        # non-stoichiometric relative to the others in systems with more than 2 species
        slab_comp = self.composition.as_dict()
        ucell_entry_comp = ucell_entry.composition.reduced_composition.as_dict()
        slab_clean_comp = Composition({el: slab_comp[el] for el in ucell_entry_comp.keys()})
        if slab_clean_comp.reduced_composition != ucell_entry.composition.reduced_composition:
            list_els = [list(entry.composition.as_dict().keys())[0] for entry in ref_entries]
            if not any([el in list_els for el in ucell_entry.composition.as_dict().keys()]):
                warnings.warn("Elemental references missing for the non-dopant species.")

        gamma = (Symbol("E_surf") - Symbol("Ebulk")) / (2 * Symbol("A"))
        ucell_comp = ucell_entry.composition
        ucell_reduced_comp = ucell_comp.reduced_composition
        ref_entries_dict = {str(list(ref.composition.as_dict().keys())[0]): \
                                ref for ref in ref_entries}
        ref_entries_dict.update(self.ads_entries_dict)

        # Calculate Gibbs free energy of the bulk per unit formula
        gbulk = ucell_entry.energy / \
                ucell_comp.get_integer_formula_and_factor()[1]

        # First we get the contribution to the bulk energy
        # from each element with an existing ref_entry.
        bulk_energy, gbulk_eqn = 0, 0
        for el, ref in ref_entries_dict.items():
            N, delu = self.composition.as_dict()[el], Symbol("delu_" + str(el))
            if el in ucell_comp.as_dict().keys():
                gbulk_eqn += ucell_reduced_comp[el] * (delu + ref.energy_per_atom)
            bulk_energy += N * (Symbol("delu_" + el) + ref.energy_per_atom)

        # Next, we add the contribution to the bulk energy from
        # the variable element (the element without a ref_entry),
        # as a function of the other elements
        for ref_el in ucell_comp.as_dict().keys():
            if str(ref_el) not in ref_entries_dict.keys():
                break
        refEperA = (gbulk - gbulk_eqn) / ucell_reduced_comp.as_dict()[ref_el]
        bulk_energy += self.composition.as_dict()[ref_el] * refEperA
        se = gamma.subs({Symbol("E_surf"): self.energy, Symbol("Ebulk"): bulk_energy,
                         Symbol("A"): self.surface_area})

        return float(se) if type(se).__name__ == "Float" else se


Instruction:
How would you explain what the following Python 3 function does
def get_unit_primitive_area(self):

        A_ads = self.surface_area
        A_clean = self.clean_entry.surface_area
        n = (A_ads / A_clean)
        return n

Response: Returns the unit area of the primitive slab system.


Instruction:
Here you have a function in Python 3, explain what it does
def get_monolayer(self):

        unit_a = self.get_unit_primitive_area
        Nsurfs = self.Nsurfs_ads_in_slab
        Nads = self.Nads_in_slab
        return Nads / (unit_a * Nsurfs)

Response: Returns the monolayer of the object.


Instruction:
Can you generate the documentation for the following Python 3 function
def Nads_in_slab(self):
        return sum([self.composition.as_dict()[a] for a \
                    in self.ads_entries_dict.keys()])

Response: Returns the total number of adsorbates in the slab on BOTH sides


Instruction:
Here you have a function in Python 3, explain what it does
def Nsurfs_ads_in_slab(self):

        struct = self.structure
        weights = [s.species.weight for s in struct]
        center_of_mass = np.average(struct.frac_coords,
                                    weights=weights, axis=0)

        Nsurfs = 0
        # Are there adsorbates on top surface?
        if any([site.species_string in self.ads_entries_dict.keys() for \
                site in struct if site.frac_coords[2] > center_of_mass[2]]):
            Nsurfs += 1
        # Are there adsorbates on bottom surface?
        if any([site.species_string in self.ads_entries_dict.keys() for \
                site in struct if site.frac_coords[2] < center_of_mass[2]]):
            Nsurfs += 1

        return Nsurfs

Response: Returns the TOTAL number of adsorbed surfaces in the slab


Instruction:
Make a summary of the following Python 3 code
def from_dict(cls, d):

        structure = SlabEntry.from_dict(d["structure"])
        energy = SlabEntry.from_dict(d["energy"])
        miller_index = d["miller_index"]
        label = d["label"]
        coverage = d["coverage"]
        adsorbates = d["adsorbates"]
        clean_entry = d["clean_entry"] = self.clean_entry

        return SlabEntry(structure, energy, miller_index, label=label,
                         coverage=coverage, adsorbates=adsorbates,
                         clean_entry=clean_entry)

Response: Returns a SlabEntry by reading in a dictionary containing information about the entry.


Instruction:
Implement a function in Python 3 to
return a slab with the adsorbates removed

Response:
def cleaned_up_slab(self):
        """
        Returns a slab with the adsorbates removed
        """
        ads_strs = list(self.ads_entries_dict.keys())
        cleaned = self.structure.copy()
        cleaned.remove_species(ads_strs)
        return cleaned


Instruction:
Write a Python 3 script for
returning a label for this particular slab based on composition coverage and Miller index.

Response:
def create_slab_label(self):
        """
        Returns a label (str) for this particular slab based
            on composition, coverage and Miller index.
        """

        if "label" in self.data.keys():
            return self.data["label"]

        label = str(self.miller_index)
        ads_strs = list(self.ads_entries_dict.keys())

        cleaned = self.cleaned_up_slab
        label += " %s" % (cleaned.composition.reduced_composition)

        if self.adsorbates:
            for ads in ads_strs:
                label += r"+%s" % (ads)
            label += r", %.3f ML" % (self.get_monolayer)
        return label


Instruction:
Can you tell what is the following Python 3 function doing
def from_computed_structure_entry(entry, miller_index, label=None,
                                      adsorbates=None, clean_entry=None, **kwargs):
        """
        Returns SlabEntry from a ComputedStructureEntry
        """
        return SlabEntry(entry.structure, entry.energy, miller_index, label=label,
                         adsorbates=adsorbates, clean_entry=clean_entry, **kwargs)

Response: Returns a SlabEntry object from a ComputedStructureEntry object


Instruction:
Can you implement a function in Python 3 that
returns the most stable entry corresponding to a particular chemical potential at a specific chemical potential.

Response:
def get_stable_entry_at_u(self, miller_index, delu_dict=None, delu_default=0,
                              no_doped=False, no_clean=False):
        """
        Returns the entry corresponding to the most stable slab for a particular
            facet at a specific chempot. We assume that surface energy is constant
            so all free variables must be set with delu_dict, otherwise they are
            assumed to be equal to delu_default.

        Args:
            miller_index ((h,k,l)): The facet to find the most stable slab in
            delu_dict (Dict): Dictionary of the chemical potentials to be set as
                constant. Note the key should be a sympy Symbol object of the
                format: Symbol("delu_el") where el is the name of the element.
            delu_default (float): Default value for all unset chemical potentials
            no_doped (bool): Consider stability of clean slabs only.
            no_clean (bool): Consider stability of doped slabs only.

        Returns:
            SlabEntry, surface_energy (float)
        """

        all_delu_dict = self.set_all_variables(delu_dict, delu_default)
        def get_coeffs(e):
            coeffs = []
            for du in all_delu_dict.keys():
                if type(self.as_coeffs_dict[e]).__name__ == 'float':
                    coeffs.append(self.as_coeffs_dict[e])
                elif du in self.as_coeffs_dict[e].keys():
                    coeffs.append(self.as_coeffs_dict[e][du])
                else:
                    coeffs.append(0)
            return np.array(coeffs)

        all_entries, all_coeffs = [], []
        for entry in self.all_slab_entries[miller_index].keys():
            if not no_clean:
                all_entries.append(entry)
                all_coeffs.append(get_coeffs(entry))
            if not no_doped:
                for ads_entry in self.all_slab_entries[miller_index][entry]:
                    all_entries.append(ads_entry)
                    all_coeffs.append(get_coeffs(ads_entry))

        du_vals = np.array(list(all_delu_dict.values()))
        all_gamma = list(np.dot(all_coeffs, du_vals.T))

        return all_entries[all_gamma.index(min(all_gamma))], float(min(all_gamma))


Instruction:
Can you tell what is the following Python 3 function doing
def wulff_from_chempot(self, delu_dict=None, delu_default=0, symprec=1e-5,
                           no_clean=False, no_doped=False):
        """
        Method to get the Wulff shape at a specific chemical potential.

        Args:
            delu_dict (Dict): Dictionary of the chemical potentials to be set as
                constant. Note the key should be a sympy Symbol object of the
                format: Symbol("delu_el") where el is the name of the element.
            delu_default (float): Default value for all unset chemical potentials
            symprec (float): See WulffShape.
            no_doped (bool): Consider stability of clean slabs only.
            no_clean (bool): Consider stability of doped slabs only.

        Returns:
            (WulffShape): The WulffShape at u_ref and u_ads.
        """

        latt = SpacegroupAnalyzer(self.ucell_entry.structure). \
            get_conventional_standard_structure().lattice

        miller_list = self.all_slab_entries.keys()
        e_surf_list = []
        for hkl in miller_list:
            # For all configurations, calculate surface energy as a
            # function of u. Use the lowest surface energy (corresponds
            # to the most stable slab termination at that particular u)
            gamma = self.get_stable_entry_at_u(hkl, delu_dict=delu_dict,
                                               delu_default=delu_default,
                                               no_clean=no_clean,
                                               no_doped=no_doped)[1]
            e_surf_list.append(gamma)

        return WulffShape(latt, miller_list, e_surf_list, symprec=symprec)

Response: Method to get the Wulff shape at a specific chemical potential.


Instruction:
How would you code a function in Python 3 to
plot the area contribution of each facet vs chemical potential.

Response:
def area_frac_vs_chempot_plot(self, ref_delu, chempot_range, delu_dict=None,
                                  delu_default=0, increments=10, no_clean=False, no_doped=False):
        """
        1D plot. Plots the change in the area contribution
        of each facet as a function of chemical potential.

        Args:
            ref_delu (sympy Symbol): The free variable chempot with the format:
                Symbol("delu_el") where el is the name of the element.
            chempot_range (list): Min/max range of chemical potential to plot along
            delu_dict (Dict): Dictionary of the chemical potentials to be set as
                constant. Note the key should be a sympy Symbol object of the
                format: Symbol("delu_el") where el is the name of the element.
            delu_default (float): Default value for all unset chemical potentials
            increments (int): Number of data points between min/max or point
                of intersection. Defaults to 10 points.

        Returns:
            (Pylab): Plot of area frac on the Wulff shape
                for each facet vs chemical potential.
        """

        delu_dict = delu_dict if delu_dict else {}
        chempot_range = sorted(chempot_range)
        all_chempots = np.linspace(min(chempot_range), max(chempot_range),
                                   increments)

        # initialize a dictionary of lists of fractional areas for each hkl
        hkl_area_dict = {}
        for hkl in self.all_slab_entries.keys():
            hkl_area_dict[hkl] = []

        # Get plot points for each Miller index
        for u in all_chempots:
            delu_dict[ref_delu] = u
            wulffshape = self.wulff_from_chempot(delu_dict=delu_dict, no_clean=no_clean,
                                                 no_doped=no_doped, delu_default=delu_default)

            for hkl in wulffshape.area_fraction_dict.keys():
                hkl_area_dict[hkl].append(wulffshape.area_fraction_dict[hkl])

        # Plot the area fraction vs chemical potential for each facet
        plt = pretty_plot(width=8, height=7)
        axes = plt.gca()

        for hkl in self.all_slab_entries.keys():
            clean_entry = list(self.all_slab_entries[hkl].keys())[0]
            # Ignore any facets that never show up on the
            # Wulff shape regardless of chemical potential
            if all([a == 0 for a in hkl_area_dict[hkl]]):
                continue
            else:
                plt.plot(all_chempots, hkl_area_dict[hkl],
                         '--', color=self.color_dict[clean_entry],
                         label=str(hkl))

        # Make the figure look nice
        plt.ylabel(r"Fractional area $A^{Wulff}_{hkl}/A^{Wulff}$")
        self.chempot_plot_addons(plt, chempot_range, str(ref_delu).split("_")[1],
                                 axes, rect=[-0.0, 0, 0.95, 1], pad=5, ylim=[0, 1])

        return plt


Instruction:
Explain what the following Python 3 code does
def get_surface_equilibrium(self, slab_entries, delu_dict=None):

        """
        Takes in a list of SlabEntries and calculates the chemical potentials
            at which all slabs in the list coexists simultaneously. Useful for
            building surface phase diagrams. Note that to solve for x equations
            (x slab_entries), there must be x free variables (chemical potentials).
            Adjust delu_dict as need be to get the correct number of free variables.
        Args:
            slab_entries (array): The coefficients of the first equation
            delu_dict (Dict): Dictionary of the chemical potentials to be set as
                constant. Note the key should be a sympy Symbol object of the
                format: Symbol("delu_el") where el is the name of the element.

        Returns:
            (array): Array containing a solution to x equations with x
                variables (x-1 chemical potential and 1 surface energy)
        """
        # Generate all possible coefficients
        all_parameters = []
        all_eqns = []
        for slab_entry in slab_entries:
            se = self.surfe_dict[slab_entry]

            # remove the free chempots we wish to keep constant and
            # set the equation to 0 (subtract gamma from both sides)
            if type(se).__name__ == "float":
                all_eqns.append(se - Symbol("gamma"))
            else:

                se = sub_chempots(se, delu_dict) if delu_dict else se
                all_eqns.append(se - Symbol("gamma"))
                all_parameters.extend([p for p in list(se.free_symbols)
                                       if p not in all_parameters])

        all_parameters.append(Symbol("gamma"))
        # Now solve the system of linear eqns to find the chempot
        # where the slabs are at equilibrium with each other

        soln = linsolve(all_eqns, all_parameters)
        if not soln:
            warnings.warn("No solution")
            return soln
        return {p: list(soln)[0][i] for i, p in enumerate(all_parameters)}

Response: This function calculates the chemical potentials at which all slabs in the list coexists simultaneously.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def stable_u_range_dict(self, chempot_range, ref_delu, no_doped=True,
                            no_clean=False, delu_dict={}, miller_index=(),
                            dmu_at_0=False, return_se_dict=False):
        """
        Creates a dictionary where each entry is a key pointing to a
        chemical potential range where the surface of that entry is stable.
        Does so by enumerating through all possible solutions (intersect)
        for surface energies of a specific facet.

        Args:
            chempot_range ([max_chempot, min_chempot]): Range to consider the
                stability of the slabs.
            ref_delu (sympy Symbol): The range stability of each slab is based
                on the chempot range of this chempot. Should be a sympy Symbol
                object of the format: Symbol("delu_el") where el is the name of
                the element
            no_doped (bool): Consider stability of clean slabs only.
            no_clean (bool): Consider stability of doped slabs only.
            delu_dict (Dict): Dictionary of the chemical potentials to be set as
                constant. Note the key should be a sympy Symbol object of the
                format: Symbol("delu_el") where el is the name of the element.
            miller_index (list): Miller index for a specific facet to get a
                dictionary for.
            dmu_at_0 (bool): If True, if the surface energies corresponding to
                the chemical potential range is between a negative and positive
                value, the value is a list of three chemical potentials with the
                one in the center corresponding a surface energy of 0. Uselful
                in identifying unphysical ranges of surface energies and their
                chemical potential range.
            return_se_dict (bool): Whether or not to return the corresponding
                dictionary of surface energies
        """

        chempot_range = sorted(chempot_range)
        stable_urange_dict, se_dict = {}, {}

        # Get all entries for a specific facet
        for hkl in self.all_slab_entries.keys():
            entries_in_hkl = []
            # Skip this facet if this is not the facet we want
            if miller_index and hkl != tuple(miller_index):
                continue
            if not no_clean:
                entries_in_hkl.extend([clean for clean in self.all_slab_entries[hkl]])
            if not no_doped:
                for entry in self.all_slab_entries[hkl]:
                    entries_in_hkl.extend([ads_entry for ads_entry in
                                           self.all_slab_entries[hkl][entry]])

            for entry in entries_in_hkl:
                stable_urange_dict[entry] = []
                se_dict[entry] = []
            # if there is only one entry for this facet, then just give it the
            # default urange, you can't make combinations with just 1 item
            if len(entries_in_hkl) == 1:
                stable_urange_dict[entries_in_hkl[0]] = chempot_range
                u1, u2 = delu_dict.copy(), delu_dict.copy()
                u1[ref_delu], u2[ref_delu] = chempot_range[0], chempot_range[1]
                se = self.as_coeffs_dict[entries_in_hkl[0]]
                se_dict[entries_in_hkl[0]] = [sub_chempots(se, u1), sub_chempots(se, u2)]
                continue

            for pair in itertools.combinations(entries_in_hkl, 2):
                # I'm assuming ref_delu was not set in delu_dict,
                # so the solution should be for ref_delu
                solution = self.get_surface_equilibrium(pair, delu_dict=delu_dict)

                # Check if this solution is stable
                if not solution:
                    continue
                new_delu_dict = delu_dict.copy()
                new_delu_dict[ref_delu] = solution[ref_delu]
                stable_entry, gamma = self.get_stable_entry_at_u(hkl, new_delu_dict,
                                                                 no_doped=no_doped,
                                                                 no_clean=no_clean)
                if stable_entry not in pair:
                    continue

                # Now check if the solution is within the chempot range
                if not (chempot_range[0] <= solution[ref_delu] <= chempot_range[1]):
                    continue

                for entry in pair:
                    stable_urange_dict[entry].append(solution[ref_delu])
                    se_dict[entry].append(gamma)

            # Now check if all entries have 2 chempot values. If only
            # one, we need to set the other value as either the upper
            # limit or lower limit of the user provided chempot_range
            new_delu_dict = delu_dict.copy()
            for u in chempot_range:
                new_delu_dict[ref_delu] = u
                entry, gamma = self.get_stable_entry_at_u(hkl, delu_dict=new_delu_dict,
                                                          no_doped=no_doped,
                                                          no_clean=no_clean)
                stable_urange_dict[entry].append(u)
                se_dict[entry].append(gamma)

        if dmu_at_0:
            for entry in se_dict.keys():
                # if se are of opposite sign, determine chempot when se=0.
                # Useful for finding a chempot range where se is unphysical
                if not stable_urange_dict[entry]:
                    continue
                if se_dict[entry][0] * se_dict[entry][1] < 0:
                    # solve for gamma=0
                    se = self.as_coeffs_dict[entry]
                    se_dict[entry].append(0)
                    stable_urange_dict[entry].append(solve(sub_chempots(se, delu_dict),
                                                           ref_delu)[0])

        # sort the chempot ranges for each facet
        for entry in stable_urange_dict.keys():
            se_dict[entry] = [se for i, se in sorted(zip(stable_urange_dict[entry],
                                                         se_dict[entry]))]
            stable_urange_dict[entry] = sorted(stable_urange_dict[entry])

        if return_se_dict:
            return stable_urange_dict, se_dict
        else:
            return stable_urange_dict

Response: Creates a dictionary that associates the values of the chemical potentials in the chemical potential range with the corresponding surface energies.


Instruction:
Can you generate a brief explanation for the following Python 3 code
def color_palette_dict(self, alpha=0.35):

        color_dict = {}
        for hkl in self.all_slab_entries.keys():
            rgb_indices = [0, 1, 2]
            color = [0, 0, 0, 1]
            random.shuffle(rgb_indices)
            for i, ind in enumerate(rgb_indices):
                if i == 2:
                    break
                color[ind] = np.random.uniform(0, 1)

            # Get the clean (solid) colors first
            clean_list = np.linspace(0, 1, len(self.all_slab_entries[hkl]))
            for i, clean in enumerate(self.all_slab_entries[hkl].keys()):
                c = copy.copy(color)
                c[rgb_indices[2]] = clean_list[i]
                color_dict[clean] = c

                # Now get the adsorbed (transparent) colors
                for ads_entry in self.all_slab_entries[hkl][clean]:
                    c_ads = copy.copy(c)
                    c_ads[3] = alpha
                    color_dict[ads_entry] = c_ads

        return color_dict

Response: Function to assign each facet a unique color to a dictionary.


Instruction:
Can you generate the documentation for the following Python 3 function
def chempot_vs_gamma_plot_one(self, plt, entry, ref_delu, chempot_range,
                                  delu_dict={}, delu_default=0, label='', JPERM2=False):
        """
        Helper function to  help plot the surface energy of a
        single SlabEntry as a function of chemical potential.

        Args:
            plt (Plot): A plot.
            entry (SlabEntry): Entry of the slab whose surface energy we want
                to plot
            ref_delu (sympy Symbol): The range stability of each slab is based
                on the chempot range of this chempot. Should be a sympy Symbol
                object of the format: Symbol("delu_el") where el is the name of
                the element
            chempot_range ([max_chempot, min_chempot]): Range to consider the
                stability of the slabs.
            delu_dict (Dict): Dictionary of the chemical potentials to be set as
                constant. Note the key should be a sympy Symbol object of the
                format: Symbol("delu_el") where el is the name of the element.
            delu_default (float): Default value for all unset chemical potentials
            label (str): Label of the slab for the legend.
            JPERM2 (bool): Whether to plot surface energy in /m^2 (True) or
                eV/A^2 (False)

        Returns:
            (Plot): Plot of surface energy vs chemical potential for one entry.
        """

        chempot_range = sorted(chempot_range)

        # use dashed lines for slabs that are not stoichiometric
        # wrt bulk. Label with formula if nonstoichiometric
        ucell_comp = self.ucell_entry.composition.reduced_composition
        if entry.adsorbates:
            s = entry.cleaned_up_slab
            clean_comp = s.composition.reduced_composition
        else:
            clean_comp = entry.composition.reduced_composition

        mark = '--' if ucell_comp != clean_comp else '-'

        delu_dict = self.set_all_variables(delu_dict, delu_default)
        delu_dict[ref_delu] = chempot_range[0]
        gamma_min = self.as_coeffs_dict[entry]
        gamma_min = gamma_min if type(gamma_min).__name__ == \
                                 "float" else sub_chempots(gamma_min, delu_dict)
        delu_dict[ref_delu] = chempot_range[1]
        gamma_max = self.as_coeffs_dict[entry]
        gamma_max = gamma_max if type(gamma_max).__name__ == \
                                 "float" else sub_chempots(gamma_max, delu_dict)
        gamma_range = [gamma_min, gamma_max]

        se_range = np.array(gamma_range) * EV_PER_ANG2_TO_JOULES_PER_M2 \
            if JPERM2 else gamma_range

        mark = entry.mark if entry.mark else mark
        c = entry.color if entry.color else self.color_dict[entry]
        plt.plot(chempot_range, se_range, mark, color=c, label=label)

        return plt

Response: This function will help plot the surface energy vs the chemical potential of one entry.


Instruction:
Can you create a Python 3 function that
plots the surface energy of each element in the chemical potential.

Response:
def chempot_vs_gamma(self, ref_delu, chempot_range, miller_index=(),
                         delu_dict={}, delu_default=0, JPERM2=False,
                         show_unstable=False, ylim=[], plt=None,
                         no_clean=False, no_doped=False,
                         use_entry_labels=False, no_label=False):
        """
        Plots the surface energy as a function of chemical potential.
            Each facet will be associated with its own distinct colors.
            Dashed lines will represent stoichiometries different from that
            of the mpid's compound. Transparent lines indicates adsorption.

        Args:
            ref_delu (sympy Symbol): The range stability of each slab is based
                on the chempot range of this chempot. Should be a sympy Symbol
                object of the format: Symbol("delu_el") where el is the name of
                the element
            chempot_range ([max_chempot, min_chempot]): Range to consider the
                stability of the slabs.
            miller_index (list): Miller index for a specific facet to get a
                dictionary for.
            delu_dict (Dict): Dictionary of the chemical potentials to be set as
                constant. Note the key should be a sympy Symbol object of the
                format: Symbol("delu_el") where el is the name of the element.
            delu_default (float): Default value for all unset chemical potentials
            JPERM2 (bool): Whether to plot surface energy in /m^2 (True) or
                eV/A^2 (False)
            show_unstable (bool): Whether or not to show parts of the surface
                energy plot outside the region of stability.
            ylim ([ymax, ymin]): Range of y axis
            no_doped (bool): Whether to plot for the clean slabs only.
            no_clean (bool): Whether to plot for the doped slabs only.
            use_entry_labels (bool): If True, will label each slab configuration
                according to their given label in the SlabEntry object.
            no_label (bool): Option to turn off labels.

        Returns:
            (Plot): Plot of surface energy vs chempot for all entries.
        """

        chempot_range = sorted(chempot_range)

        plt = pretty_plot(width=8, height=7) if not plt else plt
        axes = plt.gca()

        for hkl in self.all_slab_entries.keys():
            if miller_index and hkl != tuple(miller_index):
                continue
            # Get the chempot range of each surface if we only
            # want to show the region where each slab is stable
            if not show_unstable:
                stable_u_range_dict = self.stable_u_range_dict(chempot_range, ref_delu,
                                                               no_doped=no_doped,
                                                               delu_dict=delu_dict,
                                                               miller_index=hkl)

            already_labelled = []
            label = ''
            for clean_entry in self.all_slab_entries[hkl]:

                urange = stable_u_range_dict[clean_entry] if \
                    not show_unstable else chempot_range
                # Don't plot if the slab is unstable, plot if it is.
                if urange != []:

                    label = clean_entry.label
                    if label in already_labelled:
                        label = None
                    else:
                        already_labelled.append(label)
                    if not no_clean:
                        if use_entry_labels:
                            label = clean_entry.label
                        if no_label:
                            label = ""
                        plt = self.chempot_vs_gamma_plot_one(plt, clean_entry, ref_delu,
                                                             urange, delu_dict=delu_dict,
                                                             delu_default=delu_default,
                                                             label=label, JPERM2=JPERM2)
                if not no_doped:
                    for ads_entry in self.all_slab_entries[hkl][clean_entry]:
                        # Plot the adsorbed slabs
                        # Generate a label for the type of slab
                        urange = stable_u_range_dict[ads_entry] \
                            if not show_unstable else chempot_range
                        if urange != []:
                            if use_entry_labels:
                                label = ads_entry.label
                            if no_label:
                                label = ""
                            plt = self.chempot_vs_gamma_plot_one(plt, ads_entry,
                                                                 ref_delu, urange,
                                                                 delu_dict=delu_dict,
                                                                 delu_default=delu_default,
                                                                 label=label,
                                                                 JPERM2=JPERM2)

        # Make the figure look nice
        plt.ylabel(r"Surface energy (J/$m^{2}$)") if JPERM2 \
            else plt.ylabel(r"Surface energy (eV/$\AA^{2}$)")
        plt = self.chempot_plot_addons(plt, chempot_range, str(ref_delu).split("_")[1],
                                       axes, ylim=ylim)

        return plt


Instruction:
Implement a Python 3 function for
plotting the binding energy vs monolayer for all facets.

Response:
def monolayer_vs_BE(self, plot_eads=False):
        """
        Plots the binding energy energy as a function of monolayers (ML), i.e.
            the fractional area adsorbate density for all facets. For each
            facet at a specific monlayer, only plot the lowest binding energy.

        Args:
            plot_eads (bool): Option to plot the adsorption energy (binding
                 energy multiplied by number of adsorbates) instead.

        Returns:
            (Plot): Plot of binding energy vs monolayer for all facets.
        """

        plt = pretty_plot(width=8, height=7)
        for hkl in self.all_slab_entries.keys():
            ml_be_dict = {}
            for clean_entry in self.all_slab_entries[hkl].keys():
                if self.all_slab_entries[hkl][clean_entry]:
                    for ads_entry in self.all_slab_entries[hkl][clean_entry]:
                        if ads_entry.get_monolayer not in ml_be_dict.keys():
                            ml_be_dict[ads_entry.get_monolayer] = 1000
                        be = ads_entry.gibbs_binding_energy(eads=plot_eads)
                        if be < ml_be_dict[ads_entry.get_monolayer]:
                            ml_be_dict[ads_entry.get_monolayer] = be
            # sort the binding energies and monolayers
            # in order to properly draw a line plot
            vals = sorted(ml_be_dict.items())
            monolayers, BEs = zip(*vals)
            plt.plot(monolayers, BEs, '-o',
                     c=self.color_dict[clean_entry], label=hkl)

        adsorbates = tuple(ads_entry.ads_entries_dict.keys())
        plt.xlabel(" %s" * len(adsorbates) % adsorbates + " Coverage (ML)")
        plt.ylabel("Adsorption Energy (eV)") if plot_eads \
            else plt.ylabel("Binding Energy (eV)")
        plt.legend()
        plt.tight_layout()

        return plt


Instruction:
Make a summary of the following Python 3 code
def BE_vs_clean_SE(self, delu_dict, delu_default=0, plot_eads=False,
                       annotate_monolayer=True, JPERM2=False):
        """
        For each facet, plot the clean surface energy against the most
            stable binding energy.
        Args:
            delu_dict (Dict): Dictionary of the chemical potentials to be set as
                constant. Note the key should be a sympy Symbol object of the
                format: Symbol("delu_el") where el is the name of the element.
            delu_default (float): Default value for all unset chemical potentials
            plot_eads (bool): Option to plot the adsorption energy (binding
                energy multiplied by number of adsorbates) instead.
            annotate_monolayer (bool): Whether or not to label each data point
                with its monolayer (adsorbate density per unit primiitve area)
            JPERM2 (bool): Whether to plot surface energy in /m^2 (True) or
                eV/A^2 (False)

        Returns:
            (Plot): Plot of clean surface energy vs binding energy for
                all facets.
        """

        plt = pretty_plot(width=8, height=7)
        for hkl in self.all_slab_entries.keys():
            for clean_entry in self.all_slab_entries[hkl].keys():
                all_delu_dict = self.set_all_variables(delu_dict, delu_default)
                if self.all_slab_entries[hkl][clean_entry]:
                    clean_se = self.as_coeffs_dict[clean_entry]
                    se = sub_chempots(clean_se, all_delu_dict)
                    for ads_entry in self.all_slab_entries[hkl][clean_entry]:
                        ml = ads_entry.get_monolayer
                        be = ads_entry.gibbs_binding_energy(eads=plot_eads)

                        # Now plot the surface energy vs binding energy
                        plt.scatter(se, be)
                        if annotate_monolayer:
                            plt.annotate("%.2f" % (ml), xy=[se, be],
                                         xytext=[se, be])

        plt.xlabel(r"Surface energy ($J/m^2$)") if JPERM2 \
            else plt.xlabel(r"Surface energy ($eV/\AA^2$)")
        plt.ylabel("Adsorption Energy (eV)") if plot_eads \
            else plt.ylabel("Binding Energy (eV)")
        plt.tight_layout()
        plt.xticks(rotation=60)

        return plt

Response: Plots the clean surface energy vs the stable binding energy for each facet.


Instruction:
How would you code a function in Python 3 to
adapt from the get_chempot_range_map() method in the PhaseDiagram class. Plot the chemical potential range map based on surface energy stability. Currently works only for 2-component PDs. At the moment uses a brute force method by enumerating through the range of the first element chempot with a specified increment and determines the chempot rangeo fht e second element for each SlabEntry. Future implementation will determine the chempot range map first by solving systems of equations up to 3 instead of 2. Args: elements (list): Sequence of elements to be considered as independent variables. E.g., if you want to show the stability ranges of all Li-Co-O phases wrt to duLi and duO, you will supply [Element("Li"), Element("O")] miller_index ([h, k, l]): Miller index of the surface we are interested in ranges ([[range1], [range2]]): List of chempot ranges (max and min values) for the first and second element. incr (int): Number of points to sample along the range of the first chempot no_doped (bool): Whether or not to include doped systems. no_clean (bool): Whether or not to include clean systems. delu_dict (Dict): Dictionary of the chemical potentials to be set as constant. Note the key should be a sympy Symbol object of the format: Symbol("delu_el") where el is the name of the element. annotate (bool): Whether to annotate each "phase" with the label of the entry. If no label, uses the reduced formula show_unphyiscal_only (bool): Whether to only show the shaded region where surface energy is negative. Useful for drawing other chempot range maps.

Response:
def surface_chempot_range_map(self, elements, miller_index, ranges,
                                  incr=50, no_doped=False, no_clean=False,
                                  delu_dict=None, plt=None, annotate=True,
                                  show_unphyiscal_only=False, fontsize=10):
        """
        Adapted from the get_chempot_range_map() method in the PhaseDiagram
            class. Plot the chemical potential range map based on surface
            energy stability. Currently works only for 2-component PDs. At
            the moment uses a brute force method by enumerating through the
            range of the first element chempot with a specified increment
            and determines the chempot rangeo fht e second element for each
            SlabEntry. Future implementation will determine the chempot range
            map first by solving systems of equations up to 3 instead of 2.
        Args:
            elements (list): Sequence of elements to be considered as independent
                variables. E.g., if you want to show the stability ranges of
                all Li-Co-O phases wrt to duLi and duO, you will supply
                [Element("Li"), Element("O")]
            miller_index ([h, k, l]): Miller index of the surface we are interested in
            ranges ([[range1], [range2]]): List of chempot ranges (max and min values)
                for the first and second element.
            incr (int): Number of points to sample along the range of the first chempot
            no_doped (bool): Whether or not to include doped systems.
            no_clean (bool): Whether or not to include clean systems.
            delu_dict (Dict): Dictionary of the chemical potentials to be set as
                constant. Note the key should be a sympy Symbol object of the
                format: Symbol("delu_el") where el is the name of the element.
            annotate (bool): Whether to annotate each "phase" with the label of
                the entry. If no label, uses the reduced formula
            show_unphyiscal_only (bool): Whether to only show the shaded region where
                surface energy is negative. Useful for drawing other chempot range maps.
        """

        # Set up
        delu_dict = delu_dict if delu_dict else {}
        plt = pretty_plot(12, 8) if not plt else plt
        el1, el2 = str(elements[0]), str(elements[1])
        delu1 = Symbol("delu_%s" % (str(elements[0])))
        delu2 = Symbol("delu_%s" % (str(elements[1])))
        range1 = ranges[0]
        range2 = ranges[1]

        # Find a range map for each entry (surface). This part is very slow, will
        # need to implement a more sophisticated method of getting the range map
        vertices_dict = {}
        for dmu1 in np.linspace(range1[0], range1[1], incr):
            # Get chemical potential range of dmu2 for each increment of dmu1
            new_delu_dict = delu_dict.copy()
            new_delu_dict[delu1] = dmu1
            range_dict, se_dict = self.stable_u_range_dict(range2, delu2, dmu_at_0=True,
                                                           miller_index=miller_index,
                                                           no_doped=no_doped,
                                                           no_clean=no_clean,
                                                           delu_dict=new_delu_dict,
                                                           return_se_dict=True)

            # Save the chempot range for dmu1 and dmu2
            for entry in range_dict.keys():
                if not range_dict[entry]:
                    continue
                if entry not in vertices_dict.keys():
                    vertices_dict[entry] = []

                selist = se_dict[entry]
                vertices_dict[entry].append({delu1: dmu1, delu2: [range_dict[entry], selist]})

        # Plot the edges of the phases
        for entry in vertices_dict.keys():

            xvals, yvals = [], []

            # Plot each edge of a phase within the borders
            for ii, pt1 in enumerate(vertices_dict[entry]):

                # Determine if the surface energy at this lower range
                # of dmu2 is negative. If so, shade this region.
                if len(pt1[delu2][1]) == 3:
                    if pt1[delu2][1][0] < 0:
                        neg_dmu_range = [pt1[delu2][0][0], pt1[delu2][0][1]]
                    else:
                        neg_dmu_range = [pt1[delu2][0][1], pt1[delu2][0][2]]
                    # Shade the threshold and region at which se<=0
                    plt.plot([pt1[delu1], pt1[delu1]], neg_dmu_range, 'k--')
                elif pt1[delu2][1][0] < 0 and pt1[delu2][1][1] < 0:
                    # Any chempot at at this point will result
                    # in se<0, shade the entire y range
                    if not show_unphyiscal_only:
                        plt.plot([pt1[delu1], pt1[delu1]], range2, 'k--')

                if ii == len(vertices_dict[entry]) - 1:
                    break
                pt2 = vertices_dict[entry][ii + 1]
                if not show_unphyiscal_only:
                    plt.plot([pt1[delu1], pt2[delu1]], [pt1[delu2][0][0], pt2[delu2][0][0]], 'k')

                # Need these values to get a good position for labelling phases
                xvals.extend([pt1[delu1], pt2[delu1]])
                yvals.extend([pt1[delu2][0][0], pt2[delu2][0][0]])

            # Plot the edge along the max x value
            pt = vertices_dict[entry][-1]
            delu1, delu2 = pt.keys()
            xvals.extend([pt[delu1], pt[delu1]])
            yvals.extend(pt[delu2][0])
            if not show_unphyiscal_only:
                plt.plot([pt[delu1], pt[delu1]], [pt[delu2][0][0], pt[delu2][0][-1]], 'k')

            if annotate:
                # Label the phases
                x = np.mean([max(xvals), min(xvals)])
                y = np.mean([max(yvals), min(yvals)])
                label = entry.label if entry.label else entry.composition.reduced_formula
                plt.annotate(label, xy=[x, y], xytext=[x, y], fontsize=fontsize)

        # Label plot
        plt.xlim(range1)
        plt.ylim(range2)
        plt.xlabel(r"$\Delta\mu_{%s} (eV)$" % (el1), fontsize=25)
        plt.ylabel(r"$\Delta\mu_{%s} (eV)$" % (el2), fontsize=25)
        plt.xticks(rotation=60)

        return plt


Instruction:
How would you explain what the following Python 3 function does
def set_all_variables(self, delu_dict, delu_default):

        # Set up the variables
        all_delu_dict = {}
        for du in self.list_of_chempots:
            if delu_dict and du in delu_dict.keys():
                all_delu_dict[du] = delu_dict[du]
            elif du == 1:
                all_delu_dict[du] = du
            else:
                all_delu_dict[du] = delu_default

        return all_delu_dict

Response: Sets all variables in the chemical potentials and returns a dictionary where the keys are sympy Symbols and the values are a float.


Instruction:
Write a Python 3 function that can
return a plot of the locpot along the slab model.

Response:
def get_locpot_along_slab_plot(self, label_energies=True,
                                   plt=None, label_fontsize=10):
        """
        Returns a plot of the local potential (eV) vs the
            position along the c axis of the slab model (Ang)

        Args:
            label_energies (bool): Whether to label relevant energy
                quantities such as the work function, Fermi energy,
                vacuum locpot, bulk-like locpot
            plt (plt): Matplotlib pylab object
            label_fontsize (float): Fontsize of labels

        Returns plt of the locpot vs c axis
        """

        plt = pretty_plot(width=6, height=4) if not plt else plt

        # plot the raw locpot signal along c
        plt.plot(self.along_c, self.locpot_along_c, 'b--')

        # Get the local averaged signal of the locpot along c
        xg, yg = [], []
        for i, p in enumerate(self.locpot_along_c):
            # average signal is just the bulk-like potential when in the slab region
            in_slab = False
            for r in self.slab_regions:
                if r[0] <= self.along_c[i] <= r[1]:
                    in_slab = True
            if len(self.slab_regions) > 1:
                if self.along_c[i] >= self.slab_regions[1][1]:
                    in_slab = True
                if self.along_c[i] <= self.slab_regions[0][0]:
                    in_slab = True

            if in_slab:
                yg.append(self.ave_bulk_p)
                xg.append(self.along_c[i])
            elif p < self.ave_bulk_p:
                yg.append(self.ave_bulk_p)
                xg.append(self.along_c[i])
            else:
                yg.append(p)
                xg.append(self.along_c[i])
        xg, yg = zip(*sorted(zip(xg, yg)))
        plt.plot(xg, yg, 'r', linewidth=2.5, zorder=-1)

        # make it look nice
        if label_energies:
            plt = self.get_labels(plt, label_fontsize=label_fontsize)
        plt.xlim([0, 1])
        plt.ylim([min(self.locpot_along_c),
                  self.vacuum_locpot+self.ave_locpot*0.2])
        plt.xlabel(r"Fractional coordinates ($\hat{c}$)", fontsize=25)
        plt.xticks(fontsize=15, rotation=45)
        plt.ylabel(r"Potential (eV)", fontsize=25)
        plt.yticks(fontsize=15)

        return plt


Instruction:
Create a Python 3 function for
returning a Labelled plot of the locpot vs c axis with relevant quantities.

Response:
def get_labels(self, plt, label_fontsize=10):
        """
        Handles the optional labelling of the plot with relevant quantities
        Args:
            plt (plt): Plot of the locpot vs c axis
            label_fontsize (float): Fontsize of labels
        Returns Labelled plt
        """

        # center of vacuum and bulk region
        if len(self.slab_regions) > 1:
            label_in_vac = (self.slab_regions[0][1] + self.slab_regions[1][0])/2
            if abs(self.slab_regions[0][0]-self.slab_regions[0][1]) > \
                    abs(self.slab_regions[1][0]-self.slab_regions[1][1]):
                label_in_bulk = self.slab_regions[0][1]/2
            else:
                label_in_bulk = (self.slab_regions[1][1] + self.slab_regions[1][0]) / 2
        else:
            label_in_bulk = (self.slab_regions[0][0] + self.slab_regions[0][1])/2
            if self.slab_regions[0][0] > 1-self.slab_regions[0][1]:
                label_in_vac = self.slab_regions[0][0] / 2
            else:
                label_in_vac = (1 + self.slab_regions[0][1]) / 2

        plt.plot([0, 1], [self.vacuum_locpot]*2, 'b--', zorder=-5, linewidth=1)
        xy = [label_in_bulk, self.vacuum_locpot+self.ave_locpot*0.05]
        plt.annotate(r"$V_{vac}=%.2f$" %(self.vacuum_locpot), xy=xy,
                     xytext=xy, color='b', fontsize=label_fontsize)

        # label the fermi energy
        plt.plot([0, 1], [self.efermi]*2, 'g--',
                 zorder=-5, linewidth=3)
        xy = [label_in_bulk, self.efermi+self.ave_locpot*0.05]
        plt.annotate(r"$E_F=%.2f$" %(self.efermi), xytext=xy,
                     xy=xy, fontsize=label_fontsize, color='g')

        # label the bulk-like locpot
        plt.plot([0, 1], [self.ave_bulk_p]*2, 'r--', linewidth=1., zorder=-1)
        xy = [label_in_vac, self.ave_bulk_p + self.ave_locpot * 0.05]
        plt.annotate(r"$V^{interior}_{slab}=%.2f$" % (self.ave_bulk_p),
                     xy=xy, xytext=xy, color='r', fontsize=label_fontsize)

        # label the work function as a barrier
        plt.plot([label_in_vac]*2, [self.efermi, self.vacuum_locpot],
                 'k--', zorder=-5, linewidth=2)
        xy = [label_in_vac, self.efermi + self.ave_locpot * 0.05]
        plt.annotate(r"$\Phi=%.2f$" %(self.work_function),
                     xy=xy, xytext=xy, fontsize=label_fontsize)

        return plt